Compare commits
	
		
			80 Commits
		
	
	
		
			v0.0.2
			...
			a106100312
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +1,3 @@
 | 
			
		||||
/lure
 | 
			
		||||
/dist/
 | 
			
		||||
/internal/config/version.txt
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
before:
 | 
			
		||||
  hooks:
 | 
			
		||||
    - go mod tidy
 | 
			
		||||
    - go generate
 | 
			
		||||
builds:
 | 
			
		||||
  - id: lure
 | 
			
		||||
    env:
 | 
			
		||||
@@ -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,23 @@ 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   
 | 
			
		||||
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'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										5
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
lure:
 | 
			
		||||
lure: version.txt
 | 
			
		||||
	go build
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
@@ -10,4 +10,7 @@ install: lure
 | 
			
		||||
uninstall:
 | 
			
		||||
	rm -f /usr/local/bin/lure
 | 
			
		||||
	
 | 
			
		||||
version.txt:
 | 
			
		||||
	go generate
 | 
			
		||||
 | 
			
		||||
.PHONY: install clean uninstall
 | 
			
		||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							@@ -1,6 +1,7 @@
 | 
			
		||||
# 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.
 | 
			
		||||
@@ -11,6 +12,18 @@ LURE is written in pure Go and has zero dependencies after it's built. The only
 | 
			
		||||
 | 
			
		||||
## 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 whatever code 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)
 | 
			
		||||
@@ -60,6 +73,5 @@ As mentioned before, LURE has zero dependencies after it's built. All functional
 | 
			
		||||
 | 
			
		||||
## Planned Features
 | 
			
		||||
 | 
			
		||||
- Automated install script
 | 
			
		||||
- Automated docker-based testing tool
 | 
			
		||||
- Web interface for repos
 | 
			
		||||
							
								
								
									
										159
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								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,12 @@ 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/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 +79,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 +89,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 +131,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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -130,6 +154,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
			
		||||
	runner, err := interp.New(
 | 
			
		||||
		interp.Env(expand.ListEnviron(env...)),
 | 
			
		||||
		interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
 | 
			
		||||
		interp.ExecHandler(helpers.ExecHandler),
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
@@ -155,14 +180,11 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 := 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 +192,7 @@ 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)
 | 
			
		||||
	baseDir := filepath.Join(config.PkgsDir, vars.Name)
 | 
			
		||||
	srcdir := filepath.Join(baseDir, "src")
 | 
			
		||||
	pkgdir := filepath.Join(baseDir, "pkg")
 | 
			
		||||
 | 
			
		||||
@@ -189,16 +211,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, flattenFoundPkgs(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(flattenFoundPkgs(found, "install"))
 | 
			
		||||
		for _, script := range scripts {
 | 
			
		||||
			pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
@@ -218,16 +262,43 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = setDirVars(ctx, runner, srcdir, pkgdir)
 | 
			
		||||
	repodir := filepath.Dir(script)
 | 
			
		||||
 | 
			
		||||
	err = setDirVars(ctx, runner, srcdir, pkgdir, repodir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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 +308,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 +318,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 +335,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 +359,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)
 | 
			
		||||
@@ -386,11 +453,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 := yesNoPrompt("Would you like to remove build dependencies?", false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -401,7 +465,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
 | 
			
		||||
@@ -416,14 +480,16 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
			
		||||
 | 
			
		||||
func genBuildEnv(info *distro.OSRelease) []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()),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
@@ -442,7 +508,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
 | 
			
		||||
@@ -461,8 +527,8 @@ func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
 | 
			
		||||
 | 
			
		||||
// setDirVars sets srcdir and pkgdir. It's a very hacky way of doing so,
 | 
			
		||||
// but setting the runner's Env and Vars fields doesn't seem to work.
 | 
			
		||||
func setDirVars(ctx context.Context, runner *interp.Runner, srcdir, pkgdir string) error {
 | 
			
		||||
	cmd := "srcdir='" + srcdir + "'\npkgdir='" + pkgdir + "'\n"
 | 
			
		||||
func setDirVars(ctx context.Context, runner *interp.Runner, srcdir, pkgdir, repodir string) error {
 | 
			
		||||
	cmd := "srcdir='" + srcdir + "'\npkgdir='" + pkgdir + "'\nrepodir='" + repodir + "'\n"
 | 
			
		||||
	fl, err := syntax.NewParser().Parse(strings.NewReader(cmd), "vars")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -547,20 +613,29 @@ func getBuildVars(ctx context.Context, script string, info *distro.OSRelease) (*
 | 
			
		||||
	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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								cli.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								cli.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/AlecAivazis/survey/v2"
 | 
			
		||||
	"go.arsenm.dev/lure/internal/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								db.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/genjidb/genji"
 | 
			
		||||
	"go.arsenm.dev/logger/log"
 | 
			
		||||
	"go.arsenm.dev/lure/internal/config"
 | 
			
		||||
	"go.arsenm.dev/lure/internal/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var gdb *genji.DB
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	var err error
 | 
			
		||||
	gdb, err = genji.Open(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
 | 
			
		||||
@@ -86,7 +86,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,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,3 +3,4 @@
 | 
			
		||||
- [Build Scripts](build-scripts.md)
 | 
			
		||||
- [Usage](usage.md)
 | 
			
		||||
- [Configuration](configuration.md)
 | 
			
		||||
- [Adding Packages to LURE's repo](adding-packages.md)
 | 
			
		||||
							
								
								
									
										23
									
								
								docs/adding-packages.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								docs/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"
 | 
			
		||||
```
 | 
			
		||||
@@ -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,169 +106,185 @@ 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()
 | 
			
		||||
	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 {
 | 
			
		||||
			dstDir = filepath.Join(opts.Destination, name)
 | 
			
		||||
		}
 | 
			
		||||
	dstDir := opts.Destination
 | 
			
		||||
	if opts.EncloseGit {
 | 
			
		||||
		dstDir = filepath.Join(opts.Destination, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		depth := 0
 | 
			
		||||
		if depthStr != "" {
 | 
			
		||||
			depth, err = strconv.Atoi(depthStr)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cloneOpts := &git.CloneOptions{
 | 
			
		||||
			URL:      src.String(),
 | 
			
		||||
			Progress: os.Stderr,
 | 
			
		||||
			Depth:    depth,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		repo, err := git.PlainCloneContext(ctx, dstDir, false, cloneOpts)
 | 
			
		||||
	depth := 0
 | 
			
		||||
	if depthStr != "" {
 | 
			
		||||
		depth, err = strconv.Atoi(depthStr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		w, err := repo.Worktree()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	cloneOpts := &git.CloneOptions{
 | 
			
		||||
		URL:      src.String(),
 | 
			
		||||
		Progress: os.Stderr,
 | 
			
		||||
		Depth:    depth,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		checkoutOpts := &git.CheckoutOptions{}
 | 
			
		||||
		if refName != "" {
 | 
			
		||||
			checkoutOpts.Branch = refName
 | 
			
		||||
		} else if commit != "" {
 | 
			
		||||
			checkoutOpts.Hash = plumbing.NewHash(commit)
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	repo, err := git.PlainCloneContext(ctx, dstDir, false, cloneOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		return w.Checkout(checkoutOpts)
 | 
			
		||||
	w, err := repo.Worktree()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	checkoutOpts := &git.CheckoutOptions{}
 | 
			
		||||
	if refName != "" {
 | 
			
		||||
		checkoutOpts.Branch = refName
 | 
			
		||||
	} else if commit != "" {
 | 
			
		||||
		checkoutOpts.Hash = plumbing.NewHash(commit)
 | 
			
		||||
	} else {
 | 
			
		||||
		name := query.Get("~name")
 | 
			
		||||
		query.Del("~name")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		archive := query.Get("~archive")
 | 
			
		||||
		query.Del("~archive")
 | 
			
		||||
	return w.Checkout(checkoutOpts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		src.RawQuery = query.Encode()
 | 
			
		||||
func getFile(ctx context.Context, src *url.URL, query url.Values, opts GetOptions) error {
 | 
			
		||||
	name := query.Get("~name")
 | 
			
		||||
	query.Del("~name")
 | 
			
		||||
 | 
			
		||||
		if name == "" {
 | 
			
		||||
			name = path.Base(src.Path)
 | 
			
		||||
		}
 | 
			
		||||
	archive := query.Get("~archive")
 | 
			
		||||
	query.Del("~archive")
 | 
			
		||||
 | 
			
		||||
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, src.String(), nil)
 | 
			
		||||
	src.RawQuery = query.Encode()
 | 
			
		||||
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		name = path.Base(src.Path)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, src.String(), nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res, err := http.DefaultClient.Do(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hash := sha256.New()
 | 
			
		||||
 | 
			
		||||
	format, input, err := archiver.Identify(name, res.Body)
 | 
			
		||||
	if err == archiver.ErrNoMatch || archive == "false" {
 | 
			
		||||
		fl, err := os.Create(filepath.Join(opts.Destination, name))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		res, err := http.DefaultClient.Do(req)
 | 
			
		||||
		w := io.MultiWriter(hash, fl)
 | 
			
		||||
 | 
			
		||||
		_, err = io.Copy(w, input)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		hash := sha256.New()
 | 
			
		||||
		res.Body.Close()
 | 
			
		||||
		fl.Close()
 | 
			
		||||
 | 
			
		||||
		format, input, err := archiver.Identify(name, res.Body)
 | 
			
		||||
		if err == archiver.ErrNoMatch || archive == "false" {
 | 
			
		||||
			fl, err := os.Create(filepath.Join(opts.Destination, name))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
		if opts.SHA256Sum != nil {
 | 
			
		||||
			sum := hash.Sum(nil)
 | 
			
		||||
			if !bytes.Equal(opts.SHA256Sum, sum) {
 | 
			
		||||
				return ErrChecksumMismatch
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			w := io.MultiWriter(hash, fl)
 | 
			
		||||
 | 
			
		||||
			_, err = io.Copy(w, input)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			res.Body.Close()
 | 
			
		||||
			fl.Close()
 | 
			
		||||
 | 
			
		||||
			if opts.SHA256Sum != nil {
 | 
			
		||||
				sum := hash.Sum(nil)
 | 
			
		||||
				if !bytes.Equal(opts.SHA256Sum, sum) {
 | 
			
		||||
					return ErrChecksumMismatch
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
		}
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else {
 | 
			
		||||
		err = extractFile(ctx, input, hash, format, name, opts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		} else {
 | 
			
		||||
			r := io.TeeReader(input, hash)
 | 
			
		||||
			fname := format.Name()
 | 
			
		||||
 | 
			
		||||
			switch format := format.(type) {
 | 
			
		||||
			case archiver.Extractor:
 | 
			
		||||
				err = format.Extract(ctx, r, nil, func(ctx context.Context, f archiver.File) error {
 | 
			
		||||
					fr, err := f.Open()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					defer fr.Close()
 | 
			
		||||
 | 
			
		||||
					path := filepath.Join(opts.Destination, f.NameInArchive)
 | 
			
		||||
 | 
			
		||||
					err = os.MkdirAll(filepath.Dir(path), 0o755)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if f.IsDir() {
 | 
			
		||||
						err = os.Mkdir(path, 0o755)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							return err
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						outFl, err := os.Create(path)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							return err
 | 
			
		||||
						}
 | 
			
		||||
						defer outFl.Close()
 | 
			
		||||
 | 
			
		||||
						_, err = io.Copy(outFl, fr)
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					return nil
 | 
			
		||||
				})
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			case archiver.Decompressor:
 | 
			
		||||
				rc, err := format.OpenReader(r)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				defer rc.Close()
 | 
			
		||||
 | 
			
		||||
				path := filepath.Join(opts.Destination, name)
 | 
			
		||||
				path = strings.TrimSuffix(path, fname)
 | 
			
		||||
 | 
			
		||||
				outFl, err := os.Create(path)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				_, err = io.Copy(outFl, rc)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if opts.SHA256Sum != nil {
 | 
			
		||||
				sum := hash.Sum(nil)
 | 
			
		||||
				if !bytes.Equal(opts.SHA256Sum, sum) {
 | 
			
		||||
					return ErrChecksumMismatch
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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()
 | 
			
		||||
 | 
			
		||||
	switch format := format.(type) {
 | 
			
		||||
	case archiver.Extractor:
 | 
			
		||||
		err = format.Extract(ctx, r, nil, func(ctx context.Context, f archiver.File) error {
 | 
			
		||||
			fr, err := f.Open()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			defer fr.Close()
 | 
			
		||||
			fi, err := f.Stat()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			fm := fi.Mode()
 | 
			
		||||
 | 
			
		||||
			path := filepath.Join(opts.Destination, f.NameInArchive)
 | 
			
		||||
 | 
			
		||||
			err = os.MkdirAll(filepath.Dir(path), 0o755)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if f.IsDir() {
 | 
			
		||||
				err = os.Mkdir(path, 0o755)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				outFl, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fm.Perm())
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				defer outFl.Close()
 | 
			
		||||
 | 
			
		||||
				_, err = io.Copy(outFl, fr)
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	case archiver.Decompressor:
 | 
			
		||||
		rc, err := format.OpenReader(r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		defer rc.Close()
 | 
			
		||||
 | 
			
		||||
		path := filepath.Join(opts.Destination, name)
 | 
			
		||||
		path = strings.TrimSuffix(path, fname)
 | 
			
		||||
 | 
			
		||||
		outFl, err := os.Create(path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		_, err = io.Copy(outFl, rc)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.SHA256Sum != nil {
 | 
			
		||||
		sum := hash.Sum(nil)
 | 
			
		||||
		if !bytes.Equal(opts.SHA256Sum, sum) {
 | 
			
		||||
			return ErrChecksumMismatch
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								go.mod
									
									
									
									
									
								
							@@ -2,44 +2,52 @@ 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/genjidb/genji v0.15.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/mholt/archiver/v4 v4.0.0-alpha.7
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.5.0
 | 
			
		||||
	github.com/pelletier/go-toml/v2 v2.0.5
 | 
			
		||||
	github.com/urfave/cli/v2 v2.16.3
 | 
			
		||||
	go.arsenm.dev/logger v0.0.0-20220630204155-5ba23e583f0a
 | 
			
		||||
	go.arsenm.dev/logger v0.0.0-20221007032343-cbffce4f4334
 | 
			
		||||
	golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b
 | 
			
		||||
	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
 | 
			
		||||
	golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1
 | 
			
		||||
	mvdan.cc/sh/v3 v3.5.1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/AlekSi/pointer v1.2.0 // indirect
 | 
			
		||||
	github.com/DataDog/zstd v1.4.5 // indirect
 | 
			
		||||
	github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
 | 
			
		||||
	github.com/Masterminds/goutils v1.1.1 // indirect
 | 
			
		||||
	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/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
 | 
			
		||||
	github.com/buger/jsonparser v1.1.1 // indirect
 | 
			
		||||
	github.com/cavaliergopher/cpio v1.0.1 // indirect
 | 
			
		||||
	github.com/cespare/xxhash/v2 v2.1.1 // indirect
 | 
			
		||||
	github.com/cockroachdb/errors v1.9.0 // indirect
 | 
			
		||||
	github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
 | 
			
		||||
	github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e // indirect
 | 
			
		||||
	github.com/cockroachdb/redact v1.1.3 // indirect
 | 
			
		||||
	github.com/cpuguy83/go-md2man/v2 v2.0.2 // 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/getsentry/sentry-go v0.12.0 // 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/gogo/protobuf v1.3.2 // 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/goreleaser/chglog v0.2.2 // indirect
 | 
			
		||||
@@ -49,27 +57,31 @@ 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/kr/pretty v0.3.0 // indirect
 | 
			
		||||
	github.com/kr/text v0.2.0 // indirect
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.11 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty 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/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/rogpeppe/go-internal v1.8.1 // 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/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/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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										421
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										421
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,10 +1,22 @@
 | 
			
		||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
			
		||||
github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
 | 
			
		||||
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/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
			
		||||
github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
 | 
			
		||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
 | 
			
		||||
github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
 | 
			
		||||
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
 | 
			
		||||
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
 | 
			
		||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
 | 
			
		||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
 | 
			
		||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
 | 
			
		||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
 | 
			
		||||
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
 | 
			
		||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
 | 
			
		||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
 | 
			
		||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
 | 
			
		||||
@@ -16,8 +28,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=
 | 
			
		||||
@@ -25,21 +37,56 @@ github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c h1:bNpaLLv2Y4
 | 
			
		||||
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
 | 
			
		||||
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko=
 | 
			
		||||
github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs=
 | 
			
		||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
 | 
			
		||||
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/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
 | 
			
		||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
 | 
			
		||||
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/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
			
		||||
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/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
 | 
			
		||||
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/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
 | 
			
		||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
 | 
			
		||||
github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11 h1:IRrDwVlWQr6kS1U8/EtyA1+EHcc4yl8pndcqXWrEamg=
 | 
			
		||||
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
			
		||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 | 
			
		||||
github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
 | 
			
		||||
github.com/cockroachdb/datadriven v1.0.1-0.20211007161720-b558070c3be0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
 | 
			
		||||
github.com/cockroachdb/datadriven v1.0.1-0.20220214170620-9913f5bc19b7/go.mod h1:hi0MtSY3AYDQNDi83kDkMH5/yqM/CsIrsOITkSoH7KI=
 | 
			
		||||
github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
 | 
			
		||||
github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac=
 | 
			
		||||
github.com/cockroachdb/errors v1.8.8/go.mod h1:z6VnEL3hZ/2ONZEvG7S5Ym0bU2AqPcEKnIiA1wbsSu0=
 | 
			
		||||
github.com/cockroachdb/errors v1.9.0 h1:B48dYem5SlAY7iU8AKsgedb4gH6mo+bDkbtLIvM/a88=
 | 
			
		||||
github.com/cockroachdb/errors v1.9.0/go.mod h1:vaNcEYYqbIqB5JhKBhFV9CneUqeuEbB2OYJBK4GBNYQ=
 | 
			
		||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
 | 
			
		||||
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74=
 | 
			
		||||
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
 | 
			
		||||
github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e h1:MGFpdiedteFe4rm8UY/0FixU8RVAh4wZYUCLQys9GjI=
 | 
			
		||||
github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e/go.mod h1:pr479tNxFRmcfDyklTqoRMDDVmRlEbL+d7a7rhKnrI4=
 | 
			
		||||
github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
 | 
			
		||||
github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
 | 
			
		||||
github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
 | 
			
		||||
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=
 | 
			
		||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
 | 
			
		||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 | 
			
		||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 | 
			
		||||
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,16 +95,44 @@ 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/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
			
		||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
 | 
			
		||||
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/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 | 
			
		||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
 | 
			
		||||
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 | 
			
		||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 | 
			
		||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 | 
			
		||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
 | 
			
		||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 | 
			
		||||
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
 | 
			
		||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 | 
			
		||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
 | 
			
		||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
 | 
			
		||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 | 
			
		||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
 | 
			
		||||
github.com/genjidb/genji v0.15.1 h1:5lhRF15enjqQ0hSv7nuDPhTr8SoWv/vx19V2QDnwaPQ=
 | 
			
		||||
github.com/genjidb/genji v0.15.1/go.mod h1:fo1EzSGfPuSqz6s9VoYfX/dcZHMnKP7KONH+fJeSlEc=
 | 
			
		||||
github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk=
 | 
			
		||||
github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
 | 
			
		||||
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
 | 
			
		||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
 | 
			
		||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
 | 
			
		||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 | 
			
		||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
 | 
			
		||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
 | 
			
		||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
 | 
			
		||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 | 
			
		||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
 | 
			
		||||
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
 | 
			
		||||
@@ -67,46 +142,126 @@ 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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 | 
			
		||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 | 
			
		||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
 | 
			
		||||
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/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
 | 
			
		||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
 | 
			
		||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
 | 
			
		||||
github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 | 
			
		||||
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
 | 
			
		||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 | 
			
		||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 | 
			
		||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 | 
			
		||||
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
 | 
			
		||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 | 
			
		||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
			
		||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 | 
			
		||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 | 
			
		||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
 | 
			
		||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 | 
			
		||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 | 
			
		||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 | 
			
		||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 | 
			
		||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
			
		||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 | 
			
		||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 | 
			
		||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 | 
			
		||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 | 
			
		||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
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.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
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/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 | 
			
		||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
			
		||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
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/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
			
		||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
			
		||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
 | 
			
		||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 | 
			
		||||
github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
 | 
			
		||||
github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
 | 
			
		||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 | 
			
		||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
 | 
			
		||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
 | 
			
		||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
 | 
			
		||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
			
		||||
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
 | 
			
		||||
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
 | 
			
		||||
github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
 | 
			
		||||
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
 | 
			
		||||
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
 | 
			
		||||
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
 | 
			
		||||
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/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
			
		||||
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
 | 
			
		||||
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
 | 
			
		||||
github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
 | 
			
		||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
 | 
			
		||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
 | 
			
		||||
github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
 | 
			
		||||
github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
 | 
			
		||||
github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
 | 
			
		||||
github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
 | 
			
		||||
github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
 | 
			
		||||
github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
 | 
			
		||||
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
 | 
			
		||||
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
 | 
			
		||||
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
 | 
			
		||||
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=
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
			
		||||
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/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 | 
			
		||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
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.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
			
		||||
github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
			
		||||
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
			
		||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 | 
			
		||||
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/cpuid v1.2.1/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=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
@@ -118,55 +273,111 @@ 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/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
 | 
			
		||||
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
 | 
			
		||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
 | 
			
		||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
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.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 | 
			
		||||
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.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
			
		||||
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/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
 | 
			
		||||
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
 | 
			
		||||
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
 | 
			
		||||
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
 | 
			
		||||
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=
 | 
			
		||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7/go.mod h1:Fs8qUkO74HHaidabihzYephJH8qmGD/nCP6tE5xC9BM=
 | 
			
		||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
 | 
			
		||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 | 
			
		||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 | 
			
		||||
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
 | 
			
		||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
 | 
			
		||||
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
 | 
			
		||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
 | 
			
		||||
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
 | 
			
		||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
 | 
			
		||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 | 
			
		||||
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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 | 
			
		||||
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
 | 
			
		||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 | 
			
		||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 | 
			
		||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
			
		||||
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/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
 | 
			
		||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
 | 
			
		||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 | 
			
		||||
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
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/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
 | 
			
		||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
			
		||||
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/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 | 
			
		||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
 | 
			
		||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
 | 
			
		||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
			
		||||
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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 | 
			
		||||
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 v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
			
		||||
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
 | 
			
		||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 | 
			
		||||
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
 | 
			
		||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
			
		||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
			
		||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 | 
			
		||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
			
		||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
			
		||||
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=
 | 
			
		||||
@@ -175,63 +386,218 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
 | 
			
		||||
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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 | 
			
		||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 | 
			
		||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
			
		||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 | 
			
		||||
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=
 | 
			
		||||
github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk=
 | 
			
		||||
github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
 | 
			
		||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
 | 
			
		||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 | 
			
		||||
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
 | 
			
		||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
 | 
			
		||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 | 
			
		||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
 | 
			
		||||
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/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
 | 
			
		||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 | 
			
		||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 | 
			
		||||
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/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
			
		||||
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=
 | 
			
		||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
 | 
			
		||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
 | 
			
		||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
 | 
			
		||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
 | 
			
		||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
			
		||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
			
		||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 | 
			
		||||
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-20221007032343-cbffce4f4334 h1:S98LJOBmj1pAKSw94spJk6+n8ERlBNTxi4lt5B67nQo=
 | 
			
		||||
go.arsenm.dev/logger v0.0.0-20221007032343-cbffce4f4334/go.mod h1:RV2qydKDdoyaRkhAq8JEGvojR8eJ6bjq5WnSIlH7gYw=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
			
		||||
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
			
		||||
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-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
 | 
			
		||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
 | 
			
		||||
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/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 | 
			
		||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
			
		||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 | 
			
		||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 | 
			
		||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 | 
			
		||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 | 
			
		||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
			
		||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
			
		||||
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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 | 
			
		||||
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
			
		||||
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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
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-20210909193231-528a39cd75f3/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-20211007075335-d3039528d8ac/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 h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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=
 | 
			
		||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.6/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/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
			
		||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
			
		||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
			
		||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
			
		||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
 | 
			
		||||
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
 | 
			
		||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
 | 
			
		||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 | 
			
		||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
 | 
			
		||||
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 | 
			
		||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 | 
			
		||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 | 
			
		||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
 | 
			
		||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 | 
			
		||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 | 
			
		||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 | 
			
		||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 | 
			
		||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 | 
			
		||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
 | 
			
		||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 | 
			
		||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 | 
			
		||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 | 
			
		||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 | 
			
		||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 | 
			
		||||
google.golang.org/protobuf v1.26.0/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=
 | 
			
		||||
@@ -239,14 +605,25 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
			
		||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
			
		||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
 | 
			
		||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
 | 
			
		||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
			
		||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
			
		||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 | 
			
		||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
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=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
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=
 | 
			
		||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										256
									
								
								helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								helpers.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,256 @@
 | 
			
		||||
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,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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/completion"
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								info.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								info.go
									
									
									
									
									
								
							@@ -19,10 +19,13 @@
 | 
			
		||||
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/repos"
 | 
			
		||||
	"gopkg.in/yaml.v3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -32,27 +35,28 @@ func infoCmd(c *cli.Context) error {
 | 
			
		||||
		log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	found, err := findPkg(args.First())
 | 
			
		||||
	found, _, err := repos.FindPkgs(gdb, args.Slice())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal("Error finding package").Err(err).Send()
 | 
			
		||||
		log.Fatal("Error finding packages").Err(err).Send()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 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 len(found) == 0 {
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = yaml.NewEncoder(os.Stdout).Encode(vars)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal("Error encoding script variables").Err(err).Send()
 | 
			
		||||
	pkgs := flattenFoundPkgs(found, "show")
 | 
			
		||||
 | 
			
		||||
	for _, pkg := range pkgs {
 | 
			
		||||
		err = yaml.NewEncoder(os.Stdout).Encode(pkg)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal("Error encoding script variables").Err(err).Send()
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println("---")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								install.go
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								install.go
									
									
									
									
									
								
							@@ -20,8 +20,14 @@ package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"go.arsenm.dev/logger/log"
 | 
			
		||||
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
	"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 +42,63 @@ 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, flattenFoundPkgs(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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// flattenFoundPkgs attempts to flatten the map of slices of packages into a single slice
 | 
			
		||||
// of packages by prompting the users if multiple packages match.
 | 
			
		||||
func flattenFoundPkgs(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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								internal/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								internal/config/config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/pelletier/go-toml/v2"
 | 
			
		||||
	"go.arsenm.dev/lure/internal/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var defaultConfig = types.Config{
 | 
			
		||||
	RootCmd: "sudo",
 | 
			
		||||
	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()
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								internal/db/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								internal/db/db.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
package db
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/genjidb/genji"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Package is a LURE package's database representation
 | 
			
		||||
type Package 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"`
 | 
			
		||||
	Replaces      []string `sh:"replaces"`
 | 
			
		||||
	Depends       map[string][]string
 | 
			
		||||
	BuildDepends  map[string][]string
 | 
			
		||||
	Repository    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init initializes the database
 | 
			
		||||
func Init(db *genji.DB) error {
 | 
			
		||||
	return 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 ARRAY,
 | 
			
		||||
			licenses      ARRAY,
 | 
			
		||||
			provides      ARRAY,
 | 
			
		||||
			conflicts     ARRAY,
 | 
			
		||||
			replaces      ARRAY,
 | 
			
		||||
			depends       (...),
 | 
			
		||||
			builddepends  (...),
 | 
			
		||||
			UNIQUE(name, repository)
 | 
			
		||||
		);
 | 
			
		||||
	`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InsertPackage adds a package to the database
 | 
			
		||||
func InsertPackage(db *genji.DB, pkg Package) error {
 | 
			
		||||
	return db.Exec("INSERT INTO pkgs VALUES ? ON CONFLICT DO REPLACE;", pkg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPkgs returns a result containing packages that match the where conditions
 | 
			
		||||
func GetPkgs(db *genji.DB, where string, args ...any) (*genji.Result, error) {
 | 
			
		||||
	stream, err := db.Query("SELECT * FROM pkgs WHERE "+where, args...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return stream, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeletePkgs deletes all packages matching the where conditions
 | 
			
		||||
func DeletePkgs(db *genji.DB, where string, args ...any) error {
 | 
			
		||||
	return db.Exec("DELETE * FROM pkgs WHERE "+where, args...)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								internal/repos/find.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								internal/repos/find.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
package repos
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/genjidb/genji"
 | 
			
		||||
	"github.com/genjidb/genji/document"
 | 
			
		||||
	"github.com/genjidb/genji/types"
 | 
			
		||||
	"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 *genji.DB, pkgs []string) (map[string][]db.Package, []string, error) {
 | 
			
		||||
	found := map[string][]db.Package{}
 | 
			
		||||
	notFound := []string(nil)
 | 
			
		||||
 | 
			
		||||
	for _, pkgName := range pkgs {
 | 
			
		||||
		result, err := db.GetPkgs(gdb, "name LIKE ?", pkgName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		added := 0
 | 
			
		||||
		err = result.Iterate(func(d types.Document) error {
 | 
			
		||||
			var pkg db.Package
 | 
			
		||||
			err = document.StructScan(d, &pkg)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			added++
 | 
			
		||||
			found[pkgName] = append(found[pkgName], pkg)
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
		result.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if added == 0 {
 | 
			
		||||
			result, err := db.GetPkgs(gdb, "? IN provides", pkgName)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			err = result.Iterate(func(d types.Document) error {
 | 
			
		||||
				var pkg db.Package
 | 
			
		||||
				err = document.StructScan(d, &pkg)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				added++
 | 
			
		||||
				found[pkgName] = append(found[pkgName], pkg)
 | 
			
		||||
				return nil
 | 
			
		||||
			})
 | 
			
		||||
			result.Close()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if added == 0 {
 | 
			
		||||
			notFound = append(notFound, pkgName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return found, notFound, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										359
									
								
								internal/repos/pull.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								internal/repos/pull.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,359 @@
 | 
			
		||||
package repos
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/genjidb/genji"
 | 
			
		||||
	"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/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/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 *genji.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, 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, old, new *plumbing.Reference, gdb *genji.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 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(),
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parser := syntax.NewParser()
 | 
			
		||||
	runner, err := interp.New(
 | 
			
		||||
		interp.StatHandler(shutils.NopStat),
 | 
			
		||||
		interp.ExecHandler(shutils.NopExec),
 | 
			
		||||
		interp.OpenHandler(shutils.NopOpen),
 | 
			
		||||
		interp.ReadDirHandler(shutils.NopReadDir),
 | 
			
		||||
		interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, action := range actions {
 | 
			
		||||
		switch action.Type {
 | 
			
		||||
		case actionDelete:
 | 
			
		||||
			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:
 | 
			
		||||
			scriptFl, err := newCommit.File(action.File)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			r, err := scriptFl.Reader()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pkg := db.Package{
 | 
			
		||||
				Depends:      map[string][]string{},
 | 
			
		||||
				BuildDepends: 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *genji.DB) error {
 | 
			
		||||
	glob := filepath.Join(repoDir, "/*/lure.sh")
 | 
			
		||||
	matches, err := filepath.Glob(glob)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parser := syntax.NewParser()
 | 
			
		||||
	runner, err := interp.New(
 | 
			
		||||
		interp.StatHandler(shutils.NopStat),
 | 
			
		||||
		interp.ExecHandler(shutils.NopExec),
 | 
			
		||||
		interp.OpenHandler(shutils.NopOpen),
 | 
			
		||||
		interp.ReadDirHandler(shutils.NopReadDir),
 | 
			
		||||
		interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, match := range matches {
 | 
			
		||||
		scriptFl, err := os.Open(match)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pkg := db.Package{
 | 
			
		||||
			Depends:      map[string][]string{},
 | 
			
		||||
			BuildDepends: 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[override] = val.List
 | 
			
		||||
		} else if strings.HasPrefix(name, "build_deps") {
 | 
			
		||||
			override := strings.TrimPrefix(name, "build_deps")
 | 
			
		||||
			override = strings.TrimPrefix(override, "_")
 | 
			
		||||
 | 
			
		||||
			pkg.BuildDepends[override] = val.List
 | 
			
		||||
		} else {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -142,7 +142,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,10 +152,11 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,21 +20,34 @@ 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"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
func (ef ExecFuncs) ExecHandler(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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								internal/types/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								internal/types/config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
// Config represents the LURE configuration file
 | 
			
		||||
type Config struct {
 | 
			
		||||
	RootCmd string `toml:"rootCmd"`
 | 
			
		||||
	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"`
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								list.go
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								list.go
									
									
									
									
									
								
							@@ -1,30 +1,82 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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/genjidb/genji/document"
 | 
			
		||||
	"github.com/genjidb/genji/types"
 | 
			
		||||
	"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("*")
 | 
			
		||||
	result, err := db.GetPkgs(gdb, "true")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal("Error finding packages").Err(err).Send()
 | 
			
		||||
		log.Fatal("Error getting packages").Err(err).Send()
 | 
			
		||||
	}
 | 
			
		||||
	defer result.Close()
 | 
			
		||||
 | 
			
		||||
	for _, script := range pkgs {
 | 
			
		||||
		vars, err := getBuildVars(c.Context, script, info)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal("Error getting build variables").Err(err).Send()
 | 
			
		||||
	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()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = result.Iterate(func(d types.Document) error {
 | 
			
		||||
		var pkg db.Package
 | 
			
		||||
		err := document.StructScan(d, &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)
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal("Error iterating over packages").Err(err).Send()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								main.go
									
									
									
									
									
								
							@@ -27,9 +27,15 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
	"go.arsenm.dev/logger"
 | 
			
		||||
	"go.arsenm.dev/logger/log"
 | 
			
		||||
	"go.arsenm.dev/lure/internal/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var log = logger.NewPretty(os.Stderr)
 | 
			
		||||
//go:generate scripts/gen-version.sh
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	log.Logger = logger.NewPretty(os.Stderr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
@@ -39,6 +45,7 @@ func main() {
 | 
			
		||||
		<-ctx.Done()
 | 
			
		||||
		// Exit the program after a maximum of 200ms
 | 
			
		||||
		time.Sleep(200 * time.Millisecond)
 | 
			
		||||
		gdb.Close()
 | 
			
		||||
		os.Exit(0)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
@@ -70,6 +77,12 @@ func main() {
 | 
			
		||||
				Action: infoCmd,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Flags: []cli.Flag{
 | 
			
		||||
					&cli.BoolFlag{
 | 
			
		||||
						Name:    "installed",
 | 
			
		||||
						Aliases: []string{"I"},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				Name:    "list",
 | 
			
		||||
				Usage:   "List LURE repo packages",
 | 
			
		||||
				Aliases: []string{"ls"},
 | 
			
		||||
@@ -128,6 +141,14 @@ func main() {
 | 
			
		||||
				Aliases: []string{"ref"},
 | 
			
		||||
				Action:  refreshCmd,
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				Name:   "version",
 | 
			
		||||
				Usage:  "Display the current LURE version and exit",
 | 
			
		||||
				Action: displayVersion,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		After: func(ctx *cli.Context) error {
 | 
			
		||||
			return gdb.Close()
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -136,3 +157,8 @@ func main() {
 | 
			
		||||
		log.Error("Error while running app").Err(err).Send()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func displayVersion(c *cli.Context) error {
 | 
			
		||||
	print(config.Version)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 -U ${@:2} ;;
 | 
			
		||||
  apk) $rootCmd apk add --allow-untrusted ${@:2} ;;
 | 
			
		||||
  *) $rootCmd $1 install ${@: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!"
 | 
			
		||||
							
								
								
									
										57
									
								
								upgrade.go
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								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,43 @@ 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)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
	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 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
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// since we're not using a glob, we can assume a single item
 | 
			
		||||
		script := scripts[0]
 | 
			
		||||
		// First element is the package we want to install
 | 
			
		||||
		pkg := pkgs[0]
 | 
			
		||||
 | 
			
		||||
		vars, err := getBuildVars(ctx, script, info)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal("Error getting build variables").Err(err).Send()
 | 
			
		||||
		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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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