@leverege/build-tools 2.50.9 → 2.50.10
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 +8 -8
- package/registry-compass.yaml +20 -0
- package/src/Utils.mjs +4 -0
- package/src/bash-funcs +8 -1
- package/src/chart-compass.mjs +96 -0
- package/src/helm-charts/cnpg-db-pgbench/pooler.yaml +4 -0
- package/src/helm-charts/cnpg-db-psql-stack/cluster.yaml.ovh +21 -8
- package/src/helm-charts/cnpg-db-psql-stack/pooler.yaml +4 -0
- package/src/helm-charts/cnpg-db-tsdb-basic/cluster.yaml.ovh +3 -0
- package/src/helm-charts/cnpg-db-tsdb-basic/pooler.yaml +4 -0
- package/src/helm-charts/cnpg-db-tsdb-dense/cluster.yaml.ovh +3 -0
- package/src/helm-charts/cnpg-db-tsdb-dense/pooler.yaml +4 -0
- package/src/helmup.sh +8 -2
- package/src/zx-testing +216 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leverege/build-tools",
|
|
3
|
-
"version": "2.50.
|
|
3
|
+
"version": "2.50.10",
|
|
4
4
|
"description": "A collection of build / support tools for Leverege developers",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"author": "Leverege Devs",
|
|
55
55
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@google-cloud/artifact-registry": "^3.
|
|
57
|
+
"@google-cloud/artifact-registry": "^3.4.0",
|
|
58
58
|
"ansi-colors": "^4.1.3",
|
|
59
59
|
"chalk": "^5.3.0",
|
|
60
60
|
"command-line-args": "^5.2.1",
|
|
@@ -62,22 +62,22 @@
|
|
|
62
62
|
"deepmerge": "^4.3.1",
|
|
63
63
|
"enquirer": "^2.4.1",
|
|
64
64
|
"execa": "^8.0.1",
|
|
65
|
-
"glob": "^10.3.
|
|
65
|
+
"glob": "^10.3.16",
|
|
66
66
|
"handlebars": "^4.7.8",
|
|
67
|
-
"inquirer": "^9.2.
|
|
67
|
+
"inquirer": "^9.2.22",
|
|
68
68
|
"js-yaml": "^4.1.0",
|
|
69
69
|
"ms": "^2.1.3",
|
|
70
|
-
"npm-registry-fetch": "^17.0.
|
|
70
|
+
"npm-registry-fetch": "^17.0.1",
|
|
71
71
|
"package-up": "^5.0.0",
|
|
72
72
|
"parse-gitignore": "^2.0.0",
|
|
73
73
|
"read-pkg": "^9.0.1",
|
|
74
74
|
"readline-sync": "^1.4.10",
|
|
75
|
-
"semver": "^7.6.
|
|
75
|
+
"semver": "^7.6.2",
|
|
76
76
|
"simple-git": "^3.24.0",
|
|
77
|
-
"zx": "^8.0
|
|
77
|
+
"zx": "^8.1.0"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@leverege/eslint-config-leverege": "^4.2.0",
|
|
81
|
-
"npm": "^10.
|
|
81
|
+
"npm": "^10.8.0"
|
|
82
82
|
}
|
|
83
83
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
registry:
|
|
2
|
+
- root: us-docker.pkg.dev
|
|
3
|
+
- repositories:
|
|
4
|
+
- name: stack
|
|
5
|
+
charts:
|
|
6
|
+
- api-server
|
|
7
|
+
- authz-server
|
|
8
|
+
- emailer
|
|
9
|
+
- message-processor
|
|
10
|
+
- name: leverege
|
|
11
|
+
charts:
|
|
12
|
+
- pubsub-pulse
|
|
13
|
+
- pusher
|
|
14
|
+
- overdose
|
|
15
|
+
- name: cox-health
|
|
16
|
+
charts:
|
|
17
|
+
- actions-server
|
|
18
|
+
- analytics-server
|
|
19
|
+
- centrak-healthz
|
|
20
|
+
- centrak-ingestor
|
package/src/Utils.mjs
CHANGED
|
@@ -349,6 +349,10 @@ export const parseHelmChart = async ( helmroot = './helm' ) => {
|
|
|
349
349
|
const chartVersion = chartYaml.version
|
|
350
350
|
const chartPackage = `${chartName}-${chartVersion}.tgz`
|
|
351
351
|
const valuesYaml = YAML.load( readFileSync( `${helmroot}/values.yaml`, 'utf8' ) )
|
|
352
|
+
debug( valuesYaml, '<== helm/values.yaml' )
|
|
353
|
+
if ( !valuesYaml.image ) {
|
|
354
|
+
errorExit( 'Error: legacy helm chart detected - must upgrade to latest ignition template to proceed' )
|
|
355
|
+
}
|
|
352
356
|
const imageRegistry = valuesYaml.image?.registry
|
|
353
357
|
const registryComponents = imageRegistry.split( '/' )
|
|
354
358
|
/* eslint-enable security/detect-non-literal-fs-filename */
|
package/src/bash-funcs
CHANGED
|
@@ -169,13 +169,20 @@ function ifPluggedIn() {
|
|
|
169
169
|
local INVOKED_AS=`basename $0`
|
|
170
170
|
local PLUGIN=
|
|
171
171
|
case "$INVOKED_AS" in
|
|
172
|
-
"helmup"|"helmwhat" )
|
|
172
|
+
"helmup"|"helmwhat"|"helmcycle" )
|
|
173
173
|
PLUGIN="$1/helmup.plugin"
|
|
174
174
|
;;
|
|
175
175
|
"helmdn" )
|
|
176
176
|
PLUGIN="$1/helmdn.plugin"
|
|
177
177
|
;;
|
|
178
178
|
*)
|
|
179
|
+
cat<<PLUGIN_INVOKED_AS_ERROR
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
$RED_ERROR bash-funcs ifPluggedIn: unknown INVOKED_AS=[`color r $INVOKED_AS`]
|
|
183
|
+
|
|
184
|
+
PLUGIN_INVOKED_AS_ERROR
|
|
185
|
+
exit 1
|
|
179
186
|
;;
|
|
180
187
|
esac
|
|
181
188
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* chart-to-registry will...
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs'
|
|
6
|
+
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
import commandLineArgs from 'command-line-args'
|
|
9
|
+
import commandLineUsage from 'command-line-usage'
|
|
10
|
+
import { lt as semverLt } from 'semver'
|
|
11
|
+
import YAML from 'js-yaml'
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
condir,
|
|
15
|
+
debug,
|
|
16
|
+
errorExit,
|
|
17
|
+
log,
|
|
18
|
+
warning,
|
|
19
|
+
getGitRootDirectory,
|
|
20
|
+
parsePackageJson,
|
|
21
|
+
parseHelmChart,
|
|
22
|
+
shellCmd } from './Utils.mjs'
|
|
23
|
+
|
|
24
|
+
const commandLineOptions = [ // Use commandLineOptions to tie into the Usage statements
|
|
25
|
+
/* eslint-disable max-len */
|
|
26
|
+
{
|
|
27
|
+
name : 'location',
|
|
28
|
+
type : String,
|
|
29
|
+
description : '{green the location of the artifact registry the chart will be pushed to (default us-docker.pkg.dev)}',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name : 'project',
|
|
33
|
+
type : String,
|
|
34
|
+
description : '{green the name of the google project containing the npmrc and slack config secrets (default leverege-registry)}',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name : 'repository',
|
|
38
|
+
type : String,
|
|
39
|
+
description : '{green the target repository to receive the pushed chart}',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name : 'dry-run',
|
|
43
|
+
type : Boolean,
|
|
44
|
+
description : '{yellow perform everything except the actual chart push}',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name : 'help',
|
|
48
|
+
type : Boolean,
|
|
49
|
+
description : '{green display this help screen}',
|
|
50
|
+
},
|
|
51
|
+
/* eslint-enable max-len */
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
const sections = [
|
|
55
|
+
{
|
|
56
|
+
header : 'Leverege Helm Chart Compass (for helmup)',
|
|
57
|
+
content : `{green This tool helps helmup navigate the helm charts stored in the
|
|
58
|
+
artifact-registries.}`
|
|
59
|
+
},
|
|
60
|
+
{ header : 'Options',
|
|
61
|
+
optionList : commandLineOptions,
|
|
62
|
+
},
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
const args = commandLineArgs( commandLineOptions, { camelCase : true, partial : true } )
|
|
66
|
+
const usage = commandLineUsage( sections )
|
|
67
|
+
|
|
68
|
+
if ( args.help ) {
|
|
69
|
+
log( usage )
|
|
70
|
+
process.exit( 0 )
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* eslint-disable no-underscore-dangle */
|
|
74
|
+
if ( args._unknown ) {
|
|
75
|
+
log( usage )
|
|
76
|
+
log( `\nUnrecognized argument [${chalk.bold.red( args._unknown )}]\n` )
|
|
77
|
+
process.exit( 1 )
|
|
78
|
+
}
|
|
79
|
+
/* eslint-enable no-underscore-dangle */
|
|
80
|
+
|
|
81
|
+
const minNodejsVersion = '18.0.0'
|
|
82
|
+
if ( semverLt( process.version, minNodejsVersion ) ) {
|
|
83
|
+
errorExit( `\n***ERROR: must be running at least node ${minNodejsVersion}\n` )
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// First of all, fail if we are not in a git repository
|
|
87
|
+
let gitRoot
|
|
88
|
+
try {
|
|
89
|
+
gitRoot = await getGitRootDirectory()
|
|
90
|
+
} catch ( error ) {
|
|
91
|
+
errorExit( chalk.red.bold( error ), { errorCode : 5 } )
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const chartCompass = YAML.load( fs.readFileSync( './registry-compass.yaml', 'utf8' ) )
|
|
95
|
+
|
|
96
|
+
condir( { chartCompass }, '<==Navigation' )
|
|
@@ -12,5 +12,9 @@ spec:
|
|
|
12
12
|
pgbouncer:
|
|
13
13
|
poolMode: session
|
|
14
14
|
parameters:
|
|
15
|
+
# https://www.pgbouncer.org/config.html#generic-settings
|
|
15
16
|
max_client_conn: "500"
|
|
16
17
|
default_pool_size: "10"
|
|
18
|
+
# https://www.pgbouncer.org/config.html#log-settings
|
|
19
|
+
log_connections: "0"
|
|
20
|
+
log_disconnections: "0"
|
|
@@ -13,24 +13,35 @@ spec:
|
|
|
13
13
|
imageName: us-docker.pkg.dev/leverege-registry/system/images/leverege-pgsql:16.2-cnpg-tsdb2-beta.4
|
|
14
14
|
bootstrap:
|
|
15
15
|
initdb:
|
|
16
|
-
|
|
16
|
+
# database: models
|
|
17
17
|
postInitSQL:
|
|
18
|
+
# - CREATE DATABASE app; # operator expects this
|
|
19
|
+
# - CREATE ROLE app; # and this to exist on restore
|
|
20
|
+
# create the stack databases
|
|
18
21
|
- CREATE DATABASE authz;
|
|
19
22
|
- CREATE DATABASE fota;
|
|
20
23
|
- CREATE DATABASE geotile;
|
|
21
24
|
- CREATE DATABASE models;
|
|
22
25
|
- CREATE DATABASE scheduler;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
# owner: imagine
|
|
27
|
+
# secret:
|
|
28
|
+
# name: cnpg-db-psql-stack-imagine-pw # imagine db pw
|
|
26
29
|
|
|
27
30
|
enableSuperuserAccess: true
|
|
28
31
|
superuserSecret:
|
|
29
32
|
name: cnpg-db-psql-stack-postgres-pw # postgres db pw
|
|
30
33
|
|
|
31
34
|
affinity:
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
enablePodAntiAffinity: true
|
|
36
|
+
topologyKey: kubernetes.io/hostname # default value
|
|
37
|
+
nodeAffinity:
|
|
38
|
+
requiredDuringSchedulingIgnoredDuringExecution:
|
|
39
|
+
nodeSelectorTerms:
|
|
40
|
+
- matchExpressions:
|
|
41
|
+
- key: target-env
|
|
42
|
+
operator: In
|
|
43
|
+
values:
|
|
44
|
+
- database
|
|
34
45
|
tolerations:
|
|
35
46
|
- key: "database"
|
|
36
47
|
operator: "Equal"
|
|
@@ -83,6 +94,7 @@ spec:
|
|
|
83
94
|
cnpgCluster: cnpg-db-psql-stack
|
|
84
95
|
wal:
|
|
85
96
|
compression: gzip
|
|
97
|
+
maxParallel: 4
|
|
86
98
|
---
|
|
87
99
|
apiVersion: postgresql.cnpg.io/v1
|
|
88
100
|
kind: ScheduledBackup
|
|
@@ -91,8 +103,9 @@ metadata:
|
|
|
91
103
|
namespace: cnpg-operands
|
|
92
104
|
spec:
|
|
93
105
|
# https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format
|
|
94
|
-
schedule: "0 0
|
|
106
|
+
schedule: "0 0 */12 * * *"
|
|
95
107
|
cluster:
|
|
96
|
-
|
|
108
|
+
name: cnpg-db-psql-stack
|
|
109
|
+
method: barmanObjectStore # volumeSnapshot
|
|
97
110
|
backupOwnerReference: self
|
|
98
111
|
immediate: true
|
|
@@ -12,5 +12,9 @@ spec:
|
|
|
12
12
|
pgbouncer:
|
|
13
13
|
poolMode: session
|
|
14
14
|
parameters:
|
|
15
|
+
# https://www.pgbouncer.org/config.html#generic-settings
|
|
15
16
|
max_client_conn: "500"
|
|
16
17
|
default_pool_size: "10"
|
|
18
|
+
# https://www.pgbouncer.org/config.html#log-settings
|
|
19
|
+
log_connections: "0"
|
|
20
|
+
log_disconnections: "0"
|
|
@@ -19,6 +19,8 @@ spec:
|
|
|
19
19
|
postInitTemplateSQL:
|
|
20
20
|
- CREATE EXTENSION timescaledb;
|
|
21
21
|
- CREATE EXTENSION jsquery;
|
|
22
|
+
- CREATE DATABASE app; # for the operator restores
|
|
23
|
+
- CREATE ROLE app; # same
|
|
22
24
|
database: imagine
|
|
23
25
|
owner: imagine
|
|
24
26
|
secret:
|
|
@@ -94,6 +96,7 @@ spec:
|
|
|
94
96
|
cnpgCluster: cnpg-db-tsdb-basic
|
|
95
97
|
wal:
|
|
96
98
|
compression: gzip
|
|
99
|
+
maxParallel: 4
|
|
97
100
|
---
|
|
98
101
|
apiVersion: postgresql.cnpg.io/v1
|
|
99
102
|
kind: ScheduledBackup
|
|
@@ -12,5 +12,9 @@ spec:
|
|
|
12
12
|
pgbouncer:
|
|
13
13
|
poolMode: session
|
|
14
14
|
parameters:
|
|
15
|
+
# https://www.pgbouncer.org/config.html#generic-settings
|
|
15
16
|
max_client_conn: "500"
|
|
16
17
|
default_pool_size: "10"
|
|
18
|
+
# https://www.pgbouncer.org/config.html#log-settings
|
|
19
|
+
log_connections: "0"
|
|
20
|
+
log_disconnections: "0"
|
|
@@ -19,6 +19,8 @@ spec:
|
|
|
19
19
|
postInitTemplateSQL:
|
|
20
20
|
- CREATE EXTENSION timescaledb;
|
|
21
21
|
- CREATE EXTENSION jsquery;
|
|
22
|
+
- CREATE DATABASE app; # for the operator restores
|
|
23
|
+
- CREATE ROLE app; # same
|
|
22
24
|
database: imagine
|
|
23
25
|
owner: imagine
|
|
24
26
|
secret:
|
|
@@ -93,6 +95,7 @@ spec:
|
|
|
93
95
|
cnpgCluster: cnpg-db-tsdb-dense
|
|
94
96
|
wal:
|
|
95
97
|
compression: gzip
|
|
98
|
+
maxParallel: 4
|
|
96
99
|
---
|
|
97
100
|
apiVersion: postgresql.cnpg.io/v1
|
|
98
101
|
kind: ScheduledBackup
|
|
@@ -12,5 +12,9 @@ spec:
|
|
|
12
12
|
pgbouncer:
|
|
13
13
|
poolMode: session
|
|
14
14
|
parameters:
|
|
15
|
+
# https://www.pgbouncer.org/config.html#generic-settings
|
|
15
16
|
max_client_conn: "500"
|
|
16
17
|
default_pool_size: "10"
|
|
18
|
+
# https://www.pgbouncer.org/config.html#log-settings
|
|
19
|
+
log_connections: "0"
|
|
20
|
+
log_disconnections: "0"
|
package/src/helmup.sh
CHANGED
|
@@ -794,7 +794,13 @@ doHelmup() {
|
|
|
794
794
|
[ ! -z "$FROM_VERSIONS" ] && from="`color y \"<= from Versions.json\"`"
|
|
795
795
|
printf "$doing $from\n"
|
|
796
796
|
eval $helmup
|
|
797
|
-
|
|
797
|
+
case "$INVOKED_AS" in
|
|
798
|
+
"helmup"|"helmdn")
|
|
799
|
+
sendToSlack "$helmup"
|
|
800
|
+
;;
|
|
801
|
+
*)
|
|
802
|
+
;;
|
|
803
|
+
esac
|
|
798
804
|
}
|
|
799
805
|
|
|
800
806
|
# not a fan of global vars but bash forced me to - set by getServiceAndChartVersion
|
|
@@ -814,7 +820,7 @@ function doInstall() {
|
|
|
814
820
|
# slap additional helm syntax in there if the version is set
|
|
815
821
|
[ ! -z "$CHARTVER" ] && CHARTVER="--version $CHARTVER"
|
|
816
822
|
|
|
817
|
-
|
|
823
|
+
INVOKED_AS=`basename $0`
|
|
818
824
|
# set the dry-run flag on what
|
|
819
825
|
[ "$INVOKED_AS" == "helmwhat" ] && HELM_WHAT="--dry-run --debug" && K8S_WHAT="--dry-run=server -o yaml"
|
|
820
826
|
|
package/src/zx-testing
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
// https://github.com/google/zx
|
|
4
|
+
/* eslint-disable max-len */
|
|
5
|
+
import pkg from 'enquirer'
|
|
6
|
+
import colors from 'ansi-colors'
|
|
7
|
+
import { $, fs, argv, path, chalk } from 'zx'
|
|
8
|
+
|
|
9
|
+
const { prompt } = pkg
|
|
10
|
+
|
|
11
|
+
// This will preserve coloration from spawned child processes
|
|
12
|
+
process.env.FORCE_COLORS = 3
|
|
13
|
+
process.env.FORCE_COLOR = '1'
|
|
14
|
+
|
|
15
|
+
let TARGET
|
|
16
|
+
let CHANNEL
|
|
17
|
+
|
|
18
|
+
// A single unnamed parameter is assumed to be a deploy target.
|
|
19
|
+
// If named parameters exist, they may not be stored in argv,
|
|
20
|
+
// so don't look for a larger length of argv to decide whether to look for named paramters
|
|
21
|
+
if ( argv._.length === 1 ) {
|
|
22
|
+
TARGET = argv._[0]
|
|
23
|
+
} else {
|
|
24
|
+
TARGET = argv.target
|
|
25
|
+
|
|
26
|
+
if ( argv.channelName ) {
|
|
27
|
+
CHANNEL = {}
|
|
28
|
+
CHANNEL.name = argv.channelName
|
|
29
|
+
CHANNEL.expiration = argv.channelExpiration ?? '7d'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// If target has not been defined on the command line,
|
|
34
|
+
// then enter interactive mode
|
|
35
|
+
const INTERACTIVE_MODE = !TARGET
|
|
36
|
+
|
|
37
|
+
let exited = false
|
|
38
|
+
|
|
39
|
+
const SECRETS_DIR = 'secrets'
|
|
40
|
+
const EXIT_EVENTS = [ 'SIGINT', 'exit', 'uncaughException', 'unhandledRejection' ]
|
|
41
|
+
|
|
42
|
+
const [
|
|
43
|
+
firebaserc,
|
|
44
|
+
firebaseJson,
|
|
45
|
+
gitBranch
|
|
46
|
+
] = await Promise.all( [
|
|
47
|
+
fs.exists( path.join( process.cwd(), '.firebaserc' ) ),
|
|
48
|
+
fs.exists( path.join( process.cwd(), 'firebase.json' ) ),
|
|
49
|
+
$`git rev-parse --abbrev-ref HEAD -C ${process.cwd()}`
|
|
50
|
+
.then( ( { stdout } ) => {
|
|
51
|
+
return stdout.split( '\n' )[0]
|
|
52
|
+
} )
|
|
53
|
+
] )
|
|
54
|
+
|
|
55
|
+
if ( !firebaserc ) {
|
|
56
|
+
console.log( chalk.red( 'No .firebaserc file found. Make sure that you are executing firebaseDeploy from within a firebase project directory.' ) )
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if ( !firebaseJson ) {
|
|
60
|
+
console.log( chalk.red( 'No firebase.json file found. Make sure that you are executing firebaseDeploy from within a firebase project directory.' ) )
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if ( !firebaserc || !firebaseJson ) {
|
|
64
|
+
process.exit()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const firebasercContent = JSON.parse( await fs.readFile( path.join( process.cwd(), '.firebaserc' ), { encoding : 'utf-8' } ) )
|
|
68
|
+
|
|
69
|
+
let maxSiteIdLength = 0
|
|
70
|
+
|
|
71
|
+
const targets = Object
|
|
72
|
+
.entries( firebasercContent.targets )
|
|
73
|
+
.reduce( ( prev, [ projectId, config ] ) => {
|
|
74
|
+
Object
|
|
75
|
+
.entries( config.hosting )
|
|
76
|
+
.forEach( ( [ siteId, aliases ] ) => {
|
|
77
|
+
aliases.forEach( ( alias ) => {
|
|
78
|
+
// Store the longest length of site id for formatting later on
|
|
79
|
+
maxSiteIdLength = Math.max( siteId.length, maxSiteIdLength )
|
|
80
|
+
|
|
81
|
+
// eslint-disable-next-line no-param-reassign
|
|
82
|
+
prev[alias] = {
|
|
83
|
+
projectId,
|
|
84
|
+
siteId,
|
|
85
|
+
alias,
|
|
86
|
+
production : config.production
|
|
87
|
+
}
|
|
88
|
+
} )
|
|
89
|
+
} )
|
|
90
|
+
|
|
91
|
+
return prev
|
|
92
|
+
}, {} )
|
|
93
|
+
|
|
94
|
+
if ( INTERACTIVE_MODE ) {
|
|
95
|
+
const { targetEnv } = await prompt( [ {
|
|
96
|
+
type : 'autocomplete',
|
|
97
|
+
name : 'targetEnv',
|
|
98
|
+
message : 'To which environment would you like to deploy?',
|
|
99
|
+
choices : Object.values( targets ).map( ( t ) => {
|
|
100
|
+
const productionLabel = t.production ? colors.bold.red( '--PRODUCTION--' ) : ''
|
|
101
|
+
return { message : `${colors.bold.cyan( 'Site: ' )}${t.siteId.padEnd( maxSiteIdLength )} ${colors.bold.green( 'Project: ' )}${t.projectId} ${productionLabel}`, value : t.alias }
|
|
102
|
+
} )
|
|
103
|
+
} ] )
|
|
104
|
+
|
|
105
|
+
TARGET = targets[targetEnv]
|
|
106
|
+
|
|
107
|
+
if ( TARGET.production ) {
|
|
108
|
+
const { confirmProductionDeploy } = await prompt( [ {
|
|
109
|
+
type : 'confirm',
|
|
110
|
+
name : 'confirmProductionDeploy',
|
|
111
|
+
message : `${colors.bold.red( 'WARNING: ' )} This is a ${colors.bold.yellow( 'PRODUCTION' )} environment. Are you sure you want to deploy here?`
|
|
112
|
+
} ] )
|
|
113
|
+
if ( !confirmProductionDeploy ) {
|
|
114
|
+
console.log( 'Cancelling deployment...' )
|
|
115
|
+
process.exit()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const { useChannel } = await prompt( [ {
|
|
120
|
+
type : 'confirm',
|
|
121
|
+
name : 'useChannel',
|
|
122
|
+
message : 'Do you want to deploy to a temporary preview channel?'
|
|
123
|
+
} ] )
|
|
124
|
+
|
|
125
|
+
if ( useChannel ) {
|
|
126
|
+
const { channelName, channelExpiration } = await prompt( [ {
|
|
127
|
+
type : 'input',
|
|
128
|
+
name : 'channelName',
|
|
129
|
+
message : 'To which channel would you like to deploy?',
|
|
130
|
+
initial : gitBranch ?? 'temp-deploy'
|
|
131
|
+
}, {
|
|
132
|
+
type : 'input',
|
|
133
|
+
name : 'channelExpiration',
|
|
134
|
+
message : 'When should the channel expire?',
|
|
135
|
+
initial : '7d'
|
|
136
|
+
} ] )
|
|
137
|
+
|
|
138
|
+
CHANNEL = { name : channelName, expiration : channelExpiration }
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
TARGET = targets[TARGET]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if ( !TARGET ) {
|
|
145
|
+
console.log( chalk.red( 'No such target exists in .firebaserc config.' ) )
|
|
146
|
+
process.exit()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const [ aliasFileExists, siteIdFileExists, sharedFileExists ] = await Promise.all( [
|
|
150
|
+
fs.exists( path.join( process.cwd(), SECRETS_DIR, `${TARGET.alias}.env` ) ),
|
|
151
|
+
fs.exists( path.join( process.cwd(), SECRETS_DIR, `${TARGET.siteId}.env` ) ),
|
|
152
|
+
fs.exists( path.join( process.cwd(), SECRETS_DIR, 'shared.env' ) )
|
|
153
|
+
] )
|
|
154
|
+
|
|
155
|
+
if ( !aliasFileExists && !siteIdFileExists ) {
|
|
156
|
+
console.log( chalk.red( `No env file for ${TARGET.alias} exists. Check your secrets directory and try again` ) )
|
|
157
|
+
process.exit()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const exec = []
|
|
161
|
+
|
|
162
|
+
if ( sharedFileExists ) {
|
|
163
|
+
exec.push( fs.readFile( path.join( process.cwd(), SECRETS_DIR, 'shared.env' ), { encoding : 'utf-8' } ) )
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if ( aliasFileExists ) {
|
|
167
|
+
exec.push( fs.readFile( path.join( process.cwd(), SECRETS_DIR, `${TARGET.alias}.env` ), { encoding : 'utf-8' } ) )
|
|
168
|
+
} else {
|
|
169
|
+
exec.push( fs.readFile( path.join( process.cwd(), SECRETS_DIR, `${TARGET.siteId}.env` ), { encoding : 'utf-8' } ) )
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log( `\n\n\n${colors.bold.green( '====== Deployment Summary ======' )}` )
|
|
173
|
+
console.log( `${colors.blue( 'Firebase project:' )} ${TARGET.projectId} ${TARGET.production ? colors.yellow( '--PRODUCTION--' ) : ''}` )
|
|
174
|
+
console.log( `${colors.blue( 'Site ID:' )} ${TARGET.siteId}` )
|
|
175
|
+
console.log( `${colors.blue( 'Channel Name:' )} ${CHANNEL?.name ?? colors.italic.gray( '(none)' )}` )
|
|
176
|
+
console.log( `${colors.blue( 'Channel Expiration date:' )} ${CHANNEL?.name ? CHANNEL.expiration ?? colors.italic.gray( '(none)' ) : colors.italic.gray( '(n/a)' )}` )
|
|
177
|
+
console.log( `${colors.blue( 'Shared .env file:' )} ${sharedFileExists ? `${SECRETS_DIR}/shared.env` : colors.italic.gray( '(none)' )}` )
|
|
178
|
+
console.log( `${colors.blue( 'Additional .env overrides:' )} ${aliasFileExists ? `${SECRETS_DIR}/${TARGET.alias}.env` : `${SECRETS_DIR}/${TARGET.siteId}.env`}` )
|
|
179
|
+
console.log( `${colors.bold.green( '===============================' )}` )
|
|
180
|
+
|
|
181
|
+
if ( INTERACTIVE_MODE ) {
|
|
182
|
+
const { confirmSelections } = await prompt( [ {
|
|
183
|
+
type : 'confirm',
|
|
184
|
+
name : 'confirmSelections',
|
|
185
|
+
message : 'Proceed with this deployment?'
|
|
186
|
+
} ] )
|
|
187
|
+
|
|
188
|
+
if ( !confirmSelections ) {
|
|
189
|
+
console.log( 'Cancelling deployment...' )
|
|
190
|
+
process.exit()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const envFileContent = ( await Promise.all( exec ) ).join( '\n' )
|
|
194
|
+
|
|
195
|
+
EXIT_EVENTS
|
|
196
|
+
.forEach( ( event ) => {
|
|
197
|
+
process.on( event, () => {
|
|
198
|
+
if ( !exited ) {
|
|
199
|
+
exited = true
|
|
200
|
+
fs.removeSync( path.join( process.cwd(), '.env.temp' ) )
|
|
201
|
+
}
|
|
202
|
+
} )
|
|
203
|
+
} )
|
|
204
|
+
|
|
205
|
+
await fs.writeFile( path.join( process.cwd(), '.env.temp' ), envFileContent )
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
|
|
209
|
+
// deploying to a channel requires slightly different parameter structure
|
|
210
|
+
const deployType = CHANNEL ? `hosting:channel:deploy ${CHANNEL.name} ${CHANNEL.expiration ? `--expires ${CHANNEL.expiration}` : ''}`.split( ' ' ) : 'deploy'
|
|
211
|
+
const hostingPrefix = CHANNEL ? '' : 'hosting:'
|
|
212
|
+
|
|
213
|
+
await $`npm run clean && DOTENV_CONFIG_PATH=${path.join( process.cwd(), '.env.temp' )} DEPLOYMENT_TARGET=${TARGET.siteId} npm run build && firebase use ${TARGET.projectId} && firebase ${deployType} --only ${hostingPrefix}${TARGET.siteId}`
|
|
214
|
+
} catch ( err ) {
|
|
215
|
+
console.error( err )
|
|
216
|
+
}
|