-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
555f
committed
Mar 5, 2024
0 parents
commit 318b733
Showing
16 changed files
with
1,086 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# This workflow will build a golang project | ||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go | ||
|
||
name: Go | ||
|
||
on: | ||
push: | ||
branches: [ "master" ] | ||
pull_request: | ||
branches: [ "master" ] | ||
|
||
jobs: | ||
|
||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v3 | ||
with: | ||
go-version: 1.19 | ||
|
||
- name: Build | ||
run: go build -v ./... | ||
|
||
- name: Test | ||
run: go test -v ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Use glob syntax. | ||
syntax: glob | ||
|
||
*.DS_Store | ||
*.swp | ||
*.swo | ||
*.pyc | ||
*.php~ | ||
*.orig | ||
*~ | ||
*.db | ||
*.log | ||
public/* | ||
go-selfupdate/go-selfupdate | ||
example/example-server | ||
example/hello-updater | ||
example/public/* | ||
example/deployment/* | ||
example/go-selfupdate | ||
*cktime |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2014 Mark Sanborn | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# selfupdate | ||
|
||
Enable your Golang applications to self update. | ||
|
||
## Features | ||
|
||
* Tested on Mac, Linux, Arm, and Windows | ||
* Falls back to full binary update if diff fails to match SHA | ||
|
||
## QuickStart | ||
|
||
### Install library and update/patch creation utility | ||
|
||
`go install github.com/555f/selfupdate/cmd/selfupdatectl@latest` | ||
|
||
### Enable your App to Self Update | ||
|
||
`go get -u github.com/555f/selfupdate/...` | ||
|
||
var updater = &selfupdate.Updater{ | ||
CurrentVersion: version, // the current version of your app used to determine if an update is necessary | ||
// these endpoints can be the same if everything is hosted in the same place | ||
ApiURL: "http://updates.yourdomain.com/", // endpoint to get update manifest | ||
BinURL: "http://updates.yourdomain.com/", // endpoint to get full binaries | ||
Dir: "update/", // directory relative to your app to store temporary state files related to selfupdate | ||
CmdName: "myapp", // your app's name (must correspond to app name hosting the updates) | ||
// app name allows you to serve updates for multiple apps on the same server/endpoint | ||
} | ||
|
||
// go look for an update when your app starts up | ||
go updater.Run() | ||
// your app continues to run... | ||
|
||
### Push Out and Update | ||
|
||
selfupdatectl path-to-your-app the-version | ||
selfupdatectl myapp 1.2 | ||
|
||
By default this will create a folder in your project called *public*. You can then rsync or transfer this to your webserver or S3. To change the output directory use `-o` flag. | ||
|
||
If you are cross compiling you can specify a directory: | ||
|
||
selfupdatectl /tmp/mybinares/ 1.2 | ||
|
||
The directory should contain files with the name, $GOOS-$ARCH. Example: | ||
|
||
windows-386 | ||
darwin-amd64 | ||
linux-arm | ||
|
||
## Update Protocol | ||
|
||
Updates are fetched from an HTTP(s) server. AWS S3 or static hosting can be used. A JSON manifest file is pulled first which points to the wanted version (usually latest) and matching metadata. SHA256 hash is currently the only metadata but new fields may be added here like signatures. `selfupdate` isn't aware of any versioning schemes. It doesn't know major/minor versions. It just knows the target version by name and can apply diffs based on current version and version you wish to move to. For example 1.0 to 5.0 or 1.0 to 1.1. You don't even need to use point numbers. You can use hashes, dates, etc for versions. | ||
|
||
GET yourserver.com/appname/linux-amd64.json | ||
|
||
200 ok | ||
{ | ||
"Version": "2", | ||
"Sha256": "..." // base64 | ||
} | ||
|
||
GET fullbins.yourserver.com/appname/1.0/linux-amd64.gz | ||
|
||
200 ok | ||
[gzipped executable data] | ||
|
||
The only required files are `<appname>/<os>-<arch>.json` and `<appname>/<latest>/<os>-<arch>.gz` everything else is optional. If you wanted to you could skip using selfupdate CLI tool and generate these two files manually or with another tool. | ||
|
||
## Config | ||
|
||
Updater Config options: | ||
|
||
type Updater struct { | ||
CurrentVersion string // Currently running version. `dev` is a special version here and will cause the updater to never update. | ||
ApiURL string // Base URL for API requests (JSON files). | ||
CmdName string // Command name is appended to the ApiURL like http://apiurl/CmdName/. This represents one binary. | ||
BinURL string // Base URL for full binary downloads. | ||
Dir string // Directory to store selfupdate state. | ||
ForceCheck bool // Check for update regardless of cktime timestamp | ||
CheckTime int // Time in hours before next check | ||
RandomizeTime int // Time in hours to randomize with CheckTime | ||
Requester Requester // Optional parameter to override existing HTTP request handler | ||
Info struct { | ||
Version string | ||
Sha256 []byte | ||
} | ||
OnSuccessfulUpdate func() // Optional function to run after an update has successfully taken place | ||
} | ||
|
||
### Restart on update | ||
|
||
It is common for an app to want to restart to apply the update. `selfupdate` gives you a hook to do that but leaves it up to you on how and when to restart as it differs for all apps. If you have a service restart application like Docker or systemd you can simply exit and let the upstream app start/restart your application. Just set the `OnSuccessfulUpdate` hook: | ||
|
||
u.OnSuccessfulUpdate = func() { os.Exit(0) } | ||
|
||
Or maybe you have a fancy graceful restart library/func: | ||
|
||
u.OnSuccessfulUpdate = func() { gracefullyRestartMyApp() } | ||
|
||
## State | ||
|
||
selfupdate will keep a Go time.Time formatted timestamp in a file named `cktime` in folder specified by `Updater.Dir`. This can be useful for debugging to see when the next update can be applied or allow other applications to manipulate it. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"compress/gzip" | ||
"crypto/sha256" | ||
"encoding/json" | ||
"flag" | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
"sync" | ||
) | ||
|
||
var version, genDir string | ||
|
||
type current struct { | ||
Version string | ||
Sha256 []byte | ||
} | ||
|
||
func generateSha256(path string) []byte { | ||
h := sha256.New() | ||
b, err := os.ReadFile(path) | ||
if err != nil { | ||
fmt.Println(err) | ||
} | ||
h.Write(b) | ||
sum := h.Sum(nil) | ||
return sum | ||
} | ||
|
||
func createUpdate(path string, platform string) { | ||
c := current{Version: version, Sha256: generateSha256(path)} | ||
|
||
b, err := json.MarshalIndent(c, "", " ") | ||
if err != nil { | ||
fmt.Println("error:", err) | ||
} | ||
err = os.WriteFile(filepath.Join(genDir, platform+".json"), b, 0755) | ||
if err != nil { | ||
panic(err) | ||
} | ||
err = os.MkdirAll(filepath.Join(genDir, version), 0755) | ||
if err != nil { | ||
panic(err) | ||
} | ||
var buf bytes.Buffer | ||
w := gzip.NewWriter(&buf) | ||
defer w.Close() // You must close this first to flush the bytes to the buffer. | ||
|
||
f, err := os.ReadFile(path) | ||
if err != nil { | ||
panic(err) | ||
} | ||
_, err = w.Write(f) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
err = os.WriteFile(filepath.Join(genDir, version, platform+".gz"), buf.Bytes(), 0755) | ||
if err != nil { | ||
fmt.Println(err) | ||
} | ||
} | ||
|
||
func printUsage() { | ||
fmt.Println("") | ||
fmt.Println("Positional arguments:") | ||
fmt.Println("\tSingle platform: go-selfupdate myapp 1.2") | ||
fmt.Println("\tCross platform: go-selfupdate /tmp/mybinares/ 1.2") | ||
} | ||
|
||
func main() { | ||
outputDirFlag := flag.String("o", "public", "Output directory for writing updates") | ||
|
||
var defaultPlatform string | ||
goos := os.Getenv("GOOS") | ||
goarch := os.Getenv("GOARCH") | ||
if goos != "" && goarch != "" { | ||
defaultPlatform = goos + "-" + goarch | ||
} else { | ||
defaultPlatform = runtime.GOOS + "-" + runtime.GOARCH | ||
} | ||
platformFlag := flag.String("platform", defaultPlatform, | ||
"Target platform in the form OS-ARCH. Defaults to running os/arch or the combination of the environment variables GOOS and GOARCH if both are set.") | ||
|
||
flag.Parse() | ||
if flag.NArg() < 2 { | ||
flag.Usage() | ||
printUsage() | ||
os.Exit(0) | ||
} | ||
|
||
platform := *platformFlag | ||
appPath := flag.Arg(0) | ||
version = flag.Arg(1) | ||
genDir = *outputDirFlag | ||
|
||
err := os.MkdirAll(genDir, 0755) | ||
if err != nil { | ||
panic(err) | ||
} | ||
// If dir is given create update for each file | ||
fi, err := os.Stat(appPath) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
if fi.IsDir() { | ||
files, err := os.ReadDir(appPath) | ||
if err == nil { | ||
var wg sync.WaitGroup | ||
wg.Add(len(files)) | ||
|
||
for _, file := range files { | ||
go func(file fs.DirEntry) { | ||
defer wg.Done() | ||
createUpdate(filepath.Join(appPath, file.Name()), file.Name()) | ||
}(file) | ||
} | ||
wg.Wait() | ||
os.Exit(0) | ||
} | ||
} | ||
|
||
createUpdate(appPath, platform) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package main | ||
|
||
import "testing" | ||
|
||
func TestUpdater(t *testing.T) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
#!/bin/bash | ||
|
||
echo "This example will compile the hello-updater application a few times with" | ||
echo "different version strings and demonstrate go-selfupdate's functionality." | ||
echo "If the version is 'dev', no update checking will be performed." | ||
echo | ||
|
||
# build latest/dev local version of go-selfupdate | ||
SELFUPDATE_PATH=../cmd/go-selfupdate/main.go | ||
if [ -f "$SELFUPDATE_PATH" ]; then | ||
go build -o go-selfupdate ../cmd/go-selfupdate | ||
fi | ||
|
||
rm -rf deployment/update deployment/hello* public/hello-updater | ||
|
||
echo "Building example-server" | ||
go build -o example-server src/example-server/main.go | ||
|
||
echo "Running example server" | ||
killall -q example-server | ||
./example-server & | ||
|
||
read -n 1 -p "Press any key to start." ignored; echo | ||
|
||
echo "Building dev version of hello-updater"; echo | ||
go build -ldflags="-X main.version=dev" -o hello-updater src/hello-updater/main.go | ||
|
||
echo "Creating deployment folder and copying hello-updater to it"; echo | ||
mkdir -p deployment/ && cp hello-updater deployment/ | ||
|
||
|
||
echo "Running deployment/hello-updater" | ||
deployment/hello-updater | ||
read -n 1 -p "Press any key to continue." ignored; echo | ||
echo; echo "=========="; echo | ||
|
||
for (( minor=0; minor<=2; minor++ )); do | ||
echo "Building hello-updater with version set to 1.$minor" | ||
go build -ldflags="-X main.version=1.$minor" -o hello-updater src/hello-updater/main.go | ||
|
||
echo "Running ./go-selfupdate to make update available via example-server"; echo | ||
./go-selfupdate -o public/hello-updater/ hello-updater 1.$minor | ||
|
||
if (( $minor == 0 )); then | ||
echo "Copying version 1.0 to deployment so it can self-update"; echo | ||
cp hello-updater deployment/ | ||
cp hello-updater deployment/hello-updater-1.0 | ||
fi | ||
|
||
echo "Running deployment/hello-updater" | ||
deployment/hello-updater | ||
read -n 1 -p "Press any key to continue." ignored; echo | ||
echo; echo "=========="; echo | ||
done | ||
|
||
echo "Running deployment/hello-updater-1.0 backup copy" | ||
deployment/hello-updater-1.0 | ||
read -n 1 -p "Press any key to continue." ignored; echo | ||
echo; echo "=========="; echo | ||
|
||
echo "Building unknown version of hello-updater"; echo | ||
go build -ldflags="-X main.version=unknown" -o hello-updater src/hello-updater/main.go | ||
echo "Copying unknown version to deployment so it can self-update"; echo | ||
cp hello-updater deployment/ | ||
|
||
echo "Running deployment/hello-updater" | ||
deployment/hello-updater | ||
sleep 5 | ||
echo; echo "Re-running deployment/hello-updater" | ||
deployment/hello-updater | ||
sleep 5 | ||
echo; echo | ||
|
||
echo "Shutting down example-server" | ||
killall example-server |
Oops, something went wrong.