@xano/cli 0.0.94 → 0.0.95-beta.10
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/README.md +28 -1
- package/dist/base-command.d.ts +25 -0
- package/dist/base-command.js +37 -0
- package/dist/commands/auth/index.js +1 -1
- package/dist/commands/profile/create/index.js +2 -2
- package/dist/commands/profile/edit/index.js +2 -2
- package/dist/commands/profile/me/index.js +21 -2
- package/dist/commands/profile/wizard/index.js +3 -3
- package/dist/commands/profile/workspace/set/index.js +1 -1
- package/dist/commands/release/deploy/index.d.ts +17 -0
- package/dist/commands/release/deploy/index.js +107 -0
- package/dist/commands/sandbox/env/delete/index.d.ts +14 -0
- package/dist/commands/sandbox/env/delete/index.js +87 -0
- package/dist/commands/sandbox/env/get/index.d.ts +12 -0
- package/dist/commands/sandbox/env/get/index.js +63 -0
- package/dist/commands/sandbox/env/get_all/index.d.ts +13 -0
- package/dist/commands/sandbox/env/get_all/index.js +76 -0
- package/dist/commands/sandbox/env/list/index.d.ts +11 -0
- package/dist/commands/sandbox/env/list/index.js +65 -0
- package/dist/commands/sandbox/env/set/index.d.ts +13 -0
- package/dist/commands/sandbox/env/set/index.js +72 -0
- package/dist/commands/sandbox/env/set_all/index.d.ts +13 -0
- package/dist/commands/sandbox/env/set_all/index.js +84 -0
- package/dist/commands/sandbox/get/index.d.ts +11 -0
- package/dist/commands/sandbox/get/index.js +61 -0
- package/dist/commands/sandbox/impersonate/index.d.ts +5 -0
- package/dist/commands/sandbox/impersonate/index.js +5 -0
- package/dist/commands/sandbox/license/get/index.d.ts +13 -0
- package/dist/commands/sandbox/license/get/index.js +76 -0
- package/dist/commands/sandbox/license/set/index.d.ts +14 -0
- package/dist/commands/sandbox/license/set/index.js +93 -0
- package/dist/commands/sandbox/pull/index.d.ts +17 -0
- package/dist/commands/sandbox/pull/index.js +180 -0
- package/dist/commands/sandbox/push/index.d.ts +18 -0
- package/dist/commands/sandbox/push/index.js +141 -0
- package/dist/commands/sandbox/reset/index.d.ts +12 -0
- package/dist/commands/sandbox/reset/index.js +69 -0
- package/dist/commands/sandbox/review/index.d.ts +13 -0
- package/dist/commands/sandbox/review/index.js +92 -0
- package/dist/commands/sandbox/unit_test/list/index.d.ts +13 -0
- package/dist/commands/sandbox/unit_test/list/index.js +89 -0
- package/dist/commands/sandbox/unit_test/run/index.d.ts +14 -0
- package/dist/commands/sandbox/unit_test/run/index.js +77 -0
- package/dist/commands/sandbox/unit_test/run_all/index.d.ts +13 -0
- package/dist/commands/sandbox/unit_test/run_all/index.js +167 -0
- package/dist/commands/sandbox/workflow_test/delete/index.d.ts +17 -0
- package/dist/commands/sandbox/workflow_test/delete/index.js +59 -0
- package/dist/commands/sandbox/workflow_test/get/index.d.ts +17 -0
- package/dist/commands/sandbox/workflow_test/get/index.js +58 -0
- package/dist/commands/sandbox/workflow_test/list/index.d.ts +12 -0
- package/dist/commands/sandbox/workflow_test/list/index.js +82 -0
- package/dist/commands/sandbox/workflow_test/run/index.d.ts +17 -0
- package/dist/commands/sandbox/workflow_test/run/index.js +75 -0
- package/dist/commands/sandbox/workflow_test/run_all/index.d.ts +12 -0
- package/dist/commands/sandbox/workflow_test/run_all/index.js +153 -0
- package/dist/commands/tenant/create/index.d.ts +2 -2
- package/dist/commands/tenant/create/index.js +23 -11
- package/dist/commands/tenant/get/index.js +2 -2
- package/dist/commands/tenant/list/index.js +2 -2
- package/dist/commands/tenant/push/index.js +0 -34
- package/dist/commands/tenant/unit_test/list/index.d.ts +15 -0
- package/dist/commands/tenant/unit_test/list/index.js +140 -0
- package/dist/commands/tenant/unit_test/run/index.d.ts +16 -0
- package/dist/commands/tenant/unit_test/run/index.js +128 -0
- package/dist/commands/tenant/unit_test/run_all/index.d.ts +15 -0
- package/dist/commands/tenant/unit_test/run_all/index.js +215 -0
- package/dist/commands/tenant/workflow_test/delete/index.d.ts +19 -0
- package/dist/commands/tenant/workflow_test/delete/index.js +110 -0
- package/dist/commands/tenant/workflow_test/get/index.d.ts +19 -0
- package/dist/commands/tenant/workflow_test/get/index.js +112 -0
- package/dist/commands/tenant/workflow_test/list/index.d.ts +14 -0
- package/dist/commands/tenant/workflow_test/list/index.js +133 -0
- package/dist/commands/tenant/workflow_test/run/index.d.ts +19 -0
- package/dist/commands/tenant/workflow_test/run/index.js +126 -0
- package/dist/commands/tenant/workflow_test/run_all/index.d.ts +14 -0
- package/dist/commands/tenant/workflow_test/run_all/index.js +201 -0
- package/dist/commands/workspace/edit/index.d.ts +1 -0
- package/dist/commands/workspace/edit/index.js +16 -6
- package/dist/commands/workspace/get/index.js +9 -7
- package/dist/commands/workspace/list/index.d.ts +1 -0
- package/dist/commands/workspace/list/index.js +14 -7
- package/dist/commands/workspace/push/index.js +30 -2
- package/dist/help.d.ts +2 -1
- package/dist/help.js +39 -1
- package/oclif.manifest.json +4701 -2272
- package/package.json +17 -2
package/README.md
CHANGED
|
@@ -235,6 +235,11 @@ xano release pull ./my-release -r v1.0 --env --records
|
|
|
235
235
|
xano release push ./my-release -n "v2.0"
|
|
236
236
|
xano release push ./my-release -n "v2.0" --hotfix -d "Critical fix"
|
|
237
237
|
xano release push ./my-release -n "v2.0" --no-records --no-env
|
|
238
|
+
|
|
239
|
+
# Deploy a release to its workspace as a new branch
|
|
240
|
+
xano release deploy "v1.0"
|
|
241
|
+
xano release deploy "v1.0" --branch "restore-v1" --no-set_live
|
|
242
|
+
xano release deploy "v1.0" -w 40 -o json
|
|
238
243
|
```
|
|
239
244
|
|
|
240
245
|
### Platforms
|
|
@@ -302,7 +307,8 @@ xano tenant get <tenant_name>
|
|
|
302
307
|
|
|
303
308
|
# Create a tenant
|
|
304
309
|
xano tenant create "My Tenant"
|
|
305
|
-
xano tenant create "My Tenant" -d "Description" --cluster_id 1 --platform_id 5
|
|
310
|
+
xano tenant create "My Tenant" -d "Description" --type tier2 --cluster_id 1 --platform_id 5
|
|
311
|
+
xano tenant create "My Tenant" --type tier2 --cluster_id 1 --license ./license.yaml
|
|
306
312
|
|
|
307
313
|
# Edit a tenant
|
|
308
314
|
xano tenant edit <tenant_name> --display "New Name" -d "New description"
|
|
@@ -440,6 +446,27 @@ xano tenant cluster license set <cluster_id>
|
|
|
440
446
|
xano tenant cluster license set <cluster_id> --file ./kubeconfig.yaml
|
|
441
447
|
```
|
|
442
448
|
|
|
449
|
+
### Sandbox
|
|
450
|
+
|
|
451
|
+
Manage your sandbox tenant. Each user has a single sandbox tenant that is auto-provisioned on first use.
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
# Get your sandbox tenant (creates if needed)
|
|
455
|
+
xano sandbox get
|
|
456
|
+
xano sandbox get -o json
|
|
457
|
+
|
|
458
|
+
# Push/pull workspace data
|
|
459
|
+
xano sandbox push ./my-workspace
|
|
460
|
+
xano sandbox pull ./my-tenant
|
|
461
|
+
|
|
462
|
+
# Impersonate (open in browser)
|
|
463
|
+
xano sandbox impersonate
|
|
464
|
+
|
|
465
|
+
# Reset all workspace data
|
|
466
|
+
xano sandbox reset
|
|
467
|
+
xano sandbox reset --force
|
|
468
|
+
```
|
|
469
|
+
|
|
443
470
|
### Static Hosts
|
|
444
471
|
|
|
445
472
|
```bash
|
package/dist/base-command.d.ts
CHANGED
|
@@ -14,6 +14,17 @@ export interface CredentialsFile {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
export declare function buildUserAgent(version: string): string;
|
|
17
|
+
export interface SandboxTenant {
|
|
18
|
+
created_at?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
display?: string;
|
|
21
|
+
ephemeral?: boolean;
|
|
22
|
+
id: number;
|
|
23
|
+
name: string;
|
|
24
|
+
sandbox_expires_at?: string | number;
|
|
25
|
+
state?: string;
|
|
26
|
+
xano_domain?: string;
|
|
27
|
+
}
|
|
17
28
|
export default abstract class BaseCommand extends Command {
|
|
18
29
|
static baseFlags: {
|
|
19
30
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -38,6 +49,20 @@ export default abstract class BaseCommand extends Command {
|
|
|
38
49
|
* Load and parse the credentials file. Returns null if the file doesn't exist.
|
|
39
50
|
*/
|
|
40
51
|
protected loadCredentialsFile(): CredentialsFile | null;
|
|
52
|
+
/**
|
|
53
|
+
* Get or create the singleton sandbox environment for the authenticated user.
|
|
54
|
+
* Returns the sandbox object (existing or newly created).
|
|
55
|
+
*/
|
|
56
|
+
protected getOrCreateSandbox(profile: ProfileConfig, verbose: boolean): Promise<SandboxTenant>;
|
|
57
|
+
/**
|
|
58
|
+
* Resolve profile from flags, validating instance_origin and access_token exist.
|
|
59
|
+
*/
|
|
60
|
+
protected resolveProfile(flags: {
|
|
61
|
+
profile?: string;
|
|
62
|
+
}): {
|
|
63
|
+
profile: ProfileConfig;
|
|
64
|
+
profileName: string;
|
|
65
|
+
};
|
|
41
66
|
/**
|
|
42
67
|
* Make an HTTP request with optional verbose logging.
|
|
43
68
|
* Use this for all Metadata API calls to support the --verbose flag.
|
package/dist/base-command.js
CHANGED
|
@@ -103,6 +103,43 @@ export default class BaseCommand extends Command {
|
|
|
103
103
|
}
|
|
104
104
|
return null;
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Get or create the singleton sandbox environment for the authenticated user.
|
|
108
|
+
* Returns the sandbox object (existing or newly created).
|
|
109
|
+
*/
|
|
110
|
+
async getOrCreateSandbox(profile, verbose) {
|
|
111
|
+
const apiUrl = `${profile.instance_origin}/api:meta/sandbox/me`;
|
|
112
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
113
|
+
headers: {
|
|
114
|
+
accept: 'application/json',
|
|
115
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
116
|
+
},
|
|
117
|
+
method: 'GET',
|
|
118
|
+
}, verbose, profile.access_token);
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const errorText = await response.text();
|
|
121
|
+
this.error(`Failed to get sandbox environment: ${response.status} ${response.statusText}\n${errorText}`);
|
|
122
|
+
}
|
|
123
|
+
return (await response.json());
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Resolve profile from flags, validating instance_origin and access_token exist.
|
|
127
|
+
*/
|
|
128
|
+
resolveProfile(flags) {
|
|
129
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
130
|
+
const credentials = this.loadCredentialsFile();
|
|
131
|
+
if (!credentials || !(profileName in credentials.profiles)) {
|
|
132
|
+
this.error(`Profile '${profileName}' not found.\n` + `Create a profile using 'xano profile create'`);
|
|
133
|
+
}
|
|
134
|
+
const profile = credentials.profiles[profileName];
|
|
135
|
+
if (!profile.instance_origin) {
|
|
136
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
137
|
+
}
|
|
138
|
+
if (!profile.access_token) {
|
|
139
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
140
|
+
}
|
|
141
|
+
return { profile, profileName };
|
|
142
|
+
}
|
|
106
143
|
/**
|
|
107
144
|
* Make an HTTP request with optional verbose logging.
|
|
108
145
|
* Use this for all Metadata API calls to support the --verbose flag.
|
|
@@ -98,8 +98,8 @@ Profile 'selfhosted' created successfully at ~/.xano/credentials.yaml
|
|
|
98
98
|
const profileExists = args.name in credentials.profiles;
|
|
99
99
|
credentials.profiles[args.name] = {
|
|
100
100
|
access_token: flags.access_token,
|
|
101
|
-
account_origin: flags.account_origin ?? '',
|
|
102
|
-
instance_origin: flags.instance_origin,
|
|
101
|
+
account_origin: (flags.account_origin ?? '').replace(/\/+$/, ''),
|
|
102
|
+
instance_origin: flags.instance_origin.replace(/\/+$/, ''),
|
|
103
103
|
...(flags.workspace && { workspace: flags.workspace }),
|
|
104
104
|
...(flags.branch && { branch: flags.branch }),
|
|
105
105
|
...(flags.insecure && { insecure: true }),
|
|
@@ -128,8 +128,8 @@ Profile 'default' updated successfully at ~/.xano/credentials.yaml
|
|
|
128
128
|
// Update only the fields that were provided
|
|
129
129
|
const updatedProfile = {
|
|
130
130
|
...existingProfile,
|
|
131
|
-
...(flags.account_origin !== undefined && { account_origin: flags.account_origin }),
|
|
132
|
-
...(flags.instance_origin !== undefined && { instance_origin: flags.instance_origin }),
|
|
131
|
+
...(flags.account_origin !== undefined && { account_origin: flags.account_origin.replace(/\/+$/, '') }),
|
|
132
|
+
...(flags.instance_origin !== undefined && { instance_origin: flags.instance_origin.replace(/\/+$/, '') }),
|
|
133
133
|
...(flags.access_token !== undefined && { access_token: flags.access_token }),
|
|
134
134
|
...(flags.workspace !== undefined && { workspace: flags.workspace }),
|
|
135
135
|
...(flags.branch !== undefined && { branch: flags.branch }),
|
|
@@ -111,8 +111,27 @@ User Information:
|
|
|
111
111
|
this.log(` Name: ${inst.name}`);
|
|
112
112
|
if (inst.display)
|
|
113
113
|
this.log(` Display: ${inst.display}`);
|
|
114
|
-
if (profile.workspace)
|
|
115
|
-
|
|
114
|
+
if (profile.workspace) {
|
|
115
|
+
let wsLabel = String(profile.workspace);
|
|
116
|
+
try {
|
|
117
|
+
const wsResponse = await this.verboseFetch(`${profile.instance_origin}/api:meta/workspace/${profile.workspace}`, {
|
|
118
|
+
headers: {
|
|
119
|
+
accept: 'application/json',
|
|
120
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
121
|
+
},
|
|
122
|
+
method: 'GET',
|
|
123
|
+
}, false, profile.access_token);
|
|
124
|
+
if (wsResponse.ok) {
|
|
125
|
+
const ws = (await wsResponse.json());
|
|
126
|
+
if (ws.name)
|
|
127
|
+
wsLabel = `${ws.name} (ID: ${profile.workspace})`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Fall back to just showing the ID
|
|
132
|
+
}
|
|
133
|
+
this.log(` Workspace: ${wsLabel}`);
|
|
134
|
+
}
|
|
116
135
|
if (profile.branch)
|
|
117
136
|
this.log(` Branch: ${profile.branch}`);
|
|
118
137
|
}
|
|
@@ -126,7 +126,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
126
126
|
choices: [
|
|
127
127
|
{ name: '(Skip workspace)', value: '' },
|
|
128
128
|
...workspaces.map((ws) => ({
|
|
129
|
-
name: ws.name
|
|
129
|
+
name: `${ws.name} (${ws.id})`,
|
|
130
130
|
value: ws.id,
|
|
131
131
|
})),
|
|
132
132
|
],
|
|
@@ -339,8 +339,8 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
339
339
|
// Add or update the profile
|
|
340
340
|
credentials.profiles[profile.name] = {
|
|
341
341
|
access_token: profile.access_token,
|
|
342
|
-
account_origin: profile.account_origin,
|
|
343
|
-
instance_origin: profile.instance_origin,
|
|
342
|
+
account_origin: (profile.account_origin || '').replace(/\/+$/, ''),
|
|
343
|
+
instance_origin: profile.instance_origin.replace(/\/+$/, ''),
|
|
344
344
|
...(profile.workspace && { workspace: profile.workspace }),
|
|
345
345
|
...(profile.branch && { branch: profile.branch }),
|
|
346
346
|
...(profile.insecure && { insecure: true }),
|
|
@@ -40,7 +40,7 @@ Workspace updated to 'Production API' (xyz789) on profile 'production'
|
|
|
40
40
|
const { selectedWorkspace } = await inquirer.prompt([
|
|
41
41
|
{
|
|
42
42
|
choices: workspaces.map((ws) => ({
|
|
43
|
-
name: String(ws.id) === String(profile.workspace) ? `${ws.name} (current)` : ws.name
|
|
43
|
+
name: String(ws.id) === String(profile.workspace) ? `${ws.name} (${ws.id}) (current)` : `${ws.name} (${ws.id})`,
|
|
44
44
|
value: ws.id,
|
|
45
45
|
})),
|
|
46
46
|
message: 'Select a workspace',
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import BaseCommand from '../../../base-command.js';
|
|
2
|
+
export default class ReleaseDeploy extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
release_name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
set_live: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../base-command.js';
|
|
3
|
+
export default class ReleaseDeploy extends BaseCommand {
|
|
4
|
+
static args = {
|
|
5
|
+
release_name: Args.string({
|
|
6
|
+
description: 'Name of the release to deploy',
|
|
7
|
+
required: true,
|
|
8
|
+
}),
|
|
9
|
+
};
|
|
10
|
+
static description = 'Deploy a release to its workspace as a new branch';
|
|
11
|
+
static examples = [
|
|
12
|
+
`$ xano release deploy "v1.0"
|
|
13
|
+
Deployed release "v1.0" to workspace 40 (branch: v1.0, set live)
|
|
14
|
+
`,
|
|
15
|
+
`$ xano release deploy "v1.0" --branch "restore-v1" --no-set_live`,
|
|
16
|
+
`$ xano release deploy "v1.0" -w 40 -o json`,
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
...BaseCommand.baseFlags,
|
|
20
|
+
branch: Flags.string({
|
|
21
|
+
char: 'b',
|
|
22
|
+
description: 'Branch label for the new branch (defaults to release branch name)',
|
|
23
|
+
required: false,
|
|
24
|
+
}),
|
|
25
|
+
output: Flags.string({
|
|
26
|
+
char: 'o',
|
|
27
|
+
default: 'summary',
|
|
28
|
+
description: 'Output format',
|
|
29
|
+
options: ['summary', 'json'],
|
|
30
|
+
required: false,
|
|
31
|
+
}),
|
|
32
|
+
set_live: Flags.boolean({
|
|
33
|
+
default: false,
|
|
34
|
+
description: 'Set the new branch as live',
|
|
35
|
+
required: false,
|
|
36
|
+
}),
|
|
37
|
+
workspace: Flags.string({
|
|
38
|
+
char: 'w',
|
|
39
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
40
|
+
required: false,
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
async run() {
|
|
44
|
+
const { args, flags } = await this.parse(ReleaseDeploy);
|
|
45
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
46
|
+
const credentials = this.loadCredentialsFile();
|
|
47
|
+
if (!credentials || !(profileName in credentials.profiles)) {
|
|
48
|
+
this.error(`Profile '${profileName}' not found.\n` + `Create a profile using 'xano profile create'`);
|
|
49
|
+
}
|
|
50
|
+
const profile = credentials.profiles[profileName];
|
|
51
|
+
if (!profile.instance_origin) {
|
|
52
|
+
this.error(`Profile '${profileName}' is missing instance_origin`);
|
|
53
|
+
}
|
|
54
|
+
if (!profile.access_token) {
|
|
55
|
+
this.error(`Profile '${profileName}' is missing access_token`);
|
|
56
|
+
}
|
|
57
|
+
const workspaceId = flags.workspace || profile.workspace;
|
|
58
|
+
if (!workspaceId) {
|
|
59
|
+
this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
|
|
60
|
+
}
|
|
61
|
+
const releaseName = encodeURIComponent(args.release_name);
|
|
62
|
+
const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/release/${releaseName}/deploy`;
|
|
63
|
+
const body = {
|
|
64
|
+
set_live: flags.set_live,
|
|
65
|
+
};
|
|
66
|
+
if (flags.branch)
|
|
67
|
+
body.branch = flags.branch;
|
|
68
|
+
this.warn('This may take a few minutes. Please be patient.');
|
|
69
|
+
const startTime = Date.now();
|
|
70
|
+
try {
|
|
71
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
72
|
+
body: JSON.stringify(body),
|
|
73
|
+
headers: {
|
|
74
|
+
accept: 'application/json',
|
|
75
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
76
|
+
'Content-Type': 'application/json',
|
|
77
|
+
},
|
|
78
|
+
method: 'POST',
|
|
79
|
+
}, flags.verbose, profile.access_token);
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
const errorText = await response.text();
|
|
82
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
83
|
+
}
|
|
84
|
+
const release = (await response.json());
|
|
85
|
+
if (flags.output === 'json') {
|
|
86
|
+
this.log(JSON.stringify(release, null, 2));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
90
|
+
const branchLabel = flags.branch || release.branch || 'default';
|
|
91
|
+
const liveStatus = flags.set_live ? ', set live' : '';
|
|
92
|
+
this.log(`Deployed release "${release.name}" to workspace ${workspaceId} (branch: ${branchLabel}${liveStatus})`);
|
|
93
|
+
if (release.description)
|
|
94
|
+
this.log(` Description: ${release.description}`);
|
|
95
|
+
this.log(` Time: ${elapsed}s`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error instanceof Error) {
|
|
100
|
+
this.error(`Failed to deploy release: ${error.message}`);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
this.error(`Failed to deploy release: ${String(error)}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class SandboxEnvDelete extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
private confirm;
|
|
14
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../base-command.js';
|
|
3
|
+
export default class SandboxEnvDelete extends BaseCommand {
|
|
4
|
+
static description = 'Delete an environment variable from a sandbox environment';
|
|
5
|
+
static examples = [
|
|
6
|
+
`$ xano sandbox env delete --name DATABASE_URL
|
|
7
|
+
Are you sure you want to delete environment variable 'DATABASE_URL'? (y/N) y
|
|
8
|
+
Environment variable 'DATABASE_URL' deleted
|
|
9
|
+
`,
|
|
10
|
+
`$ xano sandbox env delete --name DATABASE_URL --force`,
|
|
11
|
+
`$ xano sandbox env delete --name DATABASE_URL -f -o json`,
|
|
12
|
+
];
|
|
13
|
+
static flags = {
|
|
14
|
+
...BaseCommand.baseFlags,
|
|
15
|
+
force: Flags.boolean({
|
|
16
|
+
char: 'f',
|
|
17
|
+
default: false,
|
|
18
|
+
description: 'Skip confirmation prompt',
|
|
19
|
+
required: false,
|
|
20
|
+
}),
|
|
21
|
+
name: Flags.string({
|
|
22
|
+
char: 'n',
|
|
23
|
+
description: 'Environment variable name',
|
|
24
|
+
required: true,
|
|
25
|
+
}),
|
|
26
|
+
output: Flags.string({
|
|
27
|
+
char: 'o',
|
|
28
|
+
default: 'summary',
|
|
29
|
+
description: 'Output format',
|
|
30
|
+
options: ['summary', 'json'],
|
|
31
|
+
required: false,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
async run() {
|
|
35
|
+
const { flags } = await this.parse(SandboxEnvDelete);
|
|
36
|
+
const { profile } = this.resolveProfile(flags);
|
|
37
|
+
const envName = flags.name;
|
|
38
|
+
if (!flags.force) {
|
|
39
|
+
const confirmed = await this.confirm(`Are you sure you want to delete environment variable '${envName}' from sandbox environment?`);
|
|
40
|
+
if (!confirmed) {
|
|
41
|
+
this.log('Deletion cancelled.');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const apiUrl = `${profile.instance_origin}/api:meta/sandbox/env/${envName}`;
|
|
46
|
+
try {
|
|
47
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
48
|
+
headers: {
|
|
49
|
+
accept: 'application/json',
|
|
50
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
51
|
+
},
|
|
52
|
+
method: 'DELETE',
|
|
53
|
+
}, flags.verbose, profile.access_token);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const errorText = await response.text();
|
|
56
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
57
|
+
}
|
|
58
|
+
if (flags.output === 'json') {
|
|
59
|
+
this.log(JSON.stringify({ deleted: true, env_name: envName }, null, 2));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
this.log(`Environment variable '${envName}' deleted from sandbox environment`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error instanceof Error) {
|
|
67
|
+
this.error(`Failed to delete sandbox environment variable: ${error.message}`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.error(`Failed to delete sandbox environment variable: ${String(error)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async confirm(message) {
|
|
75
|
+
const readline = await import('node:readline');
|
|
76
|
+
const rl = readline.createInterface({
|
|
77
|
+
input: process.stdin,
|
|
78
|
+
output: process.stdout,
|
|
79
|
+
});
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
rl.question(`${message} (y/N) `, (answer) => {
|
|
82
|
+
rl.close();
|
|
83
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class SandboxEnvGet extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import BaseCommand from '../../../../base-command.js';
|
|
3
|
+
export default class SandboxEnvGet extends BaseCommand {
|
|
4
|
+
static description = 'Get a single environment variable for a sandbox environment';
|
|
5
|
+
static examples = [
|
|
6
|
+
`$ xano sandbox env get --name DATABASE_URL
|
|
7
|
+
postgres://localhost:5432/mydb
|
|
8
|
+
`,
|
|
9
|
+
`$ xano sandbox env get --name DATABASE_URL -o json`,
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
...BaseCommand.baseFlags,
|
|
13
|
+
name: Flags.string({
|
|
14
|
+
char: 'n',
|
|
15
|
+
description: 'Environment variable name',
|
|
16
|
+
required: true,
|
|
17
|
+
}),
|
|
18
|
+
output: Flags.string({
|
|
19
|
+
char: 'o',
|
|
20
|
+
default: 'summary',
|
|
21
|
+
description: 'Output format',
|
|
22
|
+
options: ['summary', 'json'],
|
|
23
|
+
required: false,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(SandboxEnvGet);
|
|
28
|
+
const { profile } = this.resolveProfile(flags);
|
|
29
|
+
const envName = flags.name;
|
|
30
|
+
const apiUrl = `${profile.instance_origin}/api:meta/sandbox/env/${envName}`;
|
|
31
|
+
try {
|
|
32
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
33
|
+
headers: {
|
|
34
|
+
accept: 'application/json',
|
|
35
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
36
|
+
},
|
|
37
|
+
method: 'GET',
|
|
38
|
+
}, flags.verbose, profile.access_token);
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
const errorText = await response.text();
|
|
41
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
42
|
+
}
|
|
43
|
+
const envVar = (await response.json());
|
|
44
|
+
if (flags.output === 'json') {
|
|
45
|
+
this.log(JSON.stringify(envVar, null, 2));
|
|
46
|
+
}
|
|
47
|
+
else if (envVar) {
|
|
48
|
+
this.log(envVar.value);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.log(`Environment variable '${envName}' not found for sandbox environment`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof Error) {
|
|
56
|
+
this.error(`Failed to get sandbox environment variable: ${error.message}`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.error(`Failed to get sandbox environment variable: ${String(error)}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class SandboxEnvGetAll extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
view: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import BaseCommand from '../../../../base-command.js';
|
|
6
|
+
export default class SandboxEnvGetAll extends BaseCommand {
|
|
7
|
+
static description = 'Get all environment variables for a sandbox environment and save to a YAML file';
|
|
8
|
+
static examples = [
|
|
9
|
+
`$ xano sandbox env get_all
|
|
10
|
+
Environment variables saved to env_<tenant>.yaml
|
|
11
|
+
`,
|
|
12
|
+
`$ xano sandbox env get_all --file ./my-env.yaml`,
|
|
13
|
+
`$ xano sandbox env get_all --view`,
|
|
14
|
+
`$ xano sandbox env get_all -o json`,
|
|
15
|
+
];
|
|
16
|
+
static flags = {
|
|
17
|
+
...BaseCommand.baseFlags,
|
|
18
|
+
file: Flags.string({
|
|
19
|
+
char: 'f',
|
|
20
|
+
description: 'Output file path (default: env_<sandbox_name>.yaml)',
|
|
21
|
+
required: false,
|
|
22
|
+
}),
|
|
23
|
+
output: Flags.string({
|
|
24
|
+
char: 'o',
|
|
25
|
+
default: 'summary',
|
|
26
|
+
description: 'Output format',
|
|
27
|
+
options: ['summary', 'json'],
|
|
28
|
+
required: false,
|
|
29
|
+
}),
|
|
30
|
+
view: Flags.boolean({
|
|
31
|
+
default: false,
|
|
32
|
+
description: 'Print environment variables to stdout instead of saving to file',
|
|
33
|
+
required: false,
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
async run() {
|
|
37
|
+
const { flags } = await this.parse(SandboxEnvGetAll);
|
|
38
|
+
const { profile } = this.resolveProfile(flags);
|
|
39
|
+
const apiUrl = `${profile.instance_origin}/api:meta/sandbox/env_all`;
|
|
40
|
+
try {
|
|
41
|
+
const response = await this.verboseFetch(apiUrl, {
|
|
42
|
+
headers: {
|
|
43
|
+
accept: 'application/json',
|
|
44
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
45
|
+
},
|
|
46
|
+
method: 'GET',
|
|
47
|
+
}, flags.verbose, profile.access_token);
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
const errorText = await response.text();
|
|
50
|
+
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
51
|
+
}
|
|
52
|
+
const envMap = (await response.json());
|
|
53
|
+
if (flags.output === 'json') {
|
|
54
|
+
this.log(JSON.stringify(envMap, null, 2));
|
|
55
|
+
}
|
|
56
|
+
else if (flags.view) {
|
|
57
|
+
const envYaml = yaml.dump(envMap, { lineWidth: -1, sortKeys: true });
|
|
58
|
+
this.log(envYaml.trimEnd());
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const filePath = path.resolve(flags.file || `env.yaml`);
|
|
62
|
+
const envYaml = yaml.dump(envMap, { lineWidth: -1, sortKeys: true });
|
|
63
|
+
fs.writeFileSync(filePath, envYaml, 'utf8');
|
|
64
|
+
this.log(`Environment variables saved to ${filePath}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error instanceof Error) {
|
|
69
|
+
this.error(`Failed to get sandbox environment variables: ${error.message}`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.error(`Failed to get sandbox environment variables: ${String(error)}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import BaseCommand from '../../../../base-command.js';
|
|
2
|
+
export default class SandboxEnvList extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|