internal/rosa: release monitoring via Anitya
All checks were successful
Test / Create distribution (push) Successful in 1m0s
Test / Sandbox (push) Successful in 2m44s
Test / Hakurei (push) Successful in 3m35s
Test / ShareFS (push) Successful in 3m48s
Test / Sandbox (race detector) (push) Successful in 4m56s
Test / Hakurei (race detector) (push) Successful in 5m53s
Test / Flake checks (push) Successful in 1m30s

This is much more sustainable than manual package flagging.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-03-05 20:55:45 +09:00
parent 75970a5650
commit a36b3ece16
2 changed files with 77 additions and 0 deletions

View File

@@ -10,6 +10,7 @@ import (
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"syscall" "syscall"
"time" "time"
"unique" "unique"
@@ -129,6 +130,31 @@ func main() {
) )
} }
c.NewCommand("updates", command.UsageInternal, func([]string) error {
var n int
for i := range rosa.PresetEnd {
p := rosa.PArtifact(i)
meta := rosa.GetMetadata(p)
if meta.ID == 0 {
continue
}
if v, err := meta.GetVersions(ctx); err != nil {
return err
} else if current := rosa.Std.Version(p); v.Latest != current {
n++
log.Printf("%s %s < %s", meta.Name, current, v.Latest)
} else {
msg.Verbosef("%s is up to date", meta.Name)
}
}
if n > 0 {
return errors.New(strconv.Itoa(n) + " packages are out of date")
}
return nil
})
{ {
var ( var (
flagGentoo string flagGentoo string

View File

@@ -1,6 +1,11 @@
package rosa package rosa
import ( import (
"context"
"encoding/json"
"errors"
"net/http"
"strconv"
"sync" "sync"
"hakurei.app/internal/pkg" "hakurei.app/internal/pkg"
@@ -152,11 +157,57 @@ type Metadata struct {
Description string `json:"description"` Description string `json:"description"`
// Project home page. // Project home page.
Website string `json:"website,omitempty"` Website string `json:"website,omitempty"`
// Project identifier on [Anitya].
//
// [Anitya]: https://release-monitoring.org/
ID int `json:"-"`
} }
// Unversioned denotes an unversioned [PArtifact]. // Unversioned denotes an unversioned [PArtifact].
const Unversioned = "\x00" const Unversioned = "\x00"
// UnpopulatedIDError is returned by [Metadata.GetLatest] for an instance of
// [Metadata] where ID is not populated.
type UnpopulatedIDError struct{}
func (UnpopulatedIDError) Unwrap() error { return errors.ErrUnsupported }
func (UnpopulatedIDError) Error() string { return "Anitya ID is not populated" }
// Versions are package versions returned by Anitya.
type Versions struct {
// The latest version for the project, as determined by the version sorting algorithm.
Latest string `json:"latest_version"`
// List of all versions that arent flagged as pre-release.
Stable []string `json:"stable_versions"`
// List of all versions stored, sorted from newest to oldest.
All []string `json:"versions"`
}
// GetVersions returns versions fetched from Anitya.
func (meta *Metadata) GetVersions(ctx context.Context) (*Versions, error) {
if meta.ID == 0 {
return nil, UnpopulatedIDError{}
}
var resp *http.Response
if req, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
"https://release-monitoring.org/api/v2/versions/?project_id="+
strconv.Itoa(meta.ID),
nil,
); err != nil {
return nil, err
} else if resp, err = http.DefaultClient.Do(req); err != nil {
return nil, err
}
var v Versions
err := json.NewDecoder(resp.Body).Decode(&v)
return &v, errors.Join(err, resp.Body.Close())
}
var ( var (
// artifactsM is an array of [PArtifact] metadata. // artifactsM is an array of [PArtifact] metadata.
artifactsM [PresetEnd]Metadata artifactsM [PresetEnd]Metadata