Skip to content
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
19 changes: 12 additions & 7 deletions packages/build/src/packaging/package/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,27 @@ export async function execFile(...args: Parameters<typeof execFileWithoutLogging

/**
* Create a directory containing the contents of the to-be-generated tarball/zip.
*
* The tarball/zip will contain a top-level folder that will then contain all files instead of
* have all files directly in the archive.
*/
export async function createCompressedArchiveContents(pkg: PackageInformation): Promise<string> {
// For the tarball and the zip file: We put license and readme texts at the
// root of the package, and put all binaries into /bin.
export async function createCompressedArchiveContents(archiveRootName: string, pkg: PackageInformation): Promise<string> {
// For the tarball and the zip file:
// - We add a single top-level folder to contain all contents
// - We put license and readme texts directly in the top-level folder, and put all binaries into folder/bin.
const tmpDir = path.join(__dirname, '..', '..', '..', 'tmp', `pkg-${Date.now()}-${Math.random()}`);
await fs.mkdir(tmpDir, { recursive: true });
const archiveRoot = path.join(tmpDir, archiveRootName);
await fs.mkdir(archiveRoot, { recursive: true });
const docFiles = [
...pkg.otherDocFilePaths,
...pkg.binaries.map(({ license }) => license)
];
for (const { sourceFilePath, packagedFilePath } of docFiles) {
await fs.copyFile(sourceFilePath, path.join(tmpDir, packagedFilePath), COPYFILE_FICLONE);
await fs.copyFile(sourceFilePath, path.join(archiveRoot, packagedFilePath), COPYFILE_FICLONE);
}
await fs.mkdir(path.join(tmpDir, 'bin'));
await fs.mkdir(path.join(archiveRoot, 'bin'));
for (const { sourceFilePath } of pkg.binaries) {
await fs.copyFile(sourceFilePath, path.join(tmpDir, 'bin', path.basename(sourceFilePath)), COPYFILE_FICLONE);
await fs.copyFile(sourceFilePath, path.join(archiveRoot, 'bin', path.basename(sourceFilePath)), COPYFILE_FICLONE);
}
return tmpDir;
}
Expand Down
14 changes: 14 additions & 0 deletions packages/build/src/packaging/package/tarball.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { expect } from 'chai';
import { spawnSync } from 'child_process';
import { promises as fs } from 'fs';
import path from 'path';
import { withTempPackageEach } from '../../../test/helpers';
import { createPackage } from './create-package';

Expand All @@ -8,5 +11,16 @@ describe('package tarball', () => {
it('packages the executable(s)', async() => {
const tarball = await createPackage(tmpPkg.tarballDir, 'linux-x64', tmpPkg.pkgConfig);
await fs.access(tarball.path);
const tarname = path.basename(tarball.path).replace(/\.tgz$/, '');

const unzip = spawnSync('tar', [
'tf', tarball.path
], { encoding: 'utf-8' });
expect(unzip.error).to.be.undefined;
expect(unzip.stderr).to.be.empty;

expect(
unzip.stdout.split('\n').filter(l => !!l).every(l => l.startsWith(`${tarname}/`))
).to.be.true;
});
});
4 changes: 3 additions & 1 deletion packages/build/src/packaging/package/tarball.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { promises as fs } from 'fs';
import path from 'path';
import rimraf from 'rimraf';
import tar from 'tar';
import { promisify } from 'util';
Expand All @@ -9,7 +10,8 @@ import { PackageInformation } from './package-information';
* Create a tarball archive for posix.
*/
export async function createTarballPackage(pkg: PackageInformation, outFile: string): Promise<void> {
const tmpDir = await createCompressedArchiveContents(pkg);
const filename = path.basename(outFile).replace(/\.[^.]+$/, '');
const tmpDir = await createCompressedArchiveContents(filename, pkg);
await tar.c({
gzip: true,
file: outFile,
Expand Down
16 changes: 16 additions & 0 deletions packages/build/src/packaging/package/zip.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect } from 'chai';
import { spawnSync } from 'child_process';
import { promises as fs } from 'fs';
import * as path from 'path';
import sinon from 'ts-sinon';
Expand All @@ -20,6 +21,21 @@ describe('package zip', () => {
it('packages the executable(s)', async() => {
const tarball = await createPackage(tmpPkg.tarballDir, 'win32-x64', tmpPkg.pkgConfig);
await fs.access(tarball.path);
const zipname = path.basename(tarball.path).replace(/\.zip$/, '');

const unzip = spawnSync('unzip', [
'-l', tarball.path
], { encoding: 'utf-8' });
expect(unzip.error).to.be.undefined;
expect(unzip.stderr).to.be.empty;

const lines = unzip.stdout.split('\n');
expect(lines).to.have.length(13);

for (let i = 3; i < 10; i++) {
const filename = /([^\s]+)$/.exec(lines[i])?.[1] ?? '';
expect(filename.startsWith(`${zipname}/`)).to.be.true;
}
});

it('falls back to 7zip if zip is not available', async() => {
Expand Down
4 changes: 3 additions & 1 deletion packages/build/src/packaging/package/zip.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path';
import rimraf from 'rimraf';
import { promisify } from 'util';
import { createCompressedArchiveContents, execFile as execFileFn } from './helpers';
Expand All @@ -15,7 +16,8 @@ export async function createZipPackage(
// evergreen macOS and Windows machines, respectively, at this point.
// In either case, using these has the advantage of preserving executable permissions
// as opposed to using libraries like adm-zip.
const tmpDir = await createCompressedArchiveContents(pkg);
const filename = path.basename(outFile).replace(/\.[^.]+$/, '');
const tmpDir = await createCompressedArchiveContents(filename, pkg);
try {
await execFile('zip', ['-r', outFile, '.'], { cwd: tmpDir });
} catch (err) {
Expand Down