Skip to content

Commit 0d97bc0

Browse files
committed
Add command to test a local bluprint from disk
1 parent a026dc7 commit 0d97bc0

6 files changed

Lines changed: 183 additions & 3 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,6 @@ tmp/
152152
temp/
153153

154154
# End of https://www.gitignore.io/api/node,macos,linux
155-
n
155+
156+
# Editor
157+
.vscode

lib/cli.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { add, clone, newBluprint, remove, start, token } from 'bluprint';
1+
import { add, clone, newBluprint, remove, start, token, test } from 'bluprint';
22
import { name, version } from '../package.json';
33

44
import chalk from 'chalk';
@@ -66,4 +66,13 @@ yargs // eslint-disable-line no-unused-expressions
6666
}, async function({ accessToken }) {
6767
await token(accessToken);
6868
})
69+
.command('test [directory]', 'Test the bluprint in the given directory', (yargs) => {
70+
yargs
71+
.positional('directory', {
72+
describe: 'Local path to a bluprint',
73+
type: 'string',
74+
});
75+
}, async function({ directory }) {
76+
await test(directory);
77+
})
6978
.argv;

lib/commands/start/fetchBluprint/parser.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ export default (resolve, reject, filterGlobs, mergeJson) => {
4040
if (entry.type === 'Directory') {
4141
fs.mkdirSync(entryPath, { recursive: true });
4242
entry.resume();
43-
} else if (entry.type === 'File') {
43+
} else if (['File', 'SymbolicLink'].includes(entry.type)) {
4444
archive[entryPath] = [];
4545
entry.on('data', c => archive[entryPath].push(c));
46+
} else {
47+
throw new Error(`unable to handle entry of type ${entry.type}`);
4648
}
4749
},
4850
})

lib/commands/test/index.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import tar from 'tar';
4+
import os from 'os';
5+
6+
import handleActions from '../../actions';
7+
import getLogger from '../../utils/getLogger';
8+
import choosePart from '../start/choosePart';
9+
import getParser from '../start/fetchBluprint/parser';
10+
import minimatch from 'minimatch';
11+
12+
const logger = getLogger();
13+
14+
// best effort attempt at reproducing .gitignore behaviour
15+
const createIgnoreFilter = config => {
16+
const globs = fs.readFileSync(config, 'utf-8').split('\n').filter(line => !line.trim().startsWith('#'));
17+
return file => !globs.some(glob => file.startsWith(glob) || minimatch(file, glob));
18+
};
19+
20+
const getFiles = dir => {
21+
const basename = path.basename(dir);
22+
const files = fs.readdirSync(dir);
23+
const result = [];
24+
25+
let filterFn = () => true;
26+
27+
for (const file of files) {
28+
const fullPath = path.join(dir, file);
29+
30+
if (file === '.gitignore') {
31+
filterFn = createIgnoreFilter(fullPath);
32+
}
33+
34+
if (fs.statSync(fullPath).isDirectory()) {
35+
getFiles(fullPath).forEach(f => result.push(f));
36+
} else {
37+
result.push(file);
38+
}
39+
}
40+
41+
return result.filter(e => filterFn(e) && !e.match(/^\.git(\/|$)/)).map(e => [basename, path.sep, e].join(''));
42+
};
43+
44+
const buildTarball = async(directory) => {
45+
const tarball = path.join(os.tmpdir(), 'tmp-bluprint.tar');
46+
const files = getFiles(directory);
47+
48+
await tar.create({
49+
gzip: false,
50+
file: tarball,
51+
cwd: path.dirname(directory),
52+
strict: true,
53+
}, files);
54+
55+
return tarball;
56+
};
57+
58+
const fetchBluprint = async(tarballPath, filterGlobs, mergeJson) => {
59+
return new Promise((resolve, reject) => {
60+
const rs = fs.createReadStream(tarballPath);
61+
const parser = getParser(resolve, reject, filterGlobs, mergeJson);
62+
63+
rs.pipe(parser)
64+
.on('error', (e) => {
65+
logger.error(`Tarball parsing error.`);
66+
reject(e);
67+
});
68+
});
69+
};
70+
71+
const defaultInject = {
72+
method: null,
73+
category: null,
74+
bluprint: null,
75+
partConfirm: null,
76+
partChoice: null,
77+
};
78+
79+
export default async(directory, inject = defaultInject) => {
80+
const bluprintrc = JSON.parse(fs.readFileSync(path.join(directory, '.bluprintrc')));
81+
82+
const { parts, mergeJson } = bluprintrc;
83+
const { part, globs: filterGlobs } = await choosePart(parts, inject);
84+
85+
const tarball = await buildTarball(directory);
86+
87+
try {
88+
await fetchBluprint(tarball, filterGlobs, mergeJson);
89+
await handleActions(bluprintrc.actions, part);
90+
} finally {
91+
fs.rmSync(tarball);
92+
}
93+
};

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export { default as remove } from './commands/remove';
44
export { default as start } from './commands/start';
55
export { default as clone } from './commands/clone';
66
export { default as token } from './commands/token';
7+
export { default as test } from './commands/test';
78

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

test/test.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
const expect = require('expect.js');
2+
const path = require('path');
3+
const { test } = require('../dist');
4+
const os = require('os');
5+
const fs = require('fs');
6+
7+
const resolvePath = (filePath) => path.join(process.cwd(), filePath);
8+
9+
const createTestBluprint = function() {
10+
const tempdir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-bluprint'));
11+
12+
fs.writeFileSync(path.join(tempdir, '.bluprintrc'), JSON.stringify({
13+
bluprint: '^0.0.1',
14+
name: 'My test bluprint',
15+
category: 'testing',
16+
actions: [
17+
{
18+
action: 'render',
19+
engine: 'mustache',
20+
context: { foo: 'bar' },
21+
files: [
22+
'test-command-1.txt',
23+
'subdir/test-command-2.txt',
24+
],
25+
},
26+
],
27+
}));
28+
29+
fs.mkdirSync(path.join(tempdir, 'subdir'));
30+
fs.writeFileSync(path.join(tempdir, 'test-command-1.txt'), 'foo = {{ foo }}');
31+
fs.writeFileSync(path.join(tempdir, 'subdir', 'test-command-2.txt'), 'foo = {{ foo }}');
32+
fs.writeFileSync(path.join(tempdir, 'subdir', 'ignored.txt'), 'ignore me');
33+
fs.writeFileSync(path.join(tempdir, 'subdir', '.gitignore'), '# this should be ignored\nignored.*');
34+
35+
return tempdir;
36+
};
37+
38+
describe('Test command: test', function() {
39+
this.timeout(10000);
40+
41+
let testBluprint;
42+
let cleanupFiles;
43+
44+
beforeEach(async function() {
45+
testBluprint = createTestBluprint();
46+
47+
cleanupFiles = [
48+
testBluprint,
49+
resolvePath('test-command-1.txt'),
50+
resolvePath('subdir/test-command-2.txt'),
51+
resolvePath('subdir/ignored.txt'),
52+
resolvePath('subdir/.gitignore'),
53+
resolvePath('subdir'),
54+
];
55+
});
56+
57+
afterEach(function() {
58+
cleanupFiles.forEach(f => fs.rmSync(f, { recursive: true, force: true }));
59+
});
60+
61+
it('Creates a new project from a bluprint directory', async function() {
62+
await test(testBluprint);
63+
64+
expect(fs.readFileSync('test-command-1.txt', 'utf8')).to.be('foo = bar');
65+
expect(fs.readFileSync('subdir/test-command-2.txt', 'utf8')).to.be('foo = bar');
66+
});
67+
68+
it('Ignores files from .gitignore', async function() {
69+
await test(testBluprint);
70+
71+
expect(fs.existsSync('ignored.txt')).to.be(false);
72+
});
73+
});

0 commit comments

Comments
 (0)