diff --git a/.agents/skills/add-module/SKILL.md b/.agents/skills/add-module/SKILL.md index 3322878bf..1d30586db 100644 --- a/.agents/skills/add-module/SKILL.md +++ b/.agents/skills/add-module/SKILL.md @@ -31,6 +31,7 @@ For the container class, tests and docs page, start from the module closest in s - 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). +- If the vendor supports more than one major version of the image, loop the suite over an `IMAGES` list (AGENTS.md "Tests"). - 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 diff --git a/.agents/skills/update-dependencies/SKILL.md b/.agents/skills/update-dependencies/SKILL.md index aa1da46d3..bf66bf043 100644 --- a/.agents/skills/update-dependencies/SKILL.md +++ b/.agents/skills/update-dependencies/SKILL.md @@ -27,6 +27,21 @@ Don't work from a fixed list of failure causes. Packages, images and registries The AGENTS.md rules on runtime dependencies and on module images apply to every bump. +## Supported image versions + +A module's tests run against every major version the vendor still supports, from the `IMAGES` list in its test file (AGENTS.md "Tests"). Dependabot only bumps the `Dockerfile`, so the older majors in that list are kept current here. Do this on every image bump for a module, and when changing a module for any other reason: + +1. List the supported major versions from the vendor's lifecycle page or endoflife.date. +2. Compare them with the module's `IMAGES` list: + - **A listed major is past end of life:** remove it from the list. Remove the module code that exists only for it in a separate PR, since that is a breaking change. + - **A supported major is missing:** add the exact tag of its latest release. + - **A listed major has a newer minor or patch:** move it to that tag. +3. Check each new tag with `docker manifest inspect `. A registry can list a tag before its manifest exists, and the tag should have the same architectures as the `Dockerfile` image. +4. Check that the `Dockerfile` is on the newest supported version, and move it by hand if not. Dependabot has left a `Dockerfile` behind when the vendor changed tag format (Neo4j and ScyllaDB moved to calendar versions) and when the tag is not a plain version (SQL Server `2022-CU13-ubuntu-22.04`). +5. When the bump moves the `Dockerfile` to a new major, add the major it leaves to `IMAGES` in the same PR, if the vendor still supports it. +6. If a supported major fails with the module as changed for the bump, keep the old path for it. Majors that differ in ports, auth or startup get their own class (`add-module`). Dropping a supported major is a breaking change. +7. Check the module source for an image the module starts itself, such as a video recorder or the database behind an emulator. Dependabot doesn't see those. Move it to its latest exact tag and run the module's tests. + ## An image no longer pulls - Find a replacement. Prefer, in order: diff --git a/AGENTS.md b/AGENTS.md index 37dea63bf..5892f01b6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,7 +56,18 @@ If a skill or this file turns out to be wrong or incomplete, update it in the sa - Every extra image increases how long the tests take to run and how much storage they need. - Reuse an image the tests already use, for example `cristianrgreco/testcontainer:1.1.14` in `packages/testcontainers`, or the module's own image. - Module tests read their images from `packages/modules//Dockerfile` (one `FROM` line per image) with `getImage(__dirname, index)`, so Dependabot can bump them. Do not hardcode images in module source or tests. - - Exception: a second version of the *same* image must be hardcoded in its test file (for example `influxdb1-container.test.ts`, `kafka-container-7.test.ts`). Dependabot treats same-image `FROM` lines as one dependency and bumps them all to the newest tag. + - Exception: a second version of the *same* image must be hardcoded in its test file. Dependabot treats same-image `FROM` lines as one dependency and bumps them all to the newest tag. +- A module's tests cover every major version the vendor still supports, so a change can't silently break one that users still run. This is the one case where a suite pulls extra images. + - Keep one `IMAGES` list in the test file: the latest release of each older supported major, oldest first, as an exact tag, then the `Dockerfile` image. Don't list other minors or patches of a major, and don't use rolling tags such as `redis:7`. + - This applies to every image the module itself runs, so a second class, edition or variant with its own image gets its own list. Images that only help the tests, such as a CLI or the database behind an emulator, are exempt. + - Loop the whole suite over the list with `describe.each(IMAGES)`, so every test runs on every major. + - Run the majors one after another and each major's tests concurrently, as a single-image suite runs: pass `{ concurrent: false }` to `describe.each` and declare the tests with `it.concurrent`. + - A major is supported while the vendor's free or community support covers it. Leave out a major that ends within 60 days. If the vendor publishes no lifecycle, test only the `Dockerfile` image. + - Where a test only applies to some of the images, select it by matching the image with `it.skipIf` or `it.runIf`, and add a comment when the reason isn't obvious. + - A major that needs its own class or setup gets its own test file named after it, such as `-container-v.test.ts`. + - A suite too heavy to run on every major may loop a representative part of itself. Say which part in the PR. + - If a looped suite still overloads the test runner (tests far slower than locally, connection timeouts, passing only on retry), consider running its tests sequentially too, with plain `it`, and point the change out to the user. + - Versions past the vendor's end of life are out of scope. Remove their tests and the module code that exists only for them. Removing that code is a breaking change, so it goes in its own PR. - Vitest runs tests concurrently by default (`sequence.concurrent: true` in `vitest.config.ts`). - Tests that rely on shared/global mocks (for example `vi.spyOn` on shared loggers/singletons) can be flaky due to interleaving or automatic mock resets. - Prefer asserting observable behavior instead of shared global mock state when possible.