ci: Created single-line installation scripts to also install gman automatically without any package managers
This commit is contained in:
@@ -356,50 +356,6 @@ jobs:
|
|||||||
path: artifacts
|
path: artifacts
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
publish-winget-package:
|
|
||||||
needs: [publish-github-release]
|
|
||||||
name: Publish winget package
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Check if actor is repository owner
|
|
||||||
if: ${{ github.actor != github.repository_owner && env.ACT != 'true' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "You are not authorized to run this workflow."
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
|
|
||||||
- name: Get release artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: artifacts
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Set release version
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
release_version="$(cat ./artifacts/release-version)"
|
|
||||||
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Validate release environment variables
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "Release version: ${{ env.RELEASE_VERSION }}"
|
|
||||||
|
|
||||||
- name: Publish to winget (opens PR to microsoft/winget-pkgs)
|
|
||||||
if: env.ACT != 'true'
|
|
||||||
uses: vedantmgoyal9/winget-releaser@v2
|
|
||||||
with:
|
|
||||||
identifier: DarkAlex17.GMan
|
|
||||||
version: ${{ env.RELEASE_VERSION }}
|
|
||||||
release-tag: v${{ env.RELEASE_VERSION }}
|
|
||||||
installers-regex: '\.zip$'
|
|
||||||
token: ${{ secrets.WINGET_GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
publish-homebrew-formula:
|
publish-homebrew-formula:
|
||||||
needs: [publish-github-release]
|
needs: [publish-github-release]
|
||||||
name: Update Homebrew formulas
|
name: Update Homebrew formulas
|
||||||
|
|||||||
+1
-1
@@ -5,4 +5,4 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [0.0.2] - 2025-09-10
|
## [0.0.1] - 2025-09-10
|
||||||
|
|||||||
Generated
+1
-1
@@ -1576,7 +1576,7 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gman"
|
name = "gman"
|
||||||
version = "0.0.2"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gman"
|
name = "gman"
|
||||||
version = "0.0.2"
|
version = "0.0.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||||
description = "Universal command line secret management and injection tool"
|
description = "Universal command line secret management and injection tool"
|
||||||
|
|||||||
@@ -133,23 +133,30 @@ To upgrade `gman` using Homebrew:
|
|||||||
brew upgrade gman
|
brew upgrade gman
|
||||||
```
|
```
|
||||||
|
|
||||||
### Winget (Windows)
|
### Scripts
|
||||||
To install G-Man using Winget:
|
#### Linux/MacOS (`bash`)
|
||||||
|
You can use the following command to run a bash script that downloads and installs the latest version of `gman` for your
|
||||||
|
OS (Linux/MacOS) and architecture (x86_64/arm64):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/gman/main/install.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Windows/Linux/MacOS (`PowerShell`)
|
||||||
|
You can use the following command to run a PowerShell script that downloads and installs the latest version of `gman`
|
||||||
|
for your OS (Windows/Linux/MacOS) and architecture (x86_64/arm64):
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
winget install gman
|
powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/Dark-Alex-17/gman/main/scripts/install_gman.ps1 | iex"
|
||||||
|
|
||||||
# If you need to be more specific, use:
|
|
||||||
winget install --id Dark-Alex-17.gman
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Manual
|
### Manual
|
||||||
Binaries are available on the [releases](https://github.com/Dark-Alex-17/gman/releases) page for the following platforms:
|
Binaries are available on the [releases](https://github.com/Dark-Alex-17/gman/releases) page for the following platforms:
|
||||||
|
|
||||||
| Platform | Architecture(s) |
|
| Platform | Architecture(s) |
|
||||||
|----------------|----------------------------|
|
|----------------|-----------------|
|
||||||
| macOS | x86_64, arm64 |
|
| macOS | x86_64, arm64 |
|
||||||
| Linux GNU/MUSL | x86_64,armv6,armv7,aarch64 |
|
| Linux GNU/MUSL | x86_64, aarch64 |
|
||||||
| Windows | x86_64, aarch64 |
|
| Windows | x86_64, aarch64 |
|
||||||
|
|
||||||
#### Windows Instructions
|
#### Windows Instructions
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
$ErrorActionPreference = 'Stop';
|
|
||||||
|
|
||||||
$PackageName = 'gman'
|
|
||||||
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
|
|
||||||
$url64 = 'https://github.com/Dark-Alex-17/gman/releases/download/v$version/gman-x86_64-pc-windows-msvc.zip'
|
|
||||||
$checksum64 = '$hash_64'
|
|
||||||
|
|
||||||
$packageArgs = @{
|
|
||||||
packageName = $packageName
|
|
||||||
softwareName = $packageName
|
|
||||||
unzipLocation = $toolsDir
|
|
||||||
fileType = 'exe'
|
|
||||||
url = $url64
|
|
||||||
checksum = $checksum64
|
|
||||||
checksumType = 'sha256'
|
|
||||||
|
|
||||||
}
|
|
||||||
Install-ChocolateyZipPackage @packageArgs
|
|
||||||
$File = Get-ChildItem -File -Path $env:ChocolateyInstall\lib\$packageName\tools\ -Filter *.zip
|
|
||||||
Get-ChocolateyUnzip -fileFullPath $File.FullName -destination $env:ChocolateyInstall\lib\$packageName\tools\
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Read this before creating packages: https://chocolatey.org/docs/create-packages -->
|
|
||||||
<!-- It is especially important to read the above link to understand additional requirements when publishing packages to the community feed aka dot org (https://chocolatey.org/packages). -->
|
|
||||||
|
|
||||||
<!-- Test your packages in a test environment: https://github.com/chocolatey/chocolatey-test-environment -->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Reference. Chocolatey uses a special version of NuGet.Core that allows us to do more than was initially possible. As such there are certain things to be aware of:
|
|
||||||
|
|
||||||
* the package xmlns schema url may cause issues with nuget.exe
|
|
||||||
* Any of the following elements can ONLY be used by choco tools - projectSourceUrl, docsUrl, mailingListUrl, bugTrackerUrl, packageSourceUrl, provides, conflicts, replaces
|
|
||||||
* nuget.exe can still install packages with those elements but they are ignored. Any authoring tools or commands will error on those elements
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- You can embed software files directly into packages, as long as you are not bound by distribution rights. -->
|
|
||||||
<!-- * If you are an organization making private packages, you probably have no issues here -->
|
|
||||||
<!-- * If you are releasing to the community feed, you need to consider distribution rights. -->
|
|
||||||
<!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. -->
|
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
|
||||||
<metadata>
|
|
||||||
<!-- == PACKAGE SPECIFIC SECTION == -->
|
|
||||||
<id>gman</id>
|
|
||||||
<version>$version</version>
|
|
||||||
|
|
||||||
<!-- == SOFTWARE SPECIFIC SECTION == -->
|
|
||||||
<!-- This section is about the software itself -->
|
|
||||||
<title>G-Man</title>
|
|
||||||
<authors>Alex Clarke</authors>
|
|
||||||
<projectUrl>https://github.com/Dark-Alex-17/gman</projectUrl>
|
|
||||||
<licenseUrl>https://github.com/Dark-Alex-17/gman/blob/main/LICENSE</licenseUrl>
|
|
||||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
|
||||||
<projectSourceUrl>https://github.com/Dark-Alex-17/gman</projectSourceUrl>
|
|
||||||
<docsUrl>https://github.com/Dark-Alex-17/gman/blob/main/README.md</docsUrl>
|
|
||||||
<bugTrackerUrl>https://github.com/Dark-Alex-17/gman/issues</bugTrackerUrl>
|
|
||||||
<tags>cli cross-platform terminal credential-management secret-management rust</tags>
|
|
||||||
<summary>Universal CLI credential management and injection tool</summary>
|
|
||||||
<description>
|
|
||||||
Universal CLI credential management and injection tool.
|
|
||||||
|
|
||||||
**Usage**
|
|
||||||
To get started, run `gman --help` in a terminal.
|
|
||||||
|
|
||||||
For more [documentation and usage](https://github.com/Dark-Alex-17/gman/blob/main/README.md), see the [official repo](https://github.com/Dark-Alex-17/gman).
|
|
||||||
|
|
||||||
</description>
|
|
||||||
<releaseNotes>https://github.com/Dark-Alex-17/gman/releases/tag/v$version/</releaseNotes>
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<!-- this section controls what actually gets packaged into the Chocolatey package -->
|
|
||||||
<file src="tools\**" target="tools" />
|
|
||||||
<!--Building from Linux? You may need this instead: <file src="tools/**" target="tools" />-->
|
|
||||||
</files>
|
|
||||||
</package>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import hashlib
|
|
||||||
import sys
|
|
||||||
from string import Template
|
|
||||||
|
|
||||||
sys.stdout.reconfigure(encoding='utf-8')
|
|
||||||
args = sys.argv
|
|
||||||
version = args[1].replace("v", "")
|
|
||||||
template_file_path = args[2]
|
|
||||||
generated_file_path = args[3]
|
|
||||||
|
|
||||||
# Deployment files
|
|
||||||
hash_64 = args[4].strip()
|
|
||||||
|
|
||||||
print("Generating formula")
|
|
||||||
print(" VERSION: %s" % version)
|
|
||||||
print(" TEMPLATE PATH: %s" % template_file_path)
|
|
||||||
print(" SAVING AT: %s" % generated_file_path)
|
|
||||||
print(" HASH: %s" % hash_64)
|
|
||||||
|
|
||||||
with open(template_file_path, "r", encoding="utf-8") as template_file:
|
|
||||||
template = Template(template_file.read())
|
|
||||||
substitute = template.safe_substitute(version=version, hash_64=hash_64)
|
|
||||||
print("\n================== Generated package file ==================\n")
|
|
||||||
print(substitute)
|
|
||||||
print("\n============================================================\n")
|
|
||||||
|
|
||||||
with open(generated_file_path, "w", encoding="utf-8") as generated_file:
|
|
||||||
generated_file.write(substitute)
|
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
<#
|
||||||
|
gman installer (Windows/PowerShell 5+ and PowerShell 7)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/Dark-Alex-17/gman/main/scripts/install_gman.ps1 | iex"
|
||||||
|
pwsh -c "irm https://raw.githubusercontent.com/Dark-Alex-17/gman/main/scripts/install_gman.ps1 | iex -Version vX.Y.Z"
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-Version <tag> (default: latest)
|
||||||
|
-BinDir <path> (default: %LOCALAPPDATA%\gman\bin on Windows; ~/.local/bin on *nix PowerShell)
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$Version = $env:GMAN_VERSION,
|
||||||
|
[string]$BinDir = $env:BIN_DIR
|
||||||
|
)
|
||||||
|
|
||||||
|
$Repo = 'Dark-Alex-17/gman'
|
||||||
|
|
||||||
|
function Write-Info($msg) { Write-Host "[gman-install] $msg" }
|
||||||
|
function Fail($msg) { Write-Error $msg; exit 1 }
|
||||||
|
|
||||||
|
Add-Type -AssemblyName System.Runtime
|
||||||
|
$isWin = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)
|
||||||
|
$isMac = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::OSX)
|
||||||
|
$isLin = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Linux)
|
||||||
|
|
||||||
|
if ($isWin) { $os = 'windows' }
|
||||||
|
elseif ($isMac) { $os = 'darwin' }
|
||||||
|
elseif ($isLin) { $os = 'linux' }
|
||||||
|
else { Fail "Unsupported OS" }
|
||||||
|
|
||||||
|
switch ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) {
|
||||||
|
'X64' { $arch = 'x86_64' }
|
||||||
|
'Arm64'{ $arch = 'aarch64' }
|
||||||
|
default { Fail "Unsupported arch: $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $BinDir) {
|
||||||
|
if ($isWin) { $BinDir = Join-Path $env:LOCALAPPDATA 'gman\bin' }
|
||||||
|
else { $home = $env:HOME; if (-not $home) { $home = (Get-Item -Path ~).FullName }; $BinDir = Join-Path $home '.local/bin' }
|
||||||
|
}
|
||||||
|
New-Item -ItemType Directory -Force -Path $BinDir | Out-Null
|
||||||
|
|
||||||
|
Write-Info "Target: $os-$arch"
|
||||||
|
|
||||||
|
$apiBase = "https://api.github.com/repos/$Repo/releases"
|
||||||
|
$relUrl = if ($Version) { "$apiBase/tags/$Version" } else { "$apiBase/latest" }
|
||||||
|
Write-Info "Fetching release: $relUrl"
|
||||||
|
try {
|
||||||
|
$release = Invoke-RestMethod -UseBasicParsing -Headers @{ 'User-Agent' = 'gman-installer' } -Uri $relUrl -Method GET
|
||||||
|
} catch { Fail "Failed to fetch release metadata. $_" }
|
||||||
|
if (-not $release.assets) { Fail "No assets found in the release." }
|
||||||
|
|
||||||
|
$candidates = @()
|
||||||
|
if ($os -eq 'windows') {
|
||||||
|
if ($arch -eq 'x86_64') { $candidates += 'gman-x86_64-pc-windows-msvc.zip' }
|
||||||
|
else { $candidates += 'gman-aarch64-pc-windows-msvc.zip' }
|
||||||
|
} elseif ($os -eq 'darwin') {
|
||||||
|
if ($arch -eq 'x86_64') { $candidates += 'gman-x86_64-apple-darwin.tar.gz' }
|
||||||
|
else { $candidates += 'gman-aarch64-apple-darwin.tar.gz' }
|
||||||
|
} elseif ($os -eq 'linux') {
|
||||||
|
if ($arch -eq 'x86_64') {
|
||||||
|
$candidates += 'gman-x86_64-unknown-linux-gnu.tar.gz'
|
||||||
|
$candidates += 'gman-x86_64-unknown-linux-musl.tar.gz'
|
||||||
|
} else {
|
||||||
|
$candidates += 'gman-aarch64-unknown-linux-musl.tar.gz'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Fail "Unsupported OS for this installer: $os"
|
||||||
|
}
|
||||||
|
|
||||||
|
$asset = $null
|
||||||
|
foreach ($c in $candidates) {
|
||||||
|
$asset = $release.assets | Where-Object { $_.name -eq $c } | Select-Object -First 1
|
||||||
|
if ($asset) { break }
|
||||||
|
}
|
||||||
|
if (-not $asset) {
|
||||||
|
Write-Error "No matching asset found for $os-$arch. Tried:"; $candidates | ForEach-Object { Write-Error " - $_" }
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info "Selected asset: $($asset.name)"
|
||||||
|
Write-Info "Download URL: $($asset.browser_download_url)"
|
||||||
|
|
||||||
|
$tmp = New-Item -ItemType Directory -Force -Path ([IO.Path]::Combine([IO.Path]::GetTempPath(), "gman-$(Get-Random)"))
|
||||||
|
$archive = Join-Path $tmp.FullName 'asset'
|
||||||
|
try { Invoke-WebRequest -UseBasicParsing -Headers @{ 'User-Agent' = 'gman-installer' } -Uri $asset.browser_download_url -OutFile $archive } catch { Fail "Failed to download asset. $_" }
|
||||||
|
|
||||||
|
$extractDir = Join-Path $tmp.FullName 'extract'; New-Item -ItemType Directory -Force -Path $extractDir | Out-Null
|
||||||
|
|
||||||
|
if ($asset.name -match '\.zip$') {
|
||||||
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||||
|
[System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir)
|
||||||
|
} elseif ($asset.name -match '\.tar\.gz$' -or $asset.name -match '\.tgz$') {
|
||||||
|
$tar = Get-Command tar -ErrorAction SilentlyContinue
|
||||||
|
if ($tar) { & $tar.FullName -xzf $archive -C $extractDir }
|
||||||
|
else { Fail "Asset is tar archive but 'tar' is not available." }
|
||||||
|
} else {
|
||||||
|
try { Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir) }
|
||||||
|
catch {
|
||||||
|
$tar = Get-Command tar -ErrorAction SilentlyContinue
|
||||||
|
if ($tar) { & $tar.FullName -xf $archive -C $extractDir } else { Fail "Unknown archive format; neither zip nor tar workable." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$bin = $null
|
||||||
|
Get-ChildItem -Recurse -File $extractDir | ForEach-Object {
|
||||||
|
if ($isWin) { if ($_.Name -ieq 'gman.exe') { $bin = $_.FullName } }
|
||||||
|
else { if ($_.Name -ieq 'gman') { $bin = $_.FullName } }
|
||||||
|
}
|
||||||
|
if (-not $bin) { Fail "Could not find gman binary inside the archive." }
|
||||||
|
|
||||||
|
if (-not $isWin) { try { & chmod +x -- $bin } catch {} }
|
||||||
|
|
||||||
|
$exec = if ($isWin) { 'gman.exe'} else { 'gman' }
|
||||||
|
$dest = Join-Path $BinDir $exec
|
||||||
|
Copy-Item -Force $bin $dest
|
||||||
|
Write-Info "Installed: $dest"
|
||||||
|
|
||||||
|
if ($isWin) {
|
||||||
|
$pathParts = ($env:Path -split ';') | Where-Object { $_ -ne '' }
|
||||||
|
if ($pathParts -notcontains $BinDir) {
|
||||||
|
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User'); if (-not $userPath) { $userPath = '' }
|
||||||
|
if (-not ($userPath -split ';' | Where-Object { $_ -eq $BinDir })) {
|
||||||
|
$newUserPath = if ($userPath.Trim().Length -gt 0) { "$userPath;$BinDir" } else { $BinDir }
|
||||||
|
[Environment]::SetEnvironmentVariable('Path', $newUserPath, 'User')
|
||||||
|
Write-Info "Added to User PATH: $BinDir (restart shell to take effect)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (-not ($env:PATH -split ':' | Where-Object { $_ -eq $BinDir })) {
|
||||||
|
Write-Info "Note: $BinDir is not in PATH. Add it to your shell profile."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info "Done. Try: gman --help"
|
||||||
|
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# gman installer (Linux/macOS)
|
||||||
|
#
|
||||||
|
# Usage examples:
|
||||||
|
# curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/gman/main/scripts/install_gman.sh | bash
|
||||||
|
# curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/gman/main/scripts/install_gman.sh | bash -s -- --version vX.Y.Z
|
||||||
|
# BIN_DIR="$HOME/.local/bin" bash scripts/install_gman.sh
|
||||||
|
#
|
||||||
|
# Flags / Env:
|
||||||
|
# --version <tag> Release tag (default: latest). Or set GMAN_VERSION.
|
||||||
|
# --bin-dir <dir> Install directory (default: /usr/local/bin or ~/.local/bin). Or set BIN_DIR.
|
||||||
|
|
||||||
|
REPO="Dark-Alex-17/gman"
|
||||||
|
VERSION="${GMAN_VERSION:-}"
|
||||||
|
BIN_DIR="${BIN_DIR:-}"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "gman installer (Linux/macOS)"
|
||||||
|
echo
|
||||||
|
echo "Options:"
|
||||||
|
echo " --version <tag> Release tag (default: latest)"
|
||||||
|
echo " --bin-dir <dir> Install directory (default: /usr/local/bin or ~/.local/bin)"
|
||||||
|
echo " -h, --help Show help"
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--version) VERSION="$2"; shift 2;;
|
||||||
|
--bin-dir) BIN_DIR="$2"; shift 2;;
|
||||||
|
-h|--help) usage; exit 0;;
|
||||||
|
*) echo "Unknown argument: $1" >&2; usage; exit 2;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "${BIN_DIR}" ]]; then
|
||||||
|
if [[ -w "/usr/local/bin" ]]; then
|
||||||
|
BIN_DIR="/usr/local/bin"
|
||||||
|
else
|
||||||
|
BIN_DIR="${HOME}/.local/bin"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
mkdir -p "${BIN_DIR}"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[gman-install] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
need_cmd() {
|
||||||
|
if ! command -v "$1" >/dev/null 2>&1; then
|
||||||
|
echo "Error: required command '$1' not found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
need_cmd uname
|
||||||
|
need_cmd mktemp
|
||||||
|
need_cmd tar
|
||||||
|
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
DL=curl
|
||||||
|
elif command -v wget >/dev/null 2>&1; then
|
||||||
|
DL=wget
|
||||||
|
else
|
||||||
|
echo "Error: need curl or wget" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
UNAME_OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
case "$UNAME_OS" in
|
||||||
|
linux) OS=linux ;;
|
||||||
|
darwin) OS=darwin ;;
|
||||||
|
*) echo "Error: unsupported OS '$UNAME_OS'" >&2; exit 1;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
UNAME_ARCH=$(uname -m)
|
||||||
|
case "$UNAME_ARCH" in
|
||||||
|
x86_64|amd64) ARCH=x86_64 ;;
|
||||||
|
aarch64|arm64) ARCH=aarch64 ;;
|
||||||
|
*) echo "Error: unsupported arch '$UNAME_ARCH'" >&2; exit 1;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
log "Target: ${OS}-${ARCH}"
|
||||||
|
|
||||||
|
API_BASE="https://api.github.com/repos/${REPO}/releases"
|
||||||
|
if [[ -z "${VERSION}" ]]; then
|
||||||
|
RELEASE_URL="${API_BASE}/latest"
|
||||||
|
else
|
||||||
|
RELEASE_URL="${API_BASE}/tags/${VERSION}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
http_get() {
|
||||||
|
if [[ "$DL" == "curl" ]]; then
|
||||||
|
curl -fsSL -H 'User-Agent: gman-installer' "$1"
|
||||||
|
else
|
||||||
|
wget -qO- --header='User-Agent: gman-installer' "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
TMPDIR="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$TMPDIR"' EXIT
|
||||||
|
|
||||||
|
log "Fetching release metadata from $RELEASE_URL"
|
||||||
|
JSON="$TMPDIR/release.json"
|
||||||
|
if ! http_get "$RELEASE_URL" > "$JSON"; then
|
||||||
|
echo "Error: failed to fetch release metadata. Check version tag." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ASSET_CANDIDATES=()
|
||||||
|
if [[ "$OS" == "darwin" ]]; then
|
||||||
|
if [[ "$ARCH" == "x86_64" ]]; then
|
||||||
|
ASSET_CANDIDATES+=("gman-x86_64-apple-darwin.tar.gz")
|
||||||
|
else
|
||||||
|
ASSET_CANDIDATES+=("gman-aarch64-apple-darwin.tar.gz")
|
||||||
|
fi
|
||||||
|
elif [[ "$OS" == "linux" ]]; then
|
||||||
|
if [[ "$ARCH" == "x86_64" ]]; then
|
||||||
|
LIBC="musl"
|
||||||
|
if command -v getconf >/dev/null 2>&1 && getconf GNU_LIBC_VERSION >/dev/null 2>&1; then LIBC="gnu"; fi
|
||||||
|
if ldd --version 2>&1 | grep -qi glibc; then LIBC="gnu"; fi
|
||||||
|
|
||||||
|
if [[ "$LIBC" == "gnu" ]]; then
|
||||||
|
ASSET_CANDIDATES+=("gman-x86_64-unknown-linux-gnu.tar.gz")
|
||||||
|
fi
|
||||||
|
|
||||||
|
ASSET_CANDIDATES+=("gman-x86_64-unknown-linux-musl.tar.gz")
|
||||||
|
else
|
||||||
|
ASSET_CANDIDATES+=("gman-aarch64-unknown-linux-musl.tar.gz")
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Error: unsupported OS for this installer: $OS" >&2; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ASSET_NAME=""; ASSET_URL=""
|
||||||
|
for candidate in "${ASSET_CANDIDATES[@]}"; do
|
||||||
|
NAME=$(grep -oE '"name":\s*"[^"]+"' "$JSON" | sed 's/"name":\s*"//; s/"$//' | grep -Fx "$candidate" || true)
|
||||||
|
if [[ -n "$NAME" ]]; then
|
||||||
|
ASSET_NAME="$NAME"
|
||||||
|
ASSET_URL=$(awk -v pat="$NAME" '
|
||||||
|
BEGIN{ FS=":"; want=0 }
|
||||||
|
/"name"/ {
|
||||||
|
line=$0;
|
||||||
|
gsub(/^\s+|\s+$/,"",line);
|
||||||
|
gsub(/"name"\s*:\s*"|"/ ,"", line);
|
||||||
|
want = (line==pat) ? 1 : 0;
|
||||||
|
next
|
||||||
|
}
|
||||||
|
want==1 && /"browser_download_url"/ {
|
||||||
|
u=$0;
|
||||||
|
gsub(/^\s+|\s+$/,"",u);
|
||||||
|
gsub(/.*"browser_download_url"\s*:\s*"|".*/ ,"", u);
|
||||||
|
print u;
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
' "$JSON")
|
||||||
|
if [[ -n "$ASSET_URL" ]]; then break; fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$ASSET_URL" ]]; then
|
||||||
|
echo "Error: no matching asset found for ${OS}-${ARCH}. Tried:" >&2
|
||||||
|
for c in "${ASSET_CANDIDATES[@]}"; do echo " - $c" >&2; done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Selected asset: $ASSET_NAME"
|
||||||
|
log "Download URL: $ASSET_URL"
|
||||||
|
|
||||||
|
ARCHIVE="$TMPDIR/asset"
|
||||||
|
if [[ "$DL" == "curl" ]]; then
|
||||||
|
curl -fL -H 'User-Agent: gman-installer' "$ASSET_URL" -o "$ARCHIVE"
|
||||||
|
else
|
||||||
|
wget -q --header='User-Agent: gman-installer' "$ASSET_URL" -O "$ARCHIVE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORK="$TMPDIR/work"; mkdir -p "$WORK"
|
||||||
|
EXTRACTED_DIR="$WORK/extracted"; mkdir -p "$EXTRACTED_DIR"
|
||||||
|
|
||||||
|
if tar -tf "$ARCHIVE" >/dev/null 2>&1; then
|
||||||
|
tar -xzf "$ARCHIVE" -C "$EXTRACTED_DIR"
|
||||||
|
else
|
||||||
|
if command -v unzip >/dev/null 2>&1; then
|
||||||
|
unzip -q "$ARCHIVE" -d "$EXTRACTED_DIR"
|
||||||
|
else
|
||||||
|
echo "Error: unknown archive format; install 'unzip'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
BIN_PATH=""
|
||||||
|
while IFS= read -r -d '' f; do
|
||||||
|
base=$(basename "$f")
|
||||||
|
if [[ "$base" == "gman" ]]; then
|
||||||
|
BIN_PATH="$f"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done < <(find "$EXTRACTED_DIR" -type f -print0)
|
||||||
|
|
||||||
|
if [[ -z "$BIN_PATH" ]]; then
|
||||||
|
echo "Error: could not find 'gman' binary in the archive" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x "$BIN_PATH"
|
||||||
|
install -m 0755 "$BIN_PATH" "${BIN_DIR}/gman"
|
||||||
|
|
||||||
|
log "Installed: ${BIN_DIR}/gman"
|
||||||
|
|
||||||
|
case ":$PATH:" in
|
||||||
|
*":${BIN_DIR}:"*) ;;
|
||||||
|
*)
|
||||||
|
log "Note: ${BIN_DIR} is not in PATH. Add it, e.g.:"
|
||||||
|
log " export PATH=\"${BIN_DIR}:\$PATH\""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
log "Done. Try: gman --help"
|
||||||
|
|
||||||
Reference in New Issue
Block a user