Skip to content

Commit

Permalink
fix(core): determine git root correctly in sub directories (#64)
Browse files Browse the repository at this point in the history
* test(cli): add failing integration test

* fix: determine git root correctly from sub directories

* fixup! make git initializing work

fixes #62
  • Loading branch information
marionebl authored Aug 21, 2017
1 parent e6d3b3c commit d594ec4
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 27 deletions.
102 changes: 82 additions & 20 deletions @commitlint/cli/cli.test.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,135 @@
import path from 'path';
import test from 'ava';
import execa from 'execa';
import {sync as bin} from 'resolve-bin';
import * as sander from 'sander';
import stream from 'string-to-stream';
import tmp from 'tmp';

const here = path.join.bind(null, __dirname);
const fix = here.bind(null, 'fixtures');

const SIMPLE = here('fixtures/simple');
const EXTENDS_ROOT = here('fixtures/extends-root');
const EMPTY = here('fixtures/empty');

const cli = (input = '', args = [], opts = {}) => {
const c = execa(here('cli.js'), args, {
capture: ['stdout'],
cwd: opts.cwd
});
stream(input).pipe(c.stdin);
return c;
const CLI = here('cli.js');
const SIMPLE = fix('simple');
const EXTENDS_ROOT = fix('extends-root');
const EMPTY = fix('empty');

const HUSKY = tmp.dirSync().name;
const HUSKY_INTEGRATION = path.join(tmp.dirSync().name, 'integration');

const exec = (command, args = [], opts = {}) => {
return async (input = '') => {
const c = execa(command, args, {
capture: ['stdout'],
cwd: opts.cwd
});
stream(input).pipe(c.stdin);
const result = await c;
if (result.code !== 0) {
console.log(result.stderr);
}
return result;
}
};

const cli = exec.bind(null, CLI);
const git = exec.bind(null, 'git');
const mkdir = exec.bind(null, bin('mkdirp'));
const npm = exec.bind(null, 'npm');
const rm = exec.bind(null, bin('rimraf'));

test('should throw when called without [input]', t => {
t.throws(cli(), /Expected a raw commit/);
t.throws(cli()(), /Expected a raw commit/);
});

test('should reprint input from stdin', async t => {
const actual = await cli('foo: bar', [], {cwd: EMPTY});
const actual = await cli([], {cwd: EMPTY})('foo: bar');
t.true(actual.stdout.includes('foo: bar'));
});

test('should produce no success output with --quiet flag', async t => {
const actual = await cli('foo: bar', ['--quiet'], {cwd: EMPTY});
const actual = await cli(['--quiet'], {cwd: EMPTY})('foo: bar');
t.is(actual.stdout, '');
t.is(actual.stderr, '');
});

test('should produce no success output with -q flag', async t => {
const actual = await cli('foo: bar', ['-q'], {cwd: EMPTY});
const actual = await cli(['-q'], {cwd: EMPTY})('foo: bar');
t.is(actual.stdout, '');
t.is(actual.stderr, '');
});

test('should succeed for input from stdin without rules', async t => {
const actual = await cli('foo: bar', [], {cwd: EMPTY});
const actual = await cli([], {cwd: EMPTY})('foo: bar');
t.is(actual.code, 0);
});

test('should fail for input from stdin with rule from rc', async t => {
const actual = await t.throws(cli('foo: bar', [], {cwd: SIMPLE}));
const actual = await t.throws(cli([], {cwd: SIMPLE})('foo: bar'));
t.true(actual.stdout.includes('type must not be one of [foo]'));
t.is(actual.code, 1);
});

test('should fail for input from stdin with rule from js', async t => {
const actual = await t.throws(
cli('foo: bar', ['--extends', './extended'], {cwd: EXTENDS_ROOT})
cli(['--extends', './extended'], {cwd: EXTENDS_ROOT})('foo: bar')
);
t.true(actual.stdout.includes('type must not be one of [foo]'));
t.is(actual.code, 1);
});

test('should produce no error output with --quiet flag', async t => {
const actual = await t.throws(cli('foo: bar', ['--quiet'], {cwd: SIMPLE}));
const actual = await t.throws(cli(['--quiet'], {cwd: SIMPLE})('foo: bar'));
t.is(actual.stdout, '');
t.is(actual.stderr, '');
t.is(actual.code, 1);
});

test('should produce no error output with -q flag', async t => {
const actual = await t.throws(cli('foo: bar', ['-q'], {cwd: SIMPLE}));
const actual = await t.throws(cli(['-q'], {cwd: SIMPLE})('foo: bar'));
t.is(actual.stdout, '');
t.is(actual.stderr, '');
t.is(actual.code, 1);
});

test('should work with husky commitmsg hook', async () => {
const cwd = HUSKY;

await init(cwd);
await pkg(cwd);

await npm(['install', 'husky'], {cwd})();
await git(['add', 'package.json'], {cwd})();
await git(['commit', '-m', '"chore: this should work"'], {cwd})();

await rm([HUSKY])();
});

test('should work with husky commitmsg hook in sub packages', async () => {
const cwd = HUSKY_INTEGRATION;
const upper = path.dirname(HUSKY_INTEGRATION);

await mkdir([cwd])();
await init(upper);
await pkg(cwd);

await npm(['install', 'husky'], {cwd})();
await git(['add', 'package.json'], {cwd})();

await git(['commit', '-m', '"chore: this should work"'], {cwd})();

await rm([upper])();
});

async function init(cwd) {
await git(['init'], {cwd})();

return Promise.all([
git(['config', 'user.email', '"[email protected]"'], {cwd})(),
git(['config', 'user.name', '"commitlint"'], {cwd})()
]);
}

function pkg(cwd) {
return sander.writeFile(cwd, 'package.json', JSON.stringify({scripts: {commitmsg: `${CLI} -e`}}));
}
1 change: 1 addition & 0 deletions @commitlint/cli/fixtures/husky/integration/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"scripts":{"commitmsg":"commitlint -e"}}
5 changes: 5 additions & 0 deletions @commitlint/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@
"ava": "^0.18.2",
"dependency-check": "^2.9.1",
"execa": "^0.7.0",
"mkdirp": "^0.5.1",
"resolve-bin": "^0.4.0",
"rimraf": "^2.6.1",
"sander": "^0.6.0",
"string-to-stream": "^1.1.0",
"tmp": "0.0.33",
"xo": "^0.18.2"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion @commitlint/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@
"chalk": "^2.0.1",
"conventional-changelog-angular": "^1.3.3",
"conventional-commits-parser": "^1.3.0",
"find-up": "^2.1.0",
"franc": "^2.0.0",
"git-raw-commits": "^1.1.2",
"git-toplevel": "^1.1.1",
"import-from": "^2.1.0",
"lodash": "^4.17.4",
"mz": "^2.6.0",
Expand Down
34 changes: 28 additions & 6 deletions @commitlint/core/src/read.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {join} from 'path';
import path from 'path';
import exists from 'path-exists';
import up from 'find-up';
import gitRawCommits from 'git-raw-commits';
import gitToplevel from 'git-toplevel';
import {readFile} from 'mz/fs';

export default getCommitMessages;
Expand Down Expand Up @@ -45,16 +45,38 @@ function getHistoryCommits(options) {
// Check if the current repository is shallow
// () => Promise<Boolean>
async function isShallow() {
const top = await gitToplevel();
const shallow = join(top, '.git/shallow');
const top = await toplevel();

if (typeof top !== 'string') {
throw new TypeError(`Could not find git root - is this a git repository?`);
}

const shallow = path.join(top, '.git/shallow');
return exists(shallow);
}

// Get recently edited commit message
// () => Promise<Array<String>>
async function getEditCommit() {
const top = await gitToplevel();
const editFilePath = join(top, '.git/COMMIT_EDITMSG');
const top = await toplevel();

if (typeof top !== 'string') {
throw new TypeError(`Could not find git root - is this a git repository?`);
}

const editFilePath = path.join(top, '.git/COMMIT_EDITMSG');
const editFile = await readFile(editFilePath);
return [`${editFile.toString('utf-8')}\n`];
}

// Find the next git root
// (start: string) => Promise<string | null>
async function toplevel(cwd = process.cwd()) {
const found = await up('.git', {cwd});

if (typeof found !== 'string') {
return found;
}

return path.join(found, '..');
}

0 comments on commit d594ec4

Please sign in to comment.