@xano/cli 0.0.41 → 0.0.43
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 +6 -3
- package/dist/commands/auth/index.js +5 -4
- package/dist/commands/function/edit/index.js +1 -1
- package/dist/commands/function/get/index.js +1 -1
- package/dist/commands/profile/wizard/index.js +5 -4
- package/dist/commands/workspace/push/index.d.ts +2 -0
- package/dist/commands/workspace/push/index.js +43 -1
- package/oclif.manifest.json +970 -954
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -106,9 +106,12 @@ xano workspace pull ./my-workspace --draft # Include draft changes
|
|
|
106
106
|
# Push local files to workspace
|
|
107
107
|
xano workspace push ./my-workspace
|
|
108
108
|
xano workspace push ./my-workspace -b dev
|
|
109
|
-
xano workspace push ./my-workspace --
|
|
110
|
-
xano workspace push ./my-workspace --
|
|
111
|
-
xano workspace push ./my-workspace --
|
|
109
|
+
xano workspace push ./my-workspace --partial # No workspace block required
|
|
110
|
+
xano workspace push ./my-workspace --delete # Delete objects not in the push
|
|
111
|
+
xano workspace push ./my-workspace --no-records # Schema only
|
|
112
|
+
xano workspace push ./my-workspace --no-env # Skip env vars
|
|
113
|
+
xano workspace push ./my-workspace --truncate # Truncate tables before import
|
|
114
|
+
xano workspace push ./my-workspace --no-sync-guids # Skip writing GUIDs back to local files
|
|
112
115
|
```
|
|
113
116
|
|
|
114
117
|
### Branches
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ExitPromptError } from '@inquirer/core';
|
|
1
2
|
import { Command, Flags } from '@oclif/core';
|
|
2
3
|
import inquirer from 'inquirer';
|
|
3
4
|
import * as yaml from 'js-yaml';
|
|
@@ -82,7 +83,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
82
83
|
process.exit(0);
|
|
83
84
|
}
|
|
84
85
|
catch (error) {
|
|
85
|
-
if (error instanceof
|
|
86
|
+
if (error instanceof ExitPromptError) {
|
|
86
87
|
this.log('Authentication cancelled.');
|
|
87
88
|
return;
|
|
88
89
|
}
|
|
@@ -233,7 +234,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
233
234
|
],
|
|
234
235
|
message: 'Select a branch',
|
|
235
236
|
name: 'selectedBranch',
|
|
236
|
-
type: '
|
|
237
|
+
type: 'select',
|
|
237
238
|
},
|
|
238
239
|
]);
|
|
239
240
|
return selectedBranch || undefined;
|
|
@@ -247,7 +248,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
247
248
|
})),
|
|
248
249
|
message: 'Select an instance',
|
|
249
250
|
name: 'instanceId',
|
|
250
|
-
type: '
|
|
251
|
+
type: 'select',
|
|
251
252
|
},
|
|
252
253
|
]);
|
|
253
254
|
return instances.find((inst) => inst.id === instanceId);
|
|
@@ -264,7 +265,7 @@ Opening browser for Xano login at https://custom.xano.com...`,
|
|
|
264
265
|
],
|
|
265
266
|
message: 'Select a workspace',
|
|
266
267
|
name: 'selectedWorkspace',
|
|
267
|
-
type: '
|
|
268
|
+
type: 'select',
|
|
268
269
|
},
|
|
269
270
|
]);
|
|
270
271
|
if (!selectedWorkspace) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ExitPromptError } from '@inquirer/core';
|
|
1
2
|
import { Command, Flags } from '@oclif/core';
|
|
2
3
|
import inquirer from 'inquirer';
|
|
3
4
|
import * as yaml from 'js-yaml';
|
|
@@ -73,7 +74,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
73
74
|
})),
|
|
74
75
|
message: 'Select an instance',
|
|
75
76
|
name: 'instanceId',
|
|
76
|
-
type: '
|
|
77
|
+
type: 'select',
|
|
77
78
|
},
|
|
78
79
|
]);
|
|
79
80
|
const selectedInstance = instances.find((inst) => inst.id === instanceId);
|
|
@@ -120,7 +121,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
120
121
|
],
|
|
121
122
|
message: 'Select a workspace (or skip to use default)',
|
|
122
123
|
name: 'selectedWorkspace',
|
|
123
|
-
type: '
|
|
124
|
+
type: 'select',
|
|
124
125
|
},
|
|
125
126
|
]);
|
|
126
127
|
workspace = selectedWorkspace || undefined;
|
|
@@ -150,7 +151,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
150
151
|
],
|
|
151
152
|
message: 'Select a branch',
|
|
152
153
|
name: 'selectedBranch',
|
|
153
|
-
type: '
|
|
154
|
+
type: 'select',
|
|
154
155
|
},
|
|
155
156
|
]);
|
|
156
157
|
branch = selectedBranch || undefined;
|
|
@@ -170,7 +171,7 @@ Profile 'production' created successfully at ~/.xano/credentials.yaml
|
|
|
170
171
|
this.log(`✓ Profile '${profileName}' created successfully!`);
|
|
171
172
|
}
|
|
172
173
|
catch (error) {
|
|
173
|
-
if (error instanceof
|
|
174
|
+
if (error instanceof ExitPromptError) {
|
|
174
175
|
this.log('Wizard cancelled.');
|
|
175
176
|
process.exit(0);
|
|
176
177
|
}
|
|
@@ -7,7 +7,9 @@ export default class Push extends BaseCommand {
|
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
9
|
branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
delete: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
env: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
partial: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
13
|
records: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
14
|
'sync-guids': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
15
|
truncate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -25,6 +25,12 @@ Pushed 58 documents from ./backup
|
|
|
25
25
|
`,
|
|
26
26
|
`$ xano workspace push ./my-workspace -b dev
|
|
27
27
|
Pushed 42 documents from ./my-workspace
|
|
28
|
+
`,
|
|
29
|
+
`$ xano workspace push ./my-functions --partial
|
|
30
|
+
Push some files without a workspace block (implies --no-delete)
|
|
31
|
+
`,
|
|
32
|
+
`$ xano workspace push ./my-workspace --no-delete
|
|
33
|
+
Patch files without deleting existing workspace objects
|
|
28
34
|
`,
|
|
29
35
|
`$ xano workspace push ./my-workspace --no-records
|
|
30
36
|
Push schema only, skip importing table records
|
|
@@ -49,12 +55,23 @@ Push schema only, skip records and environment variables
|
|
|
49
55
|
description: 'Branch name (optional if set in profile, defaults to live)',
|
|
50
56
|
required: false,
|
|
51
57
|
}),
|
|
58
|
+
delete: Flags.boolean({
|
|
59
|
+
allowNo: true,
|
|
60
|
+
default: false,
|
|
61
|
+
description: 'Delete workspace objects not included in the push (default: false)',
|
|
62
|
+
required: false,
|
|
63
|
+
}),
|
|
52
64
|
env: Flags.boolean({
|
|
53
65
|
allowNo: true,
|
|
54
66
|
default: true,
|
|
55
67
|
description: 'Include environment variables in import (default: true, use --no-env to exclude)',
|
|
56
68
|
required: false,
|
|
57
69
|
}),
|
|
70
|
+
partial: Flags.boolean({
|
|
71
|
+
default: false,
|
|
72
|
+
description: 'Partial push — workspace block is not required, existing objects are kept (implies --no-delete)',
|
|
73
|
+
required: false,
|
|
74
|
+
}),
|
|
58
75
|
records: Flags.boolean({
|
|
59
76
|
allowNo: true,
|
|
60
77
|
default: true,
|
|
@@ -146,10 +163,14 @@ Push schema only, skip records and environment variables
|
|
|
146
163
|
}
|
|
147
164
|
// Determine branch from flag or profile
|
|
148
165
|
const branch = flags.branch || profile.branch || '';
|
|
166
|
+
// --partial implies --no-delete
|
|
167
|
+
const shouldDelete = flags.partial ? false : flags.delete;
|
|
149
168
|
// Construct the API URL
|
|
150
169
|
const queryParams = new URLSearchParams({
|
|
151
170
|
branch,
|
|
171
|
+
delete: shouldDelete.toString(),
|
|
152
172
|
env: flags.env.toString(),
|
|
173
|
+
partial: flags.partial.toString(),
|
|
153
174
|
records: flags.records.toString(),
|
|
154
175
|
truncate: flags.truncate.toString(),
|
|
155
176
|
});
|
|
@@ -200,12 +221,33 @@ Push schema only, skip records and environment variables
|
|
|
200
221
|
}
|
|
201
222
|
// Write GUIDs back to local files
|
|
202
223
|
if (flags['sync-guids'] && guidMap.length > 0) {
|
|
224
|
+
// Build a secondary lookup by type:name only (without verb/api_group)
|
|
225
|
+
// for cases where the server omits those fields
|
|
226
|
+
const baseKeyMap = new Map();
|
|
227
|
+
for (const [key, fp] of documentFileMap) {
|
|
228
|
+
const baseKey = key.split(':').slice(0, 2).join(':');
|
|
229
|
+
// Only use base key if there's no ambiguity (single entry per base key)
|
|
230
|
+
if (baseKeyMap.has(baseKey)) {
|
|
231
|
+
baseKeyMap.set(baseKey, ''); // Mark as ambiguous
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
baseKeyMap.set(baseKey, fp);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
203
237
|
let updatedCount = 0;
|
|
204
238
|
for (const entry of guidMap) {
|
|
205
239
|
if (!entry.guid)
|
|
206
240
|
continue;
|
|
207
241
|
const key = buildDocumentKey(entry.type, entry.name, entry.verb, entry.api_group);
|
|
208
|
-
|
|
242
|
+
let filePath = documentFileMap.get(key);
|
|
243
|
+
// Fallback: try type:name only if full key didn't match
|
|
244
|
+
if (!filePath) {
|
|
245
|
+
const baseKey = `${entry.type}:${entry.name}`;
|
|
246
|
+
const basePath = baseKeyMap.get(baseKey);
|
|
247
|
+
if (basePath) {
|
|
248
|
+
filePath = basePath;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
209
251
|
if (!filePath) {
|
|
210
252
|
if (flags.verbose) {
|
|
211
253
|
this.log(` No local file found for ${entry.type} "${entry.name}", skipping GUID sync`);
|