44 Commits

Author SHA1 Message Date
4d97210635 Fix AUR repo in GoReleaser config
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-02-01 12:10:19 -08:00
51f05ecc81 Remove replaces field from GoReleaser config
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-02-01 12:03:13 -08:00
27082ba223 Fix nFPM provides/conflicts values
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-02-01 12:00:37 -08:00
6c6a7152b4 Fix panic when performing incremental database updates
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-02-01 11:55:22 -08:00
681e31df4e Fix other AUR package parameters
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-31 14:17:35 -08:00
a84a9be782 Fix AUR package name
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-31 14:16:01 -08:00
825e89d0d3 Upgrade status to beta
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-31 14:08:57 -08:00
eb88fbd123 Add more logs after executing package()
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-31 14:05:45 -08:00
3c5613199e Fix AUR badge link
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-31 07:16:02 +00:00
73bdb54496 Add missing GPL headers and change year to 2023
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-30 23:12:29 -08:00
4b5fd855cc Check ~archive parameter for file downloader
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 18:07:37 -08:00
2c8eb6be48 Check ~name parameter for file downloader
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 17:56:11 -08:00
d4396756d6 Update docs for new download system
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 17:54:05 -08:00
c8be92c47b Check ~name parameter for git downloader
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 17:36:35 -08:00
ee7f4878d1 Update usage docs with new --clean/-c flag
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 22:50:26 +00:00
bd769468a9 Install already-built package by default if available
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 14:40:37 -08:00
09d9053104 Add comments to Downloader and UpdatingDownloader interfaces
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 14:19:20 -08:00
d914391c40 Fix typo in comment
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 13:43:39 -08:00
0ece536810 Normalize URL for caching
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 13:42:23 -08:00
192e3e8a4f Check provides field when filtering build dependencies
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 01:32:55 -08:00
fa4604c26c Add comments for internal/{dl,dlcache}
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 00:54:59 -08:00
163ad12575 Remove old download package
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-29 00:07:10 -08:00
2ba6136c99 Add translations for new download system
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-28 21:24:53 -08:00
438304f8ce Run go fmt
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-28 21:08:35 -08:00
657b562f31 Handle broken cache manifest
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-28 21:05:20 -08:00
d26b288cde Switch to new download system
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-28 20:53:06 -08:00
e785c6b53d Add unarchiving to file downloader
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-28 20:33:36 -08:00
ff8ed902ea Add git downloader 2023-01-28 20:32:47 -08:00
1d2d46cbc5 Add initial caching download system
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-28 15:17:22 -08:00
374c206fae Update AUR badge
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-27 22:11:09 +00:00
d906dc8d86 Add download caching system
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-25 17:23:43 -08:00
2f81f7c605 Do not use root for listing installed packages
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-25 16:47:22 -08:00
bb05a8d38b Add warning for already-installed packages 2023-01-25 16:43:25 -08:00
076f90bbd7 Add translation system for LURE CLI
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-12 19:41:52 -08:00
e772ecf2ab Fix database presence check
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-11 18:31:57 -08:00
3e0c110893 Warn user if DB does not have version 2023-01-11 18:31:08 -08:00
238f4cf682 Add localization to API server
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-11 18:07:49 -08:00
c41e7e1c18 Add language overrides to database
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-11 15:26:12 -08:00
0144ad17d9 Move database open code into internal/db
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-11 14:14:31 -08:00
ddd9d1d63d Add languages to overrides 2023-01-11 13:37:52 -08:00
0cb23911cf Fix lure target dependencies in Makefile
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-11 13:03:40 -08:00
de8399e40b Only generate for internal/config in version.txt target
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-01 20:40:05 -08:00
d226a6c154 Install to in AUR package 2023-01-01 20:30:53 -08:00
c2e0332552 Add completion files to archive
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-01-01 20:23:23 -08:00
67 changed files with 2501 additions and 621 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -4,9 +4,9 @@
[![Go Report Card](https://goreportcard.com/badge/go.arsenm.dev/lure)](https://goreportcard.com/report/go.arsenm.dev/lure) [![Go Report Card](https://goreportcard.com/badge/go.arsenm.dev/lure)](https://goreportcard.com/report/go.arsenm.dev/lure)
[![status-badge](https://ci.arsenm.dev/api/badges/Arsen6331/lure/status.svg)](https://ci.arsenm.dev/Arsen6331/lure) [![status-badge](https://ci.arsenm.dev/api/badges/Arsen6331/lure/status.svg)](https://ci.arsenm.dev/Arsen6331/lure)
[![lure-bin AUR package](https://img.shields.io/aur/version/lure-bin?label=lure-bin&logo=archlinux)](https://aur.archlinux.org/packages/lure-bin/) [![linux-user-repository-bin AUR package](https://img.shields.io/aur/version/linux-user-repository-bin?label=lure-bin&logo=archlinux)](https://aur.archlinux.org/packages/linux-user-repository-bin/)
LURE is a distro-agnostic build system for Linux, similar to the [AUR](https://wiki.archlinux.org/title/Arch_User_Repository). It is currently in 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
View File

@@ -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 {

View 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 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
}

View 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 main package main
import ( import (

View File

@@ -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()
}
} }

View 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 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)
})
}

View 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 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)

View File

@@ -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
View File

@@ -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()
}
} }

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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=

View 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 main package main
import ( import (

44
info.go
View File

@@ -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("---")
} }

View File

@@ -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()
} }

View 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 api package api
//go:generate protoc --twirp_out=. lure.proto //go:generate protoc --twirp_out=. lure.proto

View 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 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

View 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 config package config
import ( import (

View 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 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
View 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
}

View 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 config package config
import _ "embed" import _ "embed"

View File

@@ -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

View 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 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
} }

View 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 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
View 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
View 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
View 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
}

View 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
}

View 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))
}

View File

@@ -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
}

View 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 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)
}
}

View 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 pager package pager
import ( import (

View 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 pager package pager
import ( import (

View 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 repos package repos
import ( import (

View 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 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)

View 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 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
}
} }
} }
} }

View 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 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)

View File

@@ -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 {

View 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 decoder_test package decoder_test
import ( import (

View File

@@ -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

View 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 shutils_test package shutils_test
import ( import (

View File

@@ -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 {

View 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 shutils_test package shutils_test
import ( import (

View File

@@ -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

View 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
// Config represents the LURE configuration file // Config represents the LURE configuration file

View 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.

View File

@@ -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
View File

@@ -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",

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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
View 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
View 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 = 'протокол-скачивание'

View File

@@ -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()
} }

View File

@@ -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

View File

@@ -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