Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 133 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ on:
description: Optional flow basename, for example f06-session-persistence
required: false
type: string
ios_execution:
description: iOS execution topology
required: true
default: same-host
type: choice
options:
- same-host
- isolation-pair

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.ref }}
Expand All @@ -27,7 +38,9 @@ concurrency:
jobs:
ios-maestro:
name: iOS Maestro acceptance suite
if: github.event_name != 'workflow_dispatch' || inputs.platform == 'all' || inputs.platform == 'ios'
if: >-
github.event_name != 'workflow_dispatch' ||
((inputs.platform == 'all' || inputs.platform == 'ios') && inputs.ios_execution == 'same-host')
runs-on: macos-26
timeout-minutes: 165
steps:
Expand Down Expand Up @@ -71,6 +84,125 @@ jobs:
path: ${{ runner.temp }}/plogkit-e2e
retention-days: 7

ios-isolation-control:
name: Same-host build and acceptance control
if: >-
github.event_name == 'workflow_dispatch' &&
(inputs.platform == 'all' || inputs.platform == 'ios') &&
inputs.ios_execution == 'isolation-pair'
runs-on: macos-26-intel
timeout-minutes: 150
outputs:
package-ready: ${{ steps.package-upload.outcome == 'success' }}
steps:
- uses: actions/checkout@v7.0.1
- uses: pnpm/action-setup@v6.0.10
- uses: actions/setup-node@v7.0.0
with:
node-version-file: .node-version
cache: pnpm
- uses: actions/setup-java@v5.7.0
with:
distribution: temurin
java-version-file: .java-version
- name: Select pinned Xcode
run: sudo xcode-select --switch /Applications/Xcode_26.6.app/Contents/Developer
- run: pnpm install --frozen-lockfile
- name: Install Maestro CLI
run: |
MAESTRO_VERSION="$(< .maestro-version)"
MAESTRO_ARCHIVE="$RUNNER_TEMP/maestro-$MAESTRO_VERSION"
mkdir -p "$MAESTRO_ARCHIVE"
curl -fsSL -o "$MAESTRO_ARCHIVE/maestro.zip" "https://github.com/mobile-dev-inc/maestro/releases/download/cli-$MAESTRO_VERSION/maestro.zip"
curl -fsSL -o "$MAESTRO_ARCHIVE/checksums_sha256.txt" "https://github.com/mobile-dev-inc/maestro/releases/download/cli-$MAESTRO_VERSION/checksums_sha256.txt"
(cd "$MAESTRO_ARCHIVE" && shasum -a 256 -c checksums_sha256.txt)
unzip -q "$MAESTRO_ARCHIVE/maestro.zip" -d "$MAESTRO_ARCHIVE"
echo "$MAESTRO_ARCHIVE/maestro/bin" >> "$GITHUB_PATH"
- name: Build sealed iOS acceptance package
id: package-build
run: pnpm e2e:ios --build-package "$RUNNER_TEMP/plogkit-ios-package"
timeout-minutes: 60
env:
E2E_ARTIFACTS_DIR: ${{ runner.temp }}/plogkit-e2e-build-private
- name: Run same-host iOS acceptance control
run: pnpm e2e:ios --accept-package "$RUNNER_TEMP/plogkit-ios-package"
timeout-minutes: 45
env:
E2E_ARTIFACTS_DIR: ${{ runner.temp }}/plogkit-e2e-control-private
E2E_PUBLIC_ARTIFACTS_DIR: ${{ runner.temp }}/plogkit-e2e-control
E2E_FLOW: ${{ inputs.flow }}
E2E_IOS_RUNNER_LABEL: macos-26-intel
- name: Upload sealed iOS acceptance package
id: package-upload
if: ${{ always() && steps.package-build.outcome == 'success' }}
uses: actions/upload-artifact@v7.0.1
with:
name: ios-sealed-acceptance-package
compression-level: 1
if-no-files-found: error
path: ${{ runner.temp }}/plogkit-ios-package
retention-days: 1
- name: Upload same-host observations and failure artifacts
if: always()
uses: actions/upload-artifact@v7.0.1
with:
name: ios-same-host-observations
if-no-files-found: warn
path: ${{ runner.temp }}/plogkit-e2e-control
retention-days: 7

ios-isolation-fresh-host:
name: Fresh-host packaged acceptance experiment
needs: ios-isolation-control
if: ${{ always() && needs.ios-isolation-control.outputs.package-ready == 'true' }}
runs-on: macos-26-intel
timeout-minutes: 90
steps:
- uses: actions/checkout@v7.0.1
- uses: pnpm/action-setup@v6.0.10
- uses: actions/setup-node@v7.0.0
with:
node-version-file: .node-version
cache: pnpm
- uses: actions/setup-java@v5.7.0
with:
distribution: temurin
java-version-file: .java-version
- name: Select pinned Xcode
run: sudo xcode-select --switch /Applications/Xcode_26.6.app/Contents/Developer
- run: pnpm install --frozen-lockfile
- name: Download sealed iOS acceptance package
uses: actions/download-artifact@v8.0.1
with:
name: ios-sealed-acceptance-package
path: ${{ runner.temp }}/plogkit-ios-package
- name: Install Maestro CLI
run: |
MAESTRO_VERSION="$(< .maestro-version)"
MAESTRO_ARCHIVE="$RUNNER_TEMP/maestro-$MAESTRO_VERSION"
mkdir -p "$MAESTRO_ARCHIVE"
curl -fsSL -o "$MAESTRO_ARCHIVE/maestro.zip" "https://github.com/mobile-dev-inc/maestro/releases/download/cli-$MAESTRO_VERSION/maestro.zip"
curl -fsSL -o "$MAESTRO_ARCHIVE/checksums_sha256.txt" "https://github.com/mobile-dev-inc/maestro/releases/download/cli-$MAESTRO_VERSION/checksums_sha256.txt"
(cd "$MAESTRO_ARCHIVE" && shasum -a 256 -c checksums_sha256.txt)
unzip -q "$MAESTRO_ARCHIVE/maestro.zip" -d "$MAESTRO_ARCHIVE"
echo "$MAESTRO_ARCHIVE/maestro/bin" >> "$GITHUB_PATH"
- name: Run fresh-host iOS packaged acceptance
run: pnpm e2e:ios --accept-package "$RUNNER_TEMP/plogkit-ios-package"
timeout-minutes: 45
env:
E2E_ARTIFACTS_DIR: ${{ runner.temp }}/plogkit-e2e-fresh-private
E2E_PUBLIC_ARTIFACTS_DIR: ${{ runner.temp }}/plogkit-e2e-fresh
E2E_FLOW: ${{ inputs.flow }}
E2E_IOS_RUNNER_LABEL: macos-26-intel
- name: Upload fresh-host observations and failure artifacts
if: always()
uses: actions/upload-artifact@v7.0.1
with:
name: ios-fresh-host-observations
if-no-files-found: warn
path: ${{ runner.temp }}/plogkit-e2e-fresh
retention-days: 7

android-maestro:
name: Android Maestro acceptance suite
if: github.event_name != 'workflow_dispatch' || inputs.platform == 'all' || inputs.platform == 'android'
Expand Down
4 changes: 3 additions & 1 deletion docs/guides/dev-environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pnpm install

### iOS

- macOS 和完整安装的 Xcode;日常开发可使用与 Expo SDK 57 兼容的版本。本地 L4 E2E 当前要求 Apple Silicon,精确使用 Xcode 26.6(build 17F113),并只构建宿主实际执行的 arm64 Simulator slice。
- macOS 和完整安装的 Xcode;日常开发可使用与 Expo SDK 57 兼容的版本。本地 L4 E2E 精确使用 Xcode 26.6(build 17F113),并只构建宿主实际执行的单一 Simulator slice:Apple Silicon 为 `arm64`,Intel 为 `x86_64`
- 普通开发至少需要一个兼容的 iOS Simulator runtime;L4 E2E 精确使用 iOS 26.5 runtime 与 iPhone 17 Pro device type。
- CocoaPods 1.17.0。

Expand Down Expand Up @@ -161,6 +161,8 @@ pnpm e2e:android

上述三个命令都会在单次 runner 调用中完成 clean prebuild、Release 构建、产物快照、临时设备、安装与验收,不复用 development build 或可变的旧产物。`pnpm e2e` 在一台 Mac 上按 iOS、Android 顺序执行;GitHub 使用两台独立 runner 并行运行相同的平台入口。

GitHub 的 #101 手动实验在现有 `Mobile simulator E2E` workflow 中选择 `ios_execution=isolation-pair`。构建端使用显式 `--build-package` 生成一次 sealed acceptance package;同宿主控制和 fresh-host 实验分别使用 `--accept-package` 消费它。这两个参数是配对实验接口,不是复用本地旧 build 的捷径:package 必须属于同一 workflow、同一 commit、同一仓库输入和同一 x86_64/Xcode/runtime/Release 构建契约,任一身份或内容 hash 不符都会在创建设备前失败。普通 `pnpm e2e:ios` 仍保持单进程完整事务。

定位已知失败时可只运行一条 flow;它仍走完整 Release 构建与设备生命周期:

```bash
Expand Down
3 changes: 3 additions & 0 deletions docs/guides/testing-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ Maestro 在 iOS Simulator 和 Android Emulator 上驱动 clean Release standalon

每个平台只构建并冻结一份 Release 产物,随后在一个临时设备和一个 Maestro workspace 中串行执行 `e2e/config.yaml` 配置的顶层 flows。每条 flow 按 Maestro 的平台语义清空 App 数据,但不重新构建或更换受测产物;同一 flow 内可以保留数据验证生命周期。系统相册等设备级状态跨 flow 保留,因此 fixture 只注入一次,会改变系统状态的 Export 固定最后执行。任一 flow 失败都立即终止;timeout 只负责有界结束失控进程,不承担失败恢复。

Issue #101 的手动 `isolation-pair` 模式是尚未进入正式门禁的执行面实验。它在 x86_64 `macos-26-intel` 上只构建一次 host-native Release slice,把 App、dSYM、flows、subflows、fixtures 与输入 provenance 封装为同一 sealed acceptance package;同宿主控制组和全新宿主实验组都必须先校验 commit、仓库输入、执行契约、archive digest 与 payload digest,再消费该包。控制组失败不得阻止已经发布的同一包进入 fresh-host 组,两个首轮结果都计入,均不 retry。该模式在形成配对证据并由新 ADR 修订 ADR 0042 前,不替换定时或普通手动 L4。

## Scenario 可追踪性

已实现 Scenario 必须由 L2/L3 原生测试标题或 L4 Maestro Flow tags 声明至少一项自动化证据,具体格式见 [Spec 规范](../specs/README.md)。绑定是跨层多对多关系,不要求每个 Scenario 拥有独立 Maestro flow,也不维护独立映射清单。
Expand All @@ -76,6 +78,7 @@ L2/L3 层级由测试文件路径推导,L4 由顶层 Flow 路径确定。`pnpm
| ready / 正式 PR 的新提交 | macOS + Ubuntu(并行) | iOS Simulator Debug 与 Android arm64 Debug 原生集成编译 |
| 每周一 02:30(北京时间) | macOS + Ubuntu(并行) | 双端 Release standalone 的完整 Maestro 验收套件 |
| 手动 | macOS / Ubuntu(按选择) | 完整双端或指定平台 / flow |
| 手动隔离实验 | 两台 x86_64 macOS | 同一 sealed iOS package 的同宿主控制与全新宿主验收 |

Draft PR 的每次提交只运行 `pnpm verify`。转为 ready 时触发双端编译检查,此后正式 PR 的每次新提交重新运行全部三项检查。`main` ruleset 要求 PR 和这三项检查全部通过后才能合并,见 [ADR 0016](../adr/0016-git-workflow.md) 和 [ADR 0020](../adr/0020-ci-lifecycle-and-main-ruleset.md)。

Expand Down
Loading
Loading