@xano/cli 0.0.87 → 0.0.89
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.
|
@@ -52,8 +52,8 @@ Releases in workspace 5:
|
|
|
52
52
|
try {
|
|
53
53
|
const response = await this.verboseFetch(apiUrl, {
|
|
54
54
|
headers: {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
accept: 'application/json',
|
|
56
|
+
Authorization: `Bearer ${profile.access_token}`,
|
|
57
57
|
},
|
|
58
58
|
method: 'GET',
|
|
59
59
|
}, flags.verbose, profile.access_token);
|
|
@@ -61,7 +61,7 @@ Releases in workspace 5:
|
|
|
61
61
|
const errorText = await response.text();
|
|
62
62
|
this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
|
|
63
63
|
}
|
|
64
|
-
const data = await response.json();
|
|
64
|
+
const data = (await response.json());
|
|
65
65
|
let releases;
|
|
66
66
|
if (Array.isArray(data)) {
|
|
67
67
|
releases = data;
|
|
@@ -84,7 +84,10 @@ Releases in workspace 5:
|
|
|
84
84
|
for (const release of releases) {
|
|
85
85
|
const branch = release.branch ? ` - ${release.branch}` : '';
|
|
86
86
|
const hotfix = release.hotfix ? ' [hotfix]' : '';
|
|
87
|
-
|
|
87
|
+
const createdAt = release.created_at
|
|
88
|
+
? ` (${new Date(release.created_at).toLocaleString(undefined, { timeZoneName: 'short' })})`
|
|
89
|
+
: '';
|
|
90
|
+
this.log(` - ${release.name} (ID: ${release.id})${branch}${hotfix}${createdAt}`);
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
}
|
|
@@ -102,8 +105,7 @@ Releases in workspace 5:
|
|
|
102
105
|
const configDir = path.join(os.homedir(), '.xano');
|
|
103
106
|
const credentialsPath = path.join(configDir, 'credentials.yaml');
|
|
104
107
|
if (!fs.existsSync(credentialsPath)) {
|
|
105
|
-
this.error(`Credentials file not found at ${credentialsPath}\n` +
|
|
106
|
-
`Create a profile using 'xano profile create'`);
|
|
108
|
+
this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile create'`);
|
|
107
109
|
}
|
|
108
110
|
try {
|
|
109
111
|
const fileContent = fs.readFileSync(credentialsPath, 'utf8');
|
|
@@ -16,6 +16,7 @@ export default class Push extends BaseCommand {
|
|
|
16
16
|
transaction: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
17
|
truncate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
18
|
workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
filter: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
20
|
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
20
21
|
profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
22
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Args, Flags, ux } from '@oclif/core';
|
|
2
2
|
import * as yaml from 'js-yaml';
|
|
3
|
+
import { minimatch } from 'minimatch';
|
|
3
4
|
import * as fs from 'node:fs';
|
|
4
5
|
import * as os from 'node:os';
|
|
5
6
|
import * as path from 'node:path';
|
|
@@ -46,6 +47,12 @@ Push without overwriting environment variables
|
|
|
46
47
|
`,
|
|
47
48
|
`$ xano workspace push ./my-workspace --truncate
|
|
48
49
|
Truncate all table records before importing
|
|
50
|
+
`,
|
|
51
|
+
`$ xano workspace push ./my-workspace -f "**/func*"
|
|
52
|
+
Push only files matching the glob pattern
|
|
53
|
+
`,
|
|
54
|
+
`$ xano workspace push ./my-workspace -f "function/*" -f "table/*"
|
|
55
|
+
Push files matching multiple patterns
|
|
49
56
|
`,
|
|
50
57
|
];
|
|
51
58
|
static flags = {
|
|
@@ -102,6 +109,12 @@ Truncate all table records before importing
|
|
|
102
109
|
description: 'Workspace ID (optional if set in profile)',
|
|
103
110
|
required: false,
|
|
104
111
|
}),
|
|
112
|
+
filter: Flags.string({
|
|
113
|
+
char: 'f',
|
|
114
|
+
description: 'Glob pattern to filter files (e.g. "**/func*", "table/*.xs"). Matched against relative paths from the push directory.',
|
|
115
|
+
multiple: true,
|
|
116
|
+
required: false,
|
|
117
|
+
}),
|
|
105
118
|
force: Flags.boolean({
|
|
106
119
|
default: false,
|
|
107
120
|
description: 'Skip preview and confirmation prompt (for CI/CD pipelines)',
|
|
@@ -149,9 +162,22 @@ Truncate all table records before importing
|
|
|
149
162
|
this.error(`Not a directory: ${inputDir}`);
|
|
150
163
|
}
|
|
151
164
|
// Collect all .xs files from the directory tree
|
|
152
|
-
const
|
|
165
|
+
const allFiles = this.collectFiles(inputDir);
|
|
166
|
+
let files = allFiles;
|
|
167
|
+
// Apply glob filter(s) if specified
|
|
168
|
+
if (flags.filter && flags.filter.length > 0) {
|
|
169
|
+
files = allFiles.filter((f) => {
|
|
170
|
+
const rel = path.relative(inputDir, f);
|
|
171
|
+
return flags.filter.some((pattern) => minimatch(rel, pattern, { matchBase: true }));
|
|
172
|
+
});
|
|
173
|
+
this.log('');
|
|
174
|
+
this.log(` ${ux.colorize('dim', 'Filter:')} ${flags.filter.map((p) => ux.colorize('cyan', p)).join(', ')}`);
|
|
175
|
+
this.log(` ${ux.colorize('dim', 'Matched:')} ${ux.colorize('bold', String(files.length))} of ${allFiles.length} files`);
|
|
176
|
+
}
|
|
153
177
|
if (files.length === 0) {
|
|
154
|
-
this.error(
|
|
178
|
+
this.error(flags.filter
|
|
179
|
+
? `No .xs files match filter ${flags.filter.join(', ')} in ${args.directory}`
|
|
180
|
+
: `No .xs files found in ${args.directory}`);
|
|
155
181
|
}
|
|
156
182
|
// Read each file and track file path alongside content
|
|
157
183
|
const documentEntries = [];
|
|
@@ -385,7 +411,12 @@ Truncate all table records before importing
|
|
|
385
411
|
return true;
|
|
386
412
|
// For queries, operation name includes verb (e.g., "path/{id} DELETE")
|
|
387
413
|
const opName = parsed.verb ? `${parsed.name} ${parsed.verb}` : parsed.name;
|
|
388
|
-
|
|
414
|
+
if (changedKeys.has(`${parsed.type}:${opName}`))
|
|
415
|
+
return true;
|
|
416
|
+
// Keep table documents that contain records when --records is active
|
|
417
|
+
if (flags.records && parsed.type === 'table' && /\bitems\s*=\s*\[/m.test(entry.content))
|
|
418
|
+
return true;
|
|
419
|
+
return false;
|
|
389
420
|
});
|
|
390
421
|
if (filteredEntries.length === 0) {
|
|
391
422
|
this.log('No changes to push.');
|