@expo/build-tools 20.0.0 → 20.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,6 +22,7 @@ const saveBuildCache_1 = require("../steps/functions/saveBuildCache");
22
22
  const gradleConfig_1 = require("../steps/utils/android/gradleConfig");
23
23
  const artifacts_1 = require("../utils/artifacts");
24
24
  const expoUpdates_1 = require("../utils/expoUpdates");
25
+ const expoUpdatesEmbedded_1 = require("../utils/expoUpdatesEmbedded");
25
26
  const hooks_1 = require("../utils/hooks");
26
27
  const prepareBuildExecutable_1 = require("../utils/prepareBuildExecutable");
27
28
  async function androidBuilder(ctx) {
@@ -177,6 +178,11 @@ async function buildAsync(ctx) {
177
178
  logger: ctx.logger,
178
179
  });
179
180
  });
181
+ if (ctx.env.EAS_UPDATE_EXPERIMENTAL_UPLOAD_EMBEDDED_BUNDLE) {
182
+ await ctx.runBuildPhase(eas_build_job_1.BuildPhase.UPLOAD_EMBEDDED_BUNDLE, async () => {
183
+ await (0, expoUpdatesEmbedded_1.uploadEmbeddedBundleAsync)(ctx);
184
+ });
185
+ }
180
186
  await ctx.runBuildPhase(eas_build_job_1.BuildPhase.SAVE_CACHE, async () => {
181
187
  if (ctx.isLocal) {
182
188
  ctx.logger.info('Local builds do not support saving cache.');
@@ -26,6 +26,7 @@ const restoreBuildCache_1 = require("../steps/functions/restoreBuildCache");
26
26
  const saveBuildCache_1 = require("../steps/functions/saveBuildCache");
27
27
  const artifacts_1 = require("../utils/artifacts");
28
28
  const expoUpdates_1 = require("../utils/expoUpdates");
29
+ const expoUpdatesEmbedded_1 = require("../utils/expoUpdatesEmbedded");
29
30
  const hooks_1 = require("../utils/hooks");
30
31
  const prepareBuildExecutable_1 = require("../utils/prepareBuildExecutable");
31
32
  const processes_1 = require("../utils/processes");
@@ -192,6 +193,11 @@ async function buildAsync(ctx) {
192
193
  logger: ctx.logger,
193
194
  });
194
195
  });
196
+ if (ctx.env.EAS_UPDATE_EXPERIMENTAL_UPLOAD_EMBEDDED_BUNDLE) {
197
+ await ctx.runBuildPhase(eas_build_job_1.BuildPhase.UPLOAD_EMBEDDED_BUNDLE, async () => {
198
+ await (0, expoUpdatesEmbedded_1.uploadEmbeddedBundleAsync)(ctx);
199
+ });
200
+ }
195
201
  await ctx.runBuildPhase(eas_build_job_1.BuildPhase.SAVE_CACHE, async () => {
196
202
  if (ctx.isLocal) {
197
203
  ctx.logger.info('Local builds do not support saving cache.');
package/dist/datadog.d.ts CHANGED
@@ -1,7 +1,14 @@
1
+ type MetricsTarget = {
2
+ kind: 'build';
3
+ turtleBuildId: string;
4
+ } | {
5
+ kind: 'jobRun';
6
+ turtleJobRunId: string;
7
+ };
1
8
  type DatadogSetupOptions = {
2
9
  expoApiV2BaseUrl: string;
3
- turtleBuildId: string;
4
10
  robotAccessToken: string;
11
+ target: MetricsTarget;
5
12
  };
6
13
  export declare const Datadog: {
7
14
  setup(opts: DatadogSetupOptions | null): void;
package/dist/datadog.js CHANGED
@@ -13,7 +13,7 @@ exports.Datadog = {
13
13
  if (!setupOptions) {
14
14
  return;
15
15
  }
16
- const { expoApiV2BaseUrl, turtleBuildId, robotAccessToken } = setupOptions;
16
+ const { expoApiV2BaseUrl, robotAccessToken, target } = setupOptions;
17
17
  const metrics = [
18
18
  {
19
19
  name,
@@ -22,14 +22,17 @@ exports.Datadog = {
22
22
  tags,
23
23
  },
24
24
  ];
25
- const uploadPromise = (0, turtleFetch_1.turtleFetch)(new URL(`turtle-builds/${turtleBuildId}/metrics`, expoApiV2BaseUrl).toString(), 'POST', {
25
+ const metricsPath = target.kind === 'build'
26
+ ? `turtle-builds/${target.turtleBuildId}/metrics`
27
+ : `turtle-job-runs/${target.turtleJobRunId}/metrics`;
28
+ const uploadPromise = (0, turtleFetch_1.turtleFetch)(new URL(metricsPath, expoApiV2BaseUrl).toString(), 'POST', {
26
29
  json: { metrics },
27
30
  headers: {
28
31
  Authorization: `Bearer ${robotAccessToken}`,
29
32
  },
30
33
  retries: 2,
31
34
  }).catch(err => {
32
- sentry_1.Sentry.capture('Failed to report turtle build metric', err, {
35
+ sentry_1.Sentry.capture(`Failed to report turtle ${target.kind} metric`, err, {
33
36
  extras: { metrics },
34
37
  });
35
38
  });
@@ -39,26 +42,29 @@ exports.Datadog = {
39
42
  if (!setupOptions) {
40
43
  return;
41
44
  }
42
- const { expoApiV2BaseUrl, turtleBuildId, robotAccessToken } = setupOptions;
43
- const log = {
44
- buildId: turtleBuildId,
45
- message,
46
- tags,
47
- };
48
- const uploadPromise = (0, turtleFetch_1.turtleFetch)(new URL('turtle-builds/logs', expoApiV2BaseUrl).toString(), 'POST', {
45
+ const { expoApiV2BaseUrl, robotAccessToken, target } = setupOptions;
46
+ const log = target.kind === 'build'
47
+ ? { buildId: target.turtleBuildId, message, tags }
48
+ : { jobRunId: target.turtleJobRunId, message, tags };
49
+ const logsPath = target.kind === 'build' ? 'turtle-builds/logs' : 'turtle-job-runs/logs';
50
+ const uploadPromise = (0, turtleFetch_1.turtleFetch)(new URL(logsPath, expoApiV2BaseUrl).toString(), 'POST', {
49
51
  json: log,
50
52
  headers: {
51
53
  Authorization: `Bearer ${robotAccessToken}`,
52
54
  },
53
55
  shouldThrowOnNotOk: false,
54
56
  }).catch(err => {
55
- sentry_1.Sentry.capture('Failed to report turtle build log', err, {
57
+ sentry_1.Sentry.capture(`Failed to report turtle ${target.kind} log`, err, {
56
58
  extras: { log },
57
59
  });
58
60
  });
59
61
  pendingUploads.push(uploadPromise);
60
62
  },
61
63
  async flushAsync() {
62
- await Promise.allSettled(pendingUploads);
64
+ // Rotate so each flush only awaits its own batch; uploads enqueued during the
65
+ // await land in the fresh array for a later flush.
66
+ const uploads = pendingUploads;
67
+ pendingUploads = [];
68
+ await Promise.allSettled(uploads);
63
69
  },
64
70
  };
@@ -52,6 +52,12 @@ function createStartIosSimulatorBuildFunction() {
52
52
  deviceIdentifier: originalDeviceIdentifier,
53
53
  env,
54
54
  });
55
+ try {
56
+ await IosSimulatorUtils_1.IosSimulatorUtils.disableApsdAsync({ udid, env });
57
+ }
58
+ catch (err) {
59
+ logger.warn({ err }, 'Failed to disable apsd in the Simulator.');
60
+ }
55
61
  await IosSimulatorUtils_1.IosSimulatorUtils.waitForReadyAsync({ udid, env });
56
62
  logger.info('');
57
63
  const device = await IosSimulatorUtils_1.IosSimulatorUtils.getDeviceAsync({ udid, env });
@@ -76,6 +82,12 @@ function createStartIosSimulatorBuildFunction() {
76
82
  deviceIdentifier: cloneDeviceName,
77
83
  env,
78
84
  });
85
+ try {
86
+ await IosSimulatorUtils_1.IosSimulatorUtils.disableApsdAsync({ udid: cloneUdid, env });
87
+ }
88
+ catch (err) {
89
+ logger.warn({ err }, 'Failed to disable apsd in the Simulator.');
90
+ }
79
91
  await IosSimulatorUtils_1.IosSimulatorUtils.waitForReadyAsync({
80
92
  udid: cloneUdid,
81
93
  env,
@@ -135,7 +135,7 @@ async function startServeSimWithTunnelAsync({ baseDomain, env, logger, timeoutMs
135
135
  '--stream-quality',
136
136
  '0.55',
137
137
  '--codec',
138
- 'h264',
138
+ 'webrtc',
139
139
  ],
140
140
  env,
141
141
  });
@@ -49,6 +49,10 @@ export declare namespace IosSimulatorUtils {
49
49
  udid: IosSimulatorUuid;
50
50
  env: NodeJS.ProcessEnv;
51
51
  }): Promise<void>;
52
+ export function disableApsdAsync({ udid, env, }: {
53
+ udid: IosSimulatorUuid;
54
+ env: NodeJS.ProcessEnv;
55
+ }): Promise<void>;
52
56
  export function collectLogsAsync({ deviceIdentifier, env, }: {
53
57
  deviceIdentifier: IosSimulatorName | IosSimulatorUuid;
54
58
  env: NodeJS.ProcessEnv;
@@ -77,6 +77,11 @@ var IosSimulatorUtils;
77
77
  });
78
78
  }
79
79
  IosSimulatorUtils.waitForReadyAsync = waitForReadyAsync;
80
+ async function disableApsdAsync({ udid, env, }) {
81
+ await (0, turtle_spawn_1.default)('xcrun', ['simctl', 'spawn', udid, 'launchctl', 'disable', 'system/com.apple.apsd'], { env });
82
+ await (0, turtle_spawn_1.default)('xcrun', ['simctl', 'spawn', udid, 'launchctl', 'bootout', 'system/com.apple.apsd'], { env });
83
+ }
84
+ IosSimulatorUtils.disableApsdAsync = disableApsdAsync;
80
85
  async function collectLogsAsync({ deviceIdentifier, env, }) {
81
86
  const outputDir = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'ios-simulator-logs-'));
82
87
  const outputPath = node_path_1.default.join(outputDir, `${deviceIdentifier}.logarchive`);
@@ -0,0 +1,3 @@
1
+ import { BuildJob } from '@expo/eas-build-job';
2
+ import { BuildContext } from '../context';
3
+ export declare function uploadEmbeddedBundleAsync(ctx: BuildContext<BuildJob>): Promise<void>;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.uploadEmbeddedBundleAsync = uploadEmbeddedBundleAsync;
7
+ const eas_build_job_1 = require("@expo/eas-build-job");
8
+ const logger_1 = require("@expo/logger");
9
+ const results_1 = require("@expo/results");
10
+ const fs_extra_1 = __importDefault(require("fs-extra"));
11
+ const os_1 = __importDefault(require("os"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const node_stream_zip_1 = __importDefault(require("node-stream-zip"));
14
+ const artifacts_1 = require("./artifacts");
15
+ const easCli_1 = require("./easCli");
16
+ const resolve_1 = require("../ios/resolve");
17
+ const expoUpdates_1 = require("./expoUpdates");
18
+ async function uploadEmbeddedBundleAsync(ctx) {
19
+ if (!(await (0, expoUpdates_1.isEASUpdateConfigured)(ctx))) {
20
+ ctx.markBuildPhaseSkipped();
21
+ return;
22
+ }
23
+ const { platform } = ctx.job;
24
+ if (platform === eas_build_job_1.Platform.IOS && ctx.job.simulator) {
25
+ ctx.markBuildPhaseSkipped();
26
+ return;
27
+ }
28
+ const channel = ctx.job.updates?.channel;
29
+ if (!channel) {
30
+ ctx.logger.warn('Skipping embedded bundle upload: no channel configured for this build profile.');
31
+ ctx.markBuildPhaseHasWarnings();
32
+ return;
33
+ }
34
+ const projectDir = ctx.getReactNativeProjectDirectory();
35
+ let archivePattern;
36
+ if (platform === eas_build_job_1.Platform.IOS) {
37
+ archivePattern = (0, resolve_1.resolveArtifactPath)(ctx);
38
+ }
39
+ else if (platform === eas_build_job_1.Platform.ANDROID) {
40
+ archivePattern =
41
+ ctx.job.applicationArchivePath ??
42
+ 'android/app/build/outputs/**/*.{apk,aab}';
43
+ }
44
+ else {
45
+ throw new Error(`Uploading embedded updates is not supported for the ${platform} platform.`);
46
+ }
47
+ const [archivePath] = await (0, artifacts_1.findArtifacts)({
48
+ rootDir: projectDir,
49
+ patternOrPath: archivePattern,
50
+ logger: null,
51
+ }).catch(() => []);
52
+ if (!archivePath) {
53
+ ctx.logger.warn('Skipping embedded bundle upload: build archive not found.');
54
+ ctx.markBuildPhaseHasWarnings();
55
+ return;
56
+ }
57
+ const tmpDir = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'eas-embedded-bundle-'));
58
+ const bundleName = platform === eas_build_job_1.Platform.IOS ? 'main.jsbundle' : 'index.android.bundle';
59
+ const bundlePath = path_1.default.join(tmpDir, bundleName);
60
+ const manifestPath = path_1.default.join(tmpDir, 'app.manifest');
61
+ const zip = new node_stream_zip_1.default.async({ file: archivePath });
62
+ try {
63
+ const entries = Object.values(await zip.entries());
64
+ const bundleEntry = entries.find(e => platform === eas_build_job_1.Platform.IOS
65
+ ? e.name.endsWith('/main.jsbundle')
66
+ : e.name.endsWith('assets/index.android.bundle'));
67
+ const manifestEntry = entries.find(e => platform === eas_build_job_1.Platform.IOS
68
+ ? e.name.includes('EXUpdates.bundle/app.manifest')
69
+ : e.name.endsWith('assets/app.manifest'));
70
+ if (!bundleEntry || !manifestEntry) {
71
+ ctx.logger.warn('Skipping embedded bundle upload: bundle or manifest not found in archive.');
72
+ ctx.markBuildPhaseHasWarnings();
73
+ return;
74
+ }
75
+ await zip.extract(bundleEntry.name, bundlePath);
76
+ await zip.extract(manifestEntry.name, manifestPath);
77
+ const args = [
78
+ 'update:embedded:upload',
79
+ '--platform',
80
+ platform,
81
+ '--bundle',
82
+ bundlePath,
83
+ '--manifest',
84
+ manifestPath,
85
+ '--channel',
86
+ channel,
87
+ '--non-interactive',
88
+ ];
89
+ if (ctx.env.EAS_BUILD_ID) {
90
+ args.push('--build-id', ctx.env.EAS_BUILD_ID);
91
+ }
92
+ await (0, easCli_1.runEasCliCommand)({
93
+ args,
94
+ options: {
95
+ cwd: projectDir,
96
+ env: ctx.env,
97
+ logger: ctx.logger,
98
+ mode: logger_1.PipeMode.STDERR_ONLY_AS_STDOUT,
99
+ },
100
+ });
101
+ }
102
+ catch (err) {
103
+ ctx.logger.warn({ err }, 'Failed to upload embedded bundle.');
104
+ ctx.markBuildPhaseHasWarnings();
105
+ }
106
+ finally {
107
+ await (0, results_1.asyncResult)(zip.close());
108
+ }
109
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/build-tools",
3
- "version": "20.0.0",
3
+ "version": "20.1.0",
4
4
  "bugs": "https://github.com/expo/eas-cli/issues",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Expo <support@expo.io>",
@@ -38,14 +38,14 @@
38
38
  "@expo/config": "55.0.10",
39
39
  "@expo/config-plugins": "55.0.7",
40
40
  "@expo/downloader": "20.0.0",
41
- "@expo/eas-build-job": "20.0.0",
41
+ "@expo/eas-build-job": "20.1.0",
42
42
  "@expo/env": "^0.4.0",
43
43
  "@expo/logger": "20.0.0",
44
44
  "@expo/package-manager": "1.9.10",
45
45
  "@expo/plist": "^0.2.0",
46
46
  "@expo/results": "^1.0.0",
47
47
  "@expo/spawn-async": "1.7.2",
48
- "@expo/steps": "20.0.0",
48
+ "@expo/steps": "20.1.0",
49
49
  "@expo/template-file": "20.0.0",
50
50
  "@expo/turtle-spawn": "20.0.0",
51
51
  "@expo/xcpretty": "^4.3.1",
@@ -100,5 +100,5 @@
100
100
  "typescript": "^5.5.4",
101
101
  "uuid": "^9.0.1"
102
102
  },
103
- "gitHead": "890387b3b62c2ed2a946ec0e1197ff69f3e29342"
103
+ "gitHead": "33225d2b73a34db5cbdfeaa537aedb6c6b4a84af"
104
104
  }