Skip to content

Commit

Permalink
feat: Create @lerna/describe-ref
Browse files Browse the repository at this point in the history
  • Loading branch information
evocateur committed Aug 17, 2018
1 parent 67494e7 commit 8c11b75
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 0 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions utils/describe-ref/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# `@lerna/describe-ref`

> Parse [git describe][] output for lerna-related tags
## Usage

```js
const describe = require('@lerna/describe-ref');

(async () => {
const { lastTag, lastVersion, refCount, sha, isDirty } = await describe();
})();

// values listed here are their defaults
const options = {
cwd: process.cwd(),
// pass a glob to match tag name, e.g. "v*.*.*"
match: undefined,
};

const {
lastTag,
lastVersion,
refCount,
sha,
isDirty,
} = describe.sync(options);

const result = describe.parse("v1.0.0-5-gdeadbeef");
// { lastTag, lastVersion, refCount, sha, isDirty }
```

[git describe]: https://git-scm.com/docs/git-describe
115 changes: 115 additions & 0 deletions utils/describe-ref/__tests__/describe-ref.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"use strict";

jest.mock("@lerna/child-process");

const childProcess = require("@lerna/child-process");
const describeRef = require("../lib/describe-ref");

const DEFAULT_ARGS = ["describe", "--always", "--long", "--dirty", "--first-parent"];

childProcess.exec.mockResolvedValue({ stdout: "v1.2.3-4-g567890a" });
childProcess.execSync.mockReturnValue("v1.2.3-4-g567890a");

describe("describeRef()", () => {
it("resolves parsed metadata", async () => {
const result = await describeRef();

expect(childProcess.exec).lastCalledWith("git", DEFAULT_ARGS, undefined);
expect(result).toEqual({
isDirty: undefined,
lastTag: "v1.2.3",
lastVersion: "v1.2.3",
refCount: "4",
sha: "567890a",
});
});

it("accepts options.cwd", async () => {
const options = { cwd: "foo" };
await describeRef(options);

expect(childProcess.exec).lastCalledWith("git", DEFAULT_ARGS, options);
});

it("accepts options.match", async () => {
const options = { match: "v*.*.*" };
await describeRef(options);

expect(childProcess.exec).lastCalledWith("git", DEFAULT_ARGS.concat(["--match", "v*.*.*"]), options);
});
});

describe("describeRef.sync()", () => {
it("returns parsed metadata", () => {
const result = describeRef.sync();

expect(childProcess.execSync).lastCalledWith("git", DEFAULT_ARGS, undefined);
expect(result).toEqual({
isDirty: undefined,
lastTag: "v1.2.3",
lastVersion: "v1.2.3",
refCount: "4",
sha: "567890a",
});
});

it("accepts options.cwd", () => {
const options = { cwd: "foo" };
describeRef.sync(options);

expect(childProcess.execSync).lastCalledWith("git", DEFAULT_ARGS, options);
});

it("accepts options.match", () => {
const options = { match: "v*.*.*" };
describeRef.sync(options);

expect(childProcess.execSync).lastCalledWith("git", DEFAULT_ARGS.concat(["--match", "v*.*.*"]), options);
});
});

describe("describeRef.parse()", () => {
it("matches independent tags", () => {
const result = describeRef.parse("pkg-name@1.2.3-4-g567890a");

expect(result.lastTag).toBe("pkg-name@1.2.3");
expect(result.lastVersion).toBe("1.2.3");
});

it("matches independent tags for scoped packages", () => {
const result = describeRef.parse("@scope/pkg-name@1.2.3-4-g567890a");

expect(result.lastTag).toBe("@scope/pkg-name@1.2.3");
expect(result.lastVersion).toBe("1.2.3");
});

it("matches dirty annotations", () => {
const result = describeRef.parse("pkg-name@1.2.3-4-g567890a-dirty");

expect(Boolean(result.isDirty)).toBe(true);
});

it("handles non-matching strings safely", () => {
const result = describeRef.parse("poopy-pants");

expect(result).toEqual({
isDirty: undefined,
lastTag: undefined,
lastVersion: undefined,
refCount: undefined,
sha: undefined,
});
});

it("detects fallback and returns partial metadata", () => {
const result = describeRef.parse("a1b2c3d");

expect(result).toEqual({
isDirty: undefined,
lastTag: undefined,
lastVersion: undefined,
refCount: undefined,
sha: "a1b2c3d",
});
});
});
51 changes: 51 additions & 0 deletions utils/describe-ref/lib/describe-ref.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use strict";

const childProcess = require("@lerna/child-process");

module.exports = describeRef;
module.exports.parse = parse;
module.exports.sync = sync;

function getArgs(options = {}) {
const args = [
"describe",
// fallback to short sha if no tags located
"--always",
// always return full result, helps identify existing release
"--long",
// annotate if uncommitted changes present
"--dirty",
// prefer tags originating on upstream branch
"--first-parent",
];

if (options.match) {
args.push("--match", options.match);
}

return args;
}

function describeRef(options) {
const promise = childProcess.exec("git", getArgs(options), options);

return promise.then(({ stdout }) => parse(stdout));
}

function sync(options) {
const stdout = childProcess.execSync("git", getArgs(options), options);

return parse(stdout);
}

function parse(stdout) {
if (/^[0-9a-f]{7,40}$/.test(stdout)) {
// fallback received, can't provide full metadata
return { sha: stdout };
}

const [, lastTag, lastVersion, refCount, sha, isDirty] =
/^((?:.*@)?(.*))-(\d+)-g([0-9a-f]+)(-dirty)?$/.exec(stdout) || [];

return { lastTag, lastVersion, refCount, sha, isDirty };
}
34 changes: 34 additions & 0 deletions utils/describe-ref/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@lerna/describe-ref",
"version": "3.0.6",
"description": "Parse git describe output for lerna-related tags",
"keywords": [
"lerna",
"utils",
"git",
"describe"
],
"author": "Daniel Stockman <daniel.stockman@gmail.com>",
"homepage": "https://github.com/lerna/lerna/tree/master/utils/describe-ref#readme",
"license": "MIT",
"main": "lib/describe-ref.js",
"files": [
"lib"
],
"engines": {
"node": ">= 6.9.0"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/lerna/lerna.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
},
"dependencies": {
"@lerna/child-process": "file:../../core/child-process"
}
}

0 comments on commit 8c11b75

Please sign in to comment.