@xano/cli 0.0.32 → 0.0.34
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/dist/commands/tenant/backup/create/index.d.ts +1 -4
- package/dist/commands/tenant/backup/create/index.js +8 -8
- package/dist/commands/tenant/backup/delete/index.d.ts +1 -4
- package/dist/commands/tenant/backup/delete/index.js +8 -8
- package/dist/commands/tenant/backup/export/index.d.ts +1 -4
- package/dist/commands/tenant/backup/export/index.js +11 -11
- package/dist/commands/tenant/backup/import/index.d.ts +1 -4
- package/dist/commands/tenant/backup/import/index.js +8 -8
- package/dist/commands/tenant/backup/list/index.d.ts +1 -4
- package/dist/commands/tenant/backup/list/index.js +9 -9
- package/dist/commands/tenant/backup/restore/index.d.ts +1 -4
- package/dist/commands/tenant/backup/restore/index.js +10 -10
- package/dist/commands/tenant/delete/index.d.ts +1 -4
- package/dist/commands/tenant/delete/index.js +13 -13
- package/dist/commands/tenant/{deploy-platform → deploy_platform}/index.d.ts +1 -4
- package/dist/commands/tenant/{deploy-platform → deploy_platform}/index.js +7 -7
- package/dist/commands/tenant/{deploy-release → deploy_release}/index.d.ts +1 -4
- package/dist/commands/tenant/{deploy-release → deploy_release}/index.js +6 -6
- package/dist/commands/tenant/edit/index.d.ts +1 -4
- package/dist/commands/tenant/edit/index.js +6 -6
- package/dist/commands/tenant/get/index.d.ts +1 -4
- package/dist/commands/tenant/get/index.js +8 -8
- package/dist/commands/tenant/list/index.js +1 -1
- package/dist/commands/workflow_test/delete/index.d.ts +21 -0
- package/dist/commands/workflow_test/delete/index.js +130 -0
- package/dist/commands/workflow_test/get/index.d.ts +20 -0
- package/dist/commands/workflow_test/get/index.js +142 -0
- package/dist/commands/workflow_test/list/index.d.ts +14 -0
- package/dist/commands/workflow_test/list/index.js +129 -0
- package/dist/commands/workflow_test/run/index.d.ts +19 -0
- package/dist/commands/workflow_test/run/index.js +119 -0
- package/dist/commands/workflow_test/run_all/index.d.ts +14 -0
- package/dist/commands/workflow_test/run_all/index.js +204 -0
- package/dist/commands/workspace/create/index.d.ts +3 -1
- package/dist/commands/workspace/create/index.js +12 -11
- package/oclif.manifest.json +1992 -1596
- package/package.json +4 -1
- /package/dist/commands/branch/{set-live → set_live}/index.d.ts +0 -0
- /package/dist/commands/branch/{set-live → set_live}/index.js +0 -0
- /package/dist/commands/profile/{get-default → get_default}/index.d.ts +0 -0
- /package/dist/commands/profile/{get-default → get_default}/index.js +0 -0
- /package/dist/commands/profile/{set-default → set_default}/index.d.ts +0 -0
- /package/dist/commands/profile/{set-default → set_default}/index.js +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import BaseCommand from '../../../base-command.js';
|
|
7
|
+
export default class WorkflowTestRunAll extends BaseCommand {
|
|
8
|
+
static description = 'Run all workflow tests in a workspace';
|
|
9
|
+
static examples = [
|
|
10
|
+
`$ xano workflow-test run-all
|
|
11
|
+
Running 3 workflow tests...
|
|
12
|
+
|
|
13
|
+
PASS my-test (1.234s)
|
|
14
|
+
PASS auth-flow (0.567s)
|
|
15
|
+
FAIL data-validation (0.890s)
|
|
16
|
+
Error: assertion failed at step 3
|
|
17
|
+
|
|
18
|
+
Results: 2 passed, 1 failed (2.691s total)
|
|
19
|
+
`,
|
|
20
|
+
`$ xano workflow-test run-all --branch main -o json`,
|
|
21
|
+
];
|
|
22
|
+
static flags = {
|
|
23
|
+
...BaseCommand.baseFlags,
|
|
24
|
+
branch: Flags.string({
|
|
25
|
+
char: 'b',
|
|
26
|
+
description: 'Filter by branch name',
|
|
27
|
+
required: false,
|
|
28
|
+
}),
|
|
29
|
+
output: Flags.string({
|
|
30
|
+
char: 'o',
|
|
31
|
+
default: 'summary',
|
|
32
|
+
description: 'Output format',
|
|
33
|
+
options: ['summary', 'json'],
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
workspace: Flags.string({
|
|
37
|
+
char: 'w',
|
|
38
|
+
description: 'Workspace ID (uses profile workspace if not provided)',
|
|
39
|
+
required: false,
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
async run() {
|
|
43
|
+
const { flags } = await this.parse(WorkflowTestRunAll);
|
|
44
|
+
const profileName = flags.profile || this.getDefaultProfile();
|
|
45
|
+
const credentials = this.loadCredentials();
|
|
46
|
+
if (!(profileName in credentials.profiles)) {
|
|
47
|
+
this.error(`Profile '${profileName}' not found. Available profiles: ${Object.keys(credentials.profiles).join(', ')}\n` +
|
|
48
|
+
`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 baseUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/workflow_test`;
|
|
62
|
+
try {
|
|
63
|
+
// Step 1: List all workflow tests
|
|
64
|
+
const listParams = new URLSearchParams({ include_xanoscript: 'false' });
|
|
65
|
+
if (flags.branch) {
|
|
66
|
+
listParams.set('branch', flags.branch);
|
|
67
|
+
}
|
|
68
|
+
const listResponse = await this.verboseFetch(`${baseUrl}?${listParams}`, {
|
|
69
|
+
headers: {
|
|
70
|
+
'accept': 'application/json',
|
|
71
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
72
|
+
},
|
|
73
|
+
method: 'GET',
|
|
74
|
+
}, flags.verbose, profile.access_token);
|
|
75
|
+
if (!listResponse.ok) {
|
|
76
|
+
const errorText = await listResponse.text();
|
|
77
|
+
this.error(`Failed to list workflow tests: ${listResponse.status}: ${listResponse.statusText}\n${errorText}`);
|
|
78
|
+
}
|
|
79
|
+
const data = await listResponse.json();
|
|
80
|
+
let tests;
|
|
81
|
+
if (Array.isArray(data)) {
|
|
82
|
+
tests = data;
|
|
83
|
+
}
|
|
84
|
+
else if (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items)) {
|
|
85
|
+
tests = data.items;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.error('Unexpected API response format');
|
|
89
|
+
}
|
|
90
|
+
if (tests.length === 0) {
|
|
91
|
+
this.log('No workflow tests found');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (flags.output === 'summary') {
|
|
95
|
+
this.log(`Running ${tests.length} workflow test${tests.length === 1 ? '' : 's'}...\n`);
|
|
96
|
+
}
|
|
97
|
+
// Step 2: Run each test
|
|
98
|
+
const results = [];
|
|
99
|
+
let totalTiming = 0;
|
|
100
|
+
for (const test of tests) {
|
|
101
|
+
const runUrl = `${baseUrl}/${test.id}/run`;
|
|
102
|
+
try {
|
|
103
|
+
const runResponse = await this.verboseFetch(runUrl, {
|
|
104
|
+
headers: {
|
|
105
|
+
'accept': 'application/json',
|
|
106
|
+
'Authorization': `Bearer ${profile.access_token}`,
|
|
107
|
+
'Content-Type': 'application/json',
|
|
108
|
+
},
|
|
109
|
+
method: 'POST',
|
|
110
|
+
}, flags.verbose, profile.access_token);
|
|
111
|
+
if (!runResponse.ok) {
|
|
112
|
+
const errorText = await runResponse.text();
|
|
113
|
+
const result = {
|
|
114
|
+
message: `API error ${runResponse.status}: ${errorText}`,
|
|
115
|
+
name: test.name,
|
|
116
|
+
status: 'fail',
|
|
117
|
+
timing: 0,
|
|
118
|
+
};
|
|
119
|
+
results.push(result);
|
|
120
|
+
if (flags.output === 'summary') {
|
|
121
|
+
this.log(`FAIL ${test.name} (0.000s)`);
|
|
122
|
+
this.log(` Error: API error ${runResponse.status}`);
|
|
123
|
+
}
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const runResult = await runResponse.json();
|
|
127
|
+
const passed = runResult.status === 'ok';
|
|
128
|
+
const result = {
|
|
129
|
+
message: runResult.message,
|
|
130
|
+
name: test.name,
|
|
131
|
+
status: passed ? 'pass' : 'fail',
|
|
132
|
+
timing: runResult.timing,
|
|
133
|
+
};
|
|
134
|
+
results.push(result);
|
|
135
|
+
totalTiming += runResult.timing;
|
|
136
|
+
if (flags.output === 'summary') {
|
|
137
|
+
const timing = `(${runResult.timing.toFixed(3)}s)`;
|
|
138
|
+
if (passed) {
|
|
139
|
+
this.log(`PASS ${test.name} ${timing}`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.log(`FAIL ${test.name} ${timing}`);
|
|
143
|
+
if (runResult.message) {
|
|
144
|
+
this.log(` Error: ${runResult.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
151
|
+
results.push({
|
|
152
|
+
message,
|
|
153
|
+
name: test.name,
|
|
154
|
+
status: 'fail',
|
|
155
|
+
timing: 0,
|
|
156
|
+
});
|
|
157
|
+
if (flags.output === 'summary') {
|
|
158
|
+
this.log(`FAIL ${test.name} (0.000s)`);
|
|
159
|
+
this.log(` Error: ${message}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Step 3: Summary
|
|
164
|
+
const passed = results.filter(r => r.status === 'pass').length;
|
|
165
|
+
const failed = results.filter(r => r.status === 'fail').length;
|
|
166
|
+
if (flags.output === 'json') {
|
|
167
|
+
this.log(JSON.stringify({ passed, failed, total_timing: totalTiming, results }, null, 2));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
this.log(`\nResults: ${passed} passed, ${failed} failed (${totalTiming.toFixed(3)}s total)`);
|
|
171
|
+
}
|
|
172
|
+
if (failed > 0) {
|
|
173
|
+
this.exit(1);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
if (error instanceof Error) {
|
|
178
|
+
this.error(`Failed to run workflow tests: ${error.message}`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this.error(`Failed to run workflow tests: ${String(error)}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
loadCredentials() {
|
|
186
|
+
const configDir = path.join(os.homedir(), '.xano');
|
|
187
|
+
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
188
|
+
if (!fs.existsSync(credentialsPath)) {
|
|
189
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
190
|
+
`Create a profile using 'xano profile create'`);
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
194
|
+
const parsed = yaml.load(fileContent);
|
|
195
|
+
if (!parsed || typeof parsed !== 'object' || !('profiles' in parsed)) {
|
|
196
|
+
this.error('Credentials file has invalid format.');
|
|
197
|
+
}
|
|
198
|
+
return parsed;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
this.error(`Failed to parse credentials file: ${error}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import BaseCommand from '../../../base-command.js';
|
|
2
2
|
export default class WorkspaceCreate extends BaseCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
};
|
|
3
6
|
static description: string;
|
|
4
7
|
static examples: string[];
|
|
5
8
|
static flags: {
|
|
6
9
|
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
-
name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
10
|
output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
11
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
12
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -1,20 +1,26 @@
|
|
|
1
|
-
import { Flags } from '@oclif/core';
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import * as yaml from 'js-yaml';
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
4
|
import * as os from 'node:os';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import BaseCommand from '../../../base-command.js';
|
|
7
7
|
export default class WorkspaceCreate extends BaseCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
name: Args.string({
|
|
10
|
+
description: 'Name of the workspace',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
8
14
|
static description = 'Create a new workspace via the Xano Metadata API';
|
|
9
15
|
static examples = [
|
|
10
|
-
`$ xano workspace create
|
|
16
|
+
`$ xano workspace create my-workspace
|
|
11
17
|
Created workspace: my-workspace (ID: 123)
|
|
12
18
|
`,
|
|
13
|
-
`$ xano workspace create
|
|
19
|
+
`$ xano workspace create my-app --description "My application workspace"
|
|
14
20
|
Created workspace: my-app (ID: 456)
|
|
15
21
|
Description: My application workspace
|
|
16
22
|
`,
|
|
17
|
-
`$ xano workspace create
|
|
23
|
+
`$ xano workspace create new-project -d "New project workspace" -o json
|
|
18
24
|
{
|
|
19
25
|
"id": 789,
|
|
20
26
|
"name": "new-project",
|
|
@@ -29,11 +35,6 @@ Created workspace: my-app (ID: 456)
|
|
|
29
35
|
description: 'Description for the workspace',
|
|
30
36
|
required: false,
|
|
31
37
|
}),
|
|
32
|
-
name: Flags.string({
|
|
33
|
-
char: 'n',
|
|
34
|
-
description: 'Name of the workspace',
|
|
35
|
-
required: true,
|
|
36
|
-
}),
|
|
37
38
|
output: Flags.string({
|
|
38
39
|
char: 'o',
|
|
39
40
|
default: 'summary',
|
|
@@ -43,7 +44,7 @@ Created workspace: my-app (ID: 456)
|
|
|
43
44
|
}),
|
|
44
45
|
};
|
|
45
46
|
async run() {
|
|
46
|
-
const { flags } = await this.parse(WorkspaceCreate);
|
|
47
|
+
const { args, flags } = await this.parse(WorkspaceCreate);
|
|
47
48
|
// Get profile name (default or from flag/env)
|
|
48
49
|
const profileName = flags.profile || this.getDefaultProfile();
|
|
49
50
|
// Load credentials
|
|
@@ -65,7 +66,7 @@ Created workspace: my-app (ID: 456)
|
|
|
65
66
|
const apiUrl = `${profile.instance_origin}/api:meta/workspace`;
|
|
66
67
|
// Build request body
|
|
67
68
|
const body = {
|
|
68
|
-
name:
|
|
69
|
+
name: args.name,
|
|
69
70
|
};
|
|
70
71
|
if (flags.description) {
|
|
71
72
|
body.description = flags.description;
|