diff --git a/.gitignore b/.gitignore index d524f9c..6d47845 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # User Generated Content cover/ -tmp/ +release/ # Git .git diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..af0018d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Nebulous Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/cmd/skynet/main.go b/cmd/skynet/main.go index 2857944..f0fb1e8 100644 --- a/cmd/skynet/main.go +++ b/cmd/skynet/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "io" "os" "reflect" @@ -9,7 +10,28 @@ import ( "github.com/spf13/cobra/doc" ) +const ( + // mainDocFile is the filepath to the top level doc file created by cobra + mainDocFile = "./doc/skynet.md" + + // docREADMEFile is the filepath to the README file in /doc + docREADMEFile = "./doc/README.md" +) + +// Exit codes. +// inspired by sysexits.h +const ( + exitCodeGeneral = 1 // Not in sysexits.h, but is standard practice. + exitCodeUsage = 64 // EX_USAGE in sysexits.h +) + +var rootCmd *cobra.Command + var ( + // generateDocs will trigger cobra to auto generate the documentation for + // skynet commands + generateDocs bool + // Skynet Flags // // skynetPortal will define a Skynet Portal to use instead of the default. @@ -32,12 +54,30 @@ var ( customFilename string ) -// Exit codes. -// inspired by sysexits.h -const ( - exitCodeGeneral = 1 // Not in sysexits.h, but is standard practice. - exitCodeUsage = 64 // EX_USAGE in sysexits.h -) +// copyDocFile will copy the top level auto generated doc file into a README for +// /doc +func copyDocFile() error { + // Open the main doc file + source, err := os.Open(mainDocFile) + if err != nil { + return err + } + defer source.Close() + + // Open the README file + destination, err := os.Create(docREADMEFile) + if err != nil { + return err + } + defer destination.Close() + + // Copy the main doc file into the README + _, err = io.Copy(destination, source) + if err != nil { + return err + } + return nil +} // die prints its arguments to stderr, then exits the program with the default // error code. @@ -46,9 +86,25 @@ func die(args ...interface{}) { os.Exit(exitCodeGeneral) } +// generateSkyetDocs will have cobra auto generate the documentation for the +// skynet commands +func generateSkyetDocs() { + // Build the docs + err := doc.GenMarkdownTree(rootCmd, "./doc") + if err != nil { + die(err) + } + + // Copy the main skynet.md file to the doc README file + err = copyDocFile() + if err != nil { + die(err) + } +} + func main() { - root := &cobra.Command{ - Use: os.Args[0], + rootCmd = &cobra.Command{ + Use: "skynet", Short: "Perform actions related to Skynet", Long: `Perform actions related to Skynet, a file sharing and data publication platform on top of Sia.`, @@ -56,23 +112,18 @@ on top of Sia.`, } // Add Skynet Commands - root.AddCommand(skynetBlacklistCmd, skynetConvertCmd, skynetDownloadCmd, skynetLsCmd, skynetPinCmd, skynetUnpinCmd, skynetUploadCmd) + rootCmd.AddCommand(skynetBlacklistCmd, skynetConvertCmd, skynetDownloadCmd, skynetLsCmd, skynetPinCmd, skynetUnpinCmd, skynetUploadCmd) // Add Flags - root.PersistentFlags().StringVar(&skynetPortal, "portal", "", "Use a Skynet portal other than the default") + rootCmd.Flags().BoolVarP(&generateDocs, "", "d", false, "Generate the docs for skynet") + rootCmd.PersistentFlags().StringVar(&skynetPortal, "portal", "", "Use a Skynet portal other than the default") skynetUploadCmd.Flags().StringVar(&portalUploadPath, "upload-path", "", "Relative URL path of the upload endpoint") skynetUploadCmd.Flags().StringVar(&portalFileFieldName, "file-field-name", "", "Defines the fieldName for the files on the portal") skynetUploadCmd.Flags().StringVar(&portalDirectoryFileFieldName, "directory-field-name", "", "Defines the fieldName for the directory files on the portal") skynetUploadCmd.Flags().StringVar(&customFilename, "filename", "", "Custom filename for the uploaded file") - // Build the docs - err := doc.GenMarkdownTree(root, "./tmp") - if err != nil { - die(err) - } - // run - if err := root.Execute(); err != nil { + if err := rootCmd.Execute(); err != nil { // Since no commands return errors (all commands set Command.Run instead of // Command.RunE), Command.Execute() should only return an error on an // invalid command or flag. Therefore Command.Usage() was called (assuming diff --git a/cmd/skynet/skynetcmd.go b/cmd/skynet/skynetcmd.go index fd8ae88..376c92e 100644 --- a/cmd/skynet/skynetcmd.go +++ b/cmd/skynet/skynetcmd.go @@ -97,6 +97,11 @@ will pay for storage and repairs until the files are manually deleted.`, // skynetcmd displays general info about the skynet cli. func skynetcmd() { + // Check if the user wants to generate the documentation + if generateDocs { + generateSkyetDocs() + } + // Print General Info fmt.Printf("Use Skynet to upload and share content\n\n") @@ -182,7 +187,7 @@ func skynetuploadcmd(sourcePath string) { if customFilename != "" { opts.CustomFilename = customFilename } - + fmt.Println("Upload Options", opts) // Open the source file. file, err := os.Open(sourcePath) if err != nil { diff --git a/docs/README.md b/doc/README.md similarity index 88% rename from docs/README.md rename to doc/README.md index a5cea96..5e5b084 100644 --- a/docs/README.md +++ b/doc/README.md @@ -14,6 +14,7 @@ skynet [flags] ### Options ``` + -d, -- Generate the docs for skynet -h, --help help for skynet --portal string Use a Skynet portal other than the default ``` @@ -28,4 +29,4 @@ skynet [flags] * [skynet unpin](skynet_unpin.md) - Needs SDK Implementation * [skynet upload](skynet_upload.md) - Upload a file or a directory to Skynet. -###### Auto generated by spf13/cobra on 16-Jun-2020 +###### Auto generated by spf13/cobra on 18-Jun-2020 diff --git a/doc/skynet.md b/doc/skynet.md new file mode 100644 index 0000000..5e5b084 --- /dev/null +++ b/doc/skynet.md @@ -0,0 +1,32 @@ +## skynet + +Perform actions related to Skynet + +### Synopsis + +Perform actions related to Skynet, a file sharing and data publication platform +on top of Sia. + +``` +skynet [flags] +``` + +### Options + +``` + -d, -- Generate the docs for skynet + -h, --help help for skynet + --portal string Use a Skynet portal other than the default +``` + +### SEE ALSO + +* [skynet blacklist](skynet_blacklist.md) - Needs SDK Implementation +* [skynet convert](skynet_convert.md) - Needs SDK Implementation +* [skynet download](skynet_download.md) - Download a skylink from skynet. +* [skynet ls](skynet_ls.md) - Needs SDK Implementation +* [skynet pin](skynet_pin.md) - Needs SDK Implementation +* [skynet unpin](skynet_unpin.md) - Needs SDK Implementation +* [skynet upload](skynet_upload.md) - Upload a file or a directory to Skynet. + +###### Auto generated by spf13/cobra on 18-Jun-2020 diff --git a/docs/skynet_blacklist.md b/doc/skynet_blacklist.md similarity index 60% rename from docs/skynet_blacklist.md rename to doc/skynet_blacklist.md index f83958c..405ee24 100644 --- a/docs/skynet_blacklist.md +++ b/doc/skynet_blacklist.md @@ -16,8 +16,14 @@ skynet blacklist [skylink] [flags] -h, --help help for blacklist ``` +### Options inherited from parent commands + +``` + --portal string Use a Skynet portal other than the default +``` + ### SEE ALSO * [skynet](skynet.md) - Perform actions related to Skynet -###### Auto generated by spf13/cobra on 16-Jun-2020 +###### Auto generated by spf13/cobra on 18-Jun-2020 diff --git a/docs/skynet_convert.md b/doc/skynet_convert.md similarity index 62% rename from docs/skynet_convert.md rename to doc/skynet_convert.md index d187a8f..0eb40fc 100644 --- a/docs/skynet_convert.md +++ b/doc/skynet_convert.md @@ -16,8 +16,14 @@ skynet convert [source siaPath] [destination siaPath] [flags] -h, --help help for convert ``` +### Options inherited from parent commands + +``` + --portal string Use a Skynet portal other than the default +``` + ### SEE ALSO * [skynet](skynet.md) - Perform actions related to Skynet -###### Auto generated by spf13/cobra on 16-Jun-2020 +###### Auto generated by spf13/cobra on 18-Jun-2020 diff --git a/docs/skynet_download.md b/doc/skynet_download.md similarity index 68% rename from docs/skynet_download.md rename to doc/skynet_download.md index 78dd8ff..79e2241 100644 --- a/docs/skynet_download.md +++ b/doc/skynet_download.md @@ -17,8 +17,14 @@ skynet download [skylink] [destination] [flags] -h, --help help for download ``` +### Options inherited from parent commands + +``` + --portal string Use a Skynet portal other than the default +``` + ### SEE ALSO * [skynet](skynet.md) - Perform actions related to Skynet -###### Auto generated by spf13/cobra on 16-Jun-2020 +###### Auto generated by spf13/cobra on 18-Jun-2020 diff --git a/docs/skynet_ls.md b/doc/skynet_ls.md similarity index 57% rename from docs/skynet_ls.md rename to doc/skynet_ls.md index bf2abe6..b69e33d 100644 --- a/docs/skynet_ls.md +++ b/doc/skynet_ls.md @@ -16,8 +16,14 @@ skynet ls [flags] -h, --help help for ls ``` +### Options inherited from parent commands + +``` + --portal string Use a Skynet portal other than the default +``` + ### SEE ALSO * [skynet](skynet.md) - Perform actions related to Skynet -###### Auto generated by spf13/cobra on 16-Jun-2020 +###### Auto generated by spf13/cobra on 18-Jun-2020 diff --git a/docs/skynet_pin.md b/doc/skynet_pin.md similarity index 60% rename from docs/skynet_pin.md rename to doc/skynet_pin.md index 4baefdd..a14fae4 100644 --- a/docs/skynet_pin.md +++ b/doc/skynet_pin.md @@ -16,8 +16,14 @@ skynet pin [skylink] [destination siapath] [flags] -h, --help help for pin ``` +### Options inherited from parent commands + +``` + --portal string Use a Skynet portal other than the default +``` + ### SEE ALSO * [skynet](skynet.md) - Perform actions related to Skynet -###### Auto generated by spf13/cobra on 16-Jun-2020 +###### Auto generated by spf13/cobra on 18-Jun-2020 diff --git a/docs/skynet_unpin.md b/doc/skynet_unpin.md similarity index 59% rename from docs/skynet_unpin.md rename to doc/skynet_unpin.md index e30e619..d2a1dfb 100644 --- a/docs/skynet_unpin.md +++ b/doc/skynet_unpin.md @@ -16,8 +16,14 @@ skynet unpin [siapath] [flags] -h, --help help for unpin ``` +### Options inherited from parent commands + +``` + --portal string Use a Skynet portal other than the default +``` + ### SEE ALSO * [skynet](skynet.md) - Perform actions related to Skynet -###### Auto generated by spf13/cobra on 16-Jun-2020 +###### Auto generated by spf13/cobra on 18-Jun-2020 diff --git a/docs/skynet_upload.md b/doc/skynet_upload.md similarity index 55% rename from docs/skynet_upload.md rename to doc/skynet_upload.md index 1dc679d..8c83b78 100644 --- a/docs/skynet_upload.md +++ b/doc/skynet_upload.md @@ -4,12 +4,12 @@ Upload a file or a directory to Skynet. ### Synopsis -Upload a file or a directory to Skynet. A skylink will be produced which can be -shared and used to retrieve the file. If the given path is a directory all files -under that directory will be uploaded individually and an individual skylink -will be produced for each. All files that get uploaded will be pinned to Skynet -Portal used for the upload, meaning that the portal will pay for storage and -repairs until the files are manually deleted. +Upload a file or a directory to Skynet. A skylink will be produced +which can be shared and used to retrieve the file. If the given path is +a directory all files under that directory will be uploaded individually and +an individual skylink will be produced for each. All files that get uploaded +will be pinned to Skynet Portal used for the upload, meaning that the portal +will pay for storage and repairs until the files are manually deleted. ``` skynet upload [source path] [flags] @@ -25,8 +25,14 @@ skynet upload [source path] [flags] --upload-path string Relative URL path of the upload endpoint ``` +### Options inherited from parent commands + +``` + --portal string Use a Skynet portal other than the default +``` + ### SEE ALSO * [skynet](skynet.md) - Perform actions related to Skynet -###### Auto generated by spf13/cobra on 16-Jun-2020 +###### Auto generated by spf13/cobra on 18-Jun-2020 diff --git a/release-scripts/Dockerfile b/release-scripts/Dockerfile new file mode 100644 index 0000000..b8be498 --- /dev/null +++ b/release-scripts/Dockerfile @@ -0,0 +1,15 @@ +FROM golang@sha256:1e9c36b3fd7d7f9ab95835fb1ed898293ec0917e44c7e7d2766b4a2d9aa43da6 +# golang:1.14.4-buster + +ARG branch +ARG version + +RUN useradd -ms /bin/bash builder +USER builder +WORKDIR /home/builder + +RUN git clone https://github.com/NebulousLabs/skynet-cli +WORKDIR /home/builder/skynet-cli +RUN git fetch --all && git checkout $branch + +RUN ./release-scripts/release.sh $version diff --git a/release-scripts/README.md b/release-scripts/README.md new file mode 100644 index 0000000..11ee4e6 --- /dev/null +++ b/release-scripts/README.md @@ -0,0 +1,36 @@ +# Release Scripts + +This directory contains all the scripts used to build releases of skynet-cli. + +`./release.sh` compiles `skynet`binaries for each supported system. It places +those binaries along with documentation into a separate directory for each +system type. It also creates a file of SHA256 hashes containing the hash of each +binary produced. + +`./package.sh` zips the directories created by `release.sh` and adds their +SHA256 hashes to the checksums file. + +These procedures are split out so that compilation can be done in a more +reproducible environment and reduce the risk of adding non-determinism into the +compilation process. Deterministic builds are desirable so that the binaries +produced and signed as part of the release process can be verified by any +contributor as having been produced using the expected/correct source code. + +`./build-in-docker.sh` uses the reproducible build environment defined in the +`Dockerfile`. The `Dockerfile` uses a base image with just the prerequisites for +Go compilation pre-installed. This script creates a docker container which +checks out a specified branch of this repository and then runs `./release.sh`. +The outputs are copied out of the container before it is deleted. + +Currently this process can only be reproduced and verified on `linux/amd64` +systems. That is, anyone with a `linux/amd64` system can reproduce the binaries +created for **all** systems using this script. In the future this can be +improved to allow reproducibility on more systems. + +## Example +The following list of commands shows how one could use the scripts in this +directory to build skynet-cli. + +1. `./build-in-docker.sh master v1.4.4` +3. `cd ../release && sha256sum --check Sia-v1.4.4-SHA256SUMS.txt` +3. `gpg --clearsign --armor Sia-v1.4.4-SHA256SUMS.txt` diff --git a/release-scripts/build-in-docker.sh b/release-scripts/build-in-docker.sh new file mode 100755 index 0000000..7eddbe9 --- /dev/null +++ b/release-scripts/build-in-docker.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -e + +echo "$0 builds skynet-cli in a reproducible Docker build environment" + +branchName="$1" +versionName="$2" + +if [ -z $branchName ] || [ -z $versionName ]; then + echo "Usage: $0 BRANCHNAME VERSION" + exit 1 +fi + +echo Branch name: ${branchName} +echo Version: ${versionName} +echo "" + +if [ "$SIA_SILENT_RELEASE" != "true" ]; then + read -p "Continue (y/n)?" CONT + if [ "$CONT" != "y" ]; then + exit 1 + fi +fi +echo "Building Docker image..."; + +# Build the image uncached to always get the most up-to-date branch. +docker build --no-cache -t skynet-cli-builder . --build-arg branch=${branchName} --build-arg version=${versionName} + +# Create a container with the artifacts. +docker create --name build-container skynet-cli-builder + +# Copy the artifacts out. +docker cp build-container:/home/builder/skynet-cli/release/ ../ + +# Remove the build container. +docker rm build-container + +# Package the binaries produced. +./package.sh ${versionName} ../release/ + +# Print out the SHA256SUM file. +echo "SHA256SUM of binaries built: " +cat ../release/skynet-cli-${versionName}-SHA256SUMS.txt diff --git a/release-scripts/package.sh b/release-scripts/package.sh new file mode 100755 index 0000000..3fc6a56 --- /dev/null +++ b/release-scripts/package.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -e + +version="$1" + +# Directory where the binaries were placed. +binDir="$2" + +function package { + os=$1 + arch=$2 + + echo Packaging ${os}... + folder=$binDir/skynet-cli-$version-$os-$arch + ( + cd $binDir + zip -rq skynet-cli-$version-$os-$arch.zip skynet-cli-$version-$os-$arch + sha256sum skynet-cli-$version-$os-$arch.zip >> skynet-cli-$version-SHA256SUMS.txt + ) +} + +# Package amd64 binaries. +for os in darwin linux windows; do + package "$os" "amd64" +done + +# Package Raspberry Pi binaries. +package "linux" "arm64" diff --git a/release-scripts/release.sh b/release-scripts/release.sh new file mode 100755 index 0000000..4dc2ba8 --- /dev/null +++ b/release-scripts/release.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -e + +# version and keys are supplied as arguments +version="$1" +rc=`echo $version | awk -F - '{print $2}'` +if [[ -z $version ]]; then + echo "Usage: $0 VERSION" + exit 1 +fi + +# setup build-time vars +ldflags="-s -w -X 'github.com/NebulousLabs/skynet-cli/build.GitRevision=`git rev-parse --short HEAD`' -X 'github.com/NebulousLabs/skynet-cli/build.BuildTime=`git show -s --format=%ci HEAD`' -X 'github.com/NebulousLabs/skynet-cli/build.ReleaseTag=${rc}'" + +function build { + os=$1 + arch=$2 + + echo Building ${os}... + # create workspace + subFolder=skynet-cli-$version-$os-$arch + folder=release/$subFolder + rm -rf $folder + mkdir -p $folder + # compile and hash binaries + bin=skynet + if [ "$os" == "windows" ]; then + bin=skynet.exe + fi + GOOS=${os} GOARCH=${arch} go build -a -tags 'netgo' -trimpath -ldflags="$ldflags" -o $folder/$bin ./cmd/skynet + ( + cd release/ + sha256sum $subFolder/$bin >> skynet-cli-$version-SHA256SUMS.txt + ) + + cp -r doc LICENSE README.md $folder +} + +# Build amd64 binaries. +for os in darwin linux windows; do + build "$os" "amd64" +done + +# Build Raspberry Pi binaries. +build "linux" "arm64"