feat(install): verify release archive checksums in both installers#942
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe release workflow gains a step that generates ChangesChecksum Verification Feature
Sequence Diagram(s)sequenceDiagram
rect rgba(135, 206, 235, 0.5)
Note over User,GitHub Release: Bash Install Flow
User->>install: run installer
install->>GitHub API: resolve specific_version tag
GitHub API-->>install: version string
install->>GitHub Release: download archive via pinned /download/v${specific_version}/...
GitHub Release-->>install: archive file
install->>verify_checksum: verify_checksum(file, name)
verify_checksum->>GitHub Release: fetch checksums.txt via ${url%/*}/checksums.txt
GitHub Release-->>verify_checksum: expected SHA256 entry
verify_checksum->>verify_checksum: sha256sum / shasum compute actual hash
alt Checksum mismatch
verify_checksum->>install: rm -rf tmp dir, exit 1
else No tool / no entry / fetch failure
verify_checksum-->>install: soft-skip, continue
else Match
verify_checksum-->>install: proceed to extraction
end
end
rect rgba(144, 238, 144, 0.5)
Note over User,GitHub Release: PowerShell Install Flow
User->>install.ps1: run installer
install.ps1->>GitHub API: resolve specificVersion tag
GitHub API-->>install.ps1: version string
install.ps1->>GitHub Release: download zip via pinned base URL
GitHub Release-->>install.ps1: zip file
install.ps1->>Test_Checksum: Test-Checksum -Path zip -Name name -ChecksumsUrl
Test_Checksum->>GitHub Release: Invoke-WebRequest checksums.txt
GitHub Release-->>Test_Checksum: string or Byte[] content
Test_Checksum->>Test_Checksum: decode Byte[] via UTF-8 if PS 5.1
Test_Checksum->>Test_Checksum: Get-FileHash compute actual SHA256
alt Mismatch
Test_Checksum->>install.ps1: throw "Checksum mismatch"
else No entry / fetch failure
Test_Checksum-->>install.ps1: Write-Muted soft-skip
else Match
Test_Checksum-->>install.ps1: proceed to Expand-Archive
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
2 issues found across 4 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
Local review of #942. One real issue inline (the PS 5.1 byte[] case on install.ps1:89 — verification is effectively dead on the default Windows shell). Notes from triage:
False positives I verified before discarding:
tmp_dirallegedly unbound underset -uinsideverify_checksum(cubic + OCR): bashlocalis dynamically scoped —tmp_dirset indownload_and_installis visible to the function it calls. Verified with a 4-line test.\r\nline endings allegedly corrupting the extracted hash (OCR): the regex's\s*$consumes the trailing\r, and-split '\s+'puts the\rin the separator, not in[0]. The hash comes out clean.
Out of scope: the catch-all-errors behavior in Test-Checksum's try/catch (also flagged by OCR) is a deliberate design choice per the PR description (soft-skip on any fetch failure for backwards-compat with pre-checksums releases). Worth a note as a follow-up if this ever moves to GPG-signed releases, but not a defect against this PR's stated contract.
Raises the integrity bar for the standalone installers (follow-up to #930). - release.yml: generate a checksums.txt (sha256sum format) over the release archives and publish it as a release asset. - install (bash) + install.ps1: fetch checksums.txt and verify the downloaded archive's SHA256 before extracting. Hard-fail on mismatch; soft-skip with a notice when checksums.txt is absent (older pinned releases) or unreachable, so existing version-pinned installs keep working. - Cross-platform sha in bash (sha256sum or shasum -a 256); Get-FileHash on Windows. Verification runs before extraction in both. - Tests: checksum-verification.test.ts asserts release.yml publishes the file and both installers fetch + compare + hard-fail on mismatch. Verified: bash -n clean; install.ps1 parses clean and the Pester suite (6/6) still passes on PowerShell 7.6.2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a0a44a1 to
d22e374
Compare
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 `@packages/opencode/test/install/checksum-verification.test.ts`:
- Around line 60-67: The test "bash pins the download to the resolved tag"
references an undefined variable BASH on lines 65-66, but the constant is
actually named BASH_INSTALL as defined earlier in the file. Replace both
expect() calls that currently check BASH with expect(BASH_INSTALL) instead to
fix the ReferenceError that will occur when the test runs.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f0f1ba73-5fbd-46ef-bd58-23d0b1c47f9a
📒 Files selected for processing (5)
.github/workflows/release.ymlinstallinstall.ps1packages/opencode/test/install/checksum-verification.test.tstest/windows/install.Tests.ps1
d22e374 to
dd0134d
Compare
- install.ps1: decode a Byte[] checksums.txt body so verification works on Windows PowerShell 5.1. GitHub serves release assets as octet-stream, so on PS 5.1 Invoke-WebRequest returns .Content as Byte[]; it coerced to a decimal string and every check silently soft-skipped (sahrizvi, P1). - install.ps1: pin the archive and checksums.txt to the resolved release tag instead of the mutable latest/ URL, so a release published mid-install can't hand back mismatched assets and trigger a spurious hard-fail (cubic, P2). Falls back to latest/ only when the version can't be resolved. - install: in verify_checksum, clean up via $(dirname "$file") rather than the caller's dynamically-scoped $tmp_dir local — self-contained (cubic, P2). - tests: Pester coverage for Test-Checksum (String + Byte[] + mismatch paths, verified to fail without the decode) and TS guards for the decode and the PowerShell same-release pinning. Note: the bash installer is intentionally left on the latest/download path here to keep this PR disjoint from #946 (which owns the bash latest-version block); the two PRs then merge in either order with no conflict. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019M7GkS3bYZaFhEbBhVTecG
dd0134d to
cecfe06
Compare
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 `@install`:
- Around line 398-402: The rm -rf "$(dirname "$file")" command lacks a safety
guard to prevent catastrophic deletion if $file is unexpectedly empty or
resolves to a dangerous path. Add a defensive check immediately before the rm
-rf command to verify that $file is not empty and that the result of dirname
"$file" is not a dangerous path like . or /. This prevents the command from
accidentally deleting the current directory or root-level contents if upstream
code passes invalid data to this function.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 6cd11031-39a1-4d03-8431-233a74adff33
📒 Files selected for processing (4)
installinstall.ps1packages/opencode/test/install/checksum-verification.test.tstest/windows/install.Tests.ps1
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/opencode/test/install/checksum-verification.test.ts
install.ps1 had no BOM and used a few non-ASCII characters (em dash, ellipsis, right arrow) in comments and messages. Windows PowerShell 5.1 — the default shell on Windows 10 and preinstalled on Windows 11 — reads a BOM-less file as the system ANSI codepage, not UTF-8, so those multi-byte characters corrupt the token stream and the whole script fails to parse (verified on real PS 5.1: "The '<' operator is reserved", cascading to "Missing closing '}'"). This is a pre-existing issue (the characters predate this PR) that CI doesn't catch because the Pester job runs under pwsh (PowerShell 7, UTF-8 by default). Replacing the three characters with ASCII equivalents (-, ..., ->) makes the installer parse and run on PS 5.1 while keeping pwsh behavior identical. Verified end-to-end on real Windows PowerShell 5.1: resolve version -> download -> extract -> place the binary all succeed. Same transliteration is applied verbatim in #946 so the two PRs merge cleanly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019M7GkS3bYZaFhEbBhVTecG
install.ps1 had no BOM and used a few non-ASCII characters (em dash, ellipsis, right arrow) in comments and messages. Windows PowerShell 5.1 - the default shell on Windows 10 and preinstalled on Windows 11 - reads a BOM-less file as the system ANSI codepage, not UTF-8, so those multi-byte characters corrupt the token stream and the whole script fails to parse (verified on real PS 5.1). This is a pre-existing issue (the characters predate this PR) that CI doesn't catch because the Pester job runs under pwsh (PowerShell 7, UTF-8 by default). Replacing the three characters with ASCII equivalents (-, ..., ->) makes the installer parse and run on PS 5.1 while keeping pwsh behavior identical. Also removes the now-obsolete "integrity verification deferred" NOTE comment: the sibling PR #942 implements that verification and removes the same block, so deleting it here too keeps the two PRs mergeable in either order with no conflict. Same transliteration is applied verbatim in #942. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019M7GkS3bYZaFhEbBhVTecG
…l path Defensive depth (coderabbit): only `rm -rf` the cleanup dir when dirname resolves to a real subdirectory, never "." or "/", so an unexpectedly empty or root-level $file can't wipe the cwd or worse. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019M7GkS3bYZaFhEbBhVTecG
The #952 release-validation suite asserted the exact #930 URL literals. This PR builds the archive and checksums.txt from a shared $base (so they always come from the same release), so update those assertions to the $base/$url form, and convert the now-obsolete "verification deferred" test.todo into a real assertion that Test-Checksum verifies SHA256 before extraction. (This test never ran on this PR until it was retargeted from the merged feat/windows-powershell-installer branch to main.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019M7GkS3bYZaFhEbBhVTecG
Combined end-to-end verification (both installer PRs together)#942 and #946 are sibling PRs that both touch Merge safety
Real Windows verificationRan the merged
#942 — checksum verification
#946 — resilient latest-version fetch
Cross-PR composition (verified on real PowerShell)With both changes combined: when the version can't be resolved, #946 sets CIGreen on both: TypeScript, Windows Installer (Pester), and the |
❌ Tests — Failures DetectedTypeScript — 15 failure(s)
Next StepPlease address the failing cases above and re-run verification. cc @mdesmet |
sahrizvi
left a comment
There was a problem hiding this comment.
All review concerns adressed and tested E2E, so LGTM.
* fix(install): don't hard-fail when the GitHub releases API blips Reported on #930: a transient 504 from api.github.com/.../releases/latest (or the 60/hr/IP unauthenticated rate limit) aborted the whole install with "Failed to fetch version information" — even though the download itself uses releases/latest/download/<file>, which GitHub resolves server-side with no API call. The API response only feeds the version-string display and the already-installed short-circuit. Both installers now, in the latest path: - retry the API call up to 3x with linear backoff (bash uses curl --fail so a 504 retries instead of parsing an error body); - on continued failure, print a muted notice and proceed to install latest anyway (version string shown as "latest"); - only short-circuit as "already installed" on a real version match — never treat empty==empty (unresolved version + unreadable binary) as installed. Pinned-version installs (-Version / --version) are unchanged: a genuine 404 still hard-fails. Tests: version-fetch-resilience.test.ts pins the retry + graceful-degrade behavior in both installers. bash -n clean; install.ps1 parses clean and the Pester suite (6/6) still passes on PowerShell 7.6.2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(install): address latest-version-fetch review - install: append `|| true` to the retry's curl|sed assignment. Under `set -euo pipefail` a failing `curl --fail` propagated through the pipeline and aborted the script at attempt 1, before the loop could retry or degrade (sahrizvi; reproduced: exit 22 without the fix, all 3 attempts + degrade with it). Also add `--max-time 10` to bound a dead-air socket. - install.ps1: reset $specificVersion to $null (not "") on the degrade path, so the already-installed short-circuit can't false-match "" -eq "" when the version probe of a missing/corrupt binary also yields "" (dev-punia, sahrizvi). - install.ps1: add -TimeoutSec 10 to Invoke-RestMethod (defaults to 100s on PS 5.1, unbounded on PS 7+) to bound retries on dead air (sahrizvi). - tests: TS guards for `|| true`, --max-time/-TimeoutSec, and the $null reset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019M7GkS3bYZaFhEbBhVTecG * fix(install.ps1): ASCII-only so it parses on Windows PowerShell 5.1 install.ps1 had no BOM and used a few non-ASCII characters (em dash, ellipsis, right arrow) in comments and messages. Windows PowerShell 5.1 - the default shell on Windows 10 and preinstalled on Windows 11 - reads a BOM-less file as the system ANSI codepage, not UTF-8, so those multi-byte characters corrupt the token stream and the whole script fails to parse (verified on real PS 5.1). This is a pre-existing issue (the characters predate this PR) that CI doesn't catch because the Pester job runs under pwsh (PowerShell 7, UTF-8 by default). Replacing the three characters with ASCII equivalents (-, ..., ->) makes the installer parse and run on PS 5.1 while keeping pwsh behavior identical. Also removes the now-obsolete "integrity verification deferred" NOTE comment: the sibling PR #942 implements that verification and removes the same block, so deleting it here too keeps the two PRs mergeable in either order with no conflict. Same transliteration is applied verbatim in #942. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019M7GkS3bYZaFhEbBhVTecG * test: update #930 release-validation for resilient version fetch The #952 release-validation suite asserted the latest path hard-fails with "Failed to fetch version information" (>=2) and that exit 1 appears >=3 times. This PR makes the latest path retry then degrade gracefully instead of aborting, so update those assertions: the latest path no longer hard-fails (the unsupported -arch and pinned-404 paths still exit 1, hence >=2). (This test never ran on this PR until it was retargeted from the merged feat/windows-powershell-installer branch to main.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019M7GkS3bYZaFhEbBhVTecG --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: ralphstodomingo <ralphstodomingo@users.noreply.github.com>
What
Follow-up to #930 (raised in review by @coderabbitai and the consensus panel): publish a checksums file with releases and verify downloaded archives in both the curl/bash and PowerShell installers.
Stacked on
feat/windows-powershell-installer(base of this PR) becauseinstall.ps1only exists on that branch. Merge #930 first, then this — or rebase ontomainafter #930 lands.Changes
.github/workflows/release.yml— generatechecksums.txt(sha256sum *.tar.gz *.zip) and publish it as a release asset alongside the archives.install(bash) —verify_checksum()fetcheschecksums.txt, looks up the archive's expected hash, and compares (sha256sumorshasum -a 256) before extracting.install.ps1—Test-Checksumfetcheschecksums.txtand comparesGet-FileHash -Algorithm SHA256beforeExpand-Archive. Replaces the deferral note from feat: Windows PowerShell installer (install.ps1) #930.checksums.txtis absent (releases predating this change) or unreachable, so existing version-pinned installs keep working.checksum-verification.test.tsasserts the release publishes the file and both installers fetch + compare + hard-fail on mismatch.Verification
bash -n installclean;install.ps1parses clean and the Pester suite (6/6) still passes on PowerShell 7.6.2.checksum-verification.test.tsgreen (57 pass across the install/branding set).checksums.txt.🤖 Generated with Claude Code
Summary by cubic
Adds SHA256 verification to both installers and ships a
checksums.txtwith each release. PowerShell pins the archive and checksum to the same tag; both installers hard-fail on mismatch and soft-skip when checksums are missing or unreachable.New Features
checksums.txtfor all archives.installand PowerShellinstall.ps1fetchchecksums.txtand verify before extraction; cross-platform SHA tools. PowerShell pins archive andchecksums.txtto the same resolved tag to avoid a latest/ race.Bug Fixes
Invoke-WebRequestbodies on PS 5.1; ASCII-only so the script parses on Windows PowerShell 5.1.checksums.txtfrom the archive URL; safe cleanup on checksum mismatch with guards against "." and "/".packages/opencode/test/install/checksum-verification.test.ts; updatedpackages/opencode/test/release-validation/windows-installer-930-codex.test.tsto assert shared$baseURLs and that verification runs beforeExpand-Archive; Pester coverage forTest-Checksum(String/Byte[]/mismatch).Written for commit 1ecf6ae. Summary will update on new commits.
Summary by CodeRabbit
New Features
checksums.txtto verify downloaded archives.checksums.txtbefore extraction, and fails fast on mismatches.checksums.txtbefore extraction, with improved handling for different checksum file encodings.Tests
checksums.txt.