UNPKG

4.38 kBJavaScriptView Raw
1// 'sanbashi' is a word in Japanese that refers to a pier or dock, usually very large in size, such as found in Tokyo's O-daiba region
2
3let Glob = require('glob')
4let Path = require('path')
5let Inquirer = require('inquirer')
6let os = require('os')
7const Child = require('child_process')
8const log = require('./log')
9
10const DOCKERFILE_REGEX = /\bDockerfile(.\w*)?$/
11let Sanbashi = function () {}
12
13Sanbashi.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
28Sanbashi.getJobs = function (resourceRoot, dockerfiles) {
29 return dockerfiles
30 // convert all Dockerfiles into job Objects
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 // prefer closer Dockerfiles, then prefer Dockerfile over Dockerfile.web
44 .sort((a, b) => {
45 return a.depth - b.depth || a.postfix - b.postfix
46 })
47 // group all Dockerfiles for the same process type together
48 .reduce((jobs, job) => {
49 jobs[job.name] = jobs[job.name] || []
50 jobs[job.name].push(job)
51 return jobs
52 }, {})
53}
54
55Sanbashi.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
75Sanbashi.filterByProcessType = function (jobs, procs) {
76 let filteredJobs = {}
77 procs.forEach((processType) => {
78 filteredJobs[processType] = jobs[processType]
79 })
80 return filteredJobs
81}
82
83Sanbashi.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
99Sanbashi.pushImage = function (resource, verbose) {
100 let args = ['push', resource]
101 log(verbose, args)
102 return Sanbashi.cmd('docker', args)
103}
104
105Sanbashi.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
116Sanbashi.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]) // ensure exactly 2 components
121}
122
123Sanbashi.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
152module.exports = Sanbashi