Compare commits

...

75 Commits

Author SHA1 Message Date
5999c1c8e6
Disable gpg checks for zypper in install script 2023-12-25 02:27:11 +00:00
5dc31f43aa Add build depends to search package 2023-12-22 19:35:36 -08:00
5569841ee2 Add FUNDING.yml 2023-12-15 16:11:39 -08:00
2b1b1deace Execute functions in a fakeroot environment
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 20:09:07 -07:00
edad7b03b2 Move shell handlers to internal/shutils/handlers
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 19:47:07 -07:00
ef923016e4 Add SWH badge
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 18:16:10 +00:00
fd145e4751 Use the pypi API to fetch information about pip modules
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-11 20:45:11 -07:00
4218912123 Fix issue where the git downloader re-downloads the source after updating it
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-11 14:16:36 -07:00
c98741b8f2 Clarify IMPORTANT note in README
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 15:50:30 -07:00
26654ae26e Remove extra newlines in pip template
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 14:04:55 -07:00
484c8ba055 Use the pypi project page as the default homepage for pip generator
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 14:04:01 -07:00
6effa2d8fe Use the lowercase name for the pip generator
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:51:40 -07:00
f421f40fdf Check for HTTP error in gen.Pip()
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:43:50 -07:00
602a558ab1 Add gen command
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:38:39 -07:00
564f3c9b05 Make capitalization in helper command consistent with the rest of the program
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:01:19 -07:00
09ff697b71 Check if helper command exists before anything else
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 13:00:31 -07:00
3b382b9747 Add the ability to list helper commands
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-10 12:59:43 -07:00
5d49de6fde Pass required variables to helper commands invoked by lure helper
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 23:15:38 -07:00
1133785dbd Add helper command
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 23:06:48 -07:00
f32dddee63 Look for provides matches before direct matches when finding packages
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 20:05:06 -07:00
ba1a39874e Improve check for valid version when pulling repo
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 19:48:57 -07:00
00406493c2 Remove any self-provides from alpine packages
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 19:25:24 -07:00
d9659dab9c Fix packages with 'all' architecture
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 18:39:59 -07:00
046db8bf1c Add blake2s and blake2b to the list of valid hash algos
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-09 18:27:40 -07:00
94bdf8275a Fix search package
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 18:16:11 -07:00
3f3b575b63 Update import path
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 17:34:39 -07:00
7a9cea9702 Remove debug print
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 15:39:48 -07:00
0c6cdadd82 Fix content-disposition parsing
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 15:07:44 -07:00
e20ed6b5eb Set woodpecker platform to amd64
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 14:51:43 -07:00
ae99f4a136 Fix dlcache basepath
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 14:47:21 -07:00
dd216e8707 Fix differences between nfpms and archives in goreleaser file
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 14:42:34 -07:00
1fdb399ba3 Remove deprecated replacements in goreleaser file
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 14:32:09 -07:00
7598122780 Update lure-web domain to lure.sh
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 12:12:00 -07:00
04c7ad4476 Update install script
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-07 02:32:40 +00:00
f521dc7136 Update URLs
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 19:16:36 -07:00
c6c8828257 Update lure-repo URL
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 19:03:43 -07:00
dbdaaa4184 Fix potential fd/http leak
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:53:13 -07:00
e3a838f312 Only set local file name if name isn't already set
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:51:37 -07:00
383b886472 Implement local file sources
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:40:17 -07:00
b758eb39f0 Fix Content-Disposition parsing when the filename isn't quoted
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:19:07 -07:00
bb1227eadb Change ldflags to use internal/config
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:14:42 -07:00
410e005458 Add ldflags to goreleaser config
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:13:21 -07:00
6b236f6240 Make translations thread safe
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 15:07:53 -07:00
eb8dd3ad35 Make config thread safe 2023-10-06 15:07:19 -07:00
88bd90ef89 Make DB connection thread safe 2023-10-06 15:04:14 -07:00
cf932e6691 Use the alt screen for internal/pager instead of clearing the screen
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 14:51:48 -07:00
177960431c Add some comments to pkg/loggerctx
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 14:23:29 -07:00
d86776feb1 Pass logger around in a context
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-06 14:21:12 -07:00
1b8c05b257 Add FilterValue to search options 2023-09-22 16:32:08 -07:00
02a6104fb0 Remove lure-api-server 2023-09-22 15:39:53 -07:00
be1d9be7a8 Add pkg/log 2023-09-22 15:25:40 -07:00
ac45087ead Move db and config back to internal 2023-09-22 15:21:34 -07:00
4774ec3343 Add pkg/search 2023-09-22 15:17:12 -07:00
be7709a5ed Move some things out of internal
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 16:18:18 -07:00
76ba7fcc68 Update README
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 15:13:50 -07:00
02ff473241 Update install command
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 15:01:59 -07:00
6ed31f252c Add some comments to internal/build
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-21 13:27:23 -07:00
5b87990206 Disable the logger by default
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 20:31:01 -07:00
fe832c97b2 Update docs
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 20:22:02 -07:00
227c9fdda6 Add docs for opt_deps
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 16:32:49 -07:00
8dbdd3edc4 Add opt_deps to packages
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 16:30:00 -07:00
f637dd06a7 Only allow users to choose a single package in the interactive prompt 2023-09-20 15:52:25 -07:00
f66132559d Run formatter
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 15:41:03 -07:00
c2b875db6c Update and add GPL headers
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 15:38:22 -07:00
6388180768 Use internal log package to avoid breaking programs that have their own global loggers
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-20 15:33:26 -07:00
cf8d08574f Remove unnecessary comment
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 19:37:20 -07:00
0f3718648a Update dependencies
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 15:40:20 -07:00
81f9a4bf95 Move nfpm format imports to internal/build
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 15:11:53 -07:00
45522e3f3a Major refactor
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-19 14:28:05 -07:00
d59c4036ef Add osutils package
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-13 17:29:49 -07:00
ffc79b8ca3 Remove debug print 2023-08-13 17:12:28 -07:00
dada9d68f2 Add -p flag to build command
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-13 15:27:54 -07:00
10893c07c3 Account for backwards compatibility of ARM
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-11 15:22:01 -07:00
e7e742d98d Add LURE_ARCH variable 2023-08-11 14:45:54 -07:00
f2d4d5250a Fix issue where grep expression can't find latest LURE version
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-08 16:36:59 +00:00
92 changed files with 3273 additions and 5253 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
liberapay: lure

View File

@ -1,12 +1,13 @@
before:
hooks:
- go mod tidy
- go generate
builds:
- id: lure
env:
- CGO_ENABLED=0
binary: lure
ldflags:
- -X go.elara.ws/lure/internal/config.Version={{.Version}}
goos:
- linux
goarch:
@ -16,22 +17,29 @@ builds:
- arm
- riscv64
archives:
- replacements:
386: i386
amd64: x86_64
arm64: aarch64
- name_template: >-
{{- .ProjectName}}-
{{- .Version}}-
{{- .Os}}-
{{- if .Arch | eq "amd64"}}x86_64
{{- else if .Arch | eq "386"}}i386
{{- else if .Arch | eq "arm64"}}aarch64
{{- else }}{{ .Arch }}{{ end -}}
files:
- scripts/completion/*
nfpms:
- id: lure
package_name: linux-user-repository
file_name_template: '{{.PackageName}}-{{.Version}}-{{.Os}}-{{.Arch}}'
file_name_template: >-
{{- .PackageName}}-
{{- .Version}}-
{{- .Os}}-
{{- if .Arch | eq "amd64"}}x86_64
{{- else if .Arch | eq "386"}}i386
{{- else if .Arch | eq "arm64"}}aarch64
{{- else }}{{ .Arch }}{{ end -}}
description: "Linux User REpository"
replacements:
386: i386
amd64: x86_64
arm64: aarch64
homepage: 'https://gitea.elara.ws/Elara6331/lure'
homepage: 'https://lure.sh'
maintainer: 'Elara Musayelyan <elara@elara.ws>'
license: GPLv3
formats:
@ -52,7 +60,7 @@ nfpms:
dst: /usr/share/zsh/site-functions/_lure
aurs:
- name: linux-user-repository-bin
homepage: 'https://gitea.elara.ws/Elara6331/lure'
homepage: 'https://lure.sh'
description: "Linux User REpository"
maintainers:
- 'Elara Musayelyan <elara@elara.ws>'
@ -77,7 +85,7 @@ aurs:
install -Dm755 ./scripts/completion/zsh ${pkgdir}/usr/share/zsh/site-functions/_lure
release:
gitea:
owner: Elara6331
owner: lure
name: lure
gitea_urls:
api: 'https://gitea.elara.ws/api/v1/'

View File

@ -1,3 +1,4 @@
platform: linux/amd64
pipeline:
release:
image: goreleaser/goreleaser

View File

@ -1,7 +1,8 @@
PREFIX ?= /usr/local
GIT_VERSION = $(shell git describe --tags )
lure: internal/config/version.txt
CGO_ENABLED=0 go build
lure:
CGO_ENABLED=0 go build -ldflags="-X 'go.elara.ws/lure/internal/config.Version=$(GIT_VERSION)'"
clean:
rm -f lure
@ -15,8 +16,5 @@ installmisc:
uninstall:
rm -f /usr/local/bin/lure
internal/config/version.txt:
go generate ./internal/config
.PHONY: install clean uninstall
.PHONY: install clean uninstall installmisc lure

View File

@ -3,7 +3,8 @@
# LURE (Linux User REpository)
[![Go Report Card](https://goreportcard.com/badge/go.elara.ws/lure)](https://goreportcard.com/report/go.elara.ws/lure)
[![status-badge](https://ci.elara.ws/api/badges/Elara6331/lure/status.svg)](https://ci.elara.ws/Elara6331/lure)
[![status-badge](https://ci.elara.ws/api/badges/lure/lure/status.svg)](https://ci.elara.ws/lure/lure)
[![SWH](https://archive.softwareheritage.org/badge/origin/https://gitea.elara.ws/lure/lure.git/)](https://archive.softwareheritage.org/browse/origin/?origin_url=https://gitea.elara.ws/lure/lure.git)
[![linux-user-repository-bin AUR package](https://img.shields.io/aur/version/linux-user-repository-bin?label=linux-user-repository-bin&logo=archlinux)](https://aur.archlinux.org/packages/linux-user-repository-bin/)
LURE is a distro-agnostic build system for Linux, similar to the [AUR](https://wiki.archlinux.org/title/Arch_User_Repository). It is currently in **beta**. Most major bugs have been fixed, and most major features have been added. LURE is ready for general use, but may still break or change occasionally.
@ -19,14 +20,14 @@ LURE is written in pure Go and has zero dependencies after building. The only th
The LURE install script will automatically download and install the appropriate LURE package on your system. To use it, simply run the following command:
```bash
curl https://www.elara.ws/lure.sh | bash
curl -fsSL lure.sh/install | bash
```
**IMPORTANT**: This method is not recommended as it executes any code that is stored at that URL. In order to make sure nothing malicious is going to occur, download the script and inspect it before running.
**IMPORTANT**: This will download and run the script from https://lure.sh/install. Please look through any script you download from the internet (including this one) before running it.
### Packages
Distro packages and binary archives are provided at the latest Gitea release: https://gitea.elara.ws/Elara6331/lure/releases/latest
Distro packages and binary archives are provided at the latest Gitea release: https://gitea.elara.ws/lure/lure/releases/latest
LURE is also available on the AUR as [linux-user-repository-bin](https://aur.archlinux.org/packages/linux-user-repository-bin)
@ -42,7 +43,7 @@ sudo make install
## Why?
Arch Linux's AUR is a very useful feature. It's one of the main reasons I and many others use Arch on most of their devices. However, Arch is not always a good choice. Whether you're running a server that needs to be stable over long periods of time, or you're a beginner and feel intimidated by Arch, there are many different reasons not to use it. Such useful functionality should not be restricted to only a single distro. That is what LURE is meant to solve.
LURE was created because packaging software for multiple Linux distros can be difficult and error-prone, and installing those packages can be a nightmare for users unless they're available in their distro's official repositories. It automates the process of building and installing unofficial packages.
---
@ -54,29 +55,23 @@ The documentation for LURE is in the [docs](docs) directory in this repo.
## Web Interface
LURE now has a web interface! It's open source, licensed under the AGPLv3 (https://gitea.elara.ws/Elara6331/lure-web), and is available at https://lure.elara.ws.
LURE has an open source web interface, licensed under the AGPLv3 (https://gitea.elara.ws/lure/lure-web), and it's available at https://lure.sh/.
---
## Repositories
Unlike the AUR, LURE supports third-party repositories. Also unlike the AUR, LURE's repos are single git repositories containing all the build scripts. Inside each LURE repo, there is a separate directory for each package, containing a `lure.sh` script, which is a PKGBUILD-like build script for LURE. The default repository is hosted on Github: https://github.com/Elara6331/lure-repo, and information about its packages is displayed at https://lure.elara.ws/pkgs.
LURE's repos are git repositories that contain a directory for each package, with a `lure.sh` file inside. The `lure.sh` file tells LURE how to build the package and information about it. `lure.sh` scripts are similar to the AUR's PKGBUILD scripts.
---
## Dependencies
## Acknowledgements
As mentioned before, LURE has zero dependencies after compilation. Thanks to the following projects for making this possible:
Thanks to the following projects for making LURE possible:
- https://github.com/mvdan/sh
- https://github.com/go-git/go-git
- https://github.com/mholt/archiver
- https://github.com/goreleaser/nfpm
- https://github.com/charmbracelet/bubbletea
- https://gitlab.com/cznic/sqlite
---
## Planned Features
- Automated docker-based testing tool
- https://gitlab.com/cznic/sqlite

772
build.go
View File

@ -1,6 +1,6 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
* Copyright (C) 2023 Elara Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -19,730 +19,82 @@
package main
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
_ "github.com/goreleaser/nfpm/v2/apk"
_ "github.com/goreleaser/nfpm/v2/arch"
_ "github.com/goreleaser/nfpm/v2/deb"
_ "github.com/goreleaser/nfpm/v2/rpm"
"github.com/urfave/cli/v2"
"golang.org/x/exp/slices"
"github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/files"
"go.elara.ws/logger/log"
"go.elara.ws/lure/distro"
"go.elara.ws/lure/internal/cliutils"
"go.elara.ws/lure/internal/config"
"go.elara.ws/lure/internal/cpu"
"go.elara.ws/lure/internal/db"
"go.elara.ws/lure/internal/dl"
"go.elara.ws/lure/internal/repos"
"go.elara.ws/lure/internal/shutils"
"go.elara.ws/lure/internal/shutils/decoder"
"go.elara.ws/lure/manager"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"lure.sh/lure/internal/config"
"lure.sh/lure/internal/osutils"
"lure.sh/lure/internal/types"
"lure.sh/lure/pkg/build"
"lure.sh/lure/pkg/loggerctx"
"lure.sh/lure/pkg/manager"
"lure.sh/lure/pkg/repos"
)
// BuildVars represents the script variables required
// to build a package
type BuildVars struct {
Name string `sh:"name,required"`
Version string `sh:"version,required"`
Release int `sh:"release,required"`
Epoch uint `sh:"epoch"`
Description string `sh:"desc"`
Homepage string `sh:"homepage"`
Maintainer string `sh:"maintainer"`
Architectures []string `sh:"architectures"`
Licenses []string `sh:"license"`
Provides []string `sh:"provides"`
Conflicts []string `sh:"conflicts"`
Depends []string `sh:"deps"`
BuildDepends []string `sh:"build_deps"`
Replaces []string `sh:"replaces"`
Sources []string `sh:"sources"`
Checksums []string `sh:"checksums"`
Backup []string `sh:"backup"`
Scripts Scripts `sh:"scripts"`
}
type Scripts struct {
PreInstall string `sh:"preinstall"`
PostInstall string `sh:"postinstall"`
PreRemove string `sh:"preremove"`
PostRemove string `sh:"postremove"`
PreUpgrade string `sh:"preupgrade"`
PostUpgrade string `sh:"postupgrade"`
PreTrans string `sh:"pretrans"`
PostTrans string `sh:"posttrans"`
}
func buildCmd(c *cli.Context) error {
script := c.String("script")
err := repos.Pull(c.Context, gdb, cfg.Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect supported package manager on system").Send()
}
pkgPaths, _, err := buildPackage(c.Context, script, mgr, c.Bool("clean"), c.Bool("interactive"))
if err != nil {
log.Fatal("Error building package").Err(err).Send()
}
wd, err := os.Getwd()
if err != nil {
log.Fatal("Error getting working directory").Err(err).Send()
}
for _, pkgPath := range pkgPaths {
name := filepath.Base(pkgPath)
err = os.Rename(pkgPath, filepath.Join(wd, name))
if err != nil {
log.Fatal("Error moving the package").Err(err).Send()
}
}
return nil
}
// buildPackage builds the script at the given path. It returns two slices. One contains the paths
// to the built package(s), the other contains the names of the built package(s).
func buildPackage(ctx context.Context, script string, mgr manager.Manager, clean, interactive bool) ([]string, []string, error) {
info, err := distro.ParseOSRelease(ctx)
if err != nil {
return nil, nil, err
}
var distroChanged bool
if distID, ok := os.LookupEnv("LURE_DISTRO"); ok {
info.ID = distID
// Since the distro was overwritten, we don't know what the
// like distros are, so set to nil
info.Like = nil
distroChanged = true
}
fl, err := os.Open(script)
if err != nil {
return nil, nil, err
}
file, err := syntax.NewParser().Parse(fl, "lure.sh")
if err != nil {
return nil, nil, err
}
fl.Close()
scriptDir := filepath.Dir(script)
env := genBuildEnv(info, scriptDir)
// The first pass is just used to get variable values and runs before
// the script is displayed, so it is restricted so as to prevent malicious
// code from executing.
runner, err := interp.New(
interp.Env(expand.ListEnviron(env...)),
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
interp.ExecHandler(rHelpers.ExecHandler(shutils.NopExec)),
interp.ReadDirHandler(shutils.RestrictedReadDir(scriptDir)),
interp.StatHandler(shutils.RestrictedStat(scriptDir)),
interp.OpenHandler(shutils.RestrictedOpen(scriptDir)),
)
if err != nil {
return nil, nil, err
}
err = runner.Run(ctx, file)
if err != nil {
return nil, nil, err
}
dec := decoder.New(info, runner)
// If distro was changed, the list of like distros
// no longer applies, so disable its use
if distroChanged {
dec.LikeDistros = false
}
var vars BuildVars
err = dec.DecodeVars(&vars)
if err != nil {
return nil, nil, err
}
baseDir := filepath.Join(config.PkgsDir, vars.Name)
srcdir := filepath.Join(baseDir, "src")
pkgdir := filepath.Join(baseDir, "pkg")
if !clean {
builtPkgPath, ok, err := checkForBuiltPackage(mgr, &vars, getPkgFormat(mgr), baseDir)
if err != nil {
return nil, nil, err
}
if ok {
return []string{builtPkgPath}, nil, err
}
}
err = cliutils.PromptViewScript(script, vars.Name, cfg.PagerStyle, interactive, translator)
if err != nil {
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
}
if !archMatches(vars.Architectures) {
buildAnyway, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", interactive, true, translator)
if err != nil {
return nil, nil, err
}
if !buildAnyway {
os.Exit(1)
}
}
log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send()
// The second pass will be used to execute the actual functions,
// so it cannot be restricted. The script has already been displayed
// to the user by this point, so it should be safe
runner, err = interp.New(
interp.Env(expand.ListEnviron(env...)),
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
interp.ExecHandler(helpers.ExecHandler(nil)),
)
if err != nil {
return nil, nil, err
}
err = runner.Run(ctx, file)
if err != nil {
return nil, nil, err
}
dec = decoder.New(info, runner)
// If distro was changed, the list of like distros
// no longer applies, so disable its use
if distroChanged {
dec.LikeDistros = false
}
err = os.RemoveAll(baseDir)
if err != nil {
return nil, nil, err
}
err = os.MkdirAll(srcdir, 0o755)
if err != nil {
return nil, nil, err
}
err = os.MkdirAll(pkgdir, 0o755)
if err != nil {
return nil, nil, err
}
installed, err := mgr.ListInstalled(nil)
if err != nil {
return nil, nil, err
}
if instVer, ok := installed[vars.Name]; ok {
log.Warn("This package is already installed").
Str("name", vars.Name).
Str("version", instVer).
Send()
}
var buildDeps []string
if len(vars.BuildDepends) > 0 {
found, notFound, err := repos.FindPkgs(gdb, vars.BuildDepends)
if err != nil {
return nil, nil, err
}
found = filterBuildDeps(found, installed)
log.Info("Installing build dependencies").Send()
flattened := cliutils.FlattenPkgs(found, "install", interactive, translator)
buildDeps = packageNames(flattened)
installPkgs(ctx, flattened, notFound, mgr, clean, interactive)
}
var builtDeps, builtNames, repoDeps []string
if len(vars.Depends) > 0 {
log.Info("Installing dependencies").Send()
found, notFound, err := repos.FindPkgs(gdb, vars.Depends)
if err != nil {
return nil, nil, err
}
scripts := getScriptPaths(cliutils.FlattenPkgs(found, "install", interactive, translator))
for _, script := range scripts {
pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr, clean, interactive)
if err != nil {
return nil, nil, err
}
builtDeps = append(builtDeps, pkgPaths...)
builtNames = append(builtNames, pkgNames...)
builtNames = append(builtNames, filepath.Base(filepath.Dir(script)))
}
repoDeps = notFound
}
log.Info("Downloading sources").Send()
err = getSources(ctx, srcdir, &vars)
if err != nil {
return nil, nil, err
}
err = setDirVars(ctx, runner, srcdir, pkgdir)
if err != nil {
return nil, nil, err
}
fn, ok := dec.GetFunc("version")
if ok {
log.Info("Executing version()").Send()
buf := &bytes.Buffer{}
err = fn(
ctx,
interp.Dir(srcdir),
interp.StdIO(os.Stdin, buf, os.Stderr),
)
if err != nil {
return nil, nil, err
}
newVer := strings.TrimSpace(buf.String())
err = setVersion(ctx, runner, newVer)
if err != nil {
return nil, nil, err
}
vars.Version = newVer
log.Info("Updating version").Str("new", newVer).Send()
}
fn, ok = dec.GetFunc("prepare")
if ok {
log.Info("Executing prepare()").Send()
err = fn(ctx, interp.Dir(srcdir))
if err != nil {
return nil, nil, err
}
}
fn, ok = dec.GetFunc("build")
if ok {
log.Info("Executing build()").Send()
err = fn(ctx, interp.Dir(srcdir))
if err != nil {
return nil, nil, err
}
}
fn, ok = dec.GetFunc("package")
if ok {
log.Info("Executing package()").Send()
err = fn(ctx, interp.Dir(srcdir))
if err != nil {
return nil, nil, err
}
} else {
log.Fatal("The package() function is required").Send()
}
log.Info("Building package metadata").Str("name", vars.Name).Send()
uniq(
&repoDeps,
&builtDeps,
&builtNames,
)
pkgInfo := &nfpm.Info{
Name: vars.Name,
Description: vars.Description,
Arch: cpu.Arch(),
Platform: "linux",
Version: vars.Version,
Release: strconv.Itoa(vars.Release),
Homepage: vars.Homepage,
License: strings.Join(vars.Licenses, ", "),
Maintainer: vars.Maintainer,
Overridables: nfpm.Overridables{
Conflicts: vars.Conflicts,
Replaces: vars.Replaces,
Provides: vars.Provides,
Depends: append(repoDeps, builtNames...),
var buildCmd = &cli.Command{
Name: "build",
Usage: "Build a local package",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "script",
Aliases: []string{"s"},
Value: "lure.sh",
Usage: "Path to the build script",
},
}
&cli.StringFlag{
Name: "package",
Aliases: []string{"p"},
Usage: "Name of the package to build and its repo (example: default/go-bin)",
},
&cli.BoolFlag{
Name: "clean",
Aliases: []string{"c"},
Usage: "Build package from scratch even if there's an already built package available",
},
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
if vars.Epoch != 0 {
pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10)
}
script := c.String("script")
if c.String("package") != "" {
script = filepath.Join(config.GetPaths(ctx).RepoDir, c.String("package"), "lure.sh")
}
setScripts(&vars, pkgInfo, filepath.Dir(script))
err := repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
if slices.Contains(vars.Architectures, "all") {
pkgInfo.Arch = "all"
}
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect a supported package manager on the system").Send()
}
contents := []*files.Content{}
filepath.Walk(pkgdir, func(path string, fi os.FileInfo, err error) error {
trimmed := strings.TrimPrefix(path, pkgdir)
pkgPaths, _, err := build.BuildPackage(ctx, types.BuildOpts{
Script: script,
Manager: mgr,
Clean: c.Bool("clean"),
Interactive: c.Bool("interactive"),
})
if err != nil {
log.Fatal("Error building package").Err(err).Send()
}
if fi.IsDir() {
f, err := os.Open(path)
wd, err := os.Getwd()
if err != nil {
log.Fatal("Error getting working directory").Err(err).Send()
}
for _, pkgPath := range pkgPaths {
name := filepath.Base(pkgPath)
err = osutils.Move(pkgPath, filepath.Join(wd, name))
if err != nil {
return err
log.Fatal("Error moving the package").Err(err).Send()
}
_, err = f.Readdirnames(1)
if err != io.EOF {
return nil
}
contents = append(contents, &files.Content{
Source: path,
Destination: trimmed,
Type: "dir",
FileInfo: &files.ContentFileInfo{
MTime: fi.ModTime(),
},
})
f.Close()
return nil
}
if fi.Mode()&os.ModeSymlink != 0 {
link, err := os.Readlink(path)
if err != nil {
return err
}
link = strings.TrimPrefix(link, pkgdir)
contents = append(contents, &files.Content{
Source: link,
Destination: trimmed,
Type: "symlink",
FileInfo: &files.ContentFileInfo{
MTime: fi.ModTime(),
Mode: fi.Mode(),
},
})
return nil
}
fileContent := &files.Content{
Source: path,
Destination: trimmed,
FileInfo: &files.ContentFileInfo{
MTime: fi.ModTime(),
Mode: fi.Mode(),
Size: fi.Size(),
},
}
if slices.Contains(vars.Backup, trimmed) {
fileContent.Type = "config|noreplace"
}
contents = append(contents, fileContent)
return nil
})
pkgInfo.Overridables.Contents = contents
packager, err := nfpm.Get(getPkgFormat(mgr))
if err != nil {
return nil, nil, err
}
pkgName := packager.ConventionalFileName(pkgInfo)
pkgPath := filepath.Join(baseDir, pkgName)
pkgPaths := append(builtDeps, pkgPath)
pkgNames := append(builtNames, vars.Name)
pkgFile, err := os.Create(pkgPath)
if err != nil {
return nil, nil, err
}
log.Info("Compressing package").Str("name", pkgName).Send()
err = packager.Package(pkgInfo, pkgFile)
if err != nil {
return nil, nil, err
}
if len(buildDeps) > 0 {
removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove build dependencies?", interactive, false, translator)
if err != nil {
return nil, nil, err
}
if removeBuildDeps {
err = mgr.Remove(
&manager.Opts{
AsRoot: true,
NoConfirm: true,
},
buildDeps...,
)
if err != nil {
return nil, nil, err
}
}
}
uniq(&pkgPaths, &pkgNames)
return pkgPaths, pkgNames, nil
}
func checkForBuiltPackage(mgr manager.Manager, vars *BuildVars, pkgFormat, baseDir string) (string, bool, error) {
filename, err := pkgFileName(vars, pkgFormat)
if err != nil {
return "", false, err
}
pkgPath := filepath.Join(baseDir, filename)
_, err = os.Stat(pkgPath)
if err != nil {
return "", false, nil
}
return pkgPath, true, nil
}
func pkgFileName(vars *BuildVars, pkgFormat string) (string, error) {
pkgInfo := &nfpm.Info{
Name: vars.Name,
Arch: cpu.Arch(),
Version: vars.Version,
Release: strconv.Itoa(vars.Release),
Epoch: strconv.FormatUint(uint64(vars.Epoch), 10),
}
packager, err := nfpm.Get(pkgFormat)
if err != nil {
return "", err
}
return packager.ConventionalFileName(pkgInfo), nil
}
func getPkgFormat(mgr manager.Manager) string {
pkgFormat := mgr.Format()
if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
pkgFormat = format
}
return pkgFormat
}
func genBuildEnv(info *distro.OSRelease, scriptdir string) []string {
env := os.Environ()
env = append(
env,
"DISTRO_NAME="+info.Name,
"DISTRO_PRETTY_NAME="+info.PrettyName,
"DISTRO_ID="+info.ID,
"DISTRO_VERSION_ID="+info.VersionID,
"DISTRO_ID_LIKE="+strings.Join(info.Like, " "),
"ARCH="+cpu.Arch(),
"NCPU="+strconv.Itoa(runtime.NumCPU()),
"scriptdir="+scriptdir,
)
return env
}
func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
if len(bv.Sources) != len(bv.Checksums) {
log.Fatal("The checksums array must be the same length as sources").Send()
}
for i, src := range bv.Sources {
opts := dl.Options{
Name: fmt.Sprintf("%s[%d]", bv.Name, i),
URL: src,
Destination: srcdir,
Progress: os.Stderr,
}
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
algo, hashData, ok := strings.Cut(bv.Checksums[i], ":")
if ok {
checksum, err := hex.DecodeString(hashData)
if err != nil {
return err
}
opts.Hash = checksum
opts.HashAlgorithm = algo
} else {
checksum, err := hex.DecodeString(bv.Checksums[i])
if err != nil {
return err
}
opts.Hash = checksum
}
}
err := dl.Download(ctx, opts)
if err != nil {
return err
}
}
return nil
}
// setDirVars sets srcdir and pkgdir. It's a very hacky way of doing so,
// but setting the runner's Env and Vars fields doesn't seem to work.
func setDirVars(ctx context.Context, runner *interp.Runner, srcdir, pkgdir string) error {
cmd := "srcdir='" + srcdir + "'\npkgdir='" + pkgdir + "'\n"
fl, err := syntax.NewParser().Parse(strings.NewReader(cmd), "vars")
if err != nil {
return err
}
return runner.Run(ctx, fl)
}
func setScripts(vars *BuildVars, info *nfpm.Info, scriptDir string) {
if vars.Scripts.PreInstall != "" {
info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall)
}
if vars.Scripts.PostInstall != "" {
info.Scripts.PostInstall = filepath.Join(scriptDir, vars.Scripts.PostInstall)
}
if vars.Scripts.PreRemove != "" {
info.Scripts.PreRemove = filepath.Join(scriptDir, vars.Scripts.PreRemove)
}
if vars.Scripts.PostRemove != "" {
info.Scripts.PostRemove = filepath.Join(scriptDir, vars.Scripts.PostRemove)
}
if vars.Scripts.PreUpgrade != "" {
info.ArchLinux.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
info.APK.Scripts.PreUpgrade = filepath.Join(scriptDir, vars.Scripts.PreUpgrade)
}
if vars.Scripts.PostUpgrade != "" {
info.ArchLinux.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
info.APK.Scripts.PostUpgrade = filepath.Join(scriptDir, vars.Scripts.PostUpgrade)
}
if vars.Scripts.PreTrans != "" {
info.RPM.Scripts.PreTrans = filepath.Join(scriptDir, vars.Scripts.PreTrans)
}
if vars.Scripts.PostTrans != "" {
info.RPM.Scripts.PostTrans = filepath.Join(scriptDir, vars.Scripts.PostTrans)
}
}
// archMatches checks if your system architecture matches
// one of the provided architectures
func archMatches(architectures []string) bool {
if slices.Contains(architectures, "all") {
return true
}
if slices.Contains(architectures, "arm") {
architectures = append(architectures, cpu.ARMVariant())
}
return slices.Contains(architectures, cpu.Arch())
}
func setVersion(ctx context.Context, r *interp.Runner, to string) error {
fl, err := syntax.NewParser().Parse(strings.NewReader("version='"+to+"'"), "")
if err != nil {
return err
}
return r.Run(ctx, fl)
}
func filterBuildDeps(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
out := map[string][]db.Package{}
for name, pkgs := range found {
var inner []db.Package
for _, pkg := range pkgs {
if _, ok := installed[pkg.Name]; !ok {
addToFiltered := true
for _, provides := range pkg.Provides.Val {
if _, ok := installed[provides]; ok {
addToFiltered = false
break
}
}
if addToFiltered {
inner = append(inner, pkg)
}
}
}
if len(inner) > 0 {
out[name] = inner
}
}
return out
}
func packageNames(pkgs []db.Package) []string {
names := make([]string, len(pkgs))
for i, p := range pkgs {
names[i] = p.Name
}
return names
}
// uniq removes all duplicates from string slices
func uniq(ss ...*[]string) {
for _, s := range ss {
slices.Sort(*s)
*s = slices.Compact(*s)
}
},
}

View File

@ -1,3 +0,0 @@
FROM alpine:latest
COPY lure-api-server /usr/bin/lure-api-server
ENTRYPOINT lure-api-server

View File

@ -1,3 +0,0 @@
# lure-api-server
`lure-api-server` is the backend API server for lure-web, the web interface for LURE.

View File

@ -1,205 +0,0 @@
/*
* LURE - Linux User REpository
* Copyright (C) 2023 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main