Skip to content

ci: Add template integration test pipeline#23

Merged
stranma merged 4 commits intomasterfrom
feat/template-integration-ci
Mar 9, 2026
Merged

ci: Add template integration test pipeline#23
stranma merged 4 commits intomasterfrom
feat/template-integration-ci

Conversation

@stranma
Copy link
Owner

@stranma stranma commented Mar 9, 2026

Summary

  • Adds a GitHub Actions workflow (template-integration.yml) that applies setup_project.py with 5 different configurations in a matrix and verifies each produces a valid, working project
  • Includes a reusable shell script (scripts/test_template_integration.sh) that can also be run locally for debugging
  • Fixes pre-existing bug in test_hooks.py where devcontainer-policy-blocker.sh was not in the expected hooks list, and sets its missing executable bit in git

Matrix Configurations

Config Type Packages What It Tests
mono-default mono core,server Happy path
mono-renamed mono engine,daemon Package renaming + cross-refs
mono-extra-pkgs mono engine,lib:utils,daemon,worker Additional package creation
single-package single core,server Single-package layout conversion
mono-postgres mono core,server + postgres Docker Compose generation

Verification Steps (per config)

  1. Copy template to clean temp directory
  2. Run setup_project.py with CLI args
  3. Verify no {{placeholder}} patterns remain
  4. Verify correct directory structure (mono vs single)
  5. uv sync dependencies
  6. ruff check + ruff format --check
  7. pyright type checking
  8. pytest test suite
  9. Docker Compose validation (if services configured)

Test plan

  • Smoke tested mono-default, mono-renamed, and single-package configs locally (steps 1-7 pass; step 8 only fails on Windows due to missing Unix file permission support)
  • Verify all 5 matrix jobs pass in CI (Linux)
  • Verify unit-tests gate job passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • New CI workflow for template integration testing across multiple configurations; validates install, linting, type checking, and test execution for generated projects
    • Added a reusable end-to-end template validation script for local and CI runs, including project setup, placeholder checks, dependency install, and service validation
  • Documentation

    • Updated changelog and added a decision entry describing the template integration CI approach
  • Chores

    • Enhanced development container hook configuration

GitHub Actions workflow that applies setup_project.py with 5 different
configurations (mono-default, mono-renamed, mono-extra-pkgs,
single-package, mono-postgres) and verifies each produces a valid
project (uv sync, ruff, pyright, pytest). Includes reusable shell
script for local testing.

Also fixes pre-existing test_hooks.py bug: adds devcontainer-policy-blocker.sh
to expected hooks list and sets its executable bit in git.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

Warning

Rate limit exceeded

@stranma has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 7 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 777be14a-8cc6-4390-a694-9ea3e0ece74d

📥 Commits

Reviewing files that changed from the base of the PR and between 76192b6 and 4a189c5.

📒 Files selected for processing (1)
  • scripts/test_template_integration.sh
📝 Walkthrough

Walkthrough

Adds a GitHub Actions workflow for template integration testing, a reusable Bash test harness to validate generated templates across multiple configurations, and updates test hooks to include a devcontainer policy hook. The CI runs unit tests then matrixed integration tests invoking the script per configuration.

Changes

Cohort / File(s) Summary
CI Workflow
.github/workflows/template-integration.yml
New GitHub Actions workflow "Template Integration Tests" with unit-tests job and a matrixed integration-test job (5 configurations). Integration job runs scripts/test_template_integration.sh with matrix-provided params.
Integration Test Script
scripts/test_template_integration.sh
New Bash script implementing a 9-step template validation flow: copy template, apply via setup_project.py, scan for placeholders, verify structure, install deps (uv), lint (ruff), type-check (pyright), run tests (pytest), and validate .devcontainer/docker-compose.yml when services present.
Test Hooks
tests/test_hooks.py
Adds DEVCONTAINER_HOOKS containing devcontainer-policy-blocker.sh and includes it in ALL_HOOKS alongside existing security and productivity hooks.
Documentation
docs/CHANGELOG.md, docs/DECISIONS.md
Adds changelog entry and a decision note describing the Template Integration CI pipeline and related test script and matrix.

Sequence Diagram(s)

sequenceDiagram
    participant GHA as GitHub Actions
    participant Unit as Unit Tests Job
    participant Integration as Integration Tests Job
    participant Script as test_template_integration.sh
    participant Template as Template Files
    participant Tools as Tools (uv, ruff, pyright, pytest)

    GHA->>Unit: trigger unit-tests
    Unit->>Unit: run pytest tests/ -v
    Unit-->>GHA: report success

    GHA->>Integration: trigger integration-test (matrix)
    Integration->>Script: invoke with config args
    Script->>Template: copy template to work-dir
    Script->>Template: run setup_project.py to apply template
    Script->>Template: scan for unresolved placeholders
    Script->>Template: verify directory structure (mono/single)
    Script->>Tools: run uv sync (install deps)
    Script->>Tools: run ruff (lint + format)
    Script->>Tools: run pyright (type check)
    Script->>Tools: run pytest (package tests)
    Script->>Template: validate .devcontainer/docker-compose.yml (if services)
    Script-->>Integration: return pass/fail
    Integration-->>GHA: report matrix results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ci: Add template integration test pipeline' directly and accurately summarizes the main change: introducing a GitHub Actions workflow for testing template integration across multiple configurations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/template-integration-ci

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

stranma and others added 2 commits March 10, 2026 00:06
Add comment explaining cd $WORK_DIR governs all subsequent steps,
and note that --packages is ignored in single-package mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/test_template_integration.sh`:
- Around line 42-45: Reject unsafe work directories before running the recursive
delete: in the validation block that currently checks SOURCE_DIR and WORK_DIR
non-empty, add explicit guards in the script that verify WORK_DIR is not equal
to SOURCE_DIR, is not "/", "." or "~", is not the user's home directory, and is
not the repository root (or any shared/global path you want to protect); if any
check fails, print an error and exit. Ensure the same guards are applied before
the rm -rf "$WORK_DIR" call (referencing the WORK_DIR and SOURCE_DIR variables
and the rm -rf invocation) so a bad value is rejected early.
- Around line 137-145: The test currently only checks for top-level apps/libs
when PROJECT_TYPE != "single"; update the test to validate the exact package set
in $PACKAGES: parse $PACKAGES (comma/space-separated), build the expected
relative paths under apps/ or libs/ depending on PROJECT_TYPE, then assert each
expected directory exists and that the set of actual directories under apps/ and
libs/ exactly matches the expected set (fail if unexpected/stale default dirs
are present); reference the PROJECT_TYPE and PACKAGES variables and ensure the
new checks replace or augment the existing apps/libs existence assertions so
mono-renamed and mono-extra-pkgs matrix cases fail if the package
rename/extra-packages logic in setup_project.py regresses.
- Around line 192-199: The current check only ensures a top-level 'services:'
key exists in .devcontainer/docker-compose.yml but doesn't verify the requested
service; update the block that runs when SERVICES != "none" to also validate
that the compose file contains the specific service named by the SERVICES
variable (e.g., look for a service key matching "$SERVICES" under services), and
call step_fail with a clear message if that service is missing before calling
step_pass; use the existing SERVICES variable, the
.devcontainer/docker-compose.yml path, and the step_fail/step_pass helpers to
implement this additional check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8dafaaa-f666-4146-9117-c644127fe1be

📥 Commits

Reviewing files that changed from the base of the PR and between 4c2383d and 3202ea0.

📒 Files selected for processing (4)
  • .claude/hooks/devcontainer-policy-blocker.sh
  • .github/workflows/template-integration.yml
  • scripts/test_template_integration.sh
  • tests/test_hooks.py

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/test_template_integration.sh`:
- Around line 115-126: The grep currently only searches a fixed set of file
extensions so extensionless text files (e.g., .devcontainer/Dockerfile) can
escape detection; update the PLACEHOLDER_HITS search (the command that uses
PLACEHOLDER_PATTERN and --include/--exclude flags) to also scan extensionless
files by either adding a broad --include='*' (and keep relevant --exclude
entries) or explicitly including common extensionless filenames like Dockerfile
and CI config names so all text files are checked for TEMPLATE placeholders.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3e79777c-4467-43e4-9ff5-1dfeb578d2a7

📥 Commits

Reviewing files that changed from the base of the PR and between 3202ea0 and 76192b6.

📒 Files selected for processing (4)
  • .github/workflows/template-integration.yml
  • docs/CHANGELOG.md
  • docs/DECISIONS.md
  • scripts/test_template_integration.sh

Comment on lines +115 to +126
# Search for actual template placeholders (not GitHub Actions ${{ }} expressions).
# The pattern matches {{word}} but NOT ${{word}} (GHA syntax).
# Excludes setup_project.py (defines them) and test files (reference them in fixtures).
PLACEHOLDER_PATTERN='\{\{(project_name|namespace|description|author_name|author_email|python_version|base_branch|year)\}\}'
PLACEHOLDER_HITS=$(grep -rE "$PLACEHOLDER_PATTERN" \
--include='*.py' --include='*.toml' --include='*.yml' --include='*.yaml' \
--include='*.md' --include='*.json' --include='*.cfg' --include='*.ini' \
--include='*.txt' --include='*.sh' \
--exclude='setup_project.py' \
--exclude='test_setup_project.py' \
--exclude='test_template_integration.sh' \
. 2>/dev/null || true)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Scan all text files for unreplaced placeholders.

This allowlist skips extensionless text files, so files like .devcontainer/Dockerfile can retain {{python_version}} and Step 3 still passes. That defeats the main “template fully applied” assertion.

Suggested fix
-PLACEHOLDER_HITS=$(grep -rE "$PLACEHOLDER_PATTERN" \
-    --include='*.py' --include='*.toml' --include='*.yml' --include='*.yaml' \
-    --include='*.md' --include='*.json' --include='*.cfg' --include='*.ini' \
-    --include='*.txt' --include='*.sh' \
+PLACEHOLDER_HITS=$(grep -rIE "$PLACEHOLDER_PATTERN" \
     --exclude='setup_project.py' \
     --exclude='test_setup_project.py' \
     --exclude='test_template_integration.sh' \
     . 2>/dev/null || true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Search for actual template placeholders (not GitHub Actions ${{ }} expressions).
# The pattern matches {{word}} but NOT ${{word}} (GHA syntax).
# Excludes setup_project.py (defines them) and test files (reference them in fixtures).
PLACEHOLDER_PATTERN='\{\{(project_name|namespace|description|author_name|author_email|python_version|base_branch|year)\}\}'
PLACEHOLDER_HITS=$(grep -rE "$PLACEHOLDER_PATTERN" \
--include='*.py' --include='*.toml' --include='*.yml' --include='*.yaml' \
--include='*.md' --include='*.json' --include='*.cfg' --include='*.ini' \
--include='*.txt' --include='*.sh' \
--exclude='setup_project.py' \
--exclude='test_setup_project.py' \
--exclude='test_template_integration.sh' \
. 2>/dev/null || true)
# Search for actual template placeholders (not GitHub Actions ${{ }} expressions).
# The pattern matches {{word}} but NOT ${{word}} (GHA syntax).
# Excludes setup_project.py (defines them) and test files (reference them in fixtures).
PLACEHOLDER_PATTERN='\{\{(project_name|namespace|description|author_name|author_email|python_version|base_branch|year)\}\}'
PLACEHOLDER_HITS=$(grep -rIE "$PLACEHOLDER_PATTERN" \
--exclude='setup_project.py' \
--exclude='test_setup_project.py' \
--exclude='test_template_integration.sh' \
. 2>/dev/null || true)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/test_template_integration.sh` around lines 115 - 126, The grep
currently only searches a fixed set of file extensions so extensionless text
files (e.g., .devcontainer/Dockerfile) can escape detection; update the
PLACEHOLDER_HITS search (the command that uses PLACEHOLDER_PATTERN and
--include/--exclude flags) to also scan extensionless files by either adding a
broad --include='*' (and keep relevant --exclude entries) or explicitly
including common extensionless filenames like Dockerfile and CI config names so
all text files are checked for TEMPLATE placeholders.

- Reject unsafe --work-dir values (/, $HOME, same as --source-dir)
  before the rm -rf to prevent accidental data loss
- Verify each requested package exists in the correct directory
  (libs/ or apps/) based on the lib:/app: prefix convention, catching
  rename regressions that leave default names in place
- Check that the specific service (db, redis) is defined in the
  generated docker-compose.yml, not just that the file exists

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@stranma stranma merged commit 5c58f88 into master Mar 9, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant