765 lines
23 KiB
Go
765 lines
23 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/Jguer/go-alpm/v2"
|
|
"github.com/Morganamilo/go-srcinfo"
|
|
"github.com/c2h5oh/datasize"
|
|
"github.com/google/uuid"
|
|
"github.com/otiai10/copy"
|
|
"github.com/sethvargo/go-retry"
|
|
log "github.com/sirupsen/logrus"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"somegit.dev/ALHP/ALHP.GO/ent"
|
|
"somegit.dev/ALHP/ALHP.GO/ent/dbpackage"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
type ProtoPackage struct {
|
|
Pkgbase string
|
|
Srcinfo *srcinfo.Srcinfo
|
|
Arch string
|
|
PkgFiles []string
|
|
Repo dbpackage.Repository
|
|
March string
|
|
FullRepo string
|
|
Version string
|
|
DBPackage *ent.DBPackage
|
|
Pkgbuild string
|
|
State *StateInfo
|
|
}
|
|
|
|
var (
|
|
ErrorNotEligible = errors.New("package is not eligible")
|
|
)
|
|
|
|
func (p *ProtoPackage) isEligible(ctx context.Context) bool {
|
|
globMatch, err := MatchGlobList(p.Pkgbase, conf.Blacklist.Packages)
|
|
if err != nil {
|
|
log.Errorf("error parsing glob from no-build list: %v", err)
|
|
}
|
|
|
|
skipping := false
|
|
switch {
|
|
case p.Arch == "any":
|
|
log.Debugf("skipped %s: any-package", p.Pkgbase)
|
|
p.DBPackage.SkipReason = "arch = any"
|
|
p.DBPackage.Status = dbpackage.StatusSkipped
|
|
skipping = true
|
|
case globMatch:
|
|
log.Debugf("skipped %s: package on no-build list", p.Pkgbase)
|
|
p.DBPackage.SkipReason = "blacklisted"
|
|
p.DBPackage.Status = dbpackage.StatusSkipped
|
|
skipping = true
|
|
case p.DBPackage.MaxRss != nil && datasize.ByteSize(*p.DBPackage.MaxRss)*datasize.KB > conf.Build.MemoryLimit:
|
|
log.Debugf("skipped %s: memory limit exceeded (%s)", p.Pkgbase, datasize.ByteSize(*p.DBPackage.MaxRss)*datasize.KB)
|
|
p.DBPackage.SkipReason = "memory limit exceeded"
|
|
p.DBPackage.Status = dbpackage.StatusSkipped
|
|
skipping = true
|
|
case p.isPkgFailed():
|
|
log.Debugf("skipped %s: failed build", p.Pkgbase)
|
|
skipping = true
|
|
}
|
|
|
|
if skipping {
|
|
p.DBPackage = p.DBPackage.Update().SetUpdated(time.Now()).SetVersion(p.Version).SetStatus(p.DBPackage.Status).
|
|
SetSkipReason(p.DBPackage.SkipReason).SetTagRev(p.State.TagRev).SaveX(ctx)
|
|
return false
|
|
}
|
|
p.DBPackage = p.DBPackage.Update().SetUpdated(time.Now()).SetVersion(p.Version).SaveX(ctx)
|
|
|
|
if Contains(conf.Blacklist.LTO, p.Pkgbase) && p.DBPackage.Lto != dbpackage.LtoDisabled {
|
|
p.DBPackage = p.DBPackage.Update().SetLto(dbpackage.LtoDisabled).SaveX(ctx)
|
|
}
|
|
|
|
repoVer, err := p.repoVersion()
|
|
if err != nil {
|
|
p.DBPackage = p.DBPackage.Update().ClearRepoVersion().SaveX(ctx)
|
|
} else if alpm.VerCmp(repoVer, p.Version) > 0 {
|
|
log.Debugf("skipped %s: version in repo higher than in PKGBUILD (%s < %s)", p.Pkgbase, p.Version, repoVer)
|
|
p.DBPackage = p.DBPackage.Update().SetStatus(dbpackage.StatusLatest).ClearSkipReason().SetTagRev(p.State.TagRev).SaveX(ctx)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *ProtoPackage) build(ctx context.Context) (time.Duration, error) {
|
|
start := time.Now().UTC()
|
|
chroot := "build_" + uuid.New().String()
|
|
|
|
buildFolder, err := p.setupBuildDir(ctx)
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error setting up build folder: %w", err)
|
|
}
|
|
defer func() {
|
|
chroot := chroot
|
|
log.Debugf("removing chroot %s", chroot)
|
|
err := cleanBuildDir(buildFolder, filepath.Join(conf.Basedir.Work, chrootDir, chroot))
|
|
if err != nil {
|
|
log.Errorf("error removing builddir/chroot %s/%s: %v", buildDir, chroot, err)
|
|
}
|
|
}()
|
|
|
|
err = p.genSrcinfo()
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error generating srcinfo: %w", err)
|
|
}
|
|
p.Version = constructVersion(p.Srcinfo.Pkgver, p.Srcinfo.Pkgrel, p.Srcinfo.Epoch)
|
|
p.DBPackage = p.DBPackage.Update().SetPackages(packages2slice(p.Srcinfo.Packages)).SaveX(ctx)
|
|
|
|
// skip haskell packages, since they cannot be optimized currently (no -O3 & march has no effect as far as I know)
|
|
if Contains(p.Srcinfo.MakeDepends, "ghc") || Contains(p.Srcinfo.MakeDepends, "haskell-ghc") ||
|
|
Contains(p.Srcinfo.Depends, "ghc") || Contains(p.Srcinfo.Depends, "haskell-ghc") {
|
|
p.DBPackage = p.DBPackage.Update().SetStatus(dbpackage.StatusSkipped).SetSkipReason("haskell").SetTagRev(p.State.TagRev).SaveX(ctx)
|
|
buildManager.repoPurge[p.FullRepo] <- []*ProtoPackage{p}
|
|
return time.Since(start), ErrorNotEligible
|
|
}
|
|
|
|
isLatest, local, syncVersion, err := p.isMirrorLatest(alpmHandle)
|
|
if err != nil {
|
|
var multipleStateFilesError MultipleStateFilesError
|
|
var unableToSatisfyError UnableToSatisfyError
|
|
switch {
|
|
default:
|
|
return time.Since(start), fmt.Errorf("error solving deps: %w", err)
|
|
case errors.As(err, &multipleStateFilesError):
|
|
log.Infof("skipped %s: multiple PKGBUILDs for dependency found: %v", p.Srcinfo.Pkgbase, err)
|
|
p.DBPackage = p.DBPackage.Update().SetStatus(dbpackage.StatusSkipped).SetSkipReason("multiple PKGBUILD for dep. found").SaveX(ctx)
|
|
return time.Since(start), err
|
|
case errors.As(err, &unableToSatisfyError):
|
|
log.Infof("skipped %s: unable to resolve dependencies: %v", p.Srcinfo.Pkgbase, err)
|
|
p.DBPackage = p.DBPackage.Update().SetStatus(dbpackage.StatusSkipped).SetSkipReason("unable to resolve dependencies").SaveX(ctx)
|
|
return time.Since(start), ErrorNotEligible
|
|
}
|
|
}
|
|
|
|
if !isLatest {
|
|
if local != nil {
|
|
log.Infof("delayed %s: not all dependencies are up to date (local: %s==%s, sync: %s==%s)",
|
|
p.Srcinfo.Pkgbase, local.Name(), local.Version(), local.Name(), syncVersion)
|
|
p.DBPackage.Update().SetStatus(dbpackage.StatusDelayed).
|
|
SetSkipReason(fmt.Sprintf("waiting for %s==%s", local.Name(), syncVersion)).ExecX(ctx)
|
|
|
|
// Returning an error here causes the package to be purged.
|
|
// Purge delayed packages in case delay is caused by inconsistencies in state.
|
|
// Worst case would be clients downloading a package update twice, once from their official mirror,
|
|
// and then after build from ALHP. Best case we prevent a not buildable package from staying in the repos
|
|
// in an outdated version.
|
|
if time.Since(local.BuildDate()).Hours() >= 48 && p.DBPackage.RepoVersion != "" {
|
|
return time.Since(start), errors.New("overdue package waiting")
|
|
}
|
|
} else {
|
|
log.Infof("delayed %s: not all dependencies are up to date or resolvable", p.Srcinfo.Pkgbase)
|
|
p.DBPackage.Update().SetStatus(dbpackage.StatusDelayed).SetSkipReason("waiting for mirror").ExecX(ctx)
|
|
}
|
|
|
|
return time.Since(start), ErrorNotEligible
|
|
}
|
|
|
|
log.Infof("[P] build starting: %s->%s->%s", p.FullRepo, p.Pkgbase, p.Version)
|
|
|
|
p.DBPackage = p.DBPackage.Update().SetStatus(dbpackage.StatusBuilding).ClearSkipReason().SaveX(ctx)
|
|
|
|
err = p.importKeys()
|
|
if err != nil {
|
|
log.Warningf("[P] failed to import pgp keys for %s->%s->%s: %v", p.FullRepo, p.Pkgbase, p.Version, err)
|
|
}
|
|
|
|
buildNo := 1
|
|
versionSlice := strings.Split(p.DBPackage.LastVersionBuild, ".")
|
|
if strings.Join(versionSlice[:len(versionSlice)-1], ".") == p.Version {
|
|
buildNo, err = strconv.Atoi(versionSlice[len(versionSlice)-1])
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error while reading buildNo from pkgrel: %w", err)
|
|
}
|
|
buildNo++
|
|
}
|
|
|
|
err = p.increasePkgRel(buildNo)
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error while increasing pkgrel: %w", err)
|
|
}
|
|
|
|
p.PkgFiles = []string{}
|
|
|
|
// default to LTO
|
|
makepkgFile := makepkg
|
|
if p.DBPackage.Lto == dbpackage.LtoDisabled || p.DBPackage.Lto == dbpackage.LtoAutoDisabled {
|
|
// use non-lto makepkg.conf if LTO is blacklisted for this package
|
|
makepkgFile = makepkgLTO
|
|
}
|
|
cmd := exec.CommandContext(ctx, "makechrootpkg", "-c", "-D", filepath.Join(conf.Basedir.Work, makepkgDir), //nolint:gosec
|
|
"-l", chroot, "-r", filepath.Join(conf.Basedir.Work, chrootDir), "--", "-m", "--noprogressbar", "--config",
|
|
filepath.Join(conf.Basedir.Work, makepkgDir, fmt.Sprintf(makepkgFile, p.March)))
|
|
cmd.Dir = filepath.Dir(p.Pkgbuild)
|
|
var out bytes.Buffer
|
|
cmd.Stdout = &out
|
|
cmd.Stderr = &out
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error starting build: %w", err)
|
|
}
|
|
|
|
err = cmd.Wait()
|
|
|
|
Rusage, ok := cmd.ProcessState.SysUsage().(*syscall.Rusage)
|
|
if !ok {
|
|
log.Panicf("rusage is not of type *syscall.Rusage, are we running on unix-like?")
|
|
}
|
|
|
|
if err != nil {
|
|
if ctx.Err() != nil {
|
|
return time.Since(start), ctx.Err()
|
|
}
|
|
|
|
if p.DBPackage.Lto != dbpackage.LtoAutoDisabled && p.DBPackage.Lto != dbpackage.LtoDisabled &&
|
|
(reLdError.MatchString(out.String()) || reRustLTOError.MatchString(out.String())) {
|
|
p.DBPackage.Update().SetStatus(dbpackage.StatusQueued).SetSkipReason("non-LTO rebuild").SetLto(dbpackage.LtoAutoDisabled).ExecX(ctx)
|
|
return time.Since(start), errors.New("ld/lto-incompatibility error detected, LTO disabled")
|
|
}
|
|
|
|
if reDownloadError.MatchString(out.String()) || reDownloadError2.MatchString(out.String()) ||
|
|
rePortError.MatchString(out.String()) || reSigError.MatchString(out.String()) {
|
|
p.DBPackage.Update().SetStatus(dbpackage.StatusQueued).ExecX(ctx)
|
|
return time.Since(start), errors.New("known build error detected")
|
|
}
|
|
|
|
err = os.MkdirAll(filepath.Join(conf.Basedir.Repo, logDir, p.March), 0o755)
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error creating logdir: %w", err)
|
|
}
|
|
err = os.WriteFile(filepath.Join(conf.Basedir.Repo, logDir, p.March, p.Pkgbase+".log"), //nolint:gosec
|
|
[]byte(strings.ToValidUTF8(out.String(), "")), 0o644)
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error warting to logdir: %w", err)
|
|
}
|
|
|
|
p.DBPackage.Update().
|
|
SetStatus(dbpackage.StatusFailed).
|
|
ClearSkipReason().
|
|
SetBuildTimeStart(start).
|
|
ClearMaxRss().
|
|
ClearLastVersionBuild().
|
|
ClearIoOut().
|
|
ClearIoIn().
|
|
ClearUTime().
|
|
ClearSTime().
|
|
SetTagRev(p.State.TagRev).
|
|
ExecX(ctx)
|
|
return time.Since(start), fmt.Errorf("build failed: exit code %d", cmd.ProcessState.ExitCode())
|
|
}
|
|
|
|
pkgFiles, err := filepath.Glob(filepath.Join(filepath.Dir(p.Pkgbuild), "*.pkg.tar.zst"))
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error scanning builddir for artifacts: %w", err)
|
|
}
|
|
|
|
if len(pkgFiles) == 0 {
|
|
return time.Since(start), errors.New("no build-artifacts found")
|
|
}
|
|
|
|
for _, file := range pkgFiles {
|
|
cmd = exec.Command("gpg", "--batch", "--detach-sign", file)
|
|
res, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error while signing artifact: %w (%s)", err, string(res))
|
|
}
|
|
}
|
|
|
|
copyFiles, err := filepath.Glob(filepath.Join(filepath.Dir(p.Pkgbuild), "*.pkg.tar.zst*"))
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error scanning builddir for artifacts: %w", err)
|
|
}
|
|
|
|
holdingDir := filepath.Join(conf.Basedir.Work, waitingDir, p.FullRepo)
|
|
for _, file := range copyFiles {
|
|
err = os.MkdirAll(holdingDir, 0o755)
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error creating %s: %w", holdingDir, err)
|
|
}
|
|
err = copy.Copy(file, filepath.Join(holdingDir, filepath.Base(file)))
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error while copying file to %s: %w", filepath.Join(holdingDir, filepath.Base(file)), err)
|
|
}
|
|
|
|
if filepath.Ext(file) != ".sig" {
|
|
p.PkgFiles = append(p.PkgFiles, filepath.Join(holdingDir, filepath.Base(file)))
|
|
}
|
|
}
|
|
|
|
if _, err := os.Stat(filepath.Join(conf.Basedir.Repo, logDir, p.March, p.Pkgbase+".log")); err == nil {
|
|
err := os.Remove(filepath.Join(conf.Basedir.Repo, logDir, p.March, p.Pkgbase+".log"))
|
|
if err != nil {
|
|
return time.Since(start), fmt.Errorf("error removing log: %w", err)
|
|
}
|
|
}
|
|
|
|
updatePkg := p.DBPackage.Update().
|
|
SetStatus(dbpackage.StatusBuilt).
|
|
SetLto(dbpackage.LtoEnabled).
|
|
SetBuildTimeStart(start).
|
|
SetLastVersionBuild(p.Version).
|
|
SetTagRev(p.State.TagRev).
|
|
SetMaxRss(Rusage.Maxrss).
|
|
SetIoOut(Rusage.Oublock).
|
|
SetIoIn(Rusage.Inblock).
|
|
SetUTime(Rusage.Utime.Sec).
|
|
SetSTime(Rusage.Stime.Sec)
|
|
|
|
if p.DBPackage.Lto != dbpackage.LtoDisabled && p.DBPackage.Lto != dbpackage.LtoAutoDisabled {
|
|
updatePkg.SetLto(dbpackage.LtoEnabled)
|
|
}
|
|
|
|
updatePkg.ExecX(ctx)
|
|
|
|
return time.Since(start), nil
|
|
}
|
|
|
|
func (p *ProtoPackage) setupBuildDir(ctx context.Context) (string, error) {
|
|
buildDir := filepath.Join(conf.Basedir.Work, buildDir, p.March, p.Pkgbase+"-"+p.Version)
|
|
|
|
err := cleanBuildDir(buildDir, "")
|
|
if err != nil {
|
|
return "", fmt.Errorf("removing old builddir failed: %w", err)
|
|
}
|
|
|
|
err = os.MkdirAll(buildDir, 0o755)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
gitlabPath := reReplaceSinglePlus.ReplaceAllString(p.Pkgbase, "$1-$2")
|
|
gitlabPath = reReplaceRemainingPlus.ReplaceAllString(gitlabPath, "plus")
|
|
gitlabPath = reReplaceSpecialChars.ReplaceAllString(gitlabPath, "-")
|
|
gitlabPath = reReplaceUnderscore.ReplaceAllString(gitlabPath, "-")
|
|
gitlabPath = reReplaceTree.ReplaceAllString(gitlabPath, "unix-tree")
|
|
|
|
gr := retry.NewFibonacci(10 * time.Second)
|
|
gr = retry.WithMaxRetries(conf.MaxCloneRetries, gr)
|
|
|
|
if err := retry.Do(ctx, gr, func(ctx context.Context) error {
|
|
cmd := exec.CommandContext(ctx, "git", "clone", "--depth", "1", "--branch", p.State.TagVer, //nolint:gosec
|
|
fmt.Sprintf("https://gitlab.archlinux.org/archlinux/packaging/packages/%s.git", gitlabPath), buildDir)
|
|
res, err := cmd.CombinedOutput()
|
|
log.Debug(string(res))
|
|
if err != nil {
|
|
return retry.RetryableError(err)
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return "", err
|
|
}
|
|
p.Pkgbuild = filepath.Join(buildDir, "PKGBUILD")
|
|
|
|
return buildDir, nil
|
|
}
|
|
|
|
func (p *ProtoPackage) repoVersion() (string, error) {
|
|
if err := p.findPkgFiles(); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if len(p.PkgFiles) == 0 {
|
|
return "", errors.New("not found")
|
|
}
|
|
|
|
fNameSplit := strings.Split(p.PkgFiles[0], "-")
|
|
return fNameSplit[len(fNameSplit)-3] + "-" + fNameSplit[len(fNameSplit)-2], nil
|
|
}
|
|
|
|
func (p *ProtoPackage) increasePkgRel(buildNo int) error {
|
|
if p.Srcinfo == nil {
|
|
err := p.genSrcinfo()
|
|
if err != nil {
|
|
return fmt.Errorf("error generating srcinfo: %w", err)
|
|
}
|
|
}
|
|
|
|
if p.Version == "" {
|
|
p.Version = constructVersion(p.Srcinfo.Pkgver, p.Srcinfo.Pkgrel, p.Srcinfo.Epoch)
|
|
}
|
|
|
|
f, err := os.OpenFile(p.Pkgbuild, os.O_RDWR, 0o644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func(f *os.File) {
|
|
err := f.Close()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}(f)
|
|
|
|
fStr, err := io.ReadAll(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// increase buildno if already existing
|
|
var nStr string
|
|
if strings.Contains(p.Srcinfo.Pkgrel, ".") {
|
|
pkgRelSplit := strings.Split(p.Srcinfo.Pkgrel, ".")
|
|
pkgRelBuildNo, err := strconv.Atoi(pkgRelSplit[len(pkgRelSplit)-1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nStr = rePkgRel.ReplaceAllLiteralString(string(fStr), "pkgrel="+pkgRelSplit[0]+"."+strconv.Itoa(buildNo+pkgRelBuildNo))
|
|
versionSplit := strings.Split(p.Version, "-")
|
|
versionSplit[len(versionSplit)-1] = pkgRelSplit[0] + "." + strconv.Itoa(buildNo+pkgRelBuildNo)
|
|
p.Version = strings.Join(versionSplit, "-")
|
|
} else {
|
|
nStr = rePkgRel.ReplaceAllLiteralString(string(fStr), "pkgrel="+p.Srcinfo.Pkgrel+"."+strconv.Itoa(buildNo))
|
|
p.Version += "." + strconv.Itoa(buildNo)
|
|
}
|
|
|
|
_, err = f.Seek(0, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = f.Truncate(0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = f.WriteString(nStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *ProtoPackage) importKeys() error {
|
|
if p.Srcinfo == nil {
|
|
err := p.genSrcinfo()
|
|
if err != nil {
|
|
return fmt.Errorf("error generating srcinfo: %w", err)
|
|
}
|
|
}
|
|
|
|
if p.Srcinfo.ValidPGPKeys != nil {
|
|
args := []string{"--keyserver", "keyserver.ubuntu.com", "--recv-keys"}
|
|
args = append(args, p.Srcinfo.ValidPGPKeys...)
|
|
cmd := exec.Command("gpg", args...)
|
|
_, err := cmd.CombinedOutput()
|
|
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *ProtoPackage) isAvailable(h *alpm.Handle) bool {
|
|
dbs, err := h.SyncDBs()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
buildManager.alpmMutex.Lock()
|
|
defer buildManager.alpmMutex.Unlock()
|
|
|
|
var pkg alpm.IPackage
|
|
switch {
|
|
case p.Srcinfo != nil:
|
|
pkg, err = dbs.FindSatisfier(p.Srcinfo.Packages[0].Pkgname)
|
|
case p.DBPackage != nil && len(p.DBPackage.Packages) > 0:
|
|
pkg, err = dbs.FindSatisfier(p.DBPackage.Packages[0])
|
|
default:
|
|
cmd := exec.Command("unbuffer", "pacsift", "--exact", "--base="+p.Pkgbase, "--repo="+p.Repo.String(), //nolint:gosec
|
|
"--sysroot="+filepath.Join(conf.Basedir.Work, chrootDir, pristineChroot))
|
|
var res []byte
|
|
res, err = cmd.Output()
|
|
if err != nil {
|
|
log.Warningf("error getting packages from pacsift for %s: %v", p.Pkgbase, err)
|
|
return false
|
|
} else if len(res) == 0 {
|
|
return false
|
|
}
|
|
|
|
// workaround for https://github.com/andrewgregory/pacutils/issues/66
|
|
// TODO: remove once fixed
|
|
rRes := reReplacePacsiftWarning.ReplaceAllString(string(res), "")
|
|
if strings.TrimSpace(rRes) == "" {
|
|
return false
|
|
}
|
|
|
|
if len(strings.Split(strings.TrimSpace(rRes), "\n")) > 0 {
|
|
pacsiftLines := strings.Split(strings.TrimSpace(rRes), "\n")
|
|
|
|
var splitPkgs []string
|
|
for _, line := range pacsiftLines {
|
|
splitPkgs = append(splitPkgs, strings.Split(line, "/")[1])
|
|
}
|
|
|
|
if p.DBPackage != nil {
|
|
p.DBPackage = p.DBPackage.Update().SetPackages(splitPkgs).SaveX(context.Background())
|
|
}
|
|
pkg, err = dbs.FindSatisfier(splitPkgs[0])
|
|
} else {
|
|
log.Warningf("error getting packages from pacsift for %s", p.Pkgbase)
|
|
return false
|
|
}
|
|
}
|
|
if err != nil {
|
|
log.Debugf("error resolving %s: %v", p.Pkgbase, err)
|
|
return false
|
|
}
|
|
|
|
if pkg.DB().Name() != p.Repo.String() || pkg.Base() != p.Pkgbase {
|
|
log.Debugf("%s: repo (%s!=%s) or pkgbase (%s!=%s) does not match", p.Pkgbase, pkg.DB().Name(), p.Repo.String(), pkg.Base(), p.Pkgbase)
|
|
return false
|
|
}
|
|
|
|
if p.Srcinfo != nil && (!Contains(p.Srcinfo.Arch, pkg.Architecture()) || p.Srcinfo.Pkgbase != pkg.Base()) {
|
|
log.Debugf("%s: arch (%s!=%s) or pkgbase (%s!=%s) does not match", p.Pkgbase, p.Srcinfo.Arch[0],
|
|
pkg.Architecture(), pkg.Base(), p.Pkgbase)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *ProtoPackage) GitVersion(h *alpm.Handle) (string, error) {
|
|
if p.Pkgbase == "" {
|
|
return "", errors.New("invalid arguments")
|
|
}
|
|
|
|
stateFiles, _ := Glob(filepath.Join(conf.Basedir.Work, stateDir, "**/"+p.Pkgbase))
|
|
|
|
var fStateFiles []string
|
|
for _, stateFile := range stateFiles {
|
|
_, subRepo, _, err := stateFileMeta(stateFile)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if subRepo != nil {
|
|
continue
|
|
}
|
|
|
|
if !Contains(fStateFiles, stateFile) {
|
|
fStateFiles = append(fStateFiles, stateFile)
|
|
}
|
|
}
|
|
|
|
if len(fStateFiles) > 1 {
|
|
log.Infof("%s: multiple statefiles found, try resolving from mirror", p.Pkgbase)
|
|
dbs, err := h.SyncDBs()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
buildManager.alpmMutex.Lock()
|
|
iPackage, err := dbs.FindSatisfier(p.Pkgbase)
|
|
buildManager.alpmMutex.Unlock()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for _, stateFile := range fStateFiles {
|
|
repo, _, _, err := stateFileMeta(stateFile)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if iPackage.DB().Name() == repo {
|
|
fStateFiles = []string{stateFile}
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(fStateFiles) > 1 {
|
|
return "", MultipleStateFilesError{fmt.Errorf("%s: multiple statefiles found: %s", p.Pkgbase, fStateFiles)}
|
|
}
|
|
log.Infof("%s: resolving successful: MirrorRepo=%s; statefile chosen: %s", p.Pkgbase, iPackage.DB().Name(), fStateFiles[0])
|
|
} else if len(fStateFiles) == 0 {
|
|
return "", fmt.Errorf("%s: no matching statefile found (searched: %s, canidates: %s)", p.Pkgbase,
|
|
filepath.Join(conf.Basedir.Work, stateDir, "**/"+p.Pkgbase), stateFiles)
|
|
}
|
|
|
|
rawState, err := os.ReadFile(fStateFiles[0])
|
|
if err != nil {
|
|
return "", fmt.Errorf("error reading statefile %s: %w", fStateFiles[0], err)
|
|
}
|
|
state, err := parseState(string(rawState))
|
|
if err != nil {
|
|
return "", fmt.Errorf("error parsing statefile: %w", err)
|
|
}
|
|
|
|
return state.PkgVer, nil
|
|
}
|
|
|
|
func (p *ProtoPackage) isPkgFailed() bool {
|
|
if p.DBPackage.Version == "" {
|
|
return false
|
|
}
|
|
|
|
if alpm.VerCmp(p.DBPackage.Version, p.Version) < 0 {
|
|
return false
|
|
}
|
|
return p.DBPackage.Status == dbpackage.StatusFailed
|
|
}
|
|
|
|
func (p *ProtoPackage) genSrcinfo() error {
|
|
if p.Srcinfo != nil {
|
|
return nil
|
|
}
|
|
|
|
cmd := exec.Command("makepkg", "--printsrcinfo", "-p", filepath.Base(p.Pkgbuild)) //nolint:gosec
|
|
cmd.Dir = filepath.Dir(p.Pkgbuild)
|
|
res, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("makepkg exit non-zero (PKGBUILD: %s): %w (%s)", p.Pkgbuild, err, string(res))
|
|
}
|
|
|
|
info, err := srcinfo.Parse(string(res))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.Srcinfo = info
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *ProtoPackage) findPkgFiles() error {
|
|
pkgs, err := os.ReadDir(filepath.Join(conf.Basedir.Repo, p.FullRepo, "os", conf.Arch))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if p.DBPackage == nil && p.Srcinfo == nil {
|
|
return errors.New("unable to find pkgfiles without dbpkg or srcinfo present")
|
|
}
|
|
|
|
var realPkgs []string
|
|
if p.DBPackage != nil {
|
|
realPkgs = append(realPkgs, p.DBPackage.Packages...)
|
|
} else {
|
|
for _, realPkg := range p.Srcinfo.Packages {
|
|
realPkgs = append(realPkgs, realPkg.Pkgname)
|
|
}
|
|
}
|
|
|
|
var fPkg []string
|
|
for _, file := range pkgs {
|
|
if !file.IsDir() && !strings.HasSuffix(file.Name(), ".sig") {
|
|
matches := rePkgFile.FindStringSubmatch(file.Name())
|
|
|
|
if len(matches) > 1 && Contains(realPkgs, matches[1]) {
|
|
fPkg = append(fPkg, filepath.Join(conf.Basedir.Repo, p.FullRepo, "os", conf.Arch, file.Name()))
|
|
}
|
|
}
|
|
}
|
|
|
|
p.PkgFiles = fPkg
|
|
return nil
|
|
}
|
|
|
|
func (p *ProtoPackage) toDBPackage(create bool) error {
|
|
if p.DBPackage != nil {
|
|
return nil
|
|
}
|
|
|
|
dbPkg, err := db.DBPackage.Query().Where(
|
|
dbpackage.Pkgbase(p.Pkgbase),
|
|
dbpackage.March(p.March),
|
|
dbpackage.RepositoryEQ(p.Repo),
|
|
).Only(context.Background())
|
|
if err != nil && ent.IsNotFound(err) && create {
|
|
dbPkg = db.DBPackage.Create().
|
|
SetPkgbase(p.Pkgbase).
|
|
SetMarch(p.March).
|
|
SetRepository(p.Repo).
|
|
SaveX(context.Background())
|
|
} else if err != nil && !ent.IsNotFound(err) {
|
|
return err
|
|
}
|
|
|
|
p.DBPackage = dbPkg
|
|
return nil
|
|
}
|
|
|
|
func (p *ProtoPackage) exists() (bool, error) {
|
|
dbPkg, err := db.DBPackage.Query().Where(dbpackage.And(dbpackage.Pkgbase(p.Pkgbase), dbpackage.March(p.March))).Exist(context.Background())
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return dbPkg, nil
|
|
}
|
|
|
|
func (p *ProtoPackage) isMirrorLatest(h *alpm.Handle) (latest bool, foundPkg *alpm.Package, version string, err error) {
|
|
dbs, err := h.SyncDBs()
|
|
if err != nil {
|
|
return false, nil, "", err
|
|
}
|
|
|
|
allDepends := p.Srcinfo.Depends
|
|
allDepends = append(allDepends, p.Srcinfo.MakeDepends...)
|
|
// add gcc to dependents, since we can't know for sure if its in use
|
|
// prevents issues like #111
|
|
allDepends = append(allDepends, srcinfo.ArchString{
|
|
Arch: "x86_64",
|
|
Value: "gcc",
|
|
})
|
|
|
|
for _, dep := range allDepends {
|
|
buildManager.alpmMutex.Lock()
|
|
pkg, err := dbs.FindSatisfier(dep.Value)
|
|
buildManager.alpmMutex.Unlock()
|
|
if err != nil {
|
|
return false, nil, "", UnableToSatisfyError{err}
|
|
}
|
|
|
|
svn2gitVer, err := (&ProtoPackage{ //nolint:exhaustruct,exhaustivestruct
|
|
Pkgbase: pkg.Base(),
|
|
March: p.March,
|
|
}).GitVersion(h)
|
|
if err != nil {
|
|
return false, nil, "", err
|
|
} else if svn2gitVer == "" {
|
|
return false, nil, "", errors.New("no svn2git version")
|
|
}
|
|
|
|
if alpm.VerCmp(svn2gitVer, pkg.Version()) > 0 {
|
|
switch v := pkg.(type) {
|
|
case *alpm.Package:
|
|
return false, v, svn2gitVer, nil
|
|
default:
|
|
return false, nil, "", fmt.Errorf("invalid package type: %T", pkg)
|
|
}
|
|
}
|
|
}
|
|
|
|
return true, nil, "", nil
|
|
}
|
|
|
|
func (p *ProtoPackage) PkgbaseEquals(p2 *ProtoPackage, marchSensitive bool) bool {
|
|
return (marchSensitive && (p.Pkgbase == p2.Pkgbase && p.FullRepo == p2.FullRepo)) || (!marchSensitive && p.Pkgbase == p2.Pkgbase)
|
|
}
|
|
|
|
func (p *ProtoPackage) IsBuilt() (bool, error) {
|
|
if p.DBPackage == nil {
|
|
return false, nil
|
|
}
|
|
|
|
matches, err := filepath.Glob(filepath.Join(conf.Basedir.Work, waitingDir, p.FullRepo, p.DBPackage.Packages[0]+"*-x86_64.pkg.tar.zst"))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return len(matches) > 0, nil
|
|
}
|