UNPKG

4.99 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 debug = require('./debug')
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, buildArg, path) {
84 let cwd = path || 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 return Sanbashi.cmd('docker', args)
96}
97
98Sanbashi.pushImage = function (resource) {
99 let args = ['push', resource]
100 return Sanbashi.cmd('docker', args)
101}
102
103Sanbashi.pullImage = function (resource) {
104 let args = ['pull', resource]
105 return Sanbashi.cmd('docker', args)
106}
107
108Sanbashi.runImage = function (resource, command, port) {
109 let args = ['run', '--user', os.userInfo().uid, '-e', `PORT=${port}`]
110 if (command === '') {
111 args.push(resource)
112 } else {
113 args.push('-it', resource, command)
114 }
115 return Sanbashi.cmd('docker', args)
116}
117
118Sanbashi.version = function () {
119 return Sanbashi
120 .cmd('docker', ['version', '-f', '{{.Client.Version}}'], {output: true})
121 .then(version => version.split(/\./))
122 .then(([major, minor]) => [parseInt(major) || 0, parseInt(minor) || 0]) // ensure exactly 2 components
123}
124
125Sanbashi.imageID = function (tag) {
126 return Sanbashi
127 .cmd('docker', ['inspect', tag, '--format={{.Id}}'], {output: true})
128 .then(id => id.trimRight()) // Trim the new line at the end of the string
129}
130
131Sanbashi.cmd = function (cmd, args, options = {}) {
132 debug(cmd, args)
133 let stdio = [process.stdin, process.stdout, process.stderr]
134 if (options.input) {
135 stdio[0] = 'pipe'
136 }
137 if (options.output) {
138 stdio[1] = 'pipe'
139 }
140
141 return new Promise((resolve, reject) => {
142 let child = Child.spawn(cmd, args, {stdio: stdio})
143
144 if (child.stdin) {
145 child.stdin.end(options.input)
146 }
147 let stdout
148 if (child.stdout) {
149 stdout = ''
150 child.stdout.on('data', (data) => {
151 stdout += data.toString()
152 })
153 }
154 child.on('error', (err) => {
155 if (err.code === 'ENOENT' && err.path === 'docker') {
156 reject(new Error(`Cannot find docker, please ensure docker is installed.
157 If you need help installing docker, visit https://docs.docker.com/install/#supported-platforms`))
158 } else {
159 reject(err)
160 }
161 })
162 child.on('exit', (code, signal) => {
163 if (signal || code) reject(new Error(signal || code))
164 else resolve(stdout)
165 })
166 })
167}
168
169module.exports = Sanbashi