Skip to content

Commit f74c8b8

Browse files
authored
Merge pull request #165 from fulldecent/xcode-27-ci
fix: match CI to Swift 6.4 and the Xcode 27 runner
2 parents 9466a92 + 990af43 commit f74c8b8

8 files changed

Lines changed: 185 additions & 48 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,39 @@
11
name: ci
22

3-
# macos-latest is the macOS 26 image. It does not install Xcode 16.4, and the
4-
# simulator-discovery script previously used here failed on that image
5-
# (https://github.com/fulldecent/FDWaveformView/actions/runs/25414955912).
6-
# This job pins the macOS 15 image, whose default Xcode is 16.4 and which
7-
# lists an iPhone 16 simulator on iOS 18.5:
8-
# https://github.com/actions/runner-images/blob/main/images/macos/macos-15-arm64-Readme.md
3+
# GitHub-hosted Xcode 27. macos-26 stops at Xcode 26, which cannot load
4+
# swift-tools-version 6.4.
5+
# https://github.blog/changelog/2026-07-16-xcode-27-runner-image-now-in-public-preview/
6+
# https://github.com/actions/runner-images/blob/main/images/macos/xcode-27-arm64-Readme.md
7+
#
8+
# swift6-module-template runs `xcrun swift test` on this runner. This package
9+
# imports UIKit and its platforms are iOS and visionOS, so that command builds
10+
# for macOS and cannot compile. The destination below is the iPhone 17
11+
# simulator on iOS 27.0, which the Xcode 27.0 image lists.
12+
#
13+
# release.yml calls this workflow. It reads $.jobs.build-test.
914

1015
on:
16+
workflow_call:
1117
push:
12-
branches:
13-
- main
1418
pull_request:
1519

1620
concurrency:
1721
group: ci-${{ github.workflow }}-${{ github.ref }}
1822
cancel-in-progress: true
1923

2024
jobs:
21-
test:
22-
name: Build and Test
23-
runs-on: macos-15
24-
25+
build-test:
26+
name: Build and test
27+
runs-on: xcode-27
2528
steps:
26-
- name: Check out repository
27-
uses: actions/checkout@v4
28-
29-
- name: Select Xcode
30-
run: sudo xcode-select -s /Applications/Xcode_16.4.app
29+
- name: Check out this commit
30+
uses: actions/checkout@v7
3131

3232
- name: Show build environment
3333
run: |
3434
xcodebuild -version
3535
xcrun swift --version
3636
37-
- name: Build
38-
run: |
39-
xcodebuild build \
40-
-scheme FDWaveformView \
41-
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.5'
42-
4337
- name: Check formatting
4438
run: |
4539
swift format format --in-place --recursive Sources/ Tests/
@@ -49,4 +43,4 @@ jobs:
4943
run: |
5044
xcodebuild test \
5145
-scheme FDWaveformView \
52-
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.5'
46+
-destination 'platform=iOS Simulator,name=iPhone 17,OS=27.0'

‎.github/workflows/release.yml‎

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Release
2+
#
3+
# Open or update the release pull request, test the package, and publish the
4+
# GitHub Release.
5+
#
6+
# Adapted from swift6-module-template release 16.5.0:
7+
# https://github.com/fulldecent/swift6-module-template/blob/v16.5.0/.github/workflows/release.yml
8+
# That workflow attests a Linux static library from `swift build -c release`.
9+
# This package imports UIKit, so there is no Linux library to publish. Swift
10+
# Package Manager installs from the git tag. The published release is the tag
11+
# and the generated notes.
12+
#
13+
# Tags stay `5.1.1`, without a `v` prefix, because that is the shape of the
14+
# existing tags. release-please-config.json sets include-v-in-tag to false.
15+
#
16+
# One-time setup
17+
# 1. GitHub repo > Settings > General > Releases > Enable release immutability.
18+
# 2. GitHub repo > Settings > Actions > General > Workflow permissions: Read and write permissions.
19+
# 3. Under Workflow permissions, check "Allow GitHub Actions to create and approve pull requests".
20+
21+
name: Release
22+
23+
on:
24+
push:
25+
branches: ["main"]
26+
workflow_dispatch:
27+
inputs:
28+
tag:
29+
description: Tag to publish, for example 5.1.2. Leave empty to open or update the release pull request only.
30+
required: false
31+
type: string
32+
33+
permissions:
34+
contents: write
35+
issues: write
36+
pull-requests: write
37+
38+
jobs:
39+
propose:
40+
name: Open or update the release pull request
41+
if: github.event_name == 'push' || inputs.tag == ''
42+
runs-on: ubuntu-latest
43+
outputs:
44+
pr: ${{ steps.merged.outputs.pr }}
45+
tag_name: ${{ steps.merged.outputs.tag_name }}
46+
steps:
47+
- name: Open the release pull request
48+
uses: googleapis/release-please-action@v5
49+
with:
50+
config-file: release-please-config.json
51+
manifest-file: .release-please-manifest.json
52+
skip-github-release: true
53+
54+
- name: Find a merged release pull request
55+
id: merged
56+
if: github.event_name == 'push'
57+
env:
58+
GH_TOKEN: ${{ github.token }}
59+
run: |
60+
found=$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/pulls" --jq '
61+
.[] | select(.merged_at != null and any(.labels[]; .name == "autorelease: pending"))
62+
| "\(.number) \(.title | capture("release (?<v>[0-9]+[.][0-9]+[.][0-9]+)").v)"' | head -n 1)
63+
if [ -n "$found" ]; then
64+
read -r pr tag <<< "$found"
65+
echo "pr=$pr" >> "$GITHUB_OUTPUT"
66+
echo "tag_name=$tag" >> "$GITHUB_OUTPUT"
67+
fi
68+
69+
build-test:
70+
name: Build and test
71+
needs: propose
72+
if: |
73+
always() &&
74+
(needs.propose.result == 'success' || needs.propose.result == 'skipped') &&
75+
(needs.propose.outputs.tag_name != '' ||
76+
(github.event_name == 'workflow_dispatch' && inputs.tag != ''))
77+
uses: ./.github/workflows/ci.yml
78+
79+
publish:
80+
name: Publish release
81+
needs: [propose, build-test]
82+
if: |
83+
always() &&
84+
needs.build-test.result == 'success' &&
85+
(needs.propose.outputs.tag_name != '' ||
86+
(github.event_name == 'workflow_dispatch' && inputs.tag != ''))
87+
runs-on: ubuntu-latest
88+
permissions:
89+
contents: write
90+
pull-requests: write
91+
steps:
92+
- name: Resolve the tag
93+
id: tag
94+
env:
95+
TAG: ${{ inputs.tag || needs.propose.outputs.tag_name }}
96+
run: |
97+
case "$TAG" in
98+
[0-9]*.[0-9]*.[0-9]*) ;;
99+
*) echo "tag must look like 5.1.2" >&2; exit 1 ;;
100+
esac
101+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
102+
103+
- name: Publish
104+
env:
105+
GH_TOKEN: ${{ github.token }}
106+
TAG: ${{ steps.tag.outputs.tag }}
107+
run: |
108+
gh release create "$TAG" \
109+
--repo "$GITHUB_REPOSITORY" \
110+
--target "$GITHUB_SHA" \
111+
--title "$TAG" \
112+
--generate-notes \
113+
--latest
114+
115+
- name: Mark the release pull request tagged
116+
if: needs.propose.outputs.pr != ''
117+
env:
118+
GH_TOKEN: ${{ github.token }}
119+
PR: ${{ needs.propose.outputs.pr }}
120+
run: |
121+
gh api -X DELETE "repos/$GITHUB_REPOSITORY/issues/$PR/labels/autorelease:%20pending"
122+
gh api "repos/$GITHUB_REPOSITORY/issues/$PR/labels" -f "labels[]=autorelease: tagged"

‎.release-please-manifest.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
".": "5.1.1"
3+
}

‎CHANGELOG.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ All notable changes to this project will be documented in this file.
99

1010
### Changed
1111

12-
- Pin GitHub Actions to the macOS 15 image, Xcode 16.4, and an iPhone 16 simulator on iOS 18.5. The workflow on `macos-latest` failed before the package compiled.
12+
- Require Swift tools 6.4 and enable Approachable Concurrency. Xcode 26 cannot load that tools version.
13+
- Test on the GitHub-hosted Xcode 27 runner, using an iPhone 17 simulator on iOS 27.0.
14+
- Hand releases to Release Please. Commit messages use `fix:`, `feat:` or `BREAKING CHANGE:`. Tags stay unprefixed, matching `5.1.1`.
1315
- Inline `.gitignore` from the current Swift and Xcode ignore files, plus the macOS and secret rules from project-template.
14-
- Describe the release steps in terms of `CHANGELOG.md` and a GitHub tag.
1516

1617
---
1718

‎CONTRIBUTING.md‎

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
11
# Contributing
22

3-
All contributors are welcome. Please use issues and pull requests to contribute to the project. And update [CHANGELOG.md](CHANGELOG.md) when committing.
3+
All contributors are welcome. Please use issues and pull requests to contribute to the project. Commit messages use `fix:`, `feat:` or `BREAKING CHANGE:`. Release Please writes [CHANGELOG.md](CHANGELOG.md) from those messages.
44

55
## Release process
66

7-
1. Confirm the build is [passing in GitHub Actions](https://github.com/fulldecent/FDWaveformView/actions)
8-
2. Push a release commit
9-
1. Create a new Main section at the top of [CHANGELOG.md](CHANGELOG.md)
10-
2. Rename the old Main section like:
7+
Merging the release pull request tags the release and publishes the GitHub Release. The settings that pull request needs are in [Releases](README.md#releases). Confirm the build is [passing in GitHub Actions](https://github.com/fulldecent/FDWaveformView/actions) before merging it.
118

12-
```markdown
13-
## [5.1.1](https://github.com/fulldecent/FDWaveformView/releases/tag/5.1.1)
14-
15-
Released on 2025-12-06.
16-
```
17-
18-
3. Create a GitHub release
19-
1. Tag the release (like `5.1.1`)
20-
2. Paste notes from [CHANGELOG.md](CHANGELOG.md)
9+
Tags stay `5.1.1`, without a `v` prefix. That matches the tags already on this repository. [release-please-config.json](release-please-config.json) sets `include-v-in-tag` to false so Release Please finds tag `5.1.1`.
2110

2211
## Maintenance
2312

2413
Do this every quarter or so.
2514

26-
1. Review the Xcode pin in [.github/workflows/ci.yml](.github/workflows/ci.yml). The job uses the `macos-15` runner and `/Applications/Xcode_16.4.app`, then tests on an iPhone 16 simulator running iOS 18.5. Those are the versions listed for that image in the [macOS 15 runner readme](https://github.com/actions/runner-images/blob/main/images/macos/macos-15-arm64-Readme.md). `macos-latest` is the macOS 26 image and does not install Xcode 16.4, so a workflow that selects `macos-latest` and then looks up a simulator will fail before it compiles this package.
27-
2. Review [actions/checkout](https://github.com/actions/checkout) and move the `uses:` pin forward when a new major version is safe. The workflow currently uses `actions/checkout@v4`.
15+
1. Review the Swift tools version in [Package.swift](Package.swift) and the runner in [.github/workflows/ci.yml](.github/workflows/ci.yml). The job uses the GitHub-hosted `xcode-27` image because a Swift tools version of 6.4 does not load on Xcode 26. The test destination is an iPhone 17 simulator on iOS 27.0, which that image lists for Xcode 27.0. `xcrun swift test` builds this package for macOS, and the sources import UIKit, so the workflow uses `xcodebuild` instead. The image contents are in the [Xcode 27 runner readme](https://github.com/actions/runner-images/blob/main/images/macos/xcode-27-arm64-Readme.md).
16+
2. Review external actions in [.github/workflows](.github/workflows). The workflow currently uses `actions/checkout@v7` and `googleapis/release-please-action@v5`.

‎Package.swift‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 5.10
1+
// swift-tools-version: 6.4
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
@@ -19,9 +19,17 @@ let package = Package(
1919
// Targets are the basic building blocks of a package, defining a module or a test suite.
2020
// Targets can depend on other targets in this package and products from dependencies.
2121
.target(
22-
name: "FDWaveformView"),
22+
name: "FDWaveformView",
23+
swiftSettings: [
24+
.enableUpcomingFeature("ApproachableConcurrency")
25+
]
26+
),
2327
.testTarget(
2428
name: "FDWaveformViewTests",
25-
dependencies: ["FDWaveformView"]),
29+
dependencies: ["FDWaveformView"],
30+
swiftSettings: [
31+
.enableUpcomingFeature("ApproachableConcurrency")
32+
]
33+
),
2634
]
2735
)

‎README.md‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ UIView.animate(withDuration: 0.3) {
6969

7070
- Antialiased waveforms draw extra pixels to avoid jagged edges.
7171
- Autolayout-driven size changes trigger re-rendering to prevent pixelation.
72-
- Supports iOS 15+, visionOS 1+, and Swift tools version 5.10, as declared in `Package.swift`.
72+
- Supports iOS 15+, visionOS 1+, and Swift tools version 6.4, as declared in `Package.swift`. Xcode 26 cannot load that tools version.
7373
- Includes unit tests that run on GitHub Actions.
7474

7575
## Installation
@@ -110,12 +110,14 @@ A couple other things are exposed that we do not consider public API:
110110

111111
## Testing
112112

113-
GitHub Actions runs the suite on the macOS 15 image with Xcode 16.4. That image provides an iPhone 16 simulator on iOS 18.5. The command is:
113+
GitHub Actions runs the suite on the [Xcode 27 runner image](https://github.com/actions/runner-images/blob/main/images/macos/xcode-27-arm64-Readme.md). The default Xcode there is 27.0, and that copy provides an iPhone 17 simulator on iOS 27.0. The command is:
114114

115115
```sh
116-
xcodebuild test -scheme FDWaveformView -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.5'
116+
xcodebuild test -scheme FDWaveformView -destination 'platform=iOS Simulator,name=iPhone 17,OS=27.0'
117117
```
118118

119+
`xcrun swift test` builds the package for macOS. These sources import UIKit, so that command does not compile this package.
120+
119121
On another Xcode, list the simulators that copy of Xcode can see and pass one of those ids. `xcodebuild` only accepts destinations from the selected Xcode, so an id printed by a different install will fail.
120122

121123
```sh
@@ -129,7 +131,16 @@ The Example app target deploys to iOS 18.6, so its simulator has to be iOS 18.6
129131
xcodebuild build -project Example/Example.xcodeproj -scheme Example -destination 'id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
130132
```
131133

134+
## Releases
135+
136+
Commit messages use `fix:`, `feat:` or `BREAKING CHANGE:`. [Release Please](https://github.com/googleapis/release-please) opens a release pull request from those messages. Merging that pull request runs [.github/workflows/release.yml](.github/workflows/release.yml), which tests the package and publishes the GitHub Release.
137+
138+
[`.release-please-manifest.json`](.release-please-manifest.json) is the last released version, `5.1.1`. Tags do not use a `v` prefix, because the existing tags do not. Swift Package Manager installs from the git tag. This module imports UIKit, so the release does not attach a Linux static library.
139+
140+
In the repository settings, under Actions, General, Workflow permissions, select read and write permissions and check "Allow GitHub Actions to create and approve pull requests". Under General, Releases, enable release immutability.
141+
132142
## Contributing
133143

134144
- This project's layout is based on <https://github.com/fulldecent/swift6-module-template>
135145
- Ignore rules are inlined from [Swift.gitignore](https://github.com/github/gitignore/blob/main/Swift.gitignore) and [Global/Xcode.gitignore](https://github.com/github/gitignore/blob/main/Global/Xcode.gitignore). The macOS and secret rules above them come from [project-template](https://github.com/fulldecent/project-template).
146+
- Releases follow [swift6-module-template](https://github.com/fulldecent/swift6-module-template/blob/v16.5.0/.github/workflows/release.yml), release 16.5.0. The published result here is the git tag. That template publishes a Linux static library, and this module imports UIKit.

‎release-please-config.json‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
3+
"release-type": "simple",
4+
"include-component-in-tag": false,
5+
"include-v-in-tag": false,
6+
"packages": {
7+
".": {}
8+
}
9+
}

0 commit comments

Comments
 (0)