Skip to content

feat: let apify push select which Actors to push in a repository with several - #1504

Draft
DaveHanns wants to merge 6 commits into
feat/push-monorepo-preflightfrom
feat/push-select-actors
Draft

DaveHanns wants to merge 6 commits into
feat/push-monorepo-preflightfrom
feat/push-select-actors

Conversation

@DaveHanns

@DaveHanns DaveHanns commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

apify push at the root of a repository with Actors in subfolders (such as apify/actor-monorepo-example) stopped with A valid Actor could not be found and exit code 4, so pushing several Actors meant one invocation per folder. Now, when the resolved directory has no Actor config of its own but directories below it do, the command lists them and pushes what the user chose, one ordinary push per Actor, in sequence. A directory with its own config is pushed exactly as before.

Closes #1499. Part of apify/apify-core#28685.

Stacked on the preflight PR (#1502, branch feat/push-monorepo-preflight), so the per-Actor push it wraps already contains the dockerContextDir resolver.

Changes

  • src/commands/actors/push.ts: the single-Actor body moved into pushActor(dir) unchanged (same checks, exit codes, and --json envelope); run() enters selection mode only when the existing exit-4 check would fail and findActorDirs finds Actors below.
  • Selection: --all, or a repeatable --actor-dir <path> relative to the root (exclusive with --all). With neither flag, a multi-select prompt on stderr when stdin is a TTY and ci-info detects no CI; otherwise the listing (path and name) goes to stderr with Choose what to push: --all, or --actor-dir <path> for each Actor. and the command exits 4 before the login check, even for one Actor.
  • The positional actorId with more than one selected Actor exits 5 (InvalidInput). Pushes run in path order; a failure does not stop the rest, and the exit code is that of the first failed Actor. --version, --build-tag, and the other flags apply to every push.
  • --json from a root without its own config prints one document {ok, operation: "push", results: [{dir, ...single push result}], exitCode?}; an Actor that stopped before a build gets {dir, ok: false, exitCode}. Nothing else reaches stdout.
  • src/lib/find-actor-dirs.ts: finds every directory below the root with .actor/actor.json, sorted by path with / separators, skipping the hard-coded ignores of getActorLocalFilePaths (now exported as ACTOR_FILES_HARDCODED_IGNORE) and never descending below a found Actor.
  • src/lib/utils/inquirer/checkboxImpl.ts and useMultiSelectFromList: a multi-select prompt built on @inquirer/core next to confirmImpl.ts, wrapped by stdinCheckWrapper. No new dependency, so no install-size change.
  • Command framework: Flags.string({ multiple: true }) keeps every value of a repeated flag as string[]; other flags still get can only be specified once.
  • docs/reference.md regenerated with pnpm run update-docs.

Tests

  • test/local/commands/push-select.test.ts (mocked client, modeled on push-git-source.test.ts): every acceptance criterion of the issue, --version and --build-tag reaching each version payload, one FAILED build among SUCCEEDED ones (exit 1, per-entry exitCode), an Actor that throws before its build (the rest still pushed), a mocked prompt returning an empty selection, a root without Actors below keeping the exit-4 behavior, and a single-folder Actor unchanged in --json.
  • test/local/lib/find-actor-dirs.test.ts: skipped folders, no descent below an Actor, sort order, names, and / separators.
  • test/local/lib/command-framework/multiple-flag.test.ts: repeated, single, and omitted values of a multiple flag, and the unchanged rejection for a plain flag.
  • test/e2e/commands/push-select.test.ts through dist/apify.js: the listing with exit 4 and no token, --actor-dir to a non-Actor folder with exit 4, and --actor-dir to a folder outside the root with exit 4 and an empty stdout under --json.
  • Ran pnpm run lint, pnpm run format, pnpm run build, and pnpm run test:local (777 passed). The two failures are the [python] fixtures (python_support creates no .venv, spaces in path to actor writes no OUTPUT), which fail the same way on the base branch in this environment, so they are unrelated.
  • The interactive prompt was exercised by hand through a pseudo-terminal: arrow keys, space, enter, and an empty selection (results: [], exit 0).
  • Not run: the [api] case from the issue (two single-folder Actors with --all --json) needs TEST_USER_TOKEN, so it is not included; it is a follow-up for the test/api/commands/push.test.ts suite.

Notes for reviewers

  • The trigger is the existing exit-4 check (.actor/actor.json, actor.json, or the legacy apify.json), so a root that has its own config never enters selection mode, and a root without Actors below keeps today's error.
  • --actor-dir values are normalized (./actors/a/ becomes actors/a) and deduplicated; a value that resolves outside the root (../other, an absolute path elsewhere) or without a readable .actor/actor.json exits 4 before the login check.
  • Discovery uses readdir entries that are directories, so a symlink to a directory is not followed (no cycles); such an Actor can still be named with --actor-dir.
  • On the exit-4 paths in --json mode (no choice made, bad --actor-dir) nothing is printed on stdout, matching the single-Actor behavior for failures before a push. The results document is printed whenever a selection was made, including an empty one from the prompt.
  • Each selected push checks the login on its own, so without a token every entry reports exitCode: 1 and the loop continues; a single up-front check would have changed the --json output of that case.
  • multiple is meant for plain string flags; it is not combined with choices anywhere, and the choices check does not handle arrays.
  • Node.js 24.18.0 through nvm was used (.nvmrc pins 24.21.0; engines is >=22).

🤖 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.

2 participants