Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"remoteUser": "runwhen",
"updateRemoteUserUID": false,
"overrideCommand": false,
"workspaceFolder": "/home/runwhen",

"forwardPorts": [3000],
"portsAttributes": {
Expand All @@ -20,6 +19,7 @@
"GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}"
},

"postCreateCommand": "chmod 755 /home/runwhen && mkdir -p /home/runwhen/.ssh && chmod 700 /home/runwhen/.ssh && touch /home/runwhen/.ssh/authorized_keys && chmod 600 /home/runwhen/.ssh/authorized_keys",
"postStartCommand": "python -m http.server --bind 0.0.0.0 --directory /robot_logs 3000 &",

"features": {
Expand Down Expand Up @@ -63,7 +63,7 @@
}
},
"codespaces": {
"openFiles": ["codecollection/README.md"]
"openFiles": ["README.md"]
}
}
}
122 changes: 0 additions & 122 deletions .devcontainer/on-create.sh

This file was deleted.

4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ __pycache__
*robot_logs*
.python-version
.vscode/settings.json
# Generated by `task install-skills` (Cursor rules); do not commit
.cursor/rules/
node_modules/
package-lock.json
package.json
/workspace/codecollection-devtools/rw-cli-codecollection
/workspace/codecollection-devtools/rw-generic-codecollection
24 changes: 21 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ RUN set -eux; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*

# docker-in-docker (devcontainer feature) sources /etc/os-release; "forky" is rejected.
# Align with bookworm apt pin above so Codespaces can use the CI-built image + dind.
RUN set -eux; \
for f in /usr/lib/os-release /etc/os-release; do \
[ -e "$f" ] || continue; \
target="$f"; \
[ -L "$f" ] && target=$(readlink -f "$f"); \
grep -q '^ID=debian' "$target" || continue; \
sed -i \
-e 's/^VERSION_CODENAME=.*/VERSION_CODENAME=bookworm/' \
-e 's/^VERSION_ID=.*/VERSION_ID="12"/' \
-e 's/^VERSION=.*/VERSION="12 (bookworm)"/' \
-e 's|^PRETTY_NAME=.*|PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"|' \
"$target"; \
done

RUN echo "runwhen ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

# Architecture detection for multi-arch tool installs
Expand Down Expand Up @@ -160,9 +176,11 @@ RUN mkdir -p $ROBOT_LOG_DIR && \

COPY --chown=runwhen:0 .pylintrc.google LICENSE ro requirements.txt .
COPY --chown=runwhen:0 .devcontainer/ .devcontainer/
RUN mkdir -p auth && \
chown -R runwhen:0 ${RUNWHEN_HOME}/.devcontainer ${RUNWHEN_HOME}/auth && \
chmod -R 0775 ${RUNWHEN_HOME}/ro ${RUNWHEN_HOME}/auth ${RUNWHEN_HOME}/.devcontainer
RUN mkdir -p auth .ssh && \
chown -R runwhen:0 ${RUNWHEN_HOME}/.devcontainer ${RUNWHEN_HOME}/auth ${RUNWHEN_HOME}/.ssh && \
chmod -R 0775 ${RUNWHEN_HOME}/ro ${RUNWHEN_HOME}/auth ${RUNWHEN_HOME}/.devcontainer && \
chmod 755 ${RUNWHEN_HOME} && \
chmod 700 ${RUNWHEN_HOME}/.ssh

USER runwhen
ENV USER="runwhen"
Expand Down
24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ generation rules, SLI patterns, and test infrastructure conventions.
| `test-infra-azure-devops.md` | DevOps projects, pipelines, agent pools via Terraform |
| `test-infra-cloud.md` | Shared conventions across all cloud platforms |

Skills are copied to `{codecollection}/.cursor/rules/*.mdc` at setup time. A
Skills are copied to `.cursor/rules/*.mdc` (the workspace root) at setup time. A
`.gitignore` is placed in that directory to prevent accidental commits. To
re-install after an update, run:

Expand Down Expand Up @@ -197,7 +197,7 @@ Base packages installed in the image (from `requirements.txt`):
- `rw-cli-keywords` (includes `rw-core-keywords` — handles `RW_MODE=dev` for local development)
- `jmespath`, `python-dateutil`, `thefuzz`, `jinja2`

Each codecollection's `requirements.txt` is installed at bootstrap time by `on-create.sh`.
Each codecollection's `requirements.txt` is installed at bootstrap time by `task setup`.

---

Expand All @@ -206,8 +206,7 @@ Each codecollection's `requirements.txt` is installed at bootstrap time by `on-c
```
codecollection-devtools/
├── .devcontainer/
│ ├── devcontainer.json # devcontainer config (pulls pre-built image)
│ └── on-create.sh # bootstrap script (called by Taskfile)
│ └── devcontainer.json # devcontainer config (pulls pre-built image)
├── .github/
│ └── workflows/
│ ├── build-push.yaml # CI: multi-arch build → GHCR + GCP Artifact Registry
Expand Down Expand Up @@ -246,16 +245,23 @@ All image builds happen in **GitHub Actions** — never locally:
```
devcontainer opens
→ pulls pre-built image from GHCR
→ workspace root is /workspaces/codecollection-devtools/ (the repo mount)
→ starts log HTTP server on port 3000
→ user runs: task setup REPO=org/repo PR=123
1. clones repo into /home/runwhen/codecollection/
2. checks out PR branch (if PR set)
3. pip installs codecollection's requirements.txt
4. installs skills/ as .cursor/rules/*.mdc
5. verifies tools (ro, robot, kubectl, gh, python)
→ ready to develop
2. symlinks ./codecollection → /home/runwhen/codecollection
3. checks out PR branch (if PR set)
4. pip installs codecollection's requirements.txt
5. installs skills/ as .cursor/rules/*.mdc (workspace root)
6. verifies tools (ro, robot, kubectl, gh, python)
→ ready: cd codecollection/codebundles/<name> && ro
```

> **Two repos, one tree.** The workspace root is the devtools repo. The `codecollection/`
> directory is a symlink to a separate git clone. `git` commands inside `codecollection/`
> operate on the codecollection repo — not devtools. The `.gitignore` prevents the
> symlink from being tracked.

---

## For codecollection authors
Expand Down
28 changes: 20 additions & 8 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ tasks:
echo "Cloning {{.REPO_URL}} → {{.CODECOLLECTION_DIR}} ..."
git clone --branch "{{.BRANCH}}" "{{.REPO_URL}}" "{{.CODECOLLECTION_DIR}}"
fi
- |
LINK="{{.TASKFILE_DIR}}/codecollection"
if [ "$(readlink -f "$LINK" 2>/dev/null)" = "$(readlink -f "{{.CODECOLLECTION_DIR}}")" ]; then
:
else
rm -f "$LINK"
ln -s "{{.CODECOLLECTION_DIR}}" "$LINK"
echo "Symlinked $LINK → {{.CODECOLLECTION_DIR}}"
fi

checkout-pr:
desc: Check out a PR branch
Expand Down Expand Up @@ -84,22 +93,20 @@ tasks:
desc: Install CodeBundle authoring skills as Cursor rules
cmds:
- |
RULES_DIR="{{.CODECOLLECTION_DIR}}/.cursor/rules"
if [ ! -d "{{.SKILLS_SRC}}" ]; then
echo "Skills directory not found at {{.SKILLS_SRC}}, skipping."
exit 0
fi
RULES_DIR="{{.TASKFILE_DIR}}/.cursor/rules"
mkdir -p "$RULES_DIR"
for skill in "{{.SKILLS_SRC}}"/*.md; do
[ -f "$skill" ] || continue
base=$(basename "$skill" .md)
target="$RULES_DIR/${base}.mdc"
cp "$skill" "$target"
echo " Installed skill: ${base}"
cp "$skill" "$RULES_DIR/${base}.mdc"
echo " Installed: ${base}"
done
if [ ! -f "$RULES_DIR/.gitignore" ]; then
echo "# Injected by codecollection-devtools -- do not commit" > "$RULES_DIR/.gitignore"
echo "*.mdc" >> "$RULES_DIR/.gitignore"
printf '# Injected by codecollection-devtools -- do not commit\n*.mdc\n' > "$RULES_DIR/.gitignore"
fi
echo "Skills installed to $RULES_DIR"

Expand All @@ -114,12 +121,17 @@ tasks:
- 'printf " %-12s %s\n" "gh:" "$(command -v gh 2>/dev/null && echo ok || echo MISSING)"'
- 'printf " %-12s %s\n" "python:" "$(python --version 2>&1)"'
- echo ""
- echo "Codecollection at {{.CODECOLLECTION_DIR}}"
- echo "Run 'cd codecollection/codebundles/<name> && ro' to test a codebundle."
- echo "Codecollection at {{.CODECOLLECTION_DIR}} (symlinked from ./codecollection)"
- echo ""
- echo "Your work happens in the codecollection (its own git repo):"
- echo " cd codecollection/codebundles/<name> && ro"
- echo ""
- echo "Commits in ./codecollection go to the codecollection repo, not devtools."

clean:
desc: Remove the cloned codecollection (start fresh)
prompt: This will delete {{.CODECOLLECTION_DIR}}. Continue?
cmds:
- rm -f "{{.TASKFILE_DIR}}/codecollection"
- rm -rf "{{.CODECOLLECTION_DIR}}"
- echo "Cleaned. Run 'task setup REPO=... ' to start again."
10 changes: 2 additions & 8 deletions docs/cursor-remote-devcontainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,20 +75,14 @@ sed -i.bak \
~/.ssh/codespaces
```

In Cursor: **Remote-SSH: Connect to Host…** → select that host → **Open Folder** → **`/home/runwhen`**.
In Cursor: **Remote-SSH: Connect to Host…** → select that host → **Open Folder** → **`/workspaces/codecollection-devtools`** (the devcontainer workspace root). After `task setup`, the `codecollection/` symlink gives you direct access to codebundles.

---

## 5. Troubleshooting

- Run **`gh codespace ssh -c YOUR_CODESPACE_NAME --`** again if the connection or auth state seems off.
- For **`authorized_keys`** permission errors inside the codespace:

```bash
sudo chmod 755 /home/runwhen
sudo chmod 700 /home/runwhen/.ssh
sudo chmod 600 /home/runwhen/.ssh/authorized_keys
```
- SSH permission errors should not occur — the image and `postCreateCommand` set `/home/runwhen` (755), `.ssh` (700), and `authorized_keys` (600) automatically.

---

Expand Down
Loading