Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions .github/actions/npm-setup/action.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
name: "NPM Setup"
description: "Sets up Node.js and installs NPM dependencies with caching"
description: "Sets up JS runtime and installs NPM dependencies with caching"

inputs:
runner:
description: "Runner to use"
required: true
node-version:
description: "Node.js version to use"
js-runtime:
description: "JS runtime to use"
required: true
js-runtime-version:
description: "JS runtime version to use"
required: true
workspace:
description: "Key for the cache"
Expand All @@ -16,14 +19,27 @@ outputs:
workspace_path:
description: "Full path to the workspace directory"
value: ${{ steps.set-env.outputs.workspace_path }}
cmd:
description: "Command prefix (e.g. 'node' or 'bun')"
value: ${{ steps.set-env.outputs.cmd }}
script_cmd:
description: "Command prefix to run package scripts (e.g. 'npm run' or 'bun run')"
value: ${{ steps.set-env.outputs.script_cmd }}

runs:
using: "composite"
steps:
- name: Install NodeJS ${{ inputs.node-version }}
- name: Install NodeJS ${{ inputs.js-runtime-version }}
if: ${{ inputs.js-runtime == 'node' }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
node-version: ${{ inputs.js-runtime-version }}

- name: Install Bun ${{ inputs.js-runtime-version }}
if: ${{ inputs.js-runtime == 'bun' }}
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ inputs.js-runtime-version }}

- name: Set cache configuration
shell: bash
Expand All @@ -46,11 +62,19 @@ runs:
echo "workspace_path=packages/modules/${{ inputs.workspace }}" >> "$GITHUB_OUTPUT"
fi

if [ "${{ inputs.js-runtime }}" = "node" ]; then
echo "cmd=node" >> "$GITHUB_OUTPUT"
echo "script_cmd=npm" >> "$GITHUB_OUTPUT"
elif [ "${{ inputs.js-runtime }}" = "bun" ]; then
echo "cmd=bun" >> "$GITHUB_OUTPUT"
echo "script_cmd=bun" >> "$GITHUB_OUTPUT"
fi

- uses: actions/cache/restore@v4
id: npm-cache
with:
path: ${{ env.CACHE_PATHS }}
key: ${{ inputs.runner }}-node-${{ inputs.node-version }}-${{ inputs.workspace }}-${{ hashFiles('package-lock.json') }}
key: ${{ inputs.runner }}-runtime-${{ inputs.js-runtime-version }}-${{ inputs.workspace }}-${{ hashFiles('package-lock.json') }}

- name: Install dependencies
if: steps.npm-cache.outputs.cache-hit != 'true'
Expand Down
82 changes: 62 additions & 20 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,25 @@ jobs:
strategy:
fail-fast: false
matrix:
js-runtime:
- { name: node, version: 24.x }
module: ${{ fromJSON(needs.detect-modules.outputs.modules) }}
runs-on: ubuntu-22.04
steps:
- name: Code checkout
uses: actions/checkout@v5
- name: Install Node and Dependencies
id: npm-install-modules
- name: Install ${{ matrix.js-runtime.name }} and Dependencies
id: npm-install
uses: ./.github/actions/npm-setup
with:
runner: ubuntu-22.04
node-version: 24.x
js-runtime: ${{ matrix.js-runtime.name }}
js-runtime-version: ${{ matrix.js-runtime.version }}
workspace: "${{ matrix.module }}"
- name: Code linting
env:
WORKSPACE_PATH: ${{ steps.npm-install-modules.outputs.workspace_path }}
run: npm run lint:ci
WORKSPACE_PATH: ${{ steps.npm-install.outputs.workspace_path }}
run: ${{ steps.npm-install.outputs.script_cmd }} run lint:ci

compile:
if: ${{ needs.detect-modules.outputs.modules_count > 0 }}
Expand All @@ -78,23 +81,26 @@ jobs:
strategy:
fail-fast: false
matrix:
js-runtime:
- { name: node, version: 24.x }
module: ${{ fromJSON(needs.detect-modules.outputs.modules) }}
runs-on: ubuntu-22.04
steps:
- name: Code checkout
uses: actions/checkout@v5
- name: Install Node and Dependencies
- name: Install ${{ matrix.js-runtime.name }} and Dependencies
id: npm-install
uses: ./.github/actions/npm-setup
with:
runner: ubuntu-22.04
node-version: 24.x
js-runtime: ${{ matrix.js-runtime.name }}
js-runtime-version: ${{ matrix.js-runtime.version }}
workspace: "${{ matrix.module }}"
- name: Compile
run: |
npm run build --ignore-scripts --workspace packages/testcontainers -- --project tsconfig.json
${{ steps.npm-install.outputs.script_cmd }} run build --ignore-scripts --workspace packages/testcontainers -- --project tsconfig.json
if [ "${{ matrix.module }}" != "testcontainers" ]; then
npm run build --ignore-scripts --workspace ${{ steps.npm-install.outputs.workspace_path }} -- --project tsconfig.json --noEmit
${{ steps.npm-install.outputs.script_cmd }} run build --ignore-scripts --workspace ${{ steps.npm-install.outputs.workspace_path }} -- --project tsconfig.json --noEmit
fi

smoke-test:
Expand All @@ -107,31 +113,62 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [20.x, 22.x, 24.x]
js-runtime:
- { name: node, version: 20.x }
- { name: node, version: 22.x }
- { name: node, version: 24.x }
- { name: bun, version: 1.3.0 }
runs-on: ubuntu-22.04
steps:
- name: Code checkout
uses: actions/checkout@v5
- name: Install Node ${{ matrix.node-version }} and Dependencies
- name: Install ${{ matrix.js-runtime.name }} ${{ matrix.js-runtime-version }} and Dependencies
id: npm-install
uses: ./.github/actions/npm-setup
with:
runner: ubuntu-22.04
node-version: ${{ matrix.node-version }}
js-runtime: ${{ matrix.js-runtime.name }}
js-runtime-version: ${{ matrix.js-runtime.version }}
workspace: "testcontainers"
- name: Build testcontainers
run: npm run build --workspace packages/testcontainers
- name: Remove dev dependencies
run: npm prune --omit=dev --workspace packages/testcontainers
- name: Run CommonJS module smoke test
run: node packages/testcontainers/smoke-test.js
run: ${{ steps.npm-install.outputs.cmd }} packages/testcontainers/smoke-test.js
env:
DEBUG: "testcontainers*"
- name: Run ES module smoke test
run: node packages/testcontainers/smoke-test.mjs
run: ${{ steps.npm-install.outputs.cmd }} packages/testcontainers/smoke-test.mjs
env:
DEBUG: "testcontainers*"

test:
test-docker:
if: ${{ needs.detect-modules.outputs.modules_count > 0 }}
name: Tests
needs:
- detect-modules
- lint
- compile
- smoke-test
strategy:
fail-fast: false
matrix:
js-runtime:
- { name: node, version: 20.x }
- { name: node, version: 22.x }
- { name: node, version: 24.x }
- { name: bun, version: 1.3.0 }
module: ${{ fromJSON(needs.detect-modules.outputs.modules) }}
uses: ./.github/workflows/test-template.yml
with:
runner: ubuntu-22.04
js-runtime: ${{ matrix.js-runtime.name }}
js-runtime-version: ${{ matrix.js-runtime.version }}
container-runtime: docker
workspace: "${{ matrix.module }}"

test-podman:
if: ${{ needs.detect-modules.outputs.modules_count > 0 }}
name: Tests
needs:
Expand All @@ -142,14 +179,18 @@ jobs:
strategy:
fail-fast: false
matrix:
js-runtime:
- { name: node, version: 20.x }
- { name: node, version: 22.x }
- { name: node, version: 24.x }
- { name: bun, version: 1.3.0 }
module: ${{ fromJSON(needs.detect-modules.outputs.modules) }}
node-version: [20.x, 22.x, 24.x]
container-runtime: [docker, podman]
uses: ./.github/workflows/test-template.yml
with:
runner: ubuntu-22.04
node-version: ${{ matrix.node-version }}
container-runtime: ${{ matrix.container-runtime }}
js-runtime: ${{ matrix.js-runtime.name }}
js-runtime-version: ${{ matrix.js-runtime.version }}
container-runtime: podman
workspace: "${{ matrix.module }}"

end:
Expand All @@ -160,7 +201,8 @@ jobs:
- lint
- compile
- smoke-test
- test
- test-docker
- test-podman
runs-on: ubuntu-22.04
steps:
- name: Check if any jobs failed
Expand Down
12 changes: 8 additions & 4 deletions .github/workflows/test-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ on:
runner:
required: true
type: string
node-version:
js-runtime:
required: true
type: string
js-runtime-version:
required: true
type: string
container-runtime:
Expand Down Expand Up @@ -45,15 +48,16 @@ jobs:
- name: Code checkout
uses: actions/checkout@v5

- name: Install Node ${{ inputs.node-version }} and Dependencies
- name: Install ${{ inputs.js-runtime }} ${{ inputs.js-runtime-version }} and Dependencies
id: npm-install
uses: ./.github/actions/npm-setup
with:
runner: ${{ inputs.runner }}
node-version: ${{ inputs.node-version }}
js-runtime: ${{ inputs.js-runtime }}
js-runtime-version: ${{ inputs.js-runtime-version }}
workspace: "${{ inputs.workspace }}"

- name: Run tests
run: npm run test:ci -- --coverage.include=${{ steps.npm-install.outputs.workspace_path }} ${{ steps.npm-install.outputs.workspace_path }}
run: ${{ steps.npm-install.outputs.script_cmd }} run test:ci -- --coverage.include=${{ steps.npm-install.outputs.workspace_path }} ${{ steps.npm-install.outputs.workspace_path }}
env:
CI: true
6 changes: 4 additions & 2 deletions packages/testcontainers/src/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export { withFileLock } from "./file-lock";
export { hash } from "./hash";
export { Logger, buildLog, composeLog, containerLog, execLog, log, pullLog } from "./logger";
export { IntervalRetry, Retry } from "./retry";
export { IntervalRetry } from "./retry";
export type { Retry } from "./retry";
export { streamToString } from "./streams";
export * from "./time";
export * from "./type-guards";
export { RandomUuid, Uuid, randomUuid } from "./uuid";
export { RandomUuid, randomUuid } from "./uuid";
export type { Uuid } from "./uuid";
Original file line number Diff line number Diff line change
Expand Up @@ -291,9 +291,14 @@ export class DockerContainerClient implements ContainerClient {
log.debug(`Removing container...`, { containerId: container.id });
await container.remove({ v: opts?.removeVolumes });
log.debug(`Removed container`, { containerId: container.id });
} catch (err) {
log.error(`Failed to remove container: ${err}`, { containerId: container.id });
throw err;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
if (err.statusCode === 404) {
log.warn(`Failed to remove container as it no longer exists: ${err}`, { containerId: container.id });
} else {
log.error(`Failed to remove container: ${err}`, { containerId: container.id });
throw err;
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/testcontainers/src/container-runtime/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { getAuthConfig } from "./auth/get-auth-config";
export { ContainerRuntimeClient, getContainerRuntimeClient } from "./clients/client";
export { parseComposeContainerName } from "./clients/compose/parse-compose-container-name";
export { ComposeDownOptions, ComposeExecutableOptions, ComposeOptions } from "./clients/compose/types";
export { HostIp } from "./clients/types";
export type { ComposeDownOptions, ComposeExecutableOptions, ComposeOptions } from "./clients/compose/types";
export type { HostIp } from "./clients/types";
export { ImageName } from "./image-name";
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ContainerInfo } from "dockerode";
import { containerLog, log, RandomUuid, Uuid } from "../common";
import type { Uuid } from "../common";
import { containerLog, log, RandomUuid } from "../common";
import { ComposeOptions, getContainerRuntimeClient, parseComposeContainerName } from "../container-runtime";
import { StartedGenericContainer } from "../generic-container/started-generic-container";
import { getReaper } from "../reaper/reaper";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ImageBuildOptions } from "dockerode";
import path from "path";
import { log, RandomUuid, Uuid } from "../common";
import type { Uuid } from "../common";
import { log, RandomUuid } from "../common";
import { getAuthConfig, getContainerRuntimeClient, ImageName } from "../container-runtime";
import { getReaper } from "../reaper/reaper";
import { AuthConfig, BuildArgs, RegistryConfig } from "../types";
Expand Down
3 changes: 2 additions & 1 deletion packages/testcontainers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { IntervalRetry, RandomUuid, Retry, Uuid, log, randomUuid } from "./common";
export { IntervalRetry, RandomUuid, log, randomUuid } from "./common";
export type { Retry, Uuid } from "./common";
export { ContainerRuntimeClient, ImageName, getContainerRuntimeClient } from "./container-runtime";
export { DockerComposeEnvironment } from "./docker-compose-environment/docker-compose-environment";
export { DownedDockerComposeEnvironment } from "./docker-compose-environment/downed-docker-compose-environment";
Expand Down
3 changes: 2 additions & 1 deletion packages/testcontainers/src/network/network.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Dockerode from "dockerode";
import { log, RandomUuid, Uuid } from "../common";
import type { Uuid } from "../common";
import { log, RandomUuid } from "../common";
import { ContainerRuntimeClient, getContainerRuntimeClient } from "../container-runtime";
import { getReaper } from "../reaper/reaper";
import { createLabels, LABEL_TESTCONTAINERS_SESSION_ID } from "../utils/labels";
Expand Down
2 changes: 1 addition & 1 deletion packages/testcontainers/src/reaper/reaper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export async function getReaper(client: ContainerRuntimeClient): Promise<Reaper>
const reaperContainer = await findReaperContainer(client);
sessionId = reaperContainer?.Labels[LABEL_TESTCONTAINERS_SESSION_ID] ?? new RandomUuid().nextUuid();

if (process.env.TESTCONTAINERS_RYUK_DISABLED === "true") {
if (process.env.TESTCONTAINERS_RYUK_DISABLED === "true" || process.versions.bun) {
return new DisabledReaper(sessionId, "");
} else if (reaperContainer) {
return await useExistingReaper(reaperContainer, sessionId, client.info.containerRuntime.host);
Expand Down