Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 18 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,45 +1,39 @@
name: ci

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

on:
workflow_call:
push:
branches:
- main
pull_request:

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

jobs:
test:
name: Build and Test
runs-on: macos-15

build-test:
name: Build and test
runs-on: xcode-27
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.4.app
- name: Check out this commit
uses: actions/checkout@v7

- name: Show build environment
run: |
xcodebuild -version
xcrun swift --version

- name: Build
run: |
xcodebuild build \
-scheme FDWaveformView \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.5'

- name: Check formatting
run: |
swift format format --in-place --recursive Sources/ Tests/
Expand All @@ -49,4 +43,4 @@ jobs:
run: |
xcodebuild test \
-scheme FDWaveformView \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.5'
-destination 'platform=iOS Simulator,name=iPhone 17,OS=27.0'
122 changes: 122 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Release
#
# Open or update the release pull request, test the package, and publish the
# GitHub Release.
#
# Adapted from swift6-module-template release 16.5.0:
# https://github.com/fulldecent/swift6-module-template/blob/v16.5.0/.github/workflows/release.yml
# That workflow attests a Linux static library from `swift build -c release`.
# This package imports UIKit, so there is no Linux library to publish. Swift
# Package Manager installs from the git tag. The published release is the tag
# and the generated notes.
#
# Tags stay `5.1.1`, without a `v` prefix, because that is the shape of the
# existing tags. release-please-config.json sets include-v-in-tag to false.
#
# One-time setup
# 1. GitHub repo > Settings > General > Releases > Enable release immutability.
# 2. GitHub repo > Settings > Actions > General > Workflow permissions: Read and write permissions.
# 3. Under Workflow permissions, check "Allow GitHub Actions to create and approve pull requests".

name: Release

on:
push:
branches: ["main"]
workflow_dispatch:
inputs:
tag:
description: Tag to publish, for example 5.1.2. Leave empty to open or update the release pull request only.
required: false
type: string

permissions:
contents: write
issues: write
pull-requests: write

jobs:
propose:
name: Open or update the release pull request
if: github.event_name == 'push' || inputs.tag == ''
runs-on: ubuntu-latest
outputs:
pr: ${{ steps.merged.outputs.pr }}
tag_name: ${{ steps.merged.outputs.tag_name }}
steps:
- name: Open the release pull request
uses: googleapis/release-please-action@v5
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
skip-github-release: true

- name: Find a merged release pull request
id: merged
if: github.event_name == 'push'
env:
GH_TOKEN: ${{ github.token }}
run: |
found=$(gh api "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/pulls" --jq '
.[] | select(.merged_at != null and any(.labels[]; .name == "autorelease: pending"))
| "\(.number) \(.title | capture("release (?<v>[0-9]+[.][0-9]+[.][0-9]+)").v)"' | head -n 1)
if [ -n "$found" ]; then
read -r pr tag <<< "$found"
echo "pr=$pr" >> "$GITHUB_OUTPUT"
echo "tag_name=$tag" >> "$GITHUB_OUTPUT"
fi

build-test:
name: Build and test
needs: propose
if: |
always() &&
(needs.propose.result == 'success' || needs.propose.result == 'skipped') &&
(needs.propose.outputs.tag_name != '' ||
(github.event_name == 'workflow_dispatch' && inputs.tag != ''))
uses: ./.github/workflows/ci.yml

publish:
name: Publish release
needs: [propose, build-test]
if: |
always() &&
needs.build-test.result == 'success' &&
(needs.propose.outputs.tag_name != '' ||
(github.event_name == 'workflow_dispatch' && inputs.tag != ''))
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Resolve the tag
id: tag
env:
TAG: ${{ inputs.tag || needs.propose.outputs.tag_name }}
run: |
case "$TAG" in
[0-9]*.[0-9]*.[0-9]*) ;;
*) echo "tag must look like 5.1.2" >&2; exit 1 ;;
esac
Comment on lines +97 to +100
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

- name: Publish
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.tag.outputs.tag }}
run: |
gh release create "$TAG" \
--repo "$GITHUB_REPOSITORY" \
--target "$GITHUB_SHA" \
--title "$TAG" \
--generate-notes \
--latest

- name: Mark the release pull request tagged
if: needs.propose.outputs.pr != ''
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ needs.propose.outputs.pr }}
run: |
gh api -X DELETE "repos/$GITHUB_REPOSITORY/issues/$PR/labels/autorelease:%20pending"
gh api "repos/$GITHUB_REPOSITORY/issues/$PR/labels" -f "labels[]=autorelease: tagged"
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "5.1.1"
}
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ All notable changes to this project will be documented in this file.

### Changed

- 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.
- Require Swift tools 6.4 and enable Approachable Concurrency. Xcode 26 cannot load that tools version.
- Test on the GitHub-hosted Xcode 27 runner, using an iPhone 17 simulator on iOS 27.0.
- Hand releases to Release Please. Commit messages use `fix:`, `feat:` or `BREAKING CHANGE:`. Tags stay unprefixed, matching `5.1.1`.
- Inline `.gitignore` from the current Swift and Xcode ignore files, plus the macOS and secret rules from project-template.
- Describe the release steps in terms of `CHANGELOG.md` and a GitHub tag.

---

Expand Down
21 changes: 5 additions & 16 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
# Contributing

All contributors are welcome. Please use issues and pull requests to contribute to the project. And update [CHANGELOG.md](CHANGELOG.md) when committing.
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.

## Release process

1. Confirm the build is [passing in GitHub Actions](https://github.com/fulldecent/FDWaveformView/actions)
2. Push a release commit
1. Create a new Main section at the top of [CHANGELOG.md](CHANGELOG.md)
2. Rename the old Main section like:
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.

```markdown
## [5.1.1](https://github.com/fulldecent/FDWaveformView/releases/tag/5.1.1)

Released on 2025-12-06.
```

3. Create a GitHub release
1. Tag the release (like `5.1.1`)
2. Paste notes from [CHANGELOG.md](CHANGELOG.md)
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`.

## Maintenance

Do this every quarter or so.

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.
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`.
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).
2. Review external actions in [.github/workflows](.github/workflows). The workflow currently uses `actions/checkout@v7` and `googleapis/release-please-action@v5`.
14 changes: 11 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.10
// swift-tools-version: 6.4
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -19,9 +19,17 @@ let package = Package(
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "FDWaveformView"),
name: "FDWaveformView",
swiftSettings: [
.enableUpcomingFeature("ApproachableConcurrency")
]
),
.testTarget(
name: "FDWaveformViewTests",
dependencies: ["FDWaveformView"]),
dependencies: ["FDWaveformView"],
swiftSettings: [
.enableUpcomingFeature("ApproachableConcurrency")
]
),
]
)
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ UIView.animate(withDuration: 0.3) {

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

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

## Testing

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:
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:

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

`xcrun swift test` builds the package for macOS. These sources import UIKit, so that command does not compile this package.

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.

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

## Releases

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.

[`.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.

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.

## Contributing

- This project's layout is based on <https://github.com/fulldecent/swift6-module-template>
- 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).
- 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.
9 changes: 9 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"release-type": "simple",
"include-component-in-tag": false,
"include-v-in-tag": false,
"packages": {
".": {}
}
}
Loading