Compare commits

..
10 changed files with 64 additions and 106 deletions

No files matched your search

-5
View File
@@ -1,5 +0,0 @@
---
'eas-cli': minor
---
Add `--os-version` to `eas simulator` to select the iOS Simulator runtime version.
+5
View File
@@ -0,0 +1,5 @@
---
'@expo/eas-build-job': patch
---
Add types and validation for documented GitHub workflow context fields.
@@ -871,7 +871,7 @@ describe('StaticWorkflowInterpolationContextZ', () => {
});
describe('GitHub context event payload passthrough', () => {
it('accepts arbitrary fields at all event levels', () => {
it('preserves webhook fields and validates documented pull request details', () => {
const context = {
after: {},
needs: {},
@@ -895,7 +895,19 @@ describe('GitHub context event payload passthrough', () => {
},
pull_request: {
number: 123,
title: 'Fix the build',
body: null,
state: 'open',
draft: true,
merged: false,
html_url: 'https://github.com/expo/expo/pull/123',
user: { login: 'octocat', id: 1 },
labels: [{ name: 'bug', color: 'red' }],
head: { ref: 'fix-build', sha: 'abc123', repo: { name: 'expo' } },
base: { ref: 'main', sha: 'def456' },
created_at: '2026-10-01T00:00:00Z',
updated_at: '2026-10-02T00:00:00Z',
merged_at: null,
},
number: 123,
schedule: '0 0 * * *',
@@ -921,6 +933,34 @@ describe('GitHub context event payload passthrough', () => {
const parsed = StaticWorkflowInterpolationContextZ.parse(context);
expect(parsed.github?.event).toMatchObject(context.github.event);
expect(() =>
StaticWorkflowInterpolationContextZ.parse({
...context,
github: {
...context.github,
event: {
...context.github.event,
pull_request: { ...context.github.event.pull_request, title: 123 },
},
},
})
).toThrow();
});
it.each([
{ number: 123 },
{ number: 123, body: null, user: null, merged: null, merged_at: null },
])('accepts partial pull request payloads: %j', pullRequest => {
const github = {
event_name: 'pull_request',
sha: 'abc123',
ref: 'refs/heads/main',
ref_name: 'main',
ref_type: 'branch',
event: { pull_request: pullRequest },
};
expect(StaticWorkflowInterpolationContextZ.shape.github.parse(github)).toEqual(github);
});
});
+14
View File
@@ -207,6 +207,7 @@ const GitHubContextZ = z.object({
.record(z.string(), z.unknown())
.and(
z.object({
action: z.string().optional(),
label: z
.looseObject({
name: z.string(),
@@ -221,6 +222,19 @@ const GitHubContextZ = z.object({
pull_request: z
.looseObject({
number: z.number(),
title: z.string().optional(),
body: z.string().nullable().optional(),
state: z.enum(['open', 'closed']).optional(),
draft: z.boolean().optional(),
merged: z.boolean().nullable().optional(),
html_url: z.string().optional(),
user: z.looseObject({ login: z.string() }).nullable().optional(),
labels: z.array(z.looseObject({ name: z.string() })).optional(),
head: z.looseObject({ ref: z.string(), sha: z.string() }).optional(),
base: z.looseObject({ ref: z.string(), sha: z.string() }).optional(),
created_at: z.string().optional(),
updated_at: z.string().optional(),
merged_at: z.string().nullable().optional(),
})
.optional(),
number: z.number().optional(),
+1 -13
View File
@@ -46796,19 +46796,7 @@
"inputFields": [
{
"name": "deviceIdentifier",
"description": "Simulator device name or UDID to use for the session (e.g. \"iPhone 17 Pro\"). If omitted,\nthe default device of the selected iOS version is used.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "osVersion",
"description": "iOS version of the Simulator runtime, as major.minor (e.g. \"18.6\") or as a major version\n(e.g. \"26\") for the newest available release of that major. Each version runs on the macOS\nworker image that has that runtime, so the session also uses that image's Xcode. Unavailable\nversions are rejected with the list of available ones. Not every account can select a version\nyet. If omitted, the default iOS version is used.",
"description": "Simulator device name or UDID to use for the session (e.g. \"iPhone 16 Pro\").",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -1223,54 +1223,6 @@ describe(Simulator, () => {
expect(mockCreateDeviceRunSessionAsync.mock.calls[0][1]).not.toHaveProperty('ios');
});
it('forwards --os-version in the iOS create options', async () => {
const { command } = createCommand([
'--platform',
'ios',
'--non-interactive',
'--os-version',
' 18.5 ',
]);
await command.runAsync();
expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith(
graphqlClient,
expect.objectContaining({ ios: { osVersion: '18.5' } })
);
});
it('forwards --os-version together with --device', async () => {
const { command } = createCommand([
'--platform',
'ios',
'--non-interactive',
'--device',
'iPhone 16 Pro',
'--os-version',
'26',
]);
await command.runAsync();
expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith(
graphqlClient,
expect.objectContaining({ ios: { deviceIdentifier: 'iPhone 16 Pro', osVersion: '26' } })
);
});
it('rejects --os-version for Android before creating a session', async () => {
const { command } = createCommand([
'--platform',
'android',
'--non-interactive',
'--os-version',
'18.5',
]);
await expect(command.runAsync()).rejects.toThrow(
'--os-version is only supported with --platform ios.'
);
expect(mockCreateDeviceRunSessionAsync).not.toHaveBeenCalled();
});
it('omits resourceClass when --resource-class is not set', async () => {
const { command } = createCommand(['--platform', 'ios', '--non-interactive']);
await command.runAsync();
@@ -108,10 +108,6 @@ export default class Simulator extends EasCommand {
description:
'Android SDK system image package to use for the emulator (e.g. "system-images;android-35-ext15;google_apis_playstore;x86_64"). Defaults to a system image chosen by the runner. Only supported with --platform android.',
}),
'os-version': Flags.string({
description:
'iOS version of the Simulator runtime, as major.minor (e.g. "18.6") or as a major version (e.g. "26") for the newest available release of that major. Unavailable versions are rejected with the list of available ones. Defaults to the server default. Only supported with --platform ios.',
}),
'build-id': Flags.string({
description: 'EAS Build to install and launch before the simulator session is ready.',
exclusive: ['build-fingerprint', 'application-archive-url', 'expo-go'],
@@ -261,7 +257,6 @@ export default class Simulator extends EasCommand {
const tags = flags.tag?.map(tag => tag.trim()).filter(tag => tag.length > 0);
const deviceIdentifier = flags.device?.trim() || undefined;
const systemImagePackage = flags['system-image']?.trim() || undefined;
const osVersion = flags['os-version']?.trim() || undefined;
const buildId = flags['build-id']?.trim() || undefined;
const buildFingerprint = flags['build-fingerprint']?.trim() || undefined;
const applicationArchiveUrlFromFlag = flags['application-archive-url']?.trim() || undefined;
@@ -308,9 +303,6 @@ export default class Simulator extends EasCommand {
if (systemImagePackage && platform !== AppPlatform.Android) {
throw new EasCommandError('--system-image is only supported with --platform android.');
}
if (osVersion && platform !== AppPlatform.Ios) {
throw new EasCommandError('--os-version is only supported with --platform ios.');
}
let egressAllow: string[] = [];
try {
egressAllow = parseEgressAllowList(flags['egress-allow'] ?? []);
@@ -387,13 +379,8 @@ export default class Simulator extends EasCommand {
networkCapture: flags['network-capture'],
...(networkCaptureFields.length ? { networkCaptureFields } : {}),
...(platform === AppPlatform.Ios
? deviceIdentifier || osVersion
? {
ios: {
...(deviceIdentifier ? { deviceIdentifier } : {}),
...(osVersion ? { osVersion } : {}),
},
}
? deviceIdentifier
? { ios: { deviceIdentifier } }
: {}
: deviceIdentifier || systemImagePackage
? {
+1 -12
View File
@@ -6613,19 +6613,8 @@ export type CreateGitHubRepositorySettingsInput = {
};
export type CreateIosDeviceRunSessionInput = {
/**
* Simulator device name or UDID to use for the session (e.g. "iPhone 17 Pro"). If omitted,
* the default device of the selected iOS version is used.
*/
/** Simulator device name or UDID to use for the session (e.g. "iPhone 16 Pro"). */
deviceIdentifier?: InputMaybe<Scalars['String']['input']>;
/**
* iOS version of the Simulator runtime, as major.minor (e.g. "18.6") or as a major version
* (e.g. "26") for the newest available release of that major. Each version runs on the macOS
* worker image that has that runtime, so the session also uses that image's Xcode. Unavailable
* versions are rejected with the list of available ones. Not every account can select a version
* yet. If omitted, the default iOS version is used.
*/
osVersion?: InputMaybe<Scalars['String']['input']>;
};
export type CreateIosSubmissionInput = {
@@ -17,7 +17,6 @@ module Pod
%r{https?://(api\.)?github\.com/},
%r{https?://sourceforge\.net/},
%r{https?://repo1\.maven\.org/},
%r{https?://central\.sonatype\.com/},
%r{https?://boostorg\.jfrog\.io/},
%r{https?://www\.sqlite\.org/},
%r{https?://download\.videolan\.org/},
@@ -142,17 +142,6 @@ RSpec.describe ExpoCocoaPodsProxy do
downloader.download_file(path.to_s)
end
it 'fetches Sonatype snapshot artifacts via proxy' do
path = Pathname.new('some/fake/path')
url = 'https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/react-native-artifacts/0.82.0-SNAPSHOT/maven-metadata.xml'
opts = {}
downloader = Pod::Downloader::Http.new(path, url, opts)
expect(downloader).to receive(:download_file_via_proxy).once {}
downloader.download_file(path.to_s)
end
it 'does not proxy when url doesn\'t match an allowlist regex' do
path = Pathname.new('some/fake/path')
url = 'https://not-github.com/expo/test-repo/archive/main.tar.gz'