main
yml 182 lines 7.62 KB
Raw
1 name: Release SDK Swift
2
3 on:
4 push:
5 tags:
6 - "v[0-9]+.[0-9]+.[0-9]+"
7 workflow_dispatch:
8 inputs:
9 publish:
10 description: "Publish Ed Swift release assets from the selected ref"
11 type: boolean
12 default: false
13 release_tag:
14 description: "Existing semver tag to publish on manual runs"
15 type: string
16 required: false
17
18 permissions:
19 contents: write
20
21 # One release at a time per tag. Not cancel-in-progress: the later steps create
22 # a GitHub release and then push a commit and a tag to ed-swift, and killing a
23 # run part-way through that leaves a release with no assets on it, or ed-swift
24 # updated against a release that no longer matches. A duplicate run waits for
25 # the 2h20m build ahead of it instead.
26 concurrency:
27 group: release-sdk-swift-${{ github.ref }}
28 cancel-in-progress: false
29
30 jobs:
31 preflight:
32 # macOS rather than Linux: the release artifact is an XCFramework, the whole
33 # toolchain is Apple, and building onde on Linux means building mistral.rs
34 # plus a GTK/WebKit stack for the Tauri crate for no coverage anyone ships.
35 runs-on: macos-26
36 defaults:
37 run:
38 working-directory: ed
39 steps:
40 - uses: actions/checkout@v6
41 with:
42 path: ed
43 - name: Read Rust toolchain
44 run: |
45 rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
46 test -n "$rust_toolchain"
47 echo "RUST_TOOLCHAIN=$rust_toolchain" >> "$GITHUB_ENV"
48 - uses: dtolnay/rust-toolchain@stable
49 with:
50 toolchain: ${{ env.RUST_TOOLCHAIN }}
51 # The action installs a minimal profile, so the pinned toolchain has
52 # neither of these unless they are asked for by name.
53 components: rustfmt, clippy
54 - name: Validate tag matches workspace version
55 if: startsWith(github.ref, 'refs/tags/')
56 run: |
57 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"] == "ed-agent"))')"
58 test "${GITHUB_REF_NAME#v}" = "$version"
59 - run: cargo fmt --all -- --check
60 - run: cargo test --locked --workspace
61 - run: cargo clippy --locked --workspace --all-targets -- -D warnings
62 # Not --locked: packaging swaps the onde path dependency for the registry
63 # one, which is a lockfile change by definition. This step is also the
64 # gate that catches publishing Ed before the onde version it requires.
65 - run: cargo publish --dry-run -p ed-agent
66
67 build-and-release:
68 needs: preflight
69 runs-on: macos-26
70 defaults:
71 run:
72 working-directory: ed
73 steps:
74 - uses: actions/checkout@v6
75 with:
76 path: ed
77 - name: Read Rust toolchain
78 run: |
79 rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
80 test -n "$rust_toolchain"
81 echo "RUST_TOOLCHAIN=$rust_toolchain" >> "$GITHUB_ENV"
82 - uses: dtolnay/rust-toolchain@stable
83 with:
84 toolchain: ${{ env.RUST_TOOLCHAIN }}
85 targets: aarch64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-darwin
86 - uses: dtolnay/rust-toolchain@nightly
87 with:
88 components: rust-src
89 - run: .github/scripts/build-swift-xcframework.sh
90 - id: meta
91 run: |
92 echo "version=$(cat dist/swift/version.txt)" >> "$GITHUB_OUTPUT"
93 echo "checksum=$(cat dist/swift/EdFramework.checksum.txt)" >> "$GITHUB_OUTPUT"
94 - id: release
95 env:
96 INPUT_RELEASE_TAG: ${{ inputs.release_tag }}
97 run: |
98 if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
99 release_tag="${GITHUB_REF_NAME}"
100 else
101 release_tag="${INPUT_RELEASE_TAG}"
102 fi
103 echo "release_tag=$release_tag" >> "$GITHUB_OUTPUT"
104 - name: Validate release tag
105 if: startsWith(github.ref, 'refs/tags/') || inputs.publish
106 env:
107 VERSION: ${{ steps.meta.outputs.version }}
108 RELEASE_TAG: ${{ steps.release.outputs.release_tag }}
109 run: |
110 if [ -z "$RELEASE_TAG" ]; then
111 echo "::error::release_tag is required when publish=true on workflow_dispatch runs."
112 exit 1
113 fi
114 # Ed tags carry a `v` prefix, version.txt does not, so compare the
115 # semver rather than the spelling -- the same normalisation preflight
116 # does on GITHUB_REF_NAME.
117 if [ "${RELEASE_TAG#v}" != "$VERSION" ]; then
118 echo "::error::Release tag '$RELEASE_TAG' does not match workspace version '$VERSION'."
119 exit 1
120 fi
121 - name: Upload build artifact
122 if: github.event_name == 'workflow_dispatch'
123 uses: actions/upload-artifact@v7
124 with:
125 name: ed-swift-xcframework
126 path: |
127 ed/dist/swift/EdFramework.xcframework.zip
128 ed/dist/swift/EdFramework.checksum.txt
129 ed/dist/swift/version.txt
130 ed/dist/swift/Package/Sources/Ed/ed_agent_ffi.swift
131 - name: Create GitHub release
132 if: startsWith(github.ref, 'refs/tags/') || inputs.publish
133 uses: softprops/action-gh-release@v3
134 with:
135 tag_name: ${{ steps.release.outputs.release_tag }}
136 name: ${{ steps.release.outputs.release_tag }}
137 generate_release_notes: true
138 files: |
139 ed/dist/swift/EdFramework.xcframework.zip
140 ed/dist/swift/EdFramework.checksum.txt
141 - name: Checkout ed-swift
142 if: startsWith(github.ref, 'refs/tags/') || inputs.publish
143 uses: actions/checkout@v6
144 with:
145 repository: ondeinference/ed-swift
146 token: ${{ secrets.ED_SWIFT_PAT }}
147 path: ed-swift
148 - name: Update Swift package
149 if: startsWith(github.ref, 'refs/tags/') || inputs.publish
150 working-directory: ${{ github.workspace }}
151 env:
152 CHECKSUM: ${{ steps.meta.outputs.checksum }}
153 RELEASE_URL: https://github.com/${{ github.repository }}/releases/download/${{ steps.release.outputs.release_tag }}/EdFramework.xcframework.zip
154 run: |
155 python3 - <<'PY'
156 import os, pathlib, re
157 package = pathlib.Path("ed-swift/Package.swift")
158 source = package.read_text()
159 source, urls = re.subn(r'(let releaseFrameworkURL =\s*\n\s*")([^"]+)(")', rf'\g<1>{os.environ["RELEASE_URL"]}\g<3>', source, count=1)
160 source, sums = re.subn(r'(let releaseFrameworkChecksum =\s*\n\s*")([^"]+)(")', rf'\g<1>{os.environ["CHECKSUM"]}\g<3>', source, count=1)
161 if (urls, sums) != (1, 1):
162 raise RuntimeError("Package.swift release constants were not found")
163 package.write_text(source)
164 PY
165 cp ed/dist/swift/Package/Sources/Ed/ed_agent_ffi.swift ed-swift/Sources/Ed/ed_agent_ffi.swift
166 - name: Commit and tag Swift package
167 if: startsWith(github.ref, 'refs/tags/') || inputs.publish
168 working-directory: ${{ github.workspace }}/ed-swift
169 env:
170 VERSION: ${{ steps.release.outputs.release_tag }}
171 run: |
172 git config user.name "github-actions[bot]"
173 git config user.email "github-actions[bot]@users.noreply.github.com"
174 git add Package.swift Sources/Ed/ed_agent_ffi.swift
175 git diff --cached --quiet || git commit -m "Release $VERSION"
176 git push origin HEAD:main
177 if git ls-remote --tags origin "refs/tags/$VERSION" | grep -q "refs/tags/$VERSION"; then
178 echo "::warning::Tag $VERSION already exists; leaving it unchanged."
179 else
180 git tag -a "$VERSION" -m "Release $VERSION"
181 git push origin "$VERSION"
182 fi