@ondeinference / onde / commits / c52b212

Automate onde-swift release: CI updates Package.swift and onde.swift

- CI now rewrites onde-swift/Package.swift with the release url: and checksum, copies the generated onde.swift, commits, tags, and pushes to onde-swift. - Manual edits to Package.swift are no longer needed after a release. - build-swift-xcframework.yml: add steps for version validation, artifact upload, onde-swift checkout, Package.swift rewrite, onde.swift copy, and tagging. - Update AGENTS.md to document the automated release workflow and required secrets.

Seto Elkahfi committed Apr 3, 2026 at 09:18 UTC c52b21216eece667d1c9ab7dfdbfcf79713f5e27
2 files changed +148 -14
.agents/AGENTS.md
+26 -11
@@ -269,10 +269,14 @@ $BINDGEN generate target/aarch64-apple-ios/release/libonde.a \
269
270 ### Updating `onde-swift` after a new release
271
272 -1. Run the build script and collect `OndeFramework.xcframework.zip` + checksum.
273 -2. Create a GitHub Release on the `onde` repo and attach the zip.
274 -3. In `onde-swift/Package.swift`, update the `.binaryTarget` to use the new `url:` and `checksum:`.
275 -4. Commit, tag (`0.x.y`), and push `onde-swift`.
272 +This is fully automated by CI. Publishing a GitHub Release on `onde` triggers the workflow, which:
273 +
274 +1. Builds the XCFramework and attaches it to the release.
275 +2. Rewrites `onde-swift/Package.swift` with the release `url:` + `checksum:`.
276 +3. Copies the freshly generated `onde.swift` into `onde-swift/Sources/Onde/`.
277 +4. Commits, tags, and pushes `onde-swift` — Swift Package Index picks it up automatically.
278 +
279 +Manual intervention is only needed if `ONDE_SWIFT_PAT` has expired or the `onde-swift` push fails.
280
281 ---
282
@@ -289,7 +293,7 @@ onde-swift/
293
294 `Package.swift` has two targets:
295
292 -- **`OndeFramework`** — `.binaryTarget` pointing at `OndeFramework.xcframework` (local path during dev, remote URL+checksum for distribution).
296 +- **`OndeFramework`** — `.binaryTarget` using `url:` + `checksum:` for distribution. For local development, swap to the `path:` form documented in the `Package.swift` header comment and run `build-swift-xcframework.sh` first.
297 - **`Onde`** — `.target` depending on `OndeFramework`, used as the public import name in Swift.
298
299 ### Swift API Quick Reference
@@ -463,7 +467,7 @@ cargo check
467 ### Swift / SDK
468
469 - **Never manually edit `onde-swift/Sources/Onde/onde.swift`** — it is generated by `uniffi-bindgen`. Regenerate by running the build script.
466 -- **`Package.swift` binary target path** must point to the correct `.xcframework` location: local path during development, remote URL+checksum for published releases.
470 +- **`Package.swift` binary target** uses `url:` + `checksum:` in the committed form. For local development, swap to `path:` (instructions are in the file header). Never commit the `path:` form — CI overwrites it on every release.
471 - **iOS/tvOS sandbox:** always call `setupInferenceEnvironment()` at app launch before any `OndeChatEngine` call.
472
473 ---
@@ -478,17 +482,28 @@ Steps:
482 1. Install stable Rust (`1.92.0`) with iOS/macOS targets.
483 2. Install nightly Rust with `rust-src` component and tvOS targets.
484 3. Run `build-swift-xcframework.sh`.
481 -4. Upload artifacts: zip, checksum, version, generated `onde.swift`.
482 -5. On release: attach zip and checksum to the GitHub Release.
485 +4. Read `version.txt` and `OndeFramework.checksum.txt` into step outputs (`steps.meta.outputs.version` / `steps.meta.outputs.checksum`).
486 +5. Upload CI artifacts: zip, checksum, version, generated `onde.swift`.
487 +6. On release: attach zip and checksum to the GitHub Release.
488 +7. On release: check out `ondeinference/onde-swift` using `ONDE_SWIFT_PAT`.
489 +8. On release: rewrite the `.binaryTarget` in `onde-swift/Package.swift` with the release download URL and computed checksum.
490 +9. On release: copy the freshly generated `onde.swift` into `onde-swift/Sources/Onde/`.
491 +10. On release: commit both files, tag the commit with the version, and push to `ondeinference/onde-swift`.
492 +
493 +#### Required secret
494 +
495 +`ONDE_SWIFT_PAT` — a GitHub Personal Access Token with **`contents: write`** scope on the `ondeinference/onde-swift` repository. Create it at <https://github.com/settings/tokens> and add it as a repository secret on the `onde` repo.
496
497 ### Release Process (end-to-end)
498
499 1. Bump version in `onde/Cargo.toml`.
487 -2. Tag and push `onde` repo → CI builds and attaches `OndeFramework.xcframework.zip`.
488 -3. In `onde-swift`, update `Package.swift` with new `url:` and `checksum:`.
489 -4. Commit, tag, push `onde-swift` → Swift Package Index indexes it automatically.
500 +2. Create and publish a GitHub Release on the `onde` repo (tag = version, e.g. `0.2.0`).
501 +3. CI builds the XCFramework, attaches `OndeFramework.xcframework.zip` + checksum to the release, then **automatically** rewrites `onde-swift/Package.swift` with the correct `url:` + `checksum:`, commits, tags, and pushes `onde-swift`.
502 +4. Swift Package Index picks up the new `onde-swift` tag automatically — no manual step needed.
503 5. `cargo publish` to crates.io.
504
505 +> **Note:** Steps 3–4 are fully automated. Manual edits to `onde-swift/Package.swift` are no longer needed after a release. Only touch it when doing local development (use the `path:` form documented in the file header).
506 +
507 ---
508
509 ## Distribution Registry Summary
.github/workflows/build-swift-xcframework.yml
+122 -3
@@ -11,8 +11,9 @@ permissions:
11 jobs:
12 build-swift-xcframework:
13 runs-on: macos-15
14 +
15 steps:
15 - - name: Checkout
16 + - name: Checkout onde
17 uses: actions/checkout@v6
18
19 - name: Install stable Rust toolchain
@@ -21,11 +22,13 @@ jobs:
22 toolchain: 1.92.0
23 targets: aarch64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-darwin
24
25 + # tvOS targets are tier-3 — no prebuilt std libs available via rustup.
26 + # The build script uses `cargo +nightly -Z build-std` which compiles std
27 + # from source using the rust-src component. Do NOT list tvOS in targets:.
28 - name: Install nightly Rust toolchain for tvOS
29 uses: dtolnay/rust-toolchain@nightly
30 with:
31 components: rust-src
28 - targets: aarch64-apple-tvos,aarch64-apple-tvos-sim
32
33 - name: Verify Xcode
34 run: xcodebuild -version
@@ -33,8 +36,36 @@ jobs:
36 - name: Build Swift XCFramework
37 run: .github/scripts/build-swift-xcframework.sh
38
39 + # ── Read build outputs ────────────────────────────────────────────────
40 +
41 + - name: Read version and checksum
42 + id: meta
43 + run: |
44 + VERSION=$(cat dist/swift/version.txt)
45 + CHECKSUM=$(cat dist/swift/OndeFramework.checksum.txt)
46 + echo "version=$VERSION" >> "$GITHUB_OUTPUT"
47 + echo "checksum=$CHECKSUM" >> "$GITHUB_OUTPUT"
48 +
49 + # ── Validate release tag matches Cargo.toml version ─────────────────
50 +
51 + - name: Validate version consistency
52 + if: github.event_name == 'release'
53 + env:
54 + CARGO_VERSION: ${{ steps.meta.outputs.version }}
55 + RELEASE_TAG: ${{ github.event.release.tag_name }}
56 + run: |
57 + # Strip optional 'v' prefix from the release tag for comparison.
58 + NORMALIZED_TAG="${RELEASE_TAG#v}"
59 + if [ "$NORMALIZED_TAG" != "$CARGO_VERSION" ]; then
60 + echo "::error::Release tag '$RELEASE_TAG' (normalized: '$NORMALIZED_TAG') does not match Cargo.toml version '$CARGO_VERSION'. Update Cargo.toml or re-tag the release."
61 + exit 1
62 + fi
63 + echo "Version check passed: $CARGO_VERSION"
64 +
65 + # ── Upload CI artifacts (every run) ──────────────────────────────────
66 +
67 - name: Upload XCFramework artifacts
37 - uses: actions/upload-artifact@v6
68 + uses: actions/upload-artifact@v7
69 with:
70 name: onde-swift-xcframework
71 path: |
@@ -44,6 +75,8 @@ jobs:
75 dist/swift/Package/Sources/Onde/onde.swift
76 if-no-files-found: error
77
78 + # ── Attach assets to the GitHub Release (release trigger only) ───────
79 +
80 - name: Upload release assets
81 if: github.event_name == 'release'
82 uses: softprops/action-gh-release@v2
@@ -51,3 +84,89 @@ jobs:
84 files: |
85 dist/swift/OndeFramework.xcframework.zip
86 dist/swift/OndeFramework.checksum.txt
87 +
88 + # ── Update onde-swift and push a matching tag (release trigger only) ─
89 +
90 + - name: Checkout onde-swift
91 + if: github.event_name == 'release'
92 + uses: actions/checkout@v6
93 + with:
94 + repository: ondeinference/onde-swift
95 + # A PAT with `contents: write` on the onde-swift repo.
96 + # Create it at https://github.com/settings/tokens and store as
97 + # a repository secret named ONDE_SWIFT_PAT on the onde repo.
98 + token: ${{ secrets.ONDE_SWIFT_PAT }}
99 + path: onde-swift
100 +
101 + - name: Rewrite Package.swift with release URL and checksum
102 + if: github.event_name == 'release'
103 + env:
104 + CHECKSUM: ${{ steps.meta.outputs.checksum }}
105 + # Use the actual GitHub release tag for the download URL — this is
106 + # what GitHub uses to construct the asset download path.
107 + RELEASE_URL: https://github.com/${{ github.repository }}/releases/download/${{ github.event.release.tag_name }}/OndeFramework.xcframework.zip
108 + run: |
109 + python3 - <<'PY'
110 + import os, re, pathlib
111 +
112 + pkg = pathlib.Path("onde-swift/Package.swift")
113 + src = pkg.read_text()
114 +
115 + url = os.environ["RELEASE_URL"]
116 + checksum = os.environ["CHECKSUM"].strip()
117 +
118 + # Replace any existing binaryTarget block (path: or url: form).
119 + # The pattern matches from .binaryTarget( up to the closing ),
120 + # anchored on the OndeFramework name so we don't touch other targets.
121 + pattern = re.compile(
122 + r'\.binaryTarget\(\s*name:\s*"OndeFramework".*?\)',
123 + re.DOTALL,
124 + )
125 +
126 + replacement = (
127 + '.binaryTarget(\n'
128 + ' name: "OndeFramework",\n'
129 + f' url: "{url}",\n'
130 + f' checksum: "{checksum}"\n'
131 + ' )'
132 + )
133 +
134 + updated = pattern.sub(replacement, src)
135 + if updated == src:
136 + raise RuntimeError("binaryTarget pattern did not match — check Package.swift format")
137 +
138 + pkg.write_text(updated)
139 + print("Package.swift updated:")
140 + print(updated)
141 + PY
142 +
143 + - name: Copy generated onde.swift into onde-swift
144 + if: github.event_name == 'release'
145 + run: |
146 + cp dist/swift/Package/Sources/Onde/onde.swift \
147 + onde-swift/Sources/Onde/onde.swift
148 +
149 + - name: Commit and tag onde-swift
150 + if: github.event_name == 'release'
151 + env:
152 + VERSION: ${{ steps.meta.outputs.version }}
153 + run: |
154 + cd onde-swift
155 + git config user.name "github-actions[bot]"
156 + git config user.email "github-actions[bot]@users.noreply.github.com"
157 + git add Package.swift Sources/Onde/onde.swift
158 +
159 + # Idempotent: skip commit if nothing changed (safe re-run).
160 + if git diff --cached --quiet; then
161 + echo "No changes to commit — Package.swift and onde.swift are already up to date."
162 + else
163 + git commit -m "Release $VERSION"
164 + fi
165 +
166 + # Create or move the version tag to this commit.
167 + # --force handles re-runs where the tag already exists.
168 + git tag -f "$VERSION"
169 +
170 + # Push to the default branch explicitly (checkout is detached HEAD).
171 + git push origin HEAD:main
172 + git push origin "$VERSION" --force