1 name: Release SDK Swift
2
3 on:
4 push:
5 tags:
6 - "[0-9]+.[0-9]+.[0-9]+"
7 workflow_dispatch:
8
9 permissions:
10 contents: write
11
12 jobs:
13 build-and-release:
14 runs-on: macos-26
15
16 env:
17 GRESIQ_API_KEY_DEV: ${{ secrets.GRESIQ_API_KEY_DEV }}
18 GRESIQ_API_SECRET_DEV: ${{ secrets.GRESIQ_API_SECRET_DEV }}
19 GRESIQ_API_KEY_PRODUCTION: ${{ secrets.GRESIQ_API_KEY_PRODUCTION }}
20 GRESIQ_API_SECRET_PRODUCTION: ${{ secrets.GRESIQ_API_SECRET_PRODUCTION }}
21 HF_TOKEN: ${{ secrets.HF_TOKEN }}
22
23 steps:
24 - name: Checkout onde
25 uses: actions/checkout@v6
26
27 - name: Install stable Rust toolchain
28 uses: dtolnay/rust-toolchain@stable
29 with:
30 toolchain: 1.92.0
31 targets: aarch64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-darwin
32
33 # tvOS targets are tier-3 — no prebuilt std libs available via rustup.
34 # The build script uses `cargo +nightly -Z build-std` which compiles std
35 # from source using the rust-src component. Do NOT list tvOS in targets:.
36 - name: Install nightly Rust toolchain for tvOS
37 uses: dtolnay/rust-toolchain@nightly
38 with:
39 components: rust-src
40
41 - name: Verify Xcode
42 run: xcodebuild -version
43
44 - name: Build Swift XCFramework
45 run: .github/scripts/build-swift-xcframework.sh
46
47 # ── Read build outputs ────────────────────────────────────────────────
48
49 - name: Read version and checksum
50 id: meta
51 run: |
52 VERSION=$(cat dist/swift/version.txt)
53 CHECKSUM=$(cat dist/swift/OndeFramework.checksum.txt)
54 echo "version=$VERSION" >> "$GITHUB_OUTPUT"
55 echo "checksum=$CHECKSUM" >> "$GITHUB_OUTPUT"
56
57 - name: Validate tag matches Cargo.toml version
58 if: startsWith(github.ref, 'refs/tags/')
59 env:
60 CARGO_VERSION: ${{ steps.meta.outputs.version }}
61 TAG: ${{ github.ref_name }}
62 run: |
63 if [ "$TAG" != "$CARGO_VERSION" ]; then
64 echo "::error::Tag '$TAG' does not match Cargo.toml version '$CARGO_VERSION'. Update Cargo.toml or re-tag."
65 exit 1
66 fi
67 echo "Version check passed: $CARGO_VERSION"
68
69 # ── Upload CI artifacts (every run) ──────────────────────────────────
70
71 - name: Upload XCFramework artifacts
72 uses: actions/upload-artifact@v7
73 with:
74 name: onde-swift-xcframework
75 path: |
76 dist/swift/OndeFramework.xcframework.zip
77 dist/swift/OndeFramework.checksum.txt
78 dist/swift/version.txt
79 dist/swift/Package/Sources/Onde/onde.swift
80 if-no-files-found: error
81
82 # ── Create GitHub Release and attach assets (tag push only) ──────────
83
84 - name: Create GitHub Release
85 if: startsWith(github.ref, 'refs/tags/')
86 uses: softprops/action-gh-release@v2
87 with:
88 name: ${{ github.ref_name }}
89 generate_release_notes: true
90 files: |
91 dist/swift/OndeFramework.xcframework.zip
92 dist/swift/OndeFramework.checksum.txt
93
94 # ── Update onde-swift (tag push only) ────────────────────────────────
95
96 - name: Checkout onde-swift
97 if: startsWith(github.ref, 'refs/tags/')
98 uses: actions/checkout@v6
99 with:
100 repository: ondeinference/onde-swift
101 token: ${{ secrets.ONDE_SWIFT_PAT }}
102 path: onde-swift
103
104 - name: Rewrite Package.swift with release URL and checksum
105 if: startsWith(github.ref, 'refs/tags/')
106 env:
107 CHECKSUM: ${{ steps.meta.outputs.checksum }}
108 RELEASE_URL: https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/OndeFramework.xcframework.zip
109 run: |
110 python3 - <<'PY'
111 import os, re, pathlib
112
113 pkg = pathlib.Path("onde-swift/Package.swift")
114 src = pkg.read_text()
115
116 url = os.environ["RELEASE_URL"]
117 checksum = os.environ["CHECKSUM"].strip()
118
119 pattern = re.compile(
120 r'\.binaryTarget\(\s*name:\s*"OndeFramework".*?\)',
121 re.DOTALL,
122 )
123
124 replacement = (
125 '.binaryTarget(\n'
126 ' name: "OndeFramework",\n'
127 f' url: "{url}",\n'
128 f' checksum: "{checksum}"\n'
129 ' )'
130 )
131
132 updated = pattern.sub(replacement, src)
133 if updated == src:
134 raise RuntimeError("binaryTarget pattern did not match — check Package.swift format")
135
136 pkg.write_text(updated)
137 print("Package.swift updated:")
138 print(updated)
139 PY
140
141 - name: Copy generated onde.swift into onde-swift
142 if: startsWith(github.ref, 'refs/tags/')
143 run: |
144 cp dist/swift/Package/Sources/Onde/onde.swift \
145 onde-swift/Sources/Onde/onde.swift
146
147 - name: Commit and tag onde-swift
148 if: startsWith(github.ref, 'refs/tags/')
149 env:
150 VERSION: ${{ steps.meta.outputs.version }}
151 run: |
152 cd onde-swift
153 git config user.name "github-actions[bot]"
154 git config user.email "github-actions[bot]@users.noreply.github.com"
155 git add Package.swift Sources/Onde/onde.swift
156
157 if git diff --cached --quiet; then
158 echo "No changes to commit — Package.swift and onde.swift are already up to date."
159 else
160 git commit -m "Release $VERSION"
161 fi
162
163 # Push the commit to main first so the tag points to the latest HEAD
164 git push origin HEAD:main
165
166 # Create an annotated tag only if it doesn't already exist on the remote.
167 # Never force-overwrite — SPM caches the resolved commit SHA per tag,
168 # so moving a tag silently breaks every consumer's Package.resolved.
169 if git ls-remote --tags origin "refs/tags/$VERSION" | grep -q "refs/tags/$VERSION"; then
170 echo "::warning::Tag $VERSION already exists on onde-swift — skipping tag push."
171 echo "To re-release this version, delete the remote tag first:"
172 echo " git push origin :refs/tags/$VERSION"
173 else
174 git tag -a "$VERSION" -m "Release $VERSION"
175 git push origin "refs/tags/$VERSION"
176 fi