-
-
Notifications
You must be signed in to change notification settings - Fork 71
feat(install): add install.sh and install.ps1 one-line installers #549
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
65c04c0
de026bd
f6f6615
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| # GoModel installer for Windows. | ||
| # | ||
| # irm https://gomodel.enterpilot.io/install.ps1 | iex | ||
| # | ||
| # Downloads the latest release from GitHub, verifies its SHA-256 checksum, | ||
| # and installs gomodel.exe to %LOCALAPPDATA%\Programs\gomodel (added to the | ||
| # user PATH when missing). No telemetry is sent by this script. | ||
| # | ||
| # Overrides (set before running): | ||
| # $env:GOMODEL_VERSION install a specific version (e.g. v0.1.50); default: latest | ||
| # $env:GOMODEL_INSTALL_DIR install directory; default: %LOCALAPPDATA%\Programs\gomodel | ||
|
|
||
| $ErrorActionPreference = 'Stop' | ||
|
|
||
| $Repo = 'ENTERPILOT/GoModel' | ||
| $Binary = 'gomodel' | ||
|
|
||
| # PROCESSOR_ARCHITEW6432 reports the real machine architecture when running | ||
| # in a 32-bit PowerShell on a 64-bit Windows. | ||
| $rawArch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } | ||
| $arch = switch ($rawArch) { | ||
| 'AMD64' { 'amd64' } | ||
| 'ARM64' { 'arm64' } | ||
| default { throw "unsupported architecture: $rawArch" } | ||
| } | ||
|
|
||
| $tag = $env:GOMODEL_VERSION | ||
| if (-not $tag) { | ||
| # Resolve the latest tag from the releases/latest redirect, avoiding the | ||
| # GitHub API and its per-IP rate limit (same approach as install.sh). | ||
| $req = [System.Net.WebRequest]::Create("https://github.com/$Repo/releases/latest") | ||
| $req.AllowAutoRedirect = $false | ||
| $res = $req.GetResponse() | ||
| try { | ||
| $location = $res.Headers['Location'] | ||
| } | ||
| finally { | ||
| $res.Close() | ||
| } | ||
| if (-not $location) { throw 'could not resolve the latest release' } | ||
| $tag = ($location -split '/')[-1] | ||
| } | ||
| if ($tag -notmatch '^v') { throw "unexpected release tag: $tag" } | ||
| $version = $tag.TrimStart('v') | ||
|
|
||
| $archive = "${Binary}_${version}_windows_${arch}.zip" | ||
| $baseUrl = "https://github.com/$Repo/releases/download/$tag" | ||
|
|
||
| $tmpDir = Join-Path ([IO.Path]::GetTempPath()) "gomodel-install-$([IO.Path]::GetRandomFileName())" | ||
| New-Item -ItemType Directory -Path $tmpDir | Out-Null | ||
| try { | ||
| Write-Host "Downloading $Binary $tag (windows/$arch)..." | ||
| Invoke-WebRequest -Uri "$baseUrl/$archive" -OutFile (Join-Path $tmpDir $archive) | ||
| Invoke-WebRequest -Uri "$baseUrl/checksums.txt" -OutFile (Join-Path $tmpDir 'checksums.txt') | ||
|
|
||
| $expected = $null | ||
| foreach ($line in Get-Content (Join-Path $tmpDir 'checksums.txt')) { | ||
| $parts = $line.Trim() -split '\s+' | ||
| if ($parts.Length -ge 2 -and $parts[1] -eq $archive) { $expected = $parts[0]; break } | ||
| } | ||
| if (-not $expected) { throw "no checksum for $archive in checksums.txt" } | ||
| $actual = (Get-FileHash (Join-Path $tmpDir $archive) -Algorithm SHA256).Hash.ToLower() | ||
| if ($actual -ne $expected.ToLower()) { throw "checksum mismatch for $archive" } | ||
| Write-Host 'Checksum verified.' | ||
|
|
||
| Expand-Archive -Path (Join-Path $tmpDir $archive) -DestinationPath $tmpDir -Force | ||
|
|
||
| $installDir = $env:GOMODEL_INSTALL_DIR | ||
| if (-not $installDir) { $installDir = Join-Path $env:LOCALAPPDATA 'Programs\gomodel' } | ||
| New-Item -ItemType Directory -Path $installDir -Force | Out-Null | ||
| Copy-Item (Join-Path $tmpDir "$Binary.exe") (Join-Path $installDir "$Binary.exe") -Force | ||
|
|
||
| Write-Host '' | ||
| Write-Host "Installed $Binary $tag to $installDir\$Binary.exe" | ||
|
|
||
| $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') | ||
| if (($userPath -split ';') -notcontains $installDir) { | ||
| $newPath = if ([string]::IsNullOrEmpty($userPath)) { $installDir } else { "$userPath;$installDir" } | ||
| [Environment]::SetEnvironmentVariable('Path', $newPath, 'User') | ||
| Write-Host "Added $installDir to your user PATH - restart your terminal to pick it up." | ||
| } | ||
|
|
||
| Write-Host '' | ||
| Write-Host 'Get started:' | ||
| Write-Host ' $env:OPENAI_API_KEY = "sk-..." # or any other provider key' | ||
| Write-Host " $Binary" | ||
| Write-Host '' | ||
| Write-Host 'Docs: https://gomodel.enterpilot.io' | ||
| } | ||
| finally { | ||
| Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,102 @@ | ||||||||||||||||||||
| #!/bin/sh | ||||||||||||||||||||
| # GoModel installer for macOS and Linux. | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # curl -fsSL https://gomodel.enterpilot.io/install.sh | sh | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # Downloads the latest release binary from GitHub, verifies its SHA-256 | ||||||||||||||||||||
| # checksum, and installs it to /usr/local/bin (or ~/.local/bin when | ||||||||||||||||||||
| # /usr/local/bin is not writable). No telemetry is sent by this script. | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # Overrides: | ||||||||||||||||||||
| # GOMODEL_VERSION install a specific version (e.g. v0.1.50); default: latest | ||||||||||||||||||||
| # GOMODEL_INSTALL_DIR install directory; default: /usr/local/bin or ~/.local/bin | ||||||||||||||||||||
|
|
||||||||||||||||||||
| set -eu | ||||||||||||||||||||
|
|
||||||||||||||||||||
| REPO="ENTERPILOT/GoModel" | ||||||||||||||||||||
| BINARY="gomodel" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| say() { printf '%s\n' "$*"; } | ||||||||||||||||||||
| fail() { printf 'error: %s\n' "$*" >&2; exit 1; } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| command -v curl >/dev/null 2>&1 || fail "curl is required" | ||||||||||||||||||||
| command -v tar >/dev/null 2>&1 || fail "tar is required" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| case "$(uname -s)" in | ||||||||||||||||||||
| Darwin) os="darwin" ;; | ||||||||||||||||||||
| Linux) os="linux" ;; | ||||||||||||||||||||
| *) fail "unsupported OS: $(uname -s) (Windows: use install.ps1)" ;; | ||||||||||||||||||||
| esac | ||||||||||||||||||||
|
|
||||||||||||||||||||
| case "$(uname -m)" in | ||||||||||||||||||||
| x86_64 | amd64) arch="amd64" ;; | ||||||||||||||||||||
| arm64 | aarch64) arch="arm64" ;; | ||||||||||||||||||||
| *) fail "unsupported architecture: $(uname -m)" ;; | ||||||||||||||||||||
| esac | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Resolve the version from the /releases/latest redirect, avoiding the | ||||||||||||||||||||
| # GitHub API and its per-IP rate limit. | ||||||||||||||||||||
| tag="${GOMODEL_VERSION:-}" | ||||||||||||||||||||
| if [ -z "$tag" ]; then | ||||||||||||||||||||
| location=$(curl -fsSLI -o /dev/null -w '%{url_effective}' "https://github.com/$REPO/releases/latest") || | ||||||||||||||||||||
| fail "could not resolve the latest release" | ||||||||||||||||||||
| tag="${location##*/}" | ||||||||||||||||||||
| fi | ||||||||||||||||||||
| case "$tag" in | ||||||||||||||||||||
| v*) ;; | ||||||||||||||||||||
| *) fail "unexpected release tag: $tag" ;; | ||||||||||||||||||||
| esac | ||||||||||||||||||||
| version="${tag#v}" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| archive="${BINARY}_${version}_${os}_${arch}.tar.gz" | ||||||||||||||||||||
| base_url="https://github.com/$REPO/releases/download/$tag" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| tmpdir=$(mktemp -d) | ||||||||||||||||||||
| trap 'rm -rf "$tmpdir"' EXIT INT TERM | ||||||||||||||||||||
|
|
||||||||||||||||||||
| say "Downloading $BINARY $tag ($os/$arch)..." | ||||||||||||||||||||
| curl -fsSL -o "$tmpdir/$archive" "$base_url/$archive" || fail "download failed: $base_url/$archive" | ||||||||||||||||||||
| curl -fsSL -o "$tmpdir/checksums.txt" "$base_url/checksums.txt" || fail "download failed: checksums.txt" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| expected=$(awk -v f="$archive" '$2 == f { print $1 }' "$tmpdir/checksums.txt") | ||||||||||||||||||||
| [ -n "$expected" ] || fail "no checksum for $archive in checksums.txt" | ||||||||||||||||||||
| if command -v sha256sum >/dev/null 2>&1; then | ||||||||||||||||||||
| actual=$(sha256sum "$tmpdir/$archive" | awk '{ print $1 }') | ||||||||||||||||||||
| elif command -v shasum >/dev/null 2>&1; then | ||||||||||||||||||||
| actual=$(shasum -a 256 "$tmpdir/$archive" | awk '{ print $1 }') | ||||||||||||||||||||
| else | ||||||||||||||||||||
| fail "sha256sum or shasum is required to verify the download" | ||||||||||||||||||||
| fi | ||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||
| [ "$actual" = "$expected" ] || fail "checksum mismatch for $archive" | ||||||||||||||||||||
| say "Checksum verified." | ||||||||||||||||||||
|
|
||||||||||||||||||||
| tar -xzf "$tmpdir/$archive" -C "$tmpdir" "$BINARY" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| install_dir="${GOMODEL_INSTALL_DIR:-}" | ||||||||||||||||||||
| if [ -z "$install_dir" ]; then | ||||||||||||||||||||
| if [ -w /usr/local/bin ]; then | ||||||||||||||||||||
| install_dir="/usr/local/bin" | ||||||||||||||||||||
| else | ||||||||||||||||||||
| install_dir="$HOME/.local/bin" | ||||||||||||||||||||
| fi | ||||||||||||||||||||
| fi | ||||||||||||||||||||
| mkdir -p "$install_dir" || fail "cannot create $install_dir" | ||||||||||||||||||||
| [ -w "$install_dir" ] || fail "$install_dir is not writable — set GOMODEL_INSTALL_DIR to a writable directory, or rerun with sudo" | ||||||||||||||||||||
| install -m 755 "$tmpdir/$BINARY" "$install_dir/$BINARY" | ||||||||||||||||||||
|
Comment on lines
+83
to
+85
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win Prevent temporary directory cleanup on permission failure and fix the suggested path. If the target directory is not writable, the script fails and advises the user to run
Disable the cleanup trap right before failing to leave the file intact for the manual step, and use the 🐛 Proposed fix mkdir -p "$install_dir" || fail "cannot create $install_dir"
-[ -w "$install_dir" ] || fail "$install_dir is not writable — rerun with GOMODEL_INSTALL_DIR set, or run: sudo install -m 755 $tmpdir/$BINARY /usr/local/bin/"
+if [ ! -w "$install_dir" ]; then
+ trap - EXIT INT TERM
+ fail "$install_dir is not writable — rerun with GOMODEL_INSTALL_DIR set, or run: sudo install -m 755 $tmpdir/$BINARY $install_dir/"
+fi
install -m 755 "$tmpdir/$BINARY" "$install_dir/$BINARY"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
| say "" | ||||||||||||||||||||
| say "Installed $BINARY $tag to $install_dir/$BINARY" | ||||||||||||||||||||
| case ":$PATH:" in | ||||||||||||||||||||
| *":$install_dir:"*) ;; | ||||||||||||||||||||
| *) | ||||||||||||||||||||
| say "" | ||||||||||||||||||||
| say "Note: $install_dir is not on your PATH. Add it with:" | ||||||||||||||||||||
| say " export PATH=\"$install_dir:\$PATH\"" | ||||||||||||||||||||
| ;; | ||||||||||||||||||||
| esac | ||||||||||||||||||||
| say "" | ||||||||||||||||||||
| say "Get started:" | ||||||||||||||||||||
| say " export OPENAI_API_KEY=sk-... # or any other provider key" | ||||||||||||||||||||
| say " $BINARY" | ||||||||||||||||||||
| say "" | ||||||||||||||||||||
| say "Docs: https://gomodel.enterpilot.io" | ||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.