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
amd64: x86_64
arm64: aarch64
files:
- scripts/completion/*
nfpms:
- id: lure
package_name: linux-user-repository
@@ -38,27 +40,27 @@ nfpms:
- rpm
- archlinux
provides:
- lure
- linux-user-repository
conflicts:
- lure
- linux-user-repository
contents:
- src: scripts/completion/bash
dst: /usr/share/bash-completion/completions/lure
- src: scripts/completion/zsh
dst: /usr/share/zsh/site-functions/_lure
aurs:
- name: lure-bin
- name: linux-user-repository-bin
homepage: 'https://gitea.arsenm.dev/Arsen6331/lure'
description: "Linux User REpository"
maintainers:
- 'Arsen Musayelyan <arsen@arsenm.dev>'
license: GPLv3
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:
- lure
- linux-user-repository
conflicts:
- lure
- linux-user-repository
depends:
- sudo
- pacman
@@ -67,8 +69,8 @@ aurs:
install -Dm755 ./lure "${pkgdir}/usr/bin/lure"
# completions
install -Dm755 ./scripts/completion/bash /usr/share/bash-completion/completions/lure
install -Dm755 ./scripts/completion/zsh /usr/share/zsh/site-functions/_lure
install -Dm755 ./scripts/completion/bash ${pkgdir}/usr/share/bash-completion/completions/lure
install -Dm755 ./scripts/completion/zsh ${pkgdir}/usr/share/zsh/site-functions/_lure
release:
gitea:
owner: Arsen6331

View File

@@ -1,6 +1,6 @@
PREFIX ?= /usr/local
lure: version.txt
lure: internal/config/version.txt
go build
clean:
@@ -16,7 +16,7 @@ installmisc:
uninstall:
rm -f /usr/local/bin/lure
version.txt:
go generate ./...
internal/config/version.txt:
go generate ./internal/config
.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)
[![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.

159
build.go
View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@ import (
"bytes"
"context"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
@@ -40,10 +41,11 @@ import (
"github.com/goreleaser/nfpm/v2/files"
"go.arsenm.dev/logger/log"
"go.arsenm.dev/lure/distro"
"go.arsenm.dev/lure/download"
"go.arsenm.dev/lure/internal/cliutils"
"go.arsenm.dev/lure/internal/config"
"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/shutils"
"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()
}
pkgPaths, _, err := buildPackage(c.Context, script, mgr)
pkgPaths, _, err := buildPackage(c.Context, script, mgr, c.Bool("clean"))
if err != nil {
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
// 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)
if err != nil {
return nil, nil, err
@@ -187,13 +189,28 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
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 {
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
}
if !archMatches(vars.Architectures) {
buildAnyway, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", 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 {
return nil, nil, err
}
@@ -230,10 +247,6 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
dec.LikeDistros = false
}
baseDir := filepath.Join(config.PkgsDir, vars.Name)
srcdir := filepath.Join(baseDir, "src")
pkgdir := filepath.Join(baseDir, "pkg")
err = os.RemoveAll(baseDir)
if err != nil {
return nil, nil, err
@@ -254,21 +267,27 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
return nil, nil, err
}
var buildDeps []string
for _, pkgName := range vars.BuildDepends {
if _, ok := installed[pkgName]; !ok {
buildDeps = append(buildDeps, pkgName)
}
if instVer, ok := installed[vars.Name]; ok {
log.Warn("This package is already installed").
Str("name", vars.Name).
Str("version", instVer).
Send()
}
if len(buildDeps) > 0 {
found, notFound, err := repos.FindPkgs(gdb, buildDeps)
var buildDeps []string
if len(vars.BuildDepends) > 0 {
found, notFound, err := repos.FindPkgs(gdb, vars.BuildDepends)
if err != nil {
return nil, nil, err
}
found = filterBuildDeps(found, installed)
log.Info("Installing build dependencies").Send()
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
@@ -280,9 +299,9 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
return nil, nil, err
}
scripts := getScriptPaths(cliutils.FlattenPkgs(found, "install"))
scripts := getScriptPaths(cliutils.FlattenPkgs(found, "install", translator))
for _, script := range scripts {
pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr)
pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr, clean)
if err != nil {
return nil, nil, err
}
@@ -362,6 +381,8 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
log.Fatal("The package() function is required").Send()
}
log.Info("Building package metadata").Str("name", vars.Name).Send()
uniq(
&repoDeps,
&builtDeps,
@@ -464,12 +485,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
pkgInfo.Overridables.Contents = contents
pkgFormat := mgr.Format()
if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
pkgFormat = format
}
packager, err := nfpm.Get(pkgFormat)
packager, err := nfpm.Get(getPkgFormat(mgr))
if err != nil {
return nil, nil, err
}
@@ -485,13 +501,15 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
return nil, nil, err
}
log.Info("Compressing package").Str("name", pkgName).Send()
err = packager.Package(pkgInfo, pkgFile)
if err != nil {
return nil, nil, err
}
if len(buildDeps) > 0 {
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove build dependencies?", false)
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove build dependencies?", false, translator)
if err != nil {
return nil, nil, err
}
@@ -515,6 +533,47 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
return pkgPaths, pkgNames, nil
}
func checkForBuiltPackage(mgr manager.Manager, vars *BuildVars, pkgFormat, baseDir string) (string, bool, error) {
filename, err := pkgFileName(vars, pkgFormat)
if err != nil {
return "", false, err
}
pkgPath := filepath.Join(baseDir, filename)
_, err = os.Stat(pkgPath)
if err != nil {
return "", false, nil
}
return pkgPath, true, nil
}
func pkgFileName(vars *BuildVars, pkgFormat string) (string, error) {
pkgInfo := &nfpm.Info{
Name: vars.Name,
Arch: cpu.Arch(),
Version: vars.Version,
Release: strconv.Itoa(vars.Release),
Epoch: strconv.FormatUint(uint64(vars.Epoch), 10),
}
packager, err := nfpm.Get(pkgFormat)
if err != nil {
return "", err
}
return packager.ConventionalFileName(pkgInfo), nil
}
func getPkgFormat(mgr manager.Manager) string {
pkgFormat := mgr.Format()
if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
pkgFormat = format
}
return pkgFormat
}
func genBuildEnv(info *distro.OSRelease, scriptdir string) []string {
env := os.Environ()
@@ -541,10 +600,11 @@ func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
}
for i, src := range bv.Sources {
opts := download.GetOptions{
SourceURL: src,
opts := dl.Options{
Name: fmt.Sprintf("%s[%d]", bv.Name, i),
URL: src,
Destination: srcdir,
EncloseGit: true,
Progress: os.Stderr,
}
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
@@ -552,10 +612,10 @@ func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
if err != nil {
return err
}
opts.SHA256Sum = checksum
opts.SHA256 = checksum
}
err := download.Get(ctx, opts)
err := dl.Download(ctx, opts)
if err != nil {
return err
}
@@ -633,6 +693,41 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error {
return r.Run(ctx, fl)
}
func filterBuildDeps(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
out := map[string][]db.Package{}
for name, pkgs := range found {
var inner []db.Package
for _, pkg := range pkgs {
if _, ok := installed[pkg.Name]; !ok {
addToFiltered := true
for _, provides := range pkg.Provides.Val {
if _, ok := installed[provides]; ok {
addToFiltered = false
break
}
}
if addToFiltered {
inner = append(inner, pkg)
}
}
}
if len(inner) > 0 {
out[name] = inner
}
}
return out
}
func packageNames(pkgs []db.Package) []string {
names := make([]string, len(pkgs))
for i, p := range pkgs {
names[i] = p.Name
}
return names
}
// uniq removes all duplicates from string slices
func uniq(ss ...*[]string) {
for _, s := range ss {

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
import (
@@ -9,9 +27,11 @@ import (
"github.com/jmoiron/sqlx"
"github.com/twitchtv/twirp"
"go.arsenm.dev/logger/log"
"go.arsenm.dev/lure/internal/api"
"go.arsenm.dev/lure/internal/config"
"go.arsenm.dev/lure/internal/db"
"golang.org/x/text/language"
)
type lureWebAPI struct {
@@ -59,7 +79,7 @@ func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.Se
if err != nil {
return nil, err
}
out.Packages = append(out.Packages, dbPkgToAPI(pkg))
out.Packages = append(out.Packages, dbPkgToAPI(ctx, pkg))
}
return out, err
@@ -70,7 +90,7 @@ func (l lureWebAPI) GetPkg(ctx context.Context, req *api.GetPackageRequest) (*ap
if err != nil {
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) {
@@ -94,16 +114,16 @@ func (l lureWebAPI) GetBuildScript(ctx context.Context, req *api.GetBuildScriptR
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{
Name: pkg.Name,
Repository: pkg.Repository,
Version: pkg.Version,
Release: int64(pkg.Release),
Epoch: ptr(int64(pkg.Epoch)),
Description: &pkg.Description,
Homepage: &pkg.Homepage,
Maintainer: &pkg.Maintainer,
Description: performTranslation(ctx, pkg.Description.Val),
Homepage: performTranslation(ctx, pkg.Homepage.Val),
Maintainer: performTranslation(ctx, pkg.Maintainer.Val),
Architectures: pkg.Architectures.Val,
Licenses: pkg.Licenses.Val,
Provides: pkg.Provides.Val,
@@ -125,3 +145,61 @@ func dbMapToAPI(m map[string][]string) map[string]*api.StringList {
}
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
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
import (
"os"
"github.com/jmoiron/sqlx"
"go.arsenm.dev/logger/log"
"go.arsenm.dev/lure/internal/config"
@@ -12,21 +28,9 @@ import (
var gdb *sqlx.DB
func init() {
fi, err := os.Stat(config.DBPath)
if err == nil {
// 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)
var err error
gdb, err = db.Open(config.DBPath)
if err != nil {
log.Fatal("Error opening database").Err(err).Send()
}
err = db.Init(gdb)
if err != nil {
log.Fatal("Error initializing database").Err(err).Send()
}
}

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
import (
@@ -49,6 +67,7 @@ func main() {
lureWebAPI{db: gdb},
twirp.WithServerPathPrefix(""),
)
handler = withAcceptLanguage(handler)
handler = allowAllCORSHandler(handler)
handler = handleWebhook(handler, sigCh)
@@ -75,3 +94,24 @@ func allowAllCORSHandler(h http.Handler) http.Handler {
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
import (
@@ -51,7 +69,7 @@ func verifySecure(req *http.Request) error {
secretStr, ok := os.LookupEnv("LURE_API_GITHUB_SECRET")
if !ok {
return errors.New("LURE_API_GITHUB_SECRET must be set to the secret used for setting up the github webhook")
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)

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
import (
"os"
"github.com/jmoiron/sqlx"
"go.arsenm.dev/logger/log"
"go.arsenm.dev/lure/internal/config"
@@ -12,21 +28,9 @@ import (
var gdb *sqlx.DB
func init() {
fi, err := os.Stat(config.DBPath)
if err == nil {
// 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)
var err error
gdb, err = db.Open(config.DBPath)
if err != nil {
log.Fatal("Error opening database").Err(err).Send()
}
err = db.Init(gdb)
if err != nil {
log.Fatal("Error initializing database").Err(err).Send()
}
}

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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:
- `~tag`: Specify which tag 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.
- `~rev`: Specify which revision of the repo to check out.
- `~depth`: Specify what depth should be used when cloning the repo. Must be an integer.
- `~name`: Specify the name of the directory into which the git repo should be cloned.
- `~recursive`: If set to true, submodules will be cloned recursively. It is false by default.
Examples:
```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
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

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.
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:
```shell
lure in itd-bin # only finds itd-bin
lure in itd # finds itd-bin and itd-git
lure in it% # finds itd-bin, itd-git, and itgui-git
lure in -c itd-bin
```
### 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.
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:
```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
import (
"os"
"github.com/jmoiron/sqlx"
"github.com/urfave/cli/v2"
"go.arsenm.dev/logger/log"
"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()
}
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
config.DBPresent = false
err = db.Init(gdb)
gdb, err = db.Open(config.DBPath)
if err != nil {
log.Fatal("Error initializing database").Err(err).Send()
}
config.DBPresent = false
err = repos.Pull(c.Context, gdb, cfg.Repos)
if err != nil {

22
go.mod
View File

@@ -4,6 +4,7 @@ go 1.18
require (
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/charmbracelet/bubbles v0.14.0
github.com/charmbracelet/bubbletea v0.23.1
@@ -15,12 +16,16 @@ require (
github.com/mholt/archiver/v4 v4.0.0-alpha.7
github.com/mitchellh/mapstructure v1.5.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/urfave/cli/v2 v2.16.3
go.arsenm.dev/logger v0.0.0-20221220032833-ba8a3cfb4668
github.com/urfave/cli/v2 v2.23.7
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/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
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.20.0
@@ -62,10 +67,11 @@ require (
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // 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-runewidth v0.0.14 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
@@ -76,12 +82,13 @@ require (
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/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/net v0.0.0-20220812174116-3211cb980234 // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/term v0.4.0 // indirect
golang.org/x/tools v0.1.12 // indirect
gopkg.in/warnings.v0 v0.1.2 // 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-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko=
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/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
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/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
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/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
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-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 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.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/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
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/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/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/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
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/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
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/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
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/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
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.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8=
github.com/schollz/progressbar/v3 v3.13.0/go.mod h1:ZBYnSuLAX2LU8P8UiKN/KgF2DY58AJC8yfVYLPC8Ly4=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
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.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.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.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/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
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.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk=
github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
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.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
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=
gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
go.arsenm.dev/logger v0.0.0-20221220032833-ba8a3cfb4668 h1:7dSmQ79slzFpcii8zgQbEStxpkTPvq3tzWc7KX5uwGc=
go.arsenm.dev/logger v0.0.0-20221220032833-ba8a3cfb4668/go.mod h1:RV2qydKDdoyaRkhAq8JEGvojR8eJ6bjq5WnSIlH7gYw=
go.arsenm.dev/logger v0.0.0-20230126004036-a8cbbe3b6fe6 h1:p5EkGdoRX4ddMJcYQzRPTHk8oVyMv0q8agaAbPr/oak=
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-20210322153248-0c34fe9e7dc2/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-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.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
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-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.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.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
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.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
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
import (

42
info.go
View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@ import (
"github.com/urfave/cli/v2"
"go.arsenm.dev/lure/distro"
"go.arsenm.dev/lure/internal/cliutils"
"go.arsenm.dev/lure/internal/config"
"go.arsenm.dev/lure/internal/overrides"
"go.arsenm.dev/lure/internal/repos"
"gopkg.in/yaml.v3"
@@ -52,7 +53,7 @@ func infoCmd(c *cli.Context) error {
os.Exit(1)
}
pkgs := cliutils.FlattenPkgs(found, "show")
pkgs := cliutils.FlattenPkgs(found, "show", translator)
var names []string
all := c.Bool("all")
@@ -62,38 +63,29 @@ func infoCmd(c *cli.Context) error {
if err != nil {
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 {
if !all {
depsSet := false
buildDepsSet := false
for _, name := range names {
if deps, ok := pkg.Depends.Val[name]; ok && !depsSet {
pkg.Depends.Val = map[string][]string{name: deps}
depsSet = true
err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
if err != nil {
log.Fatal("Error encoding script variables").Err(err).Send()
}
if buildDeps, ok := pkg.BuildDepends.Val[name]; ok && !buildDepsSet {
pkg.BuildDepends.Val = map[string][]string{name: buildDeps}
buildDepsSet = true
}
}
if !depsSet {
pkg.Depends.Val = nil
}
if !buildDepsSet {
pkg.BuildDepends.Val = nil
}
}
} else {
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
if err != nil {
log.Fatal("Error encoding script variables").Err(err).Send()
}
}
fmt.Println("---")
}

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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()
}
installPkgs(c.Context, cliutils.FlattenPkgs(found, "install"), notFound, mgr)
installPkgs(c.Context, cliutils.FlattenPkgs(found, "install", translator), notFound, mgr, c.Bool("clean"))
return nil
}
// installPkgs installs non-LURE packages via the package manager, then builds and installs LURE
// packages
func installPkgs(ctx context.Context, pkgs []db.Package, notFound []string, mgr manager.Manager) {
func installPkgs(ctx context.Context, pkgs []db.Package, notFound []string, mgr manager.Manager, clean bool) {
if len(notFound) > 0 {
err := mgr.Install(nil, notFound...)
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
@@ -82,9 +82,9 @@ func getScriptPaths(pkgs []db.Package) []string {
}
// 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 {
builtPkgs, _, err := buildPackage(ctx, script, mgr)
builtPkgs, _, err := buildPackage(ctx, script, mgr, clean)
if err != nil {
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
//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
import (
@@ -5,16 +23,18 @@ import (
"github.com/AlecAivazis/survey/v2"
"go.arsenm.dev/logger/log"
"go.arsenm.dev/lure/internal/config"
"go.arsenm.dev/lure/internal/db"
"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
func YesNoPrompt(msg string, def bool) (bool, error) {
func YesNoPrompt(msg string, def bool, t translate.Translator) (bool, error) {
var answer bool
err := survey.AskOne(
&survey.Confirm{
Message: msg,
Message: t.TranslateTo(msg, config.Language),
Default: def,
},
&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,
// shows it if they answer yes, then asks if they'd still like to
// continue, and exits if they answer no.
func PromptViewScript(script, name, style string) error {
view, err := YesNoPrompt("Would you like to view the build script for "+name, false)
func PromptViewScript(script, name, style string, t translate.Translator) error {
view, err := YesNoPrompt(t.TranslateTo("Would you like to view the build script for", config.Language)+" "+name, false, t)
if err != nil {
return err
}
@@ -37,13 +57,13 @@ func PromptViewScript(script, name, style string) error {
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 {
return err
}
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
// 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
for _, pkgs := range found {
if len(pkgs) > 1 {
choices, err := PkgPrompt(pkgs, verb)
choices, err := PkgPrompt(pkgs, verb, t)
if err != nil {
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.
// 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))
for i, option := range options {
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{
Options: names,
Message: "Choose which package(s) to " + verb,
Message: t.TranslateTo("Choose which package(s) to "+verb, config.Language),
}
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
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
import (
@@ -71,6 +89,6 @@ func init() {
DBPath = filepath.Join(CacheDir, "db")
_, err = os.ReadDir(DBPath)
DBPresent = err == nil
fi, err := os.Stat(DBPath)
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
import _ "embed"

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
import (
@@ -6,12 +24,17 @@ import (
"encoding/json"
"errors"
"fmt"
"os"
"github.com/jmoiron/sqlx"
"go.arsenm.dev/logger/log"
"go.arsenm.dev/lure/internal/config"
"golang.org/x/exp/slices"
"modernc.org/sqlite"
)
const CurrentVersion = 1
func init() {
sqlite.MustRegisterScalarFunction("json_array_contains", 2, JsonArrayContains)
}
@@ -22,9 +45,9 @@ type Package struct {
Version string `sh:"version,required" db:"version"`
Release int `sh:"release,required" db:"release"`
Epoch uint `sh:"epoch" db:"epoch"`
Description string `sh:"desc" db:"description"`
Homepage string `sh:"homepage" db:"homepage"`
Maintainer string `sh:"maintainer" db:"maintainer"`
Description JSON[map[string]string] `db:"description"`
Homepage JSON[map[string]string] `db:"homepage"`
Maintainer JSON[map[string]string] `db:"maintainer"`
Architectures JSON[[]string] `sh:"architectures" db:"architectures"`
Licenses JSON[[]string] `sh:"license" db:"licenses"`
Provides JSON[[]string] `sh:"provides" db:"provides"`
@@ -35,8 +58,42 @@ type Package struct {
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
func Init(db *sqlx.DB) error {
func Init(db *sqlx.DB, dsn string) error {
*db = *db.Unsafe()
_, err := db.Exec(`
CREATE TABLE IF NOT EXISTS pkgs (
@@ -45,9 +102,9 @@ func Init(db *sqlx.DB) error {
version TEXT NOT NULL,
release INT NOT NULL,
epoch INT,
description TEXT,
homepage TEXT,
maintainer TEXT,
description TEXT CHECK(description = 'null' OR (JSON_VALID(description) AND JSON_TYPE(description) = 'object')),
homepage TEXT CHECK(homepage = 'null' OR (JSON_VALID(homepage) AND JSON_TYPE(homepage) = 'object')),
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')),
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')),
@@ -57,7 +114,52 @@ func Init(db *sqlx.DB) error {
builddepends TEXT CHECK(builddepends = 'null' OR (JSON_VALID(builddepends) AND JSON_TYPE(builddepends) = 'object')),
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
}

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
import (
@@ -14,9 +32,17 @@ var testPkg = db.Package{
Version: "0.0.1",
Release: 1,
Epoch: 2,
Description: "Test package",
Homepage: "https://lure.arsenm.dev",
Maintainer: "Arsen Musayelyan <arsen@arsenm.dev>",
Description: db.NewJSON(map[string]string{
"en": "Test package",
"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"}),
Licenses: db.NewJSON([]string{"GPL-3.0-or-later"}),
Provides: db.NewJSON([]string{"test"}),
@@ -32,18 +58,6 @@ var testPkg = db.Package{
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) {
gdb, err := sqlx.Open("sqlite", ":memory:")
if err != nil {
@@ -51,7 +65,7 @@ func TestInit(t *testing.T) {
}
defer gdb.Close()
err = db.Init(gdb)
err = db.Init(gdb, ":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -60,10 +74,17 @@ func TestInit(t *testing.T) {
if err != nil {
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) {
gdb, err := getDB(t)
gdb, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -86,7 +107,7 @@ func TestInsertPackage(t *testing.T) {
}
func TestGetPkgs(t *testing.T) {
gdb, err := getDB(t)
gdb, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -126,7 +147,7 @@ func TestGetPkgs(t *testing.T) {
}
func TestGetPkg(t *testing.T) {
gdb, err := getDB(t)
gdb, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -162,7 +183,7 @@ func TestGetPkg(t *testing.T) {
}
func TestDeletePkgs(t *testing.T) {
gdb, err := getDB(t)
gdb, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -200,7 +221,7 @@ func TestDeletePkgs(t *testing.T) {
}
func TestJsonArrayContains(t *testing.T) {
gdb, err := getDB(t)
gdb, err := db.Open(":memory:")
if err != nil {
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
import (
"reflect"
"runtime"
"strings"
"go.arsenm.dev/lure/distro"
"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 {
Name string
Overrides bool
LikeDistros bool
Languages []string
LanguageTags []language.Tag
}
var DefaultOpts = &Opts{
Overrides: true,
LikeDistros: true,
Languages: []string{"en"},
}
// 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 {
opts = DefaultOpts
}
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}
@@ -71,7 +101,22 @@ func Resolve(info *distro.OSRelease, opts *Opts) []string {
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 {
@@ -97,3 +142,94 @@ func (o *Opts) WithLikeDistros(v bool) *Opts {
out.LikeDistros = v
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
import (
@@ -6,6 +24,7 @@ import (
"go.arsenm.dev/lure/distro"
"go.arsenm.dev/lure/internal/overrides"
"golang.org/x/text/language"
)
var info = &distro.OSRelease{
@@ -14,9 +33,19 @@ var info = &distro.OSRelease{
}
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{
"amd64_centos_en",
"centos_en",
"amd64_rhel_en",
"rhel_en",
"amd64_fedora_en",
"fedora_en",
"amd64_en",
"amd64_centos",
"centos",
"amd64_rhel",
@@ -33,11 +62,14 @@ func TestResolve(t *testing.T) {
}
func TestResolveName(t *testing.T) {
names := overrides.Resolve(info, &overrides.Opts{
names, err := overrides.Resolve(info, &overrides.Opts{
Name: "deps",
Overrides: true,
LikeDistros: true,
})
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
expected := []string{
"deps_amd64_centos",
@@ -56,10 +88,13 @@ func TestResolveName(t *testing.T) {
}
func TestResolveNoLikeDistros(t *testing.T) {
names := overrides.Resolve(info, &overrides.Opts{
names, err := overrides.Resolve(info, &overrides.Opts{
Overrides: true,
LikeDistros: false,
})
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
expected := []string{
"amd64_centos",
@@ -74,11 +109,14 @@ func TestResolveNoLikeDistros(t *testing.T) {
}
func TestResolveNoOverrides(t *testing.T) {
names := overrides.Resolve(info, &overrides.Opts{
names, err := overrides.Resolve(info, &overrides.Opts{
Name: "deps",
Overrides: false,
LikeDistros: false,
})
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
expected := []string{"deps"}
@@ -86,3 +124,31 @@ func TestResolveNoOverrides(t *testing.T) {
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
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
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
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
import (
@@ -6,24 +24,18 @@ import (
"strings"
"testing"
"github.com/jmoiron/sqlx"
"go.arsenm.dev/lure/internal/db"
"go.arsenm.dev/lure/internal/repos"
"go.arsenm.dev/lure/internal/types"
)
func TestFindPkgs(t *testing.T) {
gdb, err := sqlx.Open("sqlite", ":memory:")
gdb, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer gdb.Close()
err = db.Init(gdb)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
setCfgDirs(t)
defer removeCacheDir(t)
@@ -69,17 +81,12 @@ func TestFindPkgs(t *testing.T) {
}
func TestFindPkgsEmpty(t *testing.T) {
gdb, err := sqlx.Open("sqlite", ":memory:")
gdb, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer gdb.Close()
err = db.Init(gdb)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
setCfgDirs(t)
defer removeCacheDir(t)
@@ -88,7 +95,10 @@ func TestFindPkgsEmpty(t *testing.T) {
Repository: "default",
Version: "0.0.1",
Release: 1,
Description: "Test package 1",
Description: db.NewJSON(map[string]string{
"en": "Test package 1",
"ru": "Проверочный пакет 1",
}),
Provides: db.NewJSON([]string{""}),
})
if err != nil {
@@ -100,7 +110,10 @@ func TestFindPkgsEmpty(t *testing.T) {
Repository: "default",
Version: "0.0.1",
Release: 1,
Description: "Test package 2",
Description: db.NewJSON(map[string]string{
"en": "Test package 2",
"ru": "Проверочный пакет 2",
}),
Provides: db.NewJSON([]string{"test"}),
})
if err != 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 repos
import (
@@ -7,6 +25,7 @@ import (
"net/url"
"os"
"path/filepath"
"reflect"
"strings"
"github.com/go-git/go-billy/v5"
@@ -18,7 +37,6 @@ import (
"github.com/pelletier/go-toml/v2"
"go.arsenm.dev/logger/log"
"go.arsenm.dev/lure/distro"
"go.arsenm.dev/lure/download"
"go.arsenm.dev/lure/internal/config"
"go.arsenm.dev/lure/internal/db"
"go.arsenm.dev/lure/internal/shutils"
@@ -103,13 +121,9 @@ func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
return err
}
if !strings.HasPrefix(repoURL.Scheme, "git+") {
repoURL.Scheme = "git+" + repoURL.Scheme
}
err = download.Get(ctx, download.GetOptions{
SourceURL: repoURL.String(),
Destination: repoDir,
_, err = git.PlainCloneContext(ctx, repoDir, false, &git.CloneOptions{
URL: repoURL.String(),
Progress: os.Stderr,
})
if err != nil {
return err
@@ -271,6 +285,9 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
}
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{}),
BuildDepends: db.NewJSON(map[string][]string{}),
Repository: repo.Name,
@@ -338,6 +355,9 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *
}
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{}),
BuildDepends: db.NewJSON(map[string][]string{}),
Repository: repo.Name,
@@ -378,20 +398,34 @@ func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runn
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) {
pkgVal := reflect.ValueOf(pkg).Elem()
for name, val := range runner.Vars {
if strings.HasPrefix(name, "deps") {
override := strings.TrimPrefix(name, "deps")
for prefix, field := range overridable {
if strings.HasPrefix(name, prefix) {
override := strings.TrimPrefix(name, prefix)
override = strings.TrimPrefix(override, "_")
pkg.Depends.Val[override] = val.List
} else if strings.HasPrefix(name, "build_deps") {
override := strings.TrimPrefix(name, "build_deps")
override = strings.TrimPrefix(override, "_")
field := pkgVal.FieldByName(field)
varVal := field.FieldByName("Val")
varType := varVal.Type()
pkg.BuildDepends.Val[override] = val.List
} else {
continue
switch varType.Elem().String() {
case "[]string":
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
import (
@@ -6,7 +24,6 @@ import (
"path/filepath"
"testing"
"github.com/jmoiron/sqlx"
"go.arsenm.dev/lure/internal/config"
"go.arsenm.dev/lure/internal/db"
"go.arsenm.dev/lure/internal/repos"
@@ -48,17 +65,12 @@ func removeCacheDir(t *testing.T) {
}
func TestPull(t *testing.T) {
gdb, err := sqlx.Open("sqlite", ":memory:")
gdb, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer gdb.Close()
err = db.Init(gdb)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
setCfgDirs(t)
defer removeCacheDir(t)

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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 {
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 {
fn, ok := d.runner.Funcs[fnName]
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
// override variables and nameref variables.
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 {
val, ok := d.runner.Vars[varName]
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
import (

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
import (

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
}
func (NopRWC) Write([]byte) (int, error) {
return 0, io.EOF
func (NopRWC) Write(b []byte) (int, error) {
return len(b), nil
}
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
import (

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
// 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
// RepoConfig represents a LURE repo's lure-repo.toml file.

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
* Copyright (C) 2022 Arsen Musayelyan
* 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
@@ -20,6 +20,7 @@ package main
import (
"context"
"embed"
"fmt"
"os"
"os/signal"
@@ -33,12 +34,26 @@ import (
"go.arsenm.dev/lure/internal/config"
"go.arsenm.dev/lure/internal/db"
"go.arsenm.dev/lure/manager"
"go.arsenm.dev/translate"
)
//go:generate scripts/gen-version.sh
//go:embed translations
var translationFS embed.FS
var translator translate.Translator
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() {
@@ -65,6 +80,13 @@ func main() {
},
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",
Usage: "Install a new package",
Aliases: []string{"in"},
@@ -78,6 +100,13 @@ func main() {
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",
Usage: "Upgrade all installed packages",
Aliases: []string{"up"},
@@ -115,6 +144,11 @@ func main() {
Value: "lure.sh",
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",
Usage: "Build a local package",

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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) {
opts = ensureOpts(opts)
out := map[string]string{}
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(a.rootCmd), "apk", "list", "-I")
} else {
cmd = exec.Command("apk", "list", "-I")
}
cmd := exec.Command("apk", "list", "-I")
stdout, err := cmd.StdoutPipe()
if err != nil {

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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) {
opts = ensureOpts(opts)
out := map[string]string{}
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")
}
cmd := exec.Command("dpkg-query", "-f", "${Package}\u200b${Version}\\n", "-W")
stdout, err := cmd.StdoutPipe()
if err != nil {

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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) {
opts = ensureOpts(opts)
out := map[string]string{}
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")
}
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
stdout, err := cmd.StdoutPipe()
if err != nil {

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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) {
opts = ensureOpts(opts)
out := map[string]string{}
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(p.rootCmd), "pacman", "-Q")
} else {
cmd = exec.Command("pacman", "-Q")
}
cmd := exec.Command("pacman", "-Q")
stdout, err := cmd.StdoutPipe()
if err != nil {

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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) {
opts = ensureOpts(opts)
out := map[string]string{}
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")
}
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
stdout, err := cmd.StdoutPipe()
if err != nil {

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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) {
opts = ensureOpts(opts)
out := map[string]string{}
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")
}
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
stdout, err := cmd.StdoutPipe()
if err != nil {

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
* Copyright (C) 2022 Arsen Musayelyan
* 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
@@ -55,7 +55,7 @@ func upgradeCmd(c *cli.Context) error {
}
if len(updates) > 0 {
installPkgs(c.Context, updates, nil, mgr)
installPkgs(c.Context, updates, nil, mgr, c.Bool("clean"))
} else {
log.Info("There is nothing to do.").Send()
}

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by