github.com/abemedia/appcast@v0.4.0/integrations/apt/build.go (about) 1 package apt 2 3 import ( 4 "context" 5 "crypto/md5" 6 "crypto/sha1" 7 "crypto/sha256" 8 "fmt" 9 "io/fs" 10 "os" 11 "path" 12 "strings" 13 "unsafe" 14 15 "github.com/abemedia/appcast/integrations/apt/deb" 16 "github.com/abemedia/appcast/pkg/crypto/pgp" 17 "github.com/abemedia/appcast/source" 18 "github.com/abemedia/appcast/target" 19 ) 20 21 type Config struct { 22 Source *source.Source 23 Version string 24 Prerelease bool 25 Target target.Target 26 PGPKey *pgp.PrivateKey 27 Compress CompressionAlgo 28 } 29 30 func Build(ctx context.Context, c *Config) error { 31 pkgs := read(ctx, c) 32 33 version := c.Version 34 if v := getVersionConstraint(pkgs); v != "" { 35 version += "," + v 36 } 37 38 releases, err := c.Source.ListReleases(ctx, &source.ListOptions{ 39 Version: version, 40 Prerelease: c.Prerelease, 41 }) 42 if err == source.ErrNoReleaseFound { 43 return nil 44 } 45 if err != nil { 46 return err 47 } 48 49 p, err := getPackages(ctx, c, releases) 50 if err != nil || p == nil { 51 return err 52 } 53 pkgs = append(p, pkgs...) 54 55 dir, err := release(c.PGPKey, c.Compress, pkgs) 56 if err != nil { 57 return err 58 } 59 defer os.RemoveAll(dir) 60 61 files := os.DirFS(dir) 62 return fs.WalkDir(files, ".", func(path string, d fs.DirEntry, err error) error { 63 if err != nil || d.IsDir() { 64 return err 65 } 66 b, err := fs.ReadFile(files, path) 67 if err != nil { 68 return err 69 } 70 w, err := c.Target.NewWriter(ctx, path) 71 if err != nil { 72 return err 73 } 74 if _, err = w.Write(b); err != nil { 75 return err 76 } 77 return w.Close() 78 }) 79 } 80 81 func read(ctx context.Context, c *Config) []*Package { 82 dist := "edge" 83 rd, err := c.Target.NewReader(ctx, "dists/edge/Release") 84 if err != nil { 85 dist = "stable" 86 rd, err = c.Target.NewReader(ctx, "dists/stable/Release") 87 if err != nil { 88 return nil 89 } 90 } 91 defer rd.Close() 92 93 r := &Releases{} 94 if err = deb.NewDecoder(rd).Decode(r); err != nil { 95 return nil 96 } 97 98 var pkgs []*Package 99 for _, arch := range strings.Split(r.Architectures, " ") { 100 path := fmt.Sprintf("dists/%s/main/binary-%s/Packages", dist, arch) 101 rd, err = c.Target.NewReader(ctx, path) 102 if err != nil { 103 return nil 104 } 105 defer rd.Close() 106 107 var p []*Package 108 if err = deb.NewDecoder(rd).Decode(&p); err != nil { 109 return nil 110 } 111 pkgs = append(pkgs, p...) 112 } 113 114 return pkgs 115 } 116 117 func getVersionConstraint(pkgs []*Package) string { 118 if len(pkgs) == 0 { 119 return "" 120 } 121 122 v := make([]byte, 0, len(pkgs)*len("!=0.0.0,")) 123 for _, p := range pkgs { 124 v = append(v, '!', '=') 125 v = append(v, strings.Replace(p.Version, "~", "-", 1)...) 126 v = append(v, ',') 127 } 128 129 return unsafe.String(unsafe.SliceData(v), len(v)-1) 130 } 131 132 func getPackages(ctx context.Context, c *Config, releases []*source.Release) ([]*Package, error) { 133 var pkgs []*Package 134 for _, release := range releases { 135 for _, asset := range release.Assets { 136 if path.Ext(asset.Name) != ".deb" { 137 continue 138 } 139 p, err := getPackage(ctx, c, release.Version, asset.Name) 140 if err != nil { 141 return nil, err 142 } 143 pkgs = append(pkgs, p) 144 } 145 } 146 return pkgs, nil 147 } 148 149 func getPackage(ctx context.Context, c *Config, version, name string) (*Package, error) { 150 b, err := c.Source.DownloadAsset(ctx, version, name) 151 if err != nil { 152 return nil, err 153 } 154 155 p, err := getControl(b) 156 if err != nil { 157 return nil, err 158 } 159 p.Size = len(b) 160 p.Filename = "pool/main/" + p.Package[0:1] + "/" + p.Package + "/" + 161 p.Package + "_" + p.Version + "_" + p.Architecture + ".deb" 162 p.MD5sum = md5.Sum(b) 163 p.SHA1 = sha1.Sum(b) 164 p.SHA256 = sha256.Sum256(b) 165 166 w, err := c.Target.NewWriter(ctx, p.Filename) 167 if err != nil { 168 return nil, err 169 } 170 if _, err = w.Write(b); err != nil { 171 return nil, err 172 } 173 if err = w.Close(); err != nil { 174 return nil, err 175 } 176 177 return p, nil 178 }