Skip to content

chore: drop devcontainer comments that only repeat the line below #171

chore: drop devcontainer comments that only repeat the line below

chore: drop devcontainer comments that only repeat the line below #171

Workflow file for this run

name: CI
on:
pull_request:
branches: [master]
# Deliberately unfiltered: "CI complete" below is the required status check
# for master, and a required check that never runs blocks a PR forever -- a
# docs-only PR would deadlock. Image builds are still skipped per-image via
# detect-changes; the always-on jobs are lint-only and take seconds.
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
# ── Lint Dockerfiles with hadolint ──────────────────────────────────
lint-dockerfiles:
name: Hadolint · ${{ matrix.dockerfile }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
dockerfile:
- bun/.devcontainer/Dockerfile
- claude-bun/.devcontainer/Dockerfile
- claude-code/.devcontainer/Dockerfile
- hugo-bun/.devcontainer/Dockerfile
- hugo-bun-node/.devcontainer/Dockerfile
- ralphex-fe/Dockerfile
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Run hadolint
uses: hadolint/hadolint-action@v3.0.0
with:
dockerfile: ${{ matrix.dockerfile }}
# ── Lint GitHub Actions workflows with actionlint ───────────────────
lint-workflows:
name: Actionlint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Run actionlint
uses: raven-actions/actionlint@v2
# ── Detect which images have changed files ──────────────────────────
detect-changes:
name: Detect changes
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
has-changes: ${{ steps.set-matrix.outputs.has-changes }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Check changed paths
uses: dorny/paths-filter@v4
id: filter
with:
filters: |
bun:
- 'bun/**'
claude-bun:
- 'claude-bun/**'
claude-code:
- 'claude-code/**'
hugo-bun:
- 'hugo-bun/**'
hugo-bun-node:
- 'hugo-bun-node/**'
ralphex-fe:
- 'ralphex-fe/**'
- name: Build matrix from changes
id: set-matrix
env:
CHANGED_BUN: ${{ steps.filter.outputs.bun }}
CHANGED_CLAUDE_BUN: ${{ steps.filter.outputs.claude-bun }}
CHANGED_CLAUDE_CODE: ${{ steps.filter.outputs.claude-code }}
CHANGED_HUGO_BUN: ${{ steps.filter.outputs.hugo-bun }}
CHANGED_HUGO_BUN_NODE: ${{ steps.filter.outputs.hugo-bun-node }}
CHANGED_RALPHEX: ${{ steps.filter.outputs.ralphex-fe }}
run: |
INCLUDES="[]"
add_image() {
local image="$1" context="$2" dockerfile="$3" verify="$4" target="${5:-}"
INCLUDES=$(echo "$INCLUDES" | jq -c \
--arg img "$image" \
--arg ctx "$context" \
--arg df "$dockerfile" \
--arg v "$verify" \
--arg tgt "$target" \
'. + [{"image":$img,"context":$ctx,"dockerfile":$df,"verify":$v,"target":$tgt}]')
}
if [ "$CHANGED_BUN" = "true" ]; then
add_image "bun" \
"bun/.devcontainer" \
"bun/.devcontainer/Dockerfile" \
"bun --version"
fi
if [ "$CHANGED_CLAUDE_BUN" = "true" ]; then
add_image "claude-bun" \
"claude-bun/.devcontainer" \
"claude-bun/.devcontainer/Dockerfile" \
"bun --version && gh --version && zsh --version && zsh -ic 'typeset -p ZSH_THEME' | grep -q powerlevel10k/powerlevel10k"
fi
if [ "$CHANGED_CLAUDE_CODE" = "true" ]; then
add_image "claude-code" \
"claude-code/.devcontainer" \
"claude-code/.devcontainer/Dockerfile" \
"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 && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/ && 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 && 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 && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/ && 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
if [ "$CHANGED_HUGO_BUN" = "true" ]; then
add_image "hugo-bun" \
"hugo-bun/.devcontainer" \
"hugo-bun/.devcontainer/Dockerfile" \
"bun --version && hugo version"
fi
if [ "$CHANGED_HUGO_BUN_NODE" = "true" ]; then
add_image "hugo-bun-node" \
"hugo-bun-node/.devcontainer" \
"hugo-bun-node/.devcontainer/Dockerfile" \
"bun --version && hugo version && node --version"
fi
if [ "$CHANGED_RALPHEX" = "true" ]; then
add_image "ralphex-fe" \
"ralphex-fe" \
"ralphex-fe/Dockerfile" \
"bun --version && hugo version && go version && docker --version && /srv/ralphex --version && rtk --version && test -r /etc/claude-code/managed-settings.json && jq -r '.managedMcpServers.mdn.url' /etc/claude-code/managed-settings.json | grep -qx https://mcp.mdn.mozilla.net/"
fi
if [ "$INCLUDES" = "[]" ]; then
{
echo "has-changes=false"
echo "matrix={\"include\":[]}"
} >> "$GITHUB_OUTPUT"
else
{
echo "has-changes=true"
echo "matrix=$(echo "$INCLUDES" | jq -c '{include:.}')"
} >> "$GITHUB_OUTPUT"
fi
# ── Build changed images (amd64 only) and verify installed tools ────
# arm64 is tested on native runners at merge time — no QEMU emulation in CI.
build-and-verify:
name: Build · ${{ matrix.image }}
needs: detect-changes
if: needs.detect-changes.outputs.has-changes == 'true'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.detect-changes.outputs.matrix) }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build amd64 image
uses: docker/build-push-action@v7
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
target: ${{ matrix.target }}
platforms: linux/amd64
load: true
tags: ${{ matrix.image }}:test
cache-from: type=gha,scope=${{ matrix.image }}-amd64
cache-to: type=gha,mode=max,scope=${{ matrix.image }}-amd64
- name: Verify tools (amd64)
env:
IMAGE_TAG: ${{ matrix.image }}:test
VERIFY_CMD: ${{ matrix.verify }}
run: docker run --rm --entrypoint sh "$IMAGE_TAG" -c "$VERIFY_CMD"
# -- Single required status check for branch protection ------------------
# Aggregates every job above so master needs exactly one required check.
# Passes when each job succeeded or was legitimately skipped (path-filtered
# builds), and fails if any job failed or was cancelled.
#
# Any job added to this workflow must also be added to `needs` below: this is
# the only required check on master and Renovate auto-merges on it, so an
# unwatched job would go red while the gate stayed green. The first step
# enforces that rather than trusting anyone to remember.
ci-complete:
name: CI complete
# always(), not !cancelled(): GitHub counts a *skipped* required check as
# passing, so !cancelled() would let a cancelled run report a green gate.
if: always()
needs: [lint-dockerfiles, lint-workflows, detect-changes, build-and-verify]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Verify every job is watched
env:
WATCHED: ${{ toJSON(needs) }}
run: |
missing=""
for job in $(yq '.jobs | keys | .[]' .github/workflows/ci.yml); do
[ "$job" = "ci-complete" ] && continue
jq -e --arg j "$job" 'has($j)' <<< "$WATCHED" > /dev/null || missing="$missing $job"
done
if [ -n "$missing" ]; then
echo "::error::Jobs missing from ci-complete.needs:$missing"
exit 1
fi
- name: Check results of all CI jobs
env:
WATCHED: ${{ toJSON(needs) }}
run: |
jq -r 'to_entries[] | "\(.key): \(.value.result)"' <<< "$WATCHED"
bad=$(jq -r '
to_entries[]
| select(.value.result != "success" and .value.result != "skipped")
| "\(.key) (\(.value.result))"' <<< "$WATCHED")
if [ -n "$bad" ]; then
echo "::error::CI jobs did not pass: $(echo "$bad" | tr '\n' ' ')"
exit 1
fi
echo "All CI jobs passed or were skipped."