@rulebricks/cli 1.9.0

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 (93) hide show
  1. package/README.md +62 -0
  2. package/dist/commands/clone.d.ts +6 -0
  3. package/dist/commands/clone.js +60 -0
  4. package/dist/commands/deploy.d.ts +8 -0
  5. package/dist/commands/deploy.js +409 -0
  6. package/dist/commands/destroy.d.ts +8 -0
  7. package/dist/commands/destroy.js +298 -0
  8. package/dist/commands/init.d.ts +7 -0
  9. package/dist/commands/init.js +201 -0
  10. package/dist/commands/logs.d.ts +9 -0
  11. package/dist/commands/logs.js +222 -0
  12. package/dist/commands/open.d.ts +7 -0
  13. package/dist/commands/open.js +139 -0
  14. package/dist/commands/status.d.ts +5 -0
  15. package/dist/commands/status.js +125 -0
  16. package/dist/commands/upgrade.d.ts +7 -0
  17. package/dist/commands/upgrade.js +239 -0
  18. package/dist/components/DNSWaitScreen.d.ts +9 -0
  19. package/dist/components/DNSWaitScreen.js +73 -0
  20. package/dist/components/Wizard/WizardContext.d.ts +176 -0
  21. package/dist/components/Wizard/WizardContext.js +346 -0
  22. package/dist/components/Wizard/index.d.ts +2 -0
  23. package/dist/components/Wizard/index.js +2 -0
  24. package/dist/components/Wizard/steps/CloudProviderStep.d.ts +6 -0
  25. package/dist/components/Wizard/steps/CloudProviderStep.js +210 -0
  26. package/dist/components/Wizard/steps/CredentialsStep.d.ts +6 -0
  27. package/dist/components/Wizard/steps/CredentialsStep.js +22 -0
  28. package/dist/components/Wizard/steps/DatabaseStep.d.ts +6 -0
  29. package/dist/components/Wizard/steps/DatabaseStep.js +80 -0
  30. package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +5 -0
  31. package/dist/components/Wizard/steps/DeploymentModeStep.js +26 -0
  32. package/dist/components/Wizard/steps/DomainStep.d.ts +6 -0
  33. package/dist/components/Wizard/steps/DomainStep.js +126 -0
  34. package/dist/components/Wizard/steps/FeatureConfigStep.d.ts +6 -0
  35. package/dist/components/Wizard/steps/FeatureConfigStep.js +765 -0
  36. package/dist/components/Wizard/steps/FeaturesStep.d.ts +6 -0
  37. package/dist/components/Wizard/steps/FeaturesStep.js +119 -0
  38. package/dist/components/Wizard/steps/ReviewStep.d.ts +6 -0
  39. package/dist/components/Wizard/steps/ReviewStep.js +56 -0
  40. package/dist/components/Wizard/steps/SMTPStep.d.ts +6 -0
  41. package/dist/components/Wizard/steps/SMTPStep.js +191 -0
  42. package/dist/components/Wizard/steps/SupabaseCredentialsStep.d.ts +6 -0
  43. package/dist/components/Wizard/steps/SupabaseCredentialsStep.js +76 -0
  44. package/dist/components/Wizard/steps/TierStep.d.ts +6 -0
  45. package/dist/components/Wizard/steps/TierStep.js +29 -0
  46. package/dist/components/Wizard/steps/VersionStep.d.ts +6 -0
  47. package/dist/components/Wizard/steps/VersionStep.js +113 -0
  48. package/dist/components/Wizard/steps/index.d.ts +12 -0
  49. package/dist/components/Wizard/steps/index.js +12 -0
  50. package/dist/components/common/AppShell.d.ts +31 -0
  51. package/dist/components/common/AppShell.js +31 -0
  52. package/dist/components/common/Box.d.ts +20 -0
  53. package/dist/components/common/Box.js +20 -0
  54. package/dist/components/common/Logo.d.ts +7 -0
  55. package/dist/components/common/Logo.js +22 -0
  56. package/dist/components/common/Spinner.d.ts +12 -0
  57. package/dist/components/common/Spinner.js +28 -0
  58. package/dist/components/common/index.d.ts +6 -0
  59. package/dist/components/common/index.js +5 -0
  60. package/dist/index.d.ts +2 -0
  61. package/dist/index.js +202 -0
  62. package/dist/lib/cloudCli.d.ts +156 -0
  63. package/dist/lib/cloudCli.js +691 -0
  64. package/dist/lib/config.d.ts +91 -0
  65. package/dist/lib/config.js +278 -0
  66. package/dist/lib/dns.d.ts +41 -0
  67. package/dist/lib/dns.js +235 -0
  68. package/dist/lib/dockerHub.d.ts +57 -0
  69. package/dist/lib/dockerHub.js +128 -0
  70. package/dist/lib/helm.d.ts +53 -0
  71. package/dist/lib/helm.js +209 -0
  72. package/dist/lib/helmValues.d.ts +17 -0
  73. package/dist/lib/helmValues.js +693 -0
  74. package/dist/lib/kubernetes.d.ts +161 -0
  75. package/dist/lib/kubernetes.js +755 -0
  76. package/dist/lib/terraform.d.ts +44 -0
  77. package/dist/lib/terraform.js +230 -0
  78. package/dist/lib/theme.d.ts +81 -0
  79. package/dist/lib/theme.js +115 -0
  80. package/dist/lib/validation.d.ts +47 -0
  81. package/dist/lib/validation.js +164 -0
  82. package/dist/lib/versions.d.ts +69 -0
  83. package/dist/lib/versions.js +139 -0
  84. package/dist/types/index.d.ts +718 -0
  85. package/dist/types/index.js +556 -0
  86. package/email-templates/email_change.html +325 -0
  87. package/email-templates/invite.html +383 -0
  88. package/email-templates/password_change.html +414 -0
  89. package/email-templates/verify.html +396 -0
  90. package/package.json +78 -0
  91. package/terraform/aws/main.tf +327 -0
  92. package/terraform/azure/main.tf +326 -0
  93. package/terraform/gcp/main.tf +369 -0
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Docker Hub API client for fetching image tags
3
+ *
4
+ * Uses the license key as a Docker PAT for authentication
5
+ * to access private Rulebricks images.
6
+ */
7
+ const DOCKER_HUB_API = 'https://hub.docker.com/v2';
8
+ const DOCKER_USERNAME = 'rulebricks';
9
+ /**
10
+ * Formats the license key as a Docker PAT
11
+ */
12
+ export function formatDockerPat(licenseKey) {
13
+ // If already formatted, return as-is
14
+ if (licenseKey.startsWith('dckr_pat_')) {
15
+ return licenseKey;
16
+ }
17
+ return `dckr_pat_${licenseKey}`;
18
+ }
19
+ /**
20
+ * Authenticates with Docker Hub using the license key as a PAT
21
+ *
22
+ * @param licenseKey - The Rulebricks license key (used as Docker PAT)
23
+ * @returns JWT token for subsequent API calls
24
+ */
25
+ export async function authenticateDockerHub(licenseKey) {
26
+ const dockerPat = formatDockerPat(licenseKey);
27
+ const response = await fetch(`${DOCKER_HUB_API}/users/login`, {
28
+ method: 'POST',
29
+ headers: {
30
+ 'Content-Type': 'application/json',
31
+ },
32
+ body: JSON.stringify({
33
+ username: DOCKER_USERNAME,
34
+ password: dockerPat,
35
+ }),
36
+ });
37
+ if (!response.ok) {
38
+ if (response.status === 401) {
39
+ throw new Error('Invalid license key - Docker Hub authentication failed');
40
+ }
41
+ throw new Error(`Docker Hub authentication failed: ${response.status} ${response.statusText}`);
42
+ }
43
+ const data = await response.json();
44
+ return data.token;
45
+ }
46
+ /**
47
+ * Fetches available tags for a Docker Hub repository
48
+ *
49
+ * @param repo - Repository name (e.g., "rulebricks/app")
50
+ * @param token - JWT token from authentication
51
+ * @param pageSize - Number of tags to fetch per page (max 100)
52
+ * @returns Array of image tags sorted by last updated (newest first)
53
+ */
54
+ export async function fetchImageTags(repo, token, pageSize = 100) {
55
+ const allTags = [];
56
+ let url = `${DOCKER_HUB_API}/repositories/${repo}/tags?page_size=${pageSize}`;
57
+ while (url) {
58
+ const response = await fetch(url, {
59
+ headers: {
60
+ 'Authorization': `Bearer ${token}`,
61
+ },
62
+ });
63
+ if (!response.ok) {
64
+ if (response.status === 404) {
65
+ throw new Error(`Repository not found: ${repo}`);
66
+ }
67
+ throw new Error(`Failed to fetch tags for ${repo}: ${response.status}`);
68
+ }
69
+ const data = await response.json();
70
+ for (const tag of data.results) {
71
+ // Skip non-semver tags like "latest", "dev", etc.
72
+ if (!isValidVersionTag(tag.name)) {
73
+ continue;
74
+ }
75
+ allTags.push({
76
+ name: tag.name,
77
+ lastUpdated: new Date(tag.last_updated),
78
+ digest: tag.digest,
79
+ fullSize: tag.full_size,
80
+ });
81
+ }
82
+ // Get next page if available (limit to reasonable number of versions)
83
+ url = data.next && allTags.length < 50 ? data.next : null;
84
+ }
85
+ // Sort by last updated, newest first
86
+ allTags.sort((a, b) => b.lastUpdated.getTime() - a.lastUpdated.getTime());
87
+ return allTags;
88
+ }
89
+ /**
90
+ * Checks if a tag looks like a valid semantic version
91
+ * Excludes "latest" and other non-numeric tags
92
+ */
93
+ function isValidVersionTag(tag) {
94
+ // Exclude "latest" and similar non-versioned tags
95
+ if (tag === 'latest' || tag === 'dev' || tag === 'main' || tag === 'master') {
96
+ return false;
97
+ }
98
+ // Match patterns like "1.2.3", "v1.2.3", "1.2.3-beta.1"
99
+ const versionPattern = /^v?\d+\.\d+\.\d+(-[\w.]+)?$/;
100
+ return versionPattern.test(tag);
101
+ }
102
+ /**
103
+ * Fetches tags for both app and HPS repositories
104
+ *
105
+ * @param licenseKey - The Rulebricks license key
106
+ * @returns Object containing app and HPS tags
107
+ */
108
+ export async function fetchAllImageTags(licenseKey) {
109
+ const token = await authenticateDockerHub(licenseKey);
110
+ const [appTags, hpsTags] = await Promise.all([
111
+ fetchImageTags('rulebricks/app', token),
112
+ fetchImageTags('rulebricks/hps', token),
113
+ ]);
114
+ return { appTags, hpsTags };
115
+ }
116
+ /**
117
+ * Normalizes a version string by removing leading 'v'
118
+ */
119
+ export function normalizeVersion(version) {
120
+ return version.replace(/^v/, '');
121
+ }
122
+ /**
123
+ * Formats a version for display (ensures 'v' prefix)
124
+ */
125
+ export function formatVersionDisplay(version) {
126
+ const normalized = normalizeVersion(version);
127
+ return `v${normalized}`;
128
+ }
@@ -0,0 +1,53 @@
1
+ import { ChartVersion } from "../types/index.js";
2
+ /**
3
+ * Checks if Helm is installed
4
+ */
5
+ export declare function isHelmInstalled(): Promise<boolean>;
6
+ /**
7
+ * Gets the installed Helm version
8
+ */
9
+ export declare function getHelmVersion(): Promise<string>;
10
+ /**
11
+ * Fetches available chart versions from the OCI registry
12
+ */
13
+ export declare function fetchChartVersions(): Promise<ChartVersion[]>;
14
+ /**
15
+ * Gets the currently installed chart version for a deployment
16
+ */
17
+ export declare function getInstalledVersion(releaseName: string, namespace: string): Promise<string | null>;
18
+ /**
19
+ * Installs the Rulebricks Helm chart
20
+ */
21
+ export declare function installChart(deploymentName: string, options: {
22
+ releaseName: string;
23
+ namespace: string;
24
+ version?: string;
25
+ wait?: boolean;
26
+ timeout?: string;
27
+ createNamespace?: boolean;
28
+ }): Promise<void>;
29
+ /**
30
+ * Upgrades the Rulebricks Helm chart
31
+ */
32
+ export declare function upgradeChart(deploymentName: string, options: {
33
+ releaseName: string;
34
+ namespace: string;
35
+ version?: string;
36
+ wait?: boolean;
37
+ timeout?: string;
38
+ }): Promise<void>;
39
+ /**
40
+ * Uninstalls the Rulebricks Helm chart
41
+ */
42
+ export declare function uninstallChart(releaseName: string, namespace: string, options?: {
43
+ wait?: boolean;
44
+ timeout?: string;
45
+ }): Promise<void>;
46
+ /**
47
+ * Performs a dry-run upgrade to preview changes
48
+ */
49
+ export declare function dryRunUpgrade(deploymentName: string, options: {
50
+ releaseName: string;
51
+ namespace: string;
52
+ version?: string;
53
+ }): Promise<string>;
@@ -0,0 +1,209 @@
1
+ import { execa } from "execa";
2
+ import { HELM_CHART_OCI } from "../types/index.js";
3
+ import { getHelmValuesPath } from "./config.js";
4
+ /**
5
+ * Extracts meaningful error message from execa error
6
+ */
7
+ function getErrorMessage(error) {
8
+ const execaError = error;
9
+ // Try stderr first, then stdout
10
+ const output = execaError.stderr || execaError.stdout || "";
11
+ if (output) {
12
+ // Get last 500 chars of output for the error message
13
+ const truncated = output.length > 500 ? "..." + output.slice(-500) : output;
14
+ return truncated;
15
+ }
16
+ return execaError.shortMessage || execaError.message || "Unknown error";
17
+ }
18
+ /**
19
+ * Checks if Helm is installed
20
+ */
21
+ export async function isHelmInstalled() {
22
+ try {
23
+ await execa("helm", ["version", "--short"]);
24
+ return true;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ /**
31
+ * Gets the installed Helm version
32
+ */
33
+ export async function getHelmVersion() {
34
+ const { stdout } = await execa("helm", ["version", "--short"]);
35
+ return stdout.trim();
36
+ }
37
+ /**
38
+ * Fetches available chart versions from the OCI registry
39
+ */
40
+ export async function fetchChartVersions() {
41
+ try {
42
+ // Use helm show chart to get info about the latest version
43
+ const { stdout } = await execa("helm", ["show", "chart", HELM_CHART_OCI]);
44
+ // Parse the chart info
45
+ const lines = stdout.split("\n");
46
+ const versionLine = lines.find((l) => l.startsWith("version:"));
47
+ const appVersionLine = lines.find((l) => l.startsWith("appVersion:"));
48
+ const version = versionLine?.split(":")[1]?.trim() || "unknown";
49
+ const appVersion = appVersionLine?.split(":")[1]?.trim() || version;
50
+ // Return at least the current version
51
+ return [
52
+ {
53
+ version,
54
+ appVersion,
55
+ created: new Date().toISOString(),
56
+ digest: "",
57
+ },
58
+ ];
59
+ }
60
+ catch (error) {
61
+ // If we can't fetch, try to get from GitHub API
62
+ return fetchVersionsFromGitHub();
63
+ }
64
+ }
65
+ /**
66
+ * Fetches versions from GitHub releases API
67
+ */
68
+ async function fetchVersionsFromGitHub() {
69
+ try {
70
+ const response = await fetch("https://api.github.com/repos/rulebricks/helm/releases");
71
+ if (!response.ok) {
72
+ throw new Error(`GitHub API returned ${response.status}`);
73
+ }
74
+ const releases = (await response.json());
75
+ return releases
76
+ .filter((r) => !r.prerelease)
77
+ .map((r) => ({
78
+ version: r.tag_name.replace(/^v/, ""),
79
+ appVersion: r.tag_name.replace(/^v/, ""),
80
+ created: r.published_at,
81
+ digest: "",
82
+ }));
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ }
88
+ /**
89
+ * Gets the currently installed chart version for a deployment
90
+ */
91
+ export async function getInstalledVersion(releaseName, namespace) {
92
+ try {
93
+ const { stdout } = await execa("helm", ["list", "-n", namespace, "-f", `^${releaseName}$`, "-o", "json"], { timeout: 15000 }); // 15 second timeout
94
+ const releases = JSON.parse(stdout);
95
+ if (releases.length > 0) {
96
+ return (releases[0].app_version || releases[0].chart.split("-").pop() || null);
97
+ }
98
+ return null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ /**
105
+ * Installs the Rulebricks Helm chart
106
+ */
107
+ export async function installChart(deploymentName, options) {
108
+ const { releaseName, namespace, version, wait = true, timeout = "15m", createNamespace = true, } = options;
109
+ const valuesPath = getHelmValuesPath(deploymentName);
110
+ const args = [
111
+ "install",
112
+ releaseName,
113
+ HELM_CHART_OCI,
114
+ "--namespace",
115
+ namespace,
116
+ "--values",
117
+ valuesPath,
118
+ ];
119
+ if (version) {
120
+ args.push("--version", version);
121
+ }
122
+ if (createNamespace) {
123
+ args.push("--create-namespace");
124
+ }
125
+ if (wait) {
126
+ args.push("--wait");
127
+ args.push("--timeout", timeout);
128
+ }
129
+ try {
130
+ await execa("helm", args);
131
+ }
132
+ catch (error) {
133
+ throw new Error(`Helm install failed:\n${getErrorMessage(error)}`);
134
+ }
135
+ }
136
+ /**
137
+ * Upgrades the Rulebricks Helm chart
138
+ */
139
+ export async function upgradeChart(deploymentName, options) {
140
+ const { releaseName, namespace, version, wait = true, timeout = "15m", } = options;
141
+ const valuesPath = getHelmValuesPath(deploymentName);
142
+ const args = [
143
+ "upgrade",
144
+ releaseName,
145
+ HELM_CHART_OCI,
146
+ "--namespace",
147
+ namespace,
148
+ "--values",
149
+ valuesPath,
150
+ ];
151
+ if (version) {
152
+ args.push("--version", version);
153
+ }
154
+ if (wait) {
155
+ args.push("--wait");
156
+ args.push("--timeout", timeout);
157
+ }
158
+ try {
159
+ await execa("helm", args);
160
+ }
161
+ catch (error) {
162
+ throw new Error(`Helm upgrade failed:\n${getErrorMessage(error)}`);
163
+ }
164
+ }
165
+ /**
166
+ * Uninstalls the Rulebricks Helm chart
167
+ */
168
+ export async function uninstallChart(releaseName, namespace, options = {}) {
169
+ const { wait = false, timeout = "10m" } = options;
170
+ const args = ["uninstall", releaseName, "--namespace", namespace];
171
+ if (wait) {
172
+ args.push("--wait");
173
+ args.push("--timeout", timeout);
174
+ }
175
+ try {
176
+ // 60 second process timeout to prevent hanging
177
+ await execa("helm", args, { timeout: 60000 });
178
+ }
179
+ catch (error) {
180
+ const execaError = error;
181
+ // Ignore "release not found" errors and timeouts (we'll continue anyway)
182
+ const errorMsg = execaError.stderr || execaError.message || "";
183
+ if (!errorMsg.includes("not found") && !execaError.timedOut) {
184
+ throw new Error(`Helm uninstall failed:\n${getErrorMessage(error)}`);
185
+ }
186
+ }
187
+ }
188
+ /**
189
+ * Performs a dry-run upgrade to preview changes
190
+ */
191
+ export async function dryRunUpgrade(deploymentName, options) {
192
+ const { releaseName, namespace, version } = options;
193
+ const valuesPath = getHelmValuesPath(deploymentName);
194
+ const args = [
195
+ "upgrade",
196
+ releaseName,
197
+ HELM_CHART_OCI,
198
+ "--namespace",
199
+ namespace,
200
+ "--values",
201
+ valuesPath,
202
+ "--dry-run",
203
+ ];
204
+ if (version) {
205
+ args.push("--version", version);
206
+ }
207
+ const { stdout } = await execa("helm", args);
208
+ return stdout;
209
+ }
@@ -0,0 +1,17 @@
1
+ import { DeploymentConfig } from "../types/index.js";
2
+ interface GenerateOptions {
3
+ tlsEnabled?: boolean;
4
+ }
5
+ /**
6
+ * Generates Helm values from the deployment configuration
7
+ */
8
+ export declare function generateHelmValues(config: DeploymentConfig, options?: GenerateOptions): Promise<void>;
9
+ /**
10
+ * Updates existing Helm values to enable or disable TLS
11
+ */
12
+ export declare function updateHelmValuesForTLS(deploymentName: string, tlsEnabled: boolean): Promise<void>;
13
+ /**
14
+ * Updates existing Helm values with new configuration
15
+ */
16
+ export declare function mergeHelmValues(existing: Record<string, unknown>, updates: Partial<Record<string, unknown>>): Record<string, unknown>;
17
+ export {};