@rulebricks/cli 2.1.6 → 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 (114) hide show
  1. package/README.md +75 -14
  2. package/cluster-setup/aws/README.md +123 -0
  3. package/cluster-setup/aws/check-aws-access.sh +242 -0
  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 +141 -0
  7. package/cluster-setup/azure/check-aks-prereqs.sh +276 -0
  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 +189 -0
  11. package/cluster-setup/gcp/check-gke-prereqs.sh +260 -0
  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 -47
  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 +174 -29
  33. package/dist/components/Wizard/WizardContext.js +896 -91
  34. package/dist/components/Wizard/steps/CloudProviderStep.js +192 -102
  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 +959 -248
  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 -7
  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 +1937 -259
  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 +126 -13
  85. package/dist/lib/kubernetes.js +624 -134
  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 +2152 -95
  101. package/dist/types/index.js +554 -286
  102. package/package.json +10 -4
  103. package/schema/values.schema.json +1934 -0
  104. package/dist/components/Wizard/steps/CredentialsStep.d.ts +0 -6
  105. package/dist/components/Wizard/steps/CredentialsStep.js +0 -22
  106. package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +0 -5
  107. package/dist/components/Wizard/steps/DeploymentModeStep.js +0 -26
  108. package/dist/components/Wizard/steps/TierStep.d.ts +0 -6
  109. package/dist/components/Wizard/steps/TierStep.js +0 -29
  110. package/dist/lib/terraform.d.ts +0 -66
  111. package/dist/lib/terraform.js +0 -754
  112. package/terraform/aws/main.tf +0 -355
  113. package/terraform/azure/main.tf +0 -371
  114. package/terraform/gcp/main.tf +0 -407
@@ -0,0 +1,189 @@
1
+ # GCP Cluster Setup
2
+
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
+
5
+ ## Files
6
+
7
+ - `check-gke-prereqs.sh` verifies `gcloud` auth, Application Default Credentials, required APIs, selected-region quota, GKE access, `kubectl`, and Helm.
8
+
9
+ ## Core Cluster Parameters
10
+
11
+ - Cluster name: `rulebricks-cluster` (`Core cluster parameters` block -> `CLUSTER_NAME`)
12
+ - Region / zone: `us-central1` / `us-central1-a` (`Core cluster parameters` block -> `REGION` / `ZONE`)
13
+ - Kubernetes version: `1.34` (`Core cluster parameters` block -> `KUBERNETES_VERSION`)
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`)
17
+ - Disk size (GB): `20` (`Core cluster parameters` block -> `DISK_SIZE`)
18
+ - Disk type: `pd-balanced` (`Core cluster parameters` block -> `DISK_TYPE`)
19
+
20
+ ## Check Access
21
+
22
+ ```bash
23
+ gcloud auth login
24
+ gcloud config set project <project-id>
25
+ gcloud auth application-default login
26
+ GCP_REGION=us-central1 bash check-gke-prereqs.sh
27
+ ```
28
+
29
+ If API warnings appear, run the suggested `gcloud services enable` commands and wait for enablement to complete.
30
+
31
+ ## Create The Cluster
32
+
33
+ Set the core cluster parameters.
34
+
35
+ ```bash
36
+ PROJECT_ID="$(gcloud config get-value project)"
37
+ CLUSTER_NAME=rulebricks-cluster
38
+ REGION=us-central1
39
+ ZONE=us-central1-a
40
+ KUBERNETES_VERSION="1.34"
41
+ NODE_COUNT=2
42
+ MAX_NODE_COUNT=4
43
+ MACHINE_TYPE=n2-standard-4
44
+ DISK_SIZE=20
45
+ DISK_TYPE=pd-balanced
46
+ ```
47
+
48
+ Enable required APIs:
49
+
50
+ ```bash
51
+ gcloud services enable \
52
+ compute.googleapis.com \
53
+ container.googleapis.com \
54
+ iam.googleapis.com \
55
+ cloudresourcemanager.googleapis.com \
56
+ --project "$PROJECT_ID"
57
+ ```
58
+
59
+ Create the VPC, subnet, NAT, and firewall rules:
60
+
61
+ ```bash
62
+ gcloud compute networks create "${CLUSTER_NAME}-vpc" \
63
+ --project "$PROJECT_ID" \
64
+ --subnet-mode custom
65
+
66
+ gcloud compute networks subnets create "${CLUSTER_NAME}-subnet" \
67
+ --project "$PROJECT_ID" \
68
+ --region "$REGION" \
69
+ --network "${CLUSTER_NAME}-vpc" \
70
+ --range 10.0.0.0/16 \
71
+ --secondary-range pods=10.1.0.0/16,services=10.2.0.0/16 \
72
+ --enable-private-ip-google-access
73
+
74
+ gcloud compute routers create "${CLUSTER_NAME}-router" \
75
+ --project "$PROJECT_ID" \
76
+ --region "$REGION" \
77
+ --network "${CLUSTER_NAME}-vpc"
78
+
79
+ gcloud compute routers nats create "${CLUSTER_NAME}-nat" \
80
+ --project "$PROJECT_ID" \
81
+ --region "$REGION" \
82
+ --router "${CLUSTER_NAME}-router" \
83
+ --auto-allocate-nat-external-ips \
84
+ --nat-all-subnet-ip-ranges
85
+
86
+ gcloud compute firewall-rules create "${CLUSTER_NAME}-allow-internal" \
87
+ --project "$PROJECT_ID" \
88
+ --network "${CLUSTER_NAME}-vpc" \
89
+ --allow tcp:0-65535,udp:0-65535,icmp \
90
+ --source-ranges 10.0.0.0/16,10.1.0.0/16,10.2.0.0/16 \
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}"
99
+ ```
100
+
101
+ Create the GKE cluster:
102
+
103
+ ```bash
104
+ gcloud container clusters create "$CLUSTER_NAME" \
105
+ --project "$PROJECT_ID" \
106
+ --region "$REGION" \
107
+ --node-locations "$ZONE" \
108
+ --cluster-version "$KUBERNETES_VERSION" \
109
+ --release-channel regular \
110
+ --network "${CLUSTER_NAME}-vpc" \
111
+ --subnetwork "${CLUSTER_NAME}-subnet" \
112
+ --enable-ip-alias \
113
+ --cluster-secondary-range-name pods \
114
+ --services-secondary-range-name services \
115
+ --enable-private-nodes \
116
+ --master-ipv4-cidr 172.16.0.0/28 \
117
+ --enable-master-authorized-networks \
118
+ --master-authorized-networks 0.0.0.0/0 \
119
+ --workload-pool "${PROJECT_ID}.svc.id.goog" \
120
+ --enable-network-policy \
121
+ --addons HttpLoadBalancing,HorizontalPodAutoscaling,GcePersistentDiskCsiDriver \
122
+ --node-pool rulebricks-nodes \
123
+ --machine-type "$MACHINE_TYPE" \
124
+ --num-nodes "$NODE_COUNT" \
125
+ --enable-autoscaling \
126
+ --min-nodes "$NODE_COUNT" \
127
+ --max-nodes "$MAX_NODE_COUNT" \
128
+ --disk-type "$DISK_TYPE" \
129
+ --disk-size "$DISK_SIZE" \
130
+ --scopes cloud-platform \
131
+ --workload-metadata GKE_METADATA \
132
+ --enable-autorepair \
133
+ --enable-autoupgrade \
134
+ --tags "gke-${CLUSTER_NAME}"
135
+ ```
136
+
137
+ Configure kubeconfig:
138
+
139
+ ```bash
140
+ gcloud container clusters get-credentials "$CLUSTER_NAME" \
141
+ --region "$REGION" \
142
+ --project "$PROJECT_ID"
143
+ ```
144
+
145
+ Use `rulebricks init` after kubeconfig works, then select this cluster from the GCP cluster list.
146
+
147
+ ## Multi-Node Scheduling
148
+
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:
156
+
157
+ ```bash
158
+ PROJECT_ID="$(gcloud config get-value project)"
159
+ CLUSTER_NAME=rulebricks-cluster
160
+ GSA=rulebricks@"$PROJECT_ID".iam.gserviceaccount.com
161
+ BUCKET="$CLUSTER_NAME-data"
162
+
163
+ gcloud iam service-accounts create rulebricks --project "$PROJECT_ID"
164
+
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" \
169
+ --member "serviceAccount:$GSA" \
170
+ --role roles/storage.objectAdmin
171
+
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
176
+ ```
177
+
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.
183
+
184
+ ## Notes
185
+
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.
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.
@@ -0,0 +1,260 @@
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
+
12
+ set -euo pipefail
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
+
21
+ PROJECT_ID="${GOOGLE_CLOUD_PROJECT:-$(gcloud config get-value project 2>/dev/null || true)}"
22
+ REGION="${GCP_REGION:-us-central1}"
23
+ REQUIRED_VCPU=8
24
+ VERBOSE="${VERBOSE:-0}"
25
+
26
+ REQUIRED_APIS=(
27
+ compute.googleapis.com
28
+ container.googleapis.com
29
+ iam.googleapis.com
30
+ cloudresourcemanager.googleapis.com
31
+ )
32
+
33
+ ACTIONS=()
34
+ BLOCKERS=0
35
+
36
+ # ---------- helpers ----------
37
+
38
+ require_cmd() {
39
+ command -v "$1" >/dev/null 2>&1 || {
40
+ printf "ERROR: required command not found: %s\n" "$1" >&2
41
+ exit 1
42
+ }
43
+ }
44
+
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
55
+ fi
56
+ return "$GC_RC"
57
+ }
58
+
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
+ }
69
+
70
+ row() {
71
+ printf " %-50s %s\n" "$1" "$2"
72
+ }
73
+
74
+ mark_blocker() { BLOCKERS=$((BLOCKERS + 1)); }
75
+ add_action() { ACTIONS+=("$1"); }
76
+
77
+ # ---------- pre-flight ----------
78
+
79
+ require_cmd gcloud
80
+ require_cmd kubectl
81
+ require_cmd helm
82
+ require_cmd awk
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
+
89
+ if [[ -z "$PROJECT_ID" ]]; then
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]}"
96
+ exit 1
97
+ fi
98
+
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
132
+
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
146
+
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))
255
+ done
256
+
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 {};