@rulebricks/cli 2.1.7 → 2.3.1

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 (117) hide show
  1. package/README.md +51 -16
  2. package/cluster-setup/aws/README.md +96 -47
  3. package/cluster-setup/aws/check-aws-access.sh +216 -52
  4. package/cluster-setup/aws/parameters.json +13 -0
  5. package/cluster-setup/aws/rulebricks-cluster.cfn.yaml +355 -0
  6. package/cluster-setup/azure/README.md +103 -55
  7. package/cluster-setup/azure/check-aks-prereqs.sh +236 -56
  8. package/cluster-setup/azure/parameters.json +30 -0
  9. package/cluster-setup/azure/rulebricks-cluster.bicep +546 -0
  10. package/cluster-setup/gcp/README.md +51 -34
  11. package/cluster-setup/gcp/check-gke-prereqs.sh +222 -60
  12. package/dist/commands/backup.d.ts +5 -0
  13. package/dist/commands/backup.js +104 -0
  14. package/dist/commands/deploy.d.ts +3 -1
  15. package/dist/commands/deploy.js +226 -326
  16. package/dist/commands/destroy.d.ts +1 -1
  17. package/dist/commands/destroy.js +73 -123
  18. package/dist/commands/init.d.ts +5 -1
  19. package/dist/commands/init.js +78 -54
  20. package/dist/commands/list.d.ts +1 -0
  21. package/dist/commands/list.js +74 -0
  22. package/dist/commands/open.d.ts +1 -1
  23. package/dist/commands/open.js +4 -12
  24. package/dist/commands/redeploy.d.ts +6 -0
  25. package/dist/commands/redeploy.js +310 -0
  26. package/dist/commands/restore.d.ts +5 -0
  27. package/dist/commands/restore.js +338 -0
  28. package/dist/commands/status.js +62 -49
  29. package/dist/commands/upgrade.js +74 -51
  30. package/dist/components/DNSWaitScreen.d.ts +5 -1
  31. package/dist/components/DNSWaitScreen.js +47 -41
  32. package/dist/components/Wizard/WizardContext.d.ts +157 -36
  33. package/dist/components/Wizard/WizardContext.js +872 -160
  34. package/dist/components/Wizard/steps/CloudProviderStep.js +192 -107
  35. package/dist/components/Wizard/steps/DomainStep.js +5 -24
  36. package/dist/components/Wizard/steps/ExternalServicesStep.d.ts +6 -0
  37. package/dist/components/Wizard/steps/ExternalServicesStep.js +645 -0
  38. package/dist/components/Wizard/steps/FeatureConfigStep.d.ts +2 -1
  39. package/dist/components/Wizard/steps/FeatureConfigStep.js +739 -425
  40. package/dist/components/Wizard/steps/FeaturesStep.js +31 -35
  41. package/dist/components/Wizard/steps/ObservabilityStep.d.ts +6 -0
  42. package/dist/components/Wizard/steps/ObservabilityStep.js +137 -0
  43. package/dist/components/Wizard/steps/ReviewStep.d.ts +2 -1
  44. package/dist/components/Wizard/steps/ReviewStep.js +56 -12
  45. package/dist/components/Wizard/steps/StorageStep.d.ts +9 -0
  46. package/dist/components/Wizard/steps/StorageStep.js +592 -0
  47. package/dist/components/Wizard/steps/SupabaseCredentialsStep.js +20 -21
  48. package/dist/components/Wizard/steps/VersionStep.js +45 -23
  49. package/dist/components/Wizard/steps/index.d.ts +3 -3
  50. package/dist/components/Wizard/steps/index.js +3 -3
  51. package/dist/components/common/CommandApproval.d.ts +12 -0
  52. package/dist/components/common/CommandApproval.js +91 -0
  53. package/dist/components/common/DeploymentPicker.d.ts +14 -0
  54. package/dist/components/common/DeploymentPicker.js +16 -0
  55. package/dist/components/common/index.d.ts +2 -0
  56. package/dist/components/common/index.js +2 -0
  57. package/dist/index.js +94 -62
  58. package/dist/lib/cloudCli.d.ts +134 -63
  59. package/dist/lib/cloudCli.js +512 -220
  60. package/dist/lib/clusterSetupDefaults.d.ts +30 -0
  61. package/dist/lib/clusterSetupDefaults.js +64 -0
  62. package/dist/lib/commandApproval.d.ts +26 -0
  63. package/dist/lib/commandApproval.js +114 -0
  64. package/dist/lib/config.d.ts +12 -10
  65. package/dist/lib/config.js +91 -33
  66. package/dist/lib/configFixtures.d.ts +5 -0
  67. package/dist/lib/configFixtures.js +513 -0
  68. package/dist/lib/deploymentHealth.d.ts +32 -0
  69. package/dist/lib/deploymentHealth.js +157 -0
  70. package/dist/lib/dns.d.ts +1 -1
  71. package/dist/lib/dns.js +19 -1
  72. package/dist/lib/dns.test.d.ts +1 -0
  73. package/dist/lib/dns.test.js +27 -0
  74. package/dist/lib/dockerHub.d.ts +12 -1
  75. package/dist/lib/dockerHub.js +18 -8
  76. package/dist/lib/helm.d.ts +4 -0
  77. package/dist/lib/helm.js +16 -0
  78. package/dist/lib/helmValues.d.ts +25 -0
  79. package/dist/lib/helmValues.js +1762 -289
  80. package/dist/lib/helmValues.test.d.ts +1 -0
  81. package/dist/lib/helmValues.test.js +966 -0
  82. package/dist/lib/htpasswd.d.ts +1 -0
  83. package/dist/lib/htpasswd.js +15 -0
  84. package/dist/lib/kubernetes.d.ts +124 -17
  85. package/dist/lib/kubernetes.js +576 -145
  86. package/dist/lib/secrets.d.ts +23 -0
  87. package/dist/lib/secrets.js +158 -0
  88. package/dist/lib/validateValues.d.ts +31 -0
  89. package/dist/lib/validateValues.js +253 -0
  90. package/dist/lib/versions.d.ts +82 -11
  91. package/dist/lib/versions.js +131 -31
  92. package/dist/lib/versions.test.d.ts +1 -0
  93. package/dist/lib/versions.test.js +81 -0
  94. package/dist/lib/wizardSteps.d.ts +14 -0
  95. package/dist/lib/wizardSteps.js +23 -0
  96. package/dist/lib/workloadIdentity.d.ts +26 -0
  97. package/dist/lib/workloadIdentity.js +323 -0
  98. package/dist/lib/workloadIdentity.test.d.ts +1 -0
  99. package/dist/lib/workloadIdentity.test.js +57 -0
  100. package/dist/types/index.d.ts +1860 -164
  101. package/dist/types/index.js +518 -295
  102. package/package.json +9 -4
  103. package/schema/values.schema.json +1934 -0
  104. package/cluster-setup/aws/cluster.yaml +0 -33
  105. package/cluster-setup/azure/main.bicep +0 -282
  106. package/cluster-setup/azure/main.parameters.json +0 -21
  107. package/dist/components/Wizard/steps/CredentialsStep.d.ts +0 -6
  108. package/dist/components/Wizard/steps/CredentialsStep.js +0 -22
  109. package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +0 -5
  110. package/dist/components/Wizard/steps/DeploymentModeStep.js +0 -26
  111. package/dist/components/Wizard/steps/TierStep.d.ts +0 -6
  112. package/dist/components/Wizard/steps/TierStep.js +0 -29
  113. package/dist/lib/terraform.d.ts +0 -66
  114. package/dist/lib/terraform.js +0 -754
  115. package/terraform/aws/main.tf +0 -355
  116. package/terraform/azure/main.tf +0 -371
  117. package/terraform/gcp/main.tf +0 -407
@@ -1,6 +1,6 @@
1
1
  # GCP Cluster Setup
2
2
 
3
- Use these commands to create a minimum GKE cluster that can run Rulebricks without using the Rulebricks CLI Terraform flow. GCP does not have an `eksctl`-style cluster YAML or a concise Bicep equivalent; the most familiar native interface is `gcloud`.
3
+ Use these commands to create a compact GKE cluster that can run Rulebricks before installing with the Rulebricks CLI. GCP does not have an `eksctl`-style cluster YAML or a concise Bicep equivalent; the most familiar native interface is `gcloud`.
4
4
 
5
5
  ## Files
6
6
 
@@ -11,10 +11,11 @@ Use these commands to create a minimum GKE cluster that can run Rulebricks witho
11
11
  - Cluster name: `rulebricks-cluster` (`Core cluster parameters` block -> `CLUSTER_NAME`)
12
12
  - Region / zone: `us-central1` / `us-central1-a` (`Core cluster parameters` block -> `REGION` / `ZONE`)
13
13
  - Kubernetes version: `1.34` (`Core cluster parameters` block -> `KUBERNETES_VERSION`)
14
- - Node count: `4` (`Core cluster parameters` block -> `NODE_COUNT`)
15
- - Machine type: `c4a-standard-2` (`Core cluster parameters` block -> `MACHINE_TYPE`)
14
+ - Initial node count: `2` (`Core cluster parameters` block -> `NODE_COUNT`)
15
+ - Autoscaling range: `2-4` nodes (`Core cluster parameters` block -> `NODE_COUNT` / `MAX_NODE_COUNT`)
16
+ - Machine type: `n2-standard-4` (`Core cluster parameters` block -> `MACHINE_TYPE`)
16
17
  - Disk size (GB): `20` (`Core cluster parameters` block -> `DISK_SIZE`)
17
- - Disk type: `hyperdisk-balanced` (`Core cluster parameters` block -> `DISK_TYPE`)
18
+ - Disk type: `pd-balanced` (`Core cluster parameters` block -> `DISK_TYPE`)
18
19
 
19
20
  ## Check Access
20
21
 
@@ -29,7 +30,7 @@ If API warnings appear, run the suggested `gcloud services enable` commands and
29
30
 
30
31
  ## Create The Cluster
31
32
 
32
- Set the core cluster parameters. The default example uses `us-central1-a` because it supports C4A ARM64 nodes.
33
+ Set the core cluster parameters.
33
34
 
34
35
  ```bash
35
36
  PROJECT_ID="$(gcloud config get-value project)"
@@ -37,10 +38,11 @@ CLUSTER_NAME=rulebricks-cluster
37
38
  REGION=us-central1
38
39
  ZONE=us-central1-a
39
40
  KUBERNETES_VERSION="1.34"
40
- NODE_COUNT=4
41
- MACHINE_TYPE=c4a-standard-2
41
+ NODE_COUNT=2
42
+ MAX_NODE_COUNT=4
43
+ MACHINE_TYPE=n2-standard-4
42
44
  DISK_SIZE=20
43
- DISK_TYPE=hyperdisk-balanced
45
+ DISK_TYPE=pd-balanced
44
46
  ```
45
47
 
46
48
  Enable required APIs:
@@ -54,7 +56,7 @@ gcloud services enable \
54
56
  --project "$PROJECT_ID"
55
57
  ```
56
58
 
57
- Create the VPC, subnet, NAT, and internal firewall rule:
59
+ Create the VPC, subnet, NAT, and firewall rules:
58
60
 
59
61
  ```bash
60
62
  gcloud compute networks create "${CLUSTER_NAME}-vpc" \
@@ -87,6 +89,13 @@ gcloud compute firewall-rules create "${CLUSTER_NAME}-allow-internal" \
87
89
  --allow tcp:0-65535,udp:0-65535,icmp \
88
90
  --source-ranges 10.0.0.0/16,10.1.0.0/16,10.2.0.0/16 \
89
91
  --target-tags "gke-${CLUSTER_NAME}"
92
+
93
+ gcloud compute firewall-rules create "${CLUSTER_NAME}-allow-web" \
94
+ --project "$PROJECT_ID" \
95
+ --network "${CLUSTER_NAME}-vpc" \
96
+ --allow tcp:80,tcp:443 \
97
+ --source-ranges 0.0.0.0/0 \
98
+ --target-tags "gke-${CLUSTER_NAME}"
90
99
  ```
91
100
 
92
101
  Create the GKE cluster:
@@ -113,13 +122,15 @@ gcloud container clusters create "$CLUSTER_NAME" \
113
122
  --node-pool rulebricks-nodes \
114
123
  --machine-type "$MACHINE_TYPE" \
115
124
  --num-nodes "$NODE_COUNT" \
125
+ --enable-autoscaling \
126
+ --min-nodes "$NODE_COUNT" \
127
+ --max-nodes "$MAX_NODE_COUNT" \
116
128
  --disk-type "$DISK_TYPE" \
117
129
  --disk-size "$DISK_SIZE" \
118
130
  --scopes cloud-platform \
119
131
  --workload-metadata GKE_METADATA \
120
132
  --enable-autorepair \
121
133
  --enable-autoupgrade \
122
- --node-labels environment=rulebricks \
123
134
  --tags "gke-${CLUSTER_NAME}"
124
135
  ```
125
136
 
@@ -131,42 +142,48 @@ gcloud container clusters get-credentials "$CLUSTER_NAME" \
131
142
  --project "$PROJECT_ID"
132
143
  ```
133
144
 
134
- Use `rulebricks init` with **Use existing Kubernetes cluster** after kubeconfig works.
145
+ Use `rulebricks init` after kubeconfig works, then select this cluster from the GCP cluster list.
135
146
 
136
- ## Optional Identity Setup
147
+ ## Multi-Node Scheduling
137
148
 
138
- If you use GCS decision-log export, bind the `vector` Kubernetes service account to a Google service account that can write to the bucket:
149
+ The default configuration starts with two 4-vCPU nodes for a simple standalone deployment and can scale out to four nodes. Splitting the baseline across two nodes provides more Kubernetes pod slots than a single large node while keeping the initial 8-vCPU footprint. Rulebricks worker pods use soft scheduling preferences so Kubernetes can place them away from the rest of the deployment when extra nodes are available. No node labels or taints are required.
150
+
151
+ ## Identity Setup (one service account, one bucket)
152
+
153
+ All Rulebricks data lives in a single GCS bucket; decision logs and database
154
+ backups are key prefixes (`decision-logs/`, `db-backups/`) within it. Create one
155
+ Google service account and the bucket — this is deployment-independent:
139
156
 
140
157
  ```bash
141
- NAMESPACE=rulebricks-demo
142
158
  PROJECT_ID="$(gcloud config get-value project)"
143
- GSA=rulebricks-vector@"$PROJECT_ID".iam.gserviceaccount.com
159
+ CLUSTER_NAME=rulebricks-cluster
160
+ GSA=rulebricks@"$PROJECT_ID".iam.gserviceaccount.com
161
+ BUCKET="$CLUSTER_NAME-data"
144
162
 
145
- gcloud iam service-accounts create rulebricks-vector \
146
- --project "$PROJECT_ID"
163
+ gcloud iam service-accounts create rulebricks --project "$PROJECT_ID"
147
164
 
148
- gcloud storage buckets add-iam-policy-binding gs://<bucket-name> \
165
+ # Create the single data bucket and grant read/write/delete (delete is needed so
166
+ # the backup job can prune backups older than the retention window).
167
+ gcloud storage buckets create "gs://$BUCKET" --project "$PROJECT_ID" --location "$REGION"
168
+ gcloud storage buckets add-iam-policy-binding "gs://$BUCKET" \
149
169
  --member "serviceAccount:$GSA" \
150
- --role roles/storage.objectCreator
151
-
152
- gcloud iam service-accounts add-iam-policy-binding "$GSA" \
153
- --project "$PROJECT_ID" \
154
- --role roles/iam.workloadIdentityUser \
155
- --member "serviceAccount:$PROJECT_ID.svc.id.goog[$NAMESPACE/vector]"
156
- ```
170
+ --role roles/storage.objectAdmin
157
171
 
158
- Annotate the service account after the Rulebricks namespace exists:
159
-
160
- ```bash
161
- kubectl annotate serviceaccount vector \
162
- --namespace "$NAMESPACE" \
163
- iam.gke.io/gcp-service-account="$GSA"
172
+ # Prometheus remote write to Google Managed Prometheus (skip if unused).
173
+ gcloud projects add-iam-policy-binding "$PROJECT_ID" \
174
+ --member "serviceAccount:$GSA" \
175
+ --role roles/monitoring.metricWriter
164
176
  ```
165
177
 
166
- Enter the Google service account email when prompted by the CLI.
178
+ The per-namespace `roles/iam.workloadIdentityUser` bindings (for `vector`,
179
+ `<release>-backup`, and `prometheus`) are **created by the Rulebricks CLI at
180
+ `rulebricks deploy` time**, since they're namespace-scoped — so this setup stays
181
+ generic and one cluster can host many deployments. Enter the Google service
182
+ account email (`$GSA`) and the `$BUCKET` name when prompted by the CLI.
167
183
 
168
184
  ## Notes
169
185
 
170
- - The example creates four `c4a-standard-2` ARM64 nodes with `hyperdisk-balanced`, matching the minimum CLI Terraform defaults.
171
- - C4A availability varies by region and zone. If you change `REGION`, choose a `ZONE` where C4A is available.
186
+ - The example creates two `n2-standard-4` nodes initially and enables autoscaling up to four nodes. The initial nodes provide 8 vCPU total for the compact Rulebricks cluster shape while avoiding single-node pod density limits.
187
+ - If you change `REGION`, choose a `ZONE` where the selected machine type is available.
172
188
  - Regional GKE clusters can multiply node counts across node locations. This example pins one node location to keep the minimum cluster shape predictable.
189
+ - The public web firewall rule allows HTTP and HTTPS to the node pool so Kubernetes LoadBalancer services and cert-manager HTTP-01 validation can receive internet traffic.
@@ -1,9 +1,28 @@
1
1
  #!/usr/bin/env bash
2
+ # Rulebricks GKE prerequisite check.
3
+ #
4
+ # Prints a short pass/fail report and a final READY / NOT READY verdict
5
+ # with the exact actions you need to take before running the GKE deploy.
6
+ #
7
+ # Env vars:
8
+ # GOOGLE_CLOUD_PROJECT GCP project id (defaults to gcloud config)
9
+ # GCP_REGION Region to check (default: us-central1)
10
+ # VERBOSE=1 Print raw gcloud error messages inline
11
+
2
12
  set -euo pipefail
3
13
 
14
+ if [[ -z "${BASH_VERSION:-}" ]]; then
15
+ exec bash "$0" "$@"
16
+ fi
17
+
18
+ # Quiet gcloud's interactive nags / survey output.
19
+ export CLOUDSDK_CORE_DISABLE_PROMPTS=1
20
+
4
21
  PROJECT_ID="${GOOGLE_CLOUD_PROJECT:-$(gcloud config get-value project 2>/dev/null || true)}"
5
22
  REGION="${GCP_REGION:-us-central1}"
6
23
  REQUIRED_VCPU=8
24
+ VERBOSE="${VERBOSE:-0}"
25
+
7
26
  REQUIRED_APIS=(
8
27
  compute.googleapis.com
9
28
  container.googleapis.com
@@ -11,88 +30,231 @@ REQUIRED_APIS=(
11
30
  cloudresourcemanager.googleapis.com
12
31
  )
13
32
 
33
+ ACTIONS=()
34
+ BLOCKERS=0
35
+
36
+ # ---------- helpers ----------
37
+
14
38
  require_cmd() {
15
39
  command -v "$1" >/dev/null 2>&1 || {
16
- echo "Missing required command: $1" >&2
40
+ printf "ERROR: required command not found: %s\n" "$1" >&2
17
41
  exit 1
18
42
  }
19
43
  }
20
44
 
21
- check_api() {
22
- local api="$1"
23
- if gcloud services list \
24
- --project "$PROJECT_ID" \
25
- --enabled \
26
- --filter="name:${api}" \
27
- --format="value(name)" | awk -v api="$api" '$0 ~ api { found=1 } END { exit found ? 0 : 1 }'; then
28
- echo "OK: $api is enabled"
29
- else
30
- echo "WARN: $api is not enabled. Run: gcloud services enable $api --project $PROJECT_ID"
45
+ # Run a gcloud command. Sets GC_STDOUT / GC_STDERR / GC_RC. Never aborts.
46
+ gc_run() {
47
+ GC_STDOUT=""; GC_STDERR=""; GC_RC=0
48
+ local _err
49
+ _err="$(mktemp)"
50
+ GC_STDOUT="$(gcloud "$@" 2>"$_err")" || GC_RC=$?
51
+ GC_STDERR="$(cat "$_err")"
52
+ rm -f "$_err"
53
+ if [[ "$VERBOSE" == "1" && -n "$GC_STDERR" ]]; then
54
+ printf " debug: %s\n" "${GC_STDERR%%$'\n'*}" >&2
31
55
  fi
56
+ return "$GC_RC"
32
57
  }
33
58
 
34
- check_quota() {
35
- echo "Checking regional CPU quota in $REGION..."
36
- local quota_line
37
- quota_line="$(gcloud compute regions describe "$REGION" \
38
- --project "$PROJECT_ID" \
39
- --format="csv[no-heading](quotas.metric,quotas.limit,quotas.usage)" 2>/dev/null \
40
- | awk -F, '$1=="CPUS"{print $2 "," $3; exit}' || true)"
41
-
42
- if [[ -z "$quota_line" ]]; then
43
- echo "WARN: Could not read regional CPU quota."
44
- return
45
- fi
46
-
47
- local limit="${quota_line%,*}"
48
- local usage="${quota_line#*,}"
49
- local available
50
- available="$(awk -v limit="$limit" -v usage="$usage" 'BEGIN { printf "%d", limit - usage }')"
59
+ is_auth_error() {
60
+ [[ "$GC_STDERR" == *"reauthentication"* ]] && return 0
61
+ [[ "$GC_STDERR" == *"credentials"*"expired"* ]] && return 0
62
+ [[ "$GC_STDERR" == *"invalid_grant"* ]] && return 0
63
+ [[ "$GC_STDERR" == *"gcloud auth login"* ]] && return 0
64
+ [[ "$GC_STDERR" == *"gcloud auth application-default login"* ]] && return 0
65
+ [[ "$GC_STDERR" == *"There was a problem refreshing"* ]] && return 0
66
+ [[ "$GC_STDERR" == *"do not have active credentials"* ]] && return 0
67
+ return 1
68
+ }
51
69
 
52
- if (( available < REQUIRED_VCPU )); then
53
- echo "WARN: ${available}/${limit} CPUs available; ${REQUIRED_VCPU}+ recommended for the included cluster commands."
54
- else
55
- echo "OK: ${available}/${limit} CPUs available."
56
- fi
70
+ row() {
71
+ printf " %-50s %s\n" "$1" "$2"
57
72
  }
58
73
 
74
+ mark_blocker() { BLOCKERS=$((BLOCKERS + 1)); }
75
+ add_action() { ACTIONS+=("$1"); }
76
+
77
+ # ---------- pre-flight ----------
78
+
59
79
  require_cmd gcloud
60
80
  require_cmd kubectl
61
81
  require_cmd helm
62
82
  require_cmd awk
63
83
 
84
+ printf "Rulebricks GKE prerequisite check\n"
85
+ printf " Region: %s\n" "$REGION"
86
+ printf " Project: %s\n" "${PROJECT_ID:-<unset>}"
87
+ printf "\n"
88
+
64
89
  if [[ -z "$PROJECT_ID" ]]; then
65
- echo "No GCP project configured. Run: gcloud config set project PROJECT_ID" >&2
90
+ row "GCP project configured" "FAIL - no project set"
91
+ add_action "Set a project: gcloud config set project <PROJECT_ID>"
92
+ printf "\n========================================\n"
93
+ printf "RESULT: NOT READY\n"
94
+ printf "========================================\n"
95
+ printf "Required actions:\n 1. %s\n" "${ACTIONS[0]}"
66
96
  exit 1
67
97
  fi
68
98
 
69
- echo "Rulebricks GKE prerequisite checks"
70
- echo "Project: $PROJECT_ID"
71
- echo "Region: $REGION"
72
- echo
99
+ # ---------- 1. Authentication ----------
100
+ AUTH_OK=0
101
+ ACTIVE_ACCOUNT=""
102
+
103
+ if gc_run auth list --filter=status:ACTIVE --format="value(account)"; then
104
+ ACTIVE_ACCOUNT="$(printf '%s' "$GC_STDOUT" | head -n 1)"
105
+ if [[ -z "$ACTIVE_ACCOUNT" ]]; then
106
+ row "gcloud user signed in" "FAIL - no active account"
107
+ add_action "Run: gcloud auth login"
108
+ mark_blocker
109
+ else
110
+ row "gcloud user signed in" "OK ($ACTIVE_ACCOUNT)"
111
+
112
+ # Now verify ADC actually works against Google APIs - this is what
113
+ # the deploy tooling uses, and the most common breakage.
114
+ if gc_run auth application-default print-access-token; then
115
+ row "Application Default Credentials" "OK"
116
+ AUTH_OK=1
117
+ else
118
+ if is_auth_error; then
119
+ row "Application Default Credentials" "FAIL - ADC missing or expired"
120
+ else
121
+ row "Application Default Credentials" "FAIL - ${GC_STDERR%%$'\n'*}"
122
+ fi
123
+ add_action "Run: gcloud auth application-default login"
124
+ mark_blocker
125
+ fi
126
+ fi
127
+ else
128
+ row "gcloud user signed in" "FAIL - ${GC_STDERR%%$'\n'*}"
129
+ add_action "Run: gcloud auth login && gcloud auth application-default login"
130
+ mark_blocker
131
+ fi
73
132
 
74
- echo "Checking gcloud account..."
75
- gcloud auth list --filter=status:ACTIVE --format="value(account)"
76
- gcloud auth application-default print-access-token >/dev/null
77
- echo "OK: gcloud auth and Application Default Credentials are available."
78
- echo
133
+ if [[ $AUTH_OK -eq 0 ]]; then
134
+ printf "\nRemaining checks skipped - fix authentication first.\n"
135
+ printf "\n========================================\n"
136
+ printf "RESULT: NOT READY\n"
137
+ printf "========================================\n"
138
+ printf "Required actions:\n"
139
+ i=1
140
+ for a in "${ACTIONS[@]}"; do
141
+ printf " %d. %s\n" "$i" "$a"
142
+ i=$((i + 1))
143
+ done
144
+ exit 1
145
+ fi
79
146
 
80
- for api in "${REQUIRED_APIS[@]}"; do
81
- check_api "$api"
147
+ # ---------- 2. Project access ----------
148
+ if gc_run projects describe "$PROJECT_ID" --format="value(projectId)"; then
149
+ row "Project '$PROJECT_ID' accessible" "OK"
150
+ else
151
+ row "Project '$PROJECT_ID' accessible" "FAIL - ${GC_STDERR%%$'\n'*}"
152
+ add_action "Ensure '$ACTIVE_ACCOUNT' has at least roles/viewer on project '$PROJECT_ID', or fix the project id."
153
+ mark_blocker
154
+ fi
155
+
156
+ # ---------- 3. Required APIs ----------
157
+ missing_apis=()
158
+ unknown_apis=()
159
+ if gc_run services list --project "$PROJECT_ID" --enabled --format="value(config.name)"; then
160
+ enabled="$GC_STDOUT"
161
+ for api in "${REQUIRED_APIS[@]}"; do
162
+ if ! printf '%s\n' "$enabled" | grep -qx "$api"; then
163
+ missing_apis+=("$api")
164
+ fi
165
+ done
166
+ else
167
+ unknown_apis=("${REQUIRED_APIS[@]}")
168
+ fi
169
+
170
+ total=${#REQUIRED_APIS[@]}
171
+
172
+ if [[ ${#unknown_apis[@]} -gt 0 ]]; then
173
+ row "Required APIs enabled" "WARN - could not list enabled services"
174
+ add_action "Verify 'serviceusage.services.list' permission, then re-run."
175
+ elif [[ ${#missing_apis[@]} -eq 0 ]]; then
176
+ row "Required APIs enabled" "OK ($total/$total)"
177
+ else
178
+ enabled_count=$((total - ${#missing_apis[@]}))
179
+ row "Required APIs enabled" "WARN ($enabled_count/$total)"
180
+ enable_cmd="gcloud services enable ${missing_apis[*]} --project $PROJECT_ID"
181
+ add_action "Enable missing APIs (takes ~1 min):"
182
+ add_action " $enable_cmd"
183
+ fi
184
+
185
+ # ---------- 4. Region + GKE access ----------
186
+ REGION_OK=1
187
+ if ! gc_run compute regions describe "$REGION" --project "$PROJECT_ID" --format="value(name)"; then
188
+ REGION_OK=0
189
+ row "Region '$REGION' accessible" "FAIL - ${GC_STDERR%%$'\n'*}"
190
+ add_action "Verify region name '$REGION' and that the Compute Engine API is enabled."
191
+ mark_blocker
192
+ fi
193
+
194
+ if [[ $REGION_OK -eq 1 ]]; then
195
+ if gc_run container clusters list --region "$REGION" --project "$PROJECT_ID" --format="value(name)"; then
196
+ row "Region + GKE list access" "OK"
197
+ else
198
+ row "Region + GKE list access" "WARN - ${GC_STDERR%%$'\n'*}"
199
+ add_action "Ensure '$ACTIVE_ACCOUNT' has roles/container.viewer (or higher) on '$PROJECT_ID'."
200
+ fi
201
+ fi
202
+
203
+ # ---------- 5. Regional CPU quota ----------
204
+ quota_label="Regional CPU quota in $REGION (need ${REQUIRED_VCPU}+)"
205
+ quota_line=""
206
+ if gc_run compute regions describe "$REGION" --project "$PROJECT_ID" \
207
+ --format="csv[no-heading](quotas.metric,quotas.limit,quotas.usage)"; then
208
+ quota_line="$(printf '%s\n' "$GC_STDOUT" | awk -F, '$1=="CPUS"{print $2 "," $3; exit}')"
209
+ fi
210
+
211
+ if [[ -z "$quota_line" ]]; then
212
+ row "$quota_label" "WARN - could not read quota"
213
+ add_action "Manually check CPU quota in Console → IAM & Admin → Quotas (region: $REGION)."
214
+ else
215
+ limit="${quota_line%,*}"
216
+ usage="${quota_line#*,}"
217
+ available="$(awk -v l="$limit" -v u="$usage" 'BEGIN { printf "%d", l - u }')"
218
+ if (( available < REQUIRED_VCPU )); then
219
+ row "$quota_label" "WARN ($available/$limit free)"
220
+ add_action "Request CPU quota increase: Console → IAM & Admin → Quotas → 'Compute Engine API CPUs' in $REGION."
221
+ else
222
+ row "$quota_label" "OK ($available/$limit free)"
223
+ fi
224
+ fi
225
+
226
+ # ---------- 6. Local tools ----------
227
+ if kubectl version --client=true >/dev/null 2>&1 && helm version >/dev/null 2>&1; then
228
+ row "Local tools (kubectl, helm)" "OK"
229
+ else
230
+ row "Local tools (kubectl, helm)" "FAIL"
231
+ add_action "Install/repair kubectl and helm."
232
+ mark_blocker
233
+ fi
234
+
235
+ # ---------- summary ----------
236
+ printf "\n========================================\n"
237
+ if [[ $BLOCKERS -eq 0 && ${#ACTIONS[@]} -eq 0 ]]; then
238
+ printf "RESULT: READY - you can run the GKE deploy.\n"
239
+ printf "========================================\n"
240
+ exit 0
241
+ elif [[ $BLOCKERS -eq 0 ]]; then
242
+ printf "RESULT: READY WITH WARNINGS\n"
243
+ printf "========================================\n"
244
+ printf "The deploy should work, but address these first if possible:\n"
245
+ else
246
+ printf "RESULT: NOT READY\n"
247
+ printf "========================================\n"
248
+ printf "Required actions:\n"
249
+ fi
250
+
251
+ i=1
252
+ for a in "${ACTIONS[@]}"; do
253
+ printf " %d. %s\n" "$i" "$a"
254
+ i=$((i + 1))
82
255
  done
83
256
 
84
- echo
85
- gcloud compute regions describe "$REGION" --project "$PROJECT_ID" >/dev/null
86
- echo "OK: region $REGION is accessible."
87
- gcloud container clusters list --region "$REGION" --project "$PROJECT_ID" >/dev/null
88
- echo "OK: GKE cluster list access works."
89
- check_quota
90
-
91
- echo
92
- echo "Checking local Kubernetes tools..."
93
- kubectl version --client=true >/dev/null
94
- helm version >/dev/null
95
- echo "OK: kubectl and Helm are installed."
96
-
97
- echo
98
- echo "GKE prerequisite checks completed. Warnings may require GCP project-admin review before cluster creation."
257
+ printf "\nRe-run this script after completing the actions above.\n"
258
+ printf "(Set VERBOSE=1 to see raw gcloud error messages.)\n"
259
+
260
+ [[ $BLOCKERS -gt 0 ]] && exit 1 || exit 0
@@ -0,0 +1,5 @@
1
+ interface BackupCommandProps {
2
+ name: string;
3
+ }
4
+ export declare function BackupCommand(props: BackupCommandProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1,104 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Box, Text, useApp } from "ink";
4
+ import { BorderBox, Logo, Spinner, StatusLine, ThemeProvider, useTheme, CommandApprovalProvider, } from "../components/common/index.js";
5
+ import { loadDeploymentConfig } from "../lib/config.js";
6
+ import { updateKubeconfig } from "../lib/cloudCli.js";
7
+ import { CommandDeniedError } from "../lib/commandApproval.js";
8
+ import { checkClusterAccessible, createJobFromCronJob, isKubectlInstalled, waitForJobComplete, } from "../lib/kubernetes.js";
9
+ import { getNamespace, getReleaseName } from "../types/index.js";
10
+ function k8sName(value) {
11
+ return value.toLowerCase().replace(/[^a-z0-9-]/g, "-").slice(0, 63).replace(/-+$/, "");
12
+ }
13
+ function BackupCommandInner({ name }) {
14
+ const { exit } = useApp();
15
+ const { colors } = useTheme();
16
+ const [step, setStep] = useState("loading");
17
+ const [error, setError] = useState(null);
18
+ const [logs, setLogs] = useState("");
19
+ const [status, setStatus] = useState({
20
+ preflight: "pending",
21
+ job: "pending",
22
+ });
23
+ useEffect(() => {
24
+ runBackup();
25
+ }, []);
26
+ async function runBackup() {
27
+ try {
28
+ const config = await loadDeploymentConfig(name);
29
+ validateConfig(config);
30
+ setStep("preflight");
31
+ setStatus((current) => ({ ...current, preflight: "running" }));
32
+ await runPreflight(config);
33
+ setStatus((current) => ({ ...current, preflight: "success" }));
34
+ const namespace = getNamespace(config.name);
35
+ const releaseName = getReleaseName(config.name);
36
+ const cronJobName = `${releaseName}-db-backup`;
37
+ const jobName = k8sName(`${cronJobName}-manual-${Date.now()}`);
38
+ setStep("running");
39
+ setStatus((current) => ({ ...current, job: "running" }));
40
+ await createJobFromCronJob(namespace, cronJobName, jobName);
41
+ const jobLogs = await waitForJobComplete(namespace, jobName);
42
+ setLogs(jobLogs);
43
+ setStatus((current) => ({ ...current, job: "success" }));
44
+ setStep("complete");
45
+ setTimeout(() => exit(), 5000);
46
+ }
47
+ catch (err) {
48
+ setError(err instanceof Error ? err.message : "Backup failed");
49
+ setStatus((current) => ({
50
+ ...current,
51
+ preflight: step === "preflight" ? "error" : current.preflight,
52
+ job: step === "running" ? "error" : current.job,
53
+ }));
54
+ setStep("error");
55
+ }
56
+ }
57
+ function validateConfig(config) {
58
+ if (config.database.type !== "self-hosted") {
59
+ throw new Error("Backups are only available for self-hosted Supabase.");
60
+ }
61
+ if (!config.storage) {
62
+ throw new Error("Shared object storage is required for database backups.");
63
+ }
64
+ if (!config.backup?.enabled) {
65
+ throw new Error("Database backups are disabled for this deployment. Re-run `rulebricks init` to enable them.");
66
+ }
67
+ }
68
+ async function runPreflight(config) {
69
+ if (!(await isKubectlInstalled())) {
70
+ throw new Error("kubectl is not installed. Please install kubectl first.");
71
+ }
72
+ let clusterError = await checkClusterAccessible();
73
+ if (clusterError &&
74
+ config.infrastructure.provider &&
75
+ config.infrastructure.region &&
76
+ config.infrastructure.clusterName) {
77
+ try {
78
+ await updateKubeconfig(config.infrastructure.provider, config.infrastructure.clusterName, config.infrastructure.region, {
79
+ gcpProjectId: config.infrastructure.gcpProjectId,
80
+ azureResourceGroup: config.infrastructure.azureResourceGroup,
81
+ });
82
+ }
83
+ catch (err) {
84
+ if (!(err instanceof CommandDeniedError)) {
85
+ throw err;
86
+ }
87
+ }
88
+ clusterError = await checkClusterAccessible();
89
+ }
90
+ if (clusterError) {
91
+ throw new Error(`Cannot access Kubernetes cluster:\n${clusterError}`);
92
+ }
93
+ }
94
+ if (step === "error") {
95
+ return (_jsx(BorderBox, { title: "Backup Failed", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.error, bold: true, children: "\u2717 Error" }), _jsx(Text, { color: colors.error, children: error })] }) }));
96
+ }
97
+ if (step === "complete") {
98
+ return (_jsx(BorderBox, { title: "Backup Complete", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.success, bold: true, children: "\u2713 Database backup completed" }), logs && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.muted, children: "Job output:" }), logs.split("\n").slice(-8).map((line, index) => (_jsx(Text, { color: colors.muted, children: line }, index)))] }))] }) }));
99
+ }
100
+ return (_jsx(BorderBox, { title: `Backing Up ${name}`, children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(StatusLine, { status: status.preflight, label: "Preflight checks" }), _jsx(StatusLine, { status: status.job, label: "Database backup job" }), _jsx(Box, { marginTop: 1, children: _jsx(Spinner, { label: step === "running" ? "Running backup job..." : "Preparing backup..." }) })] }) }));
101
+ }
102
+ export function BackupCommand(props) {
103
+ return (_jsxs(ThemeProvider, { theme: "status", children: [_jsx(Logo, {}), _jsx(CommandApprovalProvider, { children: _jsx(BackupCommandInner, { ...props }) })] }));
104
+ }
@@ -1,8 +1,10 @@
1
1
  interface DeployCommandProps {
2
2
  name: string;
3
- skipInfra?: boolean;
4
3
  skipDns?: boolean;
5
4
  version?: string;
5
+ regenerateValues?: boolean;
6
+ assumeDnsConfigured?: boolean;
7
+ inlineSecrets?: boolean;
6
8
  }
7
9
  export declare function DeployCommand(props: DeployCommandProps): import("react/jsx-runtime").JSX.Element;
8
10
  export {};