Skip to content

Fix 0.19.0 iOS binary package usage #1505

Fix 0.19.0 iOS binary package usage

Fix 0.19.0 iOS binary package usage #1505

Workflow file for this run

name: UI Tests
on:
push:
branches: [main]
paths:
- 'Example/OpenSwiftUIUITests/**'
- 'Example/setup.sh'
- 'Example/mise*.toml'
- '.github/workflows/uitests.yml'
- '.github/actions/uitests/action.yml'
workflow_dispatch:
inputs:
platform:
description: Platform to test
required: true
default: all
type: choice
options:
- all
- ios
- macos
update_reference:
description: Update reference images before running UI tests
required: false
default: false
type: boolean
configuration:
description: UI test configuration set
required: false
default: default
type: choice
options:
- default
- all
- swiftui-renderer-ag
- swiftui-renderer-iag
- openswiftui-renderer-ag
- openswiftui-renderer-iag
issue_comment:
types: [created]
permissions:
contents: read
issues: read
pull-requests: read
statuses: write
jobs:
prepare_uitests:
name: Prepare UI tests
runs-on: ubuntu-latest
outputs:
repository: ${{ steps.prepare.outputs.repository }}
ref: ${{ steps.prepare.outputs.ref }}
ios-requested: ${{ steps.prepare.outputs.ios-requested }}
macos-requested: ${{ steps.prepare.outputs.macos-requested }}
update-reference: ${{ steps.prepare.outputs.update-reference }}
status-enabled: ${{ steps.prepare.outputs.status-enabled }}
configuration-matrix: ${{ steps.prepare.outputs.configuration-matrix }}
steps:
- name: Prepare requested UI tests
id: prepare
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
const allowedAssociations = new Set(["OWNER", "MEMBER", "COLLABORATOR"]);
const configurations = {
"swiftui-renderer-ag": {
id: "swiftui-renderer-ag",
swiftui_renderer: 1,
attributegraph: 1,
compute: 0,
},
"swiftui-renderer-iag": {
id: "swiftui-renderer-iag",
swiftui_renderer: 1,
attributegraph: 0,
compute: 1,
},
"openswiftui-renderer-ag": {
id: "openswiftui-renderer-ag",
swiftui_renderer: 0,
attributegraph: 1,
compute: 0,
},
"openswiftui-renderer-iag": {
id: "openswiftui-renderer-iag",
swiftui_renderer: 0,
attributegraph: 0,
compute: 1,
},
};
const configurationAliases = {
"all-configs": "all",
"sui-ag": "swiftui-renderer-ag",
"sui-iag": "swiftui-renderer-iag",
"osui-ag": "openswiftui-renderer-ag",
"osui-iag": "openswiftui-renderer-iag",
};
const defaultConfigurationIds = [
"swiftui-renderer-ag",
"openswiftui-renderer-iag",
];
const allConfigurationIds = [
"swiftui-renderer-ag",
"swiftui-renderer-iag",
"openswiftui-renderer-ag",
"openswiftui-renderer-iag",
];
const outputs = {
repository: "",
ref: "",
"ios-requested": "false",
"macos-requested": "false",
"update-reference": "false",
"status-enabled": "false",
"configuration-matrix": JSON.stringify(defaultConfigurationIds.map((id) => configurations[id])),
};
const setOutputs = () => {
for (const [name, value] of Object.entries(outputs)) {
core.setOutput(name, value);
}
};
const requestPlatforms = (requested) => {
if (requested === "all") {
return ["ios", "macos"];
}
if (requested === "ios" || requested === "macos") {
return [requested];
}
return [];
};
const requestConfigurations = (requested) => {
const normalized = configurationAliases[requested] ?? requested;
if (normalized === "default") {
return defaultConfigurationIds.map((id) => configurations[id]);
}
if (normalized === "all") {
return allConfigurationIds.map((id) => configurations[id]);
}
if (configurations[normalized]) {
return [configurations[normalized]];
}
return [];
};
const applyRequest = (platforms, configurationMatrix, updateRequested) => {
outputs["ios-requested"] = String(platforms.includes("ios"));
outputs["macos-requested"] = String(platforms.includes("macos"));
outputs["update-reference"] = String(updateRequested);
outputs["configuration-matrix"] = JSON.stringify(configurationMatrix);
};
if (context.eventName === "push") {
applyRequest(requestPlatforms("all"), requestConfigurations("default"), false);
setOutputs();
return;
}
if (context.eventName === "workflow_dispatch") {
const inputs = context.payload.inputs ?? {};
const platforms = requestPlatforms(inputs.platform ?? "all");
const configurationMatrix = requestConfigurations(inputs.configuration ?? "default");
if (platforms.length === 0 || configurationMatrix.length === 0) {
setOutputs();
return;
}
applyRequest(platforms, configurationMatrix, inputs.update_reference === "true");
setOutputs();
return;
}
const issue = context.payload.issue;
const comment = context.payload.comment;
if (
context.eventName !== "issue_comment" ||
!issue?.pull_request ||
!comment ||
!allowedAssociations.has(comment.author_association)
) {
setOutputs();
return;
}
const body = comment.body.trim();
const args = body.split(/\s+/);
if (args[0] !== "/uitest") {
setOutputs();
return;
}
let updateRequested = false;
let requestedPlatform = "all";
let requestedConfiguration = "default";
for (const rawArg of args.slice(1)) {
const arg = rawArg.toLowerCase();
if (arg === "update") {
updateRequested = true;
} else if (arg === "ios" || arg === "macos" || arg === "all") {
requestedPlatform = arg;
} else if (arg.startsWith("config=") || arg.startsWith("configuration=")) {
requestedConfiguration = arg.split("=", 2)[1] || "default";
} else if (
arg === "default" ||
arg === "all-configs" ||
configurations[arg] ||
configurationAliases[arg]
) {
requestedConfiguration = arg;
} else {
core.warning(`Ignoring unsupported /uitest argument: ${rawArg}`);
setOutputs();
return;
}
}
const platforms = requestPlatforms(requestedPlatform);
const configurationMatrix = requestConfigurations(requestedConfiguration);
if (platforms.length === 0 || configurationMatrix.length === 0) {
setOutputs();
return;
}
const { data: pull } = await github.rest.pulls.get({ owner, repo, pull_number: issue.number });
outputs.repository = pull.head.repo.full_name;
outputs.ref = pull.head.sha;
applyRequest(platforms, configurationMatrix, updateRequested);
const sameRepository = pull.head.repo.full_name === `${owner}/${repo}`;
if (!sameRepository) {
core.warning(`Skipping PR commit status creation for fork PR ${pull.head.repo.full_name}@${pull.head.sha}.`);
setOutputs();
return;
}
outputs["status-enabled"] = "true";
const details_url = `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${context.runId}`;
for (const platform of platforms) {
const label = platform === "ios" ? "iOS" : "macOS";
for (const configuration of configurationMatrix) {
const description = `Queued by @${comment.user.login} with ${body}.`.slice(0, 140);
await github.rest.repos.createCommitStatus({
owner,
repo,
sha: pull.head.sha,
state: "pending",
target_url: details_url,
description,
context: `UI Tests / ${label} / ${configuration.id}`,
});
}
}
setOutputs();
ios_uitest:
name: Execute UI tests on iOS (${{ matrix.configuration.id }})
needs: prepare_uitests
if: >-
needs.prepare_uitests.outputs.ios-requested == 'true'
strategy:
fail-fast: false
matrix:
os: [macos-15]
xcode-version: ["26.3"]
release: [2024]
ios-version: ["18.5"]
configuration: ${{ fromJSON(needs.prepare_uitests.outputs.configuration-matrix) }}
include:
- ios-version: "18.5"
ios-simulator-name: "iPhone 16 Pro"
# Limit to self-hosted to reduce action cost
runs-on:
- self-hosted
- ${{ matrix.os }}
env:
STATUS_CONTEXT: UI Tests / iOS / ${{ matrix.configuration.id }}
STATUS_ENABLED: ${{ needs.prepare_uitests.outputs.status-enabled }}
STATUS_SHA: ${{ needs.prepare_uitests.outputs.ref }}
CONFIGURATION_NAME: ${{ matrix.configuration.id }}
OPENSWIFTUI_WERROR: 0 # Disable it to avoid enable OAG's werror and hit conflicts
OPENSWIFTUI_OPENATTRIBUTESHIMS_ATTRIBUTEGRAPH: ${{ matrix.configuration.attributegraph }}
OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE: ${{ matrix.configuration.compute }}
OPENSWIFTUI_COMPATIBILITY_TEST: 0
OPENSWIFTUI_SWIFT_LOG: 0
OPENSWIFTUI_SWIFT_CRYPTO: 0
OPENSWIFTUI_TARGET_RELEASE: ${{ matrix.release }}
OPENSWIFTUI_USE_LOCAL_DEPS: 1
OPENSWIFTUI_LINK_TESTING: 0
OPENSWIFTUI_SWIFTUI_RENDERER: ${{ matrix.configuration.swiftui_renderer }}
GH_TOKEN: ${{ github.token }}
steps:
- name: Mark PR status running
if: github.event_name == 'issue_comment' && env.STATUS_ENABLED == 'true' && env.STATUS_SHA != ''
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
await github.rest.repos.createCommitStatus({
owner,
repo,
sha: process.env.STATUS_SHA,
state: "pending",
target_url: `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${context.runId}`,
description: `iOS UI tests (${process.env.CONFIGURATION_NAME}) are running.`,
context: process.env.STATUS_CONTEXT,
});
- uses: actions/checkout@v4
with:
repository: ${{ needs.prepare_uitests.outputs.repository || github.repository }}
ref: ${{ needs.prepare_uitests.outputs.ref || github.sha }}
- name: Run UI Tests
id: run-tests
uses: ./.github/actions/uitests
with:
xcode-version: ${{ matrix.xcode-version }}
platform: ios
destination: "platform=iOS Simulator,OS=${{ matrix.ios-version }},name=${{ matrix.ios-simulator-name }}"
artifact-name: ios-uitest-snapshots-${{ matrix.ios-version }}-${{ matrix.configuration.id }}
update-reference: ${{ needs.prepare_uitests.outputs.update-reference == 'true' }}
compute: ${{ matrix.configuration.compute == 1 }}
- name: Fail if tests failed
if: steps.run-tests.outputs.test-result == 'failure'
run: exit 1
- name: Complete PR status
if: always() && github.event_name == 'issue_comment' && env.STATUS_ENABLED == 'true' && env.STATUS_SHA != ''
uses: actions/github-script@v7
env:
JOB_STATUS: ${{ job.status }}
with:
script: |
const { owner, repo } = context.repo;
const state = process.env.JOB_STATUS === "success" ? "success" :
process.env.JOB_STATUS === "cancelled" ? "error" :
"failure";
const description = process.env.JOB_STATUS === "cancelled" ?
"iOS UI tests were cancelled." :
`iOS UI tests (${process.env.CONFIGURATION_NAME}) completed with ${state}.`;
await github.rest.repos.createCommitStatus({
owner,
repo,
sha: process.env.STATUS_SHA,
state,
target_url: `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${context.runId}`,
description,
context: process.env.STATUS_CONTEXT,
});
macos_uitest:
name: Execute UI tests on macOS (${{ matrix.configuration.id }})
needs: prepare_uitests
if: >-
needs.prepare_uitests.outputs.macos-requested == 'true'
strategy:
fail-fast: false
matrix:
os: [macos-15]
xcode-version: ["26.3"]
release: [2024]
configuration: ${{ fromJSON(needs.prepare_uitests.outputs.configuration-matrix) }}
# Limit to self-hosted to reduce action cost
runs-on:
- self-hosted
- ${{ matrix.os }}
env:
STATUS_CONTEXT: UI Tests / macOS / ${{ matrix.configuration.id }}
STATUS_ENABLED: ${{ needs.prepare_uitests.outputs.status-enabled }}
STATUS_SHA: ${{ needs.prepare_uitests.outputs.ref }}
CONFIGURATION_NAME: ${{ matrix.configuration.id }}
OPENSWIFTUI_WERROR: 0
OPENSWIFTUI_OPENATTRIBUTESHIMS_ATTRIBUTEGRAPH: ${{ matrix.configuration.attributegraph }}
OPENSWIFTUI_OPENATTRIBUTESHIMS_COMPUTE: ${{ matrix.configuration.compute }}
OPENSWIFTUI_COMPATIBILITY_TEST: 0
OPENSWIFTUI_SWIFT_LOG: 0
OPENSWIFTUI_SWIFT_CRYPTO: 0
OPENSWIFTUI_TARGET_RELEASE: ${{ matrix.release }}
OPENSWIFTUI_USE_LOCAL_DEPS: 1
OPENSWIFTUI_SWIFTUI_RENDERER: ${{ matrix.configuration.swiftui_renderer }}
GH_TOKEN: ${{ github.token }}
steps:
- name: Mark PR status running
if: github.event_name == 'issue_comment' && env.STATUS_ENABLED == 'true' && env.STATUS_SHA != ''
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
await github.rest.repos.createCommitStatus({
owner,
repo,
sha: process.env.STATUS_SHA,
state: "pending",
target_url: `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${context.runId}`,
description: `macOS UI tests (${process.env.CONFIGURATION_NAME}) are running.`,
context: process.env.STATUS_CONTEXT,
});
- uses: actions/checkout@v4
with:
repository: ${{ needs.prepare_uitests.outputs.repository || github.repository }}
ref: ${{ needs.prepare_uitests.outputs.ref || github.sha }}
- name: Run UI Tests
id: run-tests
uses: ./.github/actions/uitests
with:
xcode-version: ${{ matrix.xcode-version }}
platform: macos
destination: "platform=macOS"
artifact-name: macos-uitest-snapshots-${{ matrix.configuration.id }}
update-reference: ${{ needs.prepare_uitests.outputs.update-reference == 'true' }}
compute: ${{ matrix.configuration.compute == 1 }}
- name: Fail if tests failed
if: steps.run-tests.outputs.test-result == 'failure'
run: exit 1
- name: Complete PR status
if: always() && github.event_name == 'issue_comment' && env.STATUS_ENABLED == 'true' && env.STATUS_SHA != ''
uses: actions/github-script@v7
env:
JOB_STATUS: ${{ job.status }}
with:
script: |
const { owner, repo } = context.repo;
const state = process.env.JOB_STATUS === "success" ? "success" :
process.env.JOB_STATUS === "cancelled" ? "error" :
"failure";
const description = process.env.JOB_STATUS === "cancelled" ?
"macOS UI tests were cancelled." :
`macOS UI tests (${process.env.CONFIGURATION_NAME}) completed with ${state}.`;
await github.rest.repos.createCommitStatus({
owner,
repo,
sha: process.env.STATUS_SHA,
state,
target_url: `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${context.runId}`,
description,
context: process.env.STATUS_CONTEXT,
});