Skip to content

Commit 6a6c470

Browse files
authored
fix(build): tar/zip archives contain root directory MONGOSH-833 (#955)
1 parent 1131e40 commit 6a6c470

File tree

5 files changed

+48
-9
lines changed

5 files changed

+48
-9
lines changed

packages/build/src/packaging/package/helpers.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,27 @@ export async function execFile(...args: Parameters<typeof execFileWithoutLogging
2121

2222
/**
2323
* Create a directory containing the contents of the to-be-generated tarball/zip.
24+
*
25+
* The tarball/zip will contain a top-level folder that will then contain all files instead of
26+
* have all files directly in the archive.
2427
*/
25-
export async function createCompressedArchiveContents(pkg: PackageInformation): Promise<string> {
26-
// For the tarball and the zip file: We put license and readme texts at the
27-
// root of the package, and put all binaries into /bin.
28+
export async function createCompressedArchiveContents(archiveRootName: string, pkg: PackageInformation): Promise<string> {
29+
// For the tarball and the zip file:
30+
// - We add a single top-level folder to contain all contents
31+
// - We put license and readme texts directly in the top-level folder, and put all binaries into folder/bin.
2832
const tmpDir = path.join(__dirname, '..', '..', '..', 'tmp', `pkg-${Date.now()}-${Math.random()}`);
29-
await fs.mkdir(tmpDir, { recursive: true });
33+
const archiveRoot = path.join(tmpDir, archiveRootName);
34+
await fs.mkdir(archiveRoot, { recursive: true });
3035
const docFiles = [
3136
...pkg.otherDocFilePaths,
3237
...pkg.binaries.map(({ license }) => license)
3338
];
3439
for (const { sourceFilePath, packagedFilePath } of docFiles) {
35-
await fs.copyFile(sourceFilePath, path.join(tmpDir, packagedFilePath), COPYFILE_FICLONE);
40+
await fs.copyFile(sourceFilePath, path.join(archiveRoot, packagedFilePath), COPYFILE_FICLONE);
3641
}
37-
await fs.mkdir(path.join(tmpDir, 'bin'));
42+
await fs.mkdir(path.join(archiveRoot, 'bin'));
3843
for (const { sourceFilePath } of pkg.binaries) {
39-
await fs.copyFile(sourceFilePath, path.join(tmpDir, 'bin', path.basename(sourceFilePath)), COPYFILE_FICLONE);
44+
await fs.copyFile(sourceFilePath, path.join(archiveRoot, 'bin', path.basename(sourceFilePath)), COPYFILE_FICLONE);
4045
}
4146
return tmpDir;
4247
}

packages/build/src/packaging/package/tarball.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import { expect } from 'chai';
2+
import { spawnSync } from 'child_process';
13
import { promises as fs } from 'fs';
4+
import path from 'path';
25
import { withTempPackageEach } from '../../../test/helpers';
36
import { createPackage } from './create-package';
47

@@ -8,5 +11,16 @@ describe('package tarball', () => {
811
it('packages the executable(s)', async() => {
912
const tarball = await createPackage(tmpPkg.tarballDir, 'linux-x64', tmpPkg.pkgConfig);
1013
await fs.access(tarball.path);
14+
const tarname = path.basename(tarball.path).replace(/\.tgz$/, '');
15+
16+
const unzip = spawnSync('tar', [
17+
'tf', tarball.path
18+
], { encoding: 'utf-8' });
19+
expect(unzip.error).to.be.undefined;
20+
expect(unzip.stderr).to.be.empty;
21+
22+
expect(
23+
unzip.stdout.split('\n').filter(l => !!l).every(l => l.startsWith(`${tarname}/`))
24+
).to.be.true;
1125
});
1226
});

packages/build/src/packaging/package/tarball.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { promises as fs } from 'fs';
2+
import path from 'path';
23
import rimraf from 'rimraf';
34
import tar from 'tar';
45
import { promisify } from 'util';
@@ -9,7 +10,8 @@ import { PackageInformation } from './package-information';
910
* Create a tarball archive for posix.
1011
*/
1112
export async function createTarballPackage(pkg: PackageInformation, outFile: string): Promise<void> {
12-
const tmpDir = await createCompressedArchiveContents(pkg);
13+
const filename = path.basename(outFile).replace(/\.[^.]+$/, '');
14+
const tmpDir = await createCompressedArchiveContents(filename, pkg);
1315
await tar.c({
1416
gzip: true,
1517
file: outFile,

packages/build/src/packaging/package/zip.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { expect } from 'chai';
2+
import { spawnSync } from 'child_process';
23
import { promises as fs } from 'fs';
34
import * as path from 'path';
45
import sinon from 'ts-sinon';
@@ -20,6 +21,21 @@ describe('package zip', () => {
2021
it('packages the executable(s)', async() => {
2122
const tarball = await createPackage(tmpPkg.tarballDir, 'win32-x64', tmpPkg.pkgConfig);
2223
await fs.access(tarball.path);
24+
const zipname = path.basename(tarball.path).replace(/\.zip$/, '');
25+
26+
const unzip = spawnSync('unzip', [
27+
'-l', tarball.path
28+
], { encoding: 'utf-8' });
29+
expect(unzip.error).to.be.undefined;
30+
expect(unzip.stderr).to.be.empty;
31+
32+
const lines = unzip.stdout.split('\n');
33+
expect(lines).to.have.length(13);
34+
35+
for (let i = 3; i < 10; i++) {
36+
const filename = /([^\s]+)$/.exec(lines[i])?.[1] ?? '';
37+
expect(filename.startsWith(`${zipname}/`)).to.be.true;
38+
}
2339
});
2440

2541
it('falls back to 7zip if zip is not available', async() => {

packages/build/src/packaging/package/zip.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from 'path';
12
import rimraf from 'rimraf';
23
import { promisify } from 'util';
34
import { createCompressedArchiveContents, execFile as execFileFn } from './helpers';
@@ -15,7 +16,8 @@ export async function createZipPackage(
1516
// evergreen macOS and Windows machines, respectively, at this point.
1617
// In either case, using these has the advantage of preserving executable permissions
1718
// as opposed to using libraries like adm-zip.
18-
const tmpDir = await createCompressedArchiveContents(pkg);
19+
const filename = path.basename(outFile).replace(/\.[^.]+$/, '');
20+
const tmpDir = await createCompressedArchiveContents(filename, pkg);
1921
try {
2022
await execFile('zip', ['-r', outFile, '.'], { cwd: tmpDir });
2123
} catch (err) {

0 commit comments

Comments
 (0)