@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.
- package/package.json +12 -16
- package/src/Utils.mjs +30 -0
- package/src/{build-tools.js → build-tools.mjs} +28 -12
- package/src/circle-lib.yml +150 -0
- package/src/circle-srv.yml +213 -0
- package/src/firebaseDeploy.mjs +2 -1
- package/src/firebaseServe.mjs +2 -1
- package/src/getjson.mjs +3 -3
- package/src/helm-charts/firebase-rules/.nohelm +0 -0
- package/src/helm-charts/firebase-rules/firebase.json +5 -0
- package/src/helm-charts/firebase-rules/firebaserc.ovh +24 -0
- package/src/helm-charts/firebase-rules/gitignore +2 -0
- package/src/helm-charts/firebase-rules/helmup.plugin +14 -0
- package/src/helm-charts/firebase-rules/leverege.rules.json +65 -0
- package/src/helm-charts/geotile-server/.nohelm +0 -0
- package/src/helm-charts/geotile-server/gitignore +1 -0
- package/src/helm-charts/geotile-server/helmup.plugin +6 -0
- package/src/helm-charts/velero/helmup.plugin +16 -0
- package/src/helmup.sh +47 -1
- package/src/overwhelm.mjs +55 -63
- package/src/push-my-chart.mjs +7 -6
- package/src/refresh-npm-token.mjs +142 -40
- package/src/unleash.mjs +4 -2
- package/leverege-build-tools-2.21.13.tgz +0 -0
- package/lib/server/build-tools.js +0 -138
- package/lib/server/firebaseDeploy.js +0 -195
- package/lib/server/firebaseServe.js +0 -116
- package/lib/server/getfbcfg.js +0 -25
- package/lib/server/getjson.js +0 -70
- package/lib/server/overwhelm.js +0 -447
- package/lib/server/push-my-chart.js +0 -18
- package/lib/server/refresh-npm-token.js +0 -135
- package/lib/server/tag-release.js +0 -681
- package/lib/server/unleash.js +0 -50
- package/lib/web/build-tools.js +0 -138
- package/lib/web/firebaseDeploy.js +0 -195
- package/lib/web/firebaseServe.js +0 -116
- package/lib/web/getfbcfg.js +0 -25
- package/lib/web/getjson.js +0 -70
- package/lib/web/overwhelm.js +0 -447
- package/lib/web/push-my-chart.js +0 -18
- package/lib/web/refresh-npm-token.js +0 -135
- package/lib/web/tag-release.js +0 -681
- package/lib/web/unleash.js +0 -50
- 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
|
|
13
|
-
* be set to enable debugging and
|
|
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;
|
|
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
|
|
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
|
-
|
|
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 =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
//
|
|
96
|
-
|
|
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 (
|
|
100
|
-
|
|
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
|
|
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 :
|
|
217
|
+
channel : slackConfig.channel,
|
|
116
218
|
}
|
|
117
219
|
|
|
118
|
-
if ( process.env.
|
|
119
|
-
log( {
|
|
220
|
+
if ( process.env.BUILD_TOOLS_DEBUG || args.dryRun ) {
|
|
221
|
+
log( { slackConfig, slackData }, '<==Slack skipped' )
|
|
120
222
|
} else {
|
|
121
|
-
fetch(
|
|
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
|
|
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
|
-
}
|