chore: Parallelize CI builds to reduce wall clock time#95
Conversation
- Break monolithic ci composite action into focused actions: lint, build-ios, build-macos, build-tvos, build-watchos, test-swiftpm, contract-tests - Run each as a separate parallel job in ci.yml - Remove Mint in favor of direct brew install swiftlint - Add simulator warmup step for iOS/tvOS/watchOS builds - Use --quick flag for pod lib lint since dedicated build jobs compile - Update release-please, manual-publish, and manual-publish-docs workflows to use new individual composite actions - Add setup-xcode step to build-docs job Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…download Incorporates fix from devin/1772832423-fix-contract-test-version branch. Replaces make contract-tests with launchdarkly/gh-actions/actions/contract-tests@contract-tests-v1.3.0 which properly downloads the test harness. Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
The sse-contract-tests binary does not support the -enable-persistence-tests flag that contract-tests-v1.3.0 enables by default. Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
| run: make start-contract-test-service-bg | ||
|
|
||
| - name: Run contract tests | ||
| uses: launchdarkly/gh-actions/actions/contract-tests@contract-tests-v1.3.0 |
There was a problem hiding this comment.
An action sourced from a third-party repository on GitHub is not pinned to a full length commit SHA. Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload.
⭐ Fixed in commit 8e990e4 ⭐
…ract test version resolution Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
Pins launchdarkly/gh-actions/actions/contract-tests to full commit SHA 5adb11fd6953e1bc35d9cf1fc1b4374c464e3a8b (contract-tests-v1.3.0) to mitigate supply chain risk. Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
…release resolution Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
The upstream sse-contract-tests/downloader/run.sh does not pass the GITHUB_TOKEN to the GitHub API call that resolves partial version strings (e.g. 'v2' -> 'v2.31.0'). This causes rate-limit failures on shared CI runners. Using an inline script that passes the token fixes the issue. Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
…cript (#34) **Requirements** - [x] I have added test coverage for new or changed functionality - [x] I have followed the repository's [pull request submission guidelines](../blob/master/CONTRIBUTING.md#submitting-pull-requests) - [x] I have validated my changes against all supported platform versions **Related issues** The downloader script's `resolve_version()` function calls the GitHub releases API without authentication, causing `403` rate-limit failures on shared CI runners. This was discovered while debugging contract test failures in [launchdarkly/swift-eventsource#95](launchdarkly/swift-eventsource#95). The equivalent script in [sdk-test-harness](https://github.com/launchdarkly/sdk-test-harness/blob/v2/downloader/run.sh) already has token support. **Describe the solution you've provided** Rewrites `downloader/run.sh` to fix the auth bug and add observability and cross-platform support: 1. **GitHub token auth** — passes `GITHUB_TOKEN` (via `Authorization` header) to all API and download requests, avoiding rate limiting. 2. **Cross-platform support** — adds Windows detection (MSYS/MINGW/CYGWIN) with `zip` extraction, `.exe` extension handling, and normalizes `aarch64` → `arm64`. 3. **HTTP request logging** — new `do_curl()` helper logs the request URL, HTTP status line, content-type, and `x-ratelimit-remaining` for every request. 4. **Version resolution logging** — lists all available versions and the resolved version during partial version resolution. 5. **Platform info** — outputs OS type, architecture, and archive name at startup. 6. **Bug fix** — changes `exit` → `return` in `resolve_version()` so it doesn't kill the entire script when called from a `$()` subshell. 7. **Download-only mode** — `PARAMS` is now optional. When omitted, the script downloads and extracts the binary without executing it. Adds a new `test-downloader.yml` CI workflow that verifies the script on Linux x64, Linux arm64, macOS, and Windows — testing partial (`v2`) and full (`v2.31.0`) version resolution in download-only mode, plus a step that invokes the actual [`launchdarkly/gh-actions/actions/contract-tests`](https://github.com/launchdarkly/gh-actions/tree/main/actions/contract-tests) action (with `continue-on-error` since no test service is running). **Suggested review checklist:** - [ ] The `do_curl()` function uses `eval` to construct curl commands (needed for proper header quoting). This matches the `sdk-test-harness` pattern — verify this is acceptable. - [ ] On Windows, the `[ ! -x "${EXECUTABLE}" ]` check may always be true since MSYS doesn't set Unix execute bits on extracted `.exe` files — this would cause re-downloads but is not harmful. Worth considering. - [ ] The `continue-on-error: true` on the `gh-actions/contract-tests` CI step means a download failure there won't fail CI. The direct download steps (partial + full version) are the strict verification; the action step just confirms compatibility. - [ ] Making `PARAMS` optional is a behavioral change. Existing callers (like `gh-actions/contract-tests`) always pass `PARAMS`, so this should be safe, but confirm no consumers rely on the old error behavior. - [ ] All informational output goes to stderr (via `log()`). This is correct for command substitution but means CI logs interleave stderr/stdout. **Describe alternatives you've considered** Could have forked the `gh-actions/contract-tests` action to call a fixed downloader script, but that would leave the bug in this repo for anyone using the script directly. **Additional context** - Devin session: https://app.devin.ai/sessions/2da8679d630649d4be8984e8cab6c980 - Requested by: rlamb@launchdarkly.com <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/launchdarkly/sse-contract-tests/pull/34" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches the release download/execution path and introduces OS-specific branching plus `eval`-based curl invocation, which could break downloads on some runners if edge cases were missed. > > **Overview** > Fixes the `downloader/run.sh` release downloader to be more reliable on CI by **supporting optional `GITHUB_TOKEN` auth** for GitHub API and asset downloads and adding verbose HTTP logging (status, content-type, rate-limit remaining). > > Expands the script to **support Windows shells (MSYS/MINGW/CYGWIN)** via OS/arch detection, `zip` extraction and `.exe` handling, normalizes `aarch64`→`arm64`, and changes behavior so `PARAMS` is optional (download-only mode) while also fixing `resolve_version()` to `return` instead of exiting the whole script. > > Adds a new GitHub Actions workflow `test-downloader.yml` that runs the downloader on Linux x64/arm64, macOS, and Windows, testing both partial (`v2`) and full (`v2.31.0`) version resolution, plus a best-effort compatibility check via `launchdarkly/gh-actions/actions/contract-tests@main`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 2a29832. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
The shared gh-actions/contract-tests action defaults branch to 'v2', but sse-contract-tests uses 'main' as its default branch. Without this, the curl fetch of the downloader script returns a 404. Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
The sse-contract-tests binary does not support the -enable-persistence-tests flag. The shared action defaults this to true, so we must explicitly disable it. Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| version: v2 | ||
| token: ${{ inputs.token }} | ||
| test_service_port: '8000' | ||
| test_harness_port: '8111' |
There was a problem hiding this comment.
This is a default value for the action.
There was a problem hiding this comment.
Removed test_harness_port, stop_service, and enable_persistence_tests since they all match the action's defaults. Pushed in 8155ae5.
| test_harness_port: '8111' | ||
| debug_logging: 'true' | ||
| stop_service: true | ||
| enable_persistence_tests: false |
There was a problem hiding this comment.
Actually, the shared action's action.yml defaults enable_persistence_tests to true (line 27), even though the README says false. Since sse-contract-tests doesn't support the -enable-persistence-tests flag, we need to explicitly set it to false here. I've re-added it.
I did remove test_harness_port and stop_service since those truly are defaults (8111 and true respectively).
Per reviewer feedback, remove test_harness_port, stop_service, and enable_persistence_tests since they match the shared action's defaults. Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
The shared action defaults enable_persistence_tests to true (line 27 of gh-actions/actions/contract-tests/action.yml), not false as the README states. sse-contract-tests doesn't support that flag, so we must explicitly disable it. Co-Authored-By: rlamb@launchdarkly.com <rlamb@launchdarkly.com>
Requirements
Related issues
Ports the relevant CI changes from launchdarkly/ios-client-sdk#481 to swift-eventsource to reduce CI build times. Also incorporates the contract test fix from branch
devin/1772832423-fix-contract-test-version.The contract tests previously failed due to GitHub API rate limiting in the
sse-contract-testsdownloader script. The root cause fix is in launchdarkly/sse-contract-tests#34.Describe the solution you've provided
The monolithic
.github/actions/cicomposite action ran all builds, tests, linting, and contract tests sequentially in a single job. This PR breaks it into focused composite actions that run as separate parallel jobs inci.yml:lintbrew install swiftlint,pod lib lint --quick,swiftlint lintbuild-iosbuild-macosbuild-tvosbuild-watchostest-swiftpmswift testvia SwiftPMcontract-testslaunchdarkly/gh-actions/actions/contract-tests@mainbuild-docssetup-xcode)Other changes:
Mintfiledeleted; swiftlint installed directly viabrew install swiftlintxcrun simctl list devices available) to mitigate intermittent macos-15 runner issues (actions/runner-images#12948)pod spec linttopod lib lint --allow-warnings --quick(skips full build since dedicated jobs already compile all platforms)setup-xcodemoved to workflow level (each job sets up Xcode explicitly)launchdarkly/gh-actions/actions/contract-tests@mainaction (intentionally@main— this is our own organization's action). Configured withbranch: main(sincesse-contract-testsusesmain, notv2, as its default branch) andenable_persistence_tests: false(SSE contract tests don't support that flag). Two known-flaky tests are skipped viaextra_params.release-please.yml,manual-publish.yml,manual-publish-docs.yml) updated to call individual composite actions sequentially (these must remain sequential since they gate the publish step)Checklist for human reviewer
realm/SwiftLint@0.56.2; nowbrew install swiftlintpulls whatever Homebrew provides. Verify this won't cause unexpected lint failures.pod spec linttopod lib lintwith--allow-warnings --quick. Confirmlib lintis appropriate here (validates local podspec vs. published spec).@main— useslaunchdarkly/gh-actions/actions/contract-tests@main(mutable ref) rather than a pinned SHA. This is intentional per repo owner direction — the action is from LaunchDarkly's own organization.Describe alternatives you've considered
Additional context
Note
Medium Risk
CI-only changes, but they restructure job orchestration, switch lint/tool installation sources, and move contract testing to an external action ref (
@main), any of which can cause unexpected pipeline failures or behavior changes.Overview
Refactors GitHub Actions CI from a single composite (
.github/actions/ci) into multiple focused composite actions (lint, per-platform Xcode builds/tests, SwiftPM tests, and contract tests) and updatesci.ymlto run them as separate parallel jobs onmacos-15(with Xcode 16.4 setup per job) to reduce wall-clock time.CI reliability and tooling are adjusted: adds simulator “warm-up” (
xcrun simctl list devices available) for iOS/tvOS/watchOS, switches pod linting topod lib lint --allow-warnings --quick, removes Mint/Mintfilein favor ofbrew install swiftlint, and updates contract tests to run vialaunchdarkly/gh-actions/actions/contract-tests@mainwith specific config and skipped cases.Release/publish workflows (
manual-publish*,release-please) are updated to invoke the new actions sequentially (preserving gating before publish).Written by Cursor Bugbot for commit b2e2d4b. This will update automatically on new commits. Configure here.