Skip to content

Commit

Permalink
Run install and pack when fetching git dependencies with a prepare sc…
Browse files Browse the repository at this point in the history
…ript.
  • Loading branch information
Volune committed Jun 1, 2017
1 parent 76489f9 commit 46c5075
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 17 deletions.
20 changes: 20 additions & 0 deletions __tests__/fetchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,26 @@ test('GitFetcher.fetch', async () => {
expect(name).toBe('beeper');
});

test('GitFetcher.fetch with prepare script', async () => {
const dir = await mkdir('git-fetcher-with-prepare');
const fetcher = new GitFetcher(
dir,
{
type: 'git',
reference: 'https://github.com/Volune/test-js-git-repo',
hash: '90f974f63a1ab7f9f9030808896ff39b4d597591',
registry: 'npm',
},
(await Config.create()),
);
await fetcher.fetch();
const name = (await fs.readJson(path.join(dir, 'package.json'))).name;
expect(name).toBe('test-js-git-repo');
const dependencyName = (await fs.readJson(path.join(dir, 'dependency-package.json'))).name;
expect(dependencyName).toBe('beeper');
expect(await fs.exists(path.join(dir, 'prepare.js'))).toBe(false);
});

test('TarballFetcher.fetch', async () => {
const dir = await mkdir('tarball-fetcher');
const fetcher = new TarballFetcher(
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"strip-bom": "^3.0.0",
"tar-fs": "^1.15.1",
"tar-stream": "^1.5.2",
"test-js-git-repo": "https://github.com/Volune/test-js-git-repo",
"uuid": "^3.0.1",
"v8-compile-cache": "^1.1.0",
"validate-npm-package-license": "^3.0.1"
Expand Down
16 changes: 11 additions & 5 deletions src/cli/commands/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,13 @@ export function setFlags(commander: Object) {
commander.option('-T, --save-tilde', 'DEPRECATED');
}

export async function install(config: Config, reporter: Reporter, flags: Object, lockfile: Lockfile): Promise<void> {
await wrapLifecycle(config, flags, async () => {
const install = new Install(flags, config, reporter, lockfile);
await install.init();
});
}

export async function run(config: Config, reporter: Reporter, flags: Object, args: Array<string>): Promise<void> {
let lockfile;
if (flags.lockfile === false) {
Expand Down Expand Up @@ -826,10 +833,7 @@ export async function run(config: Config, reporter: Reporter, flags: Object, arg
throw new MessageError(reporter.lang('installCommandRenamed', `yarn ${command} ${exampleArgs.join(' ')}`));
}

await wrapLifecycle(config, flags, async () => {
const install = new Install(flags, config, reporter, lockfile);
await install.init();
});
await install(config, reporter, flags, lockfile);
}

export async function wrapLifecycle(config: Config, flags: Object, factory: () => Promise<void>): Promise<void> {
Expand All @@ -842,7 +846,9 @@ export async function wrapLifecycle(config: Config, flags: Object, factory: () =
await config.executeLifecycleScript('postinstall');

if (!config.production) {
await config.executeLifecycleScript('prepublish');
if (!config.gitDependency) {
await config.executeLifecycleScript('prepublish');
}
await config.executeLifecycleScript('prepare');
}
}
12 changes: 10 additions & 2 deletions src/cli/commands/pack.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ const NEVER_IGNORE = ignoreLinesToRegex([
'!/+(changes|changelog|history)*',
]);

export async function pack(config: Config, dir: string): Promise<stream$Duplex> {
export async function packTarball(
config: Config,
{mapHeader}: {mapHeader?: (Object) => Object} = {},
): Promise<stream$Duplex> {
const pkg = await config.readRootManifest();
const {bundledDependencies, main, files: onlyFiles} = pkg;

Expand Down Expand Up @@ -127,10 +130,15 @@ export async function pack(config: Config, dir: string): Promise<stream$Duplex>
header.name = `package${suffix}`;
delete header.uid;
delete header.gid;
return header;
return mapHeader ? mapHeader(header) : header;
},
});

return packer;
}

export async function pack(config: Config, dir: string): Promise<stream$Duplex> {
const packer = await packTarball(config);
const compressor = packer.pipe(new zlib.Gzip());

return compressor;
Expand Down
5 changes: 5 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type ConfigOptions = {
ignoreEngines?: boolean,
cafile?: ?string,
production?: boolean,
gitDependency?: boolean,
binLinks?: boolean,
networkConcurrency?: number,
childConcurrency?: number,
Expand Down Expand Up @@ -142,6 +143,8 @@ export default class Config {

production: boolean;

gitDependency: boolean;

nonInteractive: boolean;

workspacesExperimental: boolean;
Expand Down Expand Up @@ -320,6 +323,8 @@ export default class Config {
this.ignorePlatform = !!opts.ignorePlatform;
this.ignoreScripts = !!opts.ignoreScripts;

this.gitDependency = !!opts.gitDependency;

this.nonInteractive = !!opts.nonInteractive;

this.requestManager.setOptions({
Expand Down
131 changes: 121 additions & 10 deletions src/fetchers/git-fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import Git from '../util/git.js';
import * as fsUtil from '../util/fs.js';
import * as constants from '../constants.js';
import * as crypto from '../util/crypto.js';
import {install} from '../cli/commands/install.js';
import Lockfile from '../lockfile/wrapper.js';
import Config from '../config.js';
import {packTarball} from '../cli/commands/pack.js';

const tarFs = require('tar-fs');
const url = require('url');
Expand All @@ -15,6 +19,8 @@ const fs = require('fs');

const invariant = require('invariant');

const PACKED_FLAG = '1';

export default class GitFetcher extends BaseFetcher {
async getLocalAvailabilityStatus(): Promise<boolean> {
// Some mirrors might still have files named "./reponame" instead of "./reponame-commit"
Expand Down Expand Up @@ -78,11 +84,7 @@ export default class GitFetcher extends BaseFetcher {
}

return new Promise((resolve, reject) => {
const untarStream = tarFs.extract(this.dest, {
dmode: 0o555, // all dirs should be readable
fmode: 0o444, // all files should be readable
chown: false, // don't chown. just leave as it is
});
const untarStream = this._createUntarStream(this.dest);

const hashStream = new crypto.HashStream();

Expand Down Expand Up @@ -117,22 +119,131 @@ export default class GitFetcher extends BaseFetcher {
const gitUrl = Git.npmUrlToGitUrl(this.reference);
const git = new Git(this.config, gitUrl, hash);
await git.init();
await git.clone(this.dest);

const manifestFile = await git.getFile('package.json');
if (!manifestFile) {
throw new MessageError(this.reporter.lang('couldntFindPackagejson', gitUrl));
}
const scripts = JSON.parse(manifestFile).scripts;
const hasPrepareScript = Boolean(scripts && scripts.prepare);

if (hasPrepareScript) {
await this.fetchFromInstallAndPack(git);
} else {
await this.fetchFromGitArchive(git);
}

return {
hash,
};
}

async fetchFromInstallAndPack(git: Git): Promise<void> {
const prepareDirectory = this.config.getTemp(
`${crypto.hash(git.gitUrl.repository)}.${git.hash}.prepare`);
await fsUtil.unlink(prepareDirectory);

await git.clone(prepareDirectory);

const [
prepareConfig,
prepareLockFile,
] = await Promise.all([
Config.create({
cwd: prepareDirectory,
gitDependency: true,
}, this.reporter),
Lockfile.fromDirectory(prepareDirectory, this.reporter),
]);
await install(prepareConfig, this.reporter, {}, prepareLockFile);

const tarballMirrorPath = this.getTarballMirrorPath();
const tarballCachePath = this.getTarballCachePath();

if (tarballMirrorPath) {
await this._packToTarball(prepareConfig, tarballMirrorPath);
}
if (tarballCachePath) {
await this._packToTarball(prepareConfig, tarballCachePath);
}

await this._packToDirectory(prepareConfig, this.dest);

await fsUtil.unlink(prepareDirectory);
}

async _packToTarball(config: Config, path: string): Promise<void> {
const tarballStream = await this._createTarballStream(config);
await new Promise((resolve, reject) => {
const writeStream = fs.createWriteStream(path);
tarballStream.on('error', reject);
writeStream.on('error', reject);
writeStream.on('end', resolve);
writeStream.on('open', () => {
tarballStream.pipe(writeStream);
});
writeStream.once('finish', resolve);
});
}

async _packToDirectory(config: Config, dest: string): Promise<void> {
const tarballStream = await this._createTarballStream(config);
await new Promise((resolve, reject) => {
const untarStream = this._createUntarStream(dest);
tarballStream.on('error', reject);
untarStream.on('error', reject);
untarStream.on('end', resolve);
untarStream.once('finish', resolve);
tarballStream.pipe(untarStream);
});
}

_createTarballStream(config: Config): Promise<stream$Duplex> {
let savedPackedHeader = false;
return packTarball(config, {
mapHeader(header: Object): Object {
if (!savedPackedHeader) {
savedPackedHeader = true;
header.pax = header.pax || {};
// add a custom data on the first header
// in order to distinguish a tar from "git archive" and a tar from "pack" command
header.pax.packed = PACKED_FLAG;
}
return header;
},
});
}

_createUntarStream(dest: string): stream$Writable {
const PREFIX = 'package/';
let isPackedTarball = undefined;
return tarFs.extract(dest, {
dmode: 0o555, // all dirs should be readable
fmode: 0o444, // all files should be readable
chown: false, // don't chown. just leave as it is
map: header => {
if (isPackedTarball === undefined) {
isPackedTarball = header.pax && header.pax.packed === PACKED_FLAG;
}
if (isPackedTarball) {
header.name = header.name.substr(PREFIX.length);
}
},
});
}

async fetchFromGitArchive(git: Git): Promise<void> {
await git.clone(this.dest);
const tarballMirrorPath = this.getTarballMirrorPath();
const tarballCachePath = this.getTarballCachePath();

if (tarballMirrorPath) {
await git.archive(tarballMirrorPath);
}

if (tarballCachePath) {
await git.archive(tarballCachePath);
}

return {
hash,
};
}

async _fetch(): Promise<FetchedOverride> {
Expand Down

0 comments on commit 46c5075

Please sign in to comment.