Skip to content

Commit 9bd6e65

Browse files
committed
fix(native-installer): POSIX compat, bash installer, onboarding hook
- Replace &>/dev/null with >/dev/null 2>&1 (POSIX) - Require curl only (not wget) — matches official installer dependency - Use bash instead of sh for piping installer (it requires bash) - Quote ${TARGET} in su -c to prevent word splitting - Pre-create ~/.local/state and ~/.claude directories - Add 99-claude-onboarding.sh post-start hook to ensure hasCompletedOnboarding is set when token auth is configured
1 parent 2b599ac commit 9bd6e65

File tree

2 files changed

+52
-4
lines changed

2 files changed

+52
-4
lines changed

.devcontainer/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
#### Claude Code Installation
8+
- **Post-start onboarding hook** (`99-claude-onboarding.sh`) — ensures `hasCompletedOnboarding: true` in `.claude.json` when token auth is configured; catches overwrites from Claude Code CLI/extension that race with `postStartCommand`
9+
510
### Changed
611

712
#### Claude Code Installation
@@ -14,6 +19,10 @@
1419
- **Update script no longer silently discards errors** — background update output now captured to log file instead of being discarded via `&>/dev/null`
1520
- **Update script simplified to native-binary-only** — removed npm fallback and `claude install` bootstrap code; added 60s timeout and transitional npm cleanup
1621
- **Alias resolution simplified**`_CLAUDE_BIN` now resolves directly to native binary path (removed npm and `/usr/local/bin` fallbacks)
22+
- **POSIX redirect** — replaced `&>/dev/null` with `>/dev/null 2>&1` in dependency check for portability
23+
- **Installer shell** — changed `sh -s` to `bash -s` when piping the official installer (it requires bash)
24+
- **Unquoted `${TARGET}`** — quoted variable in `su -c` command to prevent word splitting
25+
- **Directory prep** — added `~/.local/state` and `~/.claude` pre-creation; consolidated `chown` to cover entire `~/.local` tree
1726

1827
## [v1.14.2] - 2026-02-24
1928

.devcontainer/features/claude-code-native/install.sh

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ echo "[claude-code-native] Version: ${VERSION}"
1515

1616
# === VALIDATE DEPENDENCIES ===
1717
# The official installer (claude.ai/install.sh) requires curl internally
18-
if ! command -v curl &>/dev/null; then
18+
if ! command -v curl >/dev/null 2>&1; then
1919
echo "[claude-code-native] ERROR: curl is required"
2020
echo " Ensure common-utils feature is installed first"
2121
exit 1
@@ -47,7 +47,9 @@ echo "[claude-code-native] Installing for user: ${USERNAME} (home: ${USER_HOME})
4747
# === PREPARE DIRECTORIES ===
4848
mkdir -p "${USER_HOME}/.local/bin"
4949
mkdir -p "${USER_HOME}/.local/share/claude"
50-
chown -R "${USERNAME}:" "${USER_HOME}/.local/bin" "${USER_HOME}/.local/share/claude"
50+
mkdir -p "${USER_HOME}/.local/state"
51+
mkdir -p "${USER_HOME}/.claude"
52+
chown -R "${USERNAME}:" "${USER_HOME}/.local" "${USER_HOME}/.claude"
5153

5254
# === DETERMINE TARGET ===
5355
# The official installer accepts: stable, latest, or a specific semver
@@ -68,9 +70,9 @@ fi
6870
echo "[claude-code-native] Downloading official installer..."
6971

7072
if [ "${USERNAME}" = "root" ]; then
71-
curl -fsSL https://claude.ai/install.sh | sh -s -- "${TARGET}"
73+
curl -fsSL https://claude.ai/install.sh | bash -s -- "${TARGET}"
7274
else
73-
su - "${USERNAME}" -c "curl -fsSL https://claude.ai/install.sh | sh -s -- \"${TARGET}\""
75+
su - "${USERNAME}" -c "curl -fsSL https://claude.ai/install.sh | bash -s -- \"${TARGET}\""
7476
fi
7577

7678
# === VERIFICATION ===
@@ -87,4 +89,41 @@ else
8789
exit 1
8890
fi
8991

92+
# === POST-START HOOK ===
93+
# Ensures hasCompletedOnboarding is set when token auth is configured.
94+
# Runs as the LAST post-start hook (99- prefix) to catch overwrites from
95+
# Claude Code CLI/extension that may race with postStartCommand.
96+
HOOK_DIR="/usr/local/devcontainer-poststart.d"
97+
mkdir -p "$HOOK_DIR"
98+
cat > "$HOOK_DIR/99-claude-onboarding.sh" << 'HOOK_EOF'
99+
#!/bin/bash
100+
# Ensure hasCompletedOnboarding: true in .claude.json when token auth exists.
101+
# Runs after all setup scripts to catch any overwrites by Claude Code CLI/extension.
102+
_USERNAME="${SUDO_USER:-${USER:-vscode}}"
103+
_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6)
104+
_USER_HOME="${_USER_HOME:-/home/$_USERNAME}"
105+
CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}"
106+
CLAUDE_JSON="$CLAUDE_DIR/.claude.json"
107+
CRED_FILE="$CLAUDE_DIR/.credentials.json"
108+
109+
# Only act when token auth is configured
110+
[ -f "$CRED_FILE" ] || exit 0
111+
112+
if [ -f "$CLAUDE_JSON" ]; then
113+
if ! grep -q '"hasCompletedOnboarding"' "$CLAUDE_JSON" 2>/dev/null; then
114+
if command -v jq >/dev/null 2>&1; then
115+
jq '. + {"hasCompletedOnboarding": true}' "$CLAUDE_JSON" > "${CLAUDE_JSON}.tmp" && \
116+
mv "${CLAUDE_JSON}.tmp" "$CLAUDE_JSON"
117+
else
118+
sed -i '$ s/}$/,\n "hasCompletedOnboarding": true\n}/' "$CLAUDE_JSON"
119+
fi
120+
echo "[claude-onboarding] Injected hasCompletedOnboarding into .claude.json"
121+
fi
122+
else
123+
printf '{\n "hasCompletedOnboarding": true\n}\n' > "$CLAUDE_JSON"
124+
echo "[claude-onboarding] Created .claude.json with hasCompletedOnboarding"
125+
fi
126+
HOOK_EOF
127+
chmod +x "$HOOK_DIR/99-claude-onboarding.sh"
128+
90129
echo "[claude-code-native] Installation complete"

0 commit comments

Comments
 (0)