ReviewOS

also looking at this

craft-native/craft

fix(mobile): preserve native editing, navigation, and durable media requests

#369
Merged chris wants to merge codex/native-vertical-scroll-tab-swipes into main
36 files +864 -185

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

.github/workflows/ci.ymlmodified+2-1
Changes to .github/workflows/ci.yml
@@ -1,6 +1,7 @@
11name: CI
22
33on:
4 workflow_call: {}
45 workflow_dispatch:
56 inputs:
67 software_cold_provisioning_diagnostic:
@@ -15,7 +16,7 @@ on:
1516 - main
1617
1718concurrency:
18 group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
19 group: source-ci-${{ github.workflow }}-${{ github.head_ref || github.ref }}
1920 # Finish Main acceptance; newer commits replace the single pending run.
2021 cancel-in-progress: ${{ github.event_name == 'pull_request' }}
2122
.github/workflows/mobile-e2e.ymlmodified+1-0
Changes to .github/workflows/mobile-e2e.yml
@@ -24,6 +24,7 @@ name: Mobile E2E Testing
2424# GitHub Actions does not resolve YAML anchors, so the two path filters are
2525# written out rather than shared. Keep them identical.
2626on:
27 workflow_call: {}
2728 push:
2829 branches: [main]
2930 paths:
.github/workflows/release.ymlmodified+18-2
Changes to .github/workflows/release.yml
@@ -27,6 +27,22 @@ jobs:
2727 - name: Review dependency advisories
2828 run: bun scripts/advisory-policy.ts
2929
30 # Relative reusable workflows check the exact tagged commit. Main may keep
31 # moving while these full suites run; publication remains tied to this tag.
32 source-acceptance:
33 name: accept exact release source
34 needs: validate-release
35 uses: ./.github/workflows/ci.yml
36 permissions:
37 contents: read
38
39 native-acceptance:
40 name: accept exact native runtimes
41 needs: validate-release
42 uses: ./.github/workflows/mobile-e2e.yml
43 permissions:
44 contents: read
45
3046 release-sbom:
3147 needs: validate-release
3248 uses: ./.github/workflows/sbom.yml
@@ -38,7 +54,7 @@ jobs:
3854 pantry:
3955 # The reusable SBOM workflow includes the vulnerability scan. Publishing
4056 # must wait for its existing Critical gate, not merely attach it afterward.
41 needs: [validate-release, release-sbom]
57 needs: [validate-release, release-sbom, source-acceptance, native-acceptance]
4258 name: pantry (${{ matrix.platform.name }})
4359 strategy:
4460 fail-fast: false
@@ -673,7 +689,7 @@ jobs:
673689
674690 publish-release:
675691 name: publish complete verified release
676 needs: [validate-release, verify-release, verify-macos-downloads, verify-desktop-downloads, attach-release-sbom, prepare-npm]
692 needs: [validate-release, source-acceptance, native-acceptance, verify-release, verify-macos-downloads, verify-desktop-downloads, attach-release-sbom, prepare-npm]
677693 runs-on: ubuntu-latest
678694 permissions:
679695 contents: write
scripts/release-workflow.test.tsmodified+38-1
Changes to scripts/release-workflow.test.ts
@@ -196,11 +196,48 @@ test('every publishing path depends on release identity validation', () => {
196196 expect(release.jobs.npm.if).toBe("$")
197197})
198198
199test('tag publication requires the complete source and native acceptance workflows', () => {
200 for (const [name, file] of [['source-acceptance', 'ci.yml'], ['native-acceptance', 'mobile-e2e.yml']]) {
201 const gate = release.jobs[name]
202 expect(gate.uses).toBe(`./.github/workflows/${file}`)
203 expect(needs(gate)).toEqual(['validate-release'])
204 expect(gate.if).toBeUndefined()
205 expect(gate.with).toBeUndefined() // A release cannot request a focused suite.
206 const called = Bun.YAML.parse(readFileSync(join(import.meta.dir, '../.github/workflows', file), 'utf8')) as { on: Record<string, unknown> }
207 expect(called.on.workflow_call).toEqual({}) // No selectors or diagnostic inputs are accepted by callers.
208 }
209 for (const name of ['pantry', 'publish-release']) {
210 expect(needs(release.jobs[name])).toContain('source-acceptance')
211 expect(needs(release.jobs[name])).toContain('native-acceptance')
212 expect(release.jobs[name].if).toBeUndefined()
213 }
214 function prerequisites(name: string, found = new Set<string>()): Set<string> {
215 for (const dependency of needs(release.jobs[name])) {
216 if (found.has(dependency)) continue
217 found.add(dependency)
218 prerequisites(dependency, found)
219 }
220 return found
221 }
222 for (const name of ['pantry', 'publish-release', 'publish-pantry', 'npm', 'notify-registry']) {
223 expect(prerequisites(name).has('source-acceptance')).toBe(true)
224 expect(prerequisites(name).has('native-acceptance')).toBe(true)
225 }
226})
227
228test('called source and native acceptance have independent concurrency groups', () => {
229 const parse = (file: string): { concurrency: { group: string } } => Bun.YAML.parse(readFileSync(join(import.meta.dir, '../.github/workflows', file), 'utf8'))
230 const source = parse('ci.yml').concurrency.group
231 const native = parse('mobile-e2e.yml').concurrency.group
232 expect(source.startsWith('source-ci-')).toBe(true)
233 expect(native.startsWith('mobile-e2e-')).toBe(true)
234})
235
199236test('registry indexing waits for the public release and all of its verification gates', () => {
200237 const notify = release.jobs['notify-registry']
201238 expect(notify).toBeDefined()
202239 expect(needs(notify)).toEqual(['publish-release', 'publish-pantry'])
203 expect(needs(release.jobs['publish-release']).sort()).toEqual(['attach-release-sbom', 'prepare-npm', 'validate-release', 'verify-desktop-downloads', 'verify-macos-downloads', 'verify-release'])
240 expect(needs(release.jobs['publish-release']).sort()).toEqual(['attach-release-sbom', 'native-acceptance', 'prepare-npm', 'source-acceptance', 'validate-release', 'verify-desktop-downloads', 'verify-macos-downloads', 'verify-release'])
204241 expect(release.jobs['publish-release'].if).toBeUndefined() // A failed or skipped gate cannot publish a draft.
205242 expect(notify.if).toBeUndefined() // Default success gating: never bypass failed prerequisites.
206243 expect(steps(notify)).toContain('https://registry.pantry.dev/api/rebuild')