Repository navigation
Add agent skills for recurring repo workflows #1486
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
4c65e7b
Add agent skills for recurring repo workflows
cristianrgreco aa81c72
Merge remote-tracking branch 'origin/main' into claude/repo-skills-au…
cristianrgreco 14455df
Merge branch 'main' into claude/repo-skills-audit-83c819
cristianrgreco 980ac70
Apply review feedback on agent skills
github-actions[bot] 89876c3
Link the review-release skill for Claude Code
cristianrgreco 1d7567f
Point skills at every Testcontainers implementation, not just Java an…
github-actions[bot] 580c9cf
Copy module boilerplate from any module, not only mosquitto
github-actions[bot] d25f018
Diagnose CI from the run instead of a fixed list of failure patterns
github-actions[bot] e278a1a
Drop the 20-run flake loop from diagnose-ci
github-actions[bot] 8b6ad29
Let agents dry-run the publish workflow, but never publish
github-actions[bot] 50552c5
Stop the open-pr example from requiring a body file
github-actions[bot] 7c7e945
Type-check new modules and hand off to open-pr in add-module
cristianrgreco 9b21718
Tighten the review-release commands and the Action's approval scope
cristianrgreco 26a0dbf
Ask agents to keep code comments to a minimum
cristianrgreco 079b0db
Merge remote-tracking branch 'origin/main' into claude/repo-skills-au…
cristianrgreco d427093
Merge remote-tracking branch 'origin/main' into claude/repo-skills-au…
cristianrgreco 54b86be
Point skills at the root-cause, test-image and codeinclude rules
cristianrgreco 4758c73
Merge remote-tracking branch 'origin/main' into claude/repo-skills-au…
cristianrgreco 3725409
Follow the docs build into the Checks workflow
cristianrgreco 3042ec9
Merge remote-tracking branch 'origin/main' into claude/repo-skills-au…
cristianrgreco d91cb39
Group Development Expectations in AGENTS.md by topic
cristianrgreco 155d489
Work from the failing bump in update-dependencies
cristianrgreco 4aa8f84
Update CONTRIBUTING and list new modules in the catalog
cristianrgreco 9b33e58
Merge remote-tracking branch 'origin/main' into claude/repo-skills-au…
cristianrgreco faf903b
Mention knip in CONTRIBUTING
cristianrgreco 2283b0b
Correct three details in diagnose-ci and update-dependencies
cristianrgreco File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| --- | ||
| name: add-module | ||
| description: Adds a new Testcontainers module under packages/modules (container class, tests, Dockerfile image pin, docs page, mkdocs nav), or brings a contributor's module PR up to the repo's conventions. Use when asked to add, create, port or support a new container or service module (e.g. "add a RustFS module", "port the Java Pulsar module"), or when adding a second image or class to an existing module. | ||
| argument-hint: "[module name]" | ||
| --- | ||
|
|
||
| # Add a module | ||
|
|
||
| Copy `package.json`, `tsconfig.json` and `tsconfig.build.json` from any existing module. They have the same shape in every module. In `package.json`, change the name, description, keywords and `devDependencies`. Leave `version` and the `testcontainers` dependency as copied, because the release workflow sets both. | ||
|
|
||
| For the container class, tests and docs page, start from the module closest in shape to the new one (same kind of wait, auth or number of ports). The rules below are what reviewers keep flagging on module PRs. | ||
|
|
||
| ## Before writing code | ||
|
|
||
| - Check the module in the other implementations that have it (AGENTS.md "Cross-language Implementations"): the image, ports, wait strategy and defaults, and whether they split major versions into separate classes. | ||
| - Pin a concrete, current, multi-arch tag in the module `Dockerfile`, never `latest` or a floating major. `docker manifest inspect <image:tag>` should list both amd64 and arm64. | ||
| - The client library used in the tests goes in `devDependencies` (`npm install -w @testcontainers/<name> --save-dev <client>`). Users bring their own client. Add a runtime dependency only if the container class itself needs one. | ||
|
|
||
| ## Container class | ||
|
|
||
| - The constructor sets exposed ports, the wait strategy and `withStartupTimeout(120_000)`. Setting the wait strategy there lets users override it. | ||
| - Prefer listening-port, health-check or HTTP waits over log regexes. Log output changes between image versions. Shell-less images need a health-check or HTTP wait. | ||
| - Zero config must work: `new XContainer(IMAGE).start()` gives a container a client can connect to. Prefer defaults to getters that can return `undefined`. | ||
| - Validate `with*` inputs, and fail fast on half-set config (for example a username without a password) instead of waiting out the startup timeout. | ||
| - Started-container getters call `getMappedPort()` when they're invoked. A restarted container can get different host ports. | ||
| - Incompatible major versions (different ports, auth or startup) get separate classes, not image-tag parsing. | ||
| - Keep it small: no getters that exist only for tests, and no speculative options. | ||
|
|
||
| ## Tests | ||
|
|
||
| - Every test does a real client round trip, such as write then read, or publish then receive. Asserting that getters or connection strings look right proves nothing. Cover the default path and each option that changes behaviour. | ||
| - Use `await using`, with one container per test. Tests run concurrently. | ||
| - Import from the container file, not `./index`. Read images with `getImage(__dirname, index)` (AGENTS.md). | ||
| - Wrap the part a user would copy in `// name {` … `// }` markers inside the `it` body. The docs include these blocks, so give each marker a name that appears nowhere else in the file (AGENTS.md). | ||
|
|
||
| ## Docs and finish | ||
|
|
||
| - Copy the docs page of the module you started from. Examples come only from test blocks via `codeinclude`. Keep the "substitute `IMAGE`" line. | ||
| - Add the page to the `mkdocs.yml` Modules nav in alphabetical order. | ||
| - Run `npm run check-compiles` as well as `npx vitest run packages/modules/<name>`. Vitest and Biome don't type-check, so without it a type error in the module or its tests first shows up in CI's Compile job. | ||
| - Ship it with `open-pr`. The diff should contain only the module directory, the docs page, `mkdocs.yml`, and the lockfile entries for the new workspace and client. | ||
| - After the PR merges, list the module in the catalog at testcontainers.com/modules. Open a PR to `testcontainers/community-module-registry` that adds a `nodejs` entry to `modules/<name>/index.md`, following that repo's README. Create the folder if the module is new to the catalog. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| --- | ||
| name: diagnose-ci | ||
| description: Diagnoses red or flaky GitHub Actions checks in testcontainers-node. Reads the failed step's log and the workflow behind it, compares the run with main and the last green run, searches past PRs and issues for the error, and reproduces it locally before fixing or routing it. Use when CI, a workflow run or a job (Lint, Compile, Smoke tests, Tests) is failing or flaky, e.g. "why is CI red", "is this flaky", "fix the failing test in CI". | ||
| argument-hint: "[PR number or run id]" | ||
| --- | ||
|
|
||
| # Diagnose CI | ||
|
|
||
| Don't work from a fixed list of failure patterns. The workflows, the runner image, Podman and the images under test all keep changing. Work from the current run and what changed since the last green one. | ||
|
|
||
| ## 1. Read the failure | ||
|
|
||
| 1. Find the failed jobs with `gh pr checks <N>`, then read them with `gh run view <run-id> --log-failed`. Note the job, the step and the first error, not the last. | ||
| 2. Read what that step ran. These files are the source of truth: | ||
| - `.github/workflows/checks.yml` and `test-template.yml`: the jobs, their order and the matrix | ||
| - `.github/actions/`: the Node, npm and Podman setup | ||
| - `.github/scripts/changed-modules.mjs`: which packages ran, and why | ||
| 3. Check `retry` in `vitest.config.ts`. In CI, a red test failed every attempt, and a test that passed on a retry is still flaky. | ||
|
|
||
| ## 2. Find what changed | ||
|
|
||
| 1. Check whether `main` fails the same way: `gh run list --branch main --workflow checks.yml`. Pushes to `main` only run the tests for the packages they touch, so find the latest run that included this one. A change to core or root config runs them all. | ||
| 2. Compare with the last green run of the same job: | ||
| - In the repo: the PR diff, and `git log` for the files the step depends on, such as the module, its `Dockerfile`, `package-lock.json`, `.github/` and `vitest.config.ts`. | ||
| - Outside the repo: the runner image version ("Set up job") and the Podman version ("Verify Podman"). Both change without a commit. | ||
| 3. Search PRs and issues, open and closed, for the error. A past fix is the best lead: | ||
| - `gh pr list --state all --search "<error>"` | ||
| - `gh issue list --state all --search "<error>"` | ||
| 4. For a Podman-only failure, `git grep -n -B1 CI_PODMAN` lists the tests already skipped on Podman, and the upstream Podman issue where one is linked. | ||
|
|
||
| ## 3. Reproduce | ||
|
|
||
| - Run the failing file locally (`npx vitest run <file>`) on the same runtime and Node version. For Podman, set `DOCKER_HOST` to the Podman socket and `CI_PODMAN=true`, as `podman-setup` does. | ||
| - If it fails only in CI and is unrelated to the diff, rerun the failed jobs once (`gh run rerun <run-id> --failed`). Still report it as a flake, with the log link. | ||
|
|
||
| ## 4. Fix or route | ||
|
|
||
| - A dependency or image bump caused it: `update-dependencies`. | ||
| - It fails every time: the change from step 2 shows whether the code or the test is wrong. Fix that one. | ||
| - It's flaky: treat it as a bug. Look for a wait that passes before the service is ready, state shared between concurrent tests, or a timeout too tight for a slow image. Fix the root cause. Delays, retries and longer timeouts only hide a race (AGENTS.md). | ||
| - Ship the fix with `open-pr`. For a flaky test, link the failed CI log as the red evidence. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| --- | ||
| name: open-pr | ||
| description: Verifies, commits, pushes and opens a pull request in testcontainers-node following the repo's title, label and PR-body conventions. Use when work is ready to ship ("open a PR", "commit this", "push and raise a PR", "write the PR description") or when choosing a PR title or labels. | ||
| --- | ||
|
|
||
| # Open a PR | ||
|
|
||
| Release Drafter turns PR titles into release notes and labels into the version bump (`.github/release-drafter.yml`). Titles and labels matter as much as the code. | ||
|
|
||
| Before committing, pushing or opening anything, get the user's approval of the diff, commit message, title and body (AGENTS.md). | ||
|
|
||
| ## Before committing | ||
|
|
||
| - Branch from an up-to-date `main`. | ||
| - Run the checks in AGENTS.md "Verification". For bug fixes, keep the red-green output. | ||
| - Check that `git diff --stat main...HEAD` shows only the files you intended, and that the lockfile changes only the entries you intended. | ||
|
|
||
| ## Commit and push | ||
|
|
||
| - Commit with `git commit`. Never bypass signing (`--no-gpg-sign`, `-c commit.gpgsign=false`). If signing fails, stop and ask the user to fix it, then retry. | ||
| - Push with `git push -u origin <branch>`. Ask before any force push, including after rebasing an open PR. | ||
|
|
||
| ## Dry-run the publish | ||
|
|
||
| Run the publish dry run against the pushed branch when the change is large or possibly breaking, or could affect the release: GitHub Actions, Node or npm versions, the publish automation, package manifests or build config. A manual dispatch builds every package and runs `npm publish --ws --dry-run`. It doesn't commit, push or publish (AGENTS.md "Releases"). For the version, use the draft release's tag, the row marked `Draft` in `gh release list --limit 3`. | ||
|
|
||
| ```bash | ||
| gh workflow run npm-publish.yml --ref <branch> -f version=<next version> | ||
| gh run list --workflow npm-publish.yml --event workflow_dispatch --branch <branch> --limit 1 | ||
| gh run watch <run-id> | ||
| ``` | ||
|
|
||
| Link the run in the PR body. Dispatching needs write access to the repo. Without it, for example from a fork, say in the PR body that the change needs a dry run, so a maintainer can run it before merging. | ||
|
|
||
| ## Title | ||
|
|
||
| Write it as an imperative release-note line about the user-visible change. Don't use conventional-commit prefixes, agent names or branch names. | ||
|
|
||
| | Good | Bad | | ||
| | --- | --- | | ||
| | `Add Mosquitto module` | `Adding module mosquitto`, `feat(mosquitto): add module` | | ||
| | `Fix container exec output truncation` | `Fixed exec truncation`, `fix: exec` | | ||
|
|
||
| ## Labels | ||
|
|
||
| Every PR gets exactly one change-type label and one semver label. | ||
|
|
||
| | Change | Labels | | ||
| | --- | --- | | ||
| | Feature or new module | `enhancement` + `minor` | | ||
| | Bug fix | `bug` + `patch` | | ||
| | Breaking change (removed or renamed export, changed default, ESM-only runtime dependency, higher Node floor) | type label + `major` | | ||
| | Docs only | `documentation` + `patch` | | ||
| | Dependency update | `dependencies` + its user-facing impact | | ||
| | CI, tooling, tests, refactors | `maintenance` + `patch` | | ||
|
|
||
| ## Body | ||
|
|
||
| Include: | ||
|
|
||
| - **Summary:** what changed and why. If you followed another Testcontainers implementation, link the code you followed (AGENTS.md "Cross-language Implementations"). | ||
| - **Verification:** the commands you ran and their results, including red-green evidence for fixes. | ||
| - **Not breaking** (unless the PR is labelled `major`): why the change is backward compatible. | ||
| - `Closes #<issue>`, only if the PR fully resolves that issue. | ||
|
|
||
| ```bash | ||
| gh pr create --base main --title "<title>" --body "<body>" --label <type> --label <semver> | ||
| ``` | ||
|
|
||
| Open it ready for review. Only open it as a draft if the user asks. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| --- | ||
| name: review-pr | ||
| description: Reviews a testcontainers-node pull request (own or a contributor's) against the maintainer's recurring review feedback and drafts terse inline comments for approval. Use when asked to review, check, look over or give feedback on a PR, PR number, branch or diff, for a self-review before opening a PR, and when the @claude GitHub Action is asked to review. | ||
| argument-hint: "[PR number]" | ||
| --- | ||
|
|
||
| # Review a PR | ||
|
|
||
| Aim to raise in one pass everything the maintainer would otherwise raise over several rounds. | ||
|
|
||
| ## Gather | ||
|
|
||
| ```bash | ||
| gh pr view <N> --json title,body,labels,headRefName,comments,reviews | ||
| gh api repos/testcontainers/testcontainers-node/pulls/<N>/comments # inline review comments | ||
| gh pr diff <N> | ||
| gh pr checks <N> | ||
| ``` | ||
|
|
||
| - Read the linked issue, and read the surrounding code as well as the hunks. | ||
| - Treat earlier review rounds as context. Check that previously requested changes were made, and don't repeat points already raised. | ||
| - If CI is red, find out why first (`diagnose-ci`). | ||
| - For fork PRs, `gh pr checks` can look green while the workflows wait for approval. Check `gh run list --branch <head>`. If CI hasn't run, say so. | ||
| - For a new module, also apply the `add-module` rules. | ||
|
|
||
| ## What to look for | ||
|
|
||
| - **Title and labels:** they follow `open-pr`, and the semver label matches the real impact. | ||
| - **Tests:** | ||
| - Each test can actually fail. Flag tests that only read back getters, check a string's shape, or assert `toBeDefined()` on an API that returns 200 on errors. | ||
| - Each option has a test that would fail if the option never reached the container. | ||
| - Bug fixes come with red-green evidence. | ||
| - New cases extend the nearest existing test file with the same setup (real Docker or a mocked client) rather than adding new files. | ||
| - Tests pull no image the suite doesn't already pull (AGENTS.md). | ||
| - **Concurrency:** no shared containers, `process.env` or spies without `{ concurrent: false }`. Use `await using`. No skipping when Docker is unavailable. | ||
| - **Root cause:** a fix removes the cause of the failure. Added sleeps, retries or longer timeouts only make it less likely (AGENTS.md). | ||
| - **Completeness:** a fix applied to one path is also applied to its siblings, for example `restart()` next to `start()`, or the reuse path next to the create path. | ||
| - **Docs:** public API changes are documented, and module examples use `codeinclude`. Check that each marker name appears only once in its test file; CI can't catch a collision (AGENTS.md). | ||
| - **Design:** | ||
| - Zero-config defaults work, and invalid or half-set config fails fast. | ||
| - The wait strategy is set in the constructor, and waits are robust (listening ports, health check or HTTP rather than log regexes). | ||
| - Images are pinned in the module `Dockerfile`. | ||
| - **Scope:** | ||
| - Flag new files for a few lines of logic, dead fallbacks, duplicated constants, getters that exist only for tests, and tests of third-party behaviour. | ||
| - Flag unrelated dependency bumps; leave those to Dependabot. | ||
| - Flag code comments that restate the code or describe the change (AGENTS.md). | ||
| - **Dependencies:** runtime dependencies load from CommonJS (AGENTS.md), and well-established libraries or built-ins are preferred. | ||
| - **Breaking changes:** renamed exports, changed defaults and lowered timeouts all count. They need `major` or a non-breaking alternative. | ||
| - **Claims:** check root-cause explanations, and claims about what another Testcontainers implementation does, against the actual code. AI-written PR descriptions are often confidently wrong. | ||
|
|
||
| ## Write the comments | ||
|
|
||
| - Anchor each comment on the line it concerns, one problem per comment. Findings outside the diff hunks, such as an untouched sibling path, go in the review body. Say what's wrong, add a sentence of context if it helps, then say what to do instead. Skip restated background and severity labels. | ||
| - Keep the review body to the few must-address points, plus any high-level design note. A short thanks, a numbered list of required changes, then "Nits" matches the maintainer's style. | ||
| - Before calling something a convention, check the rest of the repo. Prefer "the other modules do X" to broad claims. | ||
| - If there's nothing worth raising, say so. | ||
|
|
||
| ## Post | ||
|
|
||
| **Locally:** show the user every comment (path, line, text), the review body, and any suggested title or label changes. Post nothing until they approve. Then post a single review: | ||
|
|
||
| ```bash | ||
| gh api --method POST repos/testcontainers/testcontainers-node/pulls/<N>/reviews --input review.json | ||
| # {"event":"COMMENT","body":"...","comments":[{"path":"...","line":42,"side":"RIGHT","body":"..."}]} | ||
| ``` | ||
|
|
||
| **As the `@claude` GitHub Action:** the triggering comment is the approval, so post the inline comments directly. Put any title or label suggestions in your reply; never change labels, approve or merge. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| --- | ||
| name: review-release | ||
| description: Reviews the draft testcontainers-node GitHub release before a maintainer publishes it, checking labels, the version bump, release-note titles and hidden breaking changes, and dry-runs the publish workflow when it has permission. Use when asked to check, review or prepare the next release or its draft notes. Never publishes; maintainers release by publishing the draft on GitHub. | ||
| argument-hint: "[version]" | ||
| --- | ||
|
|
||
| # Review a release | ||
|
|
||
| Release Drafter keeps a draft GitHub release current as PRs merge. A maintainer publishes that draft on GitHub, which runs `npm-publish.yml` to bump every workspace, push the `v<version>` commit to `main` and publish to npm. | ||
|
|
||
| Don't publish or edit the release, run `npm publish` without `--dry-run`, re-run a publish run that a release started, or change PR titles or labels (AGENTS.md "Releases"). The only thing you start is the dry run in step 3. Report what you find. | ||
|
|
||
| ## 1. Review the draft | ||
|
|
||
| 1. Find the draft with `gh release list --limit 3`. It is the row marked `Draft`, and is only listed for accounts with write access. Read it with `gh release view v<draft>`. | ||
| 2. List the PRs merged since the last release: `gh pr list --state merged --limit 200 --search "merged:>=<last release date>" --json number,title,labels`. | ||
| 3. Check: | ||
| - Every PR has a type label and a semver label. Without a type label, a PR is listed uncategorised at the top of the notes. Without a semver label, it counts as patch. | ||
| - The version is right for the highest semver label. | ||
| - The titles read as release notes. | ||
|
|
||
| ## 2. Look for hidden breaking changes | ||
|
|
||
| - Diff runtime dependencies since the last tag: `git diff v<last>..main -- 'packages/**/package.json'`. Ignore the `version` and `testcontainers` lines in every manifest. The tag sits one commit before its own version-bump commit. | ||
| - Flag any new major version that is ESM-only (AGENTS.md). | ||
| - Also check whether `engines.node` changed, or exports were removed or renamed. | ||
|
|
||
| ## 3. Dry run | ||
|
|
||
| If you have permission to dispatch workflows, dry-run the publish on `main` with the draft's version. A manual dispatch builds every package and runs `npm publish --ws --dry-run`. It doesn't commit, push or publish. | ||
|
|
||
| ```bash | ||
| gh workflow run npm-publish.yml --ref main -f version=<x.y.z> | ||
| gh run list --workflow npm-publish.yml --event workflow_dispatch --branch main --limit 1 | ||
| gh run watch <run-id> | ||
| ``` | ||
|
|
||
| Check that the run is named `Dry run publish <x.y.z>` and passes. | ||
|
|
||
| ## 4. Report | ||
|
|
||
| List each PR whose labels or title need changing, and each possible breaking change, with the fix you suggest. Link the dry run and its result, or say you couldn't run it. The maintainer applies the fixes and publishes. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.