1 |
|
2 |
|
3 | let Glob = require('glob')
|
4 | let Path = require('path')
|
5 | let Inquirer = require('inquirer')
|
6 | let os = require('os')
|
7 | const Child = require('child_process')
|
8 | const log = require('./log')
|
9 |
|
10 | const DOCKERFILE_REGEX = /\bDockerfile(.\w*)?$/
|
11 | let Sanbashi = function () {}
|
12 |
|
13 | Sanbashi.getDockerfiles = function (rootdir, recursive) {
|
14 | let match = recursive ? './**/Dockerfile?(.)*' : 'Dockerfile*'
|
15 | let dockerfiles = Glob.sync(match, {
|
16 | cwd: rootdir,
|
17 | nonull: false,
|
18 | nodir: true
|
19 | })
|
20 | if (recursive) {
|
21 | dockerfiles = dockerfiles.filter(df => df.match(/Dockerfile\.[\w]+/))
|
22 | } else {
|
23 | dockerfiles = dockerfiles.filter(df => df.match(/Dockerfile$/))
|
24 | }
|
25 | return dockerfiles.map(file => Path.join(rootdir, file))
|
26 | }
|
27 |
|
28 | Sanbashi.getJobs = function (resourceRoot, dockerfiles) {
|
29 | return dockerfiles
|
30 |
|
31 | .map((dockerfile) => {
|
32 | let match = dockerfile.match(DOCKERFILE_REGEX)
|
33 | if (!match) return
|
34 | let proc = (match[1] || '.standard').slice(1)
|
35 | return {
|
36 | name: proc,
|
37 | resource: `${resourceRoot}/${proc}`,
|
38 | dockerfile: dockerfile,
|
39 | postfix: Path.basename(dockerfile) === 'Dockerfile' ? 0 : 1,
|
40 | depth: Path.normalize(dockerfile).split(Path.sep).length
|
41 | }
|
42 | })
|
43 |
|
44 | .sort((a, b) => {
|
45 | return a.depth - b.depth || a.postfix - b.postfix
|
46 | })
|
47 |
|
48 | .reduce((jobs, job) => {
|
49 | jobs[job.name] = jobs[job.name] || []
|
50 | jobs[job.name].push(job)
|
51 | return jobs
|
52 | }, {})
|
53 | }
|
54 |
|
55 | Sanbashi.chooseJobs = async function (jobs) {
|
56 | let chosenJobs = []
|
57 | for (let processType in jobs) {
|
58 | let group = jobs[processType]
|
59 | if (group.length > 1) {
|
60 | let prompt = {
|
61 | type: 'list',
|
62 | name: processType,
|
63 | choices: group.map(j => j.dockerfile),
|
64 | message: `Found multiple Dockerfiles with process type ${processType}. Please choose one to build and push `
|
65 | }
|
66 | let answer = await Inquirer.prompt(prompt)
|
67 | chosenJobs.push(group.find(o => o.dockerfile === answer[processType]))
|
68 | } else {
|
69 | chosenJobs.push(group[0])
|
70 | }
|
71 | }
|
72 | return chosenJobs
|
73 | }
|
74 |
|
75 | Sanbashi.filterByProcessType = function (jobs, procs) {
|
76 | let filteredJobs = {}
|
77 | procs.forEach((processType) => {
|
78 | filteredJobs[processType] = jobs[processType]
|
79 | })
|
80 | return filteredJobs
|
81 | }
|
82 |
|
83 | Sanbashi.buildImage = function (dockerfile, resource, verbose, buildArg) {
|
84 | let cwd = Path.dirname(dockerfile)
|
85 | let args = ['build', '-f', dockerfile, '-t', resource]
|
86 |
|
87 | for (let i = 0; i < buildArg.length; i++) {
|
88 | if (buildArg[i].length !== 0) {
|
89 | args.push('--build-arg')
|
90 | args.push(buildArg[i])
|
91 | }
|
92 | }
|
93 |
|
94 | args.push(cwd)
|
95 | log(verbose, args)
|
96 | return Sanbashi.cmd('docker', args)
|
97 | }
|
98 |
|
99 | Sanbashi.pushImage = function (resource, verbose) {
|
100 | let args = ['push', resource]
|
101 | log(verbose, args)
|
102 | return Sanbashi.cmd('docker', args)
|
103 | }
|
104 |
|
105 | Sanbashi.runImage = function (resource, command, port, verbose) {
|
106 | let args = ['run', '--user', os.userInfo().uid, '-e', `PORT=${port}`]
|
107 | if (command === '') {
|
108 | args.push(resource)
|
109 | } else {
|
110 | args.push('-it', resource, command)
|
111 | }
|
112 | log(verbose, args)
|
113 | return Sanbashi.cmd('docker', args)
|
114 | }
|
115 |
|
116 | Sanbashi.version = function () {
|
117 | return Sanbashi
|
118 | .cmd('docker', ['version', '-f', '{{.Client.Version}}'], {output: true})
|
119 | .then(version => version.split(/\./))
|
120 | .then(([major, minor]) => [parseInt(major) || 0, parseInt(minor) || 0])
|
121 | }
|
122 |
|
123 | Sanbashi.cmd = function (cmd, args, options = {}) {
|
124 | let stdio = [process.stdin, process.stdout, process.stderr]
|
125 | if (options.input) {
|
126 | stdio[0] = 'pipe'
|
127 | }
|
128 | if (options.output) {
|
129 | stdio[1] = 'pipe'
|
130 | }
|
131 |
|
132 | return new Promise((resolve, reject) => {
|
133 | let child = Child.spawn(cmd, args, {stdio: stdio})
|
134 |
|
135 | if (child.stdin) {
|
136 | child.stdin.end(options.input)
|
137 | }
|
138 | let stdout
|
139 | if (child.stdout) {
|
140 | stdout = ''
|
141 | child.stdout.on('data', (data) => {
|
142 | stdout += data.toString()
|
143 | })
|
144 | }
|
145 | child.on('exit', (code, signal) => {
|
146 | if (signal || code) reject(signal || code)
|
147 | else resolve(stdout)
|
148 | })
|
149 | })
|
150 | }
|
151 |
|
152 | module.exports = Sanbashi
|