add apt gestion
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
parent
274ea454dd
commit
c4776d81dd
22 changed files with 1465 additions and 163 deletions
148
internal/clone/apt/metadata_gen.go
Normal file
148
internal/clone/apt/metadata_gen.go
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package apt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// InitEmptyRepo creates the directory structure for an APT mirror and writes an
|
||||
// empty Release file so APT clients don't get a 404 before the first clone.
|
||||
func InitEmptyRepo(localDir string, cfg Config) error {
|
||||
if err := os.MkdirAll(filepath.Join(localDir, "pool"), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, component := range cfg.Components {
|
||||
for _, arch := range cfg.Architectures {
|
||||
dir := filepath.Join(localDir, "dists", cfg.Suite, component, "binary-"+arch)
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
releasePath := filepath.Join(localDir, "dists", cfg.Suite, "Release")
|
||||
if _, err := os.Stat(releasePath); os.IsNotExist(err) {
|
||||
releaseData := GenerateRelease(cfg.Suite, cfg.Suite, cfg.Components, cfg.Architectures, nil)
|
||||
if err := atomicWrite(releasePath, releaseData); err != nil {
|
||||
return fmt.Errorf("write empty Release: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegenerateMetadata rebuilds local Packages, Packages.gz, and Release files
|
||||
// for all configured component/arch pairs, keeping only packages present on disk.
|
||||
func RegenerateMetadata(localDir string, cfg Config) error {
|
||||
suite := cfg.Suite
|
||||
var releaseEntries []ReleaseEntry
|
||||
|
||||
for _, component := range cfg.Components {
|
||||
for _, arch := range cfg.Architectures {
|
||||
entries, err := regenerateComponentArch(localDir, suite, component, arch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("regenerate %s/%s: %w", component, arch, err)
|
||||
}
|
||||
releaseEntries = append(releaseEntries, entries...)
|
||||
}
|
||||
}
|
||||
|
||||
// Write dists/{suite}/Release.
|
||||
releaseData := GenerateRelease(suite, suite, cfg.Components, cfg.Architectures, releaseEntries)
|
||||
releasePath := filepath.Join(localDir, "dists", suite, "Release")
|
||||
if err := os.MkdirAll(filepath.Dir(releasePath), 0o755); err != nil {
|
||||
return fmt.Errorf("create dists dir: %w", err)
|
||||
}
|
||||
if err := atomicWrite(releasePath, releaseData); err != nil {
|
||||
return fmt.Errorf("write Release: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// regenerateComponentArch regenerates Packages and Packages.gz for one component/arch
|
||||
// and returns the two ReleaseEntry values (plain then gz) for the Release file.
|
||||
func regenerateComponentArch(localDir, suite, component, arch string) ([]ReleaseEntry, error) {
|
||||
binDir := filepath.Join(localDir, "dists", suite, component, "binary-"+arch)
|
||||
upstreamGzPath := filepath.Join(binDir, upstreamPackagesGz)
|
||||
|
||||
// 1. Read upstream Packages.gz.
|
||||
gzData, err := os.ReadFile(upstreamGzPath)
|
||||
if err != nil {
|
||||
// Not cloned yet — skip silently.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 2. Decompress.
|
||||
gz, err := gzip.NewReader(bytes.NewReader(gzData))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open gzip %s: %w", upstreamGzPath, err)
|
||||
}
|
||||
rawPackages, err := io.ReadAll(gz)
|
||||
gz.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decompress %s: %w", upstreamGzPath, err)
|
||||
}
|
||||
|
||||
// 3. Parse all stanzas.
|
||||
allPkgs, err := ParsePackages(rawPackages)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse upstream Packages: %w", err)
|
||||
}
|
||||
|
||||
// 4. Filter: keep only packages present on disk.
|
||||
checker := diskPresenceChecker(localDir)
|
||||
localPkgs := FilterPackages(allPkgs, checker)
|
||||
|
||||
// 5. Re-emit filtered stanzas.
|
||||
plainBytes := emitPackages(localPkgs)
|
||||
|
||||
// 6. Gzip the filtered Packages.
|
||||
var gzBuf bytes.Buffer
|
||||
gzw := gzip.NewWriter(&gzBuf)
|
||||
if _, err := gzw.Write(plainBytes); err != nil {
|
||||
return nil, fmt.Errorf("gzip write: %w", err)
|
||||
}
|
||||
if err := gzw.Close(); err != nil {
|
||||
return nil, fmt.Errorf("gzip close: %w", err)
|
||||
}
|
||||
gzBytes := gzBuf.Bytes()
|
||||
|
||||
// 7. Write Packages and Packages.gz.
|
||||
if err := os.MkdirAll(binDir, 0o755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plainPath := filepath.Join(binDir, "Packages")
|
||||
if err := atomicWrite(plainPath, plainBytes); err != nil {
|
||||
return nil, fmt.Errorf("write Packages: %w", err)
|
||||
}
|
||||
gzPath := filepath.Join(binDir, "Packages.gz")
|
||||
if err := atomicWrite(gzPath, gzBytes); err != nil {
|
||||
return nil, fmt.Errorf("write Packages.gz: %w", err)
|
||||
}
|
||||
|
||||
// 8. Compute SHA256 hashes.
|
||||
plainSum := sha256.Sum256(plainBytes)
|
||||
gzSum := sha256.Sum256(gzBytes)
|
||||
|
||||
// Release paths are relative to dists/{suite}/.
|
||||
relBase := component + "/binary-" + arch + "/"
|
||||
|
||||
return []ReleaseEntry{
|
||||
{
|
||||
Hash: hex.EncodeToString(plainSum[:]),
|
||||
Size: int64(len(plainBytes)),
|
||||
Path: relBase + "Packages",
|
||||
},
|
||||
{
|
||||
Hash: hex.EncodeToString(gzSum[:]),
|
||||
Size: int64(len(gzBytes)),
|
||||
Path: relBase + "Packages.gz",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue