Skip to content

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

Open
DaveHanns wants to merge 47 commits into
masterfrom
feat/push-select-actors
Open

DaveHanns wants to merge 47 commits into
masterfrom
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 stopped with exit 4, so pushing several Actors meant one invocation per folder. Now, when the directory has no config file of its own but directories below it hold Actors, the command lists them and pushes the chosen ones, one ordinary push per Actor, in sequence. A single Actor is pushed with --dir, several with --all or the prompt. A directory with its own config is pushed as before.

Closes #1499. Part of apify/apify-core#28685. Stacked on #1502 (feat/push-monorepo-preflight).

Changes

  • Selection mode starts when none of .actor/actor.json, actor.json, or apify.json is in the directory itself; a root .actorignore or a .actor/ folder with only shared files does not count.
  • --all pushes every Actor found below the root, in path order. Without it, a multi-select prompt opens on a TTY outside CI; otherwise the listing goes to stderr with a hint and the command exits 4 before the login check. The hint points to --all or apify push --dir <path> for each Actor, and the listed paths are relative to where the command ran, so each one works as the --dir value.
  • No separate flag picks Actors, so --dir is the one way to push a single Actor.
  • --all in a directory that is an Actor itself exits 5.
  • Discovery skips the names getActorLocalFilePaths always ignores at every depth, does not follow symlinks or descend below a found Actor, and skips directories it cannot read.
  • An Actor in a gitignored directory is never pushed implicitly. --all skips it, names it on stderr with the apify push --dir <path> command that pushes it, and exits 4 when every Actor found is ignored. The prompt and the exit-4 listing still show it, labeled [ignored by .gitignore], and a picked one is pushed. Discovery checks each Actor directory with getGitignoreMatcher, the same check the upload uses, run once from the root. Skipped Actors are not entries in the --json results.
  • A failure does not stop the pushes after it; the exit code is that of the first failed Actor. A signal during a build wait aborts that build as before and then stops the loop, naming the Actors not pushed. A signal anywhere else ends the process as it does today, after naming the Actors not pushed; Ctrl+C at the Git-source question stops the loop.
  • A declined Git-source switch becomes {dir, ok: false, skipped: true} with no exit code and does not fail the run.
  • --json prints one document {ok, operation: "push", results: [{dir, ...single push result}], exitCode?} once a selection was made, also an empty one; ok is true when every entry is ok or skipped. On the exit-4 and exit-5 paths before a selection nothing is printed on stdout. The positional actorId applies to a single selected Actor only (exit 5 otherwise).
  • Telemetry records pushSelection: {selected, failed}.
  • The multi-select prompt is @inquirer/checkbox. Install size: the package adds 36 KB on disk (about 22 KB unpacked); its dependencies (@inquirer/core, ansi, figures, and type) are already installed for the select prompt.

Tests

  • Local: the gate, --all in an Actor directory, path order, failures mid-loop, the skipped entry, a signal during and outside a build wait, Ctrl+C at a prompt, the --json contract, the listed paths with --dir, discovery with an unreadable subdirectory and a missing root.
  • E2E: the exit-4 listing, from the root and with --dir actors.
  • [api]: two single-folder Actors with --all --json yield two SUCCEEDED entries (runs in CI).

Notes for reviewers

  • The prompt is the upstream @inquirer/checkbox; the push tests mock it, and a pseudo-terminal run confirmed the listing, the toggle, and an empty selection exiting 0.
  • Outside a build wait the loop's listener writes the not-pushed list (and, with --json, {ok: false, operation: "push", results} with the pushes so far) synchronously, removes itself and raises the signal again, so Node's default action ends the process with the same status as today.

🤖 Generated with Claude Code

…r push

The loop-level listener kept a signal from terminating the CLI anywhere in the selection, so a Ctrl+C during an upload let the current push run on. Outside a build wait the listener now names the Actors not pushed (and, with --json, prints the results so far), removes itself and raises the signal again, so Node's default action ends the process as it does for a single Actor. During a build wait the build is still aborted and the loop stops after it.
Ctrl+C at the Git-source question rejects the prompt, which ends a single push. In the loop the error was caught and the next Actor was pushed; the loop now stops and names the Actors not pushed.

@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 ❌ 13/16 — base-branch, max-files, max-lines
Review findings ✅ none
Reviewer escalation ❌ Adds new public CLI flags and a new --json output shape for apify push, changes the shared flag parser, and re-raises interrupt signals to end the process, so a person should confirm the contract and process-lifecycle changes.
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-files: 16 changed files, limit 10
  • max-lines: 1508 changed lines (+1467/-41), 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 824615776 · 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.

@DaveHanns DaveHanns linked an issue Oct 27, 2026 that may be closed by this pull request
7 tasks
Base automatically changed from feat/push-monorepo-preflight to master October 27, 2026 15:04
…tors

# Conflicts:
#	src/commands/actors/push.ts
#	src/lib/hooks/telemetry/trackEvent.ts
@apify-service-account apify-service-account added the tested Temporary label used only programatically for some analytics. label Oct 27, 2026
@apify-service-account apify-service-account added the t-builders Issues owned by the Builders team. label Oct 27, 2026
Comment thread src/commands/actors/push.ts Outdated
Comment thread src/commands/actors/push.ts Outdated
DaveHanns and others added 5 commits October 29, 2026 09:58
Co-authored-by: Edyta <142720610+szaganek@users.noreply.github.com>
Co-authored-by: Edyta <142720610+szaganek@users.noreply.github.com>

@l2ysho l2ysho 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.

pre-approved 🚀

Comment thread docs/reference.md Outdated
Comment on lines +876 to +884
--actor-dir=<value> Push the Actor in this
subdirectory. The path is relative to the directory
you push from. To push several Actors, repeat the
flag. Actors are pushed in path order. Works only
from a directory without an Actor config of its
own.
--all Push every Actor found
in the subdirectories. Works only from a directory
without an Actor config of its own.

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.

Note: (nonTTY) As user/agent can use both --dir and also --actor-dir it may be confusing. Example from my testing

# Command Result
1 push --dir actors/alpha pushed alpha
2 push --actor-dir actors/alpha same target, Pushing the Actor in actors/alpha
3 push --dir actors --actor-dir alpha same target again, printed as alpha
4 push --dir actors/alpha --actor-dir actors/alpha error, exit 5

So there are at least 3 ways how to do the same push. Maybe guide user/agent to use more specific --dir instead of introducing new --actor-dir is better idea?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. --actor-dir is gone in 8dff92a. A single Actor is now pushed with --dir, several with --all or the interactive picker, and the non-interactive message points to --dir with paths that work from where the command ran.

* `getActorLocalFilePaths` always ignores (at any depth, not only at the root) and does not descend below a
* found Actor. A directory that cannot be read is skipped; when that is the root, nothing is found.
*/
export async function findActorDirs(root: string): Promise<FoundActorDir[]> {

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.

Suggestion: this is corner case but findActorDirs should do similar gitignore check as getActorLocalFilePaths as now actors in gitignore are pushed

mkdir -p /tmp/f1min/ignored/.actor
cd /tmp/f1min
git init -q .
echo 'ignored/' > .gitignore
echo '{"actorSpecification":1,"name":"ghost","version":"0.0"}' > ignored/.actor/actor.json
git add -A
git commit -qm init
apify push --all < /dev/null

One Actor could be pushed three ways (--dir, --actor-dir, or both),
which confuses users and agents. --all and the interactive prompt stay.
Without a terminal and without --all, the listing now points to --dir
and shows paths that work as its value from where the command ran.
Restores the command framework to its master state, including the
inline invalid-choice error and the short-form = handling, which were
reshaped only for repeated values.
The exported ignore list had been inserted between the comment and the
function it describes.

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

t-builders Issues owned by the Builders team. tested Temporary label used only programatically for some analytics.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

apify push: select which Actors to push in a repository with several

5 participants