neon 6.1.0 → 6.2.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/dist/_chunks/{env-DRpFX-_d.js → env-CtOH9xsl.js} +2 -1
- package/dist/_chunks/{env_services-DlXECBPi.js → env-N19A7qND.js} +13 -120
- package/dist/commands/auth.js +3 -1
- package/dist/commands/checkout.js +76 -6
- package/dist/commands/config.js +41 -0
- package/dist/commands/dev.js +2 -2
- package/dist/commands/env.js +3 -2
- package/dist/commands/git.js +356 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/link.js +2 -1
- package/dist/context.js +92 -3
- package/dist/dev/env.js +1 -1
- package/dist/env_services.js +120 -1
- package/dist/utils/branch_name.js +29 -0
- package/dist/utils/git.js +220 -0
- package/dist/utils/hooks.js +230 -0
- package/package.json +6 -6
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { log } from "../log.js";
|
|
2
2
|
import { getCliName } from "../utils/cli_name.js";
|
|
3
3
|
import { declaredNeonServices } from "../config_services.js";
|
|
4
|
-
import {
|
|
4
|
+
import { a as defaultAiGatewayCredential, c as fetchEnvKeysState, d as isLiveCredential, h as toEntries, i as credentialName, l as functionBaseUrlKey, m as resolveBranchPolicy, n as createApiFromOptions, o as defaultStorageCredential, p as policyEnvKeys, r as credentialEnvKeys, t as NEON_ENV_VAR_KEYS, u as isFunctionBaseUrlKey } from "./env-N19A7qND.js";
|
|
5
|
+
import { ENV_PULL_SERVICES, envKeysForSelection, serviceForEnvKey, servicesForEnvKeys } from "../env_services.js";
|
|
5
6
|
import { formatInstallCommand, resolvePackageManager } from "../utils/package_manager.js";
|
|
6
7
|
import { ErrorCode, createNeonApiFromOptions, isPlatformError, loadConfigFromFile } from "@neon/config";
|
|
7
8
|
import { credentialScopesSatisfied } from "@neon/config/v1";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { NEON_SERVICES } from "../neon_services.js";
|
|
2
1
|
import { ErrorCode, PlatformError, createNeonApiFromOptions, deriveCredentialScopes, isPlatformError, resolveConfig } from "@neon/config/v1";
|
|
3
2
|
//#region ../../internals/env-core/dist/env.js
|
|
4
3
|
/**
|
|
@@ -102,6 +101,18 @@ function requiredValue(value, description) {
|
|
|
102
101
|
if (value === null) throw new Error(`fetchEnv: missing ${description}.`);
|
|
103
102
|
return value;
|
|
104
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* The {@link fetchEnv} body, with the key selection as a plain argument and no generic
|
|
106
|
+
* narrowing. Exists for callers that compute the selection at runtime — notably
|
|
107
|
+
* {@link fetchEnvReusingSecrets}, which decides which keys it still needs by checking the
|
|
108
|
+
* branch — since the public overload's `keys` is bound to a literal union those callers cannot
|
|
109
|
+
* produce without asserting.
|
|
110
|
+
*
|
|
111
|
+
* `keys === null` selects everything the policy enables.
|
|
112
|
+
*/
|
|
113
|
+
async function fetchEnvKeys(config, options, keys) {
|
|
114
|
+
return (await fetchEnvKeysState(config, options, keys)).env;
|
|
115
|
+
}
|
|
105
116
|
async function fetchEnvKeysState(config, options, keys) {
|
|
106
117
|
const api = options.api ?? createApiFromOptions(options);
|
|
107
118
|
const projectId = options.projectId;
|
|
@@ -562,122 +573,4 @@ function toEntries(env) {
|
|
|
562
573
|
return out;
|
|
563
574
|
}
|
|
564
575
|
//#endregion
|
|
565
|
-
|
|
566
|
-
const ENV_PULL_SERVICES = NEON_SERVICES;
|
|
567
|
-
const ENV_PULL_KEYS = [
|
|
568
|
-
...Object.values(NEON_ENV_VAR_KEYS.postgres),
|
|
569
|
-
NEON_ENV_VAR_KEYS.branch.name,
|
|
570
|
-
...Object.values(NEON_ENV_VAR_KEYS.auth),
|
|
571
|
-
...Object.values(NEON_ENV_VAR_KEYS.dataApi),
|
|
572
|
-
...Object.values(NEON_ENV_VAR_KEYS.storage),
|
|
573
|
-
...Object.values(NEON_ENV_VAR_KEYS.aiGateway)
|
|
574
|
-
];
|
|
575
|
-
const ENV_PULL_KEY_HELP = `${ENV_PULL_KEYS.join(", ")}, or NEON_FUNCTION_<SLUG>_BASE_URL`;
|
|
576
|
-
/** The OS-level env vars each service contributes to a pulled `.env`. */
|
|
577
|
-
const SERVICE_ENV_KEYS = {
|
|
578
|
-
postgres: Object.values(NEON_ENV_VAR_KEYS.postgres),
|
|
579
|
-
auth: Object.values(NEON_ENV_VAR_KEYS.auth),
|
|
580
|
-
"data-api": Object.values(NEON_ENV_VAR_KEYS.dataApi),
|
|
581
|
-
"object-storage": Object.values(NEON_ENV_VAR_KEYS.storage),
|
|
582
|
-
"ai-gateway": Object.values(NEON_ENV_VAR_KEYS.aiGateway),
|
|
583
|
-
functions: []
|
|
584
|
-
};
|
|
585
|
-
/**
|
|
586
|
-
* The subset of {@link SERVICE_ENV_KEYS} a pull *owns*, and so may prune from the target file
|
|
587
|
-
* when the branch no longer has it. Object storage is deliberately absent: it is emitted under
|
|
588
|
-
* the third-party `AWS_*` names, which collide with credentials a user may set by hand, so
|
|
589
|
-
* `env pull` only ever writes them. Function URL keys cannot live here because slugs are
|
|
590
|
-
* discovered at runtime.
|
|
591
|
-
*/
|
|
592
|
-
const SERVICE_OWNED_ENV_KEYS = {
|
|
593
|
-
...SERVICE_ENV_KEYS,
|
|
594
|
-
"object-storage": [],
|
|
595
|
-
functions: []
|
|
596
|
-
};
|
|
597
|
-
/**
|
|
598
|
-
* Branch identity. Not a service — every branch has a name — so a scoped pull refreshes it
|
|
599
|
-
* alongside whatever services were selected.
|
|
600
|
-
*/
|
|
601
|
-
const BRANCH_ENV_KEY = NEON_ENV_VAR_KEYS.branch.name;
|
|
602
|
-
const ENV_KEY_SERVICE = {
|
|
603
|
-
DATABASE_URL: "postgres",
|
|
604
|
-
DATABASE_URL_UNPOOLED: "postgres",
|
|
605
|
-
NEON_BRANCH: null,
|
|
606
|
-
NEON_AUTH_BASE_URL: "auth",
|
|
607
|
-
NEON_AUTH_JWKS_URL: "auth",
|
|
608
|
-
NEON_DATA_API_URL: "data-api",
|
|
609
|
-
AWS_ACCESS_KEY_ID: "object-storage",
|
|
610
|
-
AWS_SECRET_ACCESS_KEY: "object-storage",
|
|
611
|
-
AWS_ENDPOINT_URL_S3: "object-storage",
|
|
612
|
-
AWS_REGION: "object-storage",
|
|
613
|
-
NEON_AI_GATEWAY_TOKEN: "ai-gateway",
|
|
614
|
-
NEON_AI_GATEWAY_BASE_URL: "ai-gateway"
|
|
615
|
-
};
|
|
616
|
-
const serviceForEnvKey = (key) => isFunctionBaseUrlKey(key) ? "functions" : ENV_KEY_SERVICE[key];
|
|
617
|
-
/** Services that must be resolved to produce the selected env keys. */
|
|
618
|
-
const servicesForEnvKeys = (keys) => ENV_PULL_SERVICES.filter((service) => keys.some((key) => serviceForEnvKey(key) === service));
|
|
619
|
-
/** Every env var the selected services contribute, plus branch identity. */
|
|
620
|
-
const envServiceKeys = (services) => {
|
|
621
|
-
const keys = /* @__PURE__ */ new Set([BRANCH_ENV_KEY]);
|
|
622
|
-
for (const service of services) for (const key of SERVICE_ENV_KEYS[service]) keys.add(key);
|
|
623
|
-
return keys;
|
|
624
|
-
};
|
|
625
|
-
/**
|
|
626
|
-
* The env vars a pull scoped to `services` may prune. Narrower than the unscoped set on
|
|
627
|
-
* purpose: `env pull -s ai-gateway` says nothing about `DATABASE_URL`, so it must leave it
|
|
628
|
-
* alone rather than treat its absence from this pull as "the branch no longer has it".
|
|
629
|
-
* Function URL keys cannot live here because slugs are discovered at runtime.
|
|
630
|
-
*/
|
|
631
|
-
const ownedEnvServiceKeys = (services) => services.flatMap((service) => SERVICE_OWNED_ENV_KEYS[service]);
|
|
632
|
-
const envKeysForSelection = (services, envKeys) => {
|
|
633
|
-
const selected = services.length > 0 ? envServiceKeys(services) : /* @__PURE__ */ new Set();
|
|
634
|
-
for (const key of envKeys) selected.add(key);
|
|
635
|
-
if (selected.has(NEON_ENV_VAR_KEYS.storage.accessKeyId) !== selected.has(NEON_ENV_VAR_KEYS.storage.secretAccessKey)) throw new Error("AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be selected together: they are two halves of one newly issued object-storage credential. Add the missing key to --env, or use --service object-storage.");
|
|
636
|
-
const staticKeys = ENV_PULL_KEYS.filter((key) => selected.has(key));
|
|
637
|
-
const functionKeys = [...selected].filter(isFunctionBaseUrlKey).sort((left, right) => left.localeCompare(right));
|
|
638
|
-
return [...staticKeys, ...functionKeys];
|
|
639
|
-
};
|
|
640
|
-
/** Parse repeated or comma-separated `--env` values into canonical env-key order. */
|
|
641
|
-
const parseEnvPullKeys = (raw, flag) => {
|
|
642
|
-
const names = raw.flatMap((value) => value.split(",")).map((name) => name.trim()).filter((name) => name !== "");
|
|
643
|
-
const supported = `Supported values: ${ENV_PULL_KEY_HELP}.`;
|
|
644
|
-
if (names.length === 0) throw new Error(`${flag} needs at least one env variable. ${supported}`);
|
|
645
|
-
const unknown = names.filter((name) => !ENV_PULL_KEYS.some((key) => key === name) && !isFunctionBaseUrlKey(name));
|
|
646
|
-
if (unknown.length > 0) {
|
|
647
|
-
const displayNames = unknown.map(redactUnknownEnvValue);
|
|
648
|
-
const suggestions = [...new Set(unknown.map(suggestEnvPullKey).filter((key) => key !== null))];
|
|
649
|
-
const suggestion = suggestions.length > 0 ? ` Did you mean ${suggestions.join(" or ")}?` : "";
|
|
650
|
-
throw new Error(`Unknown env variable${unknown.length === 1 ? "" : "s"} ${displayNames.join(", ")}.${suggestion} ${supported}`);
|
|
651
|
-
}
|
|
652
|
-
const staticKeys = ENV_PULL_KEYS.filter((key) => names.includes(key));
|
|
653
|
-
const functionKeys = [...new Set(names.filter(isFunctionBaseUrlKey))].sort((left, right) => left.localeCompare(right));
|
|
654
|
-
return [...staticKeys, ...functionKeys];
|
|
655
|
-
};
|
|
656
|
-
const isKnownEnvPullKey = (key) => ENV_PULL_KEYS.some((supportedKey) => supportedKey === key) || isFunctionBaseUrlKey(key);
|
|
657
|
-
const redactUnknownEnvValue = (value) => {
|
|
658
|
-
const separator = value.indexOf("=");
|
|
659
|
-
if (separator !== -1) {
|
|
660
|
-
const key = value.slice(0, separator);
|
|
661
|
-
return isKnownEnvPullKey(key) ? `${key}=<redacted>` : "<redacted invalid value>";
|
|
662
|
-
}
|
|
663
|
-
return "<redacted invalid value>";
|
|
664
|
-
};
|
|
665
|
-
const suggestEnvPullKey = (value) => {
|
|
666
|
-
if (value.includes("=")) return null;
|
|
667
|
-
if (parseFunctionBaseUrlKey(value) !== null) return null;
|
|
668
|
-
const closest = ENV_PULL_KEYS.map((key) => [key, editDistance(value, key)]).sort((a, b) => a[1] - b[1])[0];
|
|
669
|
-
return closest && closest[1] <= 2 ? closest[0] : null;
|
|
670
|
-
};
|
|
671
|
-
const editDistance = (left, right) => {
|
|
672
|
-
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
673
|
-
for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
|
|
674
|
-
const current = [leftIndex];
|
|
675
|
-
for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) current[rightIndex] = Math.min((previous[rightIndex] ?? 0) + 1, (current[rightIndex - 1] ?? 0) + 1, (previous[rightIndex - 1] ?? 0) + (left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1));
|
|
676
|
-
previous = current;
|
|
677
|
-
}
|
|
678
|
-
return previous[right.length] ?? right.length;
|
|
679
|
-
};
|
|
680
|
-
/** Narrow yargs' array option value without accepting any other runtime shape. */
|
|
681
|
-
const envPullFlagValue = (value) => Array.isArray(value) ? value.map(String) : void 0;
|
|
682
|
-
//#endregion
|
|
683
|
-
export { resolveBranchPolicy as C, policyEnvKeys as S, defaultStorageCredential as _, envKeysForSelection as a, isFunctionBaseUrlKey as b, ownedEnvServiceKeys as c, servicesForEnvKeys as d, NEON_ENV_VAR_KEYS as f, defaultAiGatewayCredential as g, credentialName as h, ENV_PULL_SERVICES as i, parseEnvPullKeys as l, credentialEnvKeys as m, ENV_PULL_KEYS as n, envPullFlagValue as o, createApiFromOptions as p, ENV_PULL_KEY_HELP as r, envServiceKeys as s, BRANCH_ENV_KEY as t, serviceForEnvKey as u, fetchEnvKeysState as v, toEntries as w, isLiveCredential as x, functionBaseUrlKey as y };
|
|
576
|
+
export { defaultAiGatewayCredential as a, fetchEnvKeysState as c, isLiveCredential as d, parseFunctionBaseUrlKey as f, toEntries as h, credentialName as i, functionBaseUrlKey as l, resolveBranchPolicy as m, createApiFromOptions as n, defaultStorageCredential as o, policyEnvKeys as p, credentialEnvKeys as r, fetchEnvKeys as s, NEON_ENV_VAR_KEYS as t, isFunctionBaseUrlKey as u };
|
package/dist/commands/auth.js
CHANGED
|
@@ -5,7 +5,7 @@ import { log } from "../log.js";
|
|
|
5
5
|
import { getApiClient, isNeonApiError } from "../api.js";
|
|
6
6
|
import { setAuthContext } from "../auth_context.js";
|
|
7
7
|
import { a as isOwnedCredentialPath } from "../_chunks/paths-DMq0Lt7a.js";
|
|
8
|
-
import { currentContextFile, isAskCommand, isClaimCommand, isConfigInit, isCurrentBranchProbe, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, readContextFile } from "../context.js";
|
|
8
|
+
import { currentContextFile, isAskCommand, isClaimCommand, isConfigInit, isCurrentBranchProbe, isGitLocalCommand, isInspectDbUrl, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, isUnfollowedGitHookSync, readContextFile } from "../context.js";
|
|
9
9
|
import { _ as upsertProfile, d as newProfileLocation, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, t as DEFAULT_PROFILE, u as locationOf } from "../_chunks/profiles-CvnFEQyd.js";
|
|
10
10
|
import { extendTokenSet } from "../utils/auth.js";
|
|
11
11
|
import { AuthRefreshError, auth, classifyRefreshFailure, refreshToken } from "../auth.js";
|
|
@@ -297,6 +297,8 @@ const ensureAuth = async (props) => {
|
|
|
297
297
|
if (isCurrentBranchProbe(props)) return;
|
|
298
298
|
if (isConfigInit(props)) return;
|
|
299
299
|
if (isProfileCommand(props)) return;
|
|
300
|
+
if (isGitLocalCommand(props)) return;
|
|
301
|
+
if (isUnfollowedGitHookSync(props)) return;
|
|
300
302
|
if (isMcpOauth(props)) return;
|
|
301
303
|
if (isInspectDbUrl(props)) return;
|
|
302
304
|
if (props._[0] === "open") return;
|
|
@@ -4,9 +4,12 @@ import { log } from "../log.js";
|
|
|
4
4
|
import { isNeonApiError } from "../api.js";
|
|
5
5
|
import { applyContext, contextBranch, readContextFile } from "../context.js";
|
|
6
6
|
import { getCliName } from "../utils/cli_name.js";
|
|
7
|
+
import { loadEnvFileIntoProcess } from "../env_file.js";
|
|
7
8
|
import { createBranch, pickBranchInteractively } from "../utils/branch_picker.js";
|
|
8
9
|
import { looksLikeBranchId } from "../utils/formats.js";
|
|
9
10
|
import { fillSingleProject, listAllProjectBranches } from "../utils/enrichers.js";
|
|
11
|
+
import { buildCheckoutEvent, readGitContext } from "../utils/git.js";
|
|
12
|
+
import { buildHookBranch, loadHookConfig, resolveHookEnv, runCheckoutAfterHook, runCheckoutBeforeHook, runCreateAfterHook, runCreateBeforeHook } from "../utils/hooks.js";
|
|
10
13
|
import { autoPullEnvAfterPin } from "./env.js";
|
|
11
14
|
import { applyPolicyOnCreate, createBranchFromPolicyOnCheckout, envFlag } from "./config.js";
|
|
12
15
|
import { handler as handler$1 } from "./link.js";
|
|
@@ -66,7 +69,34 @@ const handler = async (props) => {
|
|
|
66
69
|
if (previousBranch) log.info("%s Currently on branch %s", chalk.dim("→"), chalk.cyan.bold(previousBranch));
|
|
67
70
|
if (props.create && !props.id?.trim()) throw new Error(`No branch specified. Pass a branch name with --create (e.g. \`${getCliName()} checkout dev --create\`).`);
|
|
68
71
|
const projectId = await resolveProjectId(props);
|
|
69
|
-
|
|
72
|
+
if (props.create && props.env) try {
|
|
73
|
+
loadEnvFileIntoProcess(props.env);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
log.debug("checkout: could not preload --env for hook discovery (continuing): %s", err instanceof Error ? err.message : String(err));
|
|
76
|
+
}
|
|
77
|
+
const cwd = process.cwd();
|
|
78
|
+
const git = readGitContext(cwd);
|
|
79
|
+
const event = buildCheckoutEvent(git, props.id);
|
|
80
|
+
const config = await loadHookConfig(cwd);
|
|
81
|
+
const hooks = config?.experimental?.hooks;
|
|
82
|
+
if (props.id) {
|
|
83
|
+
const renamed = await runCheckoutBeforeHook({
|
|
84
|
+
hooks,
|
|
85
|
+
event,
|
|
86
|
+
git,
|
|
87
|
+
cwd
|
|
88
|
+
});
|
|
89
|
+
if (renamed && renamed !== props.id) {
|
|
90
|
+
log.info("%s checkout.before hook mapped %s → %s", chalk.dim("→"), chalk.cyan(props.id), chalk.cyan.bold(renamed));
|
|
91
|
+
props.id = renamed;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const { branchId, branchName, created, policyApplied, policyFailure } = await resolveBranchId(props, projectId, {
|
|
95
|
+
hooks,
|
|
96
|
+
git,
|
|
97
|
+
event,
|
|
98
|
+
cwd
|
|
99
|
+
});
|
|
70
100
|
const orgId = await resolveOrgId(props, projectId);
|
|
71
101
|
applyContext(props.contextFile, {
|
|
72
102
|
projectId,
|
|
@@ -89,6 +119,39 @@ const handler = async (props) => {
|
|
|
89
119
|
branch: branchId,
|
|
90
120
|
envPull: props.envPull
|
|
91
121
|
});
|
|
122
|
+
const needsCreateAfter = created && hooks?.create?.after;
|
|
123
|
+
if (config && (hooks?.checkout?.after || needsCreateAfter)) {
|
|
124
|
+
const env = await resolveHookEnv(config, {
|
|
125
|
+
projectId,
|
|
126
|
+
branch: branchId,
|
|
127
|
+
...props.apiKey ? { apiKey: props.apiKey } : {},
|
|
128
|
+
...props.apiHost ? { apiHost: props.apiHost } : {}
|
|
129
|
+
});
|
|
130
|
+
if (env) {
|
|
131
|
+
const branch = await buildHookBranch({
|
|
132
|
+
apiClient: props.apiClient,
|
|
133
|
+
projectId,
|
|
134
|
+
branchId,
|
|
135
|
+
created
|
|
136
|
+
});
|
|
137
|
+
if (hooks?.checkout?.after) await runCheckoutAfterHook({
|
|
138
|
+
hooks,
|
|
139
|
+
branch,
|
|
140
|
+
env,
|
|
141
|
+
git,
|
|
142
|
+
event,
|
|
143
|
+
cwd
|
|
144
|
+
});
|
|
145
|
+
if (needsCreateAfter) await runCreateAfterHook({
|
|
146
|
+
hooks,
|
|
147
|
+
branch,
|
|
148
|
+
env,
|
|
149
|
+
git,
|
|
150
|
+
event,
|
|
151
|
+
cwd
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
92
155
|
if (failure) throw new Error(formatCheckoutPolicyFailure({
|
|
93
156
|
branchName,
|
|
94
157
|
branchId,
|
|
@@ -109,7 +172,7 @@ const applyPolicyOrDescribeFailure = async (props) => {
|
|
|
109
172
|
return err instanceof Error ? err.message : String(err);
|
|
110
173
|
}
|
|
111
174
|
};
|
|
112
|
-
const resolveBranchId = async (props, projectId) => {
|
|
175
|
+
const resolveBranchId = async (props, projectId, hookCtx) => {
|
|
113
176
|
const branches = await listAllProjectBranches(props.apiClient, projectId);
|
|
114
177
|
if (!props.id) {
|
|
115
178
|
const picked = await pickBranchInteractively(branches, {
|
|
@@ -125,7 +188,7 @@ const resolveBranchId = async (props, projectId) => {
|
|
|
125
188
|
policyApplied: false
|
|
126
189
|
};
|
|
127
190
|
}
|
|
128
|
-
return createCheckoutBranch(props, projectId, picked.name, branches);
|
|
191
|
+
return createCheckoutBranch(props, projectId, picked.name, branches, hookCtx);
|
|
129
192
|
}
|
|
130
193
|
const ref = props.id;
|
|
131
194
|
if (looksLikeBranchId(ref)) {
|
|
@@ -145,7 +208,7 @@ const resolveBranchId = async (props, projectId) => {
|
|
|
145
208
|
created: false,
|
|
146
209
|
policyApplied: false
|
|
147
210
|
};
|
|
148
|
-
if (props.create) return createCheckoutBranch(props, projectId, ref, branches);
|
|
211
|
+
if (props.create) return createCheckoutBranch(props, projectId, ref, branches, hookCtx);
|
|
149
212
|
const missingName = notFoundMessage(ref, branches, { suggestCreate: true });
|
|
150
213
|
if (isCi() || !process.stdout.isTTY) throw new Error(missingName);
|
|
151
214
|
log.error(missingName);
|
|
@@ -156,7 +219,7 @@ const resolveBranchId = async (props, projectId) => {
|
|
|
156
219
|
initial: true
|
|
157
220
|
});
|
|
158
221
|
if (!create) throw new Error(`Aborted: branch "${ref}" was not found and not created.`);
|
|
159
|
-
return createCheckoutBranch(props, projectId, ref, branches);
|
|
222
|
+
return createCheckoutBranch(props, projectId, ref, branches, hookCtx);
|
|
160
223
|
};
|
|
161
224
|
/**
|
|
162
225
|
* Create the branch to check out. When a `neon.ts` exists, route through the policy-driven
|
|
@@ -165,7 +228,14 @@ const resolveBranchId = async (props, projectId) => {
|
|
|
165
228
|
* a bare branch off the default — the handler then applies the policy (a no-op with no
|
|
166
229
|
* `neon.ts`).
|
|
167
230
|
*/
|
|
168
|
-
const createCheckoutBranch = async (props, projectId, name, branches) => {
|
|
231
|
+
const createCheckoutBranch = async (props, projectId, name, branches, hookCtx) => {
|
|
232
|
+
await runCreateBeforeHook({
|
|
233
|
+
hooks: hookCtx.hooks,
|
|
234
|
+
branchName: name,
|
|
235
|
+
git: hookCtx.git,
|
|
236
|
+
event: hookCtx.event,
|
|
237
|
+
cwd: hookCtx.cwd
|
|
238
|
+
});
|
|
169
239
|
const fromPolicy = await createBranchFromPolicyOnCheckout({
|
|
170
240
|
projectId,
|
|
171
241
|
branchName: name,
|
package/dist/commands/config.js
CHANGED
|
@@ -16,6 +16,8 @@ import { announceTargetBranch } from "../utils/branch_notice.js";
|
|
|
16
16
|
import { renderAppliedChanges, renderBranchSettingConflicts, renderCustomDomainFollowup } from "../utils/config_diff.js";
|
|
17
17
|
import { formatInstallCommand, installArgs, resolvePackageManager, runCommand } from "../utils/package_manager.js";
|
|
18
18
|
import { bundleEntry } from "../utils/esbuild.js";
|
|
19
|
+
import { readGitContext } from "../utils/git.js";
|
|
20
|
+
import { resolveHookEnv, runDeployAfterHook, runDeployBeforeHook } from "../utils/hooks.js";
|
|
19
21
|
import { pickServicesInteractively } from "../utils/service_picker.js";
|
|
20
22
|
import { zipBundle } from "../utils/zip.js";
|
|
21
23
|
import { autoPullEnvAfterPin } from "./env.js";
|
|
@@ -485,6 +487,27 @@ const applyCmd = async (props) => {
|
|
|
485
487
|
apiClient: props.apiClient,
|
|
486
488
|
projectId: props.projectId
|
|
487
489
|
});
|
|
490
|
+
const cwd = process.cwd();
|
|
491
|
+
const git = readGitContext(cwd);
|
|
492
|
+
const event = { type: "neon-deploy" };
|
|
493
|
+
const hooks = config.experimental?.hooks;
|
|
494
|
+
const hookBranch = {
|
|
495
|
+
projectId: props.projectId,
|
|
496
|
+
id: branch.branchId,
|
|
497
|
+
name: branch.branchName,
|
|
498
|
+
created: false,
|
|
499
|
+
isDefault: branch.isDefault ?? false,
|
|
500
|
+
isProtected: branch.isProtected ?? false,
|
|
501
|
+
...branch.parentId ? { parentId: branch.parentId } : {},
|
|
502
|
+
...branch.expiresAt ? { expiresAt: branch.expiresAt } : {}
|
|
503
|
+
};
|
|
504
|
+
await runDeployBeforeHook({
|
|
505
|
+
hooks,
|
|
506
|
+
branch: hookBranch,
|
|
507
|
+
git,
|
|
508
|
+
event,
|
|
509
|
+
cwd
|
|
510
|
+
});
|
|
488
511
|
let result;
|
|
489
512
|
try {
|
|
490
513
|
result = await apply(config, {
|
|
@@ -510,6 +533,24 @@ const applyCmd = async (props) => {
|
|
|
510
533
|
...props,
|
|
511
534
|
envPull: props.envPull !== false
|
|
512
535
|
});
|
|
536
|
+
if (hooks?.deploy?.after) {
|
|
537
|
+
const env = await resolveHookEnv(config, {
|
|
538
|
+
projectId: props.projectId,
|
|
539
|
+
branch: branchId,
|
|
540
|
+
...props.apiKey ? { apiKey: props.apiKey } : {},
|
|
541
|
+
...props.apiHost ? { apiHost: props.apiHost } : {},
|
|
542
|
+
...props.runtimeApi ? { api: props.runtimeApi } : {}
|
|
543
|
+
});
|
|
544
|
+
if (env) await runDeployAfterHook({
|
|
545
|
+
hooks,
|
|
546
|
+
branch: hookBranch,
|
|
547
|
+
env,
|
|
548
|
+
result,
|
|
549
|
+
git,
|
|
550
|
+
event,
|
|
551
|
+
cwd
|
|
552
|
+
});
|
|
553
|
+
}
|
|
513
554
|
};
|
|
514
555
|
/**
|
|
515
556
|
* Human-readable list of the services a `neon.ts` policy utilizes on the branch, shown under
|
package/dist/commands/dev.js
CHANGED
|
@@ -3,10 +3,10 @@ import { log } from "../log.js";
|
|
|
3
3
|
import { isClaimableEnvTarget } from "../claimable/state.js";
|
|
4
4
|
import { getCliName } from "../utils/cli_name.js";
|
|
5
5
|
import { readEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
6
|
-
import {
|
|
6
|
+
import { l as functionBaseUrlKey } from "../_chunks/env-N19A7qND.js";
|
|
7
7
|
import { branchIdResolve } from "../utils/enrichers.js";
|
|
8
8
|
import { bundleEntry } from "../utils/esbuild.js";
|
|
9
|
-
import { a as resolveDevEnv } from "../_chunks/env-
|
|
9
|
+
import { a as resolveDevEnv } from "../_chunks/env-CtOH9xsl.js";
|
|
10
10
|
import { findConfigFunctionBySource, resolveFunctionsFromConfig } from "../dev/functions.js";
|
|
11
11
|
import { resolveWatchInputs } from "../dev/inputs.js";
|
|
12
12
|
import { existsSync, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
package/dist/commands/env.js
CHANGED
|
@@ -5,11 +5,12 @@ import { isClaimableEnvTarget } from "../claimable/state.js";
|
|
|
5
5
|
import { getCliName } from "../utils/cli_name.js";
|
|
6
6
|
import { deprecatedServiceMessage, parseServices, servicesFlagValue, servicesOption } from "../neon_services.js";
|
|
7
7
|
import { mergeEnvFile, readEnvFile, resolveEnvFilePath } from "../env_file.js";
|
|
8
|
-
import {
|
|
8
|
+
import { t as NEON_ENV_VAR_KEYS, u as isFunctionBaseUrlKey } from "../_chunks/env-N19A7qND.js";
|
|
9
|
+
import { ENV_PULL_KEY_HELP, ENV_PULL_SERVICES, envKeysForSelection, envPullFlagValue, ownedEnvServiceKeys, parseEnvPullKeys } from "../env_services.js";
|
|
9
10
|
import { fillSingleProject, resolveBranchRef } from "../utils/enrichers.js";
|
|
10
11
|
import { warnAiGateway } from "../utils/ai_gateway_notice.js";
|
|
11
12
|
import { announceTargetBranch } from "../utils/branch_notice.js";
|
|
12
|
-
import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-
|
|
13
|
+
import { i as dropClaimableUnsupported, o as resolveNeonEnvVars } from "../_chunks/env-CtOH9xsl.js";
|
|
13
14
|
import { existsSync } from "node:fs";
|
|
14
15
|
import chalk from "chalk";
|
|
15
16
|
//#region src/commands/env.ts
|