Skip to content

Preserve symlinks when archiving build contexts#2

Closed
mazdak wants to merge 5 commits into
mainfrom
pr2/container-archiver
Closed

Preserve symlinks when archiving build contexts#2
mazdak wants to merge 5 commits into
mainfrom
pr2/container-archiver

Conversation

@mazdak
Copy link
Copy Markdown
Owner

@mazdak mazdak commented Mar 22, 2026

Problem

While validating fresh image builds for the new Compose-style plugin against resq-fullstack, we reproduced a core build failure before BuildKit ever got to the Dockerfile logic: container build could fail while archiving the build context.

The root cause is that the archiver treated non-regular filesystem entries like regular files. For symbolic links and directories, it could still derive a payload size from filesystem metadata and then try to stream file bytes for an entry type that should only have a header.

Real repositories hit this easily because build contexts often contain symlinks from virtualenvs, package managers, or worktree layouts. In that state, fresh builds fail in the client archiving step with errors like unable to write data to the archive.

Why this belongs in core

This is a plain container build bug. It is not Compose-specific; the plugin just made it easier to reproduce on a realistic workload.

What this changes

  • write archive headers directly for non-regular entries instead of trying to stream payload bytes
  • force directory and symbolic-link entries to use size 0
  • only derive archive payload size from filesystem metadata for regular files
  • add regression tests covering both relative and absolute symbolic links

Validation

  • swift test --filter ArchiverTests

Copy link
Copy Markdown

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

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

ℹ️ 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 Sources/Services/ContainerAPIService/Client/Archiver.swift
Comment thread Sources/Services/ContainerAPIService/Client/Archiver.swift Outdated
Copy link
Copy Markdown

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

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: a98e694771

ℹ️ 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 Sources/Services/ContainerAPIService/Client/Archiver.swift Outdated
Copy link
Copy Markdown

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

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: a4499f054e

ℹ️ 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 +98 to +100
let archivedPathsByHostPath = entryInfo.reduce(into: [URL: [URL]]()) { result, info in
result[info.pathOnHost.standardizedFileURL, default: []].append(info.pathInArchive)
}
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 Include the context root in absolute-symlink rewrites

Fresh evidence here is that archivedPathsByHostPath is reduced only from entryInfo, and entryInfo never contains source itself when archiving a directory. A symlink like sub/self -> /path/to/context therefore still falls through _rewriteArchivedAbsoluteSymlinkTarget, is emitted with the client-absolute target, and breaks again in staged/remote tar builds after extraction instead of pointing back at the extracted root (.. in this example).

Useful? React with 👍 / 👎.

@mazdak mazdak force-pushed the pr2/container-archiver branch from 275885b to f120d30 Compare March 23, 2026 18:48
Copy link
Copy Markdown

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

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: f120d30e07

ℹ️ 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 +481 to +484
let targetPath = URL(fileURLWithPath: symlinkTarget)
.standardizedFileURL
.resolvingSymlinksInPath()
.path
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 archived symlinks instead of resolving through them

If an absolute symlink points to another symlink that is also in the archive (for example, link -> /context/alias and alias -> target.txt), calling resolvingSymlinksInPath() here collapses the target to the final file before lookup. Archiver.uncompress will then recreate link pointing straight at target.txt, so the extracted build context no longer preserves the original symlink topology. That changes behavior for builds that copy or inspect symlinks rather than dereferencing them.

Useful? React with 👍 / 👎.

@mazdak mazdak closed this Mar 24, 2026
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.

1 participant