Swift GitHub Actions Workflows Unification. - #316
Open
rnro wants to merge 1 commit into
Open
Conversation
Bring together the best aspects of the swiftlang GitHub Actions workflows in this repository and the SwiftNIO ones at apple/swift-nio, through a new reusable workflow that sits beside the existing one and a hierarchy of supporting infrastructure. Key design features: 1) GitHub-release-based versioning 2) No skipped jobs 3) Scripts are cloned not curled 4) Custom matrix builds 5) Minimum Swift version detection `package_test.yml` runs a range of common test and build configurations on a variety of platforms against multiple Swift versions. It sits atop a matrix generation layer which takes inputs and produces a canonical work definition in YAML or JSON; that definition is then expanded into one job per entry, each executing one slice of the work. Benchmarking is a further consumer of the same layer rather than part of the recommended test workflow. The design is layered, so an adopter may use the whole stack or customize portions beyond the level the workflow allows: * Use `package_test.yml` for the full suite of conveniences. * Use a custom workflow which calls `toolchain_matrix.yml` for the matrix and `execute_matrix.yml` to run its own command across it. * Generate the work definition by some other means, or hard-code it into a workflow, and pass it to `execute_matrix.yml`. The matrix generation layer is a Swift script, `generate-matrix.swift`, run by the matrix job on `ubuntu-latest`, which comes with the Swift, jq and yq it needs. It reads the workflow's inputs from the environment, applies the defaults and the minimum version filter, and writes the work definition to standard output. In SwiftNIO it was a bash script, which executes faster, but the unified version is much more complex, so Swift seemed like the more maintainable option. A test package under `tests/MatrixGeneratorValidator` covers the inputs, their defaults and failure modes. Execution times are reduced by defaulting to running natively on the runner rather than inside a container. The toolchain is installed with swiftly, which was previously used only for macOS toolchains, so using it on Linux is new to both upstreams. Containers remain available for a package that needs a particular distribution or system dependencies, through `linux_use_docker` or by naming a distribution in `linux_os`. The existing workflows are untouched, so a repository migrates when it suits it. The most visible change on migrating is that the `*_exclude_swift_versions` inputs are gone, replaced by explicit version lists and minimum-version detection which drops anything the package manifest cannot support. A repository that was only excluding old versions can delete those inputs entirely. Migrating from the SwiftNIO workflows is a change in spelling rather than a reduction in flexibility: per-version inputs become one map, which removes the churn of adding an input per Swift release. Functionality new to this repository, some inspired by SwiftNIO and some new to both: * Simulator testing. iOS was build-only; xcodebuild now builds and tests on simulators, with tvOS, watchOS and visionOS alongside it. Mac Catalyst is new to both upstreams. * Benchmarks across the matrix. `benchmarks.yml` checks committed thresholds under `Thresholds/<swift-version>/` on every platform and Swift version, failing the job and printing a diff when one moves. This differs from the existing `performance_test.yml`, which compares a pull request against its merge base in one container and posts the table as a comment: that reports what a change did to performance, this holds a package to a standard it has written down. * A C++ interoperability check. A release-configuration build needs no dedicated input: it is a second labeled command, so `Release-Build Linux Swift 6.3` runs alongside `Debug-Test Linux Swift 6.3` from one call. * A semantic version pull request label check, `pull_request_label.yml`. * An importable Swift version matrix, `toolchain_matrix.yml`, which gives a custom workflow the current supported matrix as plain YAML, so it can be filtered or extended with yq before it is executed. * Per-version overrides, so one Swift version can take extra arguments or a different command without splitting the matrix by hand. Known limitation: a pinned tag still does not pin the scripts. This is inherited behavior. `swift_package_test.yml` and `soundness.yml` check this repository out with no `ref:`, so they resolve to its default branch, and one variant hardcodes `ref: main`. A caller on `@0.0.14` has therefore always run that tag's YAML against the default branch's scripts, and the unified workflows inherit that default. Resolving it is possible but would require stamping versions as part of the release process.
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.
Swift GitHub Actions Workflows Unification.
This PR aims to bring the best aspects of the swiftlang GitHub Actions workflows (this repository) and the SwiftNIO (apple/swift-nio) workflows together. It does this chiefly through a new re-usable workflow which sits beside the existing workflow, and a hierarchy of supporting infrastructure.
Some key design features:
The unified design provides one workflow which can be adopted to run a range of common test and build configurations on a variety of platforms against multiple Swift versions. This workflow sits atop a "matrix generation" layer which takes inputs and produces a canonical work definition (in YAML or JSON). That definition is then expanded into one job per entry, each executing one slice of the work. Benchmarking is a further consumer of the same layer rather than part of the recommended test workflow.
The design is intended to be layered, so that adopters may use the whole stack, or customize portions beyond the level which the workflow allows. Adopters may:
package_test.ymlworkflow for the full suite of conveniencestoolchain_matrix.ymlfor the matrix andexecute_matrix.ymlto run its own command across itexecute_matrix.ymlThe matrix generation layer is a Swift script,
generate-matrix.swift, run by the matrix job onubuntu-latest, which comes with the Swift, jq and yq it needs. It reads the workflow's inputs from the environment, applies the defaults and the minimum version filter, and writes the work definition to standard output. In SwiftNIO it was a bash script, which executes faster, but the unified version is much more complex, so Swift seemed like the more maintainable option. A test package undertests/MatrixGeneratorValidatorcovers the inputs, their defaults and failure modes.As part of this work an effort was also made to reduce execution times, defaulting to running natively on the runner rather than inside a container where possible. The unified workflows install the toolchain with swiftly on the runner rather than pulling a full Docker image. Containers remain available for a package that needs a particular distribution or system dependencies, either through
linux_use_dockeror by naming a distribution inlinux_os. swiftly was previously used only for macOS toolchains, so using it on Linux is new to both upstreams.Migrating from this repository's existing workflow
The new workflow is currently available alongside existing infrastructure so a repository can control when they perform the migration.
When adopting the new workflow the most visible change is that the
*_exclude_swift_versionsinputs are gone, replaced with explicit supported Swift version lists and minimum-version detection which drops anything the package manifest cannot support. A repository that was only excluding old versions based on its Swift tools version can simplify its config.Before:
After:
The new architecture means that jobs which are not intended to be run will no longer appear as skipped:
Before:

After:

Migrating from the SwiftNIO workflows
Changing from the SwiftNIO workflows means a change in spelling but no reduction in flexibility. Per-version inputs are now supplied in a map which reduces churn and synchronization problems when a new Swift version is released.
Before:
After:
Functionality new to this repository
The unification also brings some new functionality to this repository. Some inspired by SwiftNIO, some entirely new:
xcodebuildnow builds and tests on simulators, and tvOS, watchOS and visionOS join it. Mac Catalyst is new to both upstreams.benchmarks.ymlmeasures performance with Swift Package Benchmark, checking committed thresholds underThresholds/<swift-version>/on every platform and Swift version in the matrix, failing the job and printing a diff when one moves. This differs from the existingperformance_test.yml, which compares a PR against its merge base in one container and posts the table as a PR comment. The existing workflow tells you what a change did to performance; the new one holds a package to a standard it has written down.Release-Build Linux Swift 6.3runs alongsideDebug-Test Linux Swift 6.3from one call.pull_request_label.yml), which fails a PR that carries no SemVer label.toolchain_matrix.yml, provides the current supported Swift version matrix for custom workflows to utilize. The output is plain YAML, so it can be filtered or extended withyqbefore it is executed.Known limitations
swift_package_test.ymlandsoundness.ymlcheck this repository out with noref:, so they resolve to its default branch, and one variant hardcodesref: main; this means a caller on a tag e.g.@0.0.14has always run that tag's YAML against the default branch's scripts. The unified workflows inherit that default. Resolving this should be possible but would require stamping versions as part of the release process.