Skip to content

Commit

Permalink
Merge branch 'master' into otplease
Browse files Browse the repository at this point in the history
  • Loading branch information
evocateur committed May 13, 2019
2 parents cc8cfa8 + 98e77cf commit 2f8127f
Show file tree
Hide file tree
Showing 82 changed files with 2,883 additions and 720 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -7,7 +7,7 @@ git:
language: node_js

node_js:
- "11"
- "12"
- "10"
- "8"

Expand Down
3 changes: 3 additions & 0 deletions __fixtures__/prerelease-independent/lerna.json
@@ -0,0 +1,3 @@
{
"version": "independent"
}
3 changes: 3 additions & 0 deletions __fixtures__/prerelease-independent/package.json
@@ -0,0 +1,3 @@
{
"name": "independent"
}
@@ -0,0 +1,4 @@
{
"name": "package-1",
"version": "1.0.0-alpha.0"
}
@@ -0,0 +1,7 @@
{
"name": "package-2",
"version": "2.0.0-alpha.0",
"dependencies": {
"package-1": "^1.0.0-alpha.0"
}
}
@@ -0,0 +1,7 @@
{
"name": "package-3",
"version": "3.0.0-beta.3",
"devDependencies": {
"package-2": "^2.0.0-alpha.0"
}
}
@@ -0,0 +1,7 @@
{
"name": "package-4",
"version": "4.0.0",
"dependencies": {
"package-1": "^0.0.0"
}
}
@@ -0,0 +1,8 @@
{
"name": "package-5",
"dependencies": {
"package-3": "^3.0.0-beta.3"
},
"private": true,
"version": "5.0.0-alpha.0"
}
3 changes: 3 additions & 0 deletions __fixtures__/prerelease/lerna.json
@@ -0,0 +1,3 @@
{
"version": "1.0.0-alpha.0"
}
3 changes: 3 additions & 0 deletions __fixtures__/prerelease/package.json
@@ -0,0 +1,3 @@
{
"name": "normal"
}
4 changes: 4 additions & 0 deletions __fixtures__/prerelease/packages/package-1/package.json
@@ -0,0 +1,4 @@
{
"name": "package-1",
"version": "1.0.0-alpha.0"
}
7 changes: 7 additions & 0 deletions __fixtures__/prerelease/packages/package-2/package.json
@@ -0,0 +1,7 @@
{
"name": "package-2",
"version": "1.0.0-alpha.0",
"dependencies": {
"package-1": "^1.0.0-alpha.0"
}
}
10 changes: 10 additions & 0 deletions __fixtures__/prerelease/packages/package-3/package.json
@@ -0,0 +1,10 @@
{
"name": "package-3",
"version": "1.0.0-alpha.0",
"peerDependencies": {
"package-2": "^1.0.0-alpha.0"
},
"devDependencies": {
"package-2": "^1.0.0-alpha.0"
}
}
7 changes: 7 additions & 0 deletions __fixtures__/prerelease/packages/package-4/package.json
@@ -0,0 +1,7 @@
{
"name": "package-4",
"version": "1.0.0-alpha.0",
"dependencies": {
"package-1": "^0.0.0"
}
}
11 changes: 11 additions & 0 deletions __fixtures__/prerelease/packages/package-5/package.json
@@ -0,0 +1,11 @@
{
"name": "package-5",
"dependencies": {
"package-1": "^1.0.0-alpha.0"
},
"optionalDependencies": {
"package-3": "^1.0.0-alpha.0"
},
"private": true,
"version": "1.0.0-alpha.0"
}
4 changes: 4 additions & 0 deletions commands/__mocks__/@lerna/collect-updates.js
@@ -1,5 +1,7 @@
"use strict";

const { collectPackages, getPackagesForOption } = jest.requireActual("@lerna/collect-updates");

// collectUpdates.setUpdated(cwd, packageNames...)
// otherwise, enables everything
const updated = new Map();
Expand All @@ -20,3 +22,5 @@ afterEach(() => {

module.exports = mockCollectUpdates;
module.exports.setUpdated = setUpdated;
module.exports.collectPackages = collectPackages;
module.exports.getPackagesForOption = getPackagesForOption;
10 changes: 10 additions & 0 deletions commands/import/README.md
Expand Up @@ -52,3 +52,13 @@ When importing repositories, you can specify the destination directory by the di
```
$ lerna import ~/Product --dest=utilities
```

### `--preserve-commit`

Each git commit has an **author** and a **committer** (with a separate date for each). Usually they're the same person (and date), but since `lerna import` re-creates each commit from the external repository, the **committer** becomes the current git user (and date). This is *technically* correct, but may be undesireable, for example, on Github, which displays both the **author** and **committer** if they're different people, leading to potentially confusing history/blames on imported commits.

Enabling this option preserves the original **committer** (and commit date) to avoid such issues.

```
$ lerna import ~/Product --preserve-commit
```
33 changes: 33 additions & 0 deletions commands/import/__tests__/import-command.test.js
Expand Up @@ -154,6 +154,39 @@ describe("ImportCommand", () => {
expect(await lastCommitInDir(testDir)).toBe("Init commit");
});

it("preserves original committer and date with --preserve-commit", async () =>
Promise.all(
// running the same test with and without --preserve-commit
[true, false].map(async shouldPreserve => {
const [testDir, externalDir] = await initBasicFixtures();
const filePath = path.join(externalDir, "old-file");
let expectedEmail;
let expectedName;

await execa("git", ["config", "user.name", "'test-name'"], { cwd: externalDir });
await execa("git", ["config", "user.email", "'test-email@foo.bar'"], { cwd: externalDir });
await fs.writeFile(filePath, "non-empty content");
await gitAdd(externalDir, filePath);
await gitCommit(externalDir, "Non-empty commit");

if (shouldPreserve) {
await lernaImport(testDir)(externalDir, "--preserve-commit");
// original committer
expectedEmail = "test-email@foo.bar";
expectedName = "test-name";
} else {
await lernaImport(testDir)(externalDir);
// whatever the current git user is
expectedEmail = execa.sync("git", ["config", "user.email"], { cwd: testDir }).stdout;
expectedName = execa.sync("git", ["config", "user.name"], { cwd: testDir }).stdout;
}

expect(execa.sync("git", ["log", "-1", "--format=%cn <%ce>"], { cwd: testDir }).stdout).toBe(
`${expectedName} <${expectedEmail}>`
);
})
));

it("allows skipping confirmation prompt", async () => {
const [testDir, externalDir] = await initBasicFixtures();
await lernaImport(testDir)(externalDir, "--yes");
Expand Down
5 changes: 5 additions & 0 deletions commands/import/command.js
Expand Up @@ -21,6 +21,11 @@ exports.builder = yargs =>
describe: "Import destination directory for the external git repository",
type: "string",
},
"preserve-commit": {
group: "Command Options:",
describe: "Preserve original committer in addition to original author",
type: "boolean",
},
y: {
group: "Command Options:",
describe: "Skip all confirmation prompts",
Expand Down
54 changes: 48 additions & 6 deletions commands/import/index.js
Expand Up @@ -94,10 +94,16 @@ class ImportCommand extends Command {
throw new ValidationError("NOCOMMITS", `No git commits to import at "${inputPath}"`);
}

if (this.options.preserveCommit) {
// Back these up since they'll change for each commit
this.origGitEmail = this.execSync("git", ["config", "user.email"]);
this.origGitName = this.execSync("git", ["config", "user.name"]);
}

// Stash the repo's pre-import head away in case something goes wrong.
this.preImportHead = this.getCurrentSHA();

if (ChildProcessUtilities.execSync("git", ["diff-index", "HEAD"], this.execOpts)) {
if (this.execSync("git", ["diff-index", "HEAD"])) {
throw new ValidationError("ECHANGES", "Local repository has un-committed changes");
}

Expand Down Expand Up @@ -126,11 +132,15 @@ class ImportCommand extends Command {
}

getCurrentSHA() {
return ChildProcessUtilities.execSync("git", ["rev-parse", "HEAD"], this.execOpts);
return this.execSync("git", ["rev-parse", "HEAD"]);
}

getWorkspaceRoot() {
return ChildProcessUtilities.execSync("git", ["rev-parse", "--show-toplevel"], this.execOpts);
return this.execSync("git", ["rev-parse", "--show-toplevel"]);
}

execSync(cmd, args) {
return ChildProcessUtilities.execSync(cmd, args, this.execOpts);
}

externalExecSync(cmd, args) {
Expand Down Expand Up @@ -186,6 +196,18 @@ class ImportCommand extends Command {
.replace(/^(rename (from|to)) /gm, `$1 ${formattedTarget}/`);
}

getGitUserFromSha(sha) {
return {
email: this.externalExecSync("git", ["show", "-s", "--format='%ae'", sha]),
name: this.externalExecSync("git", ["show", "-s", "--format='%an'", sha]),
};
}

configureGitUser({ email, name }) {
this.execSync("git", ["config", "user.email", `"${email}"`]);
this.execSync("git", ["config", "user.name", `"${name}"`]);
}

execute() {
this.enableProgressBar();

Expand All @@ -194,13 +216,19 @@ class ImportCommand extends Command {
tracker.info(sha);

const patch = this.createPatchForCommit(sha);
const procArgs = ["am", "-3", "--keep-non-patch"];

if (this.options.preserveCommit) {
this.configureGitUser(this.getGitUserFromSha(sha));
procArgs.push("--committer-date-is-author-date");
}

// Apply the modified patch to the current lerna repository, preserving
// original commit date, author and message.
//
// Fall back to three-way merge, which can help with duplicate commits
// due to merge history.
const proc = ChildProcessUtilities.exec("git", ["am", "-3", "--keep-non-patch"], this.execOpts);
const proc = ChildProcessUtilities.exec("git", procArgs, this.execOpts);

proc.stdin.end(patch);

Expand All @@ -227,16 +255,30 @@ class ImportCommand extends Command {
.then(() => {
tracker.finish();

if (this.options.preserveCommit) {
this.configureGitUser({
email: this.origGitEmail,
name: this.origGitName,
});
}

this.logger.success("import", "finished");
})
.catch(err => {
tracker.finish();

if (this.options.preserveCommit) {
this.configureGitUser({
email: this.origGitEmail,
name: this.origGitName,
});
}

this.logger.error("import", `Rolling back to previous HEAD (commit ${this.preImportHead})`);

// Abort the failed `git am` and roll back to previous HEAD.
ChildProcessUtilities.execSync("git", ["am", "--abort"], this.execOpts);
ChildProcessUtilities.execSync("git", ["reset", "--hard", this.preImportHead], this.execOpts);
this.execSync("git", ["am", "--abort"]);
this.execSync("git", ["reset", "--hard", this.preImportHead]);

throw new ValidationError(
"EIMPORT",
Expand Down
9 changes: 9 additions & 0 deletions commands/publish/README.md
Expand Up @@ -52,6 +52,7 @@ This is useful when a previous `lerna publish` failed to publish all packages to
- [`--no-git-reset`](#--no-git-reset)
- [`--no-verify-access`](#--no-verify-access)
- [`--preid`](#--preid)
- [`--pre-dist-tag <tag>`](#--pre-dist-tag-tag)
- [`--registry <url>`](#--registry-url)
- [`--temp-tag`](#--temp-tag)
- [`--ignore-scripts`](#--ignore-scripts)
Expand Down Expand Up @@ -157,6 +158,14 @@ lerna publish --canary --preid next
When run with this flag, `lerna publish --canary` will increment `premajor`, `preminor`, `prepatch`, or `prerelease` semver
bumps using the specified [prerelease identifier](http://semver.org/#spec-item-9).

### `--pre-dist-tag <tag>`

```sh
lerna publish --pre-dist-tag next
```

Works the same as [`--dist-tag`](#--dist-tag-tag), except only applies to packages being released with a prerelease version.

### `--registry <url>`

When run with this flag, forwarded npm commands will use the specified registry for your package(s).
Expand Down
62 changes: 62 additions & 0 deletions commands/publish/__tests__/publish-tagging.test.js
Expand Up @@ -96,3 +96,65 @@ Map {
expect(npmDistTag.add).toHaveBeenCalledWith("@integration/package-1@1.0.1", "beta", conf); // <--
expect(npmDistTag.add).toHaveBeenCalledWith("@integration/package-2@1.0.1", "beta", conf);
});

test("publish prerelease --pre-dist-tag beta", async () => {
const cwd = await initFixture("normal");

collectUpdates.setUpdated(cwd, "package-1");

await lernaPublish(cwd)("prerelease", "--pre-dist-tag", "beta");

expect(npmPublish.registry.get("package-1")).toBe("beta");
expect(npmDistTag.remove).not.toHaveBeenCalled();
});

test("publish non-prerelease --pre-dist-tag beta", async () => {
const cwd = await initFixture("normal");

collectUpdates.setUpdated(cwd, "package-1");

await lernaPublish(cwd)("--pre-dist-tag", "beta");

expect(npmPublish.registry.get("package-1")).toBe("latest");
expect(npmDistTag.remove).not.toHaveBeenCalled();
});

test("publish non-prerelease --dist-tag next --pre-dist-tag beta", async () => {
const cwd = await initFixture("normal");

collectUpdates.setUpdated(cwd, "package-1");

await lernaPublish(cwd)("--dist-tag", "next", "--pre-dist-tag", "beta");

expect(npmPublish.registry.get("package-1")).toBe("next");
expect(npmDistTag.remove).not.toHaveBeenCalled();
});

test("publish --pre-dist-tag beta --temp-tag", async () => {
const cwd = await initFixture("integration");

await lernaPublish(cwd)(
"prerelease",
"--dist-tag",
"next",
"--preid",
"beta",
"--pre-dist-tag",
"beta",
"--temp-tag"
);

expect(npmPublish.registry).toMatchInlineSnapshot(`
Map {
"@integration/package-1" => "lerna-temp",
"@integration/package-2" => "lerna-temp",
}
`);

const conf = expect.objectContaining({
tag: "next",
});

expect(npmDistTag.add).toHaveBeenCalledWith("@integration/package-1@1.0.1-beta.0", "beta", conf);
expect(npmDistTag.add).toHaveBeenCalledWith("@integration/package-2@1.0.1-beta.0", "beta", conf);
});

0 comments on commit 2f8127f

Please sign in to comment.