@leverege/build-tools 2.94.0 → 2.95.0

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 (87) hide show
  1. package/.vscode/launch.json +14 -0
  2. package/package.json +5 -2
  3. package/src/Docker.mjs +3 -2
  4. package/src/Utils.mjs +53 -6
  5. package/src/artifacts/Utils.mjs +1 -1
  6. package/src/bash-funcs +1 -1
  7. package/src/git-tar.sh +1 -1
  8. package/src/helm-charts/emailer/values-local.yaml +1 -8
  9. package/src/helm-charts/prom-operator/helmup.plugin +1 -1
  10. package/src/helm-charts/traefik/helmup.plugin +1 -1
  11. package/src/helm-charts/velero/README.md +135 -0
  12. package/src/helm-charts/velero/helmup.bootstrap +1 -0
  13. package/src/helm-charts/velero/helmup.plugin +29 -2
  14. package/src/helm-charts/velero/values-local.yaml +1 -1
  15. package/src/helmup.sh +49 -41
  16. package/src/service-man/ServiceMan.mjs +75 -0
  17. package/src/service-man/config/CommandLine.mjs +199 -0
  18. package/src/service-man/config/Config.mjs +203 -0
  19. package/src/service-man/config/VersionCheck.mjs +79 -0
  20. package/src/service-man/config/iam-roles/leveregeBigQueryReader.json +10 -0
  21. package/src/service-man/config/iam-roles/leveregeBigQueryWriter.json +13 -0
  22. package/src/service-man/config/iam-roles/leveregeCloudFunctionsManager.json +12 -0
  23. package/src/service-man/config/iam-roles/leveregeFirebaseHosting.json +9 -0
  24. package/src/service-man/config/iam-roles/leveregeFirebaseMessaging.json +13 -0
  25. package/src/service-man/config/iam-roles/leveregeFirebaseUpdates.json +10 -0
  26. package/src/service-man/config/iam-roles/leveregeLogsViewer.json +8 -0
  27. package/src/service-man/config/iam-roles/leveregePubSub.json +13 -0
  28. package/src/service-man/config/iam-roles/leveregeSecretsViewer.json +9 -0
  29. package/src/service-man/config/iam-roles/leveregeStorageObjectAdmin.json +21 -0
  30. package/src/service-man/config/iam-roles/leveregeStorageObjectReader.json +13 -0
  31. package/src/service-man/config/iam-roles/leveregeSvcAcctActor.json +8 -0
  32. package/src/service-man/config/iam-roles/leveregeSvcAcctTokenCreator.json +8 -0
  33. package/src/service-man/config/index.mjs +7 -0
  34. package/src/service-man/config/secrets/authz-jwt.json +6 -0
  35. package/src/service-man/config/secrets/authz-postgres.json +7 -0
  36. package/src/service-man/config/secrets/db-timescale-dense.json +8 -0
  37. package/src/service-man/config/secrets/jwt-options.json +7 -0
  38. package/src/service-man/config/secrets/mail-gun-api-key.json +6 -0
  39. package/src/service-man/config/secrets/npmrc.json +6 -0
  40. package/src/service-man/config/secrets/oidc-client-secret.json +6 -0
  41. package/src/service-man/config/secrets/postgresql-password.json +8 -0
  42. package/src/service-man/config/secrets/session-secret.json +6 -0
  43. package/src/service-man/config/secrets/twilio.json +8 -0
  44. package/src/service-man/config/service-accounts/api-server.json +12 -0
  45. package/src/service-man/config/service-accounts/authz-server.json +5 -0
  46. package/src/service-man/config/service-accounts/db-curator.json +8 -0
  47. package/src/service-man/config/service-accounts/emailer.json +7 -0
  48. package/src/service-man/config/service-accounts/fota-server.json +6 -0
  49. package/src/service-man/config/service-accounts/geotile-server.json +8 -0
  50. package/src/service-man/config/service-accounts/imagine.json +6 -0
  51. package/src/service-man/config/service-accounts/message-processor.json +6 -0
  52. package/src/service-man/config/service-accounts/messenger.json +7 -0
  53. package/src/service-man/config/service-accounts/pubsub-pulse.json +6 -0
  54. package/src/service-man/config/service-accounts/push-notifier.json +8 -0
  55. package/src/service-man/config/service-accounts/reason.json +5 -0
  56. package/src/service-man/config/service-accounts/resource-server.json +9 -0
  57. package/src/service-man/config/service-accounts/rule-engine.json +7 -0
  58. package/src/service-man/config/service-accounts/scheduler.json +6 -0
  59. package/src/service-man/config/service-accounts/transponder.json +15 -0
  60. package/src/service-man/gcloud-k8s/RolesManager.mjs +41 -0
  61. package/src/service-man/gcloud-k8s/SecretsManager.mjs +182 -0
  62. package/src/service-man/gcloud-k8s/ServiceAccountManager.mjs +246 -0
  63. package/src/service-man/gcloud-k8s/VersionManager.mjs +114 -0
  64. package/src/service-man/gcloud-k8s/index.mjs +11 -0
  65. package/src/service-man/service-man.mjs +18 -0
  66. package/src/service-man/utils/index.mjs +83 -0
  67. package/leverege-build-tools-2.21.13.tgz +0 -0
  68. package/lib/server/build-tools.js +0 -138
  69. package/lib/server/firebaseDeploy.js +0 -195
  70. package/lib/server/firebaseServe.js +0 -116
  71. package/lib/server/getfbcfg.js +0 -25
  72. package/lib/server/getjson.js +0 -70
  73. package/lib/server/overwhelm.js +0 -447
  74. package/lib/server/push-my-chart.js +0 -18
  75. package/lib/server/refresh-npm-token.js +0 -135
  76. package/lib/server/tag-release.js +0 -681
  77. package/lib/server/unleash.js +0 -50
  78. package/lib/web/build-tools.js +0 -138
  79. package/lib/web/firebaseDeploy.js +0 -195
  80. package/lib/web/firebaseServe.js +0 -116
  81. package/lib/web/getfbcfg.js +0 -25
  82. package/lib/web/getjson.js +0 -70
  83. package/lib/web/overwhelm.js +0 -447
  84. package/lib/web/push-my-chart.js +0 -18
  85. package/lib/web/refresh-npm-token.js +0 -135
  86. package/lib/web/tag-release.js +0 -681
  87. package/lib/web/unleash.js +0 -50
@@ -0,0 +1,114 @@
1
+ import fs from 'node:fs'
2
+
3
+ import chalk from 'chalk'
4
+ import { execa } from 'execa'
5
+
6
+ import { condir, log, } from '../../Utils.mjs'
7
+
8
+ export default class VersionManager {
9
+ constructor( svcConfig ) {
10
+ this.config = svcConfig
11
+ }
12
+
13
+ // converts an array of objects with deployment name, and app/chart versions
14
+ // into an object of name/chart versions which helmup can use.
15
+ //
16
+ // Input:
17
+ // [
18
+ // { name: 'emailer', appVersion: 'v2.9.0', chartVersion: '4.6.0' },
19
+ // { name: 'fota-server', appVersion: '0.8.8', chartVersion: '1.8.9' },
20
+ // ]
21
+ //
22
+ // Output:
23
+ // {
24
+ // "emailer": { "service": "v2.9.0", "chart": "4.6.0" },
25
+ // "fota-server": { "service": "0.8.8", "chart": "1.8.9" },
26
+ // }
27
+ //
28
+ // The DTS used to be generated all of the time but it turns out that it's not
29
+ // particularly useful and was just causing git diffs every time Versions.json
30
+ // was updated. Generating the DTS is now optional but nothing currently uses it
31
+ // so this is just a reminder to my future self that this is intentional and the
32
+ // old code was left in place in case we decide to add a command line option to
33
+ // enable its presence.
34
+ //
35
+ generateVersionsFile( labels, outFile = 'Versions.json', generateDTS = false ) {
36
+ const timestampNow = () => {
37
+ const zeroPad = num => `${num}`.padStart( 2, '0' )
38
+ const now = new Date()
39
+ const humanYear = `${now.getFullYear()}-${zeroPad( now.getMonth() + 1 )}-${zeroPad( now.getDate() + 1 )}`
40
+ const humanTime = `${zeroPad( now.getHours() )}:${zeroPad( now.getMinutes() )}:${zeroPad( now.getSeconds() )}`
41
+ return generateDTS ? { lastUpdated : `${humanYear} ${humanTime}`, lastTimestamp : now.getTime() } : {}
42
+ }
43
+
44
+ const sortedChart = labels.sort( ( a, b ) => {
45
+ if ( a.name < b.name ) return -1
46
+ if ( a.name > b.name ) return 1
47
+ return 0
48
+ } )
49
+
50
+ const chart = sortedChart.reduce( ( acc, label ) => {
51
+ const { name, chartVersion } = label
52
+
53
+ // Needs to be able to handle both legacy and new chart versions
54
+ // 4.7.1 -> 4.7.1
55
+ // api-server-4.8.0 -> 4.8.0
56
+ // transponder-5.2.0-beta.12 -> 5.2.0-beta.12
57
+ const shortVersion = chartVersion.match( /.*(\d+\.\d+\.\d+.*)/ )[1]
58
+
59
+ // Legacy Versions.json also used to track the service version and whether
60
+ // or not the chart was legacy or current. Neither of those settings were
61
+ // in use so they've since been dropped.
62
+ //
63
+ acc[name] = { chart : shortVersion } // no more service/generation
64
+ return acc
65
+ }, timestampNow() )
66
+
67
+ try {
68
+ fs.writeFileSync( outFile, `${JSON.stringify( chart, null, 2 )}\n` ) // eslint-disable-line security/detect-non-literal-fs-filename
69
+ log( chalk.green( ` fetched and stored latest in ${outFile} file` ) )
70
+ } catch ( err ) {
71
+ log( err, `***ERROR*** generateVersionsFile failed to write ${outFile}` )
72
+ }
73
+ }
74
+
75
+ // Old charts label: -l=chart/version
76
+ // New charts label: -l=helm.sh/chart
77
+ async fetchCharts( chartVersionLabel, appVersionLabel, generation ) {
78
+ try {
79
+ const execaReturn = await execa( 'kubectl', [
80
+ 'get', 'deployments', '--all-namespaces', '-o=json', `-l=${chartVersionLabel}`,
81
+ ] )
82
+ const deployments = JSON.parse( execaReturn.stdout )
83
+ // log( `escaped cmd=[${execaReturn.escapedCommand}]\nreturned ${deployments.items.length} entries` )
84
+ const labels = deployments.items.reduce( ( acc, item ) => {
85
+ const label = item.metadata.labels
86
+ if ( label?.app ) {
87
+ acc.push( {
88
+ name : label['app.kubernetes.io/name'] || label.app,
89
+ chartVersion : label[chartVersionLabel],
90
+ appVersion : label[appVersionLabel],
91
+ generation,
92
+ } )
93
+ }
94
+ return acc
95
+ }, [] )
96
+ return labels
97
+ } catch ( err ) {
98
+ condir( '*** execa error ***', err )
99
+ return undefined
100
+ }
101
+ }
102
+
103
+ async start() {
104
+ log( chalk.green.bold( '\nVersion Management\n' ) )
105
+ try {
106
+ const oldEntries = await this.fetchCharts( 'chart/version', 'chart/appVersion', 'legacy' )
107
+ const newEntries = await this.fetchCharts( 'helm.sh/chart', 'app.kubernetes.io/version', 'current' )
108
+
109
+ this.generateVersionsFile( oldEntries.concat( newEntries ) )
110
+ } catch ( err ) {
111
+ condir( '*** execa error ***', err )
112
+ }
113
+ }
114
+ }
@@ -0,0 +1,11 @@
1
+ import RolesManager from './RolesManager.mjs'
2
+ import SecretsManager from './SecretsManager.mjs'
3
+ import ServiceAccountManager from './ServiceAccountManager.mjs'
4
+ import VersionManager from './VersionManager.mjs'
5
+
6
+ export {
7
+ RolesManager,
8
+ SecretsManager,
9
+ ServiceAccountManager,
10
+ VersionManager,
11
+ }
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from 'node:path'
4
+ import url from 'node:url'
5
+
6
+ import ServiceMan from './ServiceMan.mjs'
7
+
8
+ const getFilename = () => {
9
+ const filename = url.fileURLToPath( import.meta.url )
10
+ return filename
11
+ }
12
+
13
+ const getDirname = () => {
14
+ const dirname = path.dirname( getFilename() )
15
+ return dirname
16
+ }
17
+
18
+ ServiceMan.start( { sourceTop : getDirname() } )
@@ -0,0 +1,83 @@
1
+ import chalk from 'chalk'
2
+ import { $ } from 'execa'
3
+ import inquirer from 'inquirer'
4
+
5
+ import { debug } from '../../Utils.mjs'
6
+
7
+ /**
8
+ * Sets first char to Upper
9
+ */
10
+ export const capitalizeDeprecated = ( norm ) => {
11
+ return norm.charAt( 0 ).toUpperCase() + norm.slice( 1 )
12
+ }
13
+
14
+ /**
15
+ * General "yes" to proceed function.
16
+ */
17
+ export const proceed = async ( query = 'Do you wish to proceed?' ) => {
18
+ await inquirer.prompt( [
19
+ {
20
+ type : 'confirm',
21
+ name : 'proceed',
22
+ message : query,
23
+ default : true,
24
+ }
25
+ ]
26
+ ).then( ( answer ) => {
27
+ if ( !answer.proceed ) { process.exit( 0 ) }
28
+ } )
29
+ }
30
+
31
+ /**
32
+ * A simple mS sleeper.
33
+ */
34
+ export const sleepDeprecated = ( ms = 0 ) => {
35
+ return new Promise( ( resolve ) => {
36
+ setTimeout( resolve, ms )
37
+ } )
38
+ }
39
+
40
+ export const errorExitDeprecated = ( error, exception = { exitCode : 1 } ) => {
41
+ const msg = exception instanceof Error ? exception.stack || exception.message : JSON.stringify( exception, null, 2 ) // pretty-print plain objects
42
+ console.error( { error }, `\n${chalk.red.bold( msg )}\n` ) /* eslint-disable-line no-console */
43
+ process.exit( ( 'exitCode' in exception && typeof exception.exitCode === 'number' ) ? exception.exitCode : 1 )
44
+ }
45
+
46
+ export const shellCmdDeprecated = async ( cmdstr, opts = {} ) => {
47
+ try {
48
+ // due to the escaping rules we pull apart the cmdstr and invoke $ using
49
+ // the (command, argsArray) or the escaped command sent to spawn will have
50
+ // addition double quotes which causes fail
51
+ const [ command, ...args ] = cmdstr.split( ' ' )
52
+
53
+ debug( { command, args }, '<==execa $ invoke' )
54
+ const results = await $( opts )`${command} ${args}`
55
+ debug( { results }, '<==execa $ results' )
56
+
57
+ return results.stdout
58
+ } catch ( error ) {
59
+ debug( { error }, '<==shellCmd' )
60
+ throw error
61
+ }
62
+ }
63
+
64
+ export const getGcpSecretDeprecated = async ( gcpProject, secretName, opts ) => {
65
+ const secretFetchCmd = `gcloud secrets versions access latest --project=${gcpProject} --secret=${secretName}`
66
+ let secretString
67
+ try {
68
+ secretString = await shellCmdDeprecated( secretFetchCmd )
69
+ } catch ( error ) {
70
+ debug( { error }, '<==getGcpSecret' )
71
+ throw error
72
+ }
73
+
74
+ if ( !opts?.isJSON ) { return secretString }
75
+
76
+ try {
77
+ const secretJSON = JSON.parse( secretString )
78
+ return secretJSON
79
+ } catch ( error ) {
80
+ debug( { error }, '<==getGcpSecret JSON.parse' )
81
+ throw error
82
+ }
83
+ }
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
- }
@@ -1,116 +0,0 @@
1
- #!/usr/bin/env node
2
- // https://github.com/google/zx
3
- /* eslint-disable max-len */
4
- /* eslint-disable no-console */
5
- "use strict";
6
-
7
- var _enquirer = _interopRequireDefault(require("enquirer"));
8
- var _zx = require("zx");
9
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
- const {
11
- prompt
12
- } = _enquirer.default;
13
-
14
- // This will preserve coloration from spawned child processes
15
- process.env.FORCE_COLORS = 3;
16
- process.env.FORCE_COLOR = '1';
17
- let TARGET = _zx.argv._[0];
18
- let exited = false;
19
- const SECRETS_DIR = 'secrets';
20
- const EXIT_EVENTS = ['SIGINT', 'exit', 'uncaughtException', 'unhandledRejection'];
21
- const [firebaserc, firebaseJson, hasLocalConfig] = await Promise.all([_zx.fs.exists(_zx.path.join(process.cwd(), '.firebaserc')), _zx.fs.exists(_zx.path.join(process.cwd(), 'firebase.json')), _zx.fs.exists(_zx.path.join(process.cwd(), SECRETS_DIR, 'local.env'))]);
22
- if (!hasLocalConfig) {
23
- if (!firebaserc) {
24
- console.log(_zx.chalk.red('No .firebaserc file found. Make sure that you are executing firebaseDeploy from within a firebase project directory.'));
25
- }
26
- if (!firebaseJson) {
27
- console.log(_zx.chalk.red('No firebase.json file found. Make sure that you are executing firebaseDeploy from within a firebase project directory.'));
28
- }
29
- if (!firebaserc || !firebaseJson) {
30
- process.exit();
31
- }
32
- }
33
- const firebasercContent = firebaserc ? JSON.parse(await _zx.fs.readFile(_zx.path.join(process.cwd(), '.firebaserc'), {
34
- encoding: 'utf-8'
35
- })) : {
36
- targets: {}
37
- };
38
- const defaultTargets = {};
39
- if (hasLocalConfig) {
40
- defaultTargets.local = {
41
- siteId: 'local',
42
- alias: 'local'
43
- };
44
- }
45
- const targets = Object.entries(firebasercContent.targets).reduce((prev, [projectId, config]) => {
46
- Object.entries(config.hosting).forEach(([siteId, aliases]) => {
47
- aliases.forEach(alias => {
48
- prev[alias] = {
49
- projectId,
50
- siteId,
51
- alias
52
- };
53
- });
54
- });
55
- return prev;
56
- }, defaultTargets);
57
- if (!TARGET) {
58
- const choices = Object.keys(targets).map(t => ({
59
- name: t,
60
- value: t
61
- }));
62
- let targetEnv;
63
- if (choices.length > 1) {
64
- const res = await prompt({
65
- type: 'autocomplete',
66
- name: 'targetEnv',
67
- message: 'Which environment would you like to serve?',
68
- choices
69
- });
70
- targetEnv = res.targetEnv;
71
- } else {
72
- targetEnv = choices[0].value;
73
- }
74
- TARGET = targets[targetEnv];
75
- } else {
76
- TARGET = targets[TARGET];
77
- }
78
- if (!TARGET) {
79
- console.log(_zx.chalk.red('No such target exists in .firebaserc config.'));
80
- process.exit();
81
- }
82
- 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'))]);
83
- if (!aliasFileExists && !siteIdFileExists) {
84
- console.log(_zx.chalk.red(`No env file for ${TARGET.alias} exists. Check your secrets directory and try again`));
85
- process.exit();
86
- }
87
- const exec = [];
88
- if (sharedFileExists) {
89
- exec.push(_zx.fs.readFile(_zx.path.join(process.cwd(), SECRETS_DIR, 'shared.env'), {
90
- encoding: 'utf-8'
91
- }));
92
- }
93
- if (aliasFileExists) {
94
- exec.push(_zx.fs.readFile(_zx.path.join(process.cwd(), SECRETS_DIR, `${TARGET.alias}.env`), {
95
- encoding: 'utf-8'
96
- }));
97
- } else {
98
- exec.push(_zx.fs.readFile(_zx.path.join(process.cwd(), SECRETS_DIR, `${TARGET.siteId}.env`), {
99
- encoding: 'utf-8'
100
- }));
101
- }
102
- const envFileContent = (await Promise.all(exec)).join('\n');
103
- EXIT_EVENTS.forEach(event => {
104
- process.on(event, () => {
105
- if (!exited) {
106
- exited = true;
107
- _zx.fs.removeSync(_zx.path.join(process.cwd(), '.env.temp'));
108
- }
109
- });
110
- });
111
- await _zx.fs.writeFile(_zx.path.join(process.cwd(), '.env.temp'), envFileContent);
112
- try {
113
- await (0, _zx.$)`DOTENV_CONFIG_PATH=${_zx.path.join(process.cwd(), '.env.temp')} DEPLOYMENT_TARGET=${TARGET.siteId} npm run serve --colors`;
114
- } catch (err) {
115
- console.error(err);
116
- }