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

Add command to test a local bluprint from disk #30

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,6 @@ tmp/
temp/

# End of https://www.gitignore.io/api/node,macos,linux
n

# Editor
.vscode
11 changes: 10 additions & 1 deletion lib/cli.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { add, clone, newBluprint, remove, start, token } from 'bluprint';
import { add, clone, newBluprint, remove, start, token, test } from 'bluprint';
import { name, version } from '../package.json';

import chalk from 'chalk';
Expand Down Expand Up @@ -66,4 +66,13 @@ yargs // eslint-disable-line no-unused-expressions
}, async function({ accessToken }) {
await token(accessToken);
})
.command('test [directory]', 'Test the bluprint in the given directory', (yargs) => {
yargs
.positional('directory', {
describe: 'Local path to a bluprint',
type: 'string',
});
}, async function({ directory }) {
await test(directory);
})
.argv;
4 changes: 3 additions & 1 deletion lib/commands/start/fetchBluprint/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ export default (resolve, reject, filterGlobs, mergeJson) => {
if (entry.type === 'Directory') {
fs.mkdirSync(entryPath, { recursive: true });
entry.resume();
} else if (entry.type === 'File') {
} else if (['File', 'SymbolicLink'].includes(entry.type)) {
archive[entryPath] = [];
entry.on('data', c => archive[entryPath].push(c));
} else {
throw new Error(`unable to handle entry of type ${entry.type}`);
}
},
})
Expand Down
93 changes: 93 additions & 0 deletions lib/commands/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import fs from 'fs';
import path from 'path';
import tar from 'tar';
import os from 'os';

import handleActions from '../../actions';
import getLogger from '../../utils/getLogger';
import choosePart from '../start/choosePart';
import getParser from '../start/fetchBluprint/parser';
import minimatch from 'minimatch';

const logger = getLogger();

// best effort attempt at reproducing .gitignore behaviour
const createIgnoreFilter = config => {
const globs = fs.readFileSync(config, 'utf-8').split('\n').filter(line => !line.trim().startsWith('#'));
return file => !globs.some(glob => file.startsWith(glob) || minimatch(file, glob));
};

const getFiles = dir => {
const basename = path.basename(dir);
const files = fs.readdirSync(dir);
const result = [];

let filterFn = () => true;

for (const file of files) {
const fullPath = path.join(dir, file);

if (file === '.gitignore') {
filterFn = createIgnoreFilter(fullPath);
}

if (fs.statSync(fullPath).isDirectory()) {
getFiles(fullPath).forEach(f => result.push(f));
} else {
result.push(file);
}
}

return result.filter(e => filterFn(e) && !e.match(/^\.git(\/|$)/)).map(e => [basename, path.sep, e].join(''));
};

const buildTarball = async(directory) => {
const tarball = path.join(os.tmpdir(), 'tmp-bluprint.tar');
const files = getFiles(directory);

await tar.create({
gzip: false,
file: tarball,
cwd: path.dirname(directory),
strict: true,
}, files);

return tarball;
};

const fetchBluprint = async(tarballPath, filterGlobs, mergeJson) => {
return new Promise((resolve, reject) => {
const rs = fs.createReadStream(tarballPath);
const parser = getParser(resolve, reject, filterGlobs, mergeJson);

rs.pipe(parser)
.on('error', (e) => {
logger.error(`Tarball parsing error.`);
reject(e);
});
});
};

const defaultInject = {
method: null,
category: null,
bluprint: null,
partConfirm: null,
partChoice: null,
};

export default async(directory, inject = defaultInject) => {
const bluprintrc = JSON.parse(fs.readFileSync(path.join(directory, '.bluprintrc')));

const { parts, mergeJson } = bluprintrc;
const { part, globs: filterGlobs } = await choosePart(parts, inject);

const tarball = await buildTarball(directory);

try {
await fetchBluprint(tarball, filterGlobs, mergeJson);
await handleActions(bluprintrc.actions, part);
} finally {
fs.rmSync(tarball);
}
};
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export { default as remove } from './commands/remove';
export { default as start } from './commands/start';
export { default as clone } from './commands/clone';
export { default as token } from './commands/token';
export { default as test } from './commands/test';

export { default as handleActions } from './actions';

Expand Down
73 changes: 73 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const expect = require('expect.js');
const path = require('path');
const { test } = require('../dist');
const os = require('os');
const fs = require('fs');

const resolvePath = (filePath) => path.join(process.cwd(), filePath);

const createTestBluprint = function() {
const tempdir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-bluprint'));

fs.writeFileSync(path.join(tempdir, '.bluprintrc'), JSON.stringify({
bluprint: '^0.0.1',
name: 'My test bluprint',
category: 'testing',
actions: [
{
action: 'render',
engine: 'mustache',
context: { foo: 'bar' },
files: [
'test-command-1.txt',
'subdir/test-command-2.txt',
],
},
],
}));

fs.mkdirSync(path.join(tempdir, 'subdir'));
fs.writeFileSync(path.join(tempdir, 'test-command-1.txt'), 'foo = {{ foo }}');
fs.writeFileSync(path.join(tempdir, 'subdir', 'test-command-2.txt'), 'foo = {{ foo }}');
fs.writeFileSync(path.join(tempdir, 'subdir', 'ignored.txt'), 'ignore me');
fs.writeFileSync(path.join(tempdir, 'subdir', '.gitignore'), '# this should be ignored\nignored.*');

return tempdir;
};

describe('Test command: test', function() {
this.timeout(10000);

let testBluprint;
let cleanupFiles;

beforeEach(async function() {
testBluprint = createTestBluprint();

cleanupFiles = [
testBluprint,
resolvePath('test-command-1.txt'),
resolvePath('subdir/test-command-2.txt'),
resolvePath('subdir/ignored.txt'),
resolvePath('subdir/.gitignore'),
resolvePath('subdir'),
];
});

afterEach(function() {
cleanupFiles.forEach(f => fs.rmSync(f, { recursive: true, force: true }));
});

it('Creates a new project from a bluprint directory', async function() {
await test(testBluprint);

expect(fs.readFileSync('test-command-1.txt', 'utf8')).to.be('foo = bar');
expect(fs.readFileSync('subdir/test-command-2.txt', 'utf8')).to.be('foo = bar');
});

it('Ignores files from .gitignore', async function() {
await test(testBluprint);

expect(fs.existsSync('ignored.txt')).to.be(false);
});
});