Skip to content

Fix testcontainers cleanup #2023

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Fix testcontainers cleanup #2023

wants to merge 5 commits into from

Conversation

nicktrn
Copy link
Collaborator

@nicktrn nicktrn commented May 2, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced resource cleanup process with detailed logging, including timestamps, duration, order, concurrency status, and error reporting for all cleanup operations.
    • Introduced a new parallelized testing workflow running 20 simultaneous test instances with consistent Docker and environment setup.
  • Chores

    • Updated development dependencies for PostgreSQL, Redis, and testcontainers packages to the latest versions.
    • Increased container stop timeout for improved reliability during cleanup.
    • Configured Docker default address pools in test workflows to optimize network management.

Copy link

changeset-bot bot commented May 2, 2025

⚠️ No Changeset found

Latest commit: 5595852

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented May 2, 2025

Walkthrough

The changes update the development dependencies in the testcontainers package to newer versions. In the source code, a new asynchronous helper function, logCleanup, is introduced to standardize and enhance logging for resource cleanup operations. This function tracks cleanup order, concurrency, timing, and errors. All resource cleanup calls are refactored to use this new helper, and the container stop timeout is increased from 10 to 30 seconds. Error handling for network cleanup is now managed by logCleanup. Additionally, a new GitHub Actions workflow named "🐳 Testcontainers" is added to run parallel unit tests with Docker configuration, and the existing unit tests workflow is updated to configure Docker's default address pools before tests run.

Changes

File(s) Change Summary
internal-packages/testcontainers/package.json Updated devDependencies: @testcontainers/postgresql, @testcontainers/redis, and testcontainers from ^10.13.1 to ^10.25.0
internal-packages/testcontainers/src/index.ts Added logCleanup async helper for cleanup logging; refactored all cleanup calls to use it; increased container stop timeout
.github/workflows/testcontainers.yml Added new GitHub Actions workflow "🐳 Testcontainers" with 20 parallel matrix runs, Docker setup, pnpm and Node.js setup, and unit tests
.github/workflows/unit-tests.yml Added step to configure Docker daemon's default address pools before running unit tests

Sequence Diagram(s)

sequenceDiagram
    participant TestSuite
    participant Resource
    participant logCleanup

    TestSuite->>Resource: Use resource (e.g., start container)
    Note over TestSuite,Resource: Test execution
    TestSuite->>logCleanup: logCleanup("ResourceName", cleanupFn)
    activate logCleanup
    logCleanup->>Resource: cleanupFn()
    Resource-->>logCleanup: Cleanup complete or error
    logCleanup-->>TestSuite: Log details (order, concurrency, duration, errors)
Loading

Poem

In the warren of code, cleanups now gleam,
With logs that record each resource and team.
Timers and counters, concurrency tracked,
Errors and endings, all neatly packed.
Dependencies fresh as the morning dew—
This rabbit applauds the work you do! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
internal-packages/testcontainers/src/index.ts (1)

73-73: Fix empty object pattern.

There's an empty object pattern {} in the function parameters which is unnecessary.

-const network = async ({}, use: Use<StartedNetwork>) => {
+const network = async (_: {}, use: Use<StartedNetwork>) => {

or

-const network = async ({}, use: Use<StartedNetwork>) => {
+const network = async (_, use: Use<StartedNetwork>) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 73-73: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15816e9 and 68eb755.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • internal-packages/testcontainers/package.json (1 hunks)
  • internal-packages/testcontainers/src/index.ts (5 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
internal-packages/testcontainers/src/index.ts

[error] 73-73: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: units / 🧪 Unit Tests
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
internal-packages/testcontainers/package.json (1)

13-14: Version bump for testcontainers dependencies looks good.

The update of testcontainers-related packages from ^10.13.1 to ^10.25.0 aligns with the PR's goal of fixing testcontainers cleanup. This minor version update likely includes improvements to container management and cleanup mechanisms.

Also applies to: 16-16

internal-packages/testcontainers/src/index.ts (6)

34-71: Well-designed cleanup logging mechanism.

This new implementation significantly improves the observability of container cleanup operations. The logCleanup function provides:

  • Detailed timing information (start, end, duration)
  • Cleanup order tracking
  • Concurrency detection
  • Structured error handling
  • JSON-formatted logs for easier parsing

This will be valuable for diagnosing test container issues, especially in CI environments.


78-82: Good refactoring of network cleanup.

The network cleanup now uses the central logging mechanism, providing consistency with other resource cleanups.


95-97: Appropriate timeout increase for container stopping.

Increasing the container stop timeout from the default to 30 seconds is a good practice. This gives containers more time to shut down gracefully, potentially preventing issues in subsequent tests caused by lingering containers.


119-121: Good application of logCleanup to the Prisma client.

Consistently applying the same logging pattern to database disconnection will help track database-related cleanup issues.


140-142: Consistent timeout for Redis container.

The 30-second timeout for Redis container is consistent with other containers, ensuring a standardized approach to cleanup.


194-196: Electric container cleanup matches other containers.

The electric container cleanup now follows the same pattern as other containers with the standardized 30-second timeout.

Comment on lines +10 to +77
name: "🧪 Unit Tests (run ${{ matrix.run }})"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
run: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
steps:
- name: 🔧 Configure docker address pool
run: |
CONFIG='{
"default-address-pools" : [
{
"base" : "172.17.0.0/12",
"size" : 20
},
{
"base" : "192.168.0.0/16",
"size" : 24
}
]
}'
mkdir -p /etc/docker
echo "$CONFIG" | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker

- name: ⬇️ Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8.15.5

- name: ⎔ Setup node
uses: buildjet/setup-node@v4
with:
node-version: 20.11.1
cache: "pnpm"

# ..to avoid rate limits when pulling images
- name: 🐳 Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: 📥 Download deps
run: pnpm install --frozen-lockfile

- name: 📀 Generate Prisma Client
run: pnpm run generate

- name: 🧪 Run Webapp Unit Tests
run: pnpm run test:webapp
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
DIRECT_URL: postgresql://postgres:postgres@localhost:5432/postgres
SESSION_SECRET: "secret"
MAGIC_LINK_SECRET: "secret"
ENCRYPTION_KEY: "secret"

- name: 🧪 Run Package Unit Tests
run: pnpm run test:packages

- name: 🧪 Run Internal Unit Tests
run: pnpm run test:internal

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium test

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 19 hours ago

To fix the issue, we will add a permissions block at the root level of the workflow. This block will specify the minimal permissions required for the workflow to function correctly. Based on the tasks performed in the workflow, the contents: read permission is sufficient, as the workflow does not appear to modify repository contents or perform other actions requiring write access.

The permissions block will be added immediately after the name field at the top of the workflow file.


Suggested changeset 1
.github/workflows/testcontainers.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/testcontainers.yml b/.github/workflows/testcontainers.yml
--- a/.github/workflows/testcontainers.yml
+++ b/.github/workflows/testcontainers.yml
@@ -1,2 +1,4 @@
 name: "🐳 Testcontainers"
+permissions:
+  contents: read
 
EOF
@@ -1,2 +1,4 @@
name: "🐳 Testcontainers"
permissions:
contents: read

Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Contributor

@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: 0

🔭 Outside diff range comments (1)
.github/workflows/testcontainers.yml (1)

1-78: 💡 Verification agent

🧩 Analysis chain

Well-structured parallel testing workflow with Docker configuration

This new workflow efficiently runs tests in parallel with 20 matrix runs, which should significantly improve test execution time. The Docker address pool configuration matches the one added to the unit-tests workflow, ensuring consistent container networking.

A few recommendations:

  1. Add GitHub token permissions to limit the scope of the workflow
  2. Fix the trailing spaces in the YAML file for better code quality

Add a permissions block to limit token scope:

name: "🐳 Testcontainers"

on:
  workflow_call:
  workflow_dispatch:
  push:

+permissions:
+  contents: read

jobs:
  unitTests:
    name: "🧪 Unit Tests (run ${{ matrix.run }})"

And fix trailing spaces on lines 72 and 75:

          SESSION_SECRET: "secret"
          MAGIC_LINK_SECRET: "secret"
          ENCRYPTION_KEY: "secret"
-      
+
      - name: 🧪 Run Package Unit Tests
        run: pnpm run test:packages
-      
+
      - name: 🧪 Run Internal Unit Tests

🌐 Web query:

Best practices for GitHub Actions token permissions

💡 Result:

Best Practices for GitHub Actions Token Permissions

GitHub Actions provides an automated token (GITHUB_TOKEN) that grants permissions to access resources within your repository. Managing these permissions properly is crucial for maintaining security in your CI/CD workflows. Here are the best practices for handling GitHub Actions token permissions:

Follow the Principle of Least Privilege

The most fundamental best practice is to restrict GITHUB_TOKEN permissions to only what's absolutely necessary for each workflow:

  1. Set read-only permissions by default at the repository or organization level[4][6]
  2. Explicitly define permissions needed for specific jobs or workflows[5]
  3. Avoid using the default permissions which might be overly permissive[7]

For example, if your workflow only needs to read repository contents, you should explicitly set:

permissions:
  contents: read

Configure Default Settings at Organization Level

For enterprises and organizations, the most effective approach to implement least privilege is to:

  1. Configure "Read repository contents and package permissions" as the default workflow permissions at the organization level[4]
  2. This ensures all GitHub Actions workflows have only read permissions for contents and packages by default
  3. Individual workflows can then request additional permissions as needed[6]

Use Job-Specific Permissions

When different jobs within a workflow need different permission levels, set them at the job level rather than the workflow level:

jobs:
  read-only-job:
    permissions:
      contents: read
    runs-on: ubuntu-latest
    steps:
      # Steps that only need to read content
      
  deployment-job:
    permissions:
      contents: write
      deployments: write
    runs-on: ubuntu-latest
    steps:
      # Steps that need to write content or create deployments

This approach ensures each job has only the permissions it specifically requires[5].

Be Cautious with Third-Party Actions

When using actions created by third parties, it's particularly important to restrict permissions:

  1. Limit the GITHUB_TOKEN permissions when using third-party actions[5]
  2. Consider using a more restrictive set of permissions for workflows that include third-party code[7]

For example:

permissions:
  actions: none
  checks: read
  contents: read

Avoid Using Personal Access Tokens When Possible

The GITHUB_TOKEN is preferable to personal access tokens (PATs) in most cases because:

  1. It's automatically created and expired for each job[1]
  2. It has built-in scope limitations to the repository where the workflow runs[2]
  3. It's easier to audit and manage across an organization[4]

When you do need a PAT, create fine-grained tokens with limited repository access and specific permissions[8].

Implement Additional Security Controls

Beyond permission settings, consider these additional security controls:

  1. Use the StepSecurity Harden-Runner to restrict egress network traffic and implement runtime security controls[4]
  2. Monitor workflow runs for unexpected permission usage or suspicious activities
  3. Set expiration for any tokens that you create manually[8]

Monitor and Audit Token Usage

Regularly review:

  • The permissions being used in your workflows
  • The build logs of workflow runs (look at the 'Set up job' step to check GITHUB_TOKEN permissions)[4]
  • Any changes to workflows that might modify permission scopes

By implementing these best practices, you can significantly reduce the security risks associated with GitHub Actions workflows while ensuring they have the necessary permissions to function properly.

Citations:


🏁 Script executed:

#!/bin/bash
# Check for trailing whitespace in the workflow file
rg -n '\s+$' .github/workflows/testcontainers.yml || true

Length of output: 71


Add least-privileged GitHub token permissions and remove trailing whitespace

To follow the principle of least privilege, explicitly scope GITHUB_TOKEN to only read repository contents, and clean up trailing spaces in the workflow file:

• File: .github/workflows/testcontainers.yml
– Insert a permissions block immediately after the on: section:

 name: "🐳 Testcontainers"

 on:
   workflow_call:
   workflow_dispatch:
   push:

+permissions:
+  contents: read

 jobs:
   unitTests:
     …

– Remove the two trailing-space lines detected (lines 72 and 75):

         ENCRYPTION_KEY: "secret"
-      
+
     - name: 🧪 Run Package Unit Tests
       run: pnpm run test:packages
-      
+
     - name: 🧪 Run Internal Unit Tests
🧰 Tools
🪛 Checkov (3.2.334)

[MEDIUM] 67-68: Basic Auth Credentials

(CKV_SECRET_4)

🪛 YAMLlint (1.35.1)

[error] 72-72: trailing spaces

(trailing-spaces)


[error] 75-75: trailing spaces

(trailing-spaces)

🪛 GitHub Check: CodeQL

[warning] 10-77: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

🧹 Nitpick comments (1)
internal-packages/testcontainers/src/index.ts (1)

73-73: Fix empty object pattern

The empty object pattern {} should be replaced with a named parameter or removed for clarity.

-const network = async ({}, use: Use<StartedNetwork>) => {
+const network = async (_: {}, use: Use<StartedNetwork>) => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 73-73: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68eb755 and 5595852.

📒 Files selected for processing (3)
  • .github/workflows/testcontainers.yml (1 hunks)
  • .github/workflows/unit-tests.yml (1 hunks)
  • internal-packages/testcontainers/src/index.ts (5 hunks)
🧰 Additional context used
🪛 Checkov (3.2.334)
.github/workflows/testcontainers.yml

[MEDIUM] 67-68: Basic Auth Credentials

(CKV_SECRET_4)

🪛 YAMLlint (1.35.1)
.github/workflows/testcontainers.yml

[error] 72-72: trailing spaces

(trailing-spaces)


[error] 75-75: trailing spaces

(trailing-spaces)

🪛 GitHub Check: CodeQL
.github/workflows/testcontainers.yml

[warning] 10-77: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

🪛 Biome (1.9.4)
internal-packages/testcontainers/src/index.ts

[error] 73-73: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

⏰ Context from checks skipped due to timeout of 90000ms (27)
  • GitHub Check: 🧪 Unit Tests (run 20)
  • GitHub Check: 🧪 Unit Tests (run 19)
  • GitHub Check: 🧪 Unit Tests (run 18)
  • GitHub Check: 🧪 Unit Tests (run 17)
  • GitHub Check: 🧪 Unit Tests (run 16)
  • GitHub Check: 🧪 Unit Tests (run 15)
  • GitHub Check: 🧪 Unit Tests (run 14)
  • GitHub Check: 🧪 Unit Tests (run 13)
  • GitHub Check: 🧪 Unit Tests (run 12)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: 🧪 Unit Tests (run 11)
  • GitHub Check: 🧪 Unit Tests (run 10)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: 🧪 Unit Tests (run 9)
  • GitHub Check: 🧪 Unit Tests (run 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: 🧪 Unit Tests (run 7)
  • GitHub Check: units / 🧪 Unit Tests
  • GitHub Check: typecheck / typecheck
  • GitHub Check: 🧪 Unit Tests (run 6)
  • GitHub Check: 🧪 Unit Tests (run 5)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: 🧪 Unit Tests (run 4)
  • GitHub Check: 🧪 Unit Tests (run 3)
  • GitHub Check: 🧪 Unit Tests (run 2)
  • GitHub Check: 🧪 Unit Tests (run 1)
🔇 Additional comments (5)
.github/workflows/unit-tests.yml (1)

11-27: Docker configuration enhances testing reliability

Adding this Docker address pool configuration is a good way to prevent network address conflicts when running containers during tests. This approach helps ensure consistent test environments and reduces potential flaky tests due to networking issues.

Consider adding a brief comment explaining why this configuration is necessary for better context for future developers.

internal-packages/testcontainers/src/index.ts (4)

34-71: Great improvement to resource cleanup logging and error handling

The new logCleanup function is an excellent addition that addresses several important aspects of container cleanup:

  1. Structured JSON logging provides consistent format for analyzing cleanup patterns
  2. Tracking cleanup order and parallelism helps understand resource disposal sequence
  3. Error handling ensures cleanup failures are logged but don't interrupt the process
  4. Timing information helps identify slow cleanups that might need optimization

The implementation is thorough and well-documented.


79-79: Improved network cleanup with centralized error handling

The network cleanup now uses the logCleanup function, which provides consistent error handling and logging. This is an improvement over the previous implementation.


93-93: Increased container stop timeout improves cleanup reliability

Increasing the container stop timeout from 10 to 30 seconds is a good change that gives containers more time to shut down gracefully. This helps prevent potential issues with lingering containers and improves test stability.

Also applies to: 134-134, 186-186


115-115: Enhanced Prisma client cleanup

Using logCleanup for Prisma client disconnection provides consistent error handling and logging, aligning with the overall cleanup strategy improvements in this PR.

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.

2 participants