github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/release/update/update.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package update 5 6 import ( 7 "crypto/sha256" 8 "encoding/hex" 9 "encoding/json" 10 "fmt" 11 "io" 12 "net/url" 13 "os" 14 "path" 15 "strings" 16 17 releaseVersion "github.com/keybase/client/go/release/version" 18 ) 19 20 // EncodeJSON returns JSON (as bytes) for an update 21 func EncodeJSON(version string, name string, descriptionPath string, props []string, src string, uri fmt.Stringer, signaturePath string) ([]byte, error) { 22 upd := Update{ 23 Version: version, 24 Name: name, 25 } 26 27 // Get published at from version string 28 _, _, date, _, err := releaseVersion.Parse(version) 29 if err == nil { 30 t := ToTime(date) 31 upd.PublishedAt = &t 32 } 33 34 if src != "" && uri != nil { 35 fileName := path.Base(src) 36 37 // Or if we can't parse use the src file modification time 38 if upd.PublishedAt == nil { 39 var srcInfo os.FileInfo 40 srcInfo, err = os.Stat(src) 41 if err != nil { 42 return nil, err 43 } 44 t := ToTime(srcInfo.ModTime()) 45 upd.PublishedAt = &t 46 } 47 48 urlString := fmt.Sprintf("%s/%s", uri.String(), url.QueryEscape(fileName)) 49 asset := Asset{ 50 Name: fileName, 51 URL: urlString, 52 } 53 54 digest, err := digest(src) 55 if err != nil { 56 return nil, fmt.Errorf("Error creating digest: %s", err) 57 } 58 asset.Digest = digest 59 60 if signaturePath != "" { 61 sig, err := readFile(signaturePath) 62 if err != nil { 63 return nil, err 64 } 65 asset.Signature = sig 66 } 67 68 if descriptionPath != "" { 69 desc, err := readFile(descriptionPath) 70 if err != nil { 71 return nil, err 72 } 73 upd.Description = desc 74 } 75 76 upd.Asset = &asset 77 } 78 79 if props != nil { 80 uprops := []Property{} 81 for _, p := range props { 82 splitp := strings.SplitN(p, ":", 2) 83 if len(splitp) == 2 { 84 uprops = append(uprops, Property{Name: splitp[0], Value: splitp[1]}) 85 } 86 } 87 if len(uprops) > 0 { 88 upd.Props = uprops 89 } 90 } 91 92 return json.MarshalIndent(upd, "", " ") 93 } 94 95 // DecodeJSON returns an update object from JSON (bytes) 96 func DecodeJSON(r io.Reader) (*Update, error) { 97 var obj Update 98 if err := json.NewDecoder(r).Decode(&obj); err != nil { 99 return nil, err 100 } 101 return &obj, nil 102 } 103 104 func readFile(path string) (string, error) { 105 sigFile, err := os.Open(path) 106 if err != nil { 107 return "", err 108 } 109 defer func() { _ = sigFile.Close() }() 110 data, err := io.ReadAll(sigFile) 111 if err != nil { 112 return "", err 113 } 114 return string(data), nil 115 } 116 117 func digest(p string) (digest string, err error) { 118 hasher := sha256.New() 119 f, err := os.Open(p) 120 if err != nil { 121 return 122 } 123 defer func() { _ = f.Close() }() 124 if _, ioerr := io.Copy(hasher, f); ioerr != nil { 125 err = ioerr 126 return 127 } 128 digest = hex.EncodeToString(hasher.Sum(nil)) 129 return 130 }