Skip to content

Improve Windows installation with native PowerShell support and tougher shell installer fallbacks#406

Merged
jeremy merged 7 commits intomainfrom
windows-installers
Apr 29, 2026
Merged

Improve Windows installation with native PowerShell support and tougher shell installer fallbacks#406
jeremy merged 7 commits intomainfrom
windows-installers

Conversation

@robzolkos
Copy link
Copy Markdown
Collaborator

@robzolkos robzolkos commented Apr 3, 2026

Summary

Windows users were being funneled into a Unix-first curl | bash flow that can fail before the installer even runs, especially on Windows 10 with Schannel revocation errors like CRYPT_E_NO_REVOCATION_CHECK. This PR makes Windows installation first-class by adding a native PowerShell installer, while also hardening the existing shell installer for Git Bash and other bash-based environments on Windows. The result is a more reliable install experience across native Windows, Git Bash, and WSL2 without regressing Unix users.

Ref: https://3.basecamp.com/2914079/buckets/46292715/card_tables/cards/9751372396

Changes

  • add a native Windows PowerShell installer at scripts/install.ps1
  • update docs to recommend PowerShell for native Windows installs
  • keep install.sh as the installer for macOS, Linux, WSL2, and bash-based Windows environments
  • harden install.sh to better handle Windows Schannel revocation-check failures by retrying with supported curl flags
  • add fallback release resolution via the GitHub API when redirect-based latest-version detection fails
  • make installer bin-dir selection smarter by preferring an already-present PATH location (~/bin or ~/.local/bin) before falling back to platform defaults
  • align scripts/ensure-basecamp.sh with the same smarter path and Windows-friendly behavior

Notes

  • WSL2 continues to be treated as Linux and uses the Unix installer path
  • native Windows is now expected to use the PowerShell installer path
  • Git Bash remains supported via the shell installer as a secondary Windows path

Summary by cubic

Adds a native Windows PowerShell installer and toughens the bash installers across Windows/WSL2/macOS/Linux. Improves latest-version detection (incl. prereleases), SHA‑256 with optional cosign, PATH‑aware bin‑dir defaults, Schannel‑aware curl, and non‑interactive behavior; docs add OS‑specific quick starts, Git Bash callouts, and Windows Schannel guidance.

  • New Features

    • scripts/install.ps1: latest‑version via redirect with GitHub API fallback; SHA‑256 and optional cosign verification; installs to a PATH‑aware bin dir and prepends the user PATH; supports prereleases; clearer non‑interactive flow; actionable install‑failure message if basecamp.exe is in use; hardened downloads with -UseBasicParsing -ErrorAction Stop.
    • Shell installers (scripts/install.sh, scripts/ensure-basecamp.sh): Schannel‑aware curl wrapper that forces --show-error and retries on CRYPT_E_NO_REVOCATION_CHECK; smarter bin‑dir selection using current PATH (prefers ~/bin or ~/.local/bin; defaults to ~/bin on Windows) with exact PATH hints; optional cosign.
  • Bug Fixes

    • Whitespace‑tolerant GitHub API parsing for latest‑version fallback in install.sh and ensure-basecamp.sh to avoid breaks on JSON formatting changes.
    • scripts/ensure-basecamp.sh: after installing to a bin dir not on PATH, export that dir into the current PATH so the post‑install self‑check succeeds without a new shell.

Written for commit cd75c3b. Summary will update on new commits. Review in cubic

Copilot AI review requested due to automatic review settings April 3, 2026 19:03
@github-actions github-actions Bot added docs enhancement New feature or request labels Apr 3, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances Windows installation support by adding a native PowerShell installer while also hardening the existing shell-based installer for better cross-platform compatibility. It addresses Windows Schannel certificate revocation errors that previously prevented installations on Windows 10, and improves bin-directory detection to prefer existing PATH locations.

Changes:

  • Add new scripts/install.ps1 PowerShell installer for native Windows installations
  • Add Windows Schannel certificate revocation error handling to install.sh with automatic fallback flags
  • Add GitHub API fallback for version detection when redirect-based method fails
  • Implement intelligent bin-directory selection preferring already-present PATH locations
  • Update documentation to recommend PowerShell for native Windows installations

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
scripts/install.ps1 New PowerShell installer with complete Windows support including architecture detection, checksum verification, and PATH management
scripts/install.sh Enhanced shell installer with Schannel fallback handling, API fallback for version detection, and smart bin-dir selection
scripts/ensure-basecamp.sh Updated with same enhancements as install.sh for consistency
README.md Updated Quick Start and installation methods to include PowerShell option and Windows Schannel troubleshooting note
install.md Updated installation guide with separate PowerShell instructions and Windows-specific guidance

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/ensure-basecamp.sh Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/ensure-basecamp.sh">

<violation number="1" location="scripts/ensure-basecamp.sh:117">
P0: `curl_run` reads `$?` from the `if` statement instead of the failed `curl`, causing curl failures to be treated as success and skipping fallback logic.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread scripts/ensure-basecamp.sh Outdated
@robzolkos
Copy link
Copy Markdown
Collaborator Author

robzolkos commented Apr 3, 2026

Fixed — cubic found that curl_run was capturing the compound if status instead of the failed curl exit code. Both installer scripts now capture the curl status inside the failing else branch before evaluating Schannel fallback logic.

Copilot AI review requested due to automatic review settings April 3, 2026 19:41
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings April 3, 2026 20:08
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/ensure-basecamp.sh
Comment thread scripts/install.sh Outdated
Copilot AI review requested due to automatic review settings April 3, 2026 20:26
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@robzolkos robzolkos force-pushed the windows-installers branch from 6a15753 to ccc26c3 Compare April 3, 2026 20:36
Copilot AI review requested due to automatic review settings April 3, 2026 20:42
@robzolkos robzolkos force-pushed the windows-installers branch 2 times, most recently from 4913838 to 9c380bc Compare April 3, 2026 20:43
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@robzolkos robzolkos force-pushed the windows-installers branch from 9c380bc to 44dbf02 Compare April 3, 2026 20:47
Copilot AI review requested due to automatic review settings April 3, 2026 20:48
@robzolkos robzolkos force-pushed the windows-installers branch from 44dbf02 to 6874dff Compare April 3, 2026 20:48
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/ensure-basecamp.sh
@robzolkos robzolkos requested a review from jeremy April 3, 2026 20:57
jeremy added 2 commits April 29, 2026 11:08
When the chosen bin dir wasn't already on PATH, install_basecamp printed
the persistent-add instructions but didn't update PATH for the running
script. The follow-up check_basecamp call then failed because `command -v
basecamp` couldn't find it, so --install exited 1 on a genuinely successful
install. Export BIN_DIR into PATH after the existing instructions block so
the in-script check passes; the persistent-add guidance still prints because
it inspects the original PATH.
Bring install.ps1 closer to install.sh's behavior on three security and
reliability fronts, plus an actionable error for the most common Windows
re-install failure.

- Cosign signature verification when cosign.exe is on PATH, mirroring
  install.sh. Check $LASTEXITCODE explicitly because Windows PowerShell 5.1
  doesn't surface native exits via $ErrorActionPreference=Stop, so a
  silent verify failure would otherwise false-green.
- Surface an actionable error when basecamp.exe is locked. Windows holds
  an exclusive lock on running PE files; -Force doesn't help. Wrap the
  copy in a generic catch (typed catches miss the
  ActionPreferenceStopException wrapper) and include the original
  exception text so unrelated failures aren't masked.
- Try the GitHub releases/latest redirect before the API to avoid
  unauthenticated rate limits, falling back to the API on miss. Read
  Location off the response in both the success and 302-as-error paths
  so it works on Windows PowerShell 5.1 and PowerShell Core.
- Prepend BIN_DIR to the persisted user PATH so a stale basecamp.exe
  earlier in PATH doesn't shadow the freshly-installed one in new shells.
Copilot AI review requested due to automatic review settings April 29, 2026 18:08
@jeremy
Copy link
Copy Markdown
Member

jeremy commented Apr 29, 2026

Just pushed two commits addressing the blockers from my review:

  • abad3b74ensure-basecamp.sh --install: export BIN_DIR into the in-script PATH after the persistent-add instructions print, so the follow-up check_basecamp re-run can find the freshly installed binary.
  • 9fccdbf3install.ps1: cosign verification when cosign.exe is on PATH (with explicit $LASTEXITCODE check, since Windows PowerShell 5.1 doesn't propagate native exits via $ErrorActionPreference='Stop'); actionable error when basecamp.exe is in use; redirect-first in Get-LatestVersion to dodge GitHub API rate limits; and prepend (not append) when persisting BIN_DIR to the user PATH.

Heads-up: I cannot validate install.ps1 changes locally (no Windows / pwsh available in my worktree). Please run a Windows PowerShell smoke before merging. Minimum coverage:

  • Fresh install: irm https://.../install.ps1 | iex → installs, basecamp --version works in a new shell, user PATH contains the bin dir at the front.
  • Re-install with basecamp.exe running in another window: should fail with the new actionable error rather than a raw Copy-Item exception.
  • BASECAMP_BIN_DIR=C:\tools honored.
  • BASECAMP_VERSION=0.7.2 installs that exact version.
  • BASECAMP_SKIP_SETUP=1 prints next-steps without running setup.
  • With cosign.exe on PATH: signature verifies; flipping a byte in checksums.txt after download should make verification fail (or test with an unsigned tag).

A few smaller notes I'd be happy for the author to take or leave — none are blockers:

5. install.ps1 PowerShell verb names. Download-File, Verify-Checksum, Verify-CosignSignature, and Ensure-UserPath aren't on the approved-verb list. Harmless when run as a script, but generates Import-Module warnings if the script is ever dot-sourced. Cosmetic.

6. Persisted PATH change isn't broadcast. Other open shells/Explorer don't see the new PATH until restart (no WM_SETTINGCHANGE). Same as Scoop/Chocolatey, so probably fine; consider mentioning in next-steps output.

7. trap \"rm -rf '\${tmp_dir}'\" EXIT inside install_basecamp / main clobbers any pre-existing trap. Not a regression. If ensure-basecamp.sh is ever sourced into a shell that already has an EXIT trap, the source's trap is lost. Doc-level concern only.

8. get_latest_version API fallback parses JSON via parameter expansion (install.sh:167-173, ensure-basecamp.sh:160-167). Works for GitHub's current \"tag_name\":\"vX.Y.Z\" shape; will silently misparse if GitHub ever reorders or pretty-prints. Acceptable as a fallback because the redirect path will succeed first; noting for future maintainers.

9. README.md:13 PowerShell install uses raw githubusercontent.com while bash uses the basecamp.com/install-cli short URL. If a PowerShell short URL exists or can be added (e.g. basecamp.com/install-cli.ps1), prefer it for parity and so future installer relocations don't break docs.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/install.sh Outdated
Comment thread scripts/ensure-basecamp.sh Outdated
Comment thread README.md Outdated
Comment thread scripts/install.sh Outdated
Comment thread scripts/ensure-basecamp.sh Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/install.ps1">

<violation number="1" location="scripts/install.ps1:243">
P2: The new error handling always reports `basecamp.exe` as "in use" even when `Copy-Item` fails for other reasons, which can mislead troubleshooting.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread scripts/install.ps1 Outdated
jeremy added 2 commits April 29, 2026 11:16
- Mention Git Bash alongside macOS/Linux/WSL2 for the shell installer in
  README so it matches install.md and reflects this PR's Schannel hardening.
- Document the PATH-aware BASECAMP_BIN_DIR default in both install.sh and
  ensure-basecamp.sh — default_bin_dir() prefers ~/bin or ~/.local/bin if
  already on PATH before falling back to a platform default.
- Soften the install.ps1 Copy-Item failure message: lead with the generic
  install failure, suggest closing running processes if the file is in use.
  The original exception text is still appended so disk-full or permission
  failures aren't masked.
Bash parameter expansion `${api_json#*\"tag_name\":\"v}` only matched the
exact `\"tag_name\":\"v...\"` shape. GitHub currently returns minified JSON
under `Accept: application/vnd.github+json`, so the parse worked, but a
future format change or alternate endpoint that pretty-printed would have
silently fallen through (the trailing semver guard would catch the garbage,
but the fallback would be unusable).

Replace with a bash regex that tolerates spaces, tabs, and newlines around
the colon and makes the leading `v` optional. The regex is anchored on the
unescaped quotes around `tag_name`, so a release body containing the
substring `\"tag_name\":\"...\"` (which gets backslash-escaped in JSON)
can't false-match. Stays in pure bash so it works on macOS' shipped
/bin/bash 3.2 and Git Bash without depending on GNU-awk's 3-arg match().
Copilot AI review requested due to automatic review settings April 29, 2026 19:31
@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented Apr 29, 2026

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/install.sh
Comment thread scripts/install.ps1 Outdated
Comment thread install.md Outdated
- curl_run: force --show-error inside the helper. All current callers pass
  -fsSL (which already includes -S), so behavior is unchanged today, but
  encoding the flag in the helper makes the Schannel revocation fallback
  invariant — "errors must reach stderr so we can grep for
  CRYPT_E_NO_REVOCATION_CHECK" — robust to a future caller that uses -s
  alone.
- install.ps1 Download-File: pass -UseBasicParsing -ErrorAction Stop. On
  Windows PowerShell 5.1 the cmdlet initializes IE's MSHTML parser even
  for -OutFile downloads, which fails on Server Core / locked-down
  installs. -UseBasicParsing is a no-op on PowerShell 6+. -ErrorAction
  Stop doubles up on the global $ErrorActionPreference for clarity and
  defense if a future function locally relaxes it.
- install.ps1 Get-LatestVersion API fallback: same -ErrorAction Stop
  hardening on Invoke-RestMethod for consistency.
- install.md: align Step 1 heading with README — "WSL2 / Git Bash" instead
  of "WSL / Git Bash". The PR description and README both say WSL2.
@jeremy jeremy merged commit 1e9f618 into main Apr 29, 2026
25 checks passed
@jeremy jeremy deleted the windows-installers branch April 29, 2026 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants