UNPKG

3.45 kBJavaScriptView Raw
1var path = require('path');
2var child = require('child_process');
3var fs = require('fs');
4var _ = require('lodash');
5var uuid = require('node-uuid');
6var crypto = require('crypto');
7var os = require('os');
8var cli = require('heroku-cli-util');
9var directory = require('./directory');
10require('prototypes');
11
12const FILENAME = 'Dockerfile';
13
14module.exports = {
15 filename: FILENAME,
16 silent: false,
17 buildImage: buildImage,
18 ensureExecImage: ensureExecImage,
19 ensureStartImage: ensureStartImage,
20 runImage: runImage
21};
22
23function runImage(imageId, cwd, command, mount) {
24 if (!imageId) return;
25 var mountDir = crossPlatformCwd(cwd);
26 var mountComponent = mount ? `-v "${mountDir}:/app/src"` : '';
27 var envArgComponent = directory.getFormattedEnvArgComponent(cwd);
28 var runCommand = `docker run -w /app/src -p 3000:3000 --rm -it ${mountComponent} ${envArgComponent} ${imageId} sh -c "${command}" || true`;
29 var result = child.execSync(runCommand, {
30 stdio: this.silent ? [0, 'pipe', 'pipe'] : 'inherit'
31 });
32 return Buffer.isBuffer(result) ?
33 result.toString().trim() : result;
34}
35
36function buildImage(dir, id, dockerfile) {
37 cli.log('building image...');
38 var dockerfile = dockerfile || path.join(dir, FILENAME);
39 var build = child.execSync(`docker build --force-rm --file="${dockerfile}" --tag="${id}" "${dir}"`, {
40 stdio: this.silent ? [0, 'pipe', 'pipe'] : 'inherit'
41 });
42 return id;
43}
44
45function ensureExecImage(dir) {
46 var dockerfile = path.join(dir, FILENAME);
47 try {
48 var contents = fs.readFileSync(dockerfile, { encoding: 'utf8' });
49 var hash = createHash(contents);
50 var imageId = getImageId(hash);
51 imageExists(imageId) || this.buildImage(dir, imageId);
52 return imageId;
53 }
54 catch (e) {
55 if (e.code === 'ENOENT') {
56 cli.error('No Dockerfile found, did you run `heroku docker:init`?');
57 return;
58 }
59 else {
60 throw e;
61 }
62 }
63}
64
65function ensureStartImage(dir) {
66 var execImageId = this.ensureExecImage(dir);
67 if (!execImageId) {
68 return;
69 }
70 var contents = `FROM ${execImageId}`;
71 var imageId = `${execImageId}-start`;
72 var filename = `.Dockerfile-${uuid.v1()}`;
73 var filepath = path.join(dir, filename);
74 fs.writeFileSync(filepath, contents, { encoding: 'utf8' });
75 try {
76 this.buildImage(dir, imageId, filepath);
77 }
78 catch (e) {
79 fs.unlinkSync(filepath);
80 throw new Error('Unable to create start image');
81 }
82 fs.unlinkSync(filepath);
83 return imageId;
84}
85
86function createHash(contents) {
87 var md5 = crypto.createHash('md5');
88 md5.update(contents, 'utf8');
89 var digest = md5.digest('hex');
90 return digest;
91}
92
93function getImageId(hash) {
94 return `heroku-docker-${hash}`;
95}
96
97function imageExists(id) {
98 return getAllImages().indexOf(id) !== -1;
99}
100
101function getAllImages() {
102 var stdout = child.execSync(`docker images`, { encoding: 'utf8' });
103 return _.map(_.filter(stdout.split('\n'), isImage), lineToId);
104
105 function isImage(line) {
106 return line.indexOf('heroku-docker') === 0;
107 }
108
109 function lineToId(line) {
110 return line.split(' ')[0];
111 }
112}
113
114function crossPlatformCwd(cwd){
115 if (os.platform() == 'win32') {
116 // this is due to how volumes are mounted by boot2docker/virtualbox
117 var p = path.parse(cwd);
118 return path.posix.sep + p.root.split(':')[0].toLowerCase() + path.posix.sep
119 + p.dir.substring(p.root.length).replaceAll(path.sep, path.posix.sep) + path.posix.sep + p.base;
120 }
121 return cwd;
122}