neonctl 2.28.0 → 2.29.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.
- package/README.md +2 -2
- 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 +33 -33
- 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/config.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { resolveConfig } from "@neon/config";
|
|
4
|
+
import { apply, createBranch as createBranchFromPolicy, inspect, loadConfigFromFile, plan, } from "@neon/config-runtime";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { toNeonConfigView } from "../config_format.js";
|
|
7
|
+
import { contextBranch, readContextFile } from "../context.js";
|
|
8
|
+
import { isCi } from "../env.js";
|
|
9
|
+
import { loadEnvFileIntoProcess } from "../env_file.js";
|
|
10
|
+
import { log } from "../log.js";
|
|
11
|
+
import { announceTargetBranch } from "../utils/branch_notice.js";
|
|
12
|
+
import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
|
|
13
|
+
import { bundleEntry } from "../utils/esbuild.js";
|
|
14
|
+
import { addDependenciesArgs, resolvePackageManager, runCommand, } from "../utils/package_manager.js";
|
|
15
|
+
import { zipBundle } from "../utils/zip.js";
|
|
16
|
+
import { writer } from "../writer.js";
|
|
17
|
+
import { autoPullEnvAfterPin } from "./env.js";
|
|
15
18
|
/**
|
|
16
19
|
* Bundle a function with neonctl's OWN bundler (the shared esbuild helper) so the
|
|
17
20
|
* config-runtime never has to import esbuild itself. Injecting this keeps esbuild
|
|
@@ -19,19 +22,19 @@ import { autoPullEnvAfterPin } from './env.js';
|
|
|
19
22
|
* neonctl snapshot, which resolves esbuild dynamically at deploy time.
|
|
20
23
|
*/
|
|
21
24
|
const neonctlBundler = async (fn) => zipBundle(await bundleEntry(fn.source));
|
|
22
|
-
const INSPECT_FIELDS = [
|
|
25
|
+
const INSPECT_FIELDS = ["project", "branch", "config"];
|
|
23
26
|
// Deliberately minimal: action/kind/identifier are short and fixed-ish, so the table can
|
|
24
27
|
// never overflow. Per-change `details` (a function's long invocationUrl in particular) are
|
|
25
28
|
// intentionally NOT a column — they used to be JSON-stringified into a cell and blew the
|
|
26
29
|
// table past 190 cols. Function URLs are printed below as a plain list (see reportPushResult),
|
|
27
30
|
// and the full details are still available via `--output json`.
|
|
28
|
-
const APPLIED_FIELDS = [
|
|
31
|
+
const APPLIED_FIELDS = ["action", "kind", "identifier"];
|
|
29
32
|
const CONFLICT_FIELDS = [
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
"identifier",
|
|
34
|
+
"field",
|
|
35
|
+
"current",
|
|
36
|
+
"desired",
|
|
37
|
+
"reason",
|
|
35
38
|
];
|
|
36
39
|
/**
|
|
37
40
|
* Shared `--env` flag for `config plan|apply` and `deploy`. Loads a `.env` into
|
|
@@ -39,21 +42,21 @@ const CONFLICT_FIELDS = [
|
|
|
39
42
|
*/
|
|
40
43
|
export const envFlag = {
|
|
41
44
|
env: {
|
|
42
|
-
describe:
|
|
43
|
-
|
|
44
|
-
type:
|
|
45
|
+
describe: "Path to a .env file to load into the environment before evaluating neon.ts " +
|
|
46
|
+
"(so function env values resolve from it). Existing env vars are not overridden.",
|
|
47
|
+
type: "string",
|
|
45
48
|
},
|
|
46
49
|
};
|
|
47
50
|
/** Apply-only flags, exported so `deploy` can reuse the exact same surface. */
|
|
48
51
|
export const applyFlags = {
|
|
49
|
-
|
|
50
|
-
describe:
|
|
51
|
-
type:
|
|
52
|
+
"update-existing": {
|
|
53
|
+
describe: "Auto-confirm overriding existing remote settings on the branch",
|
|
54
|
+
type: "boolean",
|
|
52
55
|
default: false,
|
|
53
56
|
},
|
|
54
|
-
|
|
55
|
-
describe:
|
|
56
|
-
type:
|
|
57
|
+
"allow-protected": {
|
|
58
|
+
describe: "Auto-confirm applying to a branch marked protected on Neon",
|
|
59
|
+
type: "boolean",
|
|
57
60
|
default: false,
|
|
58
61
|
},
|
|
59
62
|
};
|
|
@@ -63,60 +66,181 @@ export const applyFlags = {
|
|
|
63
66
|
* same bundled convenience as `link` / `checkout`. On by default; `--no-env-pull` opts out.
|
|
64
67
|
*/
|
|
65
68
|
export const envPullFlag = {
|
|
66
|
-
|
|
69
|
+
"env-pull": {
|
|
67
70
|
describe: "Pull the branch's Neon env vars (DATABASE_URL, …) into a local .env after a " +
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
type:
|
|
71
|
+
"successful apply. On by default; use --no-env-pull to skip (e.g. when injecting " +
|
|
72
|
+
"env at runtime with `neon-env run` / `neon dev`).",
|
|
73
|
+
type: "boolean",
|
|
71
74
|
default: true,
|
|
72
75
|
},
|
|
73
76
|
};
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
// ── `config init` ─────────────────────────────────────────────────────────────
|
|
78
|
+
/**
|
|
79
|
+
* The published npm packages a `neon.ts` project needs — the `@neon/*` org names.
|
|
80
|
+
*
|
|
81
|
+
* ⚠️ These ship to users the next time `neonctl` is released, so do NOT release
|
|
82
|
+
* neonctl until `@neon/config` and `@neon/env` are published to npm — otherwise
|
|
83
|
+
* `config init` would install packages that don't exist yet. (The libraries are
|
|
84
|
+
* mid-migration from `@neondatabase/*`; track their publish before cutting a CLI
|
|
85
|
+
* release.)
|
|
86
|
+
*/
|
|
87
|
+
const CONFIG_PACKAGE = "@neon/config";
|
|
88
|
+
const ENV_PACKAGE = "@neon/env";
|
|
89
|
+
const REQUIRED_PACKAGES = [CONFIG_PACKAGE, ENV_PACKAGE];
|
|
90
|
+
/** package.json fields a dependency can be declared in. */
|
|
91
|
+
const DEPENDENCY_FIELDS = [
|
|
92
|
+
"dependencies",
|
|
93
|
+
"devDependencies",
|
|
94
|
+
"peerDependencies",
|
|
95
|
+
"optionalDependencies",
|
|
96
|
+
];
|
|
97
|
+
/** Config filenames the runtime loads (mirrors @neon/config's loader). */
|
|
98
|
+
const NEON_CONFIG_FILENAMES = ["neon.ts", "neon.mts", "neon.js", "neon.mjs"];
|
|
99
|
+
/** Whether `dir` already has a Neon config file the runtime would load. */
|
|
100
|
+
export const hasNeonConfigFile = (dir) => NEON_CONFIG_FILENAMES.some((name) => existsSync(join(dir, name)));
|
|
101
|
+
/** Starter `neon.ts` written by `config init` when a project has none. */
|
|
102
|
+
const NEON_CONFIG_TEMPLATE = `import { defineConfig } from "${CONFIG_PACKAGE}/v1";
|
|
103
|
+
|
|
104
|
+
export default defineConfig({
|
|
105
|
+
// Declare your Neon services here
|
|
106
|
+
auth: false,
|
|
107
|
+
// Branch policy: per-branch tuning
|
|
108
|
+
branch: (branch) => {
|
|
109
|
+
if (branch.isDefault) {
|
|
110
|
+
// Default branch: no overrides, uses project defaults
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
if (!branch.exists) {
|
|
114
|
+
// New non-default branches: auto-expire
|
|
115
|
+
// Run \`neon checkout <name>\` to create a new branch with these settings
|
|
116
|
+
return { ttl: "7d" };
|
|
117
|
+
}
|
|
118
|
+
// Existing branch: no changes
|
|
119
|
+
return {};
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
`;
|
|
123
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
124
|
+
/**
|
|
125
|
+
* The {@link REQUIRED_PACKAGES} not already declared in the project's package.json
|
|
126
|
+
* (any dependency field). A missing or malformed package.json means none are
|
|
127
|
+
* declared, so all are reported missing.
|
|
128
|
+
*/
|
|
129
|
+
const missingDependencies = (cwd) => {
|
|
130
|
+
const declared = new Set();
|
|
131
|
+
const pkgPath = join(cwd, "package.json");
|
|
132
|
+
if (existsSync(pkgPath)) {
|
|
133
|
+
let parsed;
|
|
134
|
+
try {
|
|
135
|
+
parsed = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
parsed = undefined;
|
|
139
|
+
}
|
|
140
|
+
if (isRecord(parsed)) {
|
|
141
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
142
|
+
const deps = parsed[field];
|
|
143
|
+
if (isRecord(deps)) {
|
|
144
|
+
for (const name of Object.keys(deps))
|
|
145
|
+
declared.add(name);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return REQUIRED_PACKAGES.filter((pkg) => !declared.has(pkg));
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Scaffold a `neon.ts` policy and make sure the Neon config packages are
|
|
154
|
+
* installed, so a project can go straight to `neon config plan` / `apply`.
|
|
155
|
+
* Purely local — it never touches the Neon API (see {@link isConfigInit}).
|
|
156
|
+
*/
|
|
157
|
+
export const initCmd = async (props) => {
|
|
158
|
+
const cwd = props.cwd ?? process.cwd();
|
|
159
|
+
const run = props.run ?? runCommand;
|
|
160
|
+
// 1. Scaffold neon.ts unless the project already has a Neon config file.
|
|
161
|
+
const existing = NEON_CONFIG_FILENAMES.find((name) => existsSync(join(cwd, name)));
|
|
162
|
+
if (existing) {
|
|
163
|
+
log.info("Found an existing %s — leaving it untouched.", existing);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
writeFileSync(join(cwd, "neon.ts"), NEON_CONFIG_TEMPLATE);
|
|
167
|
+
log.info("Created neon.ts with a starter policy.");
|
|
168
|
+
}
|
|
169
|
+
// 2. Make sure the config packages are installed.
|
|
170
|
+
const missing = missingDependencies(cwd);
|
|
171
|
+
if (missing.length === 0) {
|
|
172
|
+
log.info("%s are already installed.", REQUIRED_PACKAGES.join(" and "));
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
const pm = resolvePackageManager();
|
|
176
|
+
const args = addDependenciesArgs(pm, missing);
|
|
177
|
+
if (props.install === false) {
|
|
178
|
+
log.info("Install the Neon config packages to use neon.ts: %s %s", pm, args.join(" "));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
log.info("Installing %s with %s…", missing.join(", "), pm);
|
|
182
|
+
const ok = await run(pm, args, cwd);
|
|
183
|
+
if (!ok) {
|
|
184
|
+
log.warning("Could not install the config packages automatically. Run by hand: %s %s", pm, args.join(" "));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
log.info("Next: edit neon.ts, then run `neon config plan` to preview and `neon config apply`.");
|
|
189
|
+
};
|
|
190
|
+
export const command = "config";
|
|
191
|
+
export const describe = "Manage a branch with a neon.ts policy";
|
|
76
192
|
export const builder = (argv) => argv
|
|
77
|
-
.usage(
|
|
193
|
+
.usage("$0 config <sub-command> [options]")
|
|
78
194
|
.options({
|
|
79
|
-
|
|
80
|
-
describe:
|
|
81
|
-
type:
|
|
195
|
+
"project-id": {
|
|
196
|
+
describe: "Project ID",
|
|
197
|
+
type: "string",
|
|
82
198
|
},
|
|
83
199
|
branch: {
|
|
84
|
-
describe:
|
|
85
|
-
type:
|
|
200
|
+
describe: "Branch ID or name",
|
|
201
|
+
type: "string",
|
|
86
202
|
},
|
|
87
203
|
})
|
|
88
204
|
.middleware(fillSingleProject)
|
|
89
|
-
.command(
|
|
90
|
-
|
|
205
|
+
.command("status", "Show the branch's live Neon state", (yargs) => yargs.options({
|
|
206
|
+
"config-json": {
|
|
91
207
|
describe: "Print only the branch's live config as neon.ts-shaped JSON " +
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
type:
|
|
208
|
+
"(services + branch tuning + preview), to stdout. Useful for " +
|
|
209
|
+
"scripting or copying into a neon.ts.",
|
|
210
|
+
type: "boolean",
|
|
95
211
|
default: false,
|
|
96
212
|
},
|
|
97
|
-
|
|
98
|
-
describe:
|
|
99
|
-
|
|
100
|
-
type:
|
|
213
|
+
"current-branch": {
|
|
214
|
+
describe: "Print only the linked branch name from the local .neon file " +
|
|
215
|
+
"(no network). Exits non-zero when no branch is pinned.",
|
|
216
|
+
type: "boolean",
|
|
101
217
|
default: false,
|
|
102
218
|
},
|
|
103
219
|
}), (args) => status(args))
|
|
104
|
-
.command(
|
|
220
|
+
.command("plan", "Show what `config apply` would change (dry run)", (yargs) => yargs.options({
|
|
105
221
|
config: {
|
|
106
|
-
describe:
|
|
107
|
-
type:
|
|
222
|
+
describe: "Path to a neon.ts policy (defaults to walking up from cwd)",
|
|
223
|
+
type: "string",
|
|
108
224
|
},
|
|
109
225
|
...envFlag,
|
|
110
226
|
}), (args) => planCmd(args))
|
|
111
|
-
.command(
|
|
227
|
+
.command("apply", "Apply a neon.ts policy to the branch", (yargs) => yargs.options({
|
|
112
228
|
config: {
|
|
113
|
-
describe:
|
|
114
|
-
type:
|
|
229
|
+
describe: "Path to a neon.ts policy (defaults to walking up from cwd)",
|
|
230
|
+
type: "string",
|
|
115
231
|
},
|
|
116
232
|
...envFlag,
|
|
117
233
|
...applyFlags,
|
|
118
234
|
...envPullFlag,
|
|
119
|
-
}), (args) => applyCmd(args))
|
|
235
|
+
}), (args) => applyCmd(args))
|
|
236
|
+
.command("init", "Scaffold a neon.ts policy and install the Neon config packages", (yargs) => yargs.options({
|
|
237
|
+
install: {
|
|
238
|
+
describe: "Install @neon/config and @neon/env if they're missing. " +
|
|
239
|
+
"On by default; use --no-install to just print the command.",
|
|
240
|
+
type: "boolean",
|
|
241
|
+
default: true,
|
|
242
|
+
},
|
|
243
|
+
}), (args) => initCmd(args));
|
|
120
244
|
export const handler = (args) => {
|
|
121
245
|
return args;
|
|
122
246
|
};
|
|
@@ -125,7 +249,7 @@ const loadConfig = async (props) => {
|
|
|
125
249
|
// `process.env.X` sees them. Must happen before the policy module is imported/evaluated.
|
|
126
250
|
if (props.env) {
|
|
127
251
|
const applied = loadEnvFileIntoProcess(props.env);
|
|
128
|
-
log.debug(
|
|
252
|
+
log.debug("Loaded %d var(s) from %s into the environment: %s", applied.length, props.env, applied.join(", "));
|
|
129
253
|
}
|
|
130
254
|
const { config } = await loadConfigFromFile({
|
|
131
255
|
...(props.config ? { path: props.config } : {}),
|
|
@@ -143,7 +267,7 @@ export const status = async (props) => {
|
|
|
143
267
|
else {
|
|
144
268
|
// No branch pinned: hint on stderr and exit non-zero (grep-style) so a prompt's
|
|
145
269
|
// `when` hides the segment cleanly instead of rendering a bare icon.
|
|
146
|
-
log.info(
|
|
270
|
+
log.info("No branch pinned. Run `neonctl checkout <branch>` to pin a branch and pull its env vars.");
|
|
147
271
|
process.exitCode = 1;
|
|
148
272
|
}
|
|
149
273
|
return;
|
|
@@ -152,7 +276,7 @@ export const status = async (props) => {
|
|
|
152
276
|
// `--config-json` is a script-friendly mode that emits only JSON to stdout, so keep it
|
|
153
277
|
// pristine; the regular human view gets the "which branch am I inspecting" guardrail.
|
|
154
278
|
if (!props.configJson) {
|
|
155
|
-
announceTargetBranch(props, branch,
|
|
279
|
+
announceTargetBranch(props, branch, "Inspecting branch");
|
|
156
280
|
}
|
|
157
281
|
const branchId = branch.branchId;
|
|
158
282
|
const live = await inspect({
|
|
@@ -188,7 +312,7 @@ export const status = async (props) => {
|
|
|
188
312
|
export const planCmd = async (props) => {
|
|
189
313
|
const config = await loadConfig(props);
|
|
190
314
|
const branch = await resolveBranchRef(props);
|
|
191
|
-
announceTargetBranch(props, branch,
|
|
315
|
+
announceTargetBranch(props, branch, "Planning against branch");
|
|
192
316
|
const branchId = branch.branchId;
|
|
193
317
|
// `plan` is a dry run that never bundles, so its options don't accept (or need)
|
|
194
318
|
// an injected bundler — only `apply` does (it uses neonctlBundler).
|
|
@@ -199,12 +323,12 @@ export const planCmd = async (props) => {
|
|
|
199
323
|
...(props.apiHost ? { apiHost: props.apiHost } : {}),
|
|
200
324
|
...(props.runtimeApi ? { api: props.runtimeApi } : {}),
|
|
201
325
|
});
|
|
202
|
-
reportPushResult(props, result,
|
|
326
|
+
reportPushResult(props, result, "plan", utilizedServices(config));
|
|
203
327
|
};
|
|
204
328
|
export const applyCmd = async (props) => {
|
|
205
329
|
const config = await loadConfig(props);
|
|
206
330
|
const branch = await resolveBranchRef(props);
|
|
207
|
-
announceTargetBranch(props, branch,
|
|
331
|
+
announceTargetBranch(props, branch, "Applying to branch");
|
|
208
332
|
const branchId = branch.branchId;
|
|
209
333
|
const result = await apply(config, {
|
|
210
334
|
projectId: props.projectId,
|
|
@@ -216,7 +340,7 @@ export const applyCmd = async (props) => {
|
|
|
216
340
|
...(props.allowProtected ? { allowProtectedBranch: true } : {}),
|
|
217
341
|
bundleFunction: neonctlBundler,
|
|
218
342
|
});
|
|
219
|
-
reportPushResult(props, result,
|
|
343
|
+
reportPushResult(props, result, "apply", utilizedServices(config));
|
|
220
344
|
// After a successful apply/deploy, write the branch's Neon env vars to a local .env —
|
|
221
345
|
// the same bundled convenience as `link` / `checkout`, so the branch is immediately
|
|
222
346
|
// usable for local dev. `--no-env-pull` opts out; a pull failure degrades to a warning
|
|
@@ -232,7 +356,7 @@ export const applyCmd = async (props) => {
|
|
|
232
356
|
const isToggleEnabled = (toggle) => {
|
|
233
357
|
if (toggle === undefined)
|
|
234
358
|
return false;
|
|
235
|
-
if (typeof toggle ===
|
|
359
|
+
if (typeof toggle === "boolean")
|
|
236
360
|
return toggle;
|
|
237
361
|
return toggle.enabled !== false;
|
|
238
362
|
};
|
|
@@ -246,19 +370,19 @@ const isToggleEnabled = (toggle) => {
|
|
|
246
370
|
* lives in the per-branch closure), so reading it straight off `config` is accurate.
|
|
247
371
|
*/
|
|
248
372
|
const utilizedServices = (config) => {
|
|
249
|
-
const services = [
|
|
373
|
+
const services = ["Postgres"];
|
|
250
374
|
if (isToggleEnabled(config.auth))
|
|
251
|
-
services.push(
|
|
375
|
+
services.push("Neon Auth");
|
|
252
376
|
if (isToggleEnabled(config.dataApi))
|
|
253
|
-
services.push(
|
|
377
|
+
services.push("Data API");
|
|
254
378
|
if (Object.keys(config.preview?.buckets ?? {}).length > 0) {
|
|
255
|
-
services.push(
|
|
379
|
+
services.push("Object Storage");
|
|
256
380
|
}
|
|
257
381
|
if (Object.keys(config.preview?.functions ?? {}).length > 0) {
|
|
258
|
-
services.push(
|
|
382
|
+
services.push("Functions");
|
|
259
383
|
}
|
|
260
384
|
if (isToggleEnabled(config.preview?.aiGateway))
|
|
261
|
-
services.push(
|
|
385
|
+
services.push("AI Gateway");
|
|
262
386
|
return services;
|
|
263
387
|
};
|
|
264
388
|
/**
|
|
@@ -270,12 +394,12 @@ const utilizedServices = (config) => {
|
|
|
270
394
|
* for being missing from the plan above.
|
|
271
395
|
*/
|
|
272
396
|
const reportPushResult = (props, result, mode, services) => {
|
|
273
|
-
if (props.output ===
|
|
397
|
+
if (props.output === "json" || props.output === "yaml") {
|
|
274
398
|
writer(props).end({ ...result, services }, { fields: [] });
|
|
275
399
|
return;
|
|
276
400
|
}
|
|
277
401
|
const changes = result.applied
|
|
278
|
-
.filter((change) => change.action !==
|
|
402
|
+
.filter((change) => change.action !== "noop")
|
|
279
403
|
.map((change) => ({
|
|
280
404
|
action: change.action,
|
|
281
405
|
kind: change.kind,
|
|
@@ -293,11 +417,11 @@ const reportPushResult = (props, result, mode, services) => {
|
|
|
293
417
|
// Keyed by slug so a function never shows twice.
|
|
294
418
|
const functionUrlBySlug = new Map();
|
|
295
419
|
for (const change of result.applied) {
|
|
296
|
-
if (change.action ===
|
|
420
|
+
if (change.action === "noop")
|
|
297
421
|
continue;
|
|
298
422
|
const slug = change.details?.slug;
|
|
299
423
|
const invocationUrl = change.details?.invocationUrl;
|
|
300
|
-
if (typeof slug ===
|
|
424
|
+
if (typeof slug === "string" && typeof invocationUrl === "string") {
|
|
301
425
|
functionUrlBySlug.set(slug, invocationUrl);
|
|
302
426
|
}
|
|
303
427
|
}
|
|
@@ -306,11 +430,11 @@ const reportPushResult = (props, result, mode, services) => {
|
|
|
306
430
|
if (changes.length > 0) {
|
|
307
431
|
out.write(changes, {
|
|
308
432
|
fields: APPLIED_FIELDS,
|
|
309
|
-
title: mode ===
|
|
433
|
+
title: mode === "plan" ? "Planned changes" : "Applied changes",
|
|
310
434
|
});
|
|
311
435
|
}
|
|
312
436
|
if (conflicts.length > 0) {
|
|
313
|
-
out.write(conflicts, { fields: CONFLICT_FIELDS, title:
|
|
437
|
+
out.write(conflicts, { fields: CONFLICT_FIELDS, title: "Conflicts" });
|
|
314
438
|
}
|
|
315
439
|
// Flush any tables, then append the lists/summary so they read directly below them.
|
|
316
440
|
out.end();
|
|
@@ -318,7 +442,7 @@ const reportPushResult = (props, result, mode, services) => {
|
|
|
318
442
|
// which makes any bordered table overflow and wrap awkwardly in a normal terminal. A list
|
|
319
443
|
// lets each URL reflow on its own line, and stays copy-pasteable.
|
|
320
444
|
if (functionUrlBySlug.size > 0) {
|
|
321
|
-
const heading = mode ===
|
|
445
|
+
const heading = mode === "plan" ? "Function URLs (after apply)" : "Function URLs";
|
|
322
446
|
out.text(`\n${isCi() ? heading : chalk.bold(heading)}\n`);
|
|
323
447
|
for (const [slug, invocationUrl] of functionUrlBySlug) {
|
|
324
448
|
out.text(` • ${slug}: ${invocationUrl}\n`);
|
|
@@ -327,14 +451,14 @@ const reportPushResult = (props, result, mode, services) => {
|
|
|
327
451
|
if (noChanges) {
|
|
328
452
|
log.info(`No changes — branch ${result.branchName} already matches the policy.`);
|
|
329
453
|
}
|
|
330
|
-
out.text(`\nUtilized services: ${services.join(
|
|
454
|
+
out.text(`\nUtilized services: ${services.join(", ")}\n`);
|
|
331
455
|
if (conflicts.length > 0) {
|
|
332
|
-
log.info(
|
|
456
|
+
log.info("Resolve the conflicts above, or re-run with --update-existing to override the current remote settings.");
|
|
333
457
|
}
|
|
334
458
|
};
|
|
335
459
|
const stringify = (value) => value === undefined
|
|
336
|
-
?
|
|
337
|
-
: typeof value ===
|
|
460
|
+
? ""
|
|
461
|
+
: typeof value === "string"
|
|
338
462
|
? value
|
|
339
463
|
: JSON.stringify(value);
|
|
340
464
|
/**
|
|
@@ -359,7 +483,7 @@ export const applyPolicyOnCreate = async (props) => {
|
|
|
359
483
|
return;
|
|
360
484
|
throw err;
|
|
361
485
|
}
|
|
362
|
-
log.info(
|
|
486
|
+
log.info("Applying neon.ts policy to the new branch…");
|
|
363
487
|
const result = await apply(config, {
|
|
364
488
|
projectId: props.projectId,
|
|
365
489
|
branchId: props.branchId,
|
|
@@ -374,12 +498,12 @@ export const applyPolicyOnCreate = async (props) => {
|
|
|
374
498
|
};
|
|
375
499
|
/** Log a one-line summary of what applying a `neon.ts` policy changed (or that nothing did). */
|
|
376
500
|
const logPolicyResult = (result) => {
|
|
377
|
-
const changes = result.applied.filter((c) => c.action !==
|
|
501
|
+
const changes = result.applied.filter((c) => c.action !== "noop");
|
|
378
502
|
if (changes.length === 0) {
|
|
379
|
-
log.info(
|
|
503
|
+
log.info("neon.ts applied — no changes were needed.");
|
|
380
504
|
return;
|
|
381
505
|
}
|
|
382
|
-
log.info(
|
|
506
|
+
log.info("neon.ts applied — %d change%s: %s", changes.length, changes.length === 1 ? "" : "s", changes.map((c) => `${c.action} ${c.identifier}`).join(", "));
|
|
383
507
|
};
|
|
384
508
|
/**
|
|
385
509
|
* Create a branch **from** the local `neon.ts` policy. Returns `null` when there is no
|
|
@@ -414,7 +538,7 @@ export const createBranchFromPolicyOnCheckout = async (props) => {
|
|
|
414
538
|
...(props.runtimeApi ? { api: props.runtimeApi } : {}),
|
|
415
539
|
bundleFunction: neonctlBundler,
|
|
416
540
|
});
|
|
417
|
-
log.info(
|
|
541
|
+
log.info("Created branch %s (%s) from neon.ts policy.", branchName, branchId);
|
|
418
542
|
logPolicyResult(result);
|
|
419
543
|
return { branchId };
|
|
420
544
|
};
|