1 | const Command = require('../base')
|
2 | const renderShortDesc = require('../utils/renderShortDescription')
|
3 | const openBrowser = require('../utils/open-browser')
|
4 | const path = require('path')
|
5 | const chalk = require('chalk')
|
6 | const { flags } = require('@oclif/command')
|
7 | const get = require('lodash.get')
|
8 | const fs = require('fs')
|
9 | const prettyjson = require('prettyjson')
|
10 | const ora = require('ora')
|
11 | const logSymbols = require('log-symbols')
|
12 | const cliSpinnerNames = Object.keys(require('cli-spinners'))
|
13 | const randomItem = require('random-item')
|
14 |
|
15 | class 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 |
|
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 |
|
68 | const resolvedDeployPath = path.resolve(site.root, deployFolder)
|
69 | let resolvedFunctionsPath
|
70 | if (functionsFolder) resolvedFunctionsPath = path.resolve(site.root, functionsFolder)
|
71 |
|
72 |
|
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 |
|
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 |
|
129 | DeployCommand.description = `${renderShortDesc(`Create a new deploy from the contents of a folder`)}
|
130 |
|
131 | Deploys from the build settings found in the netlify.toml file, or settings from the api.
|
132 | `
|
133 |
|
134 | DeployCommand.examples = [
|
135 | 'netlify deploy',
|
136 | 'netlify deploy --prod',
|
137 | 'netlify deploy --prod --open'
|
138 | ]
|
139 |
|
140 | DeployCommand.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 |
|
161 | function deployProgressCb(ctx) {
|
162 | const events = {}
|
163 | |
164 |
|
165 |
|
166 |
|
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 |
|
197 | function 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 |
|
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 |
|
221 | module.exports = DeployCommand
|