Compare commits
60 Commits
8dbdd3edc4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 5999c1c8e6 | |||
| 5dc31f43aa | |||
| 5569841ee2 | |||
| 2b1b1deace | |||
| edad7b03b2 | |||
| ef923016e4 | |||
| fd145e4751 | |||
| 4218912123 | |||
| c98741b8f2 | |||
| 26654ae26e | |||
| 484c8ba055 | |||
| 6effa2d8fe | |||
| f421f40fdf | |||
| 602a558ab1 | |||
| 564f3c9b05 | |||
| 09ff697b71 | |||
| 3b382b9747 | |||
| 5d49de6fde | |||
| 1133785dbd | |||
| f32dddee63 | |||
| ba1a39874e | |||
| 00406493c2 | |||
| d9659dab9c | |||
| 046db8bf1c | |||
| 94bdf8275a | |||
| 3f3b575b63 | |||
| 7a9cea9702 | |||
| 0c6cdadd82 | |||
| e20ed6b5eb | |||
| ae99f4a136 | |||
| dd216e8707 | |||
| 1fdb399ba3 | |||
| 7598122780 | |||
| 04c7ad4476 | |||
| f521dc7136 | |||
| c6c8828257 | |||
| dbdaaa4184 | |||
| e3a838f312 | |||
| 383b886472 | |||
| b758eb39f0 | |||
| bb1227eadb | |||
| 410e005458 | |||
| 6b236f6240 | |||
| eb8dd3ad35 | |||
| 88bd90ef89 | |||
| cf932e6691 | |||
| 177960431c | |||
| d86776feb1 | |||
| 1b8c05b257 | |||
| 02a6104fb0 | |||
| be1d9be7a8 | |||
| ac45087ead | |||
| 4774ec3343 | |||
| be7709a5ed | |||
| 76ba7fcc68 | |||
| 02ff473241 | |||
| 6ed31f252c | |||
| 5b87990206 | |||
| fe832c97b2 | |||
| 227c9fdda6 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
liberapay: lure
|
||||
@@ -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:
|
||||
@@ -52,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>'
|
||||
@@ -77,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,3 +1,4 @@
|
||||
platform: linux/amd64
|
||||
pipeline:
|
||||
release:
|
||||
image: goreleaser/goreleaser
|
||||
|
||||
10
Makefile
10
Makefile
@@ -1,7 +1,8 @@
|
||||
PREFIX ?= /usr/local
|
||||
GIT_VERSION = $(shell git describe --tags )
|
||||
|
||||
lure: internal/config/version.txt
|
||||
CGO_ENABLED=0 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 installmisc
|
||||
.PHONY: install clean uninstall installmisc lure
|
||||
27
README.md
27
README.md
@@ -3,7 +3,8 @@
|
||||
# LURE (Linux User REpository)
|
||||
|
||||
[](https://goreportcard.com/report/go.elara.ws/lure)
|
||||
[](https://ci.elara.ws/Elara6331/lure)
|
||||
[](https://ci.elara.ws/lure/lure)
|
||||
[](https://archive.softwareheritage.org/browse/origin/?origin_url=https://gitea.elara.ws/lure/lure.git)
|
||||
[](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
|
||||
23
build.go
23
build.go
@@ -23,13 +23,13 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.elara.ws/lure/internal/build"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/osutils"
|
||||
"go.elara.ws/lure/internal/repos"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"go.elara.ws/lure/manager"
|
||||
"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"
|
||||
)
|
||||
|
||||
var buildCmd = &cli.Command{
|
||||
@@ -54,12 +54,15 @@ var buildCmd = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.Context
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
script := c.String("script")
|
||||
if c.String("package") != "" {
|
||||
script = filepath.Join(config.GetPaths().RepoDir, c.String("package"), "lure.sh")
|
||||
script = filepath.Join(config.GetPaths(ctx).RepoDir, c.String("package"), "lure.sh")
|
||||
}
|
||||
|
||||
err := repos.Pull(c.Context, config.Config().Repos)
|
||||
err := repos.Pull(ctx, config.Config(ctx).Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||
}
|
||||
@@ -69,7 +72,7 @@ var buildCmd = &cli.Command{
|
||||
log.Fatal("Unable to detect a supported package manager on the system").Send()
|
||||
}
|
||||
|
||||
pkgPaths, _, err := build.BuildPackage(c.Context, types.BuildOpts{
|
||||
pkgPaths, _, err := build.BuildPackage(ctx, types.BuildOpts{
|
||||
Script: script,
|
||||
Manager: mgr,
|
||||
Clean: c.Bool("clean"),
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM alpine:latest
|
||||
COPY lure-api-server /usr/bin/lure-api-server
|
||||
ENTRYPOINT lure-api-server
|
||||
@@ -1,3 +0,0 @@
|
||||
# lure-api-server
|
||||
|
||||
`lure-api-server` is the backend API server for lure-web, the web interface for LURE.
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/twitchtv/twirp"
|
||||
"go.elara.ws/lure/internal/api"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
type lureWebAPI struct{}
|
||||
|
||||
func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.SearchResponse, error) {
|
||||
query := "(name LIKE ? OR description LIKE ? OR json_array_contains(provides, ?))"
|
||||
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(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("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.GetPaths().RepoDir, req.Repository, req.Name, "lure.sh")
|
||||
_, err := os.Stat(scriptPath)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, twirp.NewError(twirp.NotFound, "requested package not found")
|
||||
} 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
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,58 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
)
|
||||
|
||||
//go:embed badge-logo.txt
|
||||
var logoData string
|
||||
|
||||
var _ http.HandlerFunc
|
||||
|
||||
func handleBadge() http.HandlerFunc {
|
||||
return func(res http.ResponseWriter, req *http.Request) {
|
||||
repo := chi.URLParam(req, "repo")
|
||||
name := chi.URLParam(req, "pkg")
|
||||
|
||||
pkg, err := db.GetPkg("name = ? AND repository = ?", name, repo)
|
||||
if err != nil {
|
||||
http.Error(res, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(res, req, genBadgeURL(pkg.Name, genVersion(pkg)), http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func genVersion(pkg *db.Package) string {
|
||||
sb := strings.Builder{}
|
||||
if pkg.Epoch != 0 {
|
||||
sb.WriteString(strconv.Itoa(int(pkg.Epoch)))
|
||||
sb.WriteByte(':')
|
||||
}
|
||||
|
||||
sb.WriteString(pkg.Version)
|
||||
|
||||
if pkg.Release != 0 {
|
||||
sb.WriteByte('-')
|
||||
sb.WriteString(strconv.Itoa(pkg.Release))
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func genBadgeURL(pkgName, pkgVersion string) string {
|
||||
v := url.Values{}
|
||||
v.Set("label", pkgName)
|
||||
v.Set("message", pkgVersion)
|
||||
v.Set("logo", logoData)
|
||||
v.Set("color", "blue")
|
||||
u := &url.URL{Scheme: "https", Host: "img.shields.io", Path: "/static/v1", RawQuery: v.Encode()}
|
||||
return u.String()
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build
|
||||
docker buildx build --platform linux/amd64 --tag elara6331/lure-api-server:amd64 --no-cache .
|
||||
|
||||
CGO_ENABLED=0 GOARCH=arm64 GOOS=linux go build
|
||||
docker buildx build --platform linux/arm64/v8 --tag elara6331/lure-api-server:arm64 --no-cache .
|
||||
|
||||
docker login
|
||||
docker push elara6331/lure-api-server -a
|
||||
|
||||
docker manifest rm elara6331/lure-api-server:latest
|
||||
docker manifest create elara6331/lure-api-server:latest --amend elara6331/lure-api-server:arm64 --amend elara6331/lure-api-server:amd64
|
||||
docker manifest push elara6331/lure-api-server:latest
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/twitchtv/twirp"
|
||||
"go.elara.ws/logger"
|
||||
"go.elara.ws/lure/internal/api"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"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, config.Config().Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||
}
|
||||
|
||||
sigCh := make(chan struct{}, 200)
|
||||
go repoPullWorker(ctx, sigCh)
|
||||
|
||||
apiServer := api.NewAPIServer(
|
||||
lureWebAPI{},
|
||||
twirp.WithServerPathPrefix(""),
|
||||
)
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.With(allowAllCORSHandler, withAcceptLanguage).Handle("/*", apiServer)
|
||||
r.Post("/webhook", handleWebhook(sigCh))
|
||||
r.Get("/badge/{repo}/{pkg}", handleBadge())
|
||||
|
||||
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, r)
|
||||
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)
|
||||
})
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/repos"
|
||||
)
|
||||
|
||||
func handleWebhook(sigCh chan<- struct{}) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
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.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
sigCh <- struct{}{}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
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, config.Config().Repos)
|
||||
if err != nil {
|
||||
log.Warn("Error while pulling repositories").Err(err).Send()
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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,11 +202,11 @@ 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
|
||||
|
||||
@@ -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
|
||||
|
||||
15
fix.go
15
fix.go
@@ -22,18 +22,21 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"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"
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
db.Close()
|
||||
paths := config.GetPaths()
|
||||
paths := config.GetPaths(ctx)
|
||||
|
||||
log.Info("Removing cache directory").Send()
|
||||
|
||||
@@ -49,7 +52,7 @@ var fixCmd = &cli.Command{
|
||||
log.Fatal("Unable to create new cache directory").Err(err).Send()
|
||||
}
|
||||
|
||||
err = repos.Pull(c.Context, config.Config().Repos)
|
||||
err = repos.Pull(ctx, config.Config(ctx).Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repos").Err(err).Send()
|
||||
}
|
||||
|
||||
45
gen.go
Normal file
45
gen.go
Normal 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"),
|
||||
})
|
||||
},
|
||||
}
|
||||
12
go.mod
12
go.mod
@@ -1,6 +1,8 @@
|
||||
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.7
|
||||
@@ -9,7 +11,6 @@ require (
|
||||
github.com/charmbracelet/bubbles v0.16.1
|
||||
github.com/charmbracelet/bubbletea v0.24.2
|
||||
github.com/charmbracelet/lipgloss v0.8.0
|
||||
github.com/go-chi/chi/v5 v5.0.10
|
||||
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
|
||||
@@ -20,17 +21,17 @@ require (
|
||||
github.com/muesli/reflow v0.3.0
|
||||
github.com/pelletier/go-toml/v2 v2.1.0
|
||||
github.com/schollz/progressbar/v3 v3.13.1
|
||||
github.com/twitchtv/twirp v8.1.3+incompatible
|
||||
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
|
||||
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
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
lure.sh/fakeroot v0.0.0-20231024000108-b130d64a68ee
|
||||
modernc.org/sqlite v1.25.0
|
||||
mvdan.cc/sh/v3 v3.7.0
|
||||
)
|
||||
@@ -107,7 +108,6 @@ require (
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
||||
golang.org/x/crypto v0.13.0 // 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
|
||||
|
||||
42
go.sum
42
go.sum
@@ -24,6 +24,7 @@ github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tS
|
||||
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/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
@@ -39,19 +40,25 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe
|
||||
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.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-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/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=
|
||||
@@ -64,6 +71,7 @@ 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=
|
||||
@@ -89,6 +97,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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=
|
||||
@@ -102,19 +111,21 @@ github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdf
|
||||
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/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
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=
|
||||
@@ -139,7 +150,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -148,13 +158,14 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/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=
|
||||
@@ -166,6 +177,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
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=
|
||||
@@ -179,6 +191,7 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9
|
||||
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.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
|
||||
@@ -194,6 +207,7 @@ github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Cc
|
||||
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=
|
||||
@@ -208,9 +222,11 @@ 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.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/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=
|
||||
@@ -232,6 +248,7 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
|
||||
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.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.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
|
||||
@@ -257,6 +274,7 @@ github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1n
|
||||
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/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=
|
||||
@@ -277,6 +295,7 @@ 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/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||
@@ -290,7 +309,9 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
||||
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=
|
||||
@@ -309,8 +330,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
|
||||
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.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
|
||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
@@ -323,6 +342,7 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV
|
||||
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/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=
|
||||
@@ -537,13 +557,11 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
||||
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=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
@@ -560,12 +578,16 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
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/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=
|
||||
@@ -579,9 +601,11 @@ 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.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.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=
|
||||
|
||||
86
helper.go
Normal file
86
helper.go
Normal 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
|
||||
},
|
||||
}
|
||||
24
info.go
24
info.go
@@ -22,14 +22,13 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"go.elara.ws/lure/internal/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"
|
||||
)
|
||||
|
||||
@@ -44,17 +43,20 @@ var infoCmd = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.Context
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
args := c.Args()
|
||||
if args.Len() < 1 {
|
||||
log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
|
||||
}
|
||||
|
||||
err := repos.Pull(c.Context, config.Config().Repos)
|
||||
err := repos.Pull(ctx, config.Config(ctx).Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||
}
|
||||
|
||||
found, _, err := repos.FindPkgs(args.Slice())
|
||||
found, _, err := repos.FindPkgs(ctx, args.Slice())
|
||||
if err != nil {
|
||||
log.Fatal("Error finding packages").Err(err).Send()
|
||||
}
|
||||
@@ -63,13 +65,13 @@ var infoCmd = &cli.Command{
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
pkgs := cliutils.FlattenPkgs(found, "show", c.Bool("interactive"))
|
||||
pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive"))
|
||||
|
||||
var names []string
|
||||
all := c.Bool("all")
|
||||
|
||||
if !all {
|
||||
info, err := distro.ParseOSRelease(c.Context)
|
||||
info, err := distro.ParseOSRelease(ctx)
|
||||
if err != nil {
|
||||
log.Fatal("Error parsing os-release file").Err(err).Send()
|
||||
}
|
||||
|
||||
32
install.go
32
install.go
@@ -22,14 +22,14 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.elara.ws/lure/internal/build"
|
||||
"go.elara.ws/lure/internal/cliutils"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/repos"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"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"
|
||||
)
|
||||
|
||||
var installCmd = &cli.Command{
|
||||
@@ -44,6 +44,9 @@ var installCmd = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.Context
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
args := c.Args()
|
||||
if args.Len() < 1 {
|
||||
log.Fatalf("Command install expected at least 1 argument, got %d", args.Len()).Send()
|
||||
@@ -54,18 +57,18 @@ var installCmd = &cli.Command{
|
||||
log.Fatal("Unable to detect a supported package manager on the system").Send()
|
||||
}
|
||||
|
||||
err := repos.Pull(c.Context, config.Config().Repos)
|
||||
err := repos.Pull(ctx, config.Config(ctx).Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||
}
|
||||
|
||||
found, notFound, err := repos.FindPkgs(args.Slice())
|
||||
found, notFound, err := repos.FindPkgs(ctx, args.Slice())
|
||||
if err != nil {
|
||||
log.Fatal("Error finding packages").Err(err).Send()
|
||||
}
|
||||
|
||||
pkgs := cliutils.FlattenPkgs(found, "install", c.Bool("interactive"))
|
||||
build.InstallPkgs(c.Context, pkgs, notFound, types.BuildOpts{
|
||||
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"),
|
||||
@@ -73,7 +76,8 @@ var installCmd = &cli.Command{
|
||||
return nil
|
||||
},
|
||||
BashComplete: func(c *cli.Context) {
|
||||
result, err := db.GetPkgs("true")
|
||||
log := loggerctx.From(c.Context)
|
||||
result, err := db.GetPkgs(c.Context, "true")
|
||||
if err != nil {
|
||||
log.Fatal("Error getting packages").Err(err).Send()
|
||||
}
|
||||
@@ -96,6 +100,8 @@ var removeCmd = &cli.Command{
|
||||
Usage: "Remove an installed package",
|
||||
Aliases: []string{"rm"},
|
||||
Action: func(c *cli.Context) error {
|
||||
log := loggerctx.From(c.Context)
|
||||
|
||||
args := c.Args()
|
||||
if args.Len() < 1 {
|
||||
log.Fatalf("Command remove expected at least 1 argument, got %d", args.Len()).Send()
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* 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 api
|
||||
|
||||
//go:generate protoc --twirp_out=. lure.proto
|
||||
//go:generate protoc --go_out=. lure.proto
|
||||
@@ -1,885 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc v4.24.2
|
||||
// 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
|
||||
}
|
||||
@@ -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
@@ -19,24 +19,25 @@
|
||||
package cliutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/pager"
|
||||
"go.elara.ws/lure/internal/translations"
|
||||
"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) (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: translations.Translator().TranslateTo(msg, config.Language()),
|
||||
Message: translations.Translator(ctx).TranslateTo(msg, config.Language(ctx)),
|
||||
Default: def,
|
||||
},
|
||||
&answer,
|
||||
@@ -50,13 +51,15 @@ func YesNoPrompt(msg string, interactive, def bool) (bool, error) {
|
||||
// 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) error {
|
||||
func PromptViewScript(ctx context.Context, script, name, style string, interactive bool) error {
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
if !interactive {
|
||||
return nil
|
||||
}
|
||||
|
||||
scriptPrompt := translations.Translator().TranslateTo("Would you like to view the build script for", config.Language()) + " " + name
|
||||
view, err := YesNoPrompt(scriptPrompt, interactive, false)
|
||||
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
|
||||
}
|
||||
@@ -67,13 +70,13 @@ func PromptViewScript(script, name, style string, interactive bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cont, err := YesNoPrompt("Would you still like to continue?", interactive, false)
|
||||
cont, err := YesNoPrompt(ctx, "Would you still like to continue?", interactive, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !cont {
|
||||
log.Fatal(translations.Translator().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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,11 +103,12 @@ 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) []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 {
|
||||
choice, err := PkgPrompt(pkgs, verb, interactive)
|
||||
choice, err := PkgPrompt(ctx, pkgs, verb, interactive)
|
||||
if err != nil {
|
||||
log.Fatal("Error prompting for choice of package").Send()
|
||||
}
|
||||
@@ -117,7 +121,7 @@ func FlattenPkgs(found map[string][]db.Package, verb string, interactive bool) [
|
||||
}
|
||||
|
||||
// PkgPrompt asks the user to choose between multiple packages.
|
||||
func PkgPrompt(options []db.Package, verb string, interactive bool) (db.Package, error) {
|
||||
func PkgPrompt(ctx context.Context, options []db.Package, verb string, interactive bool) (db.Package, error) {
|
||||
if !interactive {
|
||||
return options[0], nil
|
||||
}
|
||||
@@ -129,7 +133,7 @@ func PkgPrompt(options []db.Package, verb string, interactive bool) (db.Package,
|
||||
|
||||
prompt := &survey.Select{
|
||||
Options: names,
|
||||
Message: translations.Translator().TranslateTo("Choose which package to "+verb, config.Language()),
|
||||
Message: translations.Translator(ctx).TranslateTo("Choose which package to "+verb, config.Language(ctx)),
|
||||
}
|
||||
|
||||
var choice int
|
||||
@@ -143,14 +147,14 @@ func PkgPrompt(options []db.Package, verb string, interactive bool) (db.Package,
|
||||
|
||||
// ChooseOptDepends asks the user to choose between multiple optional dependencies.
|
||||
// The user may choose multiple items.
|
||||
func ChooseOptDepends(options []string, verb string, interactive bool) ([]string, error) {
|
||||
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().TranslateTo("Choose which optional package(s) to install", config.Language()),
|
||||
Message: translations.Translator(ctx).TranslateTo("Choose which optional package(s) to install", config.Language(ctx)),
|
||||
}
|
||||
|
||||
var choices []int
|
||||
|
||||
@@ -19,11 +19,13 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"lure.sh/lure/internal/types"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
)
|
||||
|
||||
var defaultConfig = &types.Config{
|
||||
@@ -33,16 +35,26 @@ var defaultConfig = &types.Config{
|
||||
Repos: []types.Repo{
|
||||
{
|
||||
Name: "default",
|
||||
URL: "https://github.com/Elara6331/lure-repo.git",
|
||||
URL: "https://github.com/lure-sh/lure-repo.git",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var config *types.Config
|
||||
var (
|
||||
configMtx sync.Mutex
|
||||
config *types.Config
|
||||
)
|
||||
|
||||
// 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)
|
||||
|
||||
func Config() *types.Config {
|
||||
if config == nil {
|
||||
cfgFl, err := os.Open(GetPaths().ConfigPath)
|
||||
cfgFl, err := os.Open(GetPaths(ctx).ConfigPath)
|
||||
if err != nil {
|
||||
log.Warn("Error opening config file, using defaults").Err(err).Send()
|
||||
return defaultConfig
|
||||
|
||||
@@ -19,19 +19,29 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
var (
|
||||
langMtx sync.Mutex
|
||||
lang language.Tag
|
||||
langSet bool
|
||||
)
|
||||
|
||||
func Language() language.Tag {
|
||||
// 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)
|
||||
@@ -45,6 +55,8 @@ func Language() language.Tag {
|
||||
return lang
|
||||
}
|
||||
|
||||
// SystemLang returns the system language based on
|
||||
// the $LANG environment variable.
|
||||
func SystemLang() string {
|
||||
lang := os.Getenv("LANG")
|
||||
lang, _, _ = strings.Cut(lang, ".")
|
||||
|
||||
@@ -19,13 +19,16 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
)
|
||||
|
||||
// Paths contains various paths used by LURE
|
||||
type Paths struct {
|
||||
ConfigDir string
|
||||
ConfigPath string
|
||||
@@ -35,9 +38,20 @@ type Paths struct {
|
||||
DBPath string
|
||||
}
|
||||
|
||||
var paths *Paths
|
||||
var (
|
||||
pathsMtx sync.Mutex
|
||||
paths *Paths
|
||||
)
|
||||
|
||||
func GetPaths() *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{}
|
||||
|
||||
@@ -1,26 +1,5 @@
|
||||
/*
|
||||
* 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 _ "embed"
|
||||
|
||||
//go:generate ../../scripts/gen-version.sh
|
||||
|
||||
//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"
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
@@ -58,7 +59,7 @@ func Arch() string {
|
||||
}
|
||||
|
||||
func IsCompatibleWith(target string, list []string) bool {
|
||||
if target == "all" {
|
||||
if target == "all" || slices.Contains(list, "all") {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -19,23 +19,27 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"golang.org/x/exp/slices"
|
||||
"modernc.org/sqlite"
|
||||
)
|
||||
|
||||
// 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
|
||||
@@ -63,31 +67,44 @@ type version struct {
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
conn *sqlx.DB
|
||||
closed = true
|
||||
)
|
||||
|
||||
func DB() *sqlx.DB {
|
||||
// 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 conn
|
||||
return getConn()
|
||||
}
|
||||
db, err := Open(config.GetPaths().DBPath)
|
||||
_, err := open(ctx, config.GetPaths(ctx).DBPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error opening database").Err(err).Send()
|
||||
}
|
||||
conn = db
|
||||
return getConn()
|
||||
}
|
||||
|
||||
func getConn() *sqlx.DB {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
return conn
|
||||
}
|
||||
|
||||
func Open(dsn string) (*sqlx.DB, error) {
|
||||
func open(ctx context.Context, dsn string) (*sqlx.DB, error) {
|
||||
db, err := sqlx.Open("sqlite", dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
conn = db
|
||||
closed = false
|
||||
mu.Unlock()
|
||||
|
||||
err = initDB(dsn)
|
||||
err = initDB(ctx, dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -95,6 +112,7 @@ func Open(dsn string) (*sqlx.DB, error) {
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// Close closes the database
|
||||
func Close() error {
|
||||
closed = true
|
||||
if conn != nil {
|
||||
@@ -104,10 +122,11 @@ func Close() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the database
|
||||
func initDB(dsn string) error {
|
||||
// initDB initializes the database
|
||||
func initDB(ctx context.Context, dsn string) error {
|
||||
log := loggerctx.From(ctx)
|
||||
conn = conn.Unsafe()
|
||||
_, err := conn.Exec(`
|
||||
_, err := conn.ExecContext(ctx, `
|
||||
CREATE TABLE IF NOT EXISTS pkgs (
|
||||
name TEXT NOT NULL,
|
||||
repository TEXT NOT NULL,
|
||||
@@ -136,54 +155,58 @@ func initDB(dsn string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ver, ok := GetVersion()
|
||||
ver, ok := GetVersion(ctx)
|
||||
if ok && ver != CurrentVersion {
|
||||
log.Warn("Database version mismatch; resetting").Int("version", ver).Int("expected", CurrentVersion).Send()
|
||||
Reset()
|
||||
return initDB(dsn)
|
||||
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(CurrentVersion)
|
||||
return addVersion(ctx, CurrentVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Reset() error {
|
||||
_, err := DB().Exec("DROP TABLE IF EXISTS pkgs;")
|
||||
// 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().Exec("DROP TABLE IF EXISTS lure_db_version;")
|
||||
_, err = DB(ctx).ExecContext(ctx, "DROP TABLE IF EXISTS lure_db_version;")
|
||||
return err
|
||||
}
|
||||
|
||||
func IsEmpty() bool {
|
||||
// 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().Get(&count, "SELECT count(1) FROM pkgs;")
|
||||
err := DB(ctx).GetContext(ctx, &count, "SELECT count(1) FROM pkgs;")
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return count == 0
|
||||
}
|
||||
|
||||
func GetVersion() (int, bool) {
|
||||
// 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(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(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,
|
||||
@@ -224,28 +247,30 @@ func InsertPackage(pkg Package) error {
|
||||
}
|
||||
|
||||
// GetPkgs returns a result containing packages that match the where conditions
|
||||
func GetPkgs(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(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(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")
|
||||
@@ -265,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}
|
||||
}
|
||||
|
||||
@@ -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.elara.ws/",
|
||||
"en": "https://lure.sh/",
|
||||
}),
|
||||
Maintainer: db.NewJSON(map[string]string{
|
||||
"en": "Elara Musayelyan <elara@elara.ws>",
|
||||
"ru": "Элара Мусаелян <arsen@arsenm.dev>",
|
||||
"ru": "Элара Мусаелян <elara@elara.ws>",
|
||||
}),
|
||||
Architectures: db.NewJSON([]string{"arm64", "amd64"}),
|
||||
Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}),
|
||||
|
||||
@@ -36,9 +36,11 @@ import (
|
||||
|
||||
"github.com/PuerkitoBio/purell"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
"go.elara.ws/lure/internal/dlcache"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"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"
|
||||
@@ -87,14 +89,12 @@ type Options struct {
|
||||
CacheDisabled bool
|
||||
PostprocDisabled bool
|
||||
Progress io.Writer
|
||||
LocalDir string
|
||||
}
|
||||
|
||||
func (opts Options) NewHash() (hash.Hash, error) {
|
||||
if opts.HashAlgorithm == "" {
|
||||
opts.HashAlgorithm = "sha256"
|
||||
}
|
||||
switch opts.HashAlgorithm {
|
||||
case "sha256":
|
||||
case "", "sha256":
|
||||
return sha256.New(), nil
|
||||
case "sha224":
|
||||
return sha256.New224(), nil
|
||||
@@ -106,8 +106,17 @@ func (opts Options) NewHash() (hash.Hash, error) {
|
||||
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)
|
||||
}
|
||||
return nil, fmt.Errorf("%w: %s", ErrNoSuchHashAlgo, opts.HashAlgorithm)
|
||||
}
|
||||
|
||||
// Manifest holds information about the type and name
|
||||
@@ -154,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
|
||||
@@ -168,7 +178,7 @@ 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 {
|
||||
@@ -181,6 +191,7 @@ func Download(ctx context.Context, opts Options) (err error) {
|
||||
URL: opts.URL,
|
||||
Destination: cacheDir,
|
||||
Progress: opts.Progress,
|
||||
LocalDir: opts.LocalDir,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -198,9 +209,10 @@ func Download(ctx context.Context, opts Options) (err error) {
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -216,7 +228,7 @@ 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
|
||||
}
|
||||
@@ -228,6 +240,7 @@ func Download(ctx context.Context, opts Options) (err error) {
|
||||
URL: opts.URL,
|
||||
Destination: cacheDir,
|
||||
Progress: opts.Progress,
|
||||
LocalDir: opts.LocalDir,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -22,18 +22,18 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"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
|
||||
@@ -68,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"
|
||||
|
||||
@@ -89,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),
|
||||
@@ -105,7 +125,7 @@ func (FileDownloader) Download(opts Options) (Type, string, error) {
|
||||
)
|
||||
defer bar.Close()
|
||||
} else {
|
||||
bar = shutils.NopRWC{}
|
||||
bar = handlers.NopRWC{}
|
||||
}
|
||||
|
||||
h, err := opts.NewHash()
|
||||
@@ -120,11 +140,11 @@ func (FileDownloader) Download(opts Options) (Type, string, error) {
|
||||
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.Hash != nil {
|
||||
sum := h.Sum(nil)
|
||||
@@ -142,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, 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
|
||||
}
|
||||
@@ -227,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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -19,29 +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 returns the base path of the download cache
|
||||
func BasePath() string {
|
||||
return filepath.Join(config.GetPaths().RepoDir, "dl")
|
||||
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()) {
|
||||
@@ -64,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 {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package dlcache_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
@@ -26,8 +27,8 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/dlcache"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/dlcache"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -35,7 +36,7 @@ func init() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.GetPaths().RepoDir = dir
|
||||
config.GetPaths(context.Background()).RepoDir = dir
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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 log
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"go.elara.ws/logger"
|
||||
)
|
||||
|
||||
var Logger logger.Logger = logger.NewCLI(os.Stderr)
|
||||
|
||||
// NoPanic prevents the logger from panicking on panic events
|
||||
func NoPanic() {
|
||||
Logger.NoPanic()
|
||||
}
|
||||
|
||||
// NoExit prevents the logger from exiting on fatal events
|
||||
func NoExit() {
|
||||
Logger.NoExit()
|
||||
}
|
||||
|
||||
// SetLevel sets the log level of the logger
|
||||
func SetLevel(l logger.LogLevel) {
|
||||
Logger.SetLevel(l)
|
||||
}
|
||||
|
||||
// Debug creates a new debug event with the given message
|
||||
func Debug(msg string) logger.LogBuilder {
|
||||
return Logger.Debug(msg)
|
||||
}
|
||||
|
||||
// Debugf creates a new debug event with the formatted message
|
||||
func Debugf(format string, v ...any) logger.LogBuilder {
|
||||
return Logger.Debugf(format, v...)
|
||||
}
|
||||
|
||||
// Info creates a new info event with the given message
|
||||
func Info(msg string) logger.LogBuilder {
|
||||
return Logger.Info(msg)
|
||||
}
|
||||
|
||||
// Infof creates a new info event with the formatted message
|
||||
func Infof(format string, v ...any) logger.LogBuilder {
|
||||
return Logger.Infof(format, v...)
|
||||
}
|
||||
|
||||
// Warn creates a new warn event with the given message
|
||||
func Warn(msg string) logger.LogBuilder {
|
||||
return Logger.Warn(msg)
|
||||
}
|
||||
|
||||
// Warnf creates a new warn event with the formatted message
|
||||
func Warnf(format string, v ...any) logger.LogBuilder {
|
||||
return Logger.Warnf(format, v...)
|
||||
}
|
||||
|
||||
// Error creates a new error event with the given message
|
||||
func Error(msg string) logger.LogBuilder {
|
||||
return Logger.Error(msg)
|
||||
}
|
||||
|
||||
// Errorf creates a new error event with the formatted message
|
||||
func Errorf(format string, v ...any) logger.LogBuilder {
|
||||
return Logger.Errorf(format, v...)
|
||||
}
|
||||
|
||||
// Fatal creates a new fatal event with the given message
|
||||
func Fatal(msg string) logger.LogBuilder {
|
||||
return Logger.Fatal(msg)
|
||||
}
|
||||
|
||||
// Fatalf creates a new fatal event with the formatted message
|
||||
func Fatalf(format string, v ...any) logger.LogBuilder {
|
||||
return Logger.Fatalf(format, v...)
|
||||
}
|
||||
|
||||
// Fatal creates a new fatal event with the given message
|
||||
func Panic(msg string) logger.LogBuilder {
|
||||
return Logger.Panic(msg)
|
||||
}
|
||||
|
||||
// Fatalf creates a new fatal event with the formatted message
|
||||
func Panicf(format string, v ...any) logger.LogBuilder {
|
||||
return Logger.Panicf(format, v...)
|
||||
}
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"go.elara.ws/lure/distro"
|
||||
"go.elara.ws/lure/internal/cpu"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"lure.sh/lure/internal/cpu"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/pkg/distro"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
@@ -23,8 +23,8 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"go.elara.ws/lure/distro"
|
||||
"go.elara.ws/lure/internal/overrides"
|
||||
"lure.sh/lure/internal/overrides"
|
||||
"lure.sh/lure/pkg/distro"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ func (p *Pager) Run() error {
|
||||
prog := tea.NewProgram(
|
||||
p.model,
|
||||
tea.WithMouseCellMotion(),
|
||||
tea.WithAltScreen(),
|
||||
)
|
||||
|
||||
_, err := prog.Run()
|
||||
@@ -74,7 +75,7 @@ type pagerModel struct {
|
||||
}
|
||||
|
||||
func (pm pagerModel) Init() tea.Cmd {
|
||||
return tea.ClearScreen
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm pagerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"go.elara.ws/lure/distro"
|
||||
"go.elara.ws/lure/internal/overrides"
|
||||
"lure.sh/lure/internal/overrides"
|
||||
"lure.sh/lure/pkg/distro"
|
||||
"golang.org/x/exp/slices"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
|
||||
@@ -27,8 +27,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.elara.ws/lure/distro"
|
||||
"go.elara.ws/lure/internal/shutils/decoder"
|
||||
"lure.sh/lure/internal/shutils/decoder"
|
||||
"lure.sh/lure/pkg/distro"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package shutils
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -16,16 +16,16 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package shutils_test
|
||||
package handlers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.elara.ws/lure/distro"
|
||||
"go.elara.ws/lure/internal/shutils"
|
||||
"go.elara.ws/lure/internal/shutils/decoder"
|
||||
"lure.sh/lure/internal/shutils/handlers"
|
||||
"lure.sh/lure/internal/shutils/decoder"
|
||||
"lure.sh/lure/pkg/distro"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
@@ -36,7 +36,7 @@ const testScript = `
|
||||
release=1
|
||||
epoch=2
|
||||
desc="Test package"
|
||||
homepage='https://lure.elara.ws'
|
||||
homepage='https://lure.sh'
|
||||
maintainer='Elara Musayelyan <elara@elara.ws>'
|
||||
architectures=('arm64' 'amd64')
|
||||
license=('GPL-3.0-or-later')
|
||||
114
internal/shutils/handlers/fakeroot.go
Normal file
114
internal/shutils/handlers/fakeroot.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"lure.sh/fakeroot"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
)
|
||||
|
||||
// FakerootExecHandler was extracted from github.com/mvdan/sh/interp/handler.go
|
||||
// and modified to run commands in a fakeroot environent.
|
||||
func FakerootExecHandler(killTimeout time.Duration) interp.ExecHandlerFunc {
|
||||
return func(ctx context.Context, args []string) error {
|
||||
hc := interp.HandlerCtx(ctx)
|
||||
path, err := interp.LookPathDir(hc.Dir, hc.Env, args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintln(hc.Stderr, err)
|
||||
return interp.NewExitStatus(127)
|
||||
}
|
||||
cmd := &exec.Cmd{
|
||||
Path: path,
|
||||
Args: args,
|
||||
Env: execEnv(hc.Env),
|
||||
Dir: hc.Dir,
|
||||
Stdin: hc.Stdin,
|
||||
Stdout: hc.Stdout,
|
||||
Stderr: hc.Stderr,
|
||||
}
|
||||
|
||||
err = fakeroot.Apply(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmd.Start()
|
||||
if err == nil {
|
||||
if done := ctx.Done(); done != nil {
|
||||
go func() {
|
||||
<-done
|
||||
|
||||
if killTimeout <= 0 || runtime.GOOS == "windows" {
|
||||
_ = cmd.Process.Signal(os.Kill)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: don't temporarily leak this goroutine
|
||||
// if the program stops itself with the
|
||||
// interrupt.
|
||||
go func() {
|
||||
time.Sleep(killTimeout)
|
||||
_ = cmd.Process.Signal(os.Kill)
|
||||
}()
|
||||
_ = cmd.Process.Signal(os.Interrupt)
|
||||
}()
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
}
|
||||
|
||||
switch x := err.(type) {
|
||||
case *exec.ExitError:
|
||||
// started, but errored - default to 1 if OS
|
||||
// doesn't have exit statuses
|
||||
if status, ok := x.Sys().(syscall.WaitStatus); ok {
|
||||
if status.Signaled() {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
return interp.NewExitStatus(uint8(128 + status.Signal()))
|
||||
}
|
||||
return interp.NewExitStatus(uint8(status.ExitStatus()))
|
||||
}
|
||||
return interp.NewExitStatus(1)
|
||||
case *exec.Error:
|
||||
// did not start
|
||||
fmt.Fprintf(hc.Stderr, "%v\n", err)
|
||||
return interp.NewExitStatus(127)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// execEnv was extracted from github.com/mvdan/sh/interp/vars.go
|
||||
func execEnv(env expand.Environ) []string {
|
||||
list := make([]string, 0, 64)
|
||||
env.Each(func(name string, vr expand.Variable) bool {
|
||||
if !vr.IsSet() {
|
||||
// If a variable is set globally but unset in the
|
||||
// runner, we need to ensure it's not part of the final
|
||||
// list. Seems like zeroing the element is enough.
|
||||
// This is a linear search, but this scenario should be
|
||||
// rare, and the number of variables shouldn't be large.
|
||||
for i, kv := range list {
|
||||
if strings.HasPrefix(kv, name+"=") {
|
||||
list[i] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
if vr.Exported && vr.Kind == expand.String {
|
||||
list = append(list, name+"="+vr.String())
|
||||
}
|
||||
return true
|
||||
})
|
||||
return list
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package shutils
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package shutils_test
|
||||
package handlers_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.elara.ws/lure/internal/shutils"
|
||||
"lure.sh/lure/internal/shutils/handlers"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
@@ -40,7 +40,7 @@ func TestNopExec(t *testing.T) {
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
runner, err := interp.New(
|
||||
interp.ExecHandler(shutils.NopExec),
|
||||
interp.ExecHandler(handlers.NopExec),
|
||||
interp.StdIO(os.Stdin, buf, buf),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package shutils
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -30,8 +30,8 @@ import (
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"go.elara.ws/lure/internal/shutils"
|
||||
"golang.org/x/exp/slices"
|
||||
"lure.sh/lure/internal/shutils/handlers"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ var (
|
||||
)
|
||||
|
||||
// Helpers contains all the helper commands
|
||||
var Helpers = shutils.ExecFuncs{
|
||||
var Helpers = handlers.ExecFuncs{
|
||||
"install-binary": installHelperCmd("/usr/bin", 0o755),
|
||||
"install-systemd-user": installHelperCmd("/usr/lib/systemd/user", 0o644),
|
||||
"install-systemd": installHelperCmd("/usr/lib/systemd/system", 0o644),
|
||||
@@ -57,14 +57,14 @@ var Helpers = shutils.ExecFuncs{
|
||||
|
||||
// Restricted contains restricted read-only helper commands
|
||||
// that don't modify any state
|
||||
var Restricted = shutils.ExecFuncs{
|
||||
var Restricted = handlers.ExecFuncs{
|
||||
"git-version": gitVersionCmd,
|
||||
}
|
||||
|
||||
func installHelperCmd(prefix string, perms os.FileMode) shutils.ExecFunc {
|
||||
func installHelperCmd(prefix string, perms os.FileMode) handlers.ExecFunc {
|
||||
return func(hc interp.HandlerContext, cmd string, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return shutils.InsufficientArgsError(cmd, 1, len(args))
|
||||
return handlers.InsufficientArgsError(cmd, 1, len(args))
|
||||
}
|
||||
|
||||
from := resolvePath(hc, args[0])
|
||||
@@ -85,7 +85,7 @@ func installHelperCmd(prefix string, perms os.FileMode) shutils.ExecFunc {
|
||||
|
||||
func installManualCmd(hc interp.HandlerContext, cmd string, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return shutils.InsufficientArgsError(cmd, 1, len(args))
|
||||
return handlers.InsufficientArgsError(cmd, 1, len(args))
|
||||
}
|
||||
|
||||
from := resolvePath(hc, args[0])
|
||||
@@ -115,7 +115,7 @@ func installCompletionCmd(hc interp.HandlerContext, cmd string, args []string) e
|
||||
}
|
||||
|
||||
if len(args) < 2 {
|
||||
return shutils.InsufficientArgsError(cmd, 2, len(args))
|
||||
return handlers.InsufficientArgsError(cmd, 2, len(args))
|
||||
}
|
||||
|
||||
shell := args[0]
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
package translations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"sync"
|
||||
|
||||
"go.elara.ws/logger"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"go.elara.ws/translate"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
@@ -30,9 +32,15 @@ import (
|
||||
//go:embed files
|
||||
var translationFS embed.FS
|
||||
|
||||
var translator *translate.Translator
|
||||
var (
|
||||
mu sync.Mutex
|
||||
translator *translate.Translator
|
||||
)
|
||||
|
||||
func Translator() *translate.Translator {
|
||||
func Translator(ctx context.Context) *translate.Translator {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
log := loggerctx.From(ctx)
|
||||
if translator == nil {
|
||||
t, err := translate.NewFromFS(translationFS)
|
||||
if err != nil {
|
||||
@@ -43,6 +51,6 @@ func Translator() *translate.Translator {
|
||||
return translator
|
||||
}
|
||||
|
||||
func NewLogger(l logger.Logger, lang language.Tag) *translate.TranslatedLogger {
|
||||
return translate.NewLogger(l, *Translator(), lang)
|
||||
func NewLogger(ctx context.Context, l logger.Logger, lang language.Tag) *translate.TranslatedLogger {
|
||||
return translate.NewLogger(l, *Translator(ctx), lang)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
package types
|
||||
|
||||
import "go.elara.ws/lure/manager"
|
||||
import "lure.sh/lure/pkg/manager"
|
||||
|
||||
type BuildOpts struct {
|
||||
Script string
|
||||
|
||||
19
list.go
19
list.go
@@ -22,11 +22,11 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/repos"
|
||||
"go.elara.ws/lure/manager"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"lure.sh/lure/pkg/manager"
|
||||
"lure.sh/lure/pkg/repos"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
@@ -41,7 +41,10 @@ var listCmd = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
err := repos.Pull(c.Context, config.Config().Repos)
|
||||
ctx := c.Context
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
err := repos.Pull(ctx, config.Config(ctx).Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repositories").Err(err).Send()
|
||||
}
|
||||
@@ -53,7 +56,7 @@ var listCmd = &cli.Command{
|
||||
args = []any{c.Args().First(), c.Args().First()}
|
||||
}
|
||||
|
||||
result, err := db.GetPkgs(where, args...)
|
||||
result, err := db.GetPkgs(ctx, where, args...)
|
||||
if err != nil {
|
||||
log.Fatal("Error getting packages").Err(err).Send()
|
||||
}
|
||||
@@ -79,7 +82,7 @@ var listCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if slices.Contains(config.Config().IgnorePkgUpdates, pkg.Name) {
|
||||
if slices.Contains(config.Config(ctx).IgnorePkgUpdates, pkg.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
39
main.go
39
main.go
@@ -28,11 +28,11 @@ import (
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.elara.ws/logger"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/translations"
|
||||
"go.elara.ws/lure/manager"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/internal/translations"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"lure.sh/lure/pkg/manager"
|
||||
)
|
||||
|
||||
var app = &cli.App{
|
||||
@@ -62,14 +62,24 @@ var app = &cli.App{
|
||||
removerepoCmd,
|
||||
refreshCmd,
|
||||
fixCmd,
|
||||
genCmd,
|
||||
helperCmd,
|
||||
versionCmd,
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
args := strings.Split(c.String("pm-args"), " ")
|
||||
if len(args) == 1 && args[0] == "" {
|
||||
return nil
|
||||
ctx := c.Context
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
cmd := c.Args().First()
|
||||
if cmd != "helper" && !config.Config(ctx).Unsafe.AllowRunAsRoot && os.Geteuid() == 0 {
|
||||
log.Fatal("Running LURE as root is forbidden as it may cause catastrophic damage to your system").Send()
|
||||
}
|
||||
manager.Args = append(manager.Args, args...)
|
||||
|
||||
if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" {
|
||||
args := strings.Split(trimmed, " ")
|
||||
manager.Args = append(manager.Args, args...)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
After: func(ctx *cli.Context) error {
|
||||
@@ -88,16 +98,13 @@ var versionCmd = &cli.Command{
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.Logger = translations.NewLogger(logger.NewCLI(os.Stderr), config.Language())
|
||||
|
||||
if !config.Config().Unsafe.AllowRunAsRoot && os.Geteuid() == 0 {
|
||||
log.Fatal("Running LURE as root is forbidden as it may cause catastrophic damage to your system").Send()
|
||||
}
|
||||
ctx := context.Background()
|
||||
log := translations.NewLogger(ctx, logger.NewCLI(os.Stderr), config.Language(ctx))
|
||||
ctx = loggerctx.With(ctx, log)
|
||||
|
||||
// Set the root command to the one set in the LURE config
|
||||
manager.DefaultRootCmd = config.Config().RootCmd
|
||||
manager.DefaultRootCmd = config.Config(ctx).RootCmd
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/goreleaser/nfpm/v2/apk"
|
||||
_ "github.com/goreleaser/nfpm/v2/arch"
|
||||
@@ -38,19 +39,19 @@ import (
|
||||
|
||||
"github.com/goreleaser/nfpm/v2"
|
||||
"github.com/goreleaser/nfpm/v2/files"
|
||||
"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/log"
|
||||
"go.elara.ws/lure/internal/repos"
|
||||
"go.elara.ws/lure/internal/shutils"
|
||||
"go.elara.ws/lure/internal/shutils/decoder"
|
||||
"go.elara.ws/lure/internal/shutils/helpers"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"go.elara.ws/lure/manager"
|
||||
"lure.sh/lure/internal/cliutils"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/cpu"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/internal/dl"
|
||||
"lure.sh/lure/internal/shutils/decoder"
|
||||
"lure.sh/lure/internal/shutils/handlers"
|
||||
"lure.sh/lure/internal/shutils/helpers"
|
||||
"lure.sh/lure/internal/types"
|
||||
"lure.sh/lure/pkg/distro"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"lure.sh/lure/pkg/manager"
|
||||
"lure.sh/lure/pkg/repos"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
@@ -59,6 +60,8 @@ import (
|
||||
// BuildPackage builds the script at the given path. It returns two slices. One contains the paths
|
||||
// to the built package(s), the other contains the names of the built package(s).
|
||||
func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string, error) {
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
info, err := distro.ParseOSRelease(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -69,13 +72,18 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// The first pass is just used to get variable values and runs before
|
||||
// the script is displayed, so it's restricted so as to prevent malicious
|
||||
// code from executing.
|
||||
vars, err := executeFirstPass(ctx, info, fl, opts.Script)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
dirs := getDirs(vars, opts.Script)
|
||||
dirs := getDirs(ctx, vars, opts.Script)
|
||||
|
||||
// If opts.Clean isn't set and we find the package already built,
|
||||
// just return it rather than rebuilding
|
||||
if !opts.Clean {
|
||||
builtPkgPath, ok, err := checkForBuiltPackage(opts.Manager, vars, getPkgFormat(opts.Manager), dirs.BaseDir)
|
||||
if err != nil {
|
||||
@@ -87,30 +95,36 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
|
||||
}
|
||||
}
|
||||
|
||||
err = cliutils.PromptViewScript(opts.Script, vars.Name, config.Config().PagerStyle, opts.Interactive)
|
||||
// Ask the user if they'd like to see the build script
|
||||
err = cliutils.PromptViewScript(ctx, opts.Script, vars.Name, config.Config(ctx).PagerStyle, opts.Interactive)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
|
||||
}
|
||||
|
||||
log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send()
|
||||
|
||||
// The second pass will be used to execute the actual code,
|
||||
// so it's unrestricted. The script has already been displayed
|
||||
// to the user by this point, so it should be safe
|
||||
dec, err := executeSecondPass(ctx, info, fl, dirs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Get the installed packages on the system
|
||||
installed, err := opts.Manager.ListInstalled(nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cont, err := performChecks(vars, opts.Interactive, installed)
|
||||
cont, err := performChecks(ctx, vars, opts.Interactive, installed)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
} else if !cont {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Prepare the directories for building
|
||||
err = prepareDirs(dirs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -126,14 +140,14 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
builtPaths, builtNames, repoDeps, err := installDeps(ctx, opts, vars)
|
||||
builtPaths, builtNames, repoDeps, err := buildLUREDeps(ctx, opts, vars)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
log.Info("Downloading sources").Send()
|
||||
|
||||
err = getSources(ctx, dirs.SrcDir, vars)
|
||||
err = getSources(ctx, dirs, vars)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -145,12 +159,14 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
|
||||
|
||||
log.Info("Building package metadata").Str("name", vars.Name).Send()
|
||||
|
||||
pkgInfo, err := buildPkgMetadata(vars, dirs, append(repoDeps, builtNames...))
|
||||
pkgFormat := getPkgFormat(opts.Manager)
|
||||
|
||||
pkgInfo, err := buildPkgMetadata(vars, dirs, pkgFormat, append(repoDeps, builtNames...))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
packager, err := nfpm.Get(getPkgFormat(opts.Manager))
|
||||
packager, err := nfpm.Get(pkgFormat)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -170,7 +186,7 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = removeBuildDeps(buildDeps, opts)
|
||||
err = removeBuildDeps(ctx, buildDeps, opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -180,12 +196,16 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
|
||||
pkgPaths := append(builtPaths, pkgPath)
|
||||
pkgNames := append(builtNames, vars.Name)
|
||||
|
||||
// Remove any duplicates from the pkgPaths and pkgNames.
|
||||
// Duplicates can be introduced if several of the dependencies
|
||||
// depend on the same packages.
|
||||
pkgPaths = removeDuplicates(pkgPaths)
|
||||
pkgNames = removeDuplicates(pkgNames)
|
||||
|
||||
return pkgPaths, pkgNames, nil
|
||||
}
|
||||
|
||||
// parseScript parses the build script using the built-in bash implementation
|
||||
func parseScript(info *distro.OSRelease, script string) (*syntax.File, error) {
|
||||
fl, err := os.Open(script)
|
||||
if err != nil {
|
||||
@@ -201,20 +221,19 @@ func parseScript(info *distro.OSRelease, script string) (*syntax.File, error) {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// executeFirstPass executes the parsed script in a restricted environment
|
||||
// to extract the build variables without executing any actual code.
|
||||
func executeFirstPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, script string) (*types.BuildVars, error) {
|
||||
scriptDir := filepath.Dir(script)
|
||||
env := createBuildEnvVars(info, types.Directories{ScriptDir: scriptDir})
|
||||
|
||||
// The first pass is just used to get variable values and runs before
|
||||
// the script is displayed, so it is restricted so as to prevent malicious
|
||||
// code from executing.
|
||||
runner, err := interp.New(
|
||||
interp.Env(expand.ListEnviron(env...)),
|
||||
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
|
||||
interp.ExecHandler(helpers.Restricted.ExecHandler(shutils.NopExec)),
|
||||
interp.ReadDirHandler(shutils.RestrictedReadDir(scriptDir)),
|
||||
interp.StatHandler(shutils.RestrictedStat(scriptDir)),
|
||||
interp.OpenHandler(shutils.RestrictedOpen(scriptDir)),
|
||||
interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)),
|
||||
interp.ReadDirHandler(handlers.RestrictedReadDir(scriptDir)),
|
||||
interp.StatHandler(handlers.RestrictedStat(scriptDir)),
|
||||
interp.OpenHandler(handlers.RestrictedOpen(scriptDir)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -236,8 +255,9 @@ func executeFirstPass(ctx context.Context, info *distro.OSRelease, fl *syntax.Fi
|
||||
return &vars, nil
|
||||
}
|
||||
|
||||
func getDirs(vars *types.BuildVars, script string) types.Directories {
|
||||
baseDir := filepath.Join(config.GetPaths().PkgsDir, vars.Name)
|
||||
// getDirs returns the appropriate directories for the script
|
||||
func getDirs(ctx context.Context, vars *types.BuildVars, script string) types.Directories {
|
||||
baseDir := filepath.Join(config.GetPaths(ctx).PkgsDir, vars.Name)
|
||||
return types.Directories{
|
||||
BaseDir: baseDir,
|
||||
SrcDir: filepath.Join(baseDir, "src"),
|
||||
@@ -246,15 +266,16 @@ func getDirs(vars *types.BuildVars, script string) types.Directories {
|
||||
}
|
||||
}
|
||||
|
||||
// executeSecondPass executes the build script for the second time, this time without any restrictions.
|
||||
// It returns a decoder that can be used to retrieve functions and variables from the script.
|
||||
func executeSecondPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, dirs types.Directories) (*decoder.Decoder, error) {
|
||||
env := createBuildEnvVars(info, dirs)
|
||||
// The second pass will be used to execute the actual functions,
|
||||
// so it cannot be restricted. The script has already been displayed
|
||||
// to the user by this point, so it should be safe
|
||||
|
||||
fakeroot := handlers.FakerootExecHandler(2 * time.Second)
|
||||
runner, err := interp.New(
|
||||
interp.Env(expand.ListEnviron(env...)),
|
||||
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
|
||||
interp.ExecHandler(helpers.Helpers.ExecHandler(nil)),
|
||||
interp.ExecHandler(helpers.Helpers.ExecHandler(fakeroot)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -268,6 +289,7 @@ func executeSecondPass(ctx context.Context, info *distro.OSRelease, fl *syntax.F
|
||||
return decoder.New(info, runner), nil
|
||||
}
|
||||
|
||||
// prepareDirs prepares the directories for building.
|
||||
func prepareDirs(dirs types.Directories) error {
|
||||
err := os.RemoveAll(dirs.BaseDir)
|
||||
if err != nil {
|
||||
@@ -280,9 +302,11 @@ func prepareDirs(dirs types.Directories) error {
|
||||
return os.MkdirAll(dirs.PkgDir, 0o755)
|
||||
}
|
||||
|
||||
func performChecks(vars *types.BuildVars, interactive bool, installed map[string]string) (bool, error) {
|
||||
// performChecks checks various things on the system to ensure that the package can be installed.
|
||||
func performChecks(ctx context.Context, vars *types.BuildVars, interactive bool, installed map[string]string) (bool, error) {
|
||||
log := loggerctx.From(ctx)
|
||||
if !cpu.IsCompatibleWith(cpu.Arch(), vars.Architectures) {
|
||||
cont, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", interactive, true)
|
||||
cont, err := cliutils.YesNoPrompt(ctx, "Your system's CPU architecture doesn't match this package. Do you want to build anyway?", interactive, true)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -302,28 +326,33 @@ func performChecks(vars *types.BuildVars, interactive bool, installed map[string
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// installBuildDeps installs any build dependencies that aren't already installed and returns
|
||||
// a slice containing the names of all the packages it installed.
|
||||
func installBuildDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) ([]string, error) {
|
||||
log := loggerctx.From(ctx)
|
||||
var buildDeps []string
|
||||
if len(vars.BuildDepends) > 0 {
|
||||
found, notFound, err := repos.FindPkgs(vars.BuildDepends)
|
||||
found, notFound, err := repos.FindPkgs(ctx, vars.BuildDepends)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
found = filterBuildDeps(found, installed)
|
||||
found = removeAlreadyInstalled(found, installed)
|
||||
|
||||
log.Info("Installing build dependencies").Send()
|
||||
|
||||
flattened := cliutils.FlattenPkgs(found, "install", opts.Interactive)
|
||||
flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive)
|
||||
buildDeps = packageNames(flattened)
|
||||
InstallPkgs(ctx, flattened, notFound, opts)
|
||||
}
|
||||
return buildDeps, nil
|
||||
}
|
||||
|
||||
// installOptDeps asks the user which, if any, optional dependencies they want to install.
|
||||
// If the user chooses to install any optional dependencies, it performs the installation.
|
||||
func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) error {
|
||||
if len(vars.OptDepends) > 0 {
|
||||
optDeps, err := cliutils.ChooseOptDepends(vars.OptDepends, "install", opts.Interactive)
|
||||
optDeps, err := cliutils.ChooseOptDepends(ctx, vars.OptDepends, "install", opts.Interactive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -332,32 +361,35 @@ func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.Build
|
||||
return nil
|
||||
}
|
||||
|
||||
found, notFound, err := repos.FindPkgs(optDeps)
|
||||
found, notFound, err := repos.FindPkgs(ctx, optDeps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found = filterBuildDeps(found, installed)
|
||||
flattened := cliutils.FlattenPkgs(found, "install", opts.Interactive)
|
||||
optDeps = packageNames(flattened)
|
||||
found = removeAlreadyInstalled(found, installed)
|
||||
flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive)
|
||||
InstallPkgs(ctx, flattened, notFound, opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func installDeps(ctx context.Context, opts types.BuildOpts, vars *types.BuildVars) (builtPaths, builtNames, repoDeps []string, err error) {
|
||||
// buildLUREDeps builds all the LURE dependencies of the package. It returns the paths and names
|
||||
// of the packages it built, as well as all the dependencies it didn't find in the LURE repo so
|
||||
// they can be installed from the system repos.
|
||||
func buildLUREDeps(ctx context.Context, opts types.BuildOpts, vars *types.BuildVars) (builtPaths, builtNames, repoDeps []string, err error) {
|
||||
log := loggerctx.From(ctx)
|
||||
if len(vars.Depends) > 0 {
|
||||
log.Info("Installing dependencies").Send()
|
||||
|
||||
found, notFound, err := repos.FindPkgs(vars.Depends)
|
||||
found, notFound, err := repos.FindPkgs(ctx, vars.Depends)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
repoDeps = notFound
|
||||
|
||||
// If there are multiple options for some packages, flatten them all into a single slice
|
||||
pkgs := cliutils.FlattenPkgs(found, "install", opts.Interactive)
|
||||
scripts := GetScriptPaths(pkgs)
|
||||
pkgs := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive)
|
||||
scripts := GetScriptPaths(ctx, pkgs)
|
||||
for _, script := range scripts {
|
||||
newOpts := opts
|
||||
newOpts.Script = script
|
||||
@@ -377,13 +409,17 @@ func installDeps(ctx context.Context, opts types.BuildOpts, vars *types.BuildVar
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any potential duplicates, which can be introduced if
|
||||
// several of the dependencies depend on the same packages.
|
||||
repoDeps = removeDuplicates(repoDeps)
|
||||
builtPaths = removeDuplicates(builtPaths)
|
||||
builtNames = removeDuplicates(builtNames)
|
||||
return builtPaths, builtNames, repoDeps, nil
|
||||
}
|
||||
|
||||
// executeFunctions executes the special LURE functions, such as version(), prepare(), etc.
|
||||
func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Directories, vars *types.BuildVars) (err error) {
|
||||
log := loggerctx.From(ctx)
|
||||
version, ok := dec.GetFunc("version")
|
||||
if ok {
|
||||
log.Info("Executing version()").Send()
|
||||
@@ -444,7 +480,8 @@ func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Dire
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, deps []string) (*nfpm.Info, error) {
|
||||
// buildPkgMetadata builds the metadata for the package that's going to be built.
|
||||
func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, pkgFormat string, deps []string) (*nfpm.Info, error) {
|
||||
pkgInfo := &nfpm.Info{
|
||||
Name: vars.Name,
|
||||
Description: vars.Description,
|
||||
@@ -463,6 +500,13 @@ func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, deps []stri
|
||||
},
|
||||
}
|
||||
|
||||
if pkgFormat == "apk" {
|
||||
// Alpine refuses to install packages that provide themselves, so remove any such provides
|
||||
pkgInfo.Overridables.Provides = slices.DeleteFunc(pkgInfo.Overridables.Provides, func(s string) bool {
|
||||
return s == pkgInfo.Name
|
||||
})
|
||||
}
|
||||
|
||||
if vars.Epoch != 0 {
|
||||
pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10)
|
||||
}
|
||||
@@ -482,6 +526,8 @@ func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, deps []stri
|
||||
return pkgInfo, nil
|
||||
}
|
||||
|
||||
// buildContents builds the contents section of the package, which contains the files
|
||||
// that will be placed into the final package.
|
||||
func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Content, error) {
|
||||
contents := []*files.Content{}
|
||||
err := filepath.Walk(dirs.PkgDir, func(path string, fi os.FileInfo, err error) error {
|
||||
@@ -493,6 +539,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
|
||||
return err
|
||||
}
|
||||
|
||||
// If the directory is empty, skip it
|
||||
_, err = f.Readdirnames(1)
|
||||
if err != io.EOF {
|
||||
return nil
|
||||
@@ -507,8 +554,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
|
||||
},
|
||||
})
|
||||
|
||||
f.Close()
|
||||
return nil
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeSymlink != 0 {
|
||||
@@ -516,6 +562,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove pkgdir from the symlink's path
|
||||
link = strings.TrimPrefix(link, dirs.PkgDir)
|
||||
|
||||
contents = append(contents, &files.Content{
|
||||
@@ -541,6 +588,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
|
||||
},
|
||||
}
|
||||
|
||||
// If the file is supposed to be backed up, set its type to config|noreplace
|
||||
if slices.Contains(vars.Backup, trimmed) {
|
||||
fileContent.Type = "config|noreplace"
|
||||
}
|
||||
@@ -552,14 +600,16 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
|
||||
return contents, err
|
||||
}
|
||||
|
||||
func removeBuildDeps(buildDeps []string, opts types.BuildOpts) error {
|
||||
// removeBuildDeps asks the user if they'd like to remove the build dependencies that were
|
||||
// installed by installBuildDeps. If so, it uses the package manager to do that.
|
||||
func removeBuildDeps(ctx context.Context, buildDeps []string, opts types.BuildOpts) error {
|
||||
if len(buildDeps) > 0 {
|
||||
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove the build dependencies?", opts.Interactive, false)
|
||||
remove, err := cliutils.YesNoPrompt(ctx, "Would you like to remove the build dependencies?", opts.Interactive, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if removeBuildDeps {
|
||||
if remove {
|
||||
err = opts.Manager.Remove(
|
||||
&manager.Opts{
|
||||
AsRoot: true,
|
||||
@@ -575,6 +625,8 @@ func removeBuildDeps(buildDeps []string, opts types.BuildOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkForBuiltPackage tries to detect a previously-built package and returns its path
|
||||
// and true if it finds one. If it doesn't find it, it returns "", false, nil.
|
||||
func checkForBuiltPackage(mgr manager.Manager, vars *types.BuildVars, pkgFormat, baseDir string) (string, bool, error) {
|
||||
filename, err := pkgFileName(vars, pkgFormat)
|
||||
if err != nil {
|
||||
@@ -591,6 +643,8 @@ func checkForBuiltPackage(mgr manager.Manager, vars *types.BuildVars, pkgFormat,
|
||||
return pkgPath, true, nil
|
||||
}
|
||||
|
||||
// pkgFileName returns the filename of the package if it were to be built.
|
||||
// This is used to check if the package has already been built.
|
||||
func pkgFileName(vars *types.BuildVars, pkgFormat string) (string, error) {
|
||||
pkgInfo := &nfpm.Info{
|
||||
Name: vars.Name,
|
||||
@@ -608,6 +662,8 @@ func pkgFileName(vars *types.BuildVars, pkgFormat string) (string, error) {
|
||||
return packager.ConventionalFileName(pkgInfo), nil
|
||||
}
|
||||
|
||||
// getPkgFormat returns the package format of the package manager,
|
||||
// or LURE_PKG_FORMAT if that's set.
|
||||
func getPkgFormat(mgr manager.Manager) string {
|
||||
pkgFormat := mgr.Format()
|
||||
if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
|
||||
@@ -616,6 +672,8 @@ func getPkgFormat(mgr manager.Manager) string {
|
||||
return pkgFormat
|
||||
}
|
||||
|
||||
// createBuildEnvVars creates the environment variables that will be set in the
|
||||
// build script when it's executed.
|
||||
func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string {
|
||||
env := os.Environ()
|
||||
|
||||
@@ -645,7 +703,9 @@ func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string
|
||||
return env
|
||||
}
|
||||
|
||||
func getSources(ctx context.Context, srcdir string, bv *types.BuildVars) error {
|
||||
// getSources downloads the sources from the script.
|
||||
func getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars) error {
|
||||
log := loggerctx.From(ctx)
|
||||
if len(bv.Sources) != len(bv.Checksums) {
|
||||
log.Fatal("The checksums array must be the same length as sources").Send()
|
||||
}
|
||||
@@ -654,11 +714,15 @@ func getSources(ctx context.Context, srcdir string, bv *types.BuildVars) error {
|
||||
opts := dl.Options{
|
||||
Name: fmt.Sprintf("%s[%d]", bv.Name, i),
|
||||
URL: src,
|
||||
Destination: srcdir,
|
||||
Destination: dirs.SrcDir,
|
||||
Progress: os.Stderr,
|
||||
LocalDir: dirs.ScriptDir,
|
||||
}
|
||||
|
||||
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
|
||||
// If the checksum contains a colon, use the part before the colon
|
||||
// as the algorithm and the part after as the actual checksum.
|
||||
// Otherwise, use the default sha256 with the whole string as the checksum.
|
||||
algo, hashData, ok := strings.Cut(bv.Checksums[i], ":")
|
||||
if ok {
|
||||
checksum, err := hex.DecodeString(hashData)
|
||||
@@ -685,6 +749,7 @@ func getSources(ctx context.Context, srcdir string, bv *types.BuildVars) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setScripts adds any hook scripts to the package metadata.
|
||||
func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) {
|
||||
if vars.Scripts.PreInstall != "" {
|
||||
info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall)
|
||||
@@ -721,6 +786,8 @@ func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) {
|
||||
}
|
||||
}
|
||||
|
||||
// setVersion changes the version variable in the script runner.
|
||||
// It's used to set the version to the output of the version() function.
|
||||
func setVersion(ctx context.Context, r *interp.Runner, to string) error {
|
||||
fl, err := syntax.NewParser().Parse(strings.NewReader("version='"+to+"'"), "")
|
||||
if err != nil {
|
||||
@@ -729,34 +796,24 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error {
|
||||
return r.Run(ctx, fl)
|
||||
}
|
||||
|
||||
// filterBuildDeps returns a map without any dependencies that are already installed
|
||||
func filterBuildDeps(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
|
||||
out := map[string][]db.Package{}
|
||||
for name, pkgs := range found {
|
||||
var inner []db.Package
|
||||
for _, pkg := range pkgs {
|
||||
if _, ok := installed[pkg.Name]; !ok {
|
||||
addToFiltered := true
|
||||
for _, provides := range pkg.Provides.Val {
|
||||
if _, ok := installed[provides]; ok {
|
||||
addToFiltered = false
|
||||
break
|
||||
}
|
||||
}
|
||||
// removeAlreadyInstalled returns a map without any dependencies that are already installed
|
||||
func removeAlreadyInstalled(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
|
||||
filteredPackages := make(map[string][]db.Package)
|
||||
|
||||
if addToFiltered {
|
||||
inner = append(inner, pkg)
|
||||
}
|
||||
for name, pkgList := range found {
|
||||
filteredPkgList := []db.Package{}
|
||||
for _, pkg := range pkgList {
|
||||
if _, isInstalled := installed[pkg.Name]; !isInstalled {
|
||||
filteredPkgList = append(filteredPkgList, pkg)
|
||||
}
|
||||
}
|
||||
|
||||
if len(inner) > 0 {
|
||||
out[name] = inner
|
||||
}
|
||||
filteredPackages[name] = filteredPkgList
|
||||
}
|
||||
return out
|
||||
|
||||
return filteredPackages
|
||||
}
|
||||
|
||||
// packageNames returns the names of all the given packages
|
||||
func packageNames(pkgs []db.Package) []string {
|
||||
names := make([]string, len(pkgs))
|
||||
for i, p := range pkgs {
|
||||
@@ -765,6 +822,7 @@ func packageNames(pkgs []db.Package) []string {
|
||||
return names
|
||||
}
|
||||
|
||||
// removeDuplicates removes any duplicates from the given slice
|
||||
func removeDuplicates(slice []string) []string {
|
||||
seen := map[string]struct{}{}
|
||||
result := []string{}
|
||||
@@ -22,15 +22,17 @@ import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/internal/types"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
)
|
||||
|
||||
// InstallPkgs installs non-LURE packages via the package manager, then builds and installs LURE
|
||||
// packages
|
||||
// InstallPkgs installs native packages via the package manager,
|
||||
// then builds and installs the LURE packages
|
||||
func InstallPkgs(ctx context.Context, lurePkgs []db.Package, nativePkgs []string, opts types.BuildOpts) {
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
if len(nativePkgs) > 0 {
|
||||
err := opts.Manager.Install(nil, nativePkgs...)
|
||||
if err != nil {
|
||||
@@ -38,22 +40,23 @@ func InstallPkgs(ctx context.Context, lurePkgs []db.Package, nativePkgs []string
|
||||
}
|
||||
}
|
||||
|
||||
InstallScripts(ctx, GetScriptPaths(lurePkgs), opts)
|
||||
InstallScripts(ctx, GetScriptPaths(ctx, lurePkgs), opts)
|
||||
}
|
||||
|
||||
// GetScriptPaths generates a slice of script paths corresponding to the
|
||||
// GetScriptPaths returns a slice of script paths corresponding to the
|
||||
// given packages
|
||||
func GetScriptPaths(pkgs []db.Package) []string {
|
||||
func GetScriptPaths(ctx context.Context, pkgs []db.Package) []string {
|
||||
var scripts []string
|
||||
for _, pkg := range pkgs {
|
||||
scriptPath := filepath.Join(config.GetPaths().RepoDir, pkg.Repository, pkg.Name, "lure.sh")
|
||||
scriptPath := filepath.Join(config.GetPaths(ctx).RepoDir, pkg.Repository, pkg.Name, "lure.sh")
|
||||
scripts = append(scripts, scriptPath)
|
||||
}
|
||||
return scripts
|
||||
}
|
||||
|
||||
// InstallScripts builds and installs LURE build scripts
|
||||
// InstallScripts builds and installs the given LURE build scripts
|
||||
func InstallScripts(ctx context.Context, scripts []string, opts types.BuildOpts) {
|
||||
log := loggerctx.From(ctx)
|
||||
for _, script := range scripts {
|
||||
opts.Script = script
|
||||
builtPkgs, _, err := BuildPackage(ctx, opts)
|
||||
@@ -20,18 +20,16 @@ package distro
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"go.elara.ws/lure/internal/shutils"
|
||||
"lure.sh/lure/internal/shutils/handlers"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
var ErrParse = errors.New("could not parse os-release file")
|
||||
|
||||
// OSRelease contains information from an os-release file
|
||||
type OSRelease struct {
|
||||
Name string
|
||||
PrettyName string
|
||||
@@ -50,7 +48,8 @@ var parsed *OSRelease
|
||||
|
||||
// OSReleaseName returns a struct parsed from the system's os-release
|
||||
// file. It checks /etc/os-release as well as /usr/lib/os-release.
|
||||
// The returned OSRelease struct is a singleton.
|
||||
// The first time it's called, it'll parse the os-release file.
|
||||
// Subsequent calls will return the same value.
|
||||
func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
|
||||
if parsed != nil {
|
||||
return parsed, nil
|
||||
@@ -75,10 +74,10 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
|
||||
// as well as no environment variables in order to prevent vulnerabilities
|
||||
// caused by changing the os-release file.
|
||||
runner, err := interp.New(
|
||||
interp.OpenHandler(shutils.NopOpen),
|
||||
interp.ExecHandler(shutils.NopExec),
|
||||
interp.ReadDirHandler(shutils.NopReadDir),
|
||||
interp.StatHandler(shutils.NopStat),
|
||||
interp.OpenHandler(handlers.NopOpen),
|
||||
interp.ExecHandler(handlers.NopExec),
|
||||
interp.ReadDirHandler(handlers.NopReadDir),
|
||||
interp.StatHandler(handlers.NopStat),
|
||||
interp.Env(expand.ListEnviron()),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -87,7 +86,7 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
|
||||
|
||||
err = runner.Run(ctx, file)
|
||||
if err != nil {
|
||||
return nil, ErrParse
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := &OSRelease{
|
||||
13
pkg/gen/funcs.go
Normal file
13
pkg/gen/funcs.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var funcs = template.FuncMap{
|
||||
"tolower": strings.ToLower,
|
||||
"firstchar": func(s string) string {
|
||||
return s[:1]
|
||||
},
|
||||
}
|
||||
84
pkg/gen/pip.go
Normal file
84
pkg/gen/pip.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
//go:embed tmpls/pip.tmpl.sh
|
||||
var pipTmpl string
|
||||
|
||||
type PipOptions struct {
|
||||
Name string
|
||||
Version string
|
||||
Description string
|
||||
}
|
||||
|
||||
type pypiAPIResponse struct {
|
||||
Info pypiInfo `json:"info"`
|
||||
URLs []pypiURL `json:"urls"`
|
||||
}
|
||||
|
||||
func (res pypiAPIResponse) SourceURL() (pypiURL, error) {
|
||||
for _, url := range res.URLs {
|
||||
if url.PackageType == "sdist" {
|
||||
return url, nil
|
||||
}
|
||||
}
|
||||
return pypiURL{}, errors.New("package doesn't have a source distribution")
|
||||
}
|
||||
|
||||
type pypiInfo struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Summary string `json:"summary"`
|
||||
Homepage string `json:"home_page"`
|
||||
License string `json:"license"`
|
||||
}
|
||||
|
||||
type pypiURL struct {
|
||||
Digests map[string]string `json:"digests"`
|
||||
Filename string `json:"filename"`
|
||||
PackageType string `json:"packagetype"`
|
||||
}
|
||||
|
||||
func Pip(w io.Writer, opts PipOptions) error {
|
||||
tmpl, err := template.New("pip").
|
||||
Funcs(funcs).
|
||||
Parse(pipTmpl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(
|
||||
"https://pypi.org/pypi/%s/%s/json",
|
||||
opts.Name,
|
||||
opts.Version,
|
||||
)
|
||||
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
return fmt.Errorf("pypi: %s", res.Status)
|
||||
}
|
||||
|
||||
var resp pypiAPIResponse
|
||||
err = json.NewDecoder(res.Body).Decode(&resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.Description != "" {
|
||||
resp.Info.Summary = opts.Description
|
||||
}
|
||||
|
||||
return tmpl.Execute(w, resp)
|
||||
}
|
||||
31
pkg/gen/tmpls/pip.tmpl.sh
Normal file
31
pkg/gen/tmpls/pip.tmpl.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
name='{{.Info.Name | tolower}}'
|
||||
version='{{.Info.Version}}'
|
||||
release='1'
|
||||
desc='{{.Info.Summary}}'
|
||||
homepage='{{.Info.Homepage}}'
|
||||
maintainer='Example <user@example.com>'
|
||||
architectures=('all')
|
||||
license=('{{if .Info.License | ne ""}}{{.Info.License}}{{else}}custom:Unknown{{end}}')
|
||||
provides=('{{.Info.Name | tolower}}')
|
||||
conflicts=('{{.Info.Name | tolower}}')
|
||||
|
||||
deps=("python3")
|
||||
deps_arch=("python")
|
||||
deps_alpine=("python3")
|
||||
|
||||
build_deps=("python3" "python3-setuptools")
|
||||
build_deps_arch=("python" "python-setuptools")
|
||||
build_deps_alpine=("python3" "py3-setuptools")
|
||||
|
||||
sources=("https://files.pythonhosted.org/packages/source/{{.SourceURL.Filename | firstchar}}/{{.Info.Name}}/{{.SourceURL.Filename}}")
|
||||
checksums=('blake2b-256:{{.SourceURL.Digests.blake2b_256}}')
|
||||
|
||||
build() {
|
||||
cd "$srcdir/{{.Info.Name}}-${version}"
|
||||
python3 setup.py build
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir/{{.Info.Name}}-${version}"
|
||||
python3 setup.py install --root="${pkgdir}/" --optimize=1 || return 1
|
||||
}
|
||||
29
pkg/loggerctx/log.go
Normal file
29
pkg/loggerctx/log.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package loggerctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.elara.ws/logger"
|
||||
)
|
||||
|
||||
// loggerCtxKey is used as the context key for loggers
|
||||
type loggerCtxKey struct{}
|
||||
|
||||
// With returns a copy of ctx containing log
|
||||
func With(ctx context.Context, log logger.Logger) context.Context {
|
||||
return context.WithValue(ctx, loggerCtxKey{}, log)
|
||||
}
|
||||
|
||||
// From attempts to get a logger from ctx. If ctx doesn't
|
||||
// contain a logger, it returns a nop logger.
|
||||
func From(ctx context.Context) logger.Logger {
|
||||
if val := ctx.Value(loggerCtxKey{}); val != nil {
|
||||
if log, ok := val.(logger.Logger); ok && log != nil {
|
||||
return log
|
||||
} else {
|
||||
return logger.NewNop()
|
||||
}
|
||||
} else {
|
||||
return logger.NewNop()
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,16 @@
|
||||
|
||||
package repos
|
||||
|
||||
import "go.elara.ws/lure/internal/db"
|
||||
import (
|
||||
"context"
|
||||
|
||||
"lure.sh/lure/internal/db"
|
||||
)
|
||||
|
||||
// FindPkgs looks for packages matching the inputs inside the database.
|
||||
// It returns a map that maps the package name input to the packages found for it.
|
||||
// It returns a map that maps the package name input to any packages found for it.
|
||||
// It also returns a slice that contains the names of all packages that were not found.
|
||||
func FindPkgs(pkgs []string) (map[string][]db.Package, []string, error) {
|
||||
func FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) {
|
||||
found := map[string][]db.Package{}
|
||||
notFound := []string(nil)
|
||||
|
||||
@@ -32,7 +36,7 @@ func FindPkgs(pkgs []string) (map[string][]db.Package, []string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
result, err := db.GetPkgs("name LIKE ?", pkgName)
|
||||
result, err := db.GetPkgs(ctx, "json_array_contains(provides, ?)", pkgName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -51,7 +55,7 @@ func FindPkgs(pkgs []string) (map[string][]db.Package, []string, error) {
|
||||
result.Close()
|
||||
|
||||
if added == 0 {
|
||||
result, err := db.GetPkgs("json_array_contains(provides, ?)", pkgName)
|
||||
result, err := db.GetPkgs(ctx, "name LIKE ?", pkgName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/repos"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/internal/types"
|
||||
"lure.sh/lure/pkg/repos"
|
||||
)
|
||||
|
||||
func TestFindPkgs(t *testing.T) {
|
||||
@@ -34,14 +34,14 @@ import (
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/format/diff"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"go.elara.ws/lure/distro"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/shutils"
|
||||
"go.elara.ws/lure/internal/shutils/decoder"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"go.elara.ws/vercmp"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/internal/shutils/decoder"
|
||||
"lure.sh/lure/internal/shutils/handlers"
|
||||
"lure.sh/lure/internal/types"
|
||||
"lure.sh/lure/pkg/distro"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
@@ -49,8 +49,15 @@ import (
|
||||
|
||||
// Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned
|
||||
// and its packages will be written to the DB. If it does exist, it will be pulled.
|
||||
// In this case, only changed packages will be processed.
|
||||
// In this case, only changed packages will be processed if possible.
|
||||
// If repos is set to nil, the repos in the LURE config will be used.
|
||||
func Pull(ctx context.Context, repos []types.Repo) error {
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
if repos == nil {
|
||||
repos = config.Config(ctx).Repos
|
||||
}
|
||||
|
||||
for _, repo := range repos {
|
||||
repoURL, err := url.Parse(repo.URL)
|
||||
if err != nil {
|
||||
@@ -58,7 +65,7 @@ func Pull(ctx context.Context, repos []types.Repo) error {
|
||||
}
|
||||
|
||||
log.Info("Pulling repository").Str("name", repo.Name).Send()
|
||||
repoDir := filepath.Join(config.GetPaths().RepoDir, repo.Name)
|
||||
repoDir := filepath.Join(config.GetPaths(ctx).RepoDir, repo.Name)
|
||||
|
||||
var repoFS billy.Filesystem
|
||||
gitDir := filepath.Join(repoDir, ".git")
|
||||
@@ -88,7 +95,7 @@ func Pull(ctx context.Context, repos []types.Repo) error {
|
||||
repoFS = w.Filesystem
|
||||
|
||||
// Make sure the DB is created even if the repo is up to date
|
||||
if !errors.Is(err, git.NoErrAlreadyUpToDate) || db.IsEmpty() {
|
||||
if !errors.Is(err, git.NoErrAlreadyUpToDate) || db.IsEmpty(ctx) {
|
||||
new, err := r.Head()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -97,7 +104,7 @@ func Pull(ctx context.Context, repos []types.Repo) error {
|
||||
// If the DB was not present at startup, that means it's
|
||||
// empty. In this case, we need to update the DB fully
|
||||
// rather than just incrementally.
|
||||
if db.IsEmpty() {
|
||||
if db.IsEmpty(ctx) {
|
||||
err = processRepoFull(ctx, repo, repoDir)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -149,9 +156,13 @@ func Pull(ctx context.Context, repos []types.Repo) error {
|
||||
}
|
||||
fl.Close()
|
||||
|
||||
currentVer, _, _ := strings.Cut(config.Version, "-")
|
||||
if vercmp.Compare(currentVer, repoCfg.Repo.MinVersion) == -1 {
|
||||
log.Warn("LURE repo's minumum LURE version is greater than the current version. Try updating LURE if something doesn't work.").Str("repo", repo.Name).Send()
|
||||
// If the version doesn't have a "v" prefix, it's not a standard version.
|
||||
// It may be "unknown" or a git version, but either way, there's no way
|
||||
// to compare it to the repo version, so only compare versions with the "v".
|
||||
if strings.HasPrefix(config.Version, "v") {
|
||||
if vercmp.Compare(config.Version, repoCfg.Repo.MinVersion) == -1 {
|
||||
log.Warn("LURE repo's minumum LURE version is greater than the current version. Try updating LURE if something doesn't work.").Str("repo", repo.Name).Send()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,11 +243,11 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
|
||||
env := append(os.Environ(), "scriptdir="+filepath.Dir(filepath.Join(repoDir, action.File)))
|
||||
runner, err := interp.New(
|
||||
interp.Env(expand.ListEnviron(env...)),
|
||||
interp.ExecHandler(shutils.NopExec),
|
||||
interp.ReadDirHandler(shutils.RestrictedReadDir(repoDir)),
|
||||
interp.StatHandler(shutils.RestrictedStat(repoDir)),
|
||||
interp.OpenHandler(shutils.RestrictedOpen(repoDir)),
|
||||
interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
|
||||
interp.ExecHandler(handlers.NopExec),
|
||||
interp.ReadDirHandler(handlers.RestrictedReadDir(repoDir)),
|
||||
interp.StatHandler(handlers.RestrictedStat(repoDir)),
|
||||
interp.OpenHandler(handlers.RestrictedOpen(repoDir)),
|
||||
interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -264,7 +275,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
|
||||
return err
|
||||
}
|
||||
|
||||
err = db.DeletePkgs("name = ? AND repository = ?", pkg.Name, repo.Name)
|
||||
err = db.DeletePkgs(ctx, "name = ? AND repository = ?", pkg.Name, repo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -299,7 +310,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
|
||||
|
||||
resolveOverrides(runner, &pkg)
|
||||
|
||||
err = db.InsertPackage(pkg)
|
||||
err = db.InsertPackage(ctx, pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -338,11 +349,11 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error
|
||||
env := append(os.Environ(), "scriptdir="+filepath.Dir(match))
|
||||
runner, err := interp.New(
|
||||
interp.Env(expand.ListEnviron(env...)),
|
||||
interp.ExecHandler(shutils.NopExec),
|
||||
interp.ReadDirHandler(shutils.RestrictedReadDir(repoDir)),
|
||||
interp.StatHandler(shutils.RestrictedStat(repoDir)),
|
||||
interp.OpenHandler(shutils.RestrictedOpen(repoDir)),
|
||||
interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
|
||||
interp.ExecHandler(handlers.NopExec),
|
||||
interp.ReadDirHandler(handlers.RestrictedReadDir(repoDir)),
|
||||
interp.StatHandler(handlers.RestrictedStat(repoDir)),
|
||||
interp.OpenHandler(handlers.RestrictedOpen(repoDir)),
|
||||
interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -369,7 +380,7 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error
|
||||
|
||||
resolveOverrides(runner, &pkg)
|
||||
|
||||
err = db.InsertPackage(pkg)
|
||||
err = db.InsertPackage(ctx, pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -24,10 +24,10 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/repos"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/internal/types"
|
||||
"lure.sh/lure/pkg/repos"
|
||||
)
|
||||
|
||||
func setCfgDirs(t *testing.T) {
|
||||
167
pkg/search/search.go
Normal file
167
pkg/search/search.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/db"
|
||||
)
|
||||
|
||||
// Filter represents search filters.
|
||||
type Filter int
|
||||
|
||||
// Filters
|
||||
const (
|
||||
FilterNone Filter = iota
|
||||
FilterInRepo
|
||||
FilterSupportsArch
|
||||
)
|
||||
|
||||
// SoryBy represents a value that packages can be sorted by.
|
||||
type SortBy int
|
||||
|
||||
// Sort values
|
||||
const (
|
||||
SortByNone = iota
|
||||
SortByName
|
||||
SortByRepo
|
||||
SortByVersion
|
||||
)
|
||||
|
||||
// Package represents a package from LURE's database
|
||||
type Package struct {
|
||||
Name string
|
||||
Version string
|
||||
Release int
|
||||
Epoch uint
|
||||
Description map[string]string
|
||||
Homepage map[string]string
|
||||
Maintainer map[string]string
|
||||
Architectures []string
|
||||
Licenses []string
|
||||
Provides []string
|
||||
Conflicts []string
|
||||
Replaces []string
|
||||
Depends map[string][]string
|
||||
BuildDepends map[string][]string
|
||||
OptDepends map[string][]string
|
||||
Repository string
|
||||
}
|
||||
|
||||
func convertPkg(p db.Package) Package {
|
||||
return Package{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
Release: p.Release,
|
||||
Epoch: p.Epoch,
|
||||
Description: p.Description.Val,
|
||||
Homepage: p.Homepage.Val,
|
||||
Maintainer: p.Maintainer.Val,
|
||||
Architectures: p.Architectures.Val,
|
||||
Licenses: p.Licenses.Val,
|
||||
Provides: p.Provides.Val,
|
||||
Conflicts: p.Conflicts.Val,
|
||||
Replaces: p.Replaces.Val,
|
||||
Depends: p.Depends.Val,
|
||||
BuildDepends: p.BuildDepends.Val,
|
||||
OptDepends: p.OptDepends.Val,
|
||||
Repository: p.Repository,
|
||||
}
|
||||
}
|
||||
|
||||
// Options contains the options for a search.
|
||||
type Options struct {
|
||||
Filter Filter
|
||||
FilterValue string
|
||||
SortBy SortBy
|
||||
Limit int64
|
||||
Query string
|
||||
}
|
||||
|
||||
// Search searches for packages in the database based on the given options.
|
||||
func Search(ctx context.Context, opts Options) ([]Package, error) {
|
||||
query := "(name LIKE ? OR description LIKE ? OR json_array_contains(provides, ?))"
|
||||
args := []any{"%" + opts.Query + "%", "%" + opts.Query + "%", opts.Query}
|
||||
|
||||
if opts.Filter != FilterNone {
|
||||
switch opts.Filter {
|
||||
case FilterInRepo:
|
||||
query += " AND repository = ?"
|
||||
case FilterSupportsArch:
|
||||
query += " AND json_array_contains(architectures, ?)"
|
||||
}
|
||||
args = append(args, opts.FilterValue)
|
||||
}
|
||||
|
||||
if opts.SortBy != SortByNone {
|
||||
switch opts.SortBy {
|
||||
case SortByName:
|
||||
query += " ORDER BY name"
|
||||
case SortByRepo:
|
||||
query += " ORDER BY repository"
|
||||
case SortByVersion:
|
||||
query += " ORDER BY version"
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Limit != 0 {
|
||||
query += " LIMIT " + strconv.FormatInt(opts.Limit, 10)
|
||||
}
|
||||
|
||||
result, err := db.GetPkgs(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out []Package
|
||||
for result.Next() {
|
||||
pkg := db.Package{}
|
||||
err = result.StructScan(&pkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, convertPkg(pkg))
|
||||
}
|
||||
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GetPkg gets a single package from the database and returns it.
|
||||
func GetPkg(ctx context.Context, repo, name string) (Package, error) {
|
||||
pkg, err := db.GetPkg(ctx, "name = ? AND repository = ?", name, repo)
|
||||
return convertPkg(*pkg), err
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrInvalidArgument is an error returned by GetScript when one of its arguments
|
||||
// contain invalid characters
|
||||
ErrInvalidArgument = errors.New("name and repository must not contain . or /")
|
||||
|
||||
// ErrScriptNotFound is returned by GetScript if it can't find the script requested
|
||||
// by the user.
|
||||
ErrScriptNotFound = errors.New("requested script not found")
|
||||
)
|
||||
|
||||
// GetScript returns a reader containing the build script for a given package.
|
||||
func GetScript(ctx context.Context, repo, name string) (io.ReadCloser, error) {
|
||||
if strings.Contains(name, "./") || strings.ContainsAny(repo, "./") {
|
||||
return nil, ErrInvalidArgument
|
||||
}
|
||||
|
||||
scriptPath := filepath.Join(config.GetPaths(ctx).RepoDir, repo, name, "lure.sh")
|
||||
fl, err := os.Open(scriptPath)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, ErrScriptNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fl, nil
|
||||
}
|
||||
34
repo.go
34
repo.go
@@ -24,11 +24,11 @@ import (
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/repos"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/internal/types"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"lure.sh/lure/pkg/repos"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
@@ -51,10 +51,13 @@ var addrepoCmd = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.Context
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
name := c.String("name")
|
||||
repoURL := c.String("url")
|
||||
|
||||
cfg := config.Config()
|
||||
cfg := config.Config(ctx)
|
||||
|
||||
for _, repo := range cfg.Repos {
|
||||
if repo.URL == repoURL {
|
||||
@@ -67,7 +70,7 @@ var addrepoCmd = &cli.Command{
|
||||
URL: repoURL,
|
||||
})
|
||||
|
||||
cfgFl, err := os.Create(config.GetPaths().ConfigPath)
|
||||
cfgFl, err := os.Create(config.GetPaths(ctx).ConfigPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error opening config file").Err(err).Send()
|
||||
}
|
||||
@@ -77,7 +80,7 @@ var addrepoCmd = &cli.Command{
|
||||
log.Fatal("Error encoding config").Err(err).Send()
|
||||
}
|
||||
|
||||
err = repos.Pull(c.Context, cfg.Repos)
|
||||
err = repos.Pull(ctx, cfg.Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repos").Err(err).Send()
|
||||
}
|
||||
@@ -99,8 +102,11 @@ var removerepoCmd = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.Context
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
name := c.String("name")
|
||||
cfg := config.Config()
|
||||
cfg := config.Config(ctx)
|
||||
|
||||
found := false
|
||||
index := 0
|
||||
@@ -116,7 +122,7 @@ var removerepoCmd = &cli.Command{
|
||||
|
||||
cfg.Repos = slices.Delete(cfg.Repos, index, index+1)
|
||||
|
||||
cfgFl, err := os.Create(config.GetPaths().ConfigPath)
|
||||
cfgFl, err := os.Create(config.GetPaths(ctx).ConfigPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error opening config file").Err(err).Send()
|
||||
}
|
||||
@@ -126,12 +132,12 @@ var removerepoCmd = &cli.Command{
|
||||
log.Fatal("Error encoding config").Err(err).Send()
|
||||
}
|
||||
|
||||
err = os.RemoveAll(filepath.Join(config.GetPaths().RepoDir, name))
|
||||
err = os.RemoveAll(filepath.Join(config.GetPaths(ctx).RepoDir, name))
|
||||
if err != nil {
|
||||
log.Fatal("Error removing repo directory").Err(err).Send()
|
||||
}
|
||||
|
||||
err = db.DeletePkgs("repository = ?", name)
|
||||
err = db.DeletePkgs(ctx, "repository = ?", name)
|
||||
if err != nil {
|
||||
log.Fatal("Error removing packages from database").Err(err).Send()
|
||||
}
|
||||
@@ -145,7 +151,9 @@ var refreshCmd = &cli.Command{
|
||||
Usage: "Pull all repositories that have changed",
|
||||
Aliases: []string{"ref"},
|
||||
Action: func(c *cli.Context) error {
|
||||
err := repos.Pull(c.Context, config.Config().Repos)
|
||||
ctx := c.Context
|
||||
log := loggerctx.From(ctx)
|
||||
err := repos.Pull(ctx, config.Config(ctx).Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repos").Err(err).Send()
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
git describe --tags > version.txt
|
||||
@@ -42,6 +42,7 @@ installPkg() {
|
||||
case $1 in
|
||||
pacman) $rootCmd pacman --noconfirm -U ${@:2} ;;
|
||||
apk) $rootCmd apk add --allow-untrusted ${@:2} ;;
|
||||
zypper) $rootCmd zypper --no-gpg-checks install ${@:2} ;;
|
||||
*) $rootCmd $1 install -y ${@:2} ;;
|
||||
esac
|
||||
}
|
||||
@@ -80,11 +81,11 @@ else
|
||||
error "No supported package manager detected!"
|
||||
fi
|
||||
|
||||
latestVersion=$(curl -sI 'https://gitea.elara.ws/Elara6331/lure/releases/latest' | grep -io 'location: .*' | rev | cut -d '/' -f1 | rev | tr -d '[:space:]')
|
||||
latestVersion=$(curl -sI 'https://gitea.elara.ws/lure/lure/releases/latest' | grep -io 'location: .*' | rev | cut -d '/' -f1 | rev | tr -d '[:space:]')
|
||||
info "Found latest LURE version:" $latestVersion
|
||||
|
||||
fname="$(mktemp -u -p /tmp "lure.XXXXXXXXXX").${pkgFormat}"
|
||||
url="https://gitea.elara.ws/Elara6331/lure/releases/download/${latestVersion}/linux-user-repository-${latestVersion#v}-linux-$(uname -m).${pkgFormat}"
|
||||
url="https://gitea.elara.ws/lure/lure/releases/download/${latestVersion}/linux-user-repository-${latestVersion#v}-linux-$(uname -m).${pkgFormat}"
|
||||
|
||||
info "Downloading LURE package"
|
||||
curl -L $url -o $fname
|
||||
|
||||
31
upgrade.go
31
upgrade.go
@@ -23,14 +23,14 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.elara.ws/lure/distro"
|
||||
"go.elara.ws/lure/internal/build"
|
||||
"go.elara.ws/lure/internal/config"
|
||||
"go.elara.ws/lure/internal/db"
|
||||
"go.elara.ws/lure/internal/log"
|
||||
"go.elara.ws/lure/internal/repos"
|
||||
"go.elara.ws/lure/internal/types"
|
||||
"go.elara.ws/lure/manager"
|
||||
"lure.sh/lure/internal/config"
|
||||
"lure.sh/lure/internal/db"
|
||||
"lure.sh/lure/internal/types"
|
||||
"lure.sh/lure/pkg/build"
|
||||
"lure.sh/lure/pkg/distro"
|
||||
"lure.sh/lure/pkg/loggerctx"
|
||||
"lure.sh/lure/pkg/manager"
|
||||
"lure.sh/lure/pkg/repos"
|
||||
"go.elara.ws/vercmp"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
@@ -48,7 +48,10 @@ var upgradeCmd = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
info, err := distro.ParseOSRelease(c.Context)
|
||||
ctx := c.Context
|
||||
log := loggerctx.From(ctx)
|
||||
|
||||
info, err := distro.ParseOSRelease(ctx)
|
||||
if err != nil {
|
||||
log.Fatal("Error parsing os-release file").Err(err).Send()
|
||||
}
|
||||
@@ -58,18 +61,18 @@ var upgradeCmd = &cli.Command{
|
||||
log.Fatal("Unable to detect a supported package manager on the system").Send()
|
||||
}
|
||||
|
||||
err = repos.Pull(c.Context, config.Config().Repos)
|
||||
err = repos.Pull(ctx, config.Config(ctx).Repos)
|
||||
if err != nil {
|
||||
log.Fatal("Error pulling repos").Err(err).Send()
|
||||
}
|
||||
|
||||
updates, err := checkForUpdates(c.Context, mgr, info)
|
||||
updates, err := checkForUpdates(ctx, mgr, info)
|
||||
if err != nil {
|
||||
log.Fatal("Error checking for updates").Err(err).Send()
|
||||
}
|
||||
|
||||
if len(updates) > 0 {
|
||||
build.InstallPkgs(c.Context, updates, nil, types.BuildOpts{
|
||||
build.InstallPkgs(ctx, updates, nil, types.BuildOpts{
|
||||
Manager: mgr,
|
||||
Clean: c.Bool("clean"),
|
||||
Interactive: c.Bool("interactive"),
|
||||
@@ -89,14 +92,14 @@ func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRe
|
||||
}
|
||||
|
||||
pkgNames := maps.Keys(installed)
|
||||
found, _, err := repos.FindPkgs(pkgNames)
|
||||
found, _, err := repos.FindPkgs(ctx, pkgNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var out []db.Package
|
||||
for pkgName, pkgs := range found {
|
||||
if slices.Contains(config.Config().IgnorePkgUpdates, pkgName) {
|
||||
if slices.Contains(config.Config(ctx).IgnorePkgUpdates, pkgName) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user