diff --git a/go.mod b/go.mod index d3322b0..de3817f 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.24 require ( github.com/Jguer/go-alpm/v2 v2.2.2 + github.com/Morganamilo/go-pacmanconf v0.0.0-20210502114700-cff030e927a5 github.com/sirupsen/logrus v1.9.3 somegit.dev/ALHP/ALHP.GO v0.0.0-20250322224907-01404adad53a ) diff --git a/main.go b/main.go index f87b211..95382bf 100644 --- a/main.go +++ b/main.go @@ -5,11 +5,15 @@ import ( "flag" "fmt" "github.com/Jguer/go-alpm/v2" + paconf "github.com/Morganamilo/go-pacmanconf" log "github.com/sirupsen/logrus" "io" "net/http" "os" + "path" + "regexp" "somegit.dev/ALHP/ALHP.GO/ent/dbpackage" + "strconv" "strings" ) @@ -18,14 +22,17 @@ const ( ) var ( - jsonFlag = flag.Bool("j", false, "output as JSON") - debugFlag = flag.Bool("d", false, "enable debug output") - exitCodeFlag = flag.Bool("e", false, "exit with non-zero if one of your packages is in queue") + jsonFlag = flag.Bool("j", false, "output as JSON") + debugFlag = flag.Bool("d", false, "enable debug output") + exitCodePackageFlag = flag.Bool("e", false, "exit with non-zero if one of your packages is in queue") + exitCodeMirrorFlag = flag.Bool("m", false, "exit with non-zero if your main mirror is out of sync") + repoRegex = regexp.MustCompile(`^\w+-x86-64-v\d$`) ) type JSONOut struct { - Total int `json:"total"` - Packages []string `json:"packages"` + Total int `json:"total"` + Packages []string `json:"packages"` + MirrorOutOfDate bool `json:"mirror_out_of_date"` } func main() { @@ -68,12 +75,60 @@ func main() { } } - if len(packagesInQueue) > 0 { + stats, err := ALHPStats() + if err != nil { + log.Errorf("error getting stats from ALHP api: %v", err) + os.Exit(2) + } + + pacmanConfig, _, err := paconf.ParseFile("/etc/pacman.conf") + if err != nil { + log.Errorf("error parsing pacman.conf: %v", err) + os.Exit(2) + } + + mirrorOutOfDate := false + for _, repo := range pacmanConfig.Repos { + if !repoRegex.MatchString(repo.Name) || len(repo.Servers) == 0 { + continue + } + + log.Debugf("checking mirror %s (%s)", repo.Servers[0], path.Join(repo.Servers[0], "../../../lastupdate")) + resp, err := http.Get(path.Join(repo.Servers[0], "../../../lastupdate")) + if err != nil { + log.Warnf("error getting mirror lastupdate: %v", err) + continue + } + + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + log.Warnf("mirror lastupdate returned %d", resp.StatusCode) + continue + } + + bResp, err := io.ReadAll(resp.Body) + if err != nil { + log.Warnf("error reading mirror lastupdate: %v", err) + } + + timestamp, err := strconv.ParseInt(string(bResp), 10, 64) + if err != nil { + log.Warnf("error parsing mirror lastupdate: %v", err) + } + + if timestamp < stats.LastMirrorTimestamp { + log.Debugf("mirror %s is out of date", repo.Servers[0]) + mirrorOutOfDate = true + } + } + + if len(packagesInQueue) == 0 { log.Debugf("found %d of your local packages in queue", len(packagesInQueue)) if *jsonFlag { err = json.NewEncoder(os.Stdout).Encode(JSONOut{ - Total: len(packagesInQueue), - Packages: packagesInQueue, + Total: len(packagesInQueue), + Packages: packagesInQueue, + MirrorOutOfDate: mirrorOutOfDate, }) if err != nil { log.Errorf("error encoding JSON: %v", err) @@ -83,17 +138,22 @@ func main() { fmt.Println(strings.Join(packagesInQueue, "\n")) } - if *exitCodeFlag { + if *exitCodePackageFlag { os.Exit(20) } } else { log.Debugf("no packages in queue") if *jsonFlag { err = json.NewEncoder(os.Stdout).Encode(JSONOut{ - Total: len(packagesInQueue), - Packages: packagesInQueue, + Total: len(packagesInQueue), + Packages: packagesInQueue, + MirrorOutOfDate: mirrorOutOfDate, }) } + + if *exitCodeMirrorFlag && mirrorOutOfDate { + os.Exit(20) + } } } @@ -128,6 +188,20 @@ type PackageResponse struct { Limit int `json:"limit"` } +type StatsResponse struct { + Failed int `json:"failed"` + Skipped int `json:"skipped"` + Latest int `json:"latest"` + Queued int `json:"queued"` + Building int `json:"building"` + LastMirrorTimestamp int64 `json:"last_mirror_timestamp"` + Lto struct { + Enabled int `json:"enabled"` + Disabled int `json:"disabled"` + Unknown int `json:"unknown"` + } `json:"lto"` +} + func ALHPBuildQueuePkgbase() ([]string, error) { resp, err := http.Get(fmt.Sprintf("%spackages?status=built&status=building&status=queued&limit=0&offset=0", APIBase)) if err != nil { @@ -157,3 +231,25 @@ func ALHPBuildQueuePkgbase() ([]string, error) { } return respArray, nil } + +func ALHPStats() (*StatsResponse, error) { + resp, err := http.Get(fmt.Sprintf("%sstats", APIBase)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + bResp, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + jResp := new(StatsResponse) + if err = json.Unmarshal(bResp, jResp); err != nil { + return nil, err + } + return jResp, nil +}