Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): determine git root correctly in sub directories #64

Merged
merged 3 commits into from
Aug 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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, '..');
}