@xano/cli 0.0.95-beta.16 → 0.0.95-beta.19

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 CHANGED
@@ -145,6 +145,8 @@ xano workspace git pull ./output -r https://github.com/owner/repo --path subdir
145
145
 
146
146
  All branch commands use **branch labels** (e.g., `v1`, `dev`), not IDs.
147
147
 
148
+ The `v1` branch is the default branch and always exists. It cannot be created, edited, or deleted.
149
+
148
150
  ```bash
149
151
  # List branches
150
152
  xano branch list
@@ -153,9 +155,9 @@ xano branch list
153
155
  xano branch get <branch_label>
154
156
 
155
157
  # Create a branch
156
- xano branch create --label dev
157
- xano branch create -l feature-auth -s dev -d "Auth feature"
158
- xano branch create -l staging --color "#ebc346"
158
+ xano branch create dev
159
+ xano branch create feature-auth -s dev -d "Auth feature"
160
+ xano branch create staging --color "#ebc346"
159
161
 
160
162
  # Edit a branch
161
163
  xano branch edit <branch_label> --label "new-label"
@@ -1,11 +1,13 @@
1
1
  import BaseCommand from '../../../base-command.js';
2
2
  export default class BranchCreate extends BaseCommand {
3
3
  static description: string;
4
+ static args: {
5
+ label: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
4
7
  static examples: string[];
5
8
  static flags: {
6
9
  color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
10
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
- label: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
11
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
12
  source: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
13
  workspace: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -1,4 +1,4 @@
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';
@@ -6,17 +6,23 @@ import * as path from 'node:path';
6
6
  import BaseCommand from '../../../base-command.js';
7
7
  export default class BranchCreate extends BaseCommand {
8
8
  static description = 'Create a new branch by cloning from an existing branch';
9
+ static args = {
10
+ label: Args.string({
11
+ description: 'Label for the new branch',
12
+ required: true,
13
+ }),
14
+ };
9
15
  static examples = [
10
- `$ xano branch create --label dev
16
+ `$ xano branch create dev
11
17
  Created branch: dev
12
18
  Cloned from: v1
13
19
  `,
14
- `$ xano branch create -l feature-auth -s dev -d "Authentication feature"
20
+ `$ xano branch create feature-auth -s dev -d "Authentication feature"
15
21
  Created branch: feature-auth
16
22
  Cloned from: dev
17
23
  Description: Authentication feature
18
24
  `,
19
- `$ xano branch create --label staging --color "#ebc346" --output json
25
+ `$ xano branch create staging --color "#ebc346" --output json
20
26
  {
21
27
  "created_at": "2024-02-11T10:00:00Z",
22
28
  "label": "staging",
@@ -37,11 +43,6 @@ Created branch: feature-auth
37
43
  description: 'Description for the new branch',
38
44
  required: false,
39
45
  }),
40
- label: Flags.string({
41
- char: 'l',
42
- description: 'Label for the new branch',
43
- required: true,
44
- }),
45
46
  output: Flags.string({
46
47
  char: 'o',
47
48
  default: 'summary',
@@ -62,7 +63,7 @@ Created branch: feature-auth
62
63
  }),
63
64
  };
64
65
  async run() {
65
- const { flags } = await this.parse(BranchCreate);
66
+ const { args, flags } = await this.parse(BranchCreate);
66
67
  // Get profile name (default or from flag/env)
67
68
  const profileName = flags.profile || this.getDefaultProfile();
68
69
  // Load credentials
@@ -84,11 +85,15 @@ Created branch: feature-auth
84
85
  const workspaceId = flags.workspace || profile.workspace;
85
86
  if (!workspaceId) {
86
87
  this.error('No workspace ID provided. Either use --workspace flag or set one in your profile.\n' +
87
- 'Usage: xano branch create --label <label> [--workspace <workspace_id>]');
88
+ 'Usage: xano branch create <label> [--workspace <workspace_id>]');
89
+ }
90
+ // Validate reserved branch names
91
+ if (args.label.toLowerCase() === 'v1') {
92
+ this.error("Cannot create a branch named 'v1'. This is the default branch and always exists.");
88
93
  }
89
94
  // Build request body
90
95
  const body = {
91
- label: flags.label,
96
+ label: args.label,
92
97
  source_branch: flags.source,
93
98
  };
94
99
  if (flags.description) {
@@ -104,8 +109,8 @@ Created branch: feature-auth
104
109
  const response = await this.verboseFetch(apiUrl, {
105
110
  body: JSON.stringify(body),
106
111
  headers: {
107
- 'accept': 'application/json',
108
- 'Authorization': `Bearer ${profile.access_token}`,
112
+ accept: 'application/json',
113
+ Authorization: `Bearer ${profile.access_token}`,
109
114
  'Content-Type': 'application/json',
110
115
  },
111
116
  method: 'POST',
@@ -114,7 +119,7 @@ Created branch: feature-auth
114
119
  const errorText = await response.text();
115
120
  this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
116
121
  }
117
- const branch = await response.json();
122
+ const branch = (await response.json());
118
123
  // Output results
119
124
  if (flags.output === 'json') {
120
125
  this.log(JSON.stringify(branch, null, 2));
@@ -145,8 +150,7 @@ Created branch: feature-auth
145
150
  const credentialsPath = path.join(configDir, 'credentials.yaml');
146
151
  // Check if credentials file exists
147
152
  if (!fs.existsSync(credentialsPath)) {
148
- this.error(`Credentials file not found at ${credentialsPath}\n` +
149
- `Create a profile using 'xano profile create'`);
153
+ this.error(`Credentials file not found at ${credentialsPath}\n` + `Create a profile using 'xano profile create'`);
150
154
  }
151
155
  // Read credentials file
152
156
  try {
@@ -159,8 +159,7 @@ Pushed 42 documents to sandbox environment from ./my-workspace
159
159
  this.log('');
160
160
  this.log(ux.colorize('red', ux.colorize('bold', '=== CRITICAL: Invalid Indexes ===')));
161
161
  this.log('');
162
- this.log(ux.colorize('red', 'The following tables have indexes referencing fields that do not exist in the schema.'));
163
- this.log(ux.colorize('red', 'These will cause the import to fail.'));
162
+ this.log(ux.colorize('red', 'The following tables have indexed referencing fields that do not exist in the schema, which may cause related issues.'));
164
163
  this.log('');
165
164
  for (const idx of badIndexes) {
166
165
  this.log(` ${ux.colorize('red', 'CRITICAL'.padEnd(16))} ${'table'.padEnd(18)} ${idx.table}`);
@@ -288,13 +288,16 @@ Push functions but exclude test files
288
288
  const errorJson = JSON.parse(errorText);
289
289
  if (errorJson.message?.includes('Push is disabled')) {
290
290
  this.log('');
291
- this.log(ux.colorize('red', ux.colorize('bold', 'Direct push to this workspace is disabled.')));
291
+ this.log(ux.colorize('red', ux.colorize('bold', 'Direct push is disabled to protect your production workspace from unintended changes.')));
292
+ this.log(ux.colorize('dim', 'Use your sandbox environment to test and review changes before applying them to your production workspace.'));
293
+ this.log('');
292
294
  this.log(ux.colorize('dim', 'To apply changes to the workspace, use the sandbox review flow:'));
293
295
  this.log(` ${ux.colorize('cyan', 'xano sandbox push')} ${ux.colorize('dim', '— push changes to your sandbox')}`);
294
296
  this.log(` ${ux.colorize('cyan', 'xano sandbox review')} ${ux.colorize('dim', '— edit any logic, inspect the snapshot diff, and promote changes to the workspace')}`);
295
297
  this.log('');
296
298
  this.log(ux.colorize('dim', 'To enable direct push, go to Workspace Settings → CLI → Allow Direct Workspace Push.'));
297
- this.log(ux.colorize('dim', 'Note: This setting does not apply to Free plan instances.'));
299
+ this.log('');
300
+ this.log(ux.colorize('dim', "Note: Free plan instances don't include sandbox environments, so direct push is always enabled."));
298
301
  this.log('');
299
302
  return;
300
303
  }
@@ -505,12 +508,13 @@ Push functions but exclude test files
505
508
  }
506
509
  // Provide clear guidance when push is disabled
507
510
  if (errorJson.message?.includes('Push is disabled')) {
508
- this.error(`Push is disabled for this workspace.\n\n` +
509
- `To enable, go to Workspace Settings and turn on "Allow Push".\n` +
510
- `Note: This setting does not apply to Free plan instances.\n\n` +
511
+ this.error(`Direct push is disabled to protect your production workspace from unintended changes.\n` +
512
+ `Use your sandbox environment to test and review changes before applying them to your production workspace.\n\n` +
511
513
  `Alternatively, use sandbox commands:\n` +
512
514
  ` xano sandbox push ${args.directory}\n` +
513
- ` xano sandbox impersonate`);
515
+ ` xano sandbox review\n\n` +
516
+ `To enable direct push, go to Workspace Settings → CLI → Allow Direct Workspace Push.\n\n` +
517
+ `Note: Free plan instances don't include sandbox environments, so direct push is always enabled.`);
514
518
  }
515
519
  }
516
520
  catch {
@@ -760,8 +764,7 @@ Push functions but exclude test files
760
764
  this.log('');
761
765
  this.log(ux.colorize('red', ux.colorize('bold', '=== CRITICAL: Invalid Indexes ===')));
762
766
  this.log('');
763
- this.log(ux.colorize('red', 'The following tables have indexes referencing fields that do not exist in the schema.'));
764
- this.log(ux.colorize('red', 'These will cause the import to fail.'));
767
+ this.log(ux.colorize('red', 'The following tables have indexed referencing fields that do not exist in the schema, which may cause related issues.'));
765
768
  this.log('');
766
769
  for (const idx of badIndexes) {
767
770
  this.log(` ${ux.colorize('red', 'CRITICAL'.padEnd(16))} ${'table'.padEnd(18)} ${idx.table}`);