Major refactor
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
d59c4036ef
commit
45522e3f3a
2
Makefile
2
Makefile
|
@ -19,4 +19,4 @@ uninstall:
|
||||||
internal/config/version.txt:
|
internal/config/version.txt:
|
||||||
go generate ./internal/config
|
go generate ./internal/config
|
||||||
|
|
||||||
.PHONY: install clean uninstall
|
.PHONY: install clean uninstall installmisc
|
760
build.go
760
build.go
|
@ -19,736 +19,84 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
_ "github.com/goreleaser/nfpm/v2/apk"
|
_ "github.com/goreleaser/nfpm/v2/apk"
|
||||||
_ "github.com/goreleaser/nfpm/v2/arch"
|
_ "github.com/goreleaser/nfpm/v2/arch"
|
||||||
_ "github.com/goreleaser/nfpm/v2/deb"
|
_ "github.com/goreleaser/nfpm/v2/deb"
|
||||||
_ "github.com/goreleaser/nfpm/v2/rpm"
|
_ "github.com/goreleaser/nfpm/v2/rpm"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
|
|
||||||
"github.com/goreleaser/nfpm/v2"
|
|
||||||
"github.com/goreleaser/nfpm/v2/files"
|
|
||||||
"go.elara.ws/logger/log"
|
"go.elara.ws/logger/log"
|
||||||
"go.elara.ws/lure/distro"
|
"go.elara.ws/lure/internal/build"
|
||||||
"go.elara.ws/lure/internal/cliutils"
|
|
||||||
"go.elara.ws/lure/internal/config"
|
"go.elara.ws/lure/internal/config"
|
||||||
"go.elara.ws/lure/internal/cpu"
|
|
||||||
"go.elara.ws/lure/internal/db"
|
|
||||||
"go.elara.ws/lure/internal/dl"
|
|
||||||
"go.elara.ws/lure/internal/repos"
|
|
||||||
"go.elara.ws/lure/internal/osutils"
|
"go.elara.ws/lure/internal/osutils"
|
||||||
"go.elara.ws/lure/internal/shutils"
|
"go.elara.ws/lure/internal/repos"
|
||||||
"go.elara.ws/lure/internal/shutils/decoder"
|
"go.elara.ws/lure/internal/types"
|
||||||
"go.elara.ws/lure/manager"
|
"go.elara.ws/lure/manager"
|
||||||
"mvdan.cc/sh/v3/expand"
|
|
||||||
"mvdan.cc/sh/v3/interp"
|
|
||||||
"mvdan.cc/sh/v3/syntax"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildVars represents the script variables required
|
var buildCmd = &cli.Command{
|
||||||
// to build a package
|
Name: "build",
|
||||||
type BuildVars struct {
|
Usage: "Build a local package",
|
||||||
Name string `sh:"name,required"`
|
Flags: []cli.Flag{
|
||||||
Version string `sh:"version,required"`
|
&cli.StringFlag{
|
||||||
Release int `sh:"release,required"`
|
Name: "script",
|
||||||
Epoch uint `sh:"epoch"`
|
Aliases: []string{"s"},
|
||||||
Description string `sh:"desc"`
|
Value: "lure.sh",
|
||||||
Homepage string `sh:"homepage"`
|
Usage: "Path to the build script",
|
||||||
Maintainer string `sh:"maintainer"`
|
|
||||||
Architectures []string `sh:"architectures"`
|
|
||||||
Licenses []string `sh:"license"`
|
|
||||||
Provides []string `sh:"provides"`
|
|
||||||
Conflicts []string `sh:"conflicts"`
|
|
||||||
Depends []string `sh:"deps"`
|
|
||||||
BuildDepends []string `sh:"build_deps"`
|
|
||||||
Replaces []string `sh:"replaces"`
|
|
||||||
Sources []string `sh:"sources"`
|
|
||||||
Checksums []string `sh:"checksums"`
|
|
||||||
Backup []string `sh:"backup"`
|
|
||||||
Scripts Scripts `sh:"scripts"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Scripts struct {
|
|
||||||
PreInstall string `sh:"preinstall"`
|
|
||||||
PostInstall string `sh:"postinstall"`
|
|
||||||
PreRemove string `sh:"preremove"`
|
|
||||||
PostRemove string `sh:"postremove"`
|
|
||||||
PreUpgrade string `sh:"preupgrade"`
|
|
||||||
PostUpgrade string `sh:"postupgrade"`
|
|
||||||
PreTrans string `sh:"pretrans"`
|
|
||||||
PostTrans string `sh:"posttrans"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildCmd(c *cli.Context) error {
|
|
||||||
script := c.String("script")
|
|
||||||
if c.String("package") != "" {
|
|
||||||
script = filepath.Join(config.RepoDir, c.String("package"), "lure.sh")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := repos.Pull(c.Context, gdb, cfg.Repos)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
mgr := manager.Detect()
|
|
||||||
if mgr == nil {
|
|
||||||
log.Fatal("Unable to detect supported package manager on system").Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgPaths, _, err := buildPackage(c.Context, script, mgr, c.Bool("clean"), c.Bool("interactive"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error building package").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error getting working directory").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pkgPath := range pkgPaths {
|
|
||||||
name := filepath.Base(pkgPath)
|
|
||||||
err = osutils.Move(pkgPath, filepath.Join(wd, name))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error moving the package").Err(err).Send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildPackage builds the script at the given path. It returns two slices. One contains the paths
|
|
||||||
// to the built package(s), the other contains the names of the built package(s).
|
|
||||||
func buildPackage(ctx context.Context, script string, mgr manager.Manager, clean, interactive bool) ([]string, []string, error) {
|
|
||||||
info, err := distro.ParseOSRelease(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var distroChanged bool
|
|
||||||
if distID, ok := os.LookupEnv("LURE_DISTRO"); ok {
|
|
||||||
info.ID = distID
|
|
||||||
// Since the distro was overwritten, we don't know what the
|
|
||||||
// like distros are, so set to nil
|
|
||||||
info.Like = nil
|
|
||||||
distroChanged = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fl, err := os.Open(script)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := syntax.NewParser().Parse(fl, "lure.sh")
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fl.Close()
|
|
||||||
|
|
||||||
scriptDir := filepath.Dir(script)
|
|
||||||
env := genBuildEnv(info, scriptDir)
|
|
||||||
|
|
||||||
// The first pass is just used to get variable values and runs before
|
|
||||||
// the script is displayed, so it is restricted so as to prevent malicious
|
|
||||||
// code from executing.
|
|
||||||
runner, err := interp.New(
|
|
||||||
interp.Env(expand.ListEnviron(env...)),
|
|
||||||
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
|
|
||||||
interp.ExecHandler(rHelpers.ExecHandler(shutils.NopExec)),
|
|
||||||
interp.ReadDirHandler(shutils.RestrictedReadDir(scriptDir)),
|
|
||||||
interp.StatHandler(shutils.RestrictedStat(scriptDir)),
|
|
||||||
interp.OpenHandler(shutils.RestrictedOpen(scriptDir)),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = runner.Run(ctx, file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dec := decoder.New(info, runner)
|
|
||||||
|
|
||||||
// If distro was changed, the list of like distros
|
|
||||||
// no longer applies, so disable its use
|
|
||||||
if distroChanged {
|
|
||||||
dec.LikeDistros = false
|
|
||||||
}
|
|
||||||
|
|
||||||
var vars BuildVars
|
|
||||||
err = dec.DecodeVars(&vars)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
baseDir := filepath.Join(config.PkgsDir, vars.Name)
|
|
||||||
srcdir := filepath.Join(baseDir, "src")
|
|
||||||
pkgdir := filepath.Join(baseDir, "pkg")
|
|
||||||
|
|
||||||
if !clean {
|
|
||||||
builtPkgPath, ok, err := checkForBuiltPackage(mgr, &vars, getPkgFormat(mgr), baseDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
return []string{builtPkgPath}, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cliutils.PromptViewScript(script, vars.Name, cfg.PagerStyle, interactive, translator)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !archMatches(vars.Architectures) {
|
|
||||||
buildAnyway, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", interactive, true, translator)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !buildAnyway {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send()
|
|
||||||
|
|
||||||
// The second pass will be used to execute the actual functions,
|
|
||||||
// so it cannot be restricted. The script has already been displayed
|
|
||||||
// to the user by this point, so it should be safe
|
|
||||||
runner, err = interp.New(
|
|
||||||
interp.Env(expand.ListEnviron(env...)),
|
|
||||||
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
|
|
||||||
interp.ExecHandler(helpers.ExecHandler(nil)),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = runner.Run(ctx, file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dec = decoder.New(info, runner)
|
|
||||||
|
|
||||||
// If distro was changed, the list of like distros
|
|
||||||
// no longer applies, so disable its use
|
|
||||||
if distroChanged {
|
|
||||||
dec.LikeDistros = false
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.RemoveAll(baseDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.MkdirAll(srcdir, 0o755)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.MkdirAll(pkgdir, 0o755)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
installed, err := mgr.ListInstalled(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if instVer, ok := installed[vars.Name]; ok {
|
|
||||||
log.Warn("This package is already installed").
|
|
||||||
Str("name", vars.Name).
|
|
||||||
Str("version", instVer).
|
|
||||||
Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
var buildDeps []string
|
|
||||||
if len(vars.BuildDepends) > 0 {
|
|
||||||
found, notFound, err := repos.FindPkgs(gdb, vars.BuildDepends)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
found = filterBuildDeps(found, installed)
|
|
||||||
|
|
||||||
log.Info("Installing build dependencies").Send()
|
|
||||||
|
|
||||||
flattened := cliutils.FlattenPkgs(found, "install", interactive, translator)
|
|
||||||
buildDeps = packageNames(flattened)
|
|
||||||
installPkgs(ctx, flattened, notFound, mgr, clean, interactive)
|
|
||||||
}
|
|
||||||
|
|
||||||
var builtDeps, builtNames, repoDeps []string
|
|
||||||
if len(vars.Depends) > 0 {
|
|
||||||
log.Info("Installing dependencies").Send()
|
|
||||||
|
|
||||||
found, notFound, err := repos.FindPkgs(gdb, vars.Depends)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
scripts := getScriptPaths(cliutils.FlattenPkgs(found, "install", interactive, translator))
|
|
||||||
for _, script := range scripts {
|
|
||||||
pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr, clean, interactive)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
builtDeps = append(builtDeps, pkgPaths...)
|
|
||||||
builtNames = append(builtNames, pkgNames...)
|
|
||||||
builtNames = append(builtNames, filepath.Base(filepath.Dir(script)))
|
|
||||||
}
|
|
||||||
repoDeps = notFound
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Downloading sources").Send()
|
|
||||||
|
|
||||||
err = getSources(ctx, srcdir, &vars)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = setDirVars(ctx, runner, srcdir, pkgdir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fn, ok := dec.GetFunc("version")
|
|
||||||
if ok {
|
|
||||||
log.Info("Executing version()").Send()
|
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
|
|
||||||
err = fn(
|
|
||||||
ctx,
|
|
||||||
interp.Dir(srcdir),
|
|
||||||
interp.StdIO(os.Stdin, buf, os.Stderr),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
newVer := strings.TrimSpace(buf.String())
|
|
||||||
err = setVersion(ctx, runner, newVer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
vars.Version = newVer
|
|
||||||
|
|
||||||
log.Info("Updating version").Str("new", newVer).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn, ok = dec.GetFunc("prepare")
|
|
||||||
if ok {
|
|
||||||
log.Info("Executing prepare()").Send()
|
|
||||||
|
|
||||||
err = fn(ctx, interp.Dir(srcdir))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn, ok = dec.GetFunc("build")
|
|
||||||
if ok {
|
|
||||||
log.Info("Executing build()").Send()
|
|
||||||
|
|
||||||
err = fn(ctx, interp.Dir(srcdir))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn, ok = dec.GetFunc("package")
|
|
||||||
if ok {
|
|
||||||
log.Info("Executing package()").Send()
|
|
||||||
|
|
||||||
err = fn(ctx, interp.Dir(srcdir))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Fatal("The package() function is required").Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Building package metadata").Str("name", vars.Name).Send()
|
|
||||||
|
|
||||||
uniq(
|
|
||||||
&repoDeps,
|
|
||||||
&builtDeps,
|
|
||||||
&builtNames,
|
|
||||||
)
|
|
||||||
|
|
||||||
pkgInfo := &nfpm.Info{
|
|
||||||
Name: vars.Name,
|
|
||||||
Description: vars.Description,
|
|
||||||
Arch: cpu.Arch(),
|
|
||||||
Platform: "linux",
|
|
||||||
Version: vars.Version,
|
|
||||||
Release: strconv.Itoa(vars.Release),
|
|
||||||
Homepage: vars.Homepage,
|
|
||||||
License: strings.Join(vars.Licenses, ", "),
|
|
||||||
Maintainer: vars.Maintainer,
|
|
||||||
Overridables: nfpm.Overridables{
|
|
||||||
Conflicts: vars.Conflicts,
|
|
||||||
Replaces: vars.Replaces,
|
|
||||||
Provides: vars.Provides,
|
|
||||||
Depends: append(repoDeps, builtNames...),
|
|
||||||
},
|
},
|
||||||
}
|
&cli.StringFlag{
|
||||||
|
Name: "package",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: "Name of the package to build and its repo (example: default/go-bin)",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "clean",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Build package from scratch even if there's an already built package available",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
script := c.String("script")
|
||||||
|
if c.String("package") != "" {
|
||||||
|
script = filepath.Join(config.GetPaths().RepoDir, c.String("package"), "lure.sh")
|
||||||
|
}
|
||||||
|
|
||||||
if vars.Epoch != 0 {
|
err := repos.Pull(c.Context, config.Config().Repos)
|
||||||
pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10)
|
if err != nil {
|
||||||
}
|
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
setScripts(&vars, pkgInfo, filepath.Dir(script))
|
mgr := manager.Detect()
|
||||||
|
if mgr == nil {
|
||||||
|
log.Fatal("Unable to detect a supported package manager on the system").Send()
|
||||||
|
}
|
||||||
|
|
||||||
if slices.Contains(vars.Architectures, "all") {
|
pkgPaths, _, err := build.BuildPackage(c.Context, types.BuildOpts{
|
||||||
pkgInfo.Arch = "all"
|
Script: script,
|
||||||
}
|
Manager: mgr,
|
||||||
|
Clean: c.Bool("clean"),
|
||||||
|
Interactive: c.Bool("interactive"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error building package").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
contents := []*files.Content{}
|
wd, err := os.Getwd()
|
||||||
filepath.Walk(pkgdir, func(path string, fi os.FileInfo, err error) error {
|
if err != nil {
|
||||||
trimmed := strings.TrimPrefix(path, pkgdir)
|
log.Fatal("Error getting working directory").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
if fi.IsDir() {
|
for _, pkgPath := range pkgPaths {
|
||||||
f, err := os.Open(path)
|
name := filepath.Base(pkgPath)
|
||||||
|
err = osutils.Move(pkgPath, filepath.Join(wd, name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Fatal("Error moving the package").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = f.Readdirnames(1)
|
|
||||||
if err != io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
contents = append(contents, &files.Content{
|
|
||||||
Source: path,
|
|
||||||
Destination: trimmed,
|
|
||||||
Type: "dir",
|
|
||||||
FileInfo: &files.ContentFileInfo{
|
|
||||||
MTime: fi.ModTime(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
f.Close()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if fi.Mode()&os.ModeSymlink != 0 {
|
|
||||||
link, err := os.Readlink(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
link = strings.TrimPrefix(link, pkgdir)
|
|
||||||
|
|
||||||
contents = append(contents, &files.Content{
|
|
||||||
Source: link,
|
|
||||||
Destination: trimmed,
|
|
||||||
Type: "symlink",
|
|
||||||
FileInfo: &files.ContentFileInfo{
|
|
||||||
MTime: fi.ModTime(),
|
|
||||||
Mode: fi.Mode(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fileContent := &files.Content{
|
|
||||||
Source: path,
|
|
||||||
Destination: trimmed,
|
|
||||||
FileInfo: &files.ContentFileInfo{
|
|
||||||
MTime: fi.ModTime(),
|
|
||||||
Mode: fi.Mode(),
|
|
||||||
Size: fi.Size(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if slices.Contains(vars.Backup, trimmed) {
|
|
||||||
fileContent.Type = "config|noreplace"
|
|
||||||
}
|
|
||||||
|
|
||||||
contents = append(contents, fileContent)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
},
|
||||||
|
|
||||||
pkgInfo.Overridables.Contents = contents
|
|
||||||
|
|
||||||
packager, err := nfpm.Get(getPkgFormat(mgr))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgName := packager.ConventionalFileName(pkgInfo)
|
|
||||||
pkgPath := filepath.Join(baseDir, pkgName)
|
|
||||||
|
|
||||||
pkgPaths := append(builtDeps, pkgPath)
|
|
||||||
pkgNames := append(builtNames, vars.Name)
|
|
||||||
|
|
||||||
pkgFile, err := os.Create(pkgPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Compressing package").Str("name", pkgName).Send()
|
|
||||||
|
|
||||||
err = packager.Package(pkgInfo, pkgFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(buildDeps) > 0 {
|
|
||||||
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove build dependencies?", interactive, false, translator)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if removeBuildDeps {
|
|
||||||
err = mgr.Remove(
|
|
||||||
&manager.Opts{
|
|
||||||
AsRoot: true,
|
|
||||||
NoConfirm: true,
|
|
||||||
},
|
|
||||||
buildDeps...,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uniq(&pkgPaths, &pkgNames)
|
|
||||||
|
|
||||||
return pkgPaths, pkgNames, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkForBuiltPackage(mgr manager.Manager, vars *BuildVars, pkgFormat, baseDir string) (string, bool, error) {
|
|
||||||
filename, err := pkgFileName(vars, pkgFormat)
|
|
||||||
if err != nil {
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgPath := filepath.Join(baseDir, filename)
|
|
||||||
|
|
||||||
_, err = os.Stat(pkgPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return pkgPath, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func pkgFileName(vars *BuildVars, pkgFormat string) (string, error) {
|
|
||||||
pkgInfo := &nfpm.Info{
|
|
||||||
Name: vars.Name,
|
|
||||||
Arch: cpu.Arch(),
|
|
||||||
Version: vars.Version,
|
|
||||||
Release: strconv.Itoa(vars.Release),
|
|
||||||
Epoch: strconv.FormatUint(uint64(vars.Epoch), 10),
|
|
||||||
}
|
|
||||||
|
|
||||||
packager, err := nfpm.Get(pkgFormat)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return packager.ConventionalFileName(pkgInfo), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPkgFormat(mgr manager.Manager) string {
|
|
||||||
pkgFormat := mgr.Format()
|
|
||||||
if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
|
|
||||||
pkgFormat = format
|
|
||||||
}
|
|
||||||
return pkgFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
func genBuildEnv(info *distro.OSRelease, scriptdir string) []string {
|
|
||||||
env := os.Environ()
|
|
||||||
|
|
||||||
env = append(
|
|
||||||
env,
|
|
||||||
"DISTRO_NAME="+info.Name,
|
|
||||||
"DISTRO_PRETTY_NAME="+info.PrettyName,
|
|
||||||
"DISTRO_ID="+info.ID,
|
|
||||||
"DISTRO_VERSION_ID="+info.VersionID,
|
|
||||||
"DISTRO_ID_LIKE="+strings.Join(info.Like, " "),
|
|
||||||
|
|
||||||
"ARCH="+cpu.Arch(),
|
|
||||||
"NCPU="+strconv.Itoa(runtime.NumCPU()),
|
|
||||||
|
|
||||||
"scriptdir="+scriptdir,
|
|
||||||
)
|
|
||||||
|
|
||||||
return env
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
|
|
||||||
if len(bv.Sources) != len(bv.Checksums) {
|
|
||||||
log.Fatal("The checksums array must be the same length as sources").Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, src := range bv.Sources {
|
|
||||||
opts := dl.Options{
|
|
||||||
Name: fmt.Sprintf("%s[%d]", bv.Name, i),
|
|
||||||
URL: src,
|
|
||||||
Destination: srcdir,
|
|
||||||
Progress: os.Stderr,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
|
|
||||||
algo, hashData, ok := strings.Cut(bv.Checksums[i], ":")
|
|
||||||
if ok {
|
|
||||||
checksum, err := hex.DecodeString(hashData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
opts.Hash = checksum
|
|
||||||
opts.HashAlgorithm = algo
|
|
||||||
} else {
|
|
||||||
checksum, err := hex.DecodeString(bv.Checksums[i])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
opts.Hash = checksum
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := dl.Download(ctx, opts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// setDirVars sets srcdir and pkgdir. It's a very hacky way of doing so,
|
|
||||||
// but setting the runner's Env and Vars fields doesn't seem to work.
|
|
||||||
func setDirVars(ctx context.Context, runner *interp.Runner, srcdir, pkgdir string) error {
|
|
||||||
cmd := "srcdir='" + srcdir + "'\npkgdir='" + pkgdir + "'\n"
|
|
||||||
fl, err := syntax.NewParser().Parse(strings.NewReader(cmd), "vars")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return runner.Run(ctx, fl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setScripts(vars *BuildVars, info *nfpm.Info, scriptDir string) {
|
|
||||||
if vars.Scripts.PreInstall != "" {
|
|
||||||
info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall)
|
|
||||||
}
|
|
||||||
|
|
||||||
if vars.Scripts.PostInstall != "" {
|
|
||||||
info.Scripts.PostInstall = filepath.Join(scriptDir, vars.Scripts.PostInstall)
|
|
||||||
}
|
|
||||||
|
|
||||||
if vars.Scripts.PreRemove != "" {
|
|
||||||
info.Scripts.PreRemove = filepath.Join(scriptDir, vars.Scripts.PreRemove)
|
|
||||||
}
|
|
||||||
|
|
||||||
if vars.Scripts.PostRemove != "" {
|
|
||||||
info.Scripts.PostRemove = filepath.Join(scriptDir, vars.Scripts.PostRemove)
|
|
||||||
}
|
|
||||||
|
|
||||||
if vars.Scripts.PreUpgrade != "" {
|
|
||||||
info.ArchLinux.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
|
|
||||||
info.APK.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
|
|
||||||
}
|
|
||||||
|
|
||||||
if vars.Scripts.PostUpgrade != "" {
|
|
||||||
info.ArchLinux.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
|
|
||||||
info.APK.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
|
|
||||||
}
|
|
||||||
|
|
||||||
if vars.Scripts.PreTrans != "" {
|
|
||||||
info.RPM.Scripts.PreTrans = filepath.Join(scriptDir, vars.Scripts.PreTrans)
|
|
||||||
}
|
|
||||||
|
|
||||||
if vars.Scripts.PostTrans != "" {
|
|
||||||
info.RPM.Scripts.PostTrans = filepath.Join(scriptDir, vars.Scripts.PostTrans)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// archMatches checks if your system architecture matches
|
|
||||||
// one of the provided architectures
|
|
||||||
func archMatches(architectures []string) bool {
|
|
||||||
if slices.Contains(architectures, "all") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, arch := range architectures {
|
|
||||||
if strings.HasPrefix(arch, "arm") {
|
|
||||||
architectures = append(architectures, cpu.CompatibleARMReverse(arch)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return slices.Contains(architectures, cpu.Arch())
|
|
||||||
}
|
|
||||||
|
|
||||||
func setVersion(ctx context.Context, r *interp.Runner, to string) error {
|
|
||||||
fl, err := syntax.NewParser().Parse(strings.NewReader("version='"+to+"'"), "")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return r.Run(ctx, fl)
|
|
||||||
}
|
|
||||||
|
|
||||||
func filterBuildDeps(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
|
|
||||||
out := map[string][]db.Package{}
|
|
||||||
for name, pkgs := range found {
|
|
||||||
var inner []db.Package
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
if _, ok := installed[pkg.Name]; !ok {
|
|
||||||
addToFiltered := true
|
|
||||||
for _, provides := range pkg.Provides.Val {
|
|
||||||
if _, ok := installed[provides]; ok {
|
|
||||||
addToFiltered = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if addToFiltered {
|
|
||||||
inner = append(inner, pkg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(inner) > 0 {
|
|
||||||
out[name] = inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func packageNames(pkgs []db.Package) []string {
|
|
||||||
names := make([]string, len(pkgs))
|
|
||||||
for i, p := range pkgs {
|
|
||||||
names[i] = p.Name
|
|
||||||
}
|
|
||||||
return names
|
|
||||||
}
|
|
||||||
|
|
||||||
// uniq removes all duplicates from string slices
|
|
||||||
func uniq(ss ...*[]string) {
|
|
||||||
for _, s := range ss {
|
|
||||||
slices.Sort(*s)
|
|
||||||
*s = slices.Compact(*s)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/twitchtv/twirp"
|
"github.com/twitchtv/twirp"
|
||||||
"go.elara.ws/logger/log"
|
"go.elara.ws/logger/log"
|
||||||
"go.elara.ws/lure/internal/api"
|
"go.elara.ws/lure/internal/api"
|
||||||
|
@ -34,9 +33,7 @@ import (
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
|
||||||
type lureWebAPI struct {
|
type lureWebAPI struct{}
|
||||||
db *sqlx.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.SearchResponse, error) {
|
func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.SearchResponse, error) {
|
||||||
query := "(name LIKE ? OR description LIKE ? OR json_array_contains(provides, ?))"
|
query := "(name LIKE ? OR description LIKE ? OR json_array_contains(provides, ?))"
|
||||||
|
@ -67,7 +64,7 @@ func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.Se
|
||||||
query += " LIMIT " + strconv.FormatInt(req.Limit, 10)
|
query += " LIMIT " + strconv.FormatInt(req.Limit, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := db.GetPkgs(l.db, query, args...)
|
result, err := db.GetPkgs(query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -86,7 +83,7 @@ func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.Se
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l lureWebAPI) GetPkg(ctx context.Context, req *api.GetPackageRequest) (*api.Package, error) {
|
func (l lureWebAPI) GetPkg(ctx context.Context, req *api.GetPackageRequest) (*api.Package, error) {
|
||||||
pkg, err := db.GetPkg(l.db, "name = ? AND repository = ?", req.Name, req.Repository)
|
pkg, err := db.GetPkg("name = ? AND repository = ?", req.Name, req.Repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -98,7 +95,7 @@ func (l lureWebAPI) GetBuildScript(ctx context.Context, req *api.GetBuildScriptR
|
||||||
return nil, twirp.NewError(twirp.InvalidArgument, "name and repository must not contain . or /")
|
return nil, twirp.NewError(twirp.InvalidArgument, "name and repository must not contain . or /")
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptPath := filepath.Join(config.RepoDir, req.Repository, req.Name, "lure.sh")
|
scriptPath := filepath.Join(config.GetPaths().RepoDir, req.Repository, req.Name, "lure.sh")
|
||||||
_, err := os.Stat(scriptPath)
|
_, err := os.Stat(scriptPath)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil, twirp.NewError(twirp.NotFound, "requested package not found")
|
return nil, twirp.NewError(twirp.NotFound, "requested package not found")
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"go.elara.ws/lure/internal/db"
|
"go.elara.ws/lure/internal/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,12 +16,12 @@ var logoData string
|
||||||
|
|
||||||
var _ http.HandlerFunc
|
var _ http.HandlerFunc
|
||||||
|
|
||||||
func handleBadge(gdb *sqlx.DB) http.HandlerFunc {
|
func handleBadge() http.HandlerFunc {
|
||||||
return func(res http.ResponseWriter, req *http.Request) {
|
return func(res http.ResponseWriter, req *http.Request) {
|
||||||
repo := chi.URLParam(req, "repo")
|
repo := chi.URLParam(req, "repo")
|
||||||
name := chi.URLParam(req, "pkg")
|
name := chi.URLParam(req, "pkg")
|
||||||
|
|
||||||
pkg, err := db.GetPkg(gdb, "name = ? AND repository = ?", name, repo)
|
pkg, err := db.GetPkg("name = ? AND repository = ?", name, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(res, err.Error(), http.StatusInternalServerError)
|
http.Error(res, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* LURE - Linux User REpository
|
|
||||||
* Copyright (C) 2023 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.elara.ws/logger/log"
|
|
||||||
"go.elara.ws/lure/internal/config"
|
|
||||||
"go.elara.ws/lure/internal/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cfg types.Config
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
err := config.Decode(&cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error decoding config file").Err(err).Send()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* LURE - Linux User REpository
|
|
||||||
* Copyright (C) 2023 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"go.elara.ws/logger/log"
|
|
||||||
"go.elara.ws/lure/internal/config"
|
|
||||||
"go.elara.ws/lure/internal/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
var gdb *sqlx.DB
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
var err error
|
|
||||||
gdb, err = db.Open(config.DBPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error opening database").Err(err).Send()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"go.elara.ws/logger"
|
"go.elara.ws/logger"
|
||||||
"go.elara.ws/logger/log"
|
"go.elara.ws/logger/log"
|
||||||
"go.elara.ws/lure/internal/api"
|
"go.elara.ws/lure/internal/api"
|
||||||
|
"go.elara.ws/lure/internal/config"
|
||||||
"go.elara.ws/lure/internal/repos"
|
"go.elara.ws/lure/internal/repos"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ func main() {
|
||||||
log.Logger = logger.NewMulti(log.Logger, logger.NewJSON(fl))
|
log.Logger = logger.NewMulti(log.Logger, logger.NewJSON(fl))
|
||||||
}
|
}
|
||||||
|
|
||||||
err := repos.Pull(ctx, gdb, cfg.Repos)
|
err := repos.Pull(ctx, config.Config().Repos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
@ -63,14 +64,14 @@ func main() {
|
||||||
go repoPullWorker(ctx, sigCh)
|
go repoPullWorker(ctx, sigCh)
|
||||||
|
|
||||||
apiServer := api.NewAPIServer(
|
apiServer := api.NewAPIServer(
|
||||||
lureWebAPI{db: gdb},
|
lureWebAPI{},
|
||||||
twirp.WithServerPathPrefix(""),
|
twirp.WithServerPathPrefix(""),
|
||||||
)
|
)
|
||||||
|
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.With(allowAllCORSHandler, withAcceptLanguage).Handle("/*", apiServer)
|
r.With(allowAllCORSHandler, withAcceptLanguage).Handle("/*", apiServer)
|
||||||
r.Post("/webhook", handleWebhook(sigCh))
|
r.Post("/webhook", handleWebhook(sigCh))
|
||||||
r.Get("/badge/{repo}/{pkg}", handleBadge(gdb))
|
r.Get("/badge/{repo}/{pkg}", handleBadge())
|
||||||
|
|
||||||
ln, err := net.Listen("tcp", *addr)
|
ln, err := net.Listen("tcp", *addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.elara.ws/logger/log"
|
"go.elara.ws/logger/log"
|
||||||
|
"go.elara.ws/lure/internal/config"
|
||||||
"go.elara.ws/lure/internal/repos"
|
"go.elara.ws/lure/internal/repos"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ func repoPullWorker(ctx context.Context, sigCh <-chan struct{}) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-sigCh:
|
case <-sigCh:
|
||||||
err := repos.Pull(ctx, gdb, cfg.Repos)
|
err := repos.Pull(ctx, config.Config().Repos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Error while pulling repositories").Err(err).Send()
|
log.Warn("Error while pulling repositories").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
36
config.go
36
config.go
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* LURE - Linux User REpository
|
|
||||||
* Copyright (C) 2023 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.elara.ws/logger/log"
|
|
||||||
"go.elara.ws/lure/internal/config"
|
|
||||||
"go.elara.ws/lure/internal/types"
|
|
||||||
"go.elara.ws/lure/manager"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cfg types.Config
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
err := config.Decode(&cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error decoding config file").Err(err).Send()
|
|
||||||
}
|
|
||||||
manager.DefaultRootCmd = cfg.RootCmd
|
|
||||||
}
|
|
36
db.go
36
db.go
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* LURE - Linux User REpository
|
|
||||||
* Copyright (C) 2023 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"go.elara.ws/lure/internal/config"
|
|
||||||
"go.elara.ws/lure/internal/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
var gdb *sqlx.DB
|
|
||||||
|
|
||||||
func loadDB() error {
|
|
||||||
ldb, err := db.Open(config.DBPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
gdb = ldb
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -103,7 +103,14 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
|
||||||
Logo: runner.Vars["LOGO"].Str,
|
Logo: runner.Vars["LOGO"].Str,
|
||||||
}
|
}
|
||||||
|
|
||||||
if runner.Vars["ID_LIKE"].IsSet() {
|
distroUpdated := false
|
||||||
|
if distID, ok := os.LookupEnv("LURE_DISTRO"); ok {
|
||||||
|
out.ID = distID
|
||||||
|
}
|
||||||
|
|
||||||
|
if distLike, ok := os.LookupEnv("LURE_DISTRO_LIKE"); ok {
|
||||||
|
out.Like = strings.Split(distLike, " ")
|
||||||
|
} else if runner.Vars["ID_LIKE"].IsSet() && !distroUpdated {
|
||||||
out.Like = strings.Split(runner.Vars["ID_LIKE"].Str, " ")
|
out.Like = strings.Split(runner.Vars["ID_LIKE"].Str, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
48
fix.go
48
fix.go
|
@ -28,36 +28,34 @@ import (
|
||||||
"go.elara.ws/lure/internal/repos"
|
"go.elara.ws/lure/internal/repos"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fixCmd(c *cli.Context) error {
|
var fixCmd = &cli.Command{
|
||||||
gdb.Close()
|
Name: "fix",
|
||||||
|
Usage: "Attempt to fix problems with LURE",
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
db.Close()
|
||||||
|
paths := config.GetPaths()
|
||||||
|
|
||||||
log.Info("Removing cache directory").Send()
|
log.Info("Removing cache directory").Send()
|
||||||
|
|
||||||
err := os.RemoveAll(config.CacheDir)
|
err := os.RemoveAll(paths.CacheDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Unable to remove cache directory").Err(err).Send()
|
log.Fatal("Unable to remove cache directory").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Rebuilding cache").Send()
|
log.Info("Rebuilding cache").Send()
|
||||||
|
|
||||||
err = os.MkdirAll(config.CacheDir, 0o755)
|
err = os.MkdirAll(paths.CacheDir, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Unable to create new cache directory").Err(err).Send()
|
log.Fatal("Unable to create new cache directory").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the DB is rebuilt when repos are pulled
|
err = repos.Pull(c.Context, config.Config().Repos)
|
||||||
gdb, err = db.Open(config.DBPath)
|
if err != nil {
|
||||||
if err != nil {
|
log.Fatal("Error pulling repos").Err(err).Send()
|
||||||
log.Fatal("Error initializing database").Err(err).Send()
|
}
|
||||||
}
|
|
||||||
config.DBPresent = false
|
|
||||||
|
|
||||||
err = repos.Pull(c.Context, gdb, cfg.Repos)
|
log.Info("Done").Send()
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error pulling repos").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Done").Send()
|
return nil
|
||||||
|
},
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
105
info.go
105
info.go
|
@ -33,61 +33,72 @@ import (
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func infoCmd(c *cli.Context) error {
|
var infoCmd = &cli.Command{
|
||||||
args := c.Args()
|
Name: "info",
|
||||||
if args.Len() < 1 {
|
Usage: "Print information about a package",
|
||||||
log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
|
Flags: []cli.Flag{
|
||||||
}
|
&cli.BoolFlag{
|
||||||
|
Name: "all",
|
||||||
err := repos.Pull(c.Context, gdb, cfg.Repos)
|
Aliases: []string{"a"},
|
||||||
if err != nil {
|
Usage: "Show all information, not just for the current distro",
|
||||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
},
|
||||||
}
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
found, _, err := repos.FindPkgs(gdb, args.Slice())
|
args := c.Args()
|
||||||
if err != nil {
|
if args.Len() < 1 {
|
||||||
log.Fatal("Error finding packages").Err(err).Send()
|
log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
|
||||||
}
|
|
||||||
|
|
||||||
if len(found) == 0 {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgs := cliutils.FlattenPkgs(found, "show", c.Bool("interactive"), translator)
|
|
||||||
|
|
||||||
var names []string
|
|
||||||
all := c.Bool("all")
|
|
||||||
|
|
||||||
if !all {
|
|
||||||
info, err := distro.ParseOSRelease(c.Context)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error parsing os-release file").Err(err).Send()
|
|
||||||
}
|
}
|
||||||
names, err = overrides.Resolve(
|
|
||||||
info,
|
|
||||||
overrides.DefaultOpts.
|
|
||||||
WithLanguages([]string{config.SystemLang()}),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error resolving overrides").Err(err).Send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pkg := range pkgs {
|
err := repos.Pull(c.Context, config.Config().Repos)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
found, _, err := repos.FindPkgs(args.Slice())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error finding packages").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(found) == 0 {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgs := cliutils.FlattenPkgs(found, "show", c.Bool("interactive"))
|
||||||
|
|
||||||
|
var names []string
|
||||||
|
all := c.Bool("all")
|
||||||
|
|
||||||
if !all {
|
if !all {
|
||||||
err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
|
info, err := distro.ParseOSRelease(c.Context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error encoding script variables").Err(err).Send()
|
log.Fatal("Error parsing os-release file").Err(err).Send()
|
||||||
}
|
}
|
||||||
} else {
|
names, err = overrides.Resolve(
|
||||||
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
|
info,
|
||||||
|
overrides.DefaultOpts.
|
||||||
|
WithLanguages([]string{config.SystemLang()}),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error encoding script variables").Err(err).Send()
|
log.Fatal("Error resolving overrides").Err(err).Send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("---")
|
for _, pkg := range pkgs {
|
||||||
}
|
if !all {
|
||||||
|
err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error encoding script variables").Err(err).Send()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error encoding script variables").Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
fmt.Println("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
154
install.go
154
install.go
|
@ -19,98 +19,98 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"fmt"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"go.elara.ws/logger/log"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
"go.elara.ws/logger/log"
|
||||||
|
"go.elara.ws/lure/internal/build"
|
||||||
"go.elara.ws/lure/internal/cliutils"
|
"go.elara.ws/lure/internal/cliutils"
|
||||||
"go.elara.ws/lure/internal/config"
|
"go.elara.ws/lure/internal/config"
|
||||||
"go.elara.ws/lure/internal/db"
|
"go.elara.ws/lure/internal/db"
|
||||||
"go.elara.ws/lure/internal/repos"
|
"go.elara.ws/lure/internal/repos"
|
||||||
|
"go.elara.ws/lure/internal/types"
|
||||||
"go.elara.ws/lure/manager"
|
"go.elara.ws/lure/manager"
|
||||||
)
|
)
|
||||||
|
|
||||||
func installCmd(c *cli.Context) error {
|
var installCmd = &cli.Command{
|
||||||
args := c.Args()
|
Name: "install",
|
||||||
if args.Len() < 1 {
|
Usage: "Install a new package",
|
||||||
log.Fatalf("Command install expected at least 1 argument, got %d", args.Len()).Send()
|
Aliases: []string{"in"},
|
||||||
}
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
mgr := manager.Detect()
|
Name: "clean",
|
||||||
if mgr == nil {
|
Aliases: []string{"c"},
|
||||||
log.Fatal("Unable to detect supported package manager on system").Send()
|
Usage: "Build package from scratch even if there's an already built package available",
|
||||||
}
|
},
|
||||||
|
},
|
||||||
err := repos.Pull(c.Context, gdb, cfg.Repos)
|
Action: func(c *cli.Context) error {
|
||||||
if err != nil {
|
args := c.Args()
|
||||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
if args.Len() < 1 {
|
||||||
}
|
log.Fatalf("Command install expected at least 1 argument, got %d", args.Len()).Send()
|
||||||
|
|
||||||
found, notFound, err := repos.FindPkgs(gdb, args.Slice())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error finding packages").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
installPkgs(c.Context, cliutils.FlattenPkgs(found, "install", c.Bool("interactive"), translator), notFound, mgr, c.Bool("clean"), c.Bool("interactive"))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// installPkgs installs non-LURE packages via the package manager, then builds and installs LURE
|
|
||||||
// packages
|
|
||||||
func installPkgs(ctx context.Context, pkgs []db.Package, notFound []string, mgr manager.Manager, clean, interactive bool) {
|
|
||||||
if len(notFound) > 0 {
|
|
||||||
err := mgr.Install(nil, notFound...)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error installing native packages").Err(err).Send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
installScripts(ctx, mgr, getScriptPaths(pkgs), clean, interactive)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getScriptPaths generates a slice of script paths corresponding to the
|
|
||||||
// given packages
|
|
||||||
func getScriptPaths(pkgs []db.Package) []string {
|
|
||||||
var scripts []string
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
scriptPath := filepath.Join(config.RepoDir, pkg.Repository, pkg.Name, "lure.sh")
|
|
||||||
scripts = append(scripts, scriptPath)
|
|
||||||
}
|
|
||||||
return scripts
|
|
||||||
}
|
|
||||||
|
|
||||||
// installScripts builds and installs LURE build scripts
|
|
||||||
func installScripts(ctx context.Context, mgr manager.Manager, scripts []string, clean, interactive bool) {
|
|
||||||
for _, script := range scripts {
|
|
||||||
builtPkgs, _, err := buildPackage(ctx, script, mgr, clean, interactive)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error building package").Err(err).Send()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mgr.InstallLocal(nil, builtPkgs...)
|
mgr := manager.Detect()
|
||||||
if err != nil {
|
if mgr == nil {
|
||||||
log.Fatal("Error installing package").Err(err).Send()
|
log.Fatal("Unable to detect a supported package manager on the system").Send()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
err := repos.Pull(c.Context, config.Config().Repos)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
found, notFound, err := repos.FindPkgs(args.Slice())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error finding packages").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgs := cliutils.FlattenPkgs(found, "install", c.Bool("interactive"))
|
||||||
|
build.InstallPkgs(c.Context, pkgs, notFound, types.BuildOpts{
|
||||||
|
Manager: mgr,
|
||||||
|
Clean: c.Bool("clean"),
|
||||||
|
Interactive: c.Bool("interactive"),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
BashComplete: func(c *cli.Context) {
|
||||||
|
result, err := db.GetPkgs("true")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error getting packages").Err(err).Send()
|
||||||
|
}
|
||||||
|
defer result.Close()
|
||||||
|
|
||||||
|
for result.Next() {
|
||||||
|
var pkg db.Package
|
||||||
|
err = result.StructScan(&pkg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error iterating over packages").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(pkg.Name)
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeCmd(c *cli.Context) error {
|
var removeCmd = &cli.Command{
|
||||||
args := c.Args()
|
Name: "remove",
|
||||||
if args.Len() < 1 {
|
Usage: "Remove an installed package",
|
||||||
log.Fatalf("Command remove expected at least 1 argument, got %d", args.Len()).Send()
|
Aliases: []string{"rm"},
|
||||||
}
|
Action: func(c *cli.Context) error {
|
||||||
|
args := c.Args()
|
||||||
|
if args.Len() < 1 {
|
||||||
|
log.Fatalf("Command remove expected at least 1 argument, got %d", args.Len()).Send()
|
||||||
|
}
|
||||||
|
|
||||||
mgr := manager.Detect()
|
mgr := manager.Detect()
|
||||||
if mgr == nil {
|
if mgr == nil {
|
||||||
log.Fatal("Unable to detect supported package manager on system").Send()
|
log.Fatal("Unable to detect a supported package manager on the system").Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
err := mgr.Remove(nil, c.Args().Slice()...)
|
err := mgr.Remove(nil, c.Args().Slice()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error removing packages").Err(err).Send()
|
log.Fatal("Error removing packages").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.28.1
|
||||||
// protoc v3.21.9
|
// protoc v4.24.2
|
||||||
// source: lure.proto
|
// source: lure.proto
|
||||||
|
|
||||||
package api
|
package api
|
||||||
|
|
|
@ -0,0 +1,728 @@
|
||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goreleaser/nfpm/v2"
|
||||||
|
"github.com/goreleaser/nfpm/v2/files"
|
||||||
|
"go.elara.ws/logger/log"
|
||||||
|
"go.elara.ws/lure/distro"
|
||||||
|
"go.elara.ws/lure/internal/cliutils"
|
||||||
|
"go.elara.ws/lure/internal/config"
|
||||||
|
"go.elara.ws/lure/internal/cpu"
|
||||||
|
"go.elara.ws/lure/internal/db"
|
||||||
|
"go.elara.ws/lure/internal/dl"
|
||||||
|
"go.elara.ws/lure/internal/repos"
|
||||||
|
"go.elara.ws/lure/internal/shutils"
|
||||||
|
"go.elara.ws/lure/internal/shutils/decoder"
|
||||||
|
"go.elara.ws/lure/internal/shutils/helpers"
|
||||||
|
"go.elara.ws/lure/internal/types"
|
||||||
|
"go.elara.ws/lure/manager"
|
||||||
|
"mvdan.cc/sh/v3/expand"
|
||||||
|
"mvdan.cc/sh/v3/interp"
|
||||||
|
"mvdan.cc/sh/v3/syntax"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildPackage builds the script at the given path. It returns two slices. One contains the paths
|
||||||
|
// to the built package(s), the other contains the names of the built package(s).
|
||||||
|
func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string, error) {
|
||||||
|
info, err := distro.ParseOSRelease(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fl, err := parseScript(info, opts.Script)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vars, err := executeFirstPass(ctx, info, fl, opts.Script)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs := getDirs(vars, opts.Script)
|
||||||
|
|
||||||
|
if !opts.Clean {
|
||||||
|
builtPkgPath, ok, err := checkForBuiltPackage(opts.Manager, vars, getPkgFormat(opts.Manager), dirs.BaseDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
return []string{builtPkgPath}, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cliutils.PromptViewScript(opts.Script, vars.Name, config.Config().PagerStyle, opts.Interactive)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send()
|
||||||
|
|
||||||
|
dec, err := executeSecondPass(ctx, info, fl, dirs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
installed, err := opts.Manager.ListInstalled(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cont, err := performChecks(vars, opts.Interactive, installed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
} else if !cont {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = prepareDirs(dirs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buildDeps, err := installBuildDeps(ctx, vars, opts, installed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
builtPaths, builtNames, repoDeps, err := installDeps(ctx, opts, vars)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Downloading sources").Send()
|
||||||
|
|
||||||
|
err = getSources(ctx, dirs.SrcDir, vars)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = executeFunctions(ctx, dec, dirs, vars)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Building package metadata").Str("name", vars.Name).Send()
|
||||||
|
|
||||||
|
pkgInfo, err := buildPkgMetadata(vars, dirs, append(repoDeps, builtNames...))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
packager, err := nfpm.Get(getPkgFormat(opts.Manager))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgName := packager.ConventionalFileName(pkgInfo)
|
||||||
|
pkgPath := filepath.Join(dirs.BaseDir, pkgName)
|
||||||
|
|
||||||
|
pkgFile, err := os.Create(pkgPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Compressing package").Str("name", pkgName).Send()
|
||||||
|
|
||||||
|
err = packager.Package(pkgInfo, pkgFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = removeBuildDeps(buildDeps, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the path and name of the package we just built to the
|
||||||
|
// appropriate slices
|
||||||
|
pkgPaths := append(builtPaths, pkgPath)
|
||||||
|
pkgNames := append(builtNames, vars.Name)
|
||||||
|
|
||||||
|
pkgPaths = removeDuplicates(pkgPaths)
|
||||||
|
pkgNames = removeDuplicates(pkgNames)
|
||||||
|
|
||||||
|
return pkgPaths, pkgNames, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseScript(info *distro.OSRelease, script string) (*syntax.File, error) {
|
||||||
|
fl, err := os.Open(script)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer fl.Close()
|
||||||
|
|
||||||
|
file, err := syntax.NewParser().Parse(fl, "lure.sh")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeFirstPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, script string) (*types.BuildVars, error) {
|
||||||
|
scriptDir := filepath.Dir(script)
|
||||||
|
env := createBuildEnvVars(info, types.Directories{ScriptDir: scriptDir})
|
||||||
|
|
||||||
|
// The first pass is just used to get variable values and runs before
|
||||||
|
// the script is displayed, so it is restricted so as to prevent malicious
|
||||||
|
// code from executing.
|
||||||
|
runner, err := interp.New(
|
||||||
|
interp.Env(expand.ListEnviron(env...)),
|
||||||
|
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
|
||||||
|
interp.ExecHandler(helpers.Restricted.ExecHandler(shutils.NopExec)),
|
||||||
|
interp.ReadDirHandler(shutils.RestrictedReadDir(scriptDir)),
|
||||||
|
interp.StatHandler(shutils.RestrictedStat(scriptDir)),
|
||||||
|
interp.OpenHandler(shutils.RestrictedOpen(scriptDir)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runner.Run(ctx, fl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dec := decoder.New(info, runner)
|
||||||
|
|
||||||
|
var vars types.BuildVars
|
||||||
|
err = dec.DecodeVars(&vars)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &vars, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDirs(vars *types.BuildVars, script string) types.Directories {
|
||||||
|
baseDir := filepath.Join(config.GetPaths().PkgsDir, vars.Name)
|
||||||
|
return types.Directories{
|
||||||
|
BaseDir: baseDir,
|
||||||
|
SrcDir: filepath.Join(baseDir, "src"),
|
||||||
|
PkgDir: filepath.Join(baseDir, "pkg"),
|
||||||
|
ScriptDir: filepath.Dir(script),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeSecondPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, dirs types.Directories) (*decoder.Decoder, error) {
|
||||||
|
env := createBuildEnvVars(info, dirs)
|
||||||
|
// The second pass will be used to execute the actual functions,
|
||||||
|
// so it cannot be restricted. The script has already been displayed
|
||||||
|
// to the user by this point, so it should be safe
|
||||||
|
runner, err := interp.New(
|
||||||
|
interp.Env(expand.ListEnviron(env...)),
|
||||||
|
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
|
||||||
|
interp.ExecHandler(helpers.Helpers.ExecHandler(nil)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runner.Run(ctx, fl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoder.New(info, runner), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareDirs(dirs types.Directories) error {
|
||||||
|
err := os.RemoveAll(dirs.BaseDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(dirs.SrcDir, 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.MkdirAll(dirs.PkgDir, 0o755)
|
||||||
|
}
|
||||||
|
|
||||||
|
func performChecks(vars *types.BuildVars, interactive bool, installed map[string]string) (bool, error) {
|
||||||
|
if !cpu.IsCompatibleWith(cpu.Arch(), vars.Architectures) {
|
||||||
|
cont, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", interactive, true)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cont {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if instVer, ok := installed[vars.Name]; ok {
|
||||||
|
log.Warn("This package is already installed").
|
||||||
|
Str("name", vars.Name).
|
||||||
|
Str("version", instVer).
|
||||||
|
Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func installBuildDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) ([]string, error) {
|
||||||
|
var buildDeps []string
|
||||||
|
if len(vars.BuildDepends) > 0 {
|
||||||
|
found, notFound, err := repos.FindPkgs(vars.BuildDepends)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
found = filterBuildDeps(found, installed)
|
||||||
|
|
||||||
|
log.Info("Installing build dependencies").Send()
|
||||||
|
|
||||||
|
flattened := cliutils.FlattenPkgs(found, "install", opts.Interactive)
|
||||||
|
buildDeps = packageNames(flattened)
|
||||||
|
InstallPkgs(ctx, flattened, notFound, opts)
|
||||||
|
}
|
||||||
|
return buildDeps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func installDeps(ctx context.Context, opts types.BuildOpts, vars *types.BuildVars) (builtPaths, builtNames, repoDeps []string, err error) {
|
||||||
|
if len(vars.Depends) > 0 {
|
||||||
|
log.Info("Installing dependencies").Send()
|
||||||
|
|
||||||
|
found, notFound, err := repos.FindPkgs(vars.Depends)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
repoDeps = notFound
|
||||||
|
|
||||||
|
// If there are multiple options for some packages, flatten them all into a single slice
|
||||||
|
pkgs := cliutils.FlattenPkgs(found, "install", opts.Interactive)
|
||||||
|
scripts := GetScriptPaths(pkgs)
|
||||||
|
for _, script := range scripts {
|
||||||
|
newOpts := opts
|
||||||
|
newOpts.Script = script
|
||||||
|
|
||||||
|
// Build the dependency
|
||||||
|
pkgPaths, pkgNames, err := BuildPackage(ctx, newOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the paths of all the built packages to builtPaths
|
||||||
|
builtPaths = append(builtPaths, pkgPaths...)
|
||||||
|
// Append the names of all the built packages to builtNames
|
||||||
|
builtNames = append(builtNames, pkgNames...)
|
||||||
|
// Append the name of the current package to builtNames
|
||||||
|
builtNames = append(builtNames, filepath.Base(filepath.Dir(script)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repoDeps = removeDuplicates(repoDeps)
|
||||||
|
builtPaths = removeDuplicates(builtPaths)
|
||||||
|
builtNames = removeDuplicates(builtNames)
|
||||||
|
return builtPaths, builtNames, repoDeps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Directories, vars *types.BuildVars) (err error) {
|
||||||
|
version, ok := dec.GetFunc("version")
|
||||||
|
if ok {
|
||||||
|
log.Info("Executing version()").Send()
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
err = version(
|
||||||
|
ctx,
|
||||||
|
interp.Dir(dirs.SrcDir),
|
||||||
|
interp.StdIO(os.Stdin, buf, os.Stderr),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newVer := strings.TrimSpace(buf.String())
|
||||||
|
err = setVersion(ctx, dec.Runner, newVer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
vars.Version = newVer
|
||||||
|
|
||||||
|
log.Info("Updating version").Str("new", newVer).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare, ok := dec.GetFunc("prepare")
|
||||||
|
if ok {
|
||||||
|
log.Info("Executing prepare()").Send()
|
||||||
|
|
||||||
|
err = prepare(ctx, interp.Dir(dirs.SrcDir))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
build, ok := dec.GetFunc("build")
|
||||||
|
if ok {
|
||||||
|
log.Info("Executing build()").Send()
|
||||||
|
|
||||||
|
err = build(ctx, interp.Dir(dirs.SrcDir))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packageFn, ok := dec.GetFunc("package")
|
||||||
|
if ok {
|
||||||
|
log.Info("Executing package()").Send()
|
||||||
|
|
||||||
|
err = packageFn(ctx, interp.Dir(dirs.SrcDir))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Fatal("The package() function is required").Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, deps []string) (*nfpm.Info, error) {
|
||||||
|
pkgInfo := &nfpm.Info{
|
||||||
|
Name: vars.Name,
|
||||||
|
Description: vars.Description,
|
||||||
|
Arch: cpu.Arch(),
|
||||||
|
Platform: "linux",
|
||||||
|
Version: vars.Version,
|
||||||
|
Release: strconv.Itoa(vars.Release),
|
||||||
|
Homepage: vars.Homepage,
|
||||||
|
License: strings.Join(vars.Licenses, ", "),
|
||||||
|
Maintainer: vars.Maintainer,
|
||||||
|
Overridables: nfpm.Overridables{
|
||||||
|
Conflicts: vars.Conflicts,
|
||||||
|
Replaces: vars.Replaces,
|
||||||
|
Provides: vars.Provides,
|
||||||
|
Depends: deps,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if vars.Epoch != 0 {
|
||||||
|
pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
setScripts(vars, pkgInfo, dirs.ScriptDir)
|
||||||
|
|
||||||
|
if slices.Contains(vars.Architectures, "all") {
|
||||||
|
pkgInfo.Arch = "all"
|
||||||
|
}
|
||||||
|
|
||||||
|
contents, err := buildContents(vars, dirs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pkgInfo.Overridables.Contents = contents
|
||||||
|
|
||||||
|
return pkgInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Content, error) {
|
||||||
|
contents := []*files.Content{}
|
||||||
|
err := filepath.Walk(dirs.PkgDir, func(path string, fi os.FileInfo, err error) error {
|
||||||
|
trimmed := strings.TrimPrefix(path, dirs.PkgDir)
|
||||||
|
|
||||||
|
if fi.IsDir() {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Readdirnames(1)
|
||||||
|
if err != io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
contents = append(contents, &files.Content{
|
||||||
|
Source: path,
|
||||||
|
Destination: trimmed,
|
||||||
|
Type: "dir",
|
||||||
|
FileInfo: &files.ContentFileInfo{
|
||||||
|
MTime: fi.ModTime(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
f.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.Mode()&os.ModeSymlink != 0 {
|
||||||
|
link, err := os.Readlink(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
link = strings.TrimPrefix(link, dirs.PkgDir)
|
||||||
|
|
||||||
|
contents = append(contents, &files.Content{
|
||||||
|
Source: link,
|
||||||
|
Destination: trimmed,
|
||||||
|
Type: "symlink",
|
||||||
|
FileInfo: &files.ContentFileInfo{
|
||||||
|
MTime: fi.ModTime(),
|
||||||
|
Mode: fi.Mode(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fileContent := &files.Content{
|
||||||
|
Source: path,
|
||||||
|
Destination: trimmed,
|
||||||
|
FileInfo: &files.ContentFileInfo{
|
||||||
|
MTime: fi.ModTime(),
|
||||||
|
Mode: fi.Mode(),
|
||||||
|
Size: fi.Size(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(vars.Backup, trimmed) {
|
||||||
|
fileContent.Type = "config|noreplace"
|
||||||
|
}
|
||||||
|
|
||||||
|
contents = append(contents, fileContent)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return contents, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeBuildDeps(buildDeps []string, opts types.BuildOpts) error {
|
||||||
|
if len(buildDeps) > 0 {
|
||||||
|
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove the build dependencies?", opts.Interactive, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if removeBuildDeps {
|
||||||
|
err = opts.Manager.Remove(
|
||||||
|
&manager.Opts{
|
||||||
|
AsRoot: true,
|
||||||
|
NoConfirm: true,
|
||||||
|
},
|
||||||
|
buildDeps...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkForBuiltPackage(mgr manager.Manager, vars *types.BuildVars, pkgFormat, baseDir string) (string, bool, error) {
|
||||||
|
filename, err := pkgFileName(vars, pkgFormat)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgPath := filepath.Join(baseDir, filename)
|
||||||
|
|
||||||
|
_, err = os.Stat(pkgPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgPath, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkgFileName(vars *types.BuildVars, pkgFormat string) (string, error) {
|
||||||
|
pkgInfo := &nfpm.Info{
|
||||||
|
Name: vars.Name,
|
||||||
|
Arch: cpu.Arch(),
|
||||||
|
Version: vars.Version,
|
||||||
|
Release: strconv.Itoa(vars.Release),
|
||||||
|
Epoch: strconv.FormatUint(uint64(vars.Epoch), 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
packager, err := nfpm.Get(pkgFormat)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return packager.ConventionalFileName(pkgInfo), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPkgFormat(mgr manager.Manager) string {
|
||||||
|
pkgFormat := mgr.Format()
|
||||||
|
if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
|
||||||
|
pkgFormat = format
|
||||||
|
}
|
||||||
|
return pkgFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string {
|
||||||
|
env := os.Environ()
|
||||||
|
|
||||||
|
env = append(
|
||||||
|
env,
|
||||||
|
"DISTRO_NAME="+info.Name,
|
||||||
|
"DISTRO_PRETTY_NAME="+info.PrettyName,
|
||||||
|
"DISTRO_ID="+info.ID,
|
||||||
|
"DISTRO_VERSION_ID="+info.VersionID,
|
||||||
|
"DISTRO_ID_LIKE="+strings.Join(info.Like, " "),
|
||||||
|
"ARCH="+cpu.Arch(),
|
||||||
|
"NCPU="+strconv.Itoa(runtime.NumCPU()),
|
||||||
|
)
|
||||||
|
|
||||||
|
if dirs.ScriptDir != "" {
|
||||||
|
env = append(env, "scriptdir="+dirs.ScriptDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dirs.PkgDir != "" {
|
||||||
|
env = append(env, "pkgdir="+dirs.PkgDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dirs.SrcDir != "" {
|
||||||
|
env = append(env, "srcdir="+dirs.SrcDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSources(ctx context.Context, srcdir string, bv *types.BuildVars) error {
|
||||||
|
if len(bv.Sources) != len(bv.Checksums) {
|
||||||
|
log.Fatal("The checksums array must be the same length as sources").Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, src := range bv.Sources {
|
||||||
|
opts := dl.Options{
|
||||||
|
Name: fmt.Sprintf("%s[%d]", bv.Name, i),
|
||||||
|
URL: src,
|
||||||
|
Destination: srcdir,
|
||||||
|
Progress: os.Stderr,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
|
||||||
|
algo, hashData, ok := strings.Cut(bv.Checksums[i], ":")
|
||||||
|
if ok {
|
||||||
|
checksum, err := hex.DecodeString(hashData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opts.Hash = checksum
|
||||||
|
opts.HashAlgorithm = algo
|
||||||
|
} else {
|
||||||
|
checksum, err := hex.DecodeString(bv.Checksums[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opts.Hash = checksum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := dl.Download(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) {
|
||||||
|
if vars.Scripts.PreInstall != "" {
|
||||||
|
info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vars.Scripts.PostInstall != "" {
|
||||||
|
info.Scripts.PostInstall = filepath.Join(scriptDir, vars.Scripts.PostInstall)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vars.Scripts.PreRemove != "" {
|
||||||
|
info.Scripts.PreRemove = filepath.Join(scriptDir, vars.Scripts.PreRemove)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vars.Scripts.PostRemove != "" {
|
||||||
|
info.Scripts.PostRemove = filepath.Join(scriptDir, vars.Scripts.PostRemove)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vars.Scripts.PreUpgrade != "" {
|
||||||
|
info.ArchLinux.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
|
||||||
|
info.APK.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vars.Scripts.PostUpgrade != "" {
|
||||||
|
info.ArchLinux.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
|
||||||
|
info.APK.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vars.Scripts.PreTrans != "" {
|
||||||
|
info.RPM.Scripts.PreTrans = filepath.Join(scriptDir, vars.Scripts.PreTrans)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vars.Scripts.PostTrans != "" {
|
||||||
|
info.RPM.Scripts.PostTrans = filepath.Join(scriptDir, vars.Scripts.PostTrans)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setVersion(ctx context.Context, r *interp.Runner, to string) error {
|
||||||
|
fl, err := syntax.NewParser().Parse(strings.NewReader("version='"+to+"'"), "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return r.Run(ctx, fl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterBuildDeps returns a map without any dependencies that are already installed
|
||||||
|
func filterBuildDeps(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
|
||||||
|
out := map[string][]db.Package{}
|
||||||
|
for name, pkgs := range found {
|
||||||
|
var inner []db.Package
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
if _, ok := installed[pkg.Name]; !ok {
|
||||||
|
addToFiltered := true
|
||||||
|
for _, provides := range pkg.Provides.Val {
|
||||||
|
if _, ok := installed[provides]; ok {
|
||||||
|
addToFiltered = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if addToFiltered {
|
||||||
|
inner = append(inner, pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(inner) > 0 {
|
||||||
|
out[name] = inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageNames(pkgs []db.Package) []string {
|
||||||
|
names := make([]string, len(pkgs))
|
||||||
|
for i, p := range pkgs {
|
||||||
|
names[i] = p.Name
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeDuplicates(slice []string) []string {
|
||||||
|
seen := map[string]struct{}{}
|
||||||
|
result := []string{}
|
||||||
|
|
||||||
|
for _, s := range slice {
|
||||||
|
if _, ok := seen[s]; !ok {
|
||||||
|
seen[s] = struct{}{}
|
||||||
|
result = append(result, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"go.elara.ws/logger/log"
|
||||||
|
"go.elara.ws/lure/internal/config"
|
||||||
|
"go.elara.ws/lure/internal/db"
|
||||||
|
"go.elara.ws/lure/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InstallPkgs installs non-LURE packages via the package manager, then builds and installs LURE
|
||||||
|
// packages
|
||||||
|
func InstallPkgs(ctx context.Context, lurePkgs []db.Package, nativePkgs []string, opts types.BuildOpts) {
|
||||||
|
if len(nativePkgs) > 0 {
|
||||||
|
err := opts.Manager.Install(nil, nativePkgs...)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error installing native packages").Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallScripts(ctx, GetScriptPaths(lurePkgs), opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScriptPaths generates a slice of script paths corresponding to the
|
||||||
|
// given packages
|
||||||
|
func GetScriptPaths(pkgs []db.Package) []string {
|
||||||
|
var scripts []string
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
scriptPath := filepath.Join(config.GetPaths().RepoDir, pkg.Repository, pkg.Name, "lure.sh")
|
||||||
|
scripts = append(scripts, scriptPath)
|
||||||
|
}
|
||||||
|
return scripts
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallScripts builds and installs LURE build scripts
|
||||||
|
func InstallScripts(ctx context.Context, scripts []string, opts types.BuildOpts) {
|
||||||
|
for _, script := range scripts {
|
||||||
|
opts.Script = script
|
||||||
|
builtPkgs, _, err := BuildPackage(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error building package").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = opts.Manager.InstallLocal(nil, builtPkgs...)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error installing package").Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,16 +26,16 @@ import (
|
||||||
"go.elara.ws/lure/internal/config"
|
"go.elara.ws/lure/internal/config"
|
||||||
"go.elara.ws/lure/internal/db"
|
"go.elara.ws/lure/internal/db"
|
||||||
"go.elara.ws/lure/internal/pager"
|
"go.elara.ws/lure/internal/pager"
|
||||||
"go.elara.ws/translate"
|
"go.elara.ws/lure/internal/translations"
|
||||||
)
|
)
|
||||||
|
|
||||||
// YesNoPrompt asks the user a yes or no question, using def as the default answer
|
// YesNoPrompt asks the user a yes or no question, using def as the default answer
|
||||||
func YesNoPrompt(msg string, interactive, def bool, t translate.Translator) (bool, error) {
|
func YesNoPrompt(msg string, interactive, def bool) (bool, error) {
|
||||||
if interactive {
|
if interactive {
|
||||||
var answer bool
|
var answer bool
|
||||||
err := survey.AskOne(
|
err := survey.AskOne(
|
||||||
&survey.Confirm{
|
&survey.Confirm{
|
||||||
Message: t.TranslateTo(msg, config.Language),
|
Message: translations.Translator().TranslateTo(msg, config.Language()),
|
||||||
Default: def,
|
Default: def,
|
||||||
},
|
},
|
||||||
&answer,
|
&answer,
|
||||||
|
@ -49,12 +49,13 @@ func YesNoPrompt(msg string, interactive, def bool, t translate.Translator) (boo
|
||||||
// PromptViewScript asks the user if they'd like to see a script,
|
// PromptViewScript asks the user if they'd like to see a script,
|
||||||
// shows it if they answer yes, then asks if they'd still like to
|
// shows it if they answer yes, then asks if they'd still like to
|
||||||
// continue, and exits if they answer no.
|
// continue, and exits if they answer no.
|
||||||
func PromptViewScript(script, name, style string, interactive bool, t translate.Translator) error {
|
func PromptViewScript(script, name, style string, interactive bool) error {
|
||||||
if !interactive {
|
if !interactive {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
view, err := YesNoPrompt(t.TranslateTo("Would you like to view the build script for", config.Language)+" "+name, interactive, false, t)
|
scriptPrompt := translations.Translator().TranslateTo("Would you like to view the build script for", config.Language()) + " " + name
|
||||||
|
view, err := YesNoPrompt(scriptPrompt, interactive, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -65,13 +66,13 @@ func PromptViewScript(script, name, style string, interactive bool, t translate.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cont, err := YesNoPrompt("Would you still like to continue?", interactive, false, t)
|
cont, err := YesNoPrompt("Would you still like to continue?", interactive, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cont {
|
if !cont {
|
||||||
log.Fatal(t.TranslateTo("User chose not to continue after reading script", config.Language)).Send()
|
log.Fatal(translations.Translator().TranslateTo("User chose not to continue after reading script", config.Language())).Send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,11 +99,11 @@ func ShowScript(path, name, style string) error {
|
||||||
|
|
||||||
// FlattenPkgs attempts to flatten the a map of slices of packages into a single slice
|
// FlattenPkgs attempts to flatten the a map of slices of packages into a single slice
|
||||||
// of packages by prompting the user if multiple packages match.
|
// of packages by prompting the user if multiple packages match.
|
||||||
func FlattenPkgs(found map[string][]db.Package, verb string, interactive bool, t translate.Translator) []db.Package {
|
func FlattenPkgs(found map[string][]db.Package, verb string, interactive bool) []db.Package {
|
||||||
var outPkgs []db.Package
|
var outPkgs []db.Package
|
||||||
for _, pkgs := range found {
|
for _, pkgs := range found {
|
||||||
if len(pkgs) > 1 && interactive {
|
if len(pkgs) > 1 && interactive {
|
||||||
choices, err := PkgPrompt(pkgs, verb, interactive, t)
|
choices, err := PkgPrompt(pkgs, verb, interactive)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error prompting for choice of package").Send()
|
log.Fatal("Error prompting for choice of package").Send()
|
||||||
}
|
}
|
||||||
|
@ -116,7 +117,7 @@ func FlattenPkgs(found map[string][]db.Package, verb string, interactive bool, t
|
||||||
|
|
||||||
// PkgPrompt asks the user to choose between multiple packages.
|
// PkgPrompt asks the user to choose between multiple packages.
|
||||||
// The user may choose multiple packages.
|
// The user may choose multiple packages.
|
||||||
func PkgPrompt(options []db.Package, verb string, interactive bool, t translate.Translator) ([]db.Package, error) {
|
func PkgPrompt(options []db.Package, verb string, interactive bool) ([]db.Package, error) {
|
||||||
if !interactive {
|
if !interactive {
|
||||||
return []db.Package{options[0]}, nil
|
return []db.Package{options[0]}, nil
|
||||||
}
|
}
|
||||||
|
@ -128,7 +129,7 @@ func PkgPrompt(options []db.Package, verb string, interactive bool, t translate.
|
||||||
|
|
||||||
prompt := &survey.MultiSelect{
|
prompt := &survey.MultiSelect{
|
||||||
Options: names,
|
Options: names,
|
||||||
Message: t.TranslateTo("Choose which package(s) to "+verb, config.Language),
|
Message: translations.Translator().TranslateTo("Choose which package(s) to "+verb, config.Language()),
|
||||||
}
|
}
|
||||||
|
|
||||||
var choices []int
|
var choices []int
|
||||||
|
|
|
@ -22,10 +22,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/pelletier/go-toml/v2"
|
"github.com/pelletier/go-toml/v2"
|
||||||
|
"go.elara.ws/logger/log"
|
||||||
"go.elara.ws/lure/internal/types"
|
"go.elara.ws/lure/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultConfig = types.Config{
|
var defaultConfig = &types.Config{
|
||||||
RootCmd: "sudo",
|
RootCmd: "sudo",
|
||||||
PagerStyle: "native",
|
PagerStyle: "native",
|
||||||
IgnorePkgUpdates: []string{},
|
IgnorePkgUpdates: []string{},
|
||||||
|
@ -37,18 +38,30 @@ var defaultConfig = types.Config{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes the config file into the given
|
var config *types.Config
|
||||||
// pointer
|
|
||||||
func Decode(cfg *types.Config) error {
|
|
||||||
cfgFl, err := os.Open(ConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer cfgFl.Close()
|
|
||||||
|
|
||||||
// Write defaults to pointer in case some values are not set in the config
|
func Config() *types.Config {
|
||||||
*cfg = defaultConfig
|
if config == nil {
|
||||||
// Set repos to nil so as to avoid a duplicate default
|
cfgFl, err := os.Open(GetPaths().ConfigPath)
|
||||||
cfg.Repos = nil
|
if err != nil {
|
||||||
return toml.NewDecoder(cfgFl).Decode(cfg)
|
log.Warn("Error opening config file, using defaults").Err(err).Send()
|
||||||
|
return defaultConfig
|
||||||
|
}
|
||||||
|
defer cfgFl.Close()
|
||||||
|
|
||||||
|
// Copy the default configuration into config
|
||||||
|
defCopy := *defaultConfig
|
||||||
|
config = &defCopy
|
||||||
|
config.Repos = nil
|
||||||
|
|
||||||
|
err = toml.NewDecoder(cfgFl).Decode(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Error decoding config file, using defaults").Err(err).Send()
|
||||||
|
// Set config back to nil so that we try again next time
|
||||||
|
config = nil
|
||||||
|
return defaultConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,69 +26,69 @@ import (
|
||||||
"go.elara.ws/logger/log"
|
"go.elara.ws/logger/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type Paths struct {
|
||||||
ConfigDir string
|
ConfigDir string
|
||||||
ConfigPath string
|
ConfigPath string
|
||||||
CacheDir string
|
CacheDir string
|
||||||
RepoDir string
|
RepoDir string
|
||||||
PkgsDir string
|
PkgsDir string
|
||||||
DBPath string
|
DBPath string
|
||||||
)
|
}
|
||||||
|
|
||||||
// DBPresent is true if the database
|
var paths *Paths
|
||||||
// was present when LURE was started
|
|
||||||
var DBPresent bool
|
func GetPaths() *Paths {
|
||||||
|
if paths == nil {
|
||||||
func init() {
|
paths = &Paths{}
|
||||||
cfgDir, err := os.UserConfigDir()
|
|
||||||
if err != nil {
|
cfgDir, err := os.UserConfigDir()
|
||||||
log.Fatal("Unable to detect user config directory").Err(err).Send()
|
if err != nil {
|
||||||
}
|
log.Fatal("Unable to detect user config directory").Err(err).Send()
|
||||||
|
}
|
||||||
ConfigDir = filepath.Join(cfgDir, "lure")
|
|
||||||
|
paths.ConfigDir = filepath.Join(cfgDir, "lure")
|
||||||
err = os.MkdirAll(ConfigDir, 0o755)
|
|
||||||
if err != nil {
|
err = os.MkdirAll(paths.ConfigDir, 0o755)
|
||||||
log.Fatal("Unable to create LURE config directory").Err(err).Send()
|
if err != nil {
|
||||||
}
|
log.Fatal("Unable to create LURE config directory").Err(err).Send()
|
||||||
|
}
|
||||||
ConfigPath = filepath.Join(ConfigDir, "lure.toml")
|
|
||||||
|
paths.ConfigPath = filepath.Join(paths.ConfigDir, "lure.toml")
|
||||||
if _, err := os.Stat(ConfigPath); err != nil {
|
|
||||||
cfgFl, err := os.Create(ConfigPath)
|
if _, err := os.Stat(paths.ConfigPath); err != nil {
|
||||||
if err != nil {
|
cfgFl, err := os.Create(paths.ConfigPath)
|
||||||
log.Fatal("Unable to create LURE config file").Err(err).Send()
|
if err != nil {
|
||||||
}
|
log.Fatal("Unable to create LURE config file").Err(err).Send()
|
||||||
|
}
|
||||||
err = toml.NewEncoder(cfgFl).Encode(&defaultConfig)
|
|
||||||
if err != nil {
|
err = toml.NewEncoder(cfgFl).Encode(&defaultConfig)
|
||||||
log.Fatal("Error encoding default configuration").Err(err).Send()
|
if err != nil {
|
||||||
}
|
log.Fatal("Error encoding default configuration").Err(err).Send()
|
||||||
|
}
|
||||||
cfgFl.Close()
|
|
||||||
}
|
cfgFl.Close()
|
||||||
|
}
|
||||||
cacheDir, err := os.UserCacheDir()
|
|
||||||
if err != nil {
|
cacheDir, err := os.UserCacheDir()
|
||||||
log.Fatal("Unable to detect cache directory").Err(err).Send()
|
if err != nil {
|
||||||
}
|
log.Fatal("Unable to detect cache directory").Err(err).Send()
|
||||||
|
}
|
||||||
CacheDir = filepath.Join(cacheDir, "lure")
|
|
||||||
RepoDir = filepath.Join(CacheDir, "repo")
|
paths.CacheDir = filepath.Join(cacheDir, "lure")
|
||||||
PkgsDir = filepath.Join(CacheDir, "pkgs")
|
paths.RepoDir = filepath.Join(paths.CacheDir, "repo")
|
||||||
|
paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs")
|
||||||
err = os.MkdirAll(RepoDir, 0o755)
|
|
||||||
if err != nil {
|
err = os.MkdirAll(paths.RepoDir, 0o755)
|
||||||
log.Fatal("Unable to create repo cache directory").Err(err).Send()
|
if err != nil {
|
||||||
}
|
log.Fatal("Unable to create repo cache directory").Err(err).Send()
|
||||||
|
}
|
||||||
err = os.MkdirAll(PkgsDir, 0o755)
|
|
||||||
if err != nil {
|
err = os.MkdirAll(paths.PkgsDir, 0o755)
|
||||||
log.Fatal("Unable to create package cache directory").Err(err).Send()
|
if err != nil {
|
||||||
}
|
log.Fatal("Unable to create package cache directory").Err(err).Send()
|
||||||
|
}
|
||||||
DBPath = filepath.Join(CacheDir, "db")
|
|
||||||
|
paths.DBPath = filepath.Join(paths.CacheDir, "db")
|
||||||
fi, err := os.Stat(DBPath)
|
}
|
||||||
DBPresent = err == nil && !fi.IsDir()
|
return paths
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,16 +26,23 @@ import (
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Language language.Tag
|
var (
|
||||||
|
lang language.Tag
|
||||||
|
langSet bool
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func Language() language.Tag {
|
||||||
lang := SystemLang()
|
if !langSet {
|
||||||
tag, err := language.Parse(lang)
|
syslang := SystemLang()
|
||||||
if err != nil {
|
tag, err := language.Parse(syslang)
|
||||||
log.Fatal("Error parsing system language").Err(err).Send()
|
if err != nil {
|
||||||
|
log.Fatal("Error parsing system language").Err(err).Send()
|
||||||
|
}
|
||||||
|
base, _ := tag.Base()
|
||||||
|
lang = language.Make(base.String())
|
||||||
|
langSet = true
|
||||||
}
|
}
|
||||||
base, _ := tag.Base()
|
return lang
|
||||||
Language = language.Make(base.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SystemLang() string {
|
func SystemLang() string {
|
||||||
|
|
|
@ -20,5 +20,7 @@ package config
|
||||||
|
|
||||||
import _ "embed"
|
import _ "embed"
|
||||||
|
|
||||||
|
//go:generate ../../scripts/gen-version.sh
|
||||||
|
|
||||||
//go:embed version.txt
|
//go:embed version.txt
|
||||||
var Version string
|
var Version string
|
||||||
|
|
|
@ -21,14 +21,15 @@ package cpu
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/sys/cpu"
|
"golang.org/x/sys/cpu"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ARMVariant checks which variant of ARM lure is running
|
// armVariant checks which variant of ARM lure is running
|
||||||
// on, by using the same detection method as Go itself
|
// on, by using the same detection method as Go itself
|
||||||
func ARMVariant() string {
|
func armVariant() string {
|
||||||
armEnv := os.Getenv("LURE_ARM_VARIANT")
|
armEnv := os.Getenv("LURE_ARM_VARIANT")
|
||||||
// ensure value has "arm" prefix, such as arm5 or arm6
|
// ensure value has "arm" prefix, such as arm5 or arm6
|
||||||
if strings.HasPrefix(armEnv, "arm") {
|
if strings.HasPrefix(armEnv, "arm") {
|
||||||
|
@ -44,34 +45,6 @@ func ARMVariant() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompatibleARM returns all the compatible ARM variants given the system architecture
|
|
||||||
func CompatibleARM(variant string) []string {
|
|
||||||
switch variant {
|
|
||||||
case "arm7", "arm":
|
|
||||||
return []string{"arm7", "arm6", "arm5"}
|
|
||||||
case "arm6":
|
|
||||||
return []string{"arm6", "arm5"}
|
|
||||||
case "arm5":
|
|
||||||
return []string{"arm5"}
|
|
||||||
default:
|
|
||||||
return []string{variant}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompatibleARMReverse returns all the compatible ARM variants given the package's architecture
|
|
||||||
func CompatibleARMReverse(variant string) []string {
|
|
||||||
switch variant {
|
|
||||||
case "arm7":
|
|
||||||
return []string{"arm7"}
|
|
||||||
case "arm6":
|
|
||||||
return []string{"arm6", "arm7"}
|
|
||||||
case "arm5", "arm":
|
|
||||||
return []string{"arm5", "arm6", "arm7"}
|
|
||||||
default:
|
|
||||||
return []string{variant}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arch returns the canonical CPU architecture of the system
|
// Arch returns the canonical CPU architecture of the system
|
||||||
func Arch() string {
|
func Arch() string {
|
||||||
arch := os.Getenv("LURE_ARCH")
|
arch := os.Getenv("LURE_ARCH")
|
||||||
|
@ -79,20 +52,65 @@ func Arch() string {
|
||||||
arch = runtime.GOARCH
|
arch = runtime.GOARCH
|
||||||
}
|
}
|
||||||
if arch == "arm" {
|
if arch == "arm" {
|
||||||
arch = ARMVariant()
|
arch = armVariant()
|
||||||
}
|
}
|
||||||
return arch
|
return arch
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arches returns all the architectures the system is compatible with
|
func IsCompatibleWith(target string, list []string) bool {
|
||||||
func Arches() []string {
|
if target == "all" {
|
||||||
arch := os.Getenv("LURE_ARCH")
|
return true
|
||||||
if arch == "" {
|
|
||||||
arch = runtime.GOARCH
|
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(arch, "arm") {
|
|
||||||
return append(CompatibleARM(arch), "arm")
|
for _, arch := range list {
|
||||||
} else {
|
if strings.HasPrefix(target, "arm") && strings.HasPrefix(arch, "arm") {
|
||||||
return []string{Arch()}
|
targetVer, err := getARMVersion(target)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
archVer, err := getARMVersion(arch)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if targetVer >= archVer {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if target == arch {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompatibleArches(arch string) ([]string, error) {
|
||||||
|
if strings.HasPrefix(arch, "arm") {
|
||||||
|
ver, err := getARMVersion(arch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ver > 5 {
|
||||||
|
var out []string
|
||||||
|
for i := ver; i >= 5; i-- {
|
||||||
|
out = append(out, "arm"+strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{arch}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getARMVersion(arch string) (int, error) {
|
||||||
|
// Extract the version number from ARM architecture
|
||||||
|
version := strings.TrimPrefix(arch, "arm")
|
||||||
|
if version == "" {
|
||||||
|
return 5, nil // Default to arm5 if version is not specified
|
||||||
|
}
|
||||||
|
return strconv.Atoi(version)
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,40 +62,52 @@ type version struct {
|
||||||
Version int `db:"version"`
|
Version int `db:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(dsn string) (*sqlx.DB, error) {
|
var (
|
||||||
if dsn != ":memory:" {
|
conn *sqlx.DB
|
||||||
fi, err := os.Stat(config.DBPath)
|
closed = true
|
||||||
if err == nil {
|
)
|
||||||
// TODO: This should be removed by the first stable release.
|
|
||||||
if fi.IsDir() {
|
|
||||||
log.Warn("Your database is using the old database engine; rebuilding").Send()
|
|
||||||
|
|
||||||
err = os.RemoveAll(config.DBPath)
|
func DB() *sqlx.DB {
|
||||||
if err != nil {
|
if conn != nil && !closed {
|
||||||
log.Fatal("Error removing old database").Err(err).Send()
|
return conn
|
||||||
}
|
|
||||||
config.DBPresent = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
db, err := Open(config.GetPaths().DBPath)
|
||||||
db, err := sqlx.Open("sqlite", dsn)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error opening database").Err(err).Send()
|
log.Fatal("Error opening database").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
conn = db
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
err = Init(db, dsn)
|
func Open(dsn string) (*sqlx.DB, error) {
|
||||||
|
db, err := sqlx.Open("sqlite", dsn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error initializing database").Err(err).Send()
|
return nil, err
|
||||||
|
}
|
||||||
|
conn = db
|
||||||
|
closed = false
|
||||||
|
|
||||||
|
err = initDB(dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Close() error {
|
||||||
|
closed = true
|
||||||
|
if conn != nil {
|
||||||
|
return conn.Close()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Init initializes the database
|
// Init initializes the database
|
||||||
func Init(db *sqlx.DB, dsn string) error {
|
func initDB(dsn string) error {
|
||||||
*db = *db.Unsafe()
|
conn = conn.Unsafe()
|
||||||
_, err := db.Exec(`
|
_, err := conn.Exec(`
|
||||||
CREATE TABLE IF NOT EXISTS pkgs (
|
CREATE TABLE IF NOT EXISTS pkgs (
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
repository TEXT NOT NULL,
|
repository TEXT NOT NULL,
|
||||||
|
@ -123,49 +135,57 @@ func Init(db *sqlx.DB, dsn string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ver, ok := GetVersion(db)
|
ver, ok := GetVersion()
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warn("Database version does not exist. Run lure fix if something isn't working.").Send()
|
log.Warn("Database version does not exist. Run lure fix if something isn't working.").Send()
|
||||||
return addVersion(db, CurrentVersion)
|
return addVersion(CurrentVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ver != CurrentVersion {
|
if ver != CurrentVersion {
|
||||||
log.Warn("Database version mismatch; rebuilding").Int("version", ver).Int("expected", CurrentVersion).Send()
|
log.Warn("Database version mismatch; rebuilding").Int("version", ver).Int("expected", CurrentVersion).Send()
|
||||||
|
|
||||||
db.Close()
|
conn.Close()
|
||||||
err = os.Remove(config.DBPath)
|
err = os.Remove(config.GetPaths().DBPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
config.DBPresent = false
|
|
||||||
|
|
||||||
tdb, err := Open(dsn)
|
tdb, err := Open(dsn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*db = *tdb
|
conn = tdb
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetVersion(db *sqlx.DB) (int, bool) {
|
func IsEmpty() bool {
|
||||||
|
var count int
|
||||||
|
err := DB().Get(&count, "SELECT count(1) FROM pkgs;")
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return count == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVersion() (int, bool) {
|
||||||
var ver version
|
var ver version
|
||||||
err := db.Get(&ver, "SELECT * FROM lure_db_version LIMIT 1;")
|
err := DB().Get(&ver, "SELECT * FROM lure_db_version LIMIT 1;")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
return ver.Version, true
|
return ver.Version, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func addVersion(db *sqlx.DB, ver int) error {
|
func addVersion(ver int) error {
|
||||||
_, err := db.Exec(`INSERT INTO lure_db_version(version) VALUES (?);`, ver)
|
_, err := DB().Exec(`INSERT INTO lure_db_version(version) VALUES (?);`, ver)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertPackage adds a package to the database
|
// InsertPackage adds a package to the database
|
||||||
func InsertPackage(db *sqlx.DB, pkg Package) error {
|
func InsertPackage(pkg Package) error {
|
||||||
_, err := db.NamedExec(`
|
_, err := DB().NamedExec(`
|
||||||
INSERT OR REPLACE INTO pkgs (
|
INSERT OR REPLACE INTO pkgs (
|
||||||
name,
|
name,
|
||||||
repository,
|
repository,
|
||||||
|
@ -204,8 +224,8 @@ func InsertPackage(db *sqlx.DB, pkg Package) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPkgs returns a result containing packages that match the where conditions
|
// GetPkgs returns a result containing packages that match the where conditions
|
||||||
func GetPkgs(db *sqlx.DB, where string, args ...any) (*sqlx.Rows, error) {
|
func GetPkgs(where string, args ...any) (*sqlx.Rows, error) {
|
||||||
stream, err := db.Queryx("SELECT * FROM pkgs WHERE "+where, args...)
|
stream, err := DB().Queryx("SELECT * FROM pkgs WHERE "+where, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -213,15 +233,15 @@ func GetPkgs(db *sqlx.DB, where string, args ...any) (*sqlx.Rows, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPkg returns a single package that match the where conditions
|
// GetPkg returns a single package that match the where conditions
|
||||||
func GetPkg(db *sqlx.DB, where string, args ...any) (*Package, error) {
|
func GetPkg(where string, args ...any) (*Package, error) {
|
||||||
out := &Package{}
|
out := &Package{}
|
||||||
err := db.Get(out, "SELECT * FROM pkgs WHERE "+where+" LIMIT 1", args...)
|
err := DB().Get(out, "SELECT * FROM pkgs WHERE "+where+" LIMIT 1", args...)
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeletePkgs deletes all packages matching the where conditions
|
// DeletePkgs deletes all packages matching the where conditions
|
||||||
func DeletePkgs(db *sqlx.DB, where string, args ...any) error {
|
func DeletePkgs(where string, args ...any) error {
|
||||||
_, err := db.Exec("DELETE FROM pkgs WHERE "+where, args...)
|
_, err := DB().Exec("DELETE FROM pkgs WHERE "+where, args...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,11 +37,11 @@ var testPkg = db.Package{
|
||||||
"ru": "Проверочный пакет",
|
"ru": "Проверочный пакет",
|
||||||
}),
|
}),
|
||||||
Homepage: db.NewJSON(map[string]string{
|
Homepage: db.NewJSON(map[string]string{
|
||||||
"en": "https://lure.arsenm.dev",
|
"en": "https://lure.elara.ws/",
|
||||||
}),
|
}),
|
||||||
Maintainer: db.NewJSON(map[string]string{
|
Maintainer: db.NewJSON(map[string]string{
|
||||||
"en": "Arsen Musayelyan <arsen@arsenm.dev>",
|
"en": "Elara Musayelyan <elara@elara.ws>",
|
||||||
"ru": "Арсен Мусаелян <arsen@arsenm.dev>",
|
"ru": "Элара Мусаелян <arsen@arsenm.dev>",
|
||||||
}),
|
}),
|
||||||
Architectures: db.NewJSON([]string{"arm64", "amd64"}),
|
Architectures: db.NewJSON([]string{"arm64", "amd64"}),
|
||||||
Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}),
|
Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}),
|
||||||
|
@ -59,23 +59,18 @@ var testPkg = db.Package{
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
_, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
err = db.Init(gdb, ":memory:")
|
_, err = db.DB().Exec("SELECT * FROM pkgs")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = gdb.Exec("SELECT * FROM pkgs")
|
ver, ok := db.GetVersion()
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ver, ok := db.GetVersion(gdb)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("Expected version to be present")
|
t.Errorf("Expected version to be present")
|
||||||
} else if ver != db.CurrentVersion {
|
} else if ver != db.CurrentVersion {
|
||||||
|
@ -84,19 +79,19 @@ func TestInit(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertPackage(t *testing.T) {
|
func TestInsertPackage(t *testing.T) {
|
||||||
gdb, err := db.Open(":memory:")
|
_, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, testPkg)
|
err = db.InsertPackage(testPkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dbPkg := db.Package{}
|
dbPkg := db.Package{}
|
||||||
err = sqlx.Get(gdb, &dbPkg, "SELECT * FROM pkgs WHERE name = 'test' AND repository = 'default'")
|
err = sqlx.Get(db.DB(), &dbPkg, "SELECT * FROM pkgs WHERE name = 'test' AND repository = 'default'")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
@ -107,28 +102,28 @@ func TestInsertPackage(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetPkgs(t *testing.T) {
|
func TestGetPkgs(t *testing.T) {
|
||||||
gdb, err := db.Open(":memory:")
|
_, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
x1 := testPkg
|
x1 := testPkg
|
||||||
x1.Name = "x1"
|
x1.Name = "x1"
|
||||||
x2 := testPkg
|
x2 := testPkg
|
||||||
x2.Name = "x2"
|
x2.Name = "x2"
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, x1)
|
err = db.InsertPackage(x1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, x2)
|
err = db.InsertPackage(x2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := db.GetPkgs(gdb, "name LIKE 'x%'")
|
result, err := db.GetPkgs("name LIKE 'x%'")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
@ -147,28 +142,28 @@ func TestGetPkgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetPkg(t *testing.T) {
|
func TestGetPkg(t *testing.T) {
|
||||||
gdb, err := db.Open(":memory:")
|
_, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
x1 := testPkg
|
x1 := testPkg
|
||||||
x1.Name = "x1"
|
x1.Name = "x1"
|
||||||
x2 := testPkg
|
x2 := testPkg
|
||||||
x2.Name = "x2"
|
x2.Name = "x2"
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, x1)
|
err = db.InsertPackage(x1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, x2)
|
err = db.InsertPackage(x2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg, err := db.GetPkg(gdb, "name LIKE 'x%' ORDER BY name")
|
pkg, err := db.GetPkg("name LIKE 'x%' ORDER BY name")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
@ -183,34 +178,34 @@ func TestGetPkg(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeletePkgs(t *testing.T) {
|
func TestDeletePkgs(t *testing.T) {
|
||||||
gdb, err := db.Open(":memory:")
|
_, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
x1 := testPkg
|
x1 := testPkg
|
||||||
x1.Name = "x1"
|
x1.Name = "x1"
|
||||||
x2 := testPkg
|
x2 := testPkg
|
||||||
x2.Name = "x2"
|
x2.Name = "x2"
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, x1)
|
err = db.InsertPackage(x1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, x2)
|
err = db.InsertPackage(x2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.DeletePkgs(gdb, "name = 'x1'")
|
err = db.DeletePkgs("name = 'x1'")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbPkg db.Package
|
var dbPkg db.Package
|
||||||
err = gdb.Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;")
|
err = db.DB().Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
@ -221,11 +216,11 @@ func TestDeletePkgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJsonArrayContains(t *testing.T) {
|
func TestJsonArrayContains(t *testing.T) {
|
||||||
gdb, err := db.Open(":memory:")
|
_, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
x1 := testPkg
|
x1 := testPkg
|
||||||
x1.Name = "x1"
|
x1.Name = "x1"
|
||||||
|
@ -233,18 +228,18 @@ func TestJsonArrayContains(t *testing.T) {
|
||||||
x2.Name = "x2"
|
x2.Name = "x2"
|
||||||
x2.Provides.Val = append(x2.Provides.Val, "x")
|
x2.Provides.Val = append(x2.Provides.Val, "x")
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, x1)
|
err = db.InsertPackage(x1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, x2)
|
err = db.InsertPackage(x2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbPkg db.Package
|
var dbPkg db.Package
|
||||||
err = gdb.Get(&dbPkg, "SELECT * FROM pkgs WHERE json_array_contains(provides, 'x');")
|
err = db.DB().Get(&dbPkg, "SELECT * FROM pkgs WHERE json_array_contains(provides, 'x');")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,10 @@ import (
|
||||||
"go.elara.ws/lure/internal/config"
|
"go.elara.ws/lure/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BasePath stores the base path to the download cache
|
// BasePath returns the base path of the download cache
|
||||||
var BasePath = filepath.Join(config.CacheDir, "dl")
|
func BasePath() string {
|
||||||
|
return filepath.Join(config.GetPaths().RepoDir, "dl")
|
||||||
|
}
|
||||||
|
|
||||||
// New creates a new directory with the given ID in the cache.
|
// New creates a new directory with the given ID in the cache.
|
||||||
// If a directory with the same ID already exists,
|
// If a directory with the same ID already exists,
|
||||||
|
@ -39,7 +41,7 @@ func New(id string) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
itemPath := filepath.Join(BasePath, h)
|
itemPath := filepath.Join(BasePath(), h)
|
||||||
|
|
||||||
fi, err := os.Stat(itemPath)
|
fi, err := os.Stat(itemPath)
|
||||||
if err == nil || (fi != nil && !fi.IsDir()) {
|
if err == nil || (fi != nil && !fi.IsDir()) {
|
||||||
|
@ -67,7 +69,7 @@ func Get(id string) (string, bool) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
itemPath := filepath.Join(BasePath, h)
|
itemPath := filepath.Join(BasePath(), h)
|
||||||
|
|
||||||
_, err = os.Stat(itemPath)
|
_, err = os.Stat(itemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"go.elara.ws/lure/internal/config"
|
||||||
"go.elara.ws/lure/internal/dlcache"
|
"go.elara.ws/lure/internal/dlcache"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ func init() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
dlcache.BasePath = dir
|
config.GetPaths().RepoDir = dir
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
|
@ -44,7 +45,7 @@ func TestNew(t *testing.T) {
|
||||||
t.Errorf("Expected no error, got %s", err)
|
t.Errorf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
exp := filepath.Join(dlcache.BasePath, sha1sum(id))
|
exp := filepath.Join(dlcache.BasePath(), sha1sum(id))
|
||||||
if dir != exp {
|
if dir != exp {
|
||||||
t.Errorf("Expected %s, got %s", exp, dir)
|
t.Errorf("Expected %s, got %s", exp, dir)
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,10 @@ func Resolve(info *distro.OSRelease, opts *Opts) ([]string, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
architectures := cpu.Arches()
|
architectures, err := cpu.CompatibleArches(cpu.Arch())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
distros := []string{info.ID}
|
distros := []string{info.ID}
|
||||||
if opts.LikeDistros {
|
if opts.LikeDistros {
|
||||||
|
@ -66,47 +69,38 @@ func Resolve(info *distro.OSRelease, opts *Opts) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var out []string
|
var out []string
|
||||||
for _, arch := range architectures {
|
for _, lang := range langs {
|
||||||
for _, distro := range distros {
|
for _, distro := range distros {
|
||||||
if opts.Name == "" {
|
for _, arch := range architectures {
|
||||||
out = append(
|
out = append(out, opts.Name+"_"+arch+"_"+distro+"_"+lang)
|
||||||
out,
|
|
||||||
arch+"_"+distro,
|
|
||||||
distro,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
out = append(
|
|
||||||
out,
|
|
||||||
opts.Name+"_"+arch+"_"+distro,
|
|
||||||
opts.Name+"_"+distro,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out = append(out, opts.Name+"_"+distro+"_"+lang)
|
||||||
}
|
}
|
||||||
if opts.Name == "" {
|
|
||||||
out = append(out, arch)
|
for _, arch := range architectures {
|
||||||
} else {
|
out = append(out, opts.Name+"_"+arch+"_"+lang)
|
||||||
out = append(out, opts.Name+"_"+arch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out = append(out, opts.Name+"_"+lang)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, distro := range distros {
|
||||||
|
for _, arch := range architectures {
|
||||||
|
out = append(out, opts.Name+"_"+arch+"_"+distro)
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, opts.Name+"_"+distro)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arch := range architectures {
|
||||||
|
out = append(out, opts.Name+"_"+arch)
|
||||||
|
}
|
||||||
|
|
||||||
out = append(out, opts.Name)
|
out = append(out, opts.Name)
|
||||||
|
|
||||||
for index, item := range out {
|
for index, item := range out {
|
||||||
out[index] = strings.ReplaceAll(item, "-", "_")
|
out[index] = strings.TrimPrefix(strings.ReplaceAll(item, "-", "_"), "_")
|
||||||
}
|
|
||||||
|
|
||||||
if len(langs) > 0 {
|
|
||||||
tmp := out
|
|
||||||
out = make([]string, 0, len(tmp)+(len(tmp)*len(langs)))
|
|
||||||
for _, lang := range langs {
|
|
||||||
for _, val := range tmp {
|
|
||||||
if val == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
out = append(out, val+"_"+lang)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out = append(out, tmp...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return out, nil
|
return out, nil
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package overrides_test
|
package overrides_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -46,6 +47,7 @@ func TestResolve(t *testing.T) {
|
||||||
"amd64_fedora_en",
|
"amd64_fedora_en",
|
||||||
"fedora_en",
|
"fedora_en",
|
||||||
"amd64_en",
|
"amd64_en",
|
||||||
|
"en",
|
||||||
"amd64_centos",
|
"amd64_centos",
|
||||||
"centos",
|
"centos",
|
||||||
"amd64_rhel",
|
"amd64_rhel",
|
||||||
|
@ -87,6 +89,43 @@ func TestResolveName(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveArch(t *testing.T) {
|
||||||
|
os.Setenv("LURE_ARCH", "arm7")
|
||||||
|
defer os.Setenv("LURE_ARCH", "")
|
||||||
|
|
||||||
|
names, err := overrides.Resolve(info, &overrides.Opts{
|
||||||
|
Name: "deps",
|
||||||
|
Overrides: true,
|
||||||
|
LikeDistros: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []string{
|
||||||
|
"deps_arm7_centos",
|
||||||
|
"deps_arm6_centos",
|
||||||
|
"deps_arm5_centos",
|
||||||
|
"deps_centos",
|
||||||
|
"deps_arm7_rhel",
|
||||||
|
"deps_arm6_rhel",
|
||||||
|
"deps_arm5_rhel",
|
||||||
|
"deps_rhel",
|
||||||
|
"deps_arm7_fedora",
|
||||||
|
"deps_arm6_fedora",
|
||||||
|
"deps_arm5_fedora",
|
||||||
|
"deps_fedora",
|
||||||
|
"deps_arm7",
|
||||||
|
"deps_arm6",
|
||||||
|
"deps_arm5",
|
||||||
|
"deps",
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(names, expected) {
|
||||||
|
t.Errorf("expected %v, got %v", expected, names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestResolveNoLikeDistros(t *testing.T) {
|
func TestResolveNoLikeDistros(t *testing.T) {
|
||||||
names, err := overrides.Resolve(info, &overrides.Opts{
|
names, err := overrides.Resolve(info, &overrides.Opts{
|
||||||
Overrides: true,
|
Overrides: true,
|
||||||
|
@ -139,9 +178,11 @@ func TestResolveLangs(t *testing.T) {
|
||||||
"amd64_centos_en",
|
"amd64_centos_en",
|
||||||
"centos_en",
|
"centos_en",
|
||||||
"amd64_en",
|
"amd64_en",
|
||||||
|
"en",
|
||||||
"amd64_centos_ru",
|
"amd64_centos_ru",
|
||||||
"centos_ru",
|
"centos_ru",
|
||||||
"amd64_ru",
|
"amd64_ru",
|
||||||
|
"ru",
|
||||||
"amd64_centos",
|
"amd64_centos",
|
||||||
"centos",
|
"centos",
|
||||||
"amd64",
|
"amd64",
|
||||||
|
|
|
@ -34,13 +34,13 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
b := lipgloss.RoundedBorder()
|
b1 := lipgloss.RoundedBorder()
|
||||||
b.Right = "\u251C"
|
b1.Right = "\u251C"
|
||||||
titleStyle = lipgloss.NewStyle().BorderStyle(b).Padding(0, 1)
|
titleStyle = lipgloss.NewStyle().BorderStyle(b1).Padding(0, 1)
|
||||||
|
|
||||||
b = lipgloss.RoundedBorder()
|
b2 := lipgloss.RoundedBorder()
|
||||||
b.Left = "\u2524"
|
b2.Left = "\u2524"
|
||||||
infoStyle = titleStyle.Copy().BorderStyle(b)
|
infoStyle = titleStyle.Copy().BorderStyle(b2)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Pager struct {
|
type Pager struct {
|
||||||
|
|
|
@ -18,15 +18,12 @@
|
||||||
|
|
||||||
package repos
|
package repos
|
||||||
|
|
||||||
import (
|
import "go.elara.ws/lure/internal/db"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"go.elara.ws/lure/internal/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FindPkgs looks for packages matching the inputs inside the database.
|
// FindPkgs looks for packages matching the inputs inside the database.
|
||||||
// It returns a map that maps the package name input to the packages found for it.
|
// It returns a map that maps the package name input to the packages found for it.
|
||||||
// It also returns a slice that contains the names of all packages that were not found.
|
// It also returns a slice that contains the names of all packages that were not found.
|
||||||
func FindPkgs(gdb *sqlx.DB, pkgs []string) (map[string][]db.Package, []string, error) {
|
func FindPkgs(pkgs []string) (map[string][]db.Package, []string, error) {
|
||||||
found := map[string][]db.Package{}
|
found := map[string][]db.Package{}
|
||||||
notFound := []string(nil)
|
notFound := []string(nil)
|
||||||
|
|
||||||
|
@ -35,7 +32,7 @@ func FindPkgs(gdb *sqlx.DB, pkgs []string) (map[string][]db.Package, []string, e
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := db.GetPkgs(gdb, "name LIKE ?", pkgName)
|
result, err := db.GetPkgs("name LIKE ?", pkgName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -54,7 +51,7 @@ func FindPkgs(gdb *sqlx.DB, pkgs []string) (map[string][]db.Package, []string, e
|
||||||
result.Close()
|
result.Close()
|
||||||
|
|
||||||
if added == 0 {
|
if added == 0 {
|
||||||
result, err := db.GetPkgs(gdb, "json_array_contains(provides, ?)", pkgName)
|
result, err := db.GetPkgs("json_array_contains(provides, ?)", pkgName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,18 +30,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFindPkgs(t *testing.T) {
|
func TestFindPkgs(t *testing.T) {
|
||||||
gdb, err := db.Open(":memory:")
|
_, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
setCfgDirs(t)
|
setCfgDirs(t)
|
||||||
defer removeCacheDir(t)
|
defer removeCacheDir(t)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
err = repos.Pull(ctx, gdb, []types.Repo{
|
err = repos.Pull(ctx, []types.Repo{
|
||||||
{
|
{
|
||||||
Name: "default",
|
Name: "default",
|
||||||
URL: "https://github.com/Arsen6331/lure-repo.git",
|
URL: "https://github.com/Arsen6331/lure-repo.git",
|
||||||
|
@ -51,7 +51,7 @@ func TestFindPkgs(t *testing.T) {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
found, notFound, err := repos.FindPkgs(gdb, []string{"itd", "nonexistentpackage1", "nonexistentpackage2"})
|
found, notFound, err := repos.FindPkgs([]string{"itd", "nonexistentpackage1", "nonexistentpackage2"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
@ -81,16 +81,16 @@ func TestFindPkgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindPkgsEmpty(t *testing.T) {
|
func TestFindPkgsEmpty(t *testing.T) {
|
||||||
gdb, err := db.Open(":memory:")
|
_, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
setCfgDirs(t)
|
setCfgDirs(t)
|
||||||
defer removeCacheDir(t)
|
defer removeCacheDir(t)
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, db.Package{
|
err = db.InsertPackage(db.Package{
|
||||||
Name: "test1",
|
Name: "test1",
|
||||||
Repository: "default",
|
Repository: "default",
|
||||||
Version: "0.0.1",
|
Version: "0.0.1",
|
||||||
|
@ -105,7 +105,7 @@ func TestFindPkgsEmpty(t *testing.T) {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, db.Package{
|
err = db.InsertPackage(db.Package{
|
||||||
Name: "test2",
|
Name: "test2",
|
||||||
Repository: "default",
|
Repository: "default",
|
||||||
Version: "0.0.1",
|
Version: "0.0.1",
|
||||||
|
@ -120,7 +120,7 @@ func TestFindPkgsEmpty(t *testing.T) {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
found, notFound, err := repos.FindPkgs(gdb, []string{"test", ""})
|
found, notFound, err := repos.FindPkgs([]string{"test", ""})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ import (
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
"github.com/go-git/go-git/v5/plumbing/format/diff"
|
"github.com/go-git/go-git/v5/plumbing/format/diff"
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/pelletier/go-toml/v2"
|
"github.com/pelletier/go-toml/v2"
|
||||||
"go.elara.ws/logger/log"
|
"go.elara.ws/logger/log"
|
||||||
"go.elara.ws/lure/distro"
|
"go.elara.ws/lure/distro"
|
||||||
|
@ -51,7 +50,7 @@ import (
|
||||||
// Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned
|
// Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned
|
||||||
// and its packages will be written to the DB. If it does exist, it will be pulled.
|
// and its packages will be written to the DB. If it does exist, it will be pulled.
|
||||||
// In this case, only changed packages will be processed.
|
// In this case, only changed packages will be processed.
|
||||||
func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
|
func Pull(ctx context.Context, repos []types.Repo) error {
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
repoURL, err := url.Parse(repo.URL)
|
repoURL, err := url.Parse(repo.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -59,7 +58,7 @@ func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Pulling repository").Str("name", repo.Name).Send()
|
log.Info("Pulling repository").Str("name", repo.Name).Send()
|
||||||
repoDir := filepath.Join(config.RepoDir, repo.Name)
|
repoDir := filepath.Join(config.GetPaths().RepoDir, repo.Name)
|
||||||
|
|
||||||
var repoFS billy.Filesystem
|
var repoFS billy.Filesystem
|
||||||
gitDir := filepath.Join(repoDir, ".git")
|
gitDir := filepath.Join(repoDir, ".git")
|
||||||
|
@ -89,7 +88,7 @@ func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
|
||||||
repoFS = w.Filesystem
|
repoFS = w.Filesystem
|
||||||
|
|
||||||
// Make sure the DB is created even if the repo is up to date
|
// Make sure the DB is created even if the repo is up to date
|
||||||
if !errors.Is(err, git.NoErrAlreadyUpToDate) || !config.DBPresent {
|
if !errors.Is(err, git.NoErrAlreadyUpToDate) || db.IsEmpty() {
|
||||||
new, err := r.Head()
|
new, err := r.Head()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -98,13 +97,13 @@ func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
|
||||||
// If the DB was not present at startup, that means it's
|
// If the DB was not present at startup, that means it's
|
||||||
// empty. In this case, we need to update the DB fully
|
// empty. In this case, we need to update the DB fully
|
||||||
// rather than just incrementally.
|
// rather than just incrementally.
|
||||||
if config.DBPresent {
|
if db.IsEmpty() {
|
||||||
err = processRepoChanges(ctx, repo, r, w, old, new, gdb)
|
err = processRepoChanges(ctx, repo, r, w, old, new)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = processRepoFull(ctx, repo, repoDir, gdb)
|
err = processRepoFull(ctx, repo, repoDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -129,7 +128,7 @@ func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = processRepoFull(ctx, repo, repoDir, gdb)
|
err = processRepoFull(ctx, repo, repoDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -171,7 +170,7 @@ type action struct {
|
||||||
File string
|
File string
|
||||||
}
|
}
|
||||||
|
|
||||||
func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference, gdb *sqlx.DB) error {
|
func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference) error {
|
||||||
oldCommit, err := r.CommitObject(old.Hash())
|
oldCommit, err := r.CommitObject(old.Hash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -265,7 +264,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.DeletePkgs(gdb, "name = ? AND repository = ?", pkg.Name, repo.Name)
|
err = db.DeletePkgs("name = ? AND repository = ?", pkg.Name, repo.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -300,7 +299,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
|
||||||
|
|
||||||
resolveOverrides(runner, &pkg)
|
resolveOverrides(runner, &pkg)
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, pkg)
|
err = db.InsertPackage(pkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -326,7 +325,7 @@ func isValid(from, to diff.File) bool {
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
|
|
||||||
func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *sqlx.DB) error {
|
func processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error {
|
||||||
glob := filepath.Join(repoDir, "/*/lure.sh")
|
glob := filepath.Join(repoDir, "/*/lure.sh")
|
||||||
matches, err := filepath.Glob(glob)
|
matches, err := filepath.Glob(glob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -370,7 +369,7 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *
|
||||||
|
|
||||||
resolveOverrides(runner, &pkg)
|
resolveOverrides(runner, &pkg)
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, pkg)
|
err = db.InsertPackage(pkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,50 +33,52 @@ import (
|
||||||
func setCfgDirs(t *testing.T) {
|
func setCfgDirs(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
paths := config.GetPaths()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
config.CacheDir, err = os.MkdirTemp("/tmp", "lure-pull-test.*")
|
paths.CacheDir, err = os.MkdirTemp("/tmp", "lure-pull-test.*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.RepoDir = filepath.Join(config.CacheDir, "repo")
|
paths.RepoDir = filepath.Join(paths.CacheDir, "repo")
|
||||||
config.PkgsDir = filepath.Join(config.CacheDir, "pkgs")
|
paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs")
|
||||||
|
|
||||||
err = os.MkdirAll(config.RepoDir, 0o755)
|
err = os.MkdirAll(paths.RepoDir, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.MkdirAll(config.PkgsDir, 0o755)
|
err = os.MkdirAll(paths.PkgsDir, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.DBPath = filepath.Join(config.CacheDir, "db")
|
paths.DBPath = filepath.Join(paths.CacheDir, "db")
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeCacheDir(t *testing.T) {
|
func removeCacheDir(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
err := os.RemoveAll(config.CacheDir)
|
err := os.RemoveAll(config.GetPaths().CacheDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPull(t *testing.T) {
|
func TestPull(t *testing.T) {
|
||||||
gdb, err := db.Open(":memory:")
|
_, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer db.Close()
|
||||||
|
|
||||||
setCfgDirs(t)
|
setCfgDirs(t)
|
||||||
defer removeCacheDir(t)
|
defer removeCacheDir(t)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
err = repos.Pull(ctx, gdb, []types.Repo{
|
err = repos.Pull(ctx, []types.Repo{
|
||||||
{
|
{
|
||||||
Name: "default",
|
Name: "default",
|
||||||
URL: "https://github.com/Arsen6331/lure-repo.git",
|
URL: "https://github.com/Arsen6331/lure-repo.git",
|
||||||
|
@ -86,7 +88,7 @@ func TestPull(t *testing.T) {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := db.GetPkgs(gdb, "name LIKE 'itd%'")
|
result, err := db.GetPkgs("name LIKE 'itd%'")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,16 +56,16 @@ func (ite InvalidTypeError) Error() string {
|
||||||
// Decoder provides methods for decoding variable values
|
// Decoder provides methods for decoding variable values
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
info *distro.OSRelease
|
info *distro.OSRelease
|
||||||
runner *interp.Runner
|
Runner *interp.Runner
|
||||||
// Enable distro overrides (true by default)
|
// Enable distro overrides (true by default)
|
||||||
Overrides bool
|
Overrides bool
|
||||||
// Enable using like distros for overrides (true by default)
|
// Enable using like distros for overrides
|
||||||
LikeDistros bool
|
LikeDistros bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new variable decoder
|
// New creates a new variable decoder
|
||||||
func New(info *distro.OSRelease, runner *interp.Runner) *Decoder {
|
func New(info *distro.OSRelease, runner *interp.Runner) *Decoder {
|
||||||
return &Decoder{info, runner, true, true}
|
return &Decoder{info, runner, true, len(info.Like) > 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeVar decodes a variable to val using reflection.
|
// DecodeVar decodes a variable to val using reflection.
|
||||||
|
@ -173,7 +173,7 @@ func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(ctx context.Context, opts ...interp.RunnerOption) error {
|
return func(ctx context.Context, opts ...interp.RunnerOption) error {
|
||||||
sub := d.runner.Subshell()
|
sub := d.Runner.Subshell()
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(sub)
|
opt(sub)
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ func (d *Decoder) getFunc(name string) *syntax.Stmt {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fnName := range names {
|
for _, fnName := range names {
|
||||||
fn, ok := d.runner.Funcs[fnName]
|
fn, ok := d.Runner.Funcs[fnName]
|
||||||
if ok {
|
if ok {
|
||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
@ -205,11 +205,11 @@ func (d *Decoder) getVar(name string) *expand.Variable {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, varName := range names {
|
for _, varName := range names {
|
||||||
val, ok := d.runner.Vars[varName]
|
val, ok := d.Runner.Vars[varName]
|
||||||
if ok {
|
if ok {
|
||||||
// Resolve nameref variables
|
// Resolve nameref variables
|
||||||
_, resolved := val.Resolve(expand.FuncEnviron(func(s string) string {
|
_, resolved := val.Resolve(expand.FuncEnviron(func(s string) string {
|
||||||
if val, ok := d.runner.Vars[s]; ok {
|
if val, ok := d.Runner.Vars[s]; ok {
|
||||||
return val.String()
|
return val.String()
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -36,8 +36,8 @@ const testScript = `
|
||||||
release=1
|
release=1
|
||||||
epoch=2
|
epoch=2
|
||||||
desc="Test package"
|
desc="Test package"
|
||||||
homepage='https://lure.arsenm.dev'
|
homepage='https://lure.elara.ws'
|
||||||
maintainer='Arsen Musayelyan <arsen@arsenm.dev>'
|
maintainer='Elara Musayelyan <elara@elara.ws>'
|
||||||
architectures=('arm64' 'amd64')
|
architectures=('arm64' 'amd64')
|
||||||
license=('GPL-3.0-or-later')
|
license=('GPL-3.0-or-later')
|
||||||
provides=('test')
|
provides=('test')
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package main
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -40,7 +40,8 @@ var (
|
||||||
ErrNoDetectManNum = errors.New("manual number cannot be detected from the filename")
|
ErrNoDetectManNum = errors.New("manual number cannot be detected from the filename")
|
||||||
)
|
)
|
||||||
|
|
||||||
var helpers = shutils.ExecFuncs{
|
// Helpers contains all the helper commands
|
||||||
|
var Helpers = shutils.ExecFuncs{
|
||||||
"install-binary": installHelperCmd("/usr/bin", 0o755),
|
"install-binary": installHelperCmd("/usr/bin", 0o755),
|
||||||
"install-systemd-user": installHelperCmd("/usr/lib/systemd/user", 0o644),
|
"install-systemd-user": installHelperCmd("/usr/lib/systemd/user", 0o644),
|
||||||
"install-systemd": installHelperCmd("/usr/lib/systemd/system", 0o644),
|
"install-systemd": installHelperCmd("/usr/lib/systemd/system", 0o644),
|
||||||
|
@ -54,8 +55,9 @@ var helpers = shutils.ExecFuncs{
|
||||||
"git-version": gitVersionCmd,
|
"git-version": gitVersionCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
// rHelpers contains restricted read-only helpers that don't modify any state
|
// Restricted contains restricted read-only helper commands
|
||||||
var rHelpers = shutils.ExecFuncs{
|
// that don't modify any state
|
||||||
|
var Restricted = shutils.ExecFuncs{
|
||||||
"git-version": gitVersionCmd,
|
"git-version": gitVersionCmd,
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ package shutils
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -30,33 +31,36 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc {
|
func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc {
|
||||||
return func(ctx context.Context, s string) ([]os.FileInfo, error) {
|
return func(ctx context.Context, s string) ([]fs.FileInfo, error) {
|
||||||
|
path := filepath.Clean(s)
|
||||||
for _, allowedPrefix := range allowedPrefixes {
|
for _, allowedPrefix := range allowedPrefixes {
|
||||||
if strings.HasPrefix(s, allowedPrefix) {
|
if strings.HasPrefix(path, allowedPrefix) {
|
||||||
return interp.DefaultReadDirHandler()(ctx, s)
|
return interp.DefaultReadDirHandler()(ctx, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, os.ErrNotExist
|
return nil, fs.ErrNotExist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RestrictedStat(allowedPrefixes ...string) interp.StatHandlerFunc {
|
func RestrictedStat(allowedPrefixes ...string) interp.StatHandlerFunc {
|
||||||
return func(ctx context.Context, s string, b bool) (os.FileInfo, error) {
|
return func(ctx context.Context, s string, b bool) (fs.FileInfo, error) {
|
||||||
|
path := filepath.Clean(s)
|
||||||
for _, allowedPrefix := range allowedPrefixes {
|
for _, allowedPrefix := range allowedPrefixes {
|
||||||
if strings.HasPrefix(s, allowedPrefix) {
|
if strings.HasPrefix(path, allowedPrefix) {
|
||||||
return interp.DefaultStatHandler()(ctx, s, b)
|
return interp.DefaultStatHandler()(ctx, s, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, os.ErrNotExist
|
return nil, fs.ErrNotExist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RestrictedOpen(allowedPrefixes ...string) interp.OpenHandlerFunc {
|
func RestrictedOpen(allowedPrefixes ...string) interp.OpenHandlerFunc {
|
||||||
return func(ctx context.Context, s string, i int, fm os.FileMode) (io.ReadWriteCloser, error) {
|
return func(ctx context.Context, s string, i int, fm fs.FileMode) (io.ReadWriteCloser, error) {
|
||||||
|
path := filepath.Clean(s)
|
||||||
for _, allowedPrefix := range allowedPrefixes {
|
for _, allowedPrefix := range allowedPrefixes {
|
||||||
if strings.HasPrefix(s, allowedPrefix) {
|
if strings.HasPrefix(path, allowedPrefix) {
|
||||||
return interp.DefaultOpenHandler()(ctx, s, i, fm)
|
return interp.DefaultOpenHandler()(ctx, s, i, fm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package translations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
|
||||||
|
"go.elara.ws/logger"
|
||||||
|
"go.elara.ws/logger/log"
|
||||||
|
"go.elara.ws/translate"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed files
|
||||||
|
var translationFS embed.FS
|
||||||
|
|
||||||
|
var translator *translate.Translator
|
||||||
|
|
||||||
|
func Translator() *translate.Translator {
|
||||||
|
if translator == nil {
|
||||||
|
t, err := translate.NewFromFS(translationFS)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error creating new translator").Err(err).Send()
|
||||||
|
}
|
||||||
|
translator = &t
|
||||||
|
}
|
||||||
|
return translator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogger(l logger.Logger, lang language.Tag) *translate.TranslatedLogger {
|
||||||
|
return translate.NewLogger(l, *Translator(), lang)
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "go.elara.ws/lure/manager"
|
||||||
|
|
||||||
|
type BuildOpts struct {
|
||||||
|
Script string
|
||||||
|
Manager manager.Manager
|
||||||
|
Clean bool
|
||||||
|
Interactive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildVars represents the script variables required
|
||||||
|
// to build a package
|
||||||
|
type BuildVars struct {
|
||||||
|
Name string `sh:"name,required"`
|
||||||
|
Version string `sh:"version,required"`
|
||||||
|
Release int `sh:"release,required"`
|
||||||
|
Epoch uint `sh:"epoch"`
|
||||||
|
Description string `sh:"desc"`
|
||||||
|
Homepage string `sh:"homepage"`
|
||||||
|
Maintainer string `sh:"maintainer"`
|
||||||
|
Architectures []string `sh:"architectures"`
|
||||||
|
Licenses []string `sh:"license"`
|
||||||
|
Provides []string `sh:"provides"`
|
||||||
|
Conflicts []string `sh:"conflicts"`
|
||||||
|
Depends []string `sh:"deps"`
|
||||||
|
BuildDepends []string `sh:"build_deps"`
|
||||||
|
Replaces []string `sh:"replaces"`
|
||||||
|
Sources []string `sh:"sources"`
|
||||||
|
Checksums []string `sh:"checksums"`
|
||||||
|
Backup []string `sh:"backup"`
|
||||||
|
Scripts Scripts `sh:"scripts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scripts struct {
|
||||||
|
PreInstall string `sh:"preinstall"`
|
||||||
|
PostInstall string `sh:"postinstall"`
|
||||||
|
PreRemove string `sh:"preremove"`
|
||||||
|
PostRemove string `sh:"postremove"`
|
||||||
|
PreUpgrade string `sh:"preupgrade"`
|
||||||
|
PostUpgrade string `sh:"postupgrade"`
|
||||||
|
PreTrans string `sh:"pretrans"`
|
||||||
|
PostTrans string `sh:"posttrans"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Directories struct {
|
||||||
|
BaseDir string
|
||||||
|
SrcDir string
|
||||||
|
PkgDir string
|
||||||
|
ScriptDir string
|
||||||
|
}
|
110
list.go
110
list.go
|
@ -23,71 +23,83 @@ import (
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"go.elara.ws/logger/log"
|
"go.elara.ws/logger/log"
|
||||||
|
"go.elara.ws/lure/internal/config"
|
||||||
"go.elara.ws/lure/internal/db"
|
"go.elara.ws/lure/internal/db"
|
||||||
"go.elara.ws/lure/internal/repos"
|
"go.elara.ws/lure/internal/repos"
|
||||||
"go.elara.ws/lure/manager"
|
"go.elara.ws/lure/manager"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
func listCmd(c *cli.Context) error {
|
var listCmd = &cli.Command{
|
||||||
err := repos.Pull(c.Context, gdb, cfg.Repos)
|
Name: "list",
|
||||||
if err != nil {
|
Usage: "List LURE repo packages",
|
||||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
Aliases: []string{"ls"},
|
||||||
}
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
where := "true"
|
Name: "installed",
|
||||||
args := []any(nil)
|
Aliases: []string{"I"},
|
||||||
if c.NArg() > 0 {
|
},
|
||||||
where = "name LIKE ? OR json_array_contains(provides, ?)"
|
},
|
||||||
args = []any{c.Args().First(), c.Args().First()}
|
Action: func(c *cli.Context) error {
|
||||||
}
|
err := repos.Pull(c.Context, config.Config().Repos)
|
||||||
|
|
||||||
result, err := db.GetPkgs(gdb, where, args...)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error getting packages").Err(err).Send()
|
|
||||||
}
|
|
||||||
defer result.Close()
|
|
||||||
|
|
||||||
var installed map[string]string
|
|
||||||
if c.Bool("installed") {
|
|
||||||
mgr := manager.Detect()
|
|
||||||
if mgr == nil {
|
|
||||||
log.Fatal("Unable to detect supported package manager on system").Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
installed, err = mgr.ListInstalled(&manager.Opts{AsRoot: false})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error listing installed packages").Err(err).Send()
|
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for result.Next() {
|
where := "true"
|
||||||
var pkg db.Package
|
args := []any(nil)
|
||||||
err := result.StructScan(&pkg)
|
if c.NArg() > 0 {
|
||||||
|
where = "name LIKE ? OR json_array_contains(provides, ?)"
|
||||||
|
args = []any{c.Args().First(), c.Args().First()}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := db.GetPkgs(where, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
log.Fatal("Error getting packages").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
defer result.Close()
|
||||||
|
|
||||||
if slices.Contains(cfg.IgnorePkgUpdates, pkg.Name) {
|
var installed map[string]string
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
version := pkg.Version
|
|
||||||
if c.Bool("installed") {
|
if c.Bool("installed") {
|
||||||
instVersion, ok := installed[pkg.Name]
|
mgr := manager.Detect()
|
||||||
if !ok {
|
if mgr == nil {
|
||||||
continue
|
log.Fatal("Unable to detect a supported package manager on the system").Send()
|
||||||
} else {
|
}
|
||||||
version = instVersion
|
|
||||||
|
installed, err = mgr.ListInstalled(&manager.Opts{AsRoot: false})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error listing installed packages").Err(err).Send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version)
|
for result.Next() {
|
||||||
}
|
var pkg db.Package
|
||||||
|
err := result.StructScan(&pkg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if slices.Contains(config.Config().IgnorePkgUpdates, pkg.Name) {
|
||||||
log.Fatal("Error iterating over packages").Err(err).Send()
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
version := pkg.Version
|
||||||
|
if c.Bool("installed") {
|
||||||
|
instVersion, ok := installed[pkg.Name]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
version = instVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error iterating over packages").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
276
main.go
276
main.go
|
@ -20,13 +20,11 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"embed"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
//"time"
|
||||||
|
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
@ -34,238 +32,78 @@ import (
|
||||||
"go.elara.ws/logger/log"
|
"go.elara.ws/logger/log"
|
||||||
"go.elara.ws/lure/internal/config"
|
"go.elara.ws/lure/internal/config"
|
||||||
"go.elara.ws/lure/internal/db"
|
"go.elara.ws/lure/internal/db"
|
||||||
|
"go.elara.ws/lure/internal/translations"
|
||||||
"go.elara.ws/lure/manager"
|
"go.elara.ws/lure/manager"
|
||||||
"go.elara.ws/translate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate scripts/gen-version.sh
|
var app = &cli.App{
|
||||||
|
Name: "lure",
|
||||||
|
Usage: "Linux User REpository",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "pm-args",
|
||||||
|
Aliases: []string{"P"},
|
||||||
|
Usage: "Arguments to be passed on to the package manager",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "interactive",
|
||||||
|
Aliases: []string{"i"},
|
||||||
|
Value: isatty.IsTerminal(os.Stdin.Fd()),
|
||||||
|
Usage: "Enable interactive questions and prompts",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Commands: []*cli.Command{
|
||||||
|
installCmd,
|
||||||
|
removeCmd,
|
||||||
|
upgradeCmd,
|
||||||
|
infoCmd,
|
||||||
|
listCmd,
|
||||||
|
buildCmd,
|
||||||
|
addrepoCmd,
|
||||||
|
removerepoCmd,
|
||||||
|
refreshCmd,
|
||||||
|
fixCmd,
|
||||||
|
versionCmd,
|
||||||
|
},
|
||||||
|
Before: func(c *cli.Context) error {
|
||||||
|
args := strings.Split(c.String("pm-args"), " ")
|
||||||
|
if len(args) == 1 && args[0] == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
manager.Args = append(manager.Args, args...)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
After: func(ctx *cli.Context) error {
|
||||||
|
return db.Close()
|
||||||
|
},
|
||||||
|
EnableBashCompletion: true,
|
||||||
|
}
|
||||||
|
|
||||||
//go:embed translations
|
var versionCmd = &cli.Command{
|
||||||
var translationFS embed.FS
|
Name: "version",
|
||||||
|
Usage: "Print the current LURE version and exit",
|
||||||
var translator translate.Translator
|
Action: func(ctx *cli.Context) error {
|
||||||
|
println(config.Version)
|
||||||
func init() {
|
return nil
|
||||||
logger := logger.NewCLI(os.Stderr)
|
},
|
||||||
|
|
||||||
t, err := translate.NewFromFS(translationFS)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Error creating new translator").Err(err).Send()
|
|
||||||
}
|
|
||||||
translator = t
|
|
||||||
|
|
||||||
log.Logger = translate.NewLogger(logger, t, config.Language)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if !cfg.Unsafe.AllowRunAsRoot && os.Geteuid() == 0 {
|
log.Logger = translations.NewLogger(logger.NewCLI(os.Stderr), config.Language())
|
||||||
|
|
||||||
|
if !config.Config().Unsafe.AllowRunAsRoot && os.Geteuid() == 0 {
|
||||||
log.Fatal("Running LURE as root is forbidden as it may cause catastrophic damage to your system").Send()
|
log.Fatal("Running LURE as root is forbidden as it may cause catastrophic damage to your system").Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
err := loadDB()
|
// Set the root command to the one set in the LURE config
|
||||||
if err != nil {
|
manager.DefaultRootCmd = config.Config().RootCmd
|
||||||
log.Fatal("Error loading database").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
|
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
// Exit the program after a maximum of 200ms
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
gdb.Close()
|
|
||||||
os.Exit(0)
|
|
||||||
}()
|
|
||||||
|
|
||||||
app := &cli.App{
|
err := app.RunContext(ctx, os.Args)
|
||||||
Name: "lure",
|
|
||||||
Usage: "Linux User REpository",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "pm-args",
|
|
||||||
Aliases: []string{"P"},
|
|
||||||
Usage: "Arguments to be passed on to the package manager",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "interactive",
|
|
||||||
Aliases: []string{"i"},
|
|
||||||
Value: isatty.IsTerminal(os.Stdin.Fd()),
|
|
||||||
Usage: "Enable interactive questions and prompts",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Commands: []*cli.Command{
|
|
||||||
{
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "clean",
|
|
||||||
Aliases: []string{"c"},
|
|
||||||
Usage: "Build package from scratch even if there's an already built package available",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Name: "install",
|
|
||||||
Usage: "Install a new package",
|
|
||||||
Aliases: []string{"in"},
|
|
||||||
Action: installCmd,
|
|
||||||
BashComplete: completionInstall,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "remove",
|
|
||||||
Usage: "Remove an installed package",
|
|
||||||
Aliases: []string{"rm"},
|
|
||||||
Action: removeCmd,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "clean",
|
|
||||||
Aliases: []string{"c"},
|
|
||||||
Usage: "Build package from scratch even if there's an already built package available",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Name: "upgrade",
|
|
||||||
Usage: "Upgrade all installed packages",
|
|
||||||
Aliases: []string{"up"},
|
|
||||||
Action: upgradeCmd,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "all",
|
|
||||||
Aliases: []string{"a"},
|
|
||||||
Usage: "Show all information, not just for the current distro",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Name: "info",
|
|
||||||
Usage: "Print information about a package",
|
|
||||||
Action: infoCmd,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "installed",
|
|
||||||
Aliases: []string{"I"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Name: "list",
|
|
||||||
Usage: "List LURE repo packages",
|
|
||||||
Aliases: []string{"ls"},
|
|
||||||
Action: listCmd,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "script",
|
|
||||||
Aliases: []string{"s"},
|
|
||||||
Value: "lure.sh",
|
|
||||||
Usage: "Path to the build script",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "package",
|
|
||||||
Aliases: []string{"p"},
|
|
||||||
Usage: "Name of the package to build and its repo (example: default/go-bin)",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "clean",
|
|
||||||
Aliases: []string{"c"},
|
|
||||||
Usage: "Build package from scratch even if there's an already built package available",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Name: "build",
|
|
||||||
Usage: "Build a local package",
|
|
||||||
Action: buildCmd,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Aliases: []string{"n"},
|
|
||||||
Required: true,
|
|
||||||
Usage: "Name of the new repo",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "url",
|
|
||||||
Aliases: []string{"u"},
|
|
||||||
Required: true,
|
|
||||||
Usage: "URL of the new repo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Name: "addrepo",
|
|
||||||
Usage: "Add a new repository",
|
|
||||||
Aliases: []string{"ar"},
|
|
||||||
Action: addrepoCmd,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Aliases: []string{"n"},
|
|
||||||
Required: true,
|
|
||||||
Usage: "Name of the repo to be deleted",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Name: "removerepo",
|
|
||||||
Usage: "Remove an existing repository",
|
|
||||||
Aliases: []string{"rr"},
|
|
||||||
Action: removerepoCmd,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "refresh",
|
|
||||||
Usage: "Pull all repositories that have changed",
|
|
||||||
Aliases: []string{"ref"},
|
|
||||||
Action: refreshCmd,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "fix",
|
|
||||||
Usage: "Attempt to fix problems with LURE",
|
|
||||||
Action: fixCmd,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "version",
|
|
||||||
Usage: "Display the current LURE version and exit",
|
|
||||||
Action: displayVersion,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Before: func(c *cli.Context) error {
|
|
||||||
args := strings.Split(c.String("pm-args"), " ")
|
|
||||||
if len(args) == 1 && args[0] == "" {
|
|
||||||
args = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
manager.Args = append(manager.Args, args...)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
After: func(ctx *cli.Context) error {
|
|
||||||
return gdb.Close()
|
|
||||||
},
|
|
||||||
EnableBashCompletion: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = app.RunContext(ctx, os.Args)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error while running app").Err(err).Send()
|
log.Error("Error while running app").Err(err).Send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func displayVersion(c *cli.Context) error {
|
|
||||||
print(config.Version)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func completionInstall(c *cli.Context) {
|
|
||||||
result, err := db.GetPkgs(gdb, "true")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error getting packages").Err(err).Send()
|
|
||||||
}
|
|
||||||
defer result.Close()
|
|
||||||
|
|
||||||
for result.Next() {
|
|
||||||
var pkg db.Package
|
|
||||||
err = result.StructScan(&pkg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error iterating over packages").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(pkg.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
178
repo.go
178
repo.go
|
@ -32,83 +32,123 @@ import (
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
func addrepoCmd(c *cli.Context) error {
|
var addrepoCmd = &cli.Command{
|
||||||
name := c.String("name")
|
Name: "addrepo",
|
||||||
repoURL := c.String("url")
|
Usage: "Add a new repository",
|
||||||
|
Aliases: []string{"ar"},
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "Name of the new repo",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "url",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "URL of the new repo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
name := c.String("name")
|
||||||
|
repoURL := c.String("url")
|
||||||
|
|
||||||
for _, repo := range cfg.Repos {
|
cfg := config.Config()
|
||||||
if repo.URL == repoURL {
|
|
||||||
log.Fatal("Repo already exists").Str("name", repo.Name).Send()
|
for _, repo := range cfg.Repos {
|
||||||
|
if repo.URL == repoURL {
|
||||||
|
log.Fatal("Repo already exists").Str("name", repo.Name).Send()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cfg.Repos = append(cfg.Repos, types.Repo{
|
cfg.Repos = append(cfg.Repos, types.Repo{
|
||||||
Name: name,
|
Name: name,
|
||||||
URL: repoURL,
|
URL: repoURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
cfgFl, err := os.Create(config.ConfigPath)
|
cfgFl, err := os.Create(config.GetPaths().ConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error opening config file").Err(err).Send()
|
log.Fatal("Error opening config file").Err(err).Send()
|
||||||
}
|
|
||||||
|
|
||||||
err = toml.NewEncoder(cfgFl).Encode(&cfg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error encoding config").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = repos.Pull(c.Context, gdb, cfg.Repos)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error pulling repos").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func removerepoCmd(c *cli.Context) error {
|
|
||||||
name := c.String("name")
|
|
||||||
|
|
||||||
found := false
|
|
||||||
index := 0
|
|
||||||
for i, repo := range cfg.Repos {
|
|
||||||
if repo.Name == name {
|
|
||||||
index = i
|
|
||||||
found = true
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
log.Fatal("Repo does not exist").Str("name", name).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.Repos = slices.Delete(cfg.Repos, index, index+1)
|
err = toml.NewEncoder(cfgFl).Encode(cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error encoding config").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
cfgFl, err := os.Create(config.ConfigPath)
|
err = repos.Pull(c.Context, cfg.Repos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error opening config file").Err(err).Send()
|
log.Fatal("Error pulling repos").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = toml.NewEncoder(cfgFl).Encode(&cfg)
|
return nil
|
||||||
if err != nil {
|
},
|
||||||
log.Fatal("Error encoding config").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.RemoveAll(filepath.Join(config.RepoDir, name))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error removing repo directory").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.DeletePkgs(gdb, "repository = ?", name)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error removing packages from database").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshCmd(c *cli.Context) error {
|
var removerepoCmd = &cli.Command{
|
||||||
err := repos.Pull(c.Context, gdb, cfg.Repos)
|
Name: "removerepo",
|
||||||
if err != nil {
|
Usage: "Remove an existing repository",
|
||||||
log.Fatal("Error pulling repos").Err(err).Send()
|
Aliases: []string{"rr"},
|
||||||
}
|
Flags: []cli.Flag{
|
||||||
return nil
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "Name of the repo to be deleted",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
name := c.String("name")
|
||||||
|
cfg := config.Config()
|
||||||
|
|
||||||
|
found := false
|
||||||
|
index := 0
|
||||||
|
for i, repo := range cfg.Repos {
|
||||||
|
if repo.Name == name {
|
||||||
|
index = i
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
log.Fatal("Repo does not exist").Str("name", name).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Repos = slices.Delete(cfg.Repos, index, index+1)
|
||||||
|
|
||||||
|
cfgFl, err := os.Create(config.GetPaths().ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error opening config file").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = toml.NewEncoder(cfgFl).Encode(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error encoding config").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll(filepath.Join(config.GetPaths().RepoDir, name))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error removing repo directory").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.DeletePkgs("repository = ?", name)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error removing packages from database").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var refreshCmd = &cli.Command{
|
||||||
|
Name: "refresh",
|
||||||
|
Usage: "Pull all repositories that have changed",
|
||||||
|
Aliases: []string{"ref"},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
err := repos.Pull(c.Context, config.Config().Repos)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error pulling repos").Err(err).Send()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
git describe --tags > internal/config/version.txt
|
git describe --tags > version.txt
|
69
upgrade.go
69
upgrade.go
|
@ -25,42 +25,61 @@ import (
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"go.elara.ws/logger/log"
|
"go.elara.ws/logger/log"
|
||||||
"go.elara.ws/lure/distro"
|
"go.elara.ws/lure/distro"
|
||||||
|
"go.elara.ws/lure/internal/build"
|
||||||
|
"go.elara.ws/lure/internal/config"
|
||||||
"go.elara.ws/lure/internal/db"
|
"go.elara.ws/lure/internal/db"
|
||||||
"go.elara.ws/lure/internal/repos"
|
"go.elara.ws/lure/internal/repos"
|
||||||
|
"go.elara.ws/lure/internal/types"
|
||||||
"go.elara.ws/lure/manager"
|
"go.elara.ws/lure/manager"
|
||||||
"go.elara.ws/vercmp"
|
"go.elara.ws/vercmp"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
func upgradeCmd(c *cli.Context) error {
|
var upgradeCmd = &cli.Command{
|
||||||
info, err := distro.ParseOSRelease(c.Context)
|
Name: "upgrade",
|
||||||
if err != nil {
|
Usage: "Upgrade all installed packages",
|
||||||
log.Fatal("Error parsing os-release file").Err(err).Send()
|
Aliases: []string{"up"},
|
||||||
}
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "clean",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Build package from scratch even if there's an already built package available",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
info, err := distro.ParseOSRelease(c.Context)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error parsing os-release file").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
mgr := manager.Detect()
|
mgr := manager.Detect()
|
||||||
if mgr == nil {
|
if mgr == nil {
|
||||||
log.Fatal("Unable to detect supported package manager on system").Send()
|
log.Fatal("Unable to detect a supported package manager on the system").Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = repos.Pull(c.Context, gdb, cfg.Repos)
|
err = repos.Pull(c.Context, config.Config().Repos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error pulling repos").Err(err).Send()
|
log.Fatal("Error pulling repos").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
updates, err := checkForUpdates(c.Context, mgr, info)
|
updates, err := checkForUpdates(c.Context, mgr, info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error checking for updates").Err(err).Send()
|
log.Fatal("Error checking for updates").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(updates) > 0 {
|
if len(updates) > 0 {
|
||||||
installPkgs(c.Context, updates, nil, mgr, c.Bool("clean"), c.Bool("interactive"))
|
build.InstallPkgs(c.Context, updates, nil, types.BuildOpts{
|
||||||
} else {
|
Manager: mgr,
|
||||||
log.Info("There is nothing to do.").Send()
|
Clean: c.Bool("clean"),
|
||||||
}
|
Interactive: c.Bool("interactive"),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
log.Info("There is nothing to do.").Send()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRelease) ([]db.Package, error) {
|
func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRelease) ([]db.Package, error) {
|
||||||
|
@ -70,14 +89,14 @@ func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRe
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgNames := maps.Keys(installed)
|
pkgNames := maps.Keys(installed)
|
||||||
found, _, err := repos.FindPkgs(gdb, pkgNames)
|
found, _, err := repos.FindPkgs(pkgNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var out []db.Package
|
var out []db.Package
|
||||||
for pkgName, pkgs := range found {
|
for pkgName, pkgs := range found {
|
||||||
if slices.Contains(cfg.IgnorePkgUpdates, pkgName) {
|
if slices.Contains(config.Config().IgnorePkgUpdates, pkgName) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue