Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
d71b158
feat: add the dockerContextDir resolver and Docker context file colle…
DaveHanns Oct 16, 2026
d49e15e
feat: refuse monorepo Actors in apify push before any platform write
DaveHanns Oct 16, 2026
0691db6
feat: let a string flag opt into keeping every value of a repeat
DaveHanns Oct 16, 2026
f4a8f16
feat: add Actor directory discovery and a multi-select prompt
DaveHanns Oct 16, 2026
6826533
feat: let apify push select which Actors to push in a repository with…
DaveHanns Oct 16, 2026
e457250
docs: regenerate the reference for the push selection flags
DaveHanns Oct 16, 2026
ab27ec9
fix: reject an --actor-dir outside the pushed directory before the lo…
DaveHanns Oct 16, 2026
bdbfdd1
fix: print the Actor config path with a slash in the --actor-dir error
DaveHanns Oct 16, 2026
8312f66
fix: resolve the Actor directory's real path and require a directory …
DaveHanns Oct 26, 2026
feb2cec
fix: return forward-slash paths from the Docker context file collector
DaveHanns Oct 26, 2026
b2ee951
fix: name the Actor folder in the monorepo refusal when actor.json ha…
DaveHanns Oct 26, 2026
d1fec8d
test: cover readGitProvenance and tidy the monorepo push test
DaveHanns Oct 26, 2026
ec9866d
Merge remote-tracking branch 'origin/feat/push-monorepo-preflight' in…
DaveHanns Oct 26, 2026
db2cb8f
refactor: split the Docker context resolution into none, within, outs…
DaveHanns Oct 26, 2026
f728886
test: cover the none and within Docker context kinds
DaveHanns Oct 26, 2026
53cc81a
Merge remote-tracking branch 'origin/feat/push-monorepo-preflight' in…
DaveHanns Oct 26, 2026
a9f2d19
refactor: rename readGitProvenance to readGitState
DaveHanns Oct 26, 2026
cff722b
Merge remote-tracking branch 'origin/feat/push-monorepo-preflight' in…
DaveHanns Oct 26, 2026
13e2da9
fix: skip directories Actor discovery cannot read
DaveHanns Oct 26, 2026
c10f77e
fix: clear the readline buffer after each key in the checkbox prompt
DaveHanns Oct 26, 2026
060f196
fix: strip the short-form = and validate choices for multiple flags
DaveHanns Oct 26, 2026
750bbe4
fix: decide selection mode on the directory's own config files and ho…
DaveHanns Oct 26, 2026
345c77f
fix: stop pushing the remaining Actors after an interrupt signal
DaveHanns Oct 26, 2026
e70478f
fix: report a declined Git-source switch as skipped in the push results
DaveHanns Oct 26, 2026
695af7f
feat: push the --actor-dir directories in path order
DaveHanns Oct 26, 2026
ea97585
feat: record the Actor selection in push telemetry
DaveHanns Oct 26, 2026
4b8a207
test: cover --all against the platform and the --all with --actor-dir…
DaveHanns Oct 26, 2026
e11dfb7
test: pin the empty stdout of a rejected actorId argument under --json
DaveHanns Oct 26, 2026
e093880
fix: end the process on a signal outside a build wait in a multi-Acto…
DaveHanns Oct 26, 2026
8246157
fix: stop a multi-Actor push after Ctrl+C at a prompt
DaveHanns Oct 26, 2026
50fb1b8
docs: explain why the Docker context is judged by its path as written
DaveHanns Oct 27, 2026
1af9abf
test: pin the Docker context classification through symlinks
DaveHanns Oct 27, 2026
29263e8
Merge remote-tracking branch 'origin/feat/push-monorepo-preflight' in…
DaveHanns Oct 27, 2026
944b6ae
fix: return the Docker context path as the upload lays it out
DaveHanns Oct 27, 2026
1ccb11a
Merge remote-tracking branch 'origin/feat/push-monorepo-preflight' in…
DaveHanns Oct 27, 2026
d19ed44
Merge remote-tracking branch 'origin/master' into feat/push-select-ac…
DaveHanns Oct 27, 2026
ddd1c35
test: expect the Console build URL without the hash in push selection…
DaveHanns Oct 27, 2026
39dc8cb
refactor: use @inquirer/checkbox for the Actor multi-select
DaveHanns Oct 27, 2026
f7d846d
Update src/commands/actors/push.ts
DaveHanns Oct 29, 2026
86c4d0b
Update src/commands/actors/push.ts
DaveHanns Oct 29, 2026
2171ff4
Merge remote-tracking branch 'origin/master' into feat/push-select-ac…
DaveHanns Oct 30, 2026
0765904
style: wrap the --all flag description to satisfy the formatter
DaveHanns Oct 30, 2026
68675e1
docs: regenerate the command reference
DaveHanns Oct 30, 2026
a781184
Merge remote-tracking branch 'origin/master' into feat/push-select-ac…
DaveHanns Oct 30, 2026
8dff92a
refactor: drop --actor-dir from apify push in favor of --dir
DaveHanns Oct 30, 2026
0170178
refactor: remove repeatable flag support that only --actor-dir used
DaveHanns Oct 30, 2026
2eb1d9c
style: keep the getActorLocalFilePaths doc comment on the function
DaveHanns Oct 30, 2026
c27e026
refactor: move the gitignore check of apify push into getGitignoreMat…
DaveHanns Oct 30, 2026
6cf42bf
feat: skip Actors in gitignored directories with apify push --all
DaveHanns Oct 30, 2026
f1f2874
docs: regenerate the command reference
DaveHanns Oct 30, 2026
857bfef
fix: don't crash Actor discovery without git when an Actor has its ow…
DaveHanns Oct 30, 2026
06d5afb
fix: quote a path with spaces in the apify push --dir advice of --all
DaveHanns Oct 30, 2026
677262b
fix: leave out every gitignored file when apify push collects files
DaveHanns Oct 30, 2026
6174b72
fix: leave out files below an ignored symlink and ignored NFD names
DaveHanns Oct 30, 2026
df104ff
fix: rank ignore rules as git does when apify push reads them itself
DaveHanns Oct 30, 2026
46a7ac4
fix: read the rules when an ignored Actor directory holds a tracked file
DaveHanns Oct 30, 2026
5b40b8b
test: keep the apify-cli .gitignore out of the no-git ignore tests
DaveHanns Oct 30, 2026
9924c9a
Merge remote-tracking branch 'origin/master' into fix/push-ignore-rules
DaveHanns Nov 3, 2026
fb56e81
test: create a nested test directory on Windows too
DaveHanns Nov 3, 2026
f9962c2
fix: apply the ignore rules of a directory pushed through a symlink b…
DaveHanns Nov 3, 2026
e220b1e
test: cover how the push reads the ignore rules itself when git can't…
DaveHanns Nov 3, 2026
fb9dc6e
test: cover the git listing and the exclude files the push reads, wit…
DaveHanns Nov 3, 2026
3bfe6d0
test: cover the directory checks of Actor discovery, with git isolate…
DaveHanns Nov 3, 2026
a89dac4
test: pin the ignore matcher edge cases and isolate git in the push t…
DaveHanns Nov 3, 2026
879676c
fix: find no files in a pushed directory that doesn't exist, instead …
DaveHanns Nov 3, 2026
c9e661d
fix: skip a .gitignore the push can't read, as git does
DaveHanns Nov 3, 2026
78ffb23
fix: apply no global excludes file when core.excludesFile is empty, a…
DaveHanns Nov 3, 2026
1c3b589
fix: drop the line that ignores a pushed directory whose name is stor…
DaveHanns Nov 3, 2026
f9c5d82
test: check by its reads that the push skips .gitignore files inside …
DaveHanns Nov 3, 2026
0ed7509
test: cover a decomposed .gitignore line that ignores a composed push…
DaveHanns Nov 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/lib/find-actor-dirs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ export async function findActorDirs(root: string): Promise<FoundActorDir[]> {
return [];
}

const isGitignored = await getGitignoreMatcher(root);
const isGitignored = await getGitignoreMatcher(
root,
found.map((actor) => `${actor.dir}/`),
);

return found
.map((actor) => ({ ...actor, ignored: isGitignored(`${actor.dir}/`) }))
Expand Down
233 changes: 156 additions & 77 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { execSync } from 'node:child_process';
import { createWriteStream, existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
import { createWriteStream, existsSync, readdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
import { mkdir, readFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { dirname, join, relative, resolve } from 'node:path';
import { dirname, join, relative, resolve, sep } from 'node:path';
import process from 'node:process';

import { DurationFormatter as SapphireDurationFormatter, TimeTypes } from '@sapphire/duration';
Expand Down Expand Up @@ -289,99 +289,178 @@ export const createSourceFiles = async (paths: string[], cwd: string) => {
};

/**
* Fallback for when git is unavailable: find all .gitignore files and build a matcher
* using the `ignore` package, scoped to each file's directory.
* Also walks ancestor directories to pick up parent .gitignore files (e.g. monorepo root),
* stopping at the first .git boundary found.
* Output of a git command run in `cwd`, or `null` when git fails (not installed, not a repository, refuses to run) or
* exits with an error code.
*/
const getGitignoreFallbackMatcher = async (cwd: string): Promise<(path: string) => boolean> => {
const gitOutput = (args: string, cwd: string, input?: string): string | null => {
try {
return execSync(`git ${args}`, {
cwd,
input,
encoding: 'utf-8',
stdio: [input === undefined ? 'ignore' : 'pipe', 'pipe', 'ignore'],
});
} catch {
return null;
}
};

const readFileOrEmpty = async (path: string) => readFile(path, 'utf-8').catch(() => '');

/** `to` relative to `from`, with `/` separators, as ignore rules and the paths they match use. */
const posixRelative = (from: string, to: string) => relative(from, to).split(sep).join('/');

/**
* Lines of an ignore file in a directory above `cwd`, where `cwdPath` leads from that directory to `cwd`. When they
* ignore `cwd` or a directory between, the lines that do are dropped, so the files inside still get the other rules.
* Pushing an ignored directory is explicit, as `apify push --dir` is.
*/
const keepLinesForCwd = (content: string, cwdPath: string): string[] => {
// In NFC, as the final matcher compares them, so a line in another Unicode form than the path still counts.
const lines = content.normalize('NFC').split(/\r?\n/);
const cwdDir = `${cwdPath}/`.normalize('NFC');
if (!cwdPath || !makeIg().add(lines).ignores(cwdDir)) {
return lines;
}

return lines.filter((line) => !makeIg().add(line).ignores(cwdDir));
};

/**
* Rewrites the lines of an ignore file in the directory `dir` to match paths relative to the directory `dir` is relative
* to, as git scopes them: a pattern with a slash before its end is anchored to `dir`, one without matches at any depth
* below it.
*/
const scopeLines = (lines: string[], dir: string): string[] => {
if (!dir) {
return lines;
}

// The directory starts every rewritten pattern, so its glob characters, and a leading ! or #, are escaped.
const base = dir.replace(/[\\*?[\]]/g, '\\$&').replace(/^[!#]/, '\\$&');
return lines.flatMap((line) => {
// Git drops trailing spaces unless a backslash escapes them.
const rule = line.replace(/(?<!\\) +$/, '');
const negation = rule.startsWith('!') ? '!' : '';
const pattern = rule.slice(negation.length);
if (!pattern || rule.startsWith('#')) {
return [];
}

const anchored = pattern.slice(0, -1).includes('/');
return [`${negation}${base}/${anchored ? pattern.replace(/^\//, '') : `**/${pattern}`}`];
});
};

/**
* Paths of the global excludes file (core.excludesFile, by default $XDG_CONFIG_HOME/git/ignore) and .git/info/exclude,
* whose rules apply from the repository root `root`. Empty when git fails.
*/
const getGitExcludeFilePaths = (cwd: string, root: string): string[] => {
const infoExclude = gitOutput('rev-parse --git-path info/exclude', cwd)?.trim();
if (!infoExclude) {
return [];
}

// An empty core.excludesFile turns the global excludes file off.
const globalExcludes =
gitOutput('config --path --get core.excludesFile', cwd)?.trim() ??
join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'git', 'ignore');

return [...(globalExcludes ? [resolve(root, globalExcludes)] : []), resolve(cwd, infoExclude)];
};

/**
* Reads the ignore rules itself, for when git can't answer: git fails, or `cwd` is itself ignored. Applies every
* .gitignore inside `cwd` and every one above it up to and including the repository root, the first directory with a
* .git, so an outer repository's rules don't apply. When git names them, also applies .git/info/exclude and the global
* excludes file, as `git ls-files --exclude-standard` does. In the files above `cwd`, the lines that ignore `cwd` are
* dropped.
*
* The rules rank as git ranks them. A deeper .gitignore wins over a shallower one, so its `!` lines re-include files.
* Every .gitignore wins over .git/info/exclude, which wins over the global excludes file. A file below an ignored
* directory stays ignored.
*/
const getGitignoreRulesMatcher = async (typedCwd: string): Promise<(path: string) => boolean> => {
// Git applies the rules by the real path, so a directory pushed through a symlink gets the ancestors and the
// .git/info/exclude path git uses. A directory that doesn't exist holds no files, and the push says so.
const cwd = existsSync(typedCwd) ? realpathSync(typedCwd) : typedCwd;

// Directories above cwd from the top down: the repository root, or the filesystem root when there's none.
const ancestors: string[] = [];
let root = cwd;
while (!existsSync(join(root, '.git')) && root !== dirname(root)) {
root = dirname(root);
ancestors.unshift(root);
}

const gitignoreFiles = await glob('**/.gitignore', {
dot: true,
cwd,
ignore: ['.git/**'],
expandDirectories: false,
});
gitignoreFiles.sort((a, b) => a.split('/').length - b.split('/').length);

const filters: { dir: string; ig: Ignore; ancestorPrefix?: string }[] = [];

for (const gitignoreFile of gitignoreFiles) {
const gitignoreDir = dirname(gitignoreFile); // e.g. 'src' or '.'
const content = await readFile(join(cwd, gitignoreFile), 'utf-8');
filters.push({ dir: gitignoreDir === '.' ? '' : gitignoreDir, ig: makeIg().add(content) });
}

// Walk ancestor directories to pick up parent .gitignore files (e.g. monorepo root).
// Check for a .git boundary FIRST so we stop before processing the git root's own
// .gitignore — that file is handled by `git ls-files` when git is available, and
// avoids accidentally applying rules from an unrelated outer repository.
let parentDir = dirname(cwd);
while (parentDir !== dirname(parentDir)) {
if (existsSync(join(parentDir, '.git'))) {
break;
}

const parentGitignorePath = join(parentDir, '.gitignore');
if (existsSync(parentGitignorePath)) {
try {
const content = await readFile(parentGitignorePath, 'utf-8');
// Paths passed to this filter are relative to cwd. To test them against
// a .gitignore that lives above cwd we need to prepend the relative path
// from the ancestor dir to cwd so the ignore patterns see the right scope.
const ancestorPrefix = relative(parentDir, cwd);
filters.push({ dir: '', ig: makeIg().add(content), ancestorPrefix });
} catch {
// Ignore read errors
}
}
// One `ignore` instance holds every line, scoped to match paths relative to root. The last matching line wins, so
// the files go in from the lowest rank to the highest.
const cwdPath = posixRelative(root, cwd);
const lines: string[] = [];
for (const excludeFile of getGitExcludeFilePaths(cwd, root)) {
lines.push(...keepLinesForCwd(await readFileOrEmpty(excludeFile), cwdPath));
}

parentDir = dirname(parentDir);
for (const dir of ancestors) {
const content = await readFileOrEmpty(join(dir, '.gitignore'));
lines.push(...scopeLines(keepLinesForCwd(content, posixRelative(dir, cwd)), posixRelative(root, dir)));
}

if (filters.length === 0) {
return () => false;
// Git skips a .gitignore it can't read, with a warning.
for (const gitignoreFile of gitignoreFiles) {
const content = await readFileOrEmpty(join(cwd, gitignoreFile));
lines.push(...scopeLines(content.split(/\r?\n/), posixRelative(root, join(cwd, dirname(gitignoreFile)))));
}

return (filePath) =>
filters.some(({ dir, ig, ancestorPrefix }) => {
let relativePath: string | null;
if (!dir) {
relativePath = ancestorPrefix ? `${ancestorPrefix}/${filePath}` : filePath;
} else if (filePath.startsWith(`${dir}/`)) {
relativePath = filePath.slice(dir.length + 1);
} else {
relativePath = null;
}
// An empty path is the directory of this .gitignore, which it can't ignore. `ignore` throws on it.
return !!relativePath && ig.ignores(relativePath);
});
// Names are compared in NFC, as git on macOS prints them, whatever form they have on disk.
const ig = makeIg().add(lines.map((line) => line.normalize('NFC')));
return (path) => ig.ignores((cwdPath ? `${cwdPath}/${path}` : path).normalize('NFC'));
};

/**
* Returns a function that says whether .gitignore rules exclude a path relative to `cwd`. A directory path needs a
* trailing slash. Asks `git ls-files`, which handles ancestor and nested .gitignore files, .git/info/exclude, and the
* global gitignore config. When git fails (not installed, not a repository), reads the .gitignore files itself.
* Returns a function that says whether ignore rules exclude a path relative to `cwd`. A directory path needs a trailing
* slash. Asks `git ls-files`, which applies ancestor and nested .gitignore files, .git/info/exclude, and the global
* excludes file. When git fails, or the rules ignore `cwd` itself, reads the rules itself.
* `getActorLocalFilePaths` and the Actor discovery of `apify push` both use it. Discovery asks from the root it
* searches, the upload from the Actor directory.
* searches, the upload from the Actor directory. Discovery names the directories it asks about in `dirs`, with a
* trailing slash: git lists a directory that holds tracked files file by file, even when the rules ignore it, so the
* rules decide about those directories directly.
*/
export const getGitignoreMatcher = async (cwd: string): Promise<(path: string) => boolean> => {
try {
const gitIgnored = execSync('git ls-files --others --ignored --exclude-standard --directory', {
cwd,
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'ignore'],
})
.split('\n')
.filter(Boolean);

if (gitIgnored.length === 0) {
return () => false;
}

const ig = makeIg().add(gitIgnored);
return (path) => ig.ignores(path);
} catch {
return getGitignoreFallbackMatcher(cwd);
}
export const getGitignoreMatcher = async (cwd: string, dirs: string[] = []): Promise<(path: string) => boolean> => {
// Git can't re-include files below an ignored directory, so it can't say which files the rules inside an ignored cwd
// exclude. Asked from inside, it lists only `./`, or each untracked file when cwd holds tracked ones.
const cwdIgnored = gitOutput('check-ignore -q --no-index ./', cwd) !== null;
// -z prints names as they are. Without it, git quotes names with special characters.
const listed = cwdIgnored
? null
: gitOutput('ls-files -z --others --ignored --exclude-standard --directory', cwd)?.split('\0').filter(Boolean);

if (!listed || listed.includes('./')) {
return getGitignoreRulesMatcher(cwd);
}

// The rules decide about the directories the caller names. Exit code 1 means they ignore none of them.
const ignoredDirs = dirs.length
? (gitOutput('check-ignore --no-index -z --stdin', cwd, dirs.join('\0'))?.split('\0') ?? [])
: [];

// Each entry is an exact path, not a pattern, and covers everything below it. A directory entry ends with `/`, but a
// symlink to a directory doesn't. Names are compared in NFC, as git on macOS prints them.
const ignored = new Set([...listed, ...ignoredDirs].map((entry) => entry.normalize('NFC')));
const prefixes = listed.map((entry) => (entry.endsWith('/') ? entry : `${entry}/`).normalize('NFC'));
return (path) => {
const name = path.normalize('NFC');
return ignored.has(name) || prefixes.some((prefix) => name.startsWith(prefix));
};
};

interface ActorIgnoreResult {
Expand Down
43 changes: 43 additions & 0 deletions test/__setup__/hooks/useIsolatedGit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { mkdirSync, writeFileSync } from 'node:fs';

import { useTempPath } from './useTempPath.js';

/**
* Gives git an empty home, XDG config directory and global config for the tests of a file, so the developer's own
* settings and global excludes file don't apply. `name` names the temp directory that holds them, so it must be unique
* per test file.
*/
export function useIsolatedGit(name: string) {
const gitHome = useTempPath(name, {
create: true,
remove: true,
cwd: false,
cwdParent: false,
});

/** Points git, and `os.homedir()`, at another home or XDG config directory. Left out, each is the empty one. */
const stubGitHome = (home = gitHome.joinPath('home'), xdgConfigHome = gitHome.joinPath('xdg')) => {
// os.homedir() reads USERPROFILE on Windows and HOME elsewhere. Git reads HOME everywhere.
vitest.stubEnv('HOME', home);
vitest.stubEnv('USERPROFILE', home);
vitest.stubEnv('XDG_CONFIG_HOME', xdgConfigHome);
vitest.stubEnv('GIT_CONFIG_GLOBAL', gitHome.joinPath('gitconfig'));
vitest.stubEnv('GIT_CONFIG_NOSYSTEM', '1');
};

beforeAll(async () => {
await gitHome.beforeAllCalls();

mkdirSync(gitHome.joinPath('home'), { recursive: true });
mkdirSync(gitHome.joinPath('xdg'), { recursive: true });
writeFileSync(gitHome.joinPath('gitconfig'), '');
stubGitHome();
});

afterAll(async () => {
vitest.unstubAllEnvs();
await gitHome.afterAllCalls();
});

return { stubGitHome };
}
5 changes: 5 additions & 0 deletions test/local/commands/push-actor-paths.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { testRunCommand } from '../../../src/lib/command-framework/apify-command
import { CommandExitCodes } from '../../../src/lib/consts.js';
import { cwdCache } from '../../../src/lib/hooks/useActorConfig.js';
import { useConsoleSpy } from '../../__setup__/hooks/useConsoleSpy.js';
import { useIsolatedGit } from '../../__setup__/hooks/useIsolatedGit.js';
import { useTempPath } from '../../__setup__/hooks/useTempPath.js';

const actName = 'push-actor-paths-actor';
Expand Down Expand Up @@ -55,6 +56,10 @@ const { beforeAllCalls, afterAllCalls, joinPath, tmpPath } = useTempPath(actName
cwdParent: false,
});

// The Actor folder sits in test/tmp, which the apify-cli .gitignore ignores, so the push reads the ignore rules itself,
// the global excludes file included.
useIsolatedGit('push-actor-paths-git-home');

const { logMessages } = useConsoleSpy();

const { ActorsPushCommand } = await import('../../../src/commands/actors/push.js');
Expand Down
4 changes: 4 additions & 0 deletions test/local/commands/push-monorepo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CommandExitCodes } from '../../../src/lib/consts.js';
import { MONOREPO_PLATFORM_SUPPORT_ISSUE_URL } from '../../../src/lib/docker-context.js';
import { cwdCache } from '../../../src/lib/hooks/useActorConfig.js';
import { useConsoleSpy } from '../../__setup__/hooks/useConsoleSpy.js';
import { useIsolatedGit } from '../../__setup__/hooks/useIsolatedGit.js';
import { useTempPath } from '../../__setup__/hooks/useTempPath.js';

const actName = 'push-monorepo-actor';
Expand Down Expand Up @@ -61,6 +62,9 @@ const { beforeAllCalls, afterAllCalls, joinPath, tmpPath, forceNewCwd } = useTem
cwdParent: false,
});

// Git reads the global excludes file, so the developer's own one could leave out the files these tests push.
useIsolatedGit('push-monorepo-git-home');

const { logMessages } = useConsoleSpy();

const { ActorsPushCommand } = await import('../../../src/commands/actors/push.js');
Expand Down
5 changes: 5 additions & 0 deletions test/local/commands/push-select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { testRunCommand } from '../../../src/lib/command-framework/apify-command
import { CommandExitCodes, LOCAL_CONFIG_PATH } from '../../../src/lib/consts.js';
import { cwdCache } from '../../../src/lib/hooks/useActorConfig.js';
import { useConsoleSpy } from '../../__setup__/hooks/useConsoleSpy.js';
import { useIsolatedGit } from '../../__setup__/hooks/useIsolatedGit.js';
import { useTempPath } from '../../__setup__/hooks/useTempPath.js';

// Whether the terminal is interactive is decided by `useStdin` and `ci-info`; both are under test control.
Expand Down Expand Up @@ -142,6 +143,10 @@ const { beforeAllCalls, afterAllCalls, joinPath, forceNewCwd } = useTempPath('pu
cwdParent: false,
});

// The Actors sit in test/tmp, which the apify-cli .gitignore ignores, so the push reads the ignore rules itself, the
// global excludes file included.
useIsolatedGit('push-select-git-home');

const { logMessages } = useConsoleSpy();

const { ActorsPushCommand } = await import('../../../src/commands/actors/push.js');
Expand Down
Loading
Loading