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
30 changes: 29 additions & 1 deletion .github/scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# release.sh parse-version <prefix> — extract SemVer from GITHUB_REF given a tag prefix
# release.sh prev-tag <prefix> — find the tag immediately before the current one
# release.sh create <artifacts_dir> — create or update a GitHub release with artifacts
# release.sh git-tag-exists <repo> <tag> — check whether a tag exists on a remote repo
#
# Environment:
# GITHUB_REF — set by GitHub Actions (e.g. refs/tags/moq-relay-v1.2.3)
Expand Down Expand Up @@ -77,13 +78,40 @@ create_release() {
fi
}

# Check whether a bare-semver tag already exists on a remote repo. This is the
# release gate for the independently-versioned Swift wrapper (like release-plz
# checking the registry): the mirror's git tag is the source of truth, so a
# version that's already published is a no-op. Writes exists=true|false to
# $GITHUB_OUTPUT. A connection failure is fatal rather than silently
# re-publishing.
git_tag_exists() {
local repo="$1"
local tag="$2"
local url="https://github.com/${repo}"

local out
out=$(git ls-remote --tags "$url" "refs/tags/${tag}") || {
echo "Failed to query tags on $url" >&2
exit 1
}

local exists
if [[ -n "$out" ]]; then exists=true; else exists=false; fi

if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
echo "exists=${exists}" >>"$GITHUB_OUTPUT"
fi
echo "Tag ${repo}@${tag}: exists=${exists}"
}

# Dispatch subcommands
case "${1:-}" in
parse-version) parse_version "$2" ;;
prev-tag) prev_tag "$2" ;;
create) create_release "$2" ;;
git-tag-exists) git_tag_exists "$2" "$3" ;;
*)
echo "Usage: $0 {parse-version|prev-tag|create} <args>" >&2
echo "Usage: $0 {parse-version|prev-tag|create|git-tag-exists} <args>" >&2
exit 1
;;
esac
2 changes: 1 addition & 1 deletion .github/workflows/release-brew.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ name: release-brew
# download them.
#
# Authenticates as the moq-bot GitHub App. The APP_ID and APP_PRIVATE_KEY
# repo secrets are already wired (see release-swift.yml). The minted
# repo secrets are already wired (see release-swift-lib.yml). The minted
# installation token is scoped to moq-dev/homebrew-tap and expires in 1
# hour.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
name: Release Swift
name: Release Swift FFI

# Builds and publishes the raw `MoqFFI` Swift package (UniFFI bindings +
# prebuilt XCFramework). Driven by the `moq-ffi-v*` tag that release-plz pushes
# when it bumps rs/moq-ffi/Cargo.toml, so this package stays lockstep with the
# moq-ffi crate. The ergonomic `Moq` wrapper is released separately
# (release-swift-lib.yml) and versions independently.

on:
push:
tags:
- "moq-ffi-v*"
pull_request:
paths:
- ".github/workflows/release-swift.yml"
- ".github/workflows/release-swift-ffi.yml"
- ".github/scripts/release.sh"
- "swift/Package.swift.template"
- "swift/scripts/package.sh"
- "swift/scripts/publish.sh"
- "swift/scripts/verify.sh"
- "swift/ffi/Package.swift.template"
- "swift/scripts/package-ffi.sh"
- "swift/scripts/publish-ffi.sh"
- "swift/scripts/verify-ffi.sh"
- "rs/moq-ffi/build.sh"

permissions:
contents: read

concurrency:
group: release-swift
group: release-swift-ffi
cancel-in-progress: false

jobs:
Expand Down Expand Up @@ -161,7 +167,7 @@ jobs:
env:
BUILD_VERSION: ${{ needs.parse-version.outputs.version }}
run: |
./swift/scripts/package.sh \
./swift/scripts/package-ffi.sh \
--version "$BUILD_VERSION" \
--lib-dir libs \
--bindings-dir bindings \
Expand All @@ -170,14 +176,14 @@ jobs:
- name: Upload Swift package
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: swift-package
name: swift-ffi-package
path: release-out/*

release:
# Attach MoqFFI.xcframework.zip (and the staged Swift package
# tarball) to the moq-ffi-v* GitHub release. The mirror's
# Package.swift binaryTarget URL points at this asset, so it must
# exist before any consumer resolves the SPM tag.
# Attach MoqFFI.xcframework.zip (and the staged Swift package tarball) to
# the moq-ffi-v* GitHub release. The mirror's Package.swift binaryTarget URL
# points at this asset, so it must exist before any consumer resolves the
# SPM tag.
name: Attach assets to GitHub release
needs: [package, parse-version]
runs-on: ubuntu-latest
Expand All @@ -189,9 +195,8 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
# No git operations here use the stored credential. gh CLI
# below picks up GH_TOKEN directly, so don't persist a
# contents-write token in .git/config.
# No git operations here use the stored credential. gh CLI below picks
# up GH_TOKEN directly, so don't persist a contents-write token.
persist-credentials: false

- name: Find previous tag
Expand All @@ -201,7 +206,7 @@ jobs:
- name: Download package
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: swift-package
name: swift-ffi-package
path: artifacts

- name: Create or update release
Expand All @@ -213,10 +218,10 @@ jobs:
run: .github/scripts/release.sh create artifacts

verify:
# Gate the mirror push on actually being able to resolve the staged
# package against the live release asset. Catches manifests that
# look syntactically fine but SPM cannot resolve (e.g. a path-based
# binaryTarget slipping in where a URL+checksum is expected).
# Gate the mirror push on actually being able to resolve the staged package
# against the live release asset. Catches manifests that look syntactically
# fine but SPM cannot resolve (e.g. a path-based binaryTarget slipping in
# where a URL+checksum is expected).
name: Verify staged package resolves
needs: [release, parse-version]
runs-on: macos-latest
Expand All @@ -230,14 +235,14 @@ jobs:
- name: Download staged package
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: swift-package
name: swift-ffi-package
path: artifacts

- name: Resolve and build smoke consumer
env:
BUILD_VERSION: ${{ needs.parse-version.outputs.version }}
run: |
./swift/scripts/verify.sh --tarball "artifacts/moq-ffi-${BUILD_VERSION}-swift.tar.gz"
./swift/scripts/verify-ffi.sh --tarball "artifacts/moq-ffi-${BUILD_VERSION}-swift-ffi.tar.gz"

publish:
name: Publish to Swift Package mirror
Expand All @@ -257,27 +262,24 @@ jobs:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
owner: moq-dev
repositories: moq-swift
# Narrow the minted token to only what publish.sh needs,
# rather than inheriting the full App installation scope.
repositories: moq-swift-ffi
# Narrow the minted token to only what publish-ffi.sh needs.
permission-contents: write

- name: Download package
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: swift-package
name: swift-ffi-package
path: swift-out

- name: Publish to moq-dev/moq-swift mirror
- name: Publish to moq-dev/moq-swift-ffi mirror
env:
BUILD_VERSION: ${{ needs.parse-version.outputs.version }}
# Token is minted fresh per run, expires in 1 hour, never at rest.
SWIFT_MIRROR_TOKEN: ${{ steps.token.outputs.token }}
# Use the moq-bot identity for the commit author so the
# mirror's git log reflects who actually pushed.
GIT_AUTHOR_NAME: moq-bot
GIT_AUTHOR_EMAIL: moq-bot[bot]@users.noreply.github.com
run: ./swift/scripts/publish.sh
run: ./swift/scripts/publish-ffi.sh

publish-dry-run:
name: Publish dry-run
Expand All @@ -293,10 +295,10 @@ jobs:
- name: Download package
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: swift-package
name: swift-ffi-package
path: swift-out

- name: Dry-run publish to mirror
env:
BUILD_VERSION: ${{ needs.parse-version.outputs.version }}
run: ./swift/scripts/publish.sh --dry-run
run: ./swift/scripts/publish-ffi.sh --dry-run
Loading
Loading