@getmonoceros/workbench 1.21.2 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled-components/features/atlassian/component.yml +52 -0
- package/bundled-components/features/atlassian/install.sh +176 -0
- package/bundled-components/features/claude-code/component.yml +32 -0
- package/bundled-components/features/claude-code/install.sh +56 -0
- package/bundled-components/features/github-cli/component.yml +18 -0
- package/bundled-components/features/github-cli/install.sh +53 -0
- package/bundled-components/languages/dotnet/component.yml +8 -0
- package/bundled-components/languages/go/component.yml +8 -0
- package/bundled-components/languages/java/component.yml +17 -0
- package/bundled-components/languages/node/component.yml +9 -0
- package/bundled-components/languages/python/component.yml +8 -0
- package/bundled-components/languages/rust/component.yml +8 -0
- package/bundled-components/services/mysql/component.yml +40 -0
- package/bundled-components/services/postgres/component.yml +34 -0
- package/bundled-components/services/redis/component.yml +16 -0
- package/dist/bin.js +791 -369
- package/dist/bin.js.map +1 -1
- package/package.json +5 -4
- package/features/atlassian/devcontainer-feature.json +0 -67
- package/features/claude-code/devcontainer-feature.json +0 -49
- package/features/github-cli/devcontainer-feature.json +0 -32
- package/templates/components/README.md +0 -95
- package/templates/components/atlassian/rovodev.yml +0 -14
- package/templates/components/atlassian/twg.yml +0 -14
- package/templates/components/atlassian.yml +0 -15
- package/templates/components/claude.yml +0 -16
- package/templates/components/dotnet.yml +0 -8
- package/templates/components/github.yml +0 -10
- package/templates/components/go.yml +0 -8
- package/templates/components/java.yml +0 -9
- package/templates/components/mysql.yml +0 -8
- package/templates/components/node.yml +0 -9
- package/templates/components/postgres.yml +0 -10
- package/templates/components/python.yml +0 -9
- package/templates/components/redis.yml +0 -7
- package/templates/components/rust.yml +0 -8
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
id: atlassian
|
|
2
|
+
category: feature
|
|
3
|
+
displayName: Atlassian
|
|
4
|
+
description: 'Rovo Dev (`acli rovodev`) and Teamwork Graph (`twg`) CLIs sharing one Atlassian account. Auth persists across rebuilds.'
|
|
5
|
+
documentationURL: https://developer.atlassian.com/cloud/
|
|
6
|
+
options:
|
|
7
|
+
rovodev:
|
|
8
|
+
type: boolean
|
|
9
|
+
default: true
|
|
10
|
+
description: 'Install acli (with the Rovo Dev agent).'
|
|
11
|
+
surface: yml
|
|
12
|
+
twg:
|
|
13
|
+
type: boolean
|
|
14
|
+
default: true
|
|
15
|
+
description: 'Install twg (Teamwork Graph CLI).'
|
|
16
|
+
surface: yml
|
|
17
|
+
instance:
|
|
18
|
+
type: string
|
|
19
|
+
default: ''
|
|
20
|
+
description: 'Atlassian site host (`yoursite.atlassian.net`); required by twg.'
|
|
21
|
+
surface: env
|
|
22
|
+
email:
|
|
23
|
+
type: string
|
|
24
|
+
default: ''
|
|
25
|
+
description: 'Atlassian account email; used with `apiToken` for non-interactive login.'
|
|
26
|
+
surface: env
|
|
27
|
+
apiToken:
|
|
28
|
+
type: string
|
|
29
|
+
default: ''
|
|
30
|
+
description: 'Atlassian API token (id.atlassian.com → Security → API tokens).'
|
|
31
|
+
surface: env
|
|
32
|
+
bitbucketToken:
|
|
33
|
+
type: string
|
|
34
|
+
default: ''
|
|
35
|
+
description: "Optional Bitbucket app password; only needed for twg's Bitbucket commands."
|
|
36
|
+
surface: env
|
|
37
|
+
feature:
|
|
38
|
+
version: 1.0.0
|
|
39
|
+
persistentHomePaths: [.config/acli, .rovodev, .config/twg, .agents]
|
|
40
|
+
vscodeExtensions: [Atlassian.atlascode]
|
|
41
|
+
briefing:
|
|
42
|
+
- whenOption: rovodev
|
|
43
|
+
text: 'Atlassian Rovo Dev — invoke via `acli rovodev`. Pre-authenticated against the Atlassian account configured in the feature options (shared with twg).'
|
|
44
|
+
- whenOption: twg
|
|
45
|
+
text: 'Atlassian Teamwork Graph CLI (`twg`) — work-data access across Jira, Confluence, Bitbucket, JSM and Assets. Pre-authenticated. Sub-skills cover status rollups, engineering work, ops health and context discovery.'
|
|
46
|
+
presets:
|
|
47
|
+
twg:
|
|
48
|
+
rovodev: false
|
|
49
|
+
twg: true
|
|
50
|
+
rovodev:
|
|
51
|
+
rovodev: true
|
|
52
|
+
twg: false
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Monoceros devcontainer feature: atlassian.
|
|
3
|
+
#
|
|
4
|
+
# Installs Atlassian CLIs that share a single Atlassian account:
|
|
5
|
+
# - Rovo Dev (acli, default on) — the `acli rovodev` AI agent
|
|
6
|
+
# - Teamwork Graph (twg, default off)
|
|
7
|
+
#
|
|
8
|
+
# Each tool is toggled independently via the `rovodev` / `twg`
|
|
9
|
+
# options. Credentials (instance / email / apiToken) are shared
|
|
10
|
+
# across both tools when set; per-tool post-create hooks under
|
|
11
|
+
# /usr/local/share/monoceros/post-create.d/ perform the
|
|
12
|
+
# non-interactive login on first container start.
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
ROVODEV="${ROVODEV:-true}"
|
|
17
|
+
TWG="${TWG:-true}"
|
|
18
|
+
INSTANCE="${INSTANCE:-}"
|
|
19
|
+
EMAIL="${EMAIL:-}"
|
|
20
|
+
APITOKEN="${APITOKEN:-}"
|
|
21
|
+
BITBUCKETTOKEN="${BITBUCKETTOKEN:-}"
|
|
22
|
+
|
|
23
|
+
if [ "${ROVODEV}" != "true" ] && [ "${TWG}" != "true" ]; then
|
|
24
|
+
echo "[atlassian] both rovodev and twg disabled — nothing to install" >&2
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
ARCH="$(dpkg --print-architecture)"
|
|
29
|
+
case "${ARCH}" in
|
|
30
|
+
amd64 | arm64) ;;
|
|
31
|
+
*)
|
|
32
|
+
echo "[atlassian] unsupported architecture: ${ARCH}" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
;;
|
|
35
|
+
esac
|
|
36
|
+
|
|
37
|
+
POST_CREATE_DIR=/usr/local/share/monoceros/post-create.d
|
|
38
|
+
mkdir -p "${POST_CREATE_DIR}"
|
|
39
|
+
|
|
40
|
+
# ─── Rovo Dev (acli) ──────────────────────────────────────────────
|
|
41
|
+
if [ "${ROVODEV}" = "true" ]; then
|
|
42
|
+
ACLI_URL="https://acli.atlassian.com/linux/latest/acli_linux_${ARCH}/acli"
|
|
43
|
+
echo "[atlassian/rovodev] downloading acli for ${ARCH} from ${ACLI_URL}"
|
|
44
|
+
TMP="$(mktemp)"
|
|
45
|
+
curl -fsSL -o "${TMP}" "${ACLI_URL}"
|
|
46
|
+
install -o root -g root -m 0755 "${TMP}" /usr/local/bin/acli
|
|
47
|
+
rm -f "${TMP}"
|
|
48
|
+
acli --version >/dev/null 2>&1 || {
|
|
49
|
+
echo "[atlassian/rovodev] ERROR: install completed but \`acli\` is not on PATH" >&2
|
|
50
|
+
exit 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
HOOK="${POST_CREATE_DIR}/atlassian-rovodev.sh"
|
|
54
|
+
if [ -n "${EMAIL}" ] && [ -n "${APITOKEN}" ]; then
|
|
55
|
+
cat >"${HOOK}" <<EOF
|
|
56
|
+
#!/usr/bin/env bash
|
|
57
|
+
# Auto-generated by the Monoceros atlassian feature (rovodev).
|
|
58
|
+
# Runs every container start. Always re-runs auth login so that
|
|
59
|
+
# rotating the apiToken in the container yml propagates through on
|
|
60
|
+
# the next \`monoceros apply\` without needing to delete the
|
|
61
|
+
# previous credentials file by hand. acli's login is a single API
|
|
62
|
+
# call; the cost is negligible compared to the container build.
|
|
63
|
+
set -euo pipefail
|
|
64
|
+
echo "[atlassian/rovodev] performing non-interactive Rovo Dev login for ${EMAIL}"
|
|
65
|
+
echo '${APITOKEN}' | acli rovodev auth login --email '${EMAIL}' --token
|
|
66
|
+
echo "[atlassian/rovodev] auth login done — on first \\\`acli rovodev run\\\` you'll be asked for your Atlassian site once; the answer persists."
|
|
67
|
+
EOF
|
|
68
|
+
chmod 0755 "${HOOK}"
|
|
69
|
+
echo "[atlassian/rovodev] post-create login hook installed"
|
|
70
|
+
else
|
|
71
|
+
rm -f "${HOOK}"
|
|
72
|
+
echo "[atlassian/rovodev] no email/apiToken set — login is manual (\`acli rovodev auth login\` once in the container)"
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# ─── Teamwork Graph (twg) ─────────────────────────────────────────
|
|
77
|
+
if [ "${TWG}" = "true" ]; then
|
|
78
|
+
echo "[atlassian/twg] installing via official install script"
|
|
79
|
+
TMP="$(mktemp)"
|
|
80
|
+
curl -fsSL -o "${TMP}" https://teamwork-graph.atlassian.com/cli/install
|
|
81
|
+
# The official install script:
|
|
82
|
+
# - Downloads the binary; we pin install dir to /usr/local/bin so
|
|
83
|
+
# both root (build time) and node (runtime) have it on PATH.
|
|
84
|
+
# - Runs a `twg consent --source direct-public-installer` step
|
|
85
|
+
# that prompts on stdin. We feed it `yes` via a heredoc; the
|
|
86
|
+
# extra blank lines are harmless padding in case a future
|
|
87
|
+
# version adds more prompts. (The recorded consent lands in
|
|
88
|
+
# /root/.config/twg/ which is not visible to the node user at
|
|
89
|
+
# runtime — we re-record it as node in the post-create hook
|
|
90
|
+
# below.)
|
|
91
|
+
# - With --skip-login / --skip-skills we keep the install
|
|
92
|
+
# non-interactive. Login + skills happen later as the node user
|
|
93
|
+
# so they persist into the bind-mounted home.
|
|
94
|
+
#
|
|
95
|
+
# Heredoc (not `yes ... |`) deliberately: a long-lived `yes` left
|
|
96
|
+
# writing into a closed pipe after the install script exits gets
|
|
97
|
+
# SIGPIPE'd, and our own `set -o pipefail` would then propagate
|
|
98
|
+
# that 141 as a feature-install failure even though the install
|
|
99
|
+
# actually succeeded.
|
|
100
|
+
bash "${TMP}" \
|
|
101
|
+
--install-dir /usr/local/bin \
|
|
102
|
+
--skip-login \
|
|
103
|
+
--skip-skills \
|
|
104
|
+
<<'TWG_INSTALL_INPUT'
|
|
105
|
+
yes
|
|
106
|
+
yes
|
|
107
|
+
yes
|
|
108
|
+
TWG_INSTALL_INPUT
|
|
109
|
+
rm -f "${TMP}"
|
|
110
|
+
|
|
111
|
+
# The install script's mktemp + chmod +x leaves twg-bin at 0700
|
|
112
|
+
# (only root can execute it). Re-chmod so the node user can run it.
|
|
113
|
+
if [ -f /usr/local/bin/twg-bin ]; then
|
|
114
|
+
chmod 0755 /usr/local/bin/twg-bin
|
|
115
|
+
fi
|
|
116
|
+
if [ -f /usr/local/bin/twg ]; then
|
|
117
|
+
chmod 0755 /usr/local/bin/twg
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
twg --version >/dev/null 2>&1 || {
|
|
121
|
+
echo "[atlassian/twg] ERROR: install completed but \`twg\` is not on PATH" >&2
|
|
122
|
+
exit 1
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
HOOK="${POST_CREATE_DIR}/atlassian-twg.sh"
|
|
126
|
+
if [ -n "${INSTANCE}" ] && [ -n "${EMAIL}" ] && [ -n "${APITOKEN}" ]; then
|
|
127
|
+
cat >"${HOOK}" <<EOF
|
|
128
|
+
#!/usr/bin/env bash
|
|
129
|
+
# Auto-generated by the Monoceros atlassian feature (twg).
|
|
130
|
+
# Runs every container start. Always re-runs auth login so that
|
|
131
|
+
# rotating the apiToken / bitbucketToken in the container yml
|
|
132
|
+
# propagates through on the next \`monoceros apply\` without
|
|
133
|
+
# needing to delete the previous credentials file by hand. twg's
|
|
134
|
+
# login is a single API call; the cost is negligible compared to
|
|
135
|
+
# the container build.
|
|
136
|
+
set -euo pipefail
|
|
137
|
+
mkdir -p "\${HOME}/.config/twg"
|
|
138
|
+
echo "[atlassian/twg] configuring twg non-interactively for ${EMAIL} @ ${INSTANCE}"
|
|
139
|
+
# Notes on the non-interactive login dance:
|
|
140
|
+
# - We don't try to re-record consent here as the node user. twg's
|
|
141
|
+
# \`consent\` subcommand demands a TTY; the install-time consent
|
|
142
|
+
# (recorded as root during the feature build) appears to be
|
|
143
|
+
# enough for \`twg login\` itself.
|
|
144
|
+
# - \`twg login --force\` reads Atlassian creds from TWG_USER /
|
|
145
|
+
# TWG_SITE / TWG_TOKEN. If TWG_BBC_TOKEN is set, twg uses that
|
|
146
|
+
# directly and skips the Bitbucket prompt. If empty, twg prompts
|
|
147
|
+
# interactively — we feed two blank lines via heredoc so the
|
|
148
|
+
# "Enter = skip" branch fires and we don't hang. Future twg
|
|
149
|
+
# versions adding another optional prompt are also covered by
|
|
150
|
+
# the second blank line.
|
|
151
|
+
TWG_USER='${EMAIL}' \\
|
|
152
|
+
TWG_SITE='${INSTANCE}' \\
|
|
153
|
+
TWG_TOKEN='${APITOKEN}' \\
|
|
154
|
+
TWG_BBC_TOKEN='${BITBUCKETTOKEN}' \\
|
|
155
|
+
twg login --force <<'TWG_LOGIN_INPUT'
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
TWG_LOGIN_INPUT
|
|
159
|
+
|
|
160
|
+
# (Re-)install twg's agent skills. Canonical install lands in
|
|
161
|
+
# /home/node/.agents/skills/twg (persisted via the feature's
|
|
162
|
+
# persistentHomePaths); per-agent wrapper scripts go into agent-
|
|
163
|
+
# specific home dirs (.claude/skills/twg, .codex/skills/twg, …)
|
|
164
|
+
# and are regenerated on each apply when those tools are present.
|
|
165
|
+
echo "[atlassian/twg] (re-)installing twg skills"
|
|
166
|
+
twg skills install --global --yes
|
|
167
|
+
EOF
|
|
168
|
+
chmod 0755 "${HOOK}"
|
|
169
|
+
echo "[atlassian/twg] post-create login hook installed"
|
|
170
|
+
else
|
|
171
|
+
rm -f "${HOOK}"
|
|
172
|
+
echo "[atlassian/twg] missing instance/email/apiToken — login is manual (\`twg login\` once in the container)"
|
|
173
|
+
fi
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
echo "[atlassian] done"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
id: claude-code
|
|
2
|
+
name: claude
|
|
3
|
+
category: feature
|
|
4
|
+
displayName: Claude Code
|
|
5
|
+
description: "Anthropic's CLI coding assistant. OAuth/subscription login persists across container rebuilds."
|
|
6
|
+
documentationURL: https://docs.anthropic.com/en/docs/claude-code
|
|
7
|
+
options:
|
|
8
|
+
version:
|
|
9
|
+
type: string
|
|
10
|
+
default: latest
|
|
11
|
+
description: 'npm-style version spec (`latest`, `^0.4`, `0.4.2`).'
|
|
12
|
+
surface: silent
|
|
13
|
+
apiKey:
|
|
14
|
+
type: string
|
|
15
|
+
default: ''
|
|
16
|
+
description: '`sk-ant-…` for API auth; empty for OAuth login on first run.'
|
|
17
|
+
surface: env
|
|
18
|
+
permissionMode:
|
|
19
|
+
type: string
|
|
20
|
+
default: auto
|
|
21
|
+
proposals: [auto, ask, edits, bypass]
|
|
22
|
+
description: 'Default Claude permission mode in this container. `auto` = Auto Mode: no per-action prompts and no recurring warning (a background classifier vets actions) — the comfortable default for an isolated container. `ask` prompts as usual; `edits` auto-accepts file edits but prompts for other commands; `bypass` skips all prompts (its one-time warning is pre-accepted). Monoceros writes this to ~/.claude/settings.json on apply; override per-project or with a CLI flag.'
|
|
23
|
+
surface: yml
|
|
24
|
+
feature:
|
|
25
|
+
version: 1.2.0
|
|
26
|
+
persistentHomePaths: [.claude]
|
|
27
|
+
persistentHomeFiles:
|
|
28
|
+
- path: .claude.json
|
|
29
|
+
initialContent: "{}\n"
|
|
30
|
+
vscodeExtensions: [anthropic.claude-code]
|
|
31
|
+
briefing:
|
|
32
|
+
- text: 'Anthropic Claude Code CLI (`claude`) — interactive coding assistant. Authenticated via OAuth/subscription unless `apiKey` was set, in which case it runs in API-key mode.'
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Monoceros devcontainer feature: claude-code.
|
|
3
|
+
#
|
|
4
|
+
# Installs the Anthropic Claude Code CLI globally via the npm registry.
|
|
5
|
+
# Login state lives at /home/node/.claude, which Monoceros bind-mounts
|
|
6
|
+
# from <container-dir>/home/.claude on the host so it survives apply
|
|
7
|
+
# rebuilds.
|
|
8
|
+
#
|
|
9
|
+
# If the optional `apiKey` option was passed in the container yml (or
|
|
10
|
+
# inherited from monoceros-config.yml defaults.features), this script
|
|
11
|
+
# writes a profile.d snippet that exports ANTHROPIC_API_KEY for every
|
|
12
|
+
# shell — Claude Code picks that up and skips the OAuth flow.
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
VERSION="${VERSION:-latest}"
|
|
17
|
+
APIKEY="${APIKEY:-}"
|
|
18
|
+
|
|
19
|
+
echo "[claude-code] installing @anthropic-ai/claude-code@${VERSION} (as node)"
|
|
20
|
+
|
|
21
|
+
# Install as the non-root `node` user, NOT root (the feature install
|
|
22
|
+
# script itself runs as root). The base image's npm global prefix
|
|
23
|
+
# (/usr/local/share/npm-global) is owned by `node`, so installing as
|
|
24
|
+
# node leaves the Claude package files node-owned too. That is exactly
|
|
25
|
+
# what lets Claude's runtime self-updater write to them and keep itself
|
|
26
|
+
# current between Monoceros `upgrade`s. Installing as root drops
|
|
27
|
+
# root-owned files into that prefix that the non-root runtime user can
|
|
28
|
+
# never overwrite — the perpetual "Auto-update failed: no write
|
|
29
|
+
# permission to npm prefix" nag, frozen at the build-time version. See
|
|
30
|
+
# ADR 0018.
|
|
31
|
+
runuser -u node -- bash -lc \
|
|
32
|
+
"npm install -g --no-audit --no-fund '@anthropic-ai/claude-code@${VERSION}'"
|
|
33
|
+
|
|
34
|
+
runuser -u node -- bash -lc 'claude --version' >/dev/null 2>&1 || {
|
|
35
|
+
echo "[claude-code] ERROR: install completed but \`claude\` is not on PATH" >&2
|
|
36
|
+
exit 1
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# When an API key is configured, drop a profile.d snippet so every
|
|
40
|
+
# login shell exports it. We deliberately don't use containerEnv: the
|
|
41
|
+
# value would land in /proc/<pid>/environ of every process; the
|
|
42
|
+
# profile.d route at least keeps it out of non-shell process env when
|
|
43
|
+
# the container starts services directly.
|
|
44
|
+
if [ -n "${APIKEY}" ]; then
|
|
45
|
+
cat >/etc/profile.d/claude-code-apikey.sh <<EOF
|
|
46
|
+
# Auto-generated by the Monoceros claude-code feature. Sets
|
|
47
|
+
# ANTHROPIC_API_KEY so Claude Code uses API auth instead of OAuth.
|
|
48
|
+
export ANTHROPIC_API_KEY='${APIKEY}'
|
|
49
|
+
EOF
|
|
50
|
+
chmod 0644 /etc/profile.d/claude-code-apikey.sh
|
|
51
|
+
echo "[claude-code] API key mode: ANTHROPIC_API_KEY wired via /etc/profile.d/"
|
|
52
|
+
else
|
|
53
|
+
echo "[claude-code] subscription/OAuth mode: run \`claude\` once interactively to log in"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
echo "[claude-code] done"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
id: github-cli
|
|
2
|
+
name: github
|
|
3
|
+
category: feature
|
|
4
|
+
displayName: GitHub CLI
|
|
5
|
+
description: 'The official `gh` CLI. Login persists across container rebuilds.'
|
|
6
|
+
documentationURL: https://cli.github.com/
|
|
7
|
+
options:
|
|
8
|
+
apiToken:
|
|
9
|
+
type: string
|
|
10
|
+
default: ''
|
|
11
|
+
description: 'GitHub PAT (scopes: repo, read:org, gist); empty for `gh auth login` on first run.'
|
|
12
|
+
surface: env
|
|
13
|
+
feature:
|
|
14
|
+
version: 1.0.0
|
|
15
|
+
persistentHomePaths: [.config/gh]
|
|
16
|
+
vscodeExtensions: [github.vscode-pull-request-github]
|
|
17
|
+
briefing:
|
|
18
|
+
- text: 'GitHub CLI (`gh`) — repo, PR, issue, gist operations against github.com. Pre-authenticated; `gh auth status` reflects the active account.'
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Monoceros devcontainer feature: github-cli.
|
|
3
|
+
#
|
|
4
|
+
# Installs the official GitHub CLI (`gh`) from cli.github.com's apt
|
|
5
|
+
# repository. When the `token` option is set, the value is exposed
|
|
6
|
+
# as GH_TOKEN via /etc/profile.d so every login shell sees an
|
|
7
|
+
# authenticated `gh`. Without a token, `gh` is installed but
|
|
8
|
+
# unauthenticated; the builder can run `gh auth login` once
|
|
9
|
+
# interactively and the resulting `~/.config/gh/` state is
|
|
10
|
+
# persisted via Monoceros' bind-mount.
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
APITOKEN="${APITOKEN:-}"
|
|
15
|
+
|
|
16
|
+
echo "[github-cli] installing gh from cli.github.com apt repo"
|
|
17
|
+
|
|
18
|
+
# Per https://cli.github.com/manual/installation (Debian/Ubuntu path).
|
|
19
|
+
type -p curl >/dev/null || {
|
|
20
|
+
apt-get update
|
|
21
|
+
apt-get install -y curl
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
|
25
|
+
| dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
|
|
26
|
+
chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
|
|
27
|
+
|
|
28
|
+
mkdir -p -m 755 /etc/apt/sources.list.d
|
|
29
|
+
ARCH="$(dpkg --print-architecture)"
|
|
30
|
+
echo "deb [arch=${ARCH} signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
|
31
|
+
> /etc/apt/sources.list.d/github-cli.list
|
|
32
|
+
|
|
33
|
+
apt-get update
|
|
34
|
+
apt-get install -y --no-install-recommends gh
|
|
35
|
+
|
|
36
|
+
gh --version >/dev/null 2>&1 || {
|
|
37
|
+
echo "[github-cli] ERROR: install completed but \`gh\` is not on PATH" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if [ -n "${APITOKEN}" ]; then
|
|
42
|
+
cat >/etc/profile.d/github-cli.sh <<EOF
|
|
43
|
+
# Auto-generated by the Monoceros github-cli feature. Exports
|
|
44
|
+
# GH_TOKEN so the GitHub CLI authenticates non-interactively.
|
|
45
|
+
export GH_TOKEN='${APITOKEN}'
|
|
46
|
+
EOF
|
|
47
|
+
chmod 0644 /etc/profile.d/github-cli.sh
|
|
48
|
+
echo "[github-cli] apiToken wired via /etc/profile.d/ → \`gh\` authenticated in login shells"
|
|
49
|
+
else
|
|
50
|
+
echo "[github-cli] no apiToken set — run \`gh auth login\` once in the container; auth state persists under ~/.config/gh"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
echo "[github-cli] done"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
id: dotnet
|
|
2
|
+
category: language
|
|
3
|
+
displayName: .NET
|
|
4
|
+
description: 'The .NET SDK via the upstream devcontainer feature. Pin a version with `dotnet:<version>`.'
|
|
5
|
+
language:
|
|
6
|
+
feature: ghcr.io/devcontainers/features/dotnet:2
|
|
7
|
+
defaultVersion: latest
|
|
8
|
+
versions: [latest, lts, 10.0, 9.0, 8.0, 7.0, 6.0]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
id: go
|
|
2
|
+
category: language
|
|
3
|
+
displayName: Go
|
|
4
|
+
description: 'The Go toolchain plus golangci-lint via the upstream devcontainer feature. Pin a version with `go:<version>`.'
|
|
5
|
+
language:
|
|
6
|
+
feature: ghcr.io/devcontainers/features/go:1
|
|
7
|
+
defaultVersion: latest
|
|
8
|
+
versions: [latest, 1.24, 1.23]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
id: java
|
|
2
|
+
category: language
|
|
3
|
+
displayName: Java
|
|
4
|
+
description: 'A JDK plus Maven and Gradle by default (the upstream feature ships only the JDK), so a plain `languages: [java]` is build-ready. Pin a JDK major with `java:<version>`.'
|
|
5
|
+
language:
|
|
6
|
+
feature: ghcr.io/devcontainers/features/java:1
|
|
7
|
+
defaultVersion: latest
|
|
8
|
+
versions: [latest, 21, 17, 11, 8]
|
|
9
|
+
options:
|
|
10
|
+
installMaven:
|
|
11
|
+
type: boolean
|
|
12
|
+
default: true
|
|
13
|
+
surface: yml
|
|
14
|
+
installGradle:
|
|
15
|
+
type: boolean
|
|
16
|
+
default: true
|
|
17
|
+
surface: yml
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
id: node
|
|
2
|
+
category: language
|
|
3
|
+
displayName: Node.js
|
|
4
|
+
description: 'Node.js, built into the runtime image. A bare `node` installs nothing extra; pin a different major with `node:<version>`.'
|
|
5
|
+
language:
|
|
6
|
+
feature: ghcr.io/devcontainers/features/node:1
|
|
7
|
+
builtin: true
|
|
8
|
+
defaultVersion: 22
|
|
9
|
+
versions: [lts, latest, 22, 20]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
id: python
|
|
2
|
+
category: language
|
|
3
|
+
displayName: Python
|
|
4
|
+
description: 'CPython plus pip and common dev tooling via the upstream devcontainer feature. Pin a version with `python:<version>`.'
|
|
5
|
+
language:
|
|
6
|
+
feature: ghcr.io/devcontainers/features/python:1
|
|
7
|
+
defaultVersion: latest
|
|
8
|
+
versions: [latest, 3.12, 3.11, 3.10, 3.9, 3.8]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
id: rust
|
|
2
|
+
category: language
|
|
3
|
+
displayName: Rust
|
|
4
|
+
description: 'rustup plus the stable toolchain (rust-analyzer, rust-src, rustfmt, clippy) via the upstream feature. Pin a version with `rust:<version>`.'
|
|
5
|
+
language:
|
|
6
|
+
feature: ghcr.io/devcontainers/features/rust:1
|
|
7
|
+
defaultVersion: latest
|
|
8
|
+
versions: [latest, 1.87]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
id: mysql
|
|
2
|
+
category: service
|
|
3
|
+
displayName: MySQL
|
|
4
|
+
description: 'MySQL with seeded dev credentials and a readiness healthcheck. Reachable in-container as host `mysql`.'
|
|
5
|
+
service:
|
|
6
|
+
image: mysql:8
|
|
7
|
+
defaultPort: 3306
|
|
8
|
+
dataMount: /var/lib/mysql
|
|
9
|
+
healthcheck:
|
|
10
|
+
test:
|
|
11
|
+
[
|
|
12
|
+
CMD,
|
|
13
|
+
mysqladmin,
|
|
14
|
+
ping,
|
|
15
|
+
-h,
|
|
16
|
+
127.0.0.1,
|
|
17
|
+
-u,
|
|
18
|
+
root,
|
|
19
|
+
'-p${MYSQL_ROOT_PASSWORD}',
|
|
20
|
+
]
|
|
21
|
+
interval: 10s
|
|
22
|
+
timeout: 5s
|
|
23
|
+
retries: 5
|
|
24
|
+
vscodeExtensions: [cweijan.vscode-database-client2]
|
|
25
|
+
connectionEnv:
|
|
26
|
+
MYSQL_HOST: ${host}
|
|
27
|
+
MYSQL_PORT: ${port}
|
|
28
|
+
MYSQL_USER: root
|
|
29
|
+
MYSQL_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
|
30
|
+
MYSQL_DATABASE: ${MYSQL_DATABASE}
|
|
31
|
+
DATABASE_URL: mysql://root:${MYSQL_ROOT_PASSWORD}@${host}:${port}/${MYSQL_DATABASE}
|
|
32
|
+
options:
|
|
33
|
+
MYSQL_ROOT_PASSWORD:
|
|
34
|
+
type: string
|
|
35
|
+
default: monoceros
|
|
36
|
+
surface: env
|
|
37
|
+
MYSQL_DATABASE:
|
|
38
|
+
type: string
|
|
39
|
+
default: monoceros
|
|
40
|
+
surface: env
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
id: postgres
|
|
2
|
+
category: service
|
|
3
|
+
displayName: PostgreSQL
|
|
4
|
+
description: 'PostgreSQL with seeded dev credentials and a readiness healthcheck. Reachable in-container as host `postgres`.'
|
|
5
|
+
service:
|
|
6
|
+
image: postgres:18
|
|
7
|
+
defaultPort: 5432
|
|
8
|
+
dataMount: /var/lib/postgresql
|
|
9
|
+
healthcheck:
|
|
10
|
+
test: [CMD, pg_isready, -U, '${POSTGRES_USER}', -d, '${POSTGRES_DB}']
|
|
11
|
+
interval: 10s
|
|
12
|
+
timeout: 5s
|
|
13
|
+
retries: 5
|
|
14
|
+
vscodeExtensions: [cweijan.vscode-database-client2]
|
|
15
|
+
connectionEnv:
|
|
16
|
+
PGHOST: ${host}
|
|
17
|
+
PGPORT: ${port}
|
|
18
|
+
PGUSER: ${POSTGRES_USER}
|
|
19
|
+
PGPASSWORD: ${POSTGRES_PASSWORD}
|
|
20
|
+
PGDATABASE: ${POSTGRES_DB}
|
|
21
|
+
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${host}:${port}/${POSTGRES_DB}
|
|
22
|
+
options:
|
|
23
|
+
POSTGRES_USER:
|
|
24
|
+
type: string
|
|
25
|
+
default: monoceros
|
|
26
|
+
surface: env
|
|
27
|
+
POSTGRES_PASSWORD:
|
|
28
|
+
type: string
|
|
29
|
+
default: monoceros
|
|
30
|
+
surface: env
|
|
31
|
+
POSTGRES_DB:
|
|
32
|
+
type: string
|
|
33
|
+
default: monoceros
|
|
34
|
+
surface: env
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: redis
|
|
2
|
+
category: service
|
|
3
|
+
displayName: Redis
|
|
4
|
+
description: 'Redis on the default port with a readiness healthcheck. Reachable in-container as host `redis`.'
|
|
5
|
+
service:
|
|
6
|
+
image: redis:8
|
|
7
|
+
defaultPort: 6379
|
|
8
|
+
dataMount: /data
|
|
9
|
+
healthcheck:
|
|
10
|
+
test: [CMD, redis-cli, ping]
|
|
11
|
+
interval: 10s
|
|
12
|
+
timeout: 5s
|
|
13
|
+
retries: 5
|
|
14
|
+
vscodeExtensions: [cweijan.vscode-database-client2]
|
|
15
|
+
connectionEnv:
|
|
16
|
+
REDIS_URL: redis://${host}:${port}
|