feat: resolve alias_of in cache pipeline

Packages with alias_of in releases.conf (e.g. dashd → dashcore,
golang → go) now get symlinked cache files so they resolve to the
same JSON as their target. 13 aliases total.

Added AliasOf as a proper field in installerconf.Conf, LinkAlias
method to fsstore, and alias handling in webicached's Run loop.
This commit is contained in:
AJ ONeal
2026-03-10 23:28:36 -06:00
parent f36e734539
commit b8c67491fe
3 changed files with 38 additions and 2 deletions

View File

@@ -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))
}

View File

@@ -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] {

View File

@@ -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)