Add validation workflow and main installer script for GitHub releases
This commit is contained in:
44
.gitea/workflows/validate.yaml
Normal file
44
.gitea/workflows/validate.yaml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
name: Validate Script
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'github-release-installer'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Validate Script
|
||||||
|
run: |
|
||||||
|
# Check if the script is executable
|
||||||
|
if [ ! -x github-release-installer ]; then
|
||||||
|
echo "Error: github-release-installer is not executable."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Syntax Check
|
||||||
|
run: |
|
||||||
|
# Check the syntax of the script
|
||||||
|
if ! bash -n github-release-installer; then
|
||||||
|
echo "Error: Syntax errors found in github-release-installer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Help Option Check
|
||||||
|
run: |
|
||||||
|
# Run the script with --help to check for basic functionality
|
||||||
|
if ! ./github-release-installer --help > /dev/null; then
|
||||||
|
echo "Error: github-release-installer did not run successfully."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Github CLI Release List Check
|
||||||
|
run: |
|
||||||
|
./github-release-installer -l cli/cli
|
||||||
|
|
||||||
|
- name: Github CLI Install Check
|
||||||
|
run: |
|
||||||
|
./github-release-installer cli/cli
|
||||||
47
README.md
Normal file
47
README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Github Release Installer Bash Script
|
||||||
|
|
||||||
|
The `github-release-installer` is a bash script that allows you to easily download and install assets from GitHub releases. It supports various options for customizing the installation process, such as specifying the release version, filtering assets by name, and performing dry runs.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Run the script with a GitHub repository in `owner/repo` format:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./github-release-installer [options] "owner/repo"
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install latest matching release asset for the current OS/arch
|
||||||
|
./github-release-installer cli/cli
|
||||||
|
|
||||||
|
# Preview what would be selected without downloading/installing
|
||||||
|
./github-release-installer --dry-run cli/cli
|
||||||
|
|
||||||
|
# List downloadable assets from the latest release
|
||||||
|
./github-release-installer --list cli/cli
|
||||||
|
|
||||||
|
# Install using explicit app and binary names
|
||||||
|
./github-release-installer --app-name "GitHub CLI" --binary-name gh cli/cli
|
||||||
|
|
||||||
|
# Download a specific asset file to a directory (no install)
|
||||||
|
./github-release-installer --file-name gh_2.70.0_macOS_arm64.zip --output-directory /tmp cli/cli
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful options:
|
||||||
|
|
||||||
|
- `--dry-run [level]`: test mode, print what would happen.
|
||||||
|
- `--list`: print release asset URLs.
|
||||||
|
- `--file-name <name>`: choose an exact asset name.
|
||||||
|
- `--type <archive|package|zip|tar.gz|tgz|deb|pkg|rpm>`: filter by asset type.
|
||||||
|
- `--binary-name <src[:dest]>`: install binary using optional destination name.
|
||||||
|
- `--output-directory <dir>`: download only, do not install.
|
||||||
|
- `--releases-json`: download latest release metadata as JSON.
|
||||||
|
|
||||||
|
For full help:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./github-release-installer --help
|
||||||
|
```
|
||||||
|
|
||||||
492
github-release-installer
Executable file
492
github-release-installer
Executable file
@@ -0,0 +1,492 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
# An installer script for GitHub releases
|
||||||
|
# Usage: github-release-installer [--test|-t|--debug|-d|--dry-run|-n] "App Name" "binary_name" "repo/name"
|
||||||
|
|
||||||
|
# Globals
|
||||||
|
GITHUB_API_BASE_URL="https://api.github.com"
|
||||||
|
GITHUB_TOKEN="${GITHUB_TOKEN:-}"
|
||||||
|
GITHUB_TOKEN_FILE="${GITHUB_TOKEN_FILE:-$HOME/.github_public_token.env}"
|
||||||
|
CURL_OPTS=("-s" "-L")
|
||||||
|
|
||||||
|
# User authentication for GitHub API requests
|
||||||
|
if [[ -z $GITHUB_TOKEN ]]; then
|
||||||
|
[[ -f $GITHUB_TOKEN_FILE ]] && source $GITHUB_TOKEN_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If we have a GitHub token, use it for authentication
|
||||||
|
[[ -n $GITHUB_TOKEN ]] && CURL_OPTS+=("--header" "Authorization: token $GITHUB_TOKEN")
|
||||||
|
|
||||||
|
# Load github-specific curl options, if present
|
||||||
|
[[ -f $HOME/.curlrc-github ]] && CURL_OPTS+=("--config" "$HOME/.curlrc-github")
|
||||||
|
|
||||||
|
function discover_system_and_architecture() {
|
||||||
|
# Detect system and architecture, unless overridden
|
||||||
|
SYSTEM="${SYSTEM:-$(uname -s)}"
|
||||||
|
ARCH="${ARCH:-$(uname -m)}"
|
||||||
|
|
||||||
|
case "${SYSTEM,,}" in
|
||||||
|
linux)
|
||||||
|
SYSTEM=Linux
|
||||||
|
SYSTEM_RE="linux"
|
||||||
|
ROOT_USER=root
|
||||||
|
ROOT_GROUP=root
|
||||||
|
|
||||||
|
case "${ARCH,,}" in
|
||||||
|
x86_64 | amd64)
|
||||||
|
ARCH="Intel 64-bit"
|
||||||
|
ARCH_RE="(x64|amd64)"
|
||||||
|
;;
|
||||||
|
aarch64 | arm64)
|
||||||
|
ARCH="ARM 64-bit"
|
||||||
|
ARCH_RE="arm64"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "This script only supports x86_64 and arm64 architectures." >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
|
||||||
|
darwin|osx|macos)
|
||||||
|
SYSTEM=MacOS
|
||||||
|
SYSTEM_RE="(darwin|osx)"
|
||||||
|
ROOT_USER=root
|
||||||
|
ROOT_GROUP=wheel
|
||||||
|
|
||||||
|
case "${ARCH,,}" in
|
||||||
|
x86_64)
|
||||||
|
ARCH="Intel 64-bit"
|
||||||
|
ARCH_RE="(x86_64|x64|amd64|universal)"
|
||||||
|
;;
|
||||||
|
arm64)
|
||||||
|
ARCH="ARM 64-bit"
|
||||||
|
ARCH_RE="(arm64|universal)"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "This script only supports x86_64 and arm64 architectures." >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "This script only supports Linux and macOS." >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function github_assets() {
|
||||||
|
if [[ -z $REPO_NAME ]]; then
|
||||||
|
echo "ERROR: Repository name is required to list GitHub assets." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Expand combined asset types
|
||||||
|
if [[ -z $ASSET_TYPE ]]; then
|
||||||
|
# Configure asset selector to match operating system packages and archives
|
||||||
|
case $SYSTEM in
|
||||||
|
Linux)
|
||||||
|
ASSET_TYPE='\.(deb|rpm|zip|tar\.gz|tgz)$'
|
||||||
|
;;
|
||||||
|
MacOS)
|
||||||
|
ASSET_TYPE='\.(pkg|zip|tar\.gz|tgz)$'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ASSET_TYPE='\.(zip|tar\.gz|tgz)$'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
elif [[ $ASSET_TYPE == "archive" ]]; then
|
||||||
|
ASSET_TYPE='\.(zip|tar\.gz|tgz)$'
|
||||||
|
elif [[ $ASSET_TYPE == "package" ]]; then
|
||||||
|
ASSET_TYPE='\.(deb|pkg|rpm)$'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check, if the asset type has a '$' at the end
|
||||||
|
if [[ ! $ASSET_TYPE =~ \$\$?$ ]]; then
|
||||||
|
ASSET_TYPE="${ASSET_TYPE}$"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Asset type should start with a dot
|
||||||
|
if [[ ! $ASSET_TYPE =~ ^\\\. ]]; then
|
||||||
|
ASSET_TYPE="\.$ASSET_TYPE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Preprocess GitHub releases API response
|
||||||
|
if [[ -z $ASSET_NAME || $ASSET_NAME =~ ^~ ]]; then
|
||||||
|
# No asset name provided, or asset name is a regex pattern
|
||||||
|
if [[ -z $ASSET_NAME ]]; then
|
||||||
|
PATTERN=".*"
|
||||||
|
[[ $TEST_MODE -gt 1 ]] && echo "[DEBUG]: Filtering assets for system regex: '$SYSTEM_RE' and architecture regex: '$ARCH_RE'." >&2
|
||||||
|
else
|
||||||
|
PATTERN="${ASSET_NAME:1}"
|
||||||
|
[[ $TEST_MODE -gt 1 ]] && echo "[DEBUG]: Filtering assets for system regex: '$SYSTEM_RE', architecture regex: '$ARCH_RE', asset type regex: '$ASSET_TYPE'." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl "${CURL_OPTS[@]}" "$GITHUB_API_BASE_URL/repos/$REPO_NAME/releases/latest" |
|
||||||
|
jq -r \
|
||||||
|
--arg PATTERN "$PATTERN" \
|
||||||
|
--arg SYSTEM "$SYSTEM_RE" \
|
||||||
|
--arg ARCH "$ARCH_RE" \
|
||||||
|
--arg ASSET_TYPE "$ASSET_TYPE" \
|
||||||
|
'{version: .tag_name | sub("^v"; ""; "i"), assets: [.assets[] | {name, browser_download_url} | select(.name | test("^" + $PATTERN + ".*" + $SYSTEM + "[_-]" + $ARCH + ".*" + $ASSET_TYPE; "i"))]}'
|
||||||
|
else
|
||||||
|
# Specific asset name provided
|
||||||
|
curl "${CURL_OPTS[@]}" "$GITHUB_API_BASE_URL/repos/$REPO_NAME/releases/latest" |
|
||||||
|
jq -r \
|
||||||
|
--arg ASSET_NAME "$ASSET_NAME" \
|
||||||
|
'{version: .tag_name | sub("^v"; ""; "i"), assets: [.assets[] | {name, browser_download_url} | select(.name == $ASSET_NAME)]}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function define_sudo_cmd() {
|
||||||
|
if [[ $(id -u) -ne 0 ]]; then
|
||||||
|
# Not running as root, need sudo for installation
|
||||||
|
SUDOCMD="sudo"
|
||||||
|
if ! command -v sudo &> /dev/null; then
|
||||||
|
echo "ERROR: sudo is not installed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
SUDOCMD=""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function github_release_installer() {
|
||||||
|
if ! command -v jq &> /dev/null || ! command -v curl &> /dev/null; then
|
||||||
|
echo "ERROR: jq or curl is not installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check, if required variables are set
|
||||||
|
if [[ -z $APP_NAME || -z $BINARY_NAME || -z $REPO_NAME ]]; then
|
||||||
|
# Check, if the values were provided as function arguments
|
||||||
|
if [[ $# -ne 3 ]]; then
|
||||||
|
echo "ERROR: APP_NAME, BINARY_NAME, and REPO_NAME must be set before calling github_release_installer." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set from function arguments
|
||||||
|
APP_NAME="$1"
|
||||||
|
BINARY_NAME="$2"
|
||||||
|
REPO_NAME="$3"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect system and architecture
|
||||||
|
discover_system_and_architecture || return 1
|
||||||
|
|
||||||
|
echo "Detected system: $SYSTEM, architecture: $ARCH"
|
||||||
|
|
||||||
|
# Download GitHub releases API response
|
||||||
|
GITHUB_API_RESPONSE="$(github_assets)"
|
||||||
|
if [[ -z $GITHUB_API_RESPONSE ]]; then
|
||||||
|
echo "ERROR: Could not download GitHub releases for '$APP_NAME'." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
[[ $TEST_MODE -gt 1 ]] && echo "[DEBUG]: GitHub API response for '$APP_NAME' is:" && cat <<< "$GITHUB_API_RESPONSE" >&2
|
||||||
|
|
||||||
|
if [[ $TEST_MODE -gt 1 ]]; then
|
||||||
|
echo "The filtered GitHub API response for '$APP_NAME' is:"
|
||||||
|
echo "$GITHUB_API_RESPONSE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION=$(jq -r .version <<< "$GITHUB_API_RESPONSE")
|
||||||
|
if [[ -z $VERSION ]]; then
|
||||||
|
echo "ERROR: Could not determine the latest version of '$APP_NAME'." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ $TEST_MODE -gt 1 ]] && echo "[DEBUG]: Latest version of '$APP_NAME' is $VERSION."
|
||||||
|
[[ $TEST_MODE -gt 1 ]] && echo "[DEBUG]: Asset name to download: ${ASSET_NAME:-<not specified>}."
|
||||||
|
|
||||||
|
NUM_ASSETS=$(jq -r '.assets | length' <<< "$GITHUB_API_RESPONSE")
|
||||||
|
if [[ $NUM_ASSETS -eq 0 ]]; then
|
||||||
|
echo "ERROR: No assets found for '$APP_NAME' version $VERSION for $SYSTEM $ARCH." >&2
|
||||||
|
return 1
|
||||||
|
elif [[ $NUM_ASSETS -gt 1 ]]; then
|
||||||
|
echo "WARNING: Multiple assets found for '$APP_NAME' version $VERSION for $SYSTEM $ARCH."
|
||||||
|
echo -e "Please specify an asset name from the list below:\n"
|
||||||
|
jq -r '.assets[] | "- \(.name)"' <<< "$GITHUB_API_RESPONSE"
|
||||||
|
echo ""
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $ASSET_NAME ]]; then
|
||||||
|
# No asset name provided, check how many assets are available
|
||||||
|
DOWNLOAD_URL=$(jq -r '.assets[].browser_download_url' <<< "$GITHUB_API_RESPONSE")
|
||||||
|
elif [[ $ASSET_NAME =~ ^~ ]]; then
|
||||||
|
# Asset name is a regex pattern
|
||||||
|
PATTERN="${ASSET_NAME:1}"
|
||||||
|
[[ $TEST_MODE -gt 0 ]] && echo "[DEBUG]: Using regex pattern to find asset: $PATTERN" >&2
|
||||||
|
DOWNLOAD_URL=$(jq -r --arg PATTERN "$PATTERN" '.assets[] | select(.name | test($PATTERN; "i")) | .browser_download_url' <<< "$GITHUB_API_RESPONSE")
|
||||||
|
else
|
||||||
|
DOWNLOAD_URL=$(jq -r --arg ASSET_NAME "$ASSET_NAME" '.assets[] | select(.name == $ASSET_NAME) | .browser_download_url' <<< "$GITHUB_API_RESPONSE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $DOWNLOAD_URL ]]; then
|
||||||
|
echo "ERROR: Could not find a suitable download URL for '$APP_NAME' version $VERSION for $SYSTEM $ARCH." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If in test mode, exit here
|
||||||
|
[[ $TEST_MODE -gt 0 ]] && return 0
|
||||||
|
|
||||||
|
# if output directory is specified, just download the file there
|
||||||
|
if [[ -n $OUTPUT_DIR ]]; then
|
||||||
|
if [[ ! -d $OUTPUT_DIR ]]; then
|
||||||
|
echo "ERROR: Output directory '$OUTPUT_DIR' does not exist." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
# Remove "-s" from CURL_OPTS to show download progress
|
||||||
|
CURL_OPTS=("${CURL_OPTS[@]:1}")
|
||||||
|
OUTPUT_NAME=$(basename "$DOWNLOAD_URL")
|
||||||
|
OUTPUT_PATH="$OUTPUT_DIR/$OUTPUT_NAME"
|
||||||
|
echo "Downloading '$APP_NAME' version $VERSION to '$OUTPUT_PATH'..."
|
||||||
|
# Execute curl to download the file and exit
|
||||||
|
exec curl "${CURL_OPTS[@]}" -o "$OUTPUT_PATH" "$DOWNLOAD_URL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "Will download '$APP_NAME' version: $VERSION\nDownload URL: \"$DOWNLOAD_URL\"."
|
||||||
|
|
||||||
|
define_sudo_cmd || return 1
|
||||||
|
|
||||||
|
TMP=$(mktemp -d -t github-release-installer-XXXXXX)
|
||||||
|
|
||||||
|
# Ensure temporary artifacts are cleaned up on exit
|
||||||
|
trap "test -d $TMP && rm -rf $TMP" EXIT
|
||||||
|
|
||||||
|
# Change to temporary directory
|
||||||
|
cd "$TMP" || return 1
|
||||||
|
# Create extraction directory
|
||||||
|
mkdir -p $TMP/extracted || return 1
|
||||||
|
|
||||||
|
echo "Downloading '$APP_NAME' version $VERSION..."
|
||||||
|
|
||||||
|
# Check, if the binary name is different from the original
|
||||||
|
if grep -q ":" <<< "$BINARY_NAME"; then
|
||||||
|
ORIGINAL_NAME="${BINARY_NAME%%:*}"
|
||||||
|
DESTINATION_NAME="${BINARY_NAME#*:}"
|
||||||
|
else
|
||||||
|
ORIGINAL_NAME="$BINARY_NAME"
|
||||||
|
DESTINATION_NAME="$BINARY_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check, the file type
|
||||||
|
if [[ $DOWNLOAD_URL == *.zip ]]; then
|
||||||
|
curl "${CURL_OPTS[@]}" -o $TMP/archive.zip $DOWNLOAD_URL || return 1
|
||||||
|
echo "Uncompressing ZIP archive of '$APP_NAME'..."
|
||||||
|
unzip -q $TMP/archive.zip -d $TMP/extracted || return 1
|
||||||
|
elif [[ $DOWNLOAD_URL =~ \.(tar\.gz|tgz)$ ]]; then
|
||||||
|
curl "${CURL_OPTS[@]}" -o $TMP/archive.tgz $DOWNLOAD_URL || return 1
|
||||||
|
echo "Uncompressing GZIPped TAR archive of '$APP_NAME'..."
|
||||||
|
tar -x -z -C $TMP/extracted -f $TMP/archive.tgz || return 1
|
||||||
|
elif [[ $DOWNLOAD_URL =~ deb$ ]]; then
|
||||||
|
echo "Downloading Debian package of '$APP_NAME'..."
|
||||||
|
curl "${CURL_OPTS[@]}" -LO $DOWNLOAD_URL || return 1
|
||||||
|
# Install the deb package to extract its contents
|
||||||
|
echo "Installing Debian package of '$APP_NAME'..."
|
||||||
|
$SUDOCMD dpkg -i $(basename $DOWNLOAD_URL)
|
||||||
|
return $?
|
||||||
|
elif [[ $DOWNLOAD_URL =~ pkg$ ]]; then
|
||||||
|
echo "Downloading macOS package of '$APP_NAME'..."
|
||||||
|
curl "${CURL_OPTS[@]}" -LO $DOWNLOAD_URL || return 1
|
||||||
|
# Install the pkg package to extract its contents
|
||||||
|
echo "Installing macOS package of '$APP_NAME'..."
|
||||||
|
$SUDOCMD installer -pkg $(basename $DOWNLOAD_URL) -target /
|
||||||
|
return $?
|
||||||
|
elif [[ $DOWNLOAD_URL =~ rpm$ ]]; then
|
||||||
|
echo "Downloading RPM package of '$APP_NAME'..."
|
||||||
|
curl "${CURL_OPTS[@]}" -LO $DOWNLOAD_URL || return 1
|
||||||
|
# Install the rpm package to extract its contents
|
||||||
|
echo "Installing RPM package of '$APP_NAME'..."
|
||||||
|
$SUDOCMD rpm -i $(basename $DOWNLOAD_URL)
|
||||||
|
return $?
|
||||||
|
else
|
||||||
|
echo "Downloading binary release of '$APP_NAME'..."
|
||||||
|
(cd $TMP/extracted && curl "${CURL_OPTS[@]}" -LO $DOWNLOAD_URL) || return 1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find the binary file by looking for a file with the exact name
|
||||||
|
BINARY_PATH=$(find $TMP/extracted -name "$ORIGINAL_NAME" -type f)
|
||||||
|
|
||||||
|
if [[ -z $BINARY_PATH ]]; then
|
||||||
|
# Try with system and architecture suffix
|
||||||
|
BINARY_PATH=$(find $TMP/extracted -name "${ORIGINAL_NAME}-${SYSTEM}-${ARCH}" -type f)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $BINARY_PATH ]]; then
|
||||||
|
echo "ERROR: Could not find binary file '$ORIGINAL_NAME' to install. Specify the correct binary name with the -b option." >&2
|
||||||
|
echo ""
|
||||||
|
echo "Available files in the extracted archive:"
|
||||||
|
find $TMP/extracted -type f | sed "s|$TMP/extracted/||"
|
||||||
|
echo ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing '$APP_NAME' binary "$ORIGINAL_NAME" to /usr/local/bin/$DESTINATION_NAME..."
|
||||||
|
$SUDOCMD install -m 755 $BINARY_PATH /usr/local/bin/$DESTINATION_NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
cat << EOF
|
||||||
|
Usage: github-release-installer [options] "repo/name"
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--dry-run Run in test mode. Optional level (default: 9).
|
||||||
|
Or set TEST_MODE environment variable to a number > 0.
|
||||||
|
-l --list "repo/name" List available assets for the given repository.
|
||||||
|
-a, --app-name "App Name" Name of the application (optional).
|
||||||
|
-b, --binary-name "binary_name[:destination_name]" Name of the binary to install and optional destination name (optional).
|
||||||
|
-j, --releases-json Only download the releases JSON file.
|
||||||
|
-o, --output-directory Only download the file to the specified directory.
|
||||||
|
-f, --file-name "file_name" Name of the file to download (optional).
|
||||||
|
-t, --type "type" Type of the asset to download: archive (zip, tar.gz, tgz) or package (deb, pkg, rpm) (optional).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
github-release-installer -a "GitHub CLI" -b gh cli/cli
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
APP_NAME=""
|
||||||
|
BINARY_NAME=""
|
||||||
|
REPO_NAME=""
|
||||||
|
ASSET_NAME=""
|
||||||
|
ASSET_TYPE=""
|
||||||
|
OUTPUT_DIR=""
|
||||||
|
RELEASES_ONLY=0
|
||||||
|
SYSTEM=""
|
||||||
|
ARCH=""
|
||||||
|
|
||||||
|
# Use value from environment variable or default to 0
|
||||||
|
TEST_MODE=${TEST_MODE:-0}
|
||||||
|
|
||||||
|
# Check, if we have been executed or sourced.
|
||||||
|
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
shopt -s nocasematch
|
||||||
|
# Parse command line arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
# Check, if the next argument is present and is a number
|
||||||
|
if [[ ${2:-} =~ ^[0-9]+$ ]]; then
|
||||||
|
TEST_MODE="$2"
|
||||||
|
shift 2
|
||||||
|
else
|
||||||
|
TEST_MODE=1
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-l|--list)
|
||||||
|
REPO_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
curl "${CURL_OPTS[@]}" "$GITHUB_API_BASE_URL/repos/$REPO_NAME/releases/latest" |
|
||||||
|
jq -r '"- \(.assets[].browser_download_url)"'
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
-a|--app-name)
|
||||||
|
APP_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-b|--binary-name)
|
||||||
|
BINARY_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-o|--output-directory)
|
||||||
|
OUTPUT_DIR="$2"
|
||||||
|
shift 2
|
||||||
|
# That trick mimicks realpath
|
||||||
|
OUTPUT_DIR="$(cd "$OUTPUT_DIR" && pwd)"
|
||||||
|
;;
|
||||||
|
-j|--releases-json)
|
||||||
|
RELEASES_ONLY=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-f|--file-name)
|
||||||
|
ASSET_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-t|--type)
|
||||||
|
if [[ $2 =~ ^(zip|gzip|gz|tar|tar\.gz|tgz|deb|pkg|rpm)$ ]]; then
|
||||||
|
ASSET_TYPE="$2$"
|
||||||
|
elif [[ $2 =~ ^(archive|package)$ ]]; then
|
||||||
|
ASSET_TYPE="$2"
|
||||||
|
else
|
||||||
|
echo "ERROR: Unknown asset type: $2" >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
# Override system and architecture detection
|
||||||
|
--system)
|
||||||
|
SYSTEM="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--arch)
|
||||||
|
ARCH="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# First positional argument is REPO_NAME
|
||||||
|
REPO_NAME="${1:-}"
|
||||||
|
shift
|
||||||
|
|
||||||
|
if [[ -z $REPO_NAME ]]; then
|
||||||
|
echo "ERROR: Repository name is required." >&2
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If APP_NAME or BINARY_NAME are missing, try to infer them
|
||||||
|
# Use last part of REPO_NAME as BINARY_NAME if not provided
|
||||||
|
if [[ -z $BINARY_NAME ]]; then
|
||||||
|
BINARY_NAME="${REPO_NAME##*/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use capitalized last part of REPO_NAME as APP_NAME if not provided
|
||||||
|
if [[ -z $APP_NAME ]]; then
|
||||||
|
APP_NAME="${REPO_NAME##*/}"
|
||||||
|
APP_NAME="${APP_NAME^[a-z]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $RELEASES_ONLY -ne 0 ]]; then
|
||||||
|
URL="$GITHUB_API_BASE_URL/repos/$REPO_NAME/releases/latest"
|
||||||
|
echo "Downloading GitHub releases for '$APP_NAME' from $URL..."
|
||||||
|
|
||||||
|
OUTPUT_PATH="${BINARY_NAME}.releases.json"
|
||||||
|
if [[ -n $OUTPUT_DIR ]]; then
|
||||||
|
OUTPUT_PATH="$OUTPUT_DIR/$OUTPUT_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! curl "${CURL_OPTS[@]}" -o "$OUTPUT_PATH" "$URL"; then
|
||||||
|
echo "ERROR: Could not download GitHub releases for '$APP_NAME' from $URL." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Downloaded GitHub releases to $OUTPUT_PATH."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
github_release_installer
|
||||||
|
exit $?
|
||||||
Reference in New Issue
Block a user