Compare commits
	
		
			117 Commits
		
	
	
		
			7164aac0b4
			...
			v0.0.7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4d97210635 | |||
| 51f05ecc81 | |||
| 27082ba223 | |||
| 6c6a7152b4 | |||
| 681e31df4e | |||
| a84a9be782 | |||
| 825e89d0d3 | |||
| eb88fbd123 | |||
| 3c5613199e | |||
| 73bdb54496 | |||
| 4b5fd855cc | |||
| 2c8eb6be48 | |||
| d4396756d6 | |||
| c8be92c47b | |||
| ee7f4878d1 | |||
| bd769468a9 | |||
| 09d9053104 | |||
| d914391c40 | |||
| 0ece536810 | |||
| 192e3e8a4f | |||
| fa4604c26c | |||
| 163ad12575 | |||
| 2ba6136c99 | |||
| 438304f8ce | |||
| 657b562f31 | |||
| d26b288cde | |||
| e785c6b53d | |||
| ff8ed902ea | |||
| 1d2d46cbc5 | |||
| 374c206fae | |||
| d906dc8d86 | |||
| 2f81f7c605 | |||
| bb05a8d38b | |||
| 076f90bbd7 | |||
| e772ecf2ab | |||
| 3e0c110893 | |||
| 238f4cf682 | |||
| c41e7e1c18 | |||
| 0144ad17d9 | |||
| ddd9d1d63d | |||
| 0cb23911cf | |||
| de8399e40b | |||
| d226a6c154 | |||
| c2e0332552 | |||
| 43baf8024a | |||
| 8a1d0f4f54 | |||
| 806f49c472 | |||
| 55132437b3 | |||
| 8b26e96af0 | |||
| bd41075e8a | |||
| 8225f41d0e | |||
| db060db4b1 | |||
| 863b6e923a | |||
| 51b41bdd90 | |||
| 76e073c77f | |||
| 770881bf67 | |||
| be79eba4c2 | |||
| 3e6d5f57cf | |||
| db5c344b2d | |||
| a750f4630e | |||
| 852e98088b | |||
| c832359e43 | |||
| fae4337748 | |||
| f21d02e5d1 | |||
| ef98a37b4a | |||
| 3829dc4cc4 | |||
| efca9d116e | |||
| acb71b873e | |||
| 8366a2ce86 | |||
| 37a6c682b5 | |||
| 80187b0969 | |||
| cecaead5b0 | |||
| a84622e182 | |||
| dd86148c22 | |||
| 6aed71af24 | |||
| 422e41db40 | |||
| 8dc0e53700 | |||
| 891df91fee | |||
| b7922f9687 | |||
| 0b53c16f9c | |||
| 81013ce376 | |||
| eb4e2281fa | |||
| c9bc1e7fc3 | |||
| 22a74aa793 | |||
| 964c45ffc4 | |||
| 9c0d9d0a34 | |||
| a8a870ce86 | |||
| fe477ba6d1 | |||
| 6cd0802f64 | |||
| 19ced9795c | |||
| dbfcde0125 | |||
| 14078ec904 | |||
| 715ec7fbe8 | |||
| 3c260a7c5f | |||
| 5e8ed1c2a0 | |||
| c51248793e | |||
| 5ed538c2c4 | |||
| 56ba3ec644 | |||
| 5d411ac538 | |||
| 269222b688 | |||
| 79c0e7eb12 | |||
| fcd5c1c14b | |||
| 9fa3977d3a | |||
| c2d396d68f | |||
| 23fd711be2 | |||
| 8f9bdf69ad | |||
| dd33a30c35 | |||
| a106100312 | |||
| df72d95ab7 | |||
| 40cf0da2c0 | |||
| 66a6b30d87 | |||
| 4eac9cd8aa | |||
| 664f01a794 | |||
| 0ac8ccac81 | |||
| 56550a5135 | |||
| b4f4633f6a | |||
| 67b9801f42 | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,5 @@
 | 
				
			|||||||
/lure
 | 
					/lure
 | 
				
			||||||
 | 
					/lure-api-server
 | 
				
			||||||
 | 
					/cmd/lure-api-server/lure-api-server
 | 
				
			||||||
/dist/
 | 
					/dist/
 | 
				
			||||||
/internal/config/version.txt
 | 
					/internal/config/version.txt
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,16 +10,18 @@ builds:
 | 
				
			|||||||
    goos:
 | 
					    goos:
 | 
				
			||||||
      - linux
 | 
					      - linux
 | 
				
			||||||
    goarch:
 | 
					    goarch:
 | 
				
			||||||
      - 386
 | 
					 | 
				
			||||||
      - amd64
 | 
					      - amd64
 | 
				
			||||||
      - arm
 | 
					      - 386
 | 
				
			||||||
      - arm64
 | 
					      - arm64
 | 
				
			||||||
 | 
					      - arm
 | 
				
			||||||
      - riscv64
 | 
					      - riscv64
 | 
				
			||||||
archives:
 | 
					archives:
 | 
				
			||||||
  - replacements:
 | 
					  - replacements:
 | 
				
			||||||
      386: i386
 | 
					      386: i386
 | 
				
			||||||
      amd64: x86_64
 | 
					      amd64: x86_64
 | 
				
			||||||
      arm64: aarch64
 | 
					      arm64: aarch64
 | 
				
			||||||
 | 
					    files:
 | 
				
			||||||
 | 
					      - scripts/completion/*
 | 
				
			||||||
nfpms:
 | 
					nfpms:
 | 
				
			||||||
  - id: lure
 | 
					  - id: lure
 | 
				
			||||||
    package_name: linux-user-repository
 | 
					    package_name: linux-user-repository
 | 
				
			||||||
@@ -38,28 +40,37 @@ nfpms:
 | 
				
			|||||||
      - rpm
 | 
					      - rpm
 | 
				
			||||||
      - archlinux
 | 
					      - archlinux
 | 
				
			||||||
    provides:
 | 
					    provides:
 | 
				
			||||||
      - lure
 | 
					      - linux-user-repository
 | 
				
			||||||
    conflicts:
 | 
					    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:
 | 
					aurs:
 | 
				
			||||||
  - name: lure-bin
 | 
					  - name: linux-user-repository-bin
 | 
				
			||||||
    homepage: 'https://gitea.arsenm.dev/Arsen6331/lure'
 | 
					    homepage: 'https://gitea.arsenm.dev/Arsen6331/lure'
 | 
				
			||||||
    description: "Linux User REpository"
 | 
					    description: "Linux User REpository"
 | 
				
			||||||
    maintainers:
 | 
					    maintainers:
 | 
				
			||||||
      - 'Arsen Musayelyan <arsen@arsenm.dev>'
 | 
					      - 'Arsen Musayelyan <arsen@arsenm.dev>'
 | 
				
			||||||
    license: GPLv3
 | 
					    license: GPLv3
 | 
				
			||||||
    private_key: '{{ .Env.AUR_KEY }}'
 | 
					    private_key: '{{ .Env.AUR_KEY }}'
 | 
				
			||||||
    git_url: 'ssh://aur@aur.archlinux.org/lure-bin.git'
 | 
					    git_url: 'ssh://aur@aur.archlinux.org/linux-user-repository-bin.git'
 | 
				
			||||||
    provides:
 | 
					    provides:
 | 
				
			||||||
      - lure
 | 
					      - linux-user-repository
 | 
				
			||||||
    conflicts:
 | 
					    conflicts:
 | 
				
			||||||
      - lure
 | 
					      - linux-user-repository
 | 
				
			||||||
    depends:
 | 
					    depends:
 | 
				
			||||||
      - sudo
 | 
					      - sudo
 | 
				
			||||||
      - pacman
 | 
					      - pacman
 | 
				
			||||||
    package: |-
 | 
					    package: |-
 | 
				
			||||||
      # binaries
 | 
					      # binaries
 | 
				
			||||||
      install -Dm755 "./lure" "${pkgdir}/usr/bin/lure"
 | 
					      install -Dm755 ./lure "${pkgdir}/usr/bin/lure"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # completions
 | 
				
			||||||
 | 
					      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:
 | 
					release:
 | 
				
			||||||
  gitea:
 | 
					  gitea:
 | 
				
			||||||
    owner: Arsen6331
 | 
					    owner: Arsen6331
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,16 +1,22 @@
 | 
				
			|||||||
lure: version.txt
 | 
					PREFIX ?= /usr/local
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lure: internal/config/version.txt
 | 
				
			||||||
	go build
 | 
						go build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
clean:
 | 
					clean:
 | 
				
			||||||
	rm -f lure
 | 
						rm -f lure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
install: lure
 | 
					install: lure installmisc
 | 
				
			||||||
	sudo install -Dm755 lure /usr/local/bin/lure
 | 
						install -Dm755 lure $(DESTDIR)$(PREFIX)/bin/lure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					installmisc:
 | 
				
			||||||
 | 
						install -Dm755 scripts/completion/bash $(DESTDIR)$(PREFIX)/share/bash-completion/completions/lure
 | 
				
			||||||
 | 
						install -Dm755 scripts/completion/zsh $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_lure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uninstall:
 | 
					uninstall:
 | 
				
			||||||
	rm -f /usr/local/bin/lure
 | 
						rm -f /usr/local/bin/lure
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
version.txt:
 | 
					internal/config/version.txt:
 | 
				
			||||||
	go generate
 | 
						go generate ./internal/config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: install clean uninstall
 | 
					.PHONY: install clean uninstall
 | 
				
			||||||
							
								
								
									
										39
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								README.md
									
									
									
									
									
								
							@@ -1,12 +1,14 @@
 | 
				
			|||||||
 | 
					<img src="assets/logo.png" alt="LURE Logo" width="200">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# LURE (Linux User REpository)
 | 
					# LURE (Linux User REpository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://goreportcard.com/report/go.arsenm.dev/lure)
 | 
					[](https://goreportcard.com/report/go.arsenm.dev/lure)
 | 
				
			||||||
[](https://ci.arsenm.dev/Arsen6331/lure)
 | 
					[](https://ci.arsenm.dev/Arsen6331/lure)
 | 
				
			||||||
[](https://aur.archlinux.org/packages/lure-bin/)
 | 
					[](https://aur.archlinux.org/packages/linux-user-repository-bin/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LURE is intended to bring the AUR to all distros. It is currently in an ***alpha*** state and may not be stable. It can download a repository, build packages in it using a bash script similar to [PKGBUILD](https://wiki.archlinux.org/title/PKGBUILD), and then install them using your system package manager.
 | 
					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 it's built. The only things LURE needs are a command for privilege elevation such as `sudo`, `doas`, etc. as well as a supported package manager. Currently, LURE supports `apt`, `pacman`, `apk`, `dnf`, `yum`, and `zypper`. If a supported package manager exists on your system, it will be detected and used automatically.
 | 
					LURE is written in pure Go and has zero dependencies after building. The only things LURE requires are a command for privilege elevation such as `sudo`, `doas`, etc. as well as a supported package manager. Currently, LURE supports `apt`, `pacman`, `apk`, `dnf`, `yum`, and `zypper`. If a supported package manager exists on your system, it will be detected and used automatically.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -20,7 +22,7 @@ The LURE install script will automatically download and install the appropriate
 | 
				
			|||||||
curl https://www.arsenm.dev/lure.sh | bash
 | 
					curl https://www.arsenm.dev/lure.sh | bash
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**IMPORTANT**: This method is not recommended as it executes whatever code 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 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Packages
 | 
					### Packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,11 +42,7 @@ sudo make install
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Why?
 | 
					## Why?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The AUR is an amazing feature, and it's one of the main reasons I use Arch on all my daily driver devices. It is really simple while providing really useful functionality. I feel such a solution shouldn't be stuck in only a single distro, so I made LURE.
 | 
					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.
 | 
				
			||||||
 | 
					 | 
				
			||||||
Like the AUR, it uses simple bash build scripts, but it doesn't depend on bash being installed at all. It uses an embedded, pure Go implementation of bash instead. Similarly, it uses Git to download the repos and sources, but doesn't depend on Git being installed.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This means it's really easy to deploy LURE on any distro that it has support for and on any CPU architecture. It also supports and automatically detects many package managers, so it's not limited to just `pacman`.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,24 +52,31 @@ 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.arsenm.dev/Arsen6331/lure-web), and is available at https://lure.arsenm.dev.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Repositories
 | 
					## Repositories
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Unlike the AUR, LURE supports using multiple repos. Also unlike the AUR, LURE's repos are a single git repo containing all the build scripts. Inside each LURE repo, there should be 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/Arsen6331/lure-repo.
 | 
					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/Arsen6331/lure-repo, and information about its packages is displayed at https://lure.arsenm.dev/pkgs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Dependencies
 | 
					## Dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
As mentioned before, LURE has zero dependencies after it's built. All functionality that could be pure Go is pure Go. Thanks to the following packages for making this possible:
 | 
					As mentioned before, LURE has zero dependencies after compilation. Thanks to the following projects for making this possible:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Bash: https://github.com/mvdan/sh
 | 
					- https://github.com/mvdan/sh
 | 
				
			||||||
- Git: https://github.com/go-git/go-git
 | 
					- https://github.com/go-git/go-git
 | 
				
			||||||
- Archiver: https://github.com/mholt/archiver
 | 
					- https://github.com/mholt/archiver
 | 
				
			||||||
- nfpm: https://github.com/goreleaser/nfpm
 | 
					- https://github.com/goreleaser/nfpm
 | 
				
			||||||
 | 
					- https://github.com/charmbracelet/bubbletea
 | 
				
			||||||
 | 
					- https://gitlab.com/cznic/sqlite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Planned Features
 | 
					## Planned Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Automated docker-based testing tool
 | 
					- Automated docker-based testing tool
 | 
				
			||||||
- Web interface for repos
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								assets/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 28 KiB  | 
							
								
								
									
										262
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										262
									
								
								build.go
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -22,6 +22,7 @@ import (
 | 
				
			|||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"encoding/hex"
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
@@ -29,7 +30,6 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/AlecAivazis/survey/v2"
 | 
					 | 
				
			||||||
	_ "github.com/goreleaser/nfpm/v2/apk"
 | 
						_ "github.com/goreleaser/nfpm/v2/apk"
 | 
				
			||||||
	_ "github.com/goreleaser/nfpm/v2/arch"
 | 
						_ "github.com/goreleaser/nfpm/v2/arch"
 | 
				
			||||||
	_ "github.com/goreleaser/nfpm/v2/deb"
 | 
						_ "github.com/goreleaser/nfpm/v2/deb"
 | 
				
			||||||
@@ -41,9 +41,11 @@ import (
 | 
				
			|||||||
	"github.com/goreleaser/nfpm/v2/files"
 | 
						"github.com/goreleaser/nfpm/v2/files"
 | 
				
			||||||
	"go.arsenm.dev/logger/log"
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
	"go.arsenm.dev/lure/distro"
 | 
						"go.arsenm.dev/lure/distro"
 | 
				
			||||||
	"go.arsenm.dev/lure/download"
 | 
						"go.arsenm.dev/lure/internal/cliutils"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/config"
 | 
						"go.arsenm.dev/lure/internal/config"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/cpu"
 | 
						"go.arsenm.dev/lure/internal/cpu"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/dl"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/repos"
 | 
						"go.arsenm.dev/lure/internal/repos"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/shutils"
 | 
						"go.arsenm.dev/lure/internal/shutils"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/shutils/decoder"
 | 
						"go.arsenm.dev/lure/internal/shutils/decoder"
 | 
				
			||||||
@@ -90,12 +92,17 @@ type Scripts struct {
 | 
				
			|||||||
func buildCmd(c *cli.Context) error {
 | 
					func buildCmd(c *cli.Context) error {
 | 
				
			||||||
	script := c.String("script")
 | 
						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()
 | 
						mgr := manager.Detect()
 | 
				
			||||||
	if mgr == nil {
 | 
						if mgr == nil {
 | 
				
			||||||
		log.Fatal("Unable to detect supported package manager on system").Send()
 | 
							log.Fatal("Unable to detect supported package manager on system").Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pkgPaths, _, err := buildPackage(c.Context, script, mgr)
 | 
						pkgPaths, _, err := buildPackage(c.Context, script, mgr, c.Bool("clean"))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal("Error building package").Err(err).Send()
 | 
							log.Fatal("Error building package").Err(err).Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -116,7 +123,9 @@ func buildCmd(c *cli.Context) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]string, []string, 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, clean bool) ([]string, []string, error) {
 | 
				
			||||||
	info, err := distro.ParseOSRelease(ctx)
 | 
						info, err := distro.ParseOSRelease(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
@@ -143,12 +152,19 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	fl.Close()
 | 
						fl.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	env := genBuildEnv(info)
 | 
						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(
 | 
						runner, err := interp.New(
 | 
				
			||||||
		interp.Env(expand.ListEnviron(env...)),
 | 
							interp.Env(expand.ListEnviron(env...)),
 | 
				
			||||||
		interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
 | 
							interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
 | 
				
			||||||
		interp.ExecHandler(helpers.ExecHandler),
 | 
							interp.ExecHandler(rHelpers.ExecHandler(shutils.NopExec)),
 | 
				
			||||||
 | 
							interp.ReadDirHandler(shutils.RestrictedReadDir(scriptDir)),
 | 
				
			||||||
 | 
							interp.StatHandler(shutils.RestrictedStat(scriptDir)),
 | 
				
			||||||
 | 
							interp.OpenHandler(shutils.RestrictedOpen(scriptDir)),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
@@ -173,8 +189,28 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
		return nil, nil, err
 | 
							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, translator)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Failed to prompt user to view build script").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !archMatches(vars.Architectures) {
 | 
						if !archMatches(vars.Architectures) {
 | 
				
			||||||
		buildAnyway, err := yesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", true)
 | 
							buildAnyway, err := cliutils.YesNoPrompt("Your system's CPU architecture doesn't match this package. Do you want to build anyway?", true, translator)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -186,9 +222,30 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send()
 | 
						log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	baseDir := filepath.Join(config.PkgsDir, vars.Name)
 | 
						// The second pass will be used to execute the actual functions,
 | 
				
			||||||
	srcdir := filepath.Join(baseDir, "src")
 | 
						// so it cannot be restricted. The script has already been displayed
 | 
				
			||||||
	pkgdir := filepath.Join(baseDir, "pkg")
 | 
						// 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)
 | 
						err = os.RemoveAll(baseDir)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -210,21 +267,27 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var buildDeps []string
 | 
						if instVer, ok := installed[vars.Name]; ok {
 | 
				
			||||||
	for _, pkgName := range vars.BuildDepends {
 | 
							log.Warn("This package is already installed").
 | 
				
			||||||
		if _, ok := installed[pkgName]; !ok {
 | 
								Str("name", vars.Name).
 | 
				
			||||||
			buildDeps = append(buildDeps, pkgName)
 | 
								Str("version", instVer).
 | 
				
			||||||
		}
 | 
								Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(buildDeps) > 0 {
 | 
						var buildDeps []string
 | 
				
			||||||
		found, notFound, err := repos.FindPkgs(gdb, buildDeps)
 | 
						if len(vars.BuildDepends) > 0 {
 | 
				
			||||||
 | 
							found, notFound, err := repos.FindPkgs(gdb, vars.BuildDepends)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							found = filterBuildDeps(found, installed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.Info("Installing build dependencies").Send()
 | 
							log.Info("Installing build dependencies").Send()
 | 
				
			||||||
		installPkgs(ctx, flattenFoundPkgs(found, "install"), notFound, mgr)
 | 
					
 | 
				
			||||||
 | 
							flattened := cliutils.FlattenPkgs(found, "install", translator)
 | 
				
			||||||
 | 
							buildDeps = packageNames(flattened)
 | 
				
			||||||
 | 
							installPkgs(ctx, flattened, notFound, mgr, clean)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var builtDeps, builtNames, repoDeps []string
 | 
						var builtDeps, builtNames, repoDeps []string
 | 
				
			||||||
@@ -236,9 +299,9 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
			return nil, nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		scripts := getScriptPaths(flattenFoundPkgs(found, "install"))
 | 
							scripts := getScriptPaths(cliutils.FlattenPkgs(found, "install", translator))
 | 
				
			||||||
		for _, script := range scripts {
 | 
							for _, script := range scripts {
 | 
				
			||||||
			pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr)
 | 
								pkgPaths, pkgNames, err := buildPackage(ctx, script, mgr, clean)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, nil, err
 | 
									return nil, nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -256,9 +319,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repodir := filepath.Dir(script)
 | 
						err = setDirVars(ctx, runner, srcdir, pkgdir)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = setDirVars(ctx, runner, srcdir, pkgdir, repodir)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -319,6 +380,8 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		log.Fatal("The package() function is required").Send()
 | 
							log.Fatal("The package() function is required").Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						log.Info("Building package metadata").Str("name", vars.Name).Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uniq(
 | 
						uniq(
 | 
				
			||||||
		&repoDeps,
 | 
							&repoDeps,
 | 
				
			||||||
@@ -386,6 +449,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								link = strings.TrimPrefix(link, pkgdir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			contents = append(contents, &files.Content{
 | 
								contents = append(contents, &files.Content{
 | 
				
			||||||
				Source:      link,
 | 
									Source:      link,
 | 
				
			||||||
@@ -421,12 +485,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	pkgInfo.Overridables.Contents = contents
 | 
						pkgInfo.Overridables.Contents = contents
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pkgFormat := mgr.Format()
 | 
						packager, err := nfpm.Get(getPkgFormat(mgr))
 | 
				
			||||||
	if format, ok := os.LookupEnv("LURE_PKG_FORMAT"); ok {
 | 
					 | 
				
			||||||
		pkgFormat = format
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	packager, err := nfpm.Get(pkgFormat)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -441,6 +500,8 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						log.Info("Compressing package").Str("name", pkgName).Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = packager.Package(pkgInfo, pkgFile)
 | 
						err = packager.Package(pkgInfo, pkgFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -448,7 +509,7 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(buildDeps) > 0 {
 | 
						if len(buildDeps) > 0 {
 | 
				
			||||||
		removeBuildDeps, err := yesNoPrompt("Would you like to remove build dependencies?", false)
 | 
							removeBuildDeps, err := cliutils.YesNoPrompt("Would you like to remove build dependencies?", false, translator)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -472,7 +533,48 @@ func buildPackage(ctx context.Context, script string, mgr manager.Manager) ([]st
 | 
				
			|||||||
	return pkgPaths, pkgNames, nil
 | 
						return pkgPaths, pkgNames, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func genBuildEnv(info *distro.OSRelease) []string {
 | 
					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 := os.Environ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	env = append(
 | 
						env = append(
 | 
				
			||||||
@@ -485,6 +587,8 @@ func genBuildEnv(info *distro.OSRelease) []string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		"ARCH="+cpu.Arch(),
 | 
							"ARCH="+cpu.Arch(),
 | 
				
			||||||
		"NCPU="+strconv.Itoa(runtime.NumCPU()),
 | 
							"NCPU="+strconv.Itoa(runtime.NumCPU()),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							"scriptdir="+scriptdir,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return env
 | 
						return env
 | 
				
			||||||
@@ -496,10 +600,11 @@ func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i, src := range bv.Sources {
 | 
						for i, src := range bv.Sources {
 | 
				
			||||||
		opts := download.GetOptions{
 | 
							opts := dl.Options{
 | 
				
			||||||
			SourceURL:   src,
 | 
								Name:        fmt.Sprintf("%s[%d]", bv.Name, i),
 | 
				
			||||||
 | 
								URL:         src,
 | 
				
			||||||
			Destination: srcdir,
 | 
								Destination: srcdir,
 | 
				
			||||||
			EncloseGit:  true,
 | 
								Progress:    os.Stderr,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if !strings.EqualFold(bv.Checksums[i], "SKIP") {
 | 
							if !strings.EqualFold(bv.Checksums[i], "SKIP") {
 | 
				
			||||||
@@ -507,10 +612,10 @@ func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
 | 
				
			|||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			opts.SHA256Sum = checksum
 | 
								opts.SHA256 = checksum
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err := download.Get(ctx, opts)
 | 
							err := dl.Download(ctx, opts)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -521,8 +626,8 @@ func getSources(ctx context.Context, srcdir string, bv *BuildVars) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// setDirVars sets srcdir and pkgdir. It's a very hacky way of doing so,
 | 
					// 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.
 | 
					// but setting the runner's Env and Vars fields doesn't seem to work.
 | 
				
			||||||
func setDirVars(ctx context.Context, runner *interp.Runner, srcdir, pkgdir, repodir string) error {
 | 
					func setDirVars(ctx context.Context, runner *interp.Runner, srcdir, pkgdir string) error {
 | 
				
			||||||
	cmd := "srcdir='" + srcdir + "'\npkgdir='" + pkgdir + "'\nrepodir='" + repodir + "'\n"
 | 
						cmd := "srcdir='" + srcdir + "'\npkgdir='" + pkgdir + "'\n"
 | 
				
			||||||
	fl, err := syntax.NewParser().Parse(strings.NewReader(cmd), "vars")
 | 
						fl, err := syntax.NewParser().Parse(strings.NewReader(cmd), "vars")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -566,47 +671,8 @@ func setScripts(vars *BuildVars, info *nfpm.Info, scriptDir string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getBuildVars only gets the build variables, while disabling exec, stat, open, and readdir
 | 
					// archMatches checks if your system architecture matches
 | 
				
			||||||
func getBuildVars(ctx context.Context, script string, info *distro.OSRelease) (*BuildVars, error) {
 | 
					// one of the provided architectures
 | 
				
			||||||
	fl, err := os.Open(script)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	file, err := syntax.NewParser().Parse(fl, "lure.sh")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fl.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	runner, err := interp.New(
 | 
					 | 
				
			||||||
		interp.Env(expand.ListEnviron()),
 | 
					 | 
				
			||||||
		interp.ExecHandler(shutils.NopExec),
 | 
					 | 
				
			||||||
		interp.StatHandler(shutils.NopStat),
 | 
					 | 
				
			||||||
		interp.OpenHandler(shutils.NopOpen),
 | 
					 | 
				
			||||||
		interp.ReadDirHandler(shutils.NopReadDir),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = runner.Run(ctx, file)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dec := decoder.New(info, runner)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var vars BuildVars
 | 
					 | 
				
			||||||
	err = dec.DecodeVars(&vars)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return &vars, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func archMatches(architectures []string) bool {
 | 
					func archMatches(architectures []string) bool {
 | 
				
			||||||
	if slices.Contains(architectures, "all") {
 | 
						if slices.Contains(architectures, "all") {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
@@ -627,6 +693,42 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error {
 | 
				
			|||||||
	return r.Run(ctx, fl)
 | 
						return r.Run(ctx, fl)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func filterBuildDeps(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
 | 
				
			||||||
 | 
						out := map[string][]db.Package{}
 | 
				
			||||||
 | 
						for name, pkgs := range found {
 | 
				
			||||||
 | 
							var inner []db.Package
 | 
				
			||||||
 | 
							for _, pkg := range pkgs {
 | 
				
			||||||
 | 
								if _, ok := installed[pkg.Name]; !ok {
 | 
				
			||||||
 | 
									addToFiltered := true
 | 
				
			||||||
 | 
									for _, provides := range pkg.Provides.Val {
 | 
				
			||||||
 | 
										if _, ok := installed[provides]; ok {
 | 
				
			||||||
 | 
											addToFiltered = false
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if addToFiltered {
 | 
				
			||||||
 | 
										inner = append(inner, pkg)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if len(inner) > 0 {
 | 
				
			||||||
 | 
								out[name] = inner
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func packageNames(pkgs []db.Package) []string {
 | 
				
			||||||
 | 
						names := make([]string, len(pkgs))
 | 
				
			||||||
 | 
						for i, p := range pkgs {
 | 
				
			||||||
 | 
							names[i] = p.Name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return names
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// uniq removes all duplicates from string slices
 | 
				
			||||||
func uniq(ss ...*[]string) {
 | 
					func uniq(ss ...*[]string) {
 | 
				
			||||||
	for _, s := range ss {
 | 
						for _, s := range ss {
 | 
				
			||||||
		slices.Sort(*s)
 | 
							slices.Sort(*s)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										43
									
								
								cli.go
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								cli.go
									
									
									
									
									
								
							@@ -1,43 +0,0 @@
 | 
				
			|||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"github.com/AlecAivazis/survey/v2"
 | 
					 | 
				
			||||||
	"go.arsenm.dev/lure/internal/db"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func pkgPrompt(options []db.Package, verb string) ([]db.Package, error) {
 | 
					 | 
				
			||||||
	names := make([]string, len(options))
 | 
					 | 
				
			||||||
	for i, option := range options {
 | 
					 | 
				
			||||||
		names[i] = option.Repository + "/" + option.Name + " " + option.Version
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	prompt := &survey.MultiSelect{
 | 
					 | 
				
			||||||
		Options: names,
 | 
					 | 
				
			||||||
		Message: "Choose which package(s) to " + verb,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var choices []int
 | 
					 | 
				
			||||||
	err := survey.AskOne(prompt, &choices)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	out := make([]db.Package, len(choices))
 | 
					 | 
				
			||||||
	for i, choiceIndex := range choices {
 | 
					 | 
				
			||||||
		out[i] = options[choiceIndex]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func yesNoPrompt(msg string, def bool) (bool, error) {
 | 
					 | 
				
			||||||
	var answer bool
 | 
					 | 
				
			||||||
	err := survey.AskOne(
 | 
					 | 
				
			||||||
		&survey.Confirm{
 | 
					 | 
				
			||||||
			Message: msg,
 | 
					 | 
				
			||||||
			Default: def,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		&answer,
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	return answer, err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										3
									
								
								cmd/lure-api-server/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								cmd/lure-api-server/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					FROM alpine:latest
 | 
				
			||||||
 | 
					COPY lure-api-server /usr/bin/lure-api-server
 | 
				
			||||||
 | 
					ENTRYPOINT lure-api-server
 | 
				
			||||||
							
								
								
									
										3
									
								
								cmd/lure-api-server/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								cmd/lure-api-server/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					# lure-api-server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`lure-api-server` is the backend API server for lure-web, the web interface for LURE.
 | 
				
			||||||
							
								
								
									
										205
									
								
								cmd/lure-api-server/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								cmd/lure-api-server/api.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,205 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"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 {
 | 
				
			||||||
 | 
						db *sqlx.DB
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.SearchResponse, error) {
 | 
				
			||||||
 | 
						query := "(name LIKE ? OR description LIKE ? OR json_array_contains(provides, ?))"
 | 
				
			||||||
 | 
						args := []any{"%" + req.Query + "%", "%" + req.Query + "%", req.Query}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if req.FilterValue != nil && req.FilterType != api.FILTER_TYPE_NO_FILTER {
 | 
				
			||||||
 | 
							switch req.FilterType {
 | 
				
			||||||
 | 
							case api.FILTER_TYPE_IN_REPOSITORY:
 | 
				
			||||||
 | 
								query += " AND repository = ?"
 | 
				
			||||||
 | 
							case api.FILTER_TYPE_SUPPORTS_ARCH:
 | 
				
			||||||
 | 
								query += " AND json_array_contains(architectures, ?)"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							args = append(args, *req.FilterValue)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if req.SortBy != api.SORT_BY_UNSORTED {
 | 
				
			||||||
 | 
							switch req.SortBy {
 | 
				
			||||||
 | 
							case api.SORT_BY_NAME:
 | 
				
			||||||
 | 
								query += " ORDER BY name"
 | 
				
			||||||
 | 
							case api.SORT_BY_REPOSITORY:
 | 
				
			||||||
 | 
								query += " ORDER BY repository"
 | 
				
			||||||
 | 
							case api.SORT_BY_VERSION:
 | 
				
			||||||
 | 
								query += " ORDER BY version"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if req.Limit != 0 {
 | 
				
			||||||
 | 
							query += " LIMIT " + strconv.FormatInt(req.Limit, 10)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := db.GetPkgs(l.db, query, args...)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out := &api.SearchResponse{}
 | 
				
			||||||
 | 
						for result.Next() {
 | 
				
			||||||
 | 
							pkg := &db.Package{}
 | 
				
			||||||
 | 
							err = result.StructScan(pkg)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							out.Packages = append(out.Packages, dbPkgToAPI(ctx, pkg))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return out, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l lureWebAPI) GetPkg(ctx context.Context, req *api.GetPackageRequest) (*api.Package, error) {
 | 
				
			||||||
 | 
						pkg, err := db.GetPkg(l.db, "name = ? AND repository = ?", req.Name, req.Repository)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return dbPkgToAPI(ctx, pkg), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l lureWebAPI) GetBuildScript(ctx context.Context, req *api.GetBuildScriptRequest) (*api.GetBuildScriptResponse, error) {
 | 
				
			||||||
 | 
						if strings.ContainsAny(req.Name, "./") || strings.ContainsAny(req.Repository, "./") {
 | 
				
			||||||
 | 
							return nil, twirp.NewError(twirp.InvalidArgument, "name and repository must not contain . or /")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scriptPath := filepath.Join(config.RepoDir, req.Repository, req.Name, "lure.sh")
 | 
				
			||||||
 | 
						_, err := os.Stat(scriptPath)
 | 
				
			||||||
 | 
						if os.IsNotExist(err) {
 | 
				
			||||||
 | 
							return nil, twirp.NewError(twirp.NotFound, "requested package not found")
 | 
				
			||||||
 | 
						} else if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := os.ReadFile(scriptPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &api.GetBuildScriptResponse{Script: string(data)}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func dbPkgToAPI(ctx context.Context, pkg *db.Package) *api.Package {
 | 
				
			||||||
 | 
						return &api.Package{
 | 
				
			||||||
 | 
							Name:          pkg.Name,
 | 
				
			||||||
 | 
							Repository:    pkg.Repository,
 | 
				
			||||||
 | 
							Version:       pkg.Version,
 | 
				
			||||||
 | 
							Release:       int64(pkg.Release),
 | 
				
			||||||
 | 
							Epoch:         ptr(int64(pkg.Epoch)),
 | 
				
			||||||
 | 
							Description:   performTranslation(ctx, pkg.Description.Val),
 | 
				
			||||||
 | 
							Homepage:      performTranslation(ctx, pkg.Homepage.Val),
 | 
				
			||||||
 | 
							Maintainer:    performTranslation(ctx, pkg.Maintainer.Val),
 | 
				
			||||||
 | 
							Architectures: pkg.Architectures.Val,
 | 
				
			||||||
 | 
							Licenses:      pkg.Licenses.Val,
 | 
				
			||||||
 | 
							Provides:      pkg.Provides.Val,
 | 
				
			||||||
 | 
							Conflicts:     pkg.Conflicts.Val,
 | 
				
			||||||
 | 
							Replaces:      pkg.Replaces.Val,
 | 
				
			||||||
 | 
							Depends:       dbMapToAPI(pkg.Depends.Val),
 | 
				
			||||||
 | 
							BuildDepends:  dbMapToAPI(pkg.BuildDepends.Val),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ptr[T any](v T) *T {
 | 
				
			||||||
 | 
						return &v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func dbMapToAPI(m map[string][]string) map[string]*api.StringList {
 | 
				
			||||||
 | 
						out := make(map[string]*api.StringList, len(m))
 | 
				
			||||||
 | 
						for override, list := range m {
 | 
				
			||||||
 | 
							out[override] = &api.StringList{Entries: list}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func performTranslation(ctx context.Context, v map[string]string) *string {
 | 
				
			||||||
 | 
						alVal := ctx.Value(acceptLanguageKey{})
 | 
				
			||||||
 | 
						langVal := ctx.Value(langParameterKey{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if alVal == nil && langVal == nil {
 | 
				
			||||||
 | 
							val, ok := v[""]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return ptr("<unknown>")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return &val
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						al, _ := alVal.(string)
 | 
				
			||||||
 | 
						lang, _ := langVal.(string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tags, _, err := language.ParseAcceptLanguage(al)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Warn("Error parsing Accept-Language header").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var bases []string
 | 
				
			||||||
 | 
						if lang != "" {
 | 
				
			||||||
 | 
							langTag, err := language.Parse(lang)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Warn("Error parsing lang parameter").Err(err).Send()
 | 
				
			||||||
 | 
								bases = getLangBases(tags)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								bases = getLangBases(append([]language.Tag{langTag}, tags...))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							bases = getLangBases(tags)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(bases) == 1 {
 | 
				
			||||||
 | 
							bases = []string{"en", ""}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, name := range bases {
 | 
				
			||||||
 | 
							val, ok := v[name]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return &val
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ptr("<unknown>")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getLangBases(langs []language.Tag) []string {
 | 
				
			||||||
 | 
						out := make([]string, len(langs)+1)
 | 
				
			||||||
 | 
						for i, lang := range langs {
 | 
				
			||||||
 | 
							base, _ := lang.Base()
 | 
				
			||||||
 | 
							out[i] = base.String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out[len(out)-1] = ""
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										34
									
								
								cmd/lure-api-server/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								cmd/lure-api-server/config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/config"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var cfg types.Config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						err := config.Decode(&cfg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error decoding config file").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										36
									
								
								cmd/lure-api-server/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								cmd/lure-api-server/db.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"github.com/jmoiron/sqlx"
 | 
				
			||||||
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/config"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var gdb *sqlx.DB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						gdb, err = db.Open(config.DBPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error opening database").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								cmd/lure-api-server/docker.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								cmd/lure-api-server/docker.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build
 | 
				
			||||||
 | 
					docker buildx build --platform linux/amd64 --tag arsen6331/lure-api-server:amd64 --no-cache .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CGO_ENABLED=0 GOARCH=arm64 GOOS=linux go build
 | 
				
			||||||
 | 
					docker buildx build --platform linux/arm64/v8 --tag arsen6331/lure-api-server:arm64 --no-cache .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					docker login
 | 
				
			||||||
 | 
					docker push arsen6331/lure-api-server -a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					docker manifest rm arsen6331/lure-api-server:latest
 | 
				
			||||||
 | 
					docker manifest create arsen6331/lure-api-server:latest --amend arsen6331/lure-api-server:arm64 --amend arsen6331/lure-api-server:amd64
 | 
				
			||||||
 | 
					docker manifest push arsen6331/lure-api-server:latest
 | 
				
			||||||
							
								
								
									
										117
									
								
								cmd/lure-api-server/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								cmd/lure-api-server/main.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"flag"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/twitchtv/twirp"
 | 
				
			||||||
 | 
						"go.arsenm.dev/logger"
 | 
				
			||||||
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/api"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/repos"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						log.Logger = logger.NewPretty(os.Stderr)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						addr := flag.String("a", ":8080", "Listen address for API server")
 | 
				
			||||||
 | 
						logFile := flag.String("l", "", "Output file for JSON log")
 | 
				
			||||||
 | 
						flag.Parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if *logFile != "" {
 | 
				
			||||||
 | 
							fl, err := os.Create(*logFile)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatal("Error creating log file").Err(err).Send()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer fl.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							log.Logger = logger.NewMulti(log.Logger, logger.NewJSON(fl))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := repos.Pull(ctx, gdb, cfg.Repos)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error pulling repositories").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sigCh := make(chan struct{}, 200)
 | 
				
			||||||
 | 
						go repoPullWorker(ctx, sigCh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var handler http.Handler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						handler = api.NewAPIServer(
 | 
				
			||||||
 | 
							lureWebAPI{db: gdb},
 | 
				
			||||||
 | 
							twirp.WithServerPathPrefix(""),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						handler = withAcceptLanguage(handler)
 | 
				
			||||||
 | 
						handler = allowAllCORSHandler(handler)
 | 
				
			||||||
 | 
						handler = handleWebhook(handler, sigCh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ln, err := net.Listen("tcp", *addr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error starting listener").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Info("Starting HTTP API server").Str("addr", ln.Addr().String()).Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = http.Serve(ln, handler)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error while running server").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func allowAllCORSHandler(h http.Handler) http.Handler {
 | 
				
			||||||
 | 
						return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
							res.Header().Set("Access-Control-Allow-Origin", "*")
 | 
				
			||||||
 | 
							res.Header().Set("Access-Control-Allow-Headers", "*")
 | 
				
			||||||
 | 
							if req.Method == http.MethodOptions {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							h.ServeHTTP(res, req)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						acceptLanguageKey struct{}
 | 
				
			||||||
 | 
						langParameterKey  struct{}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func withAcceptLanguage(h http.Handler) http.Handler {
 | 
				
			||||||
 | 
						return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
							ctx := req.Context()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							langs := req.Header.Get("Accept-Language")
 | 
				
			||||||
 | 
							ctx = context.WithValue(ctx, acceptLanguageKey{}, langs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							lang := req.URL.Query().Get("lang")
 | 
				
			||||||
 | 
							ctx = context.WithValue(ctx, langParameterKey{}, lang)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							req = req.WithContext(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							h.ServeHTTP(res, req)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										107
									
								
								cmd/lure-api-server/webhook.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								cmd/lure-api-server/webhook.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"crypto/hmac"
 | 
				
			||||||
 | 
						"crypto/sha256"
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/repos"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleWebhook(next http.Handler, sigCh chan<- struct{}) http.Handler {
 | 
				
			||||||
 | 
						return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
							if req.URL.Path == "/webhook" {
 | 
				
			||||||
 | 
								if req.Method != http.MethodPost {
 | 
				
			||||||
 | 
									res.WriteHeader(http.StatusMethodNotAllowed)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if req.Header.Get("X-GitHub-Event") != "push" {
 | 
				
			||||||
 | 
									http.Error(res, "Only push events are accepted by this bot", http.StatusBadRequest)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err := verifySecure(req)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									http.Error(res, err.Error(), http.StatusInternalServerError)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sigCh <- struct{}{}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							next.ServeHTTP(res, req)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func verifySecure(req *http.Request) error {
 | 
				
			||||||
 | 
						sigStr := req.Header.Get("X-Hub-Signature-256")
 | 
				
			||||||
 | 
						sig, err := hex.DecodeString(strings.TrimPrefix(sigStr, "sha256="))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						secretStr, ok := os.LookupEnv("LURE_API_GITHUB_SECRET")
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return errors.New("LURE_API_GITHUB_SECRET must be set to the secret used for setting up the github webhook\n\n")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						secret := []byte(secretStr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						h := hmac.New(sha256.New, secret)
 | 
				
			||||||
 | 
						_, err = io.Copy(h, req.Body)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !hmac.Equal(h.Sum(nil), sig) {
 | 
				
			||||||
 | 
							log.Warn("Insecure webhook request").
 | 
				
			||||||
 | 
								Str("from", req.RemoteAddr).
 | 
				
			||||||
 | 
								Bytes("sig", sig).
 | 
				
			||||||
 | 
								Bytes("hmac", h.Sum(nil)).
 | 
				
			||||||
 | 
								Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return errors.New("webhook signature mismatch")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func repoPullWorker(ctx context.Context, sigCh <-chan struct{}) {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-sigCh:
 | 
				
			||||||
 | 
								err := repos.Pull(ctx, gdb, cfg.Repos)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Warn("Error while pulling repositories").Err(err).Send()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case <-ctx.Done():
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										29
									
								
								db.go
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								db.go
									
									
									
									
									
								
							@@ -1,23 +1,36 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/genjidb/genji"
 | 
						"github.com/jmoiron/sqlx"
 | 
				
			||||||
	"go.arsenm.dev/logger/log"
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/config"
 | 
						"go.arsenm.dev/lure/internal/config"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/db"
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var gdb *genji.DB
 | 
					var gdb *sqlx.DB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	gdb, err = genji.Open(config.DBPath)
 | 
						gdb, err = db.Open(config.DBPath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal("Error opening database").Err(err).Send()
 | 
							log.Fatal("Error opening database").Err(err).Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = db.Init(gdb)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Fatal("Error initializing database").Err(err).Send()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -46,8 +46,16 @@ type OSRelease struct {
 | 
				
			|||||||
	Logo             string
 | 
						Logo             string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// OSReleaseName returns the NAME field of the
 | 
					var parsed *OSRelease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// OSReleaseName returns a struct parsed from the system's os-release
 | 
				
			||||||
 | 
					// file. It checks /etc/os-release as well as /usr/lib/os-release.
 | 
				
			||||||
 | 
					// The returned OSRelease struct is a singleton.
 | 
				
			||||||
func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
 | 
					func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
 | 
				
			||||||
 | 
						if parsed != nil {
 | 
				
			||||||
 | 
							return parsed, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fl, err := os.Open("/usr/lib/os-release")
 | 
						fl, err := os.Open("/usr/lib/os-release")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fl, err = os.Open("/etc/os-release")
 | 
							fl, err = os.Open("/etc/os-release")
 | 
				
			||||||
@@ -99,5 +107,6 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) {
 | 
				
			|||||||
		out.Like = strings.Split(runner.Vars["ID_LIKE"].Str, " ")
 | 
							out.Like = strings.Split(runner.Vars["ID_LIKE"].Str, " ")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parsed = out
 | 
				
			||||||
	return out, nil
 | 
						return out, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
# LURE Docs
 | 
					# LURE Docs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [Build Scripts](build-scripts.md)
 | 
					 | 
				
			||||||
- [Usage](usage.md)
 | 
					 | 
				
			||||||
- [Configuration](configuration.md)
 | 
					- [Configuration](configuration.md)
 | 
				
			||||||
- [Adding Packages to LURE's repo](adding-packages.md)
 | 
					- [Usage](usage.md)
 | 
				
			||||||
 | 
					- [Packages](packages)
 | 
				
			||||||
 | 
					    - [Build Scripts](packages/build-scripts.md)
 | 
				
			||||||
 | 
					    - [Package Conventions](packages/conventions.md)
 | 
				
			||||||
 | 
					    - [Adding Packages to LURE's repo](packages/adding-packages.md)
 | 
				
			||||||
							
								
								
									
										5
									
								
								docs/packages/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								docs/packages/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					# LURE Docs > Packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [Build Scripts](build-scripts.md)
 | 
				
			||||||
 | 
					- [Package Conventions](conventions.md)
 | 
				
			||||||
 | 
					- [Adding Packages to LURE's repo](adding-packages.md)
 | 
				
			||||||
@@ -182,20 +182,19 @@ https://example.com/archive.tar.gz?~archive=false
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
If the URL scheme starts with `git+`, the source will be downloaded as a git repo. The git download mode supports multiple parameters:
 | 
					If the URL scheme starts with `git+`, the source will be downloaded as a git repo. The git download mode supports multiple parameters:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `~tag`: Specify which tag of the repo to check out.
 | 
					- `~rev`: Specify which revision of the repo to check out.
 | 
				
			||||||
- `~branch`: Specify which branch of the repo to check out.
 | 
					 | 
				
			||||||
- `~commit`: Specify which commit of the repo to check out.
 | 
					 | 
				
			||||||
- `~depth`: Specify what depth should be used when cloning the repo. Must be an integer.
 | 
					- `~depth`: Specify what depth should be used when cloning the repo. Must be an integer.
 | 
				
			||||||
- `~name`: Specify the name of the directory into which the git repo should be cloned.
 | 
					- `~name`: Specify the name of the directory into which the git repo should be cloned.
 | 
				
			||||||
 | 
					- `~recursive`: If set to true, submodules will be cloned recursively. It is false by default.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Examples:
 | 
					Examples:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```text
 | 
					```text
 | 
				
			||||||
git+https://gitea.arsenm.dev/Arsen6331/itd?~branch=resource-loading&~depth=1
 | 
					git+https://gitea.arsenm.dev/Arsen6331/itd?~rev=resource-loading&~depth=1
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```text
 | 
					```text
 | 
				
			||||||
git+https://gitea.arsenm.dev/Arsen6331/lure?~tag=v0.0.1
 | 
					git+https://gitea.arsenm.dev/Arsen6331/lure?~rev=v0.0.1&~recursive=true
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### checksums
 | 
					### checksums
 | 
				
			||||||
							
								
								
									
										36
									
								
								docs/packages/conventions.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								docs/packages/conventions.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					# Package Conventions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## General
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Packages should have the name(s) of what they contain in their `provides` and `conflicts` arrays. That way, they can be installed by users without needing to know the full package name. For example, there are two LURE packages for ITD: `itd-bin`, and `itd-git`. Both of them have provides and conflicts arrays specifying the two commands they install: `itd`, and `itctl`. This means that if a user wants to install ITD, they simply have to type `lure in itd` and LURE will prompt them for which one they want to install.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Binary packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Packages that install download and install precompiled binaries should have a `-bin` suffix.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Git packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Packages that build and install programs from source code cloned directly from Git should have a `-git` suffix.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The versions of these packages should consist of the amount of revisions followed by the current revision, separated by a period. For example: `183.80187b0`. Note that unlike the AUR, there is no `r` at the beginning. This is because some package managers refuse to install packages whose version numbers don't start with a digit.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This version number can be obtained using the following command:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					printf "%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `version()` function for such packages should use the LURE-provided `git-version` helper command, like so:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					version() {
 | 
				
			||||||
 | 
					    cd "$srcdir/$name"
 | 
				
			||||||
 | 
					    git-version
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This uses LURE's embedded Git implementation, which ensures that the user doesn't need Git installed on their system in order to install `-git` packages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Other packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Packages that download sources for a specific version of a program should not have any suffix, even if those sources are downloaded from Git.
 | 
				
			||||||
@@ -12,6 +12,8 @@
 | 
				
			|||||||
    - [addrepo](#addrepo)
 | 
					    - [addrepo](#addrepo)
 | 
				
			||||||
    - [removerepo](#removerepo)
 | 
					    - [removerepo](#removerepo)
 | 
				
			||||||
    - [refresh](#refresh)
 | 
					    - [refresh](#refresh)
 | 
				
			||||||
 | 
					    - [fix](#fix)
 | 
				
			||||||
 | 
					    - [version](#version)
 | 
				
			||||||
- [Environment Variables](#environment-variables)
 | 
					- [Environment Variables](#environment-variables)
 | 
				
			||||||
    - [LURE_DISTRO](#lure_distro)
 | 
					    - [LURE_DISTRO](#lure_distro)
 | 
				
			||||||
    - [LURE_PKG_FORMAT](#lure_pkg_format)
 | 
					    - [LURE_PKG_FORMAT](#lure_pkg_format)
 | 
				
			||||||
@@ -23,12 +25,21 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
### install
 | 
					### install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The install command installs a command from the LURE repos. Any packages that aren't found in LURE's repos get forwarded to the system package manager for installation.
 | 
					The install command installs a package from the LURE repos. Any packages that aren't found in LURE's repos get forwarded to the system package manager for installation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Example:
 | 
					The package arguments do not have to be exact. LURE will check the `provides` array if an exact match is not found. There is also support for using "%" as a wildcard.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
					```shell
 | 
				
			||||||
lure in itd-bin
 | 
					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
 | 
					### remove
 | 
				
			||||||
@@ -45,6 +56,8 @@ lure rm firefox
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
The upgrade command looks through the packages installed on your system and sees if any of them match LURE repo packages. If they do, their versions are compared using the `rpmvercmp` algorithm. If LURE repos contain a newer version, the package is upgraded.
 | 
					The upgrade command looks through the packages installed on your system and sees if any of them match LURE repo packages. If they do, their versions are compared using the `rpmvercmp` algorithm. If LURE repos contain a newer version, the package is upgraded.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					By default, if a package has already been built, LURE will install the cached package rather than re-build it. Use the `-c` or `--clean` flag to force a re-build.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Example:
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```shell
 | 
					```shell
 | 
				
			||||||
@@ -55,20 +68,36 @@ lure up
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
The info command displays information about a package in LURE's repos.
 | 
					The info command displays information about a package in LURE's repos.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The package arguments do not have to be exact. LURE will check the `provides` array if an exact match is not found. There is also support for using "%" as a wildcard.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If multiple packages are found, you will be prompted to select which you want to show.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Example:
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```shell
 | 
					```shell
 | 
				
			||||||
lure info itd-bin
 | 
					lure info itd-bin # only finds itd-bin
 | 
				
			||||||
 | 
					lure info itd # finds itd-bin and itd-git
 | 
				
			||||||
 | 
					lure info it% # finds itd-bin, itd-git, and itgui-git
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### list
 | 
					### list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The list command lists all LURE repo packages as well as their versions
 | 
					The list command lists all LURE repo packages as well as their versions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Example:
 | 
					This command accepts a single optional argument. This argument is a pattern to filter found packages against.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The pattern does not have to be exact. LURE will check the `provides` array if an exact match is not found. There is also support for using "%" as a wildcard.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There is a `-I` or `--installed` flag that filters out any packages that are not installed on the system
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Examples:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```shell
 | 
					```shell
 | 
				
			||||||
lure ls
 | 
					lure ls # lists all LURE packages
 | 
				
			||||||
 | 
					lure ls -I # lists all installed packages
 | 
				
			||||||
 | 
					lure ls i% # lists all packages starting with "i"
 | 
				
			||||||
 | 
					lure ls %d # lists all packages ending with "d"
 | 
				
			||||||
 | 
					lure ls -I i% # lists all installed packages that start with "i"
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### build
 | 
					### build
 | 
				
			||||||
@@ -111,6 +140,26 @@ Example:
 | 
				
			|||||||
lure ref
 | 
					lure ref
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### fix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The fix command attempts to fix issues with LURE by deleting and rebuilding LURE's cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```shell
 | 
				
			||||||
 | 
					lure fix
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The version command returns the current LURE version and exits
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```shell
 | 
				
			||||||
 | 
					lure version
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Environment Variables
 | 
					## Environment Variables
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										63
									
								
								fix.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								fix.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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/urfave/cli/v2"
 | 
				
			||||||
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/config"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/repos"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func fixCmd(c *cli.Context) error {
 | 
				
			||||||
 | 
						gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Info("Removing cache directory").Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := os.RemoveAll(config.CacheDir)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Unable to remove cache directory").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Info("Rebuilding cache").Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = os.MkdirAll(config.CacheDir, 0o755)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Unable to create new cache directory").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make sure the DB is rebuilt when repos are pulled
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
							log.Fatal("Error pulling repos").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Info("Done").Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										71
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								go.mod
									
									
									
									
									
								
							@@ -4,24 +4,36 @@ go 1.18
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/AlecAivazis/survey/v2 v2.3.6
 | 
						github.com/AlecAivazis/survey/v2 v2.3.6
 | 
				
			||||||
	github.com/genjidb/genji v0.15.1
 | 
						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
 | 
				
			||||||
 | 
						github.com/charmbracelet/lipgloss v0.6.0
 | 
				
			||||||
 | 
						github.com/go-git/go-billy/v5 v5.3.1
 | 
				
			||||||
	github.com/go-git/go-git/v5 v5.4.2
 | 
						github.com/go-git/go-git/v5 v5.4.2
 | 
				
			||||||
	github.com/goreleaser/nfpm/v2 v2.20.0
 | 
						github.com/goreleaser/nfpm/v2 v2.20.0
 | 
				
			||||||
 | 
						github.com/jmoiron/sqlx v1.3.5
 | 
				
			||||||
	github.com/mholt/archiver/v4 v4.0.0-alpha.7
 | 
						github.com/mholt/archiver/v4 v4.0.0-alpha.7
 | 
				
			||||||
	github.com/mitchellh/mapstructure v1.5.0
 | 
						github.com/mitchellh/mapstructure v1.5.0
 | 
				
			||||||
	github.com/pelletier/go-toml/v2 v2.0.5
 | 
						github.com/muesli/reflow v0.3.0
 | 
				
			||||||
	github.com/urfave/cli/v2 v2.16.3
 | 
						github.com/pelletier/go-toml/v2 v2.0.6
 | 
				
			||||||
	go.arsenm.dev/logger v0.0.0-20221007032343-cbffce4f4334
 | 
						github.com/schollz/progressbar/v3 v3.13.0
 | 
				
			||||||
 | 
						github.com/twitchtv/twirp v8.1.3+incompatible
 | 
				
			||||||
 | 
						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/exp v0.0.0-20220916125017-b168a2c6b86b
 | 
				
			||||||
	golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
 | 
						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
 | 
						gopkg.in/yaml.v3 v3.0.1
 | 
				
			||||||
 | 
						modernc.org/sqlite v1.20.0
 | 
				
			||||||
	mvdan.cc/sh/v3 v3.5.1
 | 
						mvdan.cc/sh/v3 v3.5.1
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/AlekSi/pointer v1.2.0 // indirect
 | 
						github.com/AlekSi/pointer v1.2.0 // indirect
 | 
				
			||||||
	github.com/DataDog/zstd v1.4.5 // indirect
 | 
					 | 
				
			||||||
	github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
 | 
					 | 
				
			||||||
	github.com/Masterminds/goutils v1.1.1 // indirect
 | 
						github.com/Masterminds/goutils v1.1.1 // indirect
 | 
				
			||||||
	github.com/Masterminds/semver v1.5.0 // indirect
 | 
						github.com/Masterminds/semver v1.5.0 // indirect
 | 
				
			||||||
	github.com/Masterminds/semver/v3 v3.1.1 // indirect
 | 
						github.com/Masterminds/semver/v3 v3.1.1 // indirect
 | 
				
			||||||
@@ -30,26 +42,20 @@ require (
 | 
				
			|||||||
	github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c // indirect
 | 
						github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c // indirect
 | 
				
			||||||
	github.com/acomagu/bufpipe v1.0.3 // indirect
 | 
						github.com/acomagu/bufpipe v1.0.3 // indirect
 | 
				
			||||||
	github.com/andybalholm/brotli v1.0.4 // indirect
 | 
						github.com/andybalholm/brotli v1.0.4 // indirect
 | 
				
			||||||
 | 
						github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
 | 
				
			||||||
	github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
 | 
						github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
 | 
				
			||||||
	github.com/buger/jsonparser v1.1.1 // indirect
 | 
					 | 
				
			||||||
	github.com/cavaliergopher/cpio v1.0.1 // indirect
 | 
						github.com/cavaliergopher/cpio v1.0.1 // indirect
 | 
				
			||||||
	github.com/cespare/xxhash/v2 v2.1.1 // indirect
 | 
						github.com/containerd/console v1.0.3 // indirect
 | 
				
			||||||
	github.com/cockroachdb/errors v1.9.0 // indirect
 | 
					 | 
				
			||||||
	github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
 | 
					 | 
				
			||||||
	github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e // indirect
 | 
					 | 
				
			||||||
	github.com/cockroachdb/redact v1.1.3 // indirect
 | 
					 | 
				
			||||||
	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
 | 
						github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
 | 
				
			||||||
 | 
						github.com/dlclark/regexp2 v1.4.0 // indirect
 | 
				
			||||||
	github.com/dsnet/compress v0.0.1 // indirect
 | 
						github.com/dsnet/compress v0.0.1 // indirect
 | 
				
			||||||
	github.com/emirpasic/gods v1.12.0 // indirect
 | 
						github.com/emirpasic/gods v1.12.0 // indirect
 | 
				
			||||||
	github.com/frankban/quicktest v1.14.3 // indirect
 | 
						github.com/frankban/quicktest v1.14.3 // indirect
 | 
				
			||||||
	github.com/getsentry/sentry-go v0.12.0 // indirect
 | 
					 | 
				
			||||||
	github.com/go-git/gcfg v1.5.0 // indirect
 | 
						github.com/go-git/gcfg v1.5.0 // indirect
 | 
				
			||||||
	github.com/go-git/go-billy/v5 v5.3.1 // indirect
 | 
					 | 
				
			||||||
	github.com/gobwas/glob v0.2.3 // indirect
 | 
						github.com/gobwas/glob v0.2.3 // indirect
 | 
				
			||||||
	github.com/gogo/protobuf v1.3.2 // indirect
 | 
					 | 
				
			||||||
	github.com/golang/snappy v0.0.4 // indirect
 | 
						github.com/golang/snappy v0.0.4 // indirect
 | 
				
			||||||
	github.com/google/uuid v1.3.0 // indirect
 | 
						github.com/google/uuid v1.3.0 // indirect
 | 
				
			||||||
	github.com/gookit/color v1.5.1 // indirect
 | 
						github.com/gookit/color v1.5.2 // indirect
 | 
				
			||||||
	github.com/goreleaser/chglog v0.2.2 // indirect
 | 
						github.com/goreleaser/chglog v0.2.2 // indirect
 | 
				
			||||||
	github.com/goreleaser/fileglob v1.3.0 // indirect
 | 
						github.com/goreleaser/fileglob v1.3.0 // indirect
 | 
				
			||||||
	github.com/huandu/xstrings v1.3.2 // indirect
 | 
						github.com/huandu/xstrings v1.3.2 // indirect
 | 
				
			||||||
@@ -59,31 +65,48 @@ require (
 | 
				
			|||||||
	github.com/kevinburke/ssh_config v1.1.0 // indirect
 | 
						github.com/kevinburke/ssh_config v1.1.0 // indirect
 | 
				
			||||||
	github.com/klauspost/compress v1.15.11 // indirect
 | 
						github.com/klauspost/compress v1.15.11 // indirect
 | 
				
			||||||
	github.com/klauspost/pgzip v1.2.5 // indirect
 | 
						github.com/klauspost/pgzip v1.2.5 // indirect
 | 
				
			||||||
	github.com/kr/pretty v0.3.0 // indirect
 | 
						github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
 | 
				
			||||||
	github.com/kr/text v0.2.0 // indirect
 | 
					 | 
				
			||||||
	github.com/mattn/go-colorable v0.1.11 // indirect
 | 
						github.com/mattn/go-colorable v0.1.11 // indirect
 | 
				
			||||||
	github.com/mattn/go-isatty v0.0.14 // 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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 | 
				
			||||||
 | 
						github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
 | 
				
			||||||
	github.com/mitchellh/copystructure v1.2.0 // indirect
 | 
						github.com/mitchellh/copystructure v1.2.0 // indirect
 | 
				
			||||||
	github.com/mitchellh/go-homedir v1.1.0 // indirect
 | 
						github.com/mitchellh/go-homedir v1.1.0 // indirect
 | 
				
			||||||
	github.com/mitchellh/reflectwalk v1.0.2 // indirect
 | 
						github.com/mitchellh/reflectwalk v1.0.2 // indirect
 | 
				
			||||||
 | 
						github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
 | 
				
			||||||
 | 
						github.com/muesli/cancelreader v0.2.2 // indirect
 | 
				
			||||||
 | 
						github.com/muesli/termenv v0.13.0 // indirect
 | 
				
			||||||
	github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
 | 
						github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
 | 
				
			||||||
	github.com/pierrec/lz4/v4 v4.1.15 // indirect
 | 
						github.com/pierrec/lz4/v4 v4.1.15 // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
						github.com/pkg/errors v0.9.1 // indirect
 | 
				
			||||||
	github.com/rogpeppe/go-internal v1.8.1 // indirect
 | 
						github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
 | 
				
			||||||
 | 
						github.com/rivo/uniseg v0.4.3 // indirect
 | 
				
			||||||
	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 | 
						github.com/russross/blackfriday/v2 v2.1.0 // indirect
 | 
				
			||||||
	github.com/sergi/go-diff v1.2.0 // indirect
 | 
						github.com/sergi/go-diff v1.2.0 // indirect
 | 
				
			||||||
	github.com/sirupsen/logrus v1.9.0 // indirect
 | 
						github.com/sirupsen/logrus v1.9.0 // indirect
 | 
				
			||||||
	github.com/therootcompany/xz v1.0.1 // indirect
 | 
						github.com/therootcompany/xz v1.0.1 // indirect
 | 
				
			||||||
	github.com/ulikunitz/xz v0.5.10 // indirect
 | 
						github.com/ulikunitz/xz v0.5.10 // indirect
 | 
				
			||||||
 | 
						github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
 | 
				
			||||||
	github.com/xanzy/ssh-agent v0.3.1 // indirect
 | 
						github.com/xanzy/ssh-agent v0.3.1 // indirect
 | 
				
			||||||
	github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
 | 
						github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
 | 
				
			||||||
	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 | 
						github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 | 
				
			||||||
	gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
 | 
						gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
 | 
						golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
 | 
				
			||||||
 | 
						golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
 | 
				
			||||||
	golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
 | 
						golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
 | 
				
			||||||
	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
 | 
						golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
 | 
				
			||||||
	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
 | 
						golang.org/x/term v0.4.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.3.7 // indirect
 | 
						golang.org/x/tools v0.1.12 // indirect
 | 
				
			||||||
	gopkg.in/warnings.v0 v0.1.2 // indirect
 | 
						gopkg.in/warnings.v0 v0.1.2 // indirect
 | 
				
			||||||
 | 
						lukechampine.com/uint128 v1.2.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/cc/v3 v3.40.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/ccgo/v3 v3.16.13 // indirect
 | 
				
			||||||
 | 
						modernc.org/libc v1.21.5 // indirect
 | 
				
			||||||
 | 
						modernc.org/mathutil v1.5.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/memory v1.4.0 // indirect
 | 
				
			||||||
 | 
						modernc.org/opt v0.1.3 // indirect
 | 
				
			||||||
 | 
						modernc.org/strutil v1.1.3 // indirect
 | 
				
			||||||
 | 
						modernc.org/token v1.0.1 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										515
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										515
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,22 +1,8 @@
 | 
				
			|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
					 | 
				
			||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
					 | 
				
			||||||
github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
 | 
					github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
 | 
				
			||||||
github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
 | 
					github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
 | 
				
			||||||
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
 | 
					github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
 | 
				
			||||||
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
 | 
					github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
 | 
				
			||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
 | 
					 | 
				
			||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
					 | 
				
			||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
					 | 
				
			||||||
github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
 | 
					 | 
				
			||||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
 | 
					 | 
				
			||||||
github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
 | 
					 | 
				
			||||||
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
 | 
					 | 
				
			||||||
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
 | 
					github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
 | 
				
			||||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
 | 
					 | 
				
			||||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
 | 
					 | 
				
			||||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
 | 
					 | 
				
			||||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
 | 
					 | 
				
			||||||
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
 | 
					 | 
				
			||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
 | 
					github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
 | 
				
			||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
 | 
					github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
 | 
				
			||||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
 | 
					github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
 | 
				
			||||||
@@ -37,56 +23,41 @@ github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c h1:bNpaLLv2Y4
 | 
				
			|||||||
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
 | 
					github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
 | 
				
			||||||
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko=
 | 
					github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f h1:CGq7OieOz3wyQJ1fO8S0eO9TCW1JyvLrf8fhzz1i8ko=
 | 
				
			||||||
github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs=
 | 
					github.com/ProtonMail/gopenpgp/v2 v2.2.2 h1:u2m7xt+CZWj88qK1UUNBoXeJCFJwJCZ/Ff4ymGoxEXs=
 | 
				
			||||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
 | 
					github.com/PuerkitoBio/purell v1.2.0 h1:/Jdm5QfyM8zdlqT6WVZU4cfP23sot6CEHA4CS49Ezig=
 | 
				
			||||||
 | 
					github.com/PuerkitoBio/purell v1.2.0/go.mod h1:OhLRTaaIzhvIyofkJfB24gokC7tM42Px5UhoT32THBk=
 | 
				
			||||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
 | 
					github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
 | 
				
			||||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
 | 
					github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
 | 
				
			||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
 | 
					github.com/alecthomas/assert/v2 v2.2.0 h1:f6L/b7KE2bfA+9O4FL3CM/xJccDEwPVYd5fALBiuwvw=
 | 
				
			||||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
 | 
					github.com/alecthomas/chroma/v2 v2.4.0 h1:Loe2ZjT5x3q1bcWwemqyqEi8p11/IV/ncFCeLYDpWC4=
 | 
				
			||||||
 | 
					github.com/alecthomas/chroma/v2 v2.4.0/go.mod h1:6kHzqF5O6FUSJzBXW7fXELjb+e+7OXW4UpoPqMO7IBQ=
 | 
				
			||||||
 | 
					github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
 | 
				
			||||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
 | 
					github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
 | 
				
			||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
 | 
					github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
 | 
				
			||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 | 
					github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 | 
				
			||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 | 
					github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 | 
				
			||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
					 | 
				
			||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
 | 
					github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
 | 
				
			||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 | 
					github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 | 
				
			||||||
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
 | 
					github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
 | 
				
			||||||
 | 
					github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg=
 | 
				
			||||||
 | 
					github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
 | 
				
			||||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
 | 
					github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
 | 
				
			||||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
 | 
					github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
 | 
				
			||||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
 | 
					 | 
				
			||||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
 | 
					 | 
				
			||||||
github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11 h1:IRrDwVlWQr6kS1U8/EtyA1+EHcc4yl8pndcqXWrEamg=
 | 
					github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11 h1:IRrDwVlWQr6kS1U8/EtyA1+EHcc4yl8pndcqXWrEamg=
 | 
				
			||||||
github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8=
 | 
					github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8=
 | 
				
			||||||
github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
 | 
					github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
 | 
				
			||||||
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
 | 
					github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
 | 
				
			||||||
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
 | 
					github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
 | 
				
			||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
					github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og=
 | 
				
			||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 | 
					github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc=
 | 
				
			||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
					github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4=
 | 
				
			||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
					github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck=
 | 
				
			||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 | 
					github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
 | 
				
			||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 | 
					github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
 | 
				
			||||||
github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
 | 
					github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
 | 
				
			||||||
github.com/cockroachdb/datadriven v1.0.1-0.20211007161720-b558070c3be0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
 | 
					github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
 | 
				
			||||||
github.com/cockroachdb/datadriven v1.0.1-0.20220214170620-9913f5bc19b7/go.mod h1:hi0MtSY3AYDQNDi83kDkMH5/yqM/CsIrsOITkSoH7KI=
 | 
					github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
 | 
				
			||||||
github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
 | 
					github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
 | 
				
			||||||
github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac=
 | 
					github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
 | 
				
			||||||
github.com/cockroachdb/errors v1.8.8/go.mod h1:z6VnEL3hZ/2ONZEvG7S5Ym0bU2AqPcEKnIiA1wbsSu0=
 | 
					 | 
				
			||||||
github.com/cockroachdb/errors v1.9.0 h1:B48dYem5SlAY7iU8AKsgedb4gH6mo+bDkbtLIvM/a88=
 | 
					 | 
				
			||||||
github.com/cockroachdb/errors v1.9.0/go.mod h1:vaNcEYYqbIqB5JhKBhFV9CneUqeuEbB2OYJBK4GBNYQ=
 | 
					 | 
				
			||||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
 | 
					 | 
				
			||||||
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74=
 | 
					 | 
				
			||||||
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
 | 
					 | 
				
			||||||
github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e h1:MGFpdiedteFe4rm8UY/0FixU8RVAh4wZYUCLQys9GjI=
 | 
					 | 
				
			||||||
github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e/go.mod h1:pr479tNxFRmcfDyklTqoRMDDVmRlEbL+d7a7rhKnrI4=
 | 
					 | 
				
			||||||
github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
 | 
					 | 
				
			||||||
github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
 | 
					 | 
				
			||||||
github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
 | 
					 | 
				
			||||||
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=
 | 
					 | 
				
			||||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
 | 
					 | 
				
			||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
					 | 
				
			||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 | 
					 | 
				
			||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
					 | 
				
			||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 | 
					 | 
				
			||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
 | 
					github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
 | 
				
			||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 | 
					github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 | 
				
			||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
					github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
				
			||||||
@@ -95,44 +66,19 @@ github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr
 | 
				
			|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
					github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
 | 
					github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
 | 
				
			||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
					github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 | 
				
			||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
 | 
					 | 
				
			||||||
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
 | 
					github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
 | 
				
			||||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 | 
					github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
 | 
				
			||||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 | 
					github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 | 
				
			||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 | 
					github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 | 
				
			||||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
 | 
					 | 
				
			||||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 | 
					github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 | 
				
			||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 | 
					github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 | 
				
			||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
					 | 
				
			||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 | 
					 | 
				
			||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 | 
					 | 
				
			||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 | 
					 | 
				
			||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 | 
					 | 
				
			||||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 | 
					 | 
				
			||||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
 | 
					 | 
				
			||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 | 
					 | 
				
			||||||
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
 | 
					 | 
				
			||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 | 
					github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 | 
				
			||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
 | 
					 | 
				
			||||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
 | 
					github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
 | 
				
			||||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 | 
					github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 | 
				
			||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
					 | 
				
			||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 | 
					 | 
				
			||||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
 | 
					 | 
				
			||||||
github.com/genjidb/genji v0.15.1 h1:5lhRF15enjqQ0hSv7nuDPhTr8SoWv/vx19V2QDnwaPQ=
 | 
					 | 
				
			||||||
github.com/genjidb/genji v0.15.1/go.mod h1:fo1EzSGfPuSqz6s9VoYfX/dcZHMnKP7KONH+fJeSlEc=
 | 
					 | 
				
			||||||
github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk=
 | 
					 | 
				
			||||||
github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
 | 
					 | 
				
			||||||
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
 | 
					 | 
				
			||||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
 | 
					 | 
				
			||||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
 | 
					 | 
				
			||||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 | 
					github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 | 
				
			||||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
					github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
				
			||||||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
 | 
					 | 
				
			||||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
 | 
					 | 
				
			||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
 | 
					 | 
				
			||||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 | 
					github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 | 
				
			||||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
 | 
					github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
 | 
				
			||||||
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
 | 
					github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
 | 
				
			||||||
@@ -142,126 +88,53 @@ github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2Su
 | 
				
			|||||||
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
 | 
					github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
 | 
				
			||||||
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
 | 
					github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
 | 
				
			||||||
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
 | 
					github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
 | 
				
			||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 | 
					github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
 | 
				
			||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 | 
					github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 | 
				
			||||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
 | 
					 | 
				
			||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 | 
					github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 | 
				
			||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 | 
					github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 | 
				
			||||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
 | 
					 | 
				
			||||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
 | 
					 | 
				
			||||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
 | 
					 | 
				
			||||||
github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 | 
					 | 
				
			||||||
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
 | 
					 | 
				
			||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
					 | 
				
			||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 | 
					 | 
				
			||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 | 
					 | 
				
			||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 | 
					 | 
				
			||||||
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
 | 
					 | 
				
			||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 | 
					 | 
				
			||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 | 
					 | 
				
			||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
					 | 
				
			||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
					 | 
				
			||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 | 
					github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 | 
				
			||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 | 
					 | 
				
			||||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
					 | 
				
			||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 | 
					github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 | 
				
			||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
					github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
				
			||||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 | 
					 | 
				
			||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
					 | 
				
			||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
					github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
				
			||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
					 | 
				
			||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
					 | 
				
			||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
					 | 
				
			||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
					 | 
				
			||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
					github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
				
			||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 | 
					github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 | 
				
			||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 | 
					github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 | 
				
			||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 | 
					github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
 | 
				
			||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
					 | 
				
			||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
					 | 
				
			||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 | 
					github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 | 
				
			||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
					github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
				
			||||||
github.com/gookit/color v1.5.1 h1:Vjg2VEcdHpwq+oY63s/ksHrgJYCTo0bwWvmmYWdE9fQ=
 | 
					github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
 | 
				
			||||||
github.com/gookit/color v1.5.1/go.mod h1:wZFzea4X8qN6vHOSP2apMb4/+w/orMznEzYsIHPaqKM=
 | 
					github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
 | 
				
			||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 | 
					github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 | 
				
			||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
					 | 
				
			||||||
github.com/goreleaser/chglog v0.2.2 h1:V7nf07baXtGAgGevvqgW2MM4kZ6gOr12vKNSAU3VIZ0=
 | 
					github.com/goreleaser/chglog v0.2.2 h1:V7nf07baXtGAgGevvqgW2MM4kZ6gOr12vKNSAU3VIZ0=
 | 
				
			||||||
github.com/goreleaser/chglog v0.2.2/go.mod h1:2s5JwtCOWjZa8AIneL+xdUl9SRuigCjRHNHsX30dupE=
 | 
					github.com/goreleaser/chglog v0.2.2/go.mod h1:2s5JwtCOWjZa8AIneL+xdUl9SRuigCjRHNHsX30dupE=
 | 
				
			||||||
github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I=
 | 
					github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I=
 | 
				
			||||||
github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU=
 | 
					github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU=
 | 
				
			||||||
github.com/goreleaser/nfpm/v2 v2.20.0 h1:Q/CrX54KUMluz6+M/pjTbknFd5Dao8qXi0C6ZuFCtfY=
 | 
					github.com/goreleaser/nfpm/v2 v2.20.0 h1:Q/CrX54KUMluz6+M/pjTbknFd5Dao8qXi0C6ZuFCtfY=
 | 
				
			||||||
github.com/goreleaser/nfpm/v2 v2.20.0/go.mod h1:/Fh6XfwT/T+D4qtNC2iXmHSD/1UT20JkvBXyJ6nFmOY=
 | 
					github.com/goreleaser/nfpm/v2 v2.20.0/go.mod h1:/Fh6XfwT/T+D4qtNC2iXmHSD/1UT20JkvBXyJ6nFmOY=
 | 
				
			||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
					github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
 | 
				
			||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
					 | 
				
			||||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 | 
					 | 
				
			||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
					 | 
				
			||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
 | 
					github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
 | 
				
			||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
 | 
					github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
 | 
				
			||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
					 | 
				
			||||||
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
 | 
					github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
 | 
				
			||||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 | 
					github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 | 
				
			||||||
github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
 | 
					 | 
				
			||||||
github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
 | 
					 | 
				
			||||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 | 
					github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 | 
				
			||||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
 | 
					github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
 | 
				
			||||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
 | 
					github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
 | 
				
			||||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
 | 
					 | 
				
			||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
					 | 
				
			||||||
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
 | 
					 | 
				
			||||||
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
 | 
					 | 
				
			||||||
github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
 | 
					 | 
				
			||||||
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
 | 
					 | 
				
			||||||
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
 | 
					 | 
				
			||||||
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
 | 
					 | 
				
			||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 | 
					github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 | 
				
			||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 | 
					github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 | 
				
			||||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
 | 
					github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
 | 
				
			||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
					github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
 | 
				
			||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
					github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
 | 
				
			||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 | 
					github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 | 
				
			||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
					github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
 | 
				
			||||||
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
 | 
					 | 
				
			||||||
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
 | 
					 | 
				
			||||||
github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
 | 
					 | 
				
			||||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
 | 
					 | 
				
			||||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
 | 
					 | 
				
			||||||
github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
 | 
					 | 
				
			||||||
github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
 | 
					 | 
				
			||||||
github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
 | 
					 | 
				
			||||||
github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
 | 
					 | 
				
			||||||
github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
 | 
					 | 
				
			||||||
github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
 | 
					 | 
				
			||||||
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
 | 
					 | 
				
			||||||
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
 | 
					 | 
				
			||||||
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
 | 
					 | 
				
			||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 | 
					github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 | 
				
			||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 | 
					github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 | 
				
			||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
					github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
				
			||||||
github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o=
 | 
					github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o=
 | 
				
			||||||
github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
					github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
				
			||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 | 
					 | 
				
			||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 | 
					 | 
				
			||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
					 | 
				
			||||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
					github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
				
			||||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
					 | 
				
			||||||
github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
					 | 
				
			||||||
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
					 | 
				
			||||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 | 
					 | 
				
			||||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
 | 
					github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
 | 
				
			||||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
 | 
					github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
 | 
				
			||||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 | 
					github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 | 
				
			||||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 | 
					 | 
				
			||||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
 | 
					github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
 | 
				
			||||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 | 
					github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 | 
				
			||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
					github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
				
			||||||
@@ -273,331 +146,186 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
				
			|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
					github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
				
			||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
					github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
				
			||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
					github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
				
			||||||
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
 | 
					github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 | 
				
			||||||
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
 | 
					github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
 | 
				
			||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
 | 
					github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
				
			||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
					github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
 | 
				
			||||||
 | 
					github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 | 
				
			||||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
 | 
					github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
 | 
				
			||||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
 | 
					github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
 | 
				
			||||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
 | 
					github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
					github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 | 
					 | 
				
			||||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
 | 
					github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 | 
					github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
					 | 
				
			||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
					github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 | 
					 | 
				
			||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
					 | 
				
			||||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 | 
					 | 
				
			||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 | 
					github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 | 
				
			||||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
 | 
					github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
				
			||||||
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
 | 
					github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
 | 
				
			||||||
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
 | 
					github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
				
			||||||
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
 | 
					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=
 | 
				
			||||||
 | 
					github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 | 
				
			||||||
 | 
					github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
				
			||||||
 | 
					github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
 | 
				
			||||||
 | 
					github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
				
			||||||
 | 
					github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 | 
				
			||||||
 | 
					github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
 | 
				
			||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 | 
					github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 | 
				
			||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
					github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
				
			||||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7 h1:xzByj8G8tj0Oq7ZYYU4+ixL/CVb5ruWCm0EZQ1PjOkE=
 | 
					github.com/mholt/archiver/v4 v4.0.0-alpha.7 h1:xzByj8G8tj0Oq7ZYYU4+ixL/CVb5ruWCm0EZQ1PjOkE=
 | 
				
			||||||
github.com/mholt/archiver/v4 v4.0.0-alpha.7/go.mod h1:Fs8qUkO74HHaidabihzYephJH8qmGD/nCP6tE5xC9BM=
 | 
					github.com/mholt/archiver/v4 v4.0.0-alpha.7/go.mod h1:Fs8qUkO74HHaidabihzYephJH8qmGD/nCP6tE5xC9BM=
 | 
				
			||||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
 | 
					github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
 | 
				
			||||||
 | 
					github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
 | 
				
			||||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 | 
					github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 | 
				
			||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 | 
					github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 | 
				
			||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 | 
					github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 | 
				
			||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
					github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
				
			||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
					 | 
				
			||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 | 
					github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 | 
				
			||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 | 
					github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 | 
				
			||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 | 
					github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 | 
				
			||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 | 
					github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 | 
				
			||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
					github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
 | 
				
			||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
					github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
 | 
				
			||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
					github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
 | 
				
			||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
					github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
 | 
				
			||||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
 | 
					github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
 | 
				
			||||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
 | 
					github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
 | 
				
			||||||
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
 | 
					github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
 | 
				
			||||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
 | 
					github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
 | 
				
			||||||
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
 | 
					github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
 | 
				
			||||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
 | 
					github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
 | 
				
			||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 | 
					github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
 | 
				
			||||||
 | 
					github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
 | 
				
			||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 | 
					github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 | 
				
			||||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
 | 
					github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
 | 
				
			||||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
 | 
					github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
 | 
				
			||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 | 
					github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
 | 
				
			||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
					github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
 | 
				
			||||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
					 | 
				
			||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 | 
					 | 
				
			||||||
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
 | 
					 | 
				
			||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 | 
					 | 
				
			||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 | 
					 | 
				
			||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
					 | 
				
			||||||
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/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
 | 
					github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
 | 
				
			||||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
 | 
					github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
 | 
				
			||||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
 | 
					 | 
				
			||||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
 | 
					 | 
				
			||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 | 
					 | 
				
			||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
					github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
				
			||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
					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/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 | 
				
			||||||
 | 
					github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
 | 
				
			||||||
 | 
					github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
					github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
 | 
					github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
 | 
					 | 
				
			||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
					 | 
				
			||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 | 
					github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 | 
				
			||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
					github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
				
			||||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 | 
					github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
 | 
				
			||||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
 | 
					github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8=
 | 
				
			||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
 | 
					github.com/schollz/progressbar/v3 v3.13.0/go.mod h1:ZBYnSuLAX2LU8P8UiKN/KgF2DY58AJC8yfVYLPC8Ly4=
 | 
				
			||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
					 | 
				
			||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 | 
					github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 | 
				
			||||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
 | 
					github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
 | 
				
			||||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 | 
					github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 | 
				
			||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 | 
					 | 
				
			||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
 | 
					github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
 | 
				
			||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
					github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
				
			||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 | 
					github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 | 
				
			||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 | 
					github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 | 
				
			||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
					 | 
				
			||||||
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
 | 
					github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
 | 
				
			||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 | 
					 | 
				
			||||||
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
 | 
					github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
 | 
				
			||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
					 | 
				
			||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
					 | 
				
			||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 | 
					 | 
				
			||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 | 
					 | 
				
			||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
					 | 
				
			||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
					 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
					github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
				
			||||||
 | 
					github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 | 
				
			||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
					github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
				
			||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
					github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
				
			||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
					github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
				
			||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 | 
					 | 
				
			||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
 | 
					 | 
				
			||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 | 
					 | 
				
			||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 | 
					github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 | 
				
			||||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
 | 
					github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
 | 
				
			||||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
 | 
					github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
 | 
				
			||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 | 
					github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
 | 
				
			||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 | 
					github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
 | 
				
			||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
					 | 
				
			||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 | 
					 | 
				
			||||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 | 
					github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 | 
				
			||||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
 | 
					github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
 | 
				
			||||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
					github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
				
			||||||
github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk=
 | 
					github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
 | 
				
			||||||
github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
 | 
					github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
 | 
				
			||||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
 | 
					github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
 | 
				
			||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 | 
					github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
 | 
				
			||||||
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
 | 
					github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
 | 
				
			||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
 | 
					github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
 | 
				
			||||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 | 
					 | 
				
			||||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 | 
					 | 
				
			||||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
 | 
					github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
 | 
				
			||||||
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
 | 
					github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
 | 
				
			||||||
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
 | 
					github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
 | 
				
			||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
 | 
					 | 
				
			||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 | 
					 | 
				
			||||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 | 
					 | 
				
			||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
 | 
					github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
 | 
				
			||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
 | 
					 | 
				
			||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
 | 
					github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
 | 
				
			||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
					github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
 | 
				
			||||||
 | 
					github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
 | 
				
			||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
 | 
					github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
 | 
				
			||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
 | 
					github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
 | 
				
			||||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
 | 
					 | 
				
			||||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
 | 
					 | 
				
			||||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
 | 
					 | 
				
			||||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
 | 
					 | 
				
			||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					 | 
				
			||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					 | 
				
			||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 | 
					 | 
				
			||||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
 | 
					gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
 | 
				
			||||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
 | 
					gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
 | 
				
			||||||
go.arsenm.dev/logger v0.0.0-20221007032343-cbffce4f4334 h1:S98LJOBmj1pAKSw94spJk6+n8ERlBNTxi4lt5B67nQo=
 | 
					go.arsenm.dev/logger v0.0.0-20230126004036-a8cbbe3b6fe6 h1:p5EkGdoRX4ddMJcYQzRPTHk8oVyMv0q8agaAbPr/oak=
 | 
				
			||||||
go.arsenm.dev/logger v0.0.0-20221007032343-cbffce4f4334/go.mod h1:RV2qydKDdoyaRkhAq8JEGvojR8eJ6bjq5WnSIlH7gYw=
 | 
					go.arsenm.dev/logger v0.0.0-20230126004036-a8cbbe3b6fe6/go.mod h1:Sg0thD0AZwMib+fD+YFOgmTWwEOqPbMjSSmWef6mEog=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
					go.arsenm.dev/translate v0.0.0-20230113025904-5ad1ec0ed296 h1:uOJuOOn/sPe4YX9MD98tCoeLQTopIk17dJt0fwCeJrk=
 | 
				
			||||||
 | 
					go.arsenm.dev/translate v0.0.0-20230113025904-5ad1ec0ed296/go.mod h1:+rZV+tkYEPgZyP0OWBH477vWNwxN3pcAcukcjzgQjco=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
					golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 | 
					golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 | 
					golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
					golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
 | 
					golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
					golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					 | 
				
			||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					 | 
				
			||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					 | 
				
			||||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					 | 
				
			||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					 | 
				
			||||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
 | 
					 | 
				
			||||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
 | 
					 | 
				
			||||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4=
 | 
					golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
 | 
					golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
 | 
				
			||||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 | 
					golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
 | 
				
			||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
					golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 | 
				
			||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
					 | 
				
			||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
					 | 
				
			||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
					 | 
				
			||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
					 | 
				
			||||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 | 
					 | 
				
			||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 | 
					 | 
				
			||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 | 
					 | 
				
			||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 | 
					 | 
				
			||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 | 
					 | 
				
			||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					 | 
				
			||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					 | 
				
			||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
					golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
 | 
					golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
					 | 
				
			||||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
 | 
					golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
 | 
				
			||||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 | 
					golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					 | 
				
			||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					 | 
				
			||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					 | 
				
			||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					 | 
				
			||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					 | 
				
			||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					 | 
				
			||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					 | 
				
			||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					 | 
				
			||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
 | 
					golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
 | 
					golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
 | 
				
			||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 | 
					 | 
				
			||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
					golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 | 
				
			||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
 | 
				
			||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
					golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
 | 
				
			||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					 | 
				
			||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					 | 
				
			||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 | 
					 | 
				
			||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
					golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
				
			||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
					 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					 | 
				
			||||||
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
 | 
					 | 
				
			||||||
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
 | 
					 | 
				
			||||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
 | 
					 | 
				
			||||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 | 
					 | 
				
			||||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
 | 
					 | 
				
			||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
					 | 
				
			||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
					 | 
				
			||||||
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
					 | 
				
			||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
					 | 
				
			||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 | 
					 | 
				
			||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 | 
					 | 
				
			||||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
 | 
					 | 
				
			||||||
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 | 
					 | 
				
			||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
					 | 
				
			||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 | 
					 | 
				
			||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 | 
					 | 
				
			||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 | 
					 | 
				
			||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
 | 
					 | 
				
			||||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 | 
					 | 
				
			||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 | 
					google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 | 
				
			||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
					google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
 | 
				
			||||||
 | 
					google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
@@ -605,25 +333,38 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
 | 
				
			|||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
					gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
					gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
				
			||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
					gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
				
			||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
					 | 
				
			||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
 | 
					 | 
				
			||||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
 | 
					 | 
				
			||||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
					 | 
				
			||||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 | 
					 | 
				
			||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
					 | 
				
			||||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 | 
					gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 | 
				
			||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
					gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
					gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
 | 
				
			||||||
 | 
					modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
 | 
				
			||||||
 | 
					modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
 | 
				
			||||||
 | 
					modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
 | 
				
			||||||
 | 
					modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
 | 
				
			||||||
 | 
					modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
 | 
				
			||||||
 | 
					modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
 | 
				
			||||||
 | 
					modernc.org/libc v1.21.5 h1:xBkU9fnHV+hvZuPSRszN0AXDG4M7nwPLwTWwkYcvLCI=
 | 
				
			||||||
 | 
					modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
 | 
				
			||||||
 | 
					modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
 | 
				
			||||||
 | 
					modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
 | 
				
			||||||
 | 
					modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
 | 
				
			||||||
 | 
					modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
 | 
				
			||||||
 | 
					modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
 | 
				
			||||||
 | 
					modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
 | 
				
			||||||
 | 
					modernc.org/sqlite v1.20.0 h1:80zmD3BGkm8BZ5fUi/4lwJQHiO3GXgIUvZRXpoIfROY=
 | 
				
			||||||
 | 
					modernc.org/sqlite v1.20.0/go.mod h1:EsYz8rfOvLCiYTy5ZFsOYzoCcRMu98YYkwAcCw5YIYw=
 | 
				
			||||||
 | 
					modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
 | 
				
			||||||
 | 
					modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
 | 
				
			||||||
 | 
					modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
 | 
				
			||||||
 | 
					modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
 | 
				
			||||||
 | 
					modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
 | 
				
			||||||
 | 
					modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
 | 
				
			||||||
mvdan.cc/sh/v3 v3.5.1 h1:hmP3UOw4f+EYexsJjFxvU38+kn+V/s2CclXHanIBkmQ=
 | 
					mvdan.cc/sh/v3 v3.5.1 h1:hmP3UOw4f+EYexsJjFxvU38+kn+V/s2CclXHanIBkmQ=
 | 
				
			||||||
mvdan.cc/sh/v3 v3.5.1/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=
 | 
					mvdan.cc/sh/v3 v3.5.1/go.mod h1:1JcoyAKm1lZw/2bZje/iYKWicU/KMd0rsyJeKHnsK4E=
 | 
				
			||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								helpers.go
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								helpers.go
									
									
									
									
									
								
							@@ -1,3 +1,21 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@@ -35,6 +53,11 @@ var helpers = shutils.ExecFuncs{
 | 
				
			|||||||
	"git-version":          gitVersionCmd,
 | 
						"git-version":          gitVersionCmd,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// rHelpers contains restricted read-only helpers that don't modify any state
 | 
				
			||||||
 | 
					var rHelpers = shutils.ExecFuncs{
 | 
				
			||||||
 | 
						"git-version": gitVersionCmd,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func installHelperCmd(prefix string, perms os.FileMode) shutils.ExecFunc {
 | 
					func installHelperCmd(prefix string, perms os.FileMode) shutils.ExecFunc {
 | 
				
			||||||
	return func(hc interp.HandlerContext, cmd string, args []string) error {
 | 
						return func(hc interp.HandlerContext, cmd string, args []string) error {
 | 
				
			||||||
		if len(args) < 1 {
 | 
							if len(args) < 1 {
 | 
				
			||||||
@@ -98,7 +121,7 @@ func installCompletionCmd(hc interp.HandlerContext, cmd string, args []string) e
 | 
				
			|||||||
	var prefix string
 | 
						var prefix string
 | 
				
			||||||
	switch shell {
 | 
						switch shell {
 | 
				
			||||||
	case "bash":
 | 
						case "bash":
 | 
				
			||||||
		prefix = "/usr/share/bash-completion/completion"
 | 
							prefix = "/usr/share/bash-completion/completions"
 | 
				
			||||||
	case "zsh":
 | 
						case "zsh":
 | 
				
			||||||
		prefix = "/usr/share/zsh/site-functions"
 | 
							prefix = "/usr/share/zsh/site-functions"
 | 
				
			||||||
		name = "_" + name
 | 
							name = "_" + name
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										45
									
								
								info.go
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								info.go
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -25,6 +25,10 @@ import (
 | 
				
			|||||||
	"go.arsenm.dev/logger/log"
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/urfave/cli/v2"
 | 
						"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"
 | 
						"go.arsenm.dev/lure/internal/repos"
 | 
				
			||||||
	"gopkg.in/yaml.v3"
 | 
						"gopkg.in/yaml.v3"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -35,6 +39,11 @@ func infoCmd(c *cli.Context) error {
 | 
				
			|||||||
		log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
 | 
							log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := repos.Pull(c.Context, gdb, cfg.Repos)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error pulling repositories").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	found, _, err := repos.FindPkgs(gdb, args.Slice())
 | 
						found, _, err := repos.FindPkgs(gdb, args.Slice())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal("Error finding packages").Err(err).Send()
 | 
							log.Fatal("Error finding packages").Err(err).Send()
 | 
				
			||||||
@@ -44,13 +53,39 @@ func infoCmd(c *cli.Context) error {
 | 
				
			|||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pkgs := flattenFoundPkgs(found, "show")
 | 
						pkgs := cliutils.FlattenPkgs(found, "show", translator)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var names []string
 | 
				
			||||||
 | 
						all := c.Bool("all")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !all {
 | 
				
			||||||
 | 
							info, err := distro.ParseOSRelease(c.Context)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatal("Error parsing os-release file").Err(err).Send()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							names, err = overrides.Resolve(
 | 
				
			||||||
 | 
								info,
 | 
				
			||||||
 | 
								overrides.DefaultOpts.
 | 
				
			||||||
 | 
									WithLanguages([]string{config.SystemLang()}),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatal("Error resolving overrides").Err(err).Send()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, pkg := range pkgs {
 | 
						for _, pkg := range pkgs {
 | 
				
			||||||
		err = yaml.NewEncoder(os.Stdout).Encode(pkg)
 | 
							if !all {
 | 
				
			||||||
		if err != nil {
 | 
								err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
 | 
				
			||||||
			log.Fatal("Error encoding script variables").Err(err).Send()
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Fatal("Error encoding script variables").Err(err).Send()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								err = yaml.NewEncoder(os.Stdout).Encode(pkg)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Fatal("Error encoding script variables").Err(err).Send()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fmt.Println("---")
 | 
							fmt.Println("---")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								install.go
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								install.go
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -25,6 +25,7 @@ import (
 | 
				
			|||||||
	"go.arsenm.dev/logger/log"
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/urfave/cli/v2"
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/cliutils"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/config"
 | 
						"go.arsenm.dev/lure/internal/config"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/db"
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/repos"
 | 
						"go.arsenm.dev/lure/internal/repos"
 | 
				
			||||||
@@ -52,11 +53,13 @@ func installCmd(c *cli.Context) error {
 | 
				
			|||||||
		log.Fatal("Error finding packages").Err(err).Send()
 | 
							log.Fatal("Error finding packages").Err(err).Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	installPkgs(c.Context, flattenFoundPkgs(found, "install"), notFound, mgr)
 | 
						installPkgs(c.Context, cliutils.FlattenPkgs(found, "install", translator), notFound, mgr, c.Bool("clean"))
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func installPkgs(ctx context.Context, pkgs []db.Package, notFound []string, mgr manager.Manager) {
 | 
					// 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, clean bool) {
 | 
				
			||||||
	if len(notFound) > 0 {
 | 
						if len(notFound) > 0 {
 | 
				
			||||||
		err := mgr.Install(nil, notFound...)
 | 
							err := mgr.Install(nil, notFound...)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -64,9 +67,11 @@ 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
 | 
				
			||||||
 | 
					// given packages
 | 
				
			||||||
func getScriptPaths(pkgs []db.Package) []string {
 | 
					func getScriptPaths(pkgs []db.Package) []string {
 | 
				
			||||||
	var scripts []string
 | 
						var scripts []string
 | 
				
			||||||
	for _, pkg := range pkgs {
 | 
						for _, pkg := range pkgs {
 | 
				
			||||||
@@ -76,25 +81,10 @@ func getScriptPaths(pkgs []db.Package) []string {
 | 
				
			|||||||
	return scripts
 | 
						return scripts
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func flattenFoundPkgs(found map[string][]db.Package, verb string) []db.Package {
 | 
					// installScripts builds and installs LURE build scripts
 | 
				
			||||||
	var outPkgs []db.Package
 | 
					func installScripts(ctx context.Context, mgr manager.Manager, scripts []string, clean bool) {
 | 
				
			||||||
	for _, pkgs := range found {
 | 
					 | 
				
			||||||
		if len(pkgs) > 1 {
 | 
					 | 
				
			||||||
			choices, err := pkgPrompt(pkgs, verb)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				log.Fatal("Error prompting for choice of package").Send()
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			outPkgs = append(outPkgs, choices...)
 | 
					 | 
				
			||||||
		} else if len(pkgs) == 1 {
 | 
					 | 
				
			||||||
			outPkgs = append(outPkgs, pkgs[0])
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return outPkgs
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func installScripts(ctx context.Context, mgr manager.Manager, scripts []string) {
 | 
					 | 
				
			||||||
	for _, script := range scripts {
 | 
						for _, script := range scripts {
 | 
				
			||||||
		builtPkgs, _, err := buildPackage(ctx, script, mgr)
 | 
							builtPkgs, _, err := buildPackage(ctx, script, mgr, clean)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal("Error building package").Err(err).Send()
 | 
								log.Fatal("Error building package").Err(err).Send()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								internal/api/gen.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								internal/api/gen.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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
 | 
				
			||||||
 | 
					//go:generate protoc --go_out=. lure.proto
 | 
				
			||||||
							
								
								
									
										885
									
								
								internal/api/lure.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										885
									
								
								internal/api/lure.pb.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,885 @@
 | 
				
			|||||||
 | 
					// Code generated by protoc-gen-go. DO NOT EDIT.
 | 
				
			||||||
 | 
					// versions:
 | 
				
			||||||
 | 
					// 	protoc-gen-go v1.28.1
 | 
				
			||||||
 | 
					// 	protoc        v3.21.9
 | 
				
			||||||
 | 
					// source: lure.proto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 | 
				
			||||||
 | 
						protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 | 
				
			||||||
 | 
						reflect "reflect"
 | 
				
			||||||
 | 
						sync "sync"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// Verify that this generated code is sufficiently up-to-date.
 | 
				
			||||||
 | 
						_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
 | 
				
			||||||
 | 
						// Verify that runtime/protoimpl is sufficiently up-to-date.
 | 
				
			||||||
 | 
						_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SORT_BY represents possible things to sort packages by
 | 
				
			||||||
 | 
					type SORT_BY int32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						SORT_BY_UNSORTED   SORT_BY = 0
 | 
				
			||||||
 | 
						SORT_BY_NAME       SORT_BY = 1
 | 
				
			||||||
 | 
						SORT_BY_REPOSITORY SORT_BY = 2
 | 
				
			||||||
 | 
						SORT_BY_VERSION    SORT_BY = 3
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Enum value maps for SORT_BY.
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						SORT_BY_name = map[int32]string{
 | 
				
			||||||
 | 
							0: "UNSORTED",
 | 
				
			||||||
 | 
							1: "NAME",
 | 
				
			||||||
 | 
							2: "REPOSITORY",
 | 
				
			||||||
 | 
							3: "VERSION",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						SORT_BY_value = map[string]int32{
 | 
				
			||||||
 | 
							"UNSORTED":   0,
 | 
				
			||||||
 | 
							"NAME":       1,
 | 
				
			||||||
 | 
							"REPOSITORY": 2,
 | 
				
			||||||
 | 
							"VERSION":    3,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x SORT_BY) Enum() *SORT_BY {
 | 
				
			||||||
 | 
						p := new(SORT_BY)
 | 
				
			||||||
 | 
						*p = x
 | 
				
			||||||
 | 
						return p
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x SORT_BY) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (SORT_BY) Descriptor() protoreflect.EnumDescriptor {
 | 
				
			||||||
 | 
						return file_lure_proto_enumTypes[0].Descriptor()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (SORT_BY) Type() protoreflect.EnumType {
 | 
				
			||||||
 | 
						return &file_lure_proto_enumTypes[0]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x SORT_BY) Number() protoreflect.EnumNumber {
 | 
				
			||||||
 | 
						return protoreflect.EnumNumber(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use SORT_BY.Descriptor instead.
 | 
				
			||||||
 | 
					func (SORT_BY) EnumDescriptor() ([]byte, []int) {
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescGZIP(), []int{0}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FILTER_TYPE represents possible filters for packages
 | 
				
			||||||
 | 
					type FILTER_TYPE int32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						FILTER_TYPE_NO_FILTER     FILTER_TYPE = 0
 | 
				
			||||||
 | 
						FILTER_TYPE_IN_REPOSITORY FILTER_TYPE = 1
 | 
				
			||||||
 | 
						FILTER_TYPE_SUPPORTS_ARCH FILTER_TYPE = 2
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Enum value maps for FILTER_TYPE.
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						FILTER_TYPE_name = map[int32]string{
 | 
				
			||||||
 | 
							0: "NO_FILTER",
 | 
				
			||||||
 | 
							1: "IN_REPOSITORY",
 | 
				
			||||||
 | 
							2: "SUPPORTS_ARCH",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						FILTER_TYPE_value = map[string]int32{
 | 
				
			||||||
 | 
							"NO_FILTER":     0,
 | 
				
			||||||
 | 
							"IN_REPOSITORY": 1,
 | 
				
			||||||
 | 
							"SUPPORTS_ARCH": 2,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x FILTER_TYPE) Enum() *FILTER_TYPE {
 | 
				
			||||||
 | 
						p := new(FILTER_TYPE)
 | 
				
			||||||
 | 
						*p = x
 | 
				
			||||||
 | 
						return p
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x FILTER_TYPE) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (FILTER_TYPE) Descriptor() protoreflect.EnumDescriptor {
 | 
				
			||||||
 | 
						return file_lure_proto_enumTypes[1].Descriptor()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (FILTER_TYPE) Type() protoreflect.EnumType {
 | 
				
			||||||
 | 
						return &file_lure_proto_enumTypes[1]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x FILTER_TYPE) Number() protoreflect.EnumNumber {
 | 
				
			||||||
 | 
						return protoreflect.EnumNumber(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use FILTER_TYPE.Descriptor instead.
 | 
				
			||||||
 | 
					func (FILTER_TYPE) EnumDescriptor() ([]byte, []int) {
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescGZIP(), []int{1}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchRequest is a request to search for packages
 | 
				
			||||||
 | 
					type SearchRequest struct {
 | 
				
			||||||
 | 
						state         protoimpl.MessageState
 | 
				
			||||||
 | 
						sizeCache     protoimpl.SizeCache
 | 
				
			||||||
 | 
						unknownFields protoimpl.UnknownFields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Query       string      `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`
 | 
				
			||||||
 | 
						Limit       int64       `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
 | 
				
			||||||
 | 
						SortBy      SORT_BY     `protobuf:"varint,3,opt,name=sort_by,json=sortBy,proto3,enum=lure.SORT_BY" json:"sort_by,omitempty"`
 | 
				
			||||||
 | 
						FilterType  FILTER_TYPE `protobuf:"varint,4,opt,name=filter_type,json=filterType,proto3,enum=lure.FILTER_TYPE" json:"filter_type,omitempty"`
 | 
				
			||||||
 | 
						FilterValue *string     `protobuf:"bytes,5,opt,name=filter_value,json=filterValue,proto3,oneof" json:"filter_value,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchRequest) Reset() {
 | 
				
			||||||
 | 
						*x = SearchRequest{}
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							mi := &file_lure_proto_msgTypes[0]
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchRequest) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.MessageStringOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*SearchRequest) ProtoMessage() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchRequest) ProtoReflect() protoreflect.Message {
 | 
				
			||||||
 | 
						mi := &file_lure_proto_msgTypes[0]
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled && x != nil {
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							if ms.LoadMessageInfo() == nil {
 | 
				
			||||||
 | 
								ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ms
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mi.MessageOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead.
 | 
				
			||||||
 | 
					func (*SearchRequest) Descriptor() ([]byte, []int) {
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescGZIP(), []int{0}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchRequest) GetQuery() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Query
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchRequest) GetLimit() int64 {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Limit
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchRequest) GetSortBy() SORT_BY {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.SortBy
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return SORT_BY_UNSORTED
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchRequest) GetFilterType() FILTER_TYPE {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.FilterType
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return FILTER_TYPE_NO_FILTER
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchRequest) GetFilterValue() string {
 | 
				
			||||||
 | 
						if x != nil && x.FilterValue != nil {
 | 
				
			||||||
 | 
							return *x.FilterValue
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StringList contains a list of strings
 | 
				
			||||||
 | 
					type StringList struct {
 | 
				
			||||||
 | 
						state         protoimpl.MessageState
 | 
				
			||||||
 | 
						sizeCache     protoimpl.SizeCache
 | 
				
			||||||
 | 
						unknownFields protoimpl.UnknownFields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Entries []string `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *StringList) Reset() {
 | 
				
			||||||
 | 
						*x = StringList{}
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							mi := &file_lure_proto_msgTypes[1]
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *StringList) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.MessageStringOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*StringList) ProtoMessage() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *StringList) ProtoReflect() protoreflect.Message {
 | 
				
			||||||
 | 
						mi := &file_lure_proto_msgTypes[1]
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled && x != nil {
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							if ms.LoadMessageInfo() == nil {
 | 
				
			||||||
 | 
								ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ms
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mi.MessageOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use StringList.ProtoReflect.Descriptor instead.
 | 
				
			||||||
 | 
					func (*StringList) Descriptor() ([]byte, []int) {
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescGZIP(), []int{1}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *StringList) GetEntries() []string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Entries
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package represents a LURE package
 | 
				
			||||||
 | 
					type Package struct {
 | 
				
			||||||
 | 
						state         protoimpl.MessageState
 | 
				
			||||||
 | 
						sizeCache     protoimpl.SizeCache
 | 
				
			||||||
 | 
						unknownFields protoimpl.UnknownFields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Name          string                 `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 | 
				
			||||||
 | 
						Repository    string                 `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"`
 | 
				
			||||||
 | 
						Version       string                 `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
 | 
				
			||||||
 | 
						Release       int64                  `protobuf:"varint,4,opt,name=release,proto3" json:"release,omitempty"`
 | 
				
			||||||
 | 
						Epoch         *int64                 `protobuf:"varint,5,opt,name=epoch,proto3,oneof" json:"epoch,omitempty"`
 | 
				
			||||||
 | 
						Description   *string                `protobuf:"bytes,6,opt,name=description,proto3,oneof" json:"description,omitempty"`
 | 
				
			||||||
 | 
						Homepage      *string                `protobuf:"bytes,7,opt,name=homepage,proto3,oneof" json:"homepage,omitempty"`
 | 
				
			||||||
 | 
						Maintainer    *string                `protobuf:"bytes,8,opt,name=maintainer,proto3,oneof" json:"maintainer,omitempty"`
 | 
				
			||||||
 | 
						Architectures []string               `protobuf:"bytes,9,rep,name=architectures,proto3" json:"architectures,omitempty"`
 | 
				
			||||||
 | 
						Licenses      []string               `protobuf:"bytes,10,rep,name=licenses,proto3" json:"licenses,omitempty"`
 | 
				
			||||||
 | 
						Provides      []string               `protobuf:"bytes,11,rep,name=provides,proto3" json:"provides,omitempty"`
 | 
				
			||||||
 | 
						Conflicts     []string               `protobuf:"bytes,12,rep,name=conflicts,proto3" json:"conflicts,omitempty"`
 | 
				
			||||||
 | 
						Replaces      []string               `protobuf:"bytes,13,rep,name=replaces,proto3" json:"replaces,omitempty"`
 | 
				
			||||||
 | 
						Depends       map[string]*StringList `protobuf:"bytes,14,rep,name=depends,proto3" json:"depends,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 | 
				
			||||||
 | 
						BuildDepends  map[string]*StringList `protobuf:"bytes,15,rep,name=build_depends,json=buildDepends,proto3" json:"build_depends,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) Reset() {
 | 
				
			||||||
 | 
						*x = Package{}
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							mi := &file_lure_proto_msgTypes[2]
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.MessageStringOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*Package) ProtoMessage() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) ProtoReflect() protoreflect.Message {
 | 
				
			||||||
 | 
						mi := &file_lure_proto_msgTypes[2]
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled && x != nil {
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							if ms.LoadMessageInfo() == nil {
 | 
				
			||||||
 | 
								ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ms
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mi.MessageOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use Package.ProtoReflect.Descriptor instead.
 | 
				
			||||||
 | 
					func (*Package) Descriptor() ([]byte, []int) {
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescGZIP(), []int{2}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetName() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetRepository() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Repository
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetVersion() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Version
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetRelease() int64 {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Release
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetEpoch() int64 {
 | 
				
			||||||
 | 
						if x != nil && x.Epoch != nil {
 | 
				
			||||||
 | 
							return *x.Epoch
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetDescription() string {
 | 
				
			||||||
 | 
						if x != nil && x.Description != nil {
 | 
				
			||||||
 | 
							return *x.Description
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetHomepage() string {
 | 
				
			||||||
 | 
						if x != nil && x.Homepage != nil {
 | 
				
			||||||
 | 
							return *x.Homepage
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetMaintainer() string {
 | 
				
			||||||
 | 
						if x != nil && x.Maintainer != nil {
 | 
				
			||||||
 | 
							return *x.Maintainer
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetArchitectures() []string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Architectures
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetLicenses() []string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Licenses
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetProvides() []string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Provides
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetConflicts() []string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Conflicts
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetReplaces() []string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Replaces
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetDepends() map[string]*StringList {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Depends
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *Package) GetBuildDepends() map[string]*StringList {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.BuildDepends
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GetPackageRequest struct {
 | 
				
			||||||
 | 
						state         protoimpl.MessageState
 | 
				
			||||||
 | 
						sizeCache     protoimpl.SizeCache
 | 
				
			||||||
 | 
						unknownFields protoimpl.UnknownFields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Name       string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 | 
				
			||||||
 | 
						Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetPackageRequest) Reset() {
 | 
				
			||||||
 | 
						*x = GetPackageRequest{}
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							mi := &file_lure_proto_msgTypes[3]
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetPackageRequest) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.MessageStringOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*GetPackageRequest) ProtoMessage() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetPackageRequest) ProtoReflect() protoreflect.Message {
 | 
				
			||||||
 | 
						mi := &file_lure_proto_msgTypes[3]
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled && x != nil {
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							if ms.LoadMessageInfo() == nil {
 | 
				
			||||||
 | 
								ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ms
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mi.MessageOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use GetPackageRequest.ProtoReflect.Descriptor instead.
 | 
				
			||||||
 | 
					func (*GetPackageRequest) Descriptor() ([]byte, []int) {
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescGZIP(), []int{3}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetPackageRequest) GetName() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetPackageRequest) GetRepository() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Repository
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchResponse contains returned packages
 | 
				
			||||||
 | 
					type SearchResponse struct {
 | 
				
			||||||
 | 
						state         protoimpl.MessageState
 | 
				
			||||||
 | 
						sizeCache     protoimpl.SizeCache
 | 
				
			||||||
 | 
						unknownFields protoimpl.UnknownFields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Packages []*Package `protobuf:"bytes,1,rep,name=packages,proto3" json:"packages,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchResponse) Reset() {
 | 
				
			||||||
 | 
						*x = SearchResponse{}
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							mi := &file_lure_proto_msgTypes[4]
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchResponse) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.MessageStringOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*SearchResponse) ProtoMessage() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchResponse) ProtoReflect() protoreflect.Message {
 | 
				
			||||||
 | 
						mi := &file_lure_proto_msgTypes[4]
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled && x != nil {
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							if ms.LoadMessageInfo() == nil {
 | 
				
			||||||
 | 
								ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ms
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mi.MessageOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use SearchResponse.ProtoReflect.Descriptor instead.
 | 
				
			||||||
 | 
					func (*SearchResponse) Descriptor() ([]byte, []int) {
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescGZIP(), []int{4}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *SearchResponse) GetPackages() []*Package {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Packages
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GetBuildScriptRequest struct {
 | 
				
			||||||
 | 
						state         protoimpl.MessageState
 | 
				
			||||||
 | 
						sizeCache     protoimpl.SizeCache
 | 
				
			||||||
 | 
						unknownFields protoimpl.UnknownFields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Name       string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 | 
				
			||||||
 | 
						Repository string `protobuf:"bytes,2,opt,name=repository,proto3" json:"repository,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetBuildScriptRequest) Reset() {
 | 
				
			||||||
 | 
						*x = GetBuildScriptRequest{}
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							mi := &file_lure_proto_msgTypes[5]
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetBuildScriptRequest) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.MessageStringOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*GetBuildScriptRequest) ProtoMessage() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetBuildScriptRequest) ProtoReflect() protoreflect.Message {
 | 
				
			||||||
 | 
						mi := &file_lure_proto_msgTypes[5]
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled && x != nil {
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							if ms.LoadMessageInfo() == nil {
 | 
				
			||||||
 | 
								ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ms
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mi.MessageOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use GetBuildScriptRequest.ProtoReflect.Descriptor instead.
 | 
				
			||||||
 | 
					func (*GetBuildScriptRequest) Descriptor() ([]byte, []int) {
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescGZIP(), []int{5}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetBuildScriptRequest) GetName() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetBuildScriptRequest) GetRepository() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Repository
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GetBuildScriptResponse struct {
 | 
				
			||||||
 | 
						state         protoimpl.MessageState
 | 
				
			||||||
 | 
						sizeCache     protoimpl.SizeCache
 | 
				
			||||||
 | 
						unknownFields protoimpl.UnknownFields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Script string `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetBuildScriptResponse) Reset() {
 | 
				
			||||||
 | 
						*x = GetBuildScriptResponse{}
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							mi := &file_lure_proto_msgTypes[6]
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetBuildScriptResponse) String() string {
 | 
				
			||||||
 | 
						return protoimpl.X.MessageStringOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (*GetBuildScriptResponse) ProtoMessage() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetBuildScriptResponse) ProtoReflect() protoreflect.Message {
 | 
				
			||||||
 | 
						mi := &file_lure_proto_msgTypes[6]
 | 
				
			||||||
 | 
						if protoimpl.UnsafeEnabled && x != nil {
 | 
				
			||||||
 | 
							ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 | 
				
			||||||
 | 
							if ms.LoadMessageInfo() == nil {
 | 
				
			||||||
 | 
								ms.StoreMessageInfo(mi)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ms
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return mi.MessageOf(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Deprecated: Use GetBuildScriptResponse.ProtoReflect.Descriptor instead.
 | 
				
			||||||
 | 
					func (*GetBuildScriptResponse) Descriptor() ([]byte, []int) {
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescGZIP(), []int{6}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (x *GetBuildScriptResponse) GetScript() string {
 | 
				
			||||||
 | 
						if x != nil {
 | 
				
			||||||
 | 
							return x.Script
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var File_lure_proto protoreflect.FileDescriptor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var file_lure_proto_rawDesc = []byte{
 | 
				
			||||||
 | 
						0x0a, 0x0a, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6c, 0x75,
 | 
				
			||||||
 | 
						0x72, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
 | 
				
			||||||
 | 
						0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20,
 | 
				
			||||||
 | 
						0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69,
 | 
				
			||||||
 | 
						0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74,
 | 
				
			||||||
 | 
						0x12, 0x26, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28,
 | 
				
			||||||
 | 
						0x0e, 0x32, 0x0d, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x59,
 | 
				
			||||||
 | 
						0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x32, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x74,
 | 
				
			||||||
 | 
						0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e,
 | 
				
			||||||
 | 
						0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45,
 | 
				
			||||||
 | 
						0x52, 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0c,
 | 
				
			||||||
 | 
						0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01,
 | 
				
			||||||
 | 
						0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75,
 | 
				
			||||||
 | 
						0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f,
 | 
				
			||||||
 | 
						0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c,
 | 
				
			||||||
 | 
						0x69, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01,
 | 
				
			||||||
 | 
						0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xe4, 0x05,
 | 
				
			||||||
 | 
						0x0a, 0x07, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
 | 
				
			||||||
 | 
						0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a,
 | 
				
			||||||
 | 
						0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
 | 
				
			||||||
 | 
						0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x0a,
 | 
				
			||||||
 | 
						0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
 | 
				
			||||||
 | 
						0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61,
 | 
				
			||||||
 | 
						0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
 | 
				
			||||||
 | 
						0x65, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03,
 | 
				
			||||||
 | 
						0x48, 0x00, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b,
 | 
				
			||||||
 | 
						0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28,
 | 
				
			||||||
 | 
						0x09, 0x48, 0x01, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
 | 
				
			||||||
 | 
						0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x68, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67, 0x65, 0x18,
 | 
				
			||||||
 | 
						0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x08, 0x68, 0x6f, 0x6d, 0x65, 0x70, 0x61, 0x67,
 | 
				
			||||||
 | 
						0x65, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e,
 | 
				
			||||||
 | 
						0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x69, 0x6e,
 | 
				
			||||||
 | 
						0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x72, 0x63,
 | 
				
			||||||
 | 
						0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09,
 | 
				
			||||||
 | 
						0x52, 0x0d, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12,
 | 
				
			||||||
 | 
						0x1a, 0x0a, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28,
 | 
				
			||||||
 | 
						0x09, 0x52, 0x08, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70,
 | 
				
			||||||
 | 
						0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70,
 | 
				
			||||||
 | 
						0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x6c,
 | 
				
			||||||
 | 
						0x69, 0x63, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66,
 | 
				
			||||||
 | 
						0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65,
 | 
				
			||||||
 | 
						0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65,
 | 
				
			||||||
 | 
						0x73, 0x12, 0x34, 0x0a, 0x07, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03,
 | 
				
			||||||
 | 
						0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67,
 | 
				
			||||||
 | 
						0x65, 0x2e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07,
 | 
				
			||||||
 | 
						0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64,
 | 
				
			||||||
 | 
						0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f,
 | 
				
			||||||
 | 
						0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75,
 | 
				
			||||||
 | 
						0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
 | 
				
			||||||
 | 
						0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x1a, 0x4c, 0x0a,
 | 
				
			||||||
 | 
						0x0c, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
 | 
				
			||||||
 | 
						0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
 | 
				
			||||||
 | 
						0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10,
 | 
				
			||||||
 | 
						0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74,
 | 
				
			||||||
 | 
						0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x51, 0x0a, 0x11, 0x42,
 | 
				
			||||||
 | 
						0x75, 0x69, 0x6c, 0x64, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
 | 
				
			||||||
 | 
						0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
 | 
				
			||||||
 | 
						0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
 | 
				
			||||||
 | 
						0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c,
 | 
				
			||||||
 | 
						0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x08,
 | 
				
			||||||
 | 
						0x0a, 0x06, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73,
 | 
				
			||||||
 | 
						0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x68, 0x6f, 0x6d,
 | 
				
			||||||
 | 
						0x65, 0x70, 0x61, 0x67, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61,
 | 
				
			||||||
 | 
						0x69, 0x6e, 0x65, 0x72, 0x22, 0x47, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x61, 0x63, 0x6b, 0x61,
 | 
				
			||||||
 | 
						0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
 | 
				
			||||||
 | 
						0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a,
 | 
				
			||||||
 | 
						0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
 | 
				
			||||||
 | 
						0x09, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x3b, 0x0a,
 | 
				
			||||||
 | 
						0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
 | 
				
			||||||
 | 
						0x29, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
 | 
				
			||||||
 | 
						0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65,
 | 
				
			||||||
 | 
						0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x22, 0x4b, 0x0a, 0x15, 0x47, 0x65,
 | 
				
			||||||
 | 
						0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75,
 | 
				
			||||||
 | 
						0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
 | 
				
			||||||
 | 
						0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73,
 | 
				
			||||||
 | 
						0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x70,
 | 
				
			||||||
 | 
						0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x30, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x42, 0x75,
 | 
				
			||||||
 | 
						0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
 | 
				
			||||||
 | 
						0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
 | 
				
			||||||
 | 
						0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2a, 0x3e, 0x0a, 0x07, 0x53, 0x4f, 0x52,
 | 
				
			||||||
 | 
						0x54, 0x5f, 0x42, 0x59, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x4e, 0x53, 0x4f, 0x52, 0x54, 0x45, 0x44,
 | 
				
			||||||
 | 
						0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a,
 | 
				
			||||||
 | 
						0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07,
 | 
				
			||||||
 | 
						0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0b, 0x46, 0x49, 0x4c,
 | 
				
			||||||
 | 
						0x54, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x46,
 | 
				
			||||||
 | 
						0x49, 0x4c, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x5f, 0x52, 0x45,
 | 
				
			||||||
 | 
						0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x55,
 | 
				
			||||||
 | 
						0x50, 0x50, 0x4f, 0x52, 0x54, 0x53, 0x5f, 0x41, 0x52, 0x43, 0x48, 0x10, 0x02, 0x32, 0xb9, 0x01,
 | 
				
			||||||
 | 
						0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x33, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12,
 | 
				
			||||||
 | 
						0x13, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
 | 
				
			||||||
 | 
						0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x61, 0x72,
 | 
				
			||||||
 | 
						0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x47, 0x65,
 | 
				
			||||||
 | 
						0x74, 0x50, 0x6b, 0x67, 0x12, 0x17, 0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x50,
 | 
				
			||||||
 | 
						0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e,
 | 
				
			||||||
 | 
						0x6c, 0x75, 0x72, 0x65, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x4b, 0x0a, 0x0e,
 | 
				
			||||||
 | 
						0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1b,
 | 
				
			||||||
 | 
						0x2e, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63,
 | 
				
			||||||
 | 
						0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x75,
 | 
				
			||||||
 | 
						0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70,
 | 
				
			||||||
 | 
						0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2e, 0x2f,
 | 
				
			||||||
 | 
						0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						file_lure_proto_rawDescOnce sync.Once
 | 
				
			||||||
 | 
						file_lure_proto_rawDescData = file_lure_proto_rawDesc
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func file_lure_proto_rawDescGZIP() []byte {
 | 
				
			||||||
 | 
						file_lure_proto_rawDescOnce.Do(func() {
 | 
				
			||||||
 | 
							file_lure_proto_rawDescData = protoimpl.X.CompressGZIP(file_lure_proto_rawDescData)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return file_lure_proto_rawDescData
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var file_lure_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
 | 
				
			||||||
 | 
					var file_lure_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
 | 
				
			||||||
 | 
					var file_lure_proto_goTypes = []interface{}{
 | 
				
			||||||
 | 
						(SORT_BY)(0),                   // 0: lure.SORT_BY
 | 
				
			||||||
 | 
						(FILTER_TYPE)(0),               // 1: lure.FILTER_TYPE
 | 
				
			||||||
 | 
						(*SearchRequest)(nil),          // 2: lure.SearchRequest
 | 
				
			||||||
 | 
						(*StringList)(nil),             // 3: lure.StringList
 | 
				
			||||||
 | 
						(*Package)(nil),                // 4: lure.Package
 | 
				
			||||||
 | 
						(*GetPackageRequest)(nil),      // 5: lure.GetPackageRequest
 | 
				
			||||||
 | 
						(*SearchResponse)(nil),         // 6: lure.SearchResponse
 | 
				
			||||||
 | 
						(*GetBuildScriptRequest)(nil),  // 7: lure.GetBuildScriptRequest
 | 
				
			||||||
 | 
						(*GetBuildScriptResponse)(nil), // 8: lure.GetBuildScriptResponse
 | 
				
			||||||
 | 
						nil,                            // 9: lure.Package.DependsEntry
 | 
				
			||||||
 | 
						nil,                            // 10: lure.Package.BuildDependsEntry
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					var file_lure_proto_depIdxs = []int32{
 | 
				
			||||||
 | 
						0,  // 0: lure.SearchRequest.sort_by:type_name -> lure.SORT_BY
 | 
				
			||||||
 | 
						1,  // 1: lure.SearchRequest.filter_type:type_name -> lure.FILTER_TYPE
 | 
				
			||||||
 | 
						9,  // 2: lure.Package.depends:type_name -> lure.Package.DependsEntry
 | 
				
			||||||
 | 
						10, // 3: lure.Package.build_depends:type_name -> lure.Package.BuildDependsEntry
 | 
				
			||||||
 | 
						4,  // 4: lure.SearchResponse.packages:type_name -> lure.Package
 | 
				
			||||||
 | 
						3,  // 5: lure.Package.DependsEntry.value:type_name -> lure.StringList
 | 
				
			||||||
 | 
						3,  // 6: lure.Package.BuildDependsEntry.value:type_name -> lure.StringList
 | 
				
			||||||
 | 
						2,  // 7: lure.API.Search:input_type -> lure.SearchRequest
 | 
				
			||||||
 | 
						5,  // 8: lure.API.GetPkg:input_type -> lure.GetPackageRequest
 | 
				
			||||||
 | 
						7,  // 9: lure.API.GetBuildScript:input_type -> lure.GetBuildScriptRequest
 | 
				
			||||||
 | 
						6,  // 10: lure.API.Search:output_type -> lure.SearchResponse
 | 
				
			||||||
 | 
						4,  // 11: lure.API.GetPkg:output_type -> lure.Package
 | 
				
			||||||
 | 
						8,  // 12: lure.API.GetBuildScript:output_type -> lure.GetBuildScriptResponse
 | 
				
			||||||
 | 
						10, // [10:13] is the sub-list for method output_type
 | 
				
			||||||
 | 
						7,  // [7:10] is the sub-list for method input_type
 | 
				
			||||||
 | 
						7,  // [7:7] is the sub-list for extension type_name
 | 
				
			||||||
 | 
						7,  // [7:7] is the sub-list for extension extendee
 | 
				
			||||||
 | 
						0,  // [0:7] is the sub-list for field type_name
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() { file_lure_proto_init() }
 | 
				
			||||||
 | 
					func file_lure_proto_init() {
 | 
				
			||||||
 | 
						if File_lure_proto != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !protoimpl.UnsafeEnabled {
 | 
				
			||||||
 | 
							file_lure_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
 | 
				
			||||||
 | 
								switch v := v.(*SearchRequest); i {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									return &v.state
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									return &v.sizeCache
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									return &v.unknownFields
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							file_lure_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 | 
				
			||||||
 | 
								switch v := v.(*StringList); i {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									return &v.state
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									return &v.sizeCache
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									return &v.unknownFields
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							file_lure_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
 | 
				
			||||||
 | 
								switch v := v.(*Package); i {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									return &v.state
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									return &v.sizeCache
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									return &v.unknownFields
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							file_lure_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
 | 
				
			||||||
 | 
								switch v := v.(*GetPackageRequest); i {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									return &v.state
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									return &v.sizeCache
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									return &v.unknownFields
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							file_lure_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
 | 
				
			||||||
 | 
								switch v := v.(*SearchResponse); i {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									return &v.state
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									return &v.sizeCache
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									return &v.unknownFields
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							file_lure_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
 | 
				
			||||||
 | 
								switch v := v.(*GetBuildScriptRequest); i {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									return &v.state
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									return &v.sizeCache
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									return &v.unknownFields
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							file_lure_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
 | 
				
			||||||
 | 
								switch v := v.(*GetBuildScriptResponse); i {
 | 
				
			||||||
 | 
								case 0:
 | 
				
			||||||
 | 
									return &v.state
 | 
				
			||||||
 | 
								case 1:
 | 
				
			||||||
 | 
									return &v.sizeCache
 | 
				
			||||||
 | 
								case 2:
 | 
				
			||||||
 | 
									return &v.unknownFields
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						file_lure_proto_msgTypes[0].OneofWrappers = []interface{}{}
 | 
				
			||||||
 | 
						file_lure_proto_msgTypes[2].OneofWrappers = []interface{}{}
 | 
				
			||||||
 | 
						type x struct{}
 | 
				
			||||||
 | 
						out := protoimpl.TypeBuilder{
 | 
				
			||||||
 | 
							File: protoimpl.DescBuilder{
 | 
				
			||||||
 | 
								GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 | 
				
			||||||
 | 
								RawDescriptor: file_lure_proto_rawDesc,
 | 
				
			||||||
 | 
								NumEnums:      2,
 | 
				
			||||||
 | 
								NumMessages:   9,
 | 
				
			||||||
 | 
								NumExtensions: 0,
 | 
				
			||||||
 | 
								NumServices:   1,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							GoTypes:           file_lure_proto_goTypes,
 | 
				
			||||||
 | 
							DependencyIndexes: file_lure_proto_depIdxs,
 | 
				
			||||||
 | 
							EnumInfos:         file_lure_proto_enumTypes,
 | 
				
			||||||
 | 
							MessageInfos:      file_lure_proto_msgTypes,
 | 
				
			||||||
 | 
						}.Build()
 | 
				
			||||||
 | 
						File_lure_proto = out.File
 | 
				
			||||||
 | 
						file_lure_proto_rawDesc = nil
 | 
				
			||||||
 | 
						file_lure_proto_goTypes = nil
 | 
				
			||||||
 | 
						file_lure_proto_depIdxs = nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										82
									
								
								internal/api/lure.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								internal/api/lure.proto
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					syntax = "proto3";
 | 
				
			||||||
 | 
					package lure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Slight hack to provide protoc with a package name
 | 
				
			||||||
 | 
					option go_package = "../api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SORT_BY represents possible things to sort packages by
 | 
				
			||||||
 | 
					enum SORT_BY {
 | 
				
			||||||
 | 
					    UNSORTED = 0;
 | 
				
			||||||
 | 
					    NAME = 1;
 | 
				
			||||||
 | 
					    REPOSITORY = 2;
 | 
				
			||||||
 | 
					    VERSION = 3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FILTER_TYPE represents possible filters for packages
 | 
				
			||||||
 | 
					enum FILTER_TYPE {
 | 
				
			||||||
 | 
					    NO_FILTER = 0;
 | 
				
			||||||
 | 
					    IN_REPOSITORY = 1;
 | 
				
			||||||
 | 
					    SUPPORTS_ARCH = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchRequest is a request to search for packages
 | 
				
			||||||
 | 
					message SearchRequest {
 | 
				
			||||||
 | 
					    string query = 1;
 | 
				
			||||||
 | 
					    int64 limit = 2;
 | 
				
			||||||
 | 
					    SORT_BY sort_by = 3;
 | 
				
			||||||
 | 
					    FILTER_TYPE filter_type = 4;
 | 
				
			||||||
 | 
					    optional string filter_value = 5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StringList contains a list of strings
 | 
				
			||||||
 | 
					message StringList {
 | 
				
			||||||
 | 
					    repeated string entries = 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package represents a LURE package
 | 
				
			||||||
 | 
					message Package {
 | 
				
			||||||
 | 
					    string name = 1;
 | 
				
			||||||
 | 
					    string repository = 2;
 | 
				
			||||||
 | 
					    string version = 3;
 | 
				
			||||||
 | 
					    int64 release = 4;
 | 
				
			||||||
 | 
					    optional int64 epoch = 5;
 | 
				
			||||||
 | 
					    optional string description = 6;
 | 
				
			||||||
 | 
					    optional string homepage = 7;
 | 
				
			||||||
 | 
					    optional string maintainer = 8;
 | 
				
			||||||
 | 
					    repeated string architectures = 9;
 | 
				
			||||||
 | 
					    repeated string licenses = 10;
 | 
				
			||||||
 | 
					    repeated string provides = 11;
 | 
				
			||||||
 | 
					    repeated string conflicts = 12;
 | 
				
			||||||
 | 
					    repeated string replaces = 13;
 | 
				
			||||||
 | 
					    map<string, StringList> depends = 14;
 | 
				
			||||||
 | 
					    map<string, StringList> build_depends = 15;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message GetPackageRequest {
 | 
				
			||||||
 | 
					    string name = 1;
 | 
				
			||||||
 | 
					    string repository = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchResponse contains returned packages
 | 
				
			||||||
 | 
					message SearchResponse {
 | 
				
			||||||
 | 
					    repeated Package packages = 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message GetBuildScriptRequest {
 | 
				
			||||||
 | 
					    string name = 1;
 | 
				
			||||||
 | 
					    string repository = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message GetBuildScriptResponse {
 | 
				
			||||||
 | 
					    string script = 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Web is the LURE Web service
 | 
				
			||||||
 | 
					service API {
 | 
				
			||||||
 | 
					    // Search searches through LURE packages in the database
 | 
				
			||||||
 | 
					    rpc Search(SearchRequest) returns (SearchResponse);
 | 
				
			||||||
 | 
					    // GetPkg gets a single LURE package from the database
 | 
				
			||||||
 | 
					    rpc GetPkg(GetPackageRequest) returns (Package);
 | 
				
			||||||
 | 
					    // GetBuildScript returns the build script for the given package
 | 
				
			||||||
 | 
					    rpc GetBuildScript(GetBuildScriptRequest) returns (GetBuildScriptResponse);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1703
									
								
								internal/api/lure.twirp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1703
									
								
								internal/api/lure.twirp.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										134
									
								
								internal/cliutils/prompt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								internal/cliutils/prompt.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"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, t translate.Translator) (bool, error) {
 | 
				
			||||||
 | 
						var answer bool
 | 
				
			||||||
 | 
						err := survey.AskOne(
 | 
				
			||||||
 | 
							&survey.Confirm{
 | 
				
			||||||
 | 
								Message: t.TranslateTo(msg, config.Language),
 | 
				
			||||||
 | 
								Default: def,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							&answer,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						return answer, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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, 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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if view {
 | 
				
			||||||
 | 
							err = ShowScript(script, name, style)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cont, err := YesNoPrompt("Would you still like to continue?", false, t)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !cont {
 | 
				
			||||||
 | 
								log.Fatal(t.TranslateTo("User chose not to continue after reading script", config.Language)).Send()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ShowScript uses the built-in pager to display a script at a
 | 
				
			||||||
 | 
					// given path, in the given syntax highlighting style.
 | 
				
			||||||
 | 
					func ShowScript(path, name, style string) error {
 | 
				
			||||||
 | 
						scriptFl, err := os.Open(path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer scriptFl.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						str, err := pager.SyntaxHighlightBash(scriptFl, style)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pgr := pager.New(name, str)
 | 
				
			||||||
 | 
						return pgr.Run()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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, t translate.Translator) []db.Package {
 | 
				
			||||||
 | 
						var outPkgs []db.Package
 | 
				
			||||||
 | 
						for _, pkgs := range found {
 | 
				
			||||||
 | 
							if len(pkgs) > 1 {
 | 
				
			||||||
 | 
								choices, err := PkgPrompt(pkgs, verb, t)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									log.Fatal("Error prompting for choice of package").Send()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								outPkgs = append(outPkgs, choices...)
 | 
				
			||||||
 | 
							} else if len(pkgs) == 1 {
 | 
				
			||||||
 | 
								outPkgs = append(outPkgs, pkgs[0])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return outPkgs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PkgPrompt asks the user to choose between multiple packages.
 | 
				
			||||||
 | 
					// The user may choose multiple packages.
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prompt := &survey.MultiSelect{
 | 
				
			||||||
 | 
							Options: names,
 | 
				
			||||||
 | 
							Message: t.TranslateTo("Choose which package(s) to "+verb, config.Language),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var choices []int
 | 
				
			||||||
 | 
						err := survey.AskOne(prompt, &choices)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out := make([]db.Package, len(choices))
 | 
				
			||||||
 | 
						for i, choiceIndex := range choices {
 | 
				
			||||||
 | 
							out[i] = options[choiceIndex]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return out, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,3 +1,21 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package config
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@@ -8,7 +26,9 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var defaultConfig = types.Config{
 | 
					var defaultConfig = types.Config{
 | 
				
			||||||
	RootCmd: "sudo",
 | 
						RootCmd:          "sudo",
 | 
				
			||||||
 | 
						PagerStyle:       "native",
 | 
				
			||||||
 | 
						IgnorePkgUpdates: []string{},
 | 
				
			||||||
	Repos: []types.Repo{
 | 
						Repos: []types.Repo{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Name: "default",
 | 
								Name: "default",
 | 
				
			||||||
@@ -17,6 +37,8 @@ var defaultConfig = types.Config{
 | 
				
			|||||||
	},
 | 
						},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Decode decodes the config file into the given
 | 
				
			||||||
 | 
					// pointer
 | 
				
			||||||
func Decode(cfg *types.Config) error {
 | 
					func Decode(cfg *types.Config) error {
 | 
				
			||||||
	cfgFl, err := os.Open(ConfigPath)
 | 
						cfgFl, err := os.Open(ConfigPath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -24,5 +46,9 @@ func Decode(cfg *types.Config) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	defer cfgFl.Close()
 | 
						defer cfgFl.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Write defaults to pointer in case some values are not set in the config
 | 
				
			||||||
 | 
						*cfg = defaultConfig
 | 
				
			||||||
 | 
						// Set repos to nil so as to avoid a duplicate default
 | 
				
			||||||
 | 
						cfg.Repos = nil
 | 
				
			||||||
	return toml.NewDecoder(cfgFl).Decode(cfg)
 | 
						return toml.NewDecoder(cfgFl).Decode(cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,21 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package config
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@@ -17,6 +35,10 @@ var (
 | 
				
			|||||||
	DBPath     string
 | 
						DBPath     string
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DBPresent is true if the database
 | 
				
			||||||
 | 
					// was present when LURE was started
 | 
				
			||||||
 | 
					var DBPresent bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	cfgDir, err := os.UserConfigDir()
 | 
						cfgDir, err := os.UserConfigDir()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -66,4 +88,7 @@ func init() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DBPath = filepath.Join(CacheDir, "db")
 | 
						DBPath = filepath.Join(CacheDir, "db")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fi, err := os.Stat(DBPath)
 | 
				
			||||||
 | 
						DBPresent = err == nil && !fi.IsDir()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										48
									
								
								internal/config/lang.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								internal/config/lang.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
 | 
						"golang.org/x/text/language"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Language language.Tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						lang := SystemLang()
 | 
				
			||||||
 | 
						tag, err := language.Parse(lang)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error parsing system language").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						base, _ := tag.Base()
 | 
				
			||||||
 | 
						Language = language.Make(base.String())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func SystemLang() string {
 | 
				
			||||||
 | 
						lang := os.Getenv("LANG")
 | 
				
			||||||
 | 
						lang, _, _ = strings.Cut(lang, ".")
 | 
				
			||||||
 | 
						if lang == "" {
 | 
				
			||||||
 | 
							lang = "en"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return lang
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,3 +1,21 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package config
 | 
					package config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import _ "embed"
 | 
					import _ "embed"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -44,6 +44,7 @@ func ARMVariant() string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Arch returns the canonical CPU architecture of the system
 | 
				
			||||||
func Arch() string {
 | 
					func Arch() string {
 | 
				
			||||||
	arch := runtime.GOARCH
 | 
						arch := runtime.GOARCH
 | 
				
			||||||
	if arch == "arm" {
 | 
						if arch == "arm" {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,66 +1,299 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package db
 | 
					package db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/genjidb/genji"
 | 
						"database/sql"
 | 
				
			||||||
	"github.com/genjidb/genji/document"
 | 
						"database/sql/driver"
 | 
				
			||||||
 | 
						"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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package is a LURE package's database representation
 | 
				
			||||||
type Package struct {
 | 
					type Package struct {
 | 
				
			||||||
	Name          string   `sh:"name,required"`
 | 
						Name          string                    `sh:"name,required" db:"name"`
 | 
				
			||||||
	Version       string   `sh:"version,required"`
 | 
						Version       string                    `sh:"version,required" db:"version"`
 | 
				
			||||||
	Release       int      `sh:"release,required"`
 | 
						Release       int                       `sh:"release,required" db:"release"`
 | 
				
			||||||
	Epoch         uint     `sh:"epoch"`
 | 
						Epoch         uint                      `sh:"epoch" db:"epoch"`
 | 
				
			||||||
	Description   string   `sh:"desc"`
 | 
						Description   JSON[map[string]string]   `db:"description"`
 | 
				
			||||||
	Homepage      string   `sh:"homepage"`
 | 
						Homepage      JSON[map[string]string]   `db:"homepage"`
 | 
				
			||||||
	Maintainer    string   `sh:"maintainer"`
 | 
						Maintainer    JSON[map[string]string]   `db:"maintainer"`
 | 
				
			||||||
	Architectures []string `sh:"architectures"`
 | 
						Architectures JSON[[]string]            `sh:"architectures" db:"architectures"`
 | 
				
			||||||
	Licenses      []string `sh:"license"`
 | 
						Licenses      JSON[[]string]            `sh:"license" db:"licenses"`
 | 
				
			||||||
	Provides      []string `sh:"provides"`
 | 
						Provides      JSON[[]string]            `sh:"provides" db:"provides"`
 | 
				
			||||||
	Conflicts     []string `sh:"conflicts"`
 | 
						Conflicts     JSON[[]string]            `sh:"conflicts" db:"conflicts"`
 | 
				
			||||||
	Depends       map[string][]string
 | 
						Replaces      JSON[[]string]            `sh:"replaces" db:"replaces"`
 | 
				
			||||||
	BuildDepends  map[string][]string
 | 
						Depends       JSON[map[string][]string] `db:"depends"`
 | 
				
			||||||
	Replaces      []string `sh:"replaces"`
 | 
						BuildDepends  JSON[map[string][]string] `db:"builddepends"`
 | 
				
			||||||
	Repository    string
 | 
						Repository    string                    `db:"repository"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type version struct {
 | 
				
			||||||
 | 
						Version int `db:"version"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Open(dsn string) (*sqlx.DB, error) {
 | 
				
			||||||
 | 
						if dsn != ":memory:" {
 | 
				
			||||||
 | 
							fi, err := os.Stat(config.DBPath)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								// TODO: This should be removed by the first stable release.
 | 
				
			||||||
 | 
								if fi.IsDir() {
 | 
				
			||||||
 | 
									log.Warn("Your database is using the old database engine; rebuilding").Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									err = os.RemoveAll(config.DBPath)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										log.Fatal("Error removing old database").Err(err).Send()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									config.DBPresent = false
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db, err := sqlx.Open("sqlite", dsn)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error opening database").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = Init(db, dsn)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error initializing database").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return db, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Init initializes the database
 | 
					// Init initializes the database
 | 
				
			||||||
func Init(db *genji.DB) error {
 | 
					func Init(db *sqlx.DB, dsn string) error {
 | 
				
			||||||
	return db.Exec(`
 | 
						*db = *db.Unsafe()
 | 
				
			||||||
 | 
						_, err := db.Exec(`
 | 
				
			||||||
		CREATE TABLE IF NOT EXISTS pkgs (
 | 
							CREATE TABLE IF NOT EXISTS pkgs (
 | 
				
			||||||
			name       TEXT,
 | 
								name          TEXT NOT NULL,
 | 
				
			||||||
			repository TEXT NOT NULL,
 | 
								repository    TEXT NOT NULL,
 | 
				
			||||||
			UNIQUE(name, repository),
 | 
								version       TEXT NOT NULL,
 | 
				
			||||||
			...
 | 
								release       INT  NOT NULL,
 | 
				
			||||||
 | 
								epoch         INT,
 | 
				
			||||||
 | 
								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')),
 | 
				
			||||||
 | 
								conflicts     TEXT CHECK(conflicts = 'null' OR (JSON_VALID(conflicts) AND JSON_TYPE(conflicts) = 'array')),
 | 
				
			||||||
 | 
								replaces      TEXT CHECK(replaces = 'null' OR (JSON_VALID(replaces) AND JSON_TYPE(replaces) = 'array')),
 | 
				
			||||||
 | 
								depends       TEXT CHECK(depends = 'null' OR (JSON_VALID(depends) AND JSON_TYPE(depends) = 'object')),
 | 
				
			||||||
 | 
								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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InsertPackage adds a package to the database
 | 
					// InsertPackage adds a package to the database
 | 
				
			||||||
func InsertPackage(db *genji.DB, pkg Package) error {
 | 
					func InsertPackage(db *sqlx.DB, pkg Package) error {
 | 
				
			||||||
	return db.Exec("INSERT INTO pkgs VALUES ? ON CONFLICT DO REPLACE;", pkg)
 | 
						_, err := db.NamedExec(`
 | 
				
			||||||
 | 
							INSERT OR REPLACE INTO pkgs (
 | 
				
			||||||
 | 
								name,
 | 
				
			||||||
 | 
								repository,
 | 
				
			||||||
 | 
								version,
 | 
				
			||||||
 | 
								release,
 | 
				
			||||||
 | 
								epoch,
 | 
				
			||||||
 | 
								description,
 | 
				
			||||||
 | 
								homepage,
 | 
				
			||||||
 | 
								maintainer,
 | 
				
			||||||
 | 
								architectures,
 | 
				
			||||||
 | 
								licenses,
 | 
				
			||||||
 | 
								provides,
 | 
				
			||||||
 | 
								conflicts,
 | 
				
			||||||
 | 
								replaces,
 | 
				
			||||||
 | 
								depends,
 | 
				
			||||||
 | 
								builddepends
 | 
				
			||||||
 | 
							) VALUES (
 | 
				
			||||||
 | 
								:name,
 | 
				
			||||||
 | 
								:repository,
 | 
				
			||||||
 | 
								:version,
 | 
				
			||||||
 | 
								:release,
 | 
				
			||||||
 | 
								:epoch,
 | 
				
			||||||
 | 
								:description,
 | 
				
			||||||
 | 
								:homepage,
 | 
				
			||||||
 | 
								:maintainer,
 | 
				
			||||||
 | 
								:architectures,
 | 
				
			||||||
 | 
								:licenses,
 | 
				
			||||||
 | 
								:provides,
 | 
				
			||||||
 | 
								:conflicts,
 | 
				
			||||||
 | 
								:replaces,
 | 
				
			||||||
 | 
								:depends,
 | 
				
			||||||
 | 
								:builddepends
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						`, pkg)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetPkgs returns a result containing packages that match the where conditions
 | 
					// GetPkgs returns a result containing packages that match the where conditions
 | 
				
			||||||
func GetPkgs(db *genji.DB, where string, args ...any) (*genji.Result, error) {
 | 
					func GetPkgs(db *sqlx.DB, where string, args ...any) (*sqlx.Rows, error) {
 | 
				
			||||||
	stream, err := db.Query("SELECT * FROM pkgs WHERE "+where, args...)
 | 
						stream, err := db.Queryx("SELECT * FROM pkgs WHERE "+where, args...)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return stream, nil
 | 
						return stream, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetPkg returns a single package matching the where conditions
 | 
					// GetPkg returns a single package that match the where conditions
 | 
				
			||||||
func GetPkg(db *genji.DB, where string, args ...any) (*Package, error) {
 | 
					func GetPkg(db *sqlx.DB, where string, args ...any) (*Package, error) {
 | 
				
			||||||
	doc, err := db.QueryDocument("SELECT * FROM pkgs WHERE "+where, args...)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	out := &Package{}
 | 
						out := &Package{}
 | 
				
			||||||
	err = document.StructScan(doc, out)
 | 
						err := db.Get(out, "SELECT * FROM pkgs WHERE "+where+" LIMIT 1", args...)
 | 
				
			||||||
	return out, err
 | 
						return out, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeletePkgs deletes all packages matching the where conditions
 | 
					// DeletePkgs deletes all packages matching the where conditions
 | 
				
			||||||
func DeletePkgs(db *genji.DB, where string, args ...any) error {
 | 
					func DeletePkgs(db *sqlx.DB, where string, args ...any) error {
 | 
				
			||||||
	return db.Exec("DELETE * FROM pkgs WHERE "+where, args...)
 | 
						_, err := db.Exec("DELETE FROM pkgs WHERE "+where, args...)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func JsonArrayContains(ctx *sqlite.FunctionContext, args []driver.Value) (driver.Value, error) {
 | 
				
			||||||
 | 
						value, ok := args[0].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, errors.New("both arguments to json_array_contains must be strings")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						item, ok := args[1].(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, errors.New("both arguments to json_array_contains must be strings")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var array []string
 | 
				
			||||||
 | 
						err := json.Unmarshal([]byte(value), &array)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return slices.Contains(array, item), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type JSON[T any] struct {
 | 
				
			||||||
 | 
						Val T
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewJSON[T any](v T) JSON[T] {
 | 
				
			||||||
 | 
						return JSON[T]{Val: v}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *JSON[T]) Scan(val any) error {
 | 
				
			||||||
 | 
						if val == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch val := val.(type) {
 | 
				
			||||||
 | 
						case string:
 | 
				
			||||||
 | 
							err := json.Unmarshal([]byte(val), &s.Val)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case sql.NullString:
 | 
				
			||||||
 | 
							if val.Valid {
 | 
				
			||||||
 | 
								err := json.Unmarshal([]byte(val.String), &s.Val)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return errors.New("sqlite json types must be strings")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s JSON[T]) Value() (driver.Value, error) {
 | 
				
			||||||
 | 
						data, err := json.Marshal(s.Val)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return string(data), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s JSON[T]) MarshalYAML() (any, error) {
 | 
				
			||||||
 | 
						return s.Val, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s JSON[T]) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprint(s.Val)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s JSON[T]) GoString() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("%#v", s.Val)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										255
									
								
								internal/db/db_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								internal/db/db_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,255 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/jmoiron/sqlx"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var testPkg = db.Package{
 | 
				
			||||||
 | 
						Name:    "test",
 | 
				
			||||||
 | 
						Version: "0.0.1",
 | 
				
			||||||
 | 
						Release: 1,
 | 
				
			||||||
 | 
						Epoch:   2,
 | 
				
			||||||
 | 
						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"}),
 | 
				
			||||||
 | 
						Conflicts:     db.NewJSON([]string{"test"}),
 | 
				
			||||||
 | 
						Replaces:      db.NewJSON([]string{"test-old"}),
 | 
				
			||||||
 | 
						Depends: db.NewJSON(map[string][]string{
 | 
				
			||||||
 | 
							"": {"sudo"},
 | 
				
			||||||
 | 
						}),
 | 
				
			||||||
 | 
						BuildDepends: db.NewJSON(map[string][]string{
 | 
				
			||||||
 | 
							"":     {"golang"},
 | 
				
			||||||
 | 
							"arch": {"go"},
 | 
				
			||||||
 | 
						}),
 | 
				
			||||||
 | 
						Repository: "default",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestInit(t *testing.T) {
 | 
				
			||||||
 | 
						gdb, err := sqlx.Open("sqlite", ":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.Init(gdb, ":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = gdb.Exec("SELECT * FROM pkgs")
 | 
				
			||||||
 | 
						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 := db.Open(":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, testPkg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbPkg := db.Package{}
 | 
				
			||||||
 | 
						err = sqlx.Get(gdb, &dbPkg, "SELECT * FROM pkgs WHERE name = 'test' AND repository = 'default'")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(testPkg, dbPkg) {
 | 
				
			||||||
 | 
							t.Errorf("Expected test package to be the same as database package")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetPkgs(t *testing.T) {
 | 
				
			||||||
 | 
						gdb, err := db.Open(":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						x1 := testPkg
 | 
				
			||||||
 | 
						x1.Name = "x1"
 | 
				
			||||||
 | 
						x2 := testPkg
 | 
				
			||||||
 | 
						x2.Name = "x2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, x1)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, x2)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := db.GetPkgs(gdb, "name LIKE 'x%'")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for result.Next() {
 | 
				
			||||||
 | 
							var dbPkg db.Package
 | 
				
			||||||
 | 
							err = result.StructScan(&dbPkg)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !strings.HasPrefix(dbPkg.Name, "x") {
 | 
				
			||||||
 | 
								t.Errorf("Expected package name to start with 'x', got %s", dbPkg.Name)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetPkg(t *testing.T) {
 | 
				
			||||||
 | 
						gdb, err := db.Open(":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						x1 := testPkg
 | 
				
			||||||
 | 
						x1.Name = "x1"
 | 
				
			||||||
 | 
						x2 := testPkg
 | 
				
			||||||
 | 
						x2.Name = "x2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, x1)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, x2)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkg, err := db.GetPkg(gdb, "name LIKE 'x%' ORDER BY name")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if pkg.Name != "x1" {
 | 
				
			||||||
 | 
							t.Errorf("Expected x1 package, got %s", pkg.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(*pkg, x1) {
 | 
				
			||||||
 | 
							t.Errorf("Expected x1 to be %v, got %v", x1, *pkg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeletePkgs(t *testing.T) {
 | 
				
			||||||
 | 
						gdb, err := db.Open(":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						x1 := testPkg
 | 
				
			||||||
 | 
						x1.Name = "x1"
 | 
				
			||||||
 | 
						x2 := testPkg
 | 
				
			||||||
 | 
						x2.Name = "x2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, x1)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, x2)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.DeletePkgs(gdb, "name = 'x1'")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var dbPkg db.Package
 | 
				
			||||||
 | 
						err = gdb.Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if dbPkg.Name != "x2" {
 | 
				
			||||||
 | 
							t.Errorf("Expected x2 package, got %s", dbPkg.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestJsonArrayContains(t *testing.T) {
 | 
				
			||||||
 | 
						gdb, err := db.Open(":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						x1 := testPkg
 | 
				
			||||||
 | 
						x1.Name = "x1"
 | 
				
			||||||
 | 
						x2 := testPkg
 | 
				
			||||||
 | 
						x2.Name = "x2"
 | 
				
			||||||
 | 
						x2.Provides.Val = append(x2.Provides.Val, "x")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, x1)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, x2)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var dbPkg db.Package
 | 
				
			||||||
 | 
						err = gdb.Get(&dbPkg, "SELECT * FROM pkgs WHERE json_array_contains(provides, 'x');")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if dbPkg.Name != "x2" {
 | 
				
			||||||
 | 
							t.Errorf("Expected x2 package, got %s", dbPkg.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										325
									
								
								internal/dl/dl.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								internal/dl/dl.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,325 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package dl contains abstractions for downloadingfiles and directories
 | 
				
			||||||
 | 
					// from various sources.
 | 
				
			||||||
 | 
					package dl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/PuerkitoBio/purell"
 | 
				
			||||||
 | 
						"github.com/vmihailenco/msgpack/v5"
 | 
				
			||||||
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/dlcache"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const manifestFileName = ".lure_cache_manifest"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrChecksumMismatch occurs when the checksum of a downloaded file
 | 
				
			||||||
 | 
					// does not match the expected checksum provided in the Options struct.
 | 
				
			||||||
 | 
					var ErrChecksumMismatch = errors.New("dl: checksums did not match")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Downloaders contains all the downloaders in the order in which
 | 
				
			||||||
 | 
					// they should be checked
 | 
				
			||||||
 | 
					var Downloaders = []Downloader{
 | 
				
			||||||
 | 
						GitDownloader{},
 | 
				
			||||||
 | 
						FileDownloader{},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Type represents the type of download (file or directory)
 | 
				
			||||||
 | 
					type Type uint8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						TypeFile Type = iota
 | 
				
			||||||
 | 
						TypeDir
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t Type) String() string {
 | 
				
			||||||
 | 
						switch t {
 | 
				
			||||||
 | 
						case TypeFile:
 | 
				
			||||||
 | 
							return "file"
 | 
				
			||||||
 | 
						case TypeDir:
 | 
				
			||||||
 | 
							return "dir"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "<unknown>"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Options contains the options for downloading
 | 
				
			||||||
 | 
					// files and directories
 | 
				
			||||||
 | 
					type Options struct {
 | 
				
			||||||
 | 
						SHA256           []byte
 | 
				
			||||||
 | 
						Name             string
 | 
				
			||||||
 | 
						URL              string
 | 
				
			||||||
 | 
						Destination      string
 | 
				
			||||||
 | 
						CacheDisabled    bool
 | 
				
			||||||
 | 
						PostprocDisabled bool
 | 
				
			||||||
 | 
						Progress         io.Writer
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Manifest holds information about the type and name
 | 
				
			||||||
 | 
					// of a downloaded file or directory. It is stored inside
 | 
				
			||||||
 | 
					// each cache directory for later use.
 | 
				
			||||||
 | 
					type Manifest struct {
 | 
				
			||||||
 | 
						Type Type
 | 
				
			||||||
 | 
						Name string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Downloader interface {
 | 
				
			||||||
 | 
						// Name returns the name of the downloader
 | 
				
			||||||
 | 
						Name() string
 | 
				
			||||||
 | 
						// MatchURL checks if the given URL matches
 | 
				
			||||||
 | 
						// the downloader.
 | 
				
			||||||
 | 
						MatchURL(string) bool
 | 
				
			||||||
 | 
						// Download downloads the object at the URL
 | 
				
			||||||
 | 
						// provided in the options, to the destination
 | 
				
			||||||
 | 
						// given in the options. It returns a type,
 | 
				
			||||||
 | 
						// a name for the downloaded object (this may be empty),
 | 
				
			||||||
 | 
						// and an error.
 | 
				
			||||||
 | 
						Download(Options) (Type, string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdatingDownloader extends the Downloader interface
 | 
				
			||||||
 | 
					// with an Update method for protocols such as git, which
 | 
				
			||||||
 | 
					// allow for incremental updates without changing the URL.
 | 
				
			||||||
 | 
					type UpdatingDownloader interface {
 | 
				
			||||||
 | 
						Downloader
 | 
				
			||||||
 | 
						// Update checks for and performs any
 | 
				
			||||||
 | 
						// available updates for the object
 | 
				
			||||||
 | 
						// described in the options. It returns
 | 
				
			||||||
 | 
						// true if an update was performed, or
 | 
				
			||||||
 | 
						// false if no update was required.
 | 
				
			||||||
 | 
						Update(Options) (bool, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Download downloads a file or directory using the specified options.
 | 
				
			||||||
 | 
					// It first gets the appropriate downloader for the URL, then checks
 | 
				
			||||||
 | 
					// if caching is enabled. If caching is enabled, it attempts to get
 | 
				
			||||||
 | 
					// the cache directory for the URL and update it if necessary.
 | 
				
			||||||
 | 
					// If the source is found in the cache, it links it to the destination
 | 
				
			||||||
 | 
					// using hard links. If the source is not found in the cache,
 | 
				
			||||||
 | 
					// it downloads the source to a new cache directory and links it
 | 
				
			||||||
 | 
					// to the destination.
 | 
				
			||||||
 | 
					func Download(ctx context.Context, opts Options) (err error) {
 | 
				
			||||||
 | 
						normalized, err := normalizeURL(opts.URL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						opts.URL = normalized
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						d := getDownloader(opts.URL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.CacheDisabled {
 | 
				
			||||||
 | 
							_, _, err = d.Download(opts)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var t Type
 | 
				
			||||||
 | 
						cacheDir, ok := dlcache.Get(opts.URL)
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							var updated bool
 | 
				
			||||||
 | 
							if d, ok := d.(UpdatingDownloader); ok {
 | 
				
			||||||
 | 
								log.Info("Source can be updated, updating if required").Str("source", opts.Name).Str("downloader", d.Name()).Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								updated, err = d.Update(Options{
 | 
				
			||||||
 | 
									Name:        opts.Name,
 | 
				
			||||||
 | 
									URL:         opts.URL,
 | 
				
			||||||
 | 
									Destination: cacheDir,
 | 
				
			||||||
 | 
									Progress:    opts.Progress,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							m, err := getManifest(cacheDir)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								t = m.Type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								dest := filepath.Join(opts.Destination, m.Name)
 | 
				
			||||||
 | 
								ok, err := handleCache(cacheDir, dest, t)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if ok && !updated {
 | 
				
			||||||
 | 
									log.Info("Source found in cache, linked to destination").Str("source", opts.Name).Stringer("type", t).Send()
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								} else if ok {
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// If we cannot read the manifest,
 | 
				
			||||||
 | 
								// this cache entry is invalid and
 | 
				
			||||||
 | 
								// the source must be re-downloaded.
 | 
				
			||||||
 | 
								err = os.RemoveAll(cacheDir)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Info("Downloading source").Str("source", opts.Name).Str("downloader", d.Name()).Send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cacheDir, err = dlcache.New(opts.URL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t, name, err := d.Download(Options{
 | 
				
			||||||
 | 
							Name:        opts.Name,
 | 
				
			||||||
 | 
							URL:         opts.URL,
 | 
				
			||||||
 | 
							Destination: cacheDir,
 | 
				
			||||||
 | 
							Progress:    opts.Progress,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = writeManifest(cacheDir, Manifest{t, name})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dest := filepath.Join(opts.Destination, name)
 | 
				
			||||||
 | 
						_, err = handleCache(cacheDir, dest, t)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// writeManifest writes the manifest to the specified cache directory.
 | 
				
			||||||
 | 
					func writeManifest(cacheDir string, m Manifest) error {
 | 
				
			||||||
 | 
						fl, err := os.Create(filepath.Join(cacheDir, manifestFileName))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer fl.Close()
 | 
				
			||||||
 | 
						return msgpack.NewEncoder(fl).Encode(m)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getManifest reads the manifest from the specified cache directory.
 | 
				
			||||||
 | 
					func getManifest(cacheDir string) (m Manifest, err error) {
 | 
				
			||||||
 | 
						fl, err := os.Open(filepath.Join(cacheDir, manifestFileName))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return Manifest{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer fl.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = msgpack.NewDecoder(fl).Decode(&m)
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// handleCache links the cache directory or a file within it to the destination
 | 
				
			||||||
 | 
					func handleCache(cacheDir, dest string, t Type) (bool, error) {
 | 
				
			||||||
 | 
						switch t {
 | 
				
			||||||
 | 
						case TypeFile:
 | 
				
			||||||
 | 
							cd, err := os.Open(cacheDir)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return false, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							names, err := cd.Readdirnames(2)
 | 
				
			||||||
 | 
							if err == io.EOF {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							} else if err != nil {
 | 
				
			||||||
 | 
								return false, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cd.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, name := range names {
 | 
				
			||||||
 | 
								if name == manifestFileName {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err = os.Link(filepath.Join(cacheDir, names[0]), filepath.Join(dest, filepath.Base(names[0])))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return false, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						case TypeDir:
 | 
				
			||||||
 | 
							err := linkDir(cacheDir, dest)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return false, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// linkDir recursively walks through a directory, creating
 | 
				
			||||||
 | 
					// hard links for each file from the src directory to the
 | 
				
			||||||
 | 
					// dest directory. If it encounters a directory, it will
 | 
				
			||||||
 | 
					// create a directory with the same name and permissions
 | 
				
			||||||
 | 
					// in the dest directory, because hard links cannot be
 | 
				
			||||||
 | 
					// created for directories.
 | 
				
			||||||
 | 
					func linkDir(src, dest string) error {
 | 
				
			||||||
 | 
						return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if info.Name() == manifestFileName {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rel, err := filepath.Rel(src, path)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newPath := filepath.Join(dest, rel)
 | 
				
			||||||
 | 
							if info.IsDir() {
 | 
				
			||||||
 | 
								return os.MkdirAll(newPath, info.Mode())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return os.Link(path, newPath)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getDownloader(u string) Downloader {
 | 
				
			||||||
 | 
						for _, d := range Downloaders {
 | 
				
			||||||
 | 
							if d.MatchURL(u) {
 | 
				
			||||||
 | 
								return d
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// normalizeURL normalizes a URL string, so that insignificant
 | 
				
			||||||
 | 
					// differences don't change the hash.
 | 
				
			||||||
 | 
					func normalizeURL(u string) (string, error) {
 | 
				
			||||||
 | 
						const normalizationFlags = purell.FlagRemoveTrailingSlash |
 | 
				
			||||||
 | 
							purell.FlagRemoveDefaultPort |
 | 
				
			||||||
 | 
							purell.FlagLowercaseHost |
 | 
				
			||||||
 | 
							purell.FlagLowercaseScheme |
 | 
				
			||||||
 | 
							purell.FlagRemoveDuplicateSlashes |
 | 
				
			||||||
 | 
							purell.FlagRemoveFragment |
 | 
				
			||||||
 | 
							purell.FlagRemoveUnnecessaryHostDots |
 | 
				
			||||||
 | 
							purell.FlagSortQuery |
 | 
				
			||||||
 | 
							purell.FlagDecodeHexHost |
 | 
				
			||||||
 | 
							purell.FlagDecodeOctalHost |
 | 
				
			||||||
 | 
							purell.FlagDecodeUnnecessaryEscapes |
 | 
				
			||||||
 | 
							purell.FlagRemoveEmptyPortSeparator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return purell.NormalizeURLString(u, normalizationFlags)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										243
									
								
								internal/dl/file.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								internal/dl/file.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,243 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package dl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"crypto/sha256"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mholt/archiver/v4"
 | 
				
			||||||
 | 
						"github.com/schollz/progressbar/v3"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/shutils"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FileDownloader downloads files using HTTP
 | 
				
			||||||
 | 
					type FileDownloader struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Name always returns "file"
 | 
				
			||||||
 | 
					func (FileDownloader) Name() string {
 | 
				
			||||||
 | 
						return "file"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MatchURL always returns true, as FileDownloader
 | 
				
			||||||
 | 
					// is used as a fallback if nothing else matches
 | 
				
			||||||
 | 
					func (FileDownloader) MatchURL(string) bool {
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Download downloads a file using HTTP. If the file is
 | 
				
			||||||
 | 
					// compressed using a supported format, it will be extracted
 | 
				
			||||||
 | 
					func (FileDownloader) Download(opts Options) (Type, string, error) {
 | 
				
			||||||
 | 
						u, err := url.Parse(opts.URL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := u.Query()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						name := query.Get("~name")
 | 
				
			||||||
 | 
						query.Del("~name")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						archive := query.Get("~archive")
 | 
				
			||||||
 | 
						query.Del("~archive")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u.RawQuery = query.Encode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := http.Get(u.String())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if name == "" {
 | 
				
			||||||
 | 
							name = getFilename(res)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts.PostprocDisabled = archive == "false"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						path := filepath.Join(opts.Destination, name)
 | 
				
			||||||
 | 
						fl, err := os.Create(path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer fl.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var bar io.WriteCloser
 | 
				
			||||||
 | 
						if opts.Progress != nil {
 | 
				
			||||||
 | 
							bar = progressbar.NewOptions64(
 | 
				
			||||||
 | 
								res.ContentLength,
 | 
				
			||||||
 | 
								progressbar.OptionSetDescription(name),
 | 
				
			||||||
 | 
								progressbar.OptionSetWriter(opts.Progress),
 | 
				
			||||||
 | 
								progressbar.OptionShowBytes(true),
 | 
				
			||||||
 | 
								progressbar.OptionSetWidth(10),
 | 
				
			||||||
 | 
								progressbar.OptionThrottle(65*time.Millisecond),
 | 
				
			||||||
 | 
								progressbar.OptionShowCount(),
 | 
				
			||||||
 | 
								progressbar.OptionOnCompletion(func() {
 | 
				
			||||||
 | 
									_, _ = io.WriteString(opts.Progress, "\n")
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
								progressbar.OptionSpinnerType(14),
 | 
				
			||||||
 | 
								progressbar.OptionFullWidth(),
 | 
				
			||||||
 | 
								progressbar.OptionSetRenderBlankState(true),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							defer bar.Close()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							bar = shutils.NopRWC{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						h := sha256.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var w io.Writer
 | 
				
			||||||
 | 
						if opts.SHA256 != nil {
 | 
				
			||||||
 | 
							w = io.MultiWriter(fl, h, bar)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							w = io.MultiWriter(fl, bar)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = io.Copy(w, res.Body)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.SHA256 != nil {
 | 
				
			||||||
 | 
							sum := h.Sum(nil)
 | 
				
			||||||
 | 
							if !bytes.Equal(sum, opts.SHA256) {
 | 
				
			||||||
 | 
								return 0, "", ErrChecksumMismatch
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PostprocDisabled {
 | 
				
			||||||
 | 
							return TypeFile, "", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = fl.Seek(0, io.SeekStart)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						format, r, err := archiver.Identify(name, fl)
 | 
				
			||||||
 | 
						if err == archiver.ErrNoMatch {
 | 
				
			||||||
 | 
							return TypeFile, "", nil
 | 
				
			||||||
 | 
						} else if err != nil {
 | 
				
			||||||
 | 
							return 0, "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = extractFile(r, format, name, opts)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = os.Remove(path)
 | 
				
			||||||
 | 
						return TypeDir, "", err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// extractFile extracts an archive or decompresses a file
 | 
				
			||||||
 | 
					func extractFile(r io.Reader, format archiver.Format, name string, opts Options) (err error) {
 | 
				
			||||||
 | 
						fname := format.Name()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch format := format.(type) {
 | 
				
			||||||
 | 
						case archiver.Extractor:
 | 
				
			||||||
 | 
							err = format.Extract(context.Background(), r, nil, func(ctx context.Context, f archiver.File) error {
 | 
				
			||||||
 | 
								fr, err := f.Open()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer fr.Close()
 | 
				
			||||||
 | 
								fi, err := f.Stat()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								fm := fi.Mode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								path := filepath.Join(opts.Destination, f.NameInArchive)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err = os.MkdirAll(filepath.Dir(path), 0o755)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if f.IsDir() {
 | 
				
			||||||
 | 
									err = os.Mkdir(path, 0o755)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									outFl, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fm.Perm())
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer outFl.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									_, err = io.Copy(outFl, fr)
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case archiver.Decompressor:
 | 
				
			||||||
 | 
							rc, err := format.OpenReader(r)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer rc.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							path := filepath.Join(opts.Destination, name)
 | 
				
			||||||
 | 
							path = strings.TrimSuffix(path, fname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							outFl, err := os.Create(path)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_, err = io.Copy(outFl, rc)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var cdHeaderRgx = regexp.MustCompile(`filename="(.+)"`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getFilename attempts to parse the Content-Disposition
 | 
				
			||||||
 | 
					// HTTP response header and extract a filename. If the
 | 
				
			||||||
 | 
					// header does not exist, it will use the last element
 | 
				
			||||||
 | 
					// of the path.
 | 
				
			||||||
 | 
					func getFilename(res *http.Response) (name string) {
 | 
				
			||||||
 | 
						cd := res.Header.Get("Content-Disposition")
 | 
				
			||||||
 | 
						matches := cdHeaderRgx.FindStringSubmatch(cd)
 | 
				
			||||||
 | 
						if len(matches) > 1 {
 | 
				
			||||||
 | 
							name = matches[1]
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							name = path.Base(res.Request.URL.Path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return name
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										180
									
								
								internal/dl/git.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								internal/dl/git.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package dl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GitDownloader downloads Git repositories
 | 
				
			||||||
 | 
					type GitDownloader struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Name always returns "git"
 | 
				
			||||||
 | 
					func (GitDownloader) Name() string {
 | 
				
			||||||
 | 
						return "git"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MatchURL matches any URLs that start with "git+"
 | 
				
			||||||
 | 
					func (GitDownloader) MatchURL(u string) bool {
 | 
				
			||||||
 | 
						return strings.HasPrefix(u, "git+")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Download uses git to clone the repository from the specified URL.
 | 
				
			||||||
 | 
					// It allows specifying the revision, depth and recursion options
 | 
				
			||||||
 | 
					// via query string
 | 
				
			||||||
 | 
					func (GitDownloader) Download(opts Options) (Type, string, error) {
 | 
				
			||||||
 | 
						u, err := url.Parse(opts.URL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						u.Scheme = strings.TrimPrefix(u.Scheme, "git+")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := u.Query()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rev := query.Get("~rev")
 | 
				
			||||||
 | 
						query.Del("~rev")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						name := query.Get("~name")
 | 
				
			||||||
 | 
						query.Del("~name")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						depthStr := query.Get("~depth")
 | 
				
			||||||
 | 
						query.Del("~depth")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						recursive := query.Get("~recursive")
 | 
				
			||||||
 | 
						query.Del("~recursive")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u.RawQuery = query.Encode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						depth := 0
 | 
				
			||||||
 | 
						if depthStr != "" {
 | 
				
			||||||
 | 
							depth, err = strconv.Atoi(depthStr)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return 0, "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						co := &git.CloneOptions{
 | 
				
			||||||
 | 
							URL:               u.String(),
 | 
				
			||||||
 | 
							Depth:             depth,
 | 
				
			||||||
 | 
							Progress:          opts.Progress,
 | 
				
			||||||
 | 
							RecurseSubmodules: git.NoRecurseSubmodules,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if recursive == "true" {
 | 
				
			||||||
 | 
							co.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r, err := git.PlainClone(opts.Destination, false, co)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if rev != "" {
 | 
				
			||||||
 | 
							h, err := r.ResolveRevision(plumbing.Revision(rev))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return 0, "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							w, err := r.Worktree()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return 0, "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = w.Checkout(&git.CheckoutOptions{
 | 
				
			||||||
 | 
								Hash: *h,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return 0, "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if name == "" {
 | 
				
			||||||
 | 
							name = strings.TrimSuffix(path.Base(u.Path), ".git")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return TypeDir, name, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Update uses git to pull the repository and update it
 | 
				
			||||||
 | 
					// to the latest revision. It allows specifying the depth
 | 
				
			||||||
 | 
					// and recursion options via query string. It returns
 | 
				
			||||||
 | 
					// true if update was successful and false if the
 | 
				
			||||||
 | 
					// repository is already up-to-date
 | 
				
			||||||
 | 
					func (GitDownloader) Update(opts Options) (bool, error) {
 | 
				
			||||||
 | 
						u, err := url.Parse(opts.URL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						u.Scheme = strings.TrimPrefix(u.Scheme, "git+")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := u.Query()
 | 
				
			||||||
 | 
						query.Del("~rev")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						depthStr := query.Get("~depth")
 | 
				
			||||||
 | 
						query.Del("~depth")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						recursive := query.Get("~recursive")
 | 
				
			||||||
 | 
						query.Del("~recursive")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u.RawQuery = query.Encode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r, err := git.PlainOpen(opts.Destination)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w, err := r.Worktree()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						depth := 0
 | 
				
			||||||
 | 
						if depthStr != "" {
 | 
				
			||||||
 | 
							depth, err = strconv.Atoi(depthStr)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return false, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						po := &git.PullOptions{
 | 
				
			||||||
 | 
							Depth:             depth,
 | 
				
			||||||
 | 
							Progress:          opts.Progress,
 | 
				
			||||||
 | 
							RecurseSubmodules: git.NoRecurseSubmodules,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if recursive == "true" {
 | 
				
			||||||
 | 
							po.RecurseSubmodules = git.DefaultSubmoduleRecursionDepth
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = w.Pull(po)
 | 
				
			||||||
 | 
						if errors.Is(err, git.NoErrAlreadyUpToDate) {
 | 
				
			||||||
 | 
							return false, nil
 | 
				
			||||||
 | 
						} else if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										90
									
								
								internal/dlcache/dlcache.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								internal/dlcache/dlcache.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package dlcache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/sha1"
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/config"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BasePath stores the base path to the download cache
 | 
				
			||||||
 | 
					var BasePath = filepath.Join(config.CacheDir, "dl")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New creates a new directory with the given ID in the cache.
 | 
				
			||||||
 | 
					// If a directory with the same ID already exists,
 | 
				
			||||||
 | 
					// it will be deleted before creating a new one.
 | 
				
			||||||
 | 
					func New(id string) (string, error) {
 | 
				
			||||||
 | 
						h, err := hashID(id)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						itemPath := filepath.Join(BasePath, h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fi, err := os.Stat(itemPath)
 | 
				
			||||||
 | 
						if err == nil || (fi != nil && !fi.IsDir()) {
 | 
				
			||||||
 | 
							err = os.RemoveAll(itemPath)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = os.MkdirAll(itemPath, 0o755)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return itemPath, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get checks if an entry with the given ID
 | 
				
			||||||
 | 
					// already exists in the cache, and if so,
 | 
				
			||||||
 | 
					// returns the directory and true. If it
 | 
				
			||||||
 | 
					// does not exist, it returns an empty string
 | 
				
			||||||
 | 
					// and false.
 | 
				
			||||||
 | 
					func Get(id string) (string, bool) {
 | 
				
			||||||
 | 
						h, err := hashID(id)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						itemPath := filepath.Join(BasePath, h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = os.Stat(itemPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return itemPath, true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// hashID hashes the input ID with SHA1
 | 
				
			||||||
 | 
					// and returns the hex string of the hashed
 | 
				
			||||||
 | 
					// ID.
 | 
				
			||||||
 | 
					func hashID(id string) (string, error) {
 | 
				
			||||||
 | 
						h := sha1.New()
 | 
				
			||||||
 | 
						_, err := io.WriteString(h, id)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return hex.EncodeToString(h.Sum(nil)), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										74
									
								
								internal/dlcache/dlcache_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								internal/dlcache/dlcache_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package dlcache_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/sha1"
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/dlcache"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						dir, err := os.MkdirTemp("/tmp", "lure-dlcache-test.*")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dlcache.BasePath = dir
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNew(t *testing.T) {
 | 
				
			||||||
 | 
						const id = "https://example.com"
 | 
				
			||||||
 | 
						dir, err := dlcache.New(id)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exp := filepath.Join(dlcache.BasePath, sha1sum(id))
 | 
				
			||||||
 | 
						if dir != exp {
 | 
				
			||||||
 | 
							t.Errorf("Expected %s, got %s", exp, dir)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fi, err := os.Stat(dir)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("stat: expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !fi.IsDir() {
 | 
				
			||||||
 | 
							t.Errorf("Expected cache item to be a directory")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dir2, ok := dlcache.Get(id)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Errorf("Expected Get() to return valid value")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if dir2 != dir {
 | 
				
			||||||
 | 
							t.Errorf("Expected %s from Get(), got %s", dir, dir2)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func sha1sum(id string) string {
 | 
				
			||||||
 | 
						h := sha1.New()
 | 
				
			||||||
 | 
						_, _ = io.WriteString(h, id)
 | 
				
			||||||
 | 
						return hex.EncodeToString(h.Sum(nil))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										235
									
								
								internal/overrides/overrides.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								internal/overrides/overrides.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,235 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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, error) {
 | 
				
			||||||
 | 
						if opts == nil {
 | 
				
			||||||
 | 
							opts = DefaultOpts
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !opts.Overrides {
 | 
				
			||||||
 | 
							return []string{opts.Name}, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						langs, err := parseLangs(opts.Languages, opts.LanguageTags)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						architectures := []string{runtime.GOARCH}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if runtime.GOARCH == "arm" {
 | 
				
			||||||
 | 
							// More specific goes first
 | 
				
			||||||
 | 
							architectures[0] = cpu.ARMVariant()
 | 
				
			||||||
 | 
							architectures = append(architectures, "arm")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						distros := []string{info.ID}
 | 
				
			||||||
 | 
						if opts.LikeDistros {
 | 
				
			||||||
 | 
							distros = append(distros, info.Like...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []string
 | 
				
			||||||
 | 
						for _, arch := range architectures {
 | 
				
			||||||
 | 
							for _, distro := range distros {
 | 
				
			||||||
 | 
								if opts.Name == "" {
 | 
				
			||||||
 | 
									out = append(
 | 
				
			||||||
 | 
										out,
 | 
				
			||||||
 | 
										arch+"_"+distro,
 | 
				
			||||||
 | 
										distro,
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									out = append(
 | 
				
			||||||
 | 
										out,
 | 
				
			||||||
 | 
										opts.Name+"_"+arch+"_"+distro,
 | 
				
			||||||
 | 
										opts.Name+"_"+distro,
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if opts.Name == "" {
 | 
				
			||||||
 | 
								out = append(out, arch)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								out = append(out, opts.Name+"_"+arch)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out = append(out, opts.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for index, item := range out {
 | 
				
			||||||
 | 
							out[index] = strings.ReplaceAll(item, "-", "_")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 {
 | 
				
			||||||
 | 
						out := &Opts{}
 | 
				
			||||||
 | 
						*out = *o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out.Name = name
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *Opts) WithOverrides(v bool) *Opts {
 | 
				
			||||||
 | 
						out := &Opts{}
 | 
				
			||||||
 | 
						*out = *o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out.Overrides = v
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *Opts) WithLikeDistros(v bool) *Opts {
 | 
				
			||||||
 | 
						out := &Opts{}
 | 
				
			||||||
 | 
						*out = *o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										154
									
								
								internal/overrides/overrides_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								internal/overrides/overrides_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/distro"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/overrides"
 | 
				
			||||||
 | 
						"golang.org/x/text/language"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var info = &distro.OSRelease{
 | 
				
			||||||
 | 
						ID:   "centos",
 | 
				
			||||||
 | 
						Like: []string{"rhel", "fedora"},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestResolve(t *testing.T) {
 | 
				
			||||||
 | 
						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",
 | 
				
			||||||
 | 
							"rhel",
 | 
				
			||||||
 | 
							"amd64_fedora",
 | 
				
			||||||
 | 
							"fedora",
 | 
				
			||||||
 | 
							"amd64",
 | 
				
			||||||
 | 
							"",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(names, expected) {
 | 
				
			||||||
 | 
							t.Errorf("expected %v, got %v", expected, names)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestResolveName(t *testing.T) {
 | 
				
			||||||
 | 
						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",
 | 
				
			||||||
 | 
							"deps_centos",
 | 
				
			||||||
 | 
							"deps_amd64_rhel",
 | 
				
			||||||
 | 
							"deps_rhel",
 | 
				
			||||||
 | 
							"deps_amd64_fedora",
 | 
				
			||||||
 | 
							"deps_fedora",
 | 
				
			||||||
 | 
							"deps_amd64",
 | 
				
			||||||
 | 
							"deps",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(names, expected) {
 | 
				
			||||||
 | 
							t.Errorf("expected %v, got %v", expected, names)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestResolveNoLikeDistros(t *testing.T) {
 | 
				
			||||||
 | 
						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",
 | 
				
			||||||
 | 
							"centos",
 | 
				
			||||||
 | 
							"amd64",
 | 
				
			||||||
 | 
							"",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(names, expected) {
 | 
				
			||||||
 | 
							t.Errorf("expected %v, got %v", expected, names)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestResolveNoOverrides(t *testing.T) {
 | 
				
			||||||
 | 
						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"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(names, expected) {
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								internal/pager/highlighting.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								internal/pager/highlighting.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/alecthomas/chroma/v2/quick"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func SyntaxHighlightBash(r io.Reader, style string) (string, error) {
 | 
				
			||||||
 | 
						data, err := io.ReadAll(r)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						w := &bytes.Buffer{}
 | 
				
			||||||
 | 
						err = quick.Highlight(w, string(data), "bash", "terminal", style)
 | 
				
			||||||
 | 
						return w.String(), err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										142
									
								
								internal/pager/pager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								internal/pager/pager.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/charmbracelet/bubbles/viewport"
 | 
				
			||||||
 | 
						tea "github.com/charmbracelet/bubbletea"
 | 
				
			||||||
 | 
						"github.com/charmbracelet/lipgloss"
 | 
				
			||||||
 | 
						"github.com/muesli/reflow/wordwrap"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						titleStyle lipgloss.Style
 | 
				
			||||||
 | 
						infoStyle  lipgloss.Style
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						b := lipgloss.RoundedBorder()
 | 
				
			||||||
 | 
						b.Right = "\u251C"
 | 
				
			||||||
 | 
						titleStyle = lipgloss.NewStyle().BorderStyle(b).Padding(0, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b = lipgloss.RoundedBorder()
 | 
				
			||||||
 | 
						b.Left = "\u2524"
 | 
				
			||||||
 | 
						infoStyle = titleStyle.Copy().BorderStyle(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Pager struct {
 | 
				
			||||||
 | 
						model pagerModel
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(name, content string) *Pager {
 | 
				
			||||||
 | 
						return &Pager{
 | 
				
			||||||
 | 
							model: pagerModel{
 | 
				
			||||||
 | 
								name:    name,
 | 
				
			||||||
 | 
								content: content,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *Pager) Run() error {
 | 
				
			||||||
 | 
						prog := tea.NewProgram(
 | 
				
			||||||
 | 
							p.model,
 | 
				
			||||||
 | 
							tea.WithMouseCellMotion(),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := prog.Run()
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type pagerModel struct {
 | 
				
			||||||
 | 
						name     string
 | 
				
			||||||
 | 
						content  string
 | 
				
			||||||
 | 
						ready    bool
 | 
				
			||||||
 | 
						viewport viewport.Model
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pm pagerModel) Init() tea.Cmd {
 | 
				
			||||||
 | 
						return tea.ClearScreen
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pm pagerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							cmd  tea.Cmd
 | 
				
			||||||
 | 
							cmds []tea.Cmd
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch msg := msg.(type) {
 | 
				
			||||||
 | 
						case tea.KeyMsg:
 | 
				
			||||||
 | 
							k := msg.String()
 | 
				
			||||||
 | 
							if k == "ctrl+c" || k == "q" || k == "esc" {
 | 
				
			||||||
 | 
								return pm, tea.Quit
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case tea.WindowSizeMsg:
 | 
				
			||||||
 | 
							headerHeight := lipgloss.Height(pm.headerView())
 | 
				
			||||||
 | 
							footerHeight := lipgloss.Height(pm.footerView())
 | 
				
			||||||
 | 
							verticalMarginHeight := headerHeight + footerHeight
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !pm.ready {
 | 
				
			||||||
 | 
								pm.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight)
 | 
				
			||||||
 | 
								pm.viewport.HighPerformanceRendering = true
 | 
				
			||||||
 | 
								pm.viewport.YPosition = headerHeight + 1
 | 
				
			||||||
 | 
								pm.viewport.SetContent(wordwrap.String(pm.content, msg.Width))
 | 
				
			||||||
 | 
								pm.ready = true
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								pm.viewport.Width = msg.Width
 | 
				
			||||||
 | 
								pm.viewport.Height = msg.Height - verticalMarginHeight
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cmds = append(cmds, viewport.Sync(pm.viewport))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Handle keyboard and mouse events in the viewport
 | 
				
			||||||
 | 
						pm.viewport, cmd = pm.viewport.Update(msg)
 | 
				
			||||||
 | 
						cmds = append(cmds, cmd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pm, tea.Batch(cmds...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pm pagerModel) View() string {
 | 
				
			||||||
 | 
						if !pm.ready {
 | 
				
			||||||
 | 
							return "\n  Initializing..."
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Sprintf("%s\n%s\n%s", pm.headerView(), pm.viewport.View(), pm.footerView())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pm pagerModel) headerView() string {
 | 
				
			||||||
 | 
						title := titleStyle.Render(pm.name)
 | 
				
			||||||
 | 
						line := strings.Repeat("─", max(0, pm.viewport.Width-lipgloss.Width(title)))
 | 
				
			||||||
 | 
						return lipgloss.JoinHorizontal(lipgloss.Center, title, line)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pm pagerModel) footerView() string {
 | 
				
			||||||
 | 
						info := infoStyle.Render(fmt.Sprintf("%3.f%%", pm.viewport.ScrollPercent()*100))
 | 
				
			||||||
 | 
						line := strings.Repeat("─", max(0, pm.viewport.Width-lipgloss.Width(info)))
 | 
				
			||||||
 | 
						return lipgloss.JoinHorizontal(lipgloss.Center, line, info)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func max(a, b int) int {
 | 
				
			||||||
 | 
						if a > b {
 | 
				
			||||||
 | 
							return a
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,60 +1,76 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package repos
 | 
					package repos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/genjidb/genji"
 | 
						"github.com/jmoiron/sqlx"
 | 
				
			||||||
	"github.com/genjidb/genji/document"
 | 
					 | 
				
			||||||
	"github.com/genjidb/genji/types"
 | 
					 | 
				
			||||||
	"go.arsenm.dev/lure/internal/db"
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func FindPkgs(gdb *genji.DB, pkgs []string) (map[string][]db.Package, []string, error) {
 | 
					// FindPkgs looks for packages matching the inputs inside the database.
 | 
				
			||||||
 | 
					// It returns a map that maps the package name input to the packages found for it.
 | 
				
			||||||
 | 
					// It also returns a slice that contains the names of all packages that were not found.
 | 
				
			||||||
 | 
					func FindPkgs(gdb *sqlx.DB, pkgs []string) (map[string][]db.Package, []string, error) {
 | 
				
			||||||
	found := map[string][]db.Package{}
 | 
						found := map[string][]db.Package{}
 | 
				
			||||||
	notFound := []string(nil)
 | 
						notFound := []string(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, pkgName := range pkgs {
 | 
						for _, pkgName := range pkgs {
 | 
				
			||||||
 | 
							if pkgName == "" {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		result, err := db.GetPkgs(gdb, "name LIKE ?", pkgName)
 | 
							result, err := db.GetPkgs(gdb, "name LIKE ?", pkgName)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		added := 0
 | 
							added := 0
 | 
				
			||||||
		err = result.Iterate(func(d types.Document) error {
 | 
							for result.Next() {
 | 
				
			||||||
			var pkg db.Package
 | 
								var pkg db.Package
 | 
				
			||||||
			err = document.StructScan(d, &pkg)
 | 
								err = result.StructScan(&pkg)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return nil, nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			added++
 | 
								added++
 | 
				
			||||||
			found[pkgName] = append(found[pkgName], pkg)
 | 
								found[pkgName] = append(found[pkgName], pkg)
 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		result.Close()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, nil, err
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							result.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if added == 0 {
 | 
							if added == 0 {
 | 
				
			||||||
			result, err := db.GetPkgs(gdb, "? IN provides", pkgName)
 | 
								result, err := db.GetPkgs(gdb, "json_array_contains(provides, ?)", pkgName)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, nil, err
 | 
									return nil, nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			err = result.Iterate(func(d types.Document) error {
 | 
								for result.Next() {
 | 
				
			||||||
				var pkg db.Package
 | 
									var pkg db.Package
 | 
				
			||||||
				err = document.StructScan(d, &pkg)
 | 
									err = result.StructScan(&pkg)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return err
 | 
										return nil, nil, err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				added++
 | 
									added++
 | 
				
			||||||
				found[pkgName] = append(found[pkgName], pkg)
 | 
									found[pkgName] = append(found[pkgName], pkg)
 | 
				
			||||||
				return nil
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
			result.Close()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return nil, nil, err
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result.Close()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if added == 0 {
 | 
							if added == 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										148
									
								
								internal/repos/find_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								internal/repos/find_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"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 := db.Open(":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setCfgDirs(t)
 | 
				
			||||||
 | 
						defer removeCacheDir(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = repos.Pull(ctx, gdb, []types.Repo{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name: "default",
 | 
				
			||||||
 | 
								URL:  "https://github.com/Arsen6331/lure-repo.git",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						found, notFound, err := repos.FindPkgs(gdb, []string{"itd", "nonexistentpackage1", "nonexistentpackage2"})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(notFound, []string{"nonexistentpackage1", "nonexistentpackage2"}) {
 | 
				
			||||||
 | 
							t.Errorf("Expected 'nonexistentpackage{1,2} not to be found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(found) != 1 {
 | 
				
			||||||
 | 
							t.Errorf("Expected 1 package found, got %d", len(found))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						itdPkgs, ok := found["itd"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("Expected 'itd' packages to be found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(itdPkgs) < 2 {
 | 
				
			||||||
 | 
							t.Errorf("Expected two 'itd' packages to be found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, pkg := range itdPkgs {
 | 
				
			||||||
 | 
							if !strings.HasPrefix(pkg.Name, "itd") {
 | 
				
			||||||
 | 
								t.Errorf("Expected package name of all found packages to start with 'itd', got %s on element %d", pkg.Name, i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestFindPkgsEmpty(t *testing.T) {
 | 
				
			||||||
 | 
						gdb, err := db.Open(":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setCfgDirs(t)
 | 
				
			||||||
 | 
						defer removeCacheDir(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, db.Package{
 | 
				
			||||||
 | 
							Name:       "test1",
 | 
				
			||||||
 | 
							Repository: "default",
 | 
				
			||||||
 | 
							Version:    "0.0.1",
 | 
				
			||||||
 | 
							Release:    1,
 | 
				
			||||||
 | 
							Description: db.NewJSON(map[string]string{
 | 
				
			||||||
 | 
								"en": "Test package 1",
 | 
				
			||||||
 | 
								"ru": "Проверочный пакет 1",
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							Provides: db.NewJSON([]string{""}),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = db.InsertPackage(gdb, db.Package{
 | 
				
			||||||
 | 
							Name:       "test2",
 | 
				
			||||||
 | 
							Repository: "default",
 | 
				
			||||||
 | 
							Version:    "0.0.1",
 | 
				
			||||||
 | 
							Release:    1,
 | 
				
			||||||
 | 
							Description: db.NewJSON(map[string]string{
 | 
				
			||||||
 | 
								"en": "Test package 2",
 | 
				
			||||||
 | 
								"ru": "Проверочный пакет 2",
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							Provides: db.NewJSON([]string{"test"}),
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						found, notFound, err := repos.FindPkgs(gdb, []string{"test", ""})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(notFound) != 0 {
 | 
				
			||||||
 | 
							t.Errorf("Expected all packages to be found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(found) != 1 {
 | 
				
			||||||
 | 
							t.Errorf("Expected 1 package found, got %d", len(found))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testPkgs, ok := found["test"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("Expected 'test' packages to be found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(testPkgs) != 1 {
 | 
				
			||||||
 | 
							t.Errorf("Expected one 'test' package to be found, got %d", len(testPkgs))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if testPkgs[0].Name != "test2" {
 | 
				
			||||||
 | 
							t.Errorf("Expected 'test2' package, got '%s'", testPkgs[0].Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,3 +1,21 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package repos
 | 
					package repos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@@ -7,23 +25,25 @@ import (
 | 
				
			|||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/genjidb/genji"
 | 
					 | 
				
			||||||
	"github.com/go-git/go-billy/v5"
 | 
						"github.com/go-git/go-billy/v5"
 | 
				
			||||||
	"github.com/go-git/go-billy/v5/osfs"
 | 
						"github.com/go-git/go-billy/v5/osfs"
 | 
				
			||||||
	"github.com/go-git/go-git/v5"
 | 
						"github.com/go-git/go-git/v5"
 | 
				
			||||||
	"github.com/go-git/go-git/v5/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing/format/diff"
 | 
				
			||||||
 | 
						"github.com/jmoiron/sqlx"
 | 
				
			||||||
	"github.com/pelletier/go-toml/v2"
 | 
						"github.com/pelletier/go-toml/v2"
 | 
				
			||||||
	"go.arsenm.dev/logger/log"
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
	"go.arsenm.dev/lure/distro"
 | 
						"go.arsenm.dev/lure/distro"
 | 
				
			||||||
	"go.arsenm.dev/lure/download"
 | 
					 | 
				
			||||||
	"go.arsenm.dev/lure/internal/config"
 | 
						"go.arsenm.dev/lure/internal/config"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/db"
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/shutils"
 | 
						"go.arsenm.dev/lure/internal/shutils"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/shutils/decoder"
 | 
						"go.arsenm.dev/lure/internal/shutils/decoder"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/types"
 | 
						"go.arsenm.dev/lure/internal/types"
 | 
				
			||||||
	"go.arsenm.dev/lure/vercmp"
 | 
						"go.arsenm.dev/lure/vercmp"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/expand"
 | 
				
			||||||
	"mvdan.cc/sh/v3/interp"
 | 
						"mvdan.cc/sh/v3/interp"
 | 
				
			||||||
	"mvdan.cc/sh/v3/syntax"
 | 
						"mvdan.cc/sh/v3/syntax"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -31,7 +51,7 @@ import (
 | 
				
			|||||||
// Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned
 | 
					// Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned
 | 
				
			||||||
// and its packages will be written to the DB. If it does exist, it will be pulled.
 | 
					// and its packages will be written to the DB. If it does exist, it will be pulled.
 | 
				
			||||||
// In this case, only changed packages will be processed.
 | 
					// In this case, only changed packages will be processed.
 | 
				
			||||||
func Pull(ctx context.Context, gdb *genji.DB, repos []types.Repo) error {
 | 
					func Pull(ctx context.Context, gdb *sqlx.DB, repos []types.Repo) error {
 | 
				
			||||||
	for _, repo := range repos {
 | 
						for _, repo := range repos {
 | 
				
			||||||
		repoURL, err := url.Parse(repo.URL)
 | 
							repoURL, err := url.Parse(repo.URL)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -68,15 +88,26 @@ func Pull(ctx context.Context, gdb *genji.DB, repos []types.Repo) error {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			repoFS = w.Filesystem
 | 
								repoFS = w.Filesystem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !errors.Is(err, git.NoErrAlreadyUpToDate) {
 | 
								// Make sure the DB is created even if the repo is up to date
 | 
				
			||||||
 | 
								if !errors.Is(err, git.NoErrAlreadyUpToDate) || !config.DBPresent {
 | 
				
			||||||
				new, err := r.Head()
 | 
									new, err := r.Head()
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					return err
 | 
										return err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				err = processRepoChanges(ctx, repo, r, old, new, gdb)
 | 
									// If the DB was not present at startup, that means it's
 | 
				
			||||||
				if err != nil {
 | 
									// empty. In this case, we need to update the DB fully
 | 
				
			||||||
					return err
 | 
									// rather than just incrementally.
 | 
				
			||||||
 | 
									if config.DBPresent {
 | 
				
			||||||
 | 
										err = processRepoChanges(ctx, repo, r, w, old, new, gdb)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										err = processRepoFull(ctx, repo, repoDir, gdb)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
@@ -90,13 +121,9 @@ func Pull(ctx context.Context, gdb *genji.DB, repos []types.Repo) error {
 | 
				
			|||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !strings.HasPrefix(repoURL.Scheme, "git+") {
 | 
								_, err = git.PlainCloneContext(ctx, repoDir, false, &git.CloneOptions{
 | 
				
			||||||
				repoURL.Scheme = "git+" + repoURL.Scheme
 | 
									URL:      repoURL.String(),
 | 
				
			||||||
			}
 | 
									Progress: os.Stderr,
 | 
				
			||||||
 | 
					 | 
				
			||||||
			err = download.Get(ctx, download.GetOptions{
 | 
					 | 
				
			||||||
				SourceURL:   repoURL.String(),
 | 
					 | 
				
			||||||
				Destination: repoDir,
 | 
					 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
@@ -144,7 +171,7 @@ type action struct {
 | 
				
			|||||||
	File string
 | 
						File string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, old, new *plumbing.Reference, gdb *genji.DB) error {
 | 
					func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference, gdb *sqlx.DB) error {
 | 
				
			||||||
	oldCommit, err := r.CommitObject(old.Hash())
 | 
						oldCommit, err := r.CommitObject(old.Hash())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -163,6 +190,11 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
 | 
				
			|||||||
	var actions []action
 | 
						var actions []action
 | 
				
			||||||
	for _, fp := range patch.FilePatches() {
 | 
						for _, fp := range patch.FilePatches() {
 | 
				
			||||||
		from, to := fp.Files()
 | 
							from, to := fp.Files()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !isValid(from, to) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if to == nil {
 | 
							if to == nil {
 | 
				
			||||||
			actions = append(actions, action{
 | 
								actions = append(actions, action{
 | 
				
			||||||
				Type: actionDelete,
 | 
									Type: actionDelete,
 | 
				
			||||||
@@ -194,21 +226,29 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repoDir := w.Filesystem.Root()
 | 
				
			||||||
	parser := syntax.NewParser()
 | 
						parser := syntax.NewParser()
 | 
				
			||||||
	runner, err := interp.New(
 | 
					 | 
				
			||||||
		interp.StatHandler(shutils.NopStat),
 | 
					 | 
				
			||||||
		interp.ExecHandler(shutils.NopExec),
 | 
					 | 
				
			||||||
		interp.OpenHandler(shutils.NopOpen),
 | 
					 | 
				
			||||||
		interp.ReadDirHandler(shutils.NopReadDir),
 | 
					 | 
				
			||||||
		interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, action := range actions {
 | 
						for _, action := range actions {
 | 
				
			||||||
 | 
							env := append(os.Environ(), "scriptdir="+filepath.Dir(filepath.Join(repoDir, action.File)))
 | 
				
			||||||
 | 
							runner, err := interp.New(
 | 
				
			||||||
 | 
								interp.Env(expand.ListEnviron(env...)),
 | 
				
			||||||
 | 
								interp.ExecHandler(shutils.NopExec),
 | 
				
			||||||
 | 
								interp.ReadDirHandler(shutils.RestrictedReadDir(repoDir)),
 | 
				
			||||||
 | 
								interp.StatHandler(shutils.RestrictedStat(repoDir)),
 | 
				
			||||||
 | 
								interp.OpenHandler(shutils.RestrictedOpen(repoDir)),
 | 
				
			||||||
 | 
								interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch action.Type {
 | 
							switch action.Type {
 | 
				
			||||||
		case actionDelete:
 | 
							case actionDelete:
 | 
				
			||||||
 | 
								if filepath.Base(action.File) != "lure.sh" {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			scriptFl, err := oldCommit.File(action.File)
 | 
								scriptFl, err := oldCommit.File(action.File)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil
 | 
									return nil
 | 
				
			||||||
@@ -230,6 +270,10 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
 | 
				
			|||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case actionUpdate:
 | 
							case actionUpdate:
 | 
				
			||||||
 | 
								if filepath.Base(action.File) != "lure.sh" {
 | 
				
			||||||
 | 
									action.File = filepath.Join(filepath.Dir(action.File), "lure.sh")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			scriptFl, err := newCommit.File(action.File)
 | 
								scriptFl, err := newCommit.File(action.File)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil
 | 
									return nil
 | 
				
			||||||
@@ -241,8 +285,11 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			pkg := db.Package{
 | 
								pkg := db.Package{
 | 
				
			||||||
				Depends:      map[string][]string{},
 | 
									Description:  db.NewJSON(map[string]string{}),
 | 
				
			||||||
				BuildDepends: 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,
 | 
									Repository:   repo.Name,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -263,7 +310,23 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *genji.DB) error {
 | 
					// isValid makes sure the path of the file being updated is valid.
 | 
				
			||||||
 | 
					// It checks to make sure the file is not within a nested directory
 | 
				
			||||||
 | 
					// and that it is called lure.sh.
 | 
				
			||||||
 | 
					func isValid(from, to diff.File) bool {
 | 
				
			||||||
 | 
						var path string
 | 
				
			||||||
 | 
						if from != nil {
 | 
				
			||||||
 | 
							path = from.Path()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if to != nil {
 | 
				
			||||||
 | 
							path = to.Path()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						match, _ := filepath.Match("*/*.sh", path)
 | 
				
			||||||
 | 
						return match
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *sqlx.DB) error {
 | 
				
			||||||
	glob := filepath.Join(repoDir, "/*/lure.sh")
 | 
						glob := filepath.Join(repoDir, "/*/lure.sh")
 | 
				
			||||||
	matches, err := filepath.Glob(glob)
 | 
						matches, err := filepath.Glob(glob)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -271,26 +334,32 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string, gdb *
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	parser := syntax.NewParser()
 | 
						parser := syntax.NewParser()
 | 
				
			||||||
	runner, err := interp.New(
 | 
					 | 
				
			||||||
		interp.StatHandler(shutils.NopStat),
 | 
					 | 
				
			||||||
		interp.ExecHandler(shutils.NopExec),
 | 
					 | 
				
			||||||
		interp.OpenHandler(shutils.NopOpen),
 | 
					 | 
				
			||||||
		interp.ReadDirHandler(shutils.NopReadDir),
 | 
					 | 
				
			||||||
		interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, match := range matches {
 | 
						for _, match := range matches {
 | 
				
			||||||
 | 
							env := append(os.Environ(), "scriptdir="+filepath.Dir(match))
 | 
				
			||||||
 | 
							runner, err := interp.New(
 | 
				
			||||||
 | 
								interp.Env(expand.ListEnviron(env...)),
 | 
				
			||||||
 | 
								interp.ExecHandler(shutils.NopExec),
 | 
				
			||||||
 | 
								interp.ReadDirHandler(shutils.RestrictedReadDir(repoDir)),
 | 
				
			||||||
 | 
								interp.StatHandler(shutils.RestrictedStat(repoDir)),
 | 
				
			||||||
 | 
								interp.OpenHandler(shutils.RestrictedOpen(repoDir)),
 | 
				
			||||||
 | 
								interp.StdIO(shutils.NopRWC{}, shutils.NopRWC{}, shutils.NopRWC{}),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		scriptFl, err := os.Open(match)
 | 
							scriptFl, err := os.Open(match)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pkg := db.Package{
 | 
							pkg := db.Package{
 | 
				
			||||||
			Depends:      map[string][]string{},
 | 
								Description:  db.NewJSON(map[string]string{}),
 | 
				
			||||||
			BuildDepends: 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,
 | 
								Repository:   repo.Name,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -329,20 +398,34 @@ func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runn
 | 
				
			|||||||
	return d.DecodeVars(pkg)
 | 
						return d.DecodeVars(pkg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var overridable = map[string]string{
 | 
				
			||||||
 | 
						"deps":       "Depends",
 | 
				
			||||||
 | 
						"build_deps": "BuildDepends",
 | 
				
			||||||
 | 
						"desc":       "Description",
 | 
				
			||||||
 | 
						"homepage":   "Homepage",
 | 
				
			||||||
 | 
						"maintainer": "Maintainer",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func resolveOverrides(runner *interp.Runner, pkg *db.Package) {
 | 
					func resolveOverrides(runner *interp.Runner, pkg *db.Package) {
 | 
				
			||||||
 | 
						pkgVal := reflect.ValueOf(pkg).Elem()
 | 
				
			||||||
	for name, val := range runner.Vars {
 | 
						for name, val := range runner.Vars {
 | 
				
			||||||
		if strings.HasPrefix(name, "deps") {
 | 
							for prefix, field := range overridable {
 | 
				
			||||||
			override := strings.TrimPrefix(name, "deps")
 | 
								if strings.HasPrefix(name, prefix) {
 | 
				
			||||||
			override = strings.TrimPrefix(override, "_")
 | 
									override := strings.TrimPrefix(name, prefix)
 | 
				
			||||||
 | 
									override = strings.TrimPrefix(override, "_")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			pkg.Depends[override] = val.List
 | 
									field := pkgVal.FieldByName(field)
 | 
				
			||||||
		} else if strings.HasPrefix(name, "build_deps") {
 | 
									varVal := field.FieldByName("Val")
 | 
				
			||||||
			override := strings.TrimPrefix(name, "build_deps")
 | 
									varType := varVal.Type()
 | 
				
			||||||
			override = strings.TrimPrefix(override, "_")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			pkg.BuildDepends[override] = val.List
 | 
									switch varType.Elem().String() {
 | 
				
			||||||
		} else {
 | 
									case "[]string":
 | 
				
			||||||
			continue
 | 
										varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.List))
 | 
				
			||||||
 | 
									case "string":
 | 
				
			||||||
 | 
										varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.Str))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										107
									
								
								internal/repos/pull_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								internal/repos/pull_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/config"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/repos"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func setCfgDirs(t *testing.T) {
 | 
				
			||||||
 | 
						t.Helper()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						config.CacheDir, err = os.MkdirTemp("/tmp", "lure-pull-test.*")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config.RepoDir = filepath.Join(config.CacheDir, "repo")
 | 
				
			||||||
 | 
						config.PkgsDir = filepath.Join(config.CacheDir, "pkgs")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = os.MkdirAll(config.RepoDir, 0o755)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = os.MkdirAll(config.PkgsDir, 0o755)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config.DBPath = filepath.Join(config.CacheDir, "db")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func removeCacheDir(t *testing.T) {
 | 
				
			||||||
 | 
						t.Helper()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := os.RemoveAll(config.CacheDir)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPull(t *testing.T) {
 | 
				
			||||||
 | 
						gdb, err := db.Open(":memory:")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer gdb.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setCfgDirs(t)
 | 
				
			||||||
 | 
						defer removeCacheDir(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = repos.Pull(ctx, gdb, []types.Repo{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name: "default",
 | 
				
			||||||
 | 
								URL:  "https://github.com/Arsen6331/lure-repo.git",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := db.GetPkgs(gdb, "name LIKE 'itd%'")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var pkgAmt int
 | 
				
			||||||
 | 
						for result.Next() {
 | 
				
			||||||
 | 
							var dbPkg db.Package
 | 
				
			||||||
 | 
							err = result.StructScan(&dbPkg)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							pkgAmt++
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if pkgAmt < 2 {
 | 
				
			||||||
 | 
							t.Errorf("Expected 2 packages to match, got %d", pkgAmt)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -21,21 +21,19 @@ package decoder
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"runtime"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/mitchellh/mapstructure"
 | 
						"github.com/mitchellh/mapstructure"
 | 
				
			||||||
	"go.arsenm.dev/lure/distro"
 | 
						"go.arsenm.dev/lure/distro"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/cpu"
 | 
						"go.arsenm.dev/lure/internal/overrides"
 | 
				
			||||||
	"golang.org/x/exp/slices"
 | 
						"golang.org/x/exp/slices"
 | 
				
			||||||
	"mvdan.cc/sh/v3/expand"
 | 
						"mvdan.cc/sh/v3/expand"
 | 
				
			||||||
	"mvdan.cc/sh/v3/interp"
 | 
						"mvdan.cc/sh/v3/interp"
 | 
				
			||||||
	"mvdan.cc/sh/v3/syntax"
 | 
						"mvdan.cc/sh/v3/syntax"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var ErrInvalidType = errors.New("val must be a pointer to a struct")
 | 
					var ErrNotPointerToStruct = errors.New("val must be a pointer to a struct")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type VarNotFoundError struct {
 | 
					type VarNotFoundError struct {
 | 
				
			||||||
	name string
 | 
						name string
 | 
				
			||||||
@@ -45,6 +43,16 @@ func (nfe VarNotFoundError) Error() string {
 | 
				
			|||||||
	return "required variable '" + nfe.name + "' could not be found"
 | 
						return "required variable '" + nfe.name + "' could not be found"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InvalidTypeError struct {
 | 
				
			||||||
 | 
						name    string
 | 
				
			||||||
 | 
						vartype string
 | 
				
			||||||
 | 
						exptype string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ite InvalidTypeError) Error() string {
 | 
				
			||||||
 | 
						return "variable '" + ite.name + "' is of type " + ite.vartype + ", but " + ite.exptype + " is expected"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Decoder provides methods for decoding variable values
 | 
					// Decoder provides methods for decoding variable values
 | 
				
			||||||
type Decoder struct {
 | 
					type Decoder struct {
 | 
				
			||||||
	info   *distro.OSRelease
 | 
						info   *distro.OSRelease
 | 
				
			||||||
@@ -70,8 +78,20 @@ func (d *Decoder) DecodeVar(name string, val any) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
 | 
						dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
 | 
				
			||||||
		WeaklyTypedInput: true,
 | 
							WeaklyTypedInput: true,
 | 
				
			||||||
		Result:           val,
 | 
							DecodeHook: mapstructure.DecodeHookFuncValue(func(from, to reflect.Value) (interface{}, error) {
 | 
				
			||||||
		TagName:          "sh",
 | 
								if strings.Contains(to.Type().String(), "db.JSON") {
 | 
				
			||||||
 | 
									valType := to.FieldByName("Val").Type()
 | 
				
			||||||
 | 
									if !from.Type().AssignableTo(valType) {
 | 
				
			||||||
 | 
										return nil, InvalidTypeError{name, from.Type().String(), valType.String()}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									to.FieldByName("Val").Set(from)
 | 
				
			||||||
 | 
									return to, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return from.Interface(), nil
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							Result:  val,
 | 
				
			||||||
 | 
							TagName: "sh",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -92,11 +112,11 @@ func (d *Decoder) DecodeVar(name string, val any) error {
 | 
				
			|||||||
func (d *Decoder) DecodeVars(val any) error {
 | 
					func (d *Decoder) DecodeVars(val any) error {
 | 
				
			||||||
	valKind := reflect.TypeOf(val).Kind()
 | 
						valKind := reflect.TypeOf(val).Kind()
 | 
				
			||||||
	if valKind != reflect.Pointer {
 | 
						if valKind != reflect.Pointer {
 | 
				
			||||||
		return ErrInvalidType
 | 
							return ErrNotPointerToStruct
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		elemKind := reflect.TypeOf(val).Elem().Kind()
 | 
							elemKind := reflect.TypeOf(val).Elem().Kind()
 | 
				
			||||||
		if elemKind != reflect.Struct {
 | 
							if elemKind != reflect.Struct {
 | 
				
			||||||
			return ErrInvalidType
 | 
								return ErrNotPointerToStruct
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -162,7 +182,11 @@ func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Decoder) getFunc(name string) *syntax.Stmt {
 | 
					func (d *Decoder) getFunc(name string) *syntax.Stmt {
 | 
				
			||||||
	names := d.genPossibleNames(name)
 | 
						names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, fnName := range names {
 | 
						for _, fnName := range names {
 | 
				
			||||||
		fn, ok := d.runner.Funcs[fnName]
 | 
							fn, ok := d.runner.Funcs[fnName]
 | 
				
			||||||
		if ok {
 | 
							if ok {
 | 
				
			||||||
@@ -175,7 +199,11 @@ func (d *Decoder) getFunc(name string) *syntax.Stmt {
 | 
				
			|||||||
// getVar gets a variable based on its name, taking into account
 | 
					// getVar gets a variable based on its name, taking into account
 | 
				
			||||||
// override variables and nameref variables.
 | 
					// override variables and nameref variables.
 | 
				
			||||||
func (d *Decoder) getVar(name string) *expand.Variable {
 | 
					func (d *Decoder) getVar(name string) *expand.Variable {
 | 
				
			||||||
	names := d.genPossibleNames(name)
 | 
						names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, varName := range names {
 | 
						for _, varName := range names {
 | 
				
			||||||
		val, ok := d.runner.Vars[varName]
 | 
							val, ok := d.runner.Vars[varName]
 | 
				
			||||||
		if ok {
 | 
							if ok {
 | 
				
			||||||
@@ -193,43 +221,3 @@ func (d *Decoder) getVar(name string) *expand.Variable {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// genPossibleNames generates a slice of the possible names that
 | 
					 | 
				
			||||||
// could be used in the order that they should be checked
 | 
					 | 
				
			||||||
func (d *Decoder) genPossibleNames(name string) []string {
 | 
					 | 
				
			||||||
	if !d.Overrides {
 | 
					 | 
				
			||||||
		return []string{name}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	architectures := []string{runtime.GOARCH}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if runtime.GOARCH == "arm" {
 | 
					 | 
				
			||||||
		// More specific goes first
 | 
					 | 
				
			||||||
		architectures[0] = cpu.ARMVariant()
 | 
					 | 
				
			||||||
		architectures = append(architectures, "arm")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	distros := []string{d.info.ID}
 | 
					 | 
				
			||||||
	if d.LikeDistros {
 | 
					 | 
				
			||||||
		distros = append(distros, d.info.Like...)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var out []string
 | 
					 | 
				
			||||||
	for _, arch := range architectures {
 | 
					 | 
				
			||||||
		for _, distro := range distros {
 | 
					 | 
				
			||||||
			out = append(
 | 
					 | 
				
			||||||
				out,
 | 
					 | 
				
			||||||
				fmt.Sprintf("%s_%s_%s", name, arch, distro),
 | 
					 | 
				
			||||||
				fmt.Sprintf("%s_%s", name, distro),
 | 
					 | 
				
			||||||
			)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		out = append(out, fmt.Sprintf("%s_%s", name, arch))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	out = append(out, name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for index, item := range out {
 | 
					 | 
				
			||||||
		out[index] = strings.ReplaceAll(item, "-", "_")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return out
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										224
									
								
								internal/shutils/decoder/decoder_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								internal/shutils/decoder/decoder_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/distro"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/shutils/decoder"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/interp"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/syntax"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const testScript = `
 | 
				
			||||||
 | 
						name='test'
 | 
				
			||||||
 | 
						version='0.0.1'
 | 
				
			||||||
 | 
						release=1
 | 
				
			||||||
 | 
						epoch=2
 | 
				
			||||||
 | 
						desc="Test package"
 | 
				
			||||||
 | 
						homepage='https://lure.arsenm.dev'
 | 
				
			||||||
 | 
						maintainer='Arsen Musayelyan <arsen@arsenm.dev>'
 | 
				
			||||||
 | 
						architectures=('arm64' 'amd64')
 | 
				
			||||||
 | 
						license=('GPL-3.0-or-later')
 | 
				
			||||||
 | 
						provides=('test')
 | 
				
			||||||
 | 
						conflicts=('test')
 | 
				
			||||||
 | 
						replaces=('test-old')
 | 
				
			||||||
 | 
						replaces_test_os=('test-legacy')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						deps=('sudo')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						build_deps=('golang')
 | 
				
			||||||
 | 
						build_deps_arch=('go')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test() {
 | 
				
			||||||
 | 
							echo "Test"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						package() {
 | 
				
			||||||
 | 
							install-binary test
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var osRelease = &distro.OSRelease{
 | 
				
			||||||
 | 
						ID:   "test_os",
 | 
				
			||||||
 | 
						Like: []string{"arch"},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDecodeVars(t *testing.T) {
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "lure.sh")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runner, err := interp.New()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = runner.Run(ctx, fl)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dec := decoder.New(osRelease, runner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var bv BuildVars
 | 
				
			||||||
 | 
						err = dec.DecodeVars(&bv)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						expected := BuildVars{
 | 
				
			||||||
 | 
							Name:          "test",
 | 
				
			||||||
 | 
							Version:       "0.0.1",
 | 
				
			||||||
 | 
							Release:       1,
 | 
				
			||||||
 | 
							Epoch:         2,
 | 
				
			||||||
 | 
							Description:   "Test package",
 | 
				
			||||||
 | 
							Homepage:      "https://lure.arsenm.dev",
 | 
				
			||||||
 | 
							Maintainer:    "Arsen Musayelyan <arsen@arsenm.dev>",
 | 
				
			||||||
 | 
							Architectures: []string{"arm64", "amd64"},
 | 
				
			||||||
 | 
							Licenses:      []string{"GPL-3.0-or-later"},
 | 
				
			||||||
 | 
							Provides:      []string{"test"},
 | 
				
			||||||
 | 
							Conflicts:     []string{"test"},
 | 
				
			||||||
 | 
							Replaces:      []string{"test-legacy"},
 | 
				
			||||||
 | 
							Depends:       []string{"sudo"},
 | 
				
			||||||
 | 
							BuildDepends:  []string{"go"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(bv, expected) {
 | 
				
			||||||
 | 
							t.Errorf("Expected %v, got %v", expected, bv)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDecodeVarsMissing(t *testing.T) {
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const testScript = `
 | 
				
			||||||
 | 
							name='test'
 | 
				
			||||||
 | 
							epoch=2
 | 
				
			||||||
 | 
							desc="Test package"
 | 
				
			||||||
 | 
							homepage='https://lure.arsenm.dev'
 | 
				
			||||||
 | 
							maintainer='Arsen Musayelyan <arsen@arsenm.dev>'
 | 
				
			||||||
 | 
							architectures=('arm64' 'amd64')
 | 
				
			||||||
 | 
							license=('GPL-3.0-or-later')
 | 
				
			||||||
 | 
							provides=('test')
 | 
				
			||||||
 | 
							conflicts=('test')
 | 
				
			||||||
 | 
							replaces=('test-old')
 | 
				
			||||||
 | 
							replaces_test_os=('test-legacy')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							deps=('sudo')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							build_deps=('golang')
 | 
				
			||||||
 | 
							build_deps_arch=('go')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							test() {
 | 
				
			||||||
 | 
								echo "Test"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							package() {
 | 
				
			||||||
 | 
								install-binary test
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "lure.sh")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runner, err := interp.New()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = runner.Run(ctx, fl)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dec := decoder.New(osRelease, runner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var bv BuildVars
 | 
				
			||||||
 | 
						err = dec.DecodeVars(&bv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var notFoundErr decoder.VarNotFoundError
 | 
				
			||||||
 | 
						if !errors.As(err, ¬FoundErr) {
 | 
				
			||||||
 | 
							t.Fatalf("Expected VarNotFoundError, got %T %v", err, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetFunc(t *testing.T) {
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "lure.sh")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runner, err := interp.New()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = runner.Run(ctx, fl)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dec := decoder.New(osRelease, runner)
 | 
				
			||||||
 | 
						fn, ok := dec.GetFunc("test")
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("Expected test() function to exist")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf := &bytes.Buffer{}
 | 
				
			||||||
 | 
						err = fn(ctx, interp.StdIO(os.Stdin, buf, buf))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if buf.String() != "Test\n" {
 | 
				
			||||||
 | 
							t.Fatalf(`Expected "Test\n", got %#v`, buf.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -39,18 +39,25 @@ type ExecFunc func(hc interp.HandlerContext, name string, args []string) error
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type ExecFuncs map[string]ExecFunc
 | 
					type ExecFuncs map[string]ExecFunc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ef ExecFuncs) ExecHandler(ctx context.Context, args []string) error {
 | 
					// ExecHandler returns a new ExecHandlerFunc that falls back to fallback
 | 
				
			||||||
	name := args[0]
 | 
					// if the command cannot be found in the map. If fallback is nil, the default
 | 
				
			||||||
 | 
					// handler is used.
 | 
				
			||||||
 | 
					func (ef ExecFuncs) ExecHandler(fallback interp.ExecHandlerFunc) interp.ExecHandlerFunc {
 | 
				
			||||||
 | 
						return func(ctx context.Context, args []string) error {
 | 
				
			||||||
 | 
							name := args[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fn, ok := ef[name]; ok {
 | 
							if fn, ok := ef[name]; ok {
 | 
				
			||||||
		hctx := interp.HandlerCtx(ctx)
 | 
								hctx := interp.HandlerCtx(ctx)
 | 
				
			||||||
		if len(args) > 1 {
 | 
								if len(args) > 1 {
 | 
				
			||||||
			return fn(hctx, args[0], args[1:])
 | 
									return fn(hctx, args[0], args[1:])
 | 
				
			||||||
		} else {
 | 
								} else {
 | 
				
			||||||
			return fn(hctx, args[0], nil)
 | 
									return fn(hctx, args[0], nil)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defExec := interp.DefaultExecHandler(2 * time.Second)
 | 
							if fallback == nil {
 | 
				
			||||||
	return defExec(ctx, args)
 | 
								fallback = interp.DefaultExecHandler(2 * time.Second)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return fallback(ctx, args)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										124
									
								
								internal/shutils/exec_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								internal/shutils/exec_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/distro"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/shutils"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/shutils/decoder"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/interp"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/syntax"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const testScript = `
 | 
				
			||||||
 | 
						name='test'
 | 
				
			||||||
 | 
						version='0.0.1'
 | 
				
			||||||
 | 
						release=1
 | 
				
			||||||
 | 
						epoch=2
 | 
				
			||||||
 | 
						desc="Test package"
 | 
				
			||||||
 | 
						homepage='https://lure.arsenm.dev'
 | 
				
			||||||
 | 
						maintainer='Arsen Musayelyan <arsen@arsenm.dev>'
 | 
				
			||||||
 | 
						architectures=('arm64' 'amd64')
 | 
				
			||||||
 | 
						license=('GPL-3.0-or-later')
 | 
				
			||||||
 | 
						provides=('test')
 | 
				
			||||||
 | 
						conflicts=('test')
 | 
				
			||||||
 | 
						replaces=('test-old')
 | 
				
			||||||
 | 
						replaces_test_os=('test-legacy')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						deps=('sudo')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						build_deps=('golang')
 | 
				
			||||||
 | 
						build_deps_arch=('go')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test() {
 | 
				
			||||||
 | 
							test-cmd "Hello, World"
 | 
				
			||||||
 | 
							test-fb
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						package() {
 | 
				
			||||||
 | 
							install-binary test
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var osRelease = &distro.OSRelease{
 | 
				
			||||||
 | 
						ID:   "test_os",
 | 
				
			||||||
 | 
						Like: []string{"arch"},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestExecFuncs(t *testing.T) {
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fl, err := syntax.NewParser().Parse(strings.NewReader(testScript), "lure.sh")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runner, err := interp.New()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = runner.Run(ctx, fl)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dec := decoder.New(osRelease, runner)
 | 
				
			||||||
 | 
						fn, ok := dec.GetFunc("test")
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("Expected test() function to exist")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						eh := shutils.ExecFuncs{
 | 
				
			||||||
 | 
							"test-cmd": func(hc interp.HandlerContext, name string, args []string) error {
 | 
				
			||||||
 | 
								if name != "test-cmd" {
 | 
				
			||||||
 | 
									t.Errorf("Expected name to be 'test-cmd', got '%s'", name)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if len(args) < 1 {
 | 
				
			||||||
 | 
									t.Fatalf("Expected at least one argument, got %d", len(args))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if args[0] != "Hello, World" {
 | 
				
			||||||
 | 
									t.Errorf("Expected first argument to be 'Hello, World', got '%s'", args[0])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fbInvoked := false
 | 
				
			||||||
 | 
						fbHandler := func(context.Context, []string) error {
 | 
				
			||||||
 | 
							fbInvoked = true
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = fn(ctx, interp.ExecHandler(eh.ExecHandler(fbHandler)))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !fbInvoked {
 | 
				
			||||||
 | 
							t.Errorf("Expected fallback handler to be invoked")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -46,8 +46,8 @@ func (NopRWC) Read([]byte) (int, error) {
 | 
				
			|||||||
	return 0, io.EOF
 | 
						return 0, io.EOF
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (NopRWC) Write([]byte) (int, error) {
 | 
					func (NopRWC) Write(b []byte) (int, error) {
 | 
				
			||||||
	return 0, io.EOF
 | 
						return len(b), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (NopRWC) Close() error {
 | 
					func (NopRWC) Close() error {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										58
									
								
								internal/shutils/nop_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								internal/shutils/nop_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/shutils"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/interp"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/syntax"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNopExec(t *testing.T) {
 | 
				
			||||||
 | 
						ctx := context.Background()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fl, err := syntax.NewParser().Parse(strings.NewReader(`/bin/echo test`), "lure.sh")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf := &bytes.Buffer{}
 | 
				
			||||||
 | 
						runner, err := interp.New(
 | 
				
			||||||
 | 
							interp.ExecHandler(shutils.NopExec),
 | 
				
			||||||
 | 
							interp.StdIO(os.Stdin, buf, buf),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = runner.Run(ctx, fl)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("Expected no error, got %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if buf.String() != "" {
 | 
				
			||||||
 | 
							t.Fatalf("Expected empty string, got %#v", buf.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										76
									
								
								internal/shutils/restricted.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								internal/shutils/restricted.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/exp/slices"
 | 
				
			||||||
 | 
						"mvdan.cc/sh/v3/interp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc {
 | 
				
			||||||
 | 
						return func(ctx context.Context, s string) ([]os.FileInfo, error) {
 | 
				
			||||||
 | 
							for _, allowedPrefix := range allowedPrefixes {
 | 
				
			||||||
 | 
								if strings.HasPrefix(s, allowedPrefix) {
 | 
				
			||||||
 | 
									return interp.DefaultReadDirHandler()(ctx, s)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil, os.ErrNotExist
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RestrictedStat(allowedPrefixes ...string) interp.StatHandlerFunc {
 | 
				
			||||||
 | 
						return func(ctx context.Context, s string, b bool) (os.FileInfo, error) {
 | 
				
			||||||
 | 
							for _, allowedPrefix := range allowedPrefixes {
 | 
				
			||||||
 | 
								if strings.HasPrefix(s, allowedPrefix) {
 | 
				
			||||||
 | 
									return interp.DefaultStatHandler()(ctx, s, b)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil, os.ErrNotExist
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RestrictedOpen(allowedPrefixes ...string) interp.OpenHandlerFunc {
 | 
				
			||||||
 | 
						return func(ctx context.Context, s string, i int, fm os.FileMode) (io.ReadWriteCloser, error) {
 | 
				
			||||||
 | 
							for _, allowedPrefix := range allowedPrefixes {
 | 
				
			||||||
 | 
								if strings.HasPrefix(s, allowedPrefix) {
 | 
				
			||||||
 | 
									return interp.DefaultOpenHandler()(ctx, s, i, fm)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return NopRWC{}, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RestrictedExec(allowedCmds ...string) interp.ExecHandlerFunc {
 | 
				
			||||||
 | 
						return func(ctx context.Context, args []string) error {
 | 
				
			||||||
 | 
							if slices.Contains(allowedCmds, args[0]) {
 | 
				
			||||||
 | 
								return interp.DefaultExecHandler(2*time.Second)(ctx, args)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,10 +1,32 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package types
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Config represents the LURE configuration file
 | 
				
			||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
	RootCmd string `toml:"rootCmd"`
 | 
						RootCmd          string   `toml:"rootCmd"`
 | 
				
			||||||
	Repos   []Repo `toml:"repo"`
 | 
						PagerStyle       string   `toml:"pagerStyle"`
 | 
				
			||||||
 | 
						IgnorePkgUpdates []string `toml:"ignorePkgUpdates"`
 | 
				
			||||||
 | 
						Repos            []Repo   `toml:"repo"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Repo represents a LURE repo within a configuration file
 | 
				
			||||||
type Repo struct {
 | 
					type Repo struct {
 | 
				
			||||||
	Name string `toml:"name"`
 | 
						Name string `toml:"name"`
 | 
				
			||||||
	URL  string `toml:"url"`
 | 
						URL  string `toml:"url"`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +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 types
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RepoConfig represents a LURE repo's lure-repo.toml file.
 | 
				
			||||||
type RepoConfig struct {
 | 
					type RepoConfig struct {
 | 
				
			||||||
	Repo struct {
 | 
						Repo struct {
 | 
				
			||||||
		MinVersion string `toml:"minVersion"`
 | 
							MinVersion string `toml:"minVersion"`
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										37
									
								
								list.go
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								list.go
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -21,16 +21,27 @@ package main
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/genjidb/genji/document"
 | 
					 | 
				
			||||||
	"github.com/genjidb/genji/types"
 | 
					 | 
				
			||||||
	"github.com/urfave/cli/v2"
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
	"go.arsenm.dev/logger/log"
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/db"
 | 
						"go.arsenm.dev/lure/internal/db"
 | 
				
			||||||
 | 
						"go.arsenm.dev/lure/internal/repos"
 | 
				
			||||||
	"go.arsenm.dev/lure/manager"
 | 
						"go.arsenm.dev/lure/manager"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func listCmd(c *cli.Context) error {
 | 
					func listCmd(c *cli.Context) error {
 | 
				
			||||||
	result, err := db.GetPkgs(gdb, "true")
 | 
						err := repos.Pull(c.Context, gdb, cfg.Repos)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error pulling repositories").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						where := "true"
 | 
				
			||||||
 | 
						args := []any(nil)
 | 
				
			||||||
 | 
						if c.NArg() > 0 {
 | 
				
			||||||
 | 
							where = "name LIKE ? OR json_array_contains(provides, ?)"
 | 
				
			||||||
 | 
							args = []any{c.Args().First(), c.Args().First()}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result, err := db.GetPkgs(gdb, where, args...)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal("Error getting packages").Err(err).Send()
 | 
							log.Fatal("Error getting packages").Err(err).Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -43,28 +54,32 @@ func listCmd(c *cli.Context) error {
 | 
				
			|||||||
			log.Fatal("Unable to detect supported package manager on system").Send()
 | 
								log.Fatal("Unable to detect supported package manager on system").Send()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		installed, err = mgr.ListInstalled(nil)
 | 
							installed, err = mgr.ListInstalled(&manager.Opts{AsRoot: false})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal("Error listing installed packages").Err(err).Send()
 | 
								log.Fatal("Error listing installed packages").Err(err).Send()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = result.Iterate(func(d types.Document) error {
 | 
						for result.Next() {
 | 
				
			||||||
		var pkg db.Package
 | 
							var pkg db.Package
 | 
				
			||||||
		err := document.StructScan(d, &pkg)
 | 
							err := result.StructScan(&pkg)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							version := pkg.Version
 | 
				
			||||||
		if c.Bool("installed") {
 | 
							if c.Bool("installed") {
 | 
				
			||||||
			if _, ok := installed[pkg.Name]; !ok {
 | 
								instVersion, ok := installed[pkg.Name]
 | 
				
			||||||
 | 
								if !ok {
 | 
				
			||||||
				return nil
 | 
									return nil
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									version = instVersion
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, pkg.Version)
 | 
							fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version)
 | 
				
			||||||
		return nil
 | 
						}
 | 
				
			||||||
	})
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal("Error iterating over packages").Err(err).Send()
 | 
							log.Fatal("Error iterating over packages").Err(err).Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										98
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										98
									
								
								main.go
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -20,8 +20,11 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"embed"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/signal"
 | 
						"os/signal"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,12 +32,28 @@ import (
 | 
				
			|||||||
	"go.arsenm.dev/logger"
 | 
						"go.arsenm.dev/logger"
 | 
				
			||||||
	"go.arsenm.dev/logger/log"
 | 
						"go.arsenm.dev/logger/log"
 | 
				
			||||||
	"go.arsenm.dev/lure/internal/config"
 | 
						"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:generate scripts/gen-version.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//go:embed translations
 | 
				
			||||||
 | 
					var translationFS embed.FS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var translator translate.Translator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	log.Logger = logger.NewPretty(os.Stderr)
 | 
						logger := logger.NewCLI(os.Stderr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t, err := translate.NewFromFS(translationFS)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Fatal("Error creating new translator").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						translator = t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Logger = translate.NewLogger(logger, t, config.Language)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
@@ -52,12 +71,27 @@ func main() {
 | 
				
			|||||||
	app := &cli.App{
 | 
						app := &cli.App{
 | 
				
			||||||
		Name:  "lure",
 | 
							Name:  "lure",
 | 
				
			||||||
		Usage: "Linux User REpository",
 | 
							Usage: "Linux User REpository",
 | 
				
			||||||
 | 
							Flags: []cli.Flag{
 | 
				
			||||||
 | 
								&cli.StringFlag{
 | 
				
			||||||
 | 
									Name:    "pm-args",
 | 
				
			||||||
 | 
									Aliases: []string{"P"},
 | 
				
			||||||
 | 
									Usage:   "Arguments to be passed on to the package manager",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		Commands: []*cli.Command{
 | 
							Commands: []*cli.Command{
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				Name:    "install",
 | 
									Flags: []cli.Flag{
 | 
				
			||||||
				Usage:   "Install a new package",
 | 
										&cli.BoolFlag{
 | 
				
			||||||
				Aliases: []string{"in"},
 | 
											Name:    "clean",
 | 
				
			||||||
				Action:  installCmd,
 | 
											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"},
 | 
				
			||||||
 | 
									Action:       installCmd,
 | 
				
			||||||
 | 
									BashComplete: completionInstall,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				Name:    "remove",
 | 
									Name:    "remove",
 | 
				
			||||||
@@ -66,12 +100,26 @@ func main() {
 | 
				
			|||||||
				Action:  removeCmd,
 | 
									Action:  removeCmd,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
 | 
									Flags: []cli.Flag{
 | 
				
			||||||
 | 
										&cli.BoolFlag{
 | 
				
			||||||
 | 
											Name:    "clean",
 | 
				
			||||||
 | 
											Aliases: []string{"c"},
 | 
				
			||||||
 | 
											Usage:   "Build package from scratch even if there's an already built package available",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
				Name:    "upgrade",
 | 
									Name:    "upgrade",
 | 
				
			||||||
				Usage:   "Upgrade all installed packages",
 | 
									Usage:   "Upgrade all installed packages",
 | 
				
			||||||
				Aliases: []string{"up"},
 | 
									Aliases: []string{"up"},
 | 
				
			||||||
				Action:  upgradeCmd,
 | 
									Action:  upgradeCmd,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
 | 
									Flags: []cli.Flag{
 | 
				
			||||||
 | 
										&cli.BoolFlag{
 | 
				
			||||||
 | 
											Name:    "all",
 | 
				
			||||||
 | 
											Aliases: []string{"a"},
 | 
				
			||||||
 | 
											Usage:   "Show all information, not just for the current distro",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
				Name:   "info",
 | 
									Name:   "info",
 | 
				
			||||||
				Usage:  "Print information about a package",
 | 
									Usage:  "Print information about a package",
 | 
				
			||||||
				Action: infoCmd,
 | 
									Action: infoCmd,
 | 
				
			||||||
@@ -96,6 +144,11 @@ func main() {
 | 
				
			|||||||
						Value:   "lure.sh",
 | 
											Value:   "lure.sh",
 | 
				
			||||||
						Usage:   "Path to the build script",
 | 
											Usage:   "Path to the build script",
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
 | 
										&cli.BoolFlag{
 | 
				
			||||||
 | 
											Name:    "clean",
 | 
				
			||||||
 | 
											Aliases: []string{"c"},
 | 
				
			||||||
 | 
											Usage:   "Build package from scratch even if there's an already built package available",
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				Name:   "build",
 | 
									Name:   "build",
 | 
				
			||||||
				Usage:  "Build a local package",
 | 
									Usage:  "Build a local package",
 | 
				
			||||||
@@ -141,15 +194,30 @@ func main() {
 | 
				
			|||||||
				Aliases: []string{"ref"},
 | 
									Aliases: []string{"ref"},
 | 
				
			||||||
				Action:  refreshCmd,
 | 
									Action:  refreshCmd,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Name:   "fix",
 | 
				
			||||||
 | 
									Usage:  "Attempt to fix problems with LURE",
 | 
				
			||||||
 | 
									Action: fixCmd,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				Name:   "version",
 | 
									Name:   "version",
 | 
				
			||||||
				Usage:  "Display the current LURE version and exit",
 | 
									Usage:  "Display the current LURE version and exit",
 | 
				
			||||||
				Action: displayVersion,
 | 
									Action: displayVersion,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							Before: func(c *cli.Context) error {
 | 
				
			||||||
 | 
								args := strings.Split(c.String("pm-args"), " ")
 | 
				
			||||||
 | 
								if len(args) == 1 && args[0] == "" {
 | 
				
			||||||
 | 
									args = nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								manager.Args = append(manager.Args, args...)
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		After: func(ctx *cli.Context) error {
 | 
							After: func(ctx *cli.Context) error {
 | 
				
			||||||
			return gdb.Close()
 | 
								return gdb.Close()
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							EnableBashCompletion: true,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := app.RunContext(ctx, os.Args)
 | 
						err := app.RunContext(ctx, os.Args)
 | 
				
			||||||
@@ -162,3 +230,21 @@ func displayVersion(c *cli.Context) error {
 | 
				
			|||||||
	print(config.Version)
 | 
						print(config.Version)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func completionInstall(c *cli.Context) {
 | 
				
			||||||
 | 
						result, err := db.GetPkgs(gdb, "true")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal("Error getting packages").Err(err).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer result.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for result.Next() {
 | 
				
			||||||
 | 
							var pkg db.Package
 | 
				
			||||||
 | 
							err = result.StructScan(&pkg)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatal("Error iterating over packages").Err(err).Send()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fmt.Println(pkg.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -112,15 +112,8 @@ func (a *APK) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *APK) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					func (a *APK) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			||||||
	opts = ensureOpts(opts)
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
						out := map[string]string{}
 | 
				
			||||||
 | 
						cmd := exec.Command("apk", "list", "-I")
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
					 | 
				
			||||||
	if opts.AsRoot {
 | 
					 | 
				
			||||||
		cmd = exec.Command(getRootCmd(a.rootCmd), "apk", "list", "-I")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		cmd = exec.Command("apk", "list", "-I")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
						stdout, err := cmd.StdoutPipe()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -159,6 +152,7 @@ func (a *APK) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			|||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
		cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
 | 
							cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
 | 
				
			||||||
 | 
							cmd.Args = append(cmd.Args, opts.Args...)
 | 
				
			||||||
		cmd.Args = append(cmd.Args, args...)
 | 
							cmd.Args = append(cmd.Args, args...)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		cmd = exec.Command(mgrCmd, args...)
 | 
							cmd = exec.Command(mgrCmd, args...)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -104,15 +104,8 @@ func (a *APT) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *APT) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					func (a *APT) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			||||||
	opts = ensureOpts(opts)
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
						out := map[string]string{}
 | 
				
			||||||
 | 
						cmd := exec.Command("dpkg-query", "-f", "${Package}\u200b${Version}\\n", "-W")
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
					 | 
				
			||||||
	if opts.AsRoot {
 | 
					 | 
				
			||||||
		cmd = exec.Command(getRootCmd(a.rootCmd), "dpkg-query", "-f", "${Package}\u200b${Version}\\n", "-W")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		cmd = exec.Command("dpkg-query", "-f", "${Package}\u200b${Version}\\n", "-W")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
						stdout, err := cmd.StdoutPipe()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -145,6 +138,7 @@ func (a *APT) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			|||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
		cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
 | 
							cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
 | 
				
			||||||
 | 
							cmd.Args = append(cmd.Args, opts.Args...)
 | 
				
			||||||
		cmd.Args = append(cmd.Args, args...)
 | 
							cmd.Args = append(cmd.Args, args...)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		cmd = exec.Command(mgrCmd, args...)
 | 
							cmd = exec.Command(mgrCmd, args...)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -60,7 +60,7 @@ func (d *DNF) Sync(opts *Opts) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (d *DNF) Install(opts *Opts, pkgs ...string) error {
 | 
					func (d *DNF) Install(opts *Opts, pkgs ...string) error {
 | 
				
			||||||
	opts = ensureOpts(opts)
 | 
						opts = ensureOpts(opts)
 | 
				
			||||||
	cmd := d.getCmd(opts, "dnf", "install")
 | 
						cmd := d.getCmd(opts, "dnf", "install", "--allowerasing")
 | 
				
			||||||
	cmd.Args = append(cmd.Args, pkgs...)
 | 
						cmd.Args = append(cmd.Args, pkgs...)
 | 
				
			||||||
	setCmdEnv(cmd)
 | 
						setCmdEnv(cmd)
 | 
				
			||||||
	err := cmd.Run()
 | 
						err := cmd.Run()
 | 
				
			||||||
@@ -111,15 +111,8 @@ func (d *DNF) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			||||||
	opts = ensureOpts(opts)
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
						out := map[string]string{}
 | 
				
			||||||
 | 
						cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
					 | 
				
			||||||
	if opts.AsRoot {
 | 
					 | 
				
			||||||
		cmd = exec.Command(getRootCmd(d.rootCmd), "rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		cmd = exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
						stdout, err := cmd.StdoutPipe()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -153,6 +146,7 @@ func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			|||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
		cmd = exec.Command(getRootCmd(d.rootCmd), mgrCmd)
 | 
							cmd = exec.Command(getRootCmd(d.rootCmd), mgrCmd)
 | 
				
			||||||
 | 
							cmd.Args = append(cmd.Args, opts.Args...)
 | 
				
			||||||
		cmd.Args = append(cmd.Args, args...)
 | 
							cmd.Args = append(cmd.Args, args...)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		cmd = exec.Command(mgrCmd, args...)
 | 
							cmd = exec.Command(mgrCmd, args...)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -23,9 +23,12 @@ import (
 | 
				
			|||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Args []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Opts struct {
 | 
					type Opts struct {
 | 
				
			||||||
	AsRoot    bool
 | 
						AsRoot    bool
 | 
				
			||||||
	NoConfirm bool
 | 
						NoConfirm bool
 | 
				
			||||||
 | 
						Args      []string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var DefaultOpts = &Opts{
 | 
					var DefaultOpts = &Opts{
 | 
				
			||||||
@@ -114,7 +117,8 @@ func setCmdEnv(cmd *exec.Cmd) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func ensureOpts(opts *Opts) *Opts {
 | 
					func ensureOpts(opts *Opts) *Opts {
 | 
				
			||||||
	if opts == nil {
 | 
						if opts == nil {
 | 
				
			||||||
		return DefaultOpts
 | 
							opts = DefaultOpts
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						opts.Args = append(opts.Args, Args...)
 | 
				
			||||||
	return opts
 | 
						return opts
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -111,15 +111,8 @@ func (p *Pacman) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *Pacman) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					func (p *Pacman) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			||||||
	opts = ensureOpts(opts)
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
						out := map[string]string{}
 | 
				
			||||||
 | 
						cmd := exec.Command("pacman", "-Q")
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
					 | 
				
			||||||
	if opts.AsRoot {
 | 
					 | 
				
			||||||
		cmd = exec.Command(getRootCmd(p.rootCmd), "pacman", "-Q")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		cmd = exec.Command("pacman", "-Q")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
						stdout, err := cmd.StdoutPipe()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -152,6 +145,7 @@ func (p *Pacman) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			|||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
		cmd = exec.Command(getRootCmd(p.rootCmd), mgrCmd)
 | 
							cmd = exec.Command(getRootCmd(p.rootCmd), mgrCmd)
 | 
				
			||||||
 | 
							cmd.Args = append(cmd.Args, opts.Args...)
 | 
				
			||||||
		cmd.Args = append(cmd.Args, args...)
 | 
							cmd.Args = append(cmd.Args, args...)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		cmd = exec.Command(mgrCmd, args...)
 | 
							cmd = exec.Command(mgrCmd, args...)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -60,7 +60,7 @@ func (y *YUM) Sync(opts *Opts) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (y *YUM) Install(opts *Opts, pkgs ...string) error {
 | 
					func (y *YUM) Install(opts *Opts, pkgs ...string) error {
 | 
				
			||||||
	opts = ensureOpts(opts)
 | 
						opts = ensureOpts(opts)
 | 
				
			||||||
	cmd := y.getCmd(opts, "yum", "install")
 | 
						cmd := y.getCmd(opts, "yum", "install", "--allowerasing")
 | 
				
			||||||
	cmd.Args = append(cmd.Args, pkgs...)
 | 
						cmd.Args = append(cmd.Args, pkgs...)
 | 
				
			||||||
	setCmdEnv(cmd)
 | 
						setCmdEnv(cmd)
 | 
				
			||||||
	err := cmd.Run()
 | 
						err := cmd.Run()
 | 
				
			||||||
@@ -111,15 +111,8 @@ func (y *YUM) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (y *YUM) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					func (y *YUM) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			||||||
	opts = ensureOpts(opts)
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
						out := map[string]string{}
 | 
				
			||||||
 | 
						cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
					 | 
				
			||||||
	if opts.AsRoot {
 | 
					 | 
				
			||||||
		cmd = exec.Command(getRootCmd(y.rootCmd), "rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		cmd = exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
						stdout, err := cmd.StdoutPipe()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -153,6 +146,7 @@ func (y *YUM) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			|||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
		cmd = exec.Command(getRootCmd(y.rootCmd), mgrCmd)
 | 
							cmd = exec.Command(getRootCmd(y.rootCmd), mgrCmd)
 | 
				
			||||||
 | 
							cmd.Args = append(cmd.Args, opts.Args...)
 | 
				
			||||||
		cmd.Args = append(cmd.Args, args...)
 | 
							cmd.Args = append(cmd.Args, args...)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		cmd = exec.Command(mgrCmd, args...)
 | 
							cmd = exec.Command(mgrCmd, args...)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -111,15 +111,8 @@ func (z *Zypper) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (z *Zypper) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					func (z *Zypper) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			||||||
	opts = ensureOpts(opts)
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
						out := map[string]string{}
 | 
				
			||||||
 | 
						cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
					 | 
				
			||||||
	if opts.AsRoot {
 | 
					 | 
				
			||||||
		cmd = exec.Command(getRootCmd(z.rootCmd), "rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		cmd = exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
						stdout, err := cmd.StdoutPipe()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -153,6 +146,7 @@ func (z *Zypper) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			|||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
		cmd = exec.Command(getRootCmd(z.rootCmd), mgrCmd)
 | 
							cmd = exec.Command(getRootCmd(z.rootCmd), mgrCmd)
 | 
				
			||||||
 | 
							cmd.Args = append(cmd.Args, opts.Args...)
 | 
				
			||||||
		cmd.Args = append(cmd.Args, args...)
 | 
							cmd.Args = append(cmd.Args, args...)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		cmd = exec.Command(mgrCmd, args...)
 | 
							cmd = exec.Command(mgrCmd, args...)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								repo.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								repo.go
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								scripts/completion/bash
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								scripts/completion/bash
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					#! /bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: ${PROG:=$(basename ${BASH_SOURCE})}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_cli_bash_autocomplete() {
 | 
				
			||||||
 | 
					  if [[ "${COMP_WORDS[0]}" != "source" ]]; then
 | 
				
			||||||
 | 
					    local cur opts base
 | 
				
			||||||
 | 
					    COMPREPLY=()
 | 
				
			||||||
 | 
					    cur="${COMP_WORDS[COMP_CWORD]}"
 | 
				
			||||||
 | 
					    if [[ "$cur" == "-"* ]]; then
 | 
				
			||||||
 | 
					      opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion )
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
 | 
				
			||||||
 | 
					    return 0
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG
 | 
				
			||||||
 | 
					unset PROG
 | 
				
			||||||
							
								
								
									
										20
									
								
								scripts/completion/zsh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								scripts/completion/zsh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					#compdef lure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_cli_zsh_autocomplete() {
 | 
				
			||||||
 | 
					  local -a opts
 | 
				
			||||||
 | 
					  local cur
 | 
				
			||||||
 | 
					  cur=${words[-1]}
 | 
				
			||||||
 | 
					  if [[ "$cur" == "-"* ]]; then
 | 
				
			||||||
 | 
					    opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}")
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [[ "${opts[1]}" != "" ]]; then
 | 
				
			||||||
 | 
					    _describe 'values' opts
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    _files
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					compdef _cli_zsh_autocomplete lure
 | 
				
			||||||
@@ -24,9 +24,9 @@ installPkg() {
 | 
				
			|||||||
  fi
 | 
					  fi
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  case $1 in
 | 
					  case $1 in
 | 
				
			||||||
  pacman) $rootCmd pacman -U ${@:2} ;;
 | 
					  pacman) $rootCmd pacman --noconfirm -U ${@:2} ;;
 | 
				
			||||||
  apk) $rootCmd apk add --allow-untrusted ${@:2} ;;
 | 
					  apk) $rootCmd apk add --allow-untrusted ${@:2} ;;
 | 
				
			||||||
  *) $rootCmd $1 install ${@:2} ;;
 | 
					  *) $rootCmd $1 install -y ${@:2} ;;
 | 
				
			||||||
  esac
 | 
					  esac
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										143
									
								
								translations/lure.en.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								translations/lure.en.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1228660974
 | 
				
			||||||
 | 
					value = 'Pulling repository'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2779805870
 | 
				
			||||||
 | 
					value = 'Repository up to date'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1433222829
 | 
				
			||||||
 | 
					value = 'Would you like to view the build script for'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2470847050
 | 
				
			||||||
 | 
					value = 'Failed to prompt user to view build script'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 855659503
 | 
				
			||||||
 | 
					value = 'Would you still like to continue?'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1997041569
 | 
				
			||||||
 | 
					value = 'User chose not to continue after reading script'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2347700990
 | 
				
			||||||
 | 
					value = 'Building package'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2105058868
 | 
				
			||||||
 | 
					value = 'Downloading sources'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1884485082
 | 
				
			||||||
 | 
					value = 'Downloading source'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1519177982
 | 
				
			||||||
 | 
					value = 'Error building package'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2125220917
 | 
				
			||||||
 | 
					value = 'Choose which package(s) to install'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 812531604
 | 
				
			||||||
 | 
					value = 'Error prompting for choice of package'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1040982801
 | 
				
			||||||
 | 
					value = 'Updating version'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1014897988
 | 
				
			||||||
 | 
					value = 'Remove build dependencies?'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2205430948
 | 
				
			||||||
 | 
					value = 'Installing build dependencies'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2522710805
 | 
				
			||||||
 | 
					value = 'Installing dependencies'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 3602138206
 | 
				
			||||||
 | 
					value = 'Error installing package'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2235794125
 | 
				
			||||||
 | 
					value = 'Would you like to remove build dependencies?'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2562049386
 | 
				
			||||||
 | 
					value = "Your system's CPU architecture doesn't match this package. Do you want to build anyway?"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 4006393493
 | 
				
			||||||
 | 
					value = 'The checksums array must be the same length as sources'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 3759891273
 | 
				
			||||||
 | 
					value = 'The package() function is required'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1057080231
 | 
				
			||||||
 | 
					value = 'Executing package()'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2687735200
 | 
				
			||||||
 | 
					value = 'Executing prepare()'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 535572372
 | 
				
			||||||
 | 
					value = 'Executing version()'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 436644691
 | 
				
			||||||
 | 
					value = 'Executing build()'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1393316459
 | 
				
			||||||
 | 
					value = 'This package is already installed'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1267660189
 | 
				
			||||||
 | 
					value = 'Source can be updated, updating if required'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 21753247
 | 
				
			||||||
 | 
					value = 'Source found in cache, linked to destination'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 257354570
 | 
				
			||||||
 | 
					value = 'Compressing package'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2952487371
 | 
				
			||||||
 | 
					value = 'Building package metadata'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1579384326
 | 
				
			||||||
 | 
					value = 'name'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 3206337475
 | 
				
			||||||
 | 
					value = 'version'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1810056261
 | 
				
			||||||
 | 
					value = 'new'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1602912115
 | 
				
			||||||
 | 
					value = 'source'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2363381545
 | 
				
			||||||
 | 
					value = 'type'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 3419504365
 | 
				
			||||||
 | 
					value = 'downloader'
 | 
				
			||||||
							
								
								
									
										139
									
								
								translations/lure.ru.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								translations/lure.ru.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
				
			|||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1228660974
 | 
				
			||||||
 | 
					value = 'Скачивание репозитория'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2779805870
 | 
				
			||||||
 | 
					value = 'Репозиторий уже обновлен'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1433222829
 | 
				
			||||||
 | 
					value = 'Показать скрипт для пакета'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2470847050
 | 
				
			||||||
 | 
					value = 'Не удалось предложить просмотреть скрипт'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 855659503
 | 
				
			||||||
 | 
					value = 'Продолжить?'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1997041569
 | 
				
			||||||
 | 
					value = 'Пользователь решил не продолжать после просмотра скрипта'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2347700990
 | 
				
			||||||
 | 
					value = 'Сборка пакета'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2105058868
 | 
				
			||||||
 | 
					value = 'Скачивание файлов'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1884485082
 | 
				
			||||||
 | 
					value = 'Скачивание источника'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1519177982
 | 
				
			||||||
 | 
					value = 'Ошибка при сборке пакета'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2125220917
 | 
				
			||||||
 | 
					value = 'Выберите, какие пакеты установить'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 812531604
 | 
				
			||||||
 | 
					value = 'Ошибка при запросе выбора пакета'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1040982801
 | 
				
			||||||
 | 
					value = 'Обновление версии'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2235794125
 | 
				
			||||||
 | 
					value = 'Удалить зависимости сборки?'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2205430948
 | 
				
			||||||
 | 
					value = 'Установка зависимостей сборки'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2522710805
 | 
				
			||||||
 | 
					value = 'Установка зависимостей'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 3602138206
 | 
				
			||||||
 | 
					value = 'Ошибка при установке пакета'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1057080231
 | 
				
			||||||
 | 
					value = 'Вызов функции package()'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2687735200
 | 
				
			||||||
 | 
					value = 'Вызов функции prepare()'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 535572372
 | 
				
			||||||
 | 
					value = 'Вызов функции version()'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 436644691
 | 
				
			||||||
 | 
					value = 'Вызов функции build()'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2562049386
 | 
				
			||||||
 | 
					value = "Архитектура процессора вашей системы не соответствует этому пакету. Продолжать несмотря на это?"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 3759891273
 | 
				
			||||||
 | 
					value = 'Функция package() необходима'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 4006393493
 | 
				
			||||||
 | 
					value = 'Массив checksums должен быть той же длины, что и sources'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1393316459
 | 
				
			||||||
 | 
					value = 'Этот пакет уже установлен'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1267660189
 | 
				
			||||||
 | 
					value = 'Источник может быть обновлен, если требуется, обновляем'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 21753247
 | 
				
			||||||
 | 
					value = 'Источник найден в кэше'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 257354570
 | 
				
			||||||
 | 
					value = 'Сжатие пакета'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2952487371
 | 
				
			||||||
 | 
					value = 'Создание метаданных пакета'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1579384326
 | 
				
			||||||
 | 
					value = 'название'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 3206337475
 | 
				
			||||||
 | 
					value = 'версия'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1810056261
 | 
				
			||||||
 | 
					value = 'новая'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 1602912115
 | 
				
			||||||
 | 
					value = 'источник'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 2363381545
 | 
				
			||||||
 | 
					value = 'вид'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[translation]]
 | 
				
			||||||
 | 
					id = 3419504365
 | 
				
			||||||
 | 
					value = 'протокол-скачивание'
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
@@ -55,7 +55,7 @@ func upgradeCmd(c *cli.Context) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(updates) > 0 {
 | 
						if len(updates) > 0 {
 | 
				
			||||||
		installPkgs(c.Context, updates, nil, mgr)
 | 
							installPkgs(c.Context, updates, nil, mgr, c.Bool("clean"))
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		log.Info("There is nothing to do.").Send()
 | 
							log.Info("There is nothing to do.").Send()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -77,6 +77,10 @@ func checkForUpdates(ctx context.Context, mgr manager.Manager, info *distro.OSRe
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var out []db.Package
 | 
						var out []db.Package
 | 
				
			||||||
	for pkgName, pkgs := range found {
 | 
						for pkgName, pkgs := range found {
 | 
				
			||||||
 | 
							if slices.Contains(cfg.IgnorePkgUpdates, pkgName) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if len(pkgs) > 1 {
 | 
							if len(pkgs) > 1 {
 | 
				
			||||||
			// Puts the element with the highest version first
 | 
								// Puts the element with the highest version first
 | 
				
			||||||
			slices.SortFunc(pkgs, func(a, b db.Package) bool {
 | 
								slices.SortFunc(pkgs, func(a, b db.Package) bool {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * LURE - Linux User REpository
 | 
					 * LURE - Linux User REpository
 | 
				
			||||||
 * Copyright (C) 2022 Arsen Musayelyan
 | 
					 * Copyright (C) 2023 Arsen Musayelyan
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License as published by
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user