Skip to content

Commit b00b30e

Browse files
committed
[build-tools] Auto-upload embedded bundle after build when EAS_UPDATE_EXPERIMENTAL_UPLOAD_EMBEDDED_BUNDLE is set
1 parent 4375d57 commit b00b30e

4 files changed

Lines changed: 120 additions & 0 deletions

File tree

packages/build-tools/src/builders/android.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ import {
3030
import { uploadApplicationArchive } from '../utils/artifacts';
3131
import {
3232
configureExpoUpdatesIfInstalledAsync,
33+
isEASUpdateConfigured,
3334
resolveRuntimeVersionForExpoUpdatesIfConfiguredAsync,
3435
} from '../utils/expoUpdates';
36+
import { uploadEmbeddedBundleAsync } from '../utils/expoUpdatesEmbedded';
3537
import { Hook, runHookIfPresent } from '../utils/hooks';
3638
import { prepareExecutableAsync } from '../utils/prepareBuildExecutable';
3739

@@ -208,6 +210,15 @@ async function buildAsync(ctx: BuildContext<Android.Job>): Promise<void> {
208210
});
209211
});
210212

213+
if (
214+
ctx.env.EAS_UPDATE_EXPERIMENTAL_UPLOAD_EMBEDDED_BUNDLE &&
215+
(await isEASUpdateConfigured(ctx))
216+
) {
217+
await ctx.runBuildPhase(BuildPhase.UPLOAD_EMBEDDED_BUNDLE, async () => {
218+
await uploadEmbeddedBundleAsync(ctx);
219+
});
220+
}
221+
211222
await ctx.runBuildPhase(BuildPhase.SAVE_CACHE, async () => {
212223
if (ctx.isLocal) {
213224
ctx.logger.info('Local builds do not support saving cache.');

packages/build-tools/src/builders/ios.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import { saveCcacheAsync } from '../steps/functions/saveBuildCache';
2323
import { uploadApplicationArchive } from '../utils/artifacts';
2424
import {
2525
configureExpoUpdatesIfInstalledAsync,
26+
isEASUpdateConfigured,
2627
resolveRuntimeVersionForExpoUpdatesIfConfiguredAsync,
2728
} from '../utils/expoUpdates';
29+
import { uploadEmbeddedBundleAsync } from '../utils/expoUpdatesEmbedded';
2830
import { Hook, runHookIfPresent } from '../utils/hooks';
2931
import { prepareExecutableAsync } from '../utils/prepareBuildExecutable';
3032
import { getParentAndDescendantProcessPidsAsync } from '../utils/processes';
@@ -206,6 +208,15 @@ async function buildAsync(ctx: BuildContext<Ios.Job>): Promise<void> {
206208
});
207209
});
208210

211+
if (
212+
ctx.env.EAS_UPDATE_EXPERIMENTAL_UPLOAD_EMBEDDED_BUNDLE &&
213+
(await isEASUpdateConfigured(ctx))
214+
) {
215+
await ctx.runBuildPhase(BuildPhase.UPLOAD_EMBEDDED_BUNDLE, async () => {
216+
await uploadEmbeddedBundleAsync(ctx);
217+
});
218+
}
219+
209220
await ctx.runBuildPhase(BuildPhase.SAVE_CACHE, async () => {
210221
if (ctx.isLocal) {
211222
ctx.logger.info('Local builds do not support saving cache.');
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { Android, BuildJob, Ios, Platform } from '@expo/eas-build-job';
2+
import { PipeMode } from '@expo/logger';
3+
import fs from 'fs-extra';
4+
import os from 'os';
5+
import path from 'path';
6+
import StreamZip from 'node-stream-zip';
7+
8+
import { findArtifacts } from './artifacts';
9+
import { runEasCliCommand } from './easCli';
10+
import { resolveArtifactPath } from '../ios/resolve';
11+
import { BuildContext } from '../context';
12+
13+
export async function uploadEmbeddedBundleAsync(ctx: BuildContext<BuildJob>): Promise<void> {
14+
const { platform } = ctx.job;
15+
const channel = ctx.job.updates?.channel;
16+
const projectDir = ctx.getReactNativeProjectDirectory();
17+
18+
const archivePattern =
19+
platform === Platform.IOS
20+
? resolveArtifactPath(ctx as BuildContext<Ios.Job>)
21+
: ((ctx as BuildContext<Android.Job>).job.applicationArchivePath ??
22+
'android/app/build/outputs/**/*.{apk,aab}');
23+
24+
const [archivePath] = await findArtifacts({
25+
rootDir: projectDir,
26+
patternOrPath: archivePattern,
27+
logger: null,
28+
}).catch(() => [] as string[]);
29+
30+
if (!channel || !archivePath) {
31+
ctx.logger.warn(
32+
`Skipping embedded bundle upload: ${!channel ? 'no channel configured for this build profile' : 'build archive not found'}.`
33+
);
34+
ctx.markBuildPhaseHasWarnings();
35+
return;
36+
}
37+
38+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-embedded-bundle-'));
39+
const zip = new StreamZip.async({ file: archivePath });
40+
try {
41+
const entries = Object.values(await zip.entries());
42+
const bundleEntry = entries.find(e =>
43+
platform === Platform.IOS
44+
? e.name.endsWith('/main.jsbundle')
45+
: e.name === 'assets/index.android.bundle'
46+
);
47+
const manifestEntry = entries.find(e =>
48+
platform === Platform.IOS
49+
? e.name.includes('EXUpdates.bundle/app.manifest')
50+
: e.name === 'assets/app.manifest'
51+
);
52+
53+
if (!bundleEntry || !manifestEntry) {
54+
ctx.logger.warn('Skipping embedded bundle upload: bundle or manifest not found in archive.');
55+
ctx.markBuildPhaseHasWarnings();
56+
return;
57+
}
58+
59+
const bundleName = platform === Platform.IOS ? 'main.jsbundle' : 'index.android.bundle';
60+
const bundlePath = path.join(tmpDir, bundleName);
61+
const manifestPath = path.join(tmpDir, 'app.manifest');
62+
await zip.extract(bundleEntry.name, bundlePath);
63+
await zip.extract(manifestEntry.name, manifestPath);
64+
65+
const args = [
66+
'update:embedded:upload',
67+
'--platform',
68+
platform,
69+
'--bundle',
70+
bundlePath,
71+
'--manifest',
72+
manifestPath,
73+
'--channel',
74+
channel,
75+
'--non-interactive',
76+
];
77+
if (ctx.env.EAS_BUILD_ID) {
78+
args.push('--build-id', ctx.env.EAS_BUILD_ID);
79+
}
80+
await runEasCliCommand({
81+
args,
82+
options: {
83+
cwd: projectDir,
84+
env: ctx.env,
85+
logger: ctx.logger,
86+
mode: PipeMode.STDERR_ONLY_AS_STDOUT,
87+
},
88+
});
89+
} catch (err: any) {
90+
ctx.logger.warn({ err }, 'Failed to upload embedded bundle.');
91+
ctx.markBuildPhaseHasWarnings();
92+
} finally {
93+
await zip.close();
94+
}
95+
}

packages/eas-build-job/src/logs.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export enum BuildPhase {
2727
*/
2828
UPLOAD_ARTIFACTS = 'UPLOAD_ARTIFACTS',
2929
UPLOAD_APPLICATION_ARCHIVE = 'UPLOAD_APPLICATION_ARCHIVE',
30+
UPLOAD_EMBEDDED_BUNDLE = 'UPLOAD_EMBEDDED_BUNDLE',
3031
UPLOAD_BUILD_ARTIFACTS = 'UPLOAD_BUILD_ARTIFACTS',
3132
PREPARE_ARTIFACTS = 'PREPARE_ARTIFACTS',
3233
CLEAN_UP_CREDENTIALS = 'CLEAN_UP_CREDENTIALS',
@@ -91,6 +92,7 @@ export const buildPhaseDisplayName: Record<BuildPhase, string> = {
9192
[BuildPhase.CACHE_STATS]: 'Cache stats',
9293
[BuildPhase.UPLOAD_ARTIFACTS]: 'Upload artifacts',
9394
[BuildPhase.UPLOAD_APPLICATION_ARCHIVE]: 'Upload application archive',
95+
[BuildPhase.UPLOAD_EMBEDDED_BUNDLE]: 'Upload embedded bundle',
9496
[BuildPhase.UPLOAD_BUILD_ARTIFACTS]: 'Upload build artifacts',
9597
[BuildPhase.PREPARE_ARTIFACTS]: 'Prepare artifacts',
9698
[BuildPhase.CLEAN_UP_CREDENTIALS]: 'Clean up credentials',
@@ -157,6 +159,7 @@ export const buildPhaseWebsiteId: Record<BuildPhase, string> = {
157159
[BuildPhase.CACHE_STATS]: 'cache-stats',
158160
[BuildPhase.UPLOAD_ARTIFACTS]: 'upload-artifacts',
159161
[BuildPhase.UPLOAD_APPLICATION_ARCHIVE]: 'upload-application-archive',
162+
[BuildPhase.UPLOAD_EMBEDDED_BUNDLE]: 'upload-embedded-bundle',
160163
[BuildPhase.UPLOAD_BUILD_ARTIFACTS]: 'upload-build-artifacts',
161164
[BuildPhase.PREPARE_ARTIFACTS]: 'prepare-artifacts',
162165
[BuildPhase.CLEAN_UP_CREDENTIALS]: 'clean-up-credentials',

0 commit comments

Comments
 (0)