Skip to content

feat(secrets): add apify secrets check + fail-fast on array-shaped environmentVariables#1265

Draft
DaveHanns wants to merge 1 commit into
apify:masterfrom
DaveHanns:fix/f87-secrets-check-preflight
Draft

feat(secrets): add apify secrets check + fail-fast on array-shaped environmentVariables#1265
DaveHanns wants to merge 1 commit into
apify:masterfrom
DaveHanns:fix/f87-secrets-check-preflight

Conversation

@DaveHanns

Copy link
Copy Markdown
Contributor

Summary

Two related issues currently make it easy to push an Actor whose @name secret references are silently broken, and there is no CLI command to check them before pushing:

  1. No standalone way to verify secret references. Nothing checks that @name references in .actor/actor.json resolve to locally-added secrets until apify push is deep inside transformEnvToEnvVars, and even that check can be silenced with --allow-missing-secrets.

  2. Array-shaped environmentVariables are silently accepted. environmentVariables is documented as a JSON object (key → value map), but a common mistake — especially when copy-pasting from the Apify API wire shape or from other tooling — is to write it as an array of { name, value } entries. The CLI accepts that shape without complaint and ships a build where every env-var value is the literal string [object Object].

This PR adds an apify secrets check preflight command and wires the shape check into apify push so both mistakes fail fast with actionable errors.

Reproducer for the silent-array bug

Given .actor/actor.json:

{
  "actorSpecification": 1,
  "name": "test-actor",
  "version": "0.0",
  "environmentVariables": [
    { "name": "MY_TOKEN", "value": "@mySecret" }
  ]
}

apify push today completes without any warning. On the platform, the build's env vars contain a single entry whose value at runtime is literally [object Object]. Tracing through src/lib/secrets.ts:

  • transformEnvToEnvVars calls Object.keys(env) on the array, iterating '0', '1', ...
  • For each numeric key, env[key] is an { name, value } object.
  • isSecretKey({ name, value }) coerces the object to [object Object], which does not match ^@., so the "secret" branch is never taken.
  • The whole object is passed through as envVars.push({ name: '0', value: {name, value} }).

Changes

  • New apify secrets check command (src/commands/secrets/check.ts) — scans .actor/actor.json for every @name reference, verifies each resolves to a locally stored secret, and validates the shape of environmentVariables. Supports --json for scripting and --dir for pointing at another Actor directory. Exits non-zero on any failure.

  • validateEnvironmentVariablesShape and findSecretReferences helpers in src/lib/secrets.ts. Both are used by the new command and by apify push's preflight. Rejects arrays, scalars, and objects with non-string values, and emits a diff-style error message that shows the exact fix.

  • apify push preflight — the shape check now runs after useActorConfig and before any upload. The push fails fast with CommandExitCodes.InvalidActorJson, so the agent/user sees the error immediately instead of a broken deployed Actor.

  • Unit tests for both new helpers, covering array / scalar / non-string-value failure modes and reference extraction.

Example error output

$ apify push
"environmentVariables" in .actor/actor.json must be a JSON object (key-value map), not an array.

Change it from:
  "environmentVariables": [{ "name": "MY_TOKEN", "value": "@mySecret" }]
to:
  "environmentVariables": { "MY_TOKEN": "@mySecret" }

See https://docs.apify.com/platform/actors/development/actor-definition/actor-json#fields for the expected schema.
$ apify secrets check
The following secrets referenced in .actor/actor.json are missing from local storage:
  - userApiToken  (referenced by environmentVariables.ACTOR_USER_API_TOKEN)

Add them by running:
  apify secrets add userApiToken <SECRET_VALUE>

Test plan

  • pnpm test:local — unit tests for validateEnvironmentVariablesShape and findSecretReferences all pass (15/15 in test/local/lib/secrets.test.ts).
  • tsc --noEmit — clean.
  • oxlint --type-aware on all touched files — clean.
  • oxfmt --check on all touched files — clean.
  • Manual: create an Actor with array-shaped environmentVariables and run apify push — should fail with the object-vs-array error before any upload.
  • Manual: create an Actor with "environmentVariables": { "TOKEN": "@doesNotExist" } and no local secret for doesNotExist, then run apify secrets check — should exit non-zero and print the "add" command.
  • Manual: with all @name references present locally, apify secrets check should print the green "all N secret reference(s) resolve" message.

Surfaced during an evaluation of Apify surfaces for agent-driven Actor development.

…environmentVariables

Two related issues currently make it easy to push an Actor whose
`@name` secret references are silently broken:

1. Nothing verifies that every `@name` referenced in
   `.actor/actor.json`'s `environmentVariables` actually exists in the
   local secrets store *before* `apify push` uploads and builds. The
   check that does exist inside `transformEnvToEnvVars` is inline in the
   push flow, gives no way to check standalone, and is easy to bypass
   with `--allow-missing-secrets`.

2. `environmentVariables` is documented as a JSON object (key → value
   map), but a common mistake — especially when copy-pasting the
   API-wire shape — is to write it as an array of `{ name, value }`
   entries:

       "environmentVariables": [
         { "name": "MY_TOKEN", "value": "@MySecret" }
       ]

   With that shape `transformEnvToEnvVars` iterates the array's
   numeric indices, `isSecretKey` gets called on the entry object
   (coerced to `[object Object]`), never matches, and the whole
   `{ name, value }` object is passed through as an env-var value.
   The build succeeds and the deployed Actor gets literal
   `[object Object]` at runtime. There is no error at any point.

Changes
-------

- Add `apify secrets check` command that scans `.actor/actor.json`
  for every `@name` reference, verifies each resolves to a locally
  stored secret, and validates that `environmentVariables` is an
  object. Supports `--json` for scripting and `--dir` to point at an
  Actor directory. Exits non-zero (InvalidActorJson) on any failure.

- Add `validateEnvironmentVariablesShape` and `findSecretReferences`
  helpers in `src/lib/secrets.ts`. Both are used by the new command
  and by `apify push`'s preflight.

- Wire the shape check into `apify push` preflight (before any files
  are uploaded), so the array-shaped mistake fails fast with a
  message that shows exactly how to fix it, instead of silently
  building a broken Actor.

- Unit tests for both new helpers, covering the array/scalar/
  non-string-value failure modes and the reference-extraction
  behaviour.

Surfaced during an evaluation of Apify surfaces for agent-driven Actor development.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@DaveHanns DaveHanns requested a review from l2ysho as a code owner July 4, 2026 23:11
@DaveHanns DaveHanns marked this pull request as draft July 5, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants