Add a script to copy Render's env into GCP - #263
Conversation
Reads the service's variables from the Render API (or a local env file), writes known secrets to Secret Manager via gcloud stdin with accessor granted to the devasign-api service account, and writes the rest to a gitignored Cloud Run env-vars YAML. Values are never printed or put on argv; without --apply it only previews the classification. deploy/ is excluded from the image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
DevAsign Code Review
📋 Criteria not met (1) · 🐞 Bugs (2) · 📝 Nitpicks (2) · ❌ Tests failing (1)
🟡 Merge score: 59/100
7 of 8 acceptance criteria met, 1 not met.
The script implements the intended Render-to-GCP import flow and most criteria are satisfied by concrete code: env sourced from Render API or file, secret classification into Secret Manager via stdin, accessor grant to devasign-api, plain vars to a YAML, no value printing, preview-only without --app
Prompt to fix all issues
You are helping fix PR "Add a script to copy Render's env into GCP" in devasignhq/agent. Automated review surfaced the items below — failed acceptance criteria and review findings. Each item states what was required, what's wrong with the current diff, and how to fix it; the embedded fix blocks include the expected behavior and the relevant diff hunk. Apply each fix so the item is resolved. Items tagged **Blocker** gate approval; the rest are advisory but worth addressing. Don't introduce changes beyond what's listed.
## End goal
A script migrates a Render service's environment variables into GCP, storing known secrets in Secret Manager and remaining variables in a Cloud Run env-vars YAML, without exposing values.
## Failed acceptance criteria
### 1. Required: Variables not classified as secrets are written to a Cloud Run env-vars YAML file that is gitignored. (5)
What's wrong now: Non-secret vars are written to envYamlPath (`.env.cloudrun.yaml`) under --apply, satisfying the 'written to YAML' half. But the 'gitignored' requirement is not met by this diff: the only ignore change is `deploy` added to backend/.dockerignore (line 15), which excludes it from the Docker image, not from git. No .gitignore entry for `.env.cloudrun.yaml` (or `backend/deploy/`) is added in the diff, so the plaintext env YAML would be committable. The PR/comment claim that it is gitignored is unverified against the diff. If a pre-existing .gitignore already covers deploy/ it is not shown; but since the file lives in a newly-created directory, a positive ignore entry should be added.
How to fix:
**Suggested change** (`backend/deploy/gcp/import-render-env.mjs:56`):
```diff
-const envYamlPath = path.join(outDir, ".env.cloudrun.yaml");
+const envYamlPath = path.join(outDir, ".env.cloudrun.yaml"); // ensure backend/.gitignore ignores deploy/gcp/.env.cloudrun.yaml
```
Fix: Gitignore the generated Cloud Run env-vars files
File: backend/.gitignore (or backend/deploy/gcp/.gitignore)
Symbol: n/a
Issue:
The script writes `.env.cloudrun.yaml` (plaintext non-secret env values) and `.env.cloudrun.secrets` into backend/deploy/gcp/. The PR claims this YAML is gitignored, but the diff only adds `deploy` to backend/.dockerignore, which excludes it from the Docker image, not from git. Nothing prevents the plaintext env file from being committed.
Expected behavior:
The generated env YAML and secrets-spec files are ignored by git so they can never be committed.
Suggested approach:
Add a .gitignore (or append to backend/.gitignore) entries: `deploy/gcp/.env.cloudrun.yaml` and `deploy/gcp/.env.cloudrun.secrets` (or simply `deploy/gcp/.env.cloudrun.*`). Confirm no existing .gitignore already covers it before adding.
Relevant diff:
```diff
+writeFileSync(envYamlPath, plain.map((k) => `${k}: ${JSON.stringify(vars.get(k))}\n`).join(""), { mode: 0o600 });
+writeFileSync(secretsSpecPath, secrets.map((k) => `${k}=${k}:latest`).join(",") + "\n");
```
## Review findings
### 1. [Bug · Warn] `backend/deploy/gcp/import-render-env.mjs` — The Render env-vars endpoint returns a paginated response where each element is an object like `{ envVar: {...}, cursor: "..." }`. The code iterates `for (const { envVar } of page)` and also reads `page.length` and `page[page.length - 1].cursor`, which requires `page` to be an array. If the API returns a wrapped object (e.g. `{ items: [...] }`) or the destructuring assumptions are wrong, the iteration silently produces nothing or throws. Given the code assumes `page` is a bare array with `.cursor` on each element, a mismatch with the actual response shape breaks the import.
Fix: Verify Render env-vars response shape before destructuring
File: backend/deploy/gcp/import-render-env.mjs
Symbol: fromRender
Issue:
The code assumes the Render API returns a top-level array where each element has `envVar` and `cursor`. If the response is wrapped or the pagination shape differs, iteration throws or yields nothing.
Expected behavior:
The function should correctly read the array of env-var entries regardless of whether it is nested, and stop pagination on the true last page.
Suggested approach:
Confirm the actual JSON shape from the Render API docs; if entries are nested under a key, extract that array before iterating. Guard against a non-array response.
Relevant diff:
```diff
+ const page = await res.json();
+ for (const { envVar } of page) vars.set(envVar.key, envVar.value ?? "");
+ if (page.length < 100) return vars;
+ cursor = encodeURIComponent(page[page.length - 1].cursor);
```
### 2. [Bug · Warn] `backend/deploy/gcp/import-render-env.mjs` — In apply mode, secrets are written to Secret Manager first (line 139), and only afterwards is the YAML/secrets-spec written (lines 141-142). If `putSecret` throws for any key (e.g. a gcloud grant failure), the loop aborts with some secrets already written to Secret Manager but no env YAML or secrets spec file produced, leaving GCP state partially populated and no local record of what was applied.
Fix: Make apply-mode writes resilient to partial failure
File: backend/deploy/gcp/import-render-env.mjs
Symbol: (top-level apply block)
Issue:
If putSecret throws mid-loop, some secrets are already written to Secret Manager but the local YAML/secrets-spec files are never produced, leaving inconsistent state. Re-running adds further secret versions non-idempotently.
Expected behavior:
A failure partway through should leave a clear record of what was applied, or wrap the loop so the spec files still reflect completed work.
Suggested approach:
Wrap the secret loop in try/finally so the spec/YAML files are written for successfully applied keys, or collect results and report which secrets were applied before rethrowing.
Relevant diff:
```diff
+const member = `serviceAccount:${args["service-account"]}@${args.project}.iam.gserviceaccount.com`;
+for (const key of secrets) console.log(` ${key}: ${putSecret(key, vars.get(key), member)}`);
+
+writeFileSync(envYamlPath, plain.map((k) => `${k}: ${JSON.stringify(vars.get(k))}\n`).join(""), { mode: 0o600 });
```
### 3. [Convention · Nit] `backend/deploy/gcp/import-render-env.mjs` — The backend rule 'Always read, always write tests for all the changes made.' The diff adds a new 144-line backend script (import-render-env.mjs) with parsing/filtering logic (fromFile, SKIP, classification) but includes no accompanying tests.
Fix: Add tests for the new Render env import script
File: backend/deploy/gcp/import-render-env.mjs
Symbol: fromFile / SKIP / classification loop
Issue:
The repo DEVASIGN.md backend rule states 'Always read, always write tests for all the changes made.' This PR adds a new backend script with pure, testable logic (env-file parsing in fromFile, the SKIP predicate, and secret/plain/skip classification) but ships no tests.
Expected behavior:
The testable pure functions should have unit tests covering parsing edge cases (quoted values, export prefix), SKIP matching, and secret vs plain classification.
Suggested approach:
Refactor fromFile and the classification logic into exported helpers and add a test file exercising them; keep the CLI entrypoint side-effect-guarded so importing for tests does not run gcloud.
Relevant diff:
```diff
+function fromFile(file) {
+ const vars = new Map();
+ for (const line of readFileSync(file, "utf8").split(/\r?\n/)) {
+ const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
+ if (!m) continue;
+ let value = m[2].trim();
+ if (/^(["']).*\1$/.test(value)) value = value.slice(1, -1);
+ vars.set(m[1], value);
+ }
+ return vars;
+}
```
## Your task
Work through every item above — the failed acceptance criteria and each review finding. For each one: understand the gap from "What's wrong now", implement the change so the Required behavior holds (each fix block's `Expected behavior` describes the target state), and use the `Relevant diff` hunks as the anchor for where to edit. After each change, re-verify it resolves the item. Treat **Blocker**-tagged items as required (they block approval); address the rest too.
Tests by DevAsign✅ 5 of 8 criteria verified by tests, 1 failed, 2 unverifiable. Each verdict below links to its evidence. 1 — The script sources the service's environment variables either from the Render API or from a local env file. (pass)Verdict: pass Both subtests pass: sources env vars from a local env file via --from-file and from the Render API via --render-service. Test: 2 — Variables classified as known secrets are written to GCP Secret Manager. (pass)Verdict: pass A known secret key is sent to Secret Manager via the fake gcloud and is absent from the plain YAML. Test: 3 — Secret values are passed to gcloud via stdin rather than as command-line arguments (argv). (FAIL)Verdict: FAIL With a stub gcloud that exits 0, the script's putSecret write path throws 'secret DATABASE_URL:' and exits non-zero, so the secret is never delivered via stdin as claimed. Test: 4 — The devasign-api service account is granted accessor permission on the secrets written to Secret Manager. (pass)Verdict: pass gcloud is invoked to grant devasign-api accessor permission on the written secret. Test: 5 — Variables not classified as secrets are written to a Cloud Run env-vars YAML file that is gitignored. (unverifiable)Verdict: unverifiable The fixture sets no GCLOUD stub and invokes the real gcloud under --apply, so putSecret fails on a missing gcloud/credentials in CI before the YAML-output assertions run; the criterion's behavior was not exercised. Test: 6 — Environment variable values are never printed to output. (unverifiable)Verdict: unverifiable Preview-mode subtest passes; the --apply subtest crashes in putSecret before reaching the assertion that no secret value is printed, so the never-printed claim was not exercised. Test: 7 — When run without the --apply flag, the script only previews the classification and makes no changes to Secret Manager or the YAML output. (pass)Verdict: pass Preview mode without --apply neither invokes gcloud nor writes output files. Test: 8 — The deploy/ directory is excluded from the built image. (pass)Verdict: pass backend/.dockerignore excludes the deploy directory and the pattern matches backend/deploy/gcp. Test: Prompt to fix all failing tests |
Moves the secret list, skip rules, .env parsing and classification into env-classify.mjs so they can be tested without touching gcloud. The tests cover parsing edge cases, skipping, and classification, plus a drift guard: every secret-looking process.env name in backend/src must be classified as a secret, so a new one can't land in the plaintext Cloud Run YAML. Unquoted values now drop a trailing # comment, as dotenv does. npm test picks up deploy/**/*.test.mjs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
DevAsign Code Review
📋 Criteria not met (1) · 🧭 Intent (1) · 📝 Nitpicks (1) · ❌ Tests failing (1) · ✅ Fixed since last review (4)
🟡 Merge score: 67/100
10 of 11 acceptance criteria met, 1 not met.
Criteria 1-4, 6-11 are all satisfied by the code. Criterion 5 has a real defect: the script writes the plain env YAML to `.
Prompt to fix all issues
You are helping fix PR "Add a script to copy Render's env into GCP" in devasignhq/agent. Automated review surfaced the items below — failed acceptance criteria and review findings. Each item states what was required, what's wrong with the current diff, and how to fix it; the embedded fix blocks include the expected behavior and the relevant diff hunk. Apply each fix so the item is resolved. Items tagged **Blocker** gate approval; the rest are advisory but worth addressing. Don't introduce changes beyond what's listed.
## End goal
A script migrates a Render service's environment variables into GCP, storing known secrets in Secret Manager and remaining variables in a Cloud Run env-vars YAML, without exposing values.
## Failed acceptance criteria
### 1. Required: Variables not classified as secrets are written to a Cloud Run env-vars YAML file that is gitignored. (5)
What's wrong now: Non-secret variables are written to envYamlPath = backend/deploy/gcp/.env.cloudrun.yaml (line 97). The criterion requires this file to be gitignored. The only gitignore-type change in the diff is to backend/.dockerignore (adds `deploy`), which excludes the directory from the Docker image but does NOT prevent git from tracking the YAML. No .gitignore entry for .env.cloudrun.yaml (or .env.cloudrun.secrets) appears anywhere in the diff. Since the YAML contains real plaintext env values, an untracked-but-not-gitignored file risks being committed. There is no positive evidence a gitignore rule exists for this path.
How to fix:
**Suggested change** (`backend/deploy/gcp/.gitignore:1`):
```diff
+.env.cloudrun.yaml
+.env.cloudrun.secrets
+
```
Fix: Gitignore the generated Cloud Run env files
File: backend/deploy/gcp/.gitignore
Symbol: n/a
Issue:
import-render-env.mjs writes plaintext env values to backend/deploy/gcp/.env.cloudrun.yaml and secret specs to .env.cloudrun.secrets, but no .gitignore rule covers these paths. The .dockerignore `deploy` entry only excludes them from the built image, not from version control, so they can be committed and leak production config.
Expected behavior:
The generated .env.cloudrun.yaml (and .env.cloudrun.secrets) must be ignored by git so they are never committed, satisfying the criterion that the plain env YAML is gitignored.
Suggested approach:
Add a backend/deploy/gcp/.gitignore (or extend the repo root .gitignore) with entries for .env.cloudrun.yaml and .env.cloudrun.secrets.
Relevant diff:
```diff
+writeFileSync(envYamlPath, plain.map((k) => `${k}: ${JSON.stringify(vars.get(k))}\n`).join(""), { mode: 0o600 });
+writeFileSync(secretsSpecPath, secrets.map((k) => `${k}=${k}:latest`).join(",") + "\n");
```
## Review findings
### 1. [New-commit review · Warn] `backend/deploy/gcp/env-classify.mjs` — The commit message says the extraction is a behavior-preserving move ('Moves the secret list, skip rules, .env parsing and classification into env-classify.mjs'), but it changes parsing behavior: parseEnvText now strips trailing '# comment' from unquoted values (`value.replace(/\s+#.*$/, "")`), which the original fromFile did not do. This is called out in the message ('Unquoted values now drop a trailing # comment'), so it's intentional, but note it silently alters any unquoted value legitimately containing ' #' (e.g. a passphrase or free-text env var), truncating it. Since such a truncated plaintext value would still be written, this could corrupt a migrated variable.
Fix: Guard against truncating legitimate '#' in unquoted env values
File: backend/deploy/gcp/env-classify.mjs
Symbol: parseEnvText
Issue:
The commit moves .env parsing into env-classify.mjs and adds stripping of a trailing '# comment' from unquoted values to match dotenv. However, any unquoted value that legitimately contains ' #' (e.g. a token or passphrase) will be silently truncated at that point, corrupting the migrated value without warning.
Expected behavior:
Inline-comment stripping should match dotenv semantics closely and avoid silently corrupting real values; at minimum this edge case should be documented/tested so users know quoting is required for values containing '#'.
Suggested approach:
Either only strip when there is whitespace before '#' AND the value isn't clearly a single-token secret, or document the quoting requirement and add a test asserting the truncation behavior so it's an intentional, verified contract.
Relevant diff:
```diff
+ let value = m[2].trim();
+ if (/^(["']).*\1$/.test(value)) value = value.slice(1, -1);
+ else value = value.replace(/\s+#.*$/, "");
+ vars.set(m[1], value);
```
## Your task
Work through every item above — the failed acceptance criteria and each review finding. For each one: understand the gap from "What's wrong now", implement the change so the Required behavior holds (each fix block's `Expected behavior` describes the target state), and use the `Relevant diff` hunks as the anchor for where to edit. After each change, re-verify it resolves the item. Treat **Blocker**-tagged items as required (they block approval); address the rest too.
Tests by DevAsign✅ 7 of 11 criteria verified by tests, 1 failed, 3 unverifiable. Each verdict below links to its evidence. 1 — The script sources the service's environment variables either from the Render API or from a local env file. (pass)Verdict: pass parseEnvText and the --from-file vs --render-service branching both pass, proving both env sources. Test: 2 — Variables classified as known secrets are written to GCP Secret Manager. (FAIL)Verdict: FAIL The fixture writes a known secret (STRIPE_SECRET_KEY) via --from-file --apply and the script throws while writing the secret to Secret Manager (putSecret error), failing the criterion's own claim. Test: 3 — Secret values are passed to gcloud via stdin rather than as command-line arguments (argv). (unverifiable)Verdict: unverifiable The script errored at putSecret before any secrets create call was recorded, so the stdin-vs-argv assertion was never reached and this criterion was not exercised. Test: 4 — The devasign-api service account is granted accessor permission on the secrets written to Secret Manager. (unverifiable)Verdict: unverifiable The script threw at putSecret before the add-iam-policy-binding path ran, so the IAM accessor grant assertion was never reached. Test: 5 — Variables not classified as secrets are written to a Cloud Run env-vars YAML file that is gitignored. (pass)Verdict: pass Non-secret variables are written to the Cloud Run env-vars YAML under --apply and the file is gitignored. Test: 6 — Environment variable values are never printed to output. (pass)Verdict: pass Secret env var values never appear on stdout or stderr, even with --apply. Test: 7 — When run without the --apply flag, the script only previews the classification and makes no changes to Secret Manager or the YAML output. (unverifiable)Verdict: unverifiable The test errored with 'require is not defined' before asserting the no-apply preview behavior. Test: 8 — The deploy/ directory is excluded from the built image. (pass)Verdict: pass backend/.dockerignore lists an active deploy entry that excludes backend/deploy from the build context. Test: 9 — The npm test command runs the deploy classification tests (deploy/**/*.test.mjs) in addition to the existing src tests. (pass)Verdict: pass The backend package.json test script runs both the src and deploy test globs. Test: 10 — A drift-guard test fails if any secret-looking process.env variable read in backend/src is not classified as a secret (in SECRET_KEYS) or skipped. (pass)Verdict: pass The drift-guard test confirms every secret-looking process.env variable in backend/src is classified as a secret or skipped. Test: 11 — Unquoted env values have a trailing '# comment' stripped, while quoted values and values containing '=' are preserved intact. (pass)Verdict: pass parseEnvText strips trailing comments from unquoted values while preserving quoted values and equals signs. Test: Prompt to fix all failing tests |
.gcloudignore includes .dockerignore, so listing Dockerfile and .dockerignore there dropped them from `gcloud builds submit` / `gcloud run deploy --source`, and Cloud Build failed with "lstat /workspace/Dockerfile: no such file". Docker always sends both files regardless, so the entries did nothing for docker build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
DevAsign Code Review
📋 Criteria not met (1) · 📝 Nitpicks (2) · ✅ Fixed since last review (1)
✅ Merge score: 81/100
10 of 11 acceptance criteria met, 1 not met.
Most criteria remain satisfied by the cumulative diff, but two problems block a clean pass. (1) Criterion 5 requires the plain-env YAML file to be gitignored; the script writes `.
Prompt to fix all issues
You are helping fix PR "Add a script to copy Render's env into GCP" in devasignhq/agent. Automated review surfaced the items below — failed acceptance criteria and review findings. Each item states what was required, what's wrong with the current diff, and how to fix it; the embedded fix blocks include the expected behavior and the relevant diff hunk. Apply each fix so the item is resolved. Items tagged **Blocker** gate approval; the rest are advisory but worth addressing. Don't introduce changes beyond what's listed.
## End goal
A script migrates a Render service's environment variables into GCP, storing known secrets in Secret Manager and remaining variables in a Cloud Run env-vars YAML, without exposing values.
## Failed acceptance criteria
### 1. Required: Variables not classified as secrets are written to a Cloud Run env-vars YAML file that is gitignored. (5)
What's wrong now: Plain vars are written to envYamlPath = backend/deploy/gcp/.env.cloudrun.yaml (line 28/97). The criterion requires this file be gitignored. The diff contains no .gitignore entry covering .env.cloudrun.yaml (nor the .env.cloudrun.secrets sidecar). The backend/.dockerignore was updated but that only excludes from the image, not from git. I cannot confirm from the provided context that an existing .gitignore already ignores .env.cloudrun.yaml; given this was the previously-unmet criterion and no gitignore change was added, there is no positive evidence it is now satisfied.
How to fix:
**Suggested change** (`backend/deploy/gcp/.gitignore:1`):
```diff
+.env.cloudrun.yaml
+.env.cloudrun.secrets
+
```
Fix: Gitignore the generated Cloud Run env files
File: backend/deploy/gcp/.gitignore
Symbol: n/a
Issue:
import-render-env.mjs writes plaintext prod env values to .env.cloudrun.yaml and a secrets spec to .env.cloudrun.secrets under backend/deploy/gcp/. Neither is gitignored in this PR, so git will offer to commit real production secrets.
Expected behavior:
The generated env-vars YAML (and secrets spec) must be ignored by git so acceptance criterion 5 passes and no secret values can be committed.
Suggested approach:
Add a backend/deploy/gcp/.gitignore (or extend an existing .gitignore) containing `.env.cloudrun.yaml` and `.env.cloudrun.secrets`. Confirm `git check-ignore backend/deploy/gcp/.env.cloudrun.yaml` reports it as ignored.
Relevant diff:
```diff
+writeFileSync(envYamlPath, plain.map((k) => `${k}: ${JSON.stringify(vars.get(k))}\n`).join(""), { mode: 0o600 });
+writeFileSync(secretsSpecPath, secrets.map((k) => `${k}=${k}:latest`).join(",") + "\n");
```
## Your task
Work through every item above — the failed acceptance criteria and each review finding. For each one: understand the gap from "What's wrong now", implement the change so the Required behavior holds (each fix block's `Expected behavior` describes the target state), and use the `Relevant diff` hunks as the anchor for where to edit. After each change, re-verify it resolves the item. Treat **Blocker**-tagged items as required (they block approval); address the rest too.
The repo-root .env.* rule already covered them; this keeps the protection local to deploy/gcp so it doesn't depend on that rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
DevAsign Code Review
No issues found · ✅ Fixed since last review (3)
✅ Merge score: 100/100
11 of 11 acceptance criteria met.
All 11 acceptance criteria are satisfied. The script sources env vars from the Render API or a local file, classifies known secrets to Secret Manager (written via gcloud stdin with accessor grants to devasign-api), and writes non-secrets to a gitignored Cloud Run YAML.
Tests: 9 passed, 2 unverifiable — see the "Tests by DevAsign" comment.
Tests by DevAsign✅ 9 of 11 criteria verified by tests, 2 unverifiable. Each verdict below links to its evidence. 1 — The script sources the service's environment variables either from the Render API or from a local env file. (pass)Verdict: pass Both --from-file local sourcing and Render API sourcing (requiring RENDER_API_KEY) were exercised and passed. Test: 2 — Variables classified as known secrets are written to GCP Secret Manager. (unverifiable)Verdict: unverifiable The script errored at putSecret before the routing assertions ran; the failure stems from the fixture passing GCLOUD as a two-token command string it never resolves, not from misrouting secrets. Test: 3 — Secret values are passed to gcloud via stdin rather than as command-line arguments (argv). (pass)Verdict: pass Test confirmed secret values reach gcloud via stdin only and never via argv. Test: 4 — The devasign-api service account is granted accessor permission on the secrets written to Secret Manager. (unverifiable)Verdict: unverifiable The script errored at putSecret before reaching the grant step, so the add-iam-policy-binding assertions never executed; the failure is a GCLOUD-invocation issue in the fixture, not the accessor-grant claim. Test: 5 — Variables not classified as secrets are written to a Cloud Run env-vars YAML file that is gitignored. (pass)Verdict: pass Plain env var was written to .env.cloudrun.yaml and that filename is listed in .gitignore. Test: 6 — Environment variable values are never printed to output. (pass)Verdict: pass The --from-file --apply run never printed the secret or plain values it read. Test: 7 — When run without the --apply flag, the script only previews the classification and makes no changes to Secret Manager or the YAML output. (pass)Verdict: pass Preview run without --apply made zero gcloud calls and wrote no output files. Test: 8 — The deploy/ directory is excluded from the built image. (pass)Verdict: pass backend/.dockerignore excludes the deploy directory from the build context. Test: 9 — The npm test command runs the deploy classification tests (deploy/**/*.test.mjs) in addition to the existing src tests. (pass)Verdict: pass backend package.json test script includes both src and deploy test globs. Test: 10 — A drift-guard test fails if any secret-looking process.env variable read in backend/src is not classified as a secret (in SECRET_KEYS) or skipped. (pass)Verdict: pass Drift-guard test confirms every secret-looking process.env read in backend/src is classified as secret or skipped. Test: 11 — Unquoted env values have a trailing '# comment' stripped, while quoted values and values containing '=' are preserved intact. (pass)Verdict: pass Trailing hash comment stripped from unquoted values while quoted and equals-containing values are preserved. Test: |
Tests by DevAsign
The runner did not report results in time — every criterion is unverifiable for this push. 1 — The script sources the service's environment variables either from the Render API or from a local env file. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 2 — Variables classified as known secrets are written to GCP Secret Manager. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 3 — Secret values are passed to gcloud via stdin rather than as command-line arguments (argv). (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 4 — The devasign-api service account is granted accessor permission on the secrets written to Secret Manager. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 5 — Variables not classified as secrets are written to a Cloud Run env-vars YAML file that is gitignored. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 6 — Environment variable values are never printed to output. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 7 — When run without the --apply flag, the script only previews the classification and makes no changes to Secret Manager or the YAML output. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 8 — The deploy/ directory is excluded from the built image. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 9 — The npm test command runs the deploy classification tests (deploy/**/*.test.mjs) in addition to the existing src tests. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 10 — A drift-guard test fails if any secret-looking process.env variable read in backend/src is not classified as a secret (in SECRET_KEYS) or skipped. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: 11 — Unquoted env values have a trailing '# comment' stripped, while quoted values and values containing '=' are preserved intact. (unverifiable)Verdict: unverifiable the runner did not report results in time Test: |
Reads the service's variables from the Render API (or a local env file), writes known secrets to Secret Manager via gcloud stdin with accessor granted to the devasign-api service account, and writes the rest to a gitignored Cloud Run env-vars YAML. Values are never printed or put on argv; without --apply it only previews the classification.
deploy/ is excluded from the image.