Skip to content

Commit ea49d5f

Browse files
committed
Raise the Flutter iOS floor to 15 and consume CocoaPods TugboatCaptureRuntime.
Publish the Apple runtime to trunk like the Android AAR, then pin the plugin so hosted tugboat no longer stubs iOS native capture.
1 parent 3e6d983 commit ea49d5f

41 files changed

Lines changed: 636 additions & 285 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Lint TugboatCaptureRuntime.podspec (needs Xcode).
2+
name: Apple runtime
3+
4+
on:
5+
pull_request:
6+
types: [opened, synchronize, reopened, ready_for_review]
7+
paths:
8+
- 'TugboatCaptureRuntime.podspec'
9+
- 'Package.swift'
10+
- 'platforms/apple/**'
11+
- 'core/image-processing/**'
12+
- 'tool/ci/lint-apple-runtime.sh'
13+
- '.github/workflows/apple-runtime.yml'
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
pod-lib-lint:
20+
name: pod lib lint
21+
runs-on: macos-latest
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@v4
25+
- name: Lint TugboatCaptureRuntime
26+
run: bash tool/ci/lint-apple-runtime.sh
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Publishes TugboatCaptureRuntime to CocoaPods trunk.
2+
#
3+
# Merging a PR does not publish by itself. This workflow:
4+
# 1. On push to main, dispatches itself when trunk does not have s.version.
5+
# 2. On workflow_dispatch or tag apple-runtime-v*, lints and pod trunk push.
6+
#
7+
# Flutter must not pin a version that is not on trunk yet. After a successful
8+
# push, pin_flutter waits for trunk and opens a Flutter pin PR if needed.
9+
name: Publish Apple runtime
10+
11+
on:
12+
push:
13+
branches:
14+
- main
15+
tags:
16+
- 'apple-runtime-v[0-9]+.[0-9]+.[0-9]+'
17+
workflow_dispatch:
18+
19+
concurrency:
20+
group: publish-apple-${{ github.ref }}
21+
cancel-in-progress: false
22+
23+
jobs:
24+
tag_main:
25+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
26+
runs-on: ubuntu-latest
27+
permissions:
28+
contents: write
29+
actions: write
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v4
33+
with:
34+
fetch-depth: 0
35+
- name: Resolve version
36+
id: version
37+
env:
38+
GH_TOKEN: ${{ github.token }}
39+
run: |
40+
set -euo pipefail
41+
version="$(bash tool/ci/apple-runtime-version.sh)"
42+
tag="apple-runtime-v${version}"
43+
echo "version=${version}" >> "$GITHUB_OUTPUT"
44+
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
45+
echo "TugboatCaptureRuntime version: ${version}"
46+
if bash tool/ci/cocoapods-pod-has-version.sh "${version}"; then
47+
echo "CocoaPods trunk already has TugboatCaptureRuntime ${version}."
48+
echo "should_publish=false" >> "$GITHUB_OUTPUT"
49+
exit 0
50+
fi
51+
echo "should_publish=true" >> "$GITHUB_OUTPUT"
52+
- name: Create tag and dispatch publish
53+
if: steps.version.outputs.should_publish == 'true'
54+
env:
55+
GH_TOKEN: ${{ github.token }}
56+
VERSION: ${{ steps.version.outputs.version }}
57+
TAG: ${{ steps.version.outputs.tag }}
58+
run: |
59+
set -euo pipefail
60+
git fetch --tags origin
61+
if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
62+
echo "Tag ${TAG} already exists."
63+
else
64+
git config user.name "github-actions[bot]"
65+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
66+
git tag -a "${TAG}" -m "TugboatCaptureRuntime ${VERSION}"
67+
git push origin "${TAG}"
68+
fi
69+
for attempt in 1 2 3 4 5 6 7 8; do
70+
if gh workflow run publish-apple.yml --ref "${TAG}"; then
71+
echo "Dispatched publish-apple.yml on ${TAG}."
72+
exit 0
73+
fi
74+
echo "Dispatch failed (attempt ${attempt}); waiting for tag/workflow to appear."
75+
sleep 5
76+
done
77+
echo "::error::Could not dispatch publish-apple.yml on ${TAG}."
78+
exit 1
79+
80+
publish:
81+
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/apple-runtime-v')
82+
runs-on: macos-latest
83+
permissions:
84+
contents: read
85+
steps:
86+
- name: Checkout
87+
uses: actions/checkout@v4
88+
- name: Verify version
89+
id: version
90+
run: |
91+
set -euo pipefail
92+
version="$(bash tool/ci/apple-runtime-version.sh)"
93+
echo "version=${version}" >> "$GITHUB_OUTPUT"
94+
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
95+
echo "Publishing TugboatCaptureRuntime ${version} from workflow_dispatch."
96+
exit 0
97+
fi
98+
expected_tag="apple-runtime-v${version}"
99+
if [[ "${GITHUB_REF_NAME}" != "${expected_tag}" ]]; then
100+
echo "::error::Git tag ${GITHUB_REF_NAME} must be ${expected_tag} to publish TugboatCaptureRuntime ${version}."
101+
exit 1
102+
fi
103+
- name: Lint podspec
104+
run: bash tool/ci/lint-apple-runtime.sh
105+
- name: Publish to CocoaPods trunk
106+
env:
107+
VERSION: ${{ steps.version.outputs.version }}
108+
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
109+
run: |
110+
set -euo pipefail
111+
if [[ -z "${COCOAPODS_TRUNK_TOKEN}" ]]; then
112+
echo "COCOAPODS_TRUNK_TOKEN is not configured. Skipping trunk publish."
113+
echo "Create a CocoaPods trunk session and add the token as a repository secret."
114+
exit 0
115+
fi
116+
if bash tool/ci/cocoapods-pod-has-version.sh "${VERSION}"; then
117+
echo "TugboatCaptureRuntime ${VERSION} is already on CocoaPods trunk."
118+
exit 0
119+
fi
120+
pod trunk push TugboatCaptureRuntime.podspec --allow-warnings --verbose
121+
122+
pin_flutter:
123+
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/apple-runtime-v')
124+
needs: publish
125+
runs-on: ubuntu-latest
126+
permissions:
127+
contents: write
128+
pull-requests: write
129+
steps:
130+
- name: Checkout
131+
uses: actions/checkout@v4
132+
with:
133+
fetch-depth: 0
134+
token: ${{ secrets.GITHUB_TOKEN }}
135+
- name: Open Flutter Apple pin PR
136+
env:
137+
GH_TOKEN: ${{ github.token }}
138+
run: bash tool/ci/open-flutter-apple-pin-pr.sh

.github/workflows/version-bump-check.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ jobs:
2121
BASE_SHA: ${{ github.event.pull_request.base.sha }}
2222
run: bash tool/ci/check-version-policy.sh
2323

24+
apple-pin-published:
25+
name: Flutter Apple pin is on CocoaPods trunk
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@v4
30+
- name: Require pinned TugboatCaptureRuntime on trunk
31+
run: bash tool/ci/check-flutter-apple-pin-published.sh
32+
2433
license:
2534
name: AGPL-3.0-only license
2635
runs-on: ubuntu-latest

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The Tugboat CLI is maintained separately.
1616
| --- | --- |
1717
| `core/image-processing` | Portable C++ CPU core (C ABI) |
1818
| `platforms/android` | `com.gettugboat.sdk:capture-runtime` AAR |
19-
| `platforms/apple` | Apple runtime (`TugboatCaptureRuntime` 0.1.0, unpublished) |
19+
| `platforms/apple` | Apple runtime (`TugboatCaptureRuntime` 0.1.0, CocoaPods / SwiftPM) |
2020
| `sdks/flutter/packages/tugboat` | Flutter adapter / plugin ([pub.dev](https://pub.dev/packages/tugboat)) |
2121
| `sdks/flutter/packages/tugboat_dio` | Dio network evidence ([pub.dev](https://pub.dev/packages/tugboat_dio)) |
2222
| `sdks/flutter/packages/tugboat/example` | Demo app (not published) |
@@ -25,9 +25,8 @@ The Tugboat CLI is maintained separately.
2525
| `tool/ci` | Host test, generate, and release-control scripts |
2626

2727
Do not copy the C++ core into the published pub package. The Flutter Android
28-
plugin depends on Maven Central `com.gettugboat.sdk:capture-runtime`. Apple
29-
native capture still compiles `platforms/apple` sources in the monorepo;
30-
published pub checkouts without that tree stub iOS native capture.
28+
plugin depends on Maven Central `com.gettugboat.sdk:capture-runtime`. The iOS
29+
plugin depends on CocoaPods `TugboatCaptureRuntime` and requires iOS 15.
3130

3231
## Capture backends
3332

docs/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@ gates, canaries) are not listed below.
5858

5959
## Current compatibility
6060

61-
- package version: `0.8.14` (`0.8.x` patch; Android plugin uses Maven Central
62-
`capture-runtime` `0.1.0`);
61+
- package version: `0.8.15` (`0.8.x` patch; Android plugin uses Maven Central
62+
`capture-runtime` `0.1.0`; iOS plugin uses CocoaPods `TugboatCaptureRuntime`
63+
`0.1.0` and requires iOS 15);
6364
- native runtime: `capture-runtime` `0.1.0` on Maven Central;
65+
`TugboatCaptureRuntime` `0.1.0` on CocoaPods trunk;
6466
- planned first native-default adapter: Flutter `0.9.0` after privacy and performance gates;
6567
- session JSON schema: `10`;
6668
- fingerprint schema: `6`;

docs/architecture/repository-map.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@ package references sources under `platforms/apple` and
2121
Do not copy the C++ core into the published pub package. Files above a
2222
published pub archive are not part of that archive. The Flutter Android
2323
plugin depends on Maven Central `com.gettugboat.sdk:capture-runtime`. The
24-
iOS plugin compiles Apple runtime sources when `platforms/apple` resolves;
25-
published pub archives omit that tree and stub iOS native capture.
24+
iOS plugin depends on CocoaPods `TugboatCaptureRuntime` and requires iOS 15.
2625
Native Android apps consume Maven Central or the local Maven AAR; native
27-
Apple apps still use the root CocoaPod / SwiftPM package.
26+
Apple apps use the root CocoaPod / SwiftPM package.
2827

2928
Ownership rules: [repository-scope.md](repository-scope.md).
3029
Build commands: [common-build.md](../integration/common-build.md).

docs/architecture/repository-scope.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ the monorepo layout.
1717
| --- | --- | --- |
1818
| `core/image-processing` | Portable C++ CPU core | Yes |
1919
| `platforms/android/capture-runtime` | Android AAR `com.gettugboat.sdk:capture-runtime` | Yes |
20-
| `platforms/apple` | Swift/ObjC++ runtime | Yes (experimental iOS CPU; unpublished) |
20+
| `platforms/apple` | Swift/ObjC++ runtime | Yes (experimental iOS CPU; CocoaPods `0.1.0`) |
2121
| `sdks/flutter` | Flutter adapter (`tugboat`, `tugboat_dio`) | Yes (opt-in native Android) |
2222
| `sdks/react-native` | Future adapter placeholder | README only |
2323

@@ -81,15 +81,15 @@ JPEG, and SHA-256. Root `Package.swift` and
8181
`TugboatCaptureRuntime.podspec` stay at the repository root.
8282

8383
The Flutter Apple plugin captures the Flutter engine surface (`engineSurface`
84-
coverage). It is experimental and unpublished. iOS 15 is the native floor;
85-
older OS versions report `unsupportedApi` and Flutter falls back.
84+
coverage). iOS 15 is the native floor; the plugin deployment target matches.
8685

8786
## Flutter adapter scope
8887

89-
`package:tugboat` stays the public Dart API. Native Android capture is
90-
available as an opt-in on `0.8.14` via Maven Central `capture-runtime`
91-
`0.1.0`. This line does not enable native capture by default; `0.9.0` is
92-
still the planned native-default adapter.
88+
`package:tugboat` stays the public Dart API. Native Android and iOS capture
89+
are available as an opt-in on `0.8.15` via Maven Central `capture-runtime`
90+
`0.1.0` and CocoaPods `TugboatCaptureRuntime` `0.1.0`. This line does not
91+
enable native capture by default; `0.9.0` is still the planned native-default
92+
adapter.
9393

9494
In scope for the Android and Apple CPU betas:
9595

@@ -102,8 +102,8 @@ In scope for the Android and Apple CPU betas:
102102
- Android: the Flutter plugin depends on Maven Central
103103
`com.gettugboat.sdk:capture-runtime:0.1.0`. Native Android apps use the
104104
same coordinate, or `.local-maven` for unpublished runtime work. iOS: the
105-
plugin compiles `TugboatCaptureRuntime` sources; the root CocoaPod remains
106-
for native Apple apps.
105+
plugin depends on CocoaPods `TugboatCaptureRuntime` `0.1.0` and requires
106+
iOS 15. The example app path-overrides the pod to this repository.
107107

108108
`TugboatCaptureBoundary` stays. Flutter may still supply mask geometry and
109109
capture scheduling.

docs/integration/apple-development.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ Experimental CPU path for iOS 15+. Default Flutter capture remains
1414
| `platforms/apple/Sources/TugboatImageCoreBridge` | Objective-C++ bridge to the C ABI |
1515
| `platforms/apple/Tests` | XCTest (MaskMapper, runtime constants) |
1616
| `platforms/apple/Sample` | Usage notes, not a full Xcode app |
17-
| `sdks/flutter/packages/tugboat/ios` | Pigeon plugin; compiles Apple sources via `NativeRuntime` when present |
17+
| `sdks/flutter/packages/tugboat/ios` | Pigeon plugin; depends on CocoaPods `TugboatCaptureRuntime` |
1818

1919
Do not copy `core/image-processing` into the published pub package. The
20-
Flutter plugin podspec does not depend on unpublished `TugboatCaptureRuntime`.
21-
Monorepo checkouts compile Apple runtime sources through
22-
`ios/NativeRuntime` (symlinks into `platforms/apple` and `core/`). The plugin
23-
deployment target stays iOS 12; native capture still reports unsupported
24-
below iOS 15.
20+
Flutter plugin depends on CocoaPods `TugboatCaptureRuntime` `0.1.0` and
21+
requires iOS 15. The example app path-overrides the pod to this repository.
22+
Native capture still reports unsupported below iOS 15 on devices that somehow
23+
run a lower OS; hosts must set iOS 15 as the floor.
2524

2625
## Capture
2726

@@ -59,6 +58,6 @@ Requires Xcode. This Linux CI host cannot compile for simulator or device.
5958
swift test --package-path . # needs Xcode / iOS SDK
6059
```
6160

62-
Do not publish `TugboatCaptureRuntime` 0.1.0 until device privacy rows pass.
63-
The live-layer path uses Flutter's existing Metal readback. It does not call
64-
private Flutter selectors directly.
61+
`publish-apple.yml` pushes `TugboatCaptureRuntime` `0.1.0` to CocoaPods trunk
62+
when `COCOAPODS_TRUNK_TOKEN` is set. The live-layer path uses Flutter's
63+
existing Metal readback. It does not call private Flutter selectors directly.

docs/integration/common-build.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ bash tool/ci/verify-native-capture-pigeon.sh
1313
The Android AAR publishes to untracked `.local-maven/` for the standalone
1414
native sample. Hosted Maven Central publication is documented in
1515
[process.md](../releases/process.md). The Flutter plugin depends on
16-
`com.gettugboat.sdk:capture-runtime:0.1.0` from Maven Central.
16+
`com.gettugboat.sdk:capture-runtime:0.1.0` from Maven Central. The iOS plugin
17+
depends on CocoaPods `TugboatCaptureRuntime` `0.1.0` and requires iOS 15.
1718

1819
Release-control scripts (also run in CI):
1920

docs/integration/flutter-development.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ Screenshot pixels still default to `RepaintBoundary`. Enable native CPU
2222
capture only through
2323
`TugboatScreenshotCaptureBackend.nativeCpuExperimental`. In the monorepo and
2424
on pub.dev, the Android plugin depends on Maven Central
25-
`com.gettugboat.sdk:capture-runtime:0.1.0`. The iOS plugin still compiles
26-
`TugboatCaptureRuntime` from source when `platforms/apple` is present;
27-
published pub checkouts stub iOS native capture. See
25+
`com.gettugboat.sdk:capture-runtime:0.1.0`. The iOS plugin depends on
26+
CocoaPods `TugboatCaptureRuntime` `0.1.0` and requires iOS 15. The example
27+
app path-overrides that pod to this repository. See
2828
[native-cpu-experimental.md](native-cpu-experimental.md) and
2929
[apple-development.md](apple-development.md).
3030

0 commit comments

Comments
 (0)