Skip to content

Commit 9c380bc

Browse files
committed
Improve Windows installation experience
1 parent 4c6860b commit 9c380bc

File tree

5 files changed

+495
-38
lines changed

5 files changed

+495
-38
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,25 @@
99

1010
## Quick Start
1111

12+
**macOS / Linux / WSL / Git Bash**
13+
1214
```bash
1315
curl -fsSL https://basecamp.com/install-cli | bash
1416
```
1517

18+
**Windows (PowerShell)**
19+
20+
```powershell
21+
irm https://raw.githubusercontent.com/basecamp/basecamp-cli/main/scripts/install.ps1 | iex
22+
```
23+
24+
**Windows (Scoop)**
25+
26+
```powershell
27+
scoop bucket add basecamp https://github.com/basecamp/homebrew-tap
28+
scoop install basecamp-cli
29+
```
30+
1631
That's it. You now have full access to Basecamp from your terminal.
1732

1833
<details>
@@ -44,7 +59,7 @@ scoop bucket add basecamp https://github.com/basecamp/homebrew-tap
4459
scoop install basecamp-cli
4560
```
4661

47-
**Shell script:**
62+
**Shell script (macOS/Linux/WSL/Git Bash):**
4863
```bash
4964
curl -fsSL https://raw.githubusercontent.com/basecamp/basecamp-cli/main/scripts/install.sh | bash
5065
```

install.md

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,21 @@ Install the Basecamp CLI and connect it to your AI agent. Execute all steps auto
1616

1717
## Step 1: Install Basecamp CLI
1818

19-
Run this in your terminal:
19+
Run the matching command for your environment:
2020

21+
**macOS / Linux / WSL / Git Bash**
2122
```bash
2223
curl -fsSL https://basecamp.com/install-cli | bash
2324
```
2425

25-
> **Note:** The install script auto-detects non-interactive environments (CI, piped input, coding agents) and skips the interactive setup wizard. You can also explicitly skip it with `BASECAMP_SKIP_SETUP=1`.
26+
**Windows PowerShell**
27+
```powershell
28+
irm https://raw.githubusercontent.com/basecamp/basecamp-cli/main/scripts/install.ps1 | iex
29+
```
30+
31+
> **Note:** The install scripts auto-detect non-interactive environments (CI, piped input, coding agents) and skip the interactive setup wizard. You can also explicitly skip it with `BASECAMP_SKIP_SETUP=1`.
32+
>
33+
> **Windows note:** if `curl` fails with a `schannel` / `CRYPT_E_NO_REVOCATION_CHECK` TLS error, prefer the PowerShell installer, Scoop, or Git Bash's `/usr/bin/curl` instead of the system `curl.exe`.
2634
2735
Alternatively install manually:
2836

@@ -31,13 +39,18 @@ Alternatively install manually:
3139
brew install --cask basecamp/tap/basecamp-cli
3240
```
3341

34-
### Option B: Scoop (Windows)
42+
### Option B: PowerShell (Windows)
43+
```powershell
44+
irm https://raw.githubusercontent.com/basecamp/basecamp-cli/main/scripts/install.ps1 | iex
45+
```
46+
47+
### Option C: Scoop (Windows)
3548
```bash
3649
scoop bucket add basecamp https://github.com/basecamp/homebrew-tap
3750
scoop install basecamp-cli
3851
```
3952

40-
### Option C: Linux package (Debian/Ubuntu, Fedora/RHEL, Alpine)
53+
### Option D: Linux package (Debian/Ubuntu, Fedora/RHEL, Alpine)
4154
```bash
4255
# Download the matching package from https://github.com/basecamp/basecamp-cli/releases/latest
4356
sudo apt install ./basecamp-cli_*_linux_amd64.deb # Debian/Ubuntu
@@ -46,17 +59,17 @@ sudo apk add --allow-untrusted ./basecamp-cli_*_linux_amd64.apk # Alpine
4659
```
4760
Arm64: substitute `arm64` for `amd64` in the filename. Verify the SHA-256 checksum from `checksums.txt` before installing unsigned Alpine packages.
4861

49-
### Option D: Nix
62+
### Option E: Nix
5063
```bash
5164
nix profile install github:basecamp/basecamp-cli
5265
```
5366

54-
### Option E: Go install
67+
### Option F: Go install
5568
```bash
5669
go install github.com/basecamp/basecamp-cli/cmd/basecamp@latest
5770
```
5871

59-
### Option F: GitHub Release
72+
### Option G: GitHub Release
6073
Download the archive for your platform from [Releases](https://github.com/basecamp/basecamp-cli/releases), extract, and move `basecamp` to a directory on your PATH.
6174

6275
**Verify:**
@@ -65,9 +78,10 @@ basecamp --version
6578
# Expected: basecamp version X.Y.Z
6679
```
6780

68-
If `basecamp: command not found`, add to PATH:
81+
If `basecamp: command not found`, add it to PATH:
6982
```bash
70-
export PATH="$HOME/.local/bin:$PATH"
83+
export PATH="$HOME/.local/bin:$PATH" # macOS / Linux / WSL
84+
export PATH="$HOME/bin:$PATH" # Git Bash / Windows bash environments
7185
# or for go install:
7286
export PATH="$HOME/go/bin:$PATH"
7387
```

scripts/ensure-basecamp.sh

Lines changed: 118 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,42 @@ set -euo pipefail
1111

1212
MIN_VERSION="${BASECAMP_MIN_VERSION:-0.1.0}"
1313
INSTALL_URL="https://github.com/basecamp/basecamp-cli"
14-
BIN_DIR="${BASECAMP_BIN_DIR:-$HOME/.local/bin}"
14+
BIN_DIR="${BASECAMP_BIN_DIR:-}"
15+
CURL_SCHANNEL_FALLBACK_FLAG=""
16+
CURL_LAST_ERROR=""
17+
CURL_FALLBACK_NOTED=0
1518

1619
# Parse semver: returns 0 if $1 >= $2
1720
version_gte() {
1821
local v1="$1" v2="$2"
1922
printf '%s\n%s\n' "$v2" "$v1" | sort -V | head -1 | grep -qx "$v2"
2023
}
2124

25+
path_contains_dir() {
26+
local dir="$1"
27+
[[ ":$PATH:" == *":$dir:"* ]]
28+
}
29+
30+
default_bin_dir() {
31+
local platform="$1"
32+
33+
if path_contains_dir "$HOME/bin"; then
34+
echo "$HOME/bin"
35+
return 0
36+
fi
37+
38+
if path_contains_dir "$HOME/.local/bin"; then
39+
echo "$HOME/.local/bin"
40+
return 0
41+
fi
42+
43+
if [[ "$platform" == windows_* ]]; then
44+
echo "$HOME/bin"
45+
else
46+
echo "$HOME/.local/bin"
47+
fi
48+
}
49+
2250
check_basecamp() {
2351
if ! command -v basecamp &>/dev/null; then
2452
echo "basecamp not found in PATH"
@@ -60,23 +88,100 @@ detect_platform() {
6088
echo "${os}_${arch}"
6189
}
6290

91+
detect_curl_fallback() {
92+
local version_output help_output
93+
94+
version_output=$(curl --version 2>/dev/null || true)
95+
if [[ "$version_output" != *[Ss]channel* ]]; then
96+
return 0
97+
fi
98+
99+
help_output=$(curl --help all 2>/dev/null || true)
100+
if [[ "$help_output" == *"--ssl-revoke-best-effort"* ]]; then
101+
CURL_SCHANNEL_FALLBACK_FLAG="--ssl-revoke-best-effort"
102+
elif [[ "$help_output" == *"--ssl-no-revoke"* ]]; then
103+
CURL_SCHANNEL_FALLBACK_FLAG="--ssl-no-revoke"
104+
fi
105+
}
106+
107+
curl_run() {
108+
local err_file status err
109+
err_file=$(mktemp "${TMPDIR:-/tmp}/basecamp-curl.XXXXXX")
110+
111+
if curl "$@" 2>"$err_file"; then
112+
rm -f "$err_file"
113+
CURL_LAST_ERROR=""
114+
return 0
115+
else
116+
status=$?
117+
fi
118+
119+
err=$(<"$err_file")
120+
rm -f "$err_file"
121+
122+
if [[ $status -ne 0 ]] && [[ -n "$CURL_SCHANNEL_FALLBACK_FLAG" ]] && [[ "$err" == *"CRYPT_E_NO_REVOCATION_CHECK"* ]]; then
123+
if [[ $CURL_FALLBACK_NOTED -eq 0 ]]; then
124+
echo "Retrying curl with ${CURL_SCHANNEL_FALLBACK_FLAG} because Windows certificate revocation checks are unavailable..." >&2
125+
CURL_FALLBACK_NOTED=1
126+
fi
127+
128+
err_file=$(mktemp "${TMPDIR:-/tmp}/basecamp-curl.XXXXXX")
129+
if curl "$CURL_SCHANNEL_FALLBACK_FLAG" "$@" 2>"$err_file"; then
130+
rm -f "$err_file"
131+
CURL_LAST_ERROR=""
132+
return 0
133+
else
134+
status=$?
135+
fi
136+
137+
err=$(<"$err_file")
138+
rm -f "$err_file"
139+
fi
140+
141+
CURL_LAST_ERROR="$err"
142+
return "$status"
143+
}
144+
145+
get_latest_version() {
146+
local url version api_json
147+
148+
if url=$(curl_run -fsSL -o /dev/null -w '%{url_effective}' "https://github.com/basecamp/basecamp-cli/releases/latest"); then
149+
version="${url##*/}"
150+
version="${version#v}"
151+
if [[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
152+
echo "$version"
153+
return 0
154+
fi
155+
fi
156+
157+
if api_json=$(curl_run -fsSL -H 'Accept: application/vnd.github+json' -H 'User-Agent: basecamp-cli-installer' "https://api.github.com/repos/basecamp/basecamp-cli/releases/latest"); then
158+
version="${api_json#*\"tag_name\":\"v}"
159+
version="${version%%\"*}"
160+
if [[ $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
161+
echo "$version"
162+
return 0
163+
fi
164+
fi
165+
166+
echo "Could not determine latest version${CURL_LAST_ERROR:+ ($CURL_LAST_ERROR)}" >&2
167+
return 1
168+
}
169+
63170
install_basecamp() {
64171
echo "Installing basecamp..."
65172

66173
local platform version url archive_name ext tmp_dir
67174

68175
# Get platform
69176
platform=$(detect_platform) || return 1
177+
detect_curl_fallback
70178

71-
# Get latest version via redirect (avoids GitHub API rate limits and grep/sed on Windows)
72-
url=$(curl -fsSL -o /dev/null -w '%{url_effective}' "https://github.com/basecamp/basecamp-cli/releases/latest" 2>/dev/null) || true
73-
version="${url##*/}"
74-
version="${version#v}"
75-
if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
76-
echo "Could not determine latest version (resolved '${version:-<empty>}' from '${url:-<no URL>}')" >&2
77-
return 1
179+
if [[ -z "$BIN_DIR" ]]; then
180+
BIN_DIR=$(default_bin_dir "$platform")
78181
fi
79182

183+
version=$(get_latest_version) || return 1
184+
80185
# Determine archive extension
81186
if [[ "$platform" == windows_* ]]; then
82187
ext="zip"
@@ -90,10 +195,10 @@ install_basecamp() {
90195
echo "Downloading basecamp v${version} for ${platform}..."
91196

92197
tmp_dir=$(mktemp -d)
93-
trap 'rm -rf "$tmp_dir"' EXIT
198+
trap "rm -rf '${tmp_dir}'" EXIT
94199

95-
if ! curl -fsSL "$url" -o "${tmp_dir}/${archive_name}"; then
96-
echo "Failed to download from $url" >&2
200+
if ! curl_run -fsSL "$url" -o "${tmp_dir}/${archive_name}"; then
201+
echo "Failed to download from $url${CURL_LAST_ERROR:+ ($CURL_LAST_ERROR)}" >&2
97202
return 1
98203
fi
99204

@@ -121,7 +226,7 @@ install_basecamp() {
121226
if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
122227
echo ""
123228
echo "Add to your shell profile:"
124-
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
229+
echo " export PATH=\"$BIN_DIR:\$PATH\""
125230
fi
126231
}
127232

@@ -146,7 +251,7 @@ Usage:
146251
147252
Environment:
148253
BASECAMP_MIN_VERSION Minimum required version (default: $MIN_VERSION)
149-
BASECAMP_BIN_DIR Binary directory (default: ~/.local/bin)
254+
BASECAMP_BIN_DIR Binary directory (default: ~/.local/bin, or ~/bin on Windows)
150255
EOF
151256
;;
152257
*)

0 commit comments

Comments
 (0)