@leverege/build-tools 2.57.4 → 2.58.0-pedro.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leverege/build-tools",
3
- "version": "2.57.4",
3
+ "version": "2.58.0-pedro.2",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -48,6 +48,7 @@
48
48
  "pkglint": "src/pkglint.sh",
49
49
  "prune-git": "src/prune-git.sh",
50
50
  "pull-git": "src/pull-git.sh",
51
+ "push-model-to-registry": "src/push-model-to-registry.mjs",
51
52
  "push-my-chart": "src/push-my-chart.mjs",
52
53
  "refresh-npm-token": "src/refresh-npm-token.mjs",
53
54
  "refresh-py-idx": "src/refresh-py-idx.sh",
@@ -59,6 +60,7 @@
59
60
  "license": "SEE LICENSE IN LICENSE.md",
60
61
  "dependencies": {
61
62
  "@google-cloud/artifact-registry": "^3.5.0",
63
+ "@google-cloud/storage": "^7.14.0",
62
64
  "ansi-colors": "^4.1.3",
63
65
  "chalk": "^5.3.0",
64
66
  "command-line-args": "^6.0.1",
@@ -66,6 +68,7 @@
66
68
  "deepmerge": "^4.3.1",
67
69
  "enquirer": "^2.4.1",
68
70
  "execa": "^9.5.1",
71
+ "fast-crc32c": "^2.0.0",
69
72
  "glob": "^11.0.0",
70
73
  "handlebars": "^4.7.8",
71
74
  "inquirer": "^12.1.0",
@@ -78,10 +81,10 @@
78
81
  "readline-sync": "^1.4.10",
79
82
  "semver": "^7.6.3",
80
83
  "simple-git": "^3.27.0",
81
- "zx": "^8.2.2"
84
+ "zx": "^8.2.4"
82
85
  },
83
86
  "devDependencies": {
84
87
  "@leverege/eslint-config-leverege": "^5.0.1",
85
- "npm": "^10.9.0"
88
+ "npm": "^10.9.1"
86
89
  }
87
- }
90
+ }
package/src/Docker.mjs CHANGED
@@ -23,6 +23,7 @@ const dockerfileTemplate = `
23
23
  # The FROM directive sets the Base Image for subsequent instructions
24
24
  FROM node:{{nodeimage}} as intermediate
25
25
  ENV NODE_ENV production
26
+ ENV NPM_CONFIG_USERCONFIG /usr/src/app/.npmrc
26
27
 
27
28
  RUN mkdir -p /usr/src/app
28
29
  WORKDIR /usr/src/app
@@ -36,7 +37,7 @@ ENV GRPC_VERBOSITY ERROR
36
37
  # --------------------------------------------------------------
37
38
 
38
39
  # Install packages to install private repos with ssh keys
39
- COPY ./.npmrc /usr/src/app/.npmrc
40
+ COPY ./.npmrc \${NPM_CONFIG_USERCONFIG}
40
41
  RUN apk --no-cache add openssh-client && \
41
42
  apk --update add --no-cache --virtual build-dep g++ gcc libgcc \\
42
43
  libstdc++ linux-headers make {{apkadds}} && \
@@ -44,16 +45,22 @@ RUN apk --no-cache add openssh-client && \
44
45
 
45
46
  {{preInstallPluginfile}}
46
47
 
47
- # Removed --ignore-scripts from npm ci because it was causing issues with python native build
48
+ # Use SKIP_PREPARE to disable hook-and-release from running
48
49
  ENV SKIP_PREPARE=true
50
+
51
+ # Do the clean install and remove the npm token and ssh keys from the image
49
52
  RUN npm ci --only=production --no-optional {{npmlogging}} && \
50
- rm -f /usr/src/app/.npmrc /root/.ssh/*
53
+ rm -f \${NPM_CONFIG_USERCONFIG} /root/.ssh/*
51
54
 
52
55
  # --------------------------------------------------------------
53
- # On to the real build now, the thing before was just an intermediate container
56
+ # Docker Final Stage
54
57
  # --------------------------------------------------------------
55
58
 
56
59
  FROM node:{{nodeimage}}
60
+ ENV NPM_CONFIG_USERCONFIG /usr/src/app/.npmrc
61
+
62
+ # Copy .npmrc for private registry access for access to any @leverege packages
63
+ COPY ./.npmrc \${NPM_CONFIG_USERCONFIG}
57
64
 
58
65
  # Install tini for PID 1 and replace shell with bash so we can source files
59
66
  RUN apk update && \
@@ -63,8 +70,12 @@ RUN apk update && \
63
70
  mkdir -p /usr/src/app /tmp/levlog && \
64
71
  chown node:node /usr/src/app
65
72
 
73
+ # Pull in the Dockerfile.plugin file here
66
74
  {{pluginfile}}
67
75
 
76
+ # Eliminate the sensitive info from the final stage image
77
+ RUN rm -f \${NPM_CONFIG_USERCONFIG}
78
+
68
79
  ENTRYPOINT [ "/sbin/tini", "--" ]
69
80
  WORKDIR /usr/src/app
70
81
 
package/src/circleate.sh CHANGED
@@ -11,6 +11,7 @@ fi
11
11
 
12
12
  repo_type=$1
13
13
  CIRCLE_NODE_IMG="cimg/node:lts"
14
+ DOC_CREATOR_ENDPOINT="https://doc-creator.leverege.com/buildDocs/$(basename $npm_package_name)"
14
15
 
15
16
  check_circle_config(){
16
17
  if [ -d "./.circleci" ];
@@ -131,6 +132,9 @@ jobs:
131
132
  - store_artifacts:
132
133
  path: docs
133
134
  prefix: docs
135
+ - run:
136
+ name: Initiate document creation
137
+ command: curl -X GET "$DOC_CREATOR_ENDPOINT"
134
138
 
135
139
  coverage:
136
140
  # coverage runs tests, generates coverage, and saves docs
@@ -347,6 +351,9 @@ jobs:
347
351
  - store_artifacts:
348
352
  path: docs
349
353
  prefix: docs
354
+ - run:
355
+ name: Initiate document creation
356
+ command: curl -X GET "$DOC_CREATOR_ENDPOINT"
350
357
 
351
358
  coverage:
352
359
  # coverage runs tests, generates coverage, and saves docs
@@ -4,7 +4,7 @@ showInstalling "The Prometheus Operator and Components"
4
4
  addHelmRepo prometheus-community https://prometheus-community.github.io/helm-charts
5
5
 
6
6
  showInstalling "The Prometheus Operator (kube-prometheus-stack)"
7
- [ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="65"
7
+ [ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="66"
8
8
 
9
9
  NS="--namespace prometheus"
10
10
 
@@ -33,6 +33,8 @@ defaultRules:
33
33
 
34
34
  prometheus:
35
35
  prometheusSpec:
36
+ # image:
37
+ # tag: "v3.0.0"
36
38
  podMonitorSelectorNilUsesHelmValues: false
37
39
  ruleSelectorNilUsesHelmValues: false
38
40
  serviceMonitorSelectorNilUsesHelmValues: false
@@ -55,6 +57,11 @@ grafana:
55
57
  # -- the grafana admin password
56
58
  adminPassword: leverege-monitoring
57
59
  defaultDashboardsEnabled: false
60
+ persistence:
61
+ enabled: true
62
+ size: 10Gi
63
+ accessModes:
64
+ - ReadWriteOnce
58
65
  sidecar:
59
66
  dashboards:
60
67
  enabled: true
@@ -68,7 +75,7 @@ grafana:
68
75
  entryPoints:
69
76
  - websecure
70
77
  routes:
71
- - match: Host(`OVH:<PROJECT_NAME>-promop.OVH:<HOST>.com`)
78
+ - match: Host(`${PROJECT_NAME}-promop.${HOST}.com`)
72
79
  kind: Rule
73
80
  services:
74
81
  - name: prometheus-stack-grafana
@@ -98,10 +105,10 @@ alertmanager:
98
105
  receivers:
99
106
  - name: slack
100
107
  slack_configs:
101
- - channel: "OVH:<SLACK_CHANNEL>"
102
- api_url: "OVH:<SLACK_HOOK_URL>"
103
- username: "OVH:<PROJECT_NAME>"
104
- fallback: "OVH:<PROJECT_NAME> - {{ .CommonAnnotations.summary }}"
108
+ - channel: "${SLACK_CHANNEL}"
109
+ api_url: "${SLACK_HOOK_URL}"
110
+ username: "${PROJECT_NAME}"
111
+ fallback: "${PROJECT_NAME} - {{ .CommonAnnotations.summary }}"
105
112
  title: "{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}"
106
113
  title_link: "https://bitbucket.org/leverege/sre-playbook/src/master/"
107
114
  send_resolved: true
@@ -114,9 +121,9 @@ alertmanager:
114
121
  - name: slack-watchdog
115
122
  slack_configs:
116
123
  - channel: "#dead-mans-snitch_notifications"
117
- api_url: "OVH:<SLACK_HOOK_URL>"
118
- username: "OVH:<PROJECT_NAME>"
119
- fallback: "OVH:<PROJECT_NAME> - {{ .CommonAnnotations.summary }}"
124
+ api_url: "${SLACK_HOOK_URL}"
125
+ username: "${PROJECT_NAME}"
126
+ fallback: "${PROJECT_NAME} - {{ .CommonAnnotations.summary }}"
120
127
  title: "Watchdog Alert - Dead Man's Snitch"
121
128
  # text: "This is a periodic test alert to confirm the alert pipeline is functional."
122
129
  text: |-
@@ -127,7 +134,7 @@ alertmanager:
127
134
  {{ end }}
128
135
  - name: opsgenie
129
136
  opsgenie_configs:
130
- - api_key: "OVH:<OPSGENIE_APIKEY>"
137
+ - api_key: "${OPSGENIE_APIKEY}"
131
138
  priority: "{{ .CommonLabels.priority }}"
132
139
  tags: "{{ range .Alerts }}{{ .Labels.severity }},{{ end }}"
133
140
  description: |-
@@ -5,7 +5,7 @@
5
5
  fullnameOverride: "prometheus-stack-stackdriver-metrics"
6
6
 
7
7
  stackdriver:
8
- projectId: "OVH:<PROJECT_NAME>"
8
+ projectId: "${PROJECT_NAME}"
9
9
  metrics:
10
10
  typePrefixes: "pubsub.googleapis.com/subscription/oldest_unacked_message_age,pubsub.googleapis.com/subscription/num_undelivered_messages,pubsub.googleapis.com/subscription/pull_ack_request_count,pubsub.googleapis.com/subscription/streaming_pull_ack_request_count,pubsub.googleapis.com/topic/message_sizes,pubsub.googleapis.com/topic/send_request_count,firebasedatabase.googleapis.com/io/database_load,firebasedatabase.googleapis.com/network/sent_bytes_count,firebasedatabase.googleapis.com/network/active_connections"
11
11
 
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ VERSION="v0.78.1"
4
+ CRDOPTS="--force-conflicts --server-side"
5
+
6
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_alertmanagerconfigs.yaml
7
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_alertmanagers.yaml
8
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_podmonitors.yaml
9
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_probes.yaml
10
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_prometheusagents.yaml
11
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_prometheuses.yaml
12
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_prometheusrules.yaml
13
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_scrapeconfigs.yaml
14
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_servicemonitors.yaml
15
+ kubectl apply $CRDOPTS -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/$VERSION/example/prometheus-operator-crd/monitoring.coreos.com_thanosrulers.yaml
@@ -4,7 +4,7 @@ showInstalling "Redis"
4
4
 
5
5
  OCI_CHART="oci://registry-1.docker.io/bitnamicharts/redis"
6
6
 
7
- [ -z "$REDIS_CHART_VERSION" ] && REDIS_CHART_VERSION="20.2.1"
7
+ [ -z "$REDIS_CHART_VERSION" ] && REDIS_CHART_VERSION="20.3.0"
8
8
 
9
9
  helm upgrade --install redis $OCI_CHART \
10
10
  --values redis/redis-local.yaml \
package/src/overwhelm.mjs CHANGED
@@ -15,20 +15,29 @@ import parse from 'parse-gitignore'
15
15
  import ask from 'readline-sync'
16
16
  import YAML from 'js-yaml'
17
17
 
18
- import { debug, disableDebug, err, errorExit, isString, log, shellCmd } from './Utils.mjs'
18
+ import {
19
+ debug,
20
+ disableDebug,
21
+ err,
22
+ errorExit,
23
+ isString,
24
+ log,
25
+ shellCmd,
26
+ } from './Utils.mjs'
19
27
 
20
28
  const optionsDefinitions = [
21
29
  /* eslint-disable no-multi-spaces */
22
- { name : 'context', alias : 'x', type : String, default : '' },
23
- { name : 'section', alias : 's', type : String, default : 'default' },
24
- { name : 'key', alias : 'k', type : String },
25
- { name : 'export', alias : 'e', type : Boolean, default : false },
26
- { name : 'continue', alias : 'c', type : Boolean, default : false },
27
- { name : 'checkctx', alias : 't', type : Boolean, default : false }, // checks overwhelm context
28
- { name : 'fetchctx', alias : 'f', type : Boolean, default : false }, // fetch and set k8s context
29
- { name : 'genvals', alias : 'g', type : Boolean, default : false }, // only generates values.yaml
30
- { name : 'matchk8s', alias : 'm', type : Boolean, default : false }, // checks k8s context
31
- { name : 'verbose', alias : 'v', type : Boolean, default : false },
30
+ { name : 'context', alias : 'x', type : String, default : '' },
31
+ { name : 'deprecated', alias : 'd', type : Boolean, default : false },
32
+ { name : 'section', alias : 's', type : String, default : 'default' },
33
+ { name : 'key', alias : 'k', type : String },
34
+ { name : 'export', alias : 'e', type : Boolean, default : false },
35
+ { name : 'continue', alias : 'c', type : Boolean, default : false },
36
+ { name : 'checkctx', alias : 't', type : Boolean, default : false }, // checks overwhelm context
37
+ { name : 'fetchctx', alias : 'f', type : Boolean, default : false }, // fetch and set k8s context
38
+ { name : 'genvals', alias : 'g', type : Boolean, default : false }, // only generates values.yaml
39
+ { name : 'matchk8s', alias : 'm', type : Boolean, default : false }, // checks k8s context
40
+ { name : 'verbose', alias : 'v', type : Boolean, default : false },
32
41
  /* eslint-enable no-multi-spaces */
33
42
  ]
34
43
  const args = cliArgs( optionsDefinitions )
@@ -36,6 +45,12 @@ const args = cliArgs( optionsDefinitions )
36
45
  // when using --key we only want the key contents w/o debug info
37
46
  if ( args.key ) { disableDebug() }
38
47
 
48
+ // define a deprecation warning function that depends on arg.deprecated
49
+ const deprecated = ( deprecation ) => {
50
+ if ( !args.deprecated ) { return }
51
+ log( `${chalk.yellow.bold( '***DEPRECATED:' )} ${deprecation}\n` )
52
+ }
53
+
39
54
  // See if we are in a monorepo, which changes some of the rules.
40
55
  const gitTop = await shellCmd( 'git rev-parse --show-toplevel' )
41
56
 
@@ -180,7 +195,7 @@ const yams = glob.sync( 'values-*.yaml' )
180
195
 
181
196
  // A function to apply the replacements to all of the matching yaml which
182
197
  // will error out if it runs into an unknown OVH<tag>.
183
- const doReplacements = ( yamls, replacements ) => {
198
+ const doReplacements = ( yamls, replacements, fileName = 'default-config.yaml' ) => {
184
199
  let replYaml = yamls
185
200
 
186
201
  // wrap special chars with quotes if needed, like "#---incident-alerts---"
@@ -188,10 +203,18 @@ const doReplacements = ( yamls, replacements ) => {
188
203
  return /[#$?*]/g.test( raw ) ? `"${raw}"` : raw
189
204
  }
190
205
 
191
- Object.keys( replacements ).forEach( ( repl ) => {
192
- const replaceThis = new RegExp( `OVH:<${repl}>`, 'g' ) // eslint-disable-line security/detect-non-literal-regexp
193
- const withThat = quoteSpecials( `${replacements[repl]}` )
194
- replYaml = replYaml.replace( replaceThis, withThat )
206
+ // process both ${VARIABLE} and deprecate OVH:<VARIABLES> styles
207
+ Object.keys( replacements ).forEach( ( key ) => {
208
+ // replace ${VARIABLE} syntax
209
+ const replaceVariable = new RegExp( `\\$\\{${key}\\}`, 'g' ) // eslint-disable-line security/detect-non-literal-regexp
210
+ replYaml = replYaml.replace( replaceVariable, quoteSpecials( `${replacements[key]}` ) )
211
+
212
+ // replace OVH:<VARIABLE> syntax and warn about deprecation
213
+ const replaceDeprecated = new RegExp( `OVH:<${key}>`, 'g' ) // eslint-disable-line security/detect-non-literal-regexp
214
+ if ( replYaml.match( replaceDeprecated ) ) {
215
+ deprecated( `OVH:<${key}> is deprecated. Please replace it with \${${key}} in ${fileName}` )
216
+ replYaml = replYaml.replace( replaceDeprecated, quoteSpecials( `${replacements[key]}` ) )
217
+ }
195
218
  } )
196
219
 
197
220
  // Verify all replaceables have been handled, or error out.
@@ -296,7 +319,6 @@ ahoy.forEach( ( dir ) => {
296
319
  const valuesLocal = `${dir}/values-local.yaml`
297
320
 
298
321
  if ( validValuesLocalConfig( valuesLocal ) ) {
299
- debug( { valuesLocal }, 'rendering values.yaml' )
300
322
  valuesOut += '\n# OVERWHELM => values-local.yaml\n\n'
301
323
  valuesOut += fs.readFileSync( valuesLocal, { encoding : 'utf-8' } )
302
324
  }
@@ -308,28 +330,17 @@ ahoy.forEach( ( dir ) => {
308
330
  const valuesAll = YAML.load( valuesOut ) || {}
309
331
  const valuesSvc = {
310
332
  service : {
311
- project_id : 'OVH:<PROJECT_ID>',
312
- project_name : 'OVH:<PROJECT_NAME>',
313
- host : 'OVH:<HOST>',
314
- ingress : 'OVH:<INGRESS>',
333
+ /* eslint-disable no-template-curly-in-string */
334
+ project_id : '${PROJECT_ID}',
335
+ project_name : '${PROJECT_NAME}',
336
+ host : '${HOST}',
337
+ ingress : '${INGRESS}',
338
+ /* eslint-enable no-template-curly-in-string */
315
339
  }
316
340
  }
317
341
  const valuesMerged = YAML.dump( deepmerge( valuesSvc, valuesAll ), { quotingType : '"' } )
318
342
 
319
- valuesOut = doReplacements( valuesMerged, over )
320
-
321
- // DEPRECATED once siren and manheim are platform 4.0
322
- // If we are going to override then we need to convert it to YAML and
323
- // do a deepmerge to combine the values.
324
- const valuesOverride = `${dir}/values-override.yaml`
325
-
326
- if ( fs.existsSync( valuesOverride ) ) {
327
- log( `===>Applying Overrides=> ${valuesOverride}` )
328
- const overrides = YAML.load( fs.readFileSync( valuesOverride, { encofind : 'utf-8' } ) )
329
- const allValues = YAML.load( valuesOut )
330
- valuesOut = YAML.dump( deepmerge( allValues, overrides ), { quotingType : '"' } )
331
- }
332
- // DEPRECATED
343
+ valuesOut = doReplacements( valuesMerged, over, valuesLocal )
333
344
 
334
345
  fs.writeFileSync( `${dir}/values.yaml`, valuesOut )
335
346
  }
@@ -338,8 +349,9 @@ ahoy.forEach( ( dir ) => {
338
349
  // we can also apply replaceables to all .ovh files in the project
339
350
  glob.sync( '**/*.ovh' ).forEach( ( cfgmap ) => {
340
351
  const target = cfgmap.replace( /.ovh$/, '' )
352
+ deprecated( `rename deprecated ${cfgmap} OVH file to ${target}` )
341
353
  let valuesOut = fs.readFileSync( cfgmap, { encoding : 'utf-8' } )
342
- valuesOut = doReplacements( valuesOut, over )
354
+ valuesOut = doReplacements( valuesOut, over, cfgmap )
343
355
  fs.writeFileSync( target, valuesOut )
344
356
  } )
345
357
 
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import chalk from 'chalk';
6
+ import inquirer from 'inquirer';
7
+ import {Storage} from '@google-cloud/storage';
8
+ import crc32c from 'fast-crc32c';
9
+
10
+ const storage = new Storage();
11
+
12
+ function getAllFiles(dirPath, arrayOfFiles = []) {
13
+ const files = fs.readdirSync(dirPath);
14
+
15
+ files.forEach(file => {
16
+ const filePath = path.join(dirPath, file);
17
+ if (fs.lstatSync(filePath).isDirectory()) {
18
+ arrayOfFiles = getAllFiles(filePath, arrayOfFiles);
19
+ } else {
20
+ arrayOfFiles.push(filePath);
21
+ }
22
+ });
23
+
24
+ return arrayOfFiles;
25
+ }
26
+
27
+ async function main() {
28
+ // Get git branch name
29
+ const branchName = fs.readFileSync('.git/HEAD', 'utf8').split('/').pop().trim();
30
+
31
+ const bucketName = `${branchName}-triton-model-registry`;
32
+
33
+ // Get all blobs in the bucket
34
+ let files = [];
35
+ try {
36
+ [files] = await storage.bucket(bucketName).getFiles();
37
+ } catch (error) {
38
+ if (error.code === 404) {
39
+ console.error(`Bucket ${bucketName} does not exist.`);
40
+ return;
41
+ } else {
42
+ throw error;
43
+ }
44
+ }
45
+
46
+ // Get all not hidden blobs in the current path in the filesystem
47
+ const localFiles = getAllFiles('./models').filter(file => !path.basename(file).startsWith('.'));
48
+
49
+ const filesToDelete = files.filter(file => !localFiles.find(localFile => localFile === file.name));
50
+
51
+ console.log(`${chalk.underline(filesToDelete.length + " file(s)")} in ${chalk.underline(bucketName)} without a ${chalk.underline("local")} counterpart`);
52
+ filesToDelete.forEach(file => console.log(chalk.red(file.name)));
53
+
54
+ if (filesToDelete.length > 0) console.log("");
55
+
56
+ const filesToAdd = [];
57
+ const filesToReplace = [];
58
+
59
+ const metadataPromises = localFiles.map(async file => {
60
+ const localFileChecksum = Buffer.from(crc32c.calculate(fs.readFileSync(file)).toString(16), 'hex').toString('base64');
61
+ const remoteFile = files.find(blob => blob.name === file);
62
+ if (remoteFile) {
63
+ const [metadata] = await remoteFile.getMetadata();
64
+ return { file, localFileChecksum, remoteFileChecksum: metadata.crc32c };
65
+ } else {
66
+ return { file, localFileChecksum, remoteFileChecksum: null };
67
+ }
68
+ });
69
+
70
+ const metadataResults = await Promise.all(metadataPromises);
71
+
72
+ metadataResults.forEach(({ file, localFileChecksum, remoteFileChecksum }) => {
73
+ if (remoteFileChecksum) {
74
+ if (localFileChecksum !== remoteFileChecksum) {
75
+ filesToReplace.push(file);
76
+ }
77
+ } else {
78
+ filesToAdd.push(file);
79
+ }
80
+ });
81
+
82
+ console.log(`${chalk.underline(filesToAdd.length + " file(s)")} to be added to ${chalk.underline(bucketName)}`);
83
+ filesToAdd.forEach(file => console.log(chalk.green(file)));
84
+
85
+ if (filesToAdd.length > 0) console.log("");
86
+
87
+ console.log(`${chalk.underline(filesToReplace.length + " file(s)")} to be replaced in ${chalk.underline(bucketName)}`);
88
+ filesToReplace.forEach(file => console.log(chalk.yellow(file)));
89
+
90
+ if (filesToReplace.length > 0) console.log("");
91
+
92
+ if (filesToDelete.length > 0) {
93
+ const {confirm} = await inquirer.prompt({
94
+ type: 'confirm',
95
+ name: 'confirm',
96
+ message: `Do you want to delete ${filesToDelete.length} file(s) from ${bucketName}?`,
97
+ });
98
+
99
+ if (!confirm) {
100
+ return;
101
+ }
102
+
103
+ // Remove all blobs that are not in the local filesystem
104
+ const deletePromises = filesToDelete.map(async file => {
105
+ console.log(`Deleting ${file.name} from ${bucketName}`);
106
+ return storage.bucket(bucketName).file(file.name).delete();
107
+ });
108
+
109
+ await Promise.all(deletePromises);
110
+ console.log("")
111
+ }
112
+
113
+ if (filesToAdd.length > 0 || filesToReplace.length > 0) {
114
+ const {confirm} = await inquirer.prompt({
115
+ type: 'confirm',
116
+ name: 'confirm',
117
+ message: `Do you want to upload ${filesToAdd.length + filesToReplace.length} file(s) to ${bucketName}?`,
118
+ });
119
+
120
+ if (!confirm) {
121
+ return;
122
+ }
123
+
124
+ // Upload all new files
125
+ const uploadPromises = filesToAdd.map(async file => {
126
+ console.log(`Uploading ${file} to ${bucketName}`);
127
+ return storage.bucket(bucketName).upload(file, {
128
+ destination: file,
129
+ });
130
+ });
131
+
132
+ // Replace all files with different checksums
133
+ const replacePromises = filesToReplace.map(async file => {
134
+ console.log(`Replacing ${file} in ${bucketName}`);
135
+ return storage.bucket(bucketName).upload(file, {
136
+ destination: file,
137
+ });
138
+ });
139
+
140
+ await Promise.all([...uploadPromises, ...replacePromises]);
141
+ console.log("")
142
+ }
143
+ }
144
+
145
+ main().catch(console.error);