87 Commits

Author SHA1 Message Date
Elara6331 5999c1c8e6 Disable gpg checks for zypper in install script 2023-12-25 02:27:11 +00:00
Elara6331 5dc31f43aa Add build depends to search package 2023-12-22 19:35:36 -08:00
Elara6331 5569841ee2 Add FUNDING.yml 2023-12-15 16:11:39 -08:00
Elara6331 2b1b1deace Execute functions in a fakeroot environment
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 20:09:07 -07:00
Elara6331 edad7b03b2 Move shell handlers to internal/shutils/handlers
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 19:47:07 -07:00
Elara6331 ef923016e4 Add SWH badge
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 18:16:10 +00:00
Elara6331 fd145e4751 Use the pypi API to fetch information about pip modules
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-11 20:45:11 -07:00
Elara6331 4218912123 Fix issue where the git downloader re-downloads the source after updating it
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-11 14:16:36 -07:00
Elara6331 c98741b8f2 Clarify IMPORTANT note in README
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 15:50:30 -07:00
Elara6331 26654ae26e Remove extra newlines in pip template
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 14:04:55 -07:00
Elara6331 484c8ba055 Use the pypi project page as the default homepage for pip generator
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 14:04:01 -07:00
Elara6331 6effa2d8fe Use the lowercase name for the pip generator
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:51:40 -07:00
Elara6331 f421f40fdf Check for HTTP error in gen.Pip()
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:43:50 -07:00
Elara6331 602a558ab1 Add gen command
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:38:39 -07:00
Elara6331 564f3c9b05 Make capitalization in helper command consistent with the rest of the program
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:01:19 -07:00
Elara6331 09ff697b71 Check if helper command exists before anything else
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:00:31 -07:00
Elara6331 3b382b9747 Add the ability to list helper commands
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 12:59:43 -07:00
Elara6331 5d49de6fde Pass required variables to helper commands invoked by lure helper
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 23:15:38 -07:00
Elara6331 1133785dbd Add helper command
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 23:06:48 -07:00
Elara6331 f32dddee63 Look for provides matches before direct matches when finding packages
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 20:05:06 -07:00
Elara6331 ba1a39874e Improve check for valid version when pulling repo
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 19:48:57 -07:00
Elara6331 00406493c2 Remove any self-provides from alpine packages
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 19:25:24 -07:00
Elara6331 d9659dab9c Fix packages with 'all' architecture
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 18:39:59 -07:00
Elara6331 046db8bf1c Add blake2s and blake2b to the list of valid hash algos
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 18:27:40 -07:00
Elara6331 94bdf8275a Fix search package
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 18:16:11 -07:00
Elara6331 3f3b575b63 Update import path
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 17:34:39 -07:00
Elara6331 7a9cea9702 Remove debug print
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 15:39:48 -07:00
Elara6331 0c6cdadd82 Fix content-disposition parsing
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 15:07:44 -07:00
Elara6331 e20ed6b5eb Set woodpecker platform to amd64
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 14:51:43 -07:00
Elara6331 ae99f4a136 Fix dlcache basepath
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 14:47:21 -07:00
Elara6331 dd216e8707 Fix differences between nfpms and archives in goreleaser file
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 14:42:34 -07:00
Elara6331 1fdb399ba3 Remove deprecated replacements in goreleaser file
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 14:32:09 -07:00
Elara6331 7598122780 Update lure-web domain to lure.sh
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 12:12:00 -07:00
Elara6331 04c7ad4476 Update install script
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 02:32:40 +00:00
Elara6331 f521dc7136 Update URLs
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 19:16:36 -07:00
Elara6331 c6c8828257 Update lure-repo URL
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 19:03:43 -07:00
Elara6331 dbdaaa4184 Fix potential fd/http leak
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:53:13 -07:00
Elara6331 e3a838f312 Only set local file name if name isn't already set
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:51:37 -07:00
Elara6331 383b886472 Implement local file sources
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:40:17 -07:00
Elara6331 b758eb39f0 Fix Content-Disposition parsing when the filename isn't quoted
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:19:07 -07:00
Elara6331 bb1227eadb Change ldflags to use internal/config
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:14:42 -07:00
Elara6331 410e005458 Add ldflags to goreleaser config
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:13:21 -07:00
Elara6331 6b236f6240 Make translations thread safe
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:07:53 -07:00
Elara6331 eb8dd3ad35 Make config thread safe 2023-10-06 15:07:19 -07:00
Elara6331 88bd90ef89 Make DB connection thread safe 2023-10-06 15:04:14 -07:00
Elara6331 cf932e6691 Use the alt screen for internal/pager instead of clearing the screen
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 14:51:48 -07:00
Elara6331 177960431c Add some comments to pkg/loggerctx
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 14:23:29 -07:00
Elara6331 d86776feb1 Pass logger around in a context
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 14:21:12 -07:00
Elara6331 1b8c05b257 Add FilterValue to search options 2023-09-22 16:32:08 -07:00
Elara6331 02a6104fb0 Remove lure-api-server 2023-09-22 15:39:53 -07:00
Elara6331 be1d9be7a8 Add pkg/log 2023-09-22 15:25:40 -07:00
Elara6331 ac45087ead Move db and config back to internal 2023-09-22 15:21:34 -07:00
Elara6331 4774ec3343 Add pkg/search 2023-09-22 15:17:12 -07:00
Elara6331 be7709a5ed Move some things out of internal
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 16:18:18 -07:00
Elara6331 76ba7fcc68 Update README
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 15:13:50 -07:00
Elara6331 02ff473241 Update install command
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 15:01:59 -07:00
Elara6331 6ed31f252c Add some comments to internal/build
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 13:27:23 -07:00
Elara6331 5b87990206 Disable the logger by default
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 20:31:01 -07:00
Elara6331 fe832c97b2 Update docs
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 20:22:02 -07:00
Elara6331 227c9fdda6 Add docs for opt_deps
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 16:32:49 -07:00
Elara6331 8dbdd3edc4 Add opt_deps to packages
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 16:30:00 -07:00
Elara6331 f637dd06a7 Only allow users to choose a single package in the interactive prompt 2023-09-20 15:52:25 -07:00
Elara6331 f66132559d Run formatter
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 15:41:03 -07:00
Elara6331 c2b875db6c Update and add GPL headers
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 15:38:22 -07:00
Elara6331 6388180768 Use internal log package to avoid breaking programs that have their own global loggers
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 15:33:26 -07:00
Elara6331 cf8d08574f Remove unnecessary comment
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 19:37:20 -07:00
Elara6331 0f3718648a Update dependencies
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 15:40:20 -07:00
Elara6331 81f9a4bf95 Move nfpm format imports to internal/build
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 15:11:53 -07:00
Elara6331 45522e3f3a Major refactor
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 14:28:05 -07:00
Elara6331 d59c4036ef Add osutils package
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-13 17:29:49 -07:00
Elara6331 ffc79b8ca3 Remove debug print 2023-08-13 17:12:28 -07:00
Elara6331 dada9d68f2 Add -p flag to build command
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-13 15:27:54 -07:00
Elara6331 10893c07c3 Account for backwards compatibility of ARM
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-11 15:22:01 -07:00
Elara6331 e7e742d98d Add LURE_ARCH variable 2023-08-11 14:45:54 -07:00
Elara6331 f2d4d5250a Fix issue where grep expression can't find latest LURE version
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-08 16:36:59 +00:00
Elara6331 5d566fcf15 Fix panic when an element is missing in the checksums array
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-03 12:57:05 -07:00
Elara6331 f44a9509a2 Fix file downloader not returning the name of the downloaded file
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-03 12:47:17 -07:00
Elara6331 681e5fa95b Make ubuntu use multiarch tuples
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-13 11:17:24 -07:00
Elara6331 53f3265c09 Add install-icon helper 2023-07-13 11:15:02 -07:00
Elara6331 365634f4d9 Add the ability to change the hashing algorithm
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-11 17:00:30 -07:00
Elara6331 e8cb614b2e Run formatter
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-11 13:15:06 -07:00
Elara6331 7a65835816 Use aria2 for torrent downloads
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-11 13:14:54 -07:00
Elara6331 36d3be72a7 Split vercmp into a separate module
ci/woodpecker/push/woodpecker Pipeline was successful
2023-06-22 14:45:05 -07:00
Elara6331 d144a7f758 Disable cgo in Makefile
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-24 12:08:40 -07:00
Elara6331 921555b0db Add torrent downloader
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-24 11:57:50 -07:00
Elara6331 17eb37818f Update username in docker.sh
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-16 13:21:05 -07:00
Elara6331 3bc4b676d5 Switch API server to chi and add badge endpoint
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-16 12:14:37 -07:00
93 changed files with 3837 additions and 5621 deletions
+1
View File
@@ -0,0 +1 @@
liberapay: lure
+25 -13
View File
@@ -1,12 +1,13 @@
before:
hooks:
- go mod tidy
- go generate
builds:
- id: lure
env:
- CGO_ENABLED=0
binary: lure
ldflags:
- -X go.elara.ws/lure/internal/config.Version={{.Version}}
goos:
- linux
goarch:
@@ -16,22 +17,29 @@ builds:
- arm
- riscv64
archives:
- replacements:
386: i386
amd64: x86_64
arm64: aarch64
- name_template: >-
{{- .ProjectName}}-
{{- .Version}}-
{{- .Os}}-
{{- if .Arch | eq "amd64"}}x86_64
{{- else if .Arch | eq "386"}}i386
{{- else if .Arch | eq "arm64"}}aarch64
{{- else }}{{ .Arch }}{{ end -}}
files:
- scripts/completion/*
nfpms:
- id: lure
package_name: linux-user-repository
file_name_template: '{{.PackageName}}-{{.Version}}-{{.Os}}-{{.Arch}}'
file_name_template: >-
{{- .PackageName}}-
{{- .Version}}-
{{- .Os}}-
{{- if .Arch | eq "amd64"}}x86_64
{{- else if .Arch | eq "386"}}i386
{{- else if .Arch | eq "arm64"}}aarch64
{{- else }}{{ .Arch }}{{ end -}}
description: "Linux User REpository"
replacements:
386: i386
amd64: x86_64
arm64: aarch64
homepage: 'https://gitea.elara.ws/Elara6331/lure'
homepage: 'https://lure.sh'
maintainer: 'Elara Musayelyan <elara@elara.ws>'
license: GPLv3
formats:
@@ -43,6 +51,8 @@ nfpms:
- linux-user-repository
conflicts:
- linux-user-repository
recommends:
- aria2
contents:
- src: scripts/completion/bash
dst: /usr/share/bash-completion/completions/lure
@@ -50,7 +60,7 @@ nfpms:
dst: /usr/share/zsh/site-functions/_lure
aurs:
- name: linux-user-repository-bin
homepage: 'https://gitea.elara.ws/Elara6331/lure'
homepage: 'https://lure.sh'
description: "Linux User REpository"
maintainers:
- 'Elara Musayelyan <elara@elara.ws>'
@@ -64,6 +74,8 @@ aurs:
depends:
- sudo
- pacman
optdepends:
- 'aria2: for downloading torrent sources'
package: |-
# binaries
install -Dm755 ./lure "${pkgdir}/usr/bin/lure"
@@ -73,7 +85,7 @@ aurs:
install -Dm755 ./scripts/completion/zsh ${pkgdir}/usr/share/zsh/site-functions/_lure
release:
gitea:
owner: Elara6331
owner: lure
name: lure
gitea_urls:
api: 'https://gitea.elara.ws/api/v1/'
+1
View File
@@ -1,3 +1,4 @@
platform: linux/amd64
pipeline:
release:
image: goreleaser/goreleaser
+4 -6
View File
@@ -1,7 +1,8 @@
PREFIX ?= /usr/local
GIT_VERSION = $(shell git describe --tags )
lure: internal/config/version.txt
go build
lure:
CGO_ENABLED=0 go build -ldflags="-X 'go.elara.ws/lure/internal/config.Version=$(GIT_VERSION)'"
clean:
rm -f lure
@@ -15,8 +16,5 @@ installmisc:
uninstall:
rm -f /usr/local/bin/lure
internal/config/version.txt:
go generate ./internal/config
.PHONY: install clean uninstall
.PHONY: install clean uninstall installmisc lure
+11 -16
View File
@@ -3,7 +3,8 @@
# LURE (Linux User REpository)
[![Go Report Card](https://goreportcard.com/badge/go.elara.ws/lure)](https://goreportcard.com/report/go.elara.ws/lure)
[![status-badge](https://ci.elara.ws/api/badges/Elara6331/lure/status.svg)](https://ci.elara.ws/Elara6331/lure)
[![status-badge](https://ci.elara.ws/api/badges/lure/lure/status.svg)](https://ci.elara.ws/lure/lure)
[![SWH](https://archive.softwareheritage.org/badge/origin/https://gitea.elara.ws/lure/lure.git/)](https://archive.softwareheritage.org/browse/origin/?origin_url=https://gitea.elara.ws/lure/lure.git)
[![linux-user-repository-bin AUR package](https://img.shields.io/aur/version/linux-user-repository-bin?label=linux-user-repository-bin&logo=archlinux)](https://aur.archlinux.org/packages/linux-user-repository-bin/)
LURE is a distro-agnostic build system for Linux, similar to the [AUR](https://wiki.archlinux.org/title/Arch_User_Repository). It is currently in **beta**. Most major bugs have been fixed, and most major features have been added. LURE is ready for general use, but may still break or change occasionally.
@@ -19,14 +20,14 @@ LURE is written in pure Go and has zero dependencies after building. The only th
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.elara.ws/lure.sh | bash
curl -fsSL lure.sh/install | bash
```
**IMPORTANT**: This method is not recommended as it executes any code that is stored at that URL. In order to make sure nothing malicious is going to occur, download the script and inspect it before running.
**IMPORTANT**: This will download and run the script from https://lure.sh/install. Please look through any script you download from the internet (including this one) before running it.
### Packages
Distro packages and binary archives are provided at the latest Gitea release: https://gitea.elara.ws/Elara6331/lure/releases/latest
Distro packages and binary archives are provided at the latest Gitea release: https://gitea.elara.ws/lure/lure/releases/latest
LURE is also available on the AUR as [linux-user-repository-bin](https://aur.archlinux.org/packages/linux-user-repository-bin)
@@ -42,7 +43,7 @@ sudo make install
## Why?
Arch Linux's AUR is a very useful feature. It's one of the main reasons I and many others use Arch on most of their devices. However, Arch is not always a good choice. Whether you're running a server that needs to be stable over long periods of time, or you're a beginner and feel intimidated by Arch, there are many different reasons not to use it. Such useful functionality should not be restricted to only a single distro. That is what LURE is meant to solve.
LURE was created because packaging software for multiple Linux distros can be difficult and error-prone, and installing those packages can be a nightmare for users unless they're available in their distro's official repositories. It automates the process of building and installing unofficial packages.
---
@@ -54,29 +55,23 @@ The documentation for LURE is in the [docs](docs) directory in this repo.
## Web Interface
LURE now has a web interface! It's open source, licensed under the AGPLv3 (https://gitea.elara.ws/Elara6331/lure-web), and is available at https://lure.elara.ws.
LURE has an open source web interface, licensed under the AGPLv3 (https://gitea.elara.ws/lure/lure-web), and it's available at https://lure.sh/.
---
## Repositories
Unlike the AUR, LURE supports third-party repositories. Also unlike the AUR, LURE's repos are single git repositories containing all the build scripts. Inside each LURE repo, there is a separate directory for each package, containing a `lure.sh` script, which is a PKGBUILD-like build script for LURE. The default repository is hosted on Github: https://github.com/Elara6331/lure-repo, and information about its packages is displayed at https://lure.elara.ws/pkgs.
LURE's repos are git repositories that contain a directory for each package, with a `lure.sh` file inside. The `lure.sh` file tells LURE how to build the package and information about it. `lure.sh` scripts are similar to the AUR's PKGBUILD scripts.
---
## Dependencies
## Acknowledgements
As mentioned before, LURE has zero dependencies after compilation. Thanks to the following projects for making this possible:
Thanks to the following projects for making LURE possible:
- https://github.com/mvdan/sh
- https://github.com/go-git/go-git
- https://github.com/mholt/archiver
- https://github.com/goreleaser/nfpm
- https://github.com/charmbracelet/bubbletea
- https://gitlab.com/cznic/sqlite
---
## Planned Features
- Automated docker-based testing tool
- https://gitlab.com/cznic/sqlite
+62 -699
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -19,719 +19,82 @@
package main
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
_ "github.com/goreleaser/nfpm/v2/apk"
_ "github.com/goreleaser/nfpm/v2/arch"
_ "github.com/goreleaser/nfpm/v2/deb"
_ "github.com/goreleaser/nfpm/v2/rpm"
"github.com/urfave/cli/v2"
"golang.org/x/exp/slices"
"github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/files"
"go.elara.ws/logger/log"
"go.elara.ws/lure/distro"
"go.elara.ws/lure/internal/cliutils"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/cpu"
"go.elara.ws/lure/internal/db"
"go.elara.ws/lure/internal/dl"
"go.elara.ws/lure/internal/repos"
"go.elara.ws/lure/internal/shutils"
"go.elara.ws/lure/internal/shutils/decoder"
"go.elara.ws/lure/manager"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"lure.sh/lure/internal/config"
"lure.sh/lure/internal/osutils"
"lure.sh/lure/internal/types"
"lure.sh/lure/pkg/build"
"lure.sh/lure/pkg/loggerctx"
"lure.sh/lure/pkg/manager"
"lure.sh/lure/pkg/repos"
)
// BuildVars represents the script variables required
// to build a package
type BuildVars struct {
Name string `sh:"name,required"`
Version string `sh:"version,required"`
Release int `sh:"release,required"`
Epoch uint `sh:"epoch"`
Description string `sh:"desc"`
Homepage string `sh:"homepage"`
Maintainer string `sh:"maintainer"`
Architectures []string `sh:"architectures"`
Licenses []string `sh:"license"`
Provides []string `sh:"provides"`
Conflicts []string `sh:"conflicts"`
Depends []string `sh:"deps"`
BuildDepends []string `sh:"build_deps"`
Replaces []string `sh:"replaces"`
Sources []string `sh:"sources"`
Checksums []string `sh:"checksums"`
Backup []string `sh:"backup"`
Scripts Scripts `sh:"scripts"`
}
type Scripts struct {
PreInstall string `sh:"preinstall"`
PostInstall string `sh:"postinstall"`
PreRemove string `sh:"preremove"`
PostRemove string `sh:"postremove"`
PreUpgrade string `sh:"preupgrade"`
PostUpgrade string `sh:"postupgrade"`
PreTrans string `sh:"pretrans"`
PostTrans string `sh:"posttrans"`
}
func buildCmd(c *cli.Context) error {
script := c.String("script")
err := repos.Pull(c.Context, gdb, cfg.Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect supported package manager on system").Send()
}
pkgPaths, _, err := buildPackage(c.Context, script, mgr, c.Bool("clean"), c.Bool("interactive"))
if err != nil {
log.Fatal("Error building package").Err(err).Send()
}
wd, err := os.Getwd()
if err != nil {
log.Fatal("Error getting working directory").Err(err).Send()
}
for _, pkgPath := range pkgPaths {
name := filepath.Base(pkgPath)
err = 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, clean, interactive bool) ([]string, []string, error) {
info, err := distro.ParseOSRelease(ctx)
if err != nil {
return nil, nil, err
}
var distroChanged bool
if distID, ok := os.LookupEnv("LURE_DISTRO"); ok {
info.ID = distID
// Since the distro was overwritten, we don't know what the
// like distros are, so set to nil
info.Like = nil
distroChanged = true
}
fl, err := os.Open(script)
if err != nil {
return nil, nil, err
}
file, err := syntax.NewParser().Parse(fl, "lure.sh")
if err != nil {
return nil, nil, err
}
fl.Close()
scriptDir := filepath.Dir(script)
env := genBuildEnv(info, scriptDir)
// The first pass is just used to get variable values and runs before
// the script is displayed, so it is restricted so as to prevent malicious
// code from executing.
runner, err := interp.New(
interp.Env(expand.ListEnviron(env...)),
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
interp.ExecHandler(rHelpers.ExecHandler(shutils.NopExec)),
interp.ReadDirHandler(shutils.RestrictedReadDir(scriptDir)),
interp.StatHandler(shutils.RestrictedStat(scriptDir)),
interp.OpenHandler(shutils.RestrictedOpen(scriptDir)),
)
if err != nil {
return nil, nil, err
}
err = runner.Run(ctx, file)
if err != nil {
return nil, nil, err
}
dec := decoder.New(info, runner)
// If distro was changed, the list of like distros
// no longer applies, so disable its use
if distroChanged {
dec.LikeDistros = false
}
var vars BuildVars
err = dec.DecodeVars(&vars)
if err != nil {
return nil, nil, err
}
baseDir := filepath.Join(config.PkgsDir, vars.Name)
srcdir := filepath.Join(baseDir, "src")
pkgdir := filepath.Join(baseDir, "pkg")
if !clean {
builtPkgPath, ok, err := checkForBuiltPackage(mgr, &vars, getPkgFormat(mgr), baseDir)
if err != nil {
return nil, nil, err
}
if ok {
return []string{builtPkgPath}, nil, err
}
}
err = cliutils.PromptViewScript(script, vars.Name, cfg.PagerStyle, interactive, translator)
if err != nil {
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
}
if !archMatches(vars.Architectures) {
buildAnyway, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", interactive, true, translator)
if err != nil {
return nil, nil, err
}
if !buildAnyway {
os.Exit(1)
}
}
log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send()
// The second pass will be used to execute the actual functions,
// so it cannot be restricted. The script has already been displayed
// to the user by this point, so it should be safe
runner, err = interp.New(
interp.Env(expand.ListEnviron(env...)),
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
interp.ExecHandler(helpers.ExecHandler(nil)),
)
if err != nil {
return nil, nil, err
}
err = runner.Run(ctx, file)
if err != nil {
return nil, nil, err
}
dec = decoder.New(info, runner)
// If distro was changed, the list of like distros
// no longer applies, so disable its use
if distroChanged {
dec.LikeDistros = false
}
err = os.RemoveAll(baseDir)
if err != nil {
return nil, nil, err
}
err = os.MkdirAll(srcdir, 0o755)
if err != nil {
return nil, nil, err
}
err = os.MkdirAll(pkgdir, 0o755)
if err != nil {
return nil, nil, err
}
installed, err := mgr.ListInstalled(nil)
if err != nil {
return nil, nil, err
}
if instVer, ok := installed[vars.Name]; ok {
log.Warn("This package is already installed").
Str("name", vars.Name).
Str("version", instVer).
Send()
}
var buildDeps []string
if len(vars.BuildDepends) > 0 {
found, notFound, err := repos.FindPkgs(gdb, vars.BuildDepends)
if err != nil {
return nil, nil, err
}
found = filterBuildDeps(found, installed)
log.Info("Installing build dependencies").Send()
flattened := cliutils.FlattenPkgs(found, "install", interactive, translator)
buildDeps = packageNames(flattened)
installPkgs(ctx, flattened, notFound, mgr, clean, interactive)
}
var builtDeps, builtNames, repoDeps []string
if len(vars.Depends) > 0 {
log.Info("Installing dependencies").Send()
found, notFound, err := repos.FindPkgs(gdb, vars.Depends)
if err != nil {
return nil, nil, err
}
scripts := getScriptPaths(cliutils.FlattenPkgs(found, "install", interactive, translator))
for _, script := range scripts {
pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr, clean, interactive)
if err != nil {
return nil, nil, err
}
builtDeps = append(builtDeps, pkgPaths...)
builtNames = append(builtNames, pkgNames...)
builtNames = append(builtNames, filepath.Base(filepath.Dir(script)))
}
repoDeps = notFound
}
log.Info("Downloading sources").Send()
err = getSources(ctx, srcdir, &vars)
if err != nil {
return nil, nil, err
}
err = setDirVars(ctx, runner, srcdir, pkgdir)
if err != nil {
return nil, nil, err
}
fn, ok := dec.GetFunc("version")
if ok {
log.Info("Executing version()").Send()
buf := &bytes.Buffer{}
err = fn(
ctx,
interp.Dir(srcdir),
interp.StdIO(os.Stdin, buf, os.Stderr),
)
if err != nil {
return nil, nil, err
}
newVer := strings.TrimSpace(buf.String())
err = setVersion(ctx, runner, newVer)
if err != nil {
return nil, nil, err
}
vars.Version = newVer
log.Info("Updating version").Str("new", newVer).Send()
}
fn, ok = dec.GetFunc("prepare")
if ok {
log.Info("Executing prepare()").Send()
err = fn(ctx, interp.Dir(srcdir))
if err != nil {
return nil, nil, err
}
}
fn, ok = dec.GetFunc("build")
if ok {
log.Info("Executing build()").Send()
err = fn(ctx, interp.Dir(srcdir))
if err != nil {
return nil, nil, err
}
}
fn, ok = dec.GetFunc("package")
if ok {
log.Info("Executing package()").Send()
err = fn(ctx, interp.Dir(srcdir))
if err != nil {
return nil, nil, err
}
} else {
log.Fatal("The package() function is required").Send()
}
log.Info("Building package metadata").Str("name", vars.Name).Send()
uniq(
&repoDeps,
&builtDeps,
&builtNames,
)
pkgInfo := &nfpm.Info{
Name: vars.Name,
Description: vars.Description,
Arch: cpu.Arch(),
Version: vars.Version,
Release: strconv.Itoa(vars.Release),
Homepage: vars.Homepage,
License: strings.Join(vars.Licenses, ", "),
Maintainer: vars.Maintainer,
Overridables: nfpm.Overridables{
Conflicts: vars.Conflicts,
Replaces: vars.Replaces,
Provides: vars.Provides,
Depends: append(repoDeps, builtNames...),
var buildCmd = &cli.Command{
Name: "build",
Usage: "Build a local package",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "script",
Aliases: []string{"s"},
Value: "lure.sh",
Usage: "Path to the build script",
},
}
&cli.StringFlag{
Name: "package",
Aliases: []string{"p"},
Usage: "Name of the package to build and its repo (example: default/go-bin)",
},
&cli.BoolFlag{
Name: "clean",
Aliases: []string{"c"},
Usage: "Build package from scratch even if there's an already built package available",
},
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
if vars.Epoch != 0 {
pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10)
}
script := c.String("script")
if c.String("package") != "" {
script = filepath.Join(config.GetPaths(ctx).RepoDir, c.String("package"), "lure.sh")
}
setScripts(&vars, pkgInfo, filepath.Dir(script))
err := repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
if slices.Contains(vars.Architectures, "all") {
pkgInfo.Arch = "all"
}
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect a supported package manager on the system").Send()
}
contents := []*files.Content{}
filepath.Walk(pkgdir, func(path string, fi os.FileInfo, err error) error {
trimmed := strings.TrimPrefix(path, pkgdir)
pkgPaths, _, err := build.BuildPackage(ctx, types.BuildOpts{
Script: script,
Manager: mgr,
Clean: c.Bool("clean"),
Interactive: c.Bool("interactive"),
})
if err != nil {
log.Fatal("Error building package").Err(err).Send()
}
if fi.IsDir() {
f, err := os.Open(path)
wd, err := os.Getwd()
if err != nil {
log.Fatal("Error getting working directory").Err(err).Send()
}
for _, pkgPath := range pkgPaths {
name := filepath.Base(pkgPath)
err = osutils.Move(pkgPath, filepath.Join(wd, name))
if err != nil {
return err
log.Fatal("Error moving the package").Err(err).Send()
}
_, err = f.Readdirnames(1)
if err != io.EOF {
return nil
}
contents = append(contents, &files.Content{
Source: path,
Destination: trimmed,
Type: "dir",
FileInfo: &files.ContentFileInfo{
MTime: fi.ModTime(),
},
})
f.Close()
return nil
}
if fi.Mode()&os.ModeSymlink != 0 {
link, err := os.Readlink(path)
if err != nil {
return err
}
link = strings.TrimPrefix(link, pkgdir)
contents = append(contents, &files.Content{
Source: link,
Destination: trimmed,
Type: "symlink",
FileInfo: &files.ContentFileInfo{
MTime: fi.ModTime(),
Mode: fi.Mode(),
},
})
return nil
}
fileContent := &files.Content{
Source: path,
Destination: trimmed,
FileInfo: &files.ContentFileInfo{
MTime: fi.ModTime(),
Mode: fi.Mode(),
Size: fi.Size(),
},
}
if slices.Contains(vars.Backup, trimmed) {
fileContent.Type = "config|noreplace"
}
contents = append(contents, fileContent)
return nil
})
pkgInfo.Overridables.Contents = contents
packager, err := nfpm.Get(getPkgFormat(mgr))
if err != nil {
return nil, nil, err
}
pkgName := packager.ConventionalFileName(pkgInfo)
pkgPath := filepath.Join(baseDir, pkgName)
pkgPaths := append(builtDeps, pkgPath)
pkgNames := append(builtNames, vars.Name)
pkgFile, err := os.Create(pkgPath)
if err != nil {
return nil, nil, err
}
log.Info("Compressing package").Str("name", pkgName).Send()
err = packager.Package(pkgInfo, pkgFile)
if err != nil {
return nil, nil, err
}
if len(buildDeps) > 0 {
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove build dependencies?", interactive, false, translator)
if err != nil {
return nil, nil, err
}
if removeBuildDeps {
err = mgr.Remove(
&manager.Opts{
AsRoot: true,
NoConfirm: true,
},
buildDeps...,
)
if err != nil {
return nil, nil, err
}
}
}
uniq(&pkgPaths, &pkgNames)
return pkgPaths, pkgNames, nil
}
func checkForBuiltPackage(mgr manager.Manager, vars *BuildVars, pkgFormat, baseDir string) (string, bool, error) {
filename, err := pkgFileName(vars, pkgFormat)
if err != nil {
return "", false, err
}
pkgPath := filepath.Join(baseDir, filename)
_, err = os.Stat(pkgPath)
if err != nil {
return "", false, nil
}
return pkgPath, true, nil
}
func pkgFileName(vars *BuildVars, pkgFormat string) (string, error) {
pkgInfo := &nfpm.Info{
Name: vars.Name,
Arch: cpu.Arch(),
Version: vars.Version,
Release: strconv.Itoa(vars.Release),
Epoch: strconv.FormatUint(uint64(vars.Epoch), 10),
}
packager, err := nfpm.Get(pkgFormat)
if err != nil {
return "", err
}
return packager.ConventionalFileName(pkgInfo), nil
}
func getPkgFormat(mgr manager.Manager) string {
pkgFormat := mgr.Format()
if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
pkgFormat = format
}
return pkgFormat
}
func genBuildEnv(info *distro.OSRelease, scriptdir string) []string {
env := os.Environ()
env = append(
env,
"DISTRO_NAME="+info.Name,
"DISTRO_PRETTY_NAME="+info.PrettyName,
"DISTRO_ID="+info.ID,
"DISTRO_VERSION_ID="+info.VersionID,
"DISTRO_ID_LIKE="+strings.Join(info.Like, " "),
"ARCH="+cpu.Arch(),
"NCPU="+strconv.Itoa(runtime.NumCPU()),
"scriptdir="+scriptdir,
)
return env
}
func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
if len(bv.Sources) != len(bv.Checksums) {
log.Fatal("The checksums array must be the same length as sources")
}
for i, src := range bv.Sources {
opts := dl.Options{
Name: fmt.Sprintf("%s[%d]", bv.Name, i),
URL: src,
Destination: srcdir,
Progress: os.Stderr,
}
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
checksum, err := hex.DecodeString(bv.Checksums[i])
if err != nil {
return err
}
opts.SHA256 = checksum
}
err := dl.Download(ctx, opts)
if err != nil {
return err
}
}
return nil
}
// setDirVars sets srcdir and pkgdir. It's a very hacky way of doing so,
// but setting the runner's Env and Vars fields doesn't seem to work.
func setDirVars(ctx context.Context, runner *interp.Runner, srcdir, pkgdir string) error {
cmd := "srcdir='" + srcdir + "'\npkgdir='" + pkgdir + "'\n"
fl, err := syntax.NewParser().Parse(strings.NewReader(cmd), "vars")
if err != nil {
return err
}
return runner.Run(ctx, fl)
}
func setScripts(vars *BuildVars, info *nfpm.Info, scriptDir string) {
if vars.Scripts.PreInstall != "" {
info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall)
}
if vars.Scripts.PostInstall != "" {
info.Scripts.PostInstall = filepath.Join(scriptDir, vars.Scripts.PostInstall)
}
if vars.Scripts.PreRemove != "" {
info.Scripts.PreRemove = filepath.Join(scriptDir, vars.Scripts.PreRemove)
}
if vars.Scripts.PostRemove != "" {
info.Scripts.PostRemove = filepath.Join(scriptDir, vars.Scripts.PostRemove)
}
if vars.Scripts.PreUpgrade != "" {
info.ArchLinux.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
info.APK.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
}
if vars.Scripts.PostUpgrade != "" {
info.ArchLinux.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
info.APK.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
}
if vars.Scripts.PreTrans != "" {
info.RPM.Scripts.PreTrans = filepath.Join(scriptDir, vars.Scripts.PreTrans)
}
if vars.Scripts.PostTrans != "" {
info.RPM.Scripts.PostTrans = filepath.Join(scriptDir, vars.Scripts.PostTrans)
}
}
// archMatches checks if your system architecture matches
// one of the provided architectures
func archMatches(architectures []string) bool {
if slices.Contains(architectures, "all") {
return true
}
if slices.Contains(architectures, "arm") {
architectures = append(architectures, cpu.ARMVariant())
}
return slices.Contains(architectures, cpu.Arch())
}
func setVersion(ctx context.Context, r *interp.Runner, to string) error {
fl, err := syntax.NewParser().Parse(strings.NewReader("version='"+to+"'"), "")
if err != nil {
return err
}
return r.Run(ctx, fl)
}
func filterBuildDeps(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
out := map[string][]db.Package{}
for name, pkgs := range found {
var inner []db.Package
for _, pkg := range pkgs {
if _, ok := installed[pkg.Name]; !ok {
addToFiltered := true
for _, provides := range pkg.Provides.Val {
if _, ok := installed[provides]; ok {
addToFiltered = false
break
}
}
if addToFiltered {
inner = append(inner, pkg)
}
}
}
if len(inner) > 0 {
out[name] = inner
}
}
return out
}
func packageNames(pkgs []db.Package) []string {
names := make([]string, len(pkgs))
for i, p := range pkgs {
names[i] = p.Name
}
return names
}
// uniq removes all duplicates from string slices
func uniq(ss ...*[]string) {
for _, s := range ss {
slices.Sort(*s)
*s = slices.Compact(*s)
}
},
}
-3
View File
@@ -1,3 +0,0 @@
FROM alpine:latest
COPY lure-api-server /usr/bin/lure-api-server
ENTRYPOINT lure-api-server
-3
View File
@@ -1,3 +0,0 @@
# lure-api-server
`lure-api-server` is the backend API server for lure-web, the web interface for LURE.
-205
View File
@@ -1,205 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"context"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/jmoiron/sqlx"
"github.com/twitchtv/twirp"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/api"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/db"
"golang.org/x/text/language"
)
type lureWebAPI struct {
db *sqlx.DB
}
func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.SearchResponse, error) {
query := "(name LIKE ? OR description LIKE ? OR json_array_contains(provides, ?))"
args := []any{"%" + req.Query + "%", "%" + req.Query + "%", req.Query}
if req.FilterValue != nil && req.FilterType != api.FILTER_TYPE_NO_FILTER {
switch req.FilterType {
case api.FILTER_TYPE_IN_REPOSITORY:
query += " AND repository = ?"
case api.FILTER_TYPE_SUPPORTS_ARCH:
query += " AND json_array_contains(architectures, ?)"
}
args = append(args, *req.FilterValue)
}
if req.SortBy != api.SORT_BY_UNSORTED {
switch req.SortBy {
case api.SORT_BY_NAME:
query += " ORDER BY name"
case api.SORT_BY_REPOSITORY:
query += " ORDER BY repository"
case api.SORT_BY_VERSION:
query += " ORDER BY version"
}
}
if req.Limit != 0 {
query += " LIMIT " + strconv.FormatInt(req.Limit, 10)
}
result, err := db.GetPkgs(l.db, query, args...)
if err != nil {
return nil, err
}
out := &api.SearchResponse{}
for result.Next() {
pkg := &db.Package{}
err = result.StructScan(pkg)
if err != nil {
return nil, err
}
out.Packages = append(out.Packages, dbPkgToAPI(ctx, pkg))
}
return out, err
}
func (l lureWebAPI) GetPkg(ctx context.Context, req *api.GetPackageRequest) (*api.Package, error) {
pkg, err := db.GetPkg(l.db, "name = ? AND repository = ?", req.Name, req.Repository)
if err != nil {
return nil, err
}
return dbPkgToAPI(ctx, pkg), nil
}
func (l lureWebAPI) GetBuildScript(ctx context.Context, req *api.GetBuildScriptRequest) (*api.GetBuildScriptResponse, error) {
if strings.ContainsAny(req.Name, "./") || strings.ContainsAny(req.Repository, "./") {
return nil, twirp.NewError(twirp.InvalidArgument, "name and repository must not contain . or /")
}
scriptPath := filepath.Join(config.RepoDir, req.Repository, req.Name, "lure.sh")
_, err := os.Stat(scriptPath)
if os.IsNotExist(err) {
return nil, twirp.NewError(twirp.NotFound, "requested package not found")
} else if err != nil {
return nil, err
}
data, err := os.ReadFile(scriptPath)
if err != nil {
return nil, err
}
return &api.GetBuildScriptResponse{Script: string(data)}, nil
}
func dbPkgToAPI(ctx context.Context, pkg *db.Package) *api.Package {
return &api.Package{
Name: pkg.Name,
Repository: pkg.Repository,
Version: pkg.Version,
Release: int64(pkg.Release),
Epoch: ptr(int64(pkg.Epoch)),
Description: performTranslation(ctx, pkg.Description.Val),
Homepage: performTranslation(ctx, pkg.Homepage.Val),
Maintainer: performTranslation(ctx, pkg.Maintainer.Val),
Architectures: pkg.Architectures.Val,
Licenses: pkg.Licenses.Val,
Provides: pkg.Provides.Val,
Conflicts: pkg.Conflicts.Val,
Replaces: pkg.Replaces.Val,
Depends: dbMapToAPI(pkg.Depends.Val),
BuildDepends: dbMapToAPI(pkg.BuildDepends.Val),
}
}
func ptr[T any](v T) *T {
return &v
}
func dbMapToAPI(m map[string][]string) map[string]*api.StringList {
out := make(map[string]*api.StringList, len(m))
for override, list := range m {
out[override] = &api.StringList{Entries: list}
}
return out
}
func performTranslation(ctx context.Context, v map[string]string) *string {
alVal := ctx.Value(acceptLanguageKey{})
langVal := ctx.Value(langParameterKey{})
if alVal == nil && langVal == nil {
val, ok := v[""]
if !ok {
return ptr("<unknown>")
}
return &val
}
al, _ := alVal.(string)
lang, _ := langVal.(string)
tags, _, err := language.ParseAcceptLanguage(al)
if err != nil {
log.Warn("Error parsing Accept-Language header").Err(err).Send()
}
var bases []string
if lang != "" {
langTag, err := language.Parse(lang)
if err != nil {
log.Warn("Error parsing lang parameter").Err(err).Send()
bases = getLangBases(tags)
} else {
bases = getLangBases(append([]language.Tag{langTag}, tags...))
}
} else {
bases = getLangBases(tags)
}
if len(bases) == 1 {
bases = []string{"en", ""}
}
for _, name := range bases {
val, ok := v[name]
if !ok {
continue
}
return &val
}
return ptr("<unknown>")
}
func getLangBases(langs []language.Tag) []string {
out := make([]string, len(langs)+1)
for i, lang := range langs {
base, _ := lang.Base()
out[i] = base.String()
}
out[len(out)-1] = ""
return out
}
-34
View File
@@ -1,34 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/types"
)
var cfg types.Config
func init() {
err := config.Decode(&cfg)
if err != nil {
log.Fatal("Error decoding config file").Err(err).Send()
}
}
-36
View File
@@ -1,36 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/jmoiron/sqlx"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/db"
)
var gdb *sqlx.DB
func init() {
var err error
gdb, err = db.Open(config.DBPath)
if err != nil {
log.Fatal("Error opening database").Err(err).Send()
}
}
-14
View File
@@ -1,14 +0,0 @@
#!/bin/bash
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build
docker buildx build --platform linux/amd64 --tag arsen6331/lure-api-server:amd64 --no-cache .
CGO_ENABLED=0 GOARCH=arm64 GOOS=linux go build
docker buildx build --platform linux/arm64/v8 --tag arsen6331/lure-api-server:arm64 --no-cache .
docker login
docker push arsen6331/lure-api-server -a
docker manifest rm arsen6331/lure-api-server:latest
docker manifest create arsen6331/lure-api-server:latest --amend arsen6331/lure-api-server:arm64 --amend arsen6331/lure-api-server:amd64
docker manifest push arsen6331/lure-api-server:latest
-117
View File
@@ -1,117 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"context"
"flag"
"net"
"net/http"
"os"
"github.com/twitchtv/twirp"
"go.elara.ws/logger"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/api"
"go.elara.ws/lure/internal/repos"
)
func init() {
log.Logger = logger.NewPretty(os.Stderr)
}
func main() {
ctx := context.Background()
addr := flag.String("a", ":8080", "Listen address for API server")
logFile := flag.String("l", "", "Output file for JSON log")
flag.Parse()
if *logFile != "" {
fl, err := os.Create(*logFile)
if err != nil {
log.Fatal("Error creating log file").Err(err).Send()
}
defer fl.Close()
log.Logger = logger.NewMulti(log.Logger, logger.NewJSON(fl))
}
err := repos.Pull(ctx, gdb, cfg.Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
sigCh := make(chan struct{}, 200)
go repoPullWorker(ctx, sigCh)
var handler http.Handler
handler = api.NewAPIServer(
lureWebAPI{db: gdb},
twirp.WithServerPathPrefix(""),
)
handler = withAcceptLanguage(handler)
handler = allowAllCORSHandler(handler)
handler = handleWebhook(handler, sigCh)
ln, err := net.Listen("tcp", *addr)
if err != nil {
log.Fatal("Error starting listener").Err(err).Send()
}
log.Info("Starting HTTP API server").Str("addr", ln.Addr().String()).Send()
err = http.Serve(ln, handler)
if err != nil {
log.Fatal("Error while running server").Err(err).Send()
}
}
func allowAllCORSHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Access-Control-Allow-Origin", "*")
res.Header().Set("Access-Control-Allow-Headers", "*")
if req.Method == http.MethodOptions {
return
}
h.ServeHTTP(res, req)
})
}
type (
acceptLanguageKey struct{}
langParameterKey struct{}
)
func withAcceptLanguage(h http.Handler) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
ctx := req.Context()
langs := req.Header.Get("Accept-Language")
ctx = context.WithValue(ctx, acceptLanguageKey{}, langs)
lang := req.URL.Query().Get("lang")
ctx = context.WithValue(ctx, langParameterKey{}, lang)
req = req.WithContext(ctx)
h.ServeHTTP(res, req)
})
}
-107
View File
@@ -1,107 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"errors"
"io"
"net/http"
"os"
"strings"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/repos"
)
func handleWebhook(next http.Handler, sigCh chan<- struct{}) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
if req.URL.Path == "/webhook" {
if req.Method != http.MethodPost {
res.WriteHeader(http.StatusMethodNotAllowed)
return
}
if req.Header.Get("X-GitHub-Event") != "push" {
http.Error(res, "Only push events are accepted by this bot", http.StatusBadRequest)
return
}
err := verifySecure(req)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
sigCh <- struct{}{}
return
}
next.ServeHTTP(res, req)
})
}
func verifySecure(req *http.Request) error {
sigStr := req.Header.Get("X-Hub-Signature-256")
sig, err := hex.DecodeString(strings.TrimPrefix(sigStr, "sha256="))
if err != nil {
return err
}
secretStr, ok := os.LookupEnv("LURE_API_GITHUB_SECRET")
if !ok {
return errors.New("LURE_API_GITHUB_SECRET must be set to the secret used for setting up the github webhook\n\n")
}
secret := []byte(secretStr)
h := hmac.New(sha256.New, secret)
_, err = io.Copy(h, req.Body)
if err != nil {
return err
}
if !hmac.Equal(h.Sum(nil), sig) {
log.Warn("Insecure webhook request").
Str("from", req.RemoteAddr).
Bytes("sig", sig).
Bytes("hmac", h.Sum(nil)).
Send()
return errors.New("webhook signature mismatch")
}
return nil
}
func repoPullWorker(ctx context.Context, sigCh <-chan struct{}) {
for {
select {
case <-sigCh:
err := repos.Pull(ctx, gdb, cfg.Repos)
if err != nil {
log.Warn("Error while pulling repositories").Err(err).Send()
}
case <-ctx.Done():
return
}
}
}
-36
View File
@@ -1,36 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/types"
"go.elara.ws/lure/manager"
)
var cfg types.Config
func init() {
err := config.Decode(&cfg)
if err != nil {
log.Fatal("Error decoding config file").Err(err).Send()
}
manager.DefaultRootCmd = cfg.RootCmd
}
-36
View File
@@ -1,36 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/jmoiron/sqlx"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/db"
)
var gdb *sqlx.DB
func loadDB() error {
ldb, err := db.Open(config.DBPath)
if err != nil {
return err
}
gdb = ldb
return nil
}
+1 -1
View File
@@ -36,7 +36,7 @@ The `repo` array in the config specifies which repos are added to LURE. Each rep
```toml
[[repo]]
name = 'default'
url = 'https://github.com/Arsen6331/lure-repo.git'
url = 'https://github.com/Elara6331/lure-repo.git'
```
The `default` repo is added by default. Any amount of repos may be added.
+3 -3
View File
@@ -5,7 +5,7 @@
- `go` (1.18+)
- `git`
- `lure-analyzer`
- `go install go.arsenm.dev/lure-repo-bot/cmd/lure-analyzer@latest`
- `go install go.elara.ws/lure-repo-bot/cmd/lure-analyzer@latest`
- `shfmt`
- May be available in distro repos
- `go install mvdan.cc/sh/v3/cmd/shfmt@latest`
@@ -18,9 +18,9 @@ To test packages you can first create [a `lure.sh` shell file](./build-scripts.m
## 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.
LURE's repo is hosted on Github at https://github.com/Elara6331/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.
Upon submitting the PR, [lure-repo-bot](https://github.com/Elara6331/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.
+19 -5
View File
@@ -21,6 +21,7 @@ LURE uses build scripts similar to the AUR's PKGBUILDs. This is the documentatio
- [conflicts](#conflicts)
- [deps](#deps)
- [build_deps](#build_deps)
- [opt_deps](#opt_deps)
- [replaces](#replaces)
- [sources](#sources)
- [checksums](#checksums)
@@ -54,7 +55,7 @@ LURE uses build scripts similar to the AUR's PKGBUILDs. This is the documentatio
## Distro Overrides
Allowing LURE to run on different distros provides some challenges. For example, some distros use different names for their packages. This is solved using distro overrides. Any variable or function used in a LURE build script may be overridden based on distro and CPU architecture. The way you do this is by appending the distro and/or architecture to the end of the name. For example, [ITD](https://gitea.arsenm.dev/Arsen6331/itd) depends on the `pactl` command as well as DBus and BlueZ. These are named somewhat differently on different distros. For ITD, I use the following for the dependencies:
Allowing LURE to run on different distros provides some challenges. For example, some distros use different names for their packages. This is solved using distro overrides. Any variable or function used in a LURE build script may be overridden based on distro and CPU architecture. The way you do this is by appending the distro and/or architecture to the end of the name. For example, [ITD](https://gitea.elara.ws/Elara6331/itd) depends on the `pactl` command as well as DBus and BlueZ. These are named somewhat differently on different distros. For ITD, I use the following for the dependencies:
```bash
deps=('dbus' 'bluez' 'pulseaudio-utils')
@@ -117,7 +118,7 @@ The `homepage` field contains the URL to the website of the project packaged by
The `maintainer` field contains the name and email address of the person maintaining the package. Example:
```text
Arsen Musayelyan <arsen@arsenm.dev>
Elara Musayelyan <elara@elara.ws>
```
While LURE does not require this field to be set, Debian has deprecated unset maintainer fields, and may disallow their use in `.deb` packages in the future.
@@ -160,6 +161,17 @@ The `deps` array contains the dependencies for the package. LURE repos will be c
The `build_deps` array contains the dependencies that are required to build the package. They will be installed before the build starts. Similarly to the `deps` array, LURE repos will be checked first.
### opt_deps
The `opt_deps` array contains optional dependencies for the package. A description can be added after ": ", but it's not required.
```bash
opt_deps_arch=(
'git: Download git repositories'
'aria2: Download files'
)
```
### replaces
The `replaces` array contains the packages that are replaced by this package. Generally, if package managers find a package with a `replaces` field set, they will remove the listed package(s) and install that one instead. This is only useful if the packages are being stored in a repo for your package manager.
@@ -190,16 +202,18 @@ If the URL scheme starts with `git+`, the source will be downloaded as a git rep
Examples:
```text
git+https://gitea.arsenm.dev/Arsen6331/itd?~rev=resource-loading&~depth=1
git+https://gitea.elara.ws/Elara6331/itd?~rev=resource-loading&~depth=1
```
```text
git+https://gitea.arsenm.dev/Arsen6331/lure?~rev=v0.0.1&~recursive=true
git+https://gitea.elara.ws/lure/lure?~rev=v0.0.1&~recursive=true
```
### checksums
The `checksums` array must be the same length as the `sources` array. It contains sha256 checksums for the source files. The files are checked against the checksums and the build fails if they don't match.
The `checksums` array must be the same length as the `sources` array. It contains checksums for the source files. The files are checked against the checksums and the build fails if they don't match.
By default, checksums are expected to be sha256. To change the algorithm, add it before the hash with a colon in between. For example, `md5:bc0c6f5dcd06bddbca9a0163e4c9f2e1`. The following algorithms are currently supported: `sha256`, `sha224`, `sha512`, `sha384`, `sha1`, and `md5`.
To skip the check for a particular source, set the corresponding checksum to `SKIP`.
+1 -1
View File
@@ -117,7 +117,7 @@ The addrepo command adds a repository to LURE if it doesn't already exist. The `
Example:
```shell
lure ar -n default -u https://github.com/Arsen6331/lure-repo
lure ar -n default -u https://github.com/Elara6331/lure-repo
```
### removerepo
+30 -29
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -22,42 +22,43 @@ import (
"os"
"github.com/urfave/cli/v2"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/db"
"go.elara.ws/lure/internal/repos"
"lure.sh/lure/internal/config"
"lure.sh/lure/internal/db"
"lure.sh/lure/pkg/loggerctx"
"lure.sh/lure/pkg/repos"
)
func fixCmd(c *cli.Context) error {
gdb.Close()
var fixCmd = &cli.Command{
Name: "fix",
Usage: "Attempt to fix problems with LURE",
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
log.Info("Removing cache directory").Send()
db.Close()
paths := config.GetPaths(ctx)
err := os.RemoveAll(config.CacheDir)
if err != nil {
log.Fatal("Unable to remove cache directory").Err(err).Send()
}
log.Info("Removing cache directory").Send()
log.Info("Rebuilding cache").Send()
err := os.RemoveAll(paths.CacheDir)
if err != nil {
log.Fatal("Unable to remove cache directory").Err(err).Send()
}
err = os.MkdirAll(config.CacheDir, 0o755)
if err != nil {
log.Fatal("Unable to create new cache directory").Err(err).Send()
}
log.Info("Rebuilding cache").Send()
// Make sure the DB is rebuilt when repos are pulled
gdb, err = db.Open(config.DBPath)
if err != nil {
log.Fatal("Error initializing database").Err(err).Send()
}
config.DBPresent = false
err = os.MkdirAll(paths.CacheDir, 0o755)
if err != nil {
log.Fatal("Unable to create new cache directory").Err(err).Send()
}
err = repos.Pull(c.Context, gdb, cfg.Repos)
if err != nil {
log.Fatal("Error pulling repos").Err(err).Send()
}
err = repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repos").Err(err).Send()
}
log.Info("Done").Send()
log.Info("Done").Send()
return nil
return nil
},
}
+45
View File
@@ -0,0 +1,45 @@
package main
import (
"os"
"github.com/urfave/cli/v2"
"lure.sh/lure/pkg/gen"
)
var genCmd = &cli.Command{
Name: "generate",
Usage: "Generate a LURE script from a template",
Aliases: []string{"gen"},
Subcommands: []*cli.Command{
genPipCmd,
},
}
var genPipCmd = &cli.Command{
Name: "pip",
Usage: "Generate a LURE script for a pip module",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Required: true,
},
&cli.StringFlag{
Name: "version",
Aliases: []string{"v"},
Required: true,
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"d"},
},
},
Action: func(c *cli.Context) error {
return gen.Pip(os.Stdout, gen.PipOptions{
Name: c.String("name"),
Version: c.String("version"),
Description: c.String("description"),
})
},
}
+74 -60
View File
@@ -1,111 +1,125 @@
module go.elara.ws/lure
module lure.sh/lure
go 1.18
go 1.21
toolchain go1.21.3
require (
github.com/AlecAivazis/survey/v2 v2.3.6
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/PuerkitoBio/purell v1.2.0
github.com/alecthomas/chroma/v2 v2.4.0
github.com/charmbracelet/bubbles v0.14.0
github.com/charmbracelet/bubbletea v0.23.1
github.com/charmbracelet/lipgloss v0.6.0
github.com/go-git/go-billy/v5 v5.3.1
github.com/go-git/go-git/v5 v5.4.2
github.com/goreleaser/nfpm/v2 v2.20.0
github.com/alecthomas/chroma/v2 v2.9.1
github.com/charmbracelet/bubbles v0.16.1
github.com/charmbracelet/bubbletea v0.24.2
github.com/charmbracelet/lipgloss v0.8.0
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.9.0
github.com/goreleaser/nfpm/v2 v2.33.0
github.com/jmoiron/sqlx v1.3.5
github.com/mattn/go-isatty v0.0.18
github.com/mholt/archiver/v4 v4.0.0-alpha.7
github.com/mattn/go-isatty v0.0.19
github.com/mholt/archiver/v4 v4.0.0-alpha.8
github.com/mitchellh/mapstructure v1.5.0
github.com/muesli/reflow v0.3.0
github.com/pelletier/go-toml/v2 v2.0.7
github.com/schollz/progressbar/v3 v3.13.0
github.com/twitchtv/twirp v8.1.3+incompatible
github.com/urfave/cli/v2 v2.23.7
github.com/pelletier/go-toml/v2 v2.1.0
github.com/schollz/progressbar/v3 v3.13.1
github.com/urfave/cli/v2 v2.25.7
github.com/vmihailenco/msgpack/v5 v5.3.5
go.elara.ws/logger v0.0.0-20230421022458-e80700db2090
go.elara.ws/translate v0.0.0-20230421025926-32ccfcd110e6
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b
golang.org/x/sys v0.7.0
golang.org/x/text v0.9.0
google.golang.org/protobuf v1.27.1
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4
golang.org/x/crypto v0.13.0
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/sys v0.12.0
golang.org/x/text v0.13.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.20.0
mvdan.cc/sh/v3 v3.5.1
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee
modernc.org/sqlite v1.25.0
mvdan.cc/sh/v3 v3.7.0
)
require (
dario.cat/mergo v1.0.0 // indirect
github.com/AlekSi/pointer v1.2.0 // 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.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
github.com/bodgit/plumbing v1.2.0 // indirect
github.com/bodgit/sevenzip v1.3.0 // indirect
github.com/bodgit/windows v1.0.0 // indirect
github.com/cavaliergopher/cpio v1.0.1 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/connesc/cipherio v0.2.1 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
github.com/frankban/quicktest v1.14.3 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/rpmpack v0.5.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gookit/color v1.5.3 // indirect
github.com/goreleaser/chglog v0.2.2 // indirect
github.com/gookit/color v1.5.1 // indirect
github.com/goreleaser/chglog v0.5.0 // indirect
github.com/goreleaser/fileglob v1.3.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.16 // indirect
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.11 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.13.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.4 // 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/shopspring/decimal v1.2.0 // indirect
github.com/skeema/knownhosts v1.2.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/xanzy/ssh-agent v0.3.3 // 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-20220722155217-630584e8d5aa // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.6.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/tools v0.6.0 // indirect
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/term v0.12.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.21.5 // indirect
modernc.org/libc v1.24.1 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.4.0 // indirect
modernc.org/memory v1.6.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
+429 -188
View File
@@ -1,152 +1,232 @@
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=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
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/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/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
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=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
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.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
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=
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c h1:bNpaLLv2Y4kslsdkdCwAYu8Bak1aGVtxwi8Z/wy4Yuo=
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/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/gopenpgp/v2 v2.7.1 h1:Awsg7MPc2gD3I7IFac2qE3Gdls0lZW8SzrFZ3k1oz0s=
github.com/ProtonMail/gopenpgp/v2 v2.7.1/go.mod h1:/BU5gfAVwqyd8EfC3Eu7zmuhwYQpKs+cGD8M//iiaxs=
github.com/PuerkitoBio/purell v1.2.0 h1:/Jdm5QfyM8zdlqT6WVZU4cfP23sot6CEHA4CS49Ezig=
github.com/PuerkitoBio/purell v1.2.0/go.mod h1:OhLRTaaIzhvIyofkJfB24gokC7tM42Px5UhoT32THBk=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/alecthomas/assert/v2 v2.2.0 h1:f6L/b7KE2bfA+9O4FL3CM/xJccDEwPVYd5fALBiuwvw=
github.com/alecthomas/chroma/v2 v2.4.0 h1:Loe2ZjT5x3q1bcWwemqyqEi8p11/IV/ncFCeLYDpWC4=
github.com/alecthomas/chroma/v2 v2.4.0/go.mod h1:6kHzqF5O6FUSJzBXW7fXELjb+e+7OXW4UpoPqMO7IBQ=
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/chroma/v2 v2.9.1 h1:0O3lTQh9FxazJ4BYE/MOi/vDGuHn7B+6Bu902N2UZvU=
github.com/alecthomas/chroma/v2 v2.9.1/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
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/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg=
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
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/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM=
github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8=
github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY=
github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM=
github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA=
github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11 h1:IRrDwVlWQr6kS1U8/EtyA1+EHcc4yl8pndcqXWrEamg=
github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11/go.mod h1:je2KZ+LxaCNvCoKg32jtOIULcFogJKcL1ZWUaIBjKj0=
github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8=
github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og=
github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc=
github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4=
github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck=
github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY=
github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY=
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU=
github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=
github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
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=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
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-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=
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY=
github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0=
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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
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/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
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.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.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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/rpmpack v0.5.0 h1:L16KZ3QvkFGpYhmp23iQip+mx1X39foEsqszjMNBm8A=
github.com/google/rpmpack v0.5.0/go.mod h1:uqVAUVQLq8UY2hCDfmJ/+rtO3aw7qyhc90rCVEabEfI=
github.com/google/uuid v1.1.1/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.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE=
github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/goreleaser/chglog v0.2.2 h1:V7nf07baXtGAgGevvqgW2MM4kZ6gOr12vKNSAU3VIZ0=
github.com/goreleaser/chglog v0.2.2/go.mod h1:2s5JwtCOWjZa8AIneL+xdUl9SRuigCjRHNHsX30dupE=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
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 v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/goreleaser/chglog v0.5.0 h1:Sk6BMIpx8+vpAf8KyPit34OgWui8c7nKTMHhYx88jJ4=
github.com/goreleaser/chglog v0.5.0/go.mod h1:Ri46M3lrMuv76FHszs3vtABR8J8k1w9JHYAzxeeOl28=
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/goreleaser/nfpm/v2 v2.33.0 h1:yBv6jgkPwih4va/S42rceSjJ2Znt3Og/Ntc76oP0tfI=
github.com/goreleaser/nfpm/v2 v2.33.0/go.mod h1:8wwWWvJWmn84xo/Sqiv0aMvEGTHlHZTXTEuVSgQpkIM=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
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/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
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/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
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/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
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.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@@ -154,184 +234,332 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
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/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
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.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
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/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8=
github.com/schollz/progressbar/v3 v3.13.0/go.mod h1:ZBYnSuLAX2LU8P8UiKN/KgF2DY58AJC8yfVYLPC8Ly4=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE=
github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0ZxudX+ThRdWfU=
github.com/smartystreets/assertions v1.13.1/go.mod h1:cXr/IwVfSo/RbCSPhoAPv73p3hlSdrBH/b3SdnW/LMY=
github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL+JXWq3w=
github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
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/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
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=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
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/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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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.elara.ws/logger v0.0.0-20230421022458-e80700db2090 h1:RVC8XvWo6Yw4HUshqx4TSzuBDScDghafU6QFRJ4xPZg=
go.elara.ws/logger v0.0.0-20230421022458-e80700db2090/go.mod h1:qng49owViqsW5Aey93lwBXONw20oGbJIoLVscB16mPM=
go.elara.ws/translate v0.0.0-20230421025926-32ccfcd110e6 h1:4xCBxLPBn3Y2DuIcj8zQ1tQOFLrpu6tEIGUWn/Q6zPM=
go.elara.ws/translate v0.0.0-20230421025926-32ccfcd110e6/go.mod h1:NmfCFqwq7X/aqa/ZVkIysj17JyMEY4Bb5E921kMswNo=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4=
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 h1:Ep54XceQlKhcCHl9awG+wWP4kz4kIP3c3Lzw/Gc/zwY=
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4/go.mod h1:/7PNW7nFnDR5W7UXZVc04gdVLR/wBNgkm33KgIz0OBk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU=
go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=
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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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-20190108225652-1e06a53dbb7e/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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-20181221193216-37e7f081c4d4/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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/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-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
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-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
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=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
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=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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=
@@ -341,32 +569,45 @@ 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-20200313102051-9f266ea9e77c/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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee h1:kSXIuMid56Q29WEl7EQb5QUtmGqQqAu66EZ2G0OSUfU=
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee/go.mod h1:/v0u0AZ+wbzUWhV02KzciOf1KFNh7/7rbkz5Z0b5gDA=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/libc v1.21.5 h1:xBkU9fnHV+hvZuPSRszN0AXDG4M7nwPLwTWwkYcvLCI=
modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.20.0 h1:80zmD3BGkm8BZ5fUi/4lwJQHiO3GXgIUvZRXpoIfROY=
modernc.org/sqlite v1.20.0/go.mod h1:EsYz8rfOvLCiYTy5ZFsOYzoCcRMu98YYkwAcCw5YIYw=
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
mvdan.cc/sh/v3 v3.5.1 h1:hmP3UOw4f+EYexsJjFxvU38+kn+V/s2CclXHanIBkmQ=
mvdan.cc/sh/v3 v3.5.1/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg=
mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+86
View File
@@ -0,0 +1,86 @@
package main
import (
"fmt"
"os"
"strings"
"github.com/urfave/cli/v2"
"lure.sh/lure/internal/cpu"
"lure.sh/lure/internal/shutils/helpers"
"lure.sh/lure/pkg/distro"
"lure.sh/lure/pkg/loggerctx"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
)
var helperCmd = &cli.Command{
Name: "helper",
Usage: "Run a LURE helper command",
ArgsUsage: `<helper_name|"list">`,
Subcommands: []*cli.Command{helperListCmd},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "dest-dir",
Aliases: []string{"d"},
Usage: "The directory that the install commands will install to",
Value: "dest",
},
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
if c.Args().Len() < 1 {
cli.ShowSubcommandHelpAndExit(c, 1)
}
helper, ok := helpers.Helpers[c.Args().First()]
if !ok {
log.Fatal("No such helper command").Str("name", c.Args().First()).Send()
}
wd, err := os.Getwd()
if err != nil {
log.Fatal("Error getting working directory").Err(err).Send()
}
info, err := distro.ParseOSRelease(ctx)
if err != nil {
log.Fatal("Error getting working directory").Err(err).Send()
}
hc := interp.HandlerContext{
Env: expand.ListEnviron(
"pkgdir="+c.String("dest-dir"),
"DISTRO_ID="+info.ID,
"DISTRO_ID_LIKE="+strings.Join(info.Like, " "),
"ARCH="+cpu.Arch(),
),
Dir: wd,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}
return helper(hc, c.Args().First(), c.Args().Slice()[1:])
},
CustomHelpTemplate: cli.CommandHelpTemplate,
BashComplete: func(ctx *cli.Context) {
for name := range helpers.Helpers {
fmt.Println(name)
}
},
}
var helperListCmd = &cli.Command{
Name: "list",
Usage: "List all the available helper commands",
Aliases: []string{"ls"},
Action: func(ctx *cli.Context) error {
for name := range helpers.Helpers {
fmt.Println(name)
}
return nil
},
}
+67 -54
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -22,72 +22,85 @@ import (
"fmt"
"os"
"go.elara.ws/logger/log"
"github.com/urfave/cli/v2"
"go.elara.ws/lure/distro"
"go.elara.ws/lure/internal/cliutils"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/overrides"
"go.elara.ws/lure/internal/repos"
"lure.sh/lure/internal/cliutils"
"lure.sh/lure/internal/config"
"lure.sh/lure/internal/overrides"
"lure.sh/lure/pkg/distro"
"lure.sh/lure/pkg/loggerctx"
"lure.sh/lure/pkg/repos"
"gopkg.in/yaml.v3"
)
func infoCmd(c *cli.Context) error {
args := c.Args()
if args.Len() < 1 {
log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
}
var infoCmd = &cli.Command{
Name: "info",
Usage: "Print information about a package",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "all",
Aliases: []string{"a"},
Usage: "Show all information, not just for the current distro",
},
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
err := repos.Pull(c.Context, gdb, cfg.Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
found, _, err := repos.FindPkgs(gdb, args.Slice())
if err != nil {
log.Fatal("Error finding packages").Err(err).Send()
}
if len(found) == 0 {
os.Exit(1)
}
pkgs := cliutils.FlattenPkgs(found, "show", c.Bool("interactive"), translator)
var names []string
all := c.Bool("all")
if !all {
info, err := distro.ParseOSRelease(c.Context)
if err != nil {
log.Fatal("Error parsing os-release file").Err(err).Send()
args := c.Args()
if args.Len() < 1 {
log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
}
names, err = overrides.Resolve(
info,
overrides.DefaultOpts.
WithLanguages([]string{config.SystemLang()}),
)
if err != nil {
log.Fatal("Error resolving overrides").Err(err).Send()
}
}
for _, pkg := range pkgs {
err := repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
found, _, err := repos.FindPkgs(ctx, args.Slice())
if err != nil {
log.Fatal("Error finding packages").Err(err).Send()
}
if len(found) == 0 {
os.Exit(1)
}
pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive"))
var names []string
all := c.Bool("all")
if !all {
err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
info, err := distro.ParseOSRelease(ctx)
if err != nil {
log.Fatal("Error encoding script variables").Err(err).Send()
log.Fatal("Error parsing os-release file").Err(err).Send()
}
} else {
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
names, err = overrides.Resolve(
info,
overrides.DefaultOpts.
WithLanguages([]string{config.SystemLang()}),
)
if err != nil {
log.Fatal("Error encoding script variables").Err(err).Send()
log.Fatal("Error resolving overrides").Err(err).Send()
}
}
fmt.Println("---")
}
for _, pkg := range pkgs {
if !all {
err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
if err != nil {
log.Fatal("Error encoding script variables").Err(err).Send()
}
} else {
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
if err != nil {
log.Fatal("Error encoding script variables").Err(err).Send()
}
}
return nil
fmt.Println("---")
}
return nil
},
}
+88 -82
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -19,98 +19,104 @@
package main
import (
"context"
"path/filepath"
"go.elara.ws/logger/log"
"fmt"
"github.com/urfave/cli/v2"
"go.elara.ws/lure/internal/cliutils"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/db"
"go.elara.ws/lure/internal/repos"
"go.elara.ws/lure/manager"
"lure.sh/lure/internal/cliutils"
"lure.sh/lure/internal/config"
"lure.sh/lure/internal/db"
"lure.sh/lure/internal/types"
"lure.sh/lure/pkg/build"
"lure.sh/lure/pkg/loggerctx"
"lure.sh/lure/pkg/manager"
"lure.sh/lure/pkg/repos"
)
func installCmd(c *cli.Context) error {
args := c.Args()
if args.Len() < 1 {
log.Fatalf("Command install expected at least 1 argument, got %d", args.Len()).Send()
}
var installCmd = &cli.Command{
Name: "install",
Usage: "Install a new package",
Aliases: []string{"in"},
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "clean",
Aliases: []string{"c"},
Usage: "Build package from scratch even if there's an already built package available",
},
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect supported package manager on system").Send()
}
err := repos.Pull(c.Context, gdb, cfg.Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
found, notFound, err := repos.FindPkgs(gdb, args.Slice())
if err != nil {
log.Fatal("Error finding packages").Err(err).Send()
}
installPkgs(c.Context, cliutils.FlattenPkgs(found, "install", c.Bool("interactive"), translator), notFound, mgr, c.Bool("clean"), c.Bool("interactive"))
return nil
}
// installPkgs installs non-LURE packages via the package manager, then builds and installs LURE
// packages
func installPkgs(ctx context.Context, pkgs []db.Package, notFound []string, mgr manager.Manager, clean, interactive bool) {
if len(notFound) > 0 {
err := mgr.Install(nil, notFound...)
if err != nil {
log.Fatal("Error installing native packages").Err(err).Send()
}
}
installScripts(ctx, mgr, getScriptPaths(pkgs), clean, interactive)
}
// getScriptPaths generates a slice of script paths corresponding to the
// given packages
func getScriptPaths(pkgs []db.Package) []string {
var scripts []string
for _, pkg := range pkgs {
scriptPath := filepath.Join(config.RepoDir, pkg.Repository, pkg.Name, "lure.sh")
scripts = append(scripts, scriptPath)
}
return scripts
}
// installScripts builds and installs LURE build scripts
func installScripts(ctx context.Context, mgr manager.Manager, scripts []string, clean, interactive bool) {
for _, script := range scripts {
builtPkgs, _, err := buildPackage(ctx, script, mgr, clean, interactive)
if err != nil {
log.Fatal("Error building package").Err(err).Send()
args := c.Args()
if args.Len() < 1 {
log.Fatalf("Command install expected at least 1 argument, got %d", args.Len()).Send()
}
err = mgr.InstallLocal(nil, builtPkgs...)
if err != nil {
log.Fatal("Error installing package").Err(err).Send()
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect a supported package manager on the system").Send()
}
}
err := repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
found, notFound, err := repos.FindPkgs(ctx, args.Slice())
if err != nil {
log.Fatal("Error finding packages").Err(err).Send()
}
pkgs := cliutils.FlattenPkgs(ctx, found, "install", c.Bool("interactive"))
build.InstallPkgs(ctx, pkgs, notFound, types.BuildOpts{
Manager: mgr,
Clean: c.Bool("clean"),
Interactive: c.Bool("interactive"),
})
return nil
},
BashComplete: func(c *cli.Context) {
log := loggerctx.From(c.Context)
result, err := db.GetPkgs(c.Context, "true")
if err != nil {
log.Fatal("Error getting packages").Err(err).Send()
}
defer result.Close()
for result.Next() {
var pkg db.Package
err = result.StructScan(&pkg)
if err != nil {
log.Fatal("Error iterating over packages").Err(err).Send()
}
fmt.Println(pkg.Name)
}
},
}
func removeCmd(c *cli.Context) error {
args := c.Args()
if args.Len() < 1 {
log.Fatalf("Command remove expected at least 1 argument, got %d", args.Len()).Send()
}
var removeCmd = &cli.Command{
Name: "remove",
Usage: "Remove an installed package",
Aliases: []string{"rm"},
Action: func(c *cli.Context) error {
log := loggerctx.From(c.Context)
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect supported package manager on system").Send()
}
args := c.Args()
if args.Len() < 1 {
log.Fatalf("Command remove expected at least 1 argument, got %d", args.Len()).Send()
}
err := mgr.Remove(nil, c.Args().Slice()...)
if err != nil {
log.Fatal("Error removing packages").Err(err).Send()
}
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect a supported package manager on the system").Send()
}
return nil
err := mgr.Remove(nil, c.Args().Slice()...)
if err != nil {
log.Fatal("Error removing packages").Err(err).Send()
}
return nil
},
}
-22
View File
@@ -1,22 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package api
//go:generate protoc --twirp_out=. lure.proto
//go:generate protoc --go_out=. lure.proto
-885
View File
@@ -1,885 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.9
// source: lure.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// SORT_BY represents possible things to sort packages by
type SORT_BY int32
const (
SORT_BY_UNSORTED SORT_BY = 0
SORT_BY_NAME SORT_BY = 1
SORT_BY_REPOSITORY SORT_BY = 2
SORT_BY_VERSION SORT_BY = 3
)
// Enum value maps for SORT_BY.
var (
SORT_BY_name = map[int32]string{
0: "UNSORTED",
1: "NAME",
2: "REPOSITORY",
3: "VERSION",
}
SORT_BY_value = map[string]int32{
"UNSORTED": 0,
"NAME": 1,
"REPOSITORY": 2,
"VERSION": 3,
}
)
func (x SORT_BY) Enum() *SORT_BY {
p := new(SORT_BY)
*p = x
return p
}
func (x SORT_BY) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (SORT_BY) Descriptor() protoreflect.EnumDescriptor {
return file_lure_proto_enumTypes[0].Descriptor()
}
func (SORT_BY) Type() protoreflect.EnumType {
return &file_lure_proto_enumTypes[0]
}
func (x SORT_BY) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use SORT_BY.Descriptor instead.
func (SORT_BY) EnumDescriptor() ([]byte, []int) {
return file_lure_proto_rawDescGZIP(), []int{0}
}
// FILTER_TYPE represents possible filters for packages
type FILTER_TYPE int32
const (
FILTER_TYPE_NO_FILTER FILTER_TYPE = 0
FILTER_TYPE_IN_REPOSITORY FILTER_TYPE = 1
FILTER_TYPE_SUPPORTS_ARCH FILTER_TYPE = 2
)
// Enum value maps for FILTER_TYPE.
var (
FILTER_TYPE_name = map[int32]string{
0: "NO_FILTER",
1: "IN_REPOSITORY",
2: "SUPPORTS_ARCH",
}
FILTER_TYPE_value = map[string]int32{
"NO_FILTER": 0,
"IN_REPOSITORY": 1,
"SUPPORTS_ARCH": 2,
}
)
func (x FILTER_TYPE) Enum() *FILTER_TYPE {
p := new(FILTER_TYPE)
*p = x
return p
}
func (x FILTER_TYPE) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (FILTER_TYPE) Descriptor() protoreflect.EnumDescriptor {
return file_lure_proto_enumTypes[1].Descriptor()
}
func (FILTER_TYPE) Type() protoreflect.EnumType {
return &file_lure_proto_enumTypes[1]
}
func (x FILTER_TYPE) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use FILTER_TYPE.Descriptor instead.
func (FILTER_TYPE) EnumDescriptor() ([]byte, []int) {
return file_lure_proto_rawDescGZIP(), []int{1}
}
// SearchRequest is a request to search for packages
type SearchRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Query string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
Limit int64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
SortBy SORT_BY `protobuf:"varint,3,opt,name=sort_by,json=sortBy,proto3,enum=lure.SORT_BY" json:"sort_by,omitempty"`
FilterType FILTER_TYPE `protobuf:"varint,4,opt,name=filter_type,json=filterType,proto3,enum=lure.FILTER_TYPE" json:"filter_type,omitempty"`
FilterValue *string `protobuf:"bytes,5,opt,name=filter_value,json=filterValue,proto3,oneof" json:"filter_value,omitempty"`
}
func (x *SearchRequest) Reset() {
*x = SearchRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_lure_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SearchRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SearchRequest) ProtoMessage() {}
func (x *SearchRequest) ProtoReflect() protoreflect.Message {
mi := &file_lure_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead.
func (*SearchRequest) Descriptor() ([]byte, []int) {
return file_lure_proto_rawDescGZIP(), []int{0}
}
func (x *SearchRequest) GetQuery() string {
if x != nil {
return x.Query
}
return ""
}
func (x *SearchRequest) GetLimit() int64 {
if x != nil {
return x.Limit
}
return 0
}
func (x *SearchRequest) GetSortBy() SORT_BY {
if x != nil {
return x.SortBy
}
return SORT_BY_UNSORTED
}
func (x *SearchRequest) GetFilterType() FILTER_TYPE {
if x != nil {
return x.FilterType
}
return FILTER_TYPE_NO_FILTER
}
func (x *SearchRequest) GetFilterValue() string {
if x != nil && x.FilterValue != nil {
return *x.FilterValue
}
return ""
}
// StringList contains a list of strings
type StringList struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Entries []string `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"`
}
func (x *StringList) Reset() {
*x = StringList{}
if protoimpl.UnsafeEnabled {
mi := &file_lure_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *StringList) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*StringList) ProtoMessage() {}
func (x *StringList) ProtoReflect() protoreflect.Message {
mi := &file_lure_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use StringList.ProtoReflect.Descriptor instead.
func (*StringList) Descriptor() ([]byte, []int) {
return file_lure_proto_rawDescGZIP(), []int{1}
}
func (x *StringList) GetEntries() []string {
if x != nil {
return x.Entries
}
return nil
}
// Package represents a LURE package
type Package struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"`
Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
Release int64 `protobuf:"varint,4,opt,name=release,proto3" json:"release,omitempty"`
Epoch *int64 `protobuf:"varint,5,opt,name=epoch,proto3,oneof" json:"epoch,omitempty"`
Description *string `protobuf:"bytes,6,opt,name=description,proto3,oneof" json:"description,omitempty"`
Homepage *string `protobuf:"bytes,7,opt,name=homepage,proto3,oneof" json:"homepage,omitempty"`
Maintainer *string `protobuf:"bytes,8,opt,name=maintainer,proto3,oneof" json:"maintainer,omitempty"`
Architectures []string `protobuf:"bytes,9,rep,name=architectures,proto3" json:"architectures,omitempty"`
Licenses []string `protobuf:"bytes,10,rep,name=licenses,proto3" json:"licenses,omitempty"`
Provides []string `protobuf:"bytes,11,rep,name=provides,proto3" json:"provides,omitempty"`
Conflicts []string `protobuf:"bytes,12,rep,name=conflicts,proto3" json:"conflicts,omitempty"`
Replaces []string `protobuf:"bytes,13,rep,name=replaces,proto3" json:"replaces,omitempty"`
Depends map[string]*StringList `protobuf:"bytes,14,rep,name=depends,proto3" json:"depends,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
BuildDepends map[string]*StringList `protobuf:"bytes,15,rep,name=build_depends,json=buildDepends,proto3" json:"build_depends,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (x *Package) Reset() {
*x = Package{}
if protoimpl.UnsafeEnabled {
mi := &file_lure_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Package) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Package) ProtoMessage() {}
func (x *Package) ProtoReflect() protoreflect.Message {
mi := &file_lure_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Package.ProtoReflect.Descriptor instead.
func (*Package) Descriptor() ([]byte, []int) {
return file_lure_proto_rawDescGZIP(), []int{2}
}
func (x *Package) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Package) GetRepository() string {
if x != nil {
return x.Repository
}
return ""
}
func (x *Package) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
func (x *Package) GetRelease() int64 {
if x != nil {
return x.Release
}
return 0
}
func (x *Package) GetEpoch() int64 {
if x != nil && x.Epoch != nil {
return *x.Epoch
}
return 0
}
func (x *Package) GetDescription() string {
if x != nil && x.Description != nil {
return *x.Description
}
return ""
}
func (x *Package) GetHomepage() string {
if x != nil && x.Homepage != nil {
return *x.Homepage
}
return ""
}
func (x *Package) GetMaintainer() string {
if x != nil && x.Maintainer != nil {
return *x.Maintainer
}
return ""
}
func (x *Package) GetArchitectures() []string {
if x != nil {
return x.Architectures
}
return nil
}
func (x *Package) GetLicenses() []string {
if x != nil {
return x.Licenses
}
return nil
}
func (x *Package) GetProvides() []string {
if x != nil {
return x.Provides
}
return nil
}
func (x *Package) GetConflicts() []string {
if x != nil {
return x.Conflicts
}
return nil
}
func (x *Package) GetReplaces() []string {
if x != nil {
return x.Replaces
}
return nil
}
func (x *Package) GetDepends() map[string]*StringList {
if x != nil {
return x.Depends
}
return nil
}
func (x *Package) GetBuildDepends() map[string]*StringList {
if x != nil {
return x.BuildDepends
}
return nil
}
type GetPackageRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"`
}
func (x *GetPackageRequest) Reset() {
*x = GetPackageRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_lure_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetPackageRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetPackageRequest) ProtoMessage() {}
func (x *GetPackageRequest) ProtoReflect() protoreflect.Message {
mi := &file_lure_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetPackageRequest.ProtoReflect.Descriptor instead.
func (*GetPackageRequest) Descriptor() ([]byte, []int) {
return file_lure_proto_rawDescGZIP(), []int{3}
}
func (x *GetPackageRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *GetPackageRequest) GetRepository() string {
if x != nil {
return x.Repository
}
return ""
}
// SearchResponse contains returned packages
type SearchResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Packages []*Package `protobuf:"bytes,1,rep,name=packages,proto3" json:"packages,omitempty"`
}
func (x *SearchResponse) Reset() {
*x = SearchResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_lure_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SearchResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SearchResponse) ProtoMessage() {}
func (x *SearchResponse) ProtoReflect() protoreflect.Message {
mi := &file_lure_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SearchResponse.ProtoReflect.Descriptor instead.
func (*SearchResponse) Descriptor() ([]byte, []int) {
return file_lure_proto_rawDescGZIP(), []int{4}
}
func (x *SearchResponse) GetPackages() []*Package {
if x != nil {
return x.Packages
}
return nil
}
type GetBuildScriptRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"`
}
func (x *GetBuildScriptRequest) Reset() {
*x = GetBuildScriptRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_lure_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetBuildScriptRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetBuildScriptRequest) ProtoMessage() {}
func (x *GetBuildScriptRequest) ProtoReflect() protoreflect.Message {
mi := &file_lure_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetBuildScriptRequest.ProtoReflect.Descriptor instead.
func (*GetBuildScriptRequest) Descriptor() ([]byte, []int) {
return file_lure_proto_rawDescGZIP(), []int{5}
}
func (x *GetBuildScriptRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *GetBuildScriptRequest) GetRepository() string {
if x != nil {
return x.Repository
}
return ""
}
type GetBuildScriptResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Script string `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"`
}
func (x *GetBuildScriptResponse) Reset() {
*x = GetBuildScriptResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_lure_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetBuildScriptResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetBuildScriptResponse) ProtoMessage() {}
func (x *GetBuildScriptResponse) ProtoReflect() protoreflect.Message {
mi := &file_lure_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetBuildScriptResponse.ProtoReflect.Descriptor instead.
func (*GetBuildScriptResponse) Descriptor() ([]byte, []int) {
return file_lure_proto_rawDescGZIP(), []int{6}
}
func (x *GetBuildScriptResponse) GetScript() string {
if x != nil {
return x.Script
}
return ""
}
var File_lure_proto protoreflect.FileDescriptor
var file_lure_proto_rawDesc = []byte{
0x0a, 0x0a, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6c, 0x75,
0x72, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69,
0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74,
0x12, 0x26, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x0d, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x59,
0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x32, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x74,
0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e,
0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45,
0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0c,
0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c,
0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01,
0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe4, 0x05,
0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a,
0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x0a,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61,
0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
0x65, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03,
0x48, 0x00, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b,
0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x48, 0x01, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x68, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67, 0x65, 0x18,
0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x08, 0x68, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67,
0x65, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e,
0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x69, 0x6e,
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x72, 0x63,
0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09,
0x52, 0x0d, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12,
0x1a, 0x0a, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28,
0x09, 0x52, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x6c,
0x69, 0x63, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66,
0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65,
0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65,
0x73, 0x12, 0x34, 0x0a, 0x07, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67,
0x65, 0x2e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07,
0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64,
0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f,
0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75,
0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x1a, 0x4c, 0x0a,
0x0c, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10,
0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x51, 0x0a, 0x11, 0x42,
0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x08,
0x0a, 0x06, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x68, 0x6f, 0x6d,
0x65, 0x70, 0x61, 0x67, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61,
0x69, 0x6e, 0x65, 0x72, 0x22, 0x47, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x61, 0x63, 0x6b, 0x61,
0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a,
0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x3b, 0x0a,
0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x29, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x22, 0x4b, 0x0a, 0x15, 0x47, 0x65,
0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73,
0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x70,
0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x30, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x42, 0x75,
0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2a, 0x3e, 0x0a, 0x07, 0x53, 0x4f, 0x52,
0x54, 0x5f, 0x42, 0x59, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x4e, 0x53, 0x4f, 0x52, 0x54, 0x45, 0x44,
0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a,
0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07,
0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0b, 0x46, 0x49, 0x4c,
0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x46,
0x49, 0x4c, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x5f, 0x52, 0x45,
0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x55,
0x50, 0x50, 0x4f, 0x52, 0x54, 0x53, 0x5f, 0x41, 0x52, 0x43, 0x48, 0x10, 0x02, 0x32, 0xb9, 0x01,
0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x33, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
0x13, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72,
0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x47, 0x65,
0x74, 0x50, 0x6b, 0x67, 0x12, 0x17, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x50,
0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e,
0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x4b, 0x0a, 0x0e,
0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1b,
0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63,
0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x75,
0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2e, 0x2f,
0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_lure_proto_rawDescOnce sync.Once
file_lure_proto_rawDescData = file_lure_proto_rawDesc
)
func file_lure_proto_rawDescGZIP() []byte {
file_lure_proto_rawDescOnce.Do(func() {
file_lure_proto_rawDescData = protoimpl.X.CompressGZIP(file_lure_proto_rawDescData)
})
return file_lure_proto_rawDescData
}
var file_lure_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_lure_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_lure_proto_goTypes = []interface{}{
(SORT_BY)(0), // 0: lure.SORT_BY
(FILTER_TYPE)(0), // 1: lure.FILTER_TYPE
(*SearchRequest)(nil), // 2: lure.SearchRequest
(*StringList)(nil), // 3: lure.StringList
(*Package)(nil), // 4: lure.Package
(*GetPackageRequest)(nil), // 5: lure.GetPackageRequest
(*SearchResponse)(nil), // 6: lure.SearchResponse
(*GetBuildScriptRequest)(nil), // 7: lure.GetBuildScriptRequest
(*GetBuildScriptResponse)(nil), // 8: lure.GetBuildScriptResponse
nil, // 9: lure.Package.DependsEntry
nil, // 10: lure.Package.BuildDependsEntry
}
var file_lure_proto_depIdxs = []int32{
0, // 0: lure.SearchRequest.sort_by:type_name -> lure.SORT_BY
1, // 1: lure.SearchRequest.filter_type:type_name -> lure.FILTER_TYPE
9, // 2: lure.Package.depends:type_name -> lure.Package.DependsEntry
10, // 3: lure.Package.build_depends:type_name -> lure.Package.BuildDependsEntry
4, // 4: lure.SearchResponse.packages:type_name -> lure.Package
3, // 5: lure.Package.DependsEntry.value:type_name -> lure.StringList
3, // 6: lure.Package.BuildDependsEntry.value:type_name -> lure.StringList
2, // 7: lure.API.Search:input_type -> lure.SearchRequest
5, // 8: lure.API.GetPkg:input_type -> lure.GetPackageRequest
7, // 9: lure.API.GetBuildScript:input_type -> lure.GetBuildScriptRequest
6, // 10: lure.API.Search:output_type -> lure.SearchResponse
4, // 11: lure.API.GetPkg:output_type -> lure.Package
8, // 12: lure.API.GetBuildScript:output_type -> lure.GetBuildScriptResponse
10, // [10:13] is the sub-list for method output_type
7, // [7:10] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
7, // [7:7] is the sub-list for extension extendee
0, // [0:7] is the sub-list for field type_name
}
func init() { file_lure_proto_init() }
func file_lure_proto_init() {
if File_lure_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_lure_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SearchRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_lure_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StringList); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_lure_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Package); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_lure_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetPackageRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_lure_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SearchResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_lure_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetBuildScriptRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_lure_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetBuildScriptResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_lure_proto_msgTypes[0].OneofWrappers = []interface{}{}
file_lure_proto_msgTypes[2].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_lure_proto_rawDesc,
NumEnums: 2,
NumMessages: 9,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_lure_proto_goTypes,
DependencyIndexes: file_lure_proto_depIdxs,
EnumInfos: file_lure_proto_enumTypes,
MessageInfos: file_lure_proto_msgTypes,
}.Build()
File_lure_proto = out.File
file_lure_proto_rawDesc = nil
file_lure_proto_goTypes = nil
file_lure_proto_depIdxs = nil
}
-82
View File
@@ -1,82 +0,0 @@
syntax = "proto3";
package lure;
// Slight hack to provide protoc with a package name
option go_package = "../api";
// SORT_BY represents possible things to sort packages by
enum SORT_BY {
UNSORTED = 0;
NAME = 1;
REPOSITORY = 2;
VERSION = 3;
}
// FILTER_TYPE represents possible filters for packages
enum FILTER_TYPE {
NO_FILTER = 0;
IN_REPOSITORY = 1;
SUPPORTS_ARCH = 2;
}
// SearchRequest is a request to search for packages
message SearchRequest {
string query = 1;
int64 limit = 2;
SORT_BY sort_by = 3;
FILTER_TYPE filter_type = 4;
optional string filter_value = 5;
}
// StringList contains a list of strings
message StringList {
repeated string entries = 1;
}
// Package represents a LURE package
message Package {
string name = 1;
string repository = 2;
string version = 3;
int64 release = 4;
optional int64 epoch = 5;
optional string description = 6;
optional string homepage = 7;
optional string maintainer = 8;
repeated string architectures = 9;
repeated string licenses = 10;
repeated string provides = 11;
repeated string conflicts = 12;
repeated string replaces = 13;
map<string, StringList> depends = 14;
map<string, StringList> build_depends = 15;
}
message GetPackageRequest {
string name = 1;
string repository = 2;
}
// SearchResponse contains returned packages
message SearchResponse {
repeated Package packages = 1;
}
message GetBuildScriptRequest {
string name = 1;
string repository = 2;
}
message GetBuildScriptResponse {
string script = 1;
}
// Web is the LURE Web service
service API {
// Search searches through LURE packages in the database
rpc Search(SearchRequest) returns (SearchResponse);
// GetPkg gets a single LURE package from the database
rpc GetPkg(GetPackageRequest) returns (Package);
// GetBuildScript returns the build script for the given package
rpc GetBuildScript(GetBuildScriptRequest) returns (GetBuildScriptResponse);
}
File diff suppressed because it is too large Load Diff
+48 -22
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -19,23 +19,25 @@
package cliutils
import (
"context"
"os"
"strings"
"github.com/AlecAivazis/survey/v2"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/db"
"go.elara.ws/lure/internal/pager"
"go.elara.ws/translate"
"lure.sh/lure/internal/config"
"lure.sh/lure/internal/db"
"lure.sh/lure/internal/pager"
"lure.sh/lure/internal/translations"
"lure.sh/lure/pkg/loggerctx"
)
// YesNoPrompt asks the user a yes or no question, using def as the default answer
func YesNoPrompt(msg string, interactive, def bool, t translate.Translator) (bool, error) {
func YesNoPrompt(ctx context.Context, msg string, interactive, def bool) (bool, error) {
if interactive {
var answer bool
err := survey.AskOne(
&survey.Confirm{
Message: t.TranslateTo(msg, config.Language),
Message: translations.Translator(ctx).TranslateTo(msg, config.Language(ctx)),
Default: def,
},
&answer,
@@ -49,12 +51,15 @@ func YesNoPrompt(msg string, interactive, def bool, t translate.Translator) (boo
// PromptViewScript asks the user if they'd like to see a script,
// shows it if they answer yes, then asks if they'd still like to
// continue, and exits if they answer no.
func PromptViewScript(script, name, style string, interactive bool, t translate.Translator) error {
func PromptViewScript(ctx context.Context, script, name, style string, interactive bool) error {
log := loggerctx.From(ctx)
if !interactive {
return nil
}
view, err := YesNoPrompt(t.TranslateTo("Would you like to view the build script for", config.Language)+" "+name, interactive, false, t)
scriptPrompt := translations.Translator(ctx).TranslateTo("Would you like to view the build script for", config.Language(ctx)) + " " + name
view, err := YesNoPrompt(ctx, scriptPrompt, interactive, false)
if err != nil {
return err
}
@@ -65,13 +70,13 @@ func PromptViewScript(script, name, style string, interactive bool, t translate.
return err
}
cont, err := YesNoPrompt("Would you still like to continue?", interactive, false, t)
cont, err := YesNoPrompt(ctx, "Would you still like to continue?", interactive, false)
if err != nil {
return err
}
if !cont {
log.Fatal(t.TranslateTo("User chose not to continue after reading script", config.Language)).Send()
log.Fatal(translations.Translator(ctx).TranslateTo("User chose not to continue after reading script", config.Language(ctx))).Send()
}
}
@@ -98,15 +103,16 @@ func ShowScript(path, name, style string) error {
// FlattenPkgs attempts to flatten the a map of slices of packages into a single slice
// of packages by prompting the user if multiple packages match.
func FlattenPkgs(found map[string][]db.Package, verb string, interactive bool, t translate.Translator) []db.Package {
func FlattenPkgs(ctx context.Context, found map[string][]db.Package, verb string, interactive bool) []db.Package {
log := loggerctx.From(ctx)
var outPkgs []db.Package
for _, pkgs := range found {
if len(pkgs) > 1 && interactive {
choices, err := PkgPrompt(pkgs, verb, interactive, t)
choice, err := PkgPrompt(ctx, pkgs, verb, interactive)
if err != nil {
log.Fatal("Error prompting for choice of package").Send()
}
outPkgs = append(outPkgs, choices...)
outPkgs = append(outPkgs, choice)
} else if len(pkgs) == 1 || !interactive {
outPkgs = append(outPkgs, pkgs[0])
}
@@ -115,10 +121,9 @@ func FlattenPkgs(found map[string][]db.Package, verb string, interactive bool, t
}
// PkgPrompt asks the user to choose between multiple packages.
// The user may choose multiple packages.
func PkgPrompt(options []db.Package, verb string, interactive bool, t translate.Translator) ([]db.Package, error) {
func PkgPrompt(ctx context.Context, options []db.Package, verb string, interactive bool) (db.Package, error) {
if !interactive {
return []db.Package{options[0]}, nil
return options[0], nil
}
names := make([]string, len(options))
@@ -126,9 +131,30 @@ func PkgPrompt(options []db.Package, verb string, interactive bool, t translate.
names[i] = option.Repository + "/" + option.Name + " " + option.Version
}
prompt := &survey.MultiSelect{
prompt := &survey.Select{
Options: names,
Message: t.TranslateTo("Choose which package(s) to "+verb, config.Language),
Message: translations.Translator(ctx).TranslateTo("Choose which package to "+verb, config.Language(ctx)),
}
var choice int
err := survey.AskOne(prompt, &choice)
if err != nil {
return db.Package{}, err
}
return options[choice], nil
}
// ChooseOptDepends asks the user to choose between multiple optional dependencies.
// The user may choose multiple items.
func ChooseOptDepends(ctx context.Context, options []string, verb string, interactive bool) ([]string, error) {
if !interactive {
return []string{}, nil
}
prompt := &survey.MultiSelect{
Options: options,
Message: translations.Translator(ctx).TranslateTo("Choose which optional package(s) to install", config.Language(ctx)),
}
var choices []int
@@ -137,9 +163,9 @@ func PkgPrompt(options []db.Package, verb string, interactive bool, t translate.
return nil, err
}
out := make([]db.Package, len(choices))
out := make([]string, len(choices))
for i, choiceIndex := range choices {
out[i] = options[choiceIndex]
out[i], _, _ = strings.Cut(options[choiceIndex], ": ")
}
return out, nil
+42 -17
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -19,36 +19,61 @@
package config
import (
"context"
"os"
"sync"
"github.com/pelletier/go-toml/v2"
"go.elara.ws/lure/internal/types"
"lure.sh/lure/internal/types"
"lure.sh/lure/pkg/loggerctx"
)
var defaultConfig = types.Config{
var defaultConfig = &types.Config{
RootCmd: "sudo",
PagerStyle: "native",
IgnorePkgUpdates: []string{},
Repos: []types.Repo{
{
Name: "default",
URL: "https://github.com/Elara6331/lure-repo.git",
URL: "https://github.com/lure-sh/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()
var (
configMtx sync.Mutex
config *types.Config
)
// Write defaults to pointer in case some values are not set in the config
*cfg = defaultConfig
// Set repos to nil so as to avoid a duplicate default
cfg.Repos = nil
return toml.NewDecoder(cfgFl).Decode(cfg)
// Config returns a LURE configuration struct.
// The first time it's called, it'll load the config from a file.
// Subsequent calls will just return the same value.
func Config(ctx context.Context) *types.Config {
configMtx.Lock()
defer configMtx.Unlock()
log := loggerctx.From(ctx)
if config == nil {
cfgFl, err := os.Open(GetPaths(ctx).ConfigPath)
if err != nil {
log.Warn("Error opening config file, using defaults").Err(err).Send()
return defaultConfig
}
defer cfgFl.Close()
// Copy the default configuration into config
defCopy := *defaultConfig
config = &defCopy
config.Repos = nil
err = toml.NewDecoder(cfgFl).Decode(config)
if err != nil {
log.Warn("Error decoding config file, using defaults").Err(err).Send()
// Set config back to nil so that we try again next time
config = nil
return defaultConfig
}
}
return config
}
-94
View File
@@ -1,94 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package config
import (
"os"
"path/filepath"
"github.com/pelletier/go-toml/v2"
"go.elara.ws/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")
fi, err := os.Stat(DBPath)
DBPresent = err == nil && !fi.IsDir()
}
+29 -10
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -19,25 +19,44 @@
package config
import (
"context"
"os"
"strings"
"sync"
"go.elara.ws/logger/log"
"lure.sh/lure/pkg/loggerctx"
"golang.org/x/text/language"
)
var Language language.Tag
var (
langMtx sync.Mutex
lang language.Tag
langSet bool
)
func init() {
lang := SystemLang()
tag, err := language.Parse(lang)
if err != nil {
log.Fatal("Error parsing system language").Err(err).Send()
// Language returns the system language.
// The first time it's called, it'll detect the langauge based on
// the $LANG environment variable.
// Subsequent calls will just return the same value.
func Language(ctx context.Context) language.Tag {
langMtx.Lock()
defer langMtx.Unlock()
log := loggerctx.From(ctx)
if !langSet {
syslang := SystemLang()
tag, err := language.Parse(syslang)
if err != nil {
log.Fatal("Error parsing system language").Err(err).Send()
}
base, _ := tag.Base()
lang = language.Make(base.String())
langSet = true
}
base, _ := tag.Base()
Language = language.Make(base.String())
return lang
}
// SystemLang returns the system language based on
// the $LANG environment variable.
func SystemLang() string {
lang := os.Getenv("LANG")
lang, _, _ = strings.Cut(lang, ".")
+108
View File
@@ -0,0 +1,108 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Elara 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 config
import (
"context"
"os"
"path/filepath"
"sync"
"github.com/pelletier/go-toml/v2"
"lure.sh/lure/pkg/loggerctx"
)
// Paths contains various paths used by LURE
type Paths struct {
ConfigDir string
ConfigPath string
CacheDir string
RepoDir string
PkgsDir string
DBPath string
}
var (
pathsMtx sync.Mutex
paths *Paths
)
// GetPaths returns a Paths struct.
// The first time it's called, it'll generate the struct
// using information from the system.
// Subsequent calls will return the same value.
func GetPaths(ctx context.Context) *Paths {
pathsMtx.Lock()
defer pathsMtx.Unlock()
log := loggerctx.From(ctx)
if paths == nil {
paths = &Paths{}
cfgDir, err := os.UserConfigDir()
if err != nil {
log.Fatal("Unable to detect user config directory").Err(err).Send()
}
paths.ConfigDir = filepath.Join(cfgDir, "lure")
err = os.MkdirAll(paths.ConfigDir, 0o755)
if err != nil {
log.Fatal("Unable to create LURE config directory").Err(err).Send()
}
paths.ConfigPath = filepath.Join(paths.ConfigDir, "lure.toml")
if _, err := os.Stat(paths.ConfigPath); err != nil {
cfgFl, err := os.Create(paths.ConfigPath)
if err != nil {
log.Fatal("Unable to create LURE config file").Err(err).Send()
}
err = toml.NewEncoder(cfgFl).Encode(&defaultConfig)
if err != nil {
log.Fatal("Error encoding default configuration").Err(err).Send()
}
cfgFl.Close()
}
cacheDir, err := os.UserCacheDir()
if err != nil {
log.Fatal("Unable to detect cache directory").Err(err).Send()
}
paths.CacheDir = filepath.Join(cacheDir, "lure")
paths.RepoDir = filepath.Join(paths.CacheDir, "repo")
paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs")
err = os.MkdirAll(paths.RepoDir, 0o755)
if err != nil {
log.Fatal("Unable to create repo cache directory").Err(err).Send()
}
err = os.MkdirAll(paths.PkgsDir, 0o755)
if err != nil {
log.Fatal("Unable to create package cache directory").Err(err).Send()
}
paths.DBPath = filepath.Join(paths.CacheDir, "db")
}
return paths
}
+3 -22
View File
@@ -1,24 +1,5 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package config
import _ "embed"
//go:embed version.txt
var Version string
// Version contains the version of LURE. If the version
// isn't known, it'll be set to "unknown"
var Version = "unknown"
+68 -5
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -21,14 +21,16 @@ package cpu
import (
"os"
"runtime"
"strconv"
"strings"
"golang.org/x/exp/slices"
"golang.org/x/sys/cpu"
)
// ARMVariant checks which variant of ARM lure is running
// armVariant checks which variant of ARM lure is running
// on, by using the same detection method as Go itself
func ARMVariant() string {
func armVariant() string {
armEnv := os.Getenv("LURE_ARM_VARIANT")
// ensure value has "arm" prefix, such as arm5 or arm6
if strings.HasPrefix(armEnv, "arm") {
@@ -46,9 +48,70 @@ func ARMVariant() string {
// Arch returns the canonical CPU architecture of the system
func Arch() string {
arch := runtime.GOARCH
arch := os.Getenv("LURE_ARCH")
if arch == "" {
arch = runtime.GOARCH
}
if arch == "arm" {
arch = ARMVariant()
arch = armVariant()
}
return arch
}
func IsCompatibleWith(target string, list []string) bool {
if target == "all" || slices.Contains(list, "all") {
return true
}
for _, arch := range list {
if strings.HasPrefix(target, "arm") && strings.HasPrefix(arch, "arm") {
targetVer, err := getARMVersion(target)
if err != nil {
return false
}
archVer, err := getARMVersion(arch)
if err != nil {
return false
}
if targetVer >= archVer {
return true
}
}
if target == arch {
return true
}
}
return false
}
func CompatibleArches(arch string) ([]string, error) {
if strings.HasPrefix(arch, "arm") {
ver, err := getARMVersion(arch)
if err != nil {
return nil, err
}
if ver > 5 {
var out []string
for i := ver; i >= 5; i-- {
out = append(out, "arm"+strconv.Itoa(i))
}
return out, nil
}
}
return []string{arch}, nil
}
func getARMVersion(arch string) (int, error) {
// Extract the version number from ARM architecture
version := strings.TrimPrefix(arch, "arm")
if version == "" {
return 5, nil // Default to arm5 if version is not specified
}
return strconv.Atoi(version)
}
+111 -64
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -19,24 +19,27 @@
package db
import (
"context"
"database/sql"
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"os"
"sync"
"github.com/jmoiron/sqlx"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/config"
"lure.sh/lure/internal/config"
"lure.sh/lure/pkg/loggerctx"
"golang.org/x/exp/slices"
"modernc.org/sqlite"
)
const CurrentVersion = 1
// CurrentVersion is the current version of the database.
// The database is reset if its version doesn't match this.
const CurrentVersion = 2
func init() {
sqlite.MustRegisterScalarFunction("json_array_contains", 2, JsonArrayContains)
sqlite.MustRegisterScalarFunction("json_array_contains", 2, jsonArrayContains)
}
// Package is a LURE package's database representation
@@ -55,6 +58,7 @@ type Package struct {
Replaces JSON[[]string] `sh:"replaces" db:"replaces"`
Depends JSON[map[string][]string] `db:"depends"`
BuildDepends JSON[map[string][]string] `db:"builddepends"`
OptDepends JSON[map[string][]string] `db:"optdepends"`
Repository string `db:"repository"`
}
@@ -62,40 +66,67 @@ type version struct {
Version int `db:"version"`
}
func Open(dsn string) (*sqlx.DB, error) {
if dsn != ":memory:" {
fi, err := os.Stat(config.DBPath)
if err == nil {
// TODO: This should be removed by the first stable release.
if fi.IsDir() {
log.Warn("Your database is using the old database engine; rebuilding").Send()
var (
mu sync.Mutex
conn *sqlx.DB
closed = true
)
err = os.RemoveAll(config.DBPath)
if err != nil {
log.Fatal("Error removing old database").Err(err).Send()
}
config.DBPresent = false
}
}
// DB returns the LURE database.
// The first time it's called, it opens the SQLite database file.
// Subsequent calls return the same connection.
func DB(ctx context.Context) *sqlx.DB {
log := loggerctx.From(ctx)
if conn != nil && !closed {
return getConn()
}
db, err := sqlx.Open("sqlite", dsn)
_, err := open(ctx, config.GetPaths(ctx).DBPath)
if err != nil {
log.Fatal("Error opening database").Err(err).Send()
}
return getConn()
}
err = Init(db, dsn)
func getConn() *sqlx.DB {
mu.Lock()
defer mu.Unlock()
return conn
}
func open(ctx context.Context, dsn string) (*sqlx.DB, error) {
db, err := sqlx.Open("sqlite", dsn)
if err != nil {
log.Fatal("Error initializing database").Err(err).Send()
return nil, err
}
mu.Lock()
conn = db
closed = false
mu.Unlock()
err = initDB(ctx, dsn)
if err != nil {
return nil, err
}
return db, nil
}
// Init initializes the database
func Init(db *sqlx.DB, dsn string) error {
*db = *db.Unsafe()
_, err := db.Exec(`
// Close closes the database
func Close() error {
closed = true
if conn != nil {
return conn.Close()
} else {
return nil
}
}
// initDB initializes the database
func initDB(ctx context.Context, dsn string) error {
log := loggerctx.From(ctx)
conn = conn.Unsafe()
_, err := conn.ExecContext(ctx, `
CREATE TABLE IF NOT EXISTS pkgs (
name TEXT NOT NULL,
repository TEXT NOT NULL,
@@ -112,6 +143,7 @@ func Init(db *sqlx.DB, dsn string) error {
replaces TEXT CHECK(replaces = 'null' OR (JSON_VALID(replaces) AND JSON_TYPE(replaces) = 'array')),
depends TEXT CHECK(depends = 'null' OR (JSON_VALID(depends) AND JSON_TYPE(depends) = 'object')),
builddepends TEXT CHECK(builddepends = 'null' OR (JSON_VALID(builddepends) AND JSON_TYPE(builddepends) = 'object')),
optdepends TEXT CHECK(optdepends = 'null' OR (JSON_VALID(optdepends) AND JSON_TYPE(optdepends) = 'object')),
UNIQUE(name, repository)
);
@@ -123,49 +155,58 @@ func Init(db *sqlx.DB, dsn string) error {
return err
}
ver, ok := GetVersion(db)
if !ok {
ver, ok := GetVersion(ctx)
if ok && ver != CurrentVersion {
log.Warn("Database version mismatch; resetting").Int("version", ver).Int("expected", CurrentVersion).Send()
reset(ctx)
return initDB(ctx, dsn)
} else if !ok {
log.Warn("Database version does not exist. Run lure fix if something isn't working.").Send()
return addVersion(db, CurrentVersion)
}
if ver != CurrentVersion {
log.Warn("Database version mismatch; rebuilding").Int("version", ver).Int("expected", CurrentVersion).Send()
db.Close()
err = os.Remove(config.DBPath)
if err != nil {
return err
}
config.DBPresent = false
tdb, err := Open(dsn)
if err != nil {
return err
}
*db = *tdb
return addVersion(ctx, CurrentVersion)
}
return nil
}
func GetVersion(db *sqlx.DB) (int, bool) {
// reset drops all the database tables
func reset(ctx context.Context) error {
_, err := DB(ctx).ExecContext(ctx, "DROP TABLE IF EXISTS pkgs;")
if err != nil {
return err
}
_, err = DB(ctx).ExecContext(ctx, "DROP TABLE IF EXISTS lure_db_version;")
return err
}
// IsEmpty returns true if the database has no packages in it, otherwise it returns false.
func IsEmpty(ctx context.Context) bool {
var count int
err := DB(ctx).GetContext(ctx, &count, "SELECT count(1) FROM pkgs;")
if err != nil {
return true
}
return count == 0
}
// GetVersion returns the database version and a boolean indicating
// whether the database contained a version number
func GetVersion(ctx context.Context) (int, bool) {
var ver version
err := db.Get(&ver, "SELECT * FROM lure_db_version LIMIT 1;")
err := DB(ctx).GetContext(ctx, &ver, "SELECT * FROM lure_db_version LIMIT 1;")
if err != nil {
return 0, false
}
return ver.Version, true
}
func addVersion(db *sqlx.DB, ver int) error {
_, err := db.Exec(`INSERT INTO lure_db_version(version) VALUES (?);`, ver)
func addVersion(ctx context.Context, ver int) error {
_, err := DB(ctx).ExecContext(ctx, `INSERT INTO lure_db_version(version) VALUES (?);`, ver)
return err
}
// InsertPackage adds a package to the database
func InsertPackage(db *sqlx.DB, pkg Package) error {
_, err := db.NamedExec(`
func InsertPackage(ctx context.Context, pkg Package) error {
_, err := DB(ctx).NamedExecContext(ctx, `
INSERT OR REPLACE INTO pkgs (
name,
repository,
@@ -181,7 +222,8 @@ func InsertPackage(db *sqlx.DB, pkg Package) error {
conflicts,
replaces,
depends,
builddepends
builddepends,
optdepends
) VALUES (
:name,
:repository,
@@ -197,35 +239,38 @@ func InsertPackage(db *sqlx.DB, pkg Package) error {
:conflicts,
:replaces,
:depends,
:builddepends
:builddepends,
:optdepends
);
`, pkg)
return err
}
// GetPkgs returns a result containing packages that match the where conditions
func GetPkgs(db *sqlx.DB, where string, args ...any) (*sqlx.Rows, error) {
stream, err := db.Queryx("SELECT * FROM pkgs WHERE "+where, args...)
func GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) {
stream, err := DB(ctx).QueryxContext(ctx, "SELECT * FROM pkgs WHERE "+where, args...)
if err != nil {
return nil, err
}
return stream, nil
}
// GetPkg returns a single package that match the where conditions
func GetPkg(db *sqlx.DB, where string, args ...any) (*Package, error) {
// GetPkg returns a single package that matches the where conditions
func GetPkg(ctx context.Context, where string, args ...any) (*Package, error) {
out := &Package{}
err := db.Get(out, "SELECT * FROM pkgs WHERE "+where+" LIMIT 1", args...)
err := DB(ctx).GetContext(ctx, out, "SELECT * FROM pkgs WHERE "+where+" LIMIT 1", args...)
return out, err
}
// DeletePkgs deletes all packages matching the where conditions
func DeletePkgs(db *sqlx.DB, where string, args ...any) error {
_, err := db.Exec("DELETE FROM pkgs WHERE "+where, args...)
func DeletePkgs(ctx context.Context, where string, args ...any) error {
_, err := DB(ctx).ExecContext(ctx, "DELETE FROM pkgs WHERE "+where, args...)
return err
}
func JsonArrayContains(ctx *sqlite.FunctionContext, args []driver.Value) (driver.Value, error) {
// jsonArrayContains is an SQLite function that checks if a JSON array
// in the database contains a given value
func jsonArrayContains(ctx *sqlite.FunctionContext, args []driver.Value) (driver.Value, error) {
value, ok := args[0].(string)
if !ok {
return nil, errors.New("both arguments to json_array_contains must be strings")
@@ -245,10 +290,12 @@ func JsonArrayContains(ctx *sqlite.FunctionContext, args []driver.Value) (driver
return slices.Contains(array, item), nil
}
// JSON represents a JSON value in the database
type JSON[T any] struct {
Val T
}
// NewJSON creates a new database JSON value
func NewJSON[T any](v T) JSON[T] {
return JSON[T]{Val: v}
}
+34 -39
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -24,7 +24,7 @@ import (
"testing"
"github.com/jmoiron/sqlx"
"go.elara.ws/lure/internal/db"
"lure.sh/lure/internal/db"
)
var testPkg = db.Package{
@@ -37,11 +37,11 @@ var testPkg = db.Package{
"ru": "Проверочный пакет",
}),
Homepage: db.NewJSON(map[string]string{
"en": "https://lure.arsenm.dev",
"en": "https://lure.sh/",
}),
Maintainer: db.NewJSON(map[string]string{
"en": "Arsen Musayelyan <arsen@arsenm.dev>",
"ru": "Арсен Мусаелян <arsen@arsenm.dev>",
"en": "Elara Musayelyan <elara@elara.ws>",
"ru": "Элара Мусаелян <elara@elara.ws>",
}),
Architectures: db.NewJSON([]string{"arm64", "amd64"}),
Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}),
@@ -59,23 +59,18 @@ var testPkg = db.Package{
}
func TestInit(t *testing.T) {
gdb, err := sqlx.Open("sqlite", ":memory:")
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer gdb.Close()
defer db.Close()
err = db.Init(gdb, ":memory:")
_, err = db.DB().Exec("SELECT * FROM pkgs")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
_, err = gdb.Exec("SELECT * FROM pkgs")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
ver, ok := db.GetVersion(gdb)
ver, ok := db.GetVersion()
if !ok {
t.Errorf("Expected version to be present")
} else if ver != db.CurrentVersion {
@@ -84,19 +79,19 @@ func TestInit(t *testing.T) {
}
func TestInsertPackage(t *testing.T) {
gdb, err := db.Open(":memory:")
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer gdb.Close()
defer db.Close()
err = db.InsertPackage(gdb, testPkg)
err = db.InsertPackage(testPkg)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
dbPkg := db.Package{}
err = sqlx.Get(gdb, &dbPkg, "SELECT * FROM pkgs WHERE name = 'test' AND repository = 'default'")
err = sqlx.Get(db.DB(), &dbPkg, "SELECT * FROM pkgs WHERE name = 'test' AND repository = 'default'")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -107,28 +102,28 @@ func TestInsertPackage(t *testing.T) {
}
func TestGetPkgs(t *testing.T) {
gdb, err := db.Open(":memory:")
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer gdb.Close()
defer db.Close()
x1 := testPkg
x1.Name = "x1"
x2 := testPkg
x2.Name = "x2"
err = db.InsertPackage(gdb, x1)
err = db.InsertPackage(x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.InsertPackage(gdb, x2)
err = db.InsertPackage(x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
result, err := db.GetPkgs(gdb, "name LIKE 'x%'")
result, err := db.GetPkgs("name LIKE 'x%'")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -147,28 +142,28 @@ func TestGetPkgs(t *testing.T) {
}
func TestGetPkg(t *testing.T) {
gdb, err := db.Open(":memory:")
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer gdb.Close()
defer db.Close()
x1 := testPkg
x1.Name = "x1"
x2 := testPkg
x2.Name = "x2"
err = db.InsertPackage(gdb, x1)
err = db.InsertPackage(x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.InsertPackage(gdb, x2)
err = db.InsertPackage(x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
pkg, err := db.GetPkg(gdb, "name LIKE 'x%' ORDER BY name")
pkg, err := db.GetPkg("name LIKE 'x%' ORDER BY name")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -183,34 +178,34 @@ func TestGetPkg(t *testing.T) {
}
func TestDeletePkgs(t *testing.T) {
gdb, err := db.Open(":memory:")
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer gdb.Close()
defer db.Close()
x1 := testPkg
x1.Name = "x1"
x2 := testPkg
x2.Name = "x2"
err = db.InsertPackage(gdb, x1)
err = db.InsertPackage(x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.InsertPackage(gdb, x2)
err = db.InsertPackage(x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.DeletePkgs(gdb, "name = 'x1'")
err = db.DeletePkgs("name = 'x1'")
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
var dbPkg db.Package
err = gdb.Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;")
err = db.DB().Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;")
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
@@ -221,11 +216,11 @@ func TestDeletePkgs(t *testing.T) {
}
func TestJsonArrayContains(t *testing.T) {
gdb, err := db.Open(":memory:")
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer gdb.Close()
defer db.Close()
x1 := testPkg
x1.Name = "x1"
@@ -233,18 +228,18 @@ func TestJsonArrayContains(t *testing.T) {
x2.Name = "x2"
x2.Provides.Val = append(x2.Provides.Val, "x")
err = db.InsertPackage(gdb, x1)
err = db.InsertPackage(x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.InsertPackage(gdb, x2)
err = db.InsertPackage(x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
var dbPkg db.Package
err = gdb.Get(&dbPkg, "SELECT * FROM pkgs WHERE json_array_contains(provides, 'x');")
err = db.DB().Get(&dbPkg, "SELECT * FROM pkgs WHERE json_array_contains(provides, 'x');")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
+82 -28
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -22,27 +22,41 @@ package dl
import (
"context"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"errors"
"fmt"
"hash"
"io"
"os"
"path/filepath"
"strings"
"github.com/PuerkitoBio/purell"
"github.com/vmihailenco/msgpack/v5"
"go.elara.ws/logger/log"
"go.elara.ws/lure/internal/dlcache"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s"
"golang.org/x/exp/slices"
"lure.sh/lure/internal/dlcache"
"lure.sh/lure/pkg/loggerctx"
)
const manifestFileName = ".lure_cache_manifest"
// ErrChecksumMismatch occurs when the checksum of a downloaded file
// does not match the expected checksum provided in the Options struct.
var ErrChecksumMismatch = errors.New("dl: checksums did not match")
var (
ErrChecksumMismatch = errors.New("dl: checksums did not match")
ErrNoSuchHashAlgo = errors.New("dl: invalid hashing algorithm")
)
// Downloaders contains all the downloaders in the order in which
// they should be checked
var Downloaders = []Downloader{
GitDownloader{},
TorrentDownloader{},
FileDownloader{},
}
@@ -67,13 +81,42 @@ func (t Type) String() string {
// Options contains the options for downloading
// files and directories
type Options struct {
SHA256 []byte
Hash []byte
HashAlgorithm string
Name string
URL string
Destination string
CacheDisabled bool
PostprocDisabled bool
Progress io.Writer
LocalDir string
}
func (opts Options) NewHash() (hash.Hash, error) {
switch opts.HashAlgorithm {
case "", "sha256":
return sha256.New(), nil
case "sha224":
return sha256.New224(), nil
case "sha512":
return sha512.New(), nil
case "sha384":
return sha512.New384(), nil
case "sha1":
return sha1.New(), nil
case "md5":
return md5.New(), nil
case "blake2s-128":
return blake2s.New256(nil)
case "blake2s-256":
return blake2s.New256(nil)
case "blake2b-256":
return blake2b.New(32, nil)
case "blake2b-512":
return blake2b.New(64, nil)
default:
return nil, fmt.Errorf("%w: %s", ErrNoSuchHashAlgo, opts.HashAlgorithm)
}
}
// Manifest holds information about the type and name
@@ -120,6 +163,7 @@ type UpdatingDownloader interface {
// it downloads the source to a new cache directory and links it
// to the destination.
func Download(ctx context.Context, opts Options) (err error) {
log := loggerctx.From(ctx)
normalized, err := normalizeURL(opts.URL)
if err != nil {
return err
@@ -134,17 +178,20 @@ func Download(ctx context.Context, opts Options) (err error) {
}
var t Type
cacheDir, ok := dlcache.Get(opts.URL)
cacheDir, ok := dlcache.Get(ctx, opts.URL)
if ok {
var updated bool
if d, ok := d.(UpdatingDownloader); ok {
log.Info("Source can be updated, updating if required").Str("source", opts.Name).Str("downloader", d.Name()).Send()
updated, err = d.Update(Options{
Name: opts.Name,
URL: opts.URL,
Destination: cacheDir,
Progress: opts.Progress,
Hash: opts.Hash,
HashAlgorithm: opts.HashAlgorithm,
Name: opts.Name,
URL: opts.URL,
Destination: cacheDir,
Progress: opts.Progress,
LocalDir: opts.LocalDir,
})
if err != nil {
return err
@@ -156,15 +203,16 @@ func Download(ctx context.Context, opts Options) (err error) {
t = m.Type
dest := filepath.Join(opts.Destination, m.Name)
ok, err := handleCache(cacheDir, dest, t)
ok, err := handleCache(cacheDir, dest, m.Name, t)
if err != nil {
return err
}
if ok && !updated {
log.Info("Source found in cache, linked to destination").Str("source", opts.Name).Stringer("type", t).Send()
log.Info("Source found in cache and linked to destination").Str("source", opts.Name).Stringer("type", t).Send()
return nil
} else if ok {
log.Info("Source updated and linked to destination").Str("source", opts.Name).Stringer("type", t).Send()
return nil
}
} else {
@@ -180,16 +228,19 @@ func Download(ctx context.Context, opts Options) (err error) {
log.Info("Downloading source").Str("source", opts.Name).Str("downloader", d.Name()).Send()
cacheDir, err = dlcache.New(opts.URL)
cacheDir, err = dlcache.New(ctx, opts.URL)
if err != nil {
return err
}
t, name, err := d.Download(Options{
Name: opts.Name,
URL: opts.URL,
Destination: cacheDir,
Progress: opts.Progress,
Hash: opts.Hash,
HashAlgorithm: opts.HashAlgorithm,
Name: opts.Name,
URL: opts.URL,
Destination: cacheDir,
Progress: opts.Progress,
LocalDir: opts.LocalDir,
})
if err != nil {
return err
@@ -201,7 +252,7 @@ func Download(ctx context.Context, opts Options) (err error) {
}
dest := filepath.Join(opts.Destination, name)
_, err = handleCache(cacheDir, dest, t)
_, err = handleCache(cacheDir, dest, name, t)
return err
}
@@ -228,7 +279,7 @@ func getManifest(cacheDir string) (m Manifest, err error) {
}
// handleCache links the cache directory or a file within it to the destination
func handleCache(cacheDir, dest string, t Type) (bool, error) {
func handleCache(cacheDir, dest, name string, t Type) (bool, error) {
switch t {
case TypeFile:
cd, err := os.Open(cacheDir)
@@ -236,7 +287,7 @@ func handleCache(cacheDir, dest string, t Type) (bool, error) {
return false, err
}
names, err := cd.Readdirnames(2)
names, err := cd.Readdirnames(0)
if err == io.EOF {
break
} else if err != nil {
@@ -245,17 +296,13 @@ func handleCache(cacheDir, dest string, t Type) (bool, error) {
cd.Close()
for _, name := range names {
if name == manifestFileName {
continue
}
err = os.Link(filepath.Join(cacheDir, names[0]), filepath.Join(dest, filepath.Base(names[0])))
if slices.Contains(names, name) {
err = os.Link(filepath.Join(cacheDir, name), dest)
if err != nil {
return false, err
}
return true, nil
}
return true, nil
case TypeDir:
err := linkDir(cacheDir, dest)
if err != nil {
@@ -321,5 +368,12 @@ func normalizeURL(u string) (string, error) {
purell.FlagDecodeUnnecessaryEscapes |
purell.FlagRemoveEmptyPortSeparator
return purell.NormalizeURLString(u, normalizationFlags)
u, err := purell.NormalizeURLString(u, normalizationFlags)
if err != nil {
return "", err
}
// Fix magnet URLs after normalization
u = strings.Replace(u, "magnet://", "magnet:", 1)
return u, nil
}
+53 -32
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -21,20 +21,19 @@ package dl
import (
"bytes"
"context"
"crypto/sha256"
"io"
"mime"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/mholt/archiver/v4"
"github.com/schollz/progressbar/v3"
"go.elara.ws/lure/internal/shutils"
"lure.sh/lure/internal/shutils/handlers"
)
// FileDownloader downloads files using HTTP
@@ -69,14 +68,34 @@ func (FileDownloader) Download(opts Options) (Type, string, error) {
u.RawQuery = query.Encode()
res, err := http.Get(u.String())
if err != nil {
return 0, "", err
}
if name == "" {
name = getFilename(res)
var r io.ReadCloser
var size int64
if u.Scheme == "local" {
localFl, err := os.Open(filepath.Join(opts.LocalDir, u.Path))
if err != nil {
return 0, "", err
}
fi, err := localFl.Stat()
if err != nil {
return 0, "", err
}
size = fi.Size()
if name == "" {
name = fi.Name()
}
r = localFl
} else {
res, err := http.Get(u.String())
if err != nil {
return 0, "", err
}
size = res.ContentLength
if name == "" {
name = getFilename(res)
}
r = res.Body
}
defer r.Close()
opts.PostprocDisabled = archive == "false"
@@ -90,7 +109,7 @@ func (FileDownloader) Download(opts Options) (Type, string, error) {
var bar io.WriteCloser
if opts.Progress != nil {
bar = progressbar.NewOptions64(
res.ContentLength,
size,
progressbar.OptionSetDescription(name),
progressbar.OptionSetWriter(opts.Progress),
progressbar.OptionShowBytes(true),
@@ -106,33 +125,36 @@ func (FileDownloader) Download(opts Options) (Type, string, error) {
)
defer bar.Close()
} else {
bar = shutils.NopRWC{}
bar = handlers.NopRWC{}
}
h := sha256.New()
h, err := opts.NewHash()
if err != nil {
return 0, "", err
}
var w io.Writer
if opts.SHA256 != nil {
if opts.Hash != nil {
w = io.MultiWriter(fl, h, bar)
} else {
w = io.MultiWriter(fl, bar)
}
_, err = io.Copy(w, res.Body)
_, err = io.Copy(w, r)
if err != nil {
return 0, "", err
}
res.Body.Close()
r.Close()
if opts.SHA256 != nil {
if opts.Hash != nil {
sum := h.Sum(nil)
if !bytes.Equal(sum, opts.SHA256) {
if !bytes.Equal(sum, opts.Hash) {
return 0, "", ErrChecksumMismatch
}
}
if opts.PostprocDisabled {
return TypeFile, "", nil
return TypeFile, name, nil
}
_, err = fl.Seek(0, io.SeekStart)
@@ -140,14 +162,14 @@ func (FileDownloader) Download(opts Options) (Type, string, error) {
return 0, "", err
}
format, r, err := archiver.Identify(name, fl)
format, ar, err := archiver.Identify(name, fl)
if err == archiver.ErrNoMatch {
return TypeFile, "", nil
return TypeFile, name, nil
} else if err != nil {
return 0, "", err
}
err = extractFile(r, format, name, opts)
err = extractFile(ar, format, name, opts)
if err != nil {
return 0, "", err
}
@@ -225,19 +247,18 @@ func extractFile(r io.Reader, format archiver.Format, name string, opts Options)
return nil
}
var cdHeaderRgx = regexp.MustCompile(`filename="(.+)"`)
// getFilename attempts to parse the Content-Disposition
// HTTP response header and extract a filename. If the
// header does not exist, it will use the last element
// of the path.
func getFilename(res *http.Response) (name string) {
cd := res.Header.Get("Content-Disposition")
matches := cdHeaderRgx.FindStringSubmatch(cd)
if len(matches) > 1 {
name = matches[1]
} else {
name = path.Base(res.Request.URL.Path)
_, params, err := mime.ParseMediaType(res.Header.Get("Content-Disposition"))
if err != nil {
return path.Base(res.Request.URL.Path)
}
if filename, ok := params["filename"]; ok {
return filename
} else {
return path.Base(res.Request.URL.Path)
}
return name
}
+11 -1
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -177,6 +177,9 @@ func (GitDownloader) Update(opts Options) (bool, error) {
po.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth
}
m, err := getManifest(opts.Destination)
manifestOK := err == nil
err = w.Pull(po)
if errors.Is(err, git.NoErrAlreadyUpToDate) {
return false, nil
@@ -184,5 +187,12 @@ func (GitDownloader) Update(opts Options) (bool, error) {
return false, err
}
if manifestOK {
err = writeManifest(opts.Destination, m)
if err != nil {
return true, err
}
}
return true, nil
}
+90
View File
@@ -0,0 +1,90 @@
package dl
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
)
var (
urlMatchRegex = regexp.MustCompile(`(magnet|torrent\+https?):.*`)
ErrAria2NotFound = errors.New("aria2 must be installed for torrent functionality")
ErrDestinationEmpty = errors.New("the destination directory is empty")
)
type TorrentDownloader struct{}
// Name always returns "file"
func (TorrentDownloader) Name() string {
return "torrent"
}
// MatchURL returns true if the URL is a magnet link
// or an http(s) link with a "torrent+" prefix
func (TorrentDownloader) MatchURL(u string) bool {
return urlMatchRegex.MatchString(u)
}
// Download downloads a file over the BitTorrent protocol.
func (TorrentDownloader) Download(opts Options) (Type, string, error) {
aria2Path, err := exec.LookPath("aria2c")
if err != nil {
return 0, "", ErrAria2NotFound
}
opts.URL = strings.TrimPrefix(opts.URL, "torrent+")
cmd := exec.Command(aria2Path, "--summary-interval=0", "--log-level=warn", "--seed-time=0", "--dir="+opts.Destination, opts.URL)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return 0, "", fmt.Errorf("aria2c returned an error: %w", err)
}
err = removeTorrentFiles(opts.Destination)
if err != nil {
return 0, "", err
}
return determineType(opts.Destination)
}
func removeTorrentFiles(path string) error {
filePaths, err := filepath.Glob(filepath.Join(path, "*.torrent"))
if err != nil {
return err
}
for _, filePath := range filePaths {
err = os.Remove(filePath)
if err != nil {
return err
}
}
return nil
}
func determineType(path string) (Type, string, error) {
files, err := os.ReadDir(path)
if err != nil {
return 0, "", err
}
if len(files) > 1 {
return TypeDir, "", nil
} else if len(files) == 1 {
if files[0].IsDir() {
return TypeDir, files[0].Name(), nil
} else {
return TypeFile, files[0].Name(), nil
}
}
return 0, "", ErrDestinationEmpty
}
+11 -8
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -19,27 +19,30 @@
package dlcache
import (
"context"
"crypto/sha1"
"encoding/hex"
"io"
"os"
"path/filepath"
"go.elara.ws/lure/internal/config"
"lure.sh/lure/internal/config"
)
// BasePath stores the base path to the download cache
var BasePath = filepath.Join(config.CacheDir, "dl")
// BasePath returns the base path of the download cache
func BasePath(ctx context.Context) string {
return filepath.Join(config.GetPaths(ctx).CacheDir, "dl")
}
// New creates a new directory with the given ID in the cache.
// If a directory with the same ID already exists,
// it will be deleted before creating a new one.
func New(id string) (string, error) {
func New(ctx context.Context, id string) (string, error) {
h, err := hashID(id)
if err != nil {
return "", err
}
itemPath := filepath.Join(BasePath, h)
itemPath := filepath.Join(BasePath(ctx), h)
fi, err := os.Stat(itemPath)
if err == nil || (fi != nil && !fi.IsDir()) {
@@ -62,12 +65,12 @@ func New(id string) (string, error) {
// returns the directory and true. If it
// does not exist, it returns an empty string
// and false.
func Get(id string) (string, bool) {
func Get(ctx context.Context, id string) (string, bool) {
h, err := hashID(id)
if err != nil {
return "", false
}
itemPath := filepath.Join(BasePath, h)
itemPath := filepath.Join(BasePath(ctx), h)
_, err = os.Stat(itemPath)
if err != nil {
+6 -4
View File
@@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara 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
@@ -19,6 +19,7 @@
package dlcache_test
import (
"context"
"crypto/sha1"
"encoding/hex"
"io"
@@ -26,7 +27,8 @@ import (
"path/filepath"
"testing"
"go.elara.ws/lure/internal/dlcache"
"lure.sh/lure/internal/config"
"lure.sh/lure/internal/dlcache"
)
func init() {
@@ -34,7 +36,7 @@ func init() {
if err != nil {
panic(err)
}
dlcache.BasePath = dir
config.GetPaths(context.Background()).RepoDir = dir
}
func TestNew(t *testing.T) {
@@ -44,7 +46,7 @@ func TestNew(t *testing.T) {
t.Errorf("Expected no error, got %s", err)
}
exp := filepath.Join(dlcache.BasePath, sha1sum(id))
exp := filepath.Join(dlcache.BasePath(), sha1sum(id))
if dir != exp {
t.Errorf("Expected %s, got %s", exp, dir)
}
+110
View File
@@ -0,0 +1,110 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Elara 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 osutils
import (
"io"
"os"
"path/filepath"
)
// Move attempts to use os.Rename and if that fails (such as for a cross-device move),
// it instead copies the source to the destination and then removes the source.
func Move(sourcePath, destPath string) error {
// Try to rename the source to the destination
err := os.Rename(sourcePath, destPath)
if err == nil {
return nil // Successful move
}
// Rename failed, so copy the source to the destination
err = copyDirOrFile(sourcePath, destPath)
if err != nil {
return err
}
// Copy successful, remove the original source
err = os.RemoveAll(sourcePath)
if err != nil {
return err
}
return nil
}
func copyDirOrFile(sourcePath, destPath string) error {
sourceInfo, err := os.Stat(sourcePath)
if err != nil {
return err
}
if sourceInfo.IsDir() {
return copyDir(sourcePath, destPath, sourceInfo)
} else if sourceInfo.Mode().IsRegular() {
return copyFile(sourcePath, destPath, sourceInfo)
} else {
// ignore non-regular files
return nil
}
}
func copyDir(sourcePath, destPath string, sourceInfo os.FileInfo) error {
err := os.Mk