diff --git a/Makefile b/Makefile
index b42025d..8148dd9 100644
--- a/Makefile
+++ b/Makefile
@@ -19,4 +19,4 @@ uninstall:
internal/config/version.txt:
go generate ./internal/config
-.PHONY: install clean uninstall
\ No newline at end of file
+.PHONY: install clean uninstall installmisc
\ No newline at end of file
diff --git a/build.go b/build.go
index bc48647..5b989e8 100644
--- a/build.go
+++ b/build.go
@@ -19,736 +19,84 @@
package main
import (
- "bytes"
- "context"
- "encoding/hex"
- "fmt"
- "io"
"os"
"path/filepath"
- "runtime"
- "strconv"
- "strings"
_ "github.com/goreleaser/nfpm/v2/apk"
_ "github.com/goreleaser/nfpm/v2/arch"
_ "github.com/goreleaser/nfpm/v2/deb"
_ "github.com/goreleaser/nfpm/v2/rpm"
"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/lure/distro"
- "go.elara.ws/lure/internal/cliutils"
+ "go.elara.ws/lure/internal/build"
"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/shutils"
- "go.elara.ws/lure/internal/shutils/decoder"
+ "go.elara.ws/lure/internal/repos"
+ "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"
)
-// 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"`
-}
-
-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...),
+var buildCmd = &cli.Command{
+ Name: "build",
+ Usage: "Build a local package",
+ 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",
+ },
+ },
+ 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 {
- pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10)
- }
+ err := repos.Pull(c.Context, config.Config().Repos)
+ 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") {
- pkgInfo.Arch = "all"
- }
+ pkgPaths, _, err := build.BuildPackage(c.Context, types.BuildOpts{
+ 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{}
- filepath.Walk(pkgdir, func(path string, fi os.FileInfo, err error) error {
- trimmed := strings.TrimPrefix(path, pkgdir)
+ wd, err := os.Getwd()
+ if err != nil {
+ log.Fatal("Error getting working directory").Err(err).Send()
+ }
- if fi.IsDir() {
- f, err := os.Open(path)
+ for _, pkgPath := range pkgPaths {
+ name := filepath.Base(pkgPath)
+ err = osutils.Move(pkgPath, filepath.Join(wd, name))
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
- })
-
- 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)
- }
+ },
}
diff --git a/cmd/lure-api-server/api.go b/cmd/lure-api-server/api.go
index b7934e2..cdd3fdf 100644
--- a/cmd/lure-api-server/api.go
+++ b/cmd/lure-api-server/api.go
@@ -25,7 +25,6 @@ import (
"strconv"
"strings"
- "github.com/jmoiron/sqlx"
"github.com/twitchtv/twirp"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/api"
@@ -34,9 +33,7 @@ import (
"golang.org/x/text/language"
)
-type lureWebAPI struct {
- db *sqlx.DB
-}
+type lureWebAPI struct{}
func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.SearchResponse, error) {
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)
}
- result, err := db.GetPkgs(l.db, query, args...)
+ result, err := db.GetPkgs(query, args...)
if err != nil {
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) {
- 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 {
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 /")
}
- 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)
if os.IsNotExist(err) {
return nil, twirp.NewError(twirp.NotFound, "requested package not found")
diff --git a/cmd/lure-api-server/badge.go b/cmd/lure-api-server/badge.go
index 5c764bd..aa08761 100644
--- a/cmd/lure-api-server/badge.go
+++ b/cmd/lure-api-server/badge.go
@@ -8,7 +8,6 @@ import (
"strings"
"github.com/go-chi/chi/v5"
- "github.com/jmoiron/sqlx"
"go.elara.ws/lure/internal/db"
)
@@ -17,12 +16,12 @@ var logoData string
var _ http.HandlerFunc
-func handleBadge(gdb *sqlx.DB) http.HandlerFunc {
+func handleBadge() http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
repo := chi.URLParam(req, "repo")
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 {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
diff --git a/cmd/lure-api-server/config.go b/cmd/lure-api-server/config.go
deleted file mode 100644
index 255e23b..0000000
--- a/cmd/lure-api-server/config.go
+++ /dev/null
@@ -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 .
- */
-
-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()
- }
-}
diff --git a/cmd/lure-api-server/db.go b/cmd/lure-api-server/db.go
deleted file mode 100644
index c3ab12e..0000000
--- a/cmd/lure-api-server/db.go
+++ /dev/null
@@ -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 .
- */
-
-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()
- }
-}
diff --git a/cmd/lure-api-server/main.go b/cmd/lure-api-server/main.go
index be7e09b..de6e1e4 100644
--- a/cmd/lure-api-server/main.go
+++ b/cmd/lure-api-server/main.go
@@ -30,6 +30,7 @@ import (
"go.elara.ws/logger"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/api"
+ "go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/repos"
)
@@ -54,7 +55,7 @@ func main() {
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 {
log.Fatal("Error pulling repositories").Err(err).Send()
}
@@ -63,14 +64,14 @@ func main() {
go repoPullWorker(ctx, sigCh)
apiServer := api.NewAPIServer(
- lureWebAPI{db: gdb},
+ lureWebAPI{},
twirp.WithServerPathPrefix(""),
)
r := chi.NewRouter()
r.With(allowAllCORSHandler, withAcceptLanguage).Handle("/*", apiServer)
r.Post("/webhook", handleWebhook(sigCh))
- r.Get("/badge/{repo}/{pkg}", handleBadge(gdb))
+ r.Get("/badge/{repo}/{pkg}", handleBadge())
ln, err := net.Listen("tcp", *addr)
if err != nil {
diff --git a/cmd/lure-api-server/webhook.go b/cmd/lure-api-server/webhook.go
index bfc3cf8..b7ce1b9 100644
--- a/cmd/lure-api-server/webhook.go
+++ b/cmd/lure-api-server/webhook.go
@@ -30,6 +30,7 @@ import (
"strings"
"go.elara.ws/logger/log"
+ "go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/repos"
)
@@ -87,7 +88,7 @@ func repoPullWorker(ctx context.Context, sigCh <-chan struct{}) {
for {
select {
case <-sigCh:
- err := repos.Pull(ctx, gdb, cfg.Repos)
+ err := repos.Pull(ctx, config.Config().Repos)
if err != nil {
log.Warn("Error while pulling repositories").Err(err).Send()
}
diff --git a/config.go b/config.go
deleted file mode 100644
index 84a99b8..0000000
--- a/config.go
+++ /dev/null
@@ -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 .
- */
-
-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
-}
diff --git a/db.go b/db.go
deleted file mode 100644
index 4022524..0000000
--- a/db.go
+++ /dev/null
@@ -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 .
- */
-
-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
-}
diff --git a/distro/osrelease.go b/distro/osrelease.go
index 6908435..5149a0a 100644
--- a/distro/osrelease.go
+++ b/distro/osrelease.go
@@ -103,7 +103,14 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
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, " ")
}
diff --git a/fix.go b/fix.go
index 67009ea..eec332c 100644
--- a/fix.go
+++ b/fix.go
@@ -28,36 +28,34 @@ import (
"go.elara.ws/lure/internal/repos"
)
-func fixCmd(c *cli.Context) error {
- gdb.Close()
+var fixCmd = &cli.Command{
+ 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)
- if err != nil {
- log.Fatal("Unable to remove cache directory").Err(err).Send()
- }
+ err := os.RemoveAll(paths.CacheDir)
+ if err != nil {
+ 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)
- if err != nil {
- log.Fatal("Unable to create new cache directory").Err(err).Send()
- }
+ err = os.MkdirAll(paths.CacheDir, 0o755)
+ if err != nil {
+ log.Fatal("Unable to create new cache directory").Err(err).Send()
+ }
- // Make sure the DB is rebuilt when repos are pulled
- gdb, err = db.Open(config.DBPath)
- if err != nil {
- log.Fatal("Error initializing database").Err(err).Send()
- }
- config.DBPresent = false
+ err = repos.Pull(c.Context, config.Config().Repos)
+ if err != nil {
+ log.Fatal("Error pulling repos").Err(err).Send()
+ }
- err = repos.Pull(c.Context, gdb, cfg.Repos)
- if err != nil {
- log.Fatal("Error pulling repos").Err(err).Send()
- }
+ log.Info("Done").Send()
- log.Info("Done").Send()
-
- return nil
+ return nil
+ },
}
diff --git a/info.go b/info.go
index 4b44cac..b983868 100644
--- a/info.go
+++ b/info.go
@@ -33,61 +33,72 @@ import (
"gopkg.in/yaml.v3"
)
-func infoCmd(c *cli.Context) error {
- args := c.Args()
- if args.Len() < 1 {
- log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
- }
-
- err := repos.Pull(c.Context, gdb, cfg.Repos)
- if err != nil {
- log.Fatal("Error pulling repositories").Err(err).Send()
- }
-
- found, _, err := repos.FindPkgs(gdb, 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"), 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()
+var infoCmd = &cli.Command{
+ Name: "info",
+ Usage: "Print information about a package",
+ Flags: []cli.Flag{
+ &cli.BoolFlag{
+ Name: "all",
+ Aliases: []string{"a"},
+ Usage: "Show all information, not just for the current distro",
+ },
+ },
+ Action: func(c *cli.Context) error {
+ args := c.Args()
+ if args.Len() < 1 {
+ log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).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 {
- err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
+ info, err := distro.ParseOSRelease(c.Context)
if err != nil {
- log.Fatal("Error encoding script variables").Err(err).Send()
+ log.Fatal("Error parsing os-release file").Err(err).Send()
}
- } else {
- err = yaml.NewEncoder(os.Stdout).Encode(pkg)
+ names, err = overrides.Resolve(
+ info,
+ overrides.DefaultOpts.
+ WithLanguages([]string{config.SystemLang()}),
+ )
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
+ },
}
diff --git a/install.go b/install.go
index 2fa21b6..79db45a 100644
--- a/install.go
+++ b/install.go
@@ -19,98 +19,98 @@
package main
import (
- "context"
- "path/filepath"
-
- "go.elara.ws/logger/log"
+ "fmt"
"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/config"
"go.elara.ws/lure/internal/db"
"go.elara.ws/lure/internal/repos"
+ "go.elara.ws/lure/internal/types"
"go.elara.ws/lure/manager"
)
-func installCmd(c *cli.Context) error {
- args := c.Args()
- if args.Len() < 1 {
- log.Fatalf("Command install expected at least 1 argument, got %d", args.Len()).Send()
- }
-
- mgr := manager.Detect()
- if mgr == nil {
- log.Fatal("Unable to detect supported package manager on system").Send()
- }
-
- err := repos.Pull(c.Context, gdb, cfg.Repos)
- if err != nil {
- log.Fatal("Error pulling repositories").Err(err).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()
+var installCmd = &cli.Command{
+ Name: "install",
+ Usage: "Install a new package",
+ Aliases: []string{"in"},
+ 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 {
+ args := c.Args()
+ if args.Len() < 1 {
+ log.Fatalf("Command install expected at least 1 argument, got %d", args.Len()).Send()
}
- err = mgr.InstallLocal(nil, builtPkgs...)
- if err != nil {
- log.Fatal("Error installing package").Err(err).Send()
+ mgr := manager.Detect()
+ if mgr == nil {
+ 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 {
- args := c.Args()
- if args.Len() < 1 {
- log.Fatalf("Command remove expected at least 1 argument, got %d", args.Len()).Send()
- }
+var removeCmd = &cli.Command{
+ Name: "remove",
+ Usage: "Remove an installed package",
+ 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()
- if mgr == nil {
- log.Fatal("Unable to detect supported package manager on system").Send()
- }
+ mgr := manager.Detect()
+ if mgr == nil {
+ log.Fatal("Unable to detect a supported package manager on the system").Send()
+ }
- err := mgr.Remove(nil, c.Args().Slice()...)
- if err != nil {
- log.Fatal("Error removing packages").Err(err).Send()
- }
+ err := mgr.Remove(nil, c.Args().Slice()...)
+ if err != nil {
+ log.Fatal("Error removing packages").Err(err).Send()
+ }
- return nil
+ return nil
+ },
}
diff --git a/internal/api/lure.pb.go b/internal/api/lure.pb.go
index eb3b23a..7dfd48f 100644
--- a/internal/api/lure.pb.go
+++ b/internal/api/lure.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
-// protoc v3.21.9
+// protoc v4.24.2
// source: lure.proto
package api
diff --git a/internal/build/build.go b/internal/build/build.go
new file mode 100644
index 0000000..a7f7721
--- /dev/null
+++ b/internal/build/build.go
@@ -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
+}
diff --git a/internal/build/install.go b/internal/build/install.go
new file mode 100644
index 0000000..c0ab693
--- /dev/null
+++ b/internal/build/install.go
@@ -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()
+ }
+ }
+}
diff --git a/internal/cliutils/prompt.go b/internal/cliutils/prompt.go
index f95a741..86d5efc 100644
--- a/internal/cliutils/prompt.go
+++ b/internal/cliutils/prompt.go
@@ -26,16 +26,16 @@ import (
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/db"
"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
-func YesNoPrompt(msg string, interactive, def bool, t translate.Translator) (bool, error) {
+func YesNoPrompt(msg string, interactive, def bool) (bool, error) {
if interactive {
var answer bool
err := survey.AskOne(
&survey.Confirm{
- Message: t.TranslateTo(msg, config.Language),
+ Message: translations.Translator().TranslateTo(msg, config.Language()),
Default: def,
},
&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,
// shows it if they answer yes, then asks if they'd still like to
// 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 {
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 {
return err
}
@@ -65,13 +66,13 @@ func PromptViewScript(script, name, style string, interactive bool, t translate.
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 {
return err
}
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
// 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
for _, pkgs := range found {
if len(pkgs) > 1 && interactive {
- choices, err := PkgPrompt(pkgs, verb, interactive, t)
+ choices, err := PkgPrompt(pkgs, verb, interactive)
if err != nil {
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.
// 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 {
return []db.Package{options[0]}, nil
}
@@ -128,7 +129,7 @@ func PkgPrompt(options []db.Package, verb string, interactive bool, t translate.
prompt := &survey.MultiSelect{
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
diff --git a/internal/config/config.go b/internal/config/config.go
index 8d1c54e..0350446 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -22,10 +22,11 @@ import (
"os"
"github.com/pelletier/go-toml/v2"
+ "go.elara.ws/logger/log"
"go.elara.ws/lure/internal/types"
)
-var defaultConfig = types.Config{
+var defaultConfig = &types.Config{
RootCmd: "sudo",
PagerStyle: "native",
IgnorePkgUpdates: []string{},
@@ -37,18 +38,30 @@ var defaultConfig = types.Config{
},
}
-// Decode decodes the config file into the given
-// pointer
-func Decode(cfg *types.Config) error {
- cfgFl, err := os.Open(ConfigPath)
- if err != nil {
- return err
- }
- defer cfgFl.Close()
+var config *types.Config
- // Write defaults to pointer in case some values are not set in the config
- *cfg = defaultConfig
- // Set repos to nil so as to avoid a duplicate default
- cfg.Repos = nil
- return toml.NewDecoder(cfgFl).Decode(cfg)
+func Config() *types.Config {
+ if config == nil {
+ cfgFl, err := os.Open(GetPaths().ConfigPath)
+ if err != nil {
+ 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
}
diff --git a/internal/config/dirs.go b/internal/config/dirs.go
index 9785af0..90844d1 100644
--- a/internal/config/dirs.go
+++ b/internal/config/dirs.go
@@ -26,69 +26,69 @@ import (
"go.elara.ws/logger/log"
)
-var (
+type Paths struct {
ConfigDir string
ConfigPath string
CacheDir string
RepoDir string
PkgsDir string
DBPath string
-)
-
-// DBPresent is true if the database
-// was present when LURE was started
-var DBPresent bool
-
-func init() {
- cfgDir, err := os.UserConfigDir()
- if err != nil {
- log.Fatal("Unable to detect user config directory").Err(err).Send()
- }
-
- ConfigDir = filepath.Join(cfgDir, "lure")
-
- err = os.MkdirAll(ConfigDir, 0o755)
- if err != nil {
- log.Fatal("Unable to create LURE config directory").Err(err).Send()
- }
-
- ConfigPath = filepath.Join(ConfigDir, "lure.toml")
-
- if _, err := os.Stat(ConfigPath); err != nil {
- cfgFl, err := os.Create(ConfigPath)
- if err != nil {
- log.Fatal("Unable to create LURE config file").Err(err).Send()
- }
-
- err = toml.NewEncoder(cfgFl).Encode(&defaultConfig)
- if err != nil {
- log.Fatal("Error encoding default configuration").Err(err).Send()
- }
-
- cfgFl.Close()
- }
-
- cacheDir, err := os.UserCacheDir()
- if err != nil {
- log.Fatal("Unable to detect cache directory").Err(err).Send()
- }
-
- CacheDir = filepath.Join(cacheDir, "lure")
- RepoDir = filepath.Join(CacheDir, "repo")
- PkgsDir = filepath.Join(CacheDir, "pkgs")
-
- err = os.MkdirAll(RepoDir, 0o755)
- if err != nil {
- log.Fatal("Unable to create repo cache directory").Err(err).Send()
- }
-
- err = os.MkdirAll(PkgsDir, 0o755)
- if err != nil {
- log.Fatal("Unable to create package cache directory").Err(err).Send()
- }
-
- DBPath = filepath.Join(CacheDir, "db")
-
- fi, err := os.Stat(DBPath)
- DBPresent = err == nil && !fi.IsDir()
+}
+
+var paths *Paths
+
+func GetPaths() *Paths {
+ if paths == nil {
+ paths = &Paths{}
+
+ cfgDir, err := os.UserConfigDir()
+ if err != nil {
+ log.Fatal("Unable to detect user config directory").Err(err).Send()
+ }
+
+ paths.ConfigDir = filepath.Join(cfgDir, "lure")
+
+ err = os.MkdirAll(paths.ConfigDir, 0o755)
+ if err != nil {
+ log.Fatal("Unable to create LURE config directory").Err(err).Send()
+ }
+
+ paths.ConfigPath = filepath.Join(paths.ConfigDir, "lure.toml")
+
+ if _, err := os.Stat(paths.ConfigPath); err != nil {
+ cfgFl, err := os.Create(paths.ConfigPath)
+ if err != nil {
+ log.Fatal("Unable to create LURE config file").Err(err).Send()
+ }
+
+ err = toml.NewEncoder(cfgFl).Encode(&defaultConfig)
+ if err != nil {
+ log.Fatal("Error encoding default configuration").Err(err).Send()
+ }
+
+ cfgFl.Close()
+ }
+
+ cacheDir, err := os.UserCacheDir()
+ if err != nil {
+ log.Fatal("Unable to detect cache directory").Err(err).Send()
+ }
+
+ paths.CacheDir = filepath.Join(cacheDir, "lure")
+ paths.RepoDir = filepath.Join(paths.CacheDir, "repo")
+ paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs")
+
+ err = os.MkdirAll(paths.RepoDir, 0o755)
+ if err != nil {
+ log.Fatal("Unable to create repo cache directory").Err(err).Send()
+ }
+
+ err = os.MkdirAll(paths.PkgsDir, 0o755)
+ if err != nil {
+ log.Fatal("Unable to create package cache directory").Err(err).Send()
+ }
+
+ paths.DBPath = filepath.Join(paths.CacheDir, "db")
+ }
+ return paths
}
diff --git a/internal/config/lang.go b/internal/config/lang.go
index 483f0f6..72490cb 100644
--- a/internal/config/lang.go
+++ b/internal/config/lang.go
@@ -26,16 +26,23 @@ import (
"golang.org/x/text/language"
)
-var Language language.Tag
+var (
+ lang language.Tag
+ langSet bool
+)
-func init() {
- lang := SystemLang()
- tag, err := language.Parse(lang)
- if err != nil {
- log.Fatal("Error parsing system language").Err(err).Send()
+func Language() language.Tag {
+ if !langSet {
+ syslang := SystemLang()
+ tag, err := language.Parse(syslang)
+ 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()
- Language = language.Make(base.String())
+ return lang
}
func SystemLang() string {
diff --git a/internal/config/version.go b/internal/config/version.go
index 37fb029..118804a 100644
--- a/internal/config/version.go
+++ b/internal/config/version.go
@@ -20,5 +20,7 @@ package config
import _ "embed"
+//go:generate ../../scripts/gen-version.sh
+
//go:embed version.txt
var Version string
diff --git a/internal/cpu/cpu.go b/internal/cpu/cpu.go
index ff3f349..8204fec 100644
--- a/internal/cpu/cpu.go
+++ b/internal/cpu/cpu.go
@@ -21,14 +21,15 @@ package cpu
import (
"os"
"runtime"
+ "strconv"
"strings"
"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
-func ARMVariant() string {
+func armVariant() string {
armEnv := os.Getenv("LURE_ARM_VARIANT")
// ensure value has "arm" prefix, such as arm5 or arm6
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
func Arch() string {
arch := os.Getenv("LURE_ARCH")
@@ -79,20 +52,65 @@ func Arch() string {
arch = runtime.GOARCH
}
if arch == "arm" {
- arch = ARMVariant()
+ arch = armVariant()
}
return arch
}
-// Arches returns all the architectures the system is compatible with
-func Arches() []string {
- arch := os.Getenv("LURE_ARCH")
- if arch == "" {
- arch = runtime.GOARCH
+func IsCompatibleWith(target string, list []string) bool {
+ if target == "all" {
+ return true
}
- if strings.HasPrefix(arch, "arm") {
- return append(CompatibleARM(arch), "arm")
- } else {
- return []string{Arch()}
+
+ for _, arch := range list {
+ if strings.HasPrefix(target, "arm") && strings.HasPrefix(arch, "arm") {
+ 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)
}
diff --git a/internal/db/db.go b/internal/db/db.go
index 74d0016..8c977af 100644
--- a/internal/db/db.go
+++ b/internal/db/db.go
@@ -62,40 +62,52 @@ type version struct {
Version int `db:"version"`
}
-func Open(dsn string) (*sqlx.DB, error) {
- if dsn != ":memory:" {
- fi, err := os.Stat(config.DBPath)
- 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()
+var (
+ conn *sqlx.DB
+ closed = true
+)
- err = os.RemoveAll(config.DBPath)
- if err != nil {
- log.Fatal("Error removing old database").Err(err).Send()
- }
- config.DBPresent = false
- }
- }
+func DB() *sqlx.DB {
+ if conn != nil && !closed {
+ return conn
}
-
- db, err := sqlx.Open("sqlite", dsn)
+ db, err := Open(config.GetPaths().DBPath)
if err != nil {
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 {
- 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
}
+func Close() error {
+ closed = true
+ if conn != nil {
+ return conn.Close()
+ } else {
+ return nil
+ }
+}
+
// Init initializes the database
-func Init(db *sqlx.DB, dsn string) error {
- *db = *db.Unsafe()
- _, err := db.Exec(`
+func initDB(dsn string) error {
+ conn = conn.Unsafe()
+ _, err := conn.Exec(`
CREATE TABLE IF NOT EXISTS pkgs (
name TEXT NOT NULL,
repository TEXT NOT NULL,
@@ -123,49 +135,57 @@ func Init(db *sqlx.DB, dsn string) error {
return err
}
- ver, ok := GetVersion(db)
+ ver, ok := GetVersion()
if !ok {
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 {
log.Warn("Database version mismatch; rebuilding").Int("version", ver).Int("expected", CurrentVersion).Send()
- db.Close()
- err = os.Remove(config.DBPath)
+ conn.Close()
+ err = os.Remove(config.GetPaths().DBPath)
if err != nil {
return err
}
- config.DBPresent = false
tdb, err := Open(dsn)
if err != nil {
return err
}
- *db = *tdb
+ conn = tdb
}
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
- 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 {
return 0, false
}
return ver.Version, true
}
-func addVersion(db *sqlx.DB, ver int) error {
- _, err := db.Exec(`INSERT INTO lure_db_version(version) VALUES (?);`, ver)
+func addVersion(ver int) error {
+ _, err := DB().Exec(`INSERT INTO lure_db_version(version) VALUES (?);`, ver)
return err
}
// InsertPackage adds a package to the database
-func InsertPackage(db *sqlx.DB, pkg Package) error {
- _, err := db.NamedExec(`
+func InsertPackage(pkg Package) error {
+ _, err := DB().NamedExec(`
INSERT OR REPLACE INTO pkgs (
name,
repository,
@@ -204,8 +224,8 @@ func InsertPackage(db *sqlx.DB, pkg Package) error {
}
// GetPkgs returns a result containing packages that match the where conditions
-func GetPkgs(db *sqlx.DB, where string, args ...any) (*sqlx.Rows, error) {
- stream, err := db.Queryx("SELECT * FROM pkgs WHERE "+where, args...)
+func GetPkgs(where string, args ...any) (*sqlx.Rows, error) {
+ stream, err := DB().Queryx("SELECT * FROM pkgs WHERE "+where, args...)
if err != nil {
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
-func GetPkg(db *sqlx.DB, where string, args ...any) (*Package, error) {
+func GetPkg(where string, args ...any) (*Package, error) {
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
}
// DeletePkgs deletes all packages matching the where conditions
-func DeletePkgs(db *sqlx.DB, where string, args ...any) error {
- _, err := db.Exec("DELETE FROM pkgs WHERE "+where, args...)
+func DeletePkgs(where string, args ...any) error {
+ _, err := DB().Exec("DELETE FROM pkgs WHERE "+where, args...)
return err
}
diff --git a/internal/db/db_test.go b/internal/db/db_test.go
index 272b9ad..96b3129 100644
--- a/internal/db/db_test.go
+++ b/internal/db/db_test.go
@@ -37,11 +37,11 @@ var testPkg = db.Package{
"ru": "Проверочный пакет",
}),
Homepage: db.NewJSON(map[string]string{
- "en": "https://lure.arsenm.dev",
+ "en": "https://lure.elara.ws/",
}),
Maintainer: db.NewJSON(map[string]string{
- "en": "Arsen Musayelyan ",
- "ru": "Арсен Мусаелян ",
+ "en": "Elara Musayelyan ",
+ "ru": "Элара Мусаелян ",
}),
Architectures: db.NewJSON([]string{"arm64", "amd64"}),
Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}),
@@ -59,23 +59,18 @@ var testPkg = db.Package{
}
func TestInit(t *testing.T) {
- gdb, err := sqlx.Open("sqlite", ":memory:")
+ _, err := db.Open(":memory:")
if err != nil {
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 {
t.Fatalf("Expected no error, got %s", err)
}
- _, err = gdb.Exec("SELECT * FROM pkgs")
- if err != nil {
- t.Fatalf("Expected no error, got %s", err)
- }
-
- ver, ok := db.GetVersion(gdb)
+ ver, ok := db.GetVersion()
if !ok {
t.Errorf("Expected version to be present")
} else if ver != db.CurrentVersion {
@@ -84,19 +79,19 @@ func TestInit(t *testing.T) {
}
func TestInsertPackage(t *testing.T) {
- gdb, err := db.Open(":memory:")
+ _, err := db.Open(":memory:")
if err != nil {
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 {
t.Fatalf("Expected no error, got %s", err)
}
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 {
t.Fatalf("Expected no error, got %s", err)
}
@@ -107,28 +102,28 @@ func TestInsertPackage(t *testing.T) {
}
func TestGetPkgs(t *testing.T) {
- gdb, err := db.Open(":memory:")
+ _, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
- defer gdb.Close()
+ defer db.Close()
x1 := testPkg
x1.Name = "x1"
x2 := testPkg
x2.Name = "x2"
- err = db.InsertPackage(gdb, x1)
+ err = db.InsertPackage(x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
- err = db.InsertPackage(gdb, x2)
+ err = db.InsertPackage(x2)
if err != nil {
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 {
t.Fatalf("Expected no error, got %s", err)
}
@@ -147,28 +142,28 @@ func TestGetPkgs(t *testing.T) {
}
func TestGetPkg(t *testing.T) {
- gdb, err := db.Open(":memory:")
+ _, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
- defer gdb.Close()
+ defer db.Close()
x1 := testPkg
x1.Name = "x1"
x2 := testPkg
x2.Name = "x2"
- err = db.InsertPackage(gdb, x1)
+ err = db.InsertPackage(x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
- err = db.InsertPackage(gdb, x2)
+ err = db.InsertPackage(x2)
if err != nil {
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 {
t.Fatalf("Expected no error, got %s", err)
}
@@ -183,34 +178,34 @@ func TestGetPkg(t *testing.T) {
}
func TestDeletePkgs(t *testing.T) {
- gdb, err := db.Open(":memory:")
+ _, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
- defer gdb.Close()
+ defer db.Close()
x1 := testPkg
x1.Name = "x1"
x2 := testPkg
x2.Name = "x2"
- err = db.InsertPackage(gdb, x1)
+ err = db.InsertPackage(x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
- err = db.InsertPackage(gdb, x2)
+ err = db.InsertPackage(x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
- err = db.DeletePkgs(gdb, "name = 'x1'")
+ err = db.DeletePkgs("name = 'x1'")
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
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 {
t.Errorf("Expected no error, got %s", err)
}
@@ -221,11 +216,11 @@ func TestDeletePkgs(t *testing.T) {
}
func TestJsonArrayContains(t *testing.T) {
- gdb, err := db.Open(":memory:")
+ _, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
- defer gdb.Close()
+ defer db.Close()
x1 := testPkg
x1.Name = "x1"
@@ -233,18 +228,18 @@ func TestJsonArrayContains(t *testing.T) {
x2.Name = "x2"
x2.Provides.Val = append(x2.Provides.Val, "x")
- err = db.InsertPackage(gdb, x1)
+ err = db.InsertPackage(x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
- err = db.InsertPackage(gdb, x2)
+ err = db.InsertPackage(x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
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 {
t.Fatalf("Expected no error, got %s", err)
}
diff --git a/internal/dlcache/dlcache.go b/internal/dlcache/dlcache.go
index c5f9b93..bad2089 100644
--- a/internal/dlcache/dlcache.go
+++ b/internal/dlcache/dlcache.go
@@ -28,8 +28,10 @@ import (
"go.elara.ws/lure/internal/config"
)
-// BasePath stores the base path to the download cache
-var BasePath = filepath.Join(config.CacheDir, "dl")
+// BasePath returns the base path of the download cache
+func BasePath() string {
+ return filepath.Join(config.GetPaths().RepoDir, "dl")
+}
// New creates a new directory with the given ID in the cache.
// If a directory with the same ID already exists,
@@ -39,7 +41,7 @@ func New(id string) (string, error) {
if err != nil {
return "", err
}
- itemPath := filepath.Join(BasePath, h)
+ itemPath := filepath.Join(BasePath(), h)
fi, err := os.Stat(itemPath)
if err == nil || (fi != nil && !fi.IsDir()) {
@@ -67,7 +69,7 @@ func Get(id string) (string, bool) {
if err != nil {
return "", false
}
- itemPath := filepath.Join(BasePath, h)
+ itemPath := filepath.Join(BasePath(), h)
_, err = os.Stat(itemPath)
if err != nil {
diff --git a/internal/dlcache/dlcache_test.go b/internal/dlcache/dlcache_test.go
index 4c0b923..986e9d4 100644
--- a/internal/dlcache/dlcache_test.go
+++ b/internal/dlcache/dlcache_test.go
@@ -26,6 +26,7 @@ import (
"path/filepath"
"testing"
+ "go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/dlcache"
)
@@ -34,7 +35,7 @@ func init() {
if err != nil {
panic(err)
}
- dlcache.BasePath = dir
+ config.GetPaths().RepoDir = dir
}
func TestNew(t *testing.T) {
@@ -44,7 +45,7 @@ func TestNew(t *testing.T) {
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 {
t.Errorf("Expected %s, got %s", exp, dir)
}
diff --git a/internal/overrides/overrides.go b/internal/overrides/overrides.go
index 3e6c741..d8b915b 100644
--- a/internal/overrides/overrides.go
+++ b/internal/overrides/overrides.go
@@ -58,7 +58,10 @@ func Resolve(info *distro.OSRelease, opts *Opts) ([]string, error) {
return nil, err
}
- architectures := cpu.Arches()
+ architectures, err := cpu.CompatibleArches(cpu.Arch())
+ if err != nil {
+ return nil, err
+ }
distros := []string{info.ID}
if opts.LikeDistros {
@@ -66,47 +69,38 @@ func Resolve(info *distro.OSRelease, opts *Opts) ([]string, error) {
}
var out []string
- for _, arch := range architectures {
+ for _, lang := range langs {
for _, distro := range distros {
- if opts.Name == "" {
- out = append(
- out,
- arch+"_"+distro,
- distro,
- )
- } else {
- out = append(
- out,
- opts.Name+"_"+arch+"_"+distro,
- opts.Name+"_"+distro,
- )
+ for _, arch := range architectures {
+ out = append(out, opts.Name+"_"+arch+"_"+distro+"_"+lang)
}
+
+ out = append(out, opts.Name+"_"+distro+"_"+lang)
}
- if opts.Name == "" {
- out = append(out, arch)
- } else {
- out = append(out, opts.Name+"_"+arch)
+
+ for _, arch := range architectures {
+ out = append(out, opts.Name+"_"+arch+"_"+lang)
}
+
+ 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)
for index, item := range out {
- out[index] = 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...)
+ out[index] = strings.TrimPrefix(strings.ReplaceAll(item, "-", "_"), "_")
}
return out, nil
diff --git a/internal/overrides/overrides_test.go b/internal/overrides/overrides_test.go
index 2927944..99b886c 100644
--- a/internal/overrides/overrides_test.go
+++ b/internal/overrides/overrides_test.go
@@ -19,6 +19,7 @@
package overrides_test
import (
+ "os"
"reflect"
"testing"
@@ -46,6 +47,7 @@ func TestResolve(t *testing.T) {
"amd64_fedora_en",
"fedora_en",
"amd64_en",
+ "en",
"amd64_centos",
"centos",
"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) {
names, err := overrides.Resolve(info, &overrides.Opts{
Overrides: true,
@@ -139,9 +178,11 @@ func TestResolveLangs(t *testing.T) {
"amd64_centos_en",
"centos_en",
"amd64_en",
+ "en",
"amd64_centos_ru",
"centos_ru",
"amd64_ru",
+ "ru",
"amd64_centos",
"centos",
"amd64",
diff --git a/internal/pager/pager.go b/internal/pager/pager.go
index fe34ead..d1d91bb 100644
--- a/internal/pager/pager.go
+++ b/internal/pager/pager.go
@@ -34,13 +34,13 @@ var (
)
func init() {
- b := lipgloss.RoundedBorder()
- b.Right = "\u251C"
- titleStyle = lipgloss.NewStyle().BorderStyle(b).Padding(0, 1)
+ b1 := lipgloss.RoundedBorder()
+ b1.Right = "\u251C"
+ titleStyle = lipgloss.NewStyle().BorderStyle(b1).Padding(0, 1)
- b = lipgloss.RoundedBorder()
- b.Left = "\u2524"
- infoStyle = titleStyle.Copy().BorderStyle(b)
+ b2 := lipgloss.RoundedBorder()
+ b2.Left = "\u2524"
+ infoStyle = titleStyle.Copy().BorderStyle(b2)
}
type Pager struct {
diff --git a/internal/repos/find.go b/internal/repos/find.go
index 221bc39..1e9310d 100644
--- a/internal/repos/find.go
+++ b/internal/repos/find.go
@@ -18,15 +18,12 @@
package repos
-import (
- "github.com/jmoiron/sqlx"
- "go.elara.ws/lure/internal/db"
-)
+import "go.elara.ws/lure/internal/db"
// 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 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{}
notFound := []string(nil)
@@ -35,7 +32,7 @@ func FindPkgs(gdb *sqlx.DB, pkgs []string) (map[string][]db.Package, []string, e
continue
}
- result, err := db.GetPkgs(gdb, "name LIKE ?", pkgName)
+ result, err := db.GetPkgs("name LIKE ?", pkgName)
if err != nil {
return nil, nil, err
}
@@ -54,7 +51,7 @@ func FindPkgs(gdb *sqlx.DB, pkgs []string) (map[string][]db.Package, []string, e
result.Close()
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 {
return nil, nil, err
}
diff --git a/internal/repos/find_test.go b/internal/repos/find_test.go
index f88ebeb..35d40fe 100644
--- a/internal/repos/find_test.go
+++ b/internal/repos/find_test.go
@@ -30,18 +30,18 @@ import (
)
func TestFindPkgs(t *testing.T) {
- gdb, err := db.Open(":memory:")
+ _, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
- defer gdb.Close()
+ defer db.Close()
setCfgDirs(t)
defer removeCacheDir(t)
ctx := context.Background()
- err = repos.Pull(ctx, gdb, []types.Repo{
+ err = repos.Pull(ctx, []types.Repo{
{
Name: "default",
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)
}
- found, notFound, err := repos.FindPkgs(gdb, []string{"itd", "nonexistentpackage1", "nonexistentpackage2"})
+ found, notFound, err := repos.FindPkgs([]string{"itd", "nonexistentpackage1", "nonexistentpackage2"})
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -81,16 +81,16 @@ func TestFindPkgs(t *testing.T) {
}
func TestFindPkgsEmpty(t *testing.T) {
- gdb, err := db.Open(":memory:")
+ _, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
- defer gdb.Close()
+ defer db.Close()
setCfgDirs(t)
defer removeCacheDir(t)
- err = db.InsertPackage(gdb, db.Package{
+ err = db.InsertPackage(db.Package{
Name: "test1",
Repository: "default",
Version: "0.0.1",
@@ -105,7 +105,7 @@ func TestFindPkgsEmpty(t *testing.T) {
t.Fatalf("Expected no error, got %s", err)
}
- err = db.InsertPackage(gdb, db.Package{
+ err = db.InsertPackage(db.Package{
Name: "test2",
Repository: "default",
Version: "0.0.1",
@@ -120,7 +120,7 @@ func TestFindPkgsEmpty(t *testing.T) {
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 {
t.Fatalf("Expected no error, got %s", err)
}
diff --git a/internal/repos/pull.go b/internal/repos/pull.go
index d9c52cf..fe3e45b 100644
--- a/internal/repos/pull.go
+++ b/internal/repos/pull.go
@@ -33,7 +33,6 @@ import (
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/diff"
- "github.com/jmoiron/sqlx"
"github.com/pelletier/go-toml/v2"
"go.elara.ws/logger/log"
"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
// 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.
-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 {
repoURL, err := url.Parse(repo.URL)
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()
- repoDir := filepath.Join(config.RepoDir, repo.Name)
+ repoDir := filepath.Join(config.GetPaths().RepoDir, repo.Name)
var repoFS billy.Filesystem
gitDir := filepath.Join(repoDir, ".git")
@@ -89,7 +88,7 @@ func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
repoFS = w.Filesystem
// 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()
if err != nil {
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
// empty. In this case, we need to update the DB fully
// rather than just incrementally.
- if config.DBPresent {
- err = processRepoChanges(ctx, repo, r, w, old, new, gdb)
+ if db.IsEmpty() {
+ err = processRepoChanges(ctx, repo, r, w, old, new)
if err != nil {
return err
}
} else {
- err = processRepoFull(ctx, repo, repoDir, gdb)
+ err = processRepoFull(ctx, repo, repoDir)
if err != nil {
return err
}
@@ -129,7 +128,7 @@ func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
return err
}
- err = processRepoFull(ctx, repo, repoDir, gdb)
+ err = processRepoFull(ctx, repo, repoDir)
if err != nil {
return err
}
@@ -171,7 +170,7 @@ type action struct {
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())
if err != nil {
return err
@@ -265,7 +264,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
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 {
return err
}
@@ -300,7 +299,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
resolveOverrides(runner, &pkg)
- err = db.InsertPackage(gdb, pkg)
+ err = db.InsertPackage(pkg)
if err != nil {
return err
}
@@ -326,7 +325,7 @@ func isValid(from, to diff.File) bool {
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")
matches, err := filepath.Glob(glob)
if err != nil {
@@ -370,7 +369,7 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *
resolveOverrides(runner, &pkg)
- err = db.InsertPackage(gdb, pkg)
+ err = db.InsertPackage(pkg)
if err != nil {
return err
}
diff --git a/internal/repos/pull_test.go b/internal/repos/pull_test.go
index d64983a..0c72a1c 100644
--- a/internal/repos/pull_test.go
+++ b/internal/repos/pull_test.go
@@ -33,50 +33,52 @@ import (
func setCfgDirs(t *testing.T) {
t.Helper()
+ paths := config.GetPaths()
+
var err error
- config.CacheDir, err = os.MkdirTemp("/tmp", "lure-pull-test.*")
+ paths.CacheDir, err = os.MkdirTemp("/tmp", "lure-pull-test.*")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
- config.RepoDir = filepath.Join(config.CacheDir, "repo")
- config.PkgsDir = filepath.Join(config.CacheDir, "pkgs")
+ paths.RepoDir = filepath.Join(paths.CacheDir, "repo")
+ paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs")
- err = os.MkdirAll(config.RepoDir, 0o755)
+ err = os.MkdirAll(paths.RepoDir, 0o755)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
- err = os.MkdirAll(config.PkgsDir, 0o755)
+ err = os.MkdirAll(paths.PkgsDir, 0o755)
if err != nil {
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) {
t.Helper()
- err := os.RemoveAll(config.CacheDir)
+ err := os.RemoveAll(config.GetPaths().CacheDir)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
}
func TestPull(t *testing.T) {
- gdb, err := db.Open(":memory:")
+ _, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
- defer gdb.Close()
+ defer db.Close()
setCfgDirs(t)
defer removeCacheDir(t)
ctx := context.Background()
- err = repos.Pull(ctx, gdb, []types.Repo{
+ err = repos.Pull(ctx, []types.Repo{
{
Name: "default",
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)
}
- result, err := db.GetPkgs(gdb, "name LIKE 'itd%'")
+ result, err := db.GetPkgs("name LIKE 'itd%'")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
diff --git a/internal/shutils/decoder/decoder.go b/internal/shutils/decoder/decoder.go
index 2c8164b..4f308c4 100644
--- a/internal/shutils/decoder/decoder.go
+++ b/internal/shutils/decoder/decoder.go
@@ -56,16 +56,16 @@ func (ite InvalidTypeError) Error() string {
// Decoder provides methods for decoding variable values
type Decoder struct {
info *distro.OSRelease
- runner *interp.Runner
+ Runner *interp.Runner
// Enable distro overrides (true by default)
Overrides bool
- // Enable using like distros for overrides (true by default)
+ // Enable using like distros for overrides
LikeDistros bool
}
// New creates a new variable 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.
@@ -173,7 +173,7 @@ func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) {
}
return func(ctx context.Context, opts ...interp.RunnerOption) error {
- sub := d.runner.Subshell()
+ sub := d.Runner.Subshell()
for _, opt := range opts {
opt(sub)
}
@@ -188,7 +188,7 @@ func (d *Decoder) getFunc(name string) *syntax.Stmt {
}
for _, fnName := range names {
- fn, ok := d.runner.Funcs[fnName]
+ fn, ok := d.Runner.Funcs[fnName]
if ok {
return fn
}
@@ -205,11 +205,11 @@ func (d *Decoder) getVar(name string) *expand.Variable {
}
for _, varName := range names {
- val, ok := d.runner.Vars[varName]
+ val, ok := d.Runner.Vars[varName]
if ok {
// Resolve nameref variables
_, 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 ""
diff --git a/internal/shutils/exec_test.go b/internal/shutils/exec_test.go
index be14a47..c3287e0 100644
--- a/internal/shutils/exec_test.go
+++ b/internal/shutils/exec_test.go
@@ -36,8 +36,8 @@ const testScript = `
release=1
epoch=2
desc="Test package"
- homepage='https://lure.arsenm.dev'
- maintainer='Arsen Musayelyan '
+ homepage='https://lure.elara.ws'
+ maintainer='Elara Musayelyan '
architectures=('arm64' 'amd64')
license=('GPL-3.0-or-later')
provides=('test')
diff --git a/helpers.go b/internal/shutils/helpers/helpers.go
similarity index 97%
rename from helpers.go
rename to internal/shutils/helpers/helpers.go
index 10aac3b..f43547e 100644
--- a/helpers.go
+++ b/internal/shutils/helpers/helpers.go
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package main
+package helpers
import (
"errors"
@@ -40,7 +40,8 @@ var (
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-systemd-user": installHelperCmd("/usr/lib/systemd/user", 0o644),
"install-systemd": installHelperCmd("/usr/lib/systemd/system", 0o644),
@@ -54,8 +55,9 @@ var helpers = shutils.ExecFuncs{
"git-version": gitVersionCmd,
}
-// rHelpers contains restricted read-only helpers that don't modify any state
-var rHelpers = shutils.ExecFuncs{
+// Restricted contains restricted read-only helper commands
+// that don't modify any state
+var Restricted = shutils.ExecFuncs{
"git-version": gitVersionCmd,
}
diff --git a/internal/shutils/restricted.go b/internal/shutils/restricted.go
index e21e54e..e9325bb 100644
--- a/internal/shutils/restricted.go
+++ b/internal/shutils/restricted.go
@@ -21,7 +21,8 @@ package shutils
import (
"context"
"io"
- "os"
+ "io/fs"
+ "path/filepath"
"strings"
"time"
@@ -30,33 +31,36 @@ import (
)
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 {
- if strings.HasPrefix(s, allowedPrefix) {
+ if strings.HasPrefix(path, allowedPrefix) {
return interp.DefaultReadDirHandler()(ctx, s)
}
}
- return nil, os.ErrNotExist
+ return nil, fs.ErrNotExist
}
}
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 {
- if strings.HasPrefix(s, allowedPrefix) {
+ if strings.HasPrefix(path, allowedPrefix) {
return interp.DefaultStatHandler()(ctx, s, b)
}
}
- return nil, os.ErrNotExist
+ return nil, fs.ErrNotExist
}
}
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 {
- if strings.HasPrefix(s, allowedPrefix) {
+ if strings.HasPrefix(path, allowedPrefix) {
return interp.DefaultOpenHandler()(ctx, s, i, fm)
}
}
diff --git a/translations/lure.en.toml b/internal/translations/files/lure.en.toml
similarity index 100%
rename from translations/lure.en.toml
rename to internal/translations/files/lure.en.toml
diff --git a/translations/lure.ru.toml b/internal/translations/files/lure.ru.toml
similarity index 100%
rename from translations/lure.ru.toml
rename to internal/translations/files/lure.ru.toml
diff --git a/internal/translations/translations.go b/internal/translations/translations.go
new file mode 100644
index 0000000..fb10ad4
--- /dev/null
+++ b/internal/translations/translations.go
@@ -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)
+}
diff --git a/internal/types/build.go b/internal/types/build.go
new file mode 100644
index 0000000..6f1e226
--- /dev/null
+++ b/internal/types/build.go
@@ -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
+}
diff --git a/list.go b/list.go
index 24249a8..bcd0f1d 100644
--- a/list.go
+++ b/list.go
@@ -23,71 +23,83 @@ import (
"github.com/urfave/cli/v2"
"go.elara.ws/logger/log"
+ "go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/db"
"go.elara.ws/lure/internal/repos"
"go.elara.ws/lure/manager"
"golang.org/x/exp/slices"
)
-func listCmd(c *cli.Context) error {
- err := repos.Pull(c.Context, gdb, cfg.Repos)
- if err != nil {
- log.Fatal("Error pulling repositories").Err(err).Send()
- }
-
- where := "true"
- args := []any(nil)
- if c.NArg() > 0 {
- where = "name LIKE ? OR json_array_contains(provides, ?)"
- args = []any{c.Args().First(), c.Args().First()}
- }
-
- 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})
+var listCmd = &cli.Command{
+ Name: "list",
+ Usage: "List LURE repo packages",
+ Aliases: []string{"ls"},
+ Flags: []cli.Flag{
+ &cli.BoolFlag{
+ Name: "installed",
+ Aliases: []string{"I"},
+ },
+ },
+ Action: func(c *cli.Context) error {
+ err := repos.Pull(c.Context, config.Config().Repos)
if err != nil {
- log.Fatal("Error listing installed packages").Err(err).Send()
+ log.Fatal("Error pulling repositories").Err(err).Send()
}
- }
- for result.Next() {
- var pkg db.Package
- err := result.StructScan(&pkg)
+ where := "true"
+ args := []any(nil)
+ 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 {
- return err
+ log.Fatal("Error getting packages").Err(err).Send()
}
+ defer result.Close()
- if slices.Contains(cfg.IgnorePkgUpdates, pkg.Name) {
- continue
- }
-
- version := pkg.Version
+ var installed map[string]string
if c.Bool("installed") {
- instVersion, ok := installed[pkg.Name]
- if !ok {
- continue
- } else {
- version = instVersion
+ mgr := manager.Detect()
+ if mgr == nil {
+ log.Fatal("Unable to detect a supported package manager on the system").Send()
+ }
+
+ 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 {
- log.Fatal("Error iterating over packages").Err(err).Send()
- }
+ if slices.Contains(config.Config().IgnorePkgUpdates, pkg.Name) {
+ 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
+ },
}
diff --git a/main.go b/main.go
index ce05db8..fce92c0 100644
--- a/main.go
+++ b/main.go
@@ -20,13 +20,11 @@ package main
import (
"context"
- "embed"
- "fmt"
"os"
"os/signal"
"strings"
"syscall"
- "time"
+ //"time"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
@@ -34,238 +32,78 @@ import (
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/db"
+ "go.elara.ws/lure/internal/translations"
"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 translationFS embed.FS
-
-var translator translate.Translator
-
-func init() {
- 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)
+var versionCmd = &cli.Command{
+ Name: "version",
+ Usage: "Print the current LURE version and exit",
+ Action: func(ctx *cli.Context) error {
+ println(config.Version)
+ return nil
+ },
}
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()
}
- err := loadDB()
- if err != nil {
- log.Fatal("Error loading database").Err(err).Send()
- }
+ // Set the root command to the one set in the LURE config
+ manager.DefaultRootCmd = config.Config().RootCmd
ctx := context.Background()
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
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{
- 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)
+ err := app.RunContext(ctx, os.Args)
if err != nil {
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)
- }
-}
diff --git a/repo.go b/repo.go
index 80a94ee..cdf8cb5 100644
--- a/repo.go
+++ b/repo.go
@@ -32,83 +32,123 @@ import (
"golang.org/x/exp/slices"
)
-func addrepoCmd(c *cli.Context) error {
- name := c.String("name")
- repoURL := c.String("url")
+var addrepoCmd = &cli.Command{
+ Name: "addrepo",
+ 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 {
- if repo.URL == repoURL {
- log.Fatal("Repo already exists").Str("name", repo.Name).Send()
+ cfg := config.Config()
+
+ 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{
- Name: name,
- URL: repoURL,
- })
+ cfg.Repos = append(cfg.Repos, types.Repo{
+ Name: name,
+ URL: repoURL,
+ })
- cfgFl, err := os.Create(config.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 = 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
+ cfgFl, err := os.Create(config.GetPaths().ConfigPath)
+ if err != nil {
+ log.Fatal("Error opening config file").Err(err).Send()
}
- }
- 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)
- if err != nil {
- log.Fatal("Error opening config file").Err(err).Send()
- }
+ err = repos.Pull(c.Context, cfg.Repos)
+ if err != nil {
+ log.Fatal("Error pulling repos").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.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
+ return nil
+ },
}
-func refreshCmd(c *cli.Context) error {
- err := repos.Pull(c.Context, gdb, cfg.Repos)
- if err != nil {
- log.Fatal("Error pulling repos").Err(err).Send()
- }
- return nil
+var removerepoCmd = &cli.Command{
+ Name: "removerepo",
+ Usage: "Remove an existing repository",
+ Aliases: []string{"rr"},
+ Flags: []cli.Flag{
+ &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
+ },
}
diff --git a/scripts/gen-version.sh b/scripts/gen-version.sh
index 491f6de..9eb213e 100755
--- a/scripts/gen-version.sh
+++ b/scripts/gen-version.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-git describe --tags > internal/config/version.txt
\ No newline at end of file
+git describe --tags > version.txt
\ No newline at end of file
diff --git a/upgrade.go b/upgrade.go
index fa6e512..0d7f0e7 100644
--- a/upgrade.go
+++ b/upgrade.go
@@ -25,42 +25,61 @@ import (
"github.com/urfave/cli/v2"
"go.elara.ws/logger/log"
"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/repos"
+ "go.elara.ws/lure/internal/types"
"go.elara.ws/lure/manager"
"go.elara.ws/vercmp"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
-func upgradeCmd(c *cli.Context) error {
- info, err := distro.ParseOSRelease(c.Context)
- if err != nil {
- log.Fatal("Error parsing os-release file").Err(err).Send()
- }
+var upgradeCmd = &cli.Command{
+ Name: "upgrade",
+ Usage: "Upgrade all installed packages",
+ 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()
- if mgr == nil {
- log.Fatal("Unable to detect supported package manager on system").Send()
- }
+ mgr := manager.Detect()
+ if mgr == nil {
+ log.Fatal("Unable to detect a supported package manager on the system").Send()
+ }
- err = repos.Pull(c.Context, gdb, cfg.Repos)
- if err != nil {
- log.Fatal("Error pulling repos").Err(err).Send()
- }
+ err = repos.Pull(c.Context, config.Config().Repos)
+ if err != nil {
+ log.Fatal("Error pulling repos").Err(err).Send()
+ }
- updates, err := checkForUpdates(c.Context, mgr, info)
- if err != nil {
- log.Fatal("Error checking for updates").Err(err).Send()
- }
+ updates, err := checkForUpdates(c.Context, mgr, info)
+ if err != nil {
+ log.Fatal("Error checking for updates").Err(err).Send()
+ }
- if len(updates) > 0 {
- installPkgs(c.Context, updates, nil, mgr, c.Bool("clean"), c.Bool("interactive"))
- } else {
- log.Info("There is nothing to do.").Send()
- }
+ if len(updates) > 0 {
+ build.InstallPkgs(c.Context, updates, nil, types.BuildOpts{
+ Manager: mgr,
+ 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) {
@@ -70,14 +89,14 @@ func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRe
}
pkgNames := maps.Keys(installed)
- found, _, err := repos.FindPkgs(gdb, pkgNames)
+ found, _, err := repos.FindPkgs(pkgNames)
if err != nil {
return nil, err
}
var out []db.Package
for pkgName, pkgs := range found {
- if slices.Contains(cfg.IgnorePkgUpdates, pkgName) {
+ if slices.Contains(config.Config().IgnorePkgUpdates, pkgName) {
continue
}