263 lines
6.5 KiB
Go
263 lines
6.5 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"github.com/Jguer/go-alpm/v2"
|
|
paconf "github.com/Morganamilo/go-pacmanconf"
|
|
log "github.com/sirupsen/logrus"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"regexp"
|
|
"somegit.dev/ALHP/ALHP.GO/ent/dbpackage"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
APIBase = "https://api.alhp.dev/"
|
|
)
|
|
|
|
var (
|
|
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"`
|
|
MirrorOutOfDate bool `json:"mirror_out_of_date"`
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
if *debugFlag {
|
|
log.SetLevel(log.DebugLevel)
|
|
}
|
|
|
|
h, er := alpm.Initialize("/", "/var/lib/pacman")
|
|
if er != nil {
|
|
log.Errorf("error initializing alpm library: %v", er)
|
|
os.Exit(1)
|
|
}
|
|
defer func(h *alpm.Handle) {
|
|
err := h.Release()
|
|
if err != nil {
|
|
log.Errorf("error releasing alpm library: %v", err)
|
|
}
|
|
}(h)
|
|
|
|
db, er := h.LocalDB()
|
|
if er != nil {
|
|
log.Errorf("error initializing alpm library: %v", er)
|
|
os.Exit(1)
|
|
}
|
|
|
|
alhpQueue, err := ALHPBuildQueuePkgbase()
|
|
if err != nil {
|
|
log.Errorf("error getting build queue from ALHP api: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
log.Debugf("alhp build queue length: %d", len(alhpQueue))
|
|
|
|
var packagesInQueue []string
|
|
for _, pkg := range db.PkgCache().Slice() {
|
|
if Find(alhpQueue, pkg.Base()) != -1 {
|
|
log.Debugf("found package in queue: %s", pkg.Name())
|
|
packagesInQueue = append(packagesInQueue, pkg.Name())
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
pURL, err := url.Parse(repo.Servers[0])
|
|
if err != nil {
|
|
log.Warnf("error parsing mirror url: %v", err)
|
|
}
|
|
pURL.Path = path.Join(pURL.Path, "../../../lastupdate")
|
|
|
|
log.Debugf("checking mirror %s (%s)", repo.Servers[0], pURL.String())
|
|
resp, err := http.Get(pURL.String())
|
|
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,
|
|
MirrorOutOfDate: mirrorOutOfDate,
|
|
})
|
|
if err != nil {
|
|
log.Errorf("error encoding JSON: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
} else {
|
|
fmt.Println(strings.Join(packagesInQueue, "\n"))
|
|
}
|
|
|
|
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,
|
|
MirrorOutOfDate: mirrorOutOfDate,
|
|
})
|
|
}
|
|
|
|
if *exitCodeMirrorFlag && mirrorOutOfDate {
|
|
os.Exit(20)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Find[T comparable](arr []T, match T) int {
|
|
for i, v := range arr {
|
|
if v == match {
|
|
return i
|
|
}
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
type ThinPackage struct {
|
|
Pkgbase string `json:"pkgbase"`
|
|
Repo string `json:"repo"`
|
|
SplitPackages []string `json:"split_packages"`
|
|
Status dbpackage.Status `json:"status"`
|
|
SkipReason *string `json:"skip_reason,omitempty"`
|
|
LTO dbpackage.Lto `json:"lto"`
|
|
DebugSymbols dbpackage.DebugSymbols `json:"debug_symbols"`
|
|
ArchVersion string `json:"arch_version"`
|
|
RepoVersion string `json:"repo_version"`
|
|
BuildDate *string `json:"build_date,omitempty"`
|
|
PeakMem *string `json:"peak_mem,omitempty"`
|
|
}
|
|
|
|
type PackageResponse struct {
|
|
Packages []*ThinPackage `json:"packages"`
|
|
Total int `json:"total"`
|
|
Offset int `json:"offset"`
|
|
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 {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == http.StatusNotFound {
|
|
return nil, nil
|
|
} else if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("ALHP api returned HTTP %d", resp.StatusCode)
|
|
}
|
|
|
|
bResp, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
jResp := new(PackageResponse)
|
|
if err = json.Unmarshal(bResp, jResp); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
respArray := make([]string, len(jResp.Packages))
|
|
for i := range jResp.Packages {
|
|
respArray[i] = jResp.Packages[i].Pkgbase
|
|
}
|
|
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
|
|
}
|