neonctl 2.28.0 → 2.29.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 +71 -71
- package/dist/analytics.js +35 -33
- package/dist/api.js +34 -34
- package/dist/auth.js +50 -44
- package/dist/cli.js +2 -2
- package/dist/commands/auth.js +58 -52
- package/dist/commands/bootstrap.js +115 -157
- package/dist/commands/branches.js +154 -147
- package/dist/commands/bucket.js +124 -118
- package/dist/commands/checkout.js +49 -49
- package/dist/commands/config.js +212 -88
- package/dist/commands/connection_string.js +62 -62
- package/dist/commands/data_api.js +96 -96
- package/dist/commands/databases.js +23 -23
- package/dist/commands/deploy.js +12 -12
- package/dist/commands/dev.js +114 -114
- package/dist/commands/env.js +43 -43
- package/dist/commands/functions.js +97 -98
- package/dist/commands/index.js +26 -26
- package/dist/commands/init.js +23 -22
- package/dist/commands/ip_allow.js +29 -29
- package/dist/commands/link.js +223 -166
- package/dist/commands/neon_auth.js +381 -363
- package/dist/commands/operations.js +11 -11
- package/dist/commands/orgs.js +8 -8
- package/dist/commands/projects.js +101 -99
- package/dist/commands/psql.js +31 -31
- package/dist/commands/roles.js +21 -21
- package/dist/commands/schema_diff.js +23 -23
- package/dist/commands/set_context.js +17 -17
- package/dist/commands/status.js +17 -17
- package/dist/commands/user.js +5 -5
- package/dist/commands/vpc_endpoints.js +50 -50
- package/dist/config.js +7 -7
- package/dist/config_format.js +5 -5
- package/dist/context.js +23 -16
- package/dist/current_branch_fast_path.js +6 -6
- package/dist/dev/env.js +34 -34
- package/dist/dev/functions.js +4 -4
- package/dist/dev/inputs.js +6 -6
- package/dist/dev/runtime.js +25 -25
- package/dist/env.js +14 -14
- package/dist/env_file.js +13 -13
- package/dist/errors.js +19 -19
- package/dist/functions_api.js +10 -10
- package/dist/help.js +15 -15
- package/dist/index.js +94 -92
- package/dist/log.js +2 -2
- package/dist/pkg.js +5 -5
- package/dist/psql/cli.js +4 -2
- package/dist/psql/command/cmd_cond.js +61 -61
- package/dist/psql/command/cmd_connect.js +159 -154
- package/dist/psql/command/cmd_copy.js +107 -97
- package/dist/psql/command/cmd_describe.js +368 -363
- package/dist/psql/command/cmd_format.js +276 -263
- package/dist/psql/command/cmd_io.js +269 -263
- package/dist/psql/command/cmd_lo.js +74 -66
- package/dist/psql/command/cmd_meta.js +148 -148
- package/dist/psql/command/cmd_misc.js +17 -17
- package/dist/psql/command/cmd_pipeline.js +142 -135
- package/dist/psql/command/cmd_restrict.js +25 -25
- package/dist/psql/command/cmd_show.js +183 -168
- package/dist/psql/command/dispatch.js +26 -26
- package/dist/psql/command/shared.js +14 -14
- package/dist/psql/complete/filenames.js +16 -16
- package/dist/psql/complete/index.js +4 -4
- package/dist/psql/complete/matcher.js +33 -32
- package/dist/psql/complete/psqlVars.js +173 -173
- package/dist/psql/complete/queries.js +5 -3
- package/dist/psql/complete/rules.js +900 -863
- package/dist/psql/core/common.js +136 -133
- package/dist/psql/core/help.js +343 -343
- package/dist/psql/core/mainloop.js +160 -153
- package/dist/psql/core/prompt.js +126 -123
- package/dist/psql/core/settings.js +111 -111
- package/dist/psql/core/sqlHelp.js +150 -150
- package/dist/psql/core/startup.js +211 -205
- package/dist/psql/core/syncVars.js +14 -14
- package/dist/psql/core/variables.js +24 -24
- package/dist/psql/describe/formatters.js +302 -289
- package/dist/psql/describe/processNamePattern.js +28 -28
- package/dist/psql/describe/queries.js +656 -651
- package/dist/psql/index.js +436 -411
- package/dist/psql/io/history.js +36 -36
- package/dist/psql/io/input.js +15 -15
- package/dist/psql/io/lineEditor/buffer.js +27 -25
- package/dist/psql/io/lineEditor/complete.js +15 -15
- package/dist/psql/io/lineEditor/filename.js +22 -22
- package/dist/psql/io/lineEditor/index.js +65 -62
- package/dist/psql/io/lineEditor/keymap.js +325 -318
- package/dist/psql/io/lineEditor/vt100.js +60 -60
- package/dist/psql/io/pgpass.js +18 -18
- package/dist/psql/io/pgservice.js +14 -14
- package/dist/psql/io/psqlrc.js +46 -46
- package/dist/psql/print/aligned.js +175 -166
- package/dist/psql/print/asciidoc.js +51 -51
- package/dist/psql/print/crosstab.js +34 -31
- package/dist/psql/print/csv.js +25 -22
- package/dist/psql/print/html.js +54 -54
- package/dist/psql/print/json.js +12 -12
- package/dist/psql/print/latex.js +118 -118
- package/dist/psql/print/pager.js +28 -26
- package/dist/psql/print/troff.js +48 -48
- package/dist/psql/print/unaligned.js +15 -14
- package/dist/psql/print/units.js +17 -17
- package/dist/psql/scanner/slash.js +48 -46
- package/dist/psql/scanner/sql.js +88 -84
- package/dist/psql/scanner/stringutils.js +21 -17
- package/dist/psql/types/index.js +7 -7
- package/dist/psql/types/scanner.js +8 -8
- package/dist/psql/wire/connection.js +341 -327
- package/dist/psql/wire/copy.js +7 -7
- package/dist/psql/wire/pipeline.js +26 -24
- package/dist/psql/wire/protocol.js +102 -102
- package/dist/psql/wire/sasl.js +62 -62
- package/dist/psql/wire/tls.js +79 -73
- package/dist/storage_api.js +15 -15
- package/dist/test_utils/fixtures.js +34 -31
- package/dist/test_utils/oauth_server.js +5 -5
- package/dist/utils/api_enums.js +13 -13
- package/dist/utils/branch_notice.js +5 -5
- package/dist/utils/branch_picker.js +26 -26
- package/dist/utils/compute_units.js +4 -4
- package/dist/utils/enrichers.js +20 -15
- package/dist/utils/esbuild.js +28 -28
- package/dist/utils/formats.js +1 -1
- package/dist/utils/middlewares.js +3 -3
- package/dist/utils/package_manager.js +68 -0
- package/dist/utils/point_in_time.js +12 -12
- package/dist/utils/psql.js +30 -30
- package/dist/utils/string.js +2 -2
- package/dist/utils/ui.js +9 -9
- package/dist/utils/zip.js +1 -1
- package/dist/writer.js +17 -17
- package/package.json +6 -7
package/dist/commands/env.js
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { NEON_ENV_VAR_KEYS } from
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
export const command =
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { NEON_ENV_VAR_KEYS } from "@neon/env";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { resolveNeonEnvVars } from "../dev/env.js";
|
|
5
|
+
import { mergeEnvFile, readEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
6
|
+
import { log } from "../log.js";
|
|
7
|
+
import { announceTargetBranch } from "../utils/branch_notice.js";
|
|
8
|
+
import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
|
|
9
|
+
export const command = "env";
|
|
10
10
|
export const describe = "Manage a branch's Neon env variables locally";
|
|
11
11
|
/**
|
|
12
12
|
* Shown (to stderr) when `link` / `checkout` skip the bundled env pull because the user passed
|
|
13
13
|
* `--no-env-pull`. Names the two ways to get the branch's vars without an on-disk file written
|
|
14
14
|
* eagerly: an explicit `neonctl env pull`, or runtime injection via `neon-env run`.
|
|
15
15
|
*/
|
|
16
|
-
export const ENV_PULL_SKIPPED_HINT =
|
|
17
|
-
|
|
16
|
+
export const ENV_PULL_SKIPPED_HINT = "Skipped env pull (--no-env-pull). Run `neonctl env pull` to write this branch’s env vars " +
|
|
17
|
+
"(DATABASE_URL, …) into a local .env, or inject them at runtime with `neon-env run -- <your dev command>`.";
|
|
18
18
|
export const builder = (argv) => argv
|
|
19
|
-
.usage(
|
|
19
|
+
.usage("$0 env <sub-command> [options]")
|
|
20
20
|
.options({
|
|
21
|
-
|
|
22
|
-
branch: { describe:
|
|
21
|
+
"project-id": { describe: "Project ID", type: "string" },
|
|
22
|
+
branch: { describe: "Branch ID or name", type: "string" },
|
|
23
23
|
})
|
|
24
24
|
.middleware(fillSingleProject)
|
|
25
|
-
.command(
|
|
26
|
-
.usage(
|
|
25
|
+
.command("pull", "Write the branch's Neon env variables to a local .env file", (yargs) => yargs
|
|
26
|
+
.usage("$0 env pull [options]")
|
|
27
27
|
.options({
|
|
28
28
|
file: {
|
|
29
|
-
describe:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
type:
|
|
29
|
+
describe: "Target .env file to write. Defaults to an existing .env, " +
|
|
30
|
+
"otherwise .env.local. Only Neon variables are updated; other " +
|
|
31
|
+
"lines are preserved.",
|
|
32
|
+
type: "string",
|
|
33
33
|
},
|
|
34
34
|
})
|
|
35
|
-
.example(
|
|
36
|
-
.example(
|
|
35
|
+
.example("$0 env pull", "Write the linked branch's Neon vars into .env.local (or .env if present)")
|
|
36
|
+
.example("$0 env pull --branch preview --file .env.preview", "Pull a specific branch into a specific file"), async (args) => {
|
|
37
37
|
// Explicit `env pull` announces the branch it's reading from up front so the user
|
|
38
38
|
// can catch "pulled env from the wrong branch" before it overwrites their .env. The
|
|
39
39
|
// bundled auto-pull (link / checkout / apply) stays quiet — those already report the
|
|
@@ -42,7 +42,7 @@ export const builder = (argv) => argv
|
|
|
42
42
|
})
|
|
43
43
|
.demandCommand(1);
|
|
44
44
|
export const handler = (args) => args;
|
|
45
|
-
/** Every OS-level env var name `@
|
|
45
|
+
/** Every OS-level env var name `@neon/env` can emit, used only for reporting. */
|
|
46
46
|
const NEON_VAR_NAMES = Object.values(NEON_ENV_VAR_KEYS).flatMap((group) => Object.values(group));
|
|
47
47
|
/**
|
|
48
48
|
* The Neon env vars `env pull` *owns*, so it removes any that the branch no longer has when
|
|
@@ -68,7 +68,7 @@ export const pull = async (props, opts = {}) => {
|
|
|
68
68
|
const cwd = props.cwd ?? process.cwd();
|
|
69
69
|
const branch = await resolveBranchRef(props);
|
|
70
70
|
if (opts.announce) {
|
|
71
|
-
announceTargetBranch(props, branch,
|
|
71
|
+
announceTargetBranch(props, branch, "Pulling env from branch");
|
|
72
72
|
}
|
|
73
73
|
const branchId = branch.branchId;
|
|
74
74
|
// Resolve the target file first and layer its current contents under the resolver's env
|
|
@@ -91,9 +91,9 @@ export const pull = async (props, opts = {}) => {
|
|
|
91
91
|
});
|
|
92
92
|
const neonVars = pickNeonVars(vars);
|
|
93
93
|
if (Object.keys(neonVars).length === 0) {
|
|
94
|
-
log.info(
|
|
95
|
-
|
|
96
|
-
return { status:
|
|
94
|
+
log.info("No Neon env variables to pull for this branch (no DATABASE_URL or " +
|
|
95
|
+
"enabled Auth / Data API).");
|
|
96
|
+
return { status: "empty" };
|
|
97
97
|
}
|
|
98
98
|
// Reconcile rather than blindly merge: write the branch's current Neon vars and prune any
|
|
99
99
|
// Neon-owned vars the branch no longer has (e.g. NEON_AUTH_* / NEON_DATA_API_* carried over
|
|
@@ -101,11 +101,11 @@ export const pull = async (props, opts = {}) => {
|
|
|
101
101
|
const { written, removed } = mergeEnvFile(targetPath, neonVars, {
|
|
102
102
|
managedKeys: NEON_OWNED_ENV_KEYS,
|
|
103
103
|
});
|
|
104
|
-
log.info(
|
|
104
|
+
log.info("Pulled %d Neon variable%s into %s: %s", written.length, written.length === 1 ? "" : "s", targetPath, written.join(", "));
|
|
105
105
|
if (removed.length > 0) {
|
|
106
|
-
log.info(
|
|
106
|
+
log.info("Removed %d stale Neon variable%s not enabled on this branch: %s", removed.length, removed.length === 1 ? "" : "s", removed.join(", "));
|
|
107
107
|
}
|
|
108
|
-
return { status:
|
|
108
|
+
return { status: "written", written, file: targetPath };
|
|
109
109
|
};
|
|
110
110
|
/**
|
|
111
111
|
* Pull a freshly-pinned branch's Neon env vars into a local `.env`, bundled into `link` and
|
|
@@ -120,17 +120,17 @@ export const pull = async (props, opts = {}) => {
|
|
|
120
120
|
export const autoPullEnvAfterPin = async (props) => {
|
|
121
121
|
if (!props.envPull) {
|
|
122
122
|
log.info(chalk.dim(ENV_PULL_SKIPPED_HINT));
|
|
123
|
-
return { status:
|
|
123
|
+
return { status: "skipped" };
|
|
124
124
|
}
|
|
125
125
|
try {
|
|
126
126
|
return await pull(props);
|
|
127
127
|
}
|
|
128
128
|
catch (err) {
|
|
129
129
|
const message = err instanceof Error ? err.message : String(err);
|
|
130
|
-
log.warning(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return { status:
|
|
130
|
+
log.warning("Branch pinned, but pulling its Neon env vars failed: %s\n" +
|
|
131
|
+
"Run `neonctl env pull` once resolved (e.g. `neonctl deploy` if a declared service " +
|
|
132
|
+
"is missing), or inject them at runtime with `neon-env run -- <your dev command>`.", message);
|
|
133
|
+
return { status: "failed", message };
|
|
134
134
|
}
|
|
135
135
|
};
|
|
136
136
|
/**
|
|
@@ -139,14 +139,14 @@ export const autoPullEnvAfterPin = async (props) => {
|
|
|
139
139
|
*/
|
|
140
140
|
export const renderAgentPullNote = (result) => {
|
|
141
141
|
switch (result.status) {
|
|
142
|
-
case
|
|
143
|
-
return ` Pulled ${result.written.length} Neon env var${result.written.length === 1 ?
|
|
144
|
-
case
|
|
145
|
-
return
|
|
146
|
-
case
|
|
147
|
-
return (
|
|
148
|
-
|
|
149
|
-
case
|
|
142
|
+
case "written":
|
|
143
|
+
return ` Pulled ${result.written.length} Neon env var${result.written.length === 1 ? "" : "s"} into ${result.file}.`;
|
|
144
|
+
case "empty":
|
|
145
|
+
return " No Neon env vars to pull for this branch yet.";
|
|
146
|
+
case "skipped":
|
|
147
|
+
return (" Skipped env pull (--no-env-pull); run `neonctl env pull` later, " +
|
|
148
|
+
"or inject env at runtime with `neon-env run -- <your dev command>`.");
|
|
149
|
+
case "failed":
|
|
150
150
|
return ` Could not pull env vars (${result.message}); run \`neonctl env pull\` once resolved.`;
|
|
151
151
|
}
|
|
152
152
|
};
|
|
@@ -1,134 +1,133 @@
|
|
|
1
|
-
import { existsSync, statSync } from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { isNeonApiError } from
|
|
4
|
-
import {
|
|
5
|
-
import { log } from
|
|
6
|
-
import { branchIdFromProps, fillSingleProject } from
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { writer } from
|
|
10
|
-
import { createDeployment, deleteFunction, getFunction, listFunctions, } from '../functions_api.js';
|
|
1
|
+
import { existsSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { isNeonApiError, retryOnLock } from "../api.js";
|
|
4
|
+
import { createDeployment, deleteFunction, getFunction, listFunctions, } from "../functions_api.js";
|
|
5
|
+
import { log } from "../log.js";
|
|
6
|
+
import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
|
|
7
|
+
import { bundleEntry } from "../utils/esbuild.js";
|
|
8
|
+
import { zipBundle } from "../utils/zip.js";
|
|
9
|
+
import { writer } from "../writer.js";
|
|
11
10
|
const FUNCTION_FIELDS = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
"slug",
|
|
12
|
+
"name",
|
|
13
|
+
"invocation_url",
|
|
14
|
+
"created_at",
|
|
16
15
|
];
|
|
17
16
|
const FUNCTIONS_LIST_LIMIT = 100;
|
|
18
17
|
// Table columns for `functions list`. `status` is a derived field (the
|
|
19
18
|
// table writer reads flat fields only): the current deployment's status.
|
|
20
19
|
const LIST_TABLE_FIELDS = [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
"slug",
|
|
21
|
+
"name",
|
|
22
|
+
"status",
|
|
23
|
+
"invocation_url",
|
|
24
|
+
"created_at",
|
|
26
25
|
];
|
|
27
26
|
const DEPLOYMENT_FIELDS = [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
"id",
|
|
28
|
+
"status",
|
|
29
|
+
"runtime",
|
|
30
|
+
"memory_mib",
|
|
31
|
+
"created_at",
|
|
33
32
|
];
|
|
34
33
|
// Deploy emits the resolved deployment plus the function's invocation_url, so a
|
|
35
34
|
// successful `functions deploy` tells the user exactly where to call the function.
|
|
36
35
|
const DEPLOY_RESULT_FIELDS = [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
"id",
|
|
37
|
+
"status",
|
|
38
|
+
"invocation_url",
|
|
39
|
+
"runtime",
|
|
40
|
+
"memory_mib",
|
|
41
|
+
"created_at",
|
|
43
42
|
];
|
|
44
43
|
// In table mode a failed build's reason gets its own "deployment error"
|
|
45
44
|
// section after the deployment table; json/yaml carry the raw `error` field.
|
|
46
45
|
const writeDeploymentErrorSection = (out, dep) => {
|
|
47
|
-
if (dep.status ===
|
|
48
|
-
out.write({ reason: dep.error }, { fields: [
|
|
46
|
+
if (dep.status === "failed" && dep.error) {
|
|
47
|
+
out.write({ reason: dep.error }, { fields: ["reason"], title: "deployment error" });
|
|
49
48
|
}
|
|
50
49
|
};
|
|
51
50
|
const SLUG_PATTERN = /^[a-z0-9]{1,20}$/;
|
|
52
|
-
const SLUG_HELP =
|
|
51
|
+
const SLUG_HELP = "Use 1-20 lowercase letters and digits (no hyphens or other characters).";
|
|
53
52
|
// Entry-point discovery order inside --src.
|
|
54
|
-
const ENTRY_CANDIDATES = [
|
|
53
|
+
const ENTRY_CANDIDATES = ["index.ts", "index.mjs", "index.js"];
|
|
55
54
|
// Overridable so tests can poll fast; defaults to 2s in real use.
|
|
56
55
|
const POLL_INTERVAL_MS = Number(process.env.NEON_FUNCTIONS_POLL_INTERVAL_MS) || 2000;
|
|
57
56
|
// Upper bound on --wait polling so the CLI never hangs (e.g. if our deployment
|
|
58
57
|
// never shows up as current_deployment). Overridable so tests can time out fast;
|
|
59
58
|
// defaults to 10 minutes in real use.
|
|
60
59
|
const POLL_TIMEOUT_MS = Number(process.env.NEON_FUNCTIONS_POLL_TIMEOUT_MS) || 600000;
|
|
61
|
-
export const command =
|
|
62
|
-
export const describe =
|
|
63
|
-
export const aliases = [
|
|
60
|
+
export const command = "functions";
|
|
61
|
+
export const describe = "Manage Neon Functions";
|
|
62
|
+
export const aliases = ["function"];
|
|
64
63
|
export const builder = (argv) => argv
|
|
65
|
-
.usage(
|
|
64
|
+
.usage("$0 function <sub-command> [options]")
|
|
66
65
|
.options({
|
|
67
|
-
|
|
68
|
-
describe:
|
|
69
|
-
type:
|
|
66
|
+
"project-id": {
|
|
67
|
+
describe: "Project ID",
|
|
68
|
+
type: "string",
|
|
70
69
|
},
|
|
71
70
|
branch: {
|
|
72
|
-
describe:
|
|
73
|
-
type:
|
|
71
|
+
describe: "Branch ID or name",
|
|
72
|
+
type: "string",
|
|
74
73
|
},
|
|
75
74
|
})
|
|
76
75
|
.middleware(fillSingleProject)
|
|
77
|
-
.command(
|
|
78
|
-
.positional(
|
|
79
|
-
describe:
|
|
80
|
-
type:
|
|
76
|
+
.command("deploy <slug>", "Deploy a function from a local directory", (yargs) => yargs
|
|
77
|
+
.positional("slug", {
|
|
78
|
+
describe: "Function slug (1-20 lowercase letters and digits)",
|
|
79
|
+
type: "string",
|
|
81
80
|
demandOption: true,
|
|
82
81
|
})
|
|
83
82
|
.options({
|
|
84
83
|
src: {
|
|
85
|
-
describe:
|
|
86
|
-
type:
|
|
84
|
+
describe: "Function source: a directory containing index.ts, index.mjs, or index.js, or a path to the entry file",
|
|
85
|
+
type: "string",
|
|
87
86
|
},
|
|
88
87
|
// Removed flags, kept hidden so old invocations fail loudly instead
|
|
89
88
|
// of being silently ignored (the CLI has no .strictOptions()).
|
|
90
89
|
path: {
|
|
91
|
-
type:
|
|
90
|
+
type: "string",
|
|
92
91
|
hidden: true,
|
|
93
92
|
},
|
|
94
93
|
entry: {
|
|
95
|
-
type:
|
|
94
|
+
type: "string",
|
|
96
95
|
hidden: true,
|
|
97
96
|
},
|
|
98
97
|
runtime: {
|
|
99
|
-
describe:
|
|
100
|
-
type:
|
|
101
|
-
choices: [
|
|
98
|
+
describe: "Function runtime",
|
|
99
|
+
type: "string",
|
|
100
|
+
choices: ["nodejs24"],
|
|
102
101
|
},
|
|
103
102
|
env: {
|
|
104
|
-
describe:
|
|
105
|
-
type:
|
|
103
|
+
describe: "Environment variable as KEY=VALUE (repeatable)",
|
|
104
|
+
type: "string",
|
|
106
105
|
array: true,
|
|
107
106
|
},
|
|
108
107
|
wait: {
|
|
109
|
-
describe:
|
|
110
|
-
type:
|
|
108
|
+
describe: "Wait for the deployment to finish building",
|
|
109
|
+
type: "boolean",
|
|
111
110
|
default: true,
|
|
112
111
|
},
|
|
113
112
|
}), (args) => deploy(args))
|
|
114
|
-
.command(
|
|
115
|
-
.command(
|
|
116
|
-
.positional(
|
|
117
|
-
describe:
|
|
118
|
-
type:
|
|
113
|
+
.command("list", "List functions on the branch", (yargs) => yargs, (args) => list(args))
|
|
114
|
+
.command("get <slug>", "Show a function's details", (yargs) => yargs
|
|
115
|
+
.positional("slug", {
|
|
116
|
+
describe: "Function slug",
|
|
117
|
+
type: "string",
|
|
119
118
|
demandOption: true,
|
|
120
119
|
})
|
|
121
120
|
.options({
|
|
122
|
-
|
|
123
|
-
describe:
|
|
124
|
-
type:
|
|
125
|
-
alias:
|
|
121
|
+
"list-env-variables": {
|
|
122
|
+
describe: "List the environment variable names of the active deployment",
|
|
123
|
+
type: "boolean",
|
|
124
|
+
alias: "E",
|
|
126
125
|
default: false,
|
|
127
126
|
},
|
|
128
127
|
}), (args) => get(args))
|
|
129
|
-
.command(
|
|
130
|
-
describe:
|
|
131
|
-
type:
|
|
128
|
+
.command("delete <slug>", "Delete a function on the branch", (yargs) => yargs.positional("slug", {
|
|
129
|
+
describe: "Function slug",
|
|
130
|
+
type: "string",
|
|
132
131
|
demandOption: true,
|
|
133
132
|
}), (args) => deleteFn(args));
|
|
134
133
|
export const handler = (args) => {
|
|
@@ -139,7 +138,7 @@ const parseEnv = (entries) => {
|
|
|
139
138
|
return undefined;
|
|
140
139
|
const map = {};
|
|
141
140
|
for (const entry of entries) {
|
|
142
|
-
const eq = entry.indexOf(
|
|
141
|
+
const eq = entry.indexOf("=");
|
|
143
142
|
if (eq <= 0) {
|
|
144
143
|
throw new Error(`Invalid --env value "${entry}". Expected KEY=VALUE.`);
|
|
145
144
|
}
|
|
@@ -152,7 +151,7 @@ const statusHint = (slug, projectId, branchId) => `Check status with: neonctl fu
|
|
|
152
151
|
// deploy output shows where the function is reachable (not just the deployment id).
|
|
153
152
|
const emitDeployResult = (props, deployment, fn) => {
|
|
154
153
|
const out = writer(props).write({ ...deployment, invocation_url: fn?.invocation_url }, { fields: DEPLOY_RESULT_FIELDS });
|
|
155
|
-
if (props.output !==
|
|
154
|
+
if (props.output !== "json" && props.output !== "yaml") {
|
|
156
155
|
writeDeploymentErrorSection(out, deployment);
|
|
157
156
|
}
|
|
158
157
|
out.end();
|
|
@@ -163,8 +162,8 @@ const isTransient = (err) => isNeonApiError(err) &&
|
|
|
163
162
|
(err.status === undefined || err.status === 404 || err.status >= 500);
|
|
164
163
|
const deploy = async (props) => {
|
|
165
164
|
if (props.path !== undefined || props.entry !== undefined) {
|
|
166
|
-
throw new Error(
|
|
167
|
-
|
|
165
|
+
throw new Error("--path and --entry were removed. Use --src <dir>; the entry point " +
|
|
166
|
+
"is discovered as index.ts, index.mjs, or index.js in that directory.");
|
|
168
167
|
}
|
|
169
168
|
// At least one deploy option must be passed (--wait is excluded: it controls
|
|
170
169
|
// output, not what gets deployed).
|
|
@@ -172,15 +171,15 @@ const deploy = async (props) => {
|
|
|
172
171
|
props.env !== undefined ||
|
|
173
172
|
props.runtime !== undefined;
|
|
174
173
|
if (!hasOption) {
|
|
175
|
-
throw new Error(
|
|
176
|
-
|
|
174
|
+
throw new Error("Provide at least one option to deploy, e.g. --src or --env. " +
|
|
175
|
+
"See: neonctl function deploy --help.");
|
|
177
176
|
}
|
|
178
177
|
// Cheap, offline validation first - fail before any network round-trip.
|
|
179
178
|
if (!SLUG_PATTERN.test(props.slug)) {
|
|
180
179
|
throw new Error(`Invalid function slug "${props.slug}". ${SLUG_HELP}`);
|
|
181
180
|
}
|
|
182
|
-
const src = props.src ??
|
|
183
|
-
const runtime = props.runtime ??
|
|
181
|
+
const src = props.src ?? ".";
|
|
182
|
+
const runtime = props.runtime ?? "nodejs24";
|
|
184
183
|
const environment = parseEnv(props.env);
|
|
185
184
|
const srcStat = statSync(src, { throwIfNoEntry: false });
|
|
186
185
|
if (srcStat === undefined) {
|
|
@@ -191,7 +190,7 @@ const deploy = async (props) => {
|
|
|
191
190
|
? src
|
|
192
191
|
: ENTRY_CANDIDATES.map((name) => join(src, name)).find((p) => existsSync(p));
|
|
193
192
|
if (source === undefined) {
|
|
194
|
-
throw new Error(`No entry file found in ${src}. Expected one of: ${ENTRY_CANDIDATES.join(
|
|
193
|
+
throw new Error(`No entry file found in ${src}. Expected one of: ${ENTRY_CANDIDATES.join(", ")}.`);
|
|
195
194
|
}
|
|
196
195
|
// Bundle before any network round-trip so a bundling failure fails fast.
|
|
197
196
|
const zip = zipBundle(await bundleEntry(source));
|
|
@@ -219,8 +218,8 @@ const deploy = async (props) => {
|
|
|
219
218
|
const onSignal = () => {
|
|
220
219
|
interrupted = true;
|
|
221
220
|
};
|
|
222
|
-
process.once(
|
|
223
|
-
process.once(
|
|
221
|
+
process.once("SIGINT", onSignal);
|
|
222
|
+
process.once("SIGTERM", onSignal);
|
|
224
223
|
// Poll until a NEW version appears (id greater than the snapshot, or
|
|
225
224
|
// any version if there was none). --no-wait stops there; --wait stops at a
|
|
226
225
|
// terminal status. Bounded by POLL_TIMEOUT_MS so it never hangs.
|
|
@@ -252,14 +251,14 @@ const deploy = async (props) => {
|
|
|
252
251
|
resolvedFn = fn;
|
|
253
252
|
if (!props.wait)
|
|
254
253
|
break;
|
|
255
|
-
if (dep.status ===
|
|
254
|
+
if (dep.status === "completed" || dep.status === "failed")
|
|
256
255
|
break;
|
|
257
256
|
}
|
|
258
257
|
}
|
|
259
258
|
}
|
|
260
259
|
finally {
|
|
261
|
-
process.removeListener(
|
|
262
|
-
process.removeListener(
|
|
260
|
+
process.removeListener("SIGINT", onSignal);
|
|
261
|
+
process.removeListener("SIGTERM", onSignal);
|
|
263
262
|
}
|
|
264
263
|
if (interrupted) {
|
|
265
264
|
log.info(statusHint(props.slug, props.projectId, branchId));
|
|
@@ -276,11 +275,11 @@ const deploy = async (props) => {
|
|
|
276
275
|
log.info(statusHint(props.slug, props.projectId, branchId));
|
|
277
276
|
return;
|
|
278
277
|
}
|
|
279
|
-
if (resolved.status ===
|
|
278
|
+
if (resolved.status === "completed") {
|
|
280
279
|
log.info(`Function deployment ${props.slug}/${resolved.id} completed.`);
|
|
281
280
|
return;
|
|
282
281
|
}
|
|
283
|
-
if (resolved.status ===
|
|
282
|
+
if (resolved.status === "failed") {
|
|
284
283
|
throw new Error(`Function deployment ${props.slug}/${resolved.id} failed.`);
|
|
285
284
|
}
|
|
286
285
|
// --wait, new version appeared but the deadline hit before it finished.
|
|
@@ -290,20 +289,20 @@ const deploy = async (props) => {
|
|
|
290
289
|
const get = async (props) => {
|
|
291
290
|
const branchId = await branchIdFromProps(props);
|
|
292
291
|
const fn = await getFunction(props.apiClient, props.projectId, branchId, props.slug);
|
|
293
|
-
if (props.output ===
|
|
292
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
294
293
|
writer(props).end(fn, { fields: FUNCTION_FIELDS });
|
|
295
294
|
return;
|
|
296
295
|
}
|
|
297
296
|
const out = writer(props).write(fn, {
|
|
298
297
|
fields: FUNCTION_FIELDS,
|
|
299
|
-
title:
|
|
298
|
+
title: "function",
|
|
300
299
|
});
|
|
301
300
|
const current = fn.current_deployment;
|
|
302
301
|
const active = fn.active_deployment;
|
|
303
302
|
if (current && active && current.id === active.id) {
|
|
304
303
|
out.write(current, {
|
|
305
304
|
fields: DEPLOYMENT_FIELDS,
|
|
306
|
-
title:
|
|
305
|
+
title: "deployment (current, active)",
|
|
307
306
|
});
|
|
308
307
|
writeDeploymentErrorSection(out, current);
|
|
309
308
|
}
|
|
@@ -311,7 +310,7 @@ const get = async (props) => {
|
|
|
311
310
|
if (current) {
|
|
312
311
|
out.write(current, {
|
|
313
312
|
fields: DEPLOYMENT_FIELDS,
|
|
314
|
-
title:
|
|
313
|
+
title: "current deployment",
|
|
315
314
|
});
|
|
316
315
|
// The failure reason is shown only for the current deployment;
|
|
317
316
|
// the active one completed successfully by definition.
|
|
@@ -320,15 +319,15 @@ const get = async (props) => {
|
|
|
320
319
|
if (active) {
|
|
321
320
|
out.write(active, {
|
|
322
321
|
fields: DEPLOYMENT_FIELDS,
|
|
323
|
-
title:
|
|
322
|
+
title: "active deployment",
|
|
324
323
|
});
|
|
325
324
|
}
|
|
326
325
|
}
|
|
327
326
|
if (props.listEnvVariables) {
|
|
328
327
|
out.write((fn.active_deployment?.environment ?? []).map((name) => ({ name })), {
|
|
329
|
-
fields: [
|
|
330
|
-
title:
|
|
331
|
-
emptyMessage:
|
|
328
|
+
fields: ["name"],
|
|
329
|
+
title: "environment",
|
|
330
|
+
emptyMessage: "No environment variables on the active deployment.",
|
|
332
331
|
});
|
|
333
332
|
}
|
|
334
333
|
out.end();
|
|
@@ -353,22 +352,22 @@ const list = async (props) => {
|
|
|
353
352
|
for (;;) {
|
|
354
353
|
const page = await listFunctions(props.apiClient, props.projectId, branchId, { cursor, limit: FUNCTIONS_LIST_LIMIT });
|
|
355
354
|
functions.push(...page.functions);
|
|
356
|
-
log.debug(
|
|
355
|
+
log.debug("Got %d functions, next cursor: %s", page.functions.length, page.next);
|
|
357
356
|
// A server echoing the same cursor would loop forever; treat it as
|
|
358
357
|
// the end of the list.
|
|
359
358
|
if (!page.next || page.next === cursor)
|
|
360
359
|
break;
|
|
361
360
|
cursor = page.next;
|
|
362
361
|
}
|
|
363
|
-
if (props.output ===
|
|
362
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
364
363
|
writer(props).end(functions, { fields: FUNCTION_FIELDS });
|
|
365
364
|
return;
|
|
366
365
|
}
|
|
367
366
|
writer(props).end(functions.map((fn) => ({
|
|
368
367
|
...fn,
|
|
369
|
-
status: fn.current_deployment?.status ??
|
|
368
|
+
status: fn.current_deployment?.status ?? "",
|
|
370
369
|
})), {
|
|
371
370
|
fields: LIST_TABLE_FIELDS,
|
|
372
|
-
emptyMessage:
|
|
371
|
+
emptyMessage: "No functions found on this branch.",
|
|
373
372
|
});
|
|
374
373
|
};
|
package/dist/commands/index.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import * as auth from
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
7
|
-
import * as
|
|
8
|
-
import * as
|
|
9
|
-
import * as
|
|
10
|
-
import * as
|
|
11
|
-
import * as
|
|
12
|
-
import * as
|
|
13
|
-
import * as
|
|
14
|
-
import * as
|
|
15
|
-
import * as
|
|
16
|
-
import * as
|
|
17
|
-
import * as
|
|
18
|
-
import * as
|
|
19
|
-
import * as
|
|
20
|
-
import * as
|
|
21
|
-
import * as
|
|
22
|
-
import * as
|
|
23
|
-
import * as
|
|
24
|
-
import * as
|
|
25
|
-
import * as
|
|
26
|
-
import * as
|
|
1
|
+
import * as auth from "./auth.js";
|
|
2
|
+
import * as bootstrap from "./bootstrap.js";
|
|
3
|
+
import * as branches from "./branches.js";
|
|
4
|
+
import * as bucket from "./bucket.js";
|
|
5
|
+
import * as checkout from "./checkout.js";
|
|
6
|
+
import * as config from "./config.js";
|
|
7
|
+
import * as cs from "./connection_string.js";
|
|
8
|
+
import * as dataApi from "./data_api.js";
|
|
9
|
+
import * as databases from "./databases.js";
|
|
10
|
+
import * as deploy from "./deploy.js";
|
|
11
|
+
import * as dev from "./dev.js";
|
|
12
|
+
import * as env from "./env.js";
|
|
13
|
+
import * as functions from "./functions.js";
|
|
14
|
+
import * as init from "./init.js";
|
|
15
|
+
import * as ipAllow from "./ip_allow.js";
|
|
16
|
+
import * as link from "./link.js";
|
|
17
|
+
import * as neonAuth from "./neon_auth.js";
|
|
18
|
+
import * as operations from "./operations.js";
|
|
19
|
+
import * as orgs from "./orgs.js";
|
|
20
|
+
import * as projects from "./projects.js";
|
|
21
|
+
import * as psql from "./psql.js";
|
|
22
|
+
import * as roles from "./roles.js";
|
|
23
|
+
import * as setContext from "./set_context.js";
|
|
24
|
+
import * as status from "./status.js";
|
|
25
|
+
import * as users from "./user.js";
|
|
26
|
+
import * as vpcEndpoints from "./vpc_endpoints.js";
|
|
27
27
|
export default [
|
|
28
28
|
auth,
|
|
29
29
|
users,
|