diff --git a/cmd/webicached/main.go b/cmd/webicached/main.go index a13897d..35d7a21 100644 --- a/cmd/webicached/main.go +++ b/cmd/webicached/main.go @@ -132,8 +132,10 @@ func (wc *WebiCache) Run(filterPkgs []string) { log.Printf("refreshing %d packages", len(packages)) runStart := time.Now() + var aliases []pkgConf for _, pkg := range packages { - if alias := pkg.conf.Extra["alias_of"]; alias != "" { + if pkg.conf.AliasOf != "" { + aliases = append(aliases, pkg) continue } @@ -142,6 +144,13 @@ func (wc *WebiCache) Run(filterPkgs []string) { } } + // Create symlinks for aliases after all targets are written. + for _, pkg := range aliases { + if err := wc.Store.LinkAlias(pkg.name, pkg.conf.AliasOf); err != nil { + log.Printf(" ERROR alias %s → %s: %v", pkg.name, pkg.conf.AliasOf, err) + } + } + log.Printf("refreshed %d packages in %s", len(packages), time.Since(runStart)) } diff --git a/internal/installerconf/installerconf.go b/internal/installerconf/installerconf.go index 226713d..cf362c4 100644 --- a/internal/installerconf/installerconf.go +++ b/internal/installerconf/installerconf.go @@ -98,6 +98,11 @@ type Conf struct { // variant detection logic lives in Go code per-package. Variants []string + // AliasOf names another package that this one mirrors. + // When set, the package has no releases of its own — it shares + // the cache output of the named target (e.g. dashd → dashcore). + AliasOf string + // Extra holds any unrecognized keys for forward compatibility. Extra map[string]string } @@ -153,6 +158,7 @@ func Read(path string) (*Conf, error) { } c.AssetFilter = raw["asset_filter"] + c.AliasOf = raw["alias_of"] if v := raw["variants"]; v != "" { c.Variants = strings.Fields(v) @@ -164,7 +170,7 @@ func Read(path string) (*Conf, error) { "base_url": true, "url": true, "tag_prefix": true, "version_prefix": true, "version_prefixes": true, "exclude": true, "asset_exclude": true, "asset_filter": true, - "variants": true, + "variants": true, "alias_of": true, } for k, v := range raw { if !known[k] { diff --git a/internal/storage/fsstore/fsstore.go b/internal/storage/fsstore/fsstore.go index f0b08e9..0d0aa35 100644 --- a/internal/storage/fsstore/fsstore.go +++ b/internal/storage/fsstore/fsstore.go @@ -143,6 +143,27 @@ func atomicWrite(path string, data []byte) error { return nil } +// LinkAlias creates symlinks so that alias resolves to the same cache +// files as target: alias.json → target.json, alias.updated.txt → target.updated.txt. +func (s *Store) LinkAlias(alias, target string) error { + dir := filepath.Join(s.root, monthDir(time.Now())) + if err := os.MkdirAll(dir, 0o755); err != nil { + return fmt.Errorf("fsstore: mkdir: %w", err) + } + + for _, ext := range []string{".json", ".updated.txt"} { + link := filepath.Join(dir, alias+ext) + dest := target + ext // relative symlink within same dir + + // Remove existing link/file so we can recreate it. + os.Remove(link) + if err := os.Symlink(dest, link); err != nil { + return fmt.Errorf("fsstore: symlink %s → %s: %w", alias+ext, dest, err) + } + } + return nil +} + // parseTimestamp parses the "seconds.millis" format from .updated.txt files. func parseTimestamp(s string) time.Time { f, err := strconv.ParseFloat(s, 64)