feat: detect `dockerContextDir` in `apify push` and refuse monorepo Actors before any write by DaveHanns · Pull Request #1502 · apify/apify-cli · GitHub
Skip to content

feat: detect dockerContextDir in apify push and refuse monorepo Actors before any write - #1502

Draft
DaveHanns wants to merge 2 commits into
masterfrom
feat/push-monorepo-preflight
Draft

DaveHanns wants to merge 2 commits into
masterfrom
feat/push-monorepo-preflight

Conversation

@DaveHanns

@DaveHanns DaveHanns commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

apify push uploads the Actor folder, so an Actor whose .actor/actor.json sets dockerContextDir to a directory above that folder (a monorepo Actor, as in apify/actor-monorepo-example) uploads a context the platform cannot build, and --force replaces a working Git source with that broken upload. This PR makes apify push read dockerContextDir right after actor.json and stop before any Actor read or write when the Actor is a monorepo Actor, with a message that points at the tracking Epic and at the Git source workaround. A context that is not a string, does not exist, lies outside the Git repository, or (outside Git) does not contain the Actor folder is reported as an invalid actor.json.

Part of #595 (stage 1) and apify/apify-core#28685.

Changes

  • New src/lib/docker-context.ts with resolveDockerContext, getContextFilePaths, readGitProvenance, the DockerContextResolution type, monorepoUnsupportedMessage, and the Epic URL constant, lifted from the actor-runtime branch.
  • apify push runs the resolver after reading actor.json and before the Actor lookup, the Git source prompt, and any write:
    • invalid: the message on stderr, exit code 5 (InvalidActorJson).
    • context: the refusal message on stderr, exit code 255 (NotImplemented).
    • none (no field, or a context at or below the Actor folder): an ordinary push.
  • With --json, stdout stays clean and the message goes to stderr, as the other preflight failures do.
  • No flag, argument, or description changed, so docs/ is unchanged (pnpm run update-docs produces no diff).

Tests

  • test/local/lib/docker-context.test.ts (new): the resolver in a temp Git repository (undefined, "", .., ., and ../src are none; ../../.. is context with the repository root and actors/x; a sibling directory in the repository is context; a context outside the repository, a missing directory, and a non-string value are invalid with the exact messages) and outside Git (a context containing the Actor is context; a sibling directory and a missing directory are invalid), including the resolve('/repo') drive-letter technique for Windows.
  • test/local/lib/docker-context-files.test.ts (new): tracked plus untracked-not-ignored files, a locally deleted file skipped, node_modules and storage dropped from untracked files only, .actorignore not applied inside Git, symlinks kept, and the no-Git fallback through getActorLocalFilePaths with a mocked node:child_process.
  • test/local/commands/push-monorepo.test.ts (new): a temp Git repository shaped like apify/actor-monorepo-example with a mocked client; the monorepo Actor stops with exit 255, the message on stderr, and no Actor get, version create or update, or key-value store call; --json keeps stdout clean; a context outside the repository stops with exit 5; ../src pushes the Actor folder's files as usual.
  • pnpm run lint, pnpm run format, and pnpm run build pass; pnpm run update-docs produces no diff. pnpm run test:local: 749 tests pass; the only failures are the two [python] fixture tests, which need Python 3.11 or higher and the machine has Python 3.9.6 (unrelated to this change).
  • Not run: pnpm run test:api (needs TEST_USER_TOKEN); no API-facing code changed.

Notes for reviewers

  • The helpers were lifted from the actor-runtime branch (feat: Add local Actor runtime (apify runtime) #1496) rather than waiting for a split, so that branch should merge master afterward (never rebase) and drop its copy of src/lib/runtime/source-context.ts in favor of this module. The runtime-only parts (pushActorRuntimeSourceContext, monorepoOutdatedRuntimeMessage, and createContextTarball) stay on that branch.
  • Semantic changes versus the branch: a context at or below the Actor folder (.., ., ../src) is an ordinary push, because the platform accepts it. The branch returned invalid for a context below the Actor folder. The branch also skipped the existence check outside Git; here a missing context is invalid everywhere.
  • Deviations from the issue checklist: inside Git, a context that does not contain the Actor folder but lies in the repository (a sibling directory) resolves to context with the repository root as the source, so it exits 255 rather than 5; the platform clone holds the whole repository, and stage 2 uploads that root either way. Outside Git, such a context is invalid (exit 5) as the issue says. The refusal message keeps the branch's text but replaces its last line (the runtime clause) with the Apify Console Git source hint, <repository URL>#<branch>:<folder>, instead of dropping it.
  • readGitProvenance and getContextFilePaths are not called by apify push yet; they land now so that stage 2 and the actor-runtime branch share one module.
  • Stage 2 (the upload of the repository root with actorPath) follows in a stacked PR once the platform accepts actorPath on SOURCE_FILES and TARBALL versions (apify/apify-core#31473).
  • No new dependencies; the module uses Node.js built-ins and existing helpers only.
  • Local run: Node.js 24.18.0 with pnpm 12.8.1 through Corepack (the volta pin is 24.21.0, which is not installed here).

🤖 Generated with Claude Code

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.

Make apify push work for monorepo Actors that set dockerContextDir

2 participants