neonctl 2.34.1 → 2.35.1
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 +34 -0
- package/dist/commands/branches.js +7 -0
- package/dist/commands/projects.js +41 -0
- package/dist/context.js +32 -17
- package/dist/dev/env.js +8 -4
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -58,6 +58,40 @@ neon projects list --api-key <neon_api_key>
|
|
|
58
58
|
|
|
59
59
|
For information about obtaining an Neon API key, see [Authentication](https://api-docs.neon.tech/reference/authentication), in the _Neon API Reference_.
|
|
60
60
|
|
|
61
|
+
## Project and branch creation
|
|
62
|
+
|
|
63
|
+
Choose the PostgreSQL version when creating a project:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
neon projects create --name my-project --pg-version 18
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Supported versions are `14` through `19`; version `19` is available only in
|
|
70
|
+
regions where it has been enabled.
|
|
71
|
+
|
|
72
|
+
Create a protected branch when it should not be modified or deleted by routine
|
|
73
|
+
automation:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
neon branches create --project-id <project-id> --name production --protected
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Enable logical replication
|
|
80
|
+
|
|
81
|
+
Enable logical replication for every endpoint in an existing project:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
neon projects update <project-id> --enable-logical-replication
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The CLI asks for confirmation because enabling logical replication suspends
|
|
88
|
+
active endpoints and cannot be undone. For non-interactive automation, pass
|
|
89
|
+
`--yes` explicitly:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
neon projects update <project-id> --enable-logical-replication --yes
|
|
93
|
+
```
|
|
94
|
+
|
|
61
95
|
## Connect with psql
|
|
62
96
|
|
|
63
97
|
### The `psql` command
|
|
@@ -97,6 +97,10 @@ export const builder = (argv) => argv
|
|
|
97
97
|
type: "string",
|
|
98
98
|
requiresArg: true,
|
|
99
99
|
},
|
|
100
|
+
protected: {
|
|
101
|
+
describe: branchCreateRequest["branch.protected"].description,
|
|
102
|
+
type: "boolean",
|
|
103
|
+
},
|
|
100
104
|
}), (args) => create(args))
|
|
101
105
|
.command("reset <id|name>", "Reset a branch", (yargs) => yargs.options({
|
|
102
106
|
parent: {
|
|
@@ -299,6 +303,9 @@ const create = async (props) => {
|
|
|
299
303
|
expires_at: new Date(props["expires-at"]).toISOString(),
|
|
300
304
|
}
|
|
301
305
|
: {}),
|
|
306
|
+
...(props.protected !== undefined
|
|
307
|
+
? { protected: props.protected }
|
|
308
|
+
: {}),
|
|
302
309
|
},
|
|
303
310
|
endpoints: props.compute
|
|
304
311
|
? [
|
|
@@ -92,6 +92,11 @@ export const builder = (argv) => {
|
|
|
92
92
|
.description,
|
|
93
93
|
type: "string",
|
|
94
94
|
},
|
|
95
|
+
"pg-version": {
|
|
96
|
+
describe: "Major PostgreSQL version (14–19). Version 19 is available only in regions where it has been enabled.",
|
|
97
|
+
type: "number",
|
|
98
|
+
choices: [14, 15, 16, 17, 18, 19],
|
|
99
|
+
},
|
|
95
100
|
"set-context": {
|
|
96
101
|
type: "boolean",
|
|
97
102
|
describe: "Set the current context to the new project",
|
|
@@ -128,6 +133,15 @@ export const builder = (argv) => {
|
|
|
128
133
|
describe: projectUpdateRequest["project.name"].description,
|
|
129
134
|
type: "string",
|
|
130
135
|
},
|
|
136
|
+
"enable-logical-replication": {
|
|
137
|
+
describe: "Enable logical replication for all project endpoints. This suspends active endpoints and cannot be disabled.",
|
|
138
|
+
type: "boolean",
|
|
139
|
+
},
|
|
140
|
+
yes: {
|
|
141
|
+
type: "boolean",
|
|
142
|
+
default: false,
|
|
143
|
+
describe: "Skip the confirmation prompt when enabling logical replication.",
|
|
144
|
+
},
|
|
131
145
|
}), async (args) => {
|
|
132
146
|
await update(args);
|
|
133
147
|
})
|
|
@@ -226,6 +240,9 @@ const create = async (props) => {
|
|
|
226
240
|
if (props.role) {
|
|
227
241
|
project.branch.role_name = props.role;
|
|
228
242
|
}
|
|
243
|
+
if (props.pgVersion !== undefined) {
|
|
244
|
+
project.pg_version = props.pgVersion;
|
|
245
|
+
}
|
|
229
246
|
if (props.cu) {
|
|
230
247
|
project.default_endpoint_settings = props.cu
|
|
231
248
|
? getComputeUnits(props.cu)
|
|
@@ -262,6 +279,30 @@ const deleteProject = async (props) => {
|
|
|
262
279
|
};
|
|
263
280
|
const update = async (props) => {
|
|
264
281
|
const project = {};
|
|
282
|
+
if (props.enableLogicalReplication !== undefined) {
|
|
283
|
+
if (!props.enableLogicalReplication) {
|
|
284
|
+
throw new Error("Logical replication cannot be disabled once it has been enabled.");
|
|
285
|
+
}
|
|
286
|
+
if (!props.yes) {
|
|
287
|
+
if (isCi() || !process.stdin.isTTY) {
|
|
288
|
+
throw new Error("Enabling logical replication requires confirmation. Re-run interactively or pass --yes.");
|
|
289
|
+
}
|
|
290
|
+
const { proceed } = await prompts({
|
|
291
|
+
onState: onPromptState,
|
|
292
|
+
type: "confirm",
|
|
293
|
+
name: "proceed",
|
|
294
|
+
message: "Enable logical replication? This suspends active endpoints and cannot be undone.",
|
|
295
|
+
initial: false,
|
|
296
|
+
});
|
|
297
|
+
if (!proceed) {
|
|
298
|
+
log.info("Logical replication was not enabled.");
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
project.settings = {
|
|
303
|
+
enable_logical_replication: true,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
265
306
|
if (props.hipaa !== undefined) {
|
|
266
307
|
if (!project.settings) {
|
|
267
308
|
project.settings = {};
|
package/dist/context.js
CHANGED
|
@@ -33,7 +33,37 @@ export const isCurrentBranchProbe = (args) => args.currentBranch === true &&
|
|
|
33
33
|
export const isConfigInit = (args) => args._[0] === "config" && args._[1] === "init";
|
|
34
34
|
const CONTEXT_FILE = ".neon";
|
|
35
35
|
const GITIGNORE_FILE = ".gitignore";
|
|
36
|
-
const
|
|
36
|
+
const canAccessFile = (file) => {
|
|
37
|
+
try {
|
|
38
|
+
accessSync(file);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Walk upward to find an existing `.neon` file.
|
|
47
|
+
*
|
|
48
|
+
* The walk keeps the established home and POSIX-root boundaries, then also
|
|
49
|
+
* stops when resolving a parent makes no progress. That second guard handles
|
|
50
|
+
* Windows drive and UNC-share roots, whose paths do not equal `normalize("/")`.
|
|
51
|
+
*/
|
|
52
|
+
export const walkContextFile = (cwd, root, home, resolvePath, canAccess) => {
|
|
53
|
+
let currentDir = cwd;
|
|
54
|
+
while (currentDir !== root && currentDir !== home) {
|
|
55
|
+
const contextFile = resolvePath(currentDir, CONTEXT_FILE);
|
|
56
|
+
if (canAccess(contextFile)) {
|
|
57
|
+
return contextFile;
|
|
58
|
+
}
|
|
59
|
+
const parentDir = resolvePath(currentDir, "..");
|
|
60
|
+
if (parentDir === currentDir) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
currentDir = parentDir;
|
|
64
|
+
}
|
|
65
|
+
return resolvePath(cwd, CONTEXT_FILE);
|
|
66
|
+
};
|
|
37
67
|
/**
|
|
38
68
|
* Resolve the default `.neon` path for the current working directory.
|
|
39
69
|
*
|
|
@@ -52,22 +82,7 @@ const wrapWithContextFile = (dir) => resolve(dir, CONTEXT_FILE);
|
|
|
52
82
|
* `cwd` is overridable so tests can exercise the walk-up without mutating
|
|
53
83
|
* `process.cwd()` (which would race with other tests running in parallel).
|
|
54
84
|
*/
|
|
55
|
-
export const currentContextFile = (cwd = process.cwd()) =>
|
|
56
|
-
let currentDir = cwd;
|
|
57
|
-
const root = normalize("/");
|
|
58
|
-
const home = homedir();
|
|
59
|
-
while (currentDir !== root && currentDir !== home) {
|
|
60
|
-
try {
|
|
61
|
-
accessSync(resolve(currentDir, CONTEXT_FILE));
|
|
62
|
-
return wrapWithContextFile(currentDir);
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// ignore
|
|
66
|
-
}
|
|
67
|
-
currentDir = resolve(currentDir, "..");
|
|
68
|
-
}
|
|
69
|
-
return wrapWithContextFile(cwd);
|
|
70
|
-
};
|
|
85
|
+
export const currentContextFile = (cwd = process.cwd()) => walkContextFile(cwd, normalize("/"), homedir(), resolve, canAccessFile);
|
|
71
86
|
export const readContextFile = (file) => {
|
|
72
87
|
try {
|
|
73
88
|
return JSON.parse(readFileSync(file, "utf-8"));
|
package/dist/dev/env.js
CHANGED
|
@@ -45,8 +45,9 @@ export class MissingBranchContextError extends Error {
|
|
|
45
45
|
* the branch is missing, we stop with a {@link DevEnvMismatchError} pointing at
|
|
46
46
|
* `neonctl deploy`. Otherwise `fetchEnv` evaluates the policy.
|
|
47
47
|
* 2. no `neon.ts`, but a project + branch are known -> `pullConfig` reads the
|
|
48
|
-
* branch's live state (
|
|
49
|
-
* `fetchEnv` resolves what is actually enabled
|
|
48
|
+
* branch's live state (Auth / Data API enablement plus any object-storage
|
|
49
|
+
* buckets) into a config, then `fetchEnv` resolves what is actually enabled —
|
|
50
|
+
* so a branch with a bucket gets its `AWS_*` storage vars pulled with no policy.
|
|
50
51
|
* 3. otherwise -> throw {@link MissingBranchContextError}.
|
|
51
52
|
*
|
|
52
53
|
* Unlike {@link resolveDevEnv}, this never swallows errors — callers decide how to
|
|
@@ -78,8 +79,11 @@ export const resolveNeonEnvVars = async (ctx) => {
|
|
|
78
79
|
branchId: ctx.branchId,
|
|
79
80
|
...apiOptions(ctx),
|
|
80
81
|
});
|
|
81
|
-
// `pulled.config` is already a `Config` (static auth/dataApi toggles
|
|
82
|
-
// tuning closure), so it feeds
|
|
82
|
+
// `pulled.config` is already a `Config` (static auth/dataApi toggles, any
|
|
83
|
+
// object-storage `preview.buckets`, and a branch tuning closure), so it feeds
|
|
84
|
+
// straight into fetchEnv — no wrapping needed. pullConfig excludes functions and
|
|
85
|
+
// the AI Gateway (neither can be faithfully read back), so fetchEnv never probes
|
|
86
|
+
// the functions API here and only mints a storage credential when a bucket exists.
|
|
83
87
|
return await fetchAndProject(pulled.config, ctx);
|
|
84
88
|
}
|
|
85
89
|
throw new MissingBranchContextError("No project/branch context found. Link a branch (`neonctl link` / " +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neonctl",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.35.1",
|
|
4
4
|
"description": "CLI tool for Neon Serverless Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"which": "3.0.1",
|
|
52
52
|
"yaml": "^2.9.0",
|
|
53
53
|
"yargs": "17.7.2",
|
|
54
|
+
"@neon/sdk": "1.2.0",
|
|
54
55
|
"@neon/config": "0.9.5",
|
|
55
|
-
"@neon/config-runtime": "0.9.
|
|
56
|
-
"@neon/env": "0.11.4"
|
|
57
|
-
"@neon/sdk": "1.2.0"
|
|
56
|
+
"@neon/config-runtime": "0.9.6",
|
|
57
|
+
"@neon/env": "0.11.4"
|
|
58
58
|
},
|
|
59
59
|
"optionalDependencies": {
|
|
60
60
|
"esbuild": "0.28.1"
|