diff --git a/.github/workflows/installers.yml b/.github/workflows/installers.yml new file mode 100644 index 0000000..07c7560 --- /dev/null +++ b/.github/workflows/installers.yml @@ -0,0 +1,27 @@ +name: installers + +on: + release: + types: [published] + +jobs: + generate-installers: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v4 + + - name: set-version + run: | + sed -e "s/__VERSION__/${{ github.event.release.tag_name }}/" scripts/installer.tpl.sh > installer.sh + sed -e "s/__VERSION__/${{ github.event.release.tag_name }}/" scripts/installer.tpl.ps1 > installer.ps1 + + - name: upload + uses: softprops/action-gh-release@v2 + with: + files: | + installer.sh + installer.ps1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 960cc09..134ffbc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ public/ .env* *.key +installer.* \ No newline at end of file diff --git a/README.md b/README.md index 9830b53..f7ee2b4 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ and many others to pre-compile their software and put their binaries up on GitHu ## Install -## MacOS/Linux +### MacOS/Linux 1. Set your path `export PATH=$HOME/.distillery/bin:$PATH` 2. Download the latest release from the [releases page](https://github.com/ekristen/distillery/releases) @@ -46,7 +46,7 @@ and many others to pre-compile their software and put their binaries up on GitHu 4. Delete `./dist` and the .tar.gz, now use `dist` normally 5. Run `dist install owner/repo` to install a binary from GitHub Repository -## Windows +### Windows 1. [Set Your Path](#set-your-path) 2. Download the latest release from the [releases page](https://github.com/ekristen/distillery/releases) @@ -54,27 +54,21 @@ and many others to pre-compile their software and put their binaries up on GitHu 4. Delete `.\dist.exe` and the .zip, now use `dist` normally 5. Run `dist install owner/repo` to install a binary from GitHub Repository -### Set Your Path +#### Set Your Path -#### For Current Session +##### For Current Session ```powershell $env:Path = "C:\Users\\.distillery\bin;" + $env:Path ``` -#### For Current User +##### For Current User ```powershell [Environment]::SetEnvironmentVariable("Path", "C:\Users\\.distillery\bin;" + $env:Path, [EnvironmentVariableTarget]::User) ``` -### For System - -```powershell -[Environment]::SetEnvironmentVariable("Path", "C:\Users\\.distillery\bin;" + $env:Path, [EnvironmentVariableTarget]::Machine) -``` - -### Uninstall +## Uninstall 1. Run `dist info` 2. Remove the directories listed under the cleanup section @@ -114,7 +108,8 @@ dist install homebrew/opentofu - GitHub - GitLab - Homebrew (binaries only, if anything has a dependency, it will not work at this time) -- Hashicorp (special handling for their releases, pointing to github repos will automatically pass through) +- Hashicorp (special handling for their releases, pointing to GitHub repos will automatically pass through) +- Kubernetes (special handling for their releases, pointing to GitHub repos will automatically pass through) ### Authentication @@ -144,6 +139,12 @@ versions are symlinked with the suffix `@version` this means you can have multip It also means you can call any version any time using the `@version` syntax or if you are using something like [direnv](https://direnv.net/) you can set aliases in your `.envrc` file for specific versions. +#### Example + +```console +alias terraform="terraform@1.8.5" +``` + ## Directory Structure This is the default directory structure that distillery uses. Some of this can be overridden via the configuration. diff --git a/docs/config.md b/docs/config.md index 6f6fff3..6c353c0 100644 --- a/docs/config.md +++ b/docs/config.md @@ -8,66 +8,19 @@ The default location for the configuration file is operating system dependent. Y The configuration file is optional. If it is not found, the default configuration is used. -## Aliases +!!! note - "Pro Tip" + You can change the default location of your configuration file by setting the `DISTILLERY_CONFIG` environment variable. -You can configure aliases for your installation sources. This is useful if you don't want to type the whole -path all the time. +## Default Configuration -### Simple Alias Definition +=== "YAML" -```yaml -aliases: - dist: ekristen/distillery - aws-nuke: ekristen/aws-nuke - age: filosottile/age -``` + ```yaml + default_provider: github + ``` -OR in TOML +=== "TOML" -```toml -[aliases] -dist = "ekristen/distillery" -aws-nuke = "ekristen/aws-nuke" -age = "filosottile/age" -``` - -### Alias with Version - -```yaml -aliases: - dist: ekristen/distillery - aws-nuke: ekristen/aws-nuke - age: filosottile/age@1.0.0 -``` - -OR in TOML - -```toml -[aliases] -dist = "ekristen/distillery" -aws-nuke = "ekristen/aws-nuke" -age = "filosottile/age@1.0.0" -``` - -### Alias with Version as Object - -```yaml -aliases: - dist: ekristen/distillery - aws-nuke: ekristen/aws-nuke - age: - name: filosottile/age - version: 1.0.0 -``` - -OR in TOML - -```toml -[aliases] -dist = "ekristen/distillery" -aws-nuke = "ekristen/aws-nuke" - -[aliases.age] -name = "filosottile/age" -version = "1.0.0" -``` \ No newline at end of file + ```toml + default_provider = "github" + ``` \ No newline at end of file diff --git a/docs/config/aliases.md b/docs/config/aliases.md new file mode 100644 index 0000000..47a4fa4 --- /dev/null +++ b/docs/config/aliases.md @@ -0,0 +1,88 @@ +# Aliases + +You can configure aliases for your installation sources. This is useful if you don't want to type the whole +path all the time. + +## Simple Definition + +=== "YAML" + + ```yaml + aliases: + dist: ekristen/distillery + aws-nuke: ekristen/aws-nuke + age: filosottile/age + ``` + +=== "TOML" + + ```toml + [aliases] + dist = "ekristen/distillery" + aws-nuke = "ekristen/aws-nuke" + age = "filosottile/age" + ``` + +## With Version + +=== "YAML" + + ```yaml + aliases: + dist: ekristen/distillery + aws-nuke: ekristen/aws-nuke + age: filosottile/age@1.0.0 + ``` + +=== "TOML" + + ```toml + [aliases] + dist = "ekristen/distillery" + aws-nuke = "ekristen/aws-nuke" + age = "filosottile/age@1.0.0" + ``` + +## With Version as Object + +=== "YAML" + + ```yaml + aliases: + dist: ekristen/distillery + aws-nuke: ekristen/aws-nuke + age: + name: filosottile/age + version: 1.0.0 + ``` + +=== "TOML" + + ```toml + [aliases] + dist = "ekristen/distillery" + aws-nuke = "ekristen/aws-nuke" + + [aliases.age] + name = "filosottile/age" + version = "1.0.0" + ``` + +## With Providers + +=== "YAML" + + + ```yaml + aliases: + age: github/filosottile/age + gitlab-runner: gitlab/gitlab-org/gitlab-runner + ``` + +=== "TOML" + + ```toml + [aliases] + age = "github/filosottile/age" + gitlab-runner = "gitlab/gitlab-org/gitlab-runner" + ``` \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 72e8f9d..70fde69 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,15 +1,16 @@ -# Distillery +## Quickstart -![Static Badge](https://img.shields.io/badge/Status%20-%20Beta%20-%20orange) -![GitHub Release](https://img.shields.io/github/v/release/ekristen/distillery?include_prereleases) -![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/ekristen/distillery/total) -![GitHub License](https://img.shields.io/github/license/ekristen/distillery) +=== "MacOS/Linux" + ```bash + curl --proto '=https' --tlsv1.2 -LsSf https://get.dist.sh | sh + ``` -[![Known Vulnerabilities](https://snyk.io/test/github/ekristen/distillery/badge.svg)](https://snyk.io/test/github/ekristen/distillery) -[![Go Report Card](https://goreportcard.com/badge/github.com/ekristen/distillery)](https://goreportcard.com/report/github.com/ekristen/distillery) +=== "Windows" + ```powershell + iwr https://get.dist.sh/ps1 -useb | iex + ``` -Most things are working, this project follows semantic commits and semantic releases, any breaking -changes will result in new major versions. +**Note:** Yes, I know, you really shouldn't download and run scripts from the internet, but at least it's not using `sudo`! ## Overview @@ -36,49 +37,6 @@ and many others to pre-compile their software and put their binaries up on GitHu - Support checksum verifications (if they exist) - Support signatures verifications (if they exist) (**not implemented yet**) -## Install - -## MacOS/Linux - -1. Set your path `export PATH=$HOME/.distillery/bin:$PATH` -2. Download the latest release from the [releases page](https://github.com/ekristen/distillery/releases) -3. Extract and Run `./dist install ekristen/distillery` -4. Delete `./dist` and the .tar.gz, now use `dist` normally -5. Run `dist install owner/repo` to install a binary from GitHub Repository - -## Windows - -1. [Set Your Path](#set-your-path) -2. Download the latest release from the [releases page](https://github.com/ekristen/distillery/releases) -3. Extract and Run `.\dist.exe install ekristen/distillery` -4. Delete `.\dist.exe` and the .zip, now use `dist` normally -5. Run `dist install owner/repo` to install a binary from GitHub Repository - -### Set Your Path - -#### For Current Session - -```powershell -$env:Path = "C:\Users\\.distillery\bin;" + $env:Path -``` - -#### For Current User - -```powershell -[Environment]::SetEnvironmentVariable("Path", "C:\Users\\.distillery\bin;" + $env:Path, [EnvironmentVariableTarget]::User) -``` - -### For System - -```powershell -[Environment]::SetEnvironmentVariable("Path", "C:\Users\\.distillery\bin;" + $env:Path, [EnvironmentVariableTarget]::Machine) -``` - -### Uninstall - -1. Run `dist info` -2. Remove the directories listed under the cleanup section - ### Examples Install a specific version of a tool using `@version` syntax. `github` is the default scope, this implies diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..2ffe7bc --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,56 @@ +# Installation + +## Quickstart + +=== "MacOS/Linux" + ```bash + curl --proto '=https' --tlsv1.2 -LsSf https://get.dist.sh | sh + ``` + +=== "Windows" + ```powershell + iwr https://get.dist.sh/ps1 -useb | iex + ``` + +**Note:** Yes, I know, you really shouldn't download and run scripts from the internet, but at least it's not using `sudo`! + +## Manual Installation + +## Unix-like Systems + +MacOS, Linux, FreeBSD and other Unix-like systems can use the following steps to install distillery. + +1. Set your path `export PATH=$HOME/.distillery/bin:$PATH` +2. Download the latest release from the [releases page](https://github.com/ekristen/distillery/releases) +3. Extract and Run `./dist install ekristen/distillery` +4. Delete `./dist` and the .tar.gz, now use `dist` normally +5. Run `dist install owner/repo` to install a binary from GitHub Repository + +### Windows + +1. [Set Your Path](#set-your-path) +2. Download the latest release from the [releases page](https://github.com/ekristen/distillery/releases) +3. Extract and Run `.\dist.exe install ekristen/distillery` +4. Delete `.\dist.exe` and the .zip, now use `dist` normally +5. Run `dist install owner/repo` to install a binary from GitHub Repository + +#### Set Your Path + +##### For Current Session + +```powershell +$env:Path = "C:\Users\\.distillery\bin;" + $env:Path +``` + +##### For Current User + +```powershell +[Environment]::SetEnvironmentVariable("Path", "C:\Users\\.distillery\bin;" + $env:Path, [EnvironmentVariableTarget]::User) +``` + +## Uninstall + +By default, everything with distillery is installed in the `~/.distillery` directory so cleanup is easy. + +1. Run `dist info` +2. Remove the directories listed under the cleanup section \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index b58ecb6..1905787 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -38,13 +38,17 @@ theme: - navigation.footer - navigation.indexes - navigation.path - - navigation.sections - navigation.tabs + - navigation.tabs.sticky + - navigation.expand + - navigation.sections + - navigation.expand - toc.follow - toc.integrate - content.code.annotate - content.code.copy - content.tooltips + - content.tabs.link - search.highlight - search.share - search.suggest @@ -66,4 +70,8 @@ markdown_extensions: # Page tree nav: - Home: index.md - - Config: config.md + - Installation: installation.md + - Config: + - Overview: config.md + - Aliases: config/aliases.md + diff --git a/pkg/asset/asset.go b/pkg/asset/asset.go index 94cb5f1..9899a16 100644 --- a/pkg/asset/asset.go +++ b/pkg/asset/asset.go @@ -45,6 +45,7 @@ var ( bomType = filetype.AddType("bom", "application/octet-stream") pubType = filetype.AddType("pub", "text/plain") tarGzType = filetype.AddType("tgz", "application/tar+gzip") + zstdType = filetype.AddType("zst", "application/zstd") ignoreFileExtensions = []string{ ".txt", @@ -208,7 +209,7 @@ func (a *Asset) Classify(name string) Type { //nolint:gocyclo switch filetype.GetType(ext) { case matchers.TypeDeb, matchers.TypeRpm, msiType, apkType, pkgType: aType = Installer - case matchers.TypeGz, matchers.TypeZip, matchers.TypeXz, matchers.TypeTar, matchers.TypeBz2, tarGzType: + case matchers.TypeGz, matchers.TypeZip, matchers.TypeXz, matchers.TypeTar, matchers.TypeBz2, tarGzType, zstdType, matchers.TypeZstd: aType = Archive case matchers.TypeExe: aType = Binary diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index ae4e9fc..7b4bb29 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -130,13 +130,14 @@ func (p *Provider) discoverBinary(names []string, version string) error { //noli } fileScored[k] = score.Score(v, &score.Options{ - OS: detectedOS, - Arch: arch, - Extensions: ext, - Terms: names, - Versions: []string{version}, - InvalidOS: p.OSConfig.InvalidOS(), - InvalidArch: p.OSConfig.InvalidArchitectures(), + OS: detectedOS, + Arch: arch, + Extensions: ext, + Terms: names, + Versions: []string{version}, + InvalidOS: p.OSConfig.InvalidOS(), + InvalidArch: p.OSConfig.InvalidArchitectures(), + InvalidExtensions: []string{".zst"}, }) if len(fileScored[k]) > 0 { diff --git a/scripts/installer.tpl.ps1 b/scripts/installer.tpl.ps1 new file mode 100644 index 0000000..14d2f6a --- /dev/null +++ b/scripts/installer.tpl.ps1 @@ -0,0 +1,75 @@ +# Exit immediately if a command fails +$ErrorActionPreference = "Stop" + +$RELEASES_URL = "https://github.com/ekristen/distillery/releases" +$FILE_BASENAME = "distillery" +$LATEST = "__VERSION__" + +# Use the provided version or default to the latest +if (-not $env:VERSION) { + $VERSION = $LATEST +} else { + $VERSION = $env:VERSION +} + +if (-not $VERSION) { + Write-Error "Unable to get distillery version." + exit 1 +} + +# Create a temporary directory +$TMP_DIR = New-TemporaryFile | ForEach-Object { Remove-Item $_ -Force; New-Item -ItemType Directory -Path $_ } +$trap = { + Remove-Item -Recurse -Force $TMP_DIR + exit 1 +} + +# Detect OS and Architecture +$OS = "windows" # Hardcoded for Windows +$ARCH = if ([System.Environment]::Is64BitProcess) { "amd64" } else { "arm64" } + +$TAR_FILE = "${FILE_BASENAME}-${VERSION}-${OS}-${ARCH}.tar.gz" + +# Download distillery +Write-Host "Downloading distillery $VERSION..." +Invoke-WebRequest -Uri "$RELEASES_URL/download/$VERSION/$TAR_FILE" -OutFile "$TMP_DIR\$TAR_FILE" +Invoke-WebRequest -Uri "$RELEASES_URL/download/$VERSION/checksums.txt" -OutFile "$TMP_DIR\checksums.txt" + +# Verify checksums +Write-Host "Verifying checksums..." +$checksums = Get-Content "$TMP_DIR\checksums.txt" | Where-Object { $_ -match $TAR_FILE } +if (-not $checksums) { + Write-Error "Checksum not found for $TAR_FILE" + exit 1 +} +$expected = $checksums.Split(" ")[0] +$actual = (Get-FileHash -Path "$TMP_DIR\$TAR_FILE" -Algorithm SHA256).Hash +if ($expected -ne $actual) { + Write-Error "Checksum verification failed!" + exit 1 +} + +# Verify signatures if cosign is available +if (Get-Command cosign -ErrorAction SilentlyContinue) { + Write-Host "Verifying signatures..." + $REF = "refs/tags/$VERSION" + & cosign verify-blob ` + --certificate-identity-regexp "https://github.com/ekristen/distillery.*/.github/workflows/.*.yml@$REF" ` + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' ` + --cert "$RELEASES_URL/download/$VERSION/checksums.txt.pem" ` + --signature "$RELEASES_URL/download/$VERSION/checksums.txt.sig" ` + "$TMP_DIR\checksums.txt" +} else { + Write-Warning "Could not verify signatures, cosign is not installed." +} + +# Extract tar file +Write-Host "Extracting distillery..." +Expand-Archive -Path "$TMP_DIR\$TAR_FILE" -DestinationPath $TMP_DIR -Force + +# Run the installation command +Write-Host "Installing distillery..." +& "$TMP_DIR\dist" "install ekristen/distillery" @args + +# Clean up +Remove-Item -Recurse -Force $TMP_DIR diff --git a/scripts/installer.tpl.sh b/scripts/installer.tpl.sh new file mode 100644 index 0000000..4d1afe3 --- /dev/null +++ b/scripts/installer.tpl.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env sh +set -e + +RELEASES_URL="https://github.com/ekristen/distillery/releases" +FILE_BASENAME="distillery" +LATEST="__VERSION__" + +test -z "$VERSION" && VERSION="$LATEST" + +test -z "$VERSION" && { + echo "Unable to get distillery version." >&2 + exit 1 +} + +TMP_DIR="$(mktemp -d)" +# shellcheck disable=SC2064 # intentionally expands here +trap "rm -rf \"$TMP_DIR\"" EXIT INT TERM + +OS="$(uname -s | sed -e 's/\(.*\)/\L\1/')" +ARCH="$(uname -m)" +case "$ARCH" in + x86_64) ARCH="amd64" ;; # Normalize x86_64 to amd64 + aarch64) ARCH="arm64" ;; # Normalize aarch64 to arm64 +esac +TAR_FILE="${FILE_BASENAME}-${VERSION}-${OS}-${ARCH}.tar.gz" + +( + cd "$TMP_DIR" + echo "Downloading distillery $VERSION..." + curl -sfLO "$RELEASES_URL/download/$VERSION/$TAR_FILE" + curl -sfLO "$RELEASES_URL/download/$VERSION/checksums.txt" + echo "Verifying checksums..." + sha256sum --ignore-missing --quiet --check checksums.txt + if command -v cosign >/dev/null 2>&1; then + echo "Verifying signatures..." + REF="refs/tags/$VERSION" + cosign verify-blob \ + --certificate-identity-regexp "https://github.com/ekristen/distillery.*/.github/workflows/.*.yml@$REF" \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ + --cert "$RELEASES_URL/download/$VERSION/checksums.txt.pem" \ + --signature "$RELEASES_URL/download/$VERSION/checksums.txt.sig" \ + checksums.txt + else + echo "Could not verify signatures, cosign is not installed." + fi +) + +tar -xf "$TMP_DIR/$TAR_FILE" -C "$TMP_DIR" +"$TMP_DIR/dist" "install ekristen/distillery" "$@"