Skip to content

fix(zip-it-and-ship-it): create missing symlinks when archiveFormat=none #5836

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

Merged
merged 3 commits into from
Sep 18, 2024
Merged
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
13 changes: 8 additions & 5 deletions packages/zip-it-and-ship-it/src/runtimes/node/utils/zip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const createDirectory = async function ({
addBootstrapFile(srcFiles, aliases)
}

const symlinks = new Map<string, string>()
const symlinks = new Map<string, Set<string>>()

// Copying source files.
await pMap(
Expand All @@ -156,7 +156,8 @@ const createDirectory = async function ({
if (stat.isSymbolicLink()) {
const targetPath = await readLink(srcFile)

symlinks.set(targetPath, absoluteDestPath)
// Two symlinks may point at the same target path, so keep a list of symlinks to create.
symlinks.set(targetPath, (symlinks.get(targetPath) ?? new Set()).add(absoluteDestPath))

return
}
Expand All @@ -168,9 +169,11 @@ const createDirectory = async function ({

await pMap(
[...symlinks.entries()],
async ([target, path]) => {
await mkdir(dirname(path), { recursive: true })
await symlink(target, path)
async ([target, paths]) => {
for (const path of paths) {
await mkdir(dirname(path), { recursive: true })
await symlink(target, path)
}
},
{
concurrency: COPY_FILE_CONCURRENCY,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default async () =>
new Response('<h1>Hello world</h1>', {
headers: {
'content-type': 'text/html',
},
})

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { readdir } from 'fs/promises'
import { platform } from 'os'
import { join } from 'path'
import { join, sep } from 'path'

import decompress from 'decompress'
import { dir as getTmpDir } from 'tmp-promise'
Expand Down Expand Up @@ -69,6 +69,66 @@ test.skipIf(platform() === 'win32')('Symlinked directories from `includedFiles`
})
})

// Regression test for https://github.com/netlify/build/pull/5836
test('preserves multiple symlinks that link to the same target', async () => {
const { path: tmpDir } = await getTmpDir({ prefix: 'zip-it-test' })
const basePath = join(FIXTURES_ESM_DIR, 'symlinked-deps')
const mainFile = join(basePath, 'function.mjs')

// Two symlinks that point at `node_modules/.pnpm/[email protected]/node_modules/is-odd`:
//
// - `node_modules/.pnpm/[email protected]/node_modules/is-odd`
// - `node_modules/.pnpm/[email protected]/node_modules/is-odd`
expect(await readDirWithType(basePath)).toEqual({
'function.mjs': false,
['node_modules/.pnpm/[email protected]/node_modules/is-even'.replace(/\//g, sep)]: true,
['node_modules/.pnpm/[email protected]/node_modules/is-even-or-odd/index.js'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-even-or-odd/package.json'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-odd'.replace(/\//g, sep)]: true,
['node_modules/.pnpm/[email protected]/node_modules/is-even/index.js'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-even/package.json'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-odd'.replace(/\//g, sep)]: true,
['node_modules/.pnpm/[email protected]/node_modules/is-odd/index.js'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-odd/package.json'.replace(/\//g, sep)]: false,
['node_modules/is-even-or-odd'.replace(/\//g, sep)]: true,
})

await zipFunction(mainFile, tmpDir, {
archiveFormat: ARCHIVE_FORMAT.NONE,
basePath,
config: {
'*': {
includedFiles: ['**'],
},
},
featureFlags: {
zisi_fix_symlinks: true,
},
repositoryRoot: basePath,
systemLog: console.log,
debug: true,
internalSrcFolder: undefined,
})

// Test to be sure we've made both symlinks, not just one of them
expect(await readDirWithType(join(tmpDir, 'function'))).toEqual({
'___netlify-bootstrap.mjs': false,
'___netlify-entry-point.mjs': false,
'___netlify-telemetry.mjs': false,
'function.mjs': false,
['node_modules/.pnpm/[email protected]/node_modules/is-even'.replace(/\//g, sep)]: true,
['node_modules/.pnpm/[email protected]/node_modules/is-even-or-odd/index.js'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-even-or-odd/package.json'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-odd'.replace(/\//g, sep)]: true,
['node_modules/.pnpm/[email protected]/node_modules/is-even/index.js'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-even/package.json'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-odd'.replace(/\//g, sep)]: true,
['node_modules/.pnpm/[email protected]/node_modules/is-odd/index.js'.replace(/\//g, sep)]: false,
['node_modules/.pnpm/[email protected]/node_modules/is-odd/package.json'.replace(/\//g, sep)]: false,
['node_modules/is-even-or-odd'.replace(/\//g, sep)]: true,
})
})

test('symlinks in subdir of `includedFiles` are copied over successfully', async () => {
const { path: tmpDir } = await getTmpDir({ prefix: 'zip-it-test' })
const basePath = join(FIXTURES_ESM_DIR, 'symlinked-bin')
Expand Down
Loading