Skip to content

Commit

Permalink
fix(install): ignore dotfiles except for .ghost-cli in dir check
Browse files Browse the repository at this point in the history
closes #868
- add dir-is-empty util that checks empty directories correctly
  • Loading branch information
acburdine committed Jan 27, 2019
1 parent e68b65d commit 0bd929b
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 27 deletions.
8 changes: 3 additions & 5 deletions lib/commands/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ class InstallCommand extends Command {
}

run(argv) {
const fs = require('fs-extra');
const every = require('lodash/every');
const errors = require('../errors');
const yarnInstall = require('../tasks/yarn-install');
const dirIsEmpty = require('../utils/dir-is-empty');
const ensureStructure = require('../tasks/ensure-structure');

let version = argv.version;
const filesInDir = fs.readdirSync(process.cwd());

// Check if there are existing files that *aren't* ghost-cli debug log files
if (filesInDir.length && !every(filesInDir, file => file.match(/^ghost-cli-debug-.*\.log$/i))) {
// Check if the directory is empty
if (!dirIsEmpty(process.cwd())) {
return Promise.reject(new errors.SystemError('Current directory is not empty, Ghost cannot be installed here.'));
}

Expand Down
14 changes: 14 additions & 0 deletions lib/utils/dir-is-empty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const fs = require('fs');

const debugLogRegex = /^ghost-cli-debug-.*\.log$/i;
const importantDotfiles = ['.ghost-cli'];

module.exports = function dirIsEmpty(dir) {
const files = fs.readdirSync(dir);

if (!files.length) {
return true;
}

return files.every(file => file.match(debugLogRegex) || (file.startsWith('.') && !importantDotfiles.includes(file)));
};
41 changes: 19 additions & 22 deletions test/unit/commands/install-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,10 @@ describe('Unit: Commands > Install', function () {
});

it('rejects if directory is not empty', function () {
const readdirStub = sinon.stub().returns([
'.ghost-cli',
'README.md'
]);
const dirEmptyStub = sinon.stub().returns(false);

const InstallCommand = proxyquire(modulePath, {
'fs-extra': {readdirSync: readdirStub}
'../utils/dir-is-empty': dirEmptyStub
});
const testInstance = new InstallCommand({}, {});

Expand All @@ -55,16 +52,16 @@ describe('Unit: Commands > Install', function () {
}).catch((error) => {
expect(error).to.be.an.instanceof(errors.SystemError);
expect(error.message).to.match(/Current directory is not empty/);
expect(readdirStub.calledOnce).to.be.true;
expect(dirEmptyStub.calledOnce).to.be.true;
});
});

it('calls install checks first', function () {
const readdirStub = sinon.stub().returns(['ghost-cli-debug-1234.log']);
const dirEmptyStub = sinon.stub().returns(true);
const listrStub = sinon.stub().rejects();

const InstallCommand = proxyquire(modulePath, {
'fs-extra': {readdirSync: readdirStub},
'../utils/dir-is-empty': dirEmptyStub,
'./doctor': {doctorCommand: true}
});
const testInstance = new InstallCommand({listr: listrStub}, {});
Expand All @@ -73,7 +70,7 @@ describe('Unit: Commands > Install', function () {
return testInstance.run({argv: true}).then(() => {
expect(false, 'run should have rejected').to.be.true;
}).catch(() => {
expect(readdirStub.calledOnce).to.be.true;
expect(dirEmptyStub.calledOnce).to.be.true;
expect(runCommandStub.calledOnce).to.be.true;
expect(runCommandStub.calledWithExactly(
{doctorCommand: true},
Expand All @@ -84,22 +81,22 @@ describe('Unit: Commands > Install', function () {
});

it('runs local install when command is `ghost install local`', function () {
const readdirStub = sinon.stub().returns([]);
const dirEmptyStub = sinon.stub().returns(true);
const listrStub = sinon.stub();
listrStub.onFirstCall().resolves();
listrStub.onSecondCall().rejects();
const setEnvironmentStub = sinon.stub();

const InstallCommand = proxyquire(modulePath, {
'fs-extra': {readdirSync: readdirStub}
'../utils/dir-is-empty': dirEmptyStub
});
const testInstance = new InstallCommand({listr: listrStub}, {cliVersion: '1.0.0', setEnvironment: setEnvironmentStub});
const runCommandStub = sinon.stub(testInstance, 'runCommand').resolves();

return testInstance.run({version: 'local', zip: '', v1: true}).then(() => {
expect(false, 'run should have rejected').to.be.true;
}).catch(() => {
expect(readdirStub.calledOnce).to.be.true;
expect(dirEmptyStub.calledOnce).to.be.true;
expect(runCommandStub.calledOnce).to.be.true;
expect(listrStub.calledOnce).to.be.true;
expect(listrStub.args[0][1]).to.deep.equal({
Expand All @@ -114,22 +111,22 @@ describe('Unit: Commands > Install', function () {
});

it('runs local install when command is `ghost install <version> --local`', function () {
const readdirStub = sinon.stub().returns([]);
const dirEmptyStub = sinon.stub().returns(true);
const listrStub = sinon.stub();
listrStub.onFirstCall().resolves();
listrStub.onSecondCall().rejects();
const setEnvironmentStub = sinon.stub();

const InstallCommand = proxyquire(modulePath, {
'fs-extra': {readdirSync: readdirStub}
'../utils/dir-is-empty': dirEmptyStub
});
const testInstance = new InstallCommand({listr: listrStub}, {cliVersion: '1.0.0', setEnvironment: setEnvironmentStub});
const runCommandStub = sinon.stub(testInstance, 'runCommand').resolves();

return testInstance.run({version: '1.5.0', local: true, zip: '', v1: false}).then(() => {
expect(false, 'run should have rejected').to.be.true;
}).catch(() => {
expect(readdirStub.calledOnce).to.be.true;
expect(dirEmptyStub.calledOnce).to.be.true;
expect(runCommandStub.calledOnce).to.be.true;
expect(listrStub.calledOnce).to.be.true;
expect(listrStub.args[0][1]).to.deep.equal({
Expand All @@ -144,15 +141,15 @@ describe('Unit: Commands > Install', function () {
});

it('calls all tasks and returns after tasks run if --no-setup is passed', function () {
const readdirStub = sinon.stub().returns([]);
const dirEmptyStub = sinon.stub().returns(true);
const yarnInstallStub = sinon.stub().resolves();
const ensureStructureStub = sinon.stub().resolves();
const listrStub = sinon.stub().callsFake((tasks, ctx) => Promise.each(tasks, task => task.task(ctx, {})));

const InstallCommand = proxyquire(modulePath, {
'fs-extra': {readdirSync: readdirStub},
'../tasks/yarn-install': yarnInstallStub,
'../tasks/ensure-structure': ensureStructureStub
'../tasks/ensure-structure': ensureStructureStub,
'../utils/dir-is-empty': dirEmptyStub
});
const testInstance = new InstallCommand({listr: listrStub}, {cliVersion: '1.0.0'});
const runCommandStub = sinon.stub(testInstance, 'runCommand').resolves();
Expand All @@ -161,7 +158,7 @@ describe('Unit: Commands > Install', function () {
const casperStub = sinon.stub(testInstance, 'casper').resolves();

return testInstance.run({version: '1.0.0', setup: false}).then(() => {
expect(readdirStub.calledOnce).to.be.true;
expect(dirEmptyStub.calledOnce).to.be.true;
expect(listrStub.calledTwice).to.be.true;
expect(yarnInstallStub.calledOnce).to.be.true;
expect(ensureStructureStub.calledOnce).to.be.true;
Expand All @@ -173,19 +170,19 @@ describe('Unit: Commands > Install', function () {
});

it('sets local and runs setup command if setup is true', function () {
const readdirStub = sinon.stub().returns([]);
const dirEmptyStub = sinon.stub().returns(true);
const listrStub = sinon.stub().resolves();
const setEnvironmentStub = sinon.stub();

const InstallCommand = proxyquire(modulePath, {
'fs-extra': {readdirSync: readdirStub},
'../utils/dir-is-empty': dirEmptyStub,
'./setup': {SetupCommand: true}
});
const testInstance = new InstallCommand({listr: listrStub}, {cliVersion: '1.0.0', setEnvironment: setEnvironmentStub});
const runCommandStub = sinon.stub(testInstance, 'runCommand').resolves();

return testInstance.run({version: 'local', setup: true, zip: ''}).then(() => {
expect(readdirStub.calledOnce).to.be.true;
expect(dirEmptyStub.calledOnce).to.be.true;
expect(listrStub.calledOnce).to.be.true;
expect(setEnvironmentStub.calledOnce).to.be.true;
expect(setEnvironmentStub.calledWithExactly(true, true));
Expand Down
33 changes: 33 additions & 0 deletions test/unit/utils/dir-is-empty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const proxyquire = require('proxyquire');
const {expect} = require('chai');

const proxy = files => proxyquire('../../../lib/utils/dir-is-empty', {
fs: {readdirSync: () => files}
});

describe('Unit: Utils > dirIsEmpty', function () {
it('returns true if directory is empty', function () {
const fn = proxy([]);
expect(fn('dir')).to.be.true;
});

it('returns true if directory contains ghost debug log files', function () {
const fn = proxy(['ghost-cli-debug-1234.log']);
expect(fn('dir')).to.be.true;
});

it('returns true if directory contains dotfiles other than .ghost-cli', function () {
const fn = proxy(['.npmrc', '.gitignore']);
expect(fn('dir')).to.be.true;
});

it('returns false if directory contains .ghost-cli file', function () {
const fn = proxy(['.ghost-cli']);
expect(fn('dir')).to.be.false;
});

it('returns false if directory contains other files', function () {
const fn = proxy(['file.txt', 'file2.txt']);
expect(fn('dir')).to.be.false;
});
});

0 comments on commit 0bd929b

Please sign in to comment.