@leverege/build-tools 2.101.0 → 2.101.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.101.0",
3
+ "version": "2.101.2",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -48,7 +48,7 @@
48
48
  "helmup": "src/helmup.sh",
49
49
  "helmwhat": "src/helmup.sh",
50
50
  "hook-and-release": "src/hook-and-release.mjs",
51
- "init-my-chart": "src/init-my-chart.mjs",
51
+ "init-my-chart": "src/init-my-chart/init-my-chart.mjs",
52
52
  "k8scale": "src/k8scale.sh",
53
53
  "k8x": "src/k8x.sh",
54
54
  "klog": "src/klog.sh",
@@ -94,12 +94,12 @@
94
94
  "googleapis": "^171.4.0",
95
95
  "handlebars": "^4.7.9",
96
96
  "ignore": "^7.0.5",
97
- "inquirer": "^13.4.1",
97
+ "inquirer": "^13.4.2",
98
98
  "js-yaml": "^4.1.1",
99
99
  "jsdoc": "^4.0.5",
100
100
  "ms": "^2.1.3",
101
101
  "npm-registry-fetch": "^19.1.1",
102
- "ora": "^9.3.0",
102
+ "ora": "^9.4.0",
103
103
  "p-limit": "^7.3.0",
104
104
  "package-up": "^5.0.0",
105
105
  "readline-sync": "^1.4.10",
@@ -117,6 +117,6 @@
117
117
  "@leverege/eslint-config-leverege": "^5.1.2",
118
118
  "chai": "^6.2.2",
119
119
  "mocha": "^11.7.5",
120
- "npm": "^11.12.1"
120
+ "npm": "^11.13.0"
121
121
  }
122
122
  }
package/src/Utils.mjs CHANGED
@@ -560,6 +560,11 @@ export const parseHelmChart = async ( helmroot = './helm' ) => {
560
560
  } )
561
561
 
562
562
  depsOutOfSync = mismatches.length > 0
563
+
564
+ // One final check - if the helm/charts dir is missing then we also need to update dependencies
565
+ if ( hasDependencies && !chartFiles.includes( 'charts' ) ) {
566
+ depsOutOfSync = true
567
+ }
563
568
  }
564
569
  /* eslint-enable security/detect-non-literal-fs-filename */
565
570
 
package/src/bash-funcs CHANGED
@@ -229,26 +229,31 @@ function waitForIAM() {
229
229
  done
230
230
  }
231
231
 
232
- # *** DEPRECATED *** REMOVE AFTER BITNAMI ERADICATED ***
233
- # *** DEPRECATED *** REMOVE AFTER BITNAMI ERADICATED ***
234
- # *** DEPRECATED *** REMOVE AFTER BITNAMI ERADICATED ***
235
- #
236
- function addBitnamiRepo() {
237
- warning '*** the addBitnamiRepo function should be removed - DEPRECATED ***'
238
- if [ ! -z "$BITNAMI_LATEST" ] || [ "$1" == "latest" ];
239
- then
240
- BITNAMI_URL="https://charts.bitnami.com/bitnami"
241
- else
242
- cat<<CATNAMI
243
- Using the pre 2022 legacy charts from bitnami. To use latest run:
244
-
245
- `color y "BITNAMI_LATEST=1 $HELMUP_ARGS"`
246
-
247
- CATNAMI
248
- BITNAMI_URL="https://raw.githubusercontent.com/bitnami/charts/eb5f9a9513d987b519f0ecd732e7031241c50328/bitnami"
249
- fi
232
+ # waitForSAIAM SVC_ACCT PROJECT ROLE MEMBER [TIMEOUT_SECONDS]
233
+ # Like waitForIAM but checks the SA's own IAM policy (for bindings like workloadIdentityUser
234
+ # that are set via `gcloud iam service-accounts add-iam-policy-binding`, not project-level).
235
+ function waitForSAIAM() {
236
+ local svcAcct="$1" project="$2" role="$3" member="$4" timeout="${5:-120}"
237
+ local svcAcctEmail="${svcAcct}@${project}.iam.gserviceaccount.com"
238
+ local start=$(date +%s) delay=2
250
239
 
251
- addHelmRepo bitnami $BITNAMI_URL
240
+ echo "⏳ waiting for SA IAM binding ${role} → ${member} on ${svcAcctEmail} (timeout ${timeout}s)..." >&2
241
+ while true; do
242
+ local found
243
+ found="$(gcloud iam service-accounts get-iam-policy "${svcAcctEmail}" \
244
+ --flatten="bindings[].members" \
245
+ --filter="bindings.role=${role} AND bindings.members=${member}" \
246
+ --format="value(bindings.role)" 2> "$DEVNULL")"
247
+ if [[ -n "$found" ]]; then
248
+ echo "✅ SA IAM binding active: ${role} → ${member}" >&2
249
+ return 0
250
+ fi
251
+ if (( $(date +%s) - start >= timeout )); then
252
+ echo "❌ timed out waiting for SA IAM binding ${role} → ${member}" >&2
253
+ return 1
254
+ fi
255
+ sleep "$delay"; (( delay = delay < 20 ? delay * 2 : 20 ))
256
+ done
252
257
  }
253
258
 
254
259
  function removeHelmRepo(){
@@ -26,34 +26,33 @@ function installCnpgOperatorEnvironment() {
26
26
  #
27
27
  # kubectl delete serviceaccounts -n $CNPG_NAMESPACE $CNPG_SVC_ACCT &> $DEVNULL
28
28
  # gcloud --quiet iam service-accounts delete $CNPG_SVC_EMAIL --project $GCP_PROJECT_ID&> $DEVNULL
29
- kubectl describe serviceaccounts -n $CNPG_NAMESPACE $CNPG_SVC_ACCT &> $DEVNULL
29
+
30
+ createNamespaceIfNeeded $CNPG_NAMESPACE
31
+
32
+ # Guard against accidental SA deletion on re-runs. Check the GCP SA (not the
33
+ # k8s SA) — consistent with ESO bootstrap pattern and correct for new clusters
34
+ # where the GCP SA exists but the k8s SA does not yet.
35
+ gcloud iam service-accounts describe $CNPG_SVC_EMAIL --project $GCP_PROJECT_ID &> $DEVNULL
30
36
  if [ $? -eq 0 ];
31
37
  then
32
38
  cat<<SKIP_CNPG_SA_CREATION
33
39
 
34
- $YELO_WARN The CNPG SA $CNPG_SVC_ACCT already exists
40
+ $YELO_WARN The CNPG SA `color y $CNPG_SVC_ACCT` already exists - skipping creation
35
41
 
36
- Deleting the SA for the purposes of upgrading the operator is going to
37
- wreck someone's day (probably yours) if the desire is to simply upgrade
38
- in place. To force a clean reinstall, delete both the k8s and gcp SA for
39
- CNPG and try again. (see comments in helmup for more info)
42
+ To force a clean reinstall, delete both the GCP and k8s SAs for CNPG and
43
+ re-run helmup cnpg-operator.
40
44
 
41
45
  SKIP_CNPG_SA_CREATION
42
- return
46
+ else
47
+ printf "\nCreating the gcloud `color g $CNPG_SVC_ACCT` service account (SA)\n"
48
+ gcloud iam service-accounts create $CNPG_SVC_ACCT \
49
+ --project "$GCP_PROJECT_ID" \
50
+ --description "CNPG Operands SA" \
51
+ --display-name "CNPG Operands SA" &> $DEVNULL
52
+ exitOnError $? "Failed to create GCP SA `color y $CNPG_SVC_ACCT`"
53
+ waitForSA "$CNPG_SVC_ACCT" "$GCP_PROJECT_ID" 60
43
54
  fi
44
55
 
45
- # Create the operands namespace and annotate to allow redis access
46
- createNamespaceIfNeeded $CNPG_NAMESPACE
47
-
48
- # Create GCP SA and bind policies
49
- printf "\nCreating the gcloud `color g $CNPG_SVC_ACCT` service account (SA)\n"
50
- gcloud iam service-accounts create $CNPG_SVC_ACCT \
51
- --project "$GCP_PROJECT_ID" \
52
- --description "CNPG Operands SA" \
53
- --display-name "CNPG Operands SA" &> $DEVNULL
54
- warnOnError $? "the GCP SA `color y $CNPG_SVC_ACCT` may already exist"
55
- waitForSA "$CNPG_SVC_ACCT" "$GCP_PROJECT_ID" 60
56
-
57
56
  CNPG_OPS_ROLE="cnpg.operands"
58
57
  printf "\nCreating the `color g $CNPG_OPS_ROLE` IAM role\n"
59
58
  ## Attach roles
@@ -136,7 +135,6 @@ driver: pd.csi.storage.gke.io
136
135
  deletionPolicy: Retain
137
136
  EOSNAPSC
138
137
 
139
- sleep 2 # hold up processing for a moment to allow IAM mods to propagate
140
138
  }
141
139
 
142
140
  # this function is a little convoluted in the way it attempts to install the
@@ -184,7 +182,7 @@ CNPG_FETCH_CLEANUP_CRONJOB_YAML
184
182
  --project="$GCP_PROJECT_ID" \
185
183
  --description="Used for cronjob cleanup with Workload Identity" \
186
184
  --display-name="CNPG Cleanup SA" &> $DEVNULL
187
- warnOnError $? "the GCP SA $CNPG_CLEANUP_SA may already exist"
185
+ warnOnError $? "the GCP SA $CNPG_CLEANUP_SA may already exist"
188
186
  waitForSA "$CNPG_CLEANUP_SA" "$GCP_PROJECT_ID" 60
189
187
 
190
188
  gcloud projects add-iam-policy-binding "$GCP_PROJECT_ID" \
@@ -11,6 +11,8 @@ function installEsoEnvironment() {
11
11
  ESO_SA="eso-sa"
12
12
  ESO_SA_EMAIL="$ESO_SA@$GCP_PROJECT_ID.iam.gserviceaccount.com"
13
13
 
14
+ createNamespaceIfNeeded $ESO_NAMESPACE
15
+
14
16
  # Guard against accidental SA deletion on re-runs (see cnpg-operator for war story)
15
17
  gcloud iam service-accounts describe $ESO_SA_EMAIL --project $GCP_PROJECT_ID &> $DEVNULL
16
18
  if [ $? -eq 0 ];
@@ -50,8 +52,8 @@ SKIP_ESO_SA_CREATION
50
52
  --member "serviceAccount:$GCP_PROJECT_ID.svc.id.goog[$ESO_NAMESPACE/external-secrets]" \
51
53
  --role roles/iam.workloadIdentityUser $ESO_SA_EMAIL
52
54
  warnOnError $? "gcloud workload identity binding may have failed"
53
-
54
- createNamespaceIfNeeded $ESO_NAMESPACE
55
+ waitForSAIAM "$ESO_SA" "$GCP_PROJECT_ID" "roles/iam.workloadIdentityUser" \
56
+ "serviceAccount:$GCP_PROJECT_ID.svc.id.goog[$ESO_NAMESPACE/external-secrets]" 60
55
57
  }
56
58
 
57
59
  installEsoEnvironment
@@ -0,0 +1,105 @@
1
+ # CRD Upgrades — kube-prometheus-stack
2
+
3
+ ## Why this exists
4
+
5
+ Helm can install CustomResourceDefinitions (CRDs) but will not upgrade them
6
+ when you bump chart versions. This chart ships a CRD upgrade job that uses
7
+ server-side apply (SSA) to reconcile CRDs to the versions bundled with the
8
+ chart.
9
+
10
+ ## Default: disabled
11
+
12
+ The CRD upgrade job is disabled by default (`crds.enabled: false`) because it
13
+ requires cluster-admin level permissions to run. Specifically, it creates and
14
+ deletes ClusterRole and ClusterRoleBinding resources as pre-upgrade Helm hooks,
15
+ which requires `container.clusterRoles.delete` in GCP IAM. Only enable it when
16
+ explicitly bumping the chart version, and only a devops engineer should run
17
+ `helmup prom-operator` when it is enabled.
18
+
19
+ ## Settings
20
+
21
+ **`crds.enabled`** — controls whether the chart manages CRDs at all. When
22
+ `false`, the CRD upgrade job and its associated hook resources are not rendered.
23
+ Default: `false`.
24
+
25
+ **`crds.upgradeJob.enabled`** — controls whether the SSA upgrade job hook runs.
26
+ Must be `true` for CRDs to be reconciled on upgrade. Default: `false`.
27
+
28
+ **`crds.upgradeJob.forceConflicts`** — adds `--force-conflicts` to the SSA
29
+ apply, overriding fields owned by another field manager. Use only for a one-time
30
+ conflict resolution. Revert to `false` immediately after. Default: `false`.
31
+
32
+ ## Permissions requirement
33
+
34
+ The upgrade job runs as a pair of Helm pre-upgrade hooks that create a
35
+ ClusterRole and ClusterRoleBinding. Helm's `before-hook-creation` delete policy
36
+ causes it to attempt deletion of these resources before creating them — even if
37
+ they don't yet exist in the cluster. The GCP IAM permission check fires before
38
+ the existence check, so a 403 is returned for users without
39
+ `container.clusterRoles.delete` regardless of whether the resources are present.
40
+
41
+ The `admission-webhooks/job-patch` hooks have the same requirement and are
42
+ always present, making `helmup prom-operator` a full-devops-only operation
43
+ regardless of the CRD upgrade setting.
44
+
45
+ ## Hook lifecycle
46
+
47
+ The ClusterRole and ClusterRoleBinding created by the upgrade job are ephemeral.
48
+ They are deleted automatically after the job succeeds via the
49
+ `hook-delete-policy: hook-succeeded` annotation. You will not see them in
50
+ `kubectl get clusterrole` after a successful run.
51
+
52
+ ## Switching from enabled to disabled
53
+
54
+ If `crds.enabled` was previously `true` and a successful upgrade ran, the CRD
55
+ hook resources are recorded in the Helm release manifest even though they no
56
+ longer exist in the cluster. Switching to `false` and running `helmup` will
57
+ cause Helm to attempt to delete those orphaned hook resources from the previous
58
+ release — again requiring cluster-admin permissions for that one transition run.
59
+
60
+ To verify what hooks are currently stored in the release:
61
+ ```bash
62
+ helm get hooks prometheus-stack -n prometheus | grep ^"# Source"
63
+ ```
64
+
65
+ After a devops engineer runs `helmup prom-operator` with `crds.enabled: false`,
66
+ the hooks will be cleared from the release manifest and subsequent runs will not
67
+ require cluster-admin permissions (beyond the always-present admission-webhook
68
+ hooks).
69
+
70
+ ## Runbook — bumping the chart version
71
+
72
+ 1. **Preflight** — confirm current CRD state:
73
+ ```bash
74
+ kubectl get crd | grep monitoring.coreos.com
75
+ kubectl apply --server-side --dry-run=server -f <crd-dir/>
76
+ ```
77
+
78
+ 2. **Enable** — set both flags in `prometheus-stack.yaml.ovh`:
79
+ ```yaml
80
+ crds:
81
+ enabled: true
82
+ upgradeJob:
83
+ enabled: true
84
+ forceConflicts: false
85
+ ```
86
+
87
+ 3. **Run** — as a devops engineer:
88
+ ```bash
89
+ helmup prom-operator
90
+ ```
91
+
92
+ 4. **If the job fails with field-manager conflicts:**
93
+ - Confirm no other source manages these CRDs
94
+ - Temporarily set `forceConflicts: true` for this run only
95
+ - Revert to `false` immediately after
96
+
97
+ 5. **Post-check:**
98
+ ```bash
99
+ kubectl get crd | grep monitoring.coreos.com
100
+ # verify CRDs show Established: True
101
+ # verify operator pod is healthy
102
+ # verify alerts and rules are normal
103
+ ```
104
+
105
+ 6. **Disable** — revert both flags to `false` before committing.
@@ -17,11 +17,14 @@ function installStackdriverExporterEnvironment() {
17
17
 
18
18
  createNamespaceIfNeeded $SDEXP_NS
19
19
 
20
- # guarantee any previous remnants of the exporter are gone
21
- printf "\n*** Removing the previous $SDEXP_SA installation...\n"
22
- helm uninstall -n $SDEXP_NS "stackdriver-exporter" &> $DEVNULL
23
- kubectl delete serviceaccounts -n $SDEXP_NS $SDEXP_SA &> $DEVNULL
24
- printf "*** Deleting the gcloud SA $SDEXP_EM\n"
20
+ # used to remove this to avoid weird behavior when attempting to reboot
21
+ # the service with udated configs - no longer doing this (for now)
22
+ #
23
+ # guarantee any previous remnants of the exporter are gone
24
+ # printf "\n*** Removing the previous $SDEXP_SA installation...\n"
25
+ # helm uninstall -n $SDEXP_NS "stackdriver-exporter" &> $DEVNULL
26
+ # kubectl delete serviceaccounts -n $SDEXP_NS $SDEXP_SA &> $DEVNULL
27
+ # printf "*** Deleting the gcloud SA $SDEXP_EM\n"
25
28
  # gcloud --quiet iam service-accounts delete $SDEXP_EM \
26
29
  # --project $GCP_PROJECT_ID &> $DEVNULL
27
30
 
@@ -58,18 +61,19 @@ function createGrafanaOAuthSecret() {
58
61
  printf "\nCreating placeholder `color g grafana-google-oauth` secret in prometheus namespace\n"
59
62
  kubectl create secret generic grafana-google-oauth \
60
63
  --namespace prometheus \
61
- --from-literal=GF_AUTH_GOOGLE_CLIENT_ID='' \
62
- --from-literal=GF_AUTH_GOOGLE_CLIENT_SECRET=''
64
+ --from-literal=GF_AUTH_GOOGLE_CLIENT_ID='bootstrap id' \
65
+ --from-literal=GF_AUTH_GOOGLE_CLIENT_SECRET='boostrap secret'
63
66
  warnOnError $? "Failed to create grafana-google-oauth placeholder secret"
64
67
  else
65
68
  printf "\n`color g grafana-google-oauth` secret already exists - skipping\n"
66
69
  fi
67
70
 
68
- # Wire up the ExternalSecret now that ESO is guaranteed to be deployed first
69
- # in the SYSTEM array. This syncs credentials from GCP Secret Manager into the
70
- # grafana-google-oauth secret once the deployment engineer populates them.
71
- showInstalling "Grafana Google OAuth ExternalSecret"
72
- cat<<GRAFANA_OAUTH_SECRET | kubectl apply -f -
71
+ # Wire up the ExternalSecret only if ESO is enabled for this cluster.
72
+ # Set ESO_ENABLED=true in overwhelm.yaml to activate (default: false).
73
+ if [ "${ESO_ENABLED}" = "true" ];
74
+ then
75
+ showInstalling "Grafana Google OAuth ExternalSecret"
76
+ cat<<GRAFANA_OAUTH_SECRET | kubectl apply -f -
73
77
  apiVersion: external-secrets.io/v1
74
78
  kind: ExternalSecret
75
79
  metadata:
@@ -86,11 +90,15 @@ spec:
86
90
  data:
87
91
  - secretKey: GF_AUTH_GOOGLE_CLIENT_ID
88
92
  remoteRef:
89
- key: grafana-google-client-id
93
+ key: GF_AUTH_GOOGLE_CLIENT_ID
90
94
  - secretKey: GF_AUTH_GOOGLE_CLIENT_SECRET
91
95
  remoteRef:
92
- key: grafana-google-client-secret
96
+ key: GF_AUTH_GOOGLE_CLIENT_SECRET
93
97
  GRAFANA_OAUTH_SECRET
98
+ else
99
+ printf "\n`color y '***WARNING:'` ESO_ENABLED is not set - skipping Grafana OAuth ExternalSecret\n"
100
+ printf " Set ESO_ENABLED=true in overwhelm.yaml and re-run helmup prom-operator to wire up the OAuth secret.\n\n"
101
+ fi
94
102
  }
95
103
 
96
104
  installStackdriverExporterEnvironment
@@ -8,7 +8,7 @@ showInstalling "The Prometheus Operator (kube-prometheus-stack)"
8
8
 
9
9
  # https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack
10
10
  OCI_CHART="oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack"
11
- [ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="82.15.0"
11
+ [ -z "$PROMETHEUS_STACK_CHART_VERSION" ] && PROMETHEUS_STACK_CHART_VERSION="83.7.0"
12
12
  helm upgrade $NS --install prometheus-stack $OCI_CHART \
13
13
  --values prom-operator/prometheus-stack.yaml \
14
14
  --version $PROMETHEUS_STACK_CHART_VERSION $HELM_WHAT
@@ -1,50 +1,13 @@
1
+ #
2
+ # Apparently you must have devops access in order to deploy the prometheus
3
+ # operator since it attempts to create cluster roles during deployment.
4
+ #
1
5
  crds:
2
- # ─────────────────────────────────────────────────────────────────────────────
3
- # CRD MANAGEMENT — kube-prometheus-stack
4
- #
5
- # Why this exists
6
- # Helm can install CustomResourceDefinitions (CRDs) but will not upgrade them
7
- # when you bump chart versions. This chart ships a CRD upgrade job that uses
8
- # server-side apply (SSA) to reconcile CRDs to the versions bundled with the
9
- # chart.
10
- #
11
- # Safe operating model
12
- # ✅ Leave the upgrade job ENABLED if this chart is the sole owner of the
13
- # Prometheus Operator CRDs.
14
- # ⚠️ Keep "forceConflicts" DISABLED unless you hit a specific field-manager
15
- # conflict that you’ve reviewed and want this chart to own.
16
- # ❌ If another source manages CRDs (e.g., a separate CRD chart, GitOps
17
- # bootstrap, platform repo), DISABLE the job to avoid ownership fights.
18
- #
19
- # Settings
20
- # enabled: runs the SSA job on chart upgrade to reconcile CRDs.
21
- # Recommended: true if chart owns the CRDs; false if
22
- # another tool owns them.
23
- #
24
- # forceConflicts: adds `--force-conflicts` to SSA, overriding fields owned by
25
- # another controller. Use only for a one-time conflict
26
- # resolution. Can overwrite annotations and mask real
27
- # incompatibilities if left true.
28
- #
29
- # Recommended defaults
30
- # enabled: true
31
- # forceConflicts: false
32
- #
33
- # Mini-runbook
34
- # 1) Preflight:
35
- # kubectl get crd | grep monitoring.coreos.com
36
- # kubectl apply --server-side --dry-run=server -f <crd-dir/>
37
- # 2) Upgrade with enabled: true, forceConflicts: false.
38
- # 3) If job fails with conflicts:
39
- # - confirm no other source manages these CRDs
40
- # - temporarily set forceConflicts: true for this upgrade only
41
- # - revert to false afterwards
42
- # 4) Post-check: CRDs Established, operator healthy, alerts/rules normal
43
- # ─────────────────────────────────────────────────────────────────────────────
6
+ # See README-CRD-UPGRADES.md — disabled by default, full-devops only
44
7
  enabled: true
45
8
  upgradeJob:
46
9
  enabled: true
47
- forceConflicts: false # See README-CRD-UPGRADES.md
10
+ forceConflicts: false
48
11
 
49
12
  kubeControllerManager:
50
13
  enabled: false
@@ -186,7 +149,7 @@ grafana:
186
149
  envFromSecret: grafana-google-oauth
187
150
  env:
188
151
  GF_SERVER_ROOT_URL: https://${PROJECT_NAME}-monitoring.${HOST}.com
189
- GF_AUTH_GOOGLE_ENABLED: "true"
152
+ GF_AUTH_GOOGLE_ENABLED: "${ESO_ENABLED}"
190
153
  GF_AUTH_GOOGLE_SCOPES: "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"
191
154
  GF_AUTH_GOOGLE_AUTH_URL: "https://accounts.google.com/o/oauth2/auth"
192
155
  GF_AUTH_GOOGLE_TOKEN_URL: "https://accounts.google.com/o/oauth2/token"
@@ -284,7 +247,7 @@ alertmanager:
284
247
  {{- end }}
285
248
 
286
249
  {{- if .Labels.pod }}
287
- *Pod Link:* *<https://console.cloud.google.com/kubernetes/pod/${GCE_REGION}/${CLUSTER_NAME}/{{ .Labels.namespace }}/{{ .Labels.pod }}?project=${PROJECT_NAME}|`{{ .Labels.pod }}`>*
250
+ *Pod Link:* *<https://console.cloud.google.com/kubernetes/pod/${GCE_REGION}/${CLUSTER_NAME}/{{ .Labels.namespace }}/{{ .Labels.pod }}?project=${PROJECT_ID}|`{{ .Labels.pod }}`>*
288
251
  {{- end }}
289
252
 
290
253
  *Status:* `{{ .Status }}` | *Severity:* `{{ .Labels.severity }}` {{- if .Labels.container }} *Container:* `{{ .Labels.container }}` {{- end }}
@@ -5,7 +5,7 @@
5
5
  fullnameOverride: "prometheus-stack-stackdriver-metrics"
6
6
 
7
7
  stackdriver:
8
- projectId: "${PROJECT_NAME}"
8
+ projectId: "${PROJECT_ID}"
9
9
  metrics:
10
10
  typePrefixes: >
11
11
  agent.googleapis.com/cpu/utilization,
@@ -86,6 +86,8 @@ function installVeleroEnvironment() {
86
86
  --role roles/iam.workloadIdentityUser \
87
87
  $SERVICE_ACCOUNT_EMAIL
88
88
  warnOnError $? "gcloud workload identity binding may have failed"
89
+ waitForSAIAM "velero" "$GCP_PROJECT_ID" "roles/iam.workloadIdentityUser" \
90
+ "serviceAccount:$GCP_PROJECT_ID.svc.id.goog[velero/velero]" 60
89
91
 
90
92
  printf "\nCreate and annotate the k8s SA\n"
91
93
  kubectl create serviceaccount velero --namespace velero
package/src/helmup.sh CHANGED
@@ -135,7 +135,7 @@ SYSTEM=(
135
135
  cnpg-operator
136
136
  preemptible-killer
137
137
  zombie-killer
138
- reflector
138
+ # reflector
139
139
  reloader
140
140
  velero
141
141
  cnpg-db-psql-stack
@@ -218,8 +218,8 @@ WORKLOAD_ID
218
218
  --member "serviceAccount:$GCP_PROJECT_ID.svc.id.goog[${NAMESPACE}/${BOUND_SVC_ACCT}]" \
219
219
  --role roles/iam.workloadIdentityUser $SVC_ACCT_EMAIL
220
220
  warnOnError $? "gcloud workload identity binding may have failed"
221
-
222
- sleep 2 # give the policy a chance to propagate
221
+ waitForSAIAM "$SVC_ACCT" "$GCP_PROJECT_ID" "roles/iam.workloadIdentityUser" \
222
+ "serviceAccount:$GCP_PROJECT_ID.svc.id.goog[${NAMESPACE}/${BOUND_SVC_ACCT}]" 60
223
223
 
224
224
  # Verify the Kubernetes SA and create it if necessary
225
225
  printf "\nChecking for the `color g $NAMESPACE/$SVC_ACCT` k8s SA\n"
@@ -821,6 +821,10 @@ export GCP_PROJECT_ID="$(getProjectIdFromK8sContext)"
821
821
  # Same for the region, but not as prevalent
822
822
  export GCE_REGION=`overwhelm -k GCE_REGION`
823
823
 
824
+ # TODO: Temporary, until ESO is widely deployed, then the ESO_ENABLED logic
825
+ # can be removed from the configs entirely.
826
+ export ESO_ENABLED=`overwhelm -k ESO_ENABLED`
827
+
824
828
  doInstall "${SERVICES[@]}"
825
829
 
826
830
  displayBanner
@@ -17,7 +17,7 @@ const hbsContext = {
17
17
  hbsPartOf : '',
18
18
  hbsServicePreemptible : true,
19
19
  hbsRedisEnabled : true,
20
- hbsAutoScalingEnabled : true
20
+ hbsAutoscalingEnabled : true,
21
21
  }
22
22
 
23
23
  const PRESERVE_SECTIONS = new Set( [
@@ -0,0 +1 @@
1
+ {{ include "leverege.ingressroute" . }}
@@ -0,0 +1,13 @@
1
+ {
2
+ "type": "managed",
3
+ "name": "NPMRC",
4
+ "source": "leverege-registry:DEPRECATED_NPMRC",
5
+ "labels": {
6
+ "env-type": "string"
7
+ },
8
+ "annotations": {
9
+ "description": "a deprecated npmrc key",
10
+ "owner": "platform-team",
11
+ "source": "service-man"
12
+ }
13
+ }
package/src/DockerPy.mjs DELETED
@@ -1,266 +0,0 @@
1
- /**
2
- * DEPRECATED - DO NOT MODIFY
3
- */
4
- import fs from 'node:fs'
5
- import path from 'node:path'
6
- import url from 'node:url'
7
-
8
- import chalk from 'chalk'
9
- import handlebars from 'handlebars'
10
-
11
- import {
12
- errorExit,
13
- getCleanTag,
14
- getDateTimestamp,
15
- getGcpAccount,
16
- log,
17
- proceed,
18
- shellCmd,
19
- warning
20
- } from './Utils.mjs'
21
-
22
- const DIRNAME = path.dirname( url.fileURLToPath( import.meta.url ) )
23
-
24
- // The contents of these variables were initially located in files that live in
25
- // the build-tools repository, but it just became simpler to pull the contents
26
- // directly into these variables and forego the file loading.
27
- //
28
- const pluginTemplate = `
29
- # Dockerfile.plugin - optionally extend the service Docker image
30
- #
31
- # This file may be used to run additional docker commands during the image
32
- # build process without needing to maintain a local custom Dockerfile. For
33
- # example, uncommenting the following docker RUN command will cause the
34
- # curl and vim packages to be added to the deployed image thus making them
35
- # available from the pod's command line on k8s:
36
- #
37
- # RUN apk update && apk add --no-cache curl vim
38
- #
39
- # Keep in mind that the Alpine Linux base image is used to keep image
40
- # footprints small, so adding packages "just because" is not considered
41
- # a best practice.
42
- `
43
- const dockerfileTemplate = fs.readFileSync( path.join( DIRNAME, './templates/dockerfileTemplatePy.hbs' ), 'utf8' )
44
- const cloudBuildSteps = fs.readFileSync( path.join( DIRNAME, './templates/cloudBuildTargetGKE.hbs' ), 'utf8' )
45
- let bashrcTemplate = fs.readFileSync( path.join( DIRNAME, './templates/bashrcTemplate.hbs' ), 'utf8' )
46
- const bashrcPyTemplate = fs.readFileSync( path.join( DIRNAME, './templates/bashrcTemplatePy.hbs' ), 'utf8' )
47
- bashrcTemplate = `${bashrcTemplate}\n${bashrcPyTemplate}`
48
-
49
- const defaultSettings = {
50
- date : getDateTimestamp(),
51
- imageBase : 'python:3.12.8-slim-bookworm',
52
- npmlogging : process.env.VERBOSE_NPM_LOGGING ? '--ddd' : '--silent',
53
- npmVersion : 'npm@11',
54
- pluginfile : '# NO PLUGIN',
55
- preInstallPluginfile : '# NO PRE-INSTALL PLUGIN',
56
- regvers : 'package.version',
57
- runuser : 'python',
58
- }
59
-
60
- // the previous build version is simply informational for the user
61
- const previousFile = './docker/.previous'
62
-
63
- function getPreviousBuild() {
64
- return fs.existsSync( previousFile ) ? fs.readFileSync( previousFile ) : 'FIRST BUILD'
65
- }
66
-
67
- const setPreviousBuild = ( version ) => {
68
- fs.writeFileSync( previousFile, version )
69
- }
70
-
71
- // the base image string to be in the form of an image name and a version
72
- // for example:
73
- //
74
- // python:3.12.8-slim-bookworm becomes python-3.12.8
75
- // nvcr.io/nvidia/cuda:12.3.1-base-ubuntu22.04 becomes cuda-12.3.1
76
- //
77
- // which will then be used to tag the image for information purposes only
78
- const extractBaseImageTag = ( imageStr ) => {
79
- const match = imageStr.match( /(\w+:\d+(?:\.\d+)*)(?=-)/ ) // eslint-disable-line security/detect-unsafe-regex
80
- return match ? match[1].replace( ':', '-' ) : null
81
- }
82
-
83
- function generateDockerfile( options ) {
84
-
85
- const settings = { ...defaultSettings, ...options }
86
- settings.image = settings.imageBase
87
- // TODO: this is a janky way to pass the image tag back out
88
- defaultSettings.buildTag = extractBaseImageTag( settings.image )
89
-
90
- // Ensure the presence of the docker dir with plugin and bashrc - this code
91
- // could probably be tightened up a little.
92
- try {
93
- fs.readdirSync( './docker' )
94
- } catch ( err ) {
95
- warning( 'creating the missing ./docker directory' )
96
- fs.mkdirSync( './docker' ) // TODO: blindly assumes the mkdir succeeds
97
- }
98
-
99
- const bashrcFile = './docker/bashrc'
100
- let bf = bashrcTemplate
101
- const bashrcFilePlugin = './docker/Bashrc.plugin'
102
- if ( fs.existsSync( bashrcFilePlugin ) ) {
103
- const bashrcAdditions = fs.readFileSync( bashrcFilePlugin )
104
- bf = `${bf}\n${bashrcAdditions}`
105
- }
106
- fs.writeFileSync( bashrcFile, bf )
107
-
108
- const dockerfilePlugin = './docker/Dockerfile.plugin'
109
- if ( !fs.existsSync( dockerfilePlugin ) ) {
110
- warning( `creating the missing ${dockerfilePlugin} file in the docker directory` )
111
- fs.writeFileSync( dockerfilePlugin, pluginTemplate )
112
- }
113
-
114
- try {
115
- const build = fs.readdirSync( './build' )
116
- if ( build.length === 0 ) {
117
- errorExit( `***ERROR: the build dir is empty - ${chalk.yellow( 'npm run build' )}` )
118
- }
119
- } catch ( err ) {
120
- errorExit( `***ERROR: expected the ./build directory to exist - ${chalk.yellow( 'npm run build' )}` )
121
- }
122
-
123
- // At this point the plugin template better exist.
124
- if ( fs.existsSync( dockerfilePlugin ) ) {
125
- settings.pluginfile = fs.readFileSync( dockerfilePlugin )
126
- } else {
127
- errorExit( `***ERROR: something went wrong with ${dockerfilePlugin} plugin creation` )
128
- }
129
-
130
- const dockerfilePrePlugin = './docker/DockerfilePreInstall.plugin'
131
- if ( fs.existsSync( dockerfilePrePlugin ) ) {
132
- settings.preInstallPluginfile = fs.readFileSync( dockerfilePrePlugin )
133
- }
134
-
135
- const compiled = handlebars.compile( dockerfileTemplate, { noEscape : true } )
136
- const replaced = compiled( settings )
137
- // temporarily write the Dockerfile to the docker subdir - the container
138
- // image builder will then move it over to the build subdir
139
- const dockerfile = './docker/Dockerfile'
140
- try {
141
- fs.writeFileSync( dockerfile, replaced )
142
- } catch ( err ) {
143
- errorExit( `***ERROR: failed to write ${dockerfile}\n${err}` )
144
- }
145
-
146
- const previousBuild = getPreviousBuild()
147
- const dockerInfo = { dockerfile, previousBuild, ...settings }
148
- delete dockerInfo.pluginfile
149
- return dockerInfo
150
- }
151
-
152
- function formCloudBuildBucketName( artifactProject ) {
153
- return `gs://${artifactProject}_cloudbuild`
154
- }
155
-
156
- async function validateCloudBuildBucket( artifactProject ) {
157
- const bucketName = formCloudBuildBucketName( artifactProject )
158
- log( chalk.green.bold( `Verifying the build bucket exists => ${bucketName}\n` ) )
159
- try {
160
- await shellCmd( `gsutil ls -p ${artifactProject} -b ${bucketName}` )
161
- } catch ( error ) {
162
- errorExit( `***Error: from Docker.validateCloudBuildBucket\n\n${chalk.bold.yellow( error )}` )
163
- }
164
- }
165
-
166
- async function buildContainerImage( {
167
- artifactProject, artifactRegistry, containerName,
168
- imageVersion, isNpmWorkspace, options
169
- } ) {
170
- const buildWorkspace = './build/workspace' // passed to Cloud Build
171
- const imageName = `${artifactRegistry}/images/${containerName}`
172
-
173
- // clean up any residual build cruft (should be clean though)
174
- if ( fs.existsSync( buildWorkspace ) ) {
175
- fs.renameSync( buildWorkspace, `${buildWorkspace}-junk` )
176
- fs.rm( `${buildWorkspace}-junk`, { recursive : true }, ( err ) => {} ) // fire and forget
177
- }
178
-
179
- // TODO: This is janky - I'll clean it up later
180
- // NOTE: this is dealing with raw filesystem interactions, meaning there
181
- // isn't a shell performing file globbing and what not
182
- fs.renameSync( './build', './workspace' )
183
- fs.mkdirSync( './build' )
184
- fs.renameSync( './workspace', buildWorkspace )
185
- fs.renameSync( './docker/Dockerfile', './build/Dockerfile' )
186
- fs.renameSync( './docker/bashrc', './build/bashrc' )
187
- // fs.cpSync( `${process.env.HOME}/.npmrc`, './build/.npmrc' )
188
- // fs.cpSync( './package.json', `${buildWorkspace}/package.json` )
189
- // fs.cpSync( './package-lock.json', `${buildWorkspace}/package-lock.json` )
190
-
191
- setPreviousBuild( imageVersion ) // update docker/.previous file
192
-
193
- const { fullName } = await getGcpAccount()
194
- const imageBuilder = getCleanTag( fullName )
195
- const imageArch = defaultSettings.buildTag
196
- const imageInfoTag = `zzinfo_${imageArch}_${imageBuilder}_${defaultSettings.date}`
197
- const gcloudProjectNumber = await shellCmd( `gcloud projects describe ${artifactProject} --format="value(projectNumber)"` )
198
-
199
- // build the cloudbuild.yaml from the handlebars template
200
- const cloudBuildFile = './build/cloudbuild.yaml'
201
- const cbst = handlebars.compile( cloudBuildSteps, { noEscape : true } )
202
- const cbs = cbst( { artifactProject, artifactRegistry, imageName, imageVersion, imageInfoTag, } )
203
- fs.writeFileSync( cloudBuildFile, cbs )
204
-
205
- // build up the cloud build command and options
206
- const cloudBuildCmd = `gcloud builds submit --project ${artifactProject}`
207
- const cloudBuildLogs = `--gcs-log-dir ${formCloudBuildBucketName( artifactProject )}/log`
208
- const cloudBuildCfg = `--config=${cloudBuildFile}`
209
- const cloudBuildSA = `--service-account=${gcloudProjectNumber}-compute@cloudbuild.gserviceaccount.com`
210
-
211
- log( chalk.green.bold( `
212
- *** Submitting Build as ${imageBuilder} ***
213
- ${cloudBuildCmd} \\
214
- ${cloudBuildLogs} \\
215
- ${cloudBuildCfg} \\
216
- ${cloudBuildSA}
217
- ` ) )
218
-
219
- if ( process.env.DRY_RUN === '1' ) {
220
- log( `Would have run => ${cloudBuildCmd} ${cloudBuildLogs} ${cloudBuildCfg} ./build` )
221
- log( '\n***Exiting from DRY_RUN\n' )
222
- process.exit( 0 )
223
- }
224
-
225
- await shellCmd( `${cloudBuildCmd} ${cloudBuildLogs} ${cloudBuildCfg} ./build`, { stdio : 'inherit' } )
226
- }
227
-
228
- async function repoCleanup() {
229
- // remove garbage from the repo
230
- const garbage = [
231
- 'build',
232
- 'dist',
233
- 'docker/.keep',
234
- 'docker/.npmrc',
235
- 'docker/.gitignore',
236
- 'docker/.previous',
237
- 'docker/Dockerfile',
238
- 'docker/workspace',
239
- ]
240
-
241
- garbage.forEach( trash => fs.rmSync( trash, { recursive : true, force : true } ) )
242
-
243
- fs.writeFileSync( 'docker/.gitignore', '.previous\nDockerfile' )
244
-
245
- await shellCmd( 'git add -f docker' )
246
-
247
- log( `
248
- ${chalk.yellow.bold( '***IMPORTANT***' )}
249
-
250
- Please make the following .gitignore modifications:
251
- change /dist to ${chalk.green.bold( '/build' )}
252
- remove ${chalk.red.bold( '/docker' )}
253
- ` )
254
-
255
- await proceed( 'Acknowledge the .gitignore mods were made?' )
256
- }
257
-
258
- export default {
259
- getPreviousBuild,
260
- setPreviousBuild,
261
- generateDockerfile,
262
- formCloudBuildBucketName,
263
- validateCloudBuildBucket,
264
- buildContainerImage,
265
- repoCleanup,
266
- }
@@ -1,287 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * DEPRECATED - DO NOT MODIFY
4
- */
5
- import fs from 'node:fs'
6
-
7
- import toml from 'toml'
8
- import chalk from 'chalk'
9
-
10
- import {
11
- debug, log, warning,
12
- errorExit,
13
- getGitBranchAndUpstream,
14
- gitRepoIsDirty,
15
- parseHelmChart,
16
- proceed,
17
- shellCmd } from './Utils.mjs'
18
- import Docker from './DockerPy.mjs'
19
-
20
- // refresh-npm-token does not throw so no need to try, but it emits in debug
21
- // const refreshErr = await shellCmd( 'refresh-npm-token' )
22
- // if ( refreshErr && !process.env.BUILD_TOOLS_DEBUG ) {
23
- // log( refreshErr ) // most likely the token refreshed message
24
- // }
25
-
26
- warning( 'docker-to-registry-py has been DEPRECATED - use docker-to-registry' )
27
-
28
- const options = {}
29
- let imageVersion = null
30
- // Expected to be invoked like docker-to-registry-py v1.2.3
31
- for ( let n = 2; n < process.argv.length; n++ ) {
32
- if ( process.argv[n].startsWith( '-' ) ) {
33
- const str = process.argv[n].slice( 1 )
34
- if ( str.indexOf( '=' ) > 0 ) {
35
- const [ key, value ] = str.split( '=' )
36
- options[key] = value
37
- } else {
38
- options[str] = true
39
- }
40
- } else {
41
- imageVersion = process.argv[n]
42
- }
43
- }
44
- if ( !imageVersion ) {
45
- errorExit( '***Error: docker-to-registry-py requires an image version' )
46
- }
47
-
48
- // may use guide-me or guideme to ask for update guidance
49
- const giveGuidance = imageVersion.match( /^guide-*me$/ ) !== null
50
-
51
- const willGitTag = imageVersion.match( /^v\d+\.\d+\.\d+$/ )
52
- const tagInfo = willGitTag ?
53
- chalk.yellow.bold( 'will be git tagged' ) :
54
- chalk.red( 'BETA RELEASE WILL NOT BE GIT TAGGED' )
55
-
56
- const repoIsDirty = await gitRepoIsDirty()
57
-
58
- if ( willGitTag && repoIsDirty ) {
59
- log( `
60
- ${chalk.red.bold( '***ERROR: attempting to tag a non-beta dirty git repository' )}
61
-
62
- You are attempting to create a tagged release image for pushing to the
63
- artifact registry, but there are locally modified files. This is not
64
- allowed since the applied tag will not be relevant to the image version
65
- due to the pending commits.
66
-
67
- Available options for proceeding:
68
-
69
- ${chalk.green.bold( '1) cleanly commit all local mods assuming relevance' )}
70
- ${chalk.yellow( '2) eliminate unwanted local modifications' )}
71
- ${chalk.red( '3) stash anything that is irrelevant to this release' )}
72
-
73
- ` )
74
- process.exit( 1 )
75
- }
76
-
77
- if ( !fs.existsSync( './helm' ) ) {
78
- log( `
79
- ${chalk.red.bold( '***Error: docker-to-registry-py requires a helm chart directory' )}
80
-
81
- The docker-to-registry-py script expects to run in the root directory of a k8s
82
- service, which requires a helm chart directory. It also expects a docker
83
- directory but will build that if it does not exist.
84
-
85
- ` )
86
- process.exit( 1 )
87
- }
88
-
89
- const tomlStr = fs.readFileSync( './pyproject.toml' )
90
- const pyproject = toml.parse( tomlStr )
91
- const repoDescr = {
92
- artifactProject : pyproject.leverege.project || 'leverege-registry',
93
- containerName : pyproject.project.name,
94
- packageVersion : `v${pyproject.project.version}`,
95
- artifactRegistry : pyproject.leverege.registry || 'us-docker.pkg.dev/leverege-registry/stack',
96
- registryFolder : `${pyproject.leverege['registry-folder']}/images/${pyproject.project.name}`,
97
- imageBase : pyproject.leverege['image-base'],
98
- helmChart : await parseHelmChart(),
99
- }
100
-
101
- debug( { repoDescr }, '<==The Repo Description' )
102
-
103
- const dockerInfo = await Docker.generateDockerfile( {
104
- regvers : repoDescr.packageVersion,
105
- imageBase : repoDescr.imageBase,
106
- } )
107
-
108
- debug( { dockerInfo }, '<==The Docker Info' )
109
-
110
- const {
111
- artifactProject,
112
- artifactRegistry,
113
- packageVersion,
114
- containerName,
115
- helmChart,
116
- } = repoDescr
117
-
118
- if ( giveGuidance ) {
119
- await Docker.validateCloudBuildBucket( artifactProject )
120
- }
121
-
122
- const registryFolder = `${artifactRegistry}/images/${containerName}`
123
-
124
- // Update the dependencies...
125
- // log( chalk.green.bold( 'Updating dependencies and workspace...' ) )
126
- // await shellCmd( 'npm install', { stdio : 'inherit' } )
127
-
128
- if ( giveGuidance ) {
129
- await Docker.repoCleanup() // adjusts gitignore files and removes cruft
130
- log( `
131
-
132
-
133
-
134
- ${chalk.green.bold( '-------------------- Potential Helm Chart Mods --------------------' )}
135
-
136
- Moving to the new artifact-registry for docker image storage may also require
137
- helm chart changes depending on how old the current charts are. Charts that
138
- contain helm/values.yaml files resembling this:
139
-
140
- ${chalk.yellow.bold( `serviceConfig:
141
- VERSION: v1.2.3
142
- PREEMPTIBLE: true` )}
143
-
144
- are pre-ignition era charts and should be replaced with the latest helm chart
145
- templates available from ignition. Either dive in or ask devops for a hand
146
- when converting these legacy / deprecated charts.
147
-
148
-
149
- More recently updated helm charts that have already started the migration to
150
- the new approach may already have registry entries like this:
151
-
152
- ${chalk.yellow.bold( `image:
153
- registry: gcr.io/leverege-docker-images
154
- tag: ""` )}
155
-
156
- The above registry statement references the deprecated container registry and
157
- must be adjusted accordingly to resemble:
158
-
159
- ${chalk.green.bold( 'registry: us-docker.pkg.dev/leverege-registry/<registry folder>/images' )}
160
-
161
- There may also be a corresponding modification needed in the helm template
162
- deployment.yaml file in the container image spec:
163
-
164
- ${chalk.green.bold( 'image: {{ .Values.image.registry }}/{{ .Chart.Name }}:{{ default .Chart.AppVersion .Values.image.tag }}' )}
165
-
166
-
167
- ${chalk.magenta.bold( `Upgrading all service charts to the latest ignition template chart structure
168
- is the preferred approach to making the transition to the artifact registry.` )}
169
-
170
- ` )
171
- log( chalk.green.bold( '*** docker-to-registry-py guidance complete - ready for dockerization ***' ) )
172
- process.exit( 0 )
173
- }
174
-
175
- // do chart checks too
176
- let chart
177
- if ( options?.helmChecks !== 'false' ) {
178
- chart = helmChart?.yaml['Chart.yaml']
179
- const values = helmChart?.yaml['values.yaml']
180
- const expectedChartRegistry = `${artifactRegistry}/images`
181
-
182
- if ( expectedChartRegistry !== values.image?.registry ) {
183
- log( `
184
- ${chalk.red.bold( '***Error: mismatched package.json registry and helm/values.yaml' )}
185
-
186
- The registry specified in the package.json leverege stanza does not line up
187
- with the image registry in helm/values.yaml. The naming convention expects
188
- the '/images' suffix to be added to the package.json registry string and then
189
- stored as the image.registry in the helm/values.yaml file.
190
-
191
- Current settings:
192
- package.json registry => ${chalk.yellow.bold( artifactRegistry )}
193
- values.yaml registry => ${chalk.red.bold( values.image?.registry )}
194
-
195
- Expected settings:
196
- values.yaml registry => ${chalk.green.bold( expectedChartRegistry )}
197
- ` )
198
-
199
- if ( !values.image ) {
200
- log( `
201
- ${chalk.yellow.bold( '***DEPRECATED: legacy helm charts detected' )}
202
-
203
- The helm chart structure appears to be based on the pre-ignition helm chart
204
- layouts. The charts must be upgraded before docker-to-registry-py can complete
205
- its job.
206
-
207
- ` )
208
- }
209
-
210
- process.exit( 1 )
211
- }
212
- }
213
-
214
- // Give the summary and the user a chance to proceed or not
215
- log( `
216
- ${chalk.green.bold( 'Build Information:' )}
217
- Container: ${chalk.green.bold( containerName )}
218
- Version: ${chalk.green.bold( imageVersion )} ${tagInfo}
219
- Registry: ${chalk.green.bold( artifactRegistry )}
220
- Image Folder: ${chalk.yellow.bold( registryFolder )}
221
-
222
- ${chalk.green.bold( 'Docker Information:' )}
223
- NodeImage: ${chalk.green.bold( dockerInfo.imageBase || `node:${dockerInfo.nodeimage}` )}
224
- Run User: ${chalk.green.bold( dockerInfo.runuser )}
225
- Previous: ${chalk.yellow.bold( dockerInfo.previousBuild )}
226
- DateStamp: ${chalk.green.bold( dockerInfo.date )}
227
- ` )
228
-
229
- if ( imageVersion !== packageVersion ) {
230
- log( `
231
- ${chalk.yellow.bold( '***WARNING: specified version does not match project version' )}
232
- pyproject.toml => ${chalk.green.bold( packageVersion )}
233
- specified => ${chalk.red.bold( imageVersion )}
234
- ` )
235
-
236
- if ( willGitTag ) {
237
- log( chalk.red.bold( '... and you are attempting to release so NOPE!\n' ) )
238
- process.exit( 1 )
239
- }
240
- }
241
-
242
- const repoInfo = await getGitBranchAndUpstream()
243
-
244
- if ( willGitTag && !repoInfo.upstream ) {
245
- log( `
246
- ${chalk.red.bold( '***ERROR: the current branch must have an upstream' )}
247
-
248
- You are attempting to create a tagged release image for pushing to the
249
- container registry, but the current branch does not have an upstream to
250
- push to. This is not allowed since the applied tag will be stranded here
251
- in your local repository, which is what we are trying to avoid.
252
-
253
- Available options for proceeding:` )
254
- log( chalk.green.bold( `
255
- 1) set an upstream for this branch via:
256
- git push --set-upstream origin ${repoInfo.branch}` ) )
257
- log( chalk.yellow.bold( `
258
- 2) rebase, squash and merge onto a branch with an upstream` ) )
259
-
260
- process.exit( 1 )
261
- }
262
-
263
- if ( willGitTag ) {
264
- if ( chart.appVersion !== imageVersion ) {
265
- log( `
266
- ${chalk.yellow.bold( '***WARNING: the helm/Chart.yaml appVersion does not align with the build version' )}
267
- chart appVersion => ${chalk.yellow.bold( chart.appVersion )}
268
- build version => ${chalk.green.bold( imageVersion )}
269
- ` )
270
- }
271
- }
272
-
273
- await proceed()
274
-
275
- log( chalk.green.bold( 'Building the Docker Image...' ) )
276
- await Docker.buildContainerImage( { ...repoDescr, options, imageVersion } )
277
- log( chalk.blue.bold( 'Done Building the Docker Image...' ) )
278
-
279
- if ( willGitTag ) {
280
- const tagDescription = `docker_${imageVersion}`
281
- const fullImageTag = repoDescr.isMonoRepo ? `${repoDescr.containerName}/${imageVersion}` : imageVersion
282
- log( chalk.green.bold( `Tagging and Pushing ${fullImageTag}\n` ) )
283
- await shellCmd( `git tag -f ${fullImageTag} -m ${tagDescription}` )
284
- await shellCmd( 'git push --follow-tags' )
285
- } else {
286
- log( chalk.yellow.bold( '\n*** Skipped git tagging - beta release' ) )
287
- }