Skip to content

fix: switch shellcheck to local system hook, drop Docker dependency (#3) #30

fix: switch shellcheck to local system hook, drop Docker dependency (#3)

fix: switch shellcheck to local system hook, drop Docker dependency (#3) #30

Workflow file for this run

name: "Test"
on:
pull_request:
push:
branches: [main, develop]
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
permissions: {}
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: ShellCheck
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0
with:
severity: warning
- name: Validate JSON
run: |
FAIL=0
while IFS= read -r -d '' f; do
if ! python3 -m json.tool "$f" > /dev/null 2>&1; then
echo "ERROR: Invalid JSON: $f"
FAIL=1
fi
done < <(find . -name '*.json' -not -path './.git/*' -print0)
exit "$FAIL"
- name: Install uv
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
- name: Validate YAML
run: |
uvx [email protected] -d relaxed .github/workflows/
- name: Prettier check
run: |
npx [email protected] --check "**/*.{json,yml,yaml,md}" --ignore-path .gitignore
- name: Markdownlint
run: |
npx [email protected] "**/*.md" --ignore node_modules
- name: shfmt format check
run: |
curl -fsSL https://github.com/mvdan/sh/releases/download/v3.13.0/shfmt_v3.13.0_linux_amd64 \
-o /usr/local/bin/shfmt && chmod +x /usr/local/bin/shfmt
shfmt -ln bash -d -i 4 -ci src/ test/
- name: Check .sh files are executable
run: |
FAIL=0
while IFS= read -r -d '' f; do
if [[ ! -x "$f" ]]; then
echo "ERROR: $f is not executable"
FAIL=1
fi
done < <(find . -name '*.sh' -not -path './.git/*' -print0)
exit "$FAIL"
# Run all per-scenario tests (options-specific assertions)
test-scenarios:
needs: lint
runs-on: ubuntu-latest
timeout-minutes: 60 # 10 scenarios building containers takes time
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Install devcontainer CLI
run: npm install -g @devcontainers/[email protected]
- name: Run all scenarios
run: |
devcontainer features test --project-folder . 2>&1 | tee /tmp/scenario-test-output.log
# Workaround: devcontainers/[email protected] exits 0 even when feature install fails.
# Grep for known failure strings and fail explicitly. Revisit on CLI upgrade.
if grep -qE "Exit code [1-9][0-9]*|failed to install|Failed to launch|Failed:| FAIL:" /tmp/scenario-test-output.log; then
echo "ERROR: Test output contains failures."
exit 1
fi
- name: Annotate install warnings
if: always()
run: |
mapfile -t warnings < <(grep -oP '(?<=claude-code feature\] WARNING: ).*' /tmp/scenario-test-output.log 2>/dev/null | tr -d '\r' | sort -u || true)
if [[ ${#warnings[@]} -gt 0 ]]; then
echo "## :warning: Install Warnings" >> "$GITHUB_STEP_SUMMARY"
for msg in "${warnings[@]}"; do
echo "- ${msg}" >> "$GITHUB_STEP_SUMMARY"
echo "::warning title=Install Warning::${msg}"
done
fi
- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: logs-scenarios
path: /tmp/scenario-test-output.log
retention-days: 7
# Run core assertions across all supported base images
test-image-matrix:
needs: lint
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
strategy:
fail-fast: false
max-parallel: 10
matrix:
image:
# Raw OS images
- "ubuntu:22.04"
- "ubuntu:24.04"
- "debian:bullseye"
- "debian:bookworm"
- "alpine:3.19"
- "alpine:3.20"
- "alpine:3.21"
- "archlinux:latest"
- "fedora:39"
- "fedora:40"
- "rockylinux:9"
- "almalinux:9"
- "amazonlinux:2023"
# DevContainer base images
- "mcr.microsoft.com/devcontainers/base:debian"
- "mcr.microsoft.com/devcontainers/base:ubuntu"
- "mcr.microsoft.com/devcontainers/base:alpine"
- "mcr.microsoft.com/devcontainers/universal:2"
# Language-specific images
- "mcr.microsoft.com/devcontainers/python:3"
- "mcr.microsoft.com/devcontainers/javascript-node"
- "mcr.microsoft.com/devcontainers/typescript-node"
- "mcr.microsoft.com/devcontainers/rust"
- "mcr.microsoft.com/devcontainers/go"
- "mcr.microsoft.com/devcontainers/cpp"
- "mcr.microsoft.com/devcontainers/dotnet"
- "mcr.microsoft.com/devcontainers/java"
- "mcr.microsoft.com/devcontainers/ruby"
- "mcr.microsoft.com/devcontainers/php"
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Install devcontainer CLI
run: npm install -g @devcontainers/[email protected]
- name: Test on ${{ matrix.image }}
run: |
devcontainer features test \
--features claude-code \
--skip-scenarios \
--base-image "${{ matrix.image }}" \
--project-folder . 2>&1 | tee /tmp/test-output.log
# Workaround: devcontainers/[email protected] exits 0 even when feature install fails.
# Grep for known failure strings and fail explicitly. Revisit on CLI upgrade.
if grep -qE "Exit code [1-9][0-9]*|failed to install|Failed to launch|Failed:| FAIL:" /tmp/test-output.log; then
echo "ERROR: Test output contains failures."
exit 1
fi
- name: Annotate install warnings
if: always()
run: |
mapfile -t warnings < <(grep -oP '(?<=claude-code feature\] WARNING: ).*' /tmp/test-output.log 2>/dev/null | tr -d '\r' | sort -u || true)
if [[ ${#warnings[@]} -gt 0 ]]; then
echo "## :warning: Install Warnings" >> "$GITHUB_STEP_SUMMARY"
for msg in "${warnings[@]}"; do
echo "- ${msg}" >> "$GITHUB_STEP_SUMMARY"
echo "::warning title=Install Warning::${msg}"
done
fi
- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: logs-amd64-${{ strategy.job-index }}
path: /tmp/test-output.log
retention-days: 7
# arm64 tests via QEMU emulation on standard ubuntu-latest runners
test-arm64:
needs: lint
runs-on: ubuntu-latest
timeout-minutes: 60 # QEMU emulation is significantly slower than native
permissions:
contents: read
strategy:
fail-fast: false
max-parallel: 2
matrix:
image:
- "ubuntu:24.04"
- "alpine:3.21"
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up QEMU for arm64 emulation
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Install devcontainer CLI
run: npm install -g @devcontainers/[email protected]
- name: Test on ${{ matrix.image }} (arm64)
env:
DOCKER_DEFAULT_PLATFORM: linux/arm64
run: |
devcontainer features test \
--features claude-code \
--skip-scenarios \
--base-image "${{ matrix.image }}" \
--project-folder . 2>&1 | tee /tmp/test-output.log
# Workaround: devcontainers/[email protected] exits 0 even when feature install fails.
# Grep for known failure strings and fail explicitly. Revisit on CLI upgrade.
if grep -qE "Exit code [1-9][0-9]*|failed to install|Failed to launch|Failed:| FAIL:" /tmp/test-output.log; then
echo "ERROR: Test output contains failures."
exit 1
fi
- name: Annotate install warnings
if: always()
run: |
mapfile -t warnings < <(grep -oP '(?<=claude-code feature\] WARNING: ).*' /tmp/test-output.log 2>/dev/null | tr -d '\r' | sort -u || true)
if [[ ${#warnings[@]} -gt 0 ]]; then
echo "## :warning: Install Warnings" >> "$GITHUB_STEP_SUMMARY"
for msg in "${warnings[@]}"; do
echo "- ${msg}" >> "$GITHUB_STEP_SUMMARY"
echo "::warning title=Install Warning::${msg}"
done
fi
- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: logs-arm64-${{ strategy.job-index }}
path: /tmp/test-output.log
retention-days: 7