bench51815d036
add release workflow and install scripts
Tagging vX.Y.Z builds Linux (x86_64, aarch64), Windows and macOS (arm64, x86_64) archives and publishes them with SHA256SUMS and the CHANGELOG section as notes, then checks the one-line installers against the new release on every OS. PRs touching this get a dry run without publishing. install.sh (Linux, macOS) and install.ps1 (Windows) download the right archive, verify its checksum, install both binaries, and add a menu entry; both can install from a local archive and uninstall.
+457 −0
3 files changed
- A .github/workflows/release.yml +178 −0
- A install.ps1 +112 −0
- A install.sh +167 −0
| @@ -0,0 +1,178 @@ | |||
| 1 | + | name: Release | |
| 2 | + | ||
| 3 | + | # Pushing a tag vX.Y.Z (matching the version in Cargo.toml) builds bench51 | |
| 4 | + | # for every target, publishes a GitHub release with the archives, checksums | |
| 5 | + | # and the CHANGELOG.md section as notes, then installs it with the one-line | |
| 6 | + | # installers on each OS. Pull requests that touch the release machinery get | |
| 7 | + | # a dry run: everything except publishing. | |
| 8 | + | ||
| 9 | + | on: | |
| 10 | + | push: | |
| 11 | + | tags: ['v*'] | |
| 12 | + | pull_request: | |
| 13 | + | paths: | |
| 14 | + | - .github/workflows/release.yml | |
| 15 | + | - install.sh | |
| 16 | + | - install.ps1 | |
| 17 | + | workflow_dispatch: | |
| 18 | + | ||
| 19 | + | permissions: | |
| 20 | + | contents: read | |
| 21 | + | ||
| 22 | + | env: | |
| 23 | + | CARGO_TERM_COLOR: always | |
| 24 | + | ||
| 25 | + | jobs: | |
| 26 | + | build: | |
| 27 | + | name: ${{ matrix.target }} | |
| 28 | + | strategy: | |
| 29 | + | fail-fast: false | |
| 30 | + | matrix: | |
| 31 | + | include: | |
| 32 | + | # 22.04 keeps the glibc requirement low for older distributions. | |
| 33 | + | - { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 } | |
| 34 | + | - { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04-arm } | |
| 35 | + | - { target: x86_64-pc-windows-msvc, os: windows-latest } | |
| 36 | + | - { target: aarch64-apple-darwin, os: macos-latest } | |
| 37 | + | # Cross-compiled on Apple silicon, so it can't be run here. | |
| 38 | + | - { target: x86_64-apple-darwin, os: macos-latest, cross: true } | |
| 39 | + | runs-on: ${{ matrix.os }} | |
| 40 | + | steps: | |
| 41 | + | - uses: actions/checkout@v7 | |
| 42 | + | ||
| 43 | + | - name: Check the tag matches Cargo.toml | |
| 44 | + | if: startsWith(github.ref, 'refs/tags/v') | |
| 45 | + | shell: bash | |
| 46 | + | run: | | |
| 47 | + | version=$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -n 1) | |
| 48 | + | if [ "$GITHUB_REF_NAME" != "v$version" ]; then | |
| 49 | + | echo "::error::tag $GITHUB_REF_NAME does not match version $version in Cargo.toml" | |
| 50 | + | exit 1 | |
| 51 | + | fi | |
| 52 | + | ||
| 53 | + | - uses: dtolnay/rust-toolchain@stable | |
| 54 | + | with: | |
| 55 | + | targets: ${{ matrix.target }} | |
| 56 | + | ||
| 57 | + | - name: Install Linux GUI dependencies | |
| 58 | + | if: runner.os == 'Linux' | |
| 59 | + | run: | | |
| 60 | + | sudo apt-get update | |
| 61 | + | sudo apt-get install -y libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev | |
| 62 | + | ||
| 63 | + | - uses: Swatinem/rust-cache@v2 | |
| 64 | + | with: | |
| 65 | + | key: ${{ matrix.target }} | |
| 66 | + | ||
| 67 | + | - name: Build | |
| 68 | + | run: cargo build --release --locked --target ${{ matrix.target }} -p bench51 -p bench51-gui | |
| 69 | + | ||
| 70 | + | - name: Package | |
| 71 | + | shell: bash | |
| 72 | + | run: | | |
| 73 | + | name=bench51-${{ matrix.target }} | |
| 74 | + | ext= | |
| 75 | + | if [ "$RUNNER_OS" = Windows ]; then ext=.exe; fi | |
| 76 | + | mkdir -p "dist/$name" | |
| 77 | + | cp "target/${{ matrix.target }}/release/bench51$ext" "target/${{ matrix.target }}/release/bench51-gui$ext" "dist/$name/" | |
| 78 | + | cp README.md LICENSE CHANGELOG.md "dist/$name/" | |
| 79 | + | if [ "$RUNNER_OS" = Linux ]; then | |
| 80 | + | mkdir -p "dist/$name/share/applications" "dist/$name/share/icons/hicolor/scalable/apps" | |
| 81 | + | cp crates/bench51-gui/assets/bench51-gui.desktop "dist/$name/share/applications/" | |
| 82 | + | cp crates/bench51-gui/assets/bench51-gui.svg "dist/$name/share/icons/hicolor/scalable/apps/" | |
| 83 | + | fi | |
| 84 | + | cd dist | |
| 85 | + | if [ "$RUNNER_OS" = Windows ]; then | |
| 86 | + | 7z a -tzip "$name.zip" "$name" > /dev/null | |
| 87 | + | else | |
| 88 | + | tar -czf "$name.tar.gz" "$name" | |
| 89 | + | fi | |
| 90 | + | rm -rf "$name" | |
| 91 | + | ls -l | |
| 92 | + | ||
| 93 | + | - name: Install the archive and run it | |
| 94 | + | if: runner.os != 'Windows' && !matrix.cross | |
| 95 | + | shell: bash | |
| 96 | + | run: | | |
| 97 | + | sh install.sh --archive "dist/bench51-${{ matrix.target }}.tar.gz" --dir "$RUNNER_TEMP/bin" --no-desktop | |
| 98 | + | "$RUNNER_TEMP/bin/bench51-gui" --version | |
| 99 | + | printf 'ORG 0\nloop: MOV A,#1\n SJMP loop\n END\n' > "$RUNNER_TEMP/t.asm" | |
| 100 | + | "$RUNNER_TEMP/bin/bench51" --assemble "$RUNNER_TEMP/t.asm" | |
| 101 | + | test -s "$RUNNER_TEMP/t.hex" | |
| 102 | + | ||
| 103 | + | - name: Install the archive and run it | |
| 104 | + | if: runner.os == 'Windows' | |
| 105 | + | shell: pwsh | |
| 106 | + | run: | | |
| 107 | + | ./install.ps1 -Archive "dist/bench51-${{ matrix.target }}.zip" -InstallDir "$env:RUNNER_TEMP\bench51" -NoShortcut | |
| 108 | + | if ($LASTEXITCODE) { exit $LASTEXITCODE } | |
| 109 | + | # The GUI has no console; check its exit code instead of its output. | |
| 110 | + | $gui = Start-Process -FilePath "$env:RUNNER_TEMP\bench51\bench51-gui.exe" -ArgumentList '--version' -Wait -PassThru | |
| 111 | + | if ($gui.ExitCode -ne 0) { exit 1 } | |
| 112 | + | ||
| 113 | + | - uses: actions/upload-artifact@v7 | |
| 114 | + | with: | |
| 115 | + | name: bench51-${{ matrix.target }} | |
| 116 | + | path: dist/* | |
| 117 | + | if-no-files-found: error | |
| 118 | + | ||
| 119 | + | publish: | |
| 120 | + | needs: build | |
| 121 | + | if: startsWith(github.ref, 'refs/tags/v') | |
| 122 | + | runs-on: ubuntu-latest | |
| 123 | + | permissions: | |
| 124 | + | contents: write | |
| 125 | + | steps: | |
| 126 | + | - uses: actions/checkout@v7 | |
| 127 | + | ||
| 128 | + | - uses: actions/download-artifact@v8 | |
| 129 | + | with: | |
| 130 | + | path: dist | |
| 131 | + | merge-multiple: true | |
| 132 | + | ||
| 133 | + | - name: Checksums | |
| 134 | + | run: | | |
| 135 | + | cd dist | |
| 136 | + | sha256sum -- * > SHA256SUMS | |
| 137 | + | cat SHA256SUMS | |
| 138 | + | ||
| 139 | + | - name: Release notes from CHANGELOG.md | |
| 140 | + | run: | | |
| 141 | + | version=${GITHUB_REF_NAME#v} | |
| 142 | + | awk -v v="$version" '/^## \[/ { if (on) exit; on = index($0, "[" v "]") } on' CHANGELOG.md | tail -n +2 > notes.md | |
| 143 | + | if [ ! -s notes.md ]; then | |
| 144 | + | echo "::error::CHANGELOG.md has no section for $version" | |
| 145 | + | exit 1 | |
| 146 | + | fi | |
| 147 | + | cat notes.md | |
| 148 | + | ||
| 149 | + | - name: Publish | |
| 150 | + | env: | |
| 151 | + | GH_TOKEN: ${{ github.token }} | |
| 152 | + | run: gh release create "$GITHUB_REF_NAME" dist/* --verify-tag --title "bench51 $GITHUB_REF_NAME" --notes-file notes.md | |
| 153 | + | ||
| 154 | + | # Installs the published release exactly as the README tells people to. | |
| 155 | + | verify: | |
| 156 | + | name: install on ${{ matrix.os }} | |
| 157 | + | needs: publish | |
| 158 | + | strategy: | |
| 159 | + | fail-fast: false | |
| 160 | + | matrix: | |
| 161 | + | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 162 | + | runs-on: ${{ matrix.os }} | |
| 163 | + | steps: | |
| 164 | + | - name: One-line install | |
| 165 | + | if: runner.os != 'Windows' | |
| 166 | + | run: | | |
| 167 | + | curl -fsSL "https://raw.githubusercontent.com/$GITHUB_REPOSITORY/$GITHUB_REF_NAME/install.sh" | sh | |
| 168 | + | "$HOME/.local/bin/bench51" --version | grep -F "${GITHUB_REF_NAME#v}" | |
| 169 | + | "$HOME/.local/bin/bench51-gui" --version | grep -F "${GITHUB_REF_NAME#v}" | |
| 170 | + | ||
| 171 | + | - name: One-line install | |
| 172 | + | if: runner.os == 'Windows' | |
| 173 | + | shell: pwsh | |
| 174 | + | run: | | |
| 175 | + | irm "https://raw.githubusercontent.com/$env:GITHUB_REPOSITORY/$env:GITHUB_REF_NAME/install.ps1" | iex | |
| 176 | + | $version = & "$env:LOCALAPPDATA\Programs\bench51\bench51.exe" --version | |
| 177 | + | if ($version -notmatch [regex]::Escape($env:GITHUB_REF_NAME.TrimStart('v'))) { exit 1 } | |
| 178 | + | if (-not (Test-Path "$([Environment]::GetFolderPath('Programs'))\bench51.lnk")) { exit 1 } | |
| @@ -0,0 +1,112 @@ | |||
| 1 | + | # Installs bench51 (the bench51-gui desktop app and the bench51 terminal app) | |
| 2 | + | # from the GitHub releases, on Windows. | |
| 3 | + | # | |
| 4 | + | # irm https://raw.githubusercontent.com/krolyxon/bench51/master/install.ps1 | iex | |
| 5 | + | # | |
| 6 | + | # With options: | |
| 7 | + | # | |
| 8 | + | # & ([scriptblock]::Create((irm https://raw.githubusercontent.com/krolyxon/bench51/master/install.ps1))) -Version v0.2.0 | |
| 9 | + | # | |
| 10 | + | # Options: -Version <tag> (default latest), -InstallDir <path> (default | |
| 11 | + | # %LOCALAPPDATA%\Programs\bench51), -Archive <bench51-<target>.zip>, | |
| 12 | + | # -NoShortcut, -Uninstall. BENCH51_VERSION and BENCH51_INSTALL_DIR set the | |
| 13 | + | # defaults for -Version and -InstallDir when piping into iex. | |
| 14 | + | ||
| 15 | + | param( | |
| 16 | + | [string]$Version = $(if ($env:BENCH51_VERSION) { $env:BENCH51_VERSION } else { 'latest' }), | |
| 17 | + | [string]$InstallDir = $(if ($env:BENCH51_INSTALL_DIR) { $env:BENCH51_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'Programs\bench51' }), | |
| 18 | + | [string]$Archive, | |
| 19 | + | [switch]$NoShortcut, | |
| 20 | + | [switch]$Uninstall | |
| 21 | + | ) | |
| 22 | + | ||
| 23 | + | $ErrorActionPreference = 'Stop' | |
| 24 | + | # Invoke-WebRequest is much slower with the progress bar on. | |
| 25 | + | $ProgressPreference = 'SilentlyContinue' | |
| 26 | + | [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 | |
| 27 | + | ||
| 28 | + | $Repo = 'krolyxon/bench51' | |
| 29 | + | $Asset = 'bench51-x86_64-pc-windows-msvc.zip' | |
| 30 | + | $Shortcut = Join-Path ([Environment]::GetFolderPath('Programs')) 'bench51.lnk' | |
| 31 | + | ||
| 32 | + | function Write-Step([string]$Message) { | |
| 33 | + | Write-Host "bench51: $Message" | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | # Adds or removes $Dir in the user's PATH. | |
| 37 | + | function Edit-UserPath([string]$Dir, [bool]$Add) { | |
| 38 | + | $current = [Environment]::GetEnvironmentVariable('Path', 'User') | |
| 39 | + | $parts = @(($current -split ';') | Where-Object { $_ -and ($_.TrimEnd('\') -ne $Dir.TrimEnd('\')) }) | |
| 40 | + | if ($Add) { | |
| 41 | + | $parts += $Dir | |
| 42 | + | } | |
| 43 | + | [Environment]::SetEnvironmentVariable('Path', ($parts -join ';'), 'User') | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | if ($Uninstall) { | |
| 47 | + | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $InstallDir | |
| 48 | + | Remove-Item -Force -ErrorAction SilentlyContinue $Shortcut | |
| 49 | + | Edit-UserPath $InstallDir $false | |
| 50 | + | Write-Step "removed bench51 from $InstallDir" | |
| 51 | + | return | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | $Temp = Join-Path ([IO.Path]::GetTempPath()) ("bench51-" + [guid]::NewGuid()) | |
| 55 | + | New-Item -ItemType Directory -Path $Temp | Out-Null | |
| 56 | + | try { | |
| 57 | + | if (-not $Archive) { | |
| 58 | + | if ($Version -eq 'latest') { | |
| 59 | + | $Base = "https://github.com/$Repo/releases/latest/download" | |
| 60 | + | } else { | |
| 61 | + | if (-not $Version.StartsWith('v')) { | |
| 62 | + | $Version = "v$Version" | |
| 63 | + | } | |
| 64 | + | $Base = "https://github.com/$Repo/releases/download/$Version" | |
| 65 | + | } | |
| 66 | + | Write-Step "downloading $Asset ($Version)" | |
| 67 | + | $Archive = Join-Path $Temp $Asset | |
| 68 | + | $Sums = Join-Path $Temp 'SHA256SUMS' | |
| 69 | + | Invoke-WebRequest -UseBasicParsing -Uri "$Base/$Asset" -OutFile $Archive | |
| 70 | + | Invoke-WebRequest -UseBasicParsing -Uri "$Base/SHA256SUMS" -OutFile $Sums | |
| 71 | + | $line = Get-Content $Sums | Where-Object { $_ -match ('\s\*?' + [regex]::Escape($Asset) + '$') } | Select-Object -First 1 | |
| 72 | + | if (-not $line) { | |
| 73 | + | throw "$Asset is not listed in SHA256SUMS" | |
| 74 | + | } | |
| 75 | + | $expected = ($line -split '\s+')[0].ToLowerInvariant() | |
| 76 | + | $actual = (Get-FileHash -Algorithm SHA256 -Path $Archive).Hash.ToLowerInvariant() | |
| 77 | + | if ($actual -ne $expected) { | |
| 78 | + | throw "checksum mismatch for $Asset" | |
| 79 | + | } | |
| 80 | + | } | |
| 81 | + | if (-not (Test-Path $Archive)) { | |
| 82 | + | throw "no such archive: $Archive" | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | $Extracted = Join-Path $Temp 'x' | |
| 86 | + | Expand-Archive -Path $Archive -DestinationPath $Extracted -Force | |
| 87 | + | $gui = Get-ChildItem -Path $Extracted -Recurse -Filter 'bench51-gui.exe' | Select-Object -First 1 | |
| 88 | + | if (-not $gui) { | |
| 89 | + | throw 'the archive has no bench51-gui.exe' | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null | |
| 93 | + | Copy-Item -Path (Join-Path $gui.DirectoryName '*') -Destination $InstallDir -Recurse -Force | |
| 94 | + | Edit-UserPath $InstallDir $true | |
| 95 | + | $env:Path = "$env:Path;$InstallDir" | |
| 96 | + | Write-Step "installed bench51 and bench51-gui to $InstallDir" | |
| 97 | + | ||
| 98 | + | if (-not $NoShortcut) { | |
| 99 | + | $shell = New-Object -ComObject WScript.Shell | |
| 100 | + | $link = $shell.CreateShortcut($Shortcut) | |
| 101 | + | $link.TargetPath = Join-Path $InstallDir 'bench51-gui.exe' | |
| 102 | + | $link.WorkingDirectory = $env:USERPROFILE | |
| 103 | + | $link.Description = 'Intel 8051 assembler, simulator and debugger' | |
| 104 | + | $link.Save() | |
| 105 | + | Write-Step 'added bench51 to the Start menu' | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | & (Join-Path $InstallDir 'bench51.exe') --version | |
| 109 | + | Write-Step 'open a new terminal to use bench51 and bench51-gui from PATH' | |
| 110 | + | } finally { | |
| 111 | + | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $Temp | |
| 112 | + | } | |
| @@ -0,0 +1,167 @@ | |||
| 1 | + | #!/bin/sh | |
| 2 | + | # Installs bench51 (the bench51-gui desktop app and the bench51 terminal app) | |
| 3 | + | # from the GitHub releases, on Linux and macOS. | |
| 4 | + | # | |
| 5 | + | # curl -fsSL https://raw.githubusercontent.com/krolyxon/bench51/master/install.sh | sh | |
| 6 | + | # curl -fsSL https://raw.githubusercontent.com/krolyxon/bench51/master/install.sh | sh -s -- --dir /usr/local/bin | |
| 7 | + | # | |
| 8 | + | # Run with --help for all options. On Windows, use install.ps1. | |
| 9 | + | ||
| 10 | + | set -eu | |
| 11 | + | ||
| 12 | + | REPO=krolyxon/bench51 | |
| 13 | + | VERSION=${BENCH51_VERSION:-latest} | |
| 14 | + | DIR=${BENCH51_INSTALL_DIR:-$HOME/.local/bin} | |
| 15 | + | ARCHIVE= | |
| 16 | + | DESKTOP=1 | |
| 17 | + | UNINSTALL=0 | |
| 18 | + | ||
| 19 | + | DATA=${XDG_DATA_HOME:-$HOME/.local/share} | |
| 20 | + | APPS=$DATA/applications | |
| 21 | + | ICONS=$DATA/icons/hicolor/scalable/apps | |
| 22 | + | ||
| 23 | + | usage() { | |
| 24 | + | cat <<EOF | |
| 25 | + | Install bench51 from https://github.com/$REPO/releases | |
| 26 | + | ||
| 27 | + | Usage: install.sh [options] | |
| 28 | + | ||
| 29 | + | --version <tag> release to install, e.g. v0.2.0 (default: latest) | |
| 30 | + | --dir <path> where to put the binaries (default: ~/.local/bin) | |
| 31 | + | --archive <file> install from a downloaded bench51-<target>.tar.gz | |
| 32 | + | --no-desktop on Linux, skip the application menu entry and icon | |
| 33 | + | --uninstall remove bench51 from --dir and the menu | |
| 34 | + | -h, --help show this help | |
| 35 | + | ||
| 36 | + | The environment variables BENCH51_VERSION and BENCH51_INSTALL_DIR set the | |
| 37 | + | defaults for --version and --dir. | |
| 38 | + | EOF | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | say() { printf 'bench51: %s\n' "$*"; } | |
| 42 | + | die() { | |
| 43 | + | printf 'bench51: error: %s\n' "$*" >&2 | |
| 44 | + | exit 1 | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | while [ $# -gt 0 ]; do | |
| 48 | + | case $1 in | |
| 49 | + | --version) [ $# -ge 2 ] || die "--version needs a value"; VERSION=$2; shift 2 ;; | |
| 50 | + | --version=*) VERSION=${1#*=}; shift ;; | |
| 51 | + | --dir) [ $# -ge 2 ] || die "--dir needs a value"; DIR=$2; shift 2 ;; | |
| 52 | + | --dir=*) DIR=${1#*=}; shift ;; | |
| 53 | + | --archive) [ $# -ge 2 ] || die "--archive needs a value"; ARCHIVE=$2; shift 2 ;; | |
| 54 | + | --archive=*) ARCHIVE=${1#*=}; shift ;; | |
| 55 | + | --no-desktop) DESKTOP=0; shift ;; | |
| 56 | + | --uninstall) UNINSTALL=1; shift ;; | |
| 57 | + | -h | --help) usage; exit 0 ;; | |
| 58 | + | *) die "unknown option: $1 (see --help)" ;; | |
| 59 | + | esac | |
| 60 | + | done | |
| 61 | + | ||
| 62 | + | refresh_menu() { | |
| 63 | + | if command -v update-desktop-database >/dev/null 2>&1; then | |
| 64 | + | update-desktop-database -q "$APPS" 2>/dev/null || true | |
| 65 | + | fi | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | if [ "$UNINSTALL" = 1 ]; then | |
| 69 | + | rm -f "$DIR/bench51" "$DIR/bench51-gui" "$APPS/bench51-gui.desktop" "$ICONS/bench51-gui.svg" | |
| 70 | + | refresh_menu | |
| 71 | + | say "removed bench51 from $DIR" | |
| 72 | + | exit 0 | |
| 73 | + | fi | |
| 74 | + | ||
| 75 | + | # ---- Which build fits this machine ---- | |
| 76 | + | ||
| 77 | + | os=$(uname -s) | |
| 78 | + | arch=$(uname -m) | |
| 79 | + | case $os in | |
| 80 | + | Linux) os=unknown-linux-gnu ;; | |
| 81 | + | Darwin) os=apple-darwin ;; | |
| 82 | + | *) die "no prebuilt binaries for $os; on Windows use install.ps1" ;; | |
| 83 | + | esac | |
| 84 | + | case $arch in | |
| 85 | + | x86_64 | amd64) arch=x86_64 ;; | |
| 86 | + | aarch64 | arm64) arch=aarch64 ;; | |
| 87 | + | *) die "no prebuilt binaries for $arch; build from source with: cargo install --git https://github.com/$REPO bench51-gui" ;; | |
| 88 | + | esac | |
| 89 | + | TARGET=$arch-$os | |
| 90 | + | NAME=bench51-$TARGET.tar.gz | |
| 91 | + | ||
| 92 | + | download() { | |
| 93 | + | if command -v curl >/dev/null 2>&1; then | |
| 94 | + | curl --proto '=https' --tlsv1.2 -fsSL -o "$2" "$1" | |
| 95 | + | elif command -v wget >/dev/null 2>&1; then | |
| 96 | + | wget -q --https-only -O "$2" "$1" | |
| 97 | + | else | |
| 98 | + | die "neither curl nor wget is installed" | |
| 99 | + | fi | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | sha256() { | |
| 103 | + | if command -v sha256sum >/dev/null 2>&1; then | |
| 104 | + | sha256sum "$1" | cut -d ' ' -f 1 | |
| 105 | + | elif command -v shasum >/dev/null 2>&1; then | |
| 106 | + | shasum -a 256 "$1" | cut -d ' ' -f 1 | |
| 107 | + | else | |
| 108 | + | die "neither sha256sum nor shasum is installed to verify the download" | |
| 109 | + | fi | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | TMP=$(mktemp -d 2>/dev/null || mktemp -d -t bench51) | |
| 113 | + | trap 'rm -rf "$TMP"' EXIT | |
| 114 | + | trap 'exit 130' INT TERM | |
| 115 | + | ||
| 116 | + | # ---- Download and verify ---- | |
| 117 | + | ||
| 118 | + | if [ -z "$ARCHIVE" ]; then | |
| 119 | + | if [ "$VERSION" = latest ]; then | |
| 120 | + | BASE=https://github.com/$REPO/releases/latest/download | |
| 121 | + | else | |
| 122 | + | case $VERSION in v*) ;; *) VERSION=v$VERSION ;; esac | |
| 123 | + | BASE=https://github.com/$REPO/releases/download/$VERSION | |
| 124 | + | fi | |
| 125 | + | say "downloading $NAME ($VERSION)" | |
| 126 | + | download "$BASE/$NAME" "$TMP/$NAME" || die "could not download $BASE/$NAME" | |
| 127 | + | download "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" || die "could not download the checksums" | |
| 128 | + | expected=$(grep " [* ]*$NAME\$" "$TMP/SHA256SUMS" | cut -d ' ' -f 1) | |
| 129 | + | [ -n "$expected" ] || die "$NAME is not listed in SHA256SUMS" | |
| 130 | + | [ "$(sha256 "$TMP/$NAME")" = "$expected" ] || die "checksum mismatch for $NAME" | |
| 131 | + | ARCHIVE=$TMP/$NAME | |
| 132 | + | fi | |
| 133 | + | [ -f "$ARCHIVE" ] || die "no such archive: $ARCHIVE" | |
| 134 | + | ||
| 135 | + | mkdir "$TMP/x" | |
| 136 | + | tar -xzf "$ARCHIVE" -C "$TMP/x" | |
| 137 | + | SRC=$(find "$TMP/x" -type f -name bench51-gui | head -n 1) | |
| 138 | + | [ -n "$SRC" ] || die "the archive has no bench51-gui" | |
| 139 | + | SRC=$(dirname "$SRC") | |
| 140 | + | [ -f "$SRC/bench51" ] || die "the archive has no bench51" | |
| 141 | + | ||
| 142 | + | # ---- Install ---- | |
| 143 | + | ||
| 144 | + | mkdir -p "$DIR" | |
| 145 | + | for bin in bench51 bench51-gui; do | |
| 146 | + | cp "$SRC/$bin" "$DIR/$bin.tmp" | |
| 147 | + | chmod 755 "$DIR/$bin.tmp" | |
| 148 | + | mv -f "$DIR/$bin.tmp" "$DIR/$bin" | |
| 149 | + | done | |
| 150 | + | say "installed bench51 and bench51-gui to $DIR" | |
| 151 | + | ||
| 152 | + | if [ "$DESKTOP" = 1 ] && [ "$(uname -s)" = Linux ] && [ -f "$SRC/share/applications/bench51-gui.desktop" ]; then | |
| 153 | + | mkdir -p "$APPS" "$ICONS" | |
| 154 | + | # Launchers don't always see ~/.local/bin on PATH: point at the binary. | |
| 155 | + | exec_path=$(printf '%s' "$DIR/bench51-gui" | sed 's/[\\&|]/\\&/g') | |
| 156 | + | sed "s|^Exec=bench51-gui|Exec=\"$exec_path\"|" \ | |
| 157 | + | "$SRC/share/applications/bench51-gui.desktop" >"$APPS/bench51-gui.desktop" | |
| 158 | + | cp "$SRC/share/icons/hicolor/scalable/apps/bench51-gui.svg" "$ICONS/bench51-gui.svg" | |
| 159 | + | refresh_menu | |
| 160 | + | say "added bench51 to the application menu" | |
| 161 | + | fi | |
| 162 | + | ||
| 163 | + | "$DIR/bench51" --version | |
| 164 | + | case ":$PATH:" in | |
| 165 | + | *":$DIR:"*) say "run bench51-gui for the desktop app, or bench51 in a terminal" ;; | |
| 166 | + | *) say "$DIR is not on your PATH; add it, e.g. in ~/.profile: export PATH=\"$DIR:\$PATH\"" ;; | |
| 167 | + | esac | |