From 7cbd59e6a10a203bcad4895d71b491742ea0bf64 Mon Sep 17 00:00:00 2001 From: breken-ai <312387581+breken-ai@users.noreply.github.com> Date: Fri, 25 Sep 2026 21:15:30 -0700 Subject: [PATCH 1/2] [eas-cli] Write credentials.json files into the project directory updateFileAsync removed the file at the project-relative path but then created and wrote the new file relative to process.cwd(). Running `eas credentials` from a subdirectory of the project deleted the local keystore, certificate or provisioning profile and wrote the downloaded copy into the working directory, leaving credentials.json pointing at a missing file. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 2 + .../credentialsJson/__tests__/update-test.ts | 44 +++++++++++++++++++ .../src/credentials/credentialsJson/update.ts | 4 +- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b45f42e41d..a3810d4d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ This is the log of notable changes to EAS CLI and related packages. ### ๐Ÿ› Bug fixes +- [eas-cli] Write credentials downloaded to `credentials.json` into the project directory instead of the current working directory, so running `eas credentials` from a subdirectory no longer deletes the existing keystore, certificate or provisioning profile. + ### ๐Ÿงน Chores ## [24.8.0](https://github.com/expo/eas-cli/releases/tag/v24.8.0) - 2026-09-24 diff --git a/packages/eas-cli/src/credentials/credentialsJson/__tests__/update-test.ts b/packages/eas-cli/src/credentials/credentialsJson/__tests__/update-test.ts index 9d926cf206..8b51b2d8cf 100644 --- a/packages/eas-cli/src/credentials/credentialsJson/__tests__/update-test.ts +++ b/packages/eas-cli/src/credentials/credentialsJson/__tests__/update-test.ts @@ -1,5 +1,6 @@ import fs from 'fs-extra'; import { vol } from 'memfs'; +import path from 'path'; import prompts from 'prompts'; import { IosDistributionType } from '../../../graphql/generated'; @@ -115,6 +116,26 @@ describe('update credentials.json', () => { expect(keystore).toEqual('c29tZWJpbmFyeWRhdGE='); // base64 "somebinarydata" expect(newCredJson).toEqual(credJson); }); + it('should write the keystore to the project directory when it is not the current directory', async () => { + const projectDir = '/app'; + const ctx = createCtxMock({ projectDir }); + vol.fromJSON({ + '/app/credentials.json': JSON.stringify({ + android: { + keystore: { + keystorePath: 'keystore.jks', + keystorePassword: 'keystorePassword', + keyAlias: 'keyAlias', + keyPassword: 'keyPassword', + }, + }, + }), + '/app/keystore.jks': 'somebinarydata', + }); + await updateAndroidCredentialsAsync(ctx, testLegacyAndroidBuildCredentialsFragment); + expect(await fs.readFile('/app/keystore.jks', 'base64')).toEqual(testKeystore.keystore); + expect(await fs.pathExists(path.join(process.cwd(), 'keystore.jks'))).toBe(false); + }); it('should update keystore and credentials.json if android part of credentials.json is not valid', async () => { const ctx = createCtxMock(); vol.fromJSON({ @@ -257,6 +278,29 @@ describe('update credentials.json', () => { }, }); }); + it('should write ios credentials to the project directory when it is not the current directory', async () => { + const ctx = createCtxMock({ + projectDir: '/app', + ios: { + ...getNewIosApiMock(), + getIosAppCredentialsWithCommonFieldsAsync: jest.fn( + () => testCommonIosAppCredentialsFragment + ), + }, + }); + vol.fromJSON({ '/app/package.json': '{}' }); + const app = await getAppFromContextAsync(ctx); + + await updateIosCredentialsAsync(ctx, app, targets, IosDistributionType.AppStore); + + expect(await fs.readFile('/app/credentials/ios/dist-cert.p12', 'base64')).toEqual( + testDistCertFragmentNoDependencies.certificateP12 + ); + expect(await fs.readFile('/app/credentials/ios/profile.mobileprovision', 'base64')).toEqual( + testProvisioningProfileFragment.provisioningProfile + ); + expect(await fs.pathExists(path.join(process.cwd(), 'credentials'))).toBe(false); + }); it('should not do anything if no credentials are returned from www', async () => { const ctx = createCtxMock({ ios: { diff --git a/packages/eas-cli/src/credentials/credentialsJson/update.ts b/packages/eas-cli/src/credentials/credentialsJson/update.ts index 0ed7c2170d..9d2560f243 100644 --- a/packages/eas-cli/src/credentials/credentialsJson/update.ts +++ b/packages/eas-cli/src/credentials/credentialsJson/update.ts @@ -291,8 +291,8 @@ async function updateFileAsync( await fs.remove(absolutePath); } if (base64Data) { - await fs.mkdirp(path.dirname(filePath)); - await fs.writeFile(filePath, Buffer.from(base64Data, 'base64')); + await fs.mkdirp(path.dirname(absolutePath)); + await fs.writeFile(absolutePath, Buffer.from(base64Data, 'base64')); } } From c0b0af5d17c5e3e585840e9d704c753c4fb8da34 Mon Sep 17 00:00:00 2001 From: breken-ai <312387581+breken-ai@users.noreply.github.com> Date: Fri, 25 Sep 2026 21:16:43 -0700 Subject: [PATCH 2/2] [eas-cli] Link changelog entry to #4477 Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3810d4d81..49711fc13a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ This is the log of notable changes to EAS CLI and related packages. ### ๐Ÿ› Bug fixes -- [eas-cli] Write credentials downloaded to `credentials.json` into the project directory instead of the current working directory, so running `eas credentials` from a subdirectory no longer deletes the existing keystore, certificate or provisioning profile. +- [eas-cli] Write credentials downloaded to `credentials.json` into the project directory instead of the current working directory, so running `eas credentials` from a subdirectory no longer deletes the existing keystore, certificate or provisioning profile. ([#4477](https://github.com/expo/eas-cli/pull/4477) by [@breken-ai](https://github.com/breken-ai)) ### ๐Ÿงน Chores