neonctl 1.23.2 → 1.24.0

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.
@@ -12,6 +12,13 @@ const BRANCH_FIELDS = [
12
12
  'created_at',
13
13
  'updated_at',
14
14
  ];
15
+ const BRANCH_FIELDS_RESET = [
16
+ 'id',
17
+ 'name',
18
+ 'primary',
19
+ 'created_at',
20
+ 'last_reset_at',
21
+ ];
15
22
  export const command = 'branches';
16
23
  export const describe = 'Manage branches';
17
24
  export const aliases = ['branch'];
@@ -56,6 +63,16 @@ export const builder = (argv) => argv
56
63
  default: false,
57
64
  },
58
65
  }), async (args) => await create(args))
66
+ .command('reset <id|name>', 'Reset a branch', (yargs) => yargs.options({
67
+ parent: {
68
+ describe: 'Reset to a parent branch',
69
+ type: 'boolean',
70
+ default: false,
71
+ },
72
+ 'preserve-under-name': {
73
+ describe: 'Name under which to preserve the old branch',
74
+ },
75
+ }), async (args) => await reset(args))
59
76
  .command('rename <id|name> <new-name>', 'Rename a branch', (yargs) => yargs, async (args) => await rename(args))
60
77
  .command('set-primary <id|name>', 'Set a branch as primary', (yargs) => yargs, async (args) => await setPrimary(args))
61
78
  .command('add-compute <id|name>', 'Add a compute to a branch', (yargs) => yargs.options({
@@ -194,3 +211,26 @@ const addCompute = async (props) => {
194
211
  fields: ['id', 'host'],
195
212
  });
196
213
  };
214
+ const reset = async (props) => {
215
+ if (!props.parent) {
216
+ throw new Error('Only resetting to parent is supported for now');
217
+ }
218
+ const branchId = await branchIdFromProps(props);
219
+ const { data: { branch }, } = await props.apiClient.getProjectBranch(props.projectId, branchId);
220
+ if (!branch.parent_id) {
221
+ throw new Error('Branch has no parent');
222
+ }
223
+ const { data } = await retryOnLock(() => props.apiClient.request({
224
+ method: 'POST',
225
+ path: `/projects/${props.projectId}/branches/${branch.id}/reset`,
226
+ body: {
227
+ source_branch_id: branch.parent_id,
228
+ preserve_under_name: props.preserveUnderName || undefined,
229
+ },
230
+ }));
231
+ const resultBranch = data.branch;
232
+ writer(props).end(resultBranch, {
233
+ // need to reset types until we expose reset api
234
+ fields: BRANCH_FIELDS_RESET,
235
+ });
236
+ };
@@ -250,4 +250,18 @@ describe('branches', () => {
250
250
  snapshot: true,
251
251
  },
252
252
  });
253
+ testCliCommand({
254
+ name: 'reset branch to parent',
255
+ args: [
256
+ 'branches',
257
+ 'reset',
258
+ 'test_branch',
259
+ '--project-id',
260
+ 'test',
261
+ '--parent',
262
+ ],
263
+ expected: {
264
+ snapshot: true,
265
+ },
266
+ });
253
267
  });
@@ -1,6 +1,6 @@
1
1
  import { tmpdir } from 'node:os';
2
2
  import { join } from 'node:path';
3
- import { rmSync } from 'node:fs';
3
+ import { rmSync, writeFileSync } from 'node:fs';
4
4
  import { afterAll, describe } from '@jest/globals';
5
5
  import { testCliCommand } from '../test_utils/test_cli_command';
6
6
  const CONTEXT_FILE = join(tmpdir(), `neon_${Date.now()}`);
@@ -26,5 +26,28 @@ describe('set_context', () => {
26
26
  snapshot: true,
27
27
  },
28
28
  });
29
+ const overrideContextFile = join(tmpdir(), `neon_override_ctx_${Date.now()}`);
30
+ testCliCommand({
31
+ name: 'get branch id overrides context set branch',
32
+ before: async () => {
33
+ writeFileSync(overrideContextFile, JSON.stringify({
34
+ projectId: 'test',
35
+ branchId: 'br-cloudy-branch-12345678',
36
+ }));
37
+ },
38
+ after: async () => {
39
+ rmSync(overrideContextFile);
40
+ },
41
+ args: [
42
+ 'branches',
43
+ 'get',
44
+ 'br-sunny-branch-123456',
45
+ '--context-file',
46
+ overrideContextFile,
47
+ ],
48
+ expected: {
49
+ snapshot: true,
50
+ },
51
+ });
29
52
  });
30
53
  });
package/context.js CHANGED
@@ -36,7 +36,7 @@ export const enrichFromContext = (args) => {
36
36
  return;
37
37
  }
38
38
  const context = readContextFile(args.contextFile);
39
- if (!args.branch) {
39
+ if (!args.branch && !args.id && !args.name) {
40
40
  args.branch = context.branchId;
41
41
  }
42
42
  if (!args.projectId) {
package/help.js CHANGED
@@ -23,6 +23,11 @@ const formatHelp = (help) => {
23
23
  });
24
24
  commandsBlock.forEach((line) => {
25
25
  const [command, description] = splitColumns(line);
26
+ // patch the previous command if it was multiline
27
+ if (!description && ui.rows.length > 1) {
28
+ ui.rows[ui.rows.length - 2][0].text += command;
29
+ return;
30
+ }
26
31
  ui.div(chalk.cyan(command));
27
32
  ui.div({
28
33
  text: chalk.gray(drawPointer(SPACE_WIDTH)),
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "git@github.com:neondatabase/neonctl.git"
6
6
  },
7
7
  "type": "module",
8
- "version": "1.23.2",
8
+ "version": "1.24.0",
9
9
  "description": "CLI tool for NeonDB Cloud management",
10
10
  "main": "index.js",
11
11
  "author": "NeonDB",
@@ -4,13 +4,19 @@ import { join } from 'node:path';
4
4
  import { log } from '../log.js';
5
5
  import strip from 'strip-ansi';
6
6
  import { runMockServer } from './mock_server.js';
7
- export const testCliCommand = ({ args, name, expected, mockDir = 'main', }) => {
7
+ export const testCliCommand = ({ args, name, expected, before, after, mockDir = 'main', }) => {
8
8
  let server;
9
9
  describe(name, () => {
10
10
  beforeAll(async () => {
11
+ if (before) {
12
+ await before();
13
+ }
11
14
  server = await runMockServer(mockDir);
12
15
  });
13
16
  afterAll(async () => {
17
+ if (after) {
18
+ await after();
19
+ }
14
20
  return new Promise((resolve) => {
15
21
  server.close(() => {
16
22
  resolve();