Compare commits

...
Author SHA1 Message Date
evanbacon e9088dbb93 [eas-cli] Add changelog entry for non-interactive git fix 2026-02-07 12:56:02 -08:00
evanbacon f08392888d Update git.ts 2026-02-07 11:34:28 -08:00
evanbacon fbcc04eb62 Support nonInteractive in vcs ensureRepoExists
Add an optional nonInteractive parameter to Client.ensureRepoExistsAsync and implement it in GitClient. When nonInteractive is true, GitClient will auto-init the repo (git init), create an initial commit, and surface a single error with suggested git config commands if user.name/email are missing. Update callers across build/update/inspect/configure commands to pass the appropriate nonInteractive flag and adjust ensureRepoIsCleanAsync calls. Also resolve the workspace root for init operations and improve related log messages.
2026-02-07 11:32:51 -08:00
9 changed files with 48 additions and 21 deletions

No files matched your search

+4
View File
@@ -13,6 +13,10 @@ This is the log of notable changes to EAS CLI and related packages.
- Hide progress bar in build credits warning when usage reaches 100%. ([#3371](https://github.com/expo/eas-cli/pull/3371) by [@mackenco](https://github.com/mackenco))
### 🐛 Bug fixes
- Fix `--non-interactive` mode prompting for git initialization. Now auto-initializes git with an initial commit when needed. Set `EAS_NO_VCS=1` to skip. ([#3387](https://github.com/expo/eas-cli/pull/3387) by [@evanbacon](https://github.com/evanbacon))
### 🧹 Chores
- Upgrade `tar` to v7. ([#3327](https://github.com/expo/eas-cli/pull/3327) by [@KarolRzeminski](https://github.com/KarolRzeminski))
@@ -128,7 +128,7 @@ export async function runBuildAndSubmitAsync({
buildIds: string[];
buildProfiles?: ProfileData<'build'>[];
}> {
await vcsClient.ensureRepoExistsAsync();
await vcsClient.ensureRepoExistsAsync({ nonInteractive: flags.nonInteractive });
await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive);
await ensureProjectConfiguredAsync({
@@ -46,9 +46,7 @@ export default class BuildConfigure extends EasCommand {
'💡 The following process will configure your iOS and/or Android project to be compatible with EAS Build. These changes only apply to your local project files and you can safely revert them at any time.'
);
// BuildConfigure.ContextOptions.Vcs.client.getValueAsync()
await vcsClient.ensureRepoExistsAsync();
await vcsClient.ensureRepoExistsAsync({ nonInteractive: false });
const expoUpdatesIsInstalled = isExpoUpdatesInstalled(projectDir);
+2 -2
View File
@@ -74,8 +74,8 @@ export default class BuildDev extends EasCommand {
Errors.error('Running iOS builds in simulator is only supported on macOS.', { exit: 1 });
}
await vcsClient.ensureRepoExistsAsync();
await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive);
await vcsClient.ensureRepoExistsAsync({ nonInteractive: false });
await ensureRepoIsCleanAsync(vcsClient, false);
await ensureProjectConfiguredAsync({
projectDir,
nonInteractive: false,
@@ -94,7 +94,7 @@ export default class BuildInspect extends EasCommand {
await this.prepareOutputDirAsync(outputDirectory, flags.force);
if (flags.stage === InspectStage.ARCHIVE) {
await vcsClient.ensureRepoExistsAsync();
await vcsClient.ensureRepoExistsAsync({ nonInteractive: false });
await vcsClient.makeShallowCopyAsync(tmpWorkingdir);
await this.copyToOutputDirAsync(tmpWorkingdir, outputDirectory);
} else {
@@ -46,7 +46,7 @@ export default class UpdateConfigure extends EasCommand {
'💡 The following process will configure your project to use EAS Update. These changes only apply to your local project files and you can safely revert them at any time.'
);
await vcsClient.ensureRepoExistsAsync();
await vcsClient.ensureRepoExistsAsync({ nonInteractive: flags['non-interactive'] });
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
const easJsonCliConfig: EasJson['cli'] =
@@ -240,7 +240,7 @@ export default class UpdatePublish extends EasCommand {
enableJsonOutput();
}
await vcsClient.ensureRepoExistsAsync();
await vcsClient.ensureRepoExistsAsync({ nonInteractive });
await ensureRepoIsCleanAsync(vcsClient, nonInteractive);
const {
+36 -11
View File
@@ -31,7 +31,11 @@ export default class GitClient extends Client {
this.requireCommit = options.requireCommit;
}
public override async ensureRepoExistsAsync(): Promise<void> {
public override async ensureRepoExistsAsync(options?: {
nonInteractive?: boolean;
}): Promise<void> {
const nonInteractive = options?.nonInteractive ?? false;
try {
if (!(await isGitInstalledAsync())) {
Log.error(
@@ -66,11 +70,26 @@ export default class GitClient extends Client {
return;
}
const cwd = process.cwd();
const repoRoot = PackageManagerUtils.resolveWorkspaceRoot(cwd) ?? cwd;
if (nonInteractive) {
Log.log(
`No git repository found. Auto initializing in ${this.maybeCwdOverride ?? repoRoot}. ` +
`Set ${chalk.bold('EAS_NO_VCS=1')} to skip.`
);
await spawnAsync('git', ['init'], { cwd: this.maybeCwdOverride ?? repoRoot });
await this.commitAsync({
commitAllFiles: true,
commitMessage: 'Initial commit',
nonInteractive: true,
});
return;
}
Log.warn("It looks like you haven't initialized the git repository yet.");
Log.warn('EAS requires you to use a git repository for your project.');
const cwd = process.cwd();
const repoRoot = PackageManagerUtils.resolveWorkspaceRoot(cwd) ?? cwd;
const confirmInit = await confirmAsync({
message: `Would you like us to run 'git init' in ${
this.maybeCwdOverride ?? repoRoot
@@ -419,16 +438,22 @@ export default class GitClient extends Client {
return;
}
Log.warn(
`You need to configure Git with your ${[
!usernameConfigured && 'username (user.name)',
!emailConfigured && 'email address (user.email)',
const missingConfig = [
!usernameConfigured && 'username (user.name)',
!emailConfigured && 'email address (user.email)',
].filter(i => i);
Log.warn(`You need to configure Git with your ${missingConfig.join(' and ')}`);
if (nonInteractive) {
const configCommands = [
!usernameConfigured && 'git config --global user.name "Your Name"',
!emailConfigured && 'git config --global user.email "[email protected]"',
]
.filter(i => i)
.join(' and ')}`
);
if (nonInteractive) {
throw new Error('Git cannot be configured automatically in non-interactive mode');
.join(' && ');
throw new Error(
`Git ${missingConfig.join(' and ')} must be configured. Run: ${configCommands}`
);
}
if (!usernameConfigured) {
const { username } = await promptAsync({
+1 -1
View File
@@ -14,7 +14,7 @@ export abstract class Client {
// (optional) ensureRepoExistsAsync should verify whether repository exists and tooling is installed
// it's not required for minimal support, but lack of validation might cause the failure at a later stage.
public async ensureRepoExistsAsync(): Promise<void> {}
public async ensureRepoExistsAsync(_options?: { nonInteractive?: boolean }): Promise<void> {}
// (optional) checks whether commit is necessary before calling makeShallowCopyAsync
//