@leverege/build-tools 2.38.2 → 2.39.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/package.json +12 -16
  2. package/src/Utils.mjs +30 -0
  3. package/src/{build-tools.js → build-tools.mjs} +28 -12
  4. package/src/circle-lib.yml +150 -0
  5. package/src/circle-srv.yml +213 -0
  6. package/src/firebaseDeploy.mjs +2 -1
  7. package/src/firebaseServe.mjs +2 -1
  8. package/src/getjson.mjs +3 -3
  9. package/src/helm-charts/firebase-rules/.nohelm +0 -0
  10. package/src/helm-charts/firebase-rules/firebase.json +5 -0
  11. package/src/helm-charts/firebase-rules/firebaserc.ovh +24 -0
  12. package/src/helm-charts/firebase-rules/gitignore +2 -0
  13. package/src/helm-charts/firebase-rules/helmup.plugin +14 -0
  14. package/src/helm-charts/firebase-rules/leverege.rules.json +65 -0
  15. package/src/helm-charts/geotile-server/.nohelm +0 -0
  16. package/src/helm-charts/geotile-server/gitignore +1 -0
  17. package/src/helm-charts/geotile-server/helmup.plugin +6 -0
  18. package/src/helm-charts/velero/helmup.plugin +16 -0
  19. package/src/helmup.sh +47 -1
  20. package/src/overwhelm.mjs +55 -63
  21. package/src/push-my-chart.mjs +7 -6
  22. package/src/refresh-npm-token.mjs +142 -40
  23. package/src/unleash.mjs +4 -2
  24. package/leverege-build-tools-2.21.13.tgz +0 -0
  25. package/lib/server/build-tools.js +0 -138
  26. package/lib/server/firebaseDeploy.js +0 -195
  27. package/lib/server/firebaseServe.js +0 -116
  28. package/lib/server/getfbcfg.js +0 -25
  29. package/lib/server/getjson.js +0 -70
  30. package/lib/server/overwhelm.js +0 -447
  31. package/lib/server/push-my-chart.js +0 -18
  32. package/lib/server/refresh-npm-token.js +0 -135
  33. package/lib/server/tag-release.js +0 -681
  34. package/lib/server/unleash.js +0 -50
  35. package/lib/web/build-tools.js +0 -138
  36. package/lib/web/firebaseDeploy.js +0 -195
  37. package/lib/web/firebaseServe.js +0 -116
  38. package/lib/web/getfbcfg.js +0 -25
  39. package/lib/web/getjson.js +0 -70
  40. package/lib/web/overwhelm.js +0 -447
  41. package/lib/web/push-my-chart.js +0 -18
  42. package/lib/web/refresh-npm-token.js +0 -135
  43. package/lib/web/tag-release.js +0 -681
  44. package/lib/web/unleash.js +0 -50
  45. package/src/overwhelm.js +0 -396
@@ -9,10 +9,11 @@
9
9
  * reported via slack to allow DevOps operators to clean up old tokens in npm.
10
10
  *
11
11
  * For testing the Unix touch command may be used to modify the access access
12
- * time of the npmrc token. The REFRESH_NPM_TOKEN_DEBUG env variable may also
13
- * be set to enable debugging and disable the slack channel spam.
12
+ * time of the npmrc token or the --force switch to ignore the age of the file.
13
+ * The BUILD_TOOLS_DEBUG env variable may also be set to enable debugging and
14
+ * disable the slack channel spam.
14
15
  *
15
- * touch -d 2023-08-20 ~/.npmrc; REFRESH_NPM_TOKEN_DEBUG=1 refresh-npm-token.mjs
16
+ * touch -d 2023-08-20 ~/.npmrc; BUILD_TOOLS_DEBUG=1 refresh-npm-token.mjs
16
17
  */
17
18
 
18
19
  /* eslint-disable security/detect-non-literal-fs-filename */
@@ -20,24 +21,105 @@ import fs from 'node:fs'
20
21
  import os from 'node:os'
21
22
 
22
23
  import chalk from 'chalk'
23
- import execa from 'execa' // we can't use $ until we upgrade to ESM execa
24
-
24
+ import commandLineArgs from 'command-line-args'
25
+ import commandLineUsage from 'command-line-usage'
26
+ import ms from 'ms'
25
27
  import { lt as semverLt } from 'semver'
26
28
 
29
+ import { debug, log, shellCmd } from './Utils.mjs'
30
+
31
+ const refreshTokenOptions = [ // Use refreshTokenOptions to tie into the Usage statements
32
+ /* eslint-disable max-len */
33
+ {
34
+ name : 'project',
35
+ type : String,
36
+ default : false,
37
+ description : '{green the name of the google project containing the npmrc and slack config secrets (default leverege-registry)}',
38
+ },
39
+ {
40
+ name : 'npmrc-age',
41
+ type : String,
42
+ default : '24h',
43
+ description : '{green the age of the local ~/.npmrc file before a refetch is performed (default 24h)}'
44
+ },
45
+ {
46
+ name : 'force',
47
+ type : Boolean,
48
+ default : false,
49
+ description : '{green ignores age of the ~/.npmrc file and always fetches}',
50
+ },
51
+ {
52
+ name : 'dry-run',
53
+ type : Boolean,
54
+ default : false,
55
+ description : '{green performs sanity checks on the settings / environment without updating ~/.npmrc}',
56
+ },
57
+ {
58
+ name : 'help',
59
+ type : Boolean,
60
+ default : false,
61
+ description : '{green display this help screen}',
62
+ },
63
+ /* eslint-enable max-len */
64
+ ]
65
+
66
+ const sections = [
67
+ {
68
+ header : 'Leverege NPM Token Refresher',
69
+ content : `{green
70
+ This tool helps you retrieve an npmrc file stored in a secret within a GCP
71
+ Secret Manager. It enables NPM administrators to manage a single NPM token
72
+ for use by a development team. The tool checks the age of your local ~/.npmrc
73
+ file, and if it exceeds the specified --npmrc-age setting, it will fetch the
74
+ secret, overwriting the existing ~/.npmrc file.
75
+
76
+ This tool may be invoked directly from the command line, or from scripts in
77
+ the build-tools repository. For example, helmup will invoke this command for
78
+ refreshing the token and to provide an audit log to slack when deployments
79
+ are made.}
80
+ `
81
+ },
82
+ { header : 'Options',
83
+ optionList : refreshTokenOptions,
84
+ },
85
+ { header : 'Environment Variables (optional)',
86
+ content : '{bold REFRESH_NPM_PROJECT} {green may be used in lieu of --project}',
87
+ },
88
+ ]
89
+
90
+ const args = commandLineArgs( refreshTokenOptions, { camelCase : true, partial : true } )
91
+ const usage = commandLineUsage( sections )
92
+
93
+ if ( args.help ) {
94
+ log( usage )
95
+ process.exit( 0 )
96
+ }
97
+
98
+ /* eslint-disable no-underscore-dangle */
99
+ if ( args._unknown ) {
100
+ log( usage )
101
+ log( `\nUnrecognized argument [${chalk.bold.red( args._unknown )}]\n` )
102
+ process.exit( 1 )
103
+ }
104
+ /* eslint-enable no-underscore-dangle */
105
+
106
+ const gcpProject = args.project ? args.project : process.env.REFRESH_NPM_PROJECT || 'leverege-registry'
107
+
27
108
  if ( semverLt( process.version, '18.0.0' ) ) {
28
- console.log( chalk.red( '\n\n***ERROR: must be running at least node 18\n' ) ) // eslint-disable-line no-console
109
+ log( chalk.red( '\n\n***ERROR: must be running at least node 18\n' ) )
29
110
  process.exit( 1 )
30
111
  }
31
112
 
32
113
  // max age in seconds of the npmrc file before refresh
33
- const refreshThreshold = 3600 * 24 // 1 day
34
-
35
- // eslint-disable-next-line no-console
36
- const log = console.log
37
- const debug = ( obj, text ) => {
38
- if ( process.env.REFRESH_NPM_TOKEN_DEBUG ) { log( obj, chalk.yellow( text ) ) }
114
+ const refreshThreshold = ms( args.npmrcAge || '24h' ) / 1000 // 1 day of seconds
115
+ if ( refreshThreshold < 60 ) {
116
+ log( chalk.yellow.bold( `
117
+ ***WARNING the npmrc-age of ${args.npmrcAge} results in an age threshold of ${refreshThreshold} seconds
118
+ try using 24h for one day or 60m for an hour
119
+ ` ) )
39
120
  }
40
121
 
122
+ // used to pull the sanitized token [npm_mQZ0......Xmvp] reference from the npmrc file
41
123
  const fetchToken = ( filename ) => {
42
124
  const results = {
43
125
  filename,
@@ -65,60 +147,80 @@ const fetchToken = ( filename ) => {
65
147
  return results
66
148
  }
67
149
 
68
- // fetch the tokens - note the RO token is deprecated and may not exist
150
+ // fetch the tokens - note the RO token is deprecated and might not exist
69
151
  const npmrc = fetchToken( `${os.homedir()}/.npmrc` )
70
152
  const npmrcRo = fetchToken( `${os.homedir()}/.npmrc.ro` )
71
153
 
72
154
  debug( { npmrc, npmrcRo }, '<==Your current tokens' ) // debug
73
155
 
74
- if ( npmrc.exists && npmrc.lastUpdated < refreshThreshold ) { process.exit( 0 ) }
75
-
76
- // fetch the contents of the npmrc secret stored on leverege-registry
77
- let npmrcSecret
78
- const execaOpts = [
79
- 'secrets',
80
- 'versions',
81
- 'access',
82
- 'latest',
83
- '--secret=NPM_DEVELOPER_NPMRC',
84
- '--project=leverege-registry',
85
- ]
156
+ // skip file age check if this is a dry or forced run
157
+ if ( !args.dryRun && !args.force ) {
158
+ if ( npmrc.exists && npmrc.lastUpdated < refreshThreshold ) { process.exit( 0 ) }
159
+ }
160
+
161
+ const getGcpSecretCommand = ( secretName ) => {
162
+ return `gcloud secrets versions access latest --project=${gcpProject} --secret=${secretName}`
163
+ }
164
+
165
+ // fetch the contents of the npmrc secret file stored on the specified project
166
+ let npmrcFile
86
167
  try {
87
- const execaOut = await execa( 'gcloud', execaOpts )
88
- npmrcSecret = execaOut.stdout
89
- debug( { execaOut, execaOpts }, '<==exec info' )// debug
168
+ const npmTokenFetcher = getGcpSecretCommand( 'REFRESH_NPM_NPMRC' )
169
+ debug( { npmTokenFetcher }, '<==Secret Fetcher' )
170
+
171
+ npmrcFile = await shellCmd( npmTokenFetcher )
172
+ debug( { npmrcFile }, '<==shellCmd Output npmrc file' )// debug
90
173
  } catch ( err ) {
91
174
  log( chalk.yellow( `\nFailed Command => [${err.escapedCommand}]\n\n` ), chalk.red( err.stderr ) )
92
175
  process.exit( 1 )
93
176
  }
94
177
 
95
- // now update the user's npmrc file
96
- log( chalk.yellow( '\n*** Refreshing your ~/.npmrc npm token file' ) )
178
+ // fetch and parse the slack config for audits
179
+ const slackSecretName = 'REFRESH_NPM_SLACK_CONFIG'
180
+ let slackConfig
181
+ try {
182
+ const slackConfigFetcher = getGcpSecretCommand( slackSecretName )
183
+ const slackSecret = await shellCmd( slackConfigFetcher )
184
+ slackConfig = JSON.parse( slackSecret )
185
+ debug( { slackSecretName, slackConfig, slackConfigFetcher }, `<==shellCmd Output ${slackSecretName}` )
186
+ } catch ( err ) {
187
+ if ( err.escapedCommand && err.stderr ) {
188
+ log( chalk.yellow( `\nFailed Fetching Slack Secret => [${err.escapedCommand}]\n\n` ), chalk.red( err.stderr ) )
189
+ } else {
190
+ log( err, chalk.red( `\n\nFailed to fetch and / or parse secret ${slackSecretName} from ${gcpProject}` ) )
191
+ }
192
+ process.exit( 1 )
193
+ }
97
194
 
195
+ // update the user's npmrc file if this isn't a dry run
98
196
  try {
99
- if ( fs.existsSync( npmrc ) ) {
100
- fs.copyFileSync( npmrc, `${npmrc}-BAK` ) // make a backup for safety
197
+ if ( args.dryRun ) {
198
+ log( chalk.yellow( '\n*** Dry Run - files will not be updated' ) )
199
+ } else {
200
+ log( chalk.yellow( '\n*** Refreshing your ~/.npmrc npm token file' ) )
201
+ if ( fs.existsSync( npmrc ) ) {
202
+ fs.copyFileSync( npmrc, `${npmrc}-BAK` ) // make a backup for safety
203
+ }
204
+ fs.writeFileSync( npmrc.filename, npmrcFile )
101
205
  }
102
- fs.writeFileSync( npmrc.filename, npmrcSecret )
103
206
  } catch ( fileErr ) {
104
207
  log( fileErr, 'fs failed' )
105
208
  process.exit( 1 )
106
209
  }
107
210
 
211
+ // slack out the details to the configured channel
108
212
  const now = ( new Date() ).toLocaleString()
109
- const slackURL = 'https://hooks.slack.com/services/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'
110
- const execaAct = await execa( 'gcloud', [ 'config', 'get', 'account', ] )
111
- const account = execaAct.stdout
213
+ const account = await shellCmd( 'gcloud config get account' )
112
214
  const slackData = {
113
215
  // eslint-disable-next-line max-len
114
216
  text : `--- npmrc refreshed by \`${account}\` at ${now}\n tokens RW [\`${npmrc.token}\`] RO [\`${npmrcRo.token}\`]`,
115
- channel : '#dev-ops-helm',
217
+ channel : slackConfig.channel,
116
218
  }
117
219
 
118
- if ( process.env.REFRESH_NPM_TOKEN_DEBUG ) {
119
- log( { slackURL, slackData }, '<==Slack skipped' )
220
+ if ( process.env.BUILD_TOOLS_DEBUG || args.dryRun ) {
221
+ log( { slackConfig, slackData }, '<==Slack skipped' )
120
222
  } else {
121
- fetch( slackURL, {
223
+ fetch( slackConfig.url, {
122
224
  method : 'POST',
123
225
  body : JSON.stringify( slackData ),
124
226
  headers : {
package/src/unleash.mjs CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import execa from 'execa'
4
3
  import SimpleGit from 'simple-git'
5
4
 
5
+ import { shellCmd } from './Utils.mjs'
6
+
6
7
  const git = new SimpleGit()
7
8
 
8
9
  const getCurrentBranchName = async () => {
@@ -35,7 +36,8 @@ const isProtected = async () => {
35
36
 
36
37
  const npmRunner = async ( script ) => {
37
38
  try {
38
- const results = await execa( 'npm', [ 'run', script ], { stdio : 'inherit' } )
39
+ const cmd = `npm run ${script}`
40
+ const results = await shellCmd( cmd, { stdio : 'inherit' } )
39
41
  return results
40
42
  } catch ( err ) {
41
43
  process.exit( 1 )
Binary file
@@ -1,138 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
-
4
- const fs = require('node:fs');
5
- const path = require('node:path');
6
- const chalk = require('chalk');
7
- const cliArgs = require('command-line-args');
8
- const cliHelp = require('command-line-usage');
9
- const npmRegistryFetch = require('npm-registry-fetch');
10
- const semverLt = require('semver/functions/lt');
11
-
12
- /* eslint-disable no-console */
13
-
14
- const optionList = [
15
- // Use optionList to tie into the Usage statements
16
- {
17
- name: 'root',
18
- type: Boolean,
19
- default: false,
20
- description: 'returns the root of the build-tools installation'
21
- }, {
22
- name: 'latest',
23
- type: Boolean,
24
- default: false,
25
- description: 'verifies the latest build-tools are installed'
26
- }, {
27
- name: 'bashfun',
28
- type: Boolean,
29
- default: false,
30
- description: 'source the output to define common bash helper functions'
31
- }, {
32
- name: 'reporoot',
33
- type: Boolean,
34
- default: false,
35
- description: 'the root of the build-tools repo - for finding other config files'
36
- }, {
37
- name: 'help',
38
- type: Boolean,
39
- default: false,
40
- description: 'display this help screen'
41
- }, {
42
- name: 'version',
43
- type: Boolean,
44
- default: false,
45
- description: 'returns the build-tools repo version'
46
- }, {
47
- name: 'verbose',
48
- alias: 'v',
49
- type: Boolean,
50
- default: false,
51
- description: 'emit additional info at run time'
52
- }];
53
- const sections = [{
54
- header: 'A collection of build / support tools for all Leverege code',
55
- content: `README: {green https://bitbucket.org/leverege/build-tools/src/development}
56
- `
57
- }, {
58
- header: 'Options',
59
- optionList
60
- }];
61
- const args = cliArgs(optionList, {
62
- partial: true
63
- });
64
- const help = cliHelp(sections);
65
- const repoRoot = path.dirname(__dirname);
66
- const thisPackage = require(`${repoRoot}/package.json`);
67
- const thisVersion = thisPackage.version;
68
- if (args.root) {
69
- console.log(repoRoot);
70
- process.exit(0);
71
- }
72
- if (args.bashfun) {
73
- console.log(`${repoRoot}/src/bash-funcs`);
74
- process.exit(0);
75
- }
76
- if (args.reporoot) {
77
- console.log(`${repoRoot}`);
78
- process.exit(0);
79
- }
80
- const checkForLatest = async (pkg = '@leverege/build-tools') => {
81
- const getToken = () => {
82
- const tokenFile = `${process.env.HOME}/.npmrc`;
83
- if (!fs.existsSync(tokenFile)) {
84
- console.error(`Cannot find token file ${tokenFile}`);
85
- process.exit(1);
86
- }
87
- try {
88
- const tokenLine = fs.readFileSync(tokenFile).toString();
89
- const tokenRegX = new RegExp('.*registry.npmjs.org\\/:\\w+=+(.*)');
90
- const tokenStr = tokenLine.match(tokenRegX);
91
- if (!tokenStr) {
92
- console.error(`\n***ERROR: malformed npm token in ${tokenFile}\n`);
93
- process.exit(1);
94
- }
95
- return tokenLine.match(tokenRegX)[1];
96
- } catch (err) {
97
- console.error(err);
98
- }
99
- };
100
- try {
101
- const list = await npmRegistryFetch.json(pkg, {
102
- '//registry.npmjs.org/:_authToken': getToken()
103
- });
104
- return list['dist-tags'].latest;
105
- } catch (err) {
106
- console.log(err);
107
- }
108
- };
109
- const checkAndAnnounce = () => {
110
- checkForLatest().then(latestVersion => {
111
- if (semverLt(thisVersion, latestVersion)) {
112
- console.log(chalk.white`
113
- Update available ${chalk.grey(thisVersion)} \u2b62 ${chalk.green(latestVersion)}
114
- Run ${chalk.cyan('npm i -g @leverege/build-tools')} to update
115
- `);
116
- process.exit(1);
117
- } else {
118
- process.exit(0);
119
- }
120
- }).catch(err => {});
121
- };
122
- if (args.latest) {
123
- checkAndAnnounce();
124
- }
125
- if (args.version) {
126
- console.log(`build-tools version ${thisVersion}`);
127
- checkAndAnnounce();
128
- }
129
- if (args.help) {
130
- console.log(help);
131
- process.exit(0);
132
- }
133
-
134
- /* eslint-disable no-underscore-dangle */
135
- if (args._unknown) {
136
- console.log(`\nUnrecognized argument [${args._unknown}] try --help\n`);
137
- process.exit(1);
138
- }
@@ -1,195 +0,0 @@
1
- #!/usr/bin/env node
2
- /* eslint-disable no-console */
3
- // https://github.com/google/zx
4
- /* eslint-disable max-len */
5
- "use strict";
6
-
7
- var _enquirer = _interopRequireDefault(require("enquirer"));
8
- var _ansiColors = _interopRequireDefault(require("ansi-colors"));
9
- var _zx = require("zx");
10
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
- const {
12
- prompt
13
- } = _enquirer.default;
14
-
15
- // This will preserve coloration from spawned child processes
16
- process.env.FORCE_COLORS = 3;
17
- process.env.FORCE_COLOR = '1';
18
- let TARGET;
19
- let CHANNEL;
20
-
21
- // A single unnamed parameter is assumed to be a deploy target.
22
- // If named parameters exist, they may not be stored in argv,
23
- // so don't look for a larger length of argv to decide whether to look for named paramters
24
- if (_zx.argv._.length === 1) {
25
- TARGET = _zx.argv._[0];
26
- } else {
27
- TARGET = _zx.argv.target;
28
- if (_zx.argv.channelName) {
29
- CHANNEL = {};
30
- CHANNEL.name = _zx.argv.channelName;
31
- CHANNEL.expiration = _zx.argv.channelExpiration ?? '7d';
32
- }
33
- }
34
-
35
- // If target has not been defined on the command line,
36
- // then enter interactive mode
37
- const INTERACTIVE_MODE = !TARGET;
38
- let exited = false;
39
- const SECRETS_DIR = 'secrets';
40
- const EXIT_EVENTS = ['SIGINT', 'exit', 'uncaughException', 'unhandledRejection'];
41
- const [firebaserc, firebaseJson, gitBranch] = await Promise.all([_zx.fs.exists(_zx.path.join(process.cwd(), '.firebaserc')), _zx.fs.exists(_zx.path.join(process.cwd(), 'firebase.json')), (0, _zx.$)`git rev-parse --abbrev-ref HEAD -C ${process.cwd()}`.then(({
42
- stdout
43
- }) => {
44
- return stdout.split('\n')[0];
45
- })]);
46
- if (!firebaserc) {
47
- console.log(_zx.chalk.red('No .firebaserc file found. Make sure that you are executing firebaseDeploy from within a firebase project directory.'));
48
- }
49
- if (!firebaseJson) {
50
- console.log(_zx.chalk.red('No firebase.json file found. Make sure that you are executing firebaseDeploy from within a firebase project directory.'));
51
- }
52
- if (!firebaserc || !firebaseJson) {
53
- process.exit();
54
- }
55
- const firebasercContent = JSON.parse(await _zx.fs.readFile(_zx.path.join(process.cwd(), '.firebaserc'), {
56
- encoding: 'utf-8'
57
- }));
58
- let maxSiteIdLength = 0;
59
- const targets = Object.entries(firebasercContent.targets).reduce((prev, [projectId, config]) => {
60
- Object.entries(config.hosting).forEach(([siteId, aliases]) => {
61
- aliases.forEach(alias => {
62
- // Store the longest length of site id for formatting later on
63
- maxSiteIdLength = Math.max(siteId.length, maxSiteIdLength);
64
- prev[alias] = {
65
- projectId,
66
- siteId,
67
- alias,
68
- production: config.production
69
- };
70
- });
71
- });
72
- return prev;
73
- }, {});
74
- if (INTERACTIVE_MODE) {
75
- const {
76
- targetEnv
77
- } = await prompt([{
78
- type: 'autocomplete',
79
- name: 'targetEnv',
80
- message: 'To which environment would you like to deploy?',
81
- choices: Object.values(targets).map(t => {
82
- const productionLabel = t.production ? _ansiColors.default.bold.red('--PRODUCTION--') : '';
83
- return {
84
- message: `${_ansiColors.default.bold.cyan('Site: ')}${t.siteId.padEnd(maxSiteIdLength)} ${_ansiColors.default.bold.green('Project: ')}${t.projectId} ${productionLabel}`,
85
- value: t.alias
86
- };
87
- })
88
- }]);
89
- TARGET = targets[targetEnv];
90
- if (TARGET.production) {
91
- const {
92
- confirmProductionDeploy
93
- } = await prompt([{
94
- type: 'confirm',
95
- name: 'confirmProductionDeploy',
96
- message: `${_ansiColors.default.bold.red('WARNING: ')} This is a ${_ansiColors.default.bold.yellow('PRODUCTION')} environment. Are you sure you want to deploy here?`
97
- }]);
98
- if (!confirmProductionDeploy) {
99
- console.log('Cancelling deployment...');
100
- process.exit();
101
- }
102
- }
103
- const {
104
- useChannel
105
- } = await prompt([{
106
- type: 'confirm',
107
- name: 'useChannel',
108
- message: 'Do you want to deploy to a temporary preview channel?'
109
- }]);
110
- if (useChannel) {
111
- const {
112
- channelName,
113
- channelExpiration
114
- } = await prompt([{
115
- type: 'input',
116
- name: 'channelName',
117
- message: 'To which channel would you like to deploy?',
118
- initial: gitBranch ?? 'temp-deploy'
119
- }, {
120
- type: 'input',
121
- name: 'channelExpiration',
122
- message: 'When should the channel expire?',
123
- initial: '7d'
124
- }]);
125
- CHANNEL = {
126
- name: channelName,
127
- expiration: channelExpiration
128
- };
129
- }
130
- } else {
131
- TARGET = targets[TARGET];
132
- }
133
- if (!TARGET) {
134
- console.log(_zx.chalk.red('No such target exists in .firebaserc config.'));
135
- process.exit();
136
- }
137
- const [aliasFileExists, siteIdFileExists, sharedFileExists] = await Promise.all([_zx.fs.exists(_zx.path.join(process.cwd(), SECRETS_DIR, `${TARGET.alias}.env`)), _zx.fs.exists(_zx.path.join(process.cwd(), SECRETS_DIR, `${TARGET.siteId}.env`)), _zx.fs.exists(_zx.path.join(process.cwd(), SECRETS_DIR, 'shared.env'))]);
138
- if (!aliasFileExists && !siteIdFileExists) {
139
- console.log(_zx.chalk.red(`No env file for ${TARGET.alias} exists. Check your secrets directory and try again`));
140
- process.exit();
141
- }
142
- const exec = [];
143
- if (sharedFileExists) {
144
- exec.push(_zx.fs.readFile(_zx.path.join(process.cwd(), SECRETS_DIR, 'shared.env'), {
145
- encoding: 'utf-8'
146
- }));
147
- }
148
- if (aliasFileExists) {
149
- exec.push(_zx.fs.readFile(_zx.path.join(process.cwd(), SECRETS_DIR, `${TARGET.alias}.env`), {
150
- encoding: 'utf-8'
151
- }));
152
- } else {
153
- exec.push(_zx.fs.readFile(_zx.path.join(process.cwd(), SECRETS_DIR, `${TARGET.siteId}.env`), {
154
- encoding: 'utf-8'
155
- }));
156
- }
157
- console.log(`\n\n\n${_ansiColors.default.bold.green('====== Deployment Summary ======')}`);
158
- console.log(`${_ansiColors.default.blue('Firebase project:')} ${TARGET.projectId} ${TARGET.production ? _ansiColors.default.yellow('--PRODUCTION--') : ''}`);
159
- console.log(`${_ansiColors.default.blue('Site ID:')} ${TARGET.siteId}`);
160
- console.log(`${_ansiColors.default.blue('Channel Name:')} ${CHANNEL?.name ?? _ansiColors.default.italic.gray('(none)')}`);
161
- console.log(`${_ansiColors.default.blue('Channel Expiration date:')} ${CHANNEL?.name ? CHANNEL.expiration ?? _ansiColors.default.italic.gray('(none)') : _ansiColors.default.italic.gray('(n/a)')}`);
162
- console.log(`${_ansiColors.default.blue('Shared .env file:')} ${sharedFileExists ? `${SECRETS_DIR}/shared.env` : _ansiColors.default.italic.gray('(none)')}`);
163
- console.log(`${_ansiColors.default.blue('Additional .env overrides:')} ${aliasFileExists ? `${SECRETS_DIR}/${TARGET.alias}.env` : `${SECRETS_DIR}/${TARGET.siteId}.env`}`);
164
- console.log(`${_ansiColors.default.bold.green('===============================')}`);
165
- if (INTERACTIVE_MODE) {
166
- const {
167
- confirmSelections
168
- } = await prompt([{
169
- type: 'confirm',
170
- name: 'confirmSelections',
171
- message: 'Proceed with this deployment?'
172
- }]);
173
- if (!confirmSelections) {
174
- console.log('Cancelling deployment...');
175
- process.exit();
176
- }
177
- }
178
- const envFileContent = (await Promise.all(exec)).join('\n');
179
- EXIT_EVENTS.forEach(event => {
180
- process.on(event, () => {
181
- if (!exited) {
182
- exited = true;
183
- _zx.fs.removeSync(_zx.path.join(process.cwd(), '.env.temp'));
184
- }
185
- });
186
- });
187
- await _zx.fs.writeFile(_zx.path.join(process.cwd(), '.env.temp'), envFileContent);
188
- try {
189
- // deploying to a channel requires slightly different parameter structure
190
- const deployType = CHANNEL ? `hosting:channel:deploy ${CHANNEL.name} ${CHANNEL.expiration ? `--expires ${CHANNEL.expiration}` : ''}`.split(' ') : 'deploy';
191
- const hostingPrefix = CHANNEL ? '' : 'hosting:';
192
- await (0, _zx.$)`npm run clean && DOTENV_CONFIG_PATH=${_zx.path.join(process.cwd(), '.env.temp')} DEPLOYMENT_TARGET=${TARGET.siteId} npm run build && firebase use ${TARGET.projectId} && firebase ${deployType} --only ${hostingPrefix}${TARGET.siteId}`;
193
- } catch (err) {
194
- console.error(err);
195
- }