diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0f401fd..78ce31c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,8 @@ repos: files: Dockerfile$ - id: json-valid name: JSON syntax - entry: python3 -m json.tool + # json.tool takes [infile] [outfile]: never pass it several files. + entry: python3 -c 'import json, sys; [json.load(open(f)) for f in sys.argv[1:]]' language: system files: \.json$ - id: gitleaks diff --git a/README.md b/README.md index 8631c75..394e52a 100644 --- a/README.md +++ b/README.md @@ -72,11 +72,32 @@ resets the variable to its default. Create it once on each machine you release from (see `vars.example.yaml`): ```yaml -extra_git_hosts: # space-separated, added to known_hosts +extra_git_hosts: # space-separated, added to known_hosts +onedev_url: https:// # tod server URL (ONEDEV_SERVER_URL) ``` Every template must declare each variable in the file. +## OneDev CLI (tod) and Claude Code skills + +Every image has the OneDev CLI `tod` and its 8 agent skills, so Claude Code +can work with OneDev issues, pull requests and builds. + +- `tod` is built from a pinned tag (`ARG TOD_VERSION` / `TOD_COMMIT` in each + Dockerfile; the commit is checked). The official installer downloads the + latest binary without verification, so it is not used. +- The skills are installed in `/etc/claude-code/.claude/skills/`: managed, + loaded for every user, always the same version as the binary. +- Server URL: template variable `onedev_url` (`vars.yaml`), exported as + `ONEDEV_SERVER_URL` only when set. Access token: each user's own, set with + `tod config set access-token` (stored in `~/.config/tod/config`, home volume). + No token in the templates or images. +- Claude Code policy: read commands are allowed; commands that change OneDev + ask first; `tod config set` and reading the tod config are denied. +- Version hold: OneDev here is 16.4.1, and tod 4.3.3+ needs 16.5.11. Renovate + keeps tod below 4.3.3 (`renovate.json`). Remove that rule after the server + upgrade. + ## Pull request checks `.github/workflows/checks.yml` runs on each pull request to `main` diff --git a/node-js/README.md b/node-js/README.md index 1b1a6fc..84544b6 100644 --- a/node-js/README.md +++ b/node-js/README.md @@ -19,8 +19,8 @@ Docker daemon, so you can run Postgres, Redis and other services with | Node.js | Node LTS and pnpm through [mise](https://mise.jdx.dev) | | Docker | Docker Engine, Compose and Buildx **inside** the workspace | | Editor | VS Code (web) with the Claude Code extension | -| AI | Claude Code CLI (`claude`) | -| Git | `git`, GitHub CLI (`gh`), SSH through Coder | +| AI | Claude Code CLI (`claude`), with skills for OneDev | +| Git | `git`, GitHub CLI (`gh`), OneDev CLI (`tod`), SSH through Coder | | Other | `build-essential`, `python3` (node-gyp), `ripgrep`, `fd`, `jq`, `tmux`, `htop` | `NEXT_TELEMETRY_DISABLED=1` is set in the image. @@ -45,7 +45,12 @@ in the repo wins over the parameter. copy private keys into the workspace. 2. **Log in to Claude Code once.** Open a terminal and run `claude`. The login is kept in `~/.claude`. The VS Code extension uses the same login. -3. **Run the app.** Start the dev server on port 3000 (`pnpm dev`), then open +3. **Connect `tod` to OneDev (optional).** In OneDev, create an access token + (your avatar → **Access Tokens**), then run + `tod config set access-token `. The server URL is already set. Test + it with `tod project list`. Claude Code can then work with OneDev issues, + pull requests and builds through its `tod` skills. It asks before any change. +4. **Run the app.** Start the dev server on port 3000 (`pnpm dev`), then open the **Preview :3000** app in the Coder dashboard. ## What persists diff --git a/node-js/build/Dockerfile b/node-js/build/Dockerfile index 0a4f96f..08e48e3 100644 --- a/node-js/build/Dockerfile +++ b/node-js/build/Dockerfile @@ -1,6 +1,21 @@ # "node" Coder workspace (Sysbox runtime): Node.js LTS / Next.js. # Node itself is installed by mise at start (home volume), not here. +# --- OneDev CLI (tod), built from a pinned tag ------------------------------ +# The official installer always downloads the latest binary, unverified. +# Here: clone the tag, check its commit, build. Only the binary and the agent +# skills go into the final image. +FROM golang:1.27.0-bookworm AS tod +ARG TOD_VERSION=v4.3.2 +ARG TOD_COMMIT=9e280c96bf0f3e541706580c0830e5753dc5d28d +WORKDIR /src +RUN git clone --depth 1 --branch "${TOD_VERSION}" https://code.onedev.io/onedev/tod.git . \ + && test "$(git rev-parse HEAD)" = "${TOD_COMMIT}" \ + && CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/tod . \ + && mkdir -p /out/skills \ + && for d in skills/*/; do cp -r "$d" /out/skills/; done \ + && chmod -R u=rwX,go=rX /out + FROM ubuntu:24.04 SHELL ["/bin/bash", "-o", "pipefail", "-c"] @@ -75,6 +90,11 @@ RUN d=/usr/lib/code-server/lib/vscode/node_modules/shell-quote \ RUN install -d -m 0755 /etc/claude-code COPY --chmod=0644 managed-settings.json /etc/claude-code/managed-settings.json +# --- OneDev CLI (tod) and its Claude Code skills ---------------------------- +# Skills in /etc/claude-code/.claude/skills load for every user (managed). +COPY --from=tod /out/tod /usr/local/bin/tod +COPY --from=tod /out/skills /etc/claude-code/.claude/skills + # --- coder user ------------------------------------------- # UID 1000 must stay: existing home volumes are owned by 1000. RUN userdel -r ubuntu 2>/dev/null || true \ diff --git a/node-js/build/managed-settings.json b/node-js/build/managed-settings.json index 1b05ddd..778664a 100644 --- a/node-js/build/managed-settings.json +++ b/node-js/build/managed-settings.json @@ -1,9 +1,49 @@ { "permissions": { "disableBypassPermissionsMode": "disable", + "allow": [ + "Bash(tod version)", + "Bash(tod config get:*)", + "Bash(tod config path)", + "Bash(tod remote)", + "Bash(tod get-login-name:*)", + "Bash(tod get-valid-labels)", + "Bash(tod get-commit-message-requirement:*)", + "Bash(tod project:*)", + "Bash(tod issue list:*)", + "Bash(tod issue get:*)", + "Bash(tod issue current-reference)", + "Bash(tod pr list:*)", + "Bash(tod pr get:*)", + "Bash(tod pr current-reference)", + "Bash(tod build list:*)", + "Bash(tod build get:*)", + "Bash(tod build check-spec)" + ], + "ask": [ + "Bash(tod issue create:*)", + "Bash(tod issue edit:*)", + "Bash(tod issue change-state:*)", + "Bash(tod issue link:*)", + "Bash(tod issue add-comment:*)", + "Bash(tod issue log-work:*)", + "Bash(tod issue create-branch:*)", + "Bash(tod pr create:*)", + "Bash(tod pr edit:*)", + "Bash(tod pr approve:*)", + "Bash(tod pr request-changes:*)", + "Bash(tod pr merge:*)", + "Bash(tod pr discard:*)", + "Bash(tod pr add-comment:*)", + "Bash(tod pr add-code-comment:*)", + "Bash(tod code-comment:*)", + "Bash(tod build run:*)" + ], "deny": [ "Bash(sudo:*)", - "Read(/etc/claude-code/**)" + "Read(/etc/claude-code/*.json)", + "Bash(tod config set:*)", + "Read(~/.config/tod/**)" ] } -} \ No newline at end of file +} diff --git a/node-js/main.tf b/node-js/main.tf index 5b8821f..90fd64d 100644 --- a/node-js/main.tf +++ b/node-js/main.tf @@ -29,6 +29,17 @@ variable "extra_git_hosts" { } } +variable "onedev_url" { + description = "OneDev server URL for the tod CLI (e.g. https://onedev.example.com). Empty: each user sets it with tod config set." + type = string + default = "" + + validation { + condition = var.onedev_url == "" || can(regex("^https://[A-Za-z0-9.-]+(:[0-9]+)?/?$", var.onedev_url)) + error_message = "Use an https:// URL without a path, or leave it empty." + } +} + locals { template_slug = "node" preview_port = 3000 @@ -99,11 +110,12 @@ resource "coder_agent" "main" { arch = "amd64" os = "linux" - env = { + # ONEDEV_SERVER_URL only when set: tod treats an empty value as an override. + env = merge({ REPO_URL = data.coder_parameter.repo_url.value GIT_REF = data.coder_parameter.git_ref.value MISE_TOOLS = "${data.coder_parameter.node_version.value} ${data.coder_parameter.extra_mise_tools.value}" - } + }, var.onedev_url == "" ? {} : { ONEDEV_SERVER_URL = var.onedev_url }) # Git over SSH uses `coder gitssh` (GIT_SSH_COMMAND is set by the agent). # Add the Coder public key (`coder publickey`) once to GitHub and OneDev. @@ -313,7 +325,7 @@ resource "docker_image" "workspace" { # Built from build/ by GitHub Actions (.github/workflows/images.yml) and # pushed to GHCR. Not built on x1-hel1. Tags are immutable: a change under # build/ needs a new tag here. - name = "ghcr.io/moveyourdigital/coder-workspace-node:v0.6.2" + name = "ghcr.io/moveyourdigital/coder-workspace-node:v0.7.0" keep_locally = true } diff --git a/ops/README.md b/ops/README.md index 7718973..0b0a3db 100644 --- a/ops/README.md +++ b/ops/README.md @@ -31,7 +31,7 @@ their own workspace. Credentials stay in that workspace's home volume. | Network | Tailscale client, `mtr`, `tcpdump`, `nmap`, `iperf3`, `socat`, `whois`, `openssl` | apt | | Data | `borgbackup`, `postgresql-client`, `redis-tools` | apt | | Runbooks | `just` | mise | -| Base | Node LTS, `gh`, Claude Code, code-server | mise / apt | +| Base | Node LTS, `gh`, OneDev CLI (`tod`), Claude Code, code-server | mise / apt / image | mise tool versions are pinned in `main.tf` (`local.ops_tools`). @@ -84,12 +84,13 @@ docker --context swarm service ls Use `--context swarm` per command, or `docker context use swarm` with care. -### 4. Hetzner, Cloudflare, Coder +### 4. Hetzner, Cloudflare, Coder, OneDev ```bash hcloud context create # paste a project token wrangler login # or CLOUDFLARE_API_TOKEN in a local .env coder login https:// +tod config set access-token # OneDev: avatar → Access Tokens ``` Use tokens with the minimum scope for the task. @@ -103,6 +104,7 @@ Use tokens with the minimum scope for the task. | Tailscale node | `~/.local/share/tailscale/` | | SSH key to nodes | `~/.ssh/ops_ed25519` | | Coder session | `~/.config/coderv2/` | +| OneDev token (`tod`) | `~/.config/tod/config` | Do **not** store these as Coder user secrets: user secrets go to every workspace you own, including the coding workspaces. @@ -118,7 +120,8 @@ This template has a stricter managed policy than the coding templates: - **Asks first:** `terraform apply`, all `hcloud` commands, `docker stack deploy`, `docker service update/scale`, `docker node update`, `docker context use`, `ansible-playbook`, `wrangler deploy/secret`, `coder templates push`, - `ssh`, `scp`, `rsync`. + `ssh`, `scp`, `rsync`, and every `tod` command that changes OneDev + (same rules as the coding templates). The policy is a safety net, not a security boundary. Review every action that changes infrastructure. diff --git a/ops/build/Dockerfile b/ops/build/Dockerfile index 15579fa..0eeaa09 100644 --- a/ops/build/Dockerfile +++ b/ops/build/Dockerfile @@ -2,6 +2,21 @@ # Base = node template. CLI tools (hcloud, terraform, ...) are installed by # mise at start (pinned in main.tf). System tools are installed here. +# --- OneDev CLI (tod), built from a pinned tag ------------------------------ +# The official installer always downloads the latest binary, unverified. +# Here: clone the tag, check its commit, build. Only the binary and the agent +# skills go into the final image. +FROM golang:1.27.0-bookworm AS tod +ARG TOD_VERSION=v4.3.2 +ARG TOD_COMMIT=9e280c96bf0f3e541706580c0830e5753dc5d28d +WORKDIR /src +RUN git clone --depth 1 --branch "${TOD_VERSION}" https://code.onedev.io/onedev/tod.git . \ + && test "$(git rev-parse HEAD)" = "${TOD_COMMIT}" \ + && CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/tod . \ + && mkdir -p /out/skills \ + && for d in skills/*/; do cp -r "$d" /out/skills/; done \ + && chmod -R u=rwX,go=rX /out + FROM ubuntu:24.04 SHELL ["/bin/bash", "-o", "pipefail", "-c"] @@ -91,6 +106,11 @@ RUN d=/usr/lib/code-server/lib/vscode/node_modules/shell-quote \ RUN install -d -m 0755 /etc/claude-code COPY --chmod=0644 managed-settings.json /etc/claude-code/managed-settings.json +# --- OneDev CLI (tod) and its Claude Code skills ---------------------------- +# Skills in /etc/claude-code/.claude/skills load for every user (managed). +COPY --from=tod /out/tod /usr/local/bin/tod +COPY --from=tod /out/skills /etc/claude-code/.claude/skills + # --- coder user ------------------------------------------- # UID 1000 must stay: existing home volumes are owned by 1000. RUN userdel -r ubuntu 2>/dev/null || true \ diff --git a/ops/build/managed-settings.json b/ops/build/managed-settings.json index 85150ea..e5c77de 100644 --- a/ops/build/managed-settings.json +++ b/ops/build/managed-settings.json @@ -1,6 +1,59 @@ { "permissions": { "disableBypassPermissionsMode": "disable", + "allow": [ + "Bash(tod version)", + "Bash(tod config get:*)", + "Bash(tod config path)", + "Bash(tod remote)", + "Bash(tod get-login-name:*)", + "Bash(tod get-valid-labels)", + "Bash(tod get-commit-message-requirement:*)", + "Bash(tod project:*)", + "Bash(tod issue list:*)", + "Bash(tod issue get:*)", + "Bash(tod issue current-reference)", + "Bash(tod pr list:*)", + "Bash(tod pr get:*)", + "Bash(tod pr current-reference)", + "Bash(tod build list:*)", + "Bash(tod build get:*)", + "Bash(tod build check-spec)" + ], + "ask": [ + "Bash(terraform apply:*)", + "Bash(tofu apply:*)", + "Bash(hcloud:*)", + "Bash(docker stack deploy:*)", + "Bash(docker service update:*)", + "Bash(docker service scale:*)", + "Bash(docker node update:*)", + "Bash(docker context use:*)", + "Bash(ansible-playbook:*)", + "Bash(wrangler deploy:*)", + "Bash(wrangler secret:*)", + "Bash(coder templates push:*)", + "Bash(ssh:*)", + "Bash(scp:*)", + "Bash(rsync:*)", + "Bash(tod issue create:*)", + "Bash(tod issue edit:*)", + "Bash(tod issue change-state:*)", + "Bash(tod issue link:*)", + "Bash(tod issue add-comment:*)", + "Bash(tod issue log-work:*)", + "Bash(tod issue create-branch:*)", + "Bash(tod pr create:*)", + "Bash(tod pr edit:*)", + "Bash(tod pr approve:*)", + "Bash(tod pr request-changes:*)", + "Bash(tod pr merge:*)", + "Bash(tod pr discard:*)", + "Bash(tod pr add-comment:*)", + "Bash(tod pr add-code-comment:*)", + "Bash(tod code-comment:*)", + "Bash(tod build run:*)" + ], "deny": [ "Bash(sudo:*)", "Bash(terraform destroy:*)", @@ -24,31 +77,16 @@ "Bash(borg delete:*)", "Bash(borg prune:*)", "Bash(tailscale logout:*)", - "Read(/etc/claude-code/**)", + "Read(/etc/claude-code/*.json)", "Read(~/.ssh/**)", "Read(~/.config/hcloud/**)", "Read(~/.local/share/tailscale/**)", "Read(~/.wrangler/**)", "Read(~/.config/.wrangler/**)", "Read(~/.config/coderv2/**)", - "Read(~/.docker/config.json)" - ], - "ask": [ - "Bash(terraform apply:*)", - "Bash(tofu apply:*)", - "Bash(hcloud:*)", - "Bash(docker stack deploy:*)", - "Bash(docker service update:*)", - "Bash(docker service scale:*)", - "Bash(docker node update:*)", - "Bash(docker context use:*)", - "Bash(ansible-playbook:*)", - "Bash(wrangler deploy:*)", - "Bash(wrangler secret:*)", - "Bash(coder templates push:*)", - "Bash(ssh:*)", - "Bash(scp:*)", - "Bash(rsync:*)" + "Read(~/.docker/config.json)", + "Bash(tod config set:*)", + "Read(~/.config/tod/**)" ] } } diff --git a/ops/main.tf b/ops/main.tf index aebfd6b..3e6275d 100644 --- a/ops/main.tf +++ b/ops/main.tf @@ -29,6 +29,17 @@ variable "extra_git_hosts" { } } +variable "onedev_url" { + description = "OneDev server URL for the tod CLI (e.g. https://onedev.example.com). Empty: each user sets it with tod config set." + type = string + default = "" + + validation { + condition = var.onedev_url == "" || can(regex("^https://[A-Za-z0-9.-]+(:[0-9]+)?/?$", var.onedev_url)) + error_message = "Use an https:// URL without a path, or leave it empty." + } +} + locals { template_slug = "ops" @@ -117,11 +128,12 @@ resource "coder_agent" "main" { arch = "amd64" os = "linux" - env = { + # ONEDEV_SERVER_URL only when set: tod treats an empty value as an override. + env = merge({ REPO_URL = data.coder_parameter.repo_url.value GIT_REF = data.coder_parameter.git_ref.value MISE_TOOLS = "${data.coder_parameter.node_version.value} ${local.ops_tools} ${data.coder_parameter.extra_mise_tools.value}" - } + }, var.onedev_url == "" ? {} : { ONEDEV_SERVER_URL = var.onedev_url }) # Git over SSH uses `coder gitssh` (GIT_SSH_COMMAND is set by the agent). # Add the Coder public key (`coder publickey`) once to GitHub and OneDev. @@ -349,7 +361,7 @@ resource "docker_image" "workspace" { # Built from build/ by GitHub Actions (.github/workflows/images.yml) and # pushed to GHCR. Not built on x1-hel1. Tags are immutable: a change under # build/ needs a new tag here. - name = "ghcr.io/moveyourdigital/coder-workspace-ops:v0.5.0" + name = "ghcr.io/moveyourdigital/coder-workspace-ops:v0.6.0" keep_locally = true } diff --git a/php-wordpress/README.md b/php-wordpress/README.md index f6d7d14..338eda3 100644 --- a/php-wordpress/README.md +++ b/php-wordpress/README.md @@ -21,8 +21,8 @@ Docker daemon, so you can run WordPress, MariaDB and other services with | Node.js | Node LTS through [mise](https://mise.jdx.dev), for theme and asset builds | | Docker | Docker Engine, Compose and Buildx **inside** the workspace | | Editor | VS Code (web) with the Claude Code extension | -| AI | Claude Code CLI (`claude`) | -| Git | `git`, GitHub CLI (`gh`), SSH through Coder | +| AI | Claude Code CLI (`claude`), with skills for OneDev | +| Git | `git`, GitHub CLI (`gh`), OneDev CLI (`tod`), SSH through Coder | | Other | `build-essential`, `ripgrep`, `fd`, `jq`, `tmux`, `htop` | ## Parameters @@ -44,7 +44,12 @@ On each start, the workspace runs `mise install`, `npm`/`pnpm`/`yarn`/`bun insta copy private keys into the workspace. 2. **Log in to Claude Code once.** Open a terminal and run `claude`. The login is kept in `~/.claude`. The VS Code extension uses the same login. -3. **Start WordPress locally.** Use a `compose.yaml` in your project, then +3. **Connect `tod` to OneDev (optional).** In OneDev, create an access token + (your avatar → **Access Tokens**), then run + `tod config set access-token `. The server URL is already set. Test + it with `tod project list`. Claude Code can then work with OneDev issues, + pull requests and builds through its `tod` skills. It asks before any change. +4. **Start WordPress locally.** Use a `compose.yaml` in your project, then open the **Preview :8080** app in the Coder dashboard. Example `compose.yaml`: diff --git a/php-wordpress/build/Dockerfile b/php-wordpress/build/Dockerfile index a7f83ea..f4f29df 100644 --- a/php-wordpress/build/Dockerfile +++ b/php-wordpress/build/Dockerfile @@ -2,6 +2,21 @@ # PHP from apt. Node (theme builds) is installed by mise at start. # Run WordPress + MariaDB locally with docker compose inside the workspace. +# --- OneDev CLI (tod), built from a pinned tag ------------------------------ +# The official installer always downloads the latest binary, unverified. +# Here: clone the tag, check its commit, build. Only the binary and the agent +# skills go into the final image. +FROM golang:1.27.0-bookworm AS tod +ARG TOD_VERSION=v4.3.2 +ARG TOD_COMMIT=9e280c96bf0f3e541706580c0830e5753dc5d28d +WORKDIR /src +RUN git clone --depth 1 --branch "${TOD_VERSION}" https://code.onedev.io/onedev/tod.git . \ + && test "$(git rev-parse HEAD)" = "${TOD_COMMIT}" \ + && CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o /out/tod . \ + && mkdir -p /out/skills \ + && for d in skills/*/; do cp -r "$d" /out/skills/; done \ + && chmod -R u=rwX,go=rX /out + FROM ubuntu:24.04 SHELL ["/bin/bash", "-o", "pipefail", "-c"] @@ -95,6 +110,11 @@ RUN d=/usr/lib/code-server/lib/vscode/node_modules/shell-quote \ RUN install -d -m 0755 /etc/claude-code COPY --chmod=0644 managed-settings.json /etc/claude-code/managed-settings.json +# --- OneDev CLI (tod) and its Claude Code skills ---------------------------- +# Skills in /etc/claude-code/.claude/skills load for every user (managed). +COPY --from=tod /out/tod /usr/local/bin/tod +COPY --from=tod /out/skills /etc/claude-code/.claude/skills + # --- coder user ------------------------------------------- # UID 1000 must stay: existing home volumes are owned by 1000. RUN userdel -r ubuntu 2>/dev/null || true \ diff --git a/php-wordpress/build/managed-settings.json b/php-wordpress/build/managed-settings.json index 1b05ddd..778664a 100644 --- a/php-wordpress/build/managed-settings.json +++ b/php-wordpress/build/managed-settings.json @@ -1,9 +1,49 @@ { "permissions": { "disableBypassPermissionsMode": "disable", + "allow": [ + "Bash(tod version)", + "Bash(tod config get:*)", + "Bash(tod config path)", + "Bash(tod remote)", + "Bash(tod get-login-name:*)", + "Bash(tod get-valid-labels)", + "Bash(tod get-commit-message-requirement:*)", + "Bash(tod project:*)", + "Bash(tod issue list:*)", + "Bash(tod issue get:*)", + "Bash(tod issue current-reference)", + "Bash(tod pr list:*)", + "Bash(tod pr get:*)", + "Bash(tod pr current-reference)", + "Bash(tod build list:*)", + "Bash(tod build get:*)", + "Bash(tod build check-spec)" + ], + "ask": [ + "Bash(tod issue create:*)", + "Bash(tod issue edit:*)", + "Bash(tod issue change-state:*)", + "Bash(tod issue link:*)", + "Bash(tod issue add-comment:*)", + "Bash(tod issue log-work:*)", + "Bash(tod issue create-branch:*)", + "Bash(tod pr create:*)", + "Bash(tod pr edit:*)", + "Bash(tod pr approve:*)", + "Bash(tod pr request-changes:*)", + "Bash(tod pr merge:*)", + "Bash(tod pr discard:*)", + "Bash(tod pr add-comment:*)", + "Bash(tod pr add-code-comment:*)", + "Bash(tod code-comment:*)", + "Bash(tod build run:*)" + ], "deny": [ "Bash(sudo:*)", - "Read(/etc/claude-code/**)" + "Read(/etc/claude-code/*.json)", + "Bash(tod config set:*)", + "Read(~/.config/tod/**)" ] } -} \ No newline at end of file +} diff --git a/php-wordpress/main.tf b/php-wordpress/main.tf index 159d21d..6ce9407 100644 --- a/php-wordpress/main.tf +++ b/php-wordpress/main.tf @@ -29,6 +29,17 @@ variable "extra_git_hosts" { } } +variable "onedev_url" { + description = "OneDev server URL for the tod CLI (e.g. https://onedev.example.com). Empty: each user sets it with tod config set." + type = string + default = "" + + validation { + condition = var.onedev_url == "" || can(regex("^https://[A-Za-z0-9.-]+(:[0-9]+)?/?$", var.onedev_url)) + error_message = "Use an https:// URL without a path, or leave it empty." + } +} + locals { template_slug = "php-wordpress" preview_port = 8080 @@ -99,11 +110,12 @@ resource "coder_agent" "main" { arch = "amd64" os = "linux" - env = { + # ONEDEV_SERVER_URL only when set: tod treats an empty value as an override. + env = merge({ REPO_URL = data.coder_parameter.repo_url.value GIT_REF = data.coder_parameter.git_ref.value MISE_TOOLS = "${data.coder_parameter.node_version.value} ${data.coder_parameter.extra_mise_tools.value}" - } + }, var.onedev_url == "" ? {} : { ONEDEV_SERVER_URL = var.onedev_url }) # Git over SSH uses `coder gitssh` (GIT_SSH_COMMAND is set by the agent). # Add the Coder public key (`coder publickey`) once to GitHub and OneDev. @@ -316,7 +328,7 @@ resource "docker_image" "workspace" { # Built from build/ by GitHub Actions (.github/workflows/images.yml) and # pushed to GHCR. Not built on x1-hel1. Tags are immutable: a change under # build/ needs a new tag here. - name = "ghcr.io/moveyourdigital/coder-workspace-php-wordpress:v0.6.0" + name = "ghcr.io/moveyourdigital/coder-workspace-php-wordpress:v0.7.0" keep_locally = true } diff --git a/renovate.json b/renovate.json index 225c147..de83761 100644 --- a/renovate.json +++ b/renovate.json @@ -35,6 +35,19 @@ "depNameTemplate": "docker/cli", "extractVersionTemplate": "^v(?\\d+\\.\\d+\\.\\d+)$" }, + { + "customType": "regex", + "description": "tod (OneDev CLI) tag and commit in the Dockerfiles", + "managerFilePatterns": [ + "/(^|/)build/Dockerfile$/" + ], + "matchStrings": [ + "ARG TOD_VERSION=(?v\\S+)\\nARG TOD_COMMIT=(?[a-f0-9]{40})" + ], + "datasourceTemplate": "git-tags", + "depNameTemplate": "onedev/tod", + "packageNameTemplate": "https://code.onedev.io/onedev/tod.git" + }, { "customType": "regex", "description": "trivy image (tag and digest) in scripts/trivy-scan.sh", @@ -88,6 +101,13 @@ "major" ], "enabled": false + }, + { + "description": "OneDev server is 16.4.1; tod 4.3.3+ needs 16.5.11. Remove this rule after the server upgrade.", + "matchDepNames": [ + "onedev/tod" + ], + "allowedVersions": "<4.3.3" } ] } diff --git a/tests/smoke.sh b/tests/smoke.sh index 63a21c1..5336f17 100755 --- a/tests/smoke.sh +++ b/tests/smoke.sh @@ -20,6 +20,12 @@ common=' docker compose version; docker buildx version # Claude Code managed policy must be readable by coder (COPY --chmod=0644) jq -e . /etc/claude-code/managed-settings.json >/dev/null + # OneDev CLI and its managed skills (readable by coder) + tod version + n=0 + for f in /etc/claude-code/.claude/skills/*/SKILL.md; do test -r "$f" && n=$((n + 1)); done + test "$n" -ge 8 || { echo "tod skills: $n readable, want 8+" >&2; exit 1; } + echo "tod skills: $n" code-server --version | sed -n 1p code-server --auth none --bind-addr 127.0.0.1:13337 >/tmp/cs.log 2>&1 & for _ in $(seq 1 30); do diff --git a/vars.example.yaml b/vars.example.yaml index b8e66ea..46775fc 100644 --- a/vars.example.yaml +++ b/vars.example.yaml @@ -1,3 +1,4 @@ # Copy to vars.yaml (not in Git) and set the values. # `just push` passes vars.yaml to `coder templates push --variables-file`. extra_git_hosts: "" # space-separated host names, e.g. "git.example.com" +onedev_url: "" # e.g. "https://onedev.example.com" (tod CLI server)