Compare commits
143 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 43baf8024a | |||
| 8a1d0f4f54 | |||
| 806f49c472 | |||
| 55132437b3 | |||
| 8b26e96af0 | |||
| bd41075e8a | |||
| 8225f41d0e | |||
| db060db4b1 | |||
| 863b6e923a | |||
| 51b41bdd90 | |||
| 76e073c77f | |||
| 770881bf67 | |||
| be79eba4c2 | |||
| 3e6d5f57cf | |||
| db5c344b2d | |||
| a750f4630e | |||
| 852e98088b | |||
| c832359e43 | |||
| fae4337748 | |||
| f21d02e5d1 | |||
| ef98a37b4a | |||
| 3829dc4cc4 | |||
| efca9d116e | |||
| acb71b873e | |||
| 8366a2ce86 | |||
| 37a6c682b5 | |||
| 80187b0969 | |||
| cecaead5b0 | |||
| a84622e182 | |||
| dd86148c22 | |||
| 6aed71af24 | |||
| 422e41db40 | |||
| 8dc0e53700 | |||
| 891df91fee | |||
| b7922f9687 | |||
| 0b53c16f9c | |||
| 81013ce376 | |||
| eb4e2281fa | |||
| c9bc1e7fc3 | |||
| 22a74aa793 | |||
| 964c45ffc4 | |||
| 9c0d9d0a34 | |||
| a8a870ce86 | |||
| fe477ba6d1 | |||
| 6cd0802f64 | |||
| 19ced9795c | |||
| dbfcde0125 | |||
| 14078ec904 | |||
| 715ec7fbe8 | |||
| 3c260a7c5f | |||
| 5e8ed1c2a0 | |||
| c51248793e | |||
| 5ed538c2c4 | |||
| 56ba3ec644 | |||
| 5d411ac538 | |||
| 269222b688 | |||
| 79c0e7eb12 | |||
| fcd5c1c14b | |||
| 9fa3977d3a | |||
| c2d396d68f | |||
| 23fd711be2 | |||
| 8f9bdf69ad | |||
| dd33a30c35 | |||
| a106100312 | |||
| df72d95ab7 | |||
| 40cf0da2c0 | |||
| 66a6b30d87 | |||
| 4eac9cd8aa | |||
| 664f01a794 | |||
| 0ac8ccac81 | |||
| 56550a5135 | |||
| b4f4633f6a | |||
| 67b9801f42 | |||
| 7164aac0b4 | |||
| 4e71a5c35c | |||
| eaf49a4594 | |||
| c0439a2b90 | |||
| 43d6461c71 | |||
| 2e591d9f1c | |||
| 7d00c7b5fb | |||
| ead0c79139 | |||
| 1377ef1bc9 | |||
| a88adb43fe | |||
| 3663a8ef8f | |||
| 5f12d2aee2 | |||
| 99b70859d1 | |||
| 715fd6ccc9 | |||
| 9a06894cfa | |||
| 01a9f23a64 | |||
| 6013bdf8b9 | |||
| fbf0aa3b4f | |||
| e4b8348823 | |||
| 554987325b | |||
| 74051861bf | |||
| edf5b67825 | |||
| 84336e4a30 | |||
| b3479bdf91 | |||
| a2bd151837 | |||
| 46e2d3166f | |||
| 3437df8676 | |||
| 3361358b3c | |||
| 3ca052fea7 | |||
| 001e33dd2f | |||
| f30f4c7081 | |||
| 5bc81e3a30 | |||
| d941ce231e | |||
| e22bc0f10c | |||
| 8ff903b68f | |||
| 3bb7fe3690 | |||
| 26d139c34e | |||
| 8ceb61de9a | |||
| 24c807a941 | |||
| da7830d0e3 | |||
| 98a3b26a27 | |||
| da630f648d | |||
| c489f4864e | |||
| 320342cfb4 | |||
| 45ad9fbe39 | |||
| 3f2ec8ebd3 | |||
| 2c2a27c9f7 | |||
| 05a1ecea64 | |||
| d32437e8b2 | |||
| 8f95ff4676 | |||
| 7442da7105 | |||
| 07e41849e9 | |||
| 27fb08d5ba | |||
| d78064179f | |||
| 2157d9ecce | |||
| b686f810fb | |||
| c856bf0686 | |||
| c650c1dae0 | |||
| b3b6612ef2 | |||
| baf4cca4fb | |||
| e604f61151 | |||
| 8e74e58cad | |||
| be48f26e75 | |||
| b6265f4b1d | |||
| c0e535c630 | |||
| 2b6815e287 | |||
| a42c9b27e7 | |||
| e2c8335381 | |||
| b56641c659 | |||
| 61ba975e21 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,5 @@
|
||||
/lure
|
||||
/lure-api-server
|
||||
/cmd/lure-api-server/lure-api-server
|
||||
/dist/
|
||||
/internal/config/version.txt
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
- go generate
|
||||
builds:
|
||||
- id: lure
|
||||
env:
|
||||
@@ -9,10 +10,10 @@ builds:
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm
|
||||
- 386
|
||||
- arm64
|
||||
- arm
|
||||
- riscv64
|
||||
archives:
|
||||
- replacements:
|
||||
@@ -21,6 +22,7 @@ archives:
|
||||
arm64: aarch64
|
||||
nfpms:
|
||||
- id: lure
|
||||
package_name: linux-user-repository
|
||||
file_name_template: '{{.PackageName}}-{{.Version}}-{{.Os}}-{{.Arch}}'
|
||||
description: "Linux User REpository"
|
||||
replacements:
|
||||
@@ -28,18 +30,28 @@ nfpms:
|
||||
amd64: x86_64
|
||||
arm64: aarch64
|
||||
homepage: 'https://gitea.arsenm.dev/Arsen6331/lure'
|
||||
maintainer: 'Arsen Musyaelyan <arsen@arsenm.dev>'
|
||||
maintainer: 'Arsen Musayelyan <arsen@arsenm.dev>'
|
||||
license: GPLv3
|
||||
formats:
|
||||
- apk
|
||||
- deb
|
||||
- rpm
|
||||
- archlinux
|
||||
provides:
|
||||
- lure
|
||||
conflicts:
|
||||
- lure
|
||||
contents:
|
||||
- src: scripts/completion/bash
|
||||
dst: /usr/share/bash-completion/completions/lure
|
||||
- src: scripts/completion/zsh
|
||||
dst: /usr/share/zsh/site-functions/_lure
|
||||
aurs:
|
||||
- name: lure-bin
|
||||
homepage: 'https://gitea.arsenm.dev/Arsen6331/lure'
|
||||
description: "Linux User REpository"
|
||||
maintainers:
|
||||
- 'Arsen Musyaelyan <arsen@arsenm.dev>'
|
||||
- 'Arsen Musayelyan <arsen@arsenm.dev>'
|
||||
license: GPLv3
|
||||
private_key: '{{ .Env.AUR_KEY }}'
|
||||
git_url: 'ssh://aur@aur.archlinux.org/lure-bin.git'
|
||||
@@ -52,7 +64,11 @@ aurs:
|
||||
- pacman
|
||||
package: |-
|
||||
# binaries
|
||||
install -Dm755 "./lure" "${pkgdir}/usr/bin/lure"
|
||||
install -Dm755 ./lure "${pkgdir}/usr/bin/lure"
|
||||
|
||||
# completions
|
||||
install -Dm755 ./scripts/completion/bash /usr/share/bash-completion/completions/lure
|
||||
install -Dm755 ./scripts/completion/zsh /usr/share/zsh/site-functions/_lure
|
||||
release:
|
||||
gitea:
|
||||
owner: Arsen6331
|
||||
|
||||
8
.woodpecker.yml
Normal file
8
.woodpecker.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
pipeline:
|
||||
release:
|
||||
image: goreleaser/goreleaser
|
||||
commands:
|
||||
- goreleaser release
|
||||
secrets: [ gitea_token, aur_key ]
|
||||
when:
|
||||
event: tag
|
||||
15
Makefile
15
Makefile
@@ -1,13 +1,22 @@
|
||||
lure:
|
||||
PREFIX ?= /usr/local
|
||||
|
||||
lure: version.txt
|
||||
go build
|
||||
|
||||
clean:
|
||||
rm -f lure
|
||||
|
||||
install: lure
|
||||
sudo install -Dm755 lure /usr/local/bin/lure
|
||||
install: lure installmisc
|
||||
install -Dm755 lure $(DESTDIR)$(PREFIX)/bin/lure
|
||||
|
||||
installmisc:
|
||||
install -Dm755 scripts/completion/bash $(DESTDIR)$(PREFIX)/share/bash-completion/completions/lure
|
||||
install -Dm755 scripts/completion/zsh $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_lure
|
||||
|
||||
uninstall:
|
||||
rm -f /usr/local/bin/lure
|
||||
|
||||
version.txt:
|
||||
go generate ./...
|
||||
|
||||
.PHONY: install clean uninstall
|
||||
47
README.md
47
README.md
@@ -1,16 +1,31 @@
|
||||
<img src="assets/logo.png" alt="LURE Logo" width="200">
|
||||
|
||||
# LURE (Linux User REpository)
|
||||
|
||||
[](https://goreportcard.com/report/go.arsenm.dev/lure)
|
||||
[](https://ci.arsenm.dev/Arsen6331/lure)
|
||||
[](https://aur.archlinux.org/packages/lure-bin/)
|
||||
|
||||
LURE is intended to bring the AUR to all distros. It is currently in an ***alpha*** state and may not be stable. It can download a repository, build packages in it using a bash script similar to [PKGBUILD](https://wiki.archlinux.org/title/PKGBUILD), and then install them using your system package manager.
|
||||
LURE is a distro-agnostic build system for Linux, similar to the [AUR](https://wiki.archlinux.org/title/Arch_User_Repository). It is currently in an ***alpha*** state and may not be stable. It is currently able to successfully and consistently build and install packages on various distributions, but there are some bugs that still need to be ironed out.
|
||||
|
||||
LURE is written in pure Go and has zero dependencies after it's built. The only things LURE needs are a command for privilege elevation such as `sudo`, `doas`, etc. as well as a supported package manager. Currently, LURE supports `apt`, `pacman`, `apk`, `dnf`, `yum`, and `zypper`. If a supported package manager exists on your system, it will be detected and used automatically.
|
||||
LURE is written in pure Go and has zero dependencies after building. The only things LURE requires are a command for privilege elevation such as `sudo`, `doas`, etc. as well as a supported package manager. Currently, LURE supports `apt`, `pacman`, `apk`, `dnf`, `yum`, and `zypper`. If a supported package manager exists on your system, it will be detected and used automatically.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Install script
|
||||
|
||||
The LURE install script will automatically download and install the appropriate LURE package on your system. To use it, simply run the following command:
|
||||
|
||||
```bash
|
||||
curl https://www.arsenm.dev/lure.sh | bash
|
||||
```
|
||||
|
||||
**IMPORTANT**: This method is not recommended as it executes any code that is stored at that URL. In order to make sure nothing malicious is going to occur, download the script and inspect it before running.
|
||||
|
||||
### Packages
|
||||
|
||||
Distro packages and binary archives are provided at the latest Gitea release: https://gitea.arsenm.dev/Arsen6331/lure/releases/latest
|
||||
|
||||
LURE is also available on the AUR as [lure-bin](https://aur.archlinux.org/packages/lure-bin)
|
||||
@@ -27,11 +42,7 @@ sudo make install
|
||||
|
||||
## Why?
|
||||
|
||||
The AUR is an amazing feature, and it's one of the main reasons I use Arch on all my daily driver devices. It is really simple while providing really useful functionality. I feel such a solution shouldn't be stuck in only a single distro, so I made LURE.
|
||||
|
||||
Like the AUR, it uses simple bash build scripts, but it doesn't depend on bash being installed at all. It uses an embedded, pure Go implementation of bash instead. Similarly, it uses Git to download the repos and sources, but doesn't depend on Git being installed.
|
||||
|
||||
This means it's really easy to deploy LURE on any distro that it has support for and on any CPU architecture. It also supports and automatically detects many package managers, so it's not limited to just `pacman`.
|
||||
Arch Linux's AUR is a very useful feature. It's one of the main reasons I and many others use Arch on most of their devices. However, Arch is not always a good choice. Whether you're running a server that needs to be stable over long periods of time, or you're a beginner and feel intimidated by Arch, there are many different reasons not to use it. Such useful functionality should not be restricted to only a single distro. That is what LURE is meant to solve.
|
||||
|
||||
---
|
||||
|
||||
@@ -41,25 +52,31 @@ The documentation for LURE is in the [docs](docs) directory in this repo.
|
||||
|
||||
---
|
||||
|
||||
## Web Interface
|
||||
|
||||
LURE now has a web interface! It's open source, licensed under the AGPLv3 (https://gitea.arsenm.dev/Arsen6331/lure-web), and is available at https://lure.arsenm.dev.
|
||||
|
||||
---
|
||||
|
||||
## Repositories
|
||||
|
||||
Unlike the AUR, LURE supports using multiple repos. Also unlike the AUR, LURE's repos are a single git repo containing all the build scripts. Inside each LURE repo, there should be a separate directory for each package containing a `lure.sh` script, which is a PKGBUILD-like build script for LURE. The default repository is hosted on Github: https://github.com/Arsen6331/lure-repo.
|
||||
Unlike the AUR, LURE supports third-party repositories. Also unlike the AUR, LURE's repos are single git repositories containing all the build scripts. Inside each LURE repo, there is a separate directory for each package, containing a `lure.sh` script, which is a PKGBUILD-like build script for LURE. The default repository is hosted on Github: https://github.com/Arsen6331/lure-repo, and information about its packages is displayed at https://lure.arsenm.dev/pkgs.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
As mentioned before, LURE has zero dependencies after it's built. All functionality that could be pure Go is pure Go. Thanks to the following packages for making this possible:
|
||||
As mentioned before, LURE has zero dependencies after compilation. Thanks to the following projects for making this possible:
|
||||
|
||||
- Bash: https://github.com/mvdan/sh
|
||||
- Git: https://github.com/go-git/go-git
|
||||
- Archiver: https://github.com/mholt/archiver
|
||||
- nfpm: https://github.com/goreleaser/nfpm
|
||||
- https://github.com/mvdan/sh
|
||||
- https://github.com/go-git/go-git
|
||||
- https://github.com/mholt/archiver
|
||||
- https://github.com/goreleaser/nfpm
|
||||
- https://github.com/charmbracelet/bubbletea
|
||||
- https://gitlab.com/cznic/sqlite
|
||||
|
||||
---
|
||||
|
||||
## Planned Features
|
||||
|
||||
- Automated install script
|
||||
- Automated docker-based testing tool
|
||||
- Web interface for repos
|
||||
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
237
build.go
237
build.go
@@ -19,6 +19,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
@@ -28,7 +29,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
_ "github.com/goreleaser/nfpm/v2/apk"
|
||||
_ "github.com/goreleaser/nfpm/v2/arch"
|
||||
_ "github.com/goreleaser/nfpm/v2/deb"
|
||||
@@ -38,9 +38,13 @@ import (
|
||||
|
||||
"github.com/goreleaser/nfpm/v2"
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/lure/download"
|
||||
"go.arsenm.dev/lure/internal/cliutils"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/cpu"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
"go.arsenm.dev/lure/internal/shutils"
|
||||
"go.arsenm.dev/lure/internal/shutils/decoder"
|
||||
"go.arsenm.dev/lure/manager"
|
||||
@@ -76,7 +80,7 @@ type Scripts struct {
|
||||
PreInstall string `sh:"preinstall"`
|
||||
PostInstall string `sh:"postinstall"`
|
||||
PreRemove string `sh:"preremove"`
|
||||
PostRemove string `sh:"postinstall"`
|
||||
PostRemove string `sh:"postremove"`
|
||||
PreUpgrade string `sh:"preupgrade"`
|
||||
PostUpgrade string `sh:"postupgrade"`
|
||||
PreTrans string `sh:"pretrans"`
|
||||
@@ -86,21 +90,39 @@ type Scripts struct {
|
||||
func buildCmd(c *cli.Context) error {
|
||||
script := c.String("script")
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
_, pkgNames, err := buildPackage(c.Context, script, mgr)
|
||||
pkgPaths, _, err := buildPackage(c.Context, script, mgr)
|
||||
if err != nil {
|
||||
log.Fatal("Error building package").Err(err).Send()
|
||||
}
|
||||
|
||||
log.Info("Package(s) built successfully").Any("names", pkgNames).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 = os.Rename(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) ([]string, []string, error) {
|
||||
info, err := distro.ParseOSRelease(ctx)
|
||||
if err != nil {
|
||||
@@ -110,6 +132,9 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
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
|
||||
}
|
||||
|
||||
@@ -125,11 +150,19 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
|
||||
fl.Close()
|
||||
|
||||
env := genBuildEnv(info)
|
||||
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
|
||||
@@ -154,15 +187,17 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = cliutils.PromptViewScript(script, vars.Name, cfg.PagerStyle)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
|
||||
}
|
||||
|
||||
if !archMatches(vars.Architectures) {
|
||||
var buildAnyway bool
|
||||
survey.AskOne(
|
||||
&survey.Confirm{
|
||||
Message: "Your system's CPU architecture doesn't match this package. Do you want to build anyway?",
|
||||
Default: true,
|
||||
},
|
||||
&buildAnyway,
|
||||
)
|
||||
buildAnyway, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !buildAnyway {
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -170,7 +205,32 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
|
||||
log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send()
|
||||
|
||||
baseDir := filepath.Join(cacheDir, "pkgs", vars.Name)
|
||||
// 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
|
||||
}
|
||||
|
||||
baseDir := filepath.Join(config.PkgsDir, vars.Name)
|
||||
srcdir := filepath.Join(baseDir, "src")
|
||||
pkgdir := filepath.Join(baseDir, "pkg")
|
||||
|
||||
@@ -189,16 +249,38 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(vars.BuildDepends) > 0 {
|
||||
installed, err := mgr.ListInstalled(nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var buildDeps []string
|
||||
for _, pkgName := range vars.BuildDepends {
|
||||
if _, ok := installed[pkgName]; !ok {
|
||||
buildDeps = append(buildDeps, pkgName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(buildDeps) > 0 {
|
||||
found, notFound, err := repos.FindPkgs(gdb, buildDeps)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
log.Info("Installing build dependencies").Send()
|
||||
installPkgs(ctx, vars.BuildDepends, mgr)
|
||||
installPkgs(ctx, cliutils.FlattenPkgs(found, "install"), notFound, mgr)
|
||||
}
|
||||
|
||||
var builtDeps, builtNames, repoDeps []string
|
||||
if len(vars.Depends) > 0 {
|
||||
log.Info("Installing dependencies").Send()
|
||||
|
||||
scripts, notFound := findPkgs(vars.Depends)
|
||||
found, notFound, err := repos.FindPkgs(gdb, vars.Depends)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
scripts := getScriptPaths(cliutils.FlattenPkgs(found, "install"))
|
||||
for _, script := range scripts {
|
||||
pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr)
|
||||
if err != nil {
|
||||
@@ -223,11 +305,36 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
fn, ok := dec.GetFunc("prepare")
|
||||
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, srcdir)
|
||||
err = fn(ctx, interp.Dir(srcdir))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -237,7 +344,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
if ok {
|
||||
log.Info("Executing build()").Send()
|
||||
|
||||
err = fn(ctx, srcdir)
|
||||
err = fn(ctx, interp.Dir(srcdir))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -247,7 +354,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
if ok {
|
||||
log.Info("Executing package()").Send()
|
||||
|
||||
err = fn(ctx, srcdir)
|
||||
err = fn(ctx, interp.Dir(srcdir))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -264,7 +371,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
pkgInfo := &nfpm.Info{
|
||||
Name: vars.Name,
|
||||
Description: vars.Description,
|
||||
Arch: runtime.GOARCH,
|
||||
Arch: cpu.Arch(),
|
||||
Version: vars.Version,
|
||||
Release: strconv.Itoa(vars.Release),
|
||||
Homepage: vars.Homepage,
|
||||
@@ -288,10 +395,6 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
pkgInfo.Arch = "all"
|
||||
}
|
||||
|
||||
if pkgInfo.Arch == "arm" {
|
||||
pkgInfo.Arch = cpu.ARMVariant()
|
||||
}
|
||||
|
||||
contents := []*files.Content{}
|
||||
filepath.Walk(pkgdir, func(path string, fi os.FileInfo, err error) error {
|
||||
trimmed := strings.TrimPrefix(path, pkgdir)
|
||||
@@ -325,6 +428,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
link = strings.TrimPrefix(link, pkgdir)
|
||||
|
||||
contents = append(contents, &files.Content{
|
||||
Source: link,
|
||||
@@ -386,11 +490,8 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(vars.BuildDepends) > 0 {
|
||||
var removeBuildDeps bool
|
||||
err = survey.AskOne(&survey.Confirm{
|
||||
Message: "Would you like to remove build dependencies?",
|
||||
}, &removeBuildDeps)
|
||||
if len(buildDeps) > 0 {
|
||||
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove build dependencies?", false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -401,7 +502,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
AsRoot: true,
|
||||
NoConfirm: true,
|
||||
},
|
||||
vars.BuildDepends...,
|
||||
buildDeps...,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -414,17 +515,21 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
||||
return pkgPaths, pkgNames, nil
|
||||
}
|
||||
|
||||
func genBuildEnv(info *distro.OSRelease) []string {
|
||||
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_BUILD_ID="+info.BuildID,
|
||||
"DISTRO_VERSION_ID="+info.VersionID,
|
||||
"DISTRO_ID_LIKE="+strings.Join(info.Like, " "),
|
||||
|
||||
"ARCH="+runtime.GOARCH,
|
||||
"ARCH="+cpu.Arch(),
|
||||
"NCPU="+strconv.Itoa(runtime.NumCPU()),
|
||||
|
||||
"scriptdir="+scriptdir,
|
||||
)
|
||||
|
||||
return env
|
||||
@@ -442,7 +547,7 @@ func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
|
||||
EncloseGit: true,
|
||||
}
|
||||
|
||||
if bv.Checksums[i] != "SKIP" {
|
||||
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
|
||||
checksum, err := hex.DecodeString(bv.Checksums[i])
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -506,61 +611,29 @@ func setScripts(vars *BuildVars, info *nfpm.Info, scriptDir string) {
|
||||
}
|
||||
}
|
||||
|
||||
// getBuildVars only gets the build variables, while disabling exec, stat, open, and readdir
|
||||
func getBuildVars(ctx context.Context, script string, info *distro.OSRelease) (*BuildVars, error) {
|
||||
fl, err := os.Open(script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, err := syntax.NewParser().Parse(fl, "lure.sh")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fl.Close()
|
||||
|
||||
runner, err := interp.New(
|
||||
interp.Env(expand.ListEnviron()),
|
||||
interp.ExecHandler(shutils.NopExec),
|
||||
interp.StatHandler(shutils.NopStat),
|
||||
interp.OpenHandler(shutils.NopOpen),
|
||||
interp.ReadDirHandler(shutils.NopReadDir),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = runner.Run(ctx, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec := decoder.New(info, runner)
|
||||
|
||||
var vars BuildVars
|
||||
err = dec.DecodeVars(&vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &vars, nil
|
||||
}
|
||||
|
||||
// archMatches checks if your system architecture matches
|
||||
// one of the provided architectures
|
||||
func archMatches(architectures []string) bool {
|
||||
arch := runtime.GOARCH
|
||||
|
||||
if arch == "arm" {
|
||||
arch = cpu.ARMVariant()
|
||||
if slices.Contains(architectures, "all") {
|
||||
return true
|
||||
}
|
||||
|
||||
if slices.Contains(architectures, "arm") {
|
||||
architectures = append(architectures, cpu.ARMVariant())
|
||||
}
|
||||
|
||||
return slices.Contains(architectures, 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)
|
||||
}
|
||||
|
||||
// uniq removes all duplicates from string slices
|
||||
func uniq(ss ...*[]string) {
|
||||
for _, s := range ss {
|
||||
slices.Sort(*s)
|
||||
|
||||
3
cmd/lure-api-server/Dockerfile
Normal file
3
cmd/lure-api-server/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM alpine:latest
|
||||
COPY lure-api-server /usr/bin/lure-api-server
|
||||
ENTRYPOINT lure-api-server
|
||||
3
cmd/lure-api-server/README.md
Normal file
3
cmd/lure-api-server/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# lure-api-server
|
||||
|
||||
`lure-api-server` is the backend API server for lure-web, the web interface for LURE.
|
||||
127
cmd/lure-api-server/api.go
Normal file
127
cmd/lure-api-server/api.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/twitchtv/twirp"
|
||||
"go.arsenm.dev/lure/internal/api"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
)
|
||||
|
||||
type lureWebAPI struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.SearchResponse, error) {
|
||||
query := "(name LIKE ? OR description LIKE ? OR json_array_contains(provides, ?))"
|
||||
args := []any{"%" + req.Query + "%", "%" + req.Query + "%", req.Query}
|
||||
|
||||
if req.FilterValue != nil && req.FilterType != api.FILTER_TYPE_NO_FILTER {
|
||||
switch req.FilterType {
|
||||
case api.FILTER_TYPE_IN_REPOSITORY:
|
||||
query += " AND repository = ?"
|
||||
case api.FILTER_TYPE_SUPPORTS_ARCH:
|
||||
query += " AND json_array_contains(architectures, ?)"
|
||||
}
|
||||
args = append(args, *req.FilterValue)
|
||||
}
|
||||
|
||||
if req.SortBy != api.SORT_BY_UNSORTED {
|
||||
switch req.SortBy {
|
||||
case api.SORT_BY_NAME:
|
||||
query += " ORDER BY name"
|
||||
case api.SORT_BY_REPOSITORY:
|
||||
query += " ORDER BY repository"
|
||||
case api.SORT_BY_VERSION:
|
||||
query += " ORDER BY version"
|
||||
}
|
||||
}
|
||||
|
||||
if req.Limit != 0 {
|
||||
query += " LIMIT " + strconv.FormatInt(req.Limit, 10)
|
||||
}
|
||||
|
||||
result, err := db.GetPkgs(l.db, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := &api.SearchResponse{}
|
||||
for result.Next() {
|
||||
pkg := &db.Package{}
|
||||
err = result.StructScan(pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out.Packages = append(out.Packages, dbPkgToAPI(pkg))
|
||||
}
|
||||
|
||||
return out, err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dbPkgToAPI(pkg), nil
|
||||
}
|
||||
|
||||
func (l lureWebAPI) GetBuildScript(ctx context.Context, req *api.GetBuildScriptRequest) (*api.GetBuildScriptResponse, error) {
|
||||
if strings.ContainsAny(req.Name, "./") || strings.ContainsAny(req.Repository, "./") {
|
||||
return nil, twirp.NewError(twirp.InvalidArgument, "name and repository must not contain . or /")
|
||||
}
|
||||
|
||||
scriptPath := filepath.Join(config.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")
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(scriptPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &api.GetBuildScriptResponse{Script: string(data)}, nil
|
||||
}
|
||||
|
||||
func dbPkgToAPI(pkg *db.Package) *api.Package {
|
||||
return &api.Package{
|
||||
Name: pkg.Name,
|
||||
Repository: pkg.Repository,
|
||||
Version: pkg.Version,
|
||||
Release: int64(pkg.Release),
|
||||
Epoch: ptr(int64(pkg.Epoch)),
|
||||
Description: &pkg.Description,
|
||||
Homepage: &pkg.Homepage,
|
||||
Maintainer: &pkg.Maintainer,
|
||||
Architectures: pkg.Architectures.Val,
|
||||
Licenses: pkg.Licenses.Val,
|
||||
Provides: pkg.Provides.Val,
|
||||
Conflicts: pkg.Conflicts.Val,
|
||||
Replaces: pkg.Replaces.Val,
|
||||
Depends: dbMapToAPI(pkg.Depends.Val),
|
||||
BuildDepends: dbMapToAPI(pkg.BuildDepends.Val),
|
||||
}
|
||||
}
|
||||
|
||||
func ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
func dbMapToAPI(m map[string][]string) map[string]*api.StringList {
|
||||
out := make(map[string]*api.StringList, len(m))
|
||||
for override, list := range m {
|
||||
out[override] = &api.StringList{Entries: list}
|
||||
}
|
||||
return out
|
||||
}
|
||||
16
cmd/lure-api-server/config.go
Normal file
16
cmd/lure-api-server/config.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/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()
|
||||
}
|
||||
}
|
||||
32
cmd/lure-api-server/db.go
Normal file
32
cmd/lure-api-server/db.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
)
|
||||
|
||||
var gdb *sqlx.DB
|
||||
|
||||
func init() {
|
||||
fi, err := os.Stat(config.DBPath)
|
||||
if err == nil {
|
||||
// TODO: This should be removed by the first stable release.
|
||||
if fi.IsDir() {
|
||||
log.Fatal("Your package cache database is using the old database engine. Please remove ~/.cache/lure and then run `lure ref`.").Send()
|
||||
}
|
||||
}
|
||||
|
||||
gdb, err = sqlx.Open("sqlite", config.DBPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error opening database").Err(err).Send()
|
||||
}
|
||||
|
||||
err = db.Init(gdb)
|
||||
if err != nil {
|
||||
log.Fatal("Error initializing database").Err(err).Send()
|
||||
}
|
||||
}
|
||||
14
cmd/lure-api-server/docker.sh
Executable file
14
cmd/lure-api-server/docker.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build
|
||||
docker buildx build --platform linux/amd64 --tag arsen6331/lure-api-server:amd64 --no-cache .
|
||||
|
||||
CGO_ENABLED=0 GOARCH=arm64 GOOS=linux go build
|
||||
docker buildx build --platform linux/arm64/v8 --tag arsen6331/lure-api-server:arm64 --no-cache .
|
||||
|
||||
docker login
|
||||
docker push arsen6331/lure-api-server -a
|
||||
|
||||
docker manifest rm arsen6331/lure-api-server:latest
|
||||
docker manifest create arsen6331/lure-api-server:latest --amend arsen6331/lure-api-server:arm64 --amend arsen6331/lure-api-server:amd64
|
||||
docker manifest push arsen6331/lure-api-server:latest
|
||||
77
cmd/lure-api-server/main.go
Normal file
77
cmd/lure-api-server/main.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/twitchtv/twirp"
|
||||
"go.arsenm.dev/logger"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/api"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.Logger = logger.NewPretty(os.Stderr)
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
addr := flag.String("a", ":8080", "Listen address for API server")
|
||||
logFile := flag.String("l", "", "Output file for JSON log")
|
||||
flag.Parse()
|
||||
|
||||
if *logFile != "" {
|
||||
fl, err := os.Create(*logFile)
|
||||
if err != nil {
|
||||
log.Fatal("Error creating log file").Err(err).Send()
|
||||
}
|
||||
defer fl.Close()
|
||||
|
||||
log.Logger = logger.NewMulti(log.Logger, logger.NewJSON(fl))
|
||||
}
|
||||
|
||||
err := repos.Pull(ctx, gdb, cfg.Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||
}
|
||||
|
||||
sigCh := make(chan struct{}, 200)
|
||||
go repoPullWorker(ctx, sigCh)
|
||||
|
||||
var handler http.Handler
|
||||
|
||||
handler = api.NewAPIServer(
|
||||
lureWebAPI{db: gdb},
|
||||
twirp.WithServerPathPrefix(""),
|
||||
)
|
||||
handler = allowAllCORSHandler(handler)
|
||||
handler = handleWebhook(handler, sigCh)
|
||||
|
||||
ln, err := net.Listen("tcp", *addr)
|
||||
if err != nil {
|
||||
log.Fatal("Error starting listener").Err(err).Send()
|
||||
}
|
||||
|
||||
log.Info("Starting HTTP API server").Str("addr", ln.Addr().String()).Send()
|
||||
|
||||
err = http.Serve(ln, handler)
|
||||
if err != nil {
|
||||
log.Fatal("Error while running server").Err(err).Send()
|
||||
}
|
||||
}
|
||||
|
||||
func allowAllCORSHandler(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
res.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
res.Header().Set("Access-Control-Allow-Headers", "*")
|
||||
if req.Method == http.MethodOptions {
|
||||
return
|
||||
}
|
||||
h.ServeHTTP(res, req)
|
||||
})
|
||||
}
|
||||
89
cmd/lure-api-server/webhook.go
Normal file
89
cmd/lure-api-server/webhook.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
)
|
||||
|
||||
func handleWebhook(next http.Handler, sigCh chan<- struct{}) http.Handler {
|
||||
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path == "/webhook" {
|
||||
if req.Method != http.MethodPost {
|
||||
res.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Header.Get("X-GitHub-Event") != "push" {
|
||||
http.Error(res, "Only push events are accepted by this bot", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := verifySecure(req)
|
||||
if err != nil {
|
||||
http.Error(res, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
sigCh <- struct{}{}
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(res, req)
|
||||
})
|
||||
}
|
||||
|
||||
func verifySecure(req *http.Request) error {
|
||||
sigStr := req.Header.Get("X-Hub-Signature-256")
|
||||
sig, err := hex.DecodeString(strings.TrimPrefix(sigStr, "sha256="))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secretStr, ok := os.LookupEnv("LURE_API_GITHUB_SECRET")
|
||||
if !ok {
|
||||
return errors.New("LURE_API_GITHUB_SECRET must be set to the secret used for setting up the github webhook")
|
||||
}
|
||||
secret := []byte(secretStr)
|
||||
|
||||
h := hmac.New(sha256.New, secret)
|
||||
_, err = io.Copy(h, req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hmac.Equal(h.Sum(nil), sig) {
|
||||
log.Warn("Insecure webhook request").
|
||||
Str("from", req.RemoteAddr).
|
||||
Bytes("sig", sig).
|
||||
Bytes("hmac", h.Sum(nil)).
|
||||
Send()
|
||||
|
||||
return errors.New("webhook signature mismatch")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func repoPullWorker(ctx context.Context, sigCh <-chan struct{}) {
|
||||
for {
|
||||
select {
|
||||
case <-sigCh:
|
||||
err := repos.Pull(ctx, gdb, cfg.Repos)
|
||||
if err != nil {
|
||||
log.Warn("Error while pulling repositories").Err(err).Send()
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
101
config.go
101
config.go
@@ -19,107 +19,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/types"
|
||||
"go.arsenm.dev/lure/manager"
|
||||
)
|
||||
|
||||
var (
|
||||
cacheDir string
|
||||
cfgPath string
|
||||
config Config
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
RootCmd string `toml:"rootCmd"`
|
||||
Repos []Repo `toml:"repo"`
|
||||
}
|
||||
|
||||
type Repo struct {
|
||||
Name string `toml:"name"`
|
||||
URL string `toml:"url"`
|
||||
}
|
||||
|
||||
var defaultConfig = Config{
|
||||
RootCmd: "sudo",
|
||||
Repos: []Repo{
|
||||
{
|
||||
Name: "default",
|
||||
URL: "https://github.com/Arsen6331/lure-repo.git",
|
||||
},
|
||||
},
|
||||
}
|
||||
var cfg types.Config
|
||||
|
||||
func init() {
|
||||
cfg, cache, err := makeDirs()
|
||||
if err != nil {
|
||||
log.Fatal("Error creating directories").Err(err).Send()
|
||||
}
|
||||
cacheDir = cache
|
||||
|
||||
cfgPath = filepath.Join(cfg, "lure.toml")
|
||||
|
||||
cfgFl, err := os.Open(cfgPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error opening config file").Err(err).Send()
|
||||
}
|
||||
defer cfgFl.Close()
|
||||
|
||||
err = toml.NewDecoder(cfgFl).Decode(&config)
|
||||
err := config.Decode(&cfg)
|
||||
if err != nil {
|
||||
log.Fatal("Error decoding config file").Err(err).Send()
|
||||
}
|
||||
|
||||
manager.DefaultRootCmd = config.RootCmd
|
||||
}
|
||||
|
||||
func makeDirs() (string, string, error) {
|
||||
cfgDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
baseCfgPath := filepath.Join(cfgDir, "lure")
|
||||
|
||||
err = os.MkdirAll(baseCfgPath, 0o755)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
cfgPath := filepath.Join(baseCfgPath, "lure.toml")
|
||||
|
||||
if _, err := os.Stat(cfgPath); err != nil {
|
||||
cfgFl, err := os.Create(cfgPath)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
err = toml.NewEncoder(cfgFl).Encode(&defaultConfig)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
cfgFl.Close()
|
||||
}
|
||||
|
||||
cacheDir, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
baseCachePath := filepath.Join(cacheDir, "lure")
|
||||
|
||||
err = os.MkdirAll(filepath.Join(baseCachePath, "repo"), 0o755)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(filepath.Join(baseCachePath, "pkgs"), 0o755)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return baseCfgPath, baseCachePath, nil
|
||||
manager.DefaultRootCmd = cfg.RootCmd
|
||||
}
|
||||
|
||||
32
db.go
Normal file
32
db.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
)
|
||||
|
||||
var gdb *sqlx.DB
|
||||
|
||||
func init() {
|
||||
fi, err := os.Stat(config.DBPath)
|
||||
if err == nil {
|
||||
// TODO: This should be removed by the first stable release.
|
||||
if fi.IsDir() {
|
||||
log.Fatal("Your package cache database is using the old database engine. Please remove ~/.cache/lure and then run `lure ref`.").Send()
|
||||
}
|
||||
}
|
||||
|
||||
gdb, err = sqlx.Open("sqlite", config.DBPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error opening database").Err(err).Send()
|
||||
}
|
||||
|
||||
err = db.Init(gdb)
|
||||
if err != nil {
|
||||
log.Fatal("Error initializing database").Err(err).Send()
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ type OSRelease struct {
|
||||
PrettyName string
|
||||
ID string
|
||||
Like []string
|
||||
BuildID string
|
||||
VersionID string
|
||||
ANSIColor string
|
||||
HomeURL string
|
||||
DocumentationURL string
|
||||
@@ -46,8 +46,16 @@ type OSRelease struct {
|
||||
Logo string
|
||||
}
|
||||
|
||||
// OSReleaseName returns the NAME field of the
|
||||
var parsed *OSRelease
|
||||
|
||||
// OSReleaseName returns a struct parsed from the system's os-release
|
||||
// file. It checks /etc/os-release as well as /usr/lib/os-release.
|
||||
// The returned OSRelease struct is a singleton.
|
||||
func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
|
||||
if parsed != nil {
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
fl, err := os.Open("/usr/lib/os-release")
|
||||
if err != nil {
|
||||
fl, err = os.Open("/etc/os-release")
|
||||
@@ -86,7 +94,7 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
|
||||
Name: runner.Vars["NAME"].Str,
|
||||
PrettyName: runner.Vars["PRETTY_NAME"].Str,
|
||||
ID: runner.Vars["ID"].Str,
|
||||
BuildID: runner.Vars["BUILD_ID"].Str,
|
||||
VersionID: runner.Vars["VERSION_ID"].Str,
|
||||
ANSIColor: runner.Vars["ANSI_COLOR"].Str,
|
||||
HomeURL: runner.Vars["HOME_URL"].Str,
|
||||
DocumentationURL: runner.Vars["DOCUMENTATION_URL"].Str,
|
||||
@@ -99,5 +107,6 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
|
||||
out.Like = strings.Split(runner.Vars["ID_LIKE"].Str, " ")
|
||||
}
|
||||
|
||||
parsed = out
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# LURE Docs
|
||||
|
||||
- [Build Scripts](build-scripts.md)
|
||||
- [Usage](usage.md)
|
||||
- [Configuration](configuration.md)
|
||||
- [Usage](usage.md)
|
||||
- [Packages](packages)
|
||||
- [Build Scripts](packages/build-scripts.md)
|
||||
- [Package Conventions](packages/conventions.md)
|
||||
- [Adding Packages to LURE's repo](packages/adding-packages.md)
|
||||
5
docs/packages/README.md
Normal file
5
docs/packages/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# LURE Docs > Packages
|
||||
|
||||
- [Build Scripts](build-scripts.md)
|
||||
- [Package Conventions](conventions.md)
|
||||
- [Adding Packages to LURE's repo](adding-packages.md)
|
||||
23
docs/packages/adding-packages.md
Normal file
23
docs/packages/adding-packages.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Adding Packages to LURE's repo
|
||||
|
||||
## Requirements
|
||||
|
||||
- `go` (1.18+)
|
||||
- `git`
|
||||
- `lure-analyzer`
|
||||
- `go install go.arsenm.dev/lure-repo-bot/cmd/lure-analyzer@latest`
|
||||
- `shfmt`
|
||||
- May be available in distro repos
|
||||
- `go install mvdan.cc/sh/v3/cmd/shfmt@latest`
|
||||
|
||||
---
|
||||
|
||||
## How to submit a package
|
||||
|
||||
LURE's repo is hosted on Github at https://github.com/Arsen6331/lure-repo. In it, there are multiple directories each containing a `lure.sh` file. In order to add a package to LURE's repo, simply create a PR with a [build script](./build-scripts.md) and place it in a directory with the same name as the package.
|
||||
|
||||
Upon submitting the PR, [lure-repo-bot](https://github.com/Arsen6331/lure-repo-bot) will pull your PR and analyze it, providing suggestions for fixes as review comments. If there are no problems, the bot will approve your changes. If there are issues, re-request review from the bot after you've finished applying the fixes and it will automatically review the PR again.
|
||||
|
||||
All scripts submitted to the LURE repo should be formatted with `shfmt`. If they are not properly formatted, Github Actions will add suggestions in the "Files Changed" tab of the PR.
|
||||
|
||||
Once your PR is merged, LURE will pull the changed repo and your package will be available for people to install.
|
||||
@@ -26,6 +26,29 @@ LURE uses build scripts similar to the AUR's PKGBUILDs. This is the documentatio
|
||||
- [checksums](#checksums)
|
||||
- [backup](#backup)
|
||||
- [scripts](#scripts)
|
||||
- [Functions](#functions)
|
||||
- [prepare](#prepare)
|
||||
- [version](#version-1)
|
||||
- [build](#build)
|
||||
- [package](#package)
|
||||
- [Environment Variables](#environment-variables)
|
||||
- [DISTRO_NAME](#distro_name)
|
||||
- [DISTRO_PRETTY_NAME](#distro_pretty_name)
|
||||
- [DISTRO_ID](#distro_id)
|
||||
- [DISTRO_VERSION_ID](#distro_version_id)
|
||||
- [ARCH](#arch)
|
||||
- [NCPU](#ncpu)
|
||||
- [Helper Commands](#helper-commands)
|
||||
- [install-binary](#install-binary)
|
||||
- [install-systemd](#install-systemd)
|
||||
- [install-systemd-user](#install-systemd-user)
|
||||
- [install-config](#install-config)
|
||||
- [install-license](#install-license)
|
||||
- [install-completion](#install-completion)
|
||||
- [install-manual](#install-manual)
|
||||
- [install-desktop](#install-desktop)
|
||||
- [install-library](#install-library)
|
||||
- [git-version](#git-version)
|
||||
|
||||
---
|
||||
|
||||
@@ -119,7 +142,7 @@ LURE_ARM_VARIANT=arm5 lure install ...
|
||||
|
||||
### licenses
|
||||
|
||||
The `licenses` array contains the licenses used by this package. Some valid values include `GPLv3` and `MIT`.
|
||||
The `licenses` array contains the licenses used by this package. In order to standardize license names, values should be [SPDX Identifiers](https://spdx.org/licenses/) such as `Apache-2.0`, `MIT`, and `GPL-3.0-only`. If the project uses a license that is not standardized in SPDX, use the value `Custom`. If the project has multiple nonstandard licenses, include `Custom` as many times as there are nonstandard licenses.
|
||||
|
||||
### provides
|
||||
|
||||
@@ -163,6 +186,7 @@ If the URL scheme starts with `git+`, the source will be downloaded as a git rep
|
||||
- `~branch`: Specify which branch of the repo to check out.
|
||||
- `~commit`: Specify which commit of the repo to check out.
|
||||
- `~depth`: Specify what depth should be used when cloning the repo. Must be an integer.
|
||||
- `~name`: Specify the name of the directory into which the git repo should be cloned.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -217,13 +241,28 @@ The rest of the scripts are available in all packages.
|
||||
|
||||
## Functions
|
||||
|
||||
Any variables marked with `(*)` are required
|
||||
This section documents user-defined functions that can be added to build scripts. Any functions marked with `(*)` are required.
|
||||
|
||||
All functions start in the `$srcdir` directory
|
||||
All functions are executed in the `$srcdir` directory
|
||||
|
||||
### version
|
||||
|
||||
The `version()` function updates the `version` variable. This allows for automatically deriving the version from sources. This is most useful for git packages, which usually don't need to be changed, so their `version` variable stays the same.
|
||||
|
||||
An example of using this for git:
|
||||
|
||||
```bash
|
||||
version() {
|
||||
cd "$srcdir/itd"
|
||||
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||
}
|
||||
```
|
||||
|
||||
The AUR equivalent is the [`pkgver()` function](https://wiki.archlinux.org/title/VCS_package_guidelines#The_pkgver()_function)
|
||||
|
||||
### prepare
|
||||
|
||||
The `prepare()` function runs first. It is meant to prepare the sources for building and packaging. This is the function in which patches should be applied, for example, by the `patch` command, and where tools like `go generate` should be executed.
|
||||
The `prepare()` function is meant to prepare the sources for building and packaging. This is the function in which patches should be applied, for example, by the `patch` command, and where tools like `go generate` should be executed.
|
||||
|
||||
### build
|
||||
|
||||
@@ -247,3 +286,201 @@ package() {
|
||||
install -Dm644 bin.cfg ${pkgdir}/etc/bin.cfg
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
LURE exposes several values as environment variables for use in build scripts.
|
||||
|
||||
### DISTRO_NAME
|
||||
|
||||
The `DISTRO_NAME` variable is the name of the distro as defined in its `os-release` file.
|
||||
|
||||
For example, it's set to `Fedora Linux` in a Fedora 36 docker image
|
||||
|
||||
### DISTRO_PRETTY_NAME
|
||||
|
||||
The `DISTRO_PRETTY_NAME` variable is the "pretty" name of the distro as defined in its `os-release` file.
|
||||
|
||||
For example, it's set to `Fedora Linux 36 (Container Image)` in a Fedora 36 docker image
|
||||
|
||||
### DISTRO_ID
|
||||
|
||||
The `DISTRO_ID` variable is the identifier of the distro as defined in its `os-release` file. This is the same as what LURE uses for overrides.
|
||||
|
||||
For example, it's set to `fedora` in a Fedora 36 docker image
|
||||
|
||||
### DISTRO_ID_LIKE
|
||||
|
||||
The `DISTRO_ID_LIKE` variable contains identifiers of similar distros to the one running, separated by spaces.
|
||||
|
||||
For example, it's set to `opensuse suse` in an OpenSUSE Tumbleweed docker image and `rhel fedora` in a CentOS 8 docker image.
|
||||
|
||||
### DISTRO_VERSION_ID
|
||||
|
||||
The `DISTRO_VERSION_ID` variable is the version identifier of the distro as defined in its `os-release` file.
|
||||
|
||||
For example, it's set to `36` in a Fedora 36 docker image and `11` in a Debian Bullseye docker image
|
||||
|
||||
### ARCH
|
||||
|
||||
The `ARCH` variable is the architecture of the machine running the script. It uses the same naming convention as the values in the `architectures` array
|
||||
|
||||
### NCPU
|
||||
|
||||
The `NCPU` variable is the amount of CPUs available on the machine running the script. It will be set to `8` on a quad core machine with hyperthreading, for example.
|
||||
|
||||
---
|
||||
|
||||
## Helper Commands
|
||||
|
||||
LURE provides various commands to help packagers create proper cross-distro packages. These commands should be used wherever possible instead of doing the tasks manually.
|
||||
|
||||
### install-binary
|
||||
|
||||
`install-binary` accepts 1-2 arguments. The first argument is the binary you'd like to install. The second is the filename that should be used.
|
||||
|
||||
If the filename argument is not provided, tha name of the input file will be used.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
install-binary ./itd
|
||||
install-binary ./itd itd-2
|
||||
```
|
||||
|
||||
### install-systemd
|
||||
|
||||
`install-systemd` installs regular systemd system services (see `install-systemd-user` for user services)
|
||||
|
||||
It accepts 1-2 arguments. The first argument is the service you'd like to install. The second is the filename that should be used.
|
||||
|
||||
If the filename argument is not provided, tha name of the input file will be used.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
install-systemd ./syncthing@.service
|
||||
install-systemd-user ./syncthing@.service sync-thing@.service
|
||||
```
|
||||
|
||||
### install-systemd-user
|
||||
|
||||
`install-systemd-user` installs systemd user services (services like `itd` meant to be started with `--user`).
|
||||
|
||||
It accepts 1-2 arguments. The first argument is the service you'd like to install. The second is the filename that should be used.
|
||||
|
||||
If the filename argument is not provided, tha name of the input file will be used.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
install-systemd-user ./itd.service
|
||||
install-systemd-user ./itd.service infinitime-daemon.service
|
||||
```
|
||||
|
||||
### install-config
|
||||
|
||||
`install-config` installs configuration files into the `/etc` directory
|
||||
|
||||
It accepts 1-2 arguments. The first argument is the config you'd like to install. The second is the filename that should be used.
|
||||
|
||||
If the filename argument is not provided, tha name of the input file will be used.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
install-config ./itd.toml
|
||||
install-config ./itd.example.toml itd.toml
|
||||
```
|
||||
|
||||
### install-license
|
||||
|
||||
`install-license` installs a license file
|
||||
|
||||
It accepts 1-2 arguments. The first argument is the config you'd like to install. The second is the filename that should be used.
|
||||
|
||||
If the filename argument is not provided, tha name of the input file will be used.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
install-license ./LICENSE itd/LICENSE
|
||||
```
|
||||
|
||||
### install-completion
|
||||
|
||||
`install-completion` installs shell completions
|
||||
|
||||
It currently supports `bash`, `zsh`, and `fish`
|
||||
|
||||
Completions are read from stdin, so they can either be piped in or retrieved from files
|
||||
|
||||
Two arguments are required for this function. The first one is the name of the shell and the second is the name of the completion.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
./k9s completion fish | install-completion fish k9s
|
||||
install-completion bash k9s <./k9s/completions/k9s.bash
|
||||
```
|
||||
|
||||
### install-manual
|
||||
|
||||
`install-manual` installs manpages. It accepts a single argument, which is the path to the manpage.
|
||||
|
||||
The install path will be determined based on the number at the end of the filename. If a number cannot be extracted, an error will be returned.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
install-manual ./man/strelaysrv.1
|
||||
install-manual ./mdoc.7
|
||||
```
|
||||
|
||||
### install-desktop
|
||||
|
||||
`install-desktop` installs desktop files for applications. It accepts 1-2 arguments. The first argument is the config you'd like to install. The second is the filename that should be used.
|
||||
|
||||
If the filename argument is not provided, tha name of the input file will be used.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
install-desktop ./${name}/share/admc.desktop
|
||||
install-desktop ./${name}/share/admc.desktop admc-app.desktop
|
||||
```
|
||||
|
||||
### install-library
|
||||
|
||||
`install-library` installs shared and static libraries to the correct location.
|
||||
|
||||
This is the most important helper as it contains logic to figure out where to install libraries based on the target distro and CPU architecture. It should almost always be used to install all libraries.
|
||||
|
||||
It accepts 1-2 arguments. The first argument is the config you'd like to install. The second is the filename that should be used.
|
||||
|
||||
If the filename argument is not provided, tha name of the input file will be used.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
install-library ./${name}/build/libadldap.so
|
||||
```
|
||||
|
||||
### git-version
|
||||
|
||||
`git-version` returns a version number based on the git revision of a repository.
|
||||
|
||||
If an argument is provided, it will be used as the path to the repo. Otherwise, the current directory will be used.
|
||||
|
||||
The version number will be the amount of revisions, a dot, and the short hash of the current revision. For example: `118.e4b8348`.
|
||||
|
||||
The AUR's convention includes an `r` at the beginning of the version number. This is ommitted because some distros expect the version number to start with a digit.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
git-version
|
||||
git-version "$srcdir/itd"
|
||||
```
|
||||
36
docs/packages/conventions.md
Normal file
36
docs/packages/conventions.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Package Conventions
|
||||
|
||||
## General
|
||||
|
||||
Packages should have the name(s) of what they contain in their `provides` and `conflicts` arrays. That way, they can be installed by users without needing to know the full package name. For example, there are two LURE packages for ITD: `itd-bin`, and `itd-git`. Both of them have provides and conflicts arrays specifying the two commands they install: `itd`, and `itctl`. This means that if a user wants to install ITD, they simply have to type `lure in itd` and LURE will prompt them for which one they want to install.
|
||||
|
||||
## Binary packages
|
||||
|
||||
Packages that install download and install precompiled binaries should have a `-bin` suffix.
|
||||
|
||||
## Git packages
|
||||
|
||||
Packages that build and install programs from source code cloned directly from Git should have a `-git` suffix.
|
||||
|
||||
The versions of these packages should consist of the amount of revisions followed by the current revision, separated by a period. For example: `183.80187b0`. Note that unlike the AUR, there is no `r` at the beginning. This is because some package managers refuse to install packages whose version numbers don't start with a digit.
|
||||
|
||||
This version number can be obtained using the following command:
|
||||
|
||||
```bash
|
||||
printf "%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||
```
|
||||
|
||||
The `version()` function for such packages should use the LURE-provided `git-version` helper command, like so:
|
||||
|
||||
```bash
|
||||
version() {
|
||||
cd "$srcdir/$name"
|
||||
git-version
|
||||
}
|
||||
```
|
||||
|
||||
This uses LURE's embedded Git implementation, which ensures that the user doesn't need Git installed on their system in order to install `-git` packages.
|
||||
|
||||
## Other packages
|
||||
|
||||
Packages that download sources for a specific version of a program should not have any suffix, even if those sources are downloaded from Git.
|
||||
@@ -12,6 +12,8 @@
|
||||
- [addrepo](#addrepo)
|
||||
- [removerepo](#removerepo)
|
||||
- [refresh](#refresh)
|
||||
- [fix](#fix)
|
||||
- [version](#version)
|
||||
- [Environment Variables](#environment-variables)
|
||||
- [LURE_DISTRO](#lure_distro)
|
||||
- [LURE_PKG_FORMAT](#lure_pkg_format)
|
||||
@@ -23,12 +25,18 @@
|
||||
|
||||
### install
|
||||
|
||||
The install command installs a command from the LURE repos. Any packages that aren't found in LURE's repos get forwarded to the system package manager for installation.
|
||||
The install command installs a package from the LURE repos. Any packages that aren't found in LURE's repos get forwarded to the system package manager for installation.
|
||||
|
||||
Example:
|
||||
The package arguments do not have to be exact. LURE will check the `provides` array if an exact match is not found. There is also support for using "%" as a wildcard.
|
||||
|
||||
If multiple packages are found, you will be prompted to select which you want to install.
|
||||
|
||||
Examples:
|
||||
|
||||
```shell
|
||||
lure in itd-bin
|
||||
lure in itd-bin # only finds itd-bin
|
||||
lure in itd # finds itd-bin and itd-git
|
||||
lure in it% # finds itd-bin, itd-git, and itgui-git
|
||||
```
|
||||
|
||||
### remove
|
||||
@@ -55,20 +63,36 @@ lure up
|
||||
|
||||
The info command displays information about a package in LURE's repos.
|
||||
|
||||
The package arguments do not have to be exact. LURE will check the `provides` array if an exact match is not found. There is also support for using "%" as a wildcard.
|
||||
|
||||
If multiple packages are found, you will be prompted to select which you want to show.
|
||||
|
||||
Example:
|
||||
|
||||
```shell
|
||||
lure info itd-bin
|
||||
lure info itd-bin # only finds itd-bin
|
||||
lure info itd # finds itd-bin and itd-git
|
||||
lure info it% # finds itd-bin, itd-git, and itgui-git
|
||||
```
|
||||
|
||||
### list
|
||||
|
||||
The list command lists all LURE repo packages as well as their versions
|
||||
|
||||
Example:
|
||||
This command accepts a single optional argument. This argument is a pattern to filter found packages against.
|
||||
|
||||
The pattern does not have to be exact. LURE will check the `provides` array if an exact match is not found. There is also support for using "%" as a wildcard.
|
||||
|
||||
There is a `-I` or `--installed` flag that filters out any packages that are not installed on the system
|
||||
|
||||
Examples:
|
||||
|
||||
```shell
|
||||
lure ls
|
||||
lure ls # lists all LURE packages
|
||||
lure ls -I # lists all installed packages
|
||||
lure ls i% # lists all packages starting with "i"
|
||||
lure ls %d # lists all packages ending with "d"
|
||||
lure ls -I i% # lists all installed packages that start with "i"
|
||||
```
|
||||
|
||||
### build
|
||||
@@ -111,6 +135,26 @@ Example:
|
||||
lure ref
|
||||
```
|
||||
|
||||
### fix
|
||||
|
||||
The fix command attempts to fix issues with LURE by deleting and rebuilding LURE's cache
|
||||
|
||||
Example:
|
||||
|
||||
```shell
|
||||
lure fix
|
||||
```
|
||||
|
||||
### version
|
||||
|
||||
The version command returns the current LURE version and exits
|
||||
|
||||
Example:
|
||||
|
||||
```shell
|
||||
lure version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -67,6 +68,22 @@ func Get(ctx context.Context, opts GetOptions) error {
|
||||
}
|
||||
query := src.Query()
|
||||
|
||||
if strings.HasPrefix(src.Scheme, "git+") {
|
||||
err = getGit(ctx, src, query, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = getFile(ctx, src, query, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getGit(ctx context.Context, src *url.URL, query url.Values, opts GetOptions) (err error) {
|
||||
tag := query.Get("~tag")
|
||||
query.Del("~tag")
|
||||
|
||||
@@ -79,6 +96,9 @@ func Get(ctx context.Context, opts GetOptions) error {
|
||||
depthStr := query.Get("~depth")
|
||||
query.Del("~depth")
|
||||
|
||||
name := query.Get("~name")
|
||||
query.Del("~name")
|
||||
|
||||
var refName plumbing.ReferenceName
|
||||
if tag != "" {
|
||||
refName = plumbing.NewTagReferenceName(tag)
|
||||
@@ -86,12 +106,13 @@ func Get(ctx context.Context, opts GetOptions) error {
|
||||
refName = plumbing.NewBranchReferenceName(branch)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(src.Scheme, "git+") {
|
||||
src.Scheme = strings.TrimPrefix(src.Scheme, "git+")
|
||||
src.RawQuery = query.Encode()
|
||||
|
||||
name := path.Base(src.Path)
|
||||
if name == "" {
|
||||
name = path.Base(src.Path)
|
||||
name = strings.TrimSuffix(name, ".git")
|
||||
}
|
||||
|
||||
dstDir := opts.Destination
|
||||
if opts.EncloseGit {
|
||||
@@ -132,7 +153,9 @@ func Get(ctx context.Context, opts GetOptions) error {
|
||||
}
|
||||
|
||||
return w.Checkout(checkoutOpts)
|
||||
} else {
|
||||
}
|
||||
|
||||
func getFile(ctx context.Context, src *url.URL, query url.Values, opts GetOptions) error {
|
||||
name := query.Get("~name")
|
||||
query.Del("~name")
|
||||
|
||||
@@ -183,6 +206,16 @@ func Get(ctx context.Context, opts GetOptions) error {
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
err = extractFile(ctx, input, hash, format, name, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractFile(ctx context.Context, input io.Reader, hash hash.Hash, format archiver.Format, name string, opts GetOptions) (err error) {
|
||||
r := io.TeeReader(input, hash)
|
||||
fname := format.Name()
|
||||
|
||||
@@ -194,6 +227,11 @@ func Get(ctx context.Context, opts GetOptions) error {
|
||||
return err
|
||||
}
|
||||
defer fr.Close()
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fm := fi.Mode()
|
||||
|
||||
path := filepath.Join(opts.Destination, f.NameInArchive)
|
||||
|
||||
@@ -208,7 +246,7 @@ func Get(ctx context.Context, opts GetOptions) error {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
outFl, err := os.Create(path)
|
||||
outFl, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fm.Perm())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -249,8 +287,6 @@ func Get(ctx context.Context, opts GetOptions) error {
|
||||
return ErrChecksumMismatch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
52
fix.go
Normal file
52
fix.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
)
|
||||
|
||||
func fixCmd(c *cli.Context) error {
|
||||
gdb.Close()
|
||||
|
||||
log.Info("Removing cache directory").Send()
|
||||
|
||||
err := os.RemoveAll(config.CacheDir)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to remove cache directory").Err(err).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()
|
||||
}
|
||||
|
||||
gdb, err = sqlx.Open("sqlite", config.DBPath)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to create new database").Err(err).Send()
|
||||
}
|
||||
|
||||
// Make sure the DB is rebuilt when repos are pulled
|
||||
config.DBPresent = false
|
||||
|
||||
err = db.Init(gdb)
|
||||
if err != nil {
|
||||
log.Fatal("Error initializing database").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()
|
||||
|
||||
return nil
|
||||
}
|
||||
61
go.mod
61
go.mod
@@ -2,20 +2,28 @@ module go.arsenm.dev/lure
|
||||
|
||||
go 1.18
|
||||
|
||||
replace github.com/goreleaser/nfpm/v2 => github.com/Arsen6331/nfpm/v2 v2.0.0-20220922210414-eae88e8ea4b5
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6
|
||||
github.com/alecthomas/chroma/v2 v2.4.0
|
||||
github.com/charmbracelet/bubbles v0.14.0
|
||||
github.com/charmbracelet/bubbletea v0.23.1
|
||||
github.com/charmbracelet/lipgloss v0.6.0
|
||||
github.com/go-git/go-billy/v5 v5.3.1
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/goreleaser/nfpm/v2 v2.18.1
|
||||
github.com/goreleaser/nfpm/v2 v2.20.0
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/muesli/reflow v0.3.0
|
||||
github.com/pelletier/go-toml/v2 v2.0.5
|
||||
github.com/twitchtv/twirp v8.1.3+incompatible
|
||||
github.com/urfave/cli/v2 v2.16.3
|
||||
go.arsenm.dev/logger v0.0.0-20220630204155-5ba23e583f0a
|
||||
go.arsenm.dev/logger v0.0.0-20221220032833-ba8a3cfb4668
|
||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
||||
golang.org/x/sys v0.3.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.20.0
|
||||
mvdan.cc/sh/v3 v3.5.1
|
||||
)
|
||||
|
||||
@@ -25,23 +33,24 @@ require (
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
|
||||
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
|
||||
github.com/cavaliergopher/cpio v1.0.1 // indirect
|
||||
github.com/containerd/console v1.0.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/dsnet/compress v0.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/frankban/quicktest v1.14.3 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/rpmpack v0.0.0-20220314092521-38642b5e571e // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gookit/color v1.5.1 // indirect
|
||||
github.com/gookit/color v1.5.2 // indirect
|
||||
github.com/goreleaser/chglog v0.2.2 // indirect
|
||||
github.com/goreleaser/fileglob v1.3.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
@@ -49,29 +58,49 @@ require (
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.5 // indirect
|
||||
github.com/klauspost/compress v1.15.11 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.13.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.21.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.4.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
)
|
||||
|
||||
154
go.sum
154
go.sum
@@ -2,8 +2,6 @@ github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
|
||||
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
|
||||
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
|
||||
github.com/Arsen6331/nfpm/v2 v2.0.0-20220922210414-eae88e8ea4b5 h1:SFWe7Ho60w43hXEIxCdiAXZvUyM9GF/L90jMK42s8gU=
|
||||
github.com/Arsen6331/nfpm/v2 v2.0.0-20220922210414-eae88e8ea4b5/go.mod h1:O4K1mvEORY78CSCInptGG5MWJ19yr9xFTgWWUtY1R7o=
|
||||
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
@@ -16,8 +14,8 @@ github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuN
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
|
||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||
@@ -27,12 +25,19 @@ github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wy
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs=
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/alecthomas/assert/v2 v2.2.0 h1:f6L/b7KE2bfA+9O4FL3CM/xJccDEwPVYd5fALBiuwvw=
|
||||
github.com/alecthomas/chroma/v2 v2.4.0 h1:Loe2ZjT5x3q1bcWwemqyqEi8p11/IV/ncFCeLYDpWC4=
|
||||
github.com/alecthomas/chroma/v2 v2.4.0/go.mod h1:6kHzqF5O6FUSJzBXW7fXELjb+e+7OXW4UpoPqMO7IBQ=
|
||||
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg=
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11 h1:IRrDwVlWQr6kS1U8/EtyA1+EHcc4yl8pndcqXWrEamg=
|
||||
@@ -40,6 +45,17 @@ github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8
|
||||
github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
|
||||
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
|
||||
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
|
||||
github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og=
|
||||
github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc=
|
||||
github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4=
|
||||
github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck=
|
||||
github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
|
||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
|
||||
github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
|
||||
github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
|
||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
@@ -48,9 +64,12 @@ github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
@@ -67,25 +86,30 @@ github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2Su
|
||||
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
|
||||
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
|
||||
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/rpmpack v0.0.0-20220314092521-38642b5e571e h1:6Jn9JtfCn20uycra92LxTkq5yfBKNSFlRJPBk8/Cxhg=
|
||||
github.com/google/rpmpack v0.0.0-20220314092521-38642b5e571e/go.mod h1:83rLnx5vhPyN/mDzBYJWtiPf+9xnSVQynTpqZWe7OnY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/color v1.5.1 h1:Vjg2VEcdHpwq+oY63s/ksHrgJYCTo0bwWvmmYWdE9fQ=
|
||||
github.com/gookit/color v1.5.1/go.mod h1:wZFzea4X8qN6vHOSP2apMb4/+w/orMznEzYsIHPaqKM=
|
||||
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
|
||||
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/goreleaser/chglog v0.2.2 h1:V7nf07baXtGAgGevvqgW2MM4kZ6gOr12vKNSAU3VIZ0=
|
||||
github.com/goreleaser/chglog v0.2.2/go.mod h1:2s5JwtCOWjZa8AIneL+xdUl9SRuigCjRHNHsX30dupE=
|
||||
github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I=
|
||||
github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU=
|
||||
github.com/goreleaser/nfpm/v2 v2.20.0 h1:Q/CrX54KUMluz6+M/pjTbknFd5Dao8qXi0C6ZuFCtfY=
|
||||
github.com/goreleaser/nfpm/v2 v2.20.0/go.mod h1:/Fh6XfwT/T+D4qtNC2iXmHSD/1UT20JkvBXyJ6nFmOY=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
||||
@@ -96,6 +120,8 @@ github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
@@ -103,9 +129,8 @@ github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT
|
||||
github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o=
|
||||
github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.5 h1:qyCLMz2JCrKADihKOh9FxnW3houKeNsp2h5OEz0QSEA=
|
||||
github.com/klauspost/compress v1.15.5/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
|
||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@@ -118,14 +143,30 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7 h1:xzByj8G8tj0Oq7ZYYU4+ixL/CVb5ruWCm0EZQ1PjOkE=
|
||||
@@ -138,28 +179,47 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||
github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
|
||||
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
|
||||
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -170,11 +230,12 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
||||
github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
|
||||
github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
@@ -184,26 +245,29 @@ github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6e
|
||||
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
|
||||
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
|
||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
|
||||
go.arsenm.dev/logger v0.0.0-20220630204155-5ba23e583f0a h1:9SF7Af1OnN9BlWk9yNYCZuv/uAhDMhzfZxtWJEqy6EE=
|
||||
go.arsenm.dev/logger v0.0.0-20220630204155-5ba23e583f0a/go.mod h1:RV2qydKDdoyaRkhAq8JEGvojR8eJ6bjq5WnSIlH7gYw=
|
||||
go.arsenm.dev/logger v0.0.0-20221220032833-ba8a3cfb4668 h1:7dSmQ79slzFpcii8zgQbEStxpkTPvq3tzWc7KX5uwGc=
|
||||
go.arsenm.dev/logger v0.0.0-20221220032833-ba8a3cfb4668/go.mod h1:RV2qydKDdoyaRkhAq8JEGvojR8eJ6bjq5WnSIlH7gYw=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4=
|
||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -220,9 +284,14 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
@@ -231,7 +300,12 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -248,5 +322,29 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/libc v1.21.5 h1:xBkU9fnHV+hvZuPSRszN0AXDG4M7nwPLwTWwkYcvLCI=
|
||||
modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
|
||||
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.20.0 h1:80zmD3BGkm8BZ5fUi/4lwJQHiO3GXgIUvZRXpoIfROY=
|
||||
modernc.org/sqlite v1.20.0/go.mod h1:EsYz8rfOvLCiYTy5ZFsOYzoCcRMu98YYkwAcCw5YIYw=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
|
||||
mvdan.cc/sh/v3 v3.5.1 h1:hmP3UOw4f+EYexsJjFxvU38+kn+V/s2CclXHanIBkmQ=
|
||||
mvdan.cc/sh/v3 v3.5.1/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=
|
||||
|
||||
261
helpers.go
Normal file
261
helpers.go
Normal file
@@ -0,0 +1,261 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"go.arsenm.dev/lure/internal/shutils"
|
||||
"golang.org/x/exp/slices"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoPipe = errors.New("command requires data to be piped in")
|
||||
ErrNoDetectManNum = errors.New("manual number cannot be detected from the filename")
|
||||
)
|
||||
|
||||
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),
|
||||
"install-config": installHelperCmd("/etc", 0o644),
|
||||
"install-license": installHelperCmd("/usr/share/licenses", 0o644),
|
||||
"install-desktop": installHelperCmd("/usr/share/applications", 0o644),
|
||||
"install-manual": installManualCmd,
|
||||
"install-completion": installCompletionCmd,
|
||||
"install-library": installLibraryCmd,
|
||||
"git-version": gitVersionCmd,
|
||||
}
|
||||
|
||||
// rHelpers contains restricted read-only helpers that don't modify any state
|
||||
var rHelpers = shutils.ExecFuncs{
|
||||
"git-version": gitVersionCmd,
|
||||
}
|
||||
|
||||
func installHelperCmd(prefix string, perms os.FileMode) shutils.ExecFunc {
|
||||
return func(hc interp.HandlerContext, cmd string, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return shutils.InsufficientArgsError(cmd, 1, len(args))
|
||||
}
|
||||
|
||||
from := resolvePath(hc, args[0])
|
||||
to := ""
|
||||
if len(args) > 1 {
|
||||
to = filepath.Join(hc.Env.Get("pkgdir").Str, prefix, args[1])
|
||||
} else {
|
||||
to = filepath.Join(hc.Env.Get("pkgdir").Str, prefix, filepath.Base(from))
|
||||
}
|
||||
|
||||
err := helperInstall(from, to, perms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", cmd, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func installManualCmd(hc interp.HandlerContext, cmd string, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return shutils.InsufficientArgsError(cmd, 1, len(args))
|
||||
}
|
||||
|
||||
from := resolvePath(hc, args[0])
|
||||
number := filepath.Base(from)
|
||||
// The man page may be compressed with gzip.
|
||||
// If it is, the .gz extension must be removed to properly
|
||||
// detect the number at the end of the filename.
|
||||
number = strings.TrimSuffix(number, ".gz")
|
||||
number = strings.TrimPrefix(filepath.Ext(number), ".")
|
||||
|
||||
// If number is not actually a number, return an error
|
||||
if _, err := strconv.Atoi(number); err != nil {
|
||||
return fmt.Errorf("install-manual: %w", ErrNoDetectManNum)
|
||||
}
|
||||
|
||||
prefix := "/usr/share/man/man" + number
|
||||
to := filepath.Join(hc.Env.Get("pkgdir").Str, prefix, filepath.Base(from))
|
||||
|
||||
return helperInstall(from, to, 0o644)
|
||||
}
|
||||
|
||||
func installCompletionCmd(hc interp.HandlerContext, cmd string, args []string) error {
|
||||
// If the command's stdin is the same as the system's,
|
||||
// that means nothing was piped in. In this case, return an error.
|
||||
if hc.Stdin == os.Stdin {
|
||||
return fmt.Errorf("install-completion: %w", ErrNoPipe)
|
||||
}
|
||||
|
||||
if len(args) < 2 {
|
||||
return shutils.InsufficientArgsError(cmd, 2, len(args))
|
||||
}
|
||||
|
||||
shell := args[0]
|
||||
name := args[1]
|
||||
|
||||
var prefix string
|
||||
switch shell {
|
||||
case "bash":
|
||||
prefix = "/usr/share/bash-completion/completions"
|
||||
case "zsh":
|
||||
prefix = "/usr/share/zsh/site-functions"
|
||||
name = "_" + name
|
||||
case "fish":
|
||||
prefix = "/usr/share/fish/vendor_completions.d"
|
||||
name += ".fish"
|
||||
}
|
||||
|
||||
path := filepath.Join(hc.Env.Get("pkgdir").Str, prefix, name)
|
||||
|
||||
err := os.MkdirAll(filepath.Dir(path), 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst, err := os.OpenFile(path, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
_, err = io.Copy(dst, hc.Stdin)
|
||||
return err
|
||||
}
|
||||
|
||||
func installLibraryCmd(hc interp.HandlerContext, cmd string, args []string) error {
|
||||
prefix := getLibPrefix(hc)
|
||||
fn := installHelperCmd(prefix, 0o755)
|
||||
return fn(hc, cmd, args)
|
||||
}
|
||||
|
||||
// See https://wiki.debian.org/Multiarch/Tuples
|
||||
var multiarchTupleMap = map[string]string{
|
||||
"386": "i386-linux-gnu",
|
||||
"amd64": "x86_64-linux-gnu",
|
||||
"arm5": "arm-linux-gnueabi",
|
||||
"arm6": "arm-linux-gnueabihf",
|
||||
"arm7": "arm-linux-gnueabihf",
|
||||
"arm64": "aarch64-linux-gnu",
|
||||
"mips": "mips-linux-gnu",
|
||||
"mipsle": "mipsel-linux-gnu",
|
||||
"mips64": "mips64-linux-gnuabi64",
|
||||
"mips64le": "mips64el-linux-gnuabi64",
|
||||
"ppc64": "powerpc64-linux-gnu",
|
||||
"ppc64le": "powerpc64le-linux-gnu",
|
||||
"s390x": "s390x-linux-gnu",
|
||||
"riscv64": "riscv64-linux-gnu",
|
||||
"loong64": "loongarch64-linux-gnu",
|
||||
}
|
||||
|
||||
// usrLibDistros is a list of distros that don't support
|
||||
// /usr/lib64, and must use /usr/lib
|
||||
var usrLibDistros = []string{
|
||||
"arch",
|
||||
"alpine",
|
||||
"void",
|
||||
"chimera",
|
||||
}
|
||||
|
||||
// Based on CMake's GNUInstallDirs
|
||||
func getLibPrefix(hc interp.HandlerContext) string {
|
||||
if dir, ok := os.LookupEnv("LURE_LIB_DIR"); ok {
|
||||
return dir
|
||||
}
|
||||
|
||||
out := "/usr/lib"
|
||||
|
||||
distroID := hc.Env.Get("DISTRO_ID").Str
|
||||
distroLike := strings.Split(hc.Env.Get("DISTRO_ID_LIKE").Str, " ")
|
||||
|
||||
for _, usrLibDistro := range usrLibDistros {
|
||||
if distroID == usrLibDistro || slices.Contains(distroLike, usrLibDistro) {
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
wordSize := unsafe.Sizeof(uintptr(0))
|
||||
if wordSize == 8 {
|
||||
out = "/usr/lib64"
|
||||
}
|
||||
|
||||
architecture := hc.Env.Get("ARCH").Str
|
||||
|
||||
if distroID == "debian" || slices.Contains(distroLike, "debian") {
|
||||
triple, ok := multiarchTupleMap[architecture]
|
||||
if ok {
|
||||
out = filepath.Join("/usr/lib", triple)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func gitVersionCmd(hc interp.HandlerContext, cmd string, args []string) error {
|
||||
path := hc.Dir
|
||||
if len(args) > 0 {
|
||||
path = resolvePath(hc, args[0])
|
||||
}
|
||||
|
||||
r, err := git.PlainOpen(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("git-version: %w", err)
|
||||
}
|
||||
|
||||
revNum := 0
|
||||
commits, err := r.Log(&git.LogOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("git-version: %w", err)
|
||||
}
|
||||
|
||||
commits.ForEach(func(*object.Commit) error {
|
||||
revNum++
|
||||
return nil
|
||||
})
|
||||
|
||||
HEAD, err := r.Head()
|
||||
if err != nil {
|
||||
return fmt.Errorf("git-version: %w", err)
|
||||
}
|
||||
|
||||
hash := HEAD.Hash().String()
|
||||
|
||||
fmt.Fprintf(hc.Stdout, "%d.%s", revNum, hash[:7])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func helperInstall(from, to string, perms os.FileMode) error {
|
||||
err := os.MkdirAll(filepath.Dir(to), 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := os.Open(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
dst, err := os.OpenFile(to, os.O_TRUNC|os.O_CREATE|os.O_RDWR, perms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
_, err = io.Copy(dst, src)
|
||||
return err
|
||||
}
|
||||
|
||||
func resolvePath(hc interp.HandlerContext, path string) string {
|
||||
if !filepath.IsAbs(path) {
|
||||
return filepath.Join(hc.Dir, path)
|
||||
}
|
||||
return path
|
||||
}
|
||||
64
info.go
64
info.go
@@ -19,10 +19,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"go.arsenm.dev/logger/log"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/lure/internal/cliutils"
|
||||
"go.arsenm.dev/lure/internal/overrides"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -32,28 +38,64 @@ func infoCmd(c *cli.Context) error {
|
||||
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")
|
||||
|
||||
var names []string
|
||||
all := c.Bool("all")
|
||||
|
||||
if !all {
|
||||
info, err := distro.ParseOSRelease(c.Context)
|
||||
if err != nil {
|
||||
log.Fatal("Error parsing os-release").Err(err).Send()
|
||||
log.Fatal("Error parsing os-release file").Err(err).Send()
|
||||
}
|
||||
names = overrides.Resolve(info, overrides.DefaultOpts)
|
||||
}
|
||||
|
||||
found, err := findPkg(args.First())
|
||||
if err != nil {
|
||||
log.Fatal("Error finding package").Err(err).Send()
|
||||
for _, pkg := range pkgs {
|
||||
if !all {
|
||||
depsSet := false
|
||||
buildDepsSet := false
|
||||
for _, name := range names {
|
||||
if deps, ok := pkg.Depends.Val[name]; ok && !depsSet {
|
||||
pkg.Depends.Val = map[string][]string{name: deps}
|
||||
depsSet = true
|
||||
}
|
||||
|
||||
// if multiple are matched, only use the first one
|
||||
script := found[0]
|
||||
|
||||
vars, err := getBuildVars(c.Context, script, info)
|
||||
if err != nil {
|
||||
log.Fatal("Error getting build variables").Err(err).Send()
|
||||
if buildDeps, ok := pkg.BuildDepends.Val[name]; ok && !buildDepsSet {
|
||||
pkg.BuildDepends.Val = map[string][]string{name: buildDeps}
|
||||
buildDepsSet = true
|
||||
}
|
||||
}
|
||||
|
||||
err = yaml.NewEncoder(os.Stdout).Encode(vars)
|
||||
if !depsSet {
|
||||
pkg.Depends.Val = nil
|
||||
}
|
||||
|
||||
if !buildDepsSet {
|
||||
pkg.BuildDepends.Val = nil
|
||||
}
|
||||
}
|
||||
|
||||
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
|
||||
if err != nil {
|
||||
log.Fatal("Error encoding script variables").Err(err).Send()
|
||||
}
|
||||
fmt.Println("---")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
43
install.go
43
install.go
@@ -20,8 +20,15 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
"go.arsenm.dev/logger/log"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.arsenm.dev/lure/internal/cliutils"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
"go.arsenm.dev/lure/manager"
|
||||
)
|
||||
|
||||
@@ -36,29 +43,45 @@ func installCmd(c *cli.Context) error {
|
||||
log.Fatal("Unable to detect supported package manager on system").Send()
|
||||
}
|
||||
|
||||
installPkgs(c.Context, args.Slice(), mgr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func installPkgs(ctx context.Context, pkgs []string, mgr manager.Manager) {
|
||||
err := pullRepos(ctx)
|
||||
err := repos.Pull(c.Context, gdb, cfg.Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||
}
|
||||
|
||||
scripts, notFound := findPkgs(pkgs)
|
||||
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"), notFound, mgr)
|
||||
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) {
|
||||
if len(notFound) > 0 {
|
||||
err = mgr.Install(nil, notFound...)
|
||||
err := mgr.Install(nil, notFound...)
|
||||
if err != nil {
|
||||
log.Fatal("Error installing native packages").Err(err).Send()
|
||||
}
|
||||
}
|
||||
|
||||
installScripts(ctx, mgr, scripts)
|
||||
installScripts(ctx, mgr, getScriptPaths(pkgs))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
for _, script := range scripts {
|
||||
builtPkgs, _, err := buildPackage(ctx, script, mgr)
|
||||
|
||||
4
internal/api/gen.go
Normal file
4
internal/api/gen.go
Normal file
@@ -0,0 +1,4 @@
|
||||
package api
|
||||
|
||||
//go:generate protoc --twirp_out=. lure.proto
|
||||
//go:generate protoc --go_out=. lure.proto
|
||||
885
internal/api/lure.pb.go
Normal file
885
internal/api/lure.pb.go
Normal file
@@ -0,0 +1,885 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.9
|
||||
// source: lure.proto
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// SORT_BY represents possible things to sort packages by
|
||||
type SORT_BY int32
|
||||
|
||||
const (
|
||||
SORT_BY_UNSORTED SORT_BY = 0
|
||||
SORT_BY_NAME SORT_BY = 1
|
||||
SORT_BY_REPOSITORY SORT_BY = 2
|
||||
SORT_BY_VERSION SORT_BY = 3
|
||||
)
|
||||
|
||||
// Enum value maps for SORT_BY.
|
||||
var (
|
||||
SORT_BY_name = map[int32]string{
|
||||
0: "UNSORTED",
|
||||
1: "NAME",
|
||||
2: "REPOSITORY",
|
||||
3: "VERSION",
|
||||
}
|
||||
SORT_BY_value = map[string]int32{
|
||||
"UNSORTED": 0,
|
||||
"NAME": 1,
|
||||
"REPOSITORY": 2,
|
||||
"VERSION": 3,
|
||||
}
|
||||
)
|
||||
|
||||
func (x SORT_BY) Enum() *SORT_BY {
|
||||
p := new(SORT_BY)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x SORT_BY) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (SORT_BY) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_lure_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (SORT_BY) Type() protoreflect.EnumType {
|
||||
return &file_lure_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x SORT_BY) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SORT_BY.Descriptor instead.
|
||||
func (SORT_BY) EnumDescriptor() ([]byte, []int) {
|
||||
return file_lure_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
// FILTER_TYPE represents possible filters for packages
|
||||
type FILTER_TYPE int32
|
||||
|
||||
const (
|
||||
FILTER_TYPE_NO_FILTER FILTER_TYPE = 0
|
||||
FILTER_TYPE_IN_REPOSITORY FILTER_TYPE = 1
|
||||
FILTER_TYPE_SUPPORTS_ARCH FILTER_TYPE = 2
|
||||
)
|
||||
|
||||
// Enum value maps for FILTER_TYPE.
|
||||
var (
|
||||
FILTER_TYPE_name = map[int32]string{
|
||||
0: "NO_FILTER",
|
||||
1: "IN_REPOSITORY",
|
||||
2: "SUPPORTS_ARCH",
|
||||
}
|
||||
FILTER_TYPE_value = map[string]int32{
|
||||
"NO_FILTER": 0,
|
||||
"IN_REPOSITORY": 1,
|
||||
"SUPPORTS_ARCH": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x FILTER_TYPE) Enum() *FILTER_TYPE {
|
||||
p := new(FILTER_TYPE)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x FILTER_TYPE) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (FILTER_TYPE) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_lure_proto_enumTypes[1].Descriptor()
|
||||
}
|
||||
|
||||
func (FILTER_TYPE) Type() protoreflect.EnumType {
|
||||
return &file_lure_proto_enumTypes[1]
|
||||
}
|
||||
|
||||
func (x FILTER_TYPE) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FILTER_TYPE.Descriptor instead.
|
||||
func (FILTER_TYPE) EnumDescriptor() ([]byte, []int) {
|
||||
return file_lure_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
// SearchRequest is a request to search for packages
|
||||
type SearchRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Query string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
|
||||
Limit int64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||
SortBy SORT_BY `protobuf:"varint,3,opt,name=sort_by,json=sortBy,proto3,enum=lure.SORT_BY" json:"sort_by,omitempty"`
|
||||
FilterType FILTER_TYPE `protobuf:"varint,4,opt,name=filter_type,json=filterType,proto3,enum=lure.FILTER_TYPE" json:"filter_type,omitempty"`
|
||||
FilterValue *string `protobuf:"bytes,5,opt,name=filter_value,json=filterValue,proto3,oneof" json:"filter_value,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SearchRequest) Reset() {
|
||||
*x = SearchRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_lure_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SearchRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SearchRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SearchRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_lure_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SearchRequest) Descriptor() ([]byte, []int) {
|
||||
return file_lure_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetQuery() string {
|
||||
if x != nil {
|
||||
return x.Query
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetLimit() int64 {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetSortBy() SORT_BY {
|
||||
if x != nil {
|
||||
return x.SortBy
|
||||
}
|
||||
return SORT_BY_UNSORTED
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetFilterType() FILTER_TYPE {
|
||||
if x != nil {
|
||||
return x.FilterType
|
||||
}
|
||||
return FILTER_TYPE_NO_FILTER
|
||||
}
|
||||
|
||||
func (x *SearchRequest) GetFilterValue() string {
|
||||
if x != nil && x.FilterValue != nil {
|
||||
return *x.FilterValue
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// StringList contains a list of strings
|
||||
type StringList struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Entries []string `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"`
|
||||
}
|
||||
|
||||
func (x *StringList) Reset() {
|
||||
*x = StringList{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_lure_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *StringList) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*StringList) ProtoMessage() {}
|
||||
|
||||
func (x *StringList) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_lure_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StringList.ProtoReflect.Descriptor instead.
|
||||
func (*StringList) Descriptor() ([]byte, []int) {
|
||||
return file_lure_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *StringList) GetEntries() []string {
|
||||
if x != nil {
|
||||
return x.Entries
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Package represents a LURE package
|
||||
type Package struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"`
|
||||
Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
|
||||
Release int64 `protobuf:"varint,4,opt,name=release,proto3" json:"release,omitempty"`
|
||||
Epoch *int64 `protobuf:"varint,5,opt,name=epoch,proto3,oneof" json:"epoch,omitempty"`
|
||||
Description *string `protobuf:"bytes,6,opt,name=description,proto3,oneof" json:"description,omitempty"`
|
||||
Homepage *string `protobuf:"bytes,7,opt,name=homepage,proto3,oneof" json:"homepage,omitempty"`
|
||||
Maintainer *string `protobuf:"bytes,8,opt,name=maintainer,proto3,oneof" json:"maintainer,omitempty"`
|
||||
Architectures []string `protobuf:"bytes,9,rep,name=architectures,proto3" json:"architectures,omitempty"`
|
||||
Licenses []string `protobuf:"bytes,10,rep,name=licenses,proto3" json:"licenses,omitempty"`
|
||||
Provides []string `protobuf:"bytes,11,rep,name=provides,proto3" json:"provides,omitempty"`
|
||||
Conflicts []string `protobuf:"bytes,12,rep,name=conflicts,proto3" json:"conflicts,omitempty"`
|
||||
Replaces []string `protobuf:"bytes,13,rep,name=replaces,proto3" json:"replaces,omitempty"`
|
||||
Depends map[string]*StringList `protobuf:"bytes,14,rep,name=depends,proto3" json:"depends,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
BuildDepends map[string]*StringList `protobuf:"bytes,15,rep,name=build_depends,json=buildDepends,proto3" json:"build_depends,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
}
|
||||
|
||||
func (x *Package) Reset() {
|
||||
*x = Package{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_lure_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Package) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Package) ProtoMessage() {}
|
||||
|
||||
func (x *Package) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_lure_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Package.ProtoReflect.Descriptor instead.
|
||||
func (*Package) Descriptor() ([]byte, []int) {
|
||||
return file_lure_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *Package) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Package) GetRepository() string {
|
||||
if x != nil {
|
||||
return x.Repository
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Package) GetVersion() string {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Package) GetRelease() int64 {
|
||||
if x != nil {
|
||||
return x.Release
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Package) GetEpoch() int64 {
|
||||
if x != nil && x.Epoch != nil {
|
||||
return *x.Epoch
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Package) GetDescription() string {
|
||||
if x != nil && x.Description != nil {
|
||||
return *x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Package) GetHomepage() string {
|
||||
if x != nil && x.Homepage != nil {
|
||||
return *x.Homepage
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Package) GetMaintainer() string {
|
||||
if x != nil && x.Maintainer != nil {
|
||||
return *x.Maintainer
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Package) GetArchitectures() []string {
|
||||
if x != nil {
|
||||
return x.Architectures
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Package) GetLicenses() []string {
|
||||
if x != nil {
|
||||
return x.Licenses
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Package) GetProvides() []string {
|
||||
if x != nil {
|
||||
return x.Provides
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Package) GetConflicts() []string {
|
||||
if x != nil {
|
||||
return x.Conflicts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Package) GetReplaces() []string {
|
||||
if x != nil {
|
||||
return x.Replaces
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Package) GetDepends() map[string]*StringList {
|
||||
if x != nil {
|
||||
return x.Depends
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Package) GetBuildDepends() map[string]*StringList {
|
||||
if x != nil {
|
||||
return x.BuildDepends
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetPackageRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetPackageRequest) Reset() {
|
||||
*x = GetPackageRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_lure_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetPackageRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetPackageRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetPackageRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_lure_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetPackageRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetPackageRequest) Descriptor() ([]byte, []int) {
|
||||
return file_lure_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *GetPackageRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetPackageRequest) GetRepository() string {
|
||||
if x != nil {
|
||||
return x.Repository
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// SearchResponse contains returned packages
|
||||
type SearchResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Packages []*Package `protobuf:"bytes,1,rep,name=packages,proto3" json:"packages,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SearchResponse) Reset() {
|
||||
*x = SearchResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_lure_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SearchResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SearchResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SearchResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_lure_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SearchResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SearchResponse) Descriptor() ([]byte, []int) {
|
||||
return file_lure_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *SearchResponse) GetPackages() []*Package {
|
||||
if x != nil {
|
||||
return x.Packages
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetBuildScriptRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetBuildScriptRequest) Reset() {
|
||||
*x = GetBuildScriptRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_lure_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetBuildScriptRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetBuildScriptRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetBuildScriptRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_lure_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetBuildScriptRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetBuildScriptRequest) Descriptor() ([]byte, []int) {
|
||||
return file_lure_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *GetBuildScriptRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetBuildScriptRequest) GetRepository() string {
|
||||
if x != nil {
|
||||
return x.Repository
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetBuildScriptResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Script string `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetBuildScriptResponse) Reset() {
|
||||
*x = GetBuildScriptResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_lure_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetBuildScriptResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetBuildScriptResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetBuildScriptResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_lure_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetBuildScriptResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetBuildScriptResponse) Descriptor() ([]byte, []int) {
|
||||
return file_lure_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *GetBuildScriptResponse) GetScript() string {
|
||||
if x != nil {
|
||||
return x.Script
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_lure_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_lure_proto_rawDesc = []byte{
|
||||
0x0a, 0x0a, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6c, 0x75,
|
||||
0x72, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69,
|
||||
0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74,
|
||||
0x12, 0x26, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x0e, 0x32, 0x0d, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x59,
|
||||
0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x32, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x74,
|
||||
0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e,
|
||||
0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45,
|
||||
0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0c,
|
||||
0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75,
|
||||
0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01,
|
||||
0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe4, 0x05,
|
||||
0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a,
|
||||
0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x0a,
|
||||
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61,
|
||||
0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
|
||||
0x65, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03,
|
||||
0x48, 0x00, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b,
|
||||
0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28,
|
||||
0x09, 0x48, 0x01, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x68, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67, 0x65, 0x18,
|
||||
0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x08, 0x68, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67,
|
||||
0x65, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e,
|
||||
0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x69, 0x6e,
|
||||
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x72, 0x63,
|
||||
0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x0d, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12,
|
||||
0x1a, 0x0a, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70,
|
||||
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x6c,
|
||||
0x69, 0x63, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66,
|
||||
0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65,
|
||||
0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65,
|
||||
0x73, 0x12, 0x34, 0x0a, 0x07, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67,
|
||||
0x65, 0x2e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07,
|
||||
0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64,
|
||||
0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f,
|
||||
0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75,
|
||||
0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
|
||||
0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x1a, 0x4c, 0x0a,
|
||||
0x0c, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
|
||||
0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10,
|
||||
0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74,
|
||||
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x51, 0x0a, 0x11, 0x42,
|
||||
0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x08,
|
||||
0x0a, 0x06, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73,
|
||||
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x68, 0x6f, 0x6d,
|
||||
0x65, 0x70, 0x61, 0x67, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61,
|
||||
0x69, 0x6e, 0x65, 0x72, 0x22, 0x47, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x61, 0x63, 0x6b, 0x61,
|
||||
0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a,
|
||||
0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x3b, 0x0a,
|
||||
0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x29, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
|
||||
0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x22, 0x4b, 0x0a, 0x15, 0x47, 0x65,
|
||||
0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73,
|
||||
0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x70,
|
||||
0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x30, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x42, 0x75,
|
||||
0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2a, 0x3e, 0x0a, 0x07, 0x53, 0x4f, 0x52,
|
||||
0x54, 0x5f, 0x42, 0x59, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x4e, 0x53, 0x4f, 0x52, 0x54, 0x45, 0x44,
|
||||
0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a,
|
||||
0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07,
|
||||
0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0b, 0x46, 0x49, 0x4c,
|
||||
0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x46,
|
||||
0x49, 0x4c, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x5f, 0x52, 0x45,
|
||||
0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x55,
|
||||
0x50, 0x50, 0x4f, 0x52, 0x54, 0x53, 0x5f, 0x41, 0x52, 0x43, 0x48, 0x10, 0x02, 0x32, 0xb9, 0x01,
|
||||
0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x33, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
|
||||
0x13, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72,
|
||||
0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x47, 0x65,
|
||||
0x74, 0x50, 0x6b, 0x67, 0x12, 0x17, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x50,
|
||||
0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e,
|
||||
0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x4b, 0x0a, 0x0e,
|
||||
0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1b,
|
||||
0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63,
|
||||
0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x75,
|
||||
0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70,
|
||||
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2e, 0x2f,
|
||||
0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_lure_proto_rawDescOnce sync.Once
|
||||
file_lure_proto_rawDescData = file_lure_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_lure_proto_rawDescGZIP() []byte {
|
||||
file_lure_proto_rawDescOnce.Do(func() {
|
||||
file_lure_proto_rawDescData = protoimpl.X.CompressGZIP(file_lure_proto_rawDescData)
|
||||
})
|
||||
return file_lure_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_lure_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_lure_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
|
||||
var file_lure_proto_goTypes = []interface{}{
|
||||
(SORT_BY)(0), // 0: lure.SORT_BY
|
||||
(FILTER_TYPE)(0), // 1: lure.FILTER_TYPE
|
||||
(*SearchRequest)(nil), // 2: lure.SearchRequest
|
||||
(*StringList)(nil), // 3: lure.StringList
|
||||
(*Package)(nil), // 4: lure.Package
|
||||
(*GetPackageRequest)(nil), // 5: lure.GetPackageRequest
|
||||
(*SearchResponse)(nil), // 6: lure.SearchResponse
|
||||
(*GetBuildScriptRequest)(nil), // 7: lure.GetBuildScriptRequest
|
||||
(*GetBuildScriptResponse)(nil), // 8: lure.GetBuildScriptResponse
|
||||
nil, // 9: lure.Package.DependsEntry
|
||||
nil, // 10: lure.Package.BuildDependsEntry
|
||||
}
|
||||
var file_lure_proto_depIdxs = []int32{
|
||||
0, // 0: lure.SearchRequest.sort_by:type_name -> lure.SORT_BY
|
||||
1, // 1: lure.SearchRequest.filter_type:type_name -> lure.FILTER_TYPE
|
||||
9, // 2: lure.Package.depends:type_name -> lure.Package.DependsEntry
|
||||
10, // 3: lure.Package.build_depends:type_name -> lure.Package.BuildDependsEntry
|
||||
4, // 4: lure.SearchResponse.packages:type_name -> lure.Package
|
||||
3, // 5: lure.Package.DependsEntry.value:type_name -> lure.StringList
|
||||
3, // 6: lure.Package.BuildDependsEntry.value:type_name -> lure.StringList
|
||||
2, // 7: lure.API.Search:input_type -> lure.SearchRequest
|
||||
5, // 8: lure.API.GetPkg:input_type -> lure.GetPackageRequest
|
||||
7, // 9: lure.API.GetBuildScript:input_type -> lure.GetBuildScriptRequest
|
||||
6, // 10: lure.API.Search:output_type -> lure.SearchResponse
|
||||
4, // 11: lure.API.GetPkg:output_type -> lure.Package
|
||||
8, // 12: lure.API.GetBuildScript:output_type -> lure.GetBuildScriptResponse
|
||||
10, // [10:13] is the sub-list for method output_type
|
||||
7, // [7:10] is the sub-list for method input_type
|
||||
7, // [7:7] is the sub-list for extension type_name
|
||||
7, // [7:7] is the sub-list for extension extendee
|
||||
0, // [0:7] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_lure_proto_init() }
|
||||
func file_lure_proto_init() {
|
||||
if File_lure_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_lure_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SearchRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_lure_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*StringList); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_lure_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Package); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_lure_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetPackageRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_lure_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SearchResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_lure_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetBuildScriptRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_lure_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetBuildScriptResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_lure_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||
file_lure_proto_msgTypes[2].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_lure_proto_rawDesc,
|
||||
NumEnums: 2,
|
||||
NumMessages: 9,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_lure_proto_goTypes,
|
||||
DependencyIndexes: file_lure_proto_depIdxs,
|
||||
EnumInfos: file_lure_proto_enumTypes,
|
||||
MessageInfos: file_lure_proto_msgTypes,
|
||||
}.Build()
|
||||
File_lure_proto = out.File
|
||||
file_lure_proto_rawDesc = nil
|
||||
file_lure_proto_goTypes = nil
|
||||
file_lure_proto_depIdxs = nil
|
||||
}
|
||||
82
internal/api/lure.proto
Normal file
82
internal/api/lure.proto
Normal file
@@ -0,0 +1,82 @@
|
||||
syntax = "proto3";
|
||||
package lure;
|
||||
|
||||
// Slight hack to provide protoc with a package name
|
||||
option go_package = "../api";
|
||||
|
||||
// SORT_BY represents possible things to sort packages by
|
||||
enum SORT_BY {
|
||||
UNSORTED = 0;
|
||||
NAME = 1;
|
||||
REPOSITORY = 2;
|
||||
VERSION = 3;
|
||||
}
|
||||
|
||||
// FILTER_TYPE represents possible filters for packages
|
||||
enum FILTER_TYPE {
|
||||
NO_FILTER = 0;
|
||||
IN_REPOSITORY = 1;
|
||||
SUPPORTS_ARCH = 2;
|
||||
}
|
||||
|
||||
// SearchRequest is a request to search for packages
|
||||
message SearchRequest {
|
||||
string query = 1;
|
||||
int64 limit = 2;
|
||||
SORT_BY sort_by = 3;
|
||||
FILTER_TYPE filter_type = 4;
|
||||
optional string filter_value = 5;
|
||||
}
|
||||
|
||||
// StringList contains a list of strings
|
||||
message StringList {
|
||||
repeated string entries = 1;
|
||||
}
|
||||
|
||||
// Package represents a LURE package
|
||||
message Package {
|
||||
string name = 1;
|
||||
string repository = 2;
|
||||
string version = 3;
|
||||
int64 release = 4;
|
||||
optional int64 epoch = 5;
|
||||
optional string description = 6;
|
||||
optional string homepage = 7;
|
||||
optional string maintainer = 8;
|
||||
repeated string architectures = 9;
|
||||
repeated string licenses = 10;
|
||||
repeated string provides = 11;
|
||||
repeated string conflicts = 12;
|
||||
repeated string replaces = 13;
|
||||
map<string, StringList> depends = 14;
|
||||
map<string, StringList> build_depends = 15;
|
||||
}
|
||||
|
||||
message GetPackageRequest {
|
||||
string name = 1;
|
||||
string repository = 2;
|
||||
}
|
||||
|
||||
// SearchResponse contains returned packages
|
||||
message SearchResponse {
|
||||
repeated Package packages = 1;
|
||||
}
|
||||
|
||||
message GetBuildScriptRequest {
|
||||
string name = 1;
|
||||
string repository = 2;
|
||||
}
|
||||
|
||||
message GetBuildScriptResponse {
|
||||
string script = 1;
|
||||
}
|
||||
|
||||
// Web is the LURE Web service
|
||||
service API {
|
||||
// Search searches through LURE packages in the database
|
||||
rpc Search(SearchRequest) returns (SearchResponse);
|
||||
// GetPkg gets a single LURE package from the database
|
||||
rpc GetPkg(GetPackageRequest) returns (Package);
|
||||
// GetBuildScript returns the build script for the given package
|
||||
rpc GetBuildScript(GetBuildScriptRequest) returns (GetBuildScriptResponse);
|
||||
}
|
||||
1703
internal/api/lure.twirp.go
Normal file
1703
internal/api/lure.twirp.go
Normal file
File diff suppressed because it is too large
Load Diff
114
internal/cliutils/prompt.go
Normal file
114
internal/cliutils/prompt.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package cliutils
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/internal/pager"
|
||||
)
|
||||
|
||||
// YesNoPrompt asks the user a yes or no question, using def as the default answer
|
||||
func YesNoPrompt(msg string, def bool) (bool, error) {
|
||||
var answer bool
|
||||
err := survey.AskOne(
|
||||
&survey.Confirm{
|
||||
Message: msg,
|
||||
Default: def,
|
||||
},
|
||||
&answer,
|
||||
)
|
||||
return answer, err
|
||||
}
|
||||
|
||||
// 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) error {
|
||||
view, err := YesNoPrompt("Would you like to view the build script for "+name, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if view {
|
||||
err = ShowScript(script, name, style)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cont, err := YesNoPrompt("Would you still like to continue?", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !cont {
|
||||
log.Fatal("User chose not to continue after reading script").Send()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShowScript uses the built-in pager to display a script at a
|
||||
// given path, in the given syntax highlighting style.
|
||||
func ShowScript(path, name, style string) error {
|
||||
scriptFl, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer scriptFl.Close()
|
||||
|
||||
str, err := pager.SyntaxHighlightBash(scriptFl, style)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pgr := pager.New(name, str)
|
||||
return pgr.Run()
|
||||
}
|
||||
|
||||
// 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) []db.Package {
|
||||
var outPkgs []db.Package
|
||||
for _, pkgs := range found {
|
||||
if len(pkgs) > 1 {
|
||||
choices, err := PkgPrompt(pkgs, verb)
|
||||
if err != nil {
|
||||
log.Fatal("Error prompting for choice of package").Send()
|
||||
}
|
||||
outPkgs = append(outPkgs, choices...)
|
||||
} else if len(pkgs) == 1 {
|
||||
outPkgs = append(outPkgs, pkgs[0])
|
||||
}
|
||||
}
|
||||
return outPkgs
|
||||
}
|
||||
|
||||
// PkgPrompt asks the user to choose between multiple packages.
|
||||
// The user may choose multiple packages.
|
||||
func PkgPrompt(options []db.Package, verb string) ([]db.Package, error) {
|
||||
names := make([]string, len(options))
|
||||
for i, option := range options {
|
||||
names[i] = option.Repository + "/" + option.Name + " " + option.Version
|
||||
}
|
||||
|
||||
prompt := &survey.MultiSelect{
|
||||
Options: names,
|
||||
Message: "Choose which package(s) to " + verb,
|
||||
}
|
||||
|
||||
var choices []int
|
||||
err := survey.AskOne(prompt, &choices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make([]db.Package, len(choices))
|
||||
for i, choiceIndex := range choices {
|
||||
out[i] = options[choiceIndex]
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
36
internal/config/config.go
Normal file
36
internal/config/config.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"go.arsenm.dev/lure/internal/types"
|
||||
)
|
||||
|
||||
var defaultConfig = types.Config{
|
||||
RootCmd: "sudo",
|
||||
PagerStyle: "native",
|
||||
IgnorePkgUpdates: []string{},
|
||||
Repos: []types.Repo{
|
||||
{
|
||||
Name: "default",
|
||||
URL: "https://github.com/Arsen6331/lure-repo.git",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
// 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)
|
||||
}
|
||||
76
internal/config/dirs.go
Normal file
76
internal/config/dirs.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"go.arsenm.dev/logger/log"
|
||||
)
|
||||
|
||||
var (
|
||||
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")
|
||||
|
||||
_, err = os.ReadDir(DBPath)
|
||||
DBPresent = err == nil
|
||||
}
|
||||
6
internal/config/version.go
Normal file
6
internal/config/version.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package config
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed version.txt
|
||||
var Version string
|
||||
@@ -1,7 +1,26 @@
|
||||
/*
|
||||
* LURE - Linux User REpository
|
||||
* Copyright (C) 2022 Arsen Musayelyan
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
@@ -24,3 +43,12 @@ func ARMVariant() string {
|
||||
return "arm5"
|
||||
}
|
||||
}
|
||||
|
||||
// Arch returns the canonical CPU architecture of the system
|
||||
func Arch() string {
|
||||
arch := runtime.GOARCH
|
||||
if arch == "arm" {
|
||||
arch = ARMVariant()
|
||||
}
|
||||
return arch
|
||||
}
|
||||
|
||||
197
internal/db/db.go
Normal file
197
internal/db/db.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/exp/slices"
|
||||
"modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func init() {
|
||||
sqlite.MustRegisterScalarFunction("json_array_contains", 2, JsonArrayContains)
|
||||
}
|
||||
|
||||
// Package is a LURE package's database representation
|
||||
type Package struct {
|
||||
Name string `sh:"name,required" db:"name"`
|
||||
Version string `sh:"version,required" db:"version"`
|
||||
Release int `sh:"release,required" db:"release"`
|
||||
Epoch uint `sh:"epoch" db:"epoch"`
|
||||
Description string `sh:"desc" db:"description"`
|
||||
Homepage string `sh:"homepage" db:"homepage"`
|
||||
Maintainer string `sh:"maintainer" db:"maintainer"`
|
||||
Architectures JSON[[]string] `sh:"architectures" db:"architectures"`
|
||||
Licenses JSON[[]string] `sh:"license" db:"licenses"`
|
||||
Provides JSON[[]string] `sh:"provides" db:"provides"`
|
||||
Conflicts JSON[[]string] `sh:"conflicts" db:"conflicts"`
|
||||
Replaces JSON[[]string] `sh:"replaces" db:"replaces"`
|
||||
Depends JSON[map[string][]string] `db:"depends"`
|
||||
BuildDepends JSON[map[string][]string] `db:"builddepends"`
|
||||
Repository string `db:"repository"`
|
||||
}
|
||||
|
||||
// Init initializes the database
|
||||
func Init(db *sqlx.DB) error {
|
||||
*db = *db.Unsafe()
|
||||
_, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS pkgs (
|
||||
name TEXT NOT NULL,
|
||||
repository TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
release INT NOT NULL,
|
||||
epoch INT,
|
||||
description TEXT,
|
||||
homepage TEXT,
|
||||
maintainer TEXT,
|
||||
architectures TEXT CHECK(architectures = 'null' OR (JSON_VALID(architectures) AND JSON_TYPE(architectures) = 'array')),
|
||||
licenses TEXT CHECK(licenses = 'null' OR (JSON_VALID(licenses) AND JSON_TYPE(licenses) = 'array')),
|
||||
provides TEXT CHECK(provides = 'null' OR (JSON_VALID(provides) AND JSON_TYPE(provides) = 'array')),
|
||||
conflicts TEXT CHECK(conflicts = 'null' OR (JSON_VALID(conflicts) AND JSON_TYPE(conflicts) = 'array')),
|
||||
replaces TEXT CHECK(replaces = 'null' OR (JSON_VALID(replaces) AND JSON_TYPE(replaces) = 'array')),
|
||||
depends TEXT CHECK(depends = 'null' OR (JSON_VALID(depends) AND JSON_TYPE(depends) = 'object')),
|
||||
builddepends TEXT CHECK(builddepends = 'null' OR (JSON_VALID(builddepends) AND JSON_TYPE(builddepends) = 'object')),
|
||||
UNIQUE(name, repository)
|
||||
);
|
||||
`)
|
||||
return err
|
||||
}
|
||||
|
||||
// InsertPackage adds a package to the database
|
||||
func InsertPackage(db *sqlx.DB, pkg Package) error {
|
||||
_, err := db.NamedExec(`
|
||||
INSERT OR REPLACE INTO pkgs (
|
||||
name,
|
||||
repository,
|
||||
version,
|
||||
release,
|
||||
epoch,
|
||||
description,
|
||||
homepage,
|
||||
maintainer,
|
||||
architectures,
|
||||
licenses,
|
||||
provides,
|
||||
conflicts,
|
||||
replaces,
|
||||
depends,
|
||||
builddepends
|
||||
) VALUES (
|
||||
:name,
|
||||
:repository,
|
||||
:version,
|
||||
:release,
|
||||
:epoch,
|
||||
:description,
|
||||
:homepage,
|
||||
:maintainer,
|
||||
:architectures,
|
||||
:licenses,
|
||||
:provides,
|
||||
:conflicts,
|
||||
:replaces,
|
||||
:depends,
|
||||
:builddepends
|
||||
);
|
||||
`, pkg)
|
||||
return err
|
||||
}
|
||||
|
||||
// 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...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
// GetPkg returns a single package that match the where conditions
|
||||
func GetPkg(db *sqlx.DB, where string, args ...any) (*Package, error) {
|
||||
out := &Package{}
|
||||
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...)
|
||||
return err
|
||||
}
|
||||
|
||||
func JsonArrayContains(ctx *sqlite.FunctionContext, args []driver.Value) (driver.Value, error) {
|
||||
value, ok := args[0].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("both arguments to json_array_contains must be strings")
|
||||
}
|
||||
|
||||
item, ok := args[1].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("both arguments to json_array_contains must be strings")
|
||||
}
|
||||
|
||||
var array []string
|
||||
err := json.Unmarshal([]byte(value), &array)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return slices.Contains(array, item), nil
|
||||
}
|
||||
|
||||
type JSON[T any] struct {
|
||||
Val T
|
||||
}
|
||||
|
||||
func NewJSON[T any](v T) JSON[T] {
|
||||
return JSON[T]{Val: v}
|
||||
}
|
||||
|
||||
func (s *JSON[T]) Scan(val any) error {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
err := json.Unmarshal([]byte(val), &s.Val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case sql.NullString:
|
||||
if val.Valid {
|
||||
err := json.Unmarshal([]byte(val.String), &s.Val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return errors.New("sqlite json types must be strings")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s JSON[T]) Value() (driver.Value, error) {
|
||||
data, err := json.Marshal(s.Val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func (s JSON[T]) MarshalYAML() (any, error) {
|
||||
return s.Val, nil
|
||||
}
|
||||
|
||||
func (s JSON[T]) String() string {
|
||||
return fmt.Sprint(s.Val)
|
||||
}
|
||||
|
||||
func (s JSON[T]) GoString() string {
|
||||
return fmt.Sprintf("%#v", s.Val)
|
||||
}
|
||||
234
internal/db/db_test.go
Normal file
234
internal/db/db_test.go
Normal file
@@ -0,0 +1,234 @@
|
||||
package db_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
)
|
||||
|
||||
var testPkg = db.Package{
|
||||
Name: "test",
|
||||
Version: "0.0.1",
|
||||
Release: 1,
|
||||
Epoch: 2,
|
||||
Description: "Test package",
|
||||
Homepage: "https://lure.arsenm.dev",
|
||||
Maintainer: "Arsen Musayelyan <arsen@arsenm.dev>",
|
||||
Architectures: db.NewJSON([]string{"arm64", "amd64"}),
|
||||
Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}),
|
||||
Provides: db.NewJSON([]string{"test"}),
|
||||
Conflicts: db.NewJSON([]string{"test"}),
|
||||
Replaces: db.NewJSON([]string{"test-old"}),
|
||||
Depends: db.NewJSON(map[string][]string{
|
||||
"": {"sudo"},
|
||||
}),
|
||||
BuildDepends: db.NewJSON(map[string][]string{
|
||||
"": {"golang"},
|
||||
"arch": {"go"},
|
||||
}),
|
||||
Repository: "default",
|
||||
}
|
||||
|
||||
func getDB(t *testing.T) (*sqlx.DB, error) {
|
||||
t.Helper()
|
||||
|
||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = db.Init(gdb)
|
||||
return gdb, err
|
||||
}
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
defer gdb.Close()
|
||||
|
||||
err = db.Init(gdb)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertPackage(t *testing.T) {
|
||||
gdb, err := getDB(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
defer gdb.Close()
|
||||
|
||||
err = db.InsertPackage(gdb, 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'")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(testPkg, dbPkg) {
|
||||
t.Errorf("Expected test package to be the same as database package")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPkgs(t *testing.T) {
|
||||
gdb, err := getDB(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
defer gdb.Close()
|
||||
|
||||
x1 := testPkg
|
||||
x1.Name = "x1"
|
||||
x2 := testPkg
|
||||
x2.Name = "x2"
|
||||
|
||||
err = db.InsertPackage(gdb, x1)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = db.InsertPackage(gdb, x2)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
result, err := db.GetPkgs(gdb, "name LIKE 'x%'")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
for result.Next() {
|
||||
var dbPkg db.Package
|
||||
err = result.StructScan(&dbPkg)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(dbPkg.Name, "x") {
|
||||
t.Errorf("Expected package name to start with 'x', got %s", dbPkg.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPkg(t *testing.T) {
|
||||
gdb, err := getDB(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
defer gdb.Close()
|
||||
|
||||
x1 := testPkg
|
||||
x1.Name = "x1"
|
||||
x2 := testPkg
|
||||
x2.Name = "x2"
|
||||
|
||||
err = db.InsertPackage(gdb, x1)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = db.InsertPackage(gdb, x2)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
pkg, err := db.GetPkg(gdb, "name LIKE 'x%' ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if pkg.Name != "x1" {
|
||||
t.Errorf("Expected x1 package, got %s", pkg.Name)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*pkg, x1) {
|
||||
t.Errorf("Expected x1 to be %v, got %v", x1, *pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeletePkgs(t *testing.T) {
|
||||
gdb, err := getDB(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
defer gdb.Close()
|
||||
|
||||
x1 := testPkg
|
||||
x1.Name = "x1"
|
||||
x2 := testPkg
|
||||
x2.Name = "x2"
|
||||
|
||||
err = db.InsertPackage(gdb, x1)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = db.InsertPackage(gdb, x2)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = db.DeletePkgs(gdb, "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;")
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if dbPkg.Name != "x2" {
|
||||
t.Errorf("Expected x2 package, got %s", dbPkg.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonArrayContains(t *testing.T) {
|
||||
gdb, err := getDB(t)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
defer gdb.Close()
|
||||
|
||||
x1 := testPkg
|
||||
x1.Name = "x1"
|
||||
x2 := testPkg
|
||||
x2.Name = "x2"
|
||||
x2.Provides.Val = append(x2.Provides.Val, "x")
|
||||
|
||||
err = db.InsertPackage(gdb, x1)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = db.InsertPackage(gdb, 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');")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if dbPkg.Name != "x2" {
|
||||
t.Errorf("Expected x2 package, got %s", dbPkg.Name)
|
||||
}
|
||||
}
|
||||
99
internal/overrides/overrides.go
Normal file
99
internal/overrides/overrides.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package overrides
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/lure/internal/cpu"
|
||||
)
|
||||
|
||||
type Opts struct {
|
||||
Name string
|
||||
Overrides bool
|
||||
LikeDistros bool
|
||||
}
|
||||
|
||||
var DefaultOpts = &Opts{
|
||||
Overrides: true,
|
||||
LikeDistros: true,
|
||||
}
|
||||
|
||||
// Resolve generates a slice of possible override names in the order that they should be checked
|
||||
func Resolve(info *distro.OSRelease, opts *Opts) []string {
|
||||
if opts == nil {
|
||||
opts = DefaultOpts
|
||||
}
|
||||
|
||||
if !opts.Overrides {
|
||||
return []string{opts.Name}
|
||||
}
|
||||
|
||||
architectures := []string{runtime.GOARCH}
|
||||
|
||||
if runtime.GOARCH == "arm" {
|
||||
// More specific goes first
|
||||
architectures[0] = cpu.ARMVariant()
|
||||
architectures = append(architectures, "arm")
|
||||
}
|
||||
|
||||
distros := []string{info.ID}
|
||||
if opts.LikeDistros {
|
||||
distros = append(distros, info.Like...)
|
||||
}
|
||||
|
||||
var out []string
|
||||
for _, arch := range architectures {
|
||||
for _, distro := range distros {
|
||||
if opts.Name == "" {
|
||||
out = append(
|
||||
out,
|
||||
arch+"_"+distro,
|
||||
distro,
|
||||
)
|
||||
} else {
|
||||
out = append(
|
||||
out,
|
||||
opts.Name+"_"+arch+"_"+distro,
|
||||
opts.Name+"_"+distro,
|
||||
)
|
||||
}
|
||||
}
|
||||
if opts.Name == "" {
|
||||
out = append(out, arch)
|
||||
} else {
|
||||
out = append(out, opts.Name+"_"+arch)
|
||||
}
|
||||
}
|
||||
out = append(out, opts.Name)
|
||||
|
||||
for index, item := range out {
|
||||
out[index] = strings.ReplaceAll(item, "-", "_")
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (o *Opts) WithName(name string) *Opts {
|
||||
out := &Opts{}
|
||||
*out = *o
|
||||
|
||||
out.Name = name
|
||||
return out
|
||||
}
|
||||
|
||||
func (o *Opts) WithOverrides(v bool) *Opts {
|
||||
out := &Opts{}
|
||||
*out = *o
|
||||
|
||||
out.Overrides = v
|
||||
return out
|
||||
}
|
||||
|
||||
func (o *Opts) WithLikeDistros(v bool) *Opts {
|
||||
out := &Opts{}
|
||||
*out = *o
|
||||
|
||||
out.LikeDistros = v
|
||||
return out
|
||||
}
|
||||
88
internal/overrides/overrides_test.go
Normal file
88
internal/overrides/overrides_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package overrides_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/lure/internal/overrides"
|
||||
)
|
||||
|
||||
var info = &distro.OSRelease{
|
||||
ID: "centos",
|
||||
Like: []string{"rhel", "fedora"},
|
||||
}
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
names := overrides.Resolve(info, nil)
|
||||
|
||||
expected := []string{
|
||||
"amd64_centos",
|
||||
"centos",
|
||||
"amd64_rhel",
|
||||
"rhel",
|
||||
"amd64_fedora",
|
||||
"fedora",
|
||||
"amd64",
|
||||
"",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(names, expected) {
|
||||
t.Errorf("expected %v, got %v", expected, names)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveName(t *testing.T) {
|
||||
names := overrides.Resolve(info, &overrides.Opts{
|
||||
Name: "deps",
|
||||
Overrides: true,
|
||||
LikeDistros: true,
|
||||
})
|
||||
|
||||
expected := []string{
|
||||
"deps_amd64_centos",
|
||||
"deps_centos",
|
||||
"deps_amd64_rhel",
|
||||
"deps_rhel",
|
||||
"deps_amd64_fedora",
|
||||
"deps_fedora",
|
||||
"deps_amd64",
|
||||
"deps",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(names, expected) {
|
||||
t.Errorf("expected %v, got %v", expected, names)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveNoLikeDistros(t *testing.T) {
|
||||
names := overrides.Resolve(info, &overrides.Opts{
|
||||
Overrides: true,
|
||||
LikeDistros: false,
|
||||
})
|
||||
|
||||
expected := []string{
|
||||
"amd64_centos",
|
||||
"centos",
|
||||
"amd64",
|
||||
"",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(names, expected) {
|
||||
t.Errorf("expected %v, got %v", expected, names)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveNoOverrides(t *testing.T) {
|
||||
names := overrides.Resolve(info, &overrides.Opts{
|
||||
Name: "deps",
|
||||
Overrides: false,
|
||||
LikeDistros: false,
|
||||
})
|
||||
|
||||
expected := []string{"deps"}
|
||||
|
||||
if !reflect.DeepEqual(names, expected) {
|
||||
t.Errorf("expected %v, got %v", expected, names)
|
||||
}
|
||||
}
|
||||
19
internal/pager/highlighting.go
Normal file
19
internal/pager/highlighting.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package pager
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/alecthomas/chroma/v2/quick"
|
||||
)
|
||||
|
||||
func SyntaxHighlightBash(r io.Reader, style string) (string, error) {
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
w := &bytes.Buffer{}
|
||||
err = quick.Highlight(w, string(data), "bash", "terminal", style)
|
||||
return w.String(), err
|
||||
}
|
||||
124
internal/pager/pager.go
Normal file
124
internal/pager/pager.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package pager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/muesli/reflow/wordwrap"
|
||||
)
|
||||
|
||||
var (
|
||||
titleStyle lipgloss.Style
|
||||
infoStyle lipgloss.Style
|
||||
)
|
||||
|
||||
func init() {
|
||||
b := lipgloss.RoundedBorder()
|
||||
b.Right = "\u251C"
|
||||
titleStyle = lipgloss.NewStyle().BorderStyle(b).Padding(0, 1)
|
||||
|
||||
b = lipgloss.RoundedBorder()
|
||||
b.Left = "\u2524"
|
||||
infoStyle = titleStyle.Copy().BorderStyle(b)
|
||||
}
|
||||
|
||||
type Pager struct {
|
||||
model pagerModel
|
||||
}
|
||||
|
||||
func New(name, content string) *Pager {
|
||||
return &Pager{
|
||||
model: pagerModel{
|
||||
name: name,
|
||||
content: content,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pager) Run() error {
|
||||
prog := tea.NewProgram(
|
||||
p.model,
|
||||
tea.WithMouseCellMotion(),
|
||||
)
|
||||
|
||||
_, err := prog.Run()
|
||||
return err
|
||||
}
|
||||
|
||||
type pagerModel struct {
|
||||
name string
|
||||
content string
|
||||
ready bool
|
||||
viewport viewport.Model
|
||||
}
|
||||
|
||||
func (pm pagerModel) Init() tea.Cmd {
|
||||
return tea.ClearScreen
|
||||
}
|
||||
|
||||
func (pm pagerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var (
|
||||
cmd tea.Cmd
|
||||
cmds []tea.Cmd
|
||||
)
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
k := msg.String()
|
||||
if k == "ctrl+c" || k == "q" || k == "esc" {
|
||||
return pm, tea.Quit
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
headerHeight := lipgloss.Height(pm.headerView())
|
||||
footerHeight := lipgloss.Height(pm.footerView())
|
||||
verticalMarginHeight := headerHeight + footerHeight
|
||||
|
||||
if !pm.ready {
|
||||
pm.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight)
|
||||
pm.viewport.HighPerformanceRendering = true
|
||||
pm.viewport.YPosition = headerHeight + 1
|
||||
pm.viewport.SetContent(wordwrap.String(pm.content, msg.Width))
|
||||
pm.ready = true
|
||||
} else {
|
||||
pm.viewport.Width = msg.Width
|
||||
pm.viewport.Height = msg.Height - verticalMarginHeight
|
||||
}
|
||||
|
||||
cmds = append(cmds, viewport.Sync(pm.viewport))
|
||||
}
|
||||
|
||||
// Handle keyboard and mouse events in the viewport
|
||||
pm.viewport, cmd = pm.viewport.Update(msg)
|
||||
cmds = append(cmds, cmd)
|
||||
|
||||
return pm, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func (pm pagerModel) View() string {
|
||||
if !pm.ready {
|
||||
return "\n Initializing..."
|
||||
}
|
||||
return fmt.Sprintf("%s\n%s\n%s", pm.headerView(), pm.viewport.View(), pm.footerView())
|
||||
}
|
||||
|
||||
func (pm pagerModel) headerView() string {
|
||||
title := titleStyle.Render(pm.name)
|
||||
line := strings.Repeat("─", max(0, pm.viewport.Width-lipgloss.Width(title)))
|
||||
return lipgloss.JoinHorizontal(lipgloss.Center, title, line)
|
||||
}
|
||||
|
||||
func (pm pagerModel) footerView() string {
|
||||
info := infoStyle.Render(fmt.Sprintf("%3.f%%", pm.viewport.ScrollPercent()*100))
|
||||
line := strings.Repeat("─", max(0, pm.viewport.Width-lipgloss.Width(info)))
|
||||
return lipgloss.JoinHorizontal(lipgloss.Center, line, info)
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
64
internal/repos/find.go
Normal file
64
internal/repos/find.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package repos
|
||||
|
||||
import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.arsenm.dev/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) {
|
||||
found := map[string][]db.Package{}
|
||||
notFound := []string(nil)
|
||||
|
||||
for _, pkgName := range pkgs {
|
||||
if pkgName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
result, err := db.GetPkgs(gdb, "name LIKE ?", pkgName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
added := 0
|
||||
for result.Next() {
|
||||
var pkg db.Package
|
||||
err = result.StructScan(&pkg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
added++
|
||||
found[pkgName] = append(found[pkgName], pkg)
|
||||
}
|
||||
result.Close()
|
||||
|
||||
if added == 0 {
|
||||
result, err := db.GetPkgs(gdb, "json_array_contains(provides, ?)", pkgName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for result.Next() {
|
||||
var pkg db.Package
|
||||
err = result.StructScan(&pkg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
added++
|
||||
found[pkgName] = append(found[pkgName], pkg)
|
||||
}
|
||||
|
||||
result.Close()
|
||||
}
|
||||
|
||||
if added == 0 {
|
||||
notFound = append(notFound, pkgName)
|
||||
}
|
||||
}
|
||||
|
||||
return found, notFound, nil
|
||||
}
|
||||
135
internal/repos/find_test.go
Normal file
135
internal/repos/find_test.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package repos_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
"go.arsenm.dev/lure/internal/types"
|
||||
)
|
||||
|
||||
func TestFindPkgs(t *testing.T) {
|
||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
defer gdb.Close()
|
||||
|
||||
err = db.Init(gdb)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
setCfgDirs(t)
|
||||
defer removeCacheDir(t)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
err = repos.Pull(ctx, gdb, []types.Repo{
|
||||
{
|
||||
Name: "default",
|
||||
URL: "https://github.com/Arsen6331/lure-repo.git",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
found, notFound, err := repos.FindPkgs(gdb, []string{"itd", "nonexistentpackage1", "nonexistentpackage2"})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(notFound, []string{"nonexistentpackage1", "nonexistentpackage2"}) {
|
||||
t.Errorf("Expected 'nonexistentpackage{1,2} not to be found")
|
||||
}
|
||||
|
||||
if len(found) != 1 {
|
||||
t.Errorf("Expected 1 package found, got %d", len(found))
|
||||
}
|
||||
|
||||
itdPkgs, ok := found["itd"]
|
||||
if !ok {
|
||||
t.Fatalf("Expected 'itd' packages to be found")
|
||||
}
|
||||
|
||||
if len(itdPkgs) < 2 {
|
||||
t.Errorf("Expected two 'itd' packages to be found")
|
||||
}
|
||||
|
||||
for i, pkg := range itdPkgs {
|
||||
if !strings.HasPrefix(pkg.Name, "itd") {
|
||||
t.Errorf("Expected package name of all found packages to start with 'itd', got %s on element %d", pkg.Name, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindPkgsEmpty(t *testing.T) {
|
||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
defer gdb.Close()
|
||||
|
||||
err = db.Init(gdb)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
setCfgDirs(t)
|
||||
defer removeCacheDir(t)
|
||||
|
||||
err = db.InsertPackage(gdb, db.Package{
|
||||
Name: "test1",
|
||||
Repository: "default",
|
||||
Version: "0.0.1",
|
||||
Release: 1,
|
||||
Description: "Test package 1",
|
||||
Provides: db.NewJSON([]string{""}),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = db.InsertPackage(gdb, db.Package{
|
||||
Name: "test2",
|
||||
Repository: "default",
|
||||
Version: "0.0.1",
|
||||
Release: 1,
|
||||
Description: "Test package 2",
|
||||
Provides: db.NewJSON([]string{"test"}),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
found, notFound, err := repos.FindPkgs(gdb, []string{"test", ""})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if len(notFound) != 0 {
|
||||
t.Errorf("Expected all packages to be found")
|
||||
}
|
||||
|
||||
if len(found) != 1 {
|
||||
t.Errorf("Expected 1 package found, got %d", len(found))
|
||||
}
|
||||
|
||||
testPkgs, ok := found["test"]
|
||||
if !ok {
|
||||
t.Fatalf("Expected 'test' packages to be found")
|
||||
}
|
||||
|
||||
if len(testPkgs) != 1 {
|
||||
t.Errorf("Expected one 'test' package to be found, got %d", len(testPkgs))
|
||||
}
|
||||
|
||||
if testPkgs[0].Name != "test2" {
|
||||
t.Errorf("Expected 'test2' package, got '%s'", testPkgs[0].Name)
|
||||
}
|
||||
}
|
||||
397
internal/repos/pull.go
Normal file
397
internal/repos/pull.go
Normal file
@@ -0,0 +1,397 @@
|
||||
package repos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/osfs"
|
||||
"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.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/lure/download"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/internal/shutils"
|
||||
"go.arsenm.dev/lure/internal/shutils/decoder"
|
||||
"go.arsenm.dev/lure/internal/types"
|
||||
"go.arsenm.dev/lure/vercmp"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
for _, repo := range repos {
|
||||
repoURL, err := url.Parse(repo.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Pulling repository").Str("name", repo.Name).Send()
|
||||
repoDir := filepath.Join(config.RepoDir, repo.Name)
|
||||
|
||||
var repoFS billy.Filesystem
|
||||
gitDir := filepath.Join(repoDir, ".git")
|
||||
// Only pull repos that contain valid git repos
|
||||
if fi, err := os.Stat(gitDir); err == nil && fi.IsDir() {
|
||||
r, err := git.PlainOpen(repoDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := r.Worktree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
old, err := r.Head()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.PullContext(ctx, &git.PullOptions{Progress: os.Stderr})
|
||||
if errors.Is(err, git.NoErrAlreadyUpToDate) {
|
||||
log.Info("Repository up to date").Str("name", repo.Name).Send()
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
new, err := r.Head()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = processRepoFull(ctx, repo, repoDir, gdb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = os.RemoveAll(repoDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(repoDir, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(repoURL.Scheme, "git+") {
|
||||
repoURL.Scheme = "git+" + repoURL.Scheme
|
||||
}
|
||||
|
||||
err = download.Get(ctx, download.GetOptions{
|
||||
SourceURL: repoURL.String(),
|
||||
Destination: repoDir,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = processRepoFull(ctx, repo, repoDir, gdb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repoFS = osfs.New(repoDir)
|
||||
}
|
||||
|
||||
fl, err := repoFS.Open("lure-repo.toml")
|
||||
if err != nil {
|
||||
log.Warn("Git repository does not appear to be a valid LURE repo").Str("repo", repo.Name).Send()
|
||||
continue
|
||||
}
|
||||
|
||||
var repoCfg types.RepoConfig
|
||||
err = toml.NewDecoder(fl).Decode(&repoCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fl.Close()
|
||||
|
||||
currentVer, _, _ := strings.Cut(config.Version, "-")
|
||||
if vercmp.Compare(currentVer, repoCfg.Repo.MinVersion) == -1 {
|
||||
log.Warn("LURE repo's minumum LURE version is greater than the current version. Try updating LURE if something doesn't work.").Str("repo", repo.Name).Send()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type actionType uint8
|
||||
|
||||
const (
|
||||
actionDelete actionType = iota
|
||||
actionUpdate
|
||||
)
|
||||
|
||||
type action struct {
|
||||
Type actionType
|
||||
File string
|
||||
}
|
||||
|
||||
func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference, gdb *sqlx.DB) error {
|
||||
oldCommit, err := r.CommitObject(old.Hash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newCommit, err := r.CommitObject(new.Hash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patch, err := oldCommit.Patch(newCommit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var actions []action
|
||||
for _, fp := range patch.FilePatches() {
|
||||
from, to := fp.Files()
|
||||
|
||||
if !isValid(from, to) {
|
||||
continue
|
||||
}
|
||||
|
||||
if to == nil {
|
||||
actions = append(actions, action{
|
||||
Type: actionDelete,
|
||||
File: from.Path(),
|
||||
})
|
||||
} else if from == nil {
|
||||
actions = append(actions, action{
|
||||
Type: actionUpdate,
|
||||
File: to.Path(),
|
||||
})
|
||||
} else {
|
||||
if from.Path() != to.Path() {
|
||||
actions = append(actions,
|
||||
action{
|
||||
Type: actionDelete,
|
||||
File: from.Path(),
|
||||
},
|
||||
action{
|
||||
Type: actionUpdate,
|
||||
File: to.Path(),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
actions = append(actions, action{
|
||||
Type: actionUpdate,
|
||||
File: to.Path(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repoDir := w.Filesystem.Root()
|
||||
parser := syntax.NewParser()
|
||||
|
||||
for _, action := range actions {
|
||||
env := append(os.Environ(), "scriptdir="+filepath.Dir(filepath.Join(repoDir, action.File)))
|
||||
runner, err := interp.New(
|
||||
interp.Env(expand.ListEnviron(env...)),
|
||||
interp.ExecHandler(shutils.NopExec),
|
||||
interp.ReadDirHandler(shutils.RestrictedReadDir(repoDir)),
|
||||
interp.StatHandler(shutils.RestrictedStat(repoDir)),
|
||||
interp.OpenHandler(shutils.RestrictedOpen(repoDir)),
|
||||
interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch action.Type {
|
||||
case actionDelete:
|
||||
if filepath.Base(action.File) != "lure.sh" {
|
||||
continue
|
||||
}
|
||||
|
||||
scriptFl, err := oldCommit.File(action.File)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
r, err := scriptFl.Reader()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pkg db.Package
|
||||
err = parseScript(ctx, parser, runner, r, &pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = db.DeletePkgs(gdb, "name = ? AND repository = ?", pkg.Name, repo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case actionUpdate:
|
||||
if filepath.Base(action.File) != "lure.sh" {
|
||||
action.File = filepath.Join(filepath.Dir(action.File), "lure.sh")
|
||||
}
|
||||
|
||||
scriptFl, err := newCommit.File(action.File)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
r, err := scriptFl.Reader()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pkg := db.Package{
|
||||
Depends: db.NewJSON(map[string][]string{}),
|
||||
BuildDepends: db.NewJSON(map[string][]string{}),
|
||||
Repository: repo.Name,
|
||||
}
|
||||
|
||||
err = parseScript(ctx, parser, runner, r, &pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resolveOverrides(runner, &pkg)
|
||||
|
||||
err = db.InsertPackage(gdb, pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isValid makes sure the path of the file being updated is valid.
|
||||
// It checks to make sure the file is not within a nested directory
|
||||
// and that it is called lure.sh.
|
||||
func isValid(from, to diff.File) bool {
|
||||
var path string
|
||||
if from != nil {
|
||||
path = from.Path()
|
||||
}
|
||||
if to != nil {
|
||||
path = to.Path()
|
||||
}
|
||||
|
||||
match, _ := filepath.Match("*/*.sh", path)
|
||||
return match
|
||||
}
|
||||
|
||||
func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *sqlx.DB) error {
|
||||
glob := filepath.Join(repoDir, "/*/lure.sh")
|
||||
matches, err := filepath.Glob(glob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parser := syntax.NewParser()
|
||||
|
||||
for _, match := range matches {
|
||||
env := append(os.Environ(), "scriptdir="+filepath.Dir(match))
|
||||
runner, err := interp.New(
|
||||
interp.Env(expand.ListEnviron(env...)),
|
||||
interp.ExecHandler(shutils.NopExec),
|
||||
interp.ReadDirHandler(shutils.RestrictedReadDir(repoDir)),
|
||||
interp.StatHandler(shutils.RestrictedStat(repoDir)),
|
||||
interp.OpenHandler(shutils.RestrictedOpen(repoDir)),
|
||||
interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scriptFl, err := os.Open(match)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pkg := db.Package{
|
||||
Depends: db.NewJSON(map[string][]string{}),
|
||||
BuildDepends: db.NewJSON(map[string][]string{}),
|
||||
Repository: repo.Name,
|
||||
}
|
||||
|
||||
err = parseScript(ctx, parser, runner, scriptFl, &pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resolveOverrides(runner, &pkg)
|
||||
|
||||
err = db.InsertPackage(gdb, pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runner, r io.ReadCloser, pkg *db.Package) error {
|
||||
defer r.Close()
|
||||
fl, err := parser.Parse(r, "lure.sh")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runner.Reset()
|
||||
err = runner.Run(ctx, fl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d := decoder.New(&distro.OSRelease{}, runner)
|
||||
d.Overrides = false
|
||||
d.LikeDistros = false
|
||||
return d.DecodeVars(pkg)
|
||||
}
|
||||
|
||||
func resolveOverrides(runner *interp.Runner, pkg *db.Package) {
|
||||
for name, val := range runner.Vars {
|
||||
if strings.HasPrefix(name, "deps") {
|
||||
override := strings.TrimPrefix(name, "deps")
|
||||
override = strings.TrimPrefix(override, "_")
|
||||
|
||||
pkg.Depends.Val[override] = val.List
|
||||
} else if strings.HasPrefix(name, "build_deps") {
|
||||
override := strings.TrimPrefix(name, "build_deps")
|
||||
override = strings.TrimPrefix(override, "_")
|
||||
|
||||
pkg.BuildDepends.Val[override] = val.List
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
95
internal/repos/pull_test.go
Normal file
95
internal/repos/pull_test.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package repos_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
"go.arsenm.dev/lure/internal/types"
|
||||
)
|
||||
|
||||
func setCfgDirs(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
var err error
|
||||
config.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")
|
||||
|
||||
err = os.MkdirAll(config.RepoDir, 0o755)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = os.MkdirAll(config.PkgsDir, 0o755)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
config.DBPath = filepath.Join(config.CacheDir, "db")
|
||||
}
|
||||
|
||||
func removeCacheDir(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
err := os.RemoveAll(config.CacheDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPull(t *testing.T) {
|
||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
defer gdb.Close()
|
||||
|
||||
err = db.Init(gdb)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
setCfgDirs(t)
|
||||
defer removeCacheDir(t)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
err = repos.Pull(ctx, gdb, []types.Repo{
|
||||
{
|
||||
Name: "default",
|
||||
URL: "https://github.com/Arsen6331/lure-repo.git",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
result, err := db.GetPkgs(gdb, "name LIKE 'itd%'")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
var pkgAmt int
|
||||
for result.Next() {
|
||||
var dbPkg db.Package
|
||||
err = result.StructScan(&dbPkg)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
pkgAmt++
|
||||
}
|
||||
|
||||
if pkgAmt < 2 {
|
||||
t.Errorf("Expected 2 packages to match, got %d", pkgAmt)
|
||||
}
|
||||
}
|
||||
@@ -21,21 +21,19 @@ package decoder
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/lure/internal/cpu"
|
||||
"go.arsenm.dev/lure/internal/overrides"
|
||||
"golang.org/x/exp/slices"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
var ErrInvalidType = errors.New("val must be a pointer to a struct")
|
||||
var ErrNotPointerToStruct = errors.New("val must be a pointer to a struct")
|
||||
|
||||
type VarNotFoundError struct {
|
||||
name string
|
||||
@@ -45,6 +43,16 @@ func (nfe VarNotFoundError) Error() string {
|
||||
return "required variable '" + nfe.name + "' could not be found"
|
||||
}
|
||||
|
||||
type InvalidTypeError struct {
|
||||
name string
|
||||
vartype string
|
||||
exptype string
|
||||
}
|
||||
|
||||
func (ite InvalidTypeError) Error() string {
|
||||
return "variable '" + ite.name + "' is of type " + ite.vartype + ", but " + ite.exptype + " is expected"
|
||||
}
|
||||
|
||||
// Decoder provides methods for decoding variable values
|
||||
type Decoder struct {
|
||||
info *distro.OSRelease
|
||||
@@ -70,6 +78,18 @@ func (d *Decoder) DecodeVar(name string, val any) error {
|
||||
|
||||
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
WeaklyTypedInput: true,
|
||||
DecodeHook: mapstructure.DecodeHookFuncValue(func(from, to reflect.Value) (interface{}, error) {
|
||||
if strings.Contains(to.Type().String(), "db.JSON") {
|
||||
valType := to.FieldByName("Val").Type()
|
||||
if !from.Type().AssignableTo(valType) {
|
||||
return nil, InvalidTypeError{name, from.Type().String(), valType.String()}
|
||||
}
|
||||
|
||||
to.FieldByName("Val").Set(from)
|
||||
return to, nil
|
||||
}
|
||||
return from.Interface(), nil
|
||||
}),
|
||||
Result: val,
|
||||
TagName: "sh",
|
||||
})
|
||||
@@ -92,11 +112,11 @@ func (d *Decoder) DecodeVar(name string, val any) error {
|
||||
func (d *Decoder) DecodeVars(val any) error {
|
||||
valKind := reflect.TypeOf(val).Kind()
|
||||
if valKind != reflect.Pointer {
|
||||
return ErrInvalidType
|
||||
return ErrNotPointerToStruct
|
||||
} else {
|
||||
elemKind := reflect.TypeOf(val).Elem().Kind()
|
||||
if elemKind != reflect.Struct {
|
||||
return ErrInvalidType
|
||||
return ErrNotPointerToStruct
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +162,7 @@ func (d *Decoder) DecodeVars(val any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ScriptFunc func(ctx context.Context, sir string, args ...string) error
|
||||
type ScriptFunc func(ctx context.Context, opts ...interp.RunnerOption) error
|
||||
|
||||
// GetFunc returns a function corresponding to a bash function
|
||||
// with the given name
|
||||
@@ -152,16 +172,17 @@ func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return func(ctx context.Context, dir string, args ...string) error {
|
||||
return func(ctx context.Context, opts ...interp.RunnerOption) error {
|
||||
sub := d.runner.Subshell()
|
||||
interp.Params(args...)(sub)
|
||||
interp.Dir(dir)(sub)
|
||||
for _, opt := range opts {
|
||||
opt(sub)
|
||||
}
|
||||
return sub.Run(ctx, fn)
|
||||
}, true
|
||||
}
|
||||
|
||||
func (d *Decoder) getFunc(name string) *syntax.Stmt {
|
||||
names := d.genPossibleNames(name)
|
||||
names := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
|
||||
for _, fnName := range names {
|
||||
fn, ok := d.runner.Funcs[fnName]
|
||||
if ok {
|
||||
@@ -174,7 +195,7 @@ func (d *Decoder) getFunc(name string) *syntax.Stmt {
|
||||
// getVar gets a variable based on its name, taking into account
|
||||
// override variables and nameref variables.
|
||||
func (d *Decoder) getVar(name string) *expand.Variable {
|
||||
names := d.genPossibleNames(name)
|
||||
names := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
|
||||
for _, varName := range names {
|
||||
val, ok := d.runner.Vars[varName]
|
||||
if ok {
|
||||
@@ -192,43 +213,3 @@ func (d *Decoder) getVar(name string) *expand.Variable {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// genPossibleNames generates a slice of the possible names that
|
||||
// could be used in the order that they should be checked
|
||||
func (d *Decoder) genPossibleNames(name string) []string {
|
||||
if !d.Overrides {
|
||||
return []string{name}
|
||||
}
|
||||
|
||||
architectures := []string{runtime.GOARCH}
|
||||
|
||||
if runtime.GOARCH == "arm" {
|
||||
// More specific goes first
|
||||
architectures[0] = cpu.ARMVariant()
|
||||
architectures = append(architectures, "arm")
|
||||
}
|
||||
|
||||
distros := []string{d.info.ID}
|
||||
if d.LikeDistros {
|
||||
distros = append(distros, d.info.Like...)
|
||||
}
|
||||
|
||||
var out []string
|
||||
for _, arch := range architectures {
|
||||
for _, distro := range distros {
|
||||
out = append(
|
||||
out,
|
||||
fmt.Sprintf("%s_%s_%s", name, arch, distro),
|
||||
fmt.Sprintf("%s_%s", name, distro),
|
||||
)
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%s_%s", name, arch))
|
||||
}
|
||||
out = append(out, name)
|
||||
|
||||
for index, item := range out {
|
||||
out[index] = strings.ReplaceAll(item, "-", "_")
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
206
internal/shutils/decoder/decoder_test.go
Normal file
206
internal/shutils/decoder/decoder_test.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package decoder_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/lure/internal/shutils/decoder"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
const testScript = `
|
||||
name='test'
|
||||
version='0.0.1'
|
||||
release=1
|
||||
epoch=2
|
||||
desc="Test package"
|
||||
homepage='https://lure.arsenm.dev'
|
||||
maintainer='Arsen Musayelyan <arsen@arsenm.dev>'
|
||||
architectures=('arm64' 'amd64')
|
||||
license=('GPL-3.0-or-later')
|
||||
provides=('test')
|
||||
conflicts=('test')
|
||||
replaces=('test-old')
|
||||
replaces_test_os=('test-legacy')
|
||||
|
||||
deps=('sudo')
|
||||
|
||||
build_deps=('golang')
|
||||
build_deps_arch=('go')
|
||||
|
||||
test() {
|
||||
echo "Test"
|
||||
}
|
||||
|
||||
package() {
|
||||
install-binary test
|
||||
}
|
||||
`
|
||||
|
||||
var osRelease = &distro.OSRelease{
|
||||
ID: "test_os",
|
||||
Like: []string{"arch"},
|
||||
}
|
||||
|
||||
func TestDecodeVars(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "lure.sh")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
runner, err := interp.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = runner.Run(ctx, fl)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
dec := decoder.New(osRelease, runner)
|
||||
|
||||
var bv BuildVars
|
||||
err = dec.DecodeVars(&bv)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
expected := BuildVars{
|
||||
Name: "test",
|
||||
Version: "0.0.1",
|
||||
Release: 1,
|
||||
Epoch: 2,
|
||||
Description: "Test package",
|
||||
Homepage: "https://lure.arsenm.dev",
|
||||
Maintainer: "Arsen Musayelyan <arsen@arsenm.dev>",
|
||||
Architectures: []string{"arm64", "amd64"},
|
||||
Licenses: []string{"GPL-3.0-or-later"},
|
||||
Provides: []string{"test"},
|
||||
Conflicts: []string{"test"},
|
||||
Replaces: []string{"test-legacy"},
|
||||
Depends: []string{"sudo"},
|
||||
BuildDepends: []string{"go"},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(bv, expected) {
|
||||
t.Errorf("Expected %v, got %v", expected, bv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeVarsMissing(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
const testScript = `
|
||||
name='test'
|
||||
epoch=2
|
||||
desc="Test package"
|
||||
homepage='https://lure.arsenm.dev'
|
||||
maintainer='Arsen Musayelyan <arsen@arsenm.dev>'
|
||||
architectures=('arm64' 'amd64')
|
||||
license=('GPL-3.0-or-later')
|
||||
provides=('test')
|
||||
conflicts=('test')
|
||||
replaces=('test-old')
|
||||
replaces_test_os=('test-legacy')
|
||||
|
||||
deps=('sudo')
|
||||
|
||||
build_deps=('golang')
|
||||
build_deps_arch=('go')
|
||||
|
||||
test() {
|
||||
echo "Test"
|
||||
}
|
||||
|
||||
package() {
|
||||
install-binary test
|
||||
}
|
||||
`
|
||||
|
||||
fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "lure.sh")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
runner, err := interp.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = runner.Run(ctx, fl)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
dec := decoder.New(osRelease, runner)
|
||||
|
||||
var bv BuildVars
|
||||
err = dec.DecodeVars(&bv)
|
||||
|
||||
var notFoundErr decoder.VarNotFoundError
|
||||
if !errors.As(err, ¬FoundErr) {
|
||||
t.Fatalf("Expected VarNotFoundError, got %T %v", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFunc(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "lure.sh")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
runner, err := interp.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = runner.Run(ctx, fl)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
dec := decoder.New(osRelease, runner)
|
||||
fn, ok := dec.GetFunc("test")
|
||||
if !ok {
|
||||
t.Fatalf("Expected test() function to exist")
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
err = fn(ctx, interp.StdIO(os.Stdin, buf, buf))
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if buf.String() != "Test\n" {
|
||||
t.Fatalf(`Expected "Test\n", got %#v`, buf.String())
|
||||
}
|
||||
}
|
||||
@@ -20,24 +20,44 @@ package shutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
)
|
||||
|
||||
type ExecFuncs map[string]func(interp.HandlerContext, []string) uint8
|
||||
func InsufficientArgsError(cmd string, exp, got int) error {
|
||||
argsWord := "arguments"
|
||||
if exp == 1 {
|
||||
argsWord = "argument"
|
||||
}
|
||||
|
||||
func (ef ExecFuncs) ExecHandler(ctx context.Context, args []string) error {
|
||||
return fmt.Errorf("%s: command requires at least %d %s, got %d", cmd, exp, argsWord, got)
|
||||
}
|
||||
|
||||
type ExecFunc func(hc interp.HandlerContext, name string, args []string) error
|
||||
|
||||
type ExecFuncs map[string]ExecFunc
|
||||
|
||||
// ExecHandler returns a new ExecHandlerFunc that falls back to fallback
|
||||
// if the command cannot be found in the map. If fallback is nil, the default
|
||||
// handler is used.
|
||||
func (ef ExecFuncs) ExecHandler(fallback interp.ExecHandlerFunc) interp.ExecHandlerFunc {
|
||||
return func(ctx context.Context, args []string) error {
|
||||
name := args[0]
|
||||
|
||||
if fn, ok := ef[name]; ok {
|
||||
hctx := interp.HandlerCtx(ctx)
|
||||
ec := fn(hctx, args)
|
||||
if ec != 0 {
|
||||
return interp.NewExitStatus(ec)
|
||||
if len(args) > 1 {
|
||||
return fn(hctx, args[0], args[1:])
|
||||
} else {
|
||||
return fn(hctx, args[0], nil)
|
||||
}
|
||||
}
|
||||
|
||||
defExec := interp.DefaultExecHandler(2 * time.Second)
|
||||
return defExec(ctx, args)
|
||||
if fallback == nil {
|
||||
fallback = interp.DefaultExecHandler(2 * time.Second)
|
||||
}
|
||||
return fallback(ctx, args)
|
||||
}
|
||||
}
|
||||
|
||||
106
internal/shutils/exec_test.go
Normal file
106
internal/shutils/exec_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package shutils_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/lure/internal/shutils"
|
||||
"go.arsenm.dev/lure/internal/shutils/decoder"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
const testScript = `
|
||||
name='test'
|
||||
version='0.0.1'
|
||||
release=1
|
||||
epoch=2
|
||||
desc="Test package"
|
||||
homepage='https://lure.arsenm.dev'
|
||||
maintainer='Arsen Musayelyan <arsen@arsenm.dev>'
|
||||
architectures=('arm64' 'amd64')
|
||||
license=('GPL-3.0-or-later')
|
||||
provides=('test')
|
||||
conflicts=('test')
|
||||
replaces=('test-old')
|
||||
replaces_test_os=('test-legacy')
|
||||
|
||||
deps=('sudo')
|
||||
|
||||
build_deps=('golang')
|
||||
build_deps_arch=('go')
|
||||
|
||||
test() {
|
||||
test-cmd "Hello, World"
|
||||
test-fb
|
||||
}
|
||||
|
||||
package() {
|
||||
install-binary test
|
||||
}
|
||||
`
|
||||
|
||||
var osRelease = &distro.OSRelease{
|
||||
ID: "test_os",
|
||||
Like: []string{"arch"},
|
||||
}
|
||||
|
||||
func TestExecFuncs(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "lure.sh")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
runner, err := interp.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = runner.Run(ctx, fl)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
dec := decoder.New(osRelease, runner)
|
||||
fn, ok := dec.GetFunc("test")
|
||||
if !ok {
|
||||
t.Fatalf("Expected test() function to exist")
|
||||
}
|
||||
|
||||
eh := shutils.ExecFuncs{
|
||||
"test-cmd": func(hc interp.HandlerContext, name string, args []string) error {
|
||||
if name != "test-cmd" {
|
||||
t.Errorf("Expected name to be 'test-cmd', got '%s'", name)
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
t.Fatalf("Expected at least one argument, got %d", len(args))
|
||||
}
|
||||
|
||||
if args[0] != "Hello, World" {
|
||||
t.Errorf("Expected first argument to be 'Hello, World', got '%s'", args[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
fbInvoked := false
|
||||
fbHandler := func(context.Context, []string) error {
|
||||
fbInvoked = true
|
||||
return nil
|
||||
}
|
||||
|
||||
err = fn(ctx, interp.ExecHandler(eh.ExecHandler(fbHandler)))
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if !fbInvoked {
|
||||
t.Errorf("Expected fallback handler to be invoked")
|
||||
}
|
||||
}
|
||||
40
internal/shutils/nop_test.go
Normal file
40
internal/shutils/nop_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package shutils_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.arsenm.dev/lure/internal/shutils"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
func TestNopExec(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
fl, err := syntax.NewParser().Parse(strings.NewReader(`/bin/echo test`), "lure.sh")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
runner, err := interp.New(
|
||||
interp.ExecHandler(shutils.NopExec),
|
||||
interp.StdIO(os.Stdin, buf, buf),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
err = runner.Run(ctx, fl)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if buf.String() != "" {
|
||||
t.Fatalf("Expected empty string, got %#v", buf.String())
|
||||
}
|
||||
}
|
||||
76
internal/shutils/restricted.go
Normal file
76
internal/shutils/restricted.go
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* LURE - Linux User REpository
|
||||
* Copyright (C) 2022 Arsen Musayelyan
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package shutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
)
|
||||
|
||||
func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc {
|
||||
return func(ctx context.Context, s string) ([]os.FileInfo, error) {
|
||||
for _, allowedPrefix := range allowedPrefixes {
|
||||
if strings.HasPrefix(s, allowedPrefix) {
|
||||
return interp.DefaultReadDirHandler()(ctx, s)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
}
|
||||
|
||||
func RestrictedStat(allowedPrefixes ...string) interp.StatHandlerFunc {
|
||||
return func(ctx context.Context, s string, b bool) (os.FileInfo, error) {
|
||||
for _, allowedPrefix := range allowedPrefixes {
|
||||
if strings.HasPrefix(s, allowedPrefix) {
|
||||
return interp.DefaultStatHandler()(ctx, s, b)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
}
|
||||
|
||||
func RestrictedOpen(allowedPrefixes ...string) interp.OpenHandlerFunc {
|
||||
return func(ctx context.Context, s string, i int, fm os.FileMode) (io.ReadWriteCloser, error) {
|
||||
for _, allowedPrefix := range allowedPrefixes {
|
||||
if strings.HasPrefix(s, allowedPrefix) {
|
||||
return interp.DefaultOpenHandler()(ctx, s, i, fm)
|
||||
}
|
||||
}
|
||||
|
||||
return NopRWC{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func RestrictedExec(allowedCmds ...string) interp.ExecHandlerFunc {
|
||||
return func(ctx context.Context, args []string) error {
|
||||
if slices.Contains(allowedCmds, args[0]) {
|
||||
return interp.DefaultExecHandler(2*time.Second)(ctx, args)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
15
internal/types/config.go
Normal file
15
internal/types/config.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package types
|
||||
|
||||
// Config represents the LURE configuration file
|
||||
type Config struct {
|
||||
RootCmd string `toml:"rootCmd"`
|
||||
PagerStyle string `toml:"pagerStyle"`
|
||||
IgnorePkgUpdates []string `toml:"ignorePkgUpdates"`
|
||||
Repos []Repo `toml:"repo"`
|
||||
}
|
||||
|
||||
// Repo represents a LURE repo within a configuration file
|
||||
type Repo struct {
|
||||
Name string `toml:"name"`
|
||||
URL string `toml:"url"`
|
||||
}
|
||||
8
internal/types/repo.go
Normal file
8
internal/types/repo.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package types
|
||||
|
||||
// RepoConfig represents a LURE repo's lure-repo.toml file.
|
||||
type RepoConfig struct {
|
||||
Repo struct {
|
||||
MinVersion string `toml:"minVersion"`
|
||||
}
|
||||
}
|
||||
77
list.go
77
list.go
@@ -1,30 +1,87 @@
|
||||
/*
|
||||
* LURE - Linux User REpository
|
||||
* Copyright (C) 2022 Arsen Musayelyan
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
"go.arsenm.dev/lure/manager"
|
||||
)
|
||||
|
||||
func listCmd(c *cli.Context) error {
|
||||
info, err := distro.ParseOSRelease(c.Context)
|
||||
err := repos.Pull(c.Context, gdb, cfg.Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error parsing os-release").Err(err).Send()
|
||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||
}
|
||||
|
||||
pkgs, err := findPkg("*")
|
||||
if err != nil {
|
||||
log.Fatal("Error finding packages").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()}
|
||||
}
|
||||
|
||||
for _, script := range pkgs {
|
||||
vars, err := getBuildVars(c.Context, script, info)
|
||||
result, err := db.GetPkgs(gdb, where, args...)
|
||||
if err != nil {
|
||||
log.Fatal("Error getting build variables").Err(err).Send()
|
||||
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()
|
||||
}
|
||||
|
||||
fmt.Println(vars.Name, vars.Version)
|
||||
installed, err = mgr.ListInstalled(&manager.Opts{AsRoot: false})
|
||||
if err != nil {
|
||||
log.Fatal("Error listing installed packages").Err(err).Send()
|
||||
}
|
||||
}
|
||||
|
||||
for result.Next() {
|
||||
var pkg db.Package
|
||||
err := result.StructScan(&pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := pkg.Version
|
||||
if c.Bool("installed") {
|
||||
instVersion, ok := installed[pkg.Name]
|
||||
if !ok {
|
||||
return nil
|
||||
} 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
|
||||
|
||||
80
main.go
80
main.go
@@ -20,16 +20,26 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.arsenm.dev/logger"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/manager"
|
||||
)
|
||||
|
||||
var log = logger.NewPretty(os.Stderr)
|
||||
//go:generate scripts/gen-version.sh
|
||||
|
||||
func init() {
|
||||
log.Logger = logger.NewCLI(os.Stderr)
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
@@ -39,18 +49,27 @@ func main() {
|
||||
<-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",
|
||||
},
|
||||
},
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Install a new package",
|
||||
Aliases: []string{"in"},
|
||||
Action: installCmd,
|
||||
BashComplete: completionInstall,
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
@@ -65,11 +84,24 @@ func main() {
|
||||
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"},
|
||||
@@ -128,7 +160,30 @@ func main() {
|
||||
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)
|
||||
@@ -136,3 +191,26 @@ func main() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ func (a *APK) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
|
||||
var cmd *exec.Cmd
|
||||
if opts.AsRoot {
|
||||
cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
|
||||
cmd.Args = append(cmd.Args, opts.Args...)
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
} else {
|
||||
cmd = exec.Command(mgrCmd, args...)
|
||||
|
||||
@@ -145,6 +145,7 @@ func (a *APT) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
|
||||
var cmd *exec.Cmd
|
||||
if opts.AsRoot {
|
||||
cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
|
||||
cmd.Args = append(cmd.Args, opts.Args...)
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
} else {
|
||||
cmd = exec.Command(mgrCmd, args...)
|
||||
|
||||
@@ -60,7 +60,7 @@ func (d *DNF) Sync(opts *Opts) error {
|
||||
|
||||
func (d *DNF) Install(opts *Opts, pkgs ...string) error {
|
||||
opts = ensureOpts(opts)
|
||||
cmd := d.getCmd(opts, "dnf", "install")
|
||||
cmd := d.getCmd(opts, "dnf", "install", "--allowerasing")
|
||||
cmd.Args = append(cmd.Args, pkgs...)
|
||||
setCmdEnv(cmd)
|
||||
err := cmd.Run()
|
||||
@@ -137,6 +137,7 @@ func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
version = strings.TrimPrefix(version, "0:")
|
||||
out[name] = version
|
||||
}
|
||||
|
||||
@@ -152,6 +153,7 @@ func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
|
||||
var cmd *exec.Cmd
|
||||
if opts.AsRoot {
|
||||
cmd = exec.Command(getRootCmd(d.rootCmd), mgrCmd)
|
||||
cmd.Args = append(cmd.Args, opts.Args...)
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
} else {
|
||||
cmd = exec.Command(mgrCmd, args...)
|
||||
|
||||
@@ -23,9 +23,12 @@ import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var Args []string
|
||||
|
||||
type Opts struct {
|
||||
AsRoot bool
|
||||
NoConfirm bool
|
||||
Args []string
|
||||
}
|
||||
|
||||
var DefaultOpts = &Opts{
|
||||
@@ -114,7 +117,8 @@ func setCmdEnv(cmd *exec.Cmd) {
|
||||
|
||||
func ensureOpts(opts *Opts) *Opts {
|
||||
if opts == nil {
|
||||
return DefaultOpts
|
||||
opts = DefaultOpts
|
||||
}
|
||||
opts.Args = append(opts.Args, Args...)
|
||||
return opts
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func (p *Pacman) Sync(opts *Opts) error {
|
||||
|
||||
func (p *Pacman) Install(opts *Opts, pkgs ...string) error {
|
||||
opts = ensureOpts(opts)
|
||||
cmd := p.getCmd(opts, "pacman", "-S")
|
||||
cmd := p.getCmd(opts, "pacman", "-S", "--needed")
|
||||
cmd.Args = append(cmd.Args, pkgs...)
|
||||
setCmdEnv(cmd)
|
||||
err := cmd.Run()
|
||||
@@ -72,7 +72,7 @@ func (p *Pacman) Install(opts *Opts, pkgs ...string) error {
|
||||
|
||||
func (p *Pacman) InstallLocal(opts *Opts, pkgs ...string) error {
|
||||
opts = ensureOpts(opts)
|
||||
cmd := p.getCmd(opts, "pacman", "-U")
|
||||
cmd := p.getCmd(opts, "pacman", "-U", "--needed")
|
||||
cmd.Args = append(cmd.Args, pkgs...)
|
||||
setCmdEnv(cmd)
|
||||
err := cmd.Run()
|
||||
@@ -152,6 +152,7 @@ func (p *Pacman) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
|
||||
var cmd *exec.Cmd
|
||||
if opts.AsRoot {
|
||||
cmd = exec.Command(getRootCmd(p.rootCmd), mgrCmd)
|
||||
cmd.Args = append(cmd.Args, opts.Args...)
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
} else {
|
||||
cmd = exec.Command(mgrCmd, args...)
|
||||
|
||||
@@ -60,7 +60,7 @@ func (y *YUM) Sync(opts *Opts) error {
|
||||
|
||||
func (y *YUM) Install(opts *Opts, pkgs ...string) error {
|
||||
opts = ensureOpts(opts)
|
||||
cmd := y.getCmd(opts, "yum", "install")
|
||||
cmd := y.getCmd(opts, "yum", "install", "--allowerasing")
|
||||
cmd.Args = append(cmd.Args, pkgs...)
|
||||
setCmdEnv(cmd)
|
||||
err := cmd.Run()
|
||||
@@ -137,6 +137,7 @@ func (y *YUM) ListInstalled(opts *Opts) (map[string]string, error) {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
version = strings.TrimPrefix(version, "0:")
|
||||
out[name] = version
|
||||
}
|
||||
|
||||
@@ -152,6 +153,7 @@ func (y *YUM) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
|
||||
var cmd *exec.Cmd
|
||||
if opts.AsRoot {
|
||||
cmd = exec.Command(getRootCmd(y.rootCmd), mgrCmd)
|
||||
cmd.Args = append(cmd.Args, opts.Args...)
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
} else {
|
||||
cmd = exec.Command(mgrCmd, args...)
|
||||
|
||||
@@ -137,6 +137,7 @@ func (z *Zypper) ListInstalled(opts *Opts) (map[string]string, error) {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
version = strings.TrimPrefix(version, "0:")
|
||||
out[name] = version
|
||||
}
|
||||
|
||||
@@ -152,6 +153,7 @@ func (z *Zypper) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
|
||||
var cmd *exec.Cmd
|
||||
if opts.AsRoot {
|
||||
cmd = exec.Command(getRootCmd(z.rootCmd), mgrCmd)
|
||||
cmd.Args = append(cmd.Args, opts.Args...)
|
||||
cmd.Args = append(cmd.Args, args...)
|
||||
} else {
|
||||
cmd = exec.Command(mgrCmd, args...)
|
||||
|
||||
186
repo.go
186
repo.go
@@ -19,53 +19,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.arsenm.dev/lure/download"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/internal/config"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
"go.arsenm.dev/lure/internal/types"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type PkgNotFoundError struct {
|
||||
pkgName string
|
||||
}
|
||||
|
||||
func (p PkgNotFoundError) Error() string {
|
||||
return "package '" + p.pkgName + "' could not be found in any repository"
|
||||
}
|
||||
|
||||
func addrepoCmd(c *cli.Context) error {
|
||||
name := c.String("name")
|
||||
repoURL := c.String("url")
|
||||
|
||||
for _, repo := range config.Repos {
|
||||
for _, repo := range cfg.Repos {
|
||||
if repo.URL == repoURL {
|
||||
log.Fatal("Repo already exists").Str("name", repo.Name).Send()
|
||||
}
|
||||
}
|
||||
|
||||
config.Repos = append(config.Repos, Repo{
|
||||
cfg.Repos = append(cfg.Repos, types.Repo{
|
||||
Name: name,
|
||||
URL: repoURL,
|
||||
})
|
||||
|
||||
cfgFl, err := os.Create(cfgPath)
|
||||
cfgFl, err := os.Create(config.ConfigPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error opening config file").Err(err).Send()
|
||||
}
|
||||
|
||||
err = toml.NewEncoder(cfgFl).Encode(&config)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -74,7 +70,7 @@ func removerepoCmd(c *cli.Context) error {
|
||||
|
||||
found := false
|
||||
index := 0
|
||||
for i, repo := range config.Repos {
|
||||
for i, repo := range cfg.Repos {
|
||||
if repo.Name == name {
|
||||
index = i
|
||||
found = true
|
||||
@@ -84,167 +80,35 @@ func removerepoCmd(c *cli.Context) error {
|
||||
log.Fatal("Repo does not exist").Str("name", name).Send()
|
||||
}
|
||||
|
||||
config.Repos = slices.Delete(config.Repos, index, index+1)
|
||||
cfg.Repos = slices.Delete(cfg.Repos, index, index+1)
|
||||
|
||||
cfgFl, err := os.Create(cfgPath)
|
||||
cfgFl, err := os.Create(config.ConfigPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error opening config file").Err(err).Send()
|
||||
}
|
||||
|
||||
err = toml.NewEncoder(cfgFl).Encode(&config)
|
||||
err = toml.NewEncoder(cfgFl).Encode(&cfg)
|
||||
if err != nil {
|
||||
log.Fatal("Error encoding config").Err(err).Send()
|
||||
}
|
||||
|
||||
err = os.RemoveAll(filepath.Join(cacheDir, "repo", name))
|
||||
err = os.RemoveAll(filepath.Join(config.RepoDir, name))
|
||||
if err != nil {
|
||||
log.Fatal("Error removing repo directory").Err(err).Send()
|
||||
}
|
||||
|
||||
err = db.DeletePkgs(gdb, "repository = ?", name)
|
||||
if err != nil {
|
||||
log.Fatal("Error removing packages from database").Err(err).Send()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func refreshCmd(c *cli.Context) error {
|
||||
err := pullRepos(c.Context)
|
||||
err := repos.Pull(c.Context, gdb, cfg.Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repos").Err(err).Send()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findPkg(pkg string) ([]string, error) {
|
||||
baseRepoDir := filepath.Join(cacheDir, "repo")
|
||||
|
||||
var out []string
|
||||
for _, repo := range config.Repos {
|
||||
repoDir := filepath.Join(baseRepoDir, repo.Name)
|
||||
err := os.MkdirAll(repoDir, 0o755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
glob := filepath.Join(repoDir, pkg, "lure.sh")
|
||||
matches, err := filepath.Glob(glob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(matches) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, matches...)
|
||||
}
|
||||
|
||||
if len(out) == 0 {
|
||||
return nil, PkgNotFoundError{pkgName: pkg}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func pkgPrompt(options []string) ([]string, error) {
|
||||
names := make([]string, len(options))
|
||||
for i, option := range options {
|
||||
names[i] = filepath.Base(filepath.Dir(option))
|
||||
}
|
||||
|
||||
prompt := &survey.MultiSelect{
|
||||
Options: names,
|
||||
Message: "Choose which package(s) to install",
|
||||
}
|
||||
|
||||
var choices []int
|
||||
err := survey.AskOne(prompt, &choices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make([]string, len(choices))
|
||||
for i, choiceIndex := range choices {
|
||||
out[i] = options[choiceIndex]
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func findPkgs(pkgs []string) (scripts, notFound []string) {
|
||||
for _, pkg := range pkgs {
|
||||
found, err := findPkg(pkg)
|
||||
if _, ok := err.(PkgNotFoundError); ok {
|
||||
notFound = append(notFound, pkg)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(found) == 1 {
|
||||
scripts = append(scripts, found...)
|
||||
} else {
|
||||
choices, err := pkgPrompt(found)
|
||||
if err != nil {
|
||||
log.Fatal("Error prompting for package choices").Err(err).Send()
|
||||
}
|
||||
|
||||
scripts = append(scripts, choices...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func pullRepos(ctx context.Context) error {
|
||||
baseRepoDir := filepath.Join(cacheDir, "repo")
|
||||
|
||||
for _, repo := range config.Repos {
|
||||
repoURL, err := url.Parse(repo.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Pulling repository").Str("name", repo.Name).Send()
|
||||
repoDir := filepath.Join(baseRepoDir, repo.Name)
|
||||
|
||||
gitDir := filepath.Join(repoDir, ".git")
|
||||
if fi, err := os.Stat(gitDir); err == nil && fi.IsDir() {
|
||||
r, err := git.PlainOpen(repoDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := r.Worktree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.PullContext(ctx, &git.PullOptions{Progress: os.Stderr})
|
||||
if err == git.NoErrAlreadyUpToDate {
|
||||
log.Info("Repository up to date").Str("name", repo.Name).Send()
|
||||
continue
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = os.RemoveAll(repoDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(repoDir, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(repoURL.Scheme, "git+") {
|
||||
repoURL.Scheme = "git+" + repoURL.Scheme
|
||||
}
|
||||
|
||||
err = download.Get(ctx, download.GetOptions{
|
||||
SourceURL: repoURL.String(),
|
||||
Destination: repoDir,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
21
scripts/completion/bash
Normal file
21
scripts/completion/bash
Normal file
@@ -0,0 +1,21 @@
|
||||
#! /bin/bash
|
||||
|
||||
: ${PROG:=$(basename ${BASH_SOURCE})}
|
||||
|
||||
_cli_bash_autocomplete() {
|
||||
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
|
||||
local cur opts base
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
if [[ "$cur" == "-"* ]]; then
|
||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion )
|
||||
else
|
||||
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
||||
fi
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG
|
||||
unset PROG
|
||||
20
scripts/completion/zsh
Normal file
20
scripts/completion/zsh
Normal file
@@ -0,0 +1,20 @@
|
||||
#compdef lure
|
||||
|
||||
_cli_zsh_autocomplete() {
|
||||
local -a opts
|
||||
local cur
|
||||
cur=${words[-1]}
|
||||
if [[ "$cur" == "-"* ]]; then
|
||||
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
|
||||
else
|
||||
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}")
|
||||
fi
|
||||
|
||||
if [[ "${opts[1]}" != "" ]]; then
|
||||
_describe 'values' opts
|
||||
else
|
||||
_files
|
||||
fi
|
||||
}
|
||||
|
||||
compdef _cli_zsh_autocomplete lure
|
||||
3
scripts/gen-version.sh
Executable file
3
scripts/gen-version.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
git describe --tags > internal/config/version.txt
|
||||
82
scripts/install.sh
Normal file
82
scripts/install.sh
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
|
||||
info() {
|
||||
echo $'\x1b[32m[INFO]\x1b[0m' $@
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo $'\x1b[31m[WARN]\x1b[0m' $@
|
||||
}
|
||||
|
||||
error() {
|
||||
echo $'\x1b[31;1m[ERR]\x1b[0m' $@
|
||||
exit 1
|
||||
}
|
||||
|
||||
installPkg() {
|
||||
rootCmd=""
|
||||
if command -v doas &>/dev/null; then
|
||||
rootCmd="doas"
|
||||
elif command -v sudo &>/dev/null; then
|
||||
rootCmd="sudo"
|
||||
else
|
||||
warn "No privilege elevation command (e.g. sudo, doas) detected"
|
||||
fi
|
||||
|
||||
case $1 in
|
||||
pacman) $rootCmd pacman --noconfirm -U ${@:2} ;;
|
||||
apk) $rootCmd apk add --allow-untrusted ${@:2} ;;
|
||||
*) $rootCmd $1 install -y ${@:2} ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if ! command -v curl &>/dev/null; then
|
||||
error "This script requires the curl command. Please install it and run again."
|
||||
fi
|
||||
|
||||
pkgFormat=""
|
||||
pkgMgr=""
|
||||
if command -v pacman &>/dev/null; then
|
||||
info "Detected pacman"
|
||||
pkgFormat="pkg.tar.zst"
|
||||
pkgMgr="pacman"
|
||||
elif command -v apt &>/dev/null; then
|
||||
info "Detected apt"
|
||||
pkgFormat="deb"
|
||||
pkgMgr="apt"
|
||||
elif command -v dnf &>/dev/null; then
|
||||
info "Detected dnf"
|
||||
pkgFormat="rpm"
|
||||
pkgMgr="dnf"
|
||||
elif command -v yum &>/dev/null; then
|
||||
info "Detected yum"
|
||||
pkgFormat="rpm"
|
||||
pkgMgr="yum"
|
||||
elif command -v zypper &>/dev/null; then
|
||||
info "Detected zypper"
|
||||
pkgFormat="rpm"
|
||||
pkgMgr="zypper"
|
||||
elif command -v apk &>/dev/null; then
|
||||
info "Detected apk"
|
||||
pkgFormat="apk"
|
||||
pkgMgr="apk"
|
||||
else
|
||||
error "No supported package manager detected!"
|
||||
fi
|
||||
|
||||
latestVersion=$(curl -sI 'https://gitea.arsenm.dev/Arsen6331/lure/releases/latest' | grep -o 'location: .*' | rev | cut -d '/' -f1 | rev | tr -d '[:space:]')
|
||||
info "Found latest LURE version:" $latestVersion
|
||||
|
||||
fname="$(mktemp -u -p /tmp "lure.XXXXXXXXXX").${pkgFormat}"
|
||||
url="https://gitea.arsenm.dev/Arsen6331/lure/releases/download/${latestVersion}/linux-user-repository-${latestVersion#v}-linux-$(uname -m).${pkgFormat}"
|
||||
|
||||
info "Downloading LURE package"
|
||||
curl -L $url -o $fname
|
||||
|
||||
info "Installing LURE package"
|
||||
installPkg $pkgMgr $fname
|
||||
|
||||
info "Cleaning up"
|
||||
rm $fname
|
||||
|
||||
info "Done!"
|
||||
55
upgrade.go
55
upgrade.go
@@ -23,8 +23,14 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.arsenm.dev/logger/log"
|
||||
"go.arsenm.dev/lure/distro"
|
||||
"go.arsenm.dev/lure/internal/db"
|
||||
"go.arsenm.dev/lure/internal/repos"
|
||||
"go.arsenm.dev/lure/manager"
|
||||
"go.arsenm.dev/lure/vercmp"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func upgradeCmd(c *cli.Context) error {
|
||||
@@ -38,13 +44,18 @@ func upgradeCmd(c *cli.Context) error {
|
||||
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 repos").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, mgr)
|
||||
installPkgs(c.Context, updates, nil, mgr)
|
||||
} else {
|
||||
log.Info("There is nothing to do.").Send()
|
||||
}
|
||||
@@ -52,41 +63,47 @@ func upgradeCmd(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRelease) ([]string, error) {
|
||||
func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRelease) ([]db.Package, error) {
|
||||
installed, err := mgr.ListInstalled(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out []string
|
||||
for name, version := range installed {
|
||||
scripts, err := findPkg(name)
|
||||
pkgNames := maps.Keys(installed)
|
||||
found, _, err := repos.FindPkgs(gdb, pkgNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out []db.Package
|
||||
for pkgName, pkgs := range found {
|
||||
if slices.Contains(cfg.IgnorePkgUpdates, pkgName) {
|
||||
continue
|
||||
}
|
||||
|
||||
// since we're not using a glob, we can assume a single item
|
||||
script := scripts[0]
|
||||
|
||||
vars, err := getBuildVars(ctx, script, info)
|
||||
if err != nil {
|
||||
log.Fatal("Error getting build variables").Err(err).Send()
|
||||
if len(pkgs) > 1 {
|
||||
// Puts the element with the highest version first
|
||||
slices.SortFunc(pkgs, func(a, b db.Package) bool {
|
||||
return vercmp.Compare(a.Version, b.Version) == 1
|
||||
})
|
||||
}
|
||||
|
||||
repoVer := vars.Version
|
||||
if vars.Release != 0 && vars.Epoch == 0 {
|
||||
repoVer = fmt.Sprintf("%s-%d", vars.Version, vars.Release)
|
||||
} else if vars.Release != 0 && vars.Epoch != 0 {
|
||||
repoVer = fmt.Sprintf("%d:%s-%d", vars.Epoch, vars.Version, vars.Release)
|
||||
// First element is the package we want to install
|
||||
pkg := pkgs[0]
|
||||
|
||||
repoVer := pkg.Version
|
||||
if pkg.Release != 0 && pkg.Epoch == 0 {
|
||||
repoVer = fmt.Sprintf("%s-%d", pkg.Version, pkg.Release)
|
||||
} else if pkg.Release != 0 && pkg.Epoch != 0 {
|
||||
repoVer = fmt.Sprintf("%d:%s-%d", pkg.Epoch, pkg.Version, pkg.Release)
|
||||
}
|
||||
|
||||
c := vercmp(repoVer, version)
|
||||
c := vercmp.Compare(repoVer, installed[pkgName])
|
||||
if c == 0 || c == -1 {
|
||||
continue
|
||||
} else if c == 1 {
|
||||
out = append(out, name)
|
||||
out = append(out, pkg)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -16,20 +16,21 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
package vercmp
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// vercmp compares two version strings.
|
||||
// Compare compares two version strings.
|
||||
// It returns 1 if v1 is greater,
|
||||
// 0 if the versions are equal,
|
||||
// and -1 if v2 is greater
|
||||
func vercmp(v1, v2 string) int {
|
||||
func Compare(v1, v2 string) int {
|
||||
if v1 == v2 {
|
||||
return 0
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package main
|
||||
package vercmp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -67,13 +67,13 @@ func TestVerCmp(t *testing.T) {
|
||||
|
||||
for _, it := range table {
|
||||
t.Run(it.v1+"/"+it.v2, func(t *testing.T) {
|
||||
c := vercmp(it.v1, it.v2)
|
||||
c := Compare(it.v1, it.v2)
|
||||
if c != it.expected {
|
||||
t.Errorf("Expected %d, got %d", it.expected, c)
|
||||
}
|
||||
|
||||
// Ensure opposite comparison gives opposite value
|
||||
c = -vercmp(it.v2, it.v1)
|
||||
c = -Compare(it.v2, it.v1)
|
||||
if c != it.expected {
|
||||
t.Errorf("Expected %d, got %d (opposite)", it.expected, c)
|
||||
}
|
||||
Reference in New Issue
Block a user