') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Archiver: preserve symlinks and explicit build-context entries by mazdak · Pull Request #1391 · apple/container · GitHub
Skip to content

Archiver: preserve symlinks and explicit build-context entries - #1391

Open
mazdak wants to merge 2 commits into
apple:mainfrom
mazdak:mazdak/archiver-buildfs-fixes
Open

Archiver: preserve symlinks and explicit build-context entries#1391
mazdak wants to merge 2 commits into
apple:mainfrom
mazdak:mazdak/archiver-buildfs-fixes

Conversation

@mazdak

@mazdakmazdak commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • Bug fix

Motivation and Context

While building out a Docker Compose-style plugin and validating it against our own real development stack, we ran into two classes of problems in the build-context path:

  • Correctness issues in archive generation, especially around symlinks
  • Very high client-side overhead while preparing and streaming build contexts.

This PR fixes build-context archiving in container so symlinks are preserved correctly, digest calculation reflects symlink target changes, and fssync archives the exact selected file set in the already-computed order.

Before this change, build-context archiving could mis-handle symlinks, archive a broader tree walk than necessary, and produce a digest that did not change when symlink targets changed.

Part of the fix was also more generally related to the Containerization framework. This PR intentionally stays independently mergeable against the current released containerization dependency, so it keeps the local archiver implementation needed for these fixes today. The Containerization PR is: apple/containerization#652

Validation

This is an example of the problem the PR actually fixes.

Build context:

ctx="$(mktemp -d /private/tmp/container-retest-rel.XXXXXX)/ctx"
mkdir -p "$ctx"
printf 'hello\n' > "$ctx/target.txt"
ln -s target.txt "$ctx/link.txt"
cat > "$ctx/Dockerfile" <<'EOF'
FROM alpine:3.20
COPY . /ctx
RUN echo "link target: $(readlink /ctx/link.txt)" && \
test "$(readlink /ctx/link.txt)" = "target.txt" && \
test "$(cat /ctx/link.txt)" = "hello"
EOF

Build command:

 container build -t rel-patch-retest -f Dockerfile .

Observed results:

  • main branch: failed
    • link target: came back empty
    • exit code 1
  • patched branch: passed
    • link target: target.txt
    • Successfully built rel-patch-retest:latest
    • exit code 0
  • Before this fix, container build could lose symlink metadata in the archived build context.
  • A relative symlink like link.txt -> target.txt arrived broken inside the build.
  • After this fix, the same build context preserves the symlink correctly and the build succeeds.

Testing

  • Tested locally
  • Added/updated tests

return hasher.finalize()
}

public static func uncompress(source: URL, destination: URL) throws {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I removed this in #1372 because it's insecure and should not be used. See ArchiveReader.extractContents() in containerization in this commit: apple/containerization@3e93416

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Fixed

@jglogan

Copy link
Copy Markdown
Contributor

Can you provide a simple container build command, Dockerfile and build context that fails and is addressed by the fix(es) you propose? Thanks!

@mazdak
mazdakforce-pushed the mazdak/archiver-buildfs-fixes branch from 51afad8 to a1a47f2CompareApril 5, 2026 21:32
@mazdak

Copy link
Copy Markdown
ContributorAuthor

Can you provide a simple container build command, Dockerfile and build context that fails and is addressed by the fix(es) you propose? Thanks!

Please see PR description

@mazdak
mazdak requested a review from jgloganApril 6, 2026 13:36
@mazdak
mazdakforce-pushed the mazdak/archiver-buildfs-fixes branch from a1a47f2 to 88f75afCompareJune 2, 2026 00:12
@mazdak

Copy link
Copy Markdown
ContributorAuthor

@jglogan are you still considering this?

Sign up for freeto 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

@mazdak@jglogan