Repository navigation
Adopt StationCast CI and release conventions - #131
Merged
Merged
Conversation
scripts/check_version.py fails when the MARKETING_VERSION or the CURRENT_PROJECT_VERSION entries in project.pbxproj disagree, when a configuration lacks one, when a value is malformed, or when --tag is not v + MARKETING_VERSION. scripts/bump_version.py replaces every entry, so no configuration keeps an old value. pyproject.toml holds only the dev tools (ruff, mypy --strict, pytest) and the scripts' dependencies; it builds no package.
Ported from StationCast, with HealthExporter's bundle ID as the default. The App Store Connect auth code moves to scripts/asc_api.py, which both scripts share. Output goes through logging to stdout.
- .swiftformat and .swiftlint.yml come from StationCast, with the paths set to HealthExporter/ and HealthExporterTests/. - ci/lint_swift.sh runs both tools and fails if either one fails. - .pre-commit-config.yaml runs the file checks, gitleaks, SwiftFormat, SwiftLint, and ruff and mypy on scripts/. - .gitignore ignores the Python caches, *.xccheckout and *.moved-aside.
SwiftFormat 0.63.0 with the new .swiftformat. Its noForceUnwrapInTests rule rewrote one check in ExportLogicTests to #expect(try #require(a == b)), which does not compile. That line is now #expect(result?.endDate == end).
- Use HKQuantityType(_:) and TimeZone.gmt, which do not return an optional. - Write the fixed test dates as Date(timeIntervalSince1970:) values. - Make the test helpers throw, and use #require in place of ! and try!. - Use isEmpty in place of a comparison with 0 or "". - Keep one force unwrap in HealthKitQueryHelpersTests, with a reason: a stored property initializer cannot throw.
- AGENTS.md: add the Verification, PR Labels and Guardrails sections. Say that the tests run in CI, not on a laptop. List GEMINI.md and the skills folder. - GEMINI.md is a symlink to AGENTS.md. - .agents/skills/deploy-to-phone installs a Debug build on the owner's iPhone, with the Xcode that matches the device iOS. .claude/skills links to it. - .claude/settings.json allows xcodebuild and xcrun, as StationCast does.
The four findings are pod checksums in Podfile.lock and Pods/Manifest.lock from commit e0fb575. Neither file is in the tree now.
- skip-release skips the bump, commit, tag, release and TestFlight dispatch; skip-release with a semver:* label fails the job. - Run on pull_request_target from main, so a fork merge can release. - Bump with scripts/bump_version.py and check the tag with scripts/check_version.py before the push. - Push the release commit and the tag with one git push --atomic, and tag a stranded release commit instead of bumping again. - Check out with RELEASE_DEPLOY_KEY when it is set, else GITHUB_TOKEN. - Add a pull request template with the release labels.
- Detect iOS changes: skip the tests when only docs, assets or repo metadata changed; run them if the filter fails. - Name the test job 'iOS tests' for the ruleset, and fail fork pull requests in a Linux guard job instead of skipping them. - Cancel superseded runs on pull requests only. - Probe simctl with a 30-second watchdog, and document the recovery. - Lint Swift before the tests, and set job timeouts. - Move to actions/checkout@v7 and actions/upload-artifact@v7.
- After an upload, set What to Test from ci/testflight-notes.txt for the exact build and version, with retries. - In the cleanup, restore the keychain lock settings and lock it. - Add a 90-minute timeout and move to actions/checkout@v7.
- Scripts: ruff, mypy --strict, pytest (failing on a skip) and the version check, on the Linux runner, with a per-run uv cache. - Gitleaks: scan new commits on every pull request and push with a pinned, checksummed release. Ignore old CocoaPods checksums. - Promote a TestFlight build to external testers by dispatch. - Dependabot: GitHub Actions, monthly, labeled skip-release.
Name the runners by label only, since the repo is public.
# Conflicts: # .gitleaksignore
detect-private-key flagged the literal BEGIN PRIVATE KEY marker. Also drop the stale bump-commit example from the commit guidelines.
Each release merge dispatches release-testflight.yml, which runs the suite on the tag, so a push run on main only repeated it on the shared runner. Run the tests after a lint failure too, so one run reports both.
37 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adopts StationCast's CI and release conventions, in one batch, so that the shared macOS runner tests them once. It is part of #126.
Releases (
bump-build-number.yml)skip-releaseskips the bump, the commit, the tag, the release and the TestFlight upload.skip-releasewith anysemver:*label fails the job. With no label, the bump is a patch. (1.1, 1.2)Release vX.Y.Z (build N), withSource merge: #N (title).git push --atomicsends the commit and the tag together. A "Detect stranded release" step tags a release commit that has no tag, and the release step skips a release that already exists. (1.3)pull_request_target, withmainchecked out. The job never runs code from the PR. (1.4)scripts/check_version.pyandscripts/bump_version.py, with tests written first. The check fails when the version entries disagree, or when the tag does not match. (1.5)RELEASE_DEPLOY_KEY, which is now set. Without the key, it falls back toGITHUB_TOKEN. (2.2)Tests (
ios-tests.yml)iOS tests. (3.1)mainno longer runs the suite. Each release merge dispatchesrelease-testflight.yml, which tests the tag, so the push run only repeated the test.ci/run_ios_tests.shprobessimctlwith a 30-second watchdog. Every job hastimeout-minutes. (3.4)ci/lint_swift.shruns SwiftFormat and SwiftLint--strictbefore the tests. The tests run after a lint failure too, so one run reports both. (3.5)disable.HKQuantityType(.x)replaces three force unwraps, and.gmtreplaces one.actions/checkout@v7andactions/upload-artifact@v7(Node 24). (3.6)TestFlight (
release-testflight.yml)scripts/update_testflight_notes.pysets "What to Test" fromci/testflight-notes.txt, for the exact build. (4.1)promote-testflight-external.ymldispatch adds a build toExternal Hoffgroup. (4.3)Tooling and hygiene
pyproject.toml,.python-versionanduv.lock. AScriptsjob on Linux runs ruff,mypy --strictandpytest -rs. (4.2)Gitleaksjob runs on every PR and push..gitleaksignorelists 4 old CocoaPods checksums. pre-commit adds the standard hooks, gitleaks, SwiftFormat, SwiftLint, ruff and mypy. (5.2)skip-release. (5.3).gitignore: Python and Xcode leftovers. (5.4)AGENTS.mdgets new "Verification", "PR Labels" and "Guardrails" sections, and a rewritten "CI and releases" section with no host names. (6.1)GEMINI.mdsymlink. (6.2)deploy-to-phoneskill. (6.3).claude/settings.json. (6.4)Already applied in the repo settings
skip-releaseandsemver:patchlabels exist.all_external_contributors).Deferred
Verification
actionlint: no findings on all 7 workflows.uv run ruff format --check . && uv run ruff check . && uv run mypy && uv run pytest -rs: 74 passed, 0 skipped.scripts/check_version.py --tag v2.5.4: passes on the real project.ci/lint_swift.sh: 0 violations.uvx pre-commit run --all-files: all 12 hooks pass.xcodebuild build-for-testing, compile only: 0 errors and 0 Swift warnings.iOS testscheck.Release
skip-release. This PR changes CI, tooling and code style only. Nothing changes for a user.