@timekast/factory 1.17.2 → 1.17.5
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/commands/provision.js +22 -1
- package/dist/lib/env-file.js +29 -0
- package/package.json +2 -1
|
@@ -29,7 +29,7 @@ import { apexZoneOf, CloudflareApiError, CLOUDFLARE_API_BASE, ensureCnameRecord,
|
|
|
29
29
|
import { createDevelopBranch, createNeonProject, createProjectApiKey, deleteNeonApiKeysByName, deleteNeonProject, getConnectionUri, listNeonBranches, listNeonProjects, NeonApiError, NEON_API_BASE, NEON_DEVELOP_BRANCH, } from '../lib/neon-api.js';
|
|
30
30
|
import { defaultEnvPath, readVercelToken, runProvisionPreflight, } from '../lib/provision-preflight.js';
|
|
31
31
|
import { addVercelDomain, buildEnvPlan, createVercelProject, generateAuthSecret, generateMfaEncryptionKey, getLinkedRepoId, getVercelDomainStatus, GitHubAppNotInstalledError, linkGitRepo, listVercelProjects, patchProjectSettings, pushEnvPlan, removeVercelDomain, triggerDeployment, upsertEnvVar, listEnvVars, VercelApiError, writeVercelProjectJson, } from '../lib/vercel-env.js';
|
|
32
|
-
import { writeE2eLocalCreds, writeEnvLocal } from '../lib/env-file.js';
|
|
32
|
+
import { writeE2eLocalCreds, writeEmailLocalCreds, writeEnvLocal } from '../lib/env-file.js';
|
|
33
33
|
import { adoptState, ADOPT_UNKNOWN, clearProvisionState, clearState, hasProvisionState, isServiceComplete, PROVISION_DIR, PROVISION_STATE_FILE, readDestroyTarget, readProvisionState, writeProvisionState, } from '../lib/provision-state.js';
|
|
34
34
|
/**
|
|
35
35
|
* The THREE GitHub Secrets the E2E runner (`pnpm test:e2e` → `e2e-runner.ts` +
|
|
@@ -752,6 +752,12 @@ export async function provisionVercel(rootDir, state, teamId, deps = {}) {
|
|
|
752
752
|
writeE2eLocalCreds(rootDir, state.neon.projectId, readNeonApiKey(envPath))) {
|
|
753
753
|
console.log(' + .env.local (NEON_API_KEY · NEON_PROJECT_ID — para `pnpm test:e2e` local)');
|
|
754
754
|
}
|
|
755
|
+
// Email config: write the org Resend key (from the rail) + the fixed EMAIL_FROM so local
|
|
756
|
+
// sends (`invite:admin`, magic-link, `db:seed`) DELIVER instead of the console fallback.
|
|
757
|
+
// No-op without a rail key. Non-prod sends are auto-tagged `[Dev]` by the dispatcher.
|
|
758
|
+
if (writeEmailLocalCreds(rootDir, readGateValue('RESEND_API_KEY', envPath), PROVISION_EMAIL_FROM)) {
|
|
759
|
+
console.log(' + .env.local (EMAIL_PROVIDER=resend · RESEND_API_KEY · EMAIL_FROM — envío de correo local)');
|
|
760
|
+
}
|
|
755
761
|
// `pnpm install` the derived dir (issue §4). A missing package.json (non-Node
|
|
756
762
|
// derivative) is reported, NOT fatal (issue §8).
|
|
757
763
|
if (existsSync(join(rootDir, 'package.json'))) {
|
|
@@ -2490,6 +2496,21 @@ export async function runProvision(flags) {
|
|
|
2490
2496
|
await provisionNeon(rootDir, runState);
|
|
2491
2497
|
writeProvisionState(rootDir, runState);
|
|
2492
2498
|
}
|
|
2499
|
+
// 5a.2. Ensure the local E2E creds are in `.env.local` even on a resume/adopt where
|
|
2500
|
+
// the Vercel substrate was already `done` (writeE2eLocalCreds only runs inside
|
|
2501
|
+
// that step's first pass). Idempotent — `setEnvLine` replaces/appends the two
|
|
2502
|
+
// keys — so `pnpm test:e2e` works locally without a manual edit after a re-run.
|
|
2503
|
+
if (!flags.dryRun && runState.neon?.projectId) {
|
|
2504
|
+
if (writeE2eLocalCreds(rootDir, runState.neon.projectId, readNeonApiKey())) {
|
|
2505
|
+
console.log(' + .env.local (NEON_API_KEY · NEON_PROJECT_ID — para `pnpm test:e2e` local)');
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
// Same for email config on resume/adopt (writeEmailLocalCreds only runs in the first
|
|
2509
|
+
// provisionVercel pass). Idempotent — setEnvLine replaces/appends. No-op without a rail key.
|
|
2510
|
+
if (!flags.dryRun &&
|
|
2511
|
+
writeEmailLocalCreds(rootDir, readGateValue('RESEND_API_KEY', defaultEnvPath()), PROVISION_EMAIL_FROM)) {
|
|
2512
|
+
console.log(' + .env.local (EMAIL_PROVIDER=resend · RESEND_API_KEY · EMAIL_FROM — envío de correo local)');
|
|
2513
|
+
}
|
|
2493
2514
|
// 5b. Client domain (PROV-006) — runs AFTER the services so the Vercel project
|
|
2494
2515
|
// (and the interim domains) already exist. Standalone patch or post-services.
|
|
2495
2516
|
if (hasDomain) {
|
package/dist/lib/env-file.js
CHANGED
|
@@ -108,3 +108,32 @@ export function writeE2eLocalCreds(rootDir, projectId, apiKey) {
|
|
|
108
108
|
chmodSync(localPath, 0o600); // holds NEON_API_KEY → owner-only.
|
|
109
109
|
return true;
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Write the org email config into `.env.local` so local sends (`invite:admin`, magic-link,
|
|
113
|
+
* `db:seed`) actually DELIVER instead of falling back to the console. Sets `EMAIL_PROVIDER=resend`
|
|
114
|
+
* + `RESEND_API_KEY` (the org key from the rail — already on the dev's host; global, not per-env)
|
|
115
|
+
* + `EMAIL_FROM` (the fixed org sender). Non-production sends are auto-tagged `[Dev]` by the
|
|
116
|
+
* dispatcher (`src/lib/email/index.ts`), so local delivery is safe AND distinguishable.
|
|
117
|
+
*
|
|
118
|
+
* This closes the source-inconsistency bug: `invite-admin --target main` writes to the prod DB
|
|
119
|
+
* but the script always reads email config from `.env.local` — which provision otherwise never
|
|
120
|
+
* populated (it only synced Resend to Vercel), so the invite email silently fell back to console.
|
|
121
|
+
*
|
|
122
|
+
* Returns false (no-op) when the rail key is missing or `.env.local` does not exist yet
|
|
123
|
+
* (`writeEnvLocal` creates it first). Safe on an existing file: `setEnvLine` only touches these
|
|
124
|
+
* three keys, preserving the rest.
|
|
125
|
+
*/
|
|
126
|
+
export function writeEmailLocalCreds(rootDir, resendKey, emailFrom) {
|
|
127
|
+
if (!resendKey)
|
|
128
|
+
return false;
|
|
129
|
+
const localPath = join(rootDir, '.env.local');
|
|
130
|
+
if (!existsSync(localPath))
|
|
131
|
+
return false;
|
|
132
|
+
let body = readFileSync(localPath, 'utf8');
|
|
133
|
+
body = setEnvLine(body, 'EMAIL_PROVIDER', 'resend');
|
|
134
|
+
body = setEnvLine(body, 'RESEND_API_KEY', resendKey);
|
|
135
|
+
body = setEnvLine(body, 'EMAIL_FROM', emailFrom);
|
|
136
|
+
writeFileSync(localPath, body, 'utf8');
|
|
137
|
+
chmodSync(localPath, 0o600); // holds RESEND_API_KEY → owner-only.
|
|
138
|
+
return true;
|
|
139
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timekast/factory",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.5",
|
|
4
4
|
"description": "Public, thin CLI to bootstrap and maintain TimeKast Factory derived projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"prepublishOnly": "pnpm build",
|
|
36
36
|
"test": "vitest run",
|
|
37
37
|
"test:unit": "vitest run tests/unit",
|
|
38
|
+
"test:provision": "vitest run tests/integration/provision-cycle.test.ts",
|
|
38
39
|
"test:watch": "vitest"
|
|
39
40
|
},
|
|
40
41
|
"dependencies": {
|