Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions docs/install/install.ps1
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]
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
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
}
102 changes: 102 additions & 0 deletions docs/install/install.sh
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
Comment thread
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 sudo install .... However, there are two issues here:

  1. The global trap configured on line 55 removes the temporary directory immediately when the script exits, which means the suggested sudo install command will fail because the downloaded binary was deleted.
  2. The suggested command hardcodes /usr/local/bin/, ignoring the custom GOMODEL_INSTALL_DIR if 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.

Suggested change
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/`.


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"