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
6 changes: 6 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@ COPY --from=ralphex-download /usr/local/bin/ralphex /usr/local/bin/ralphex
COPY --from=hadolint-download /usr/local/bin/hadolint /usr/local/bin/hadolint
COPY --from=actionlint-download /usr/local/bin/actionlint /usr/local/bin/actionlint

# ── gh-stack (native stacked PRs) ────────────────────────────────────────────
# Installed as node so it lands in ~/.local/share/gh/extensions. See #160.
# renovate: datasource=github-releases depName=github/gh-stack
ARG GH_STACK_VERSION=0.1.1
RUN gh extension install github/gh-stack --pin "v${GH_STACK_VERSION}"

# ── Claude Code CLI ──────────────────────────────────────────────────────────
# npm install (not native installer) to avoid rate-limiting in parallel builds.
# See: claude-code/.devcontainer/Dockerfile for rationale.
Expand Down
1 change: 1 addition & 0 deletions .github/renovate.json5
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
'@anthropic-ai/claude-code',
'agent-browser',
'cli/cli', // gh — upstream .deb; apt's trixie build is frozen at 2.46.0
'github/gh-stack', // gh extension for native stacked PRs
'docker/cli', // docker CLI static binary (download.docker.com).
// github-tags, NOT github-releases: moby/moby tags its
// releases 'docker-v29.8.0', which extractVersion cannot
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/build-claude-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,19 @@ jobs:
matrix:
include:
- image-suffix: claude-code
verify-command: "bun --version || true && claude --version && mise --version && zsh --version && gh --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
verify-command: "bun --version || true && claude --version && mise --version && zsh --version && gh --version && gh stack --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
runner: ubuntu-24.04
arch: amd64
- image-suffix: claude-code
verify-command: "bun --version || true && claude --version && mise --version && zsh --version && gh --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
verify-command: "bun --version || true && claude --version && mise --version && zsh --version && gh --version && gh stack --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
runner: ubuntu-24.04-arm
arch: arm64
- image-suffix: claude-code-sandbox
verify-command: "claude --version && mise --version && zsh --version && gh --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
verify-command: "claude --version && mise --version && zsh --version && gh --version && gh stack --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
runner: ubuntu-24.04
arch: amd64
- image-suffix: claude-code-sandbox
verify-command: "claude --version && mise --version && zsh --version && gh --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
verify-command: "claude --version && mise --version && zsh --version && gh --version && gh stack --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node"
runner: ubuntu-24.04-arm
arch: arm64
runs-on: ${{ matrix.runner }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,12 @@ jobs:
add_image "claude-code" \
"claude-code/.devcontainer" \
"claude-code/.devcontainer/Dockerfile" \
"bun --version || true && claude --version && mise --version && zsh --version && gh --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node" \
"bun --version || true && claude --version && mise --version && zsh --version && gh --version && gh stack --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node" \
"default"
add_image "claude-code-sandbox" \
"claude-code/.devcontainer" \
"claude-code/.devcontainer/Dockerfile" \
"claude --version && mise --version && zsh --version && gh --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node" \
"claude --version && mise --version && zsh --version && gh --version && gh stack --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k && stat -c %U /home/node/.local/share | grep -qx node && stat -c %U /home/node/.config/gh | grep -qx node" \
"sandbox"
fi

Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,12 @@ Images from this repository are built and published to GitHub Container Registry
### Automatically, via Renovate

The agent tooling in the `claude-code` and `ralphex-fe` images — `rtk`, `ralphex`, the Claude Code
CLI, and `agent-browser` — is pinned as `ARG`s carrying `# renovate:` annotations. Renovate watches
their releases and opens a single grouped bump PR when one ships; CI verifies it, it auto-merges, and
that merge rebuilds the affected images. No upstream release means no PR and no rebuild. Scope and
grouping live in [`.github/renovate.json5`](./.github/renovate.json5); the Dependency Dashboard
issue tracks what is pending. Everything else — including base images and Bun/Hugo — stays manual.
CLI, `agent-browser`, and the `gh-stack` extension — is pinned as `ARG`s carrying `# renovate:`
annotations. Renovate watches their releases and opens a single grouped bump PR when one ships; CI
verifies it, it auto-merges, and that merge rebuilds the affected images. No upstream release means no
PR and no rebuild. Scope and grouping live in [`.github/renovate.json5`](./.github/renovate.json5);
the Dependency Dashboard issue tracks what is pending. Everything else — including base images and
Bun/Hugo — stays manual.

> **Setup requirement — Mend portal toggles.** Installing the Renovate app with "All repositories"
> makes Mend default the repo to **Silent mode** (`dryRun=lookup`), where it scans and shows updates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Use to audit a project's .devcontainer/ and bundled .claude/skills/
metadata:
author: Serge Gatezh
url: https://github.com/gatezh
version: "1.1.0"
version: "1.2.0"
---

# Devcontainer Upstream Sync
Expand Down Expand Up @@ -68,6 +68,7 @@ but don't, and includes files that shouldn't be tracked.
| `.devcontainer/claude-sandbox/init-firewall.sh` | `.devcontainer/claude-sandbox/init-firewall.sh` *(repo root, not `claude-code/`)* | exemplar |
| `.claude/skills/sandbox-fetch-docs/SKILL.md` | `claude-code/.claude/skills/sandbox-fetch-docs/SKILL.md` | framework-track |
| `.claude/skills/sandbox-playwright/SKILL.md` | `claude-code/.claude/skills/sandbox-playwright/SKILL.md` | framework-track |
| `.claude/skills/stacked-prs/SKILL.md` | `claude-code/.claude/skills/stacked-prs/SKILL.md` | framework-track |
| `.claude/skills/devcontainer-upstream-sync/SKILL.md` | `claude-code/.claude/skills/devcontainer-upstream-sync/SKILL.md` | framework-track |
| `.claude/settings.json` | `claude-code/.claude/settings.json` | starter-customize |
| `.mise.toml` | `claude-code/mise.toml` | starter-customize |
Expand Down
48 changes: 48 additions & 0 deletions claude-code/.claude/skills/stacked-prs/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
name: stacked-prs
description: Use when opening PRs for branches that depend on each other, splitting work into a chain of PRs, or merging several dependent PRs together — in THIS devcontainer. Triggers on "stacked PRs", "stack these PRs", "open PRs for dependent branches", "chain PRs", "PR on top of PR", "merge all at once", or whenever about to run `gh pr create --base <another-feature-branch>`.
compatibility: Designed for the gatezh/devcontainers claude-code image, which bakes in the github/gh-stack gh extension. Elsewhere, run `gh extension install github/gh-stack` first.
metadata:
author: Serge Gatezh
url: https://github.com/gatezh
version: "1.0.0"
---

# Stacked PRs with `gh stack`

## Overview

GitHub has native stacked PRs, driven by the `gh stack` extension (preinstalled in this image — check with `gh stack --version`). A native stack links the PRs on GitHub, keeps each PR's base on the branch below it, and can merge the stack atomically.

**Never hand-chain PRs with `gh pr create --base <feature-branch>`.** GitHub cannot merge that chain as one unit, and every merge below forces a manual retarget and rebase above. If PRs are already hand-chained, convert them with `gh stack link`.

Order is always **bottom → top**: the bottom branch is based on trunk and merges first.

## Which command

| Situation | Command |
|---|---|
| New multi-part work | `gh stack init <first-branch>`, commit, then `gh stack add <next-branch>` per layer |
| Adopt existing local branches | `gh stack init <bottom> <middle> <top>` |
| Push everything and open the PRs | `gh stack submit --auto` (drafts; add `--open` for ready-for-review) |
| PRs or branches already exist | `gh stack link <bottom> ... <top>` — PR numbers, URLs or branch names |
| Merge up to and including PR N | `gh stack merge <N> --yes` — all-or-nothing |
| Trunk moved / a lower PR merged | `gh stack sync` (fetch, rebase, push, refresh PR state) |
| Inspect the stack | `gh stack view --json` |

`link` pushes branch arguments, opens PRs for branches that lack one, and fixes wrong base branches. It only adds to a stack, never removes.

## Rules for non-interactive use

Agent shells may look like a TTY, and bare commands then block on a prompt or TUI. Always pass arguments and flags:

- `init` and `add` with explicit branch names; `submit --auto`; `merge <target> --yes`; `view --json`.
- Never run `gh stack modify` or `gh stack switch` — both are TUI-only.
- With more than one git remote, pass `--remote <name>` to `submit`, `push`, `sync`, `rebase` and `link`, or set `git config remote.pushDefault <name>`.
- `submit --auto` generates PR titles and bodies. Set them afterwards with `gh pr edit <N> --title ... --body-file ...`.
- Use `gh stack merge`, not `gh pr merge`, for stacked PRs. Pass `--squash`, `--merge` or `--rebase`, or it reuses the last method.

## More detail

- `gh stack <command> --help` is authoritative. `gh stack help <command>` prints only the top-level help.
- Docs: https://gh.io/stacks. For the upstream skill, which covers rebase conflicts, stack design and troubleshooting, run `gh skill install github/gh-stack`.
7 changes: 7 additions & 0 deletions claude-code/.devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ RUN mkdir -p /etc/claude-code
COPY --chown=root:root --chmod=0644 managed-settings.json /etc/claude-code/managed-settings.json
USER node

# ── gh-stack (native stacked PRs) ────────────────────────────────────────────
# Baked as node into ~/.local/share/gh/extensions, which is on the image layer (only
# ~/.config/gh is a volume); a volume over ~/.local/share/gh would shadow it. See #160.
# renovate: datasource=github-releases depName=github/gh-stack
ARG GH_STACK_VERSION=0.1.1
RUN gh extension install github/gh-stack --pin "v${GH_STACK_VERSION}"

# ── Claude Code CLI ───────────────────────────────────────────────────────────
# npm, not the native installer: the installer rate-limits (429) under parallel
# Docker builds. npm stays supported "for compatibility reasons" — this is that
Expand Down
Loading
Loading