UNPKG

6.07 kBJavaScriptView Raw
1const Command = require('../base')
2const renderShortDesc = require('../utils/renderShortDescription')
3const openBrowser = require('../utils/open-browser')
4const path = require('path')
5const chalk = require('chalk')
6const { flags } = require('@oclif/command')
7const get = require('lodash.get')
8const fs = require('fs')
9const prettyjson = require('prettyjson')
10const ora = require('ora')
11const logSymbols = require('log-symbols')
12const cliSpinnerNames = Object.keys(require('cli-spinners'))
13const randomItem = require('random-item')
14
15class DeployCommand extends Command {
16 async run() {
17 const accessToken = this.getAuthToken()
18 const { flags } = this.parse(DeployCommand)
19 const { api, site } = this.netlify
20
21 const deployToProduction = flags.prod
22
23 if (!accessToken) {
24 this.error(`Not logged in. Log in to deploy to a site`)
25 }
26
27 const siteId = site.get('siteId')
28 if (!siteId) {
29 this.log('Please link project to a netlify site first')
30 this.exit()
31 } else {
32 try {
33 await api.getSite({ siteId })
34 } catch (e) {
35 this.error(e.message)
36 }
37 }
38
39 // TODO: abstract settings lookup
40 const deployFolder =
41 flags['dir'] ||
42 get(site.config, 'build.publish') ||
43 get(await api.getSite({ siteId }), 'build_settings.dir')
44
45 const functionsFolder =
46 flags.functions ||
47 get(site.config, 'build.functions') ||
48 get(await api.getSite({ siteId }), 'build_settings.functions_dir')
49
50 if (!deployFolder) {
51
52 this.log(`Can't determine a deploy folder.`)
53 this.log()
54 this.log('Please define one in your site settings, netlify.toml or pass one as a flag')
55 this.log()
56 this.log(`Example using CLI flag:
57
58# deploy to preview URL
59${chalk.cyanBright.bold('netlify deploy --dir your-build-directory')}
60
61# deploy to live URL with the --prod flag
62${chalk.cyanBright.bold('netlify deploy --dir your-build-directory --prod')}
63`)
64 this.exit()
65 }
66
67 // TODO go through the above resolution, and make sure the resolve algorithm makes sense
68 const resolvedDeployPath = path.resolve(site.root, deployFolder)
69 let resolvedFunctionsPath
70 if (functionsFolder) resolvedFunctionsPath = path.resolve(site.root, functionsFolder)
71
72 // cliUx.action.start(`Starting a deploy from ${resolvedDeployPath}`)
73
74 ensureDirectory(resolvedDeployPath, this.exit)
75
76 if (resolvedFunctionsPath) {
77 ensureDirectory(resolvedFunctionsPath, this.exit)
78 }
79
80 let results
81 try {
82 if (deployToProduction) {
83 this.log('Deploying to live site url...')
84 } else {
85 this.log('Deploying to draft url...')
86 }
87
88 results = await api.deploy(siteId, resolvedDeployPath, resolvedFunctionsPath, site.configPath, {
89 statusCb: deployProgressCb(this),
90 draft: !deployToProduction
91 })
92 } catch (e) {
93 this.error(e)
94 }
95 // cliUx.action.stop(`Finished deploy ${results.deployId}`)
96
97 const siteUrl = results.deploy.ssl_url || results.deploy.url
98 const deployUrl = get(results, 'deploy.deploy_ssl_url') || get(results, 'deploy.deploy_url')
99
100 const msgData = {
101 Logs: `${get(results, 'deploy.admin_url')}/deploys/${get(results, 'deploy.id')}`,
102 'Unique Deploy URL': deployUrl
103 }
104
105 if (deployToProduction) {
106 msgData['Live Url'] = siteUrl
107 } else {
108 delete msgData['Unique Deploy URL']
109 msgData['Live Draft Url'] = deployUrl
110 }
111 this.log()
112 this.log(prettyjson.render(msgData))
113
114 if (!deployToProduction) {
115 console.log()
116 console.log('If everything looks good on your draft URL. Take it live with the --prod flag')
117 console.log(`${chalk.cyanBright.bold('netlify deploy --prod')}`)
118 console.log()
119 }
120
121 if (flags['open']) {
122 const urlToOpen = (flags['prod']) ? siteUrl : deployUrl
123 await openBrowser(urlToOpen)
124 this.exit()
125 }
126 }
127}
128
129DeployCommand.description = `${renderShortDesc(`Create a new deploy from the contents of a folder`)}
130
131Deploys from the build settings found in the netlify.toml file, or settings from the api.
132`
133
134DeployCommand.examples = [
135 'netlify deploy',
136 'netlify deploy --prod',
137 'netlify deploy --prod --open'
138]
139
140DeployCommand.flags = {
141 dir: flags.string({
142 char: 'd',
143 description: 'Specify a folder to deploy'
144 }),
145 functions: flags.string({
146 char: 'f',
147 description: 'Specify a functions folder to deploy'
148 }),
149 prod: flags.boolean({
150 char: 'p',
151 description: 'Deploy to production',
152 default: false,
153 }),
154 open: flags.boolean({
155 char: 'o',
156 description: 'Open site after deploy',
157 default: false,
158 })
159}
160
161function deployProgressCb(ctx) {
162 const events = {}
163 /* statusObj: {
164 type: name-of-step
165 msg: msg to print
166 phase: [start, progress, stop]
167 }
168 */
169 return ev => {
170 switch (ev.phase) {
171 case 'start': {
172 const spinner = ev.spinner || randomItem(cliSpinnerNames)
173 events[ev.type] = ora({
174 text: ev.msg,
175 spinner: spinner
176 }).start()
177 return
178 }
179 case 'progress': {
180 const spinner = events[ev.type]
181 if (spinner) spinner.text = ev.msg
182 return
183 }
184 case 'stop':
185 default: {
186 const spinner = events[ev.type]
187 if (spinner) {
188 spinner.stopAndPersist({ text: ev.msg, symbol: logSymbols.success })
189 delete events[ev.type]
190 }
191 return
192 }
193 }
194 }
195}
196
197function ensureDirectory(resolvedDeployPath, exit) {
198 let stat
199 try {
200 stat = fs.statSync(resolvedDeployPath)
201 } catch (e) {
202 if (e.code === 'ENOENT') {
203 console.log('No such directory! Make sure to run your build command locally first')
204 exit()
205 }
206
207 // Improve the message of permission errors
208 if (e.code === 'EACCES') {
209 console.log('Permission error when trying to access deploy folder')
210 exit()
211 }
212 throw e
213 }
214 if (!stat.isDirectory) {
215 console.log('Deploy target must be a directory')
216 exit()
217 }
218 return stat
219}
220
221module.exports = DeployCommand