UNPKG

8.73 kBJavaScriptView Raw
1/* eslint-disable max-len */
2const _ = require('underscore');
3const ora = require('ora');
4const prompt = require('prompt');
5const deployments = require('../lib/deployments');
6const domains = require('../lib/domains');
7const environments = require('../lib/environments');
8const releases = require('../lib/releases');
9const notice = require('../lib/notice');
10const assertPkg = require('../lib/package-json').assert;
11
12const spinner = ora();
13
14prompt.colors = false;
15prompt.message = '';
16prompt.delimiter = '';
17
18/**
19 * Ask whether user is sure
20 */
21const areYouSure = () => new Promise((resolve, reject) => {
22 const schema = {
23 properties: {
24 ok: {
25 description: 'Releasing to all domains at once. Are you sure?',
26 default: 'Y',
27 type: 'string',
28 },
29 },
30 };
31 prompt.start();
32 prompt.get(schema, (err, result) => {
33 if (err) return reject(err);
34
35 return resolve(result);
36 });
37});
38
39/**
40 * Ask user for a deployment key
41 */
42const askDeploymentKey = () => new Promise((resolve, reject) => {
43 const schema = {
44 properties: {
45 deploy_key_index: {
46 description: 'Deployment to release:',
47 default: 'A',
48 required: true,
49 },
50 },
51 };
52 prompt.start();
53 prompt.get(schema, (err, result) => {
54 if (err) return reject(err);
55
56 return resolve(result);
57 });
58});
59
60/**
61 * Ask user for a domain to release to
62 */
63const askReleaseDomain = () => new Promise((resolve, reject) => {
64 console.log(`
65You can select one or more domains to release (using the same deployment key).
66Simply type the letters associated with all the domains you want to release,
67like so: A B E.
68
69Simply press <Enter> if you want to release to all your domains in one go.
70`);
71
72 const schema = {
73 properties: {
74 domain_name_index: {
75 description: 'Domain name(s) for release:',
76 default: '',
77 },
78 },
79 };
80 prompt.start();
81 prompt.get(schema, (err, result) => {
82 if (err) return reject(err);
83
84 return resolve(result);
85 });
86});
87
88/**
89 * Show available domains
90 * @param _domains
91 * @param siteName
92 */
93const showAvailableDomains = (_domains, siteName) => {
94 console.log(`Here are the most recent domains for ${siteName}:`);
95
96 let code = 65; /* 'A' */
97 _domains.forEach(d => console.log(`${String.fromCharCode(code++)}) ${d.env || 'prod'}\t${d.domain_name}`));
98};
99
100/**
101 * Show available deployment keys
102 * @param _deployments
103 * @param siteName
104 */
105const showAvailableDeployments = (_deployments, siteName) => {
106 console.log(`\nHere are the most recent deployments for ${siteName}:`);
107
108 let code = 65; /* 'A' */
109 _deployments.forEach(d => console.log(`${String.fromCharCode(code++)}) ${d.env || 'prod'}\t${d.deploy_key} ${d.description || ''}`));
110};
111
112/**
113 * Ask user which environment to use
114 */
115const askEnvironment = () => new Promise((resolve, reject) => {
116 console.log(`
117Please select the environment to which you want to attach the domain.
118`);
119 const schema = {
120 properties: {
121 environment_index: {
122 description: 'Environment to use:',
123 pattern: /^(?!-)[a-zA-Z]$/,
124 default: 'A',
125 required: true,
126 },
127 },
128 };
129 prompt.start();
130 prompt.get(schema, (err, result) => {
131 if (err) return reject(err);
132
133 return resolve(result);
134 });
135});
136
137/**
138 * Get domains and deployments in one go
139 * @param siteName
140 */
141const getDomainsAndDeployments = async (siteName) => ({
142 deployments: await deployments.getAvailableDeployments(siteName),
143 domains: await domains.getAvailableDomains(siteName),
144});
145
146/**
147 * Show error message
148 * @param err
149 */
150const error = (err) => {
151 console.log('Oops! Something went wrong:');
152 console.log(err.message ? err.message : err);
153};
154
155/**
156 * Pick environment from a list
157 * @param envs
158 */
159const pickEnvironment = async (envs) => {
160 if (envs.environments.length < 1) return 'prod';
161 if (envs.environments.length < 2) return envs.environments[0].name;
162
163 await environments.showAvailableEnvironments(envs);
164 const env = await askEnvironment();
165 const index = env.environment_index.toUpperCase().charCodeAt(0) - 65;
166 if (index > envs.environments.length - 1) {
167 throw new Error('Invalid input.');
168 }
169 return envs.environments[index].name;
170};
171
172/**
173 * Release the latest version for all domains - don't ask for user input
174 * @param argv
175 */
176const releaseLatest = async (argv) => {
177 const domainsToRelease = [];
178 const { siteName } = argv;
179 let deployKey = null;
180
181 spinner.start('Retrieving environments. Please wait...');
182 const envs = await environments.getAvailableEnvironments(siteName);
183 spinner.stop();
184
185 const envName = await pickEnvironment(envs);
186
187 spinner.start('Retrieving domains and deployments. Please wait...');
188 const results = await getDomainsAndDeployments(siteName);
189 spinner.stop();
190
191 const listOfDeployments = _.filter(results.deployments.deployments, d => d.env === envName);
192 const listOfDomains = _.filter(results.domains.domains, d => d.env === envName);
193 if (listOfDomains.length === 0) {
194 throw new Error('This environment contains no domains.');
195 }
196
197 deployKey = listOfDeployments[0].deploy_key;
198 listOfDomains.map(d => domainsToRelease.push(d.domain_name));
199
200 spinner.start('Creating new release(s)...');
201 await Promise.all(domainsToRelease.map(d => releases.createRelease(siteName, deployKey, d, envName)));
202 spinner.succeed('Release(s) successfully created.');
203};
204
205/**
206 * Release a new version for one or more domains - ask for user input
207 * @param argv
208 */
209const release = async (argv) => {
210 const domainsToRelease = [];
211 const { siteName } = argv;
212 let deployKey = null;
213
214 spinner.start('Retrieving environments. Please wait...');
215 const envs = await environments.getAvailableEnvironments(siteName);
216 spinner.stop();
217
218 const envName = await pickEnvironment(envs);
219
220 spinner.start('Retrieving domains and deployments. Please wait...');
221 const results = await getDomainsAndDeployments(siteName);
222 spinner.stop();
223
224 const listOfDeployments = _.filter(results.deployments.deployments, d => d.env === envName);
225 const listOfDomains = _.filter(results.domains.domains, d => d.env === envName);
226 if (listOfDomains.length === 0) {
227 throw new Error('This environment contains no domains.');
228 }
229
230 showAvailableDomains(listOfDomains, siteName);
231 const domainResponse = await askReleaseDomain();
232
233 // Convert to uppercase, remove non A-Z characters and remove duplicates
234 let ignored = false;
235 const domainIndex = domainResponse.domain_name_index;
236 if (domainIndex === '') {
237 const sure = await areYouSure();
238 if (sure.ok.toLowerCase() !== 'y') {
239 console.log('Aborted by user.');
240 process.exit(0);
241 }
242 listOfDomains.map(d => domainsToRelease.push(d.domain_name));
243 } else {
244 const answers = domainIndex
245 .toUpperCase().replace(/[^A-Z]/, '')
246 .split('').filter((t, i, a) => a.indexOf(t) === i);
247
248 answers.forEach(a => {
249 const index = a.charCodeAt(0) - 65;
250 if (index > listOfDomains.length - 1) {
251 if (!ignored) {
252 ignored = true;
253 console.log('One or more invalid responses ignored.');
254 }
255 } else {
256 domainsToRelease.push(listOfDomains[index].domain_name);
257 }
258 });
259 }
260
261 showAvailableDeployments(listOfDeployments, siteName);
262 const deployResponse = await askDeploymentKey(true);
263
264 const index = deployResponse.deploy_key_index.substr(0, 1).toUpperCase().charCodeAt(0) - 65;
265 if (index > listOfDeployments.length - 1) {
266 throw new Error('Invalid response. Aborted by user.');
267 }
268 deployKey = listOfDeployments[index].deploy_key;
269
270 spinner.start('Creating new release(s)...');
271 await Promise.all(domainsToRelease.map(d => releases.createRelease(siteName, deployKey, d, envName)));
272 spinner.succeed('Release(s) successfully created.');
273};
274
275exports.command = 'release';
276exports.desc = 'Release your site';
277exports.handler = (argv) => {
278 assertPkg();
279
280 notice();
281
282 if (!argv.siteName) {
283 console.log('This project does not have a site name. Please create a site first.');
284 process.exit(255);
285 }
286
287 (argv.a ? releaseLatest : release)(argv)
288 .catch(err => {
289 spinner.stop();
290
291 error(err);
292 });
293};