@expo/build-tools 20.0.0 → 20.2.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.
- package/dist/builders/android.js +6 -0
- package/dist/builders/custom.js +23 -0
- package/dist/builders/ios.js +6 -0
- package/dist/common/installDependencies.d.ts +10 -1
- package/dist/common/installDependencies.js +95 -1
- package/dist/common/prebuild.js +2 -3
- package/dist/{ios → common}/xcpretty.d.ts +1 -0
- package/dist/{ios → common}/xcpretty.js +18 -3
- package/dist/datadog.d.ts +8 -1
- package/dist/datadog.js +23 -13
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/ios/fastlane.js +1 -1
- package/dist/ios/pod.js +1 -1
- package/dist/runtimeSettings.d.ts +12 -0
- package/dist/runtimeSettings.js +120 -0
- package/dist/steps/easFunctions.js +1 -1
- package/dist/steps/functions/downloadBuild.d.ts +5 -3
- package/dist/steps/functions/downloadBuild.js +34 -4
- package/dist/steps/functions/findAndUploadBuildArtifacts.js +2 -2
- package/dist/steps/functions/installMaestro.js +13 -2
- package/dist/steps/functions/maestroResultParser.d.ts +18 -0
- package/dist/steps/functions/maestroResultParser.js +132 -3
- package/dist/steps/functions/maestroTests.js +26 -13
- package/dist/steps/functions/repack.d.ts +3 -1
- package/dist/steps/functions/repack.js +15 -1
- package/dist/steps/functions/reportMaestroTestResults.js +39 -20
- package/dist/steps/functions/restoreBuildCache.js +9 -6
- package/dist/steps/functions/startAgentDeviceRemoteSession.d.ts +1 -1
- package/dist/steps/functions/startAgentDeviceRemoteSession.js +101 -22
- package/dist/steps/functions/startArgentRemoteSession.js +1 -1
- package/dist/steps/functions/startIosSimulator.js +12 -0
- package/dist/steps/functions/startServeSimRemoteSession.js +1 -1
- package/dist/steps/functions/uploadArtifact.js +1 -1
- package/dist/steps/utils/ios/fastlane.js +1 -1
- package/dist/steps/utils/remoteDeviceRunSession.d.ts +29 -1
- package/dist/steps/utils/remoteDeviceRunSession.js +76 -2
- package/dist/utils/IosSimulatorUtils.d.ts +4 -0
- package/dist/utils/IosSimulatorUtils.js +5 -0
- package/dist/utils/expoUpdatesEmbedded.d.ts +3 -0
- package/dist/utils/expoUpdatesEmbedded.js +109 -0
- package/package.json +5 -5
- package/dist/steps/utils/ios/xcpretty.d.ts +0 -15
- package/dist/steps/utils/ios/xcpretty.js +0 -92
package/dist/builders/android.js
CHANGED
|
@@ -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.');
|
package/dist/builders/custom.js
CHANGED
|
@@ -13,6 +13,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
13
13
|
const easBuildInternal_1 = require("../common/easBuildInternal");
|
|
14
14
|
const projectSources_1 = require("../common/projectSources");
|
|
15
15
|
const customBuildContext_1 = require("../customBuildContext");
|
|
16
|
+
const datadog_1 = require("../datadog");
|
|
16
17
|
const xcodeBuildLogs_1 = require("../ios/xcodeBuildLogs");
|
|
17
18
|
const easFunctionGroups_1 = require("../steps/easFunctionGroups");
|
|
18
19
|
const easFunctions_1 = require("../steps/easFunctions");
|
|
@@ -68,6 +69,7 @@ async function runCustomBuildAsync(ctx) {
|
|
|
68
69
|
throw parseError;
|
|
69
70
|
}
|
|
70
71
|
});
|
|
72
|
+
logUserProvidedCustomFunctions(workflow);
|
|
71
73
|
try {
|
|
72
74
|
try {
|
|
73
75
|
await workflow.executeAsync();
|
|
@@ -92,3 +94,24 @@ async function runCustomBuildAsync(ctx) {
|
|
|
92
94
|
}
|
|
93
95
|
return ctx.artifacts;
|
|
94
96
|
}
|
|
97
|
+
function logUserProvidedCustomFunctions(workflow) {
|
|
98
|
+
for (const buildFunction of Object.values(workflow.buildFunctions)) {
|
|
99
|
+
if (!buildFunction.customFunctionModulePath) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
datadog_1.Datadog.log('Custom build saw user-provided function', {
|
|
103
|
+
event: 'custom_build_user_provided_function',
|
|
104
|
+
custom_function_id: buildFunction.getFullId(),
|
|
105
|
+
custom_function_module_path: buildFunction.customFunctionModulePath,
|
|
106
|
+
custom_function_input_count: String(buildFunction.inputProviders?.length ?? 0),
|
|
107
|
+
custom_function_output_count: String(buildFunction.outputProviders?.length ?? 0),
|
|
108
|
+
...(buildFunction.name ? { custom_function_name: buildFunction.name } : {}),
|
|
109
|
+
...(buildFunction.shell ? { custom_function_shell: buildFunction.shell } : {}),
|
|
110
|
+
...(buildFunction.supportedRuntimePlatforms
|
|
111
|
+
? {
|
|
112
|
+
custom_function_supported_runtime_platforms: buildFunction.supportedRuntimePlatforms.join(','),
|
|
113
|
+
}
|
|
114
|
+
: {}),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
package/dist/builders/ios.js
CHANGED
|
@@ -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.');
|
|
@@ -2,14 +2,23 @@ import { Job } from '@expo/eas-build-job';
|
|
|
2
2
|
import { SpawnOptions, SpawnPromise, SpawnResult } from '@expo/turtle-spawn';
|
|
3
3
|
import { BuildContext } from '../context';
|
|
4
4
|
import { PackageManager } from '../utils/packageManager';
|
|
5
|
-
export declare function installDependenciesAsync({ packageManager, env, logger, infoCallbackFn, cwd, useFrozenLockfile, }: {
|
|
5
|
+
export declare function installDependenciesAsync({ packageManager, env, logger, infoCallbackFn, lineTransformer, cwd, useFrozenLockfile, }: {
|
|
6
6
|
packageManager: PackageManager;
|
|
7
7
|
env: Record<string, string | undefined>;
|
|
8
8
|
cwd: string;
|
|
9
9
|
logger: Exclude<SpawnOptions['logger'], undefined>;
|
|
10
10
|
infoCallbackFn?: SpawnOptions['infoCallbackFn'];
|
|
11
|
+
lineTransformer?: SpawnOptions['lineTransformer'];
|
|
11
12
|
useFrozenLockfile: boolean;
|
|
12
13
|
}): Promise<{
|
|
13
14
|
spawnPromise: SpawnPromise<SpawnResult>;
|
|
14
15
|
}>;
|
|
16
|
+
export declare function installDependenciesWithNpmCacheFallbackAsync({ packageManager, env, logger, infoCallbackFn, cwd, useFrozenLockfile, }: {
|
|
17
|
+
packageManager: PackageManager;
|
|
18
|
+
env: Record<string, string | undefined>;
|
|
19
|
+
cwd: string;
|
|
20
|
+
logger: Exclude<SpawnOptions['logger'], undefined>;
|
|
21
|
+
infoCallbackFn?: SpawnOptions['infoCallbackFn'];
|
|
22
|
+
useFrozenLockfile: boolean;
|
|
23
|
+
}): Promise<void>;
|
|
15
24
|
export declare function resolvePackagerDir(ctx: BuildContext<Job>): string;
|
|
@@ -4,12 +4,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.installDependenciesAsync = installDependenciesAsync;
|
|
7
|
+
exports.installDependenciesWithNpmCacheFallbackAsync = installDependenciesWithNpmCacheFallbackAsync;
|
|
7
8
|
exports.resolvePackagerDir = resolvePackagerDir;
|
|
9
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
8
10
|
const turtle_spawn_1 = __importDefault(require("@expo/turtle-spawn"));
|
|
9
11
|
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const sentry_1 = require("../sentry");
|
|
10
13
|
const packageManager_1 = require("../utils/packageManager");
|
|
11
14
|
const project_1 = require("../utils/project");
|
|
12
|
-
async function installDependenciesAsync({ packageManager, env, logger, infoCallbackFn, cwd, useFrozenLockfile, }) {
|
|
15
|
+
async function installDependenciesAsync({ packageManager, env, logger, infoCallbackFn, lineTransformer, cwd, useFrozenLockfile, }) {
|
|
13
16
|
let args;
|
|
14
17
|
switch (packageManager) {
|
|
15
18
|
case packageManager_1.PackageManager.NPM: {
|
|
@@ -60,10 +63,67 @@ async function installDependenciesAsync({ packageManager, env, logger, infoCallb
|
|
|
60
63
|
cwd,
|
|
61
64
|
logger,
|
|
62
65
|
infoCallbackFn,
|
|
66
|
+
lineTransformer,
|
|
63
67
|
env,
|
|
64
68
|
}),
|
|
65
69
|
};
|
|
66
70
|
}
|
|
71
|
+
async function installDependenciesWithNpmCacheFallbackAsync({ packageManager, env, logger, infoCallbackFn, cwd, useFrozenLockfile, }) {
|
|
72
|
+
const npmCacheUrl = env.NPM_CONFIG_REGISTRY;
|
|
73
|
+
let firstErrorLine;
|
|
74
|
+
let errorLineCount = 0;
|
|
75
|
+
try {
|
|
76
|
+
await (await installDependenciesAsync({
|
|
77
|
+
packageManager,
|
|
78
|
+
env,
|
|
79
|
+
logger,
|
|
80
|
+
infoCallbackFn,
|
|
81
|
+
lineTransformer: (line) => {
|
|
82
|
+
if (isNpmCacheRegistryErrorLine(line, { env, npmCacheUrl })) {
|
|
83
|
+
firstErrorLine ??= line;
|
|
84
|
+
errorLineCount += 1;
|
|
85
|
+
}
|
|
86
|
+
return line;
|
|
87
|
+
},
|
|
88
|
+
cwd,
|
|
89
|
+
useFrozenLockfile,
|
|
90
|
+
})).spawnPromise;
|
|
91
|
+
if (firstErrorLine) {
|
|
92
|
+
sentry_1.Sentry.capture(new NpmCacheRegistryNonFatalError(), {
|
|
93
|
+
level: 'warning',
|
|
94
|
+
tags: { packageManager },
|
|
95
|
+
extras: { cwd, npmCacheUrl, useFrozenLockfile, firstErrorLine, errorLineCount },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
if (!isNpmCacheInstallFailure(err, { env, npmCacheUrl })) {
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
logger.warn(`Failed to install dependencies using the npm cache registry (${npmCacheUrl}). Retrying without the npm cache registry.`);
|
|
104
|
+
sentry_1.Sentry.capture(new NpmCacheRegistryInstallError(err), {
|
|
105
|
+
level: 'warning',
|
|
106
|
+
tags: { packageManager },
|
|
107
|
+
extras: {
|
|
108
|
+
cwd,
|
|
109
|
+
npmCacheUrl,
|
|
110
|
+
useFrozenLockfile,
|
|
111
|
+
originalErrorMessage: err instanceof Error ? err.message : String(err),
|
|
112
|
+
status: err instanceof Error ? err.status : undefined,
|
|
113
|
+
signal: err instanceof Error ? err.signal : undefined,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
const { NPM_CONFIG_REGISTRY: _NPM_CONFIG_REGISTRY, ...fallbackEnv } = env;
|
|
117
|
+
await (await installDependenciesAsync({
|
|
118
|
+
packageManager,
|
|
119
|
+
env: fallbackEnv,
|
|
120
|
+
logger,
|
|
121
|
+
infoCallbackFn,
|
|
122
|
+
cwd,
|
|
123
|
+
useFrozenLockfile,
|
|
124
|
+
})).spawnPromise;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
67
127
|
function resolvePackagerDir(ctx) {
|
|
68
128
|
const packagerRunDir = (0, packageManager_1.findPackagerRootDir)(ctx.getReactNativeProjectDirectory());
|
|
69
129
|
if (packagerRunDir !== ctx.getReactNativeProjectDirectory()) {
|
|
@@ -72,3 +132,37 @@ function resolvePackagerDir(ctx) {
|
|
|
72
132
|
}
|
|
73
133
|
return packagerRunDir;
|
|
74
134
|
}
|
|
135
|
+
function isNpmCacheInstallFailure(err, { env, npmCacheUrl }) {
|
|
136
|
+
if (!isNpmCacheRegistryEnabled(env, npmCacheUrl)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const errorOutput = getErrorOutput(err);
|
|
140
|
+
return errorOutput.includes(npmCacheUrl);
|
|
141
|
+
}
|
|
142
|
+
function isNpmCacheRegistryErrorLine(line, { env, npmCacheUrl }) {
|
|
143
|
+
return (isNpmCacheRegistryEnabled(env, npmCacheUrl) &&
|
|
144
|
+
line.includes(npmCacheUrl) &&
|
|
145
|
+
/(?:error|failed|ENOTFOUND|ECONN|ETIMEDOUT|EAI_AGAIN|FetchError)/i.test(line));
|
|
146
|
+
}
|
|
147
|
+
function isNpmCacheRegistryEnabled(env, npmCacheUrl) {
|
|
148
|
+
return env.EAS_USE_NPM_CACHE === '1' && !!npmCacheUrl;
|
|
149
|
+
}
|
|
150
|
+
function getErrorOutput(err) {
|
|
151
|
+
if (!(err instanceof Error)) {
|
|
152
|
+
return '';
|
|
153
|
+
}
|
|
154
|
+
const { stdout, stderr } = err;
|
|
155
|
+
return [stdout, stderr].filter(Boolean).join('\n');
|
|
156
|
+
}
|
|
157
|
+
class NpmCacheRegistryNonFatalError extends eas_build_job_1.SystemError {
|
|
158
|
+
name = 'NpmCacheRegistryNonFatalError';
|
|
159
|
+
constructor() {
|
|
160
|
+
super('Non-fatal npm cache registry error during dependency install');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
class NpmCacheRegistryInstallError extends eas_build_job_1.SystemError {
|
|
164
|
+
name = 'NpmCacheRegistryInstallError';
|
|
165
|
+
constructor(cause) {
|
|
166
|
+
super('Failed to install dependencies using npm cache registry', { cause });
|
|
167
|
+
}
|
|
168
|
+
}
|
package/dist/common/prebuild.js
CHANGED
|
@@ -19,15 +19,14 @@ async function prebuildAsync(ctx, { logger, workingDir, options }) {
|
|
|
19
19
|
options: spawnOptions,
|
|
20
20
|
packageManager: ctx.packageManager,
|
|
21
21
|
});
|
|
22
|
-
|
|
22
|
+
await (0, installDependencies_1.installDependenciesWithNpmCacheFallbackAsync)({
|
|
23
23
|
packageManager: ctx.packageManager,
|
|
24
24
|
env: ctx.env,
|
|
25
25
|
logger,
|
|
26
26
|
cwd: (0, installDependencies_1.resolvePackagerDir)(ctx),
|
|
27
27
|
// prebuild sometimes modifies package.json, so we don't want to use frozen lockfile
|
|
28
28
|
useFrozenLockfile: false,
|
|
29
|
-
})
|
|
30
|
-
await installDependenciesSpawnPromise;
|
|
29
|
+
});
|
|
31
30
|
}
|
|
32
31
|
function getPrebuildCommandArgs(ctx) {
|
|
33
32
|
let prebuildCommand = ctx.job.experimental?.prebuildCommand ?? `prebuild --no-install --platform ${ctx.job.platform}`;
|
|
@@ -10,12 +10,14 @@ const assert_1 = __importDefault(require("assert"));
|
|
|
10
10
|
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
11
11
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const sentry_1 = require("../sentry");
|
|
13
14
|
const CHECK_FILE_INTERVAL_MS = 1000;
|
|
14
15
|
class XcodeBuildLogger {
|
|
15
16
|
logger;
|
|
16
17
|
projectRoot;
|
|
17
18
|
loggerError;
|
|
18
19
|
flushing = false;
|
|
20
|
+
reportedFormatterError = false;
|
|
19
21
|
logReaderPromise;
|
|
20
22
|
logsPath;
|
|
21
23
|
constructor(logger, projectRoot) {
|
|
@@ -62,9 +64,22 @@ class XcodeBuildLogger {
|
|
|
62
64
|
this.logReaderPromise = (0, spawn_async_1.default)('tail', ['-n', '+0', '-f', logsPath], { stdio: 'pipe' });
|
|
63
65
|
(0, assert_1.default)(this.logReaderPromise.child.stdout, 'stdout is not available');
|
|
64
66
|
this.logReaderPromise.child.stdout.on('data', (data) => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
try {
|
|
68
|
+
const lines = formatter.pipe(data.toString());
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
this.logger.info(line);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
// The formatter can throw on unexpected xcodebuild output. Fall back to
|
|
75
|
+
// raw logs instead of crashing the process with an uncaught exception.
|
|
76
|
+
if (!this.reportedFormatterError) {
|
|
77
|
+
this.reportedFormatterError = true;
|
|
78
|
+
sentry_1.Sentry.capture('xcpretty formatter failed to parse xcodebuild logs', err, {
|
|
79
|
+
extras: { data: data.toString().slice(-2000) },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
this.logger.info(data.toString());
|
|
68
83
|
}
|
|
69
84
|
});
|
|
70
85
|
await this.logReaderPromise;
|
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
|
@@ -5,6 +5,9 @@ const sentry_1 = require("./sentry");
|
|
|
5
5
|
const turtleFetch_1 = require("./utils/turtleFetch");
|
|
6
6
|
let setupOptions = null;
|
|
7
7
|
let pendingUploads = [];
|
|
8
|
+
function withTargetTag(tags, target) {
|
|
9
|
+
return { ...tags, target: target.kind === 'build' ? 'build' : 'job_run' };
|
|
10
|
+
}
|
|
8
11
|
exports.Datadog = {
|
|
9
12
|
setup(opts) {
|
|
10
13
|
setupOptions = opts;
|
|
@@ -13,23 +16,26 @@ exports.Datadog = {
|
|
|
13
16
|
if (!setupOptions) {
|
|
14
17
|
return;
|
|
15
18
|
}
|
|
16
|
-
const { expoApiV2BaseUrl,
|
|
19
|
+
const { expoApiV2BaseUrl, robotAccessToken, target } = setupOptions;
|
|
17
20
|
const metrics = [
|
|
18
21
|
{
|
|
19
22
|
name,
|
|
20
23
|
type: 'distribution',
|
|
21
24
|
value,
|
|
22
|
-
tags,
|
|
25
|
+
tags: withTargetTag(tags, target),
|
|
23
26
|
},
|
|
24
27
|
];
|
|
25
|
-
const
|
|
28
|
+
const metricsPath = target.kind === 'build'
|
|
29
|
+
? `turtle-builds/${target.turtleBuildId}/metrics`
|
|
30
|
+
: `turtle-job-runs/${target.turtleJobRunId}/metrics`;
|
|
31
|
+
const uploadPromise = (0, turtleFetch_1.turtleFetch)(new URL(metricsPath, expoApiV2BaseUrl).toString(), 'POST', {
|
|
26
32
|
json: { metrics },
|
|
27
33
|
headers: {
|
|
28
34
|
Authorization: `Bearer ${robotAccessToken}`,
|
|
29
35
|
},
|
|
30
36
|
retries: 2,
|
|
31
37
|
}).catch(err => {
|
|
32
|
-
sentry_1.Sentry.capture(
|
|
38
|
+
sentry_1.Sentry.capture(`Failed to report turtle ${target.kind} metric`, err, {
|
|
33
39
|
extras: { metrics },
|
|
34
40
|
});
|
|
35
41
|
});
|
|
@@ -39,26 +45,30 @@ exports.Datadog = {
|
|
|
39
45
|
if (!setupOptions) {
|
|
40
46
|
return;
|
|
41
47
|
}
|
|
42
|
-
const { expoApiV2BaseUrl,
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
message,
|
|
46
|
-
tags
|
|
47
|
-
|
|
48
|
-
const uploadPromise = (0, turtleFetch_1.turtleFetch)(new URL(
|
|
48
|
+
const { expoApiV2BaseUrl, robotAccessToken, target } = setupOptions;
|
|
49
|
+
const tagsWithTarget = withTargetTag(tags, target);
|
|
50
|
+
const log = target.kind === 'build'
|
|
51
|
+
? { buildId: target.turtleBuildId, message, tags: tagsWithTarget }
|
|
52
|
+
: { jobRunId: target.turtleJobRunId, message, tags: tagsWithTarget };
|
|
53
|
+
const logsPath = target.kind === 'build' ? 'turtle-builds/logs' : 'turtle-job-runs/logs';
|
|
54
|
+
const uploadPromise = (0, turtleFetch_1.turtleFetch)(new URL(logsPath, expoApiV2BaseUrl).toString(), 'POST', {
|
|
49
55
|
json: log,
|
|
50
56
|
headers: {
|
|
51
57
|
Authorization: `Bearer ${robotAccessToken}`,
|
|
52
58
|
},
|
|
53
59
|
shouldThrowOnNotOk: false,
|
|
54
60
|
}).catch(err => {
|
|
55
|
-
sentry_1.Sentry.capture(
|
|
61
|
+
sentry_1.Sentry.capture(`Failed to report turtle ${target.kind} log`, err, {
|
|
56
62
|
extras: { log },
|
|
57
63
|
});
|
|
58
64
|
});
|
|
59
65
|
pendingUploads.push(uploadPromise);
|
|
60
66
|
},
|
|
61
67
|
async flushAsync() {
|
|
62
|
-
|
|
68
|
+
// Rotate so each flush only awaits its own batch; uploads enqueued during the
|
|
69
|
+
// await land in the fresh array for a later flush.
|
|
70
|
+
const uploads = pendingUploads;
|
|
71
|
+
pendingUploads = [];
|
|
72
|
+
await Promise.allSettled(uploads);
|
|
63
73
|
},
|
|
64
74
|
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -64,3 +64,4 @@ var datadog_1 = require("./datadog");
|
|
|
64
64
|
Object.defineProperty(exports, "Datadog", { enumerable: true, get: function () { return datadog_1.Datadog; } });
|
|
65
65
|
var sentry_1 = require("./sentry");
|
|
66
66
|
Object.defineProperty(exports, "Sentry", { enumerable: true, get: function () { return sentry_1.Sentry; } });
|
|
67
|
+
__exportStar(require("./runtimeSettings"), exports);
|
package/dist/ios/fastlane.js
CHANGED
|
@@ -14,8 +14,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
14
14
|
const fastfile_1 = require("./fastfile");
|
|
15
15
|
const gymfile_1 = require("./gymfile");
|
|
16
16
|
const tvos_1 = require("./tvos");
|
|
17
|
-
const xcpretty_1 = require("./xcpretty");
|
|
18
17
|
const fastlane_1 = require("../common/fastlane");
|
|
18
|
+
const xcpretty_1 = require("../common/xcpretty");
|
|
19
19
|
const context_1 = require("../context");
|
|
20
20
|
async function runFastlaneGym(ctx, { scheme, buildConfiguration, credentials, entitlements, extraEnv, }) {
|
|
21
21
|
const workspacePath = path_1.default.join(ctx.getReactNativeProjectDirectory(), 'ios');
|
package/dist/ios/pod.js
CHANGED
|
@@ -42,7 +42,7 @@ async function resolvePrecompiledModulesPodInstallEnvAsync(ctx) {
|
|
|
42
42
|
ctx.logger.info('EXPO_USE_PRECOMPILED_MODULES=0 is set; not enabling precompiled modules use.');
|
|
43
43
|
return {};
|
|
44
44
|
}
|
|
45
|
-
if (ctx.
|
|
45
|
+
if (ctx.env.EAS_USE_PRECOMPILED_MODULES !== '1') {
|
|
46
46
|
return {};
|
|
47
47
|
}
|
|
48
48
|
let expoPackageVersion;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Env } from '@expo/eas-build-job';
|
|
2
|
+
export declare namespace RuntimeSettings {
|
|
3
|
+
function loadAsync({ environment, env: nextRuntimeEnvironment, }: {
|
|
4
|
+
environment: string;
|
|
5
|
+
env?: Env;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
function isUsingIosPrecompiledModulesEnabled(): boolean;
|
|
8
|
+
function getNpmCacheUrl(): string | null;
|
|
9
|
+
function getNodeJsCacheUrl(): string | null;
|
|
10
|
+
function getMavenCacheUrl(): string | null;
|
|
11
|
+
function getCocoapodsCacheUrl(): string | null;
|
|
12
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
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.RuntimeSettings = void 0;
|
|
7
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const sentry_1 = require("./sentry");
|
|
10
|
+
const ENVIRONMENT_TO_RUNTIME_SETTINGS_URL = {
|
|
11
|
+
staging: 'https://storage.googleapis.com/eas-workflows-staging/runtime-settings.json',
|
|
12
|
+
production: 'https://storage.googleapis.com/eas-workflows-production/runtime-settings.json',
|
|
13
|
+
};
|
|
14
|
+
const RuntimeSettingsSchema = zod_1.z
|
|
15
|
+
.object({
|
|
16
|
+
caches: zod_1.z.record(zod_1.z.string(), zod_1.z
|
|
17
|
+
.object({
|
|
18
|
+
npm: zod_1.z.boolean(),
|
|
19
|
+
nodejs: zod_1.z.boolean(),
|
|
20
|
+
maven: zod_1.z.boolean(),
|
|
21
|
+
cocoapods: zod_1.z.boolean(),
|
|
22
|
+
})
|
|
23
|
+
.partial()),
|
|
24
|
+
iosPrecompiledModules: zod_1.z.boolean(),
|
|
25
|
+
})
|
|
26
|
+
.partial();
|
|
27
|
+
let runtimeSettings = {};
|
|
28
|
+
let runtimeEnvironment = {};
|
|
29
|
+
var RuntimeSettings;
|
|
30
|
+
(function (RuntimeSettings) {
|
|
31
|
+
async function loadAsync({ environment, env: nextRuntimeEnvironment = {}, }) {
|
|
32
|
+
runtimeEnvironment = nextRuntimeEnvironment;
|
|
33
|
+
const url = ENVIRONMENT_TO_RUNTIME_SETTINGS_URL[environment];
|
|
34
|
+
if (!url) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const response = await (0, node_fetch_1.default)(url, {
|
|
39
|
+
signal: AbortSignal.timeout(1000),
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
sentry_1.Sentry.capture('Failed to fetch worker runtime settings', {
|
|
43
|
+
extras: { url, status: response.status },
|
|
44
|
+
level: 'warning',
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
runtimeSettings = RuntimeSettingsSchema.parse(await response.json());
|
|
49
|
+
if (runtimeSettings.caches && !runtimeSettings.caches[process.platform]) {
|
|
50
|
+
sentry_1.Sentry.capture(new Error(`Runtime settings are missing cache settings for platform "${process.platform}"`), {
|
|
51
|
+
extras: {
|
|
52
|
+
configuredPlatforms: Object.keys(runtimeSettings.caches),
|
|
53
|
+
platform: process.platform,
|
|
54
|
+
},
|
|
55
|
+
level: 'error',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
sentry_1.Sentry.capture('Failed to load worker runtime settings', err instanceof Error ? err : new Error(String(err)), {
|
|
61
|
+
extras: { url },
|
|
62
|
+
level: 'warning',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
RuntimeSettings.loadAsync = loadAsync;
|
|
67
|
+
function isUsingIosPrecompiledModulesEnabled() {
|
|
68
|
+
return runtimeSettings.iosPrecompiledModules ?? false;
|
|
69
|
+
}
|
|
70
|
+
RuntimeSettings.isUsingIosPrecompiledModulesEnabled = isUsingIosPrecompiledModulesEnabled;
|
|
71
|
+
function getNpmCacheUrl() {
|
|
72
|
+
if (runtimeEnvironment['EAS_BUILD_DISABLE_NPM_CACHE'] === '1') {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const runtimeEnabled = runtimeSettings.caches?.[process.platform]?.npm;
|
|
76
|
+
if (runtimeEnabled === false) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const envOverride = runtimeEnvironment['EAS_USE_NPM_CACHE'];
|
|
80
|
+
const enabled = envOverride === '1' || (envOverride !== '0' && runtimeEnabled);
|
|
81
|
+
return enabled ? process.env.EAS_NPM_CACHE_URL || null : null;
|
|
82
|
+
}
|
|
83
|
+
RuntimeSettings.getNpmCacheUrl = getNpmCacheUrl;
|
|
84
|
+
function getNodeJsCacheUrl() {
|
|
85
|
+
const runtimeEnabled = runtimeSettings.caches?.[process.platform]?.nodejs;
|
|
86
|
+
if (runtimeEnabled === false) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const envOverride = runtimeEnvironment['EAS_USE_NODEJS_CACHE'];
|
|
90
|
+
const enabled = envOverride === '1' || (envOverride !== '0' && runtimeEnabled);
|
|
91
|
+
return enabled ? process.env.EAS_NODEJS_CACHE_URL || null : null;
|
|
92
|
+
}
|
|
93
|
+
RuntimeSettings.getNodeJsCacheUrl = getNodeJsCacheUrl;
|
|
94
|
+
function getMavenCacheUrl() {
|
|
95
|
+
if (runtimeEnvironment['EAS_BUILD_DISABLE_MAVEN_CACHE'] === '1') {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const runtimeEnabled = runtimeSettings.caches?.[process.platform]?.maven;
|
|
99
|
+
if (runtimeEnabled === false) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const envOverride = runtimeEnvironment['EAS_USE_MAVEN_CACHE'];
|
|
103
|
+
const enabled = envOverride === '1' || (envOverride !== '0' && runtimeEnabled);
|
|
104
|
+
return enabled ? process.env.EAS_MAVEN_CACHE_URL || null : null;
|
|
105
|
+
}
|
|
106
|
+
RuntimeSettings.getMavenCacheUrl = getMavenCacheUrl;
|
|
107
|
+
function getCocoapodsCacheUrl() {
|
|
108
|
+
if (runtimeEnvironment['EAS_BUILD_DISABLE_COCOAPODS_CACHE'] === '1') {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const runtimeEnabled = runtimeSettings.caches?.[process.platform]?.cocoapods;
|
|
112
|
+
if (runtimeEnabled === false) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const envOverride = runtimeEnvironment['EAS_USE_COCOAPODS_CACHE'];
|
|
116
|
+
const enabled = envOverride === '1' || (envOverride !== '0' && runtimeEnabled);
|
|
117
|
+
return enabled ? process.env.EAS_COCOAPODS_CACHE_URL || null : null;
|
|
118
|
+
}
|
|
119
|
+
RuntimeSettings.getCocoapodsCacheUrl = getCocoapodsCacheUrl;
|
|
120
|
+
})(RuntimeSettings || (exports.RuntimeSettings = RuntimeSettings = {}));
|
|
@@ -57,7 +57,7 @@ function getEasFunctions(ctx) {
|
|
|
57
57
|
(0, installNodeModules_1.createInstallNodeModulesBuildFunction)(),
|
|
58
58
|
(0, prebuild_1.createPrebuildBuildFunction)(),
|
|
59
59
|
(0, readIpaInfo_1.createReadIpaInfoBuildFunction)(),
|
|
60
|
-
(0, downloadBuild_1.createDownloadBuildFunction)(),
|
|
60
|
+
(0, downloadBuild_1.createDownloadBuildFunction)(ctx),
|
|
61
61
|
(0, export_1.createEasExportBuildFunction)(),
|
|
62
62
|
(0, deploy_1.createEasDeployBuildFunction)(),
|
|
63
63
|
(0, repack_1.createRepackBuildFunction)(),
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { bunyan } from '@expo/logger';
|
|
2
2
|
import { BuildFunction } from '@expo/steps';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { Client } from '@urql/core';
|
|
4
|
+
import { CustomBuildContext } from '../../customBuildContext';
|
|
5
|
+
export declare function createDownloadBuildFunction(ctx: CustomBuildContext): BuildFunction;
|
|
6
|
+
export declare function downloadBuildAsync({ logger, buildId, graphqlClient, robotAccessToken, extensions, }: {
|
|
5
7
|
logger: bunyan;
|
|
6
8
|
buildId: string;
|
|
7
|
-
|
|
9
|
+
graphqlClient: Client;
|
|
8
10
|
robotAccessToken: string | null;
|
|
9
11
|
extensions: string[];
|
|
10
12
|
}): Promise<{
|
|
@@ -9,6 +9,7 @@ const eas_build_job_1 = require("@expo/eas-build-job");
|
|
|
9
9
|
const results_1 = require("@expo/results");
|
|
10
10
|
const steps_1 = require("@expo/steps");
|
|
11
11
|
const fast_glob_1 = require("fast-glob");
|
|
12
|
+
const gql_tada_1 = require("gql.tada");
|
|
12
13
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
13
14
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
15
|
const node_os_1 = __importDefault(require("node:os"));
|
|
@@ -21,7 +22,20 @@ const files_1 = require("../../utils/files");
|
|
|
21
22
|
const retryOnDNSFailure_1 = require("../../utils/retryOnDNSFailure");
|
|
22
23
|
const strings_1 = require("../../utils/strings");
|
|
23
24
|
const streamPipeline = (0, util_1.promisify)(stream_1.default.pipeline);
|
|
24
|
-
|
|
25
|
+
const BUILD_BY_ID_QUERY = (0, gql_tada_1.graphql)(`
|
|
26
|
+
query DownloadBuildByIdQuery($buildId: ID!) {
|
|
27
|
+
builds {
|
|
28
|
+
byId(buildId: $buildId) {
|
|
29
|
+
id
|
|
30
|
+
platform
|
|
31
|
+
artifacts {
|
|
32
|
+
applicationArchiveUrl
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
`);
|
|
38
|
+
function createDownloadBuildFunction(ctx) {
|
|
25
39
|
return new steps_1.BuildFunction({
|
|
26
40
|
namespace: 'eas',
|
|
27
41
|
id: 'download_build',
|
|
@@ -55,7 +69,7 @@ function createDownloadBuildFunction() {
|
|
|
55
69
|
const { artifactPath } = await downloadBuildAsync({
|
|
56
70
|
logger,
|
|
57
71
|
buildId,
|
|
58
|
-
|
|
72
|
+
graphqlClient: ctx.graphqlClient,
|
|
59
73
|
robotAccessToken: stepsCtx.global.staticContext.job.secrets?.robotAccessToken ?? null,
|
|
60
74
|
extensions,
|
|
61
75
|
});
|
|
@@ -63,9 +77,25 @@ function createDownloadBuildFunction() {
|
|
|
63
77
|
},
|
|
64
78
|
});
|
|
65
79
|
}
|
|
66
|
-
async function
|
|
80
|
+
async function fetchApplicationArchiveUrlAsync({ buildId, graphqlClient, }) {
|
|
81
|
+
const result = await graphqlClient.query(BUILD_BY_ID_QUERY, { buildId }).toPromise();
|
|
82
|
+
if (result.error) {
|
|
83
|
+
const { error } = result;
|
|
84
|
+
const message = `Could not fetch build ${buildId}: ${error.message}`;
|
|
85
|
+
throw error.networkError || result.error.response?.status >= 500
|
|
86
|
+
? new eas_build_job_1.SystemError(message, { cause: error })
|
|
87
|
+
: new eas_build_job_1.UserError('EAS_DOWNLOAD_BUILD_FETCH_FAILED', message, { cause: error });
|
|
88
|
+
}
|
|
89
|
+
const applicationArchiveUrl = result.data?.builds.byId?.artifacts?.applicationArchiveUrl;
|
|
90
|
+
if (!applicationArchiveUrl) {
|
|
91
|
+
throw new eas_build_job_1.UserError('EAS_DOWNLOAD_BUILD_NO_APPLICATION_ARCHIVE', 'Build does not have an application archive url');
|
|
92
|
+
}
|
|
93
|
+
return applicationArchiveUrl;
|
|
94
|
+
}
|
|
95
|
+
async function downloadBuildAsync({ logger, buildId, graphqlClient, robotAccessToken, extensions, }) {
|
|
67
96
|
const downloadDestinationDirectory = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'download_build-downloaded-'));
|
|
68
|
-
const
|
|
97
|
+
const downloadUrl = await fetchApplicationArchiveUrlAsync({ buildId, graphqlClient });
|
|
98
|
+
const response = await (0, retryOnDNSFailure_1.retryOnDNSFailure)(node_fetch_1.default)(downloadUrl, {
|
|
69
99
|
headers: robotAccessToken ? { Authorization: `Bearer ${robotAccessToken}` } : undefined,
|
|
70
100
|
});
|
|
71
101
|
if (!response.ok) {
|