Skip to content

feat: validate actor.json paths before apify push uploads anything - #1503

Open
DaveHanns wants to merge 17 commits into
feat/push-monorepo-preflightfrom
feat/push-validate-actor-json-paths
Open

DaveHanns wants to merge 17 commits into
feat/push-monorepo-preflightfrom
feat/push-validate-actor-json-paths

Conversation

@DaveHanns

@DaveHanns DaveHanns commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

apify push checks none of the file-path fields in .actor/actor.json. The platform resolves them after the upload: a missing schema fails the build, and a missing dockerfile, readme, or changelog only logs a warning and falls back to the default, so the build goes green with the wrong image or README. This PR applies the worker's rules in the CLI before the Actor is looked up or written.

Closes #1500. Part of apify/apify-core#28685. Stacked on #1502: the check runs after the dockerContextDir resolver.

Changes

  • New src/lib/actor-paths.ts: checkActorPaths returns one problem (field, value, resolved path, reason) per failing field; formatActorPathProblems builds the message. push.ts prints it and exits with code 5.
  • Fields: dockerfile, readme, changelog, input, output, webServerSchema, the storages schemas, plus inputSchema and outputSchema. The worker copies a set alias over input or output, so the alias wins and the base key is skipped. Inline objects and empty strings (unset on the worker) are skipped.
  • Rules: no backslash (the platform resolves paths with forward slashes), relative to .actor/, inside the upload root, an existing file, and among the files push uploads. dockerfile, readme, and changelog match case-insensitively, as the worker finds them by a directory listing; schemas match exactly.
  • Symlinks count by their own path: push reads through a link and uploads the content there, so a link to a file outside the upload root passes when push sends it.
  • When dockerContextDir points inside the Actor folder, dockerfile must lie inside that context: the worker runs BuildKit with --local dockerfile=<context dir> and filename=<Dockerfile relative to the context>, so a Dockerfile outside it cannot be read by the build. This goes beyond the issue's checklist, because the same constraint applies to today's push.

Tests

  • test/local/lib/actor-paths.test.ts: every rule, the aliases, empty strings, backslashes, symlinks (a dangling one and a symlinked Actor folder included), and the case mismatch reason.
  • test/local/commands/push-actor-paths.test.ts: wrong paths stop push with exit code 5 before any Actor read or write, --json keeps stdout clean, and valid paths push as usual.
  • Lint, format, build, update-docs (no change), and test:local pass, except the two [python] fixture tests that need a newer Python.

Notes for reviewers

  • The helper returns a list instead of throwing, so stage 2 of monorepo push can pass the repository root as uploadRoot.
  • Roots are real paths, as the resolver returns them, so a symlinked --dir is not mistaken for an escape.

🤖 Generated with Claude Code

push reads through a symlink and uploads the content at the link's own
path, so a link to a file outside the upload root builds on the platform.
The realpath escape rule refused it; the uploaded files rule already
rejects anything push does not send.
The worker copies inputSchema over input and outputSchema over output
before resolving, so a schema path under the alias was never checked.
The worker treats an empty string as an unset field, so an empty value
was wrongly reported as the .actor folder being a directory. A backslash
passes path.resolve on Windows but is a filename character on the Linux
worker, so it is refused before any resolution.
The lib test now builds its fixture through useTempPath, as the other
lib tests do, adds a changelog file so the case-insensitive match is
checked for all three listed fields, and covers the reason given when
the uploaded file differs only in case. The command test restores the
real process.exitCode next to the mocked copy.
checkActorPaths resolves the roots and builds the posix set of uploaded
files once and hands them to every field, instead of each field
recomputing them.
@DaveHanns
DaveHanns marked this pull request as ready for review October 26, 2026 15:46
@DaveHanns
DaveHanns requested a review from l2ysho as a code owner October 26, 2026 15:46

@apify-factory apify-factory left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, nothing blocking.

🏭 Factory review — ℹ️ not auto-approved

This change was reviewed but cannot be auto-approved: a gate failed, so the review cannot end in an approval.

check result
Static gates ❌ 14/16 — base-branch, max-lines
Review findings ✅ none
Reviewer escalation ❌ The new check makes apify push fail on paths that used to push fine, based on platform worker rules this repo can't confirm (for example, a missing readme or dockerfile used to fall back to the default).
Juror 1 — claude-sonnet-5-5, adversarial ⏭️ skipped — a gate failed
Why it was not auto-approved

Failed gates:

  • base-branch: base is feat/push-monorepo-preflight, required master
  • max-lines: 646 changed lines (+644/-2), limit 200

The comments above are the review itself; this section only explains the approval decision. Push a change and request @apify-factory again to re-run it, or ask a human to review.

Reviewed e3edc7960 · workflow run

@apify-factory apify-factory left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, nothing blocking.

This branch has not been deployed

No deployments
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.

3 participants