github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/integrations/apt/release.go (about) 1 package apt 2 3 import ( 4 "crypto/md5" 5 "crypto/sha1" 6 "crypto/sha256" 7 "fmt" 8 "io/fs" 9 "os" 10 "path/filepath" 11 "sort" 12 "strings" 13 "time" 14 15 "golang.org/x/mod/semver" 16 17 "github.com/kubri/kubri/integrations/apt/deb" 18 "github.com/kubri/kubri/pkg/crypto/pgp" 19 ) 20 21 func release(key *pgp.PrivateKey, algos CompressionAlgo, p []*Package) (string, error) { 22 dir, err := os.MkdirTemp("", "") 23 if err != nil { 24 return "", err 25 } 26 27 stable := make([]*Package, 0, len(p)) 28 for _, pkg := range p { 29 if semver.Prerelease("v"+pkg.Version) == "" { 30 stable = append(stable, pkg) 31 } 32 } 33 if err = releaseSuite(key, algos, stable, "stable", dir); err != nil { 34 return "", err 35 } 36 37 // If not all packages are stable publish a separate `edge` dist. 38 if len(p) > len(stable) { 39 if err = releaseSuite(key, algos, p, "edge", dir); err != nil { 40 return "", err 41 } 42 } 43 44 if key != nil { 45 b, err := pgp.MarshalPublicKey(pgp.Public(key)) 46 if err != nil { 47 return "", err 48 } 49 if err = os.WriteFile(filepath.Join(dir, "key.asc"), b, 0o600); err != nil { 50 return "", err 51 } 52 } 53 54 return dir, nil 55 } 56 57 func releaseSuite(key *pgp.PrivateKey, algos CompressionAlgo, p []*Package, suite, root string) error { 58 dir := filepath.Join(root, "dists", suite) 59 60 r := Releases{ 61 Suite: suite, 62 Codename: suite, 63 Components: "main", 64 } 65 66 byArch := map[string][]*Package{} 67 for _, pkg := range p { 68 byArch[pkg.Architecture] = append(byArch[pkg.Architecture], pkg) 69 } 70 71 { 72 var as []string 73 for a, pkgs := range byArch { 74 if err := releaseArch(algos, pkgs, suite, a, dir); err != nil { 75 return err 76 } 77 as = append(as, a) 78 } 79 sort.Strings(as) 80 r.Architectures = strings.Join(as, " ") 81 } 82 83 dirfs := os.DirFS(dir) 84 err := fs.WalkDir(dirfs, ".", func(path string, d fs.DirEntry, err error) error { 85 if err != nil || d.IsDir() { 86 return err 87 } 88 89 b, err := fs.ReadFile(dirfs, path) 90 if err != nil { 91 return err 92 } 93 94 r.MD5Sum += fmt.Sprintf("\n%x %d %s", md5.Sum(b), len(b), path) 95 r.SHA1 += fmt.Sprintf("\n%x %d %s", sha1.Sum(b), len(b), path) 96 r.SHA256 += fmt.Sprintf("\n%x %d %s", sha256.Sum256(b), len(b), path) 97 98 return nil 99 }) 100 if err != nil { 101 return err 102 } 103 104 return writeRelease(dir, r, key) 105 } 106 107 func releaseArch(algos CompressionAlgo, p []*Package, suite, arch, root string) error { 108 r := Release{ 109 Archive: suite, 110 Suite: suite, 111 Component: "main", 112 Architecture: arch, 113 } 114 115 dir := filepath.Join(root, "main", "binary-"+arch) 116 if err := os.MkdirAll(dir, os.ModePerm); err != nil { 117 return err 118 } 119 if err := writeFile(filepath.Join(dir, "Release"), r); err != nil { 120 return err 121 } 122 return writePackages(filepath.Join(dir, "Packages"), p, algos) 123 } 124 125 func writeFile(path string, v any) error { 126 f, err := os.Create(path) 127 if err != nil { 128 return err 129 } 130 w, err := compress(filepath.Ext(path))(f) 131 if err != nil { 132 return err 133 } 134 if err = deb.NewEncoder(w).Encode(v); err != nil { 135 return err 136 } 137 if err = w.Close(); err != nil { 138 return err 139 } 140 return f.Close() 141 } 142 143 func writePackages(path string, v []*Package, algos CompressionAlgo) error { 144 b, err := deb.Marshal(v) 145 if err != nil { 146 return err 147 } 148 149 for _, ext := range compressionExtensions(algos) { 150 f, err := os.Create(path + ext) 151 if err != nil { 152 return err 153 } 154 w, err := compress(ext)(f) 155 if err != nil { 156 return err 157 } 158 if _, err = w.Write(b); err != nil { 159 return err 160 } 161 if err = w.Close(); err != nil { 162 return err 163 } 164 if err = f.Close(); err != nil { 165 return err 166 } 167 } 168 169 return nil 170 } 171 172 var timeNow = time.Now //nolint:gochecknoglobals 173 174 func writeRelease(dir string, r Releases, key *pgp.PrivateKey) error { 175 r.Date = timeNow().UTC() 176 177 b, err := deb.Marshal(r) 178 if err != nil { 179 return err 180 } 181 182 if err = os.WriteFile(filepath.Join(dir, "Release"), b, 0o600); err != nil { 183 return err 184 } 185 186 if key != nil { 187 sig, err := pgp.Sign(key, b) 188 if err != nil { 189 return err 190 } 191 if err = os.WriteFile(filepath.Join(dir, "Release.gpg"), sig, 0o600); err != nil { 192 return err 193 } 194 b, err = pgp.SignText(key, b) 195 if err != nil { 196 return err 197 } 198 } 199 200 return os.WriteFile(filepath.Join(dir, "InRelease"), b, 0o600) 201 }