Skip to content
Permalink
Browse files
feat(changed): Support list output options
BREAKING CHANGE: The package names emitted to stdout are no longer prefixed by a "- ", and private packages are no longer displayed by default.
  • Loading branch information
evocateur committed Jul 30, 2018
1 parent dff8176 commit 6ecdd83d14f3b9d0cac62cc68b4066ec554e79ce
Showing 7 changed files with 121 additions and 152 deletions.
@@ -1,37 +1,29 @@
# `@lerna/changed`

> Check which packages have changed since the last publish
> List local packages that have changed since the last tagged release
## Usage

The output of `lerna changed` is a list of packages that would be the subjects of the next `lerna version` or `lerna publish` execution.

```sh
$ lerna changed
package-1
package-2
```

Check which `packages` have changed since the last release (the last git tag).

Lerna determines the last git tag created and runs `git diff --name-only v6.8.1` to get all files changed since that tag. It then returns an array of packages that have an updated file.

**Note that configuration for the `publish` command _also_ affects the
`changed` command. For example `command.publish.ignoreChanges`**
**Note:** `lerna.json` configuration for `lerna publish` _and_ `lerna version` also affects
`lerna changed`, e.g. `command.publish.ignoreChanges`.

## Options

### `--json`
`lerna changed` supports all of the flags supported by [`lerna ls`](https://github.com/lerna/lerna/tree/master/commands/list#options):

```sh
$ lerna changed --json
```

When run with this flag, `changed` will return an array of objects in the following format:
* [`--json`](https://github.com/lerna/lerna/tree/master/commands/list#--json)
* [`-a`, `--all`](https://github.com/lerna/lerna/tree/master/commands/list#--all)
* [`-l`, `--long`](https://github.com/lerna/lerna/tree/master/commands/list#--long)
* [`-p`, `--parseable`](https://github.com/lerna/lerna/tree/master/commands/list#--parseable)

```json
[
{
"name": "package",
"version": "1.0.0",
"private": false
}
]
```
Unlike `lerna ls`, however, `lerna changed` **does not** support [filter options](https://www.npmjs.com/package/@lerna/filter-options), as filtering is not supported by `lerna version` or `lerna publish`.

`lerna changed` also supports all the flags supported by [`lerna version`](https://github.com/lerna/lerna/tree/master/commands/version#options), but the only relevant one is [`--ignore-changes`](https://github.com/lerna/lerna/tree/master/commands/version#--ignore-changes).

This file was deleted.

@@ -19,11 +19,15 @@ const updateLernaConfig = require("@lerna-test/update-lerna-config");
// file under test
const lernaChanged = require("@lerna-test/command-runner")(require("../command"));

const touchFile = cwd => filePath => touch(path.join(cwd, filePath));
// remove quotes around strings
expect.addSnapshotSerializer({ test: val => typeof val === "string", print: val => val });

// normalize temp directory paths in snapshots
expect.addSnapshotSerializer(require("@lerna-test/serialize-tempdir"));

const setupGitChanges = async (cwd, filePaths) => {
await gitTag(cwd, "v1.0.0");
await Promise.all(filePaths.map(touchFile(cwd)));
await Promise.all(filePaths.map(fp => touch(path.join(cwd, fp))));
await gitAdd(cwd, "-A");
await gitCommit(cwd, "Commit");
};
@@ -40,15 +44,23 @@ describe("ChangedCommand", () => {
await setupGitChanges(testDir, ["packages/package-2/random-file"]);
await lernaChanged(testDir)();

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`
package-2
package-3
`);
});

it("should list all packages when no tag is found", async () => {
const testDir = await initFixture("basic");

await lernaChanged(testDir)();

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`
package-1
package-2
package-3
package-4
`);
});

it("should list changes with --force-publish", async () => {
@@ -57,7 +69,12 @@ describe("ChangedCommand", () => {
await setupGitChanges(testDir, ["packages/package-2/random-file"]);
await lernaChanged(testDir)("--force-publish");

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`
package-1
package-2
package-3
package-4
`);
});

it("should list changes with --force-publish package-2,package-4", async () => {
@@ -66,7 +83,11 @@ describe("ChangedCommand", () => {
await setupGitChanges(testDir, ["packages/package-3/random-file"]);
await lernaChanged(testDir)("--force-publish", "package-2,package-4");

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`
package-2
package-3
package-4
`);
});

it("should list changes with --force-publish package-2 --force-publish package-4", async () => {
@@ -75,7 +96,11 @@ describe("ChangedCommand", () => {
await setupGitChanges(testDir, ["packages/package-3/random-file"]);
await lernaChanged(testDir)("--force-publish", "package-2", "--force-publish", "package-4");

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`
package-2
package-3
package-4
`);
});

it("should list changes without ignored files", async () => {
@@ -92,16 +117,16 @@ describe("ChangedCommand", () => {
await setupGitChanges(testDir, ["packages/package-2/ignored-file", "packages/package-3/random-file"]);
await lernaChanged(testDir)();

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`package-3`);
});

it("should list changes in private packages", async () => {
it("should list changes in private packages with --all", async () => {
const testDir = await initFixture("basic");

await setupGitChanges(testDir, ["packages/package-5/random-file"]);
await lernaChanged(testDir)();
await lernaChanged(testDir)("--all");

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`package-5 (PRIVATE)`);
});

it("should return a non-zero exit code when there are no changes", async () => {
@@ -115,6 +140,20 @@ describe("ChangedCommand", () => {
// reset exit code
process.exitCode = undefined;
});

it("supports all listable flags", async () => {
const testDir = await initFixture("basic");

await lernaChanged(testDir)("-alp");

expect(output.logged()).toMatchInlineSnapshot(`
<PROJECT_ROOT>/packages/package-1:package-1:1.0.0
<PROJECT_ROOT>/packages/package-2:package-2:1.0.0
<PROJECT_ROOT>/packages/package-3:package-3:1.0.0
<PROJECT_ROOT>/packages/package-4:package-4:1.0.0
<PROJECT_ROOT>/packages/package-5:package-5:1.0.0:PRIVATE
`);
});
});

/** =========================================================================
@@ -128,7 +167,10 @@ describe("ChangedCommand", () => {
await setupGitChanges(testDir, ["packages/package-3/random-file"]);
await lernaChanged(testDir)();

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`
package-3
package-4
`);
});

it("should list changes with --force-publish *", async () => {
@@ -137,7 +179,12 @@ describe("ChangedCommand", () => {
await setupGitChanges(testDir, ["packages/package-2/random-file"]);
await lernaChanged(testDir)("--force-publish", "*");

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`
package-1
package-2
package-3
package-4
`);
});

it("should list changes with --force-publish package-2", async () => {
@@ -146,7 +193,11 @@ describe("ChangedCommand", () => {
await setupGitChanges(testDir, ["packages/package-4/random-file"]);
await lernaChanged(testDir)("--force-publish", "package-2");

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`
package-2
package-3
package-4
`);
});

it("should list changes without ignored files", async () => {
@@ -163,7 +214,10 @@ describe("ChangedCommand", () => {
await setupGitChanges(testDir, ["packages/package-2/ignored-file", "packages/package-3/random-file"]);
await lernaChanged(testDir)();

expect(output.logged()).toMatchSnapshot();
expect(output.logged()).toMatchInlineSnapshot(`
package-3
package-4
`);
});

it("should return a non-zero exit code when there are no changes", async () => {
@@ -192,7 +246,22 @@ describe("ChangedCommand", () => {

// Output should be a parseable string
const jsonOutput = JSON.parse(output.logged());
expect(jsonOutput).toMatchSnapshot();
expect(jsonOutput).toMatchInlineSnapshot(`
Array [
Object {
location: <PROJECT_ROOT>/packages/package-2,
name: package-2,
private: false,
version: 1.0.0,
},
Object {
location: <PROJECT_ROOT>/packages/package-3,
name: package-3,
private: false,
version: 1.0.0,
},
]
`);
});
});
});
@@ -1,6 +1,7 @@
"use strict";

const versionOptions = require("@lerna/version/command").builder;
const listable = require("@lerna/listable");

/**
* @see https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module
@@ -9,16 +10,13 @@ exports.command = "changed";

exports.aliases = ["updated"];

exports.describe = "Check which packages have changed since the last release";
exports.describe = "List local packages that have changed since the last tagged release";

exports.builder = yargs =>
versionOptions(yargs, true).options({
json: {
describe: "Show information in JSON format",
group: "Command Options:",
type: "boolean",
},
});
exports.builder = yargs => {
listable.options(yargs);

return versionOptions(yargs, true);
};

exports.handler = function handler(argv) {
return require(".")(argv);

0 comments on commit 6ecdd83

Please sign in to comment.