neon 4.14.6 → 4.15.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 +7 -3
- package/dist/commands/checkout.js +14 -5
- package/dist/commands/config.js +2 -1
- package/dist/context.js +17 -5
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -440,8 +440,8 @@ The branch argument is **optional**: run `neon checkout` with no branch in an in
|
|
|
440
440
|
|
|
441
441
|
Branch **id vs name** is detected automatically (a `br-…` value is treated as an id):
|
|
442
442
|
|
|
443
|
-
- **id** — matched strictly by id. A non-existent id is a hard "not found" error (ids are server-assigned, so checkout never creates one).
|
|
444
|
-
- **name** — matched by name. If the name doesn't exist,
|
|
443
|
+
- **id** — matched strictly by id. A non-existent id is a hard "not found" error (ids are server-assigned, so checkout never creates one, including with `--create`).
|
|
444
|
+
- **name** — matched by name. If the name doesn't exist, pass `--create` to create it (equivalent to `neon branch create --name <name>`: branched from the project's default branch with a read-write compute, or from `neon.ts` when that file is present), then check it out. `--create` is a no-op when the name already exists. Without `--create`, an interactive terminal offers to create the branch; in CI or with no TTY the error names `--create`.
|
|
445
445
|
|
|
446
446
|
The project is resolved through the standard neon chain, each entry winning over the next:
|
|
447
447
|
|
|
@@ -457,11 +457,15 @@ The resolved branch is then written (by name) to the same `.neon` file `link` us
|
|
|
457
457
|
$ neon checkout main --project-id polished-snowflake-12345678
|
|
458
458
|
INFO: Checked out branch br-main-branch-87654321 on project polished-snowflake-12345678. Updated /path/to/cwd/.neon.
|
|
459
459
|
|
|
460
|
+
$ neon checkout dev --create --project-id polished-snowflake-12345678
|
|
461
|
+
INFO: Created branch dev (br-dev-branch-12345678).
|
|
462
|
+
INFO: Checked out branch br-dev-branch-12345678 on project polished-snowflake-12345678. Updated /path/to/cwd/.neon.
|
|
463
|
+
|
|
460
464
|
$ cat .neon
|
|
461
465
|
{
|
|
462
466
|
"orgId": "org-abc123",
|
|
463
467
|
"projectId": "polished-snowflake-12345678",
|
|
464
|
-
"branch": "
|
|
468
|
+
"branch": "dev"
|
|
465
469
|
}
|
|
466
470
|
```
|
|
467
471
|
|
|
@@ -28,7 +28,7 @@ const formatCheckoutPolicyFailure = (opts) => {
|
|
|
28
28
|
return [
|
|
29
29
|
`Branch ${opts.branchName} (${opts.branchId}) was created and checked out, but applying neon.ts to it failed: ${opts.failure}`,
|
|
30
30
|
`The branch is usable but does not match the policy, and \`${cli} checkout\` never reconciles a branch that already exists.`,
|
|
31
|
-
`Fix the cause above, then run \`${cli} deploy --update-existing${envArg}\` to apply the policy to it — or, if your policy only configures new branches (keyed on \`!branch.exists\`), delete the branch and check it out again: \`${cli} branches delete ${opts.branchName}\` then \`${cli} checkout ${opts.branchName}${envArg}\`.`
|
|
31
|
+
`Fix the cause above, then run \`${cli} deploy --update-existing${envArg}\` to apply the policy to it — or, if your policy only configures new branches (keyed on \`!branch.exists\`), delete the branch and check it out again: \`${cli} branches delete ${opts.branchName}\` then \`${cli} checkout ${opts.branchName} --create${envArg}\`.`
|
|
32
32
|
].join("\n");
|
|
33
33
|
};
|
|
34
34
|
const builder = (argv) => argv.usage("$0 checkout [id|name] [options]").positional("id", {
|
|
@@ -44,6 +44,11 @@ const builder = (argv) => argv.usage("$0 checkout [id|name] [options]").position
|
|
|
44
44
|
type: "boolean",
|
|
45
45
|
default: true
|
|
46
46
|
},
|
|
47
|
+
create: {
|
|
48
|
+
describe: "Create the named branch if it does not exist, then check it out",
|
|
49
|
+
type: "boolean",
|
|
50
|
+
default: false
|
|
51
|
+
},
|
|
47
52
|
...envFlag,
|
|
48
53
|
env: {
|
|
49
54
|
...envFlag.env,
|
|
@@ -52,12 +57,14 @@ const builder = (argv) => argv.usage("$0 checkout [id|name] [options]").position
|
|
|
52
57
|
}).example([
|
|
53
58
|
["$0 checkout", "Pick a branch interactively from the project in the closest .neon file"],
|
|
54
59
|
["$0 checkout main", "Pin the branch named \"main\" in the closest .neon file"],
|
|
55
|
-
["$0 checkout
|
|
60
|
+
["$0 checkout dev --create", "Create and pin a missing branch named dev"],
|
|
61
|
+
["$0 checkout feat --create --env .env.local", "Create feat from neon.ts, loading Function env from .env.local first"],
|
|
56
62
|
["$0 checkout br-cool-snow-12345678 --project-id project-id-123", "Pin a branch by id for an explicit project"]
|
|
57
63
|
]);
|
|
58
64
|
const handler = async (props) => {
|
|
59
65
|
const previousBranch = contextBranch(readContextFile(props.contextFile));
|
|
60
66
|
if (previousBranch) log.info("%s Currently on branch %s", chalk.dim("→"), chalk.cyan.bold(previousBranch));
|
|
67
|
+
if (props.create && !props.id?.trim()) throw new Error(`No branch specified. Pass a branch name with --create (e.g. \`${getCliName()} checkout dev --create\`).`);
|
|
61
68
|
const projectId = await resolveProjectId(props);
|
|
62
69
|
const { branchId, branchName, created, policyApplied, policyFailure } = await resolveBranchId(props, projectId);
|
|
63
70
|
const orgId = await resolveOrgId(props, projectId);
|
|
@@ -137,8 +144,10 @@ const resolveBranchId = async (props, projectId) => {
|
|
|
137
144
|
created: false,
|
|
138
145
|
policyApplied: false
|
|
139
146
|
};
|
|
140
|
-
if (
|
|
141
|
-
|
|
147
|
+
if (props.create) return createCheckoutBranch(props, projectId, ref, branches);
|
|
148
|
+
const missingName = notFoundMessage(ref, branches, { suggestCreate: true });
|
|
149
|
+
if (isCi() || !process.stdout.isTTY) throw new Error(missingName);
|
|
150
|
+
log.error(missingName);
|
|
142
151
|
const { create } = await prompts({
|
|
143
152
|
type: "confirm",
|
|
144
153
|
name: "create",
|
|
@@ -178,7 +187,7 @@ const createCheckoutBranch = async (props, projectId, name, branches) => {
|
|
|
178
187
|
policyApplied: false
|
|
179
188
|
};
|
|
180
189
|
};
|
|
181
|
-
const notFoundMessage = (ref, branches) => `Branch ${ref} not found
|
|
190
|
+
const notFoundMessage = (ref, branches, opts) => `Branch ${ref} not found.${opts?.suggestCreate ? " Pass --create to create it." : ""}\nAvailable branches: ${branches.map((b) => b.name).join(", ")}`;
|
|
182
191
|
/**
|
|
183
192
|
* Resolve the org id to heal into the context file.
|
|
184
193
|
*
|
package/dist/commands/config.js
CHANGED
|
@@ -2,7 +2,7 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
|
2
2
|
import { isCi } from "../env.js";
|
|
3
3
|
import { log } from "../log.js";
|
|
4
4
|
import { getApiClient } from "../api.js";
|
|
5
|
-
import { contextBranch, readContextFile } from "../context.js";
|
|
5
|
+
import { contextBranch, ensureDirectoryGitignored, readContextFile } from "../context.js";
|
|
6
6
|
import { getCliName } from "../utils/cli_name.js";
|
|
7
7
|
import { toNeonConfigView } from "../config_format.js";
|
|
8
8
|
import { declaredNeonServices } from "../config_services.js";
|
|
@@ -229,6 +229,7 @@ const initCmd = async (props) => {
|
|
|
229
229
|
const args = installArgs(pm, missing);
|
|
230
230
|
if (props.install === false) log.info("Install the Neon config packages to use neon.ts: %s", formatInstallCommand(pm, missing));
|
|
231
231
|
else {
|
|
232
|
+
ensureDirectoryGitignored(join(cwd, "node_modules"));
|
|
232
233
|
log.info("Installing %s with %s…", missing.join(", "), pm);
|
|
233
234
|
if (!await run(pm, args, cwd)) log.warning("Could not install the config packages automatically. Run by hand: %s", formatInstallCommand(pm, missing));
|
|
234
235
|
}
|
package/dist/context.js
CHANGED
|
@@ -199,21 +199,33 @@ const setContext = (file, context) => {
|
|
|
199
199
|
* must not be blocked by a `.gitignore` write error.
|
|
200
200
|
*/
|
|
201
201
|
const ensureGitignored = (file) => {
|
|
202
|
+
ensureGitignoreEntry(file, basenameOf(file));
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Make sure a directory is ignored by the `.gitignore` in its parent. The written entry keeps
|
|
206
|
+
* the conventional trailing slash, while coverage checks also recognize an existing entry
|
|
207
|
+
* without one.
|
|
208
|
+
*/
|
|
209
|
+
const ensureDirectoryGitignored = (directory) => {
|
|
210
|
+
const normalized = directory.replace(/[\\/]+$/, "");
|
|
211
|
+
ensureGitignoreEntry(directory, `${basenameOf(normalized)}/`);
|
|
212
|
+
};
|
|
213
|
+
const ensureGitignoreEntry = (path, entry) => {
|
|
202
214
|
try {
|
|
203
|
-
const dir = dirname(
|
|
204
|
-
const entry = basenameOf(file);
|
|
215
|
+
const dir = dirname(path);
|
|
205
216
|
const gitignorePath = resolve(dir, GITIGNORE_FILE);
|
|
217
|
+
const coveredEntry = entry.replace(/\/$/, "");
|
|
206
218
|
if (!existsSync(gitignorePath)) {
|
|
207
219
|
writeFileSync(gitignorePath, `${entry}\n`);
|
|
208
220
|
return;
|
|
209
221
|
}
|
|
210
222
|
const current = readFileSync(gitignorePath, "utf-8");
|
|
211
|
-
if (hasGitignoreEntry(current,
|
|
223
|
+
if (hasGitignoreEntry(current, coveredEntry)) return;
|
|
212
224
|
const addition = `${current.length > 0 && !current.endsWith("\n") ? "\n" : ""}${entry}\n`;
|
|
213
225
|
writeFileSync(gitignorePath, current + addition);
|
|
214
226
|
} catch (err) {
|
|
215
227
|
const message = err instanceof Error ? err.message : String(err);
|
|
216
|
-
log.debug("Failed to update .gitignore next to %s: %s",
|
|
228
|
+
log.debug("Failed to update .gitignore next to %s: %s", path, message);
|
|
217
229
|
}
|
|
218
230
|
};
|
|
219
231
|
const basenameOf = (file) => {
|
|
@@ -248,4 +260,4 @@ const globToRegExp = (pattern) => {
|
|
|
248
260
|
return new RegExp(`^${source}$`);
|
|
249
261
|
};
|
|
250
262
|
//#endregion
|
|
251
|
-
export { applyContext, contextBranch, currentContextFile, enrichFromContext, ensureGitignored, isApiKeysCommand, isAskCommand, isClaimCommand, isConfigInit, isCurrentBranchProbe, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, readContextFile, setContext, updateContextFile, walkContextFile };
|
|
263
|
+
export { applyContext, contextBranch, currentContextFile, enrichFromContext, ensureDirectoryGitignored, ensureGitignored, isApiKeysCommand, isAskCommand, isClaimCommand, isConfigInit, isCurrentBranchProbe, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, readContextFile, setContext, updateContextFile, walkContextFile };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.15.0",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"yaml": "^2.9.0",
|
|
64
64
|
"yargs": "17.7.2",
|
|
65
65
|
"yoctocolors": "^2.1.2",
|
|
66
|
-
"@neon/config
|
|
67
|
-
"@neon/config": "1.
|
|
68
|
-
"@neon/sdk": "4.
|
|
66
|
+
"@neon/config": "1.3.3",
|
|
67
|
+
"@neon/config-runtime": "1.2.4",
|
|
68
|
+
"@neon/sdk": "4.1.0"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
71
|
"@napi-rs/keyring": "1.3.0",
|
|
@@ -98,8 +98,8 @@
|
|
|
98
98
|
"typescript": "^5.9.0",
|
|
99
99
|
"vitest": "^3.0.9",
|
|
100
100
|
"@neon-internals/cli-core": "0.0.0",
|
|
101
|
+
"@neon-internals/env-core": "0.0.11",
|
|
101
102
|
"@neon/e2e-harness": "0.0.0",
|
|
102
|
-
"@neon-internals/env-core": "0.0.10",
|
|
103
103
|
"@neon/functions": "0.9.0"
|
|
104
104
|
},
|
|
105
105
|
"publishConfig": {
|