Repository navigation
Add Swift SDK release workflow #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| name: Release SDK Swift | ||
|
|
||
| # Swift SDK releases use their own `swift-vX.Y.Z` tags so they never trigger | ||
| # release.yml (CLI, plugin, Pages), whose `v*` pattern does not match them. | ||
| on: | ||
| push: | ||
| tags: | ||
| - "swift-v[0-9]+.[0-9]+.[0-9]+" | ||
| workflow_dispatch: | ||
| inputs: | ||
| publish: | ||
| description: "Publish demucs-swift release assets from the selected ref" | ||
| type: boolean | ||
| default: false | ||
| release_tag: | ||
| description: "Existing swift-vX.Y.Z tag to publish on manual runs" | ||
| type: string | ||
| required: false | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| # One release at a time per tag. Not cancel-in-progress: the later steps create | ||
| # a GitHub release and then push a commit and a tag to demucs-swift, and killing | ||
| # a run part-way through leaves a release with no assets on it, or demucs-swift | ||
| # pointing at a release that no longer matches. | ||
| concurrency: | ||
| group: release-sdk-swift-${{ github.ref }} | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| preflight: | ||
| # macOS rather than Linux: the release artifact is an XCFramework and | ||
| # demucs-ffi pulls in burn's Metal backend on Apple targets. | ||
| runs-on: macos-26 | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| - uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| components: rustfmt, clippy | ||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| key: sdk-swift-preflight | ||
| - name: Validate tag matches demucs-ffi version | ||
| if: startsWith(github.ref, 'refs/tags/') | ||
| run: | | ||
| version="$(cargo metadata --no-deps --format-version 1 | python3 -c 'import json,sys; print(next(p["version"] for p in json.load(sys.stdin)["packages"] if p["name"] == "demucs-ffi"))')" | ||
| if [ "${GITHUB_REF_NAME#swift-v}" != "$version" ]; then | ||
| echo "::error::Tag '$GITHUB_REF_NAME' does not match demucs-ffi version '$version'." | ||
| exit 1 | ||
| fi | ||
| - run: cargo fmt --all -- --check | ||
| - run: cargo clippy --locked -p demucs-ffi --all-targets -- -D warnings | ||
| - run: cargo test --locked -p demucs-ffi | ||
|
|
||
| build-and-release: | ||
| needs: preflight | ||
| runs-on: macos-26 | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| path: demucs-rs | ||
| - uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| targets: aarch64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-darwin | ||
| # tvOS and visionOS are tier 3: the script builds them with | ||
| # `+nightly -Z build-std`, which needs rust-src. | ||
| - uses: dtolnay/rust-toolchain@nightly | ||
| with: | ||
| components: rust-src | ||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| workspaces: demucs-rs | ||
| key: sdk-swift-apple | ||
| - name: Build XCFramework | ||
| working-directory: demucs-rs | ||
| run: .github/scripts/build-swift-xcframework.sh | ||
| - id: meta | ||
| working-directory: demucs-rs | ||
| run: | | ||
| echo "version=$(cat dist/swift/version.txt)" >> "$GITHUB_OUTPUT" | ||
| echo "checksum=$(cat dist/swift/DemucsFramework.checksum.txt)" >> "$GITHUB_OUTPUT" | ||
| - id: release | ||
| env: | ||
| INPUT_RELEASE_TAG: ${{ inputs.release_tag }} | ||
| run: | | ||
| if [ "${GITHUB_REF_TYPE}" = "tag" ]; then | ||
| release_tag="${GITHUB_REF_NAME}" | ||
| else | ||
| release_tag="${INPUT_RELEASE_TAG}" | ||
| fi | ||
| echo "release_tag=$release_tag" >> "$GITHUB_OUTPUT" | ||
| - name: Validate release tag | ||
| if: startsWith(github.ref, 'refs/tags/') || inputs.publish | ||
| env: | ||
| VERSION: ${{ steps.meta.outputs.version }} | ||
| RELEASE_TAG: ${{ steps.release.outputs.release_tag }} | ||
| HAS_PAT: ${{ secrets.DEMUCS_SWIFT_PAT != '' }} | ||
| run: | | ||
| # Fail before the GitHub release exists rather than after, when the | ||
| # push to demucs-swift would be the first thing to notice. | ||
| if [ "$HAS_PAT" != "true" ]; then | ||
| echo "::error::DEMUCS_SWIFT_PAT is not set; it needs contents:write on SplitFireAI/demucs-swift." | ||
| exit 1 | ||
| fi | ||
| if [ -z "$RELEASE_TAG" ]; then | ||
| echo "::error::release_tag is required when publish=true on workflow_dispatch runs." | ||
| exit 1 | ||
| fi | ||
| if [ "$RELEASE_TAG" != "swift-v$VERSION" ]; then | ||
| echo "::error::Release tag '$RELEASE_TAG' does not match 'swift-v$VERSION' (demucs-ffi version)." | ||
| exit 1 | ||
| fi | ||
| - name: Checkout demucs-swift | ||
|
setoelkahfi marked this conversation as resolved.
|
||
| uses: actions/checkout@v6 | ||
| with: | ||
| repository: SplitFireAI/demucs-swift | ||
| # Read-only use for dry runs; the PAT is only needed to push, but a | ||
| # private repository would need it to clone as well. | ||
| token: ${{ secrets.DEMUCS_SWIFT_PAT || github.token }} | ||
| path: demucs-swift | ||
| # Full history and tags: the push step rebases onto main, and the | ||
| # commit step compares against an existing release tag. | ||
| fetch-depth: 0 | ||
| # Gate the release on the Swift package building and passing its tests | ||
| # against the framework just built. Package.swift prefers a local | ||
| # DemucsFramework.xcframework over the release URL when one is present, | ||
| # so this exercises the new binary without publishing it first. | ||
| - name: Test Swift package against the new framework | ||
| working-directory: demucs-swift | ||
| run: | | ||
| cp -R ../demucs-rs/dist/swift/DemucsFramework.xcframework DemucsFramework.xcframework | ||
| cp ../demucs-rs/dist/swift/Package/Sources/Demucs/demucs_ffi.swift Sources/Demucs/demucs_ffi.swift | ||
| swift test | ||
| rm -rf DemucsFramework.xcframework .build | ||
| - name: Upload build artifact | ||
| if: github.event_name == 'workflow_dispatch' | ||
| uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: demucs-swift-xcframework | ||
| path: | | ||
| demucs-rs/dist/swift/DemucsFramework.xcframework.zip | ||
| demucs-rs/dist/swift/DemucsFramework.checksum.txt | ||
| demucs-rs/dist/swift/version.txt | ||
| demucs-rs/dist/swift/Package/Sources/Demucs/demucs_ffi.swift | ||
| - name: Update Swift package | ||
| if: startsWith(github.ref, 'refs/tags/') || inputs.publish | ||
| env: | ||
| CHECKSUM: ${{ steps.meta.outputs.checksum }} | ||
| RELEASE_URL: https://github.com/${{ github.repository }}/releases/download/${{ steps.release.outputs.release_tag }}/DemucsFramework.xcframework.zip | ||
| run: | | ||
| python3 - <<'PY' | ||
| import os, pathlib, re | ||
| package = pathlib.Path("demucs-swift/Package.swift") | ||
| source = package.read_text() | ||
| source, urls = re.subn(r'(let releaseFrameworkURL =\s*")([^"]+)(")', rf'\g<1>{os.environ["RELEASE_URL"]}\g<3>', source, count=1) | ||
| source, sums = re.subn(r'(let releaseFrameworkChecksum =\s*")([^"]+)(")', rf'\g<1>{os.environ["CHECKSUM"]}\g<3>', source, count=1) | ||
| if (urls, sums) != (1, 1): | ||
| raise RuntimeError("Package.swift release constants were not found") | ||
| package.write_text(source) | ||
| PY | ||
| # Commit locally before the GitHub release exists, so a conflicting tag | ||
| # stops the run while nothing has been published yet. | ||
| - name: Commit Swift package | ||
| if: startsWith(github.ref, 'refs/tags/') || inputs.publish | ||
| working-directory: demucs-swift | ||
| env: | ||
| # SwiftPM resolves `from: "0.1.0"` against plain semver tags, so | ||
| # demucs-swift is tagged X.Y.Z rather than swift-vX.Y.Z. | ||
| VERSION: ${{ steps.meta.outputs.version }} | ||
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| git add Package.swift Sources/Demucs/demucs_ffi.swift | ||
| git diff --cached --quiet || git commit -m "Release $VERSION" | ||
| # A rerun of the same release finds its own tag, which is fine. A tag | ||
| # pinning a different framework would leave SwiftPM users on it. | ||
| if git rev-parse --verify --quiet "refs/tags/$VERSION^{commit}" > /dev/null; then | ||
| if ! git diff --quiet "refs/tags/$VERSION" HEAD -- Package.swift Sources/Demucs/demucs_ffi.swift; then | ||
| echo "::error::Tag $VERSION already exists on demucs-swift with a different Package.swift or bindings." | ||
| exit 1 | ||
| fi | ||
| fi | ||
| - name: Create GitHub release | ||
| if: startsWith(github.ref, 'refs/tags/') || inputs.publish | ||
| uses: softprops/action-gh-release@v3 | ||
| with: | ||
| tag_name: ${{ steps.release.outputs.release_tag }} | ||
| name: Swift SDK ${{ steps.meta.outputs.version }} | ||
| generate_release_notes: true | ||
| # Keep "Latest" on the CLI/plugin releases from release.yml. | ||
| make_latest: false | ||
| files: | | ||
| demucs-rs/dist/swift/DemucsFramework.xcframework.zip | ||
| demucs-rs/dist/swift/DemucsFramework.checksum.txt | ||
| - name: Push and tag Swift package | ||
| if: startsWith(github.ref, 'refs/tags/') || inputs.publish | ||
| working-directory: demucs-swift | ||
| env: | ||
| VERSION: ${{ steps.meta.outputs.version }} | ||
| run: | | ||
| # main may have moved while the Swift tests ran. If this fails, the | ||
| # release above is already up; rerunning the job replaces its assets | ||
| # and retries the push. | ||
| git pull --rebase origin main | ||
| git push origin HEAD:main | ||
|
setoelkahfi marked this conversation as resolved.
|
||
| if git rev-parse --verify --quiet "refs/tags/$VERSION^{commit}" > /dev/null; then | ||
| echo "Tag $VERSION already exists with the same release files." | ||
| else | ||
| git tag -a "$VERSION" -m "Release $VERSION" | ||
| git push origin "$VERSION" | ||
| fi | ||
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.