Skip to content

CI: share iOS port build across iOS workflows via reusable workflow#4837

Merged
shai-almog merged 1 commit intomasterfrom
ci-ios-port-share
May 1, 2026
Merged

CI: share iOS port build across iOS workflows via reusable workflow#4837
shai-almog merged 1 commit intomasterfrom
ci-ios-port-share

Conversation

@liannacasper
Copy link
Copy Markdown
Collaborator

Summary

The biggest remaining iOS performance gap from the original audit: the iOS port is built from scratch in every iOS workflow on every PR (5 builds across scripts-ios.yml, scripts-ios-native.yml, and ios-packaging.yml's 3-row matrix). This PR makes that build run at most once per workflow run, and shares the result across workflows when source hasn't changed.

How

A new reusable workflow .github/workflows/_build-ios-port.yml (workflow_call) runs setup-workspace + build-ios-port. Each of the 3 iOS workflows now has 2 jobs:

build-port (uses _build-ios-port.yml)  →  test job (needs: build-port)

The test job just restores the populated caches and runs build-ios-app.sh + tests. It uses actions/cache/restore@v4 with fail-on-cache-miss: true so a missing cache is a clear error, not silent regression.

The cache that does the heavy lifting

A new cn1-built cache stores the built CN1 + iOS port artifacts:

  • ~/.m2/repository/com/codenameone
  • Themes/
  • Ports/iOSPort/nativeSources/

Keyed on a composite hash of:

  • All Java / Objective-C / theme source under CodenameOne/src, Ports/iOSPort, vm/JavaAPI, vm/ByteCodeTranslator, Themes/
  • Every pom.xml
  • setup-workspace.sh, build-ios-port.sh, build-native-themes.sh, and the reusable workflow itself

No restore-keys — exact match only. A stale artifact set never satisfies a different source state.

The cn1-built cache is restored after the broader ~/.m2/repository cache so it overwrites any stale com/codenameone/ subtree pulled in by the POM-keyed m2 cache.

On cache hit, build-port skips both setup-workspace and build-ios-port and the job finishes in ~1–2 min instead of ~30 min. The 3 test jobs then restore the cache and run.

Cross-workflow benefit (the M6 part)

The cn1-built-${runner.os}-${source-hash} cache key is identical across all 3 iOS workflows. When scripts-ios.yml runs first on a PR and populates the cache, ios-packaging.yml and scripts-ios-native.yml later in the same PR cache-hit and skip their entire iOS port rebuild.

A PR that doesn't touch CN1 / iOS port source at all will hit the cache from master's last successful run.

Expected savings

Rough timings:

Scenario Before After
First run, source changed ~33 min × 3 matrix in parallel = ~37 min wall, ~99 min CPU ~30 min build-port → ~10 min × 3 in parallel = ~40 min wall, ~60 min CPU
Subsequent run, source unchanged ~37 min wall, ~99 min CPU ~2 min build-port → ~10 min × 3 in parallel = ~12 min wall, ~32 min CPU
Cross-workflow on same PR 3 × ~33 min = ~99 min macOS minutes First workflow: ~33 min, others hit cache: ~12 min each → ~57 min total

So the warm-cache path (the common case for typical PRs) goes from ~37 min wall-clock to ~12 min, and three iOS workflows on the same PR collectively go from ~3 × 33 min to ~33 + 12 + 12 min.

Bonus: parparvm-tests.yml self-trigger bug

parparvm-tests.yml's paths: filter didn't include itself, so my changes to that workflow in #4836 never actually triggered the workflow's own validation. Fixed by adding .github/workflows/parparvm-tests.yml to its paths: list.

Risks / things to watch

  • fail-on-cache-miss: true on the test jobs. If the cn1-built cache somehow doesn't survive the build-port → test-job handoff (e.g., GitHub cache eviction in the seconds between jobs), the test jobs fail loudly. This is intentional — silent fall-through could mask a problem.
  • Hash completeness. The source hash covers Java, Objective-C, theme, properties, XML, CSS files plus all POMs and the build scripts. If a build output changes based on a file we didn't include, we'd serve a stale artifact. The composite is conservative; happy to tighten or loosen if you spot a gap.
  • First-run wall-clock is slightly slower (~40 min vs ~37 min) because build-port is serial before the matrix. The CPU savings (~40 min less per first run) offset that, and every subsequent run is dramatically faster.
  • Within-PR cancellation. The reusable workflow has its own concurrency: group (mac-ios-port-${{ github.workflow }}-${{ github.ref_name }}) so a new push cancels the in-flight build-port too.

Test plan

  • All 5 modified/new workflows parse (verified locally with yaml.safe_load).
  • First PR run (cold cache): all 3 iOS workflows succeed; build-port does the full build, test jobs find the cn1-built cache and skip ahead to build-ios-app.sh.
  • Second push on same PR (warm cache): build-port logs "cn1-built cache HIT — setup-workspace and build-ios-port were skipped." Test jobs run as before.
  • Verify cross-workflow share: if scripts-ios.yml runs first then ios-packaging.yml runs later on the same PR, ios-packaging.yml's build-port logs a cache hit.
  • Confirm parparvm-tests.yml runs on this PR (since it modifies .github/workflows/parparvm-tests.yml, which is now in the paths filter).

Out of scope (kept as future work)

  • M3/M4: Dedupe the Maven + Ant double-build inside pr.yml.
  • L6: Drop redundant push: triggers on test-only workflows.
  • Apply the same reusable-workflow pattern to scripts-android.yml + scripts-javase.yml.

🤖 Generated with Claude Code

@github-actions
Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: Vale failed (exit code 2) (report)
  • Image references: No unused images detected (report)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@github-actions
Copy link
Copy Markdown
Contributor

Cloudflare Preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 644 total, 0 failed, 2 skipped

Benchmark Results

  • Execution Time: 10622 ms

  • Hotspots (Top 20 sampled methods):

    • 23.99% java.lang.String.indexOf (445 samples)
    • 19.14% java.util.ArrayList.indexOf (355 samples)
    • 18.27% com.codename1.tools.translator.Parser.isMethodUsed (339 samples)
    • 4.69% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (87 samples)
    • 4.53% java.lang.Object.hashCode (84 samples)
    • 2.86% java.lang.System.identityHashCode (53 samples)
    • 1.73% com.codename1.tools.translator.ByteCodeClass.updateAllDependencies (32 samples)
    • 1.67% com.codename1.tools.translator.BytecodeMethod.optimize (31 samples)
    • 1.56% com.codename1.tools.translator.ByteCodeClass.markDependent (29 samples)
    • 1.35% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (25 samples)
    • 1.29% com.codename1.tools.translator.ByteCodeClass.calcUsedByNative (24 samples)
    • 1.29% com.codename1.tools.translator.BytecodeMethod.appendMethodSignatureSuffixFromDesc (24 samples)
    • 1.02% java.lang.StringBuilder.append (19 samples)
    • 0.65% com.codename1.tools.translator.Parser.cullMethods (12 samples)
    • 0.65% com.codename1.tools.translator.BytecodeMethod.isMethodUsedByNative (12 samples)
    • 0.59% com.codename1.tools.translator.BytecodeMethod.appendCMethodPrefix (11 samples)
    • 0.59% sun.nio.ch.FileDispatcherImpl.write0 (11 samples)
    • 0.54% com.codename1.tools.translator.Parser.writeOutput (10 samples)
    • 0.49% com.codename1.tools.translator.ByteCodeField.equals (9 samples)
    • 0.49% com.codename1.tools.translator.BytecodeMethod.addInstruction (9 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@shai-almog
Copy link
Copy Markdown
Collaborator

shai-almog commented Apr 30, 2026

Compared 85 screenshots: 85 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 181 seconds

Build and Run Timing

Metric Duration
Simulator Boot 64000 ms
Simulator Boot (Run) 1000 ms
App Install 25000 ms
App Launch 7000 ms
Test Execution 287000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1540.000 ms
Base64 CN1 encode 1535.000 ms
Base64 encode ratio (CN1/native) 0.997x (0.3% faster)
Base64 native decode 1099.000 ms
Base64 CN1 decode 1673.000 ms
Base64 decode ratio (CN1/native) 1.522x (52.2% slower)
Base64 SIMD encode 578.000 ms
Base64 encode ratio (SIMD/native) 0.375x (62.5% faster)
Base64 encode ratio (SIMD/CN1) 0.377x (62.3% faster)
Base64 SIMD decode 489.000 ms
Base64 decode ratio (SIMD/native) 0.445x (55.5% faster)
Base64 decode ratio (SIMD/CN1) 0.292x (70.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 64.000 ms
Image createMask (SIMD on) 15.000 ms
Image createMask ratio (SIMD on/off) 0.234x (76.6% faster)
Image applyMask (SIMD off) 141.000 ms
Image applyMask (SIMD on) 87.000 ms
Image applyMask ratio (SIMD on/off) 0.617x (38.3% faster)
Image modifyAlpha (SIMD off) 162.000 ms
Image modifyAlpha (SIMD on) 79.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.488x (51.2% faster)
Image modifyAlpha removeColor (SIMD off) 173.000 ms
Image modifyAlpha removeColor (SIMD on) 105.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.607x (39.3% faster)
Image PNG encode (SIMD off) 1229.000 ms
Image PNG encode (SIMD on) 1074.000 ms
Image PNG encode ratio (SIMD on/off) 0.874x (12.6% faster)
Image JPEG encode 654.000 ms

Introduces a reusable workflow .github/workflows/_build-ios-port.yml
(workflow_call) that runs scripts/setup-workspace.sh and
scripts/build-ios-port.sh once per workflow run. The three iOS
workflows (scripts-ios.yml, scripts-ios-native.yml, ios-packaging.yml)
now have two jobs each: a build-port job that calls the reusable
workflow, and the existing test job which "needs: build-port" and
restores the populated caches.

A new "cn1-built" cache stores the built CN1 + iOS port artifacts
(~/.m2/repository/com/codenameone, Themes, Ports/iOSPort/nativeSources)
keyed on a composite hash of:
- CN1 / iOS port / VM Java/native source files
- All pom.xml files
- setup-workspace.sh / build-ios-port.sh / build-native-themes.sh /
  the reusable workflow itself

There are no restore-keys on this cache (exact match only) so a stale
artifact set never satisfies a different source state. On cache hit
the build-port job skips both setup-workspace and build-ios-port and
finishes in ~1-2 min instead of ~30 min.

Cross-workflow benefit: the cn1-built cache is keyed only on the
composite source hash, so the first iOS workflow that runs on a PR
populates it and the other two skip their builds. On the same PR
this typically saves ~2x ~30 min macOS minutes.

Within ios-packaging.yml the matrix entries are now 3 small jobs that
just restore caches and run build-ios-app + tests, replacing the prior
3 full duplicate pipelines.

Also fixes a self-trigger bug in parparvm-tests.yml: changes to the
workflow file itself didn't trigger a CI run because the paths filter
omitted .github/workflows/parparvm-tests.yml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog shai-almog merged commit d119e5b into master May 1, 2026
17 checks passed
liannacasper pushed a commit that referenced this pull request May 1, 2026
…llow-up)

#4837 (d119e5b) split the iOS port build into a reusable workflow whose
output is consumed by build-ios via the cn1-built cache. build-ios-metal
was still running setup-workspace.sh + build-ios-port.sh inline, costing
~12 minutes of redundant work per PR even though the source SHA matches
what build-port already cached.

Add the same cache-restore steps build-ios uses:
- needs: build-port so the cache is populated before the Metal job starts
- Compute CN1 source hash + setup-workspace hash (same formulas as the
  reusable workflow + build-ios)
- Cache codenameone-tools, Maven repository, cn1-binaries, CocoaPods/gems
- Restore the cn1-built bundle (~/.m2/repository/com/codenameone, Themes,
  Ports/iOSPort/nativeSources) with fail-on-cache-miss so a missed cache
  surfaces immediately rather than re-running the full build

Drops Setup workspace + Build iOS port from the Metal job (the cache
restore covers both). Timeout reduced from 60 to 45 minutes since the
remaining work is the sample-app build + Metal UI test run, both of
which fit under 30 minutes already.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants