polats Claude Opus 5 (1M context) commited on
Commit
edf5b8c
·
1 Parent(s): 63ac17f

Load Codespaces secrets before starting the server

Browse files

A codespace came up serving nothing. The image was fine and the hook did run, but
the log was empty and OPENCODE_SERVER_PASSWORD was absent from the environment.

Codespaces secrets are not passed to postStartCommand. They are written to
/workspaces/.codespaces/shared/user-secrets-envs.json and applied to interactive
shells, so a server started by a lifecycle hook sees none of them — and
entrypoint.sh then refuses to run, exactly as it should, leaving a public port
with nothing behind it.

start-opencode.sh reads that file first, without overriding anything
devcontainer.json set explicitly, and says so plainly when the password is still
missing rather than failing silently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

.devcontainer/devcontainer.json CHANGED
@@ -53,12 +53,12 @@
53
  },
54
 
55
  // Runs on create *and* on every resume from an idle stop, which is what makes the codespace
56
- // behave like a server rather than a dev box you have to log into and restart by hand.
57
- // Backgrounded so the codespace can finish starting; the log is where a refused boot
58
- // (usually a missing password) explains itself.
59
- // setsid detaches the server from the shell that postStartCommand runs in, which exits as
60
- // soon as it returns; a plain background job is not guaranteed to outlive it.
61
- "postStartCommand": "setsid nohup /home/node/entrypoint.sh > /tmp/opencode.log 2>&1 < /dev/null & echo started",
62
 
63
  "customizations": {
64
  "vscode": {
 
53
  },
54
 
55
  // Runs on create *and* on every resume from an idle stop, which is what makes the codespace
56
+ // behave like a server rather than a dev box you have to restart by hand.
57
+ //
58
+ // It goes through start-opencode.sh rather than straight to the entrypoint because
59
+ // Codespaces secrets are not in this hook's environment — see that script. setsid detaches
60
+ // the server from the shell, which exits as soon as the hook returns.
61
+ "postStartCommand": "setsid nohup ${containerWorkspaceFolder}/.devcontainer/start-opencode.sh > /tmp/opencode.log 2>&1 < /dev/null & echo started",
62
 
63
  "customizations": {
64
  "vscode": {
.devcontainer/start-opencode.sh ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Boot the opencode server inside a GitHub Codespace.
3
+ #
4
+ # This exists for one reason: Codespaces secrets are not in postStartCommand's environment.
5
+ # They are written to a JSON file and applied to interactive shells, so a server started by
6
+ # the lifecycle hook sees none of them — including OPENCODE_SERVER_PASSWORD, without which
7
+ # entrypoint.sh correctly refuses to run and the codespace comes up serving nothing.
8
+ #
9
+ # Sourcing that file first is what makes the codespace behave like the Space and the Railway
10
+ # service, where the platform hands the process its variables directly.
11
+ set -euo pipefail
12
+
13
+ SECRETS="/workspaces/.codespaces/shared/user-secrets-envs.json"
14
+
15
+ if [ -f "$SECRETS" ]; then
16
+ # A flat { "NAME": "value" } object. Exported only if not already set, so a variable given
17
+ # explicitly by devcontainer.json still wins.
18
+ while IFS='=' read -r name value; do
19
+ [ -n "$name" ] || continue
20
+ if [ -z "${!name:-}" ]; then
21
+ export "$name=$value"
22
+ fi
23
+ done < <(python3 -c '
24
+ import json, sys
25
+ with open(sys.argv[1]) as handle:
26
+ for name, value in json.load(handle).items():
27
+ if isinstance(value, str) and "\n" not in value:
28
+ print(f"{name}={value}")
29
+ ' "$SECRETS")
30
+ echo "codespaces: loaded secrets from $(basename "$SECRETS")"
31
+ else
32
+ echo "codespaces: no secrets file at $SECRETS"
33
+ fi
34
+
35
+ if [ -z "${OPENCODE_SERVER_PASSWORD:-}" ]; then
36
+ echo "codespaces: OPENCODE_SERVER_PASSWORD is still unset after loading secrets." >&2
37
+ echo " Set it as a Codespaces secret, not a repository or Actions secret:" >&2
38
+ echo " gh secret set OPENCODE_SERVER_PASSWORD --app codespaces --repo OWNER/REPO" >&2
39
+ fi
40
+
41
+ exec /home/node/entrypoint.sh