Closed thomasf closed 7 years ago
My version has deviated a bit now for the sake of simplicity so it's not directly transferable anymore:..
package main
import (
"bytes"
"compress/gzip"
"crypto/sha256"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/kr/binarydist"
)
var genDir string
type Version struct {
Version string
Path string
Sha256 []byte
}
func generateSha256(path string) []byte {
h := sha256.New()
b, err := ioutil.ReadFile(path)
if err != nil {
fmt.Println(err)
}
h.Write(b)
sum := h.Sum(nil)
return sum
}
type gzReader struct {
z, r io.ReadCloser
}
func (g *gzReader) Read(p []byte) (int, error) {
return g.z.Read(p)
}
func (g *gzReader) Close() error {
g.z.Close()
return g.r.Close()
}
func newGzReader(r io.ReadCloser) io.ReadCloser {
var err error
g := new(gzReader)
g.r = r
g.z, err = gzip.NewReader(r)
if err != nil {
panic(err)
}
return g
}
func (v *Version) createUpdate(platform string) error {
path := v.Path
v.Sha256 = generateSha256(path)
version := v.Version
os.MkdirAll(filepath.Join(genDir, version), 0755)
var buf bytes.Buffer
w := gzip.NewWriter(&buf)
f, err := ioutil.ReadFile(path)
if err != nil {
return err
}
w.Write(f)
w.Close() // You must close this first to flush the bytes to the buffer.
err = ioutil.WriteFile(filepath.Join(genDir, version, platform+".gz"), buf.Bytes(), 0755)
files, err := ioutil.ReadDir(genDir)
if err != nil {
return err
}
for _, file := range files {
if file.IsDir() == false {
continue
}
if file.Name() == version {
continue
}
os.Mkdir(filepath.Join(genDir, file.Name(), version), 0755)
fName := filepath.Join(genDir, file.Name(), platform+".gz")
old, err := os.Open(fName)
if err != nil {
// Don't have an old release for this os/arch, continue on
continue
}
fName = filepath.Join(genDir, version, platform+".gz")
newF, err := os.Open(fName)
if err != nil {
fmt.Fprintf(os.Stderr, "Can't open %s: error: %s\n", fName, err)
os.Exit(1)
}
ar := newGzReader(old)
defer ar.Close()
br := newGzReader(newF)
defer br.Close()
patch := new(bytes.Buffer)
if err := binarydist.Diff(ar, br, patch); err != nil {
panic(err)
}
ioutil.WriteFile(filepath.Join(genDir, file.Name(), version, platform), patch.Bytes(), 0755)
}
b, err := json.MarshalIndent(v, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(genDir, platform+".json"), b, 0755)
if err != nil {
return err
}
return nil
}
func main() {
outputDirFlag := flag.String("output", "public", "Output directory for writing updates")
platformFlag := flag.String("platform", "", "Target platform in the form os-arch.")
versionFlag := flag.String("version", "", "Version number to publish")
binFlag := flag.String("binary", "", "Path to the binary for the published version.")
flag.Parse()
platform := *platformFlag
appPath := *binFlag
version := *versionFlag
genDir = *outputDirFlag
if platform == "" || appPath == "" || version == "" {
flag.Usage()
fmt.Println("ERROR: Platform, Binary and Version are required to be set.")
os.Exit(1)
}
os.MkdirAll(genDir, 0755)
v := Version{
Version: version,
Path: appPath,
}
err := v.createUpdate(platform)
if err != nil {
panic(err)
}
}
Otherwise a client will will try to fetch files that are not yet written if the output path is directly served by the an http server.