@rmartz/envctl 3.1.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 +79 -0
- package/dist/envctl.d.ts +3 -0
- package/dist/envctl.d.ts.map +1 -0
- package/dist/envctl.js +36 -0
- package/dist/envctl.js.map +1 -0
- package/dist/lib/auth.d.ts +21 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +148 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/check-firebase.d.ts +6 -0
- package/dist/lib/check-firebase.d.ts.map +1 -0
- package/dist/lib/check-firebase.js +81 -0
- package/dist/lib/check-firebase.js.map +1 -0
- package/dist/lib/check-live.d.ts +3 -0
- package/dist/lib/check-live.d.ts.map +1 -0
- package/dist/lib/check-live.js +50 -0
- package/dist/lib/check-live.js.map +1 -0
- package/dist/lib/check.d.ts +9 -0
- package/dist/lib/check.d.ts.map +1 -0
- package/dist/lib/check.js +165 -0
- package/dist/lib/check.js.map +1 -0
- package/dist/lib/cli/help.d.ts +4 -0
- package/dist/lib/cli/help.d.ts.map +1 -0
- package/dist/lib/cli/help.js +52 -0
- package/dist/lib/cli/help.js.map +1 -0
- package/dist/lib/cli/registry.d.ts +19 -0
- package/dist/lib/cli/registry.d.ts.map +1 -0
- package/dist/lib/cli/registry.js +111 -0
- package/dist/lib/cli/registry.js.map +1 -0
- package/dist/lib/cli/router.d.ts +22 -0
- package/dist/lib/cli/router.d.ts.map +1 -0
- package/dist/lib/cli/router.js +98 -0
- package/dist/lib/cli/router.js.map +1 -0
- package/dist/lib/cli/version.d.ts +2 -0
- package/dist/lib/cli/version.d.ts.map +1 -0
- package/dist/lib/cli/version.js +58 -0
- package/dist/lib/cli/version.js.map +1 -0
- package/dist/lib/commands/auth.d.ts +2 -0
- package/dist/lib/commands/auth.d.ts.map +1 -0
- package/dist/lib/commands/auth.js +33 -0
- package/dist/lib/commands/auth.js.map +1 -0
- package/dist/lib/commands/bootstrap-args.d.ts +11 -0
- package/dist/lib/commands/bootstrap-args.d.ts.map +1 -0
- package/dist/lib/commands/bootstrap-args.js +111 -0
- package/dist/lib/commands/bootstrap-args.js.map +1 -0
- package/dist/lib/commands/bootstrap.d.ts +5 -0
- package/dist/lib/commands/bootstrap.d.ts.map +1 -0
- package/dist/lib/commands/bootstrap.js +166 -0
- package/dist/lib/commands/bootstrap.js.map +1 -0
- package/dist/lib/commands/check.d.ts +3 -0
- package/dist/lib/commands/check.d.ts.map +1 -0
- package/dist/lib/commands/check.js +47 -0
- package/dist/lib/commands/check.js.map +1 -0
- package/dist/lib/commands/config-pull.d.ts +12 -0
- package/dist/lib/commands/config-pull.d.ts.map +1 -0
- package/dist/lib/commands/config-pull.js +161 -0
- package/dist/lib/commands/config-pull.js.map +1 -0
- package/dist/lib/commands/config-push-args.d.ts +9 -0
- package/dist/lib/commands/config-push-args.d.ts.map +1 -0
- package/dist/lib/commands/config-push-args.js +108 -0
- package/dist/lib/commands/config-push-args.js.map +1 -0
- package/dist/lib/commands/config-push.d.ts +6 -0
- package/dist/lib/commands/config-push.d.ts.map +1 -0
- package/dist/lib/commands/config-push.js +166 -0
- package/dist/lib/commands/config-push.js.map +1 -0
- package/dist/lib/commands/deployment-config.d.ts +9 -0
- package/dist/lib/commands/deployment-config.d.ts.map +1 -0
- package/dist/lib/commands/deployment-config.js +73 -0
- package/dist/lib/commands/deployment-config.js.map +1 -0
- package/dist/lib/commands/env-plan.d.ts +4 -0
- package/dist/lib/commands/env-plan.d.ts.map +1 -0
- package/dist/lib/commands/env-plan.js +85 -0
- package/dist/lib/commands/env-plan.js.map +1 -0
- package/dist/lib/commands/env.d.ts +4 -0
- package/dist/lib/commands/env.d.ts.map +1 -0
- package/dist/lib/commands/env.js +122 -0
- package/dist/lib/commands/env.js.map +1 -0
- package/dist/lib/commands/init.d.ts +3 -0
- package/dist/lib/commands/init.d.ts.map +1 -0
- package/dist/lib/commands/init.js +59 -0
- package/dist/lib/commands/init.js.map +1 -0
- package/dist/lib/commands/secrets-args.d.ts +13 -0
- package/dist/lib/commands/secrets-args.d.ts.map +1 -0
- package/dist/lib/commands/secrets-args.js +136 -0
- package/dist/lib/commands/secrets-args.js.map +1 -0
- package/dist/lib/commands/secrets-plan.d.ts +9 -0
- package/dist/lib/commands/secrets-plan.d.ts.map +1 -0
- package/dist/lib/commands/secrets-plan.js +109 -0
- package/dist/lib/commands/secrets-plan.js.map +1 -0
- package/dist/lib/commands/secrets.d.ts +7 -0
- package/dist/lib/commands/secrets.d.ts.map +1 -0
- package/dist/lib/commands/secrets.js +191 -0
- package/dist/lib/commands/secrets.js.map +1 -0
- package/dist/lib/deployments.d.ts +4 -0
- package/dist/lib/deployments.d.ts.map +1 -0
- package/dist/lib/deployments.js +83 -0
- package/dist/lib/deployments.js.map +1 -0
- package/dist/lib/environments.d.ts +4 -0
- package/dist/lib/environments.d.ts.map +1 -0
- package/dist/lib/environments.js +97 -0
- package/dist/lib/environments.js.map +1 -0
- package/dist/lib/firebase-credential.d.ts +28 -0
- package/dist/lib/firebase-credential.d.ts.map +1 -0
- package/dist/lib/firebase-credential.js +50 -0
- package/dist/lib/firebase-credential.js.map +1 -0
- package/dist/lib/firebase-invalidate.d.ts +4 -0
- package/dist/lib/firebase-invalidate.d.ts.map +1 -0
- package/dist/lib/firebase-invalidate.js +61 -0
- package/dist/lib/firebase-invalidate.js.map +1 -0
- package/dist/lib/firebase-vars.d.ts +14 -0
- package/dist/lib/firebase-vars.d.ts.map +1 -0
- package/dist/lib/firebase-vars.js +40 -0
- package/dist/lib/firebase-vars.js.map +1 -0
- package/dist/lib/firebase.d.ts +20 -0
- package/dist/lib/firebase.d.ts.map +1 -0
- package/dist/lib/firebase.js +189 -0
- package/dist/lib/firebase.js.map +1 -0
- package/dist/lib/gcp.d.ts +4 -0
- package/dist/lib/gcp.d.ts.map +1 -0
- package/dist/lib/gcp.js +53 -0
- package/dist/lib/gcp.js.map +1 -0
- package/dist/lib/generated-secrets.d.ts +16 -0
- package/dist/lib/generated-secrets.d.ts.map +1 -0
- package/dist/lib/generated-secrets.js +68 -0
- package/dist/lib/generated-secrets.js.map +1 -0
- package/dist/lib/logger.d.ts +7 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +60 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/manifest/index.d.ts +6 -0
- package/dist/lib/manifest/index.d.ts.map +1 -0
- package/dist/lib/manifest/index.js +15 -0
- package/dist/lib/manifest/index.js.map +1 -0
- package/dist/lib/manifest/parse.d.ts +11 -0
- package/dist/lib/manifest/parse.d.ts.map +1 -0
- package/dist/lib/manifest/parse.js +214 -0
- package/dist/lib/manifest/parse.js.map +1 -0
- package/dist/lib/manifest/resolve.d.ts +9 -0
- package/dist/lib/manifest/resolve.d.ts.map +1 -0
- package/dist/lib/manifest/resolve.js +34 -0
- package/dist/lib/manifest/resolve.js.map +1 -0
- package/dist/lib/manifest/types.d.ts +57 -0
- package/dist/lib/manifest/types.d.ts.map +1 -0
- package/dist/lib/manifest/types.js +12 -0
- package/dist/lib/manifest/types.js.map +1 -0
- package/dist/lib/manifest/write.d.ts +3 -0
- package/dist/lib/manifest/write.d.ts.map +1 -0
- package/dist/lib/manifest/write.js +110 -0
- package/dist/lib/manifest/write.js.map +1 -0
- package/dist/lib/project.d.ts +6 -0
- package/dist/lib/project.d.ts.map +1 -0
- package/dist/lib/project.js +56 -0
- package/dist/lib/project.js.map +1 -0
- package/dist/lib/providers/deployment.d.ts +19 -0
- package/dist/lib/providers/deployment.d.ts.map +1 -0
- package/dist/lib/providers/deployment.js +62 -0
- package/dist/lib/providers/deployment.js.map +1 -0
- package/dist/lib/providers/registry.d.ts +5 -0
- package/dist/lib/providers/registry.d.ts.map +1 -0
- package/dist/lib/providers/registry.js +31 -0
- package/dist/lib/providers/registry.js.map +1 -0
- package/dist/lib/providers/service.d.ts +35 -0
- package/dist/lib/providers/service.d.ts.map +1 -0
- package/dist/lib/providers/service.js +81 -0
- package/dist/lib/providers/service.js.map +1 -0
- package/dist/lib/rotation-preflight.d.ts +11 -0
- package/dist/lib/rotation-preflight.d.ts.map +1 -0
- package/dist/lib/rotation-preflight.js +47 -0
- package/dist/lib/rotation-preflight.js.map +1 -0
- package/dist/lib/rotation.d.ts +33 -0
- package/dist/lib/rotation.d.ts.map +1 -0
- package/dist/lib/rotation.js +168 -0
- package/dist/lib/rotation.js.map +1 -0
- package/dist/lib/sentry.d.ts +5 -0
- package/dist/lib/sentry.d.ts.map +1 -0
- package/dist/lib/sentry.js +132 -0
- package/dist/lib/sentry.js.map +1 -0
- package/dist/lib/subprocess.d.ts +8 -0
- package/dist/lib/subprocess.d.ts.map +1 -0
- package/dist/lib/subprocess.js +30 -0
- package/dist/lib/subprocess.js.map +1 -0
- package/dist/lib/targets.d.ts +3 -0
- package/dist/lib/targets.d.ts.map +1 -0
- package/dist/lib/targets.js +25 -0
- package/dist/lib/targets.js.map +1 -0
- package/dist/lib/vercel-api.d.ts +50 -0
- package/dist/lib/vercel-api.d.ts.map +1 -0
- package/dist/lib/vercel-api.js +176 -0
- package/dist/lib/vercel-api.js.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# envctl
|
|
2
|
+
|
|
3
|
+
A personal, `gh`-style command-line tool for managing deployment configuration and atomically rotating provider secrets across projects and their environments. The hosting/secret providers (Vercel, Firebase, …) sit behind the tool as pluggable backends.
|
|
4
|
+
|
|
5
|
+
See the [Vision](https://github.com/rmartz/envctl/issues/1) for the design and desired functionality.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
envctl is published to GitHub Packages as `@rmartz/envctl` and installed as a personal global CLI — never a per-project dependency.
|
|
10
|
+
|
|
11
|
+
**One-time setup.** GitHub Packages requires authentication (even for public packages), so add to your `~/.npmrc`:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
@rmartz:registry=https://npm.pkg.github.com
|
|
15
|
+
//npm.pkg.github.com/:_authToken=<a GitHub token with the read:packages scope>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Install and update:**
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add -g @rmartz/envctl # install
|
|
22
|
+
pnpm add -g @rmartz/envctl@latest # update to the newest release
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then, from any project directory:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
envctl --version
|
|
29
|
+
envctl config push --dry-run # preview the public-var sync for the current project
|
|
30
|
+
envctl config pull # write .env.local from the development environment
|
|
31
|
+
envctl secrets rotate # atomically rotate Firebase/Sentry secrets and redeploy
|
|
32
|
+
envctl secrets init firebase # bootstrap secrets for a fresh project (auto-detects if omitted)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`secrets rotate` mints each new credential, redeploys, then invalidates the old
|
|
36
|
+
one, so the project is never left without a working credential. It relies on the
|
|
37
|
+
same auth as the rest of the CLI — `VERCEL_TOKEN` or `vercel login`,
|
|
38
|
+
`SENTRY_AUTH_TOKEN`, and an authenticated `gcloud` for Firebase key minting (run
|
|
39
|
+
`envctl auth status` to check). Pass `--no-invalidate` to keep the old keys or
|
|
40
|
+
`--refresh-previews` to redeploy active PR previews afterward.
|
|
41
|
+
|
|
42
|
+
New to a project, or unsure which command to reach for? Start with the
|
|
43
|
+
**[Vercel deploy runbook](docs/runbook-vercel-deploy.md)** — a scenario-driven
|
|
44
|
+
playbook (cold-start, mint, rotate, add a provider, public-var change, local
|
|
45
|
+
pull) with copy-pasteable sequences and a "how you know it worked" check for
|
|
46
|
+
each. For mechanism detail, see the
|
|
47
|
+
[secrets rotation engine](docs/secrets-rotation.md) for the full mint → deploy →
|
|
48
|
+
verify → invalidate flow, and the [docs index](docs/index.md) for the
|
|
49
|
+
[config-push](docs/config-push.md) and [environments](docs/env.md) subsystems.
|
|
50
|
+
|
|
51
|
+
## Bootstrap a blank environment
|
|
52
|
+
|
|
53
|
+
Bring a linked but otherwise empty Vercel project fully online in one step:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
envctl bootstrap
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Prerequisites:**
|
|
60
|
+
|
|
61
|
+
1. A linked Vercel project (`.vercel/project.json`, created by `vercel link`)
|
|
62
|
+
2. An authenticated `gcloud` with a **pre-existing** Firebase project and service account — if the project uses Firebase (envctl mints keys for existing resources; creating the service account from scratch is tracked in [#70](https://github.com/rmartz/envctl/issues/70))
|
|
63
|
+
3. A Sentry token (`SENTRY_AUTH_TOKEN` or `sentry-cli login`) and a **pre-existing** Sentry project — if the project uses Sentry
|
|
64
|
+
|
|
65
|
+
`bootstrap` runs four phases in order: push public variables (`config push`), initialize any missing provider secrets (`secrets init`), pull a local dotenv file (`config pull`), and trigger redeployments so running environments pick up the pushed vars (`triggerAndWaitRedeployments` — skipped with a log message on a zero-deployment project). It is idempotent — safe to re-run after a partial failure, and existing secrets are never rotated. Pass `--dry-run` to preview every phase without making changes.
|
|
66
|
+
|
|
67
|
+
See the [Bootstrap subsystem docs](docs/bootstrap.md) for the full option reference.
|
|
68
|
+
|
|
69
|
+
## Development
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pnpm install
|
|
73
|
+
pnpm build # tsc → dist/
|
|
74
|
+
pnpm run test:ts # vitest
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Releases
|
|
78
|
+
|
|
79
|
+
Releases are automated with [semantic-release](https://semantic-release.gitbook.io/): a merge to `main` computes the next version from the Conventional Commit history, tags it, creates a GitHub release, and publishes `@rmartz/envctl` to GitHub Packages.
|
package/dist/envctl.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envctl.d.ts","sourceRoot":"","sources":["../src/envctl.ts"],"names":[],"mappings":""}
|
package/dist/envctl.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const logger_1 = require("./lib/logger");
|
|
5
|
+
const help_1 = require("./lib/cli/help");
|
|
6
|
+
const registry_1 = require("./lib/cli/registry");
|
|
7
|
+
const router_1 = require("./lib/cli/router");
|
|
8
|
+
const version_1 = require("./lib/cli/version");
|
|
9
|
+
async function main() {
|
|
10
|
+
const registry = (0, registry_1.buildRegistry)();
|
|
11
|
+
const { workingDir, rest } = (0, router_1.extractGlobalArgs)(process.argv.slice(2), process.cwd());
|
|
12
|
+
const result = (0, router_1.route)(rest, registry);
|
|
13
|
+
switch (result.kind) {
|
|
14
|
+
case "version":
|
|
15
|
+
console.log((0, version_1.readPackageVersion)(__dirname));
|
|
16
|
+
return;
|
|
17
|
+
case "help":
|
|
18
|
+
console.log(result.group ? (0, help_1.renderGroupHelp)(result.group) : (0, help_1.renderTopHelp)(registry));
|
|
19
|
+
return;
|
|
20
|
+
case "error":
|
|
21
|
+
console.error(result.message);
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
return;
|
|
24
|
+
case "run":
|
|
25
|
+
await result.command.run({ workingDir }, result.args);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
main().catch((error) => {
|
|
30
|
+
if (error instanceof logger_1.FatalError) {
|
|
31
|
+
console.error(error.message);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
throw error;
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=envctl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envctl.js","sourceRoot":"","sources":["../src/envctl.ts"],"names":[],"mappings":";;;AACA,yCAA0C;AAC1C,yCAAgE;AAChE,iDAAmD;AACnD,6CAA4D;AAC5D,+CAAuD;AAEvD,KAAK,UAAU,IAAI;IACjB,MAAM,QAAQ,GAAG,IAAA,wBAAa,GAAE,CAAC;IACjC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,IAAA,0BAAiB,EAC5C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,GAAG,EAAE,CACd,CAAC;IACF,MAAM,MAAM,GAAG,IAAA,cAAK,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAErC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO,CAAC,GAAG,CAAC,IAAA,4BAAkB,EAAC,SAAS,CAAC,CAAC,CAAC;YAC3C,OAAO;QACT,KAAK,MAAM;YACT,OAAO,CAAC,GAAG,CACT,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAA,sBAAe,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAA,oBAAa,EAAC,QAAQ,CAAC,CACvE,CAAC;YACF,OAAO;QACT,KAAK,OAAO;YACV,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,KAAK,KAAK;YACR,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACtD,OAAO;IACX,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,IAAI,KAAK,YAAY,mBAAU,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,KAAK,CAAC;AACd,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the Vercel API token to use, preferring VERCEL_TOKEN env var and
|
|
3
|
+
* falling back to the token stored by the Vercel CLI (vercel login).
|
|
4
|
+
*
|
|
5
|
+
* Returns undefined if no token is available.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveVercelToken(): string | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Returns the Sentry auth token, preferring SENTRY_AUTH_TOKEN and falling back
|
|
10
|
+
* to the token stored by sentry-cli (`sentry-cli login` → ~/.sentryclirc).
|
|
11
|
+
* Returns undefined if none is available.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveSentryToken(): string | undefined;
|
|
14
|
+
export interface AuthState {
|
|
15
|
+
authenticated: boolean;
|
|
16
|
+
source?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function vercelAuthState(): AuthState;
|
|
19
|
+
export declare function sentryAuthState(): AuthState;
|
|
20
|
+
export declare function gcpAuthState(): AuthState;
|
|
21
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AA+DA;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAEvD;AAqBD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAGvD;AAKD,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,eAAe,IAAI,SAAS,CAK3C;AAED,wBAAgB,eAAe,IAAI,SAAS,CAM3C;AAID,wBAAgB,YAAY,IAAI,SAAS,CAcxC"}
|
package/dist/lib/auth.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.resolveVercelToken = resolveVercelToken;
|
|
37
|
+
exports.resolveSentryToken = resolveSentryToken;
|
|
38
|
+
exports.vercelAuthState = vercelAuthState;
|
|
39
|
+
exports.sentryAuthState = sentryAuthState;
|
|
40
|
+
exports.gcpAuthState = gcpAuthState;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const os = __importStar(require("os"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const subprocess_1 = require("./subprocess");
|
|
45
|
+
function vercelCliAuthPath() {
|
|
46
|
+
if (process.env.__VERCEL_CLI_AUTH_PATH)
|
|
47
|
+
return process.env.__VERCEL_CLI_AUTH_PATH;
|
|
48
|
+
switch (process.platform) {
|
|
49
|
+
case "darwin":
|
|
50
|
+
return path.join(os.homedir(), "Library", "Application Support", "com.vercel.cli", "auth.json");
|
|
51
|
+
case "win32":
|
|
52
|
+
return path.join(process.env.APPDATA ?? os.homedir(), "com.vercel.cli", "auth.json");
|
|
53
|
+
default:
|
|
54
|
+
return path.join(process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share"), "com.vercel.cli", "auth.json");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function readCliToken() {
|
|
58
|
+
const authPath = vercelCliAuthPath();
|
|
59
|
+
let raw;
|
|
60
|
+
try {
|
|
61
|
+
raw = fs.readFileSync(authPath, "utf-8");
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
let auth;
|
|
67
|
+
try {
|
|
68
|
+
auth = JSON.parse(raw);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
if (!auth.token)
|
|
74
|
+
return undefined;
|
|
75
|
+
if (auth.expiresAt !== undefined && Date.now() / 1000 > auth.expiresAt) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
return auth.token;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns the Vercel API token to use, preferring VERCEL_TOKEN env var and
|
|
82
|
+
* falling back to the token stored by the Vercel CLI (vercel login).
|
|
83
|
+
*
|
|
84
|
+
* Returns undefined if no token is available.
|
|
85
|
+
*/
|
|
86
|
+
function resolveVercelToken() {
|
|
87
|
+
return process.env.VERCEL_TOKEN ?? readCliToken() ?? undefined;
|
|
88
|
+
}
|
|
89
|
+
function sentryClircPath() {
|
|
90
|
+
return (process.env.__SENTRY_CLIRC_PATH ?? path.join(os.homedir(), ".sentryclirc"));
|
|
91
|
+
}
|
|
92
|
+
// Reads the `token = …` value from sentry-cli's ~/.sentryclirc (INI-style).
|
|
93
|
+
// Graceful: a missing or unparseable file yields undefined.
|
|
94
|
+
function readSentryClircToken() {
|
|
95
|
+
let raw;
|
|
96
|
+
try {
|
|
97
|
+
raw = fs.readFileSync(sentryClircPath(), "utf-8");
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
const match = /^\s*token\s*=\s*(\S+)\s*$/m.exec(raw);
|
|
103
|
+
return match ? match[1] : undefined;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Returns the Sentry auth token, preferring SENTRY_AUTH_TOKEN and falling back
|
|
107
|
+
* to the token stored by sentry-cli (`sentry-cli login` → ~/.sentryclirc).
|
|
108
|
+
* Returns undefined if none is available.
|
|
109
|
+
*/
|
|
110
|
+
function resolveSentryToken() {
|
|
111
|
+
// `||` (not `??`) so a blank SENTRY_AUTH_TOKEN falls through to the CLI store.
|
|
112
|
+
return process.env.SENTRY_AUTH_TOKEN || readSentryClircToken() || undefined;
|
|
113
|
+
}
|
|
114
|
+
function vercelAuthState() {
|
|
115
|
+
if (process.env.VERCEL_TOKEN)
|
|
116
|
+
return { authenticated: true, source: "VERCEL_TOKEN env var" };
|
|
117
|
+
if (readCliToken())
|
|
118
|
+
return { authenticated: true, source: "vercel login" };
|
|
119
|
+
return { authenticated: false };
|
|
120
|
+
}
|
|
121
|
+
function sentryAuthState() {
|
|
122
|
+
if (process.env.SENTRY_AUTH_TOKEN)
|
|
123
|
+
return { authenticated: true, source: "SENTRY_AUTH_TOKEN env var" };
|
|
124
|
+
if (readSentryClircToken())
|
|
125
|
+
return { authenticated: true, source: "sentry-cli login (~/.sentryclirc)" };
|
|
126
|
+
return { authenticated: false };
|
|
127
|
+
}
|
|
128
|
+
// GCP auth is owned entirely by the gcloud CLI; report the active account via a
|
|
129
|
+
// local, side-effect-free `gcloud auth list` query (no token minting).
|
|
130
|
+
function gcpAuthState() {
|
|
131
|
+
if (!(0, subprocess_1.commandExists)("gcloud"))
|
|
132
|
+
return { authenticated: false };
|
|
133
|
+
try {
|
|
134
|
+
const account = (0, subprocess_1.run)("gcloud", [
|
|
135
|
+
"auth",
|
|
136
|
+
"list",
|
|
137
|
+
"--filter=status:ACTIVE",
|
|
138
|
+
"--format=value(account)",
|
|
139
|
+
]).trim();
|
|
140
|
+
if (account)
|
|
141
|
+
return { authenticated: true, source: `gcloud: ${account}` };
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// gcloud present but the query failed — treat as unauthenticated.
|
|
145
|
+
}
|
|
146
|
+
return { authenticated: false };
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqEA,gDAEC;AA0BD,gDAGC;AAUD,0CAKC;AAED,0CAMC;AAID,oCAcC;AA7ID,uCAAyB;AACzB,uCAAyB;AACzB,2CAA6B;AAE7B,6CAA4D;AAO5D,SAAS,iBAAiB;IACxB,IAAI,OAAO,CAAC,GAAG,CAAC,sBAAsB;QACpC,OAAO,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;IAC5C,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,IAAI,CACd,EAAE,CAAC,OAAO,EAAE,EACZ,SAAS,EACT,qBAAqB,EACrB,gBAAgB,EAChB,WAAW,CACZ,CAAC;QACJ,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,IAAI,CACd,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,OAAO,EAAE,EACnC,gBAAgB,EAChB,WAAW,CACZ,CAAC;QACJ;YACE,OAAO,IAAI,CAAC,IAAI,CACd,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EACvE,gBAAgB,EAChB,WAAW,CACZ,CAAC;IACN,CAAC;AACH,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;IACrC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,IAAgB,CAAC;IACrB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAElC,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QACvE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,kBAAkB;IAChC,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,YAAY,EAAE,IAAI,SAAS,CAAC;AACjE,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAC3E,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,4DAA4D;AAC5D,SAAS,oBAAoB;IAC3B,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,4BAA4B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB;IAChC,+EAA+E;IAC/E,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,oBAAoB,EAAE,IAAI,SAAS,CAAC;AAC9E,CAAC;AAUD,SAAgB,eAAe;IAC7B,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAC1B,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACjE,IAAI,YAAY,EAAE;QAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAC3E,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,SAAgB,eAAe;IAC7B,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC/B,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;IACtE,IAAI,oBAAoB,EAAE;QACxB,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,mCAAmC,EAAE,CAAC;IAC9E,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,gFAAgF;AAChF,uEAAuE;AACvE,SAAgB,YAAY;IAC1B,IAAI,CAAC,IAAA,0BAAa,EAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,gBAAM,EAAC,QAAQ,EAAE;YAC/B,MAAM;YACN,MAAM;YACN,wBAAwB;YACxB,yBAAyB;SAC1B,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,OAAO;YAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,OAAO,EAAE,EAAE,CAAC;IAC5E,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;IACpE,CAAC;IACD,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Finding } from "./check";
|
|
2
|
+
import type { DeploymentProvider } from "./providers/deployment";
|
|
3
|
+
import type { VercelEnvVar } from "./vercel-api";
|
|
4
|
+
export declare function checkFirebaseSaDrift(deployment: DeploymentProvider, configDir: string, liveEnvs: VercelEnvVar[]): Promise<Finding[]>;
|
|
5
|
+
export declare function checkFirebaseProjectDrift(deployment: DeploymentProvider, configDir: string, liveEnvs: VercelEnvVar[]): Promise<Finding[]>;
|
|
6
|
+
//# sourceMappingURL=check-firebase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-firebase.d.ts","sourceRoot":"","sources":["../../src/lib/check-firebase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAQ3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAUjD,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,YAAY,EAAE,GACvB,OAAO,CAAC,OAAO,EAAE,CAAC,CAqCpB;AAaD,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,YAAY,EAAE,GACvB,OAAO,CAAC,OAAO,EAAE,CAAC,CAkCpB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkFirebaseSaDrift = checkFirebaseSaDrift;
|
|
4
|
+
exports.checkFirebaseProjectDrift = checkFirebaseProjectDrift;
|
|
5
|
+
const check_1 = require("./check");
|
|
6
|
+
const environments_1 = require("./environments");
|
|
7
|
+
const firebase_credential_1 = require("./firebase-credential");
|
|
8
|
+
const firebase_vars_1 = require("./firebase-vars");
|
|
9
|
+
const manifest_1 = require("./manifest");
|
|
10
|
+
const targets_1 = require("./targets");
|
|
11
|
+
// #103: `FIREBASE_SA_EMAIL` is a deprecated committed var that duplicates the
|
|
12
|
+
// service-account identity the app already reads from the credential's
|
|
13
|
+
// `clientEmail`. This flags a project that still declares it against the live
|
|
14
|
+
// credential — an **error** when the two name different service accounts (the
|
|
15
|
+
// drift class #97/#98 were created to kill: envctl would mint keys for one SA
|
|
16
|
+
// while the app authenticates as another), a deprecation **warning** when it
|
|
17
|
+
// merely duplicates the credential. Part of `envctl check --live`, since the
|
|
18
|
+
// credential's value lives in the provider, not in git.
|
|
19
|
+
async function checkFirebaseSaDrift(deployment, configDir, liveEnvs) {
|
|
20
|
+
const manifest = (0, manifest_1.parseManifest)(configDir);
|
|
21
|
+
const spec = (0, firebase_credential_1.resolveFirebaseCredential)(manifest.services.find((s) => s.provider === "firebase"));
|
|
22
|
+
const resolveTarget = (0, targets_1.envTargetResolver)(configDir);
|
|
23
|
+
const findings = [];
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
for (const env of manifest.environments) {
|
|
26
|
+
const declared = (0, environments_1.parseDeploymentEnv)(configDir, env).FIREBASE_SA_EMAIL;
|
|
27
|
+
if (!declared)
|
|
28
|
+
continue;
|
|
29
|
+
const target = resolveTarget(env);
|
|
30
|
+
const seenKey = `${target}|${declared}`;
|
|
31
|
+
if (seen.has(seenKey))
|
|
32
|
+
continue;
|
|
33
|
+
seen.add(seenKey);
|
|
34
|
+
if (!(0, firebase_vars_1.hasFirebaseCredentialForEnv)(liveEnvs, spec.names, target))
|
|
35
|
+
continue;
|
|
36
|
+
const sa = await (0, firebase_vars_1.getFirebaseSaForEnv)(target, liveEnvs, spec.names, deployment);
|
|
37
|
+
if (!sa?.email)
|
|
38
|
+
continue;
|
|
39
|
+
findings.push(sa.email === declared
|
|
40
|
+
? (0, check_1.mkWarning)(`FIREBASE_SA_EMAIL for '${env}' duplicates the credential's ${spec.names.clientEmail} — it is deprecated; remove it and let envctl derive the service account from the credential.`)
|
|
41
|
+
: (0, check_1.mkError)(`FIREBASE_SA_EMAIL for '${env}' (${declared}) disagrees with the credential's ${spec.names.clientEmail} (${sa.email}) on the '${target}' target — they name different service accounts. Remove the deprecated FIREBASE_SA_EMAIL and let envctl derive the identity from the credential.`));
|
|
42
|
+
}
|
|
43
|
+
return findings;
|
|
44
|
+
}
|
|
45
|
+
// #121: the committed `FIREBASE_PROJECT_ID` (a public var in `deployment/{env}.yml`)
|
|
46
|
+
// is the target's own GCP project — the project envctl mints keys in at init, and
|
|
47
|
+
// the same project the app's credential authenticates against (the credential's
|
|
48
|
+
// `projectId` var). If the two disagree, envctl would mint keys in one GCP
|
|
49
|
+
// project while the app runs against another — the projectId analog of the
|
|
50
|
+
// SA-email drift #103 addressed. Unlike `FIREBASE_SA_EMAIL`, `FIREBASE_PROJECT_ID`
|
|
51
|
+
// is **not** deprecated: #127 established that init must resolve the project
|
|
52
|
+
// strictly from the target's own declared value, so a matching committed value is
|
|
53
|
+
// correct and required — only a mismatch (drift) is flagged, as an error. Part of
|
|
54
|
+
// `envctl check --live` (the credential's value lives in the provider, not git);
|
|
55
|
+
// deferred to this follow-up by #127.
|
|
56
|
+
async function checkFirebaseProjectDrift(deployment, configDir, liveEnvs) {
|
|
57
|
+
const manifest = (0, manifest_1.parseManifest)(configDir);
|
|
58
|
+
const spec = (0, firebase_credential_1.resolveFirebaseCredential)(manifest.services.find((s) => s.provider === "firebase"));
|
|
59
|
+
const resolveTarget = (0, targets_1.envTargetResolver)(configDir);
|
|
60
|
+
const findings = [];
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
for (const env of manifest.environments) {
|
|
63
|
+
const declared = (0, environments_1.parseDeploymentEnv)(configDir, env).FIREBASE_PROJECT_ID;
|
|
64
|
+
if (!declared)
|
|
65
|
+
continue;
|
|
66
|
+
const target = resolveTarget(env);
|
|
67
|
+
const seenKey = `${target}|${declared}`;
|
|
68
|
+
if (seen.has(seenKey))
|
|
69
|
+
continue;
|
|
70
|
+
seen.add(seenKey);
|
|
71
|
+
if (!(0, firebase_vars_1.hasFirebaseCredentialForEnv)(liveEnvs, spec.names, target))
|
|
72
|
+
continue;
|
|
73
|
+
const sa = await (0, firebase_vars_1.getFirebaseSaForEnv)(target, liveEnvs, spec.names, deployment);
|
|
74
|
+
if (!sa?.gcpProject)
|
|
75
|
+
continue;
|
|
76
|
+
if (sa.gcpProject !== declared)
|
|
77
|
+
findings.push((0, check_1.mkError)(`FIREBASE_PROJECT_ID for '${env}' (${declared}) disagrees with the credential's ${spec.names.projectId} (${sa.gcpProject}) on the '${target}' target — envctl would mint keys in one GCP project while the app authenticates against another. Reconcile the committed FIREBASE_PROJECT_ID with the live credential.`));
|
|
78
|
+
}
|
|
79
|
+
return findings;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=check-firebase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-firebase.js","sourceRoot":"","sources":["../../src/lib/check-firebase.ts"],"names":[],"mappings":";;AAoBA,oDAyCC;AAaD,8DAsCC;AAhHD,mCAA2D;AAC3D,iDAAoD;AACpD,+DAAkE;AAClE,mDAGyB;AACzB,yCAA2C;AAE3C,uCAA8C;AAG9C,8EAA8E;AAC9E,uEAAuE;AACvE,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,wDAAwD;AACjD,KAAK,UAAU,oBAAoB,CACxC,UAA8B,EAC9B,SAAiB,EACjB,QAAwB;IAExB,MAAM,QAAQ,GAAG,IAAA,wBAAa,EAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAA,+CAAyB,EACpC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CACzD,CAAC;IACF,MAAM,aAAa,GAAG,IAAA,2BAAiB,EAAC,SAAS,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAA,iCAAkB,EAAC,SAAS,EAAE,GAAG,CAAC,CAAC,iBAAiB,CAAC;QACtE,IAAI,CAAC,QAAQ;YAAE,SAAS;QACxB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,GAAG,MAAM,IAAI,QAAQ,EAAE,CAAC;QACxC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QAChC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAElB,IAAI,CAAC,IAAA,2CAA2B,EAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC;YAAE,SAAS;QACzE,MAAM,EAAE,GAAG,MAAM,IAAA,mCAAmB,EAClC,MAAM,EACN,QAAQ,EACR,IAAI,CAAC,KAAK,EACV,UAAU,CACX,CAAC;QACF,IAAI,CAAC,EAAE,EAAE,KAAK;YAAE,SAAS;QAEzB,QAAQ,CAAC,IAAI,CACX,EAAE,CAAC,KAAK,KAAK,QAAQ;YACnB,CAAC,CAAC,IAAA,iBAAS,EACP,0BAA0B,GAAG,iCAAiC,IAAI,CAAC,KAAK,CAAC,WAAW,+FAA+F,CACpL;YACH,CAAC,CAAC,IAAA,eAAO,EACL,0BAA0B,GAAG,MAAM,QAAQ,qCAAqC,IAAI,CAAC,KAAK,CAAC,WAAW,KAAK,EAAE,CAAC,KAAK,aAAa,MAAM,kJAAkJ,CACzR,CACN,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,qFAAqF;AACrF,kFAAkF;AAClF,gFAAgF;AAChF,2EAA2E;AAC3E,2EAA2E;AAC3E,mFAAmF;AACnF,6EAA6E;AAC7E,kFAAkF;AAClF,kFAAkF;AAClF,iFAAiF;AACjF,sCAAsC;AAC/B,KAAK,UAAU,yBAAyB,CAC7C,UAA8B,EAC9B,SAAiB,EACjB,QAAwB;IAExB,MAAM,QAAQ,GAAG,IAAA,wBAAa,EAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAA,+CAAyB,EACpC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CACzD,CAAC;IACF,MAAM,aAAa,GAAG,IAAA,2BAAiB,EAAC,SAAS,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAA,iCAAkB,EAAC,SAAS,EAAE,GAAG,CAAC,CAAC,mBAAmB,CAAC;QACxE,IAAI,CAAC,QAAQ;YAAE,SAAS;QACxB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,GAAG,MAAM,IAAI,QAAQ,EAAE,CAAC;QACxC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QAChC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAElB,IAAI,CAAC,IAAA,2CAA2B,EAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC;YAAE,SAAS;QACzE,MAAM,EAAE,GAAG,MAAM,IAAA,mCAAmB,EAClC,MAAM,EACN,QAAQ,EACR,IAAI,CAAC,KAAK,EACV,UAAU,CACX,CAAC;QACF,IAAI,CAAC,EAAE,EAAE,UAAU;YAAE,SAAS;QAE9B,IAAI,EAAE,CAAC,UAAU,KAAK,QAAQ;YAC5B,QAAQ,CAAC,IAAI,CACX,IAAA,eAAO,EACL,4BAA4B,GAAG,MAAM,QAAQ,qCAAqC,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,EAAE,CAAC,UAAU,aAAa,MAAM,yKAAyK,CACrT,CACF,CAAC;IACN,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-live.d.ts","sourceRoot":"","sources":["../../src/lib/check-live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAmB3D,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CA4CtE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkLive = checkLive;
|
|
4
|
+
const check_1 = require("./check");
|
|
5
|
+
const check_firebase_1 = require("./check-firebase");
|
|
6
|
+
const deployment_config_1 = require("./commands/deployment-config");
|
|
7
|
+
const environments_1 = require("./environments");
|
|
8
|
+
const manifest_1 = require("./manifest");
|
|
9
|
+
const registry_1 = require("./providers/registry");
|
|
10
|
+
const targets_1 = require("./targets");
|
|
11
|
+
// Live reconciliation (`envctl check --live`): compares the public config vars
|
|
12
|
+
// the project declares against what is actually present on the deployment
|
|
13
|
+
// provider, surfacing drift. Requires network + auth, which is why it is behind
|
|
14
|
+
// a flag. Secrets (Firebase/Sentry credentials, generated values) are owned by
|
|
15
|
+
// the rotation engine and deliberately out of scope here — this checks the
|
|
16
|
+
// public variables `config push` manages, so declared-vs-live stays apples to
|
|
17
|
+
// apples.
|
|
18
|
+
async function checkLive(workingDir) {
|
|
19
|
+
const configDir = (0, deployment_config_1.deploymentDir)(workingDir);
|
|
20
|
+
let deployment;
|
|
21
|
+
try {
|
|
22
|
+
deployment = (0, registry_1.resolveProjectDeployment)(configDir, workingDir);
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
return [
|
|
26
|
+
(0, check_1.mkError)(`--live check skipped: ${e instanceof Error ? e.message : String(e)}`),
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
const manifest = (0, manifest_1.parseManifest)(configDir);
|
|
30
|
+
const resolveTarget = (0, targets_1.envTargetResolver)(configDir);
|
|
31
|
+
const live = (await deployment.listEnvVars()).envs;
|
|
32
|
+
const findings = [];
|
|
33
|
+
for (const env of manifest.environments) {
|
|
34
|
+
const target = resolveTarget(env);
|
|
35
|
+
const declared = (0, environments_1.parseDeploymentEnv)(configDir, env);
|
|
36
|
+
for (const key of Object.keys(declared)) {
|
|
37
|
+
const present = live.some((e) => e.key === key && e.target.includes(target));
|
|
38
|
+
if (!present)
|
|
39
|
+
findings.push((0, check_1.mkWarning)(`declared variable '${key}' for '${env}' is not present on the '${target}' target (drift).`));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// The deprecated FIREBASE_SA_EMAIL must agree with the credential's live
|
|
43
|
+
// clientEmail (#103).
|
|
44
|
+
findings.push(...(await (0, check_firebase_1.checkFirebaseSaDrift)(deployment, configDir, live)));
|
|
45
|
+
// The committed FIREBASE_PROJECT_ID must agree with the credential's live
|
|
46
|
+
// projectId (#121).
|
|
47
|
+
findings.push(...(await (0, check_firebase_1.checkFirebaseProjectDrift)(deployment, configDir, live)));
|
|
48
|
+
return findings;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=check-live.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-live.js","sourceRoot":"","sources":["../../src/lib/check-live.ts"],"names":[],"mappings":";;AAmBA,8BA4CC;AA/DD,mCAA2D;AAC3D,qDAG0B;AAC1B,oEAA6D;AAC7D,iDAAoD;AACpD,yCAA2C;AAE3C,mDAAgE;AAChE,uCAA8C;AAE9C,+EAA+E;AAC/E,0EAA0E;AAC1E,gFAAgF;AAChF,+EAA+E;AAC/E,2EAA2E;AAC3E,8EAA8E;AAC9E,UAAU;AACH,KAAK,UAAU,SAAS,CAAC,UAAkB;IAChD,MAAM,SAAS,GAAG,IAAA,iCAAa,EAAC,UAAU,CAAC,CAAC;IAE5C,IAAI,UAA8B,CAAC;IACnC,IAAI,CAAC;QACH,UAAU,GAAG,IAAA,mCAAwB,EAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,IAAA,eAAO,EACL,yBAAyB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACtE;SACF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAA,wBAAa,EAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,aAAa,GAAG,IAAA,2BAAiB,EAAC,SAAS,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,CAAC,MAAM,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC;IAEnD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAA,iCAAkB,EAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACpD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CACvB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAClD,CAAC;YACF,IAAI,CAAC,OAAO;gBACV,QAAQ,CAAC,IAAI,CACX,IAAA,iBAAS,EACP,sBAAsB,GAAG,UAAU,GAAG,4BAA4B,MAAM,mBAAmB,CAC5F,CACF,CAAC;QACN,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,sBAAsB;IACtB,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAA,qCAAoB,EAAC,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5E,0EAA0E;IAC1E,oBAAoB;IACpB,QAAQ,CAAC,IAAI,CACX,GAAG,CAAC,MAAM,IAAA,0CAAyB,EAAC,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAClE,CAAC;IACF,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type Severity = "error" | "warning";
|
|
2
|
+
export interface Finding {
|
|
3
|
+
readonly severity: Severity;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const mkError: (message: string) => Finding;
|
|
7
|
+
export declare const mkWarning: (message: string) => Finding;
|
|
8
|
+
export declare function checkStatic(workingDir: string): Finding[];
|
|
9
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/lib/check.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAC3C,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,KAAG,OAGxC,CAAC;AACH,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,KAAG,OAG1C,CAAC;AAKH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,EAAE,CAmCzD"}
|