diff --git a/CHANGELOG.md b/CHANGELOG.md index b45f42e41d..49711fc13a 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. ([#4477](https://github.com/expo/eas-cli/pull/4477) by [@breken-ai](https://github.com/breken-ai)) + ### ๐Ÿงน 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')); } }