feat(install): add install.sh and install.ps1 one-line installers#549
feat(install): add install.sh and install.ps1 one-line installers#549SantiagoDePolonia wants to merge 2 commits into
Conversation
Both scripts resolve the latest GitHub release, download the platform archive, verify its SHA-256 against checksums.txt, and install the binary (/usr/local/bin or ~/.local/bin on Unix; %LOCALAPPDATA%\Programs on Windows, with user-PATH registration). Version and install dir are overridable via GOMODEL_VERSION / GOMODEL_INSTALL_DIR. The scripts send no telemetry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds macOS/Linux and Windows installer scripts that resolve GoModel releases, select supported architectures, verify archive checksums, install binaries to configurable locations, update PATH guidance, and clean up temporary files. ChangesCross-platform installation
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Installer
participant GitHubReleases
participant InstallDirectory
Installer->>GitHubReleases: Resolve release and download archive
Installer->>Installer: Verify archive checksum
Installer->>InstallDirectory: Extract and install GoModel binary
Installer-->>Installer: Print PATH and setup guidance
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@install/install.ps1`:
- Around line 41-43: Update the checksum selection in the PowerShell install
flow around $expected to parse each checksums.txt row into fields and require
the second field to equal $archive exactly, matching the behavior in install.sh;
retain the first-field checksum as the selected value and avoid substring regex
matching.
- Around line 59-62: Update the PATH assignment in the user PATH handling block
to avoid prepending a semicolon when $userPath is unset or empty. Build the new
value conditionally while preserving the existing behavior of appending
$installDir when a user PATH already exists and only setting it when $installDir
is not present.
- Line 62: Update the Write-Host message in the install script to replace the
non-ASCII em dash with a plain ASCII hyphen, keeping the rest of the user PATH
guidance unchanged.
- Around line 18-22: Update the architecture selection around $arch to prefer
$env:PROCESSOR_ARCHITEW6432 when it is set, falling back to
$env:PROCESSOR_ARCHITECTURE otherwise, then retain the existing AMD64, ARM64,
and unsupported-architecture mappings.
In `@install/install.sh`:
- Around line 81-83: Update the non-writable-directory branch guarded by `[ -w
"$install_dir" ]` to disable the temporary-directory cleanup trap before calling
`fail`, preserving the downloaded binary for manual installation. Correct the
suggested `sudo install` command to use the `$install_dir` variable instead of
hardcoding `/usr/local/bin/`.
- Around line 63-67: Update the checksum selection logic around sha256sum and
shasum to explicitly check whether the fallback shasum utility exists before
invoking it. If neither checksum tool is available, terminate with a clear error
message instead of computing an empty hash or reporting a misleading checksum
mismatch.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5eb1a131-693d-4f88-b10c-047d2c1cdbb2
📒 Files selected for processing (2)
install/install.ps1install/install.sh
| $arch = switch ($env:PROCESSOR_ARCHITECTURE) { | ||
| 'AMD64' { 'amd64' } | ||
| 'ARM64' { 'arm64' } | ||
| default { throw "unsupported architecture: $env:PROCESSOR_ARCHITECTURE" } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show file size and relevant section
wc -l install/install.ps1
sed -n '1,120p' install/install.ps1
# Trace all uses of $arch in this file
rg -n '\$arch\b|PROCESSOR_ARCHITECTURE|PROCESSOR_ARCHITEW6432' install/install.ps1Repository: ENTERPILOT/GoModel
Length of output: 3507
🏁 Script executed:
#!/bin/bash
set -euo pipefail
wc -l install/install.ps1
sed -n '1,120p' install/install.ps1
rg -n '\$arch\b|PROCESSOR_ARCHITECTURE|PROCESSOR_ARCHITEW6432' install/install.ps1Repository: ENTERPILOT/GoModel
Length of output: 3507
Detect the native OS architecture under WOW64. install/install.ps1:18-22 A 32-bit PowerShell session reports x86 here, so supported amd64/arm64 Windows installs can be rejected; use PROCESSOR_ARCHITEW6432 when present.
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'install.ps1'
(PSUseBOMForUnicodeEncodedFile)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@install/install.ps1` around lines 18 - 22, Update the architecture selection
around $arch to prefer $env:PROCESSOR_ARCHITEW6432 when it is set, falling back
to $env:PROCESSOR_ARCHITECTURE otherwise, then retain the existing AMD64, ARM64,
and unsupported-architecture mappings.
| $expected = (Get-Content (Join-Path $tmpDir 'checksums.txt') | | ||
| Where-Object { $_ -match [regex]::Escape($archive) } | | ||
| ForEach-Object { ($_ -split '\s+')[0] }) | Select-Object -First 1 |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Match the checksum filename exactly.
The substring regex can select a row for a similarly named artifact. Parse the checksum and require the second field to equal $archive, matching install/install.sh.
Proposed fix
$expected = (Get-Content (Join-Path $tmpDir 'checksums.txt') |
- Where-Object { $_ -match [regex]::Escape($archive) } |
- ForEach-Object { ($_ -split '\s+')[0] }) | Select-Object -First 1
+ ForEach-Object {
+ $parts = $_ -split '\s+', 2
+ if ($parts.Count -eq 2 -and $parts[1].Trim() -eq $archive) {
+ $parts[0]
+ }
+ }) | Select-Object -First 1📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $expected = (Get-Content (Join-Path $tmpDir 'checksums.txt') | | |
| Where-Object { $_ -match [regex]::Escape($archive) } | | |
| ForEach-Object { ($_ -split '\s+')[0] }) | Select-Object -First 1 | |
| $expected = (Get-Content (Join-Path $tmpDir 'checksums.txt') | | |
| ForEach-Object { | |
| $parts = $_ -split '\s+', 2 | |
| if ($parts.Count -eq 2 -and $parts[1].Trim() -eq $archive) { | |
| $parts[0] | |
| } | |
| }) | Select-Object -First 1 |
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'install.ps1'
(PSUseBOMForUnicodeEncodedFile)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@install/install.ps1` around lines 41 - 43, Update the checksum selection in
the PowerShell install flow around $expected to parse each checksums.txt row
into fields and require the second field to equal $archive exactly, matching the
behavior in install.sh; retain the first-field checksum as the selected value
and avoid substring regex matching.
| $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') | ||
| if (($userPath -split ';') -notcontains $installDir) { | ||
| [Environment]::SetEnvironmentVariable('Path', "$userPath;$installDir", 'User') | ||
| Write-Host "Added $installDir to your user PATH — restart your terminal to pick it up." |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Avoid creating an empty PATH entry.
When the user PATH is unset, Line 61 persists ;$installDir. Build the value conditionally.
Proposed fix
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
if (($userPath -split ';') -notcontains $installDir) {
- [Environment]::SetEnvironmentVariable('Path', "$userPath;$installDir", 'User')
+ $newUserPath = if ([string]::IsNullOrWhiteSpace($userPath)) {
+ $installDir
+ } else {
+ "$userPath;$installDir"
+ }
+ [Environment]::SetEnvironmentVariable('Path', $newUserPath, 'User')📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') | |
| if (($userPath -split ';') -notcontains $installDir) { | |
| [Environment]::SetEnvironmentVariable('Path', "$userPath;$installDir", 'User') | |
| Write-Host "Added $installDir to your user PATH — restart your terminal to pick it up." | |
| $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') | |
| if (($userPath -split ';') -notcontains $installDir) { | |
| $newUserPath = if ([string]::IsNullOrWhiteSpace($userPath)) { | |
| $installDir | |
| } else { | |
| "$userPath;$installDir" | |
| } | |
| [Environment]::SetEnvironmentVariable('Path', $newUserPath, 'User') | |
| Write-Host "Added $installDir to your user PATH — restart your terminal to pick it up." |
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'install.ps1'
(PSUseBOMForUnicodeEncodedFile)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@install/install.ps1` around lines 59 - 62, Update the PATH assignment in the
user PATH handling block to avoid prepending a semicolon when $userPath is unset
or empty. Build the new value conditionally while preserving the existing
behavior of appending $installDir when a user PATH already exists and only
setting it when $installDir is not present.
| $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') | ||
| if (($userPath -split ';') -notcontains $installDir) { | ||
| [Environment]::SetEnvironmentVariable('Path', "$userPath;$installDir", 'User') | ||
| Write-Host "Added $installDir to your user PATH — restart your terminal to pick it up." |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
python3 - <<'PY'
from pathlib import Path
p = Path('install/install.ps1')
data = p.read_bytes()
print('byte_len', len(data))
print('starts_with_utf8_bom', data.startswith(b'\xef\xbb\xbf'))
# show non-ascii bytes with positions
non_ascii = [(i,b) for i,b in enumerate(data) if b >= 0x80]
print('non_ascii_count', len(non_ascii))
print('first_non_ascii', non_ascii[:20])
# print lines around 62 with repr and bytes
text = data.decode('utf-8')
lines = text.splitlines()
for n in range(58, 66):
if 1 <= n <= len(lines):
print(f'ল{n}: {lines[n-1]!r}')
PYRepository: ENTERPILOT/GoModel
Length of output: 669
🏁 Script executed:
sed -n '58,66p' install/install.ps1 | cat -nRepository: ENTERPILOT/GoModel
Length of output: 587
🏁 Script executed:
python3 - <<'PY'
from pathlib import Path
p = Path('install/install.ps1')
data = p.read_bytes()
print('byte_len', len(data))
print('starts_with_utf8_bom', data.startswith(b'\xef\xbb\xbf'))
non_ascii = [(i,b) for i,b in enumerate(data) if b >= 0x80]
print('non_ascii_count', len(non_ascii))
print('first_non_ascii', non_ascii[:20])
text = data.decode('utf-8')
for n, line in enumerate(text.splitlines(), 1):
if 58 <= n <= 66:
print(f'{n}: {line!r}')
PYRepository: ENTERPILOT/GoModel
Length of output: 697
Replace the em dash with - or add a UTF-8 BOM.
install/install.ps1 is UTF-8 without a BOM, so the non-ASCII em dash can be misread by Windows PowerShell 5.1. Using plain ASCII avoids the encoding warning.
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'install.ps1'
(PSUseBOMForUnicodeEncodedFile)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@install/install.ps1` at line 62, Update the Write-Host message in the install
script to replace the non-ASCII em dash with a plain ASCII hyphen, keeping the
rest of the user PATH guidance unchanged.
Source: Linters/SAST tools
| if command -v sha256sum >/dev/null 2>&1; then | ||
| actual=$(sha256sum "$tmpdir/$archive" | awk '{ print $1 }') | ||
| else | ||
| actual=$(shasum -a 256 "$tmpdir/$archive" | awk '{ print $1 }') | ||
| fi |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Handle missing checksum utilities cleanly.
If sha256sum is missing, the script falls back to shasum. However, if both tools are missing from the system, the shasum execution will fail silently, resulting in an empty hash and a misleading "checksum mismatch" error.
Explicitly verify the fallback tool's presence and fail with a clear message if neither is available.
🛠️ Proposed fix
if command -v sha256sum >/dev/null 2>&1; then
actual=$(sha256sum "$tmpdir/$archive" | awk '{ print $1 }')
-else
+elif command -v shasum >/dev/null 2>&1; then
actual=$(shasum -a 256 "$tmpdir/$archive" | awk '{ print $1 }')
+else
+ fail "neither sha256sum nor shasum is available"
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if command -v sha256sum >/dev/null 2>&1; then | |
| actual=$(sha256sum "$tmpdir/$archive" | awk '{ print $1 }') | |
| else | |
| actual=$(shasum -a 256 "$tmpdir/$archive" | awk '{ print $1 }') | |
| fi | |
| 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 "neither sha256sum nor shasum is available" | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@install/install.sh` around lines 63 - 67, Update the checksum selection logic
around sha256sum and shasum to explicitly check whether the fallback shasum
utility exists before invoking it. If neither checksum tool is available,
terminate with a clear error message instead of computing an empty hash or
reporting a misleading checksum mismatch.
| 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/" | ||
| install -m 755 "$tmpdir/$BINARY" "$install_dir/$BINARY" |
There was a problem hiding this comment.
🎯 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 sudo install .... However, there are two issues here:
- The global trap configured on line 55 removes the temporary directory immediately when the script exits, which means the suggested
sudo installcommand will fail because the downloaded binary was deleted. - The suggested command hardcodes
/usr/local/bin/, ignoring the customGOMODEL_INSTALL_DIRif the user provided one.
Disable the cleanup trap right before failing to leave the file intact for the manual step, and use the $install_dir variable.
🐛 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
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 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/" | |
| install -m 755 "$tmpdir/$BINARY" "$install_dir/$BINARY" | |
| mkdir -p "$install_dir" || fail "cannot create $install_dir" | |
| 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" |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@install/install.sh` around lines 81 - 83, Update the non-writable-directory
branch guarded by `[ -w "$install_dir" ]` to disable the temporary-directory
cleanup trap before calling `fail`, preserving the downloaded binary for manual
installation. Correct the suggested `sudo install` command to use the
`$install_dir` variable instead of hardcoding `/usr/local/bin/`.
…t lean Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/install/install.ps1`:
- Around line 25-27: Update the tag resolution branch in the PowerShell
installer to avoid the unauthenticated GitHub API call. Mirror install.sh by
resolving the releases/latest HTTP redirect and extracting the redirected
release tag, while preserving the provided $tag path and existing behavior when
a tag is supplied.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3c92f6b9-01b0-4ea9-a27e-7882c7a6ab13
📒 Files selected for processing (2)
docs/install/install.ps1docs/install/install.sh
| if (-not $tag) { | ||
| $tag = (Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest").tag_name | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Avoid unauthenticated GitHub API calls to prevent rate limiting.
The script uses the GitHub API to fetch the latest release tag. Unauthenticated GitHub API requests are subject to a strict rate limit (60 requests per hour per IP), which frequently causes failures in CI environments, enterprise networks, or shared IPs. The shell script (install.sh) already avoids this by resolving the /releases/latest HTTP redirect instead.
You can resolve the redirect in PowerShell without hitting the API rate limit:
🛠️ Proposed fix
$tag = $env:GOMODEL_VERSION
if (-not $tag) {
- $tag = (Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest").tag_name
+ $req = [System.Net.WebRequest]::Create("https://github.com/$Repo/releases/latest")
+ $req.AllowAutoRedirect = $false
+ $res = $req.GetResponse()
+ $tag = ($res.Headers.Location -split '/')[-1]
+ $res.Close()
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (-not $tag) { | |
| $tag = (Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest").tag_name | |
| } | |
| if (-not $tag) { | |
| $req = [System.Net.WebRequest]::Create("https://github.com/$Repo/releases/latest") | |
| $req.AllowAutoRedirect = $false | |
| $res = $req.GetResponse() | |
| $tag = ($res.Headers.Location -split '/')[-1] | |
| $res.Close() | |
| } |
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'install.ps1'
(PSUseBOMForUnicodeEncodedFile)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/install/install.ps1` around lines 25 - 27, Update the tag resolution
branch in the PowerShell installer to avoid the unauthenticated GitHub API call.
Mirror install.sh by resolving the releases/latest HTTP redirect and extracting
the redirected release tag, while preserving the provided $tag path and existing
behavior when a tag is supplied.
Summary
docs/install/install.sh— macOS/Linux installer: detects OS/arch, resolves the latest release via thereleases/latestredirect (no GitHub API rate limit), downloads the GoReleaser archive, verifies its SHA-256 againstchecksums.txt, and installs to/usr/local/bin(or~/.local/binwhen not writable). Overridable viaGOMODEL_VERSION/GOMODEL_INSTALL_DIR. No sudo, no telemetry.docs/install/install.ps1— Windows equivalent: installs to%LOCALAPPDATA%\Programs\gomodeland registers it on the user PATH when missing.User-visible impact
Enables the one-line install flow:
curl -fsSL https://gomodel.enterpilot.io/install.sh | shThe serving endpoint (a Cloudflare Worker on gomodel.enterpilot.io) relays these files from
main, so they go live once this merges and the Worker is deployed.Testing
install.shverified end-to-end on macOS arm64 against the real v0.1.53 release (download, checksum, install, binary runs).sh -nclean;install.ps1untested on a real Windows box (worth one manual run before announcing).🤖 Generated with Claude Code
Summary by CodeRabbit