Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This is the log of notable changes to EAS CLI and related packages.

- [eas-cli] Fix workflow:logs for builds using built-in EAS build steps. ([#3523](https://github.com/expo/eas-cli/pull/3523) by [@douglowder](https://github.com/douglowder))
- [build-tools][worker] Read Expo app config with `expo config` CLI invocation before falling back to `@expo/config` ([#3536](https://github.com/expo/eas-cli/pull/3536) by [@kitten](https://github.com/kitten))
- Fix workflow detection for tracked `.gitignore`d native projects when `requireCommit` is disabled. ([#3561](https://github.com/expo/eas-cli/pull/3561) by [@sjchmiela](https://github.com/sjchmiela))

### 🧹 Chores

Expand Down Expand Up @@ -64,6 +65,8 @@ This is the log of notable changes to EAS CLI and related packages.
- Remove --environment flag requirement for update:configure. ([#3440](https://github.com/expo/eas-cli/pull/3440) by [@douglowder](https://github.com/douglowder))
- Fix login spinner interfering with prompts in `eas go` command when not logged in. ([#3451](https://github.com/expo/eas-cli/pull/3451) by [@byronkarlen](https://github.com/byronkarlen))

### 🧹 Chores

## [18.0.6](https://github.com/expo/eas-cli/releases/tag/v18.0.6) - 2026-02-27

## [18.0.5](https://github.com/expo/eas-cli/releases/tag/v18.0.5) - 2026-02-25
Expand Down
63 changes: 63 additions & 0 deletions packages/eas-cli/src/project/__tests__/workflow-git.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import spawnAsync from '@expo/spawn-async';
import { Platform, Workflow } from '@expo/eas-build-job';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';

import GitClient from '../../vcs/clients/git';
import { resolveWorkflowAsync } from '../workflow';

describe('resolveWorkflowAsync with GitClient', () => {
let repoRoot: string;

beforeEach(async () => {
repoRoot = await fs.realpath(
await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-workflow-test-'))
);
await spawnAsync('git', ['init'], { cwd: repoRoot });
await spawnAsync('git', ['config', 'user.email', 'test@example.com'], { cwd: repoRoot });
await spawnAsync('git', ['config', 'user.name', 'Test User'], { cwd: repoRoot });
});

afterEach(async () => {
await fs.rm(repoRoot, { recursive: true, force: true });
});

it('treats tracked gitignored native projects as managed when requireCommit is false', async () => {
const vcsClient = new GitClient({
requireCommit: false,
maybeCwdOverride: repoRoot,
});

await fs.mkdir(path.join(repoRoot, 'ios', 'app.xcodeproj'), { recursive: true });
await fs.writeFile(path.join(repoRoot, 'ios', 'app.xcodeproj', 'project.pbxproj'), 'fake');
await fs.writeFile(path.join(repoRoot, '.gitignore'), 'ios/\n');

await spawnAsync('git', ['add', '.gitignore'], { cwd: repoRoot });
await spawnAsync('git', ['add', '-f', 'ios/app.xcodeproj/project.pbxproj'], { cwd: repoRoot });
await spawnAsync('git', ['commit', '-m', 'test setup'], { cwd: repoRoot });

await expect(resolveWorkflowAsync(repoRoot, Platform.IOS, vcsClient)).resolves.toBe(
Workflow.MANAGED
);
});

it('treats tracked gitignored native projects as generic when requireCommit is true', async () => {
const vcsClient = new GitClient({
requireCommit: true,
maybeCwdOverride: repoRoot,
});

await fs.mkdir(path.join(repoRoot, 'ios', 'app.xcodeproj'), { recursive: true });
await fs.writeFile(path.join(repoRoot, 'ios', 'app.xcodeproj', 'project.pbxproj'), 'fake');
await fs.writeFile(path.join(repoRoot, '.gitignore'), 'ios/\n');

await spawnAsync('git', ['add', '.gitignore'], { cwd: repoRoot });
await spawnAsync('git', ['add', '-f', 'ios/app.xcodeproj/project.pbxproj'], { cwd: repoRoot });
await spawnAsync('git', ['commit', '-m', 'test setup'], { cwd: repoRoot });

await expect(resolveWorkflowAsync(repoRoot, Platform.IOS, vcsClient)).resolves.toBe(
Workflow.GENERIC
);
});
});
4 changes: 2 additions & 2 deletions packages/eas-cli/src/vcs/clients/__tests__/git.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ describe('git', () => {
await fs.rm(`${repoRoot}/.gitignore`);
});

it.each(testFiles.filter(file => file.gitignore && !file.commit))(
it.each(testFiles.filter(file => file.gitignore))(
'$filename should be ignored',
async file => {
expect(await vcs.isFileIgnoredAsync(file.filename)).toBe(true);
}
);

it.each(testFiles.filter(file => !file.gitignore || file.commit))(
it.each(testFiles.filter(file => !file.gitignore))(
'$filename should not be ignored',
async file => {
expect(await vcs.isFileIgnoredAsync(file.filename)).toBe(false);
Expand Down
12 changes: 9 additions & 3 deletions packages/eas-cli/src/vcs/clients/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,18 @@ export default class GitClient extends Client {
return wouldNotBeCopiedToClone && (!isTracked || wouldBeDeletedFromClone);
}

if (isTracked) {
return false; // Tracked files aren't ignored even if they match ignore patterns
if (isTracked && this.requireCommit) {
// With requireCommit=true we rely on a Git checkout, so tracked files are included
// even if they match ignore patterns.
return false;
}

try {
await spawnAsync('git', ['check-ignore', '-q', filePath], { cwd: rootPath });
await spawnAsync(
'git',
['check-ignore', '-q', ...(this.requireCommit ? [] : ['--no-index']), filePath],
{ cwd: rootPath }
);
return true;
} catch {
return false;
Expand Down
Loading