aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/rosa/state.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-05-19 00:03:42 +0900
committerOphestra <cat@gensokyo.uk>2026-05-19 00:44:24 +0900
commit8807cbc7305e424725b2be8eed7ecd9234f7772e (patch)
tree5998f055396505becf0f99fcba3c86514f00650b /internal/rosa/state.go
parent0e95573f187f64de1a46f27af67325e57037b9a9 (diff)
internal/rosa: create metadata alongside artifact
This enables deferring evaluation of azalea-based packages and fixes the longstanding quirk of version being disjoint from other metadata. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/rosa/state.go')
-rw-r--r--internal/rosa/state.go117
1 files changed, 67 insertions, 50 deletions
diff --git a/internal/rosa/state.go b/internal/rosa/state.go
index efb51d96..c2f0650b 100644
--- a/internal/rosa/state.go
+++ b/internal/rosa/state.go
@@ -70,11 +70,9 @@ type (
// P represents multiple [ArtifactH].
type P []ArtifactH
-// Artifact is stage-agnostic immutable data with a deterministic resulting
-// [pkg.Artifact]. It can be created natively or through evaluation.
-type Artifact struct {
- f func(t Toolchain) (a pkg.Artifact, version string)
-
+// Metadata is stage-agnostic immutable data around [Artifact] not directly
+// representable in the resulting [pkg.Artifact].
+type Metadata struct {
// Unique package name.
Name string `json:"name"`
// Short user-facing description.
@@ -85,6 +83,8 @@ type Artifact struct {
// Runtime dependencies.
Dependencies P `json:"dependencies"`
+ // Package version.
+ Version string `json:"version"`
// Project identifier on [Anitya].
//
// [Anitya]: https://release-monitoring.org/
@@ -98,7 +98,7 @@ type Artifact struct {
}
// GetLatest returns the latest version described by v.
-func (meta *Artifact) GetLatest(v *Versions) string {
+func (meta *Metadata) GetLatest(v *Versions) string {
if meta.latest != nil {
return meta.latest(v)
}
@@ -134,7 +134,7 @@ func (v *Versions) getStable() string {
}
// GetVersions returns versions fetched from Anitya.
-func (meta *Artifact) GetVersions(ctx context.Context) (*Versions, error) {
+func (meta *Metadata) GetVersions(ctx context.Context) (*Versions, error) {
if meta.ID == 0 {
return nil, UnpopulatedIDError{}
}
@@ -160,10 +160,13 @@ func (meta *Artifact) GetVersions(ctx context.Context) (*Versions, error) {
return &v, errors.Join(err, resp.Body.Close())
}
-// A cachedArtifact holds [pkg.Artifact] and its corresponding version string.
+// Artifact is a lazily initialised [pkg.Artifact] with associated [Metadata].
+type Artifact func(t Toolchain) (meta *Metadata, a pkg.Artifact)
+
+// A cachedArtifact caches satisfied [Artifact].
type cachedArtifact struct {
- a pkg.Artifact
- v string
+ meta *Metadata
+ a pkg.Artifact
}
const (
@@ -242,20 +245,20 @@ func (s *S) DropCaches(targetArch string, flags int) {
}
}
-// Get returns the address of the named [Artifact].
-func (s *S) Get(handle ArtifactH) (meta *Artifact) {
+// get returns the named [Artifact].
+func (s *S) get(handle ArtifactH) (f Artifact) {
s.wantsArch()
v, ok := s.artifacts.Load(handle)
if ok {
- meta = v.(*Artifact)
+ f = v.(Artifact)
}
return
}
-// MustGet is like Get, but panics if the named [Artifact] is not registered.
-func (s *S) MustGet(handle ArtifactH) (meta *Artifact) {
- meta = s.Get(handle)
- if meta == nil {
+// mustGet is like get, but panics if the named [Artifact] is not registered.
+func (s *S) mustGet(handle ArtifactH) (f Artifact) {
+ f = s.get(handle)
+ if f == nil {
panic(HandleError(handle))
}
return
@@ -282,8 +285,8 @@ func (e LoadError) Error() string {
return "cannot load " + strconv.Quote(e.Handle.String()) + ": " + e.Err.Error()
}
-// Load returns the resulting [pkg.Artifact] of [ArtifactH].
-func (t Toolchain) Load(handle ArtifactH) (pkg.Artifact, string) {
+// Load satisfies an [Artifact] referred to by an [ArtifactH].
+func (t Toolchain) Load(handle ArtifactH) (*Metadata, pkg.Artifact) {
defer func() {
r := recover()
if r == nil {
@@ -305,36 +308,36 @@ func (t Toolchain) Load(handle ArtifactH) (pkg.Artifact, string) {
e, ok := t.c[t.stage].Load(handle)
if ok {
r := e.(cachedArtifact)
- return r.a, r.v
+ return r.meta, r.a
}
- meta := t.Get(handle)
- if meta == nil {
- return nil, ""
+ f := t.get(handle)
+ if f == nil {
+ return nil, nil
}
var r cachedArtifact
- r.a, r.v = meta.f(t)
+ r.meta, r.a = f(t)
t.c[t.stage].Store(handle, r)
- return r.a, r.v
+ return r.meta, r.a
}
// MustLoad is like Load, but panics if the named [Artifact] is not registered.
-func (t Toolchain) MustLoad(handle ArtifactH) (pkg.Artifact, string) {
- a, version := t.Load(handle)
- if a == nil {
+func (t Toolchain) MustLoad(handle ArtifactH) (*Metadata, pkg.Artifact) {
+ meta, a := t.Load(handle)
+ if meta == nil {
panic(HandleError(handle))
}
- return a, version
+ return meta, a
}
// Register arranges for a new [Artifact] to be cured under s. It returns false
// if another [Artifact] is already registered under the same name.
-func (s *S) Register(meta *Artifact) bool {
- if meta.Name == "" {
+func (s *S) Register(name string, f Artifact) bool {
+ if name == "" {
return false
}
- p := ArtifactH(unique.Make(meta.Name))
- _, ok := s.artifacts.LoadOrStore(p, meta)
+ p := ArtifactH(unique.Make(name))
+ _, ok := s.artifacts.LoadOrStore(p, f)
if !ok {
s.artifactCount.Add(1)
}
@@ -346,16 +349,34 @@ func (s *S) Register(meta *Artifact) bool {
type RegisterError ArtifactH
func (e RegisterError) Error() string {
+ if ArtifactH(e).String() == "" {
+ return "attempting to register invalid name"
+ }
return "attempting to register " + strconv.Quote(ArtifactH(e).String()) + " twice"
}
// MustRegister is like Register, but panics if registration fails.
-func (s *S) MustRegister(meta *Artifact) {
- if !s.Register(meta) {
- panic(RegisterError(H(meta.Name)))
+func (s *S) MustRegister(name string, f Artifact) {
+ if !s.Register(name, f) {
+ panic(RegisterError(H(name)))
}
}
+// mustRegister registers an [Artifact] with the old function signature.
+//
+// Deprecated: Artifacts should be migrated to Register.
+func (s *S) mustRegister(
+ f func(t Toolchain) (pkg.Artifact, string),
+ meta *Metadata,
+) {
+ s.MustRegister(meta.Name, func(t Toolchain) (*Metadata, pkg.Artifact) {
+ v := *meta
+ a, version := f(t)
+ v.Version = version
+ return &v, a
+ })
+}
+
// Count returns the number of [Artifact] registered to s.
func (s *S) Count() int {
return int(s.artifactCount.Load())
@@ -567,7 +588,7 @@ func (ctx *evalContext) f(
return unique.Make(azalea.Ident(name))
}
- meta := Artifact{Name: string(name)}
+ meta := Metadata{Name: string(name)}
var (
attr PackageAttr
patches []string
@@ -575,7 +596,6 @@ func (ctx *evalContext) f(
early bool
anitya int64
- version string
sourceA any
helper Helper
@@ -584,11 +604,9 @@ func (ctx *evalContext) f(
if err = args.Apply(map[unique.Handle[azalea.Ident]]any{
k("description"): &meta.Description,
k("website"): &meta.Website,
+ k("version"): &meta.Version,
k("anitya"): &anitya,
- k("version"): &version,
- k("source"): &sourceA,
-
k("writable"): &attr.Writable,
k("chmod"): &attr.Chmod,
k("enterSource"): &attr.EnterSource,
@@ -598,6 +616,7 @@ func (ctx *evalContext) f(
k("exclusive"): &excl,
k("toyboxEarly"): &early,
+ k("source"): &sourceA,
k("exec"): &helper,
k("inputs"): &inputs,
k("runtime"): &runtimes,
@@ -630,7 +649,7 @@ func (ctx *evalContext) f(
}
meta.ID = int(anitya)
- meta.f = func(t Toolchain) (pkg.Artifact, string) {
+ v = Artifact(func(t Toolchain) (*Metadata, pkg.Artifact) {
var source pkg.Artifact
switch p := sourceA.(type) {
case pkg.Artifact:
@@ -646,17 +665,15 @@ func (ctx *evalContext) f(
})
}
- return t.NewPackage(
+ return &meta, t.NewPackage(
meta.Name,
- version,
+ meta.Version,
source,
&attr,
helper,
inputsH...,
- ), version
- }
-
- v = meta
+ )
+ })
set = true
return
}
@@ -686,13 +703,13 @@ func (s *S) Evaluate(r io.Reader, b fs.FS) error {
ctx := evalContext{b}
for _, f := range pending {
- meta, set, err := azalea.Evaluate[Artifact](ctx.f, s.getS(), *f)
+ lf, set, err := azalea.Evaluate[Artifact](ctx.f, s.getS(), *f)
if err != nil {
return err
} else if !set {
return errors.New("unexpected unset")
}
- if !s.Register(&meta) {
+ if !s.Register(string(f.Ident), lf) {
return RegisterError(H(string(f.Ident)))
}
}