Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d97210635 | |||
| 51f05ecc81 | |||
| 27082ba223 | |||
| 6c6a7152b4 | |||
| 681e31df4e | |||
| a84a9be782 | |||
| 825e89d0d3 | |||
| eb88fbd123 | |||
| 3c5613199e | |||
| 73bdb54496 | |||
| 4b5fd855cc | |||
| 2c8eb6be48 | |||
| d4396756d6 | |||
| c8be92c47b | |||
| ee7f4878d1 | |||
| bd769468a9 | |||
| 09d9053104 | |||
| d914391c40 | |||
| 0ece536810 | |||
| 192e3e8a4f | |||
| fa4604c26c | |||
| 163ad12575 | |||
| 2ba6136c99 | |||
| 438304f8ce | |||
| 657b562f31 | |||
| d26b288cde | |||
| e785c6b53d | |||
| ff8ed902ea | |||
| 1d2d46cbc5 | |||
| 374c206fae | |||
| d906dc8d86 | |||
| 2f81f7c605 | |||
| bb05a8d38b | |||
| 076f90bbd7 | |||
| e772ecf2ab | |||
| 3e0c110893 | |||
| 238f4cf682 | |||
| c41e7e1c18 | |||
| 0144ad17d9 | |||
| ddd9d1d63d | |||
| 0cb23911cf | |||
| de8399e40b | |||
| d226a6c154 | |||
| c2e0332552 |
@@ -20,6 +20,8 @@ archives:
|
|||||||
386: i386
|
386: i386
|
||||||
amd64: x86_64
|
amd64: x86_64
|
||||||
arm64: aarch64
|
arm64: aarch64
|
||||||
|
files:
|
||||||
|
- scripts/completion/*
|
||||||
nfpms:
|
nfpms:
|
||||||
- id: lure
|
- id: lure
|
||||||
package_name: linux-user-repository
|
package_name: linux-user-repository
|
||||||
@@ -38,27 +40,27 @@ nfpms:
|
|||||||
- rpm
|
- rpm
|
||||||
- archlinux
|
- archlinux
|
||||||
provides:
|
provides:
|
||||||
- lure
|
- linux-user-repository
|
||||||
conflicts:
|
conflicts:
|
||||||
- lure
|
- linux-user-repository
|
||||||
contents:
|
contents:
|
||||||
- src: scripts/completion/bash
|
- src: scripts/completion/bash
|
||||||
dst: /usr/share/bash-completion/completions/lure
|
dst: /usr/share/bash-completion/completions/lure
|
||||||
- src: scripts/completion/zsh
|
- src: scripts/completion/zsh
|
||||||
dst: /usr/share/zsh/site-functions/_lure
|
dst: /usr/share/zsh/site-functions/_lure
|
||||||
aurs:
|
aurs:
|
||||||
- name: lure-bin
|
- name: linux-user-repository-bin
|
||||||
homepage: 'https://gitea.arsenm.dev/Arsen6331/lure'
|
homepage: 'https://gitea.arsenm.dev/Arsen6331/lure'
|
||||||
description: "Linux User REpository"
|
description: "Linux User REpository"
|
||||||
maintainers:
|
maintainers:
|
||||||
- 'Arsen Musayelyan <arsen@arsenm.dev>'
|
- 'Arsen Musayelyan <arsen@arsenm.dev>'
|
||||||
license: GPLv3
|
license: GPLv3
|
||||||
private_key: '{{ .Env.AUR_KEY }}'
|
private_key: '{{ .Env.AUR_KEY }}'
|
||||||
git_url: 'ssh://aur@aur.archlinux.org/lure-bin.git'
|
git_url: 'ssh://aur@aur.archlinux.org/linux-user-repository-bin.git'
|
||||||
provides:
|
provides:
|
||||||
- lure
|
- linux-user-repository
|
||||||
conflicts:
|
conflicts:
|
||||||
- lure
|
- linux-user-repository
|
||||||
depends:
|
depends:
|
||||||
- sudo
|
- sudo
|
||||||
- pacman
|
- pacman
|
||||||
@@ -67,8 +69,8 @@ aurs:
|
|||||||
install -Dm755 ./lure "${pkgdir}/usr/bin/lure"
|
install -Dm755 ./lure "${pkgdir}/usr/bin/lure"
|
||||||
|
|
||||||
# completions
|
# completions
|
||||||
install -Dm755 ./scripts/completion/bash /usr/share/bash-completion/completions/lure
|
install -Dm755 ./scripts/completion/bash ${pkgdir}/usr/share/bash-completion/completions/lure
|
||||||
install -Dm755 ./scripts/completion/zsh /usr/share/zsh/site-functions/_lure
|
install -Dm755 ./scripts/completion/zsh ${pkgdir}/usr/share/zsh/site-functions/_lure
|
||||||
release:
|
release:
|
||||||
gitea:
|
gitea:
|
||||||
owner: Arsen6331
|
owner: Arsen6331
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -1,6 +1,6 @@
|
|||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
|
|
||||||
lure: version.txt
|
lure: internal/config/version.txt
|
||||||
go build
|
go build
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@@ -16,7 +16,7 @@ installmisc:
|
|||||||
uninstall:
|
uninstall:
|
||||||
rm -f /usr/local/bin/lure
|
rm -f /usr/local/bin/lure
|
||||||
|
|
||||||
version.txt:
|
internal/config/version.txt:
|
||||||
go generate ./...
|
go generate ./internal/config
|
||||||
|
|
||||||
.PHONY: install clean uninstall
|
.PHONY: install clean uninstall
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
[](https://goreportcard.com/report/go.arsenm.dev/lure)
|
[](https://goreportcard.com/report/go.arsenm.dev/lure)
|
||||||
[](https://ci.arsenm.dev/Arsen6331/lure)
|
[](https://ci.arsenm.dev/Arsen6331/lure)
|
||||||
[](https://aur.archlinux.org/packages/lure-bin/)
|
[](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 an ***alpha*** state and may not be stable. It is currently able to successfully and consistently build and install packages on various distributions, but there are some bugs that still need to be ironed out.
|
LURE is 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.
|
||||||
|
|
||||||
LURE is written in pure Go and has zero dependencies after building. The only things LURE requires are a command for privilege elevation such as `sudo`, `doas`, etc. as well as a supported package manager. Currently, LURE supports `apt`, `pacman`, `apk`, `dnf`, `yum`, and `zypper`. If a supported package manager exists on your system, it will be detected and used automatically.
|
LURE is written in pure Go and has zero dependencies after building. The only things LURE requires are a command for privilege elevation such as `sudo`, `doas`, etc. as well as a supported package manager. Currently, LURE supports `apt`, `pacman`, `apk`, `dnf`, `yum`, and `zypper`. If a supported package manager exists on your system, it will be detected and used automatically.
|
||||||
|
|
||||||
|
|||||||
159
build.go
159
build.go
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -40,10 +41,11 @@ import (
|
|||||||
"github.com/goreleaser/nfpm/v2/files"
|
"github.com/goreleaser/nfpm/v2/files"
|
||||||
"go.arsenm.dev/logger/log"
|
"go.arsenm.dev/logger/log"
|
||||||
"go.arsenm.dev/lure/distro"
|
"go.arsenm.dev/lure/distro"
|
||||||
"go.arsenm.dev/lure/download"
|
|
||||||
"go.arsenm.dev/lure/internal/cliutils"
|
"go.arsenm.dev/lure/internal/cliutils"
|
||||||
"go.arsenm.dev/lure/internal/config"
|
"go.arsenm.dev/lure/internal/config"
|
||||||
"go.arsenm.dev/lure/internal/cpu"
|
"go.arsenm.dev/lure/internal/cpu"
|
||||||
|
"go.arsenm.dev/lure/internal/db"
|
||||||
|
"go.arsenm.dev/lure/internal/dl"
|
||||||
"go.arsenm.dev/lure/internal/repos"
|
"go.arsenm.dev/lure/internal/repos"
|
||||||
"go.arsenm.dev/lure/internal/shutils"
|
"go.arsenm.dev/lure/internal/shutils"
|
||||||
"go.arsenm.dev/lure/internal/shutils/decoder"
|
"go.arsenm.dev/lure/internal/shutils/decoder"
|
||||||
@@ -100,7 +102,7 @@ func buildCmd(c *cli.Context) error {
|
|||||||
log.Fatal("Unable to detect supported package manager on system").Send()
|
log.Fatal("Unable to detect supported package manager on system").Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgPaths, _, err := buildPackage(c.Context, script, mgr)
|
pkgPaths, _, err := buildPackage(c.Context, script, mgr, c.Bool("clean"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error building package").Err(err).Send()
|
log.Fatal("Error building package").Err(err).Send()
|
||||||
}
|
}
|
||||||
@@ -123,7 +125,7 @@ func buildCmd(c *cli.Context) error {
|
|||||||
|
|
||||||
// buildPackage builds the script at the given path. It returns two slices. One contains the paths
|
// 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).
|
// to the built package(s), the other contains the names of the built package(s).
|
||||||
func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]string, []string, error) {
|
func buildPackage(ctx context.Context, script string, mgr manager.Manager, clean bool) ([]string, []string, error) {
|
||||||
info, err := distro.ParseOSRelease(ctx)
|
info, err := distro.ParseOSRelease(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -187,13 +189,28 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cliutils.PromptViewScript(script, vars.Name, cfg.PagerStyle)
|
baseDir := filepath.Join(config.PkgsDir, vars.Name)
|
||||||
|
srcdir := filepath.Join(baseDir, "src")
|
||||||
|
pkgdir := filepath.Join(baseDir, "pkg")
|
||||||
|
|
||||||
|
if !clean {
|
||||||
|
builtPkgPath, ok, err := checkForBuiltPackage(mgr, &vars, getPkgFormat(mgr), baseDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
return []string{builtPkgPath}, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cliutils.PromptViewScript(script, vars.Name, cfg.PagerStyle, translator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
|
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !archMatches(vars.Architectures) {
|
if !archMatches(vars.Architectures) {
|
||||||
buildAnyway, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", true)
|
buildAnyway, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", true, translator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -230,10 +247,6 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
|||||||
dec.LikeDistros = false
|
dec.LikeDistros = false
|
||||||
}
|
}
|
||||||
|
|
||||||
baseDir := filepath.Join(config.PkgsDir, vars.Name)
|
|
||||||
srcdir := filepath.Join(baseDir, "src")
|
|
||||||
pkgdir := filepath.Join(baseDir, "pkg")
|
|
||||||
|
|
||||||
err = os.RemoveAll(baseDir)
|
err = os.RemoveAll(baseDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -254,21 +267,27 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var buildDeps []string
|
if instVer, ok := installed[vars.Name]; ok {
|
||||||
for _, pkgName := range vars.BuildDepends {
|
log.Warn("This package is already installed").
|
||||||
if _, ok := installed[pkgName]; !ok {
|
Str("name", vars.Name).
|
||||||
buildDeps = append(buildDeps, pkgName)
|
Str("version", instVer).
|
||||||
}
|
Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(buildDeps) > 0 {
|
var buildDeps []string
|
||||||
found, notFound, err := repos.FindPkgs(gdb, buildDeps)
|
if len(vars.BuildDepends) > 0 {
|
||||||
|
found, notFound, err := repos.FindPkgs(gdb, vars.BuildDepends)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
found = filterBuildDeps(found, installed)
|
||||||
|
|
||||||
log.Info("Installing build dependencies").Send()
|
log.Info("Installing build dependencies").Send()
|
||||||
installPkgs(ctx, cliutils.FlattenPkgs(found, "install"), notFound, mgr)
|
|
||||||
|
flattened := cliutils.FlattenPkgs(found, "install", translator)
|
||||||
|
buildDeps = packageNames(flattened)
|
||||||
|
installPkgs(ctx, flattened, notFound, mgr, clean)
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtDeps, builtNames, repoDeps []string
|
var builtDeps, builtNames, repoDeps []string
|
||||||
@@ -280,9 +299,9 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
scripts := getScriptPaths(cliutils.FlattenPkgs(found, "install"))
|
scripts := getScriptPaths(cliutils.FlattenPkgs(found, "install", translator))
|
||||||
for _, script := range scripts {
|
for _, script := range scripts {
|
||||||
pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr)
|
pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr, clean)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -361,6 +380,8 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
|||||||
} else {
|
} else {
|
||||||
log.Fatal("The package() function is required").Send()
|
log.Fatal("The package() function is required").Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Info("Building package metadata").Str("name", vars.Name).Send()
|
||||||
|
|
||||||
uniq(
|
uniq(
|
||||||
&repoDeps,
|
&repoDeps,
|
||||||
@@ -464,12 +485,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
|||||||
|
|
||||||
pkgInfo.Overridables.Contents = contents
|
pkgInfo.Overridables.Contents = contents
|
||||||
|
|
||||||
pkgFormat := mgr.Format()
|
packager, err := nfpm.Get(getPkgFormat(mgr))
|
||||||
if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
|
|
||||||
pkgFormat = format
|
|
||||||
}
|
|
||||||
|
|
||||||
packager, err := nfpm.Get(pkgFormat)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -484,6 +500,8 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Info("Compressing package").Str("name", pkgName).Send()
|
||||||
|
|
||||||
err = packager.Package(pkgInfo, pkgFile)
|
err = packager.Package(pkgInfo, pkgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -491,7 +509,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(buildDeps) > 0 {
|
if len(buildDeps) > 0 {
|
||||||
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove build dependencies?", false)
|
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove build dependencies?", false, translator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -515,6 +533,47 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
|
|||||||
return pkgPaths, pkgNames, nil
|
return pkgPaths, pkgNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkForBuiltPackage(mgr manager.Manager, vars *BuildVars, pkgFormat, baseDir string) (string, bool, error) {
|
||||||
|
filename, err := pkgFileName(vars, pkgFormat)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgPath := filepath.Join(baseDir, filename)
|
||||||
|
|
||||||
|
_, err = os.Stat(pkgPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgPath, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkgFileName(vars *BuildVars, pkgFormat string) (string, error) {
|
||||||
|
pkgInfo := &nfpm.Info{
|
||||||
|
Name: vars.Name,
|
||||||
|
Arch: cpu.Arch(),
|
||||||
|
Version: vars.Version,
|
||||||
|
Release: strconv.Itoa(vars.Release),
|
||||||
|
Epoch: strconv.FormatUint(uint64(vars.Epoch), 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
packager, err := nfpm.Get(pkgFormat)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return packager.ConventionalFileName(pkgInfo), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPkgFormat(mgr manager.Manager) string {
|
||||||
|
pkgFormat := mgr.Format()
|
||||||
|
if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
|
||||||
|
pkgFormat = format
|
||||||
|
}
|
||||||
|
return pkgFormat
|
||||||
|
}
|
||||||
|
|
||||||
func genBuildEnv(info *distro.OSRelease, scriptdir string) []string {
|
func genBuildEnv(info *distro.OSRelease, scriptdir string) []string {
|
||||||
env := os.Environ()
|
env := os.Environ()
|
||||||
|
|
||||||
@@ -541,10 +600,11 @@ func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, src := range bv.Sources {
|
for i, src := range bv.Sources {
|
||||||
opts := download.GetOptions{
|
opts := dl.Options{
|
||||||
SourceURL: src,
|
Name: fmt.Sprintf("%s[%d]", bv.Name, i),
|
||||||
|
URL: src,
|
||||||
Destination: srcdir,
|
Destination: srcdir,
|
||||||
EncloseGit: true,
|
Progress: os.Stderr,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
|
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
|
||||||
@@ -552,10 +612,10 @@ func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
opts.SHA256Sum = checksum
|
opts.SHA256 = checksum
|
||||||
}
|
}
|
||||||
|
|
||||||
err := download.Get(ctx, opts)
|
err := dl.Download(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -633,6 +693,41 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error {
|
|||||||
return r.Run(ctx, fl)
|
return r.Run(ctx, fl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterBuildDeps(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
|
||||||
|
out := map[string][]db.Package{}
|
||||||
|
for name, pkgs := range found {
|
||||||
|
var inner []db.Package
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
if _, ok := installed[pkg.Name]; !ok {
|
||||||
|
addToFiltered := true
|
||||||
|
for _, provides := range pkg.Provides.Val {
|
||||||
|
if _, ok := installed[provides]; ok {
|
||||||
|
addToFiltered = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if addToFiltered {
|
||||||
|
inner = append(inner, pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(inner) > 0 {
|
||||||
|
out[name] = inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageNames(pkgs []db.Package) []string {
|
||||||
|
names := make([]string, len(pkgs))
|
||||||
|
for i, p := range pkgs {
|
||||||
|
names[i] = p.Name
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
// uniq removes all duplicates from string slices
|
// uniq removes all duplicates from string slices
|
||||||
func uniq(ss ...*[]string) {
|
func uniq(ss ...*[]string) {
|
||||||
for _, s := range ss {
|
for _, s := range ss {
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -9,9 +27,11 @@ import (
|
|||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/twitchtv/twirp"
|
"github.com/twitchtv/twirp"
|
||||||
|
"go.arsenm.dev/logger/log"
|
||||||
"go.arsenm.dev/lure/internal/api"
|
"go.arsenm.dev/lure/internal/api"
|
||||||
"go.arsenm.dev/lure/internal/config"
|
"go.arsenm.dev/lure/internal/config"
|
||||||
"go.arsenm.dev/lure/internal/db"
|
"go.arsenm.dev/lure/internal/db"
|
||||||
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
|
||||||
type lureWebAPI struct {
|
type lureWebAPI struct {
|
||||||
@@ -59,7 +79,7 @@ func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.Se
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out.Packages = append(out.Packages, dbPkgToAPI(pkg))
|
out.Packages = append(out.Packages, dbPkgToAPI(ctx, pkg))
|
||||||
}
|
}
|
||||||
|
|
||||||
return out, err
|
return out, err
|
||||||
@@ -70,7 +90,7 @@ func (l lureWebAPI) GetPkg(ctx context.Context, req *api.GetPackageRequest) (*ap
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dbPkgToAPI(pkg), nil
|
return dbPkgToAPI(ctx, pkg), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l lureWebAPI) GetBuildScript(ctx context.Context, req *api.GetBuildScriptRequest) (*api.GetBuildScriptResponse, error) {
|
func (l lureWebAPI) GetBuildScript(ctx context.Context, req *api.GetBuildScriptRequest) (*api.GetBuildScriptResponse, error) {
|
||||||
@@ -94,16 +114,16 @@ func (l lureWebAPI) GetBuildScript(ctx context.Context, req *api.GetBuildScriptR
|
|||||||
return &api.GetBuildScriptResponse{Script: string(data)}, nil
|
return &api.GetBuildScriptResponse{Script: string(data)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dbPkgToAPI(pkg *db.Package) *api.Package {
|
func dbPkgToAPI(ctx context.Context, pkg *db.Package) *api.Package {
|
||||||
return &api.Package{
|
return &api.Package{
|
||||||
Name: pkg.Name,
|
Name: pkg.Name,
|
||||||
Repository: pkg.Repository,
|
Repository: pkg.Repository,
|
||||||
Version: pkg.Version,
|
Version: pkg.Version,
|
||||||
Release: int64(pkg.Release),
|
Release: int64(pkg.Release),
|
||||||
Epoch: ptr(int64(pkg.Epoch)),
|
Epoch: ptr(int64(pkg.Epoch)),
|
||||||
Description: &pkg.Description,
|
Description: performTranslation(ctx, pkg.Description.Val),
|
||||||
Homepage: &pkg.Homepage,
|
Homepage: performTranslation(ctx, pkg.Homepage.Val),
|
||||||
Maintainer: &pkg.Maintainer,
|
Maintainer: performTranslation(ctx, pkg.Maintainer.Val),
|
||||||
Architectures: pkg.Architectures.Val,
|
Architectures: pkg.Architectures.Val,
|
||||||
Licenses: pkg.Licenses.Val,
|
Licenses: pkg.Licenses.Val,
|
||||||
Provides: pkg.Provides.Val,
|
Provides: pkg.Provides.Val,
|
||||||
@@ -125,3 +145,61 @@ func dbMapToAPI(m map[string][]string) map[string]*api.StringList {
|
|||||||
}
|
}
|
||||||
return out
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,8 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"go.arsenm.dev/logger/log"
|
"go.arsenm.dev/logger/log"
|
||||||
"go.arsenm.dev/lure/internal/config"
|
"go.arsenm.dev/lure/internal/config"
|
||||||
@@ -12,21 +28,9 @@ import (
|
|||||||
var gdb *sqlx.DB
|
var gdb *sqlx.DB
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
fi, err := os.Stat(config.DBPath)
|
var err error
|
||||||
if err == nil {
|
gdb, err = db.Open(config.DBPath)
|
||||||
// TODO: This should be removed by the first stable release.
|
|
||||||
if fi.IsDir() {
|
|
||||||
log.Fatal("Your package cache database is using the old database engine. Please remove ~/.cache/lure and then run `lure ref`.").Send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gdb, err = sqlx.Open("sqlite", config.DBPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error opening database").Err(err).Send()
|
log.Fatal("Error opening database").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.Init(gdb)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error initializing database").Err(err).Send()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -49,6 +67,7 @@ func main() {
|
|||||||
lureWebAPI{db: gdb},
|
lureWebAPI{db: gdb},
|
||||||
twirp.WithServerPathPrefix(""),
|
twirp.WithServerPathPrefix(""),
|
||||||
)
|
)
|
||||||
|
handler = withAcceptLanguage(handler)
|
||||||
handler = allowAllCORSHandler(handler)
|
handler = allowAllCORSHandler(handler)
|
||||||
handler = handleWebhook(handler, sigCh)
|
handler = handleWebhook(handler, sigCh)
|
||||||
|
|
||||||
@@ -75,3 +94,24 @@ func allowAllCORSHandler(h http.Handler) http.Handler {
|
|||||||
h.ServeHTTP(res, req)
|
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,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -51,7 +69,7 @@ func verifySecure(req *http.Request) error {
|
|||||||
|
|
||||||
secretStr, ok := os.LookupEnv("LURE_API_GITHUB_SECRET")
|
secretStr, ok := os.LookupEnv("LURE_API_GITHUB_SECRET")
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("LURE_API_GITHUB_SECRET must be set to the secret used for setting up the github webhook")
|
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)
|
secret := []byte(secretStr)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
36
db.go
36
db.go
@@ -1,8 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"go.arsenm.dev/logger/log"
|
"go.arsenm.dev/logger/log"
|
||||||
"go.arsenm.dev/lure/internal/config"
|
"go.arsenm.dev/lure/internal/config"
|
||||||
@@ -12,21 +28,9 @@ import (
|
|||||||
var gdb *sqlx.DB
|
var gdb *sqlx.DB
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
fi, err := os.Stat(config.DBPath)
|
var err error
|
||||||
if err == nil {
|
gdb, err = db.Open(config.DBPath)
|
||||||
// TODO: This should be removed by the first stable release.
|
|
||||||
if fi.IsDir() {
|
|
||||||
log.Fatal("Your package cache database is using the old database engine. Please remove ~/.cache/lure and then run `lure ref`.").Send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gdb, err = sqlx.Open("sqlite", config.DBPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error opening database").Err(err).Send()
|
log.Fatal("Error opening database").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.Init(gdb)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error initializing database").Err(err).Send()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -182,20 +182,19 @@ https://example.com/archive.tar.gz?~archive=false
|
|||||||
|
|
||||||
If the URL scheme starts with `git+`, the source will be downloaded as a git repo. The git download mode supports multiple parameters:
|
If the URL scheme starts with `git+`, the source will be downloaded as a git repo. The git download mode supports multiple parameters:
|
||||||
|
|
||||||
- `~tag`: Specify which tag of the repo to check out.
|
- `~rev`: Specify which revision of the repo to check out.
|
||||||
- `~branch`: Specify which branch of the repo to check out.
|
|
||||||
- `~commit`: Specify which commit of the repo to check out.
|
|
||||||
- `~depth`: Specify what depth should be used when cloning the repo. Must be an integer.
|
- `~depth`: Specify what depth should be used when cloning the repo. Must be an integer.
|
||||||
- `~name`: Specify the name of the directory into which the git repo should be cloned.
|
- `~name`: Specify the name of the directory into which the git repo should be cloned.
|
||||||
|
- `~recursive`: If set to true, submodules will be cloned recursively. It is false by default.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
git+https://gitea.arsenm.dev/Arsen6331/itd?~branch=resource-loading&~depth=1
|
git+https://gitea.arsenm.dev/Arsen6331/itd?~rev=resource-loading&~depth=1
|
||||||
```
|
```
|
||||||
|
|
||||||
```text
|
```text
|
||||||
git+https://gitea.arsenm.dev/Arsen6331/lure?~tag=v0.0.1
|
git+https://gitea.arsenm.dev/Arsen6331/lure?~rev=v0.0.1&~recursive=true
|
||||||
```
|
```
|
||||||
|
|
||||||
### checksums
|
### checksums
|
||||||
|
|||||||
@@ -31,12 +31,15 @@ The package arguments do not have to be exact. LURE will check the `provides` ar
|
|||||||
|
|
||||||
If multiple packages are found, you will be prompted to select which you want to install.
|
If multiple packages are found, you will be prompted to select which you want to install.
|
||||||
|
|
||||||
|
By default, if a package has already been built, LURE will install the cached package rather than re-build it. Use the `-c` or `--clean` flag to force a re-build.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
lure in itd-bin # only finds itd-bin
|
lure in itd-bin # only finds itd-bin
|
||||||
lure in itd # finds itd-bin and itd-git
|
lure in itd # finds itd-bin and itd-git
|
||||||
lure in it% # finds itd-bin, itd-git, and itgui-git
|
lure in it% # finds itd-bin, itd-git, and itgui-git
|
||||||
|
lure in -c itd-bin
|
||||||
```
|
```
|
||||||
|
|
||||||
### remove
|
### remove
|
||||||
@@ -53,6 +56,8 @@ lure rm firefox
|
|||||||
|
|
||||||
The upgrade command looks through the packages installed on your system and sees if any of them match LURE repo packages. If they do, their versions are compared using the `rpmvercmp` algorithm. If LURE repos contain a newer version, the package is upgraded.
|
The upgrade command looks through the packages installed on your system and sees if any of them match LURE repo packages. If they do, their versions are compared using the `rpmvercmp` algorithm. If LURE repos contain a newer version, the package is upgraded.
|
||||||
|
|
||||||
|
By default, if a package has already been built, LURE will install the cached package rather than re-build it. Use the `-c` or `--clean` flag to force a re-build.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|||||||
@@ -1,292 +0,0 @@
|
|||||||
/*
|
|
||||||
* LURE - Linux User REpository
|
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package download
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/sha256"
|
|
||||||
"errors"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
"github.com/mholt/archiver/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrChecksumMismatch = errors.New("checksums did not match")
|
|
||||||
|
|
||||||
type GetOptions struct {
|
|
||||||
SourceURL string
|
|
||||||
Destination string
|
|
||||||
SHA256Sum []byte
|
|
||||||
// EncloseGit determines if Get will create an enclosing
|
|
||||||
// directory for git repos
|
|
||||||
EncloseGit bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get downloads from a URL
|
|
||||||
func Get(ctx context.Context, opts GetOptions) error {
|
|
||||||
dest, err := filepath.Abs(opts.Destination)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
opts.Destination = dest
|
|
||||||
|
|
||||||
err = os.MkdirAll(opts.Destination, 0o755)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
src, err := url.Parse(opts.SourceURL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
query := src.Query()
|
|
||||||
|
|
||||||
if strings.HasPrefix(src.Scheme, "git+") {
|
|
||||||
err = getGit(ctx, src, query, opts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = getFile(ctx, src, query, opts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getGit(ctx context.Context, src *url.URL, query url.Values, opts GetOptions) (err error) {
|
|
||||||
tag := query.Get("~tag")
|
|
||||||
query.Del("~tag")
|
|
||||||
|
|
||||||
branch := query.Get("~branch")
|
|
||||||
query.Del("~branch")
|
|
||||||
|
|
||||||
commit := query.Get("~commit")
|
|
||||||
query.Del("~commit")
|
|
||||||
|
|
||||||
depthStr := query.Get("~depth")
|
|
||||||
query.Del("~depth")
|
|
||||||
|
|
||||||
name := query.Get("~name")
|
|
||||||
query.Del("~name")
|
|
||||||
|
|
||||||
var refName plumbing.ReferenceName
|
|
||||||
if tag != "" {
|
|
||||||
refName = plumbing.NewTagReferenceName(tag)
|
|
||||||
} else if branch != "" {
|
|
||||||
refName = plumbing.NewBranchReferenceName(branch)
|
|
||||||
}
|
|
||||||
|
|
||||||
src.Scheme = strings.TrimPrefix(src.Scheme, "git+")
|
|
||||||
src.RawQuery = query.Encode()
|
|
||||||
|
|
||||||
if name == "" {
|
|
||||||
name = path.Base(src.Path)
|
|
||||||
name = strings.TrimSuffix(name, ".git")
|
|
||||||
}
|
|
||||||
|
|
||||||
dstDir := opts.Destination
|
|
||||||
if opts.EncloseGit {
|
|
||||||
dstDir = filepath.Join(opts.Destination, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
depth := 0
|
|
||||||
if depthStr != "" {
|
|
||||||
depth, err = strconv.Atoi(depthStr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cloneOpts := &git.CloneOptions{
|
|
||||||
URL: src.String(),
|
|
||||||
Progress: os.Stderr,
|
|
||||||
Depth: depth,
|
|
||||||
}
|
|
||||||
|
|
||||||
repo, err := git.PlainCloneContext(ctx, dstDir, false, cloneOpts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := repo.Worktree()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
checkoutOpts := &git.CheckoutOptions{}
|
|
||||||
if refName != "" {
|
|
||||||
checkoutOpts.Branch = refName
|
|
||||||
} else if commit != "" {
|
|
||||||
checkoutOpts.Hash = plumbing.NewHash(commit)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.Checkout(checkoutOpts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFile(ctx context.Context, src *url.URL, query url.Values, opts GetOptions) error {
|
|
||||||
name := query.Get("~name")
|
|
||||||
query.Del("~name")
|
|
||||||
|
|
||||||
archive := query.Get("~archive")
|
|
||||||
query.Del("~archive")
|
|
||||||
|
|
||||||
src.RawQuery = query.Encode()
|
|
||||||
|
|
||||||
if name == "" {
|
|
||||||
name = path.Base(src.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, src.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := sha256.New()
|
|
||||||
|
|
||||||
format, input, err := archiver.Identify(name, res.Body)
|
|
||||||
if err == archiver.ErrNoMatch || archive == "false" {
|
|
||||||
fl, err := os.Create(filepath.Join(opts.Destination, name))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := io.MultiWriter(hash, fl)
|
|
||||||
|
|
||||||
_, err = io.Copy(w, input)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
res.Body.Close()
|
|
||||||
fl.Close()
|
|
||||||
|
|
||||||
if opts.SHA256Sum != nil {
|
|
||||||
sum := hash.Sum(nil)
|
|
||||||
if !bytes.Equal(opts.SHA256Sum, sum) {
|
|
||||||
return ErrChecksumMismatch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
err = extractFile(ctx, input, hash, format, name, opts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractFile(ctx context.Context, input io.Reader, hash hash.Hash, format archiver.Format, name string, opts GetOptions) (err error) {
|
|
||||||
r := io.TeeReader(input, hash)
|
|
||||||
fname := format.Name()
|
|
||||||
|
|
||||||
switch format := format.(type) {
|
|
||||||
case archiver.Extractor:
|
|
||||||
err = format.Extract(ctx, r, nil, func(ctx context.Context, f archiver.File) error {
|
|
||||||
fr, err := f.Open()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer fr.Close()
|
|
||||||
fi, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fm := fi.Mode()
|
|
||||||
|
|
||||||
path := filepath.Join(opts.Destination, f.NameInArchive)
|
|
||||||
|
|
||||||
err = os.MkdirAll(filepath.Dir(path), 0o755)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.IsDir() {
|
|
||||||
err = os.Mkdir(path, 0o755)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
outFl, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fm.Perm())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer outFl.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(outFl, fr)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case archiver.Decompressor:
|
|
||||||
rc, err := format.OpenReader(r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer rc.Close()
|
|
||||||
|
|
||||||
path := filepath.Join(opts.Destination, name)
|
|
||||||
path = strings.TrimSuffix(path, fname)
|
|
||||||
|
|
||||||
outFl, err := os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = io.Copy(outFl, rc)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.SHA256Sum != nil {
|
|
||||||
sum := hash.Sum(nil)
|
|
||||||
if !bytes.Equal(opts.SHA256Sum, sum) {
|
|
||||||
return ErrChecksumMismatch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
29
fix.go
29
fix.go
@@ -1,9 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"go.arsenm.dev/logger/log"
|
"go.arsenm.dev/logger/log"
|
||||||
"go.arsenm.dev/lure/internal/config"
|
"go.arsenm.dev/lure/internal/config"
|
||||||
@@ -28,18 +45,12 @@ func fixCmd(c *cli.Context) error {
|
|||||||
log.Fatal("Unable to create new cache directory").Err(err).Send()
|
log.Fatal("Unable to create new cache directory").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
gdb, err = sqlx.Open("sqlite", config.DBPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Unable to create new database").Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the DB is rebuilt when repos are pulled
|
// Make sure the DB is rebuilt when repos are pulled
|
||||||
config.DBPresent = false
|
gdb, err = db.Open(config.DBPath)
|
||||||
|
|
||||||
err = db.Init(gdb)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error initializing database").Err(err).Send()
|
log.Fatal("Error initializing database").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
config.DBPresent = false
|
||||||
|
|
||||||
err = repos.Pull(c.Context, gdb, cfg.Repos)
|
err = repos.Pull(c.Context, gdb, cfg.Repos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
22
go.mod
22
go.mod
@@ -4,6 +4,7 @@ go 1.18
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.6
|
github.com/AlecAivazis/survey/v2 v2.3.6
|
||||||
|
github.com/PuerkitoBio/purell v1.2.0
|
||||||
github.com/alecthomas/chroma/v2 v2.4.0
|
github.com/alecthomas/chroma/v2 v2.4.0
|
||||||
github.com/charmbracelet/bubbles v0.14.0
|
github.com/charmbracelet/bubbles v0.14.0
|
||||||
github.com/charmbracelet/bubbletea v0.23.1
|
github.com/charmbracelet/bubbletea v0.23.1
|
||||||
@@ -15,12 +16,16 @@ require (
|
|||||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7
|
github.com/mholt/archiver/v4 v4.0.0-alpha.7
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/muesli/reflow v0.3.0
|
github.com/muesli/reflow v0.3.0
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5
|
github.com/pelletier/go-toml/v2 v2.0.6
|
||||||
|
github.com/schollz/progressbar/v3 v3.13.0
|
||||||
github.com/twitchtv/twirp v8.1.3+incompatible
|
github.com/twitchtv/twirp v8.1.3+incompatible
|
||||||
github.com/urfave/cli/v2 v2.16.3
|
github.com/urfave/cli/v2 v2.23.7
|
||||||
go.arsenm.dev/logger v0.0.0-20221220032833-ba8a3cfb4668
|
github.com/vmihailenco/msgpack/v5 v5.3.5
|
||||||
|
go.arsenm.dev/logger v0.0.0-20230126004036-a8cbbe3b6fe6
|
||||||
|
go.arsenm.dev/translate v0.0.0-20230113025904-5ad1ec0ed296
|
||||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b
|
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b
|
||||||
golang.org/x/sys v0.3.0
|
golang.org/x/sys v0.4.0
|
||||||
|
golang.org/x/text v0.6.0
|
||||||
google.golang.org/protobuf v1.27.1
|
google.golang.org/protobuf v1.27.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
modernc.org/sqlite v1.20.0
|
modernc.org/sqlite v1.20.0
|
||||||
@@ -62,10 +67,11 @@ require (
|
|||||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||||
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
@@ -76,12 +82,13 @@ require (
|
|||||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.4.3 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/sergi/go-diff v1.2.0 // indirect
|
github.com/sergi/go-diff v1.2.0 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||||
github.com/therootcompany/xz v1.0.1 // indirect
|
github.com/therootcompany/xz v1.0.1 // indirect
|
||||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
@@ -90,8 +97,7 @@ require (
|
|||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
|
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
golang.org/x/term v0.4.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
|
||||||
golang.org/x/tools v0.1.12 // indirect
|
golang.org/x/tools v0.1.12 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
lukechampine.com/uint128 v1.2.0 // indirect
|
lukechampine.com/uint128 v1.2.0 // indirect
|
||||||
|
|||||||
46
go.sum
46
go.sum
@@ -23,6 +23,8 @@ github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c h1:bNpaLLv2Y4
|
|||||||
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko=
|
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko=
|
||||||
github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs=
|
github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs=
|
||||||
|
github.com/PuerkitoBio/purell v1.2.0 h1:/Jdm5QfyM8zdlqT6WVZU4cfP23sot6CEHA4CS49Ezig=
|
||||||
|
github.com/PuerkitoBio/purell v1.2.0/go.mod h1:OhLRTaaIzhvIyofkJfB24gokC7tM42Px5UhoT32THBk=
|
||||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||||
github.com/alecthomas/assert/v2 v2.2.0 h1:f6L/b7KE2bfA+9O4FL3CM/xJccDEwPVYd5fALBiuwvw=
|
github.com/alecthomas/assert/v2 v2.2.0 h1:f6L/b7KE2bfA+9O4FL3CM/xJccDEwPVYd5fALBiuwvw=
|
||||||
@@ -123,6 +125,7 @@ github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c
|
|||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
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 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
@@ -156,8 +159,9 @@ github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHR
|
|||||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
@@ -171,6 +175,8 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
|
|||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7 h1:xzByj8G8tj0Oq7ZYYU4+ixL/CVb5ruWCm0EZQ1PjOkE=
|
github.com/mholt/archiver/v4 v4.0.0-alpha.7 h1:xzByj8G8tj0Oq7ZYYU4+ixL/CVb5ruWCm0EZQ1PjOkE=
|
||||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7/go.mod h1:Fs8qUkO74HHaidabihzYephJH8qmGD/nCP6tE5xC9BM=
|
github.com/mholt/archiver/v4 v4.0.0-alpha.7/go.mod h1:Fs8qUkO74HHaidabihzYephJH8qmGD/nCP6tE5xC9BM=
|
||||||
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||||
|
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
@@ -194,8 +200,8 @@ github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4Y
|
|||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
|
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
|
||||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -206,13 +212,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
|
||||||
|
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||||
|
github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8=
|
||||||
|
github.com/schollz/progressbar/v3 v3.13.0/go.mod h1:ZBYnSuLAX2LU8P8UiKN/KgF2DY58AJC8yfVYLPC8Ly4=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
@@ -225,13 +234,16 @@ github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hg
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
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 h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
|
||||||
@@ -239,8 +251,12 @@ github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vF
|
|||||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk=
|
github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
|
||||||
github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
|
github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||||
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
|
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
|
||||||
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
|
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
|
||||||
@@ -252,8 +268,10 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT
|
|||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
|
gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
|
||||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
|
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
|
||||||
go.arsenm.dev/logger v0.0.0-20221220032833-ba8a3cfb4668 h1:7dSmQ79slzFpcii8zgQbEStxpkTPvq3tzWc7KX5uwGc=
|
go.arsenm.dev/logger v0.0.0-20230126004036-a8cbbe3b6fe6 h1:p5EkGdoRX4ddMJcYQzRPTHk8oVyMv0q8agaAbPr/oak=
|
||||||
go.arsenm.dev/logger v0.0.0-20221220032833-ba8a3cfb4668/go.mod h1:RV2qydKDdoyaRkhAq8JEGvojR8eJ6bjq5WnSIlH7gYw=
|
go.arsenm.dev/logger v0.0.0-20230126004036-a8cbbe3b6fe6/go.mod h1:Sg0thD0AZwMib+fD+YFOgmTWwEOqPbMjSSmWef6mEog=
|
||||||
|
go.arsenm.dev/translate v0.0.0-20230113025904-5ad1ec0ed296 h1:uOJuOOn/sPe4YX9MD98tCoeLQTopIk17dJt0fwCeJrk=
|
||||||
|
go.arsenm.dev/translate v0.0.0-20230113025904-5ad1ec0ed296/go.mod h1:+rZV+tkYEPgZyP0OWBH477vWNwxN3pcAcukcjzgQjco=
|
||||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
@@ -290,15 +308,17 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
|
||||||
|
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||||
|
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
|||||||
18
helpers.go
18
helpers.go
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
44
info.go
44
info.go
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"go.arsenm.dev/lure/distro"
|
"go.arsenm.dev/lure/distro"
|
||||||
"go.arsenm.dev/lure/internal/cliutils"
|
"go.arsenm.dev/lure/internal/cliutils"
|
||||||
|
"go.arsenm.dev/lure/internal/config"
|
||||||
"go.arsenm.dev/lure/internal/overrides"
|
"go.arsenm.dev/lure/internal/overrides"
|
||||||
"go.arsenm.dev/lure/internal/repos"
|
"go.arsenm.dev/lure/internal/repos"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@@ -52,7 +53,7 @@ func infoCmd(c *cli.Context) error {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pkgs := cliutils.FlattenPkgs(found, "show")
|
pkgs := cliutils.FlattenPkgs(found, "show", translator)
|
||||||
|
|
||||||
var names []string
|
var names []string
|
||||||
all := c.Bool("all")
|
all := c.Bool("all")
|
||||||
@@ -62,38 +63,29 @@ func infoCmd(c *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error parsing os-release file").Err(err).Send()
|
log.Fatal("Error parsing os-release file").Err(err).Send()
|
||||||
}
|
}
|
||||||
names = overrides.Resolve(info, overrides.DefaultOpts)
|
names, err = overrides.Resolve(
|
||||||
|
info,
|
||||||
|
overrides.DefaultOpts.
|
||||||
|
WithLanguages([]string{config.SystemLang()}),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error resolving overrides").Err(err).Send()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
if !all {
|
if !all {
|
||||||
depsSet := false
|
err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
|
||||||
buildDepsSet := false
|
if err != nil {
|
||||||
for _, name := range names {
|
log.Fatal("Error encoding script variables").Err(err).Send()
|
||||||
if deps, ok := pkg.Depends.Val[name]; ok && !depsSet {
|
|
||||||
pkg.Depends.Val = map[string][]string{name: deps}
|
|
||||||
depsSet = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if buildDeps, ok := pkg.BuildDepends.Val[name]; ok && !buildDepsSet {
|
|
||||||
pkg.BuildDepends.Val = map[string][]string{name: buildDeps}
|
|
||||||
buildDepsSet = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if !depsSet {
|
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
|
||||||
pkg.Depends.Val = nil
|
if err != nil {
|
||||||
}
|
log.Fatal("Error encoding script variables").Err(err).Send()
|
||||||
|
|
||||||
if !buildDepsSet {
|
|
||||||
pkg.BuildDepends.Val = nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error encoding script variables").Err(err).Send()
|
|
||||||
}
|
|
||||||
fmt.Println("---")
|
fmt.Println("---")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
install.go
12
install.go
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -53,13 +53,13 @@ func installCmd(c *cli.Context) error {
|
|||||||
log.Fatal("Error finding packages").Err(err).Send()
|
log.Fatal("Error finding packages").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
installPkgs(c.Context, cliutils.FlattenPkgs(found, "install"), notFound, mgr)
|
installPkgs(c.Context, cliutils.FlattenPkgs(found, "install", translator), notFound, mgr, c.Bool("clean"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// installPkgs installs non-LURE packages via the package manager, then builds and installs LURE
|
// installPkgs installs non-LURE packages via the package manager, then builds and installs LURE
|
||||||
// packages
|
// packages
|
||||||
func installPkgs(ctx context.Context, pkgs []db.Package, notFound []string, mgr manager.Manager) {
|
func installPkgs(ctx context.Context, pkgs []db.Package, notFound []string, mgr manager.Manager, clean bool) {
|
||||||
if len(notFound) > 0 {
|
if len(notFound) > 0 {
|
||||||
err := mgr.Install(nil, notFound...)
|
err := mgr.Install(nil, notFound...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -67,7 +67,7 @@ func installPkgs(ctx context.Context, pkgs []db.Package, notFound []string, mgr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
installScripts(ctx, mgr, getScriptPaths(pkgs))
|
installScripts(ctx, mgr, getScriptPaths(pkgs), clean)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getScriptPaths generates a slice of script paths corresponding to the
|
// getScriptPaths generates a slice of script paths corresponding to the
|
||||||
@@ -82,9 +82,9 @@ func getScriptPaths(pkgs []db.Package) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// installScripts builds and installs LURE build scripts
|
// installScripts builds and installs LURE build scripts
|
||||||
func installScripts(ctx context.Context, mgr manager.Manager, scripts []string) {
|
func installScripts(ctx context.Context, mgr manager.Manager, scripts []string, clean bool) {
|
||||||
for _, script := range scripts {
|
for _, script := range scripts {
|
||||||
builtPkgs, _, err := buildPackage(ctx, script, mgr)
|
builtPkgs, _, err := buildPackage(ctx, script, mgr, clean)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error building package").Err(err).Send()
|
log.Fatal("Error building package").Err(err).Send()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package api
|
package api
|
||||||
|
|
||||||
//go:generate protoc --twirp_out=. lure.proto
|
//go:generate protoc --twirp_out=. lure.proto
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package cliutils
|
package cliutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -5,16 +23,18 @@ import (
|
|||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"go.arsenm.dev/logger/log"
|
"go.arsenm.dev/logger/log"
|
||||||
|
"go.arsenm.dev/lure/internal/config"
|
||||||
"go.arsenm.dev/lure/internal/db"
|
"go.arsenm.dev/lure/internal/db"
|
||||||
"go.arsenm.dev/lure/internal/pager"
|
"go.arsenm.dev/lure/internal/pager"
|
||||||
|
"go.arsenm.dev/translate"
|
||||||
)
|
)
|
||||||
|
|
||||||
// YesNoPrompt asks the user a yes or no question, using def as the default answer
|
// YesNoPrompt asks the user a yes or no question, using def as the default answer
|
||||||
func YesNoPrompt(msg string, def bool) (bool, error) {
|
func YesNoPrompt(msg string, def bool, t translate.Translator) (bool, error) {
|
||||||
var answer bool
|
var answer bool
|
||||||
err := survey.AskOne(
|
err := survey.AskOne(
|
||||||
&survey.Confirm{
|
&survey.Confirm{
|
||||||
Message: msg,
|
Message: t.TranslateTo(msg, config.Language),
|
||||||
Default: def,
|
Default: def,
|
||||||
},
|
},
|
||||||
&answer,
|
&answer,
|
||||||
@@ -25,8 +45,8 @@ func YesNoPrompt(msg string, def bool) (bool, error) {
|
|||||||
// PromptViewScript asks the user if they'd like to see a script,
|
// 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
|
// shows it if they answer yes, then asks if they'd still like to
|
||||||
// continue, and exits if they answer no.
|
// continue, and exits if they answer no.
|
||||||
func PromptViewScript(script, name, style string) error {
|
func PromptViewScript(script, name, style string, t translate.Translator) error {
|
||||||
view, err := YesNoPrompt("Would you like to view the build script for "+name, false)
|
view, err := YesNoPrompt(t.TranslateTo("Would you like to view the build script for", config.Language)+" "+name, false, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -37,13 +57,13 @@ func PromptViewScript(script, name, style string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cont, err := YesNoPrompt("Would you still like to continue?", false)
|
cont, err := YesNoPrompt("Would you still like to continue?", false, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cont {
|
if !cont {
|
||||||
log.Fatal("User chose not to continue after reading script").Send()
|
log.Fatal(t.TranslateTo("User chose not to continue after reading script", config.Language)).Send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,11 +90,11 @@ func ShowScript(path, name, style string) error {
|
|||||||
|
|
||||||
// FlattenPkgs attempts to flatten the a map of slices of packages into a single slice
|
// 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.
|
// of packages by prompting the user if multiple packages match.
|
||||||
func FlattenPkgs(found map[string][]db.Package, verb string) []db.Package {
|
func FlattenPkgs(found map[string][]db.Package, verb string, t translate.Translator) []db.Package {
|
||||||
var outPkgs []db.Package
|
var outPkgs []db.Package
|
||||||
for _, pkgs := range found {
|
for _, pkgs := range found {
|
||||||
if len(pkgs) > 1 {
|
if len(pkgs) > 1 {
|
||||||
choices, err := PkgPrompt(pkgs, verb)
|
choices, err := PkgPrompt(pkgs, verb, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error prompting for choice of package").Send()
|
log.Fatal("Error prompting for choice of package").Send()
|
||||||
}
|
}
|
||||||
@@ -88,7 +108,7 @@ func FlattenPkgs(found map[string][]db.Package, verb string) []db.Package {
|
|||||||
|
|
||||||
// PkgPrompt asks the user to choose between multiple packages.
|
// PkgPrompt asks the user to choose between multiple packages.
|
||||||
// The user may choose multiple packages.
|
// The user may choose multiple packages.
|
||||||
func PkgPrompt(options []db.Package, verb string) ([]db.Package, error) {
|
func PkgPrompt(options []db.Package, verb string, t translate.Translator) ([]db.Package, error) {
|
||||||
names := make([]string, len(options))
|
names := make([]string, len(options))
|
||||||
for i, option := range options {
|
for i, option := range options {
|
||||||
names[i] = option.Repository + "/" + option.Name + " " + option.Version
|
names[i] = option.Repository + "/" + option.Name + " " + option.Version
|
||||||
@@ -96,7 +116,7 @@ func PkgPrompt(options []db.Package, verb string) ([]db.Package, error) {
|
|||||||
|
|
||||||
prompt := &survey.MultiSelect{
|
prompt := &survey.MultiSelect{
|
||||||
Options: names,
|
Options: names,
|
||||||
Message: "Choose which package(s) to " + verb,
|
Message: t.TranslateTo("Choose which package(s) to "+verb, config.Language),
|
||||||
}
|
}
|
||||||
|
|
||||||
var choices []int
|
var choices []int
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -71,6 +89,6 @@ func init() {
|
|||||||
|
|
||||||
DBPath = filepath.Join(CacheDir, "db")
|
DBPath = filepath.Join(CacheDir, "db")
|
||||||
|
|
||||||
_, err = os.ReadDir(DBPath)
|
fi, err := os.Stat(DBPath)
|
||||||
DBPresent = err == nil
|
DBPresent = err == nil && !fi.IsDir()
|
||||||
}
|
}
|
||||||
|
|||||||
48
internal/config/lang.go
Normal file
48
internal/config/lang.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.arsenm.dev/logger/log"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Language language.Tag
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
lang := SystemLang()
|
||||||
|
tag, err := language.Parse(lang)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error parsing system language").Err(err).Send()
|
||||||
|
}
|
||||||
|
base, _ := tag.Base()
|
||||||
|
Language = language.Make(base.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func SystemLang() string {
|
||||||
|
lang := os.Getenv("LANG")
|
||||||
|
lang, _, _ = strings.Cut(lang, ".")
|
||||||
|
if lang == "" {
|
||||||
|
lang = "en"
|
||||||
|
}
|
||||||
|
return lang
|
||||||
|
}
|
||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import _ "embed"
|
import _ "embed"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,12 +24,17 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"go.arsenm.dev/logger/log"
|
||||||
|
"go.arsenm.dev/lure/internal/config"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
"modernc.org/sqlite"
|
"modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const CurrentVersion = 1
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
sqlite.MustRegisterScalarFunction("json_array_contains", 2, JsonArrayContains)
|
sqlite.MustRegisterScalarFunction("json_array_contains", 2, JsonArrayContains)
|
||||||
}
|
}
|
||||||
@@ -22,9 +45,9 @@ type Package struct {
|
|||||||
Version string `sh:"version,required" db:"version"`
|
Version string `sh:"version,required" db:"version"`
|
||||||
Release int `sh:"release,required" db:"release"`
|
Release int `sh:"release,required" db:"release"`
|
||||||
Epoch uint `sh:"epoch" db:"epoch"`
|
Epoch uint `sh:"epoch" db:"epoch"`
|
||||||
Description string `sh:"desc" db:"description"`
|
Description JSON[map[string]string] `db:"description"`
|
||||||
Homepage string `sh:"homepage" db:"homepage"`
|
Homepage JSON[map[string]string] `db:"homepage"`
|
||||||
Maintainer string `sh:"maintainer" db:"maintainer"`
|
Maintainer JSON[map[string]string] `db:"maintainer"`
|
||||||
Architectures JSON[[]string] `sh:"architectures" db:"architectures"`
|
Architectures JSON[[]string] `sh:"architectures" db:"architectures"`
|
||||||
Licenses JSON[[]string] `sh:"license" db:"licenses"`
|
Licenses JSON[[]string] `sh:"license" db:"licenses"`
|
||||||
Provides JSON[[]string] `sh:"provides" db:"provides"`
|
Provides JSON[[]string] `sh:"provides" db:"provides"`
|
||||||
@@ -35,8 +58,42 @@ type Package struct {
|
|||||||
Repository string `db:"repository"`
|
Repository string `db:"repository"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type version struct {
|
||||||
|
Version int `db:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(dsn string) (*sqlx.DB, error) {
|
||||||
|
if dsn != ":memory:" {
|
||||||
|
fi, err := os.Stat(config.DBPath)
|
||||||
|
if err == nil {
|
||||||
|
// TODO: This should be removed by the first stable release.
|
||||||
|
if fi.IsDir() {
|
||||||
|
log.Warn("Your database is using the old database engine; rebuilding").Send()
|
||||||
|
|
||||||
|
err = os.RemoveAll(config.DBPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error removing old database").Err(err).Send()
|
||||||
|
}
|
||||||
|
config.DBPresent = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := sqlx.Open("sqlite", dsn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error opening database").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Init(db, dsn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error initializing database").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Init initializes the database
|
// Init initializes the database
|
||||||
func Init(db *sqlx.DB) error {
|
func Init(db *sqlx.DB, dsn string) error {
|
||||||
*db = *db.Unsafe()
|
*db = *db.Unsafe()
|
||||||
_, err := db.Exec(`
|
_, err := db.Exec(`
|
||||||
CREATE TABLE IF NOT EXISTS pkgs (
|
CREATE TABLE IF NOT EXISTS pkgs (
|
||||||
@@ -45,9 +102,9 @@ func Init(db *sqlx.DB) error {
|
|||||||
version TEXT NOT NULL,
|
version TEXT NOT NULL,
|
||||||
release INT NOT NULL,
|
release INT NOT NULL,
|
||||||
epoch INT,
|
epoch INT,
|
||||||
description TEXT,
|
description TEXT CHECK(description = 'null' OR (JSON_VALID(description) AND JSON_TYPE(description) = 'object')),
|
||||||
homepage TEXT,
|
homepage TEXT CHECK(homepage = 'null' OR (JSON_VALID(homepage) AND JSON_TYPE(homepage) = 'object')),
|
||||||
maintainer TEXT,
|
maintainer TEXT CHECK(maintainer = 'null' OR (JSON_VALID(maintainer) AND JSON_TYPE(maintainer) = 'object')),
|
||||||
architectures TEXT CHECK(architectures = 'null' OR (JSON_VALID(architectures) AND JSON_TYPE(architectures) = 'array')),
|
architectures TEXT CHECK(architectures = 'null' OR (JSON_VALID(architectures) AND JSON_TYPE(architectures) = 'array')),
|
||||||
licenses TEXT CHECK(licenses = 'null' OR (JSON_VALID(licenses) AND JSON_TYPE(licenses) = 'array')),
|
licenses TEXT CHECK(licenses = 'null' OR (JSON_VALID(licenses) AND JSON_TYPE(licenses) = 'array')),
|
||||||
provides TEXT CHECK(provides = 'null' OR (JSON_VALID(provides) AND JSON_TYPE(provides) = 'array')),
|
provides TEXT CHECK(provides = 'null' OR (JSON_VALID(provides) AND JSON_TYPE(provides) = 'array')),
|
||||||
@@ -57,7 +114,52 @@ func Init(db *sqlx.DB) error {
|
|||||||
builddepends TEXT CHECK(builddepends = 'null' OR (JSON_VALID(builddepends) AND JSON_TYPE(builddepends) = 'object')),
|
builddepends TEXT CHECK(builddepends = 'null' OR (JSON_VALID(builddepends) AND JSON_TYPE(builddepends) = 'object')),
|
||||||
UNIQUE(name, repository)
|
UNIQUE(name, repository)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS lure_db_version (
|
||||||
|
version INT NOT NULL
|
||||||
|
);
|
||||||
`)
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ver, ok := GetVersion(db)
|
||||||
|
if !ok {
|
||||||
|
log.Warn("Database version does not exist. Run lure fix if something isn't working.").Send()
|
||||||
|
return addVersion(db, CurrentVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ver != CurrentVersion {
|
||||||
|
log.Warn("Database version mismatch; rebuilding").Int("version", ver).Int("expected", CurrentVersion).Send()
|
||||||
|
|
||||||
|
db.Close()
|
||||||
|
err = os.Remove(config.DBPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.DBPresent = false
|
||||||
|
|
||||||
|
tdb, err := Open(dsn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*db = *tdb
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVersion(db *sqlx.DB) (int, bool) {
|
||||||
|
var ver version
|
||||||
|
err := db.Get(&ver, "SELECT * FROM lure_db_version LIMIT 1;")
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return ver.Version, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func addVersion(db *sqlx.DB, ver int) error {
|
||||||
|
_, err := db.Exec(`INSERT INTO lure_db_version(version) VALUES (?);`, ver)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package db_test
|
package db_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -10,13 +28,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var testPkg = db.Package{
|
var testPkg = db.Package{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Version: "0.0.1",
|
Version: "0.0.1",
|
||||||
Release: 1,
|
Release: 1,
|
||||||
Epoch: 2,
|
Epoch: 2,
|
||||||
Description: "Test package",
|
Description: db.NewJSON(map[string]string{
|
||||||
Homepage: "https://lure.arsenm.dev",
|
"en": "Test package",
|
||||||
Maintainer: "Arsen Musayelyan <arsen@arsenm.dev>",
|
"ru": "Проверочный пакет",
|
||||||
|
}),
|
||||||
|
Homepage: db.NewJSON(map[string]string{
|
||||||
|
"en": "https://lure.arsenm.dev",
|
||||||
|
}),
|
||||||
|
Maintainer: db.NewJSON(map[string]string{
|
||||||
|
"en": "Arsen Musayelyan <arsen@arsenm.dev>",
|
||||||
|
"ru": "Арсен Мусаелян <arsen@arsenm.dev>",
|
||||||
|
}),
|
||||||
Architectures: db.NewJSON([]string{"arm64", "amd64"}),
|
Architectures: db.NewJSON([]string{"arm64", "amd64"}),
|
||||||
Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}),
|
Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}),
|
||||||
Provides: db.NewJSON([]string{"test"}),
|
Provides: db.NewJSON([]string{"test"}),
|
||||||
@@ -32,18 +58,6 @@ var testPkg = db.Package{
|
|||||||
Repository: "default",
|
Repository: "default",
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDB(t *testing.T) (*sqlx.DB, error) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.Init(gdb)
|
|
||||||
return gdb, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
gdb, err := sqlx.Open("sqlite", ":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -51,7 +65,7 @@ func TestInit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer gdb.Close()
|
||||||
|
|
||||||
err = db.Init(gdb)
|
err = db.Init(gdb, ":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
@@ -60,10 +74,17 @@ func TestInit(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ver, ok := db.GetVersion(gdb)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Expected version to be present")
|
||||||
|
} else if ver != db.CurrentVersion {
|
||||||
|
t.Errorf("Expected version %d, got %d", db.CurrentVersion, ver)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertPackage(t *testing.T) {
|
func TestInsertPackage(t *testing.T) {
|
||||||
gdb, err := getDB(t)
|
gdb, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
@@ -86,7 +107,7 @@ func TestInsertPackage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetPkgs(t *testing.T) {
|
func TestGetPkgs(t *testing.T) {
|
||||||
gdb, err := getDB(t)
|
gdb, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
@@ -126,7 +147,7 @@ func TestGetPkgs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetPkg(t *testing.T) {
|
func TestGetPkg(t *testing.T) {
|
||||||
gdb, err := getDB(t)
|
gdb, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
@@ -162,7 +183,7 @@ func TestGetPkg(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDeletePkgs(t *testing.T) {
|
func TestDeletePkgs(t *testing.T) {
|
||||||
gdb, err := getDB(t)
|
gdb, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
@@ -200,7 +221,7 @@ func TestDeletePkgs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJsonArrayContains(t *testing.T) {
|
func TestJsonArrayContains(t *testing.T) {
|
||||||
gdb, err := getDB(t)
|
gdb, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
325
internal/dl/dl.go
Normal file
325
internal/dl/dl.go
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package dl contains abstractions for downloadingfiles and directories
|
||||||
|
// from various sources.
|
||||||
|
package dl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/purell"
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
"go.arsenm.dev/logger/log"
|
||||||
|
"go.arsenm.dev/lure/internal/dlcache"
|
||||||
|
)
|
||||||
|
|
||||||
|
const manifestFileName = ".lure_cache_manifest"
|
||||||
|
|
||||||
|
// ErrChecksumMismatch occurs when the checksum of a downloaded file
|
||||||
|
// does not match the expected checksum provided in the Options struct.
|
||||||
|
var ErrChecksumMismatch = errors.New("dl: checksums did not match")
|
||||||
|
|
||||||
|
// Downloaders contains all the downloaders in the order in which
|
||||||
|
// they should be checked
|
||||||
|
var Downloaders = []Downloader{
|
||||||
|
GitDownloader{},
|
||||||
|
FileDownloader{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type represents the type of download (file or directory)
|
||||||
|
type Type uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeFile Type = iota
|
||||||
|
TypeDir
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t Type) String() string {
|
||||||
|
switch t {
|
||||||
|
case TypeFile:
|
||||||
|
return "file"
|
||||||
|
case TypeDir:
|
||||||
|
return "dir"
|
||||||
|
}
|
||||||
|
return "<unknown>"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options contains the options for downloading
|
||||||
|
// files and directories
|
||||||
|
type Options struct {
|
||||||
|
SHA256 []byte
|
||||||
|
Name string
|
||||||
|
URL string
|
||||||
|
Destination string
|
||||||
|
CacheDisabled bool
|
||||||
|
PostprocDisabled bool
|
||||||
|
Progress io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manifest holds information about the type and name
|
||||||
|
// of a downloaded file or directory. It is stored inside
|
||||||
|
// each cache directory for later use.
|
||||||
|
type Manifest struct {
|
||||||
|
Type Type
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Downloader interface {
|
||||||
|
// Name returns the name of the downloader
|
||||||
|
Name() string
|
||||||
|
// MatchURL checks if the given URL matches
|
||||||
|
// the downloader.
|
||||||
|
MatchURL(string) bool
|
||||||
|
// Download downloads the object at the URL
|
||||||
|
// provided in the options, to the destination
|
||||||
|
// given in the options. It returns a type,
|
||||||
|
// a name for the downloaded object (this may be empty),
|
||||||
|
// and an error.
|
||||||
|
Download(Options) (Type, string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatingDownloader extends the Downloader interface
|
||||||
|
// with an Update method for protocols such as git, which
|
||||||
|
// allow for incremental updates without changing the URL.
|
||||||
|
type UpdatingDownloader interface {
|
||||||
|
Downloader
|
||||||
|
// Update checks for and performs any
|
||||||
|
// available updates for the object
|
||||||
|
// described in the options. It returns
|
||||||
|
// true if an update was performed, or
|
||||||
|
// false if no update was required.
|
||||||
|
Update(Options) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download downloads a file or directory using the specified options.
|
||||||
|
// It first gets the appropriate downloader for the URL, then checks
|
||||||
|
// if caching is enabled. If caching is enabled, it attempts to get
|
||||||
|
// the cache directory for the URL and update it if necessary.
|
||||||
|
// If the source is found in the cache, it links it to the destination
|
||||||
|
// using hard links. If the source is not found in the cache,
|
||||||
|
// it downloads the source to a new cache directory and links it
|
||||||
|
// to the destination.
|
||||||
|
func Download(ctx context.Context, opts Options) (err error) {
|
||||||
|
normalized, err := normalizeURL(opts.URL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opts.URL = normalized
|
||||||
|
|
||||||
|
d := getDownloader(opts.URL)
|
||||||
|
|
||||||
|
if opts.CacheDisabled {
|
||||||
|
_, _, err = d.Download(opts)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var t Type
|
||||||
|
cacheDir, ok := dlcache.Get(opts.URL)
|
||||||
|
if ok {
|
||||||
|
var updated bool
|
||||||
|
if d, ok := d.(UpdatingDownloader); ok {
|
||||||
|
log.Info("Source can be updated, updating if required").Str("source", opts.Name).Str("downloader", d.Name()).Send()
|
||||||
|
|
||||||
|
updated, err = d.Update(Options{
|
||||||
|
Name: opts.Name,
|
||||||
|
URL: opts.URL,
|
||||||
|
Destination: cacheDir,
|
||||||
|
Progress: opts.Progress,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := getManifest(cacheDir)
|
||||||
|
if err == nil {
|
||||||
|
t = m.Type
|
||||||
|
|
||||||
|
dest := filepath.Join(opts.Destination, m.Name)
|
||||||
|
ok, err := handleCache(cacheDir, dest, t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok && !updated {
|
||||||
|
log.Info("Source found in cache, linked to destination").Str("source", opts.Name).Stringer("type", t).Send()
|
||||||
|
return nil
|
||||||
|
} else if ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we cannot read the manifest,
|
||||||
|
// this cache entry is invalid and
|
||||||
|
// the source must be re-downloaded.
|
||||||
|
err = os.RemoveAll(cacheDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Downloading source").Str("source", opts.Name).Str("downloader", d.Name()).Send()
|
||||||
|
|
||||||
|
cacheDir, err = dlcache.New(opts.URL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, name, err := d.Download(Options{
|
||||||
|
Name: opts.Name,
|
||||||
|
URL: opts.URL,
|
||||||
|
Destination: cacheDir,
|
||||||
|
Progress: opts.Progress,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeManifest(cacheDir, Manifest{t, name})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dest := filepath.Join(opts.Destination, name)
|
||||||
|
_, err = handleCache(cacheDir, dest, t)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeManifest writes the manifest to the specified cache directory.
|
||||||
|
func writeManifest(cacheDir string, m Manifest) error {
|
||||||
|
fl, err := os.Create(filepath.Join(cacheDir, manifestFileName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fl.Close()
|
||||||
|
return msgpack.NewEncoder(fl).Encode(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getManifest reads the manifest from the specified cache directory.
|
||||||
|
func getManifest(cacheDir string) (m Manifest, err error) {
|
||||||
|
fl, err := os.Open(filepath.Join(cacheDir, manifestFileName))
|
||||||
|
if err != nil {
|
||||||
|
return Manifest{}, err
|
||||||
|
}
|
||||||
|
defer fl.Close()
|
||||||
|
|
||||||
|
err = msgpack.NewDecoder(fl).Decode(&m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleCache links the cache directory or a file within it to the destination
|
||||||
|
func handleCache(cacheDir, dest string, t Type) (bool, error) {
|
||||||
|
switch t {
|
||||||
|
case TypeFile:
|
||||||
|
cd, err := os.Open(cacheDir)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
names, err := cd.Readdirnames(2)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cd.Close()
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
if name == manifestFileName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Link(filepath.Join(cacheDir, names[0]), filepath.Join(dest, filepath.Base(names[0])))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
case TypeDir:
|
||||||
|
err := linkDir(cacheDir, dest)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// linkDir recursively walks through a directory, creating
|
||||||
|
// hard links for each file from the src directory to the
|
||||||
|
// dest directory. If it encounters a directory, it will
|
||||||
|
// create a directory with the same name and permissions
|
||||||
|
// in the dest directory, because hard links cannot be
|
||||||
|
// created for directories.
|
||||||
|
func linkDir(src, dest string) error {
|
||||||
|
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Name() == manifestFileName {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rel, err := filepath.Rel(src, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newPath := filepath.Join(dest, rel)
|
||||||
|
if info.IsDir() {
|
||||||
|
return os.MkdirAll(newPath, info.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Link(path, newPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDownloader(u string) Downloader {
|
||||||
|
for _, d := range Downloaders {
|
||||||
|
if d.MatchURL(u) {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizeURL normalizes a URL string, so that insignificant
|
||||||
|
// differences don't change the hash.
|
||||||
|
func normalizeURL(u string) (string, error) {
|
||||||
|
const normalizationFlags = purell.FlagRemoveTrailingSlash |
|
||||||
|
purell.FlagRemoveDefaultPort |
|
||||||
|
purell.FlagLowercaseHost |
|
||||||
|
purell.FlagLowercaseScheme |
|
||||||
|
purell.FlagRemoveDuplicateSlashes |
|
||||||
|
purell.FlagRemoveFragment |
|
||||||
|
purell.FlagRemoveUnnecessaryHostDots |
|
||||||
|
purell.FlagSortQuery |
|
||||||
|
purell.FlagDecodeHexHost |
|
||||||
|
purell.FlagDecodeOctalHost |
|
||||||
|
purell.FlagDecodeUnnecessaryEscapes |
|
||||||
|
purell.FlagRemoveEmptyPortSeparator
|
||||||
|
|
||||||
|
return purell.NormalizeURLString(u, normalizationFlags)
|
||||||
|
}
|
||||||
243
internal/dl/file.go
Normal file
243
internal/dl/file.go
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mholt/archiver/v4"
|
||||||
|
"github.com/schollz/progressbar/v3"
|
||||||
|
"go.arsenm.dev/lure/internal/shutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileDownloader downloads files using HTTP
|
||||||
|
type FileDownloader struct{}
|
||||||
|
|
||||||
|
// Name always returns "file"
|
||||||
|
func (FileDownloader) Name() string {
|
||||||
|
return "file"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchURL always returns true, as FileDownloader
|
||||||
|
// is used as a fallback if nothing else matches
|
||||||
|
func (FileDownloader) MatchURL(string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download downloads a file using HTTP. If the file is
|
||||||
|
// compressed using a supported format, it will be extracted
|
||||||
|
func (FileDownloader) Download(opts Options) (Type, string, error) {
|
||||||
|
u, err := url.Parse(opts.URL)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
query := u.Query()
|
||||||
|
|
||||||
|
name := query.Get("~name")
|
||||||
|
query.Del("~name")
|
||||||
|
|
||||||
|
archive := query.Get("~archive")
|
||||||
|
query.Del("~archive")
|
||||||
|
|
||||||
|
u.RawQuery = query.Encode()
|
||||||
|
|
||||||
|
res, err := http.Get(u.String())
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
name = getFilename(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.PostprocDisabled = archive == "false"
|
||||||
|
|
||||||
|
path := filepath.Join(opts.Destination, name)
|
||||||
|
fl, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
defer fl.Close()
|
||||||
|
|
||||||
|
var bar io.WriteCloser
|
||||||
|
if opts.Progress != nil {
|
||||||
|
bar = progressbar.NewOptions64(
|
||||||
|
res.ContentLength,
|
||||||
|
progressbar.OptionSetDescription(name),
|
||||||
|
progressbar.OptionSetWriter(opts.Progress),
|
||||||
|
progressbar.OptionShowBytes(true),
|
||||||
|
progressbar.OptionSetWidth(10),
|
||||||
|
progressbar.OptionThrottle(65*time.Millisecond),
|
||||||
|
progressbar.OptionShowCount(),
|
||||||
|
progressbar.OptionOnCompletion(func() {
|
||||||
|
_, _ = io.WriteString(opts.Progress, "\n")
|
||||||
|
}),
|
||||||
|
progressbar.OptionSpinnerType(14),
|
||||||
|
progressbar.OptionFullWidth(),
|
||||||
|
progressbar.OptionSetRenderBlankState(true),
|
||||||
|
)
|
||||||
|
defer bar.Close()
|
||||||
|
} else {
|
||||||
|
bar = shutils.NopRWC{}
|
||||||
|
}
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
|
||||||
|
var w io.Writer
|
||||||
|
if opts.SHA256 != nil {
|
||||||
|
w = io.MultiWriter(fl, h, bar)
|
||||||
|
} else {
|
||||||
|
w = io.MultiWriter(fl, bar)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(w, res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
|
||||||
|
if opts.SHA256 != nil {
|
||||||
|
sum := h.Sum(nil)
|
||||||
|
if !bytes.Equal(sum, opts.SHA256) {
|
||||||
|
return 0, "", ErrChecksumMismatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.PostprocDisabled {
|
||||||
|
return TypeFile, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fl.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
format, r, err := archiver.Identify(name, fl)
|
||||||
|
if err == archiver.ErrNoMatch {
|
||||||
|
return TypeFile, "", nil
|
||||||
|
} else if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = extractFile(r, format, name, opts)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Remove(path)
|
||||||
|
return TypeDir, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractFile extracts an archive or decompresses a file
|
||||||
|
func extractFile(r io.Reader, format archiver.Format, name string, opts Options) (err error) {
|
||||||
|
fname := format.Name()
|
||||||
|
|
||||||
|
switch format := format.(type) {
|
||||||
|
case archiver.Extractor:
|
||||||
|
err = format.Extract(context.Background(), r, nil, func(ctx context.Context, f archiver.File) error {
|
||||||
|
fr, err := f.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fr.Close()
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fm := fi.Mode()
|
||||||
|
|
||||||
|
path := filepath.Join(opts.Destination, f.NameInArchive)
|
||||||
|
|
||||||
|
err = os.MkdirAll(filepath.Dir(path), 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.IsDir() {
|
||||||
|
err = os.Mkdir(path, 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outFl, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fm.Perm())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer outFl.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(outFl, fr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case archiver.Decompressor:
|
||||||
|
rc, err := format.OpenReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
path := filepath.Join(opts.Destination, name)
|
||||||
|
path = strings.TrimSuffix(path, fname)
|
||||||
|
|
||||||
|
outFl, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(outFl, rc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
180
internal/dl/git.go
Normal file
180
internal/dl/git.go
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitDownloader downloads Git repositories
|
||||||
|
type GitDownloader struct{}
|
||||||
|
|
||||||
|
// Name always returns "git"
|
||||||
|
func (GitDownloader) Name() string {
|
||||||
|
return "git"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchURL matches any URLs that start with "git+"
|
||||||
|
func (GitDownloader) MatchURL(u string) bool {
|
||||||
|
return strings.HasPrefix(u, "git+")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download uses git to clone the repository from the specified URL.
|
||||||
|
// It allows specifying the revision, depth and recursion options
|
||||||
|
// via query string
|
||||||
|
func (GitDownloader) Download(opts Options) (Type, string, error) {
|
||||||
|
u, err := url.Parse(opts.URL)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
u.Scheme = strings.TrimPrefix(u.Scheme, "git+")
|
||||||
|
|
||||||
|
query := u.Query()
|
||||||
|
|
||||||
|
rev := query.Get("~rev")
|
||||||
|
query.Del("~rev")
|
||||||
|
|
||||||
|
name := query.Get("~name")
|
||||||
|
query.Del("~name")
|
||||||
|
|
||||||
|
depthStr := query.Get("~depth")
|
||||||
|
query.Del("~depth")
|
||||||
|
|
||||||
|
recursive := query.Get("~recursive")
|
||||||
|
query.Del("~recursive")
|
||||||
|
|
||||||
|
u.RawQuery = query.Encode()
|
||||||
|
|
||||||
|
depth := 0
|
||||||
|
if depthStr != "" {
|
||||||
|
depth, err = strconv.Atoi(depthStr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
co := &git.CloneOptions{
|
||||||
|
URL: u.String(),
|
||||||
|
Depth: depth,
|
||||||
|
Progress: opts.Progress,
|
||||||
|
RecurseSubmodules: git.NoRecurseSubmodules,
|
||||||
|
}
|
||||||
|
|
||||||
|
if recursive == "true" {
|
||||||
|
co.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := git.PlainClone(opts.Destination, false, co)
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rev != "" {
|
||||||
|
h, err := r.ResolveRevision(plumbing.Revision(rev))
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := r.Worktree()
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.Checkout(&git.CheckoutOptions{
|
||||||
|
Hash: *h,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
name = strings.TrimSuffix(path.Base(u.Path), ".git")
|
||||||
|
}
|
||||||
|
|
||||||
|
return TypeDir, name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update uses git to pull the repository and update it
|
||||||
|
// to the latest revision. It allows specifying the depth
|
||||||
|
// and recursion options via query string. It returns
|
||||||
|
// true if update was successful and false if the
|
||||||
|
// repository is already up-to-date
|
||||||
|
func (GitDownloader) Update(opts Options) (bool, error) {
|
||||||
|
u, err := url.Parse(opts.URL)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
u.Scheme = strings.TrimPrefix(u.Scheme, "git+")
|
||||||
|
|
||||||
|
query := u.Query()
|
||||||
|
query.Del("~rev")
|
||||||
|
|
||||||
|
depthStr := query.Get("~depth")
|
||||||
|
query.Del("~depth")
|
||||||
|
|
||||||
|
recursive := query.Get("~recursive")
|
||||||
|
query.Del("~recursive")
|
||||||
|
|
||||||
|
u.RawQuery = query.Encode()
|
||||||
|
|
||||||
|
r, err := git.PlainOpen(opts.Destination)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := r.Worktree()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
depth := 0
|
||||||
|
if depthStr != "" {
|
||||||
|
depth, err = strconv.Atoi(depthStr)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
po := &git.PullOptions{
|
||||||
|
Depth: depth,
|
||||||
|
Progress: opts.Progress,
|
||||||
|
RecurseSubmodules: git.NoRecurseSubmodules,
|
||||||
|
}
|
||||||
|
|
||||||
|
if recursive == "true" {
|
||||||
|
po.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.Pull(po)
|
||||||
|
if errors.Is(err, git.NoErrAlreadyUpToDate) {
|
||||||
|
return false, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
90
internal/dlcache/dlcache.go
Normal file
90
internal/dlcache/dlcache.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dlcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"go.arsenm.dev/lure/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BasePath stores the base path to the download cache
|
||||||
|
var BasePath = filepath.Join(config.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) {
|
||||||
|
h, err := hashID(id)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
itemPath := filepath.Join(BasePath, h)
|
||||||
|
|
||||||
|
fi, err := os.Stat(itemPath)
|
||||||
|
if err == nil || (fi != nil && !fi.IsDir()) {
|
||||||
|
err = os.RemoveAll(itemPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.MkdirAll(itemPath, 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get checks if an entry with the given ID
|
||||||
|
// already exists in the cache, and if so,
|
||||||
|
// returns the directory and true. If it
|
||||||
|
// does not exist, it returns an empty string
|
||||||
|
// and false.
|
||||||
|
func Get(id string) (string, bool) {
|
||||||
|
h, err := hashID(id)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
itemPath := filepath.Join(BasePath, h)
|
||||||
|
|
||||||
|
_, err = os.Stat(itemPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemPath, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashID hashes the input ID with SHA1
|
||||||
|
// and returns the hex string of the hashed
|
||||||
|
// ID.
|
||||||
|
func hashID(id string) (string, error) {
|
||||||
|
h := sha1.New()
|
||||||
|
_, err := io.WriteString(h, id)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(h.Sum(nil)), nil
|
||||||
|
}
|
||||||
74
internal/dlcache/dlcache_test.go
Normal file
74
internal/dlcache/dlcache_test.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dlcache_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.arsenm.dev/lure/internal/dlcache"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dir, err := os.MkdirTemp("/tmp", "lure-dlcache-test.*")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dlcache.BasePath = dir
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
const id = "https://example.com"
|
||||||
|
dir, err := dlcache.New(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no error, got %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
exp := filepath.Join(dlcache.BasePath, sha1sum(id))
|
||||||
|
if dir != exp {
|
||||||
|
t.Errorf("Expected %s, got %s", exp, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("stat: expected no error, got %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fi.IsDir() {
|
||||||
|
t.Errorf("Expected cache item to be a directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir2, ok := dlcache.Get(id)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Expected Get() to return valid value")
|
||||||
|
}
|
||||||
|
if dir2 != dir {
|
||||||
|
t.Errorf("Expected %s from Get(), got %s", dir, dir2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sha1sum(id string) string {
|
||||||
|
h := sha1.New()
|
||||||
|
_, _ = io.WriteString(h, id)
|
||||||
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
@@ -1,32 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package overrides
|
package overrides
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.arsenm.dev/lure/distro"
|
"go.arsenm.dev/lure/distro"
|
||||||
"go.arsenm.dev/lure/internal/cpu"
|
"go.arsenm.dev/lure/internal/cpu"
|
||||||
|
"go.arsenm.dev/lure/internal/db"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Opts struct {
|
type Opts struct {
|
||||||
Name string
|
Name string
|
||||||
Overrides bool
|
Overrides bool
|
||||||
LikeDistros bool
|
LikeDistros bool
|
||||||
|
Languages []string
|
||||||
|
LanguageTags []language.Tag
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultOpts = &Opts{
|
var DefaultOpts = &Opts{
|
||||||
Overrides: true,
|
Overrides: true,
|
||||||
LikeDistros: true,
|
LikeDistros: true,
|
||||||
|
Languages: []string{"en"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve generates a slice of possible override names in the order that they should be checked
|
// Resolve generates a slice of possible override names in the order that they should be checked
|
||||||
func Resolve(info *distro.OSRelease, opts *Opts) []string {
|
func Resolve(info *distro.OSRelease, opts *Opts) ([]string, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = DefaultOpts
|
opts = DefaultOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opts.Overrides {
|
if !opts.Overrides {
|
||||||
return []string{opts.Name}
|
return []string{opts.Name}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
langs, err := parseLangs(opts.Languages, opts.LanguageTags)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
architectures := []string{runtime.GOARCH}
|
architectures := []string{runtime.GOARCH}
|
||||||
@@ -71,7 +101,22 @@ func Resolve(info *distro.OSRelease, opts *Opts) []string {
|
|||||||
out[index] = strings.ReplaceAll(item, "-", "_")
|
out[index] = strings.ReplaceAll(item, "-", "_")
|
||||||
}
|
}
|
||||||
|
|
||||||
return out
|
if len(langs) > 0 {
|
||||||
|
tmp := out
|
||||||
|
out = make([]string, 0, len(tmp)+(len(tmp)*len(langs)))
|
||||||
|
for _, lang := range langs {
|
||||||
|
for _, val := range tmp {
|
||||||
|
if val == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, val+"_"+lang)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out = append(out, tmp...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Opts) WithName(name string) *Opts {
|
func (o *Opts) WithName(name string) *Opts {
|
||||||
@@ -97,3 +142,94 @@ func (o *Opts) WithLikeDistros(v bool) *Opts {
|
|||||||
out.LikeDistros = v
|
out.LikeDistros = v
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Opts) WithLanguages(langs []string) *Opts {
|
||||||
|
out := &Opts{}
|
||||||
|
*out = *o
|
||||||
|
|
||||||
|
out.Languages = langs
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Opts) WithLanguageTags(langs []string) *Opts {
|
||||||
|
out := &Opts{}
|
||||||
|
*out = *o
|
||||||
|
|
||||||
|
out.Languages = langs
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolvedPackage is a LURE package after its overrides
|
||||||
|
// have been resolved
|
||||||
|
type ResolvedPackage struct {
|
||||||
|
Name string `sh:"name"`
|
||||||
|
Version string `sh:"version"`
|
||||||
|
Release int `sh:"release"`
|
||||||
|
Epoch uint `sh:"epoch"`
|
||||||
|
Description string `db:"description"`
|
||||||
|
Homepage string `db:"homepage"`
|
||||||
|
Maintainer string `db:"maintainer"`
|
||||||
|
Architectures []string `sh:"architectures"`
|
||||||
|
Licenses []string `sh:"license"`
|
||||||
|
Provides []string `sh:"provides"`
|
||||||
|
Conflicts []string `sh:"conflicts"`
|
||||||
|
Replaces []string `sh:"replaces"`
|
||||||
|
Depends []string `sh:"deps"`
|
||||||
|
BuildDepends []string `sh:"build_deps"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolvePackage(pkg *db.Package, overrides []string) *ResolvedPackage {
|
||||||
|
out := &ResolvedPackage{}
|
||||||
|
outVal := reflect.ValueOf(out).Elem()
|
||||||
|
pkgVal := reflect.ValueOf(pkg).Elem()
|
||||||
|
|
||||||
|
for i := 0; i < outVal.NumField(); i++ {
|
||||||
|
fieldVal := outVal.Field(i)
|
||||||
|
fieldType := fieldVal.Type()
|
||||||
|
pkgFieldVal := pkgVal.FieldByName(outVal.Type().Field(i).Name)
|
||||||
|
pkgFieldType := pkgFieldVal.Type()
|
||||||
|
|
||||||
|
if strings.HasPrefix(pkgFieldType.String(), "db.JSON") {
|
||||||
|
pkgFieldVal = pkgFieldVal.FieldByName("Val")
|
||||||
|
pkgFieldType = pkgFieldVal.Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgFieldType.AssignableTo(fieldType) {
|
||||||
|
fieldVal.Set(pkgFieldVal)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgFieldVal.Kind() == reflect.Map && pkgFieldType.Elem().AssignableTo(fieldType) {
|
||||||
|
for _, override := range overrides {
|
||||||
|
overrideVal := pkgFieldVal.MapIndex(reflect.ValueOf(override))
|
||||||
|
if !overrideVal.IsValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldVal.Set(overrideVal)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLangs(langs []string, tags []language.Tag) ([]string, error) {
|
||||||
|
out := make([]string, len(tags)+len(langs))
|
||||||
|
for i, tag := range tags {
|
||||||
|
base, _ := tag.Base()
|
||||||
|
out[i] = base.String()
|
||||||
|
}
|
||||||
|
for i, lang := range langs {
|
||||||
|
tag, err := language.Parse(lang)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
base, _ := tag.Base()
|
||||||
|
out[len(tags)+i] = base.String()
|
||||||
|
}
|
||||||
|
slices.Sort(out)
|
||||||
|
out = slices.Compact(out)
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package overrides_test
|
package overrides_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,6 +24,7 @@ import (
|
|||||||
|
|
||||||
"go.arsenm.dev/lure/distro"
|
"go.arsenm.dev/lure/distro"
|
||||||
"go.arsenm.dev/lure/internal/overrides"
|
"go.arsenm.dev/lure/internal/overrides"
|
||||||
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
|
||||||
var info = &distro.OSRelease{
|
var info = &distro.OSRelease{
|
||||||
@@ -14,9 +33,19 @@ var info = &distro.OSRelease{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestResolve(t *testing.T) {
|
func TestResolve(t *testing.T) {
|
||||||
names := overrides.Resolve(info, nil)
|
names, err := overrides.Resolve(info, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
expected := []string{
|
expected := []string{
|
||||||
|
"amd64_centos_en",
|
||||||
|
"centos_en",
|
||||||
|
"amd64_rhel_en",
|
||||||
|
"rhel_en",
|
||||||
|
"amd64_fedora_en",
|
||||||
|
"fedora_en",
|
||||||
|
"amd64_en",
|
||||||
"amd64_centos",
|
"amd64_centos",
|
||||||
"centos",
|
"centos",
|
||||||
"amd64_rhel",
|
"amd64_rhel",
|
||||||
@@ -33,11 +62,14 @@ func TestResolve(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveName(t *testing.T) {
|
func TestResolveName(t *testing.T) {
|
||||||
names := overrides.Resolve(info, &overrides.Opts{
|
names, err := overrides.Resolve(info, &overrides.Opts{
|
||||||
Name: "deps",
|
Name: "deps",
|
||||||
Overrides: true,
|
Overrides: true,
|
||||||
LikeDistros: true,
|
LikeDistros: true,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
expected := []string{
|
expected := []string{
|
||||||
"deps_amd64_centos",
|
"deps_amd64_centos",
|
||||||
@@ -56,10 +88,13 @@ func TestResolveName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveNoLikeDistros(t *testing.T) {
|
func TestResolveNoLikeDistros(t *testing.T) {
|
||||||
names := overrides.Resolve(info, &overrides.Opts{
|
names, err := overrides.Resolve(info, &overrides.Opts{
|
||||||
Overrides: true,
|
Overrides: true,
|
||||||
LikeDistros: false,
|
LikeDistros: false,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
expected := []string{
|
expected := []string{
|
||||||
"amd64_centos",
|
"amd64_centos",
|
||||||
@@ -74,11 +109,14 @@ func TestResolveNoLikeDistros(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveNoOverrides(t *testing.T) {
|
func TestResolveNoOverrides(t *testing.T) {
|
||||||
names := overrides.Resolve(info, &overrides.Opts{
|
names, err := overrides.Resolve(info, &overrides.Opts{
|
||||||
Name: "deps",
|
Name: "deps",
|
||||||
Overrides: false,
|
Overrides: false,
|
||||||
LikeDistros: false,
|
LikeDistros: false,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
expected := []string{"deps"}
|
expected := []string{"deps"}
|
||||||
|
|
||||||
@@ -86,3 +124,31 @@ func TestResolveNoOverrides(t *testing.T) {
|
|||||||
t.Errorf("expected %v, got %v", expected, names)
|
t.Errorf("expected %v, got %v", expected, names)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveLangs(t *testing.T) {
|
||||||
|
names, err := overrides.Resolve(info, &overrides.Opts{
|
||||||
|
Overrides: true,
|
||||||
|
Languages: []string{"ru_RU", "en", "en_US"},
|
||||||
|
LanguageTags: []language.Tag{language.BritishEnglish},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []string{
|
||||||
|
"amd64_centos_en",
|
||||||
|
"centos_en",
|
||||||
|
"amd64_en",
|
||||||
|
"amd64_centos_ru",
|
||||||
|
"centos_ru",
|
||||||
|
"amd64_ru",
|
||||||
|
"amd64_centos",
|
||||||
|
"centos",
|
||||||
|
"amd64",
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(names, expected) {
|
||||||
|
t.Errorf("expected %v, got %v", expected, names)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package pager
|
package pager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package pager
|
package pager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package repos
|
package repos
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package repos_test
|
package repos_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,24 +24,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"go.arsenm.dev/lure/internal/db"
|
"go.arsenm.dev/lure/internal/db"
|
||||||
"go.arsenm.dev/lure/internal/repos"
|
"go.arsenm.dev/lure/internal/repos"
|
||||||
"go.arsenm.dev/lure/internal/types"
|
"go.arsenm.dev/lure/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFindPkgs(t *testing.T) {
|
func TestFindPkgs(t *testing.T) {
|
||||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
gdb, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer gdb.Close()
|
||||||
|
|
||||||
err = db.Init(gdb)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
setCfgDirs(t)
|
setCfgDirs(t)
|
||||||
defer removeCacheDir(t)
|
defer removeCacheDir(t)
|
||||||
|
|
||||||
@@ -69,39 +81,40 @@ func TestFindPkgs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFindPkgsEmpty(t *testing.T) {
|
func TestFindPkgsEmpty(t *testing.T) {
|
||||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
gdb, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer gdb.Close()
|
||||||
|
|
||||||
err = db.Init(gdb)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
setCfgDirs(t)
|
setCfgDirs(t)
|
||||||
defer removeCacheDir(t)
|
defer removeCacheDir(t)
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, db.Package{
|
err = db.InsertPackage(gdb, db.Package{
|
||||||
Name: "test1",
|
Name: "test1",
|
||||||
Repository: "default",
|
Repository: "default",
|
||||||
Version: "0.0.1",
|
Version: "0.0.1",
|
||||||
Release: 1,
|
Release: 1,
|
||||||
Description: "Test package 1",
|
Description: db.NewJSON(map[string]string{
|
||||||
Provides: db.NewJSON([]string{""}),
|
"en": "Test package 1",
|
||||||
|
"ru": "Проверочный пакет 1",
|
||||||
|
}),
|
||||||
|
Provides: db.NewJSON([]string{""}),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.InsertPackage(gdb, db.Package{
|
err = db.InsertPackage(gdb, db.Package{
|
||||||
Name: "test2",
|
Name: "test2",
|
||||||
Repository: "default",
|
Repository: "default",
|
||||||
Version: "0.0.1",
|
Version: "0.0.1",
|
||||||
Release: 1,
|
Release: 1,
|
||||||
Description: "Test package 2",
|
Description: db.NewJSON(map[string]string{
|
||||||
Provides: db.NewJSON([]string{"test"}),
|
"en": "Test package 2",
|
||||||
|
"ru": "Проверочный пакет 2",
|
||||||
|
}),
|
||||||
|
Provides: db.NewJSON([]string{"test"}),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package repos
|
package repos
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -7,6 +25,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-git/go-billy/v5"
|
"github.com/go-git/go-billy/v5"
|
||||||
@@ -18,7 +37,6 @@ import (
|
|||||||
"github.com/pelletier/go-toml/v2"
|
"github.com/pelletier/go-toml/v2"
|
||||||
"go.arsenm.dev/logger/log"
|
"go.arsenm.dev/logger/log"
|
||||||
"go.arsenm.dev/lure/distro"
|
"go.arsenm.dev/lure/distro"
|
||||||
"go.arsenm.dev/lure/download"
|
|
||||||
"go.arsenm.dev/lure/internal/config"
|
"go.arsenm.dev/lure/internal/config"
|
||||||
"go.arsenm.dev/lure/internal/db"
|
"go.arsenm.dev/lure/internal/db"
|
||||||
"go.arsenm.dev/lure/internal/shutils"
|
"go.arsenm.dev/lure/internal/shutils"
|
||||||
@@ -103,13 +121,9 @@ func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(repoURL.Scheme, "git+") {
|
_, err = git.PlainCloneContext(ctx, repoDir, false, &git.CloneOptions{
|
||||||
repoURL.Scheme = "git+" + repoURL.Scheme
|
URL: repoURL.String(),
|
||||||
}
|
Progress: os.Stderr,
|
||||||
|
|
||||||
err = download.Get(ctx, download.GetOptions{
|
|
||||||
SourceURL: repoURL.String(),
|
|
||||||
Destination: repoDir,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -271,6 +285,9 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pkg := db.Package{
|
pkg := db.Package{
|
||||||
|
Description: db.NewJSON(map[string]string{}),
|
||||||
|
Homepage: db.NewJSON(map[string]string{}),
|
||||||
|
Maintainer: db.NewJSON(map[string]string{}),
|
||||||
Depends: db.NewJSON(map[string][]string{}),
|
Depends: db.NewJSON(map[string][]string{}),
|
||||||
BuildDepends: db.NewJSON(map[string][]string{}),
|
BuildDepends: db.NewJSON(map[string][]string{}),
|
||||||
Repository: repo.Name,
|
Repository: repo.Name,
|
||||||
@@ -338,6 +355,9 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *
|
|||||||
}
|
}
|
||||||
|
|
||||||
pkg := db.Package{
|
pkg := db.Package{
|
||||||
|
Description: db.NewJSON(map[string]string{}),
|
||||||
|
Homepage: db.NewJSON(map[string]string{}),
|
||||||
|
Maintainer: db.NewJSON(map[string]string{}),
|
||||||
Depends: db.NewJSON(map[string][]string{}),
|
Depends: db.NewJSON(map[string][]string{}),
|
||||||
BuildDepends: db.NewJSON(map[string][]string{}),
|
BuildDepends: db.NewJSON(map[string][]string{}),
|
||||||
Repository: repo.Name,
|
Repository: repo.Name,
|
||||||
@@ -378,20 +398,34 @@ func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runn
|
|||||||
return d.DecodeVars(pkg)
|
return d.DecodeVars(pkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var overridable = map[string]string{
|
||||||
|
"deps": "Depends",
|
||||||
|
"build_deps": "BuildDepends",
|
||||||
|
"desc": "Description",
|
||||||
|
"homepage": "Homepage",
|
||||||
|
"maintainer": "Maintainer",
|
||||||
|
}
|
||||||
|
|
||||||
func resolveOverrides(runner *interp.Runner, pkg *db.Package) {
|
func resolveOverrides(runner *interp.Runner, pkg *db.Package) {
|
||||||
|
pkgVal := reflect.ValueOf(pkg).Elem()
|
||||||
for name, val := range runner.Vars {
|
for name, val := range runner.Vars {
|
||||||
if strings.HasPrefix(name, "deps") {
|
for prefix, field := range overridable {
|
||||||
override := strings.TrimPrefix(name, "deps")
|
if strings.HasPrefix(name, prefix) {
|
||||||
override = strings.TrimPrefix(override, "_")
|
override := strings.TrimPrefix(name, prefix)
|
||||||
|
override = strings.TrimPrefix(override, "_")
|
||||||
|
|
||||||
pkg.Depends.Val[override] = val.List
|
field := pkgVal.FieldByName(field)
|
||||||
} else if strings.HasPrefix(name, "build_deps") {
|
varVal := field.FieldByName("Val")
|
||||||
override := strings.TrimPrefix(name, "build_deps")
|
varType := varVal.Type()
|
||||||
override = strings.TrimPrefix(override, "_")
|
|
||||||
|
|
||||||
pkg.BuildDepends.Val[override] = val.List
|
switch varType.Elem().String() {
|
||||||
} else {
|
case "[]string":
|
||||||
continue
|
varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.List))
|
||||||
|
case "string":
|
||||||
|
varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.Str))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package repos_test
|
package repos_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,7 +24,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"go.arsenm.dev/lure/internal/config"
|
"go.arsenm.dev/lure/internal/config"
|
||||||
"go.arsenm.dev/lure/internal/db"
|
"go.arsenm.dev/lure/internal/db"
|
||||||
"go.arsenm.dev/lure/internal/repos"
|
"go.arsenm.dev/lure/internal/repos"
|
||||||
@@ -48,17 +65,12 @@ func removeCacheDir(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPull(t *testing.T) {
|
func TestPull(t *testing.T) {
|
||||||
gdb, err := sqlx.Open("sqlite", ":memory:")
|
gdb, err := db.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
t.Fatalf("Expected no error, got %s", err)
|
||||||
}
|
}
|
||||||
defer gdb.Close()
|
defer gdb.Close()
|
||||||
|
|
||||||
err = db.Init(gdb)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no error, got %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
setCfgDirs(t)
|
setCfgDirs(t)
|
||||||
defer removeCacheDir(t)
|
defer removeCacheDir(t)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -182,7 +182,11 @@ func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) getFunc(name string) *syntax.Stmt {
|
func (d *Decoder) getFunc(name string) *syntax.Stmt {
|
||||||
names := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
|
names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, fnName := range names {
|
for _, fnName := range names {
|
||||||
fn, ok := d.runner.Funcs[fnName]
|
fn, ok := d.runner.Funcs[fnName]
|
||||||
if ok {
|
if ok {
|
||||||
@@ -195,7 +199,11 @@ func (d *Decoder) getFunc(name string) *syntax.Stmt {
|
|||||||
// getVar gets a variable based on its name, taking into account
|
// getVar gets a variable based on its name, taking into account
|
||||||
// override variables and nameref variables.
|
// override variables and nameref variables.
|
||||||
func (d *Decoder) getVar(name string) *expand.Variable {
|
func (d *Decoder) getVar(name string) *expand.Variable {
|
||||||
names := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
|
names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, varName := range names {
|
for _, varName := range names {
|
||||||
val, ok := d.runner.Vars[varName]
|
val, ok := d.runner.Vars[varName]
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package decoder_test
|
package decoder_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package shutils_test
|
package shutils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -46,8 +46,8 @@ func (NopRWC) Read([]byte) (int, error) {
|
|||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
func (NopRWC) Write([]byte) (int, error) {
|
func (NopRWC) Write(b []byte) (int, error) {
|
||||||
return 0, io.EOF
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (NopRWC) Close() error {
|
func (NopRWC) Close() error {
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package shutils_test
|
package shutils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
// Config represents the LURE configuration file
|
// Config represents the LURE configuration file
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* LURE - Linux User REpository
|
||||||
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
// RepoConfig represents a LURE repo's lure-repo.toml file.
|
// RepoConfig represents a LURE repo's lure-repo.toml file.
|
||||||
|
|||||||
2
list.go
2
list.go
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
38
main.go
38
main.go
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,6 +20,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -33,12 +34,26 @@ import (
|
|||||||
"go.arsenm.dev/lure/internal/config"
|
"go.arsenm.dev/lure/internal/config"
|
||||||
"go.arsenm.dev/lure/internal/db"
|
"go.arsenm.dev/lure/internal/db"
|
||||||
"go.arsenm.dev/lure/manager"
|
"go.arsenm.dev/lure/manager"
|
||||||
|
"go.arsenm.dev/translate"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate scripts/gen-version.sh
|
//go:generate scripts/gen-version.sh
|
||||||
|
|
||||||
|
//go:embed translations
|
||||||
|
var translationFS embed.FS
|
||||||
|
|
||||||
|
var translator translate.Translator
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.Logger = logger.NewCLI(os.Stderr)
|
logger := logger.NewCLI(os.Stderr)
|
||||||
|
|
||||||
|
t, err := translate.NewFromFS(translationFS)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("Error creating new translator").Err(err).Send()
|
||||||
|
}
|
||||||
|
translator = t
|
||||||
|
|
||||||
|
log.Logger = translate.NewLogger(logger, t, config.Language)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -65,6 +80,13 @@ func main() {
|
|||||||
},
|
},
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
{
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "clean",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Build package from scratch even if there's an already built package available",
|
||||||
|
},
|
||||||
|
},
|
||||||
Name: "install",
|
Name: "install",
|
||||||
Usage: "Install a new package",
|
Usage: "Install a new package",
|
||||||
Aliases: []string{"in"},
|
Aliases: []string{"in"},
|
||||||
@@ -78,6 +100,13 @@ func main() {
|
|||||||
Action: removeCmd,
|
Action: removeCmd,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "clean",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Build package from scratch even if there's an already built package available",
|
||||||
|
},
|
||||||
|
},
|
||||||
Name: "upgrade",
|
Name: "upgrade",
|
||||||
Usage: "Upgrade all installed packages",
|
Usage: "Upgrade all installed packages",
|
||||||
Aliases: []string{"up"},
|
Aliases: []string{"up"},
|
||||||
@@ -115,6 +144,11 @@ func main() {
|
|||||||
Value: "lure.sh",
|
Value: "lure.sh",
|
||||||
Usage: "Path to the build script",
|
Usage: "Path to the build script",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "clean",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Build package from scratch even if there's an already built package available",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Name: "build",
|
Name: "build",
|
||||||
Usage: "Build a local package",
|
Usage: "Build a local package",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -112,15 +112,8 @@ func (a *APK) UpgradeAll(opts *Opts) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *APK) ListInstalled(opts *Opts) (map[string]string, error) {
|
func (a *APK) ListInstalled(opts *Opts) (map[string]string, error) {
|
||||||
opts = ensureOpts(opts)
|
|
||||||
out := map[string]string{}
|
out := map[string]string{}
|
||||||
|
cmd := exec.Command("apk", "list", "-I")
|
||||||
var cmd *exec.Cmd
|
|
||||||
if opts.AsRoot {
|
|
||||||
cmd = exec.Command(getRootCmd(a.rootCmd), "apk", "list", "-I")
|
|
||||||
} else {
|
|
||||||
cmd = exec.Command("apk", "list", "-I")
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -104,15 +104,8 @@ func (a *APT) UpgradeAll(opts *Opts) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *APT) ListInstalled(opts *Opts) (map[string]string, error) {
|
func (a *APT) ListInstalled(opts *Opts) (map[string]string, error) {
|
||||||
opts = ensureOpts(opts)
|
|
||||||
out := map[string]string{}
|
out := map[string]string{}
|
||||||
|
cmd := exec.Command("dpkg-query", "-f", "${Package}\u200b${Version}\\n", "-W")
|
||||||
var cmd *exec.Cmd
|
|
||||||
if opts.AsRoot {
|
|
||||||
cmd = exec.Command(getRootCmd(a.rootCmd), "dpkg-query", "-f", "${Package}\u200b${Version}\\n", "-W")
|
|
||||||
} else {
|
|
||||||
cmd = exec.Command("dpkg-query", "-f", "${Package}\u200b${Version}\\n", "-W")
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -111,15 +111,8 @@ func (d *DNF) UpgradeAll(opts *Opts) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) {
|
func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) {
|
||||||
opts = ensureOpts(opts)
|
|
||||||
out := map[string]string{}
|
out := map[string]string{}
|
||||||
|
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
|
||||||
var cmd *exec.Cmd
|
|
||||||
if opts.AsRoot {
|
|
||||||
cmd = exec.Command(getRootCmd(d.rootCmd), "rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
|
|
||||||
} else {
|
|
||||||
cmd = exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -111,15 +111,8 @@ func (p *Pacman) UpgradeAll(opts *Opts) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pacman) ListInstalled(opts *Opts) (map[string]string, error) {
|
func (p *Pacman) ListInstalled(opts *Opts) (map[string]string, error) {
|
||||||
opts = ensureOpts(opts)
|
|
||||||
out := map[string]string{}
|
out := map[string]string{}
|
||||||
|
cmd := exec.Command("pacman", "-Q")
|
||||||
var cmd *exec.Cmd
|
|
||||||
if opts.AsRoot {
|
|
||||||
cmd = exec.Command(getRootCmd(p.rootCmd), "pacman", "-Q")
|
|
||||||
} else {
|
|
||||||
cmd = exec.Command("pacman", "-Q")
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -111,15 +111,8 @@ func (y *YUM) UpgradeAll(opts *Opts) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (y *YUM) ListInstalled(opts *Opts) (map[string]string, error) {
|
func (y *YUM) ListInstalled(opts *Opts) (map[string]string, error) {
|
||||||
opts = ensureOpts(opts)
|
|
||||||
out := map[string]string{}
|
out := map[string]string{}
|
||||||
|
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
|
||||||
var cmd *exec.Cmd
|
|
||||||
if opts.AsRoot {
|
|
||||||
cmd = exec.Command(getRootCmd(y.rootCmd), "rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
|
|
||||||
} else {
|
|
||||||
cmd = exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -111,15 +111,8 @@ func (z *Zypper) UpgradeAll(opts *Opts) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (z *Zypper) ListInstalled(opts *Opts) (map[string]string, error) {
|
func (z *Zypper) ListInstalled(opts *Opts) (map[string]string, error) {
|
||||||
opts = ensureOpts(opts)
|
|
||||||
out := map[string]string{}
|
out := map[string]string{}
|
||||||
|
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
|
||||||
var cmd *exec.Cmd
|
|
||||||
if opts.AsRoot {
|
|
||||||
cmd = exec.Command(getRootCmd(z.rootCmd), "rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
|
|
||||||
} else {
|
|
||||||
cmd = exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
2
repo.go
2
repo.go
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
143
translations/lure.en.toml
Normal file
143
translations/lure.en.toml
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
[[translation]]
|
||||||
|
id = 1228660974
|
||||||
|
value = 'Pulling repository'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2779805870
|
||||||
|
value = 'Repository up to date'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1433222829
|
||||||
|
value = 'Would you like to view the build script for'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2470847050
|
||||||
|
value = 'Failed to prompt user to view build script'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 855659503
|
||||||
|
value = 'Would you still like to continue?'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1997041569
|
||||||
|
value = 'User chose not to continue after reading script'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2347700990
|
||||||
|
value = 'Building package'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2105058868
|
||||||
|
value = 'Downloading sources'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1884485082
|
||||||
|
value = 'Downloading source'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1519177982
|
||||||
|
value = 'Error building package'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2125220917
|
||||||
|
value = 'Choose which package(s) to install'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 812531604
|
||||||
|
value = 'Error prompting for choice of package'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1040982801
|
||||||
|
value = 'Updating version'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1014897988
|
||||||
|
value = 'Remove build dependencies?'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2205430948
|
||||||
|
value = 'Installing build dependencies'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2522710805
|
||||||
|
value = 'Installing dependencies'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 3602138206
|
||||||
|
value = 'Error installing package'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2235794125
|
||||||
|
value = 'Would you like to remove build dependencies?'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2562049386
|
||||||
|
value = "Your system's CPU architecture doesn't match this package. Do you want to build anyway?"
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 4006393493
|
||||||
|
value = 'The checksums array must be the same length as sources'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 3759891273
|
||||||
|
value = 'The package() function is required'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1057080231
|
||||||
|
value = 'Executing package()'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2687735200
|
||||||
|
value = 'Executing prepare()'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 535572372
|
||||||
|
value = 'Executing version()'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 436644691
|
||||||
|
value = 'Executing build()'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1393316459
|
||||||
|
value = 'This package is already installed'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1267660189
|
||||||
|
value = 'Source can be updated, updating if required'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 21753247
|
||||||
|
value = 'Source found in cache, linked to destination'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 257354570
|
||||||
|
value = 'Compressing package'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2952487371
|
||||||
|
value = 'Building package metadata'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1579384326
|
||||||
|
value = 'name'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 3206337475
|
||||||
|
value = 'version'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1810056261
|
||||||
|
value = 'new'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1602912115
|
||||||
|
value = 'source'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2363381545
|
||||||
|
value = 'type'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 3419504365
|
||||||
|
value = 'downloader'
|
||||||
139
translations/lure.ru.toml
Normal file
139
translations/lure.ru.toml
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
[[translation]]
|
||||||
|
id = 1228660974
|
||||||
|
value = 'Скачивание репозитория'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2779805870
|
||||||
|
value = 'Репозиторий уже обновлен'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1433222829
|
||||||
|
value = 'Показать скрипт для пакета'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2470847050
|
||||||
|
value = 'Не удалось предложить просмотреть скрипт'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 855659503
|
||||||
|
value = 'Продолжить?'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1997041569
|
||||||
|
value = 'Пользователь решил не продолжать после просмотра скрипта'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2347700990
|
||||||
|
value = 'Сборка пакета'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2105058868
|
||||||
|
value = 'Скачивание файлов'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1884485082
|
||||||
|
value = 'Скачивание источника'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1519177982
|
||||||
|
value = 'Ошибка при сборке пакета'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2125220917
|
||||||
|
value = 'Выберите, какие пакеты установить'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 812531604
|
||||||
|
value = 'Ошибка при запросе выбора пакета'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1040982801
|
||||||
|
value = 'Обновление версии'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2235794125
|
||||||
|
value = 'Удалить зависимости сборки?'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2205430948
|
||||||
|
value = 'Установка зависимостей сборки'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2522710805
|
||||||
|
value = 'Установка зависимостей'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 3602138206
|
||||||
|
value = 'Ошибка при установке пакета'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1057080231
|
||||||
|
value = 'Вызов функции package()'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2687735200
|
||||||
|
value = 'Вызов функции prepare()'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 535572372
|
||||||
|
value = 'Вызов функции version()'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 436644691
|
||||||
|
value = 'Вызов функции build()'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2562049386
|
||||||
|
value = "Архитектура процессора вашей системы не соответствует этому пакету. Продолжать несмотря на это?"
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 3759891273
|
||||||
|
value = 'Функция package() необходима'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 4006393493
|
||||||
|
value = 'Массив checksums должен быть той же длины, что и sources'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1393316459
|
||||||
|
value = 'Этот пакет уже установлен'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1267660189
|
||||||
|
value = 'Источник может быть обновлен, если требуется, обновляем'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 21753247
|
||||||
|
value = 'Источник найден в кэше'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 257354570
|
||||||
|
value = 'Сжатие пакета'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2952487371
|
||||||
|
value = 'Создание метаданных пакета'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1579384326
|
||||||
|
value = 'название'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 3206337475
|
||||||
|
value = 'версия'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1810056261
|
||||||
|
value = 'новая'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 1602912115
|
||||||
|
value = 'источник'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 2363381545
|
||||||
|
value = 'вид'
|
||||||
|
|
||||||
|
[[translation]]
|
||||||
|
id = 3419504365
|
||||||
|
value = 'протокол-скачивание'
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -55,7 +55,7 @@ func upgradeCmd(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(updates) > 0 {
|
if len(updates) > 0 {
|
||||||
installPkgs(c.Context, updates, nil, mgr)
|
installPkgs(c.Context, updates, nil, mgr, c.Bool("clean"))
|
||||||
} else {
|
} else {
|
||||||
log.Info("There is nothing to do.").Send()
|
log.Info("There is nothing to do.").Send()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* LURE - Linux User REpository
|
* LURE - Linux User REpository
|
||||||
* Copyright (C) 2022 Arsen Musayelyan
|
* Copyright (C) 2023 Arsen Musayelyan
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
Reference in New Issue
Block a user