Skip to content

Replace ESM-only archive and port dependencies#1370

Closed
cristianrgreco wants to merge 5 commits into
mainfrom
codex/remove-esm-only-deps
Closed

Replace ESM-only archive and port dependencies#1370
cristianrgreco wants to merge 5 commits into
mainfrom
codex/remove-esm-only-deps

Conversation

@cristianrgreco

Copy link
Copy Markdown
Collaborator

Summary

  • Replace direct archiver usage with an internal tar-stream archive builder for copy-to-container paths.
  • Replace direct get-port usage with a native net.listen(0) implementation exposed as getRandomPort().
  • Remove direct runtime dependencies on archiver and get-port, plus related test-only uses, while adding focused tar archive tests.

Verification

  • npm run format
  • npm run lint
  • npm run check-compiles
  • npm test -- --run packages/testcontainers/src/utils/port-generator.test.ts
  • npm test -- --run packages/testcontainers/src/utils/tar-archive.test.ts
  • npm test -- --run packages/testcontainers/src/generic-container/generic-container.test.ts -t "bind to specified host port|copy"
  • npm test -- --run packages/modules/couchbase/src/couchbase-container.test.ts -t "preserve fixed host port binding"
  • CJS smoke against packages/testcontainers/build/index.js for getRandomPort

Test Results

  • Port generator tests: 2 passed.
  • Tar archive helper tests: 2 passed.
  • Focused GenericContainer copy and fixed port tests: 16 passed, 35 skipped by filter.
  • Focused Couchbase fixed port test: 1 passed, 8 skipped by filter.

Semver Impact

Patch. This removes direct ESM-only runtime dependencies while preserving the existing CommonJS package shape and public copy APIs. RandomPortGenerator remains exported for compatibility; getRandomPort() is additive. Existing copy-to-container behavior is covered by Docker-backed tests, including file, directory, content, permission, and archive ownership paths.

The direct consumer install footprint drops by about 3.8 MiB in a clean production install comparison.

@cristianrgreco cristianrgreco added dependencies Pull requests that update a dependency file patch Backward compatible bug fix labels Jun 18, 2026
@netlify

netlify Bot commented Jun 18, 2026

Copy link
Copy Markdown

Deploy Preview for testcontainers-node ready!

Name Link
🔨 Latest commit 4e29c7d
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-node/deploys/6a33c6aa2922270008dea608
😎 Deploy Preview https://deploy-preview-1370--testcontainers-node.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e23a2df7a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/testcontainers/src/utils/tar-archive.ts Outdated
@cristianrgreco cristianrgreco force-pushed the codex/remove-esm-only-deps branch from e23a2df to 3a0853f Compare June 18, 2026 08:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a0853f7e3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/modules/azurecosmosdb/src/azure-cosmosdb-emulator-container.ts Outdated
};

const createContentArchiveEntry = async (content: Content, target: string, mode?: number): Promise<TarArchiveEntry> => {
const buffer = await toBuffer(content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid buffering copied content streams

When a caller passes a Readable to withCopyContentToContainer or copyContentToContainer, this awaits toBuffer(content) before the archive is handed to Docker, so the entire stream must fit in memory and upload cannot begin until it has fully drained. The previous archiver.append path accepted streams directly, so large generated payloads that used to stream through can now exhaust heap; keep stream entries streaming or otherwise avoid materializing the full content.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I checked the previous archiver tar path before changing this. archiver.append(Readable) only streamed directly when stats/size were provided; for a Readable without stats it used collectStream(...) and buffered the stream before writing the tar entry. ContentToCopy does not currently carry size metadata, and tar-stream needs size before writing the header, so this PR preserves the existing behavior rather than introducing a new API. We can add optional size-aware streaming separately if we want to support that.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9019356c85

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +22 to +26
await new Promise<void>((resolve, reject) => {
server.close((err) => (err ? reject(err) : resolve()));
});

return address.port;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve in-process port reservations

After finding a free port, this closes the probe socket and immediately returns the number without recording it. In the common case where the same process asks for several ports before Docker actually binds them (for example parallel container starts or tests that allocate several fixed host ports), a later call can receive the same released port; the old get-port path kept returned ports out of the candidate set temporarily, so those in-process collisions were avoided. Add a short-lived/reserved set or keep the socket reserved until the caller binds it.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eefa0960ec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

header: {
name: normalizeTarPath(target),
type: "symlink",
linkname: await fs.readlink(source),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize absolute symlink targets when copying directories

When a copied directory contains an absolute symlink to another file inside the same source tree (for example link -> /host/src/file), this stores the host path verbatim as the tar link target. Docker then recreates an absolute link in the container pointing at /host/src/file, while the file itself was copied under the requested container target, so the symlink is broken; the previous archiver.directory path normalized symlink targets relative to the symlink location. Convert absolute symlink targets that resolve inside the copied tree to the corresponding target-relative linkname before archiving.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e29c7dd34

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

header: {
name: normalizeTarPath(target),
type: "directory",
mode: getEntryMode(stats, mode),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve Windows directory execute bits

On Windows hosts, copied subdirectories are archived with the raw fs.lstat mode when the caller doesn't pass mode. Windows commonly reports directory permissions as 0666 with no execute bits, while the previous archiver path normalized directory entries to traversable modes; extracting this tar into a Linux container can leave nested copied directories inaccessible, so files under them cannot be reached unless every directory copy supplies an explicit mode. Default directory entries to executable permissions on win32, e.g. the old 0755 behavior, before writing the header.

Useful? React with 👍 / 👎.

@cristianrgreco

Copy link
Copy Markdown
Collaborator Author

As we can see from the PR feedback, trying to replace archiver with tar-stream is more complex than we'd like. As of now I'm not comfortable owning so much low-level tar code. Closing for now

@cristianrgreco cristianrgreco deleted the codex/remove-esm-only-deps branch June 18, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file patch Backward compatible bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant