Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2fd1d1a

Browse files
authoredMay 7, 2025··
Merge pull request #190 from powersync-ja/refactor-swift-release
Simplify release process for Swift build
2 parents db76b6d + c9b8184 commit 2fd1d1a

File tree

7 files changed

+130
-214
lines changed

7 files changed

+130
-214
lines changed
 

‎.github/workflows/deploy.yml

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,35 @@
1-
name: Deploy to Sonatype
1+
name: Release
22

33
on: workflow_dispatch
44

5-
permissions:
6-
contents: read
7-
85
jobs:
9-
test:
10-
uses: ./.github/workflows/test.yml
11-
deploy:
12-
needs: [test]
6+
draft_release:
7+
permissions:
8+
contents: write
9+
name: Create Draft Release on GitHub
10+
runs-on: ubuntu-latest
11+
outputs:
12+
tag: ${{ steps.tag.outputs.tag }}
13+
steps:
14+
- uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0
17+
18+
- name: Set tag name
19+
id: tag
20+
run: |
21+
tag=$(basename "${{ github.ref }}")
22+
echo "tag=$tag" >> $GITHUB_OUTPUT
23+
- name: Create Release
24+
env:
25+
GH_TOKEN: ${{ github.token }}
26+
GH_REPO: ${{ github.repository }}
27+
run: |
28+
tag="${{ steps.tag.outputs.tag }}"
29+
body="Pending release for XCFramework, $tag"
30+
gh release create --draft "$tag" --title "$tag" --notes "$body"
31+
32+
maven_publish:
1333
runs-on: macos-latest
1434
steps:
1535
- uses: actions/checkout@v4
@@ -38,17 +58,69 @@ jobs:
3858
-Ppowersync.binaries.allPlatforms="true" \
3959
publishAllPublicationsToSonatypeRepository
4060
shell: bash
41-
# This will change Package.swift in Github packages to direct to new maven central KMMBridge zip file
42-
call-kmmbridge-publish:
43-
needs: deploy
61+
62+
build_xcframeworks:
63+
name: Build XCFrameworks
64+
runs-on: macos-latest
65+
steps:
66+
- uses: actions/checkout@v4
67+
- name: Validate Gradle Wrapper
68+
uses: gradle/wrapper-validation-action@v1
69+
- uses: actions/cache@v3
70+
with:
71+
path: ~/.konan
72+
key: ${{ runner.os }}-${{ hashFiles('**/.lock') }}
73+
- name: Set up JDK 17
74+
uses: actions/setup-java@v3
75+
with:
76+
java-version: '17'
77+
distribution: 'temurin'
78+
- name: Set up Gradle
79+
uses: gradle/actions/setup-gradle@v4
80+
- name: Build frameworks
81+
run: "./gradlew PowerSyncKotlin:buildRelease"
82+
83+
- uses: actions/upload-artifact@v4
84+
with:
85+
name: XCFramework
86+
retention-days: 1 # Only used temporarily
87+
compression-level: 0 # We're already uploading a compressed file
88+
path: PowerSyncKotlin/build/FrameworkArchives/PowersyncKotlinRelease.zip
89+
if-no-files-found: error
90+
91+
add_assets:
4492
permissions:
4593
contents: write
46-
packages: write
47-
uses: touchlab/KMMBridgeGithubWorkflow/.github/workflows/faktorybuildautoversion.yml@v1.2
48-
with:
49-
jvmVersion: 17
50-
versionBaseProperty: SWIFT_LIBRARY_VERSION
51-
publishTask: kmmBridgePublish
52-
secrets:
53-
gradle_params: -PsigningInMemoryKey="${{ secrets.SIGNING_KEY }}" -PsigningInMemoryKeyId="${{ secrets.SIGNING_KEY_ID }}" -PsigningInMemoryKeyPassword="${{ secrets.SIGNING_PASSWORD }}"
94+
needs: [draft_release, build_xcframeworks]
95+
name: Add assets to pending release
96+
runs-on: ubuntu-latest
97+
steps:
98+
- uses: actions/checkout@v4
99+
with:
100+
fetch-depth: 0
101+
- uses: actions/download-artifact@v4
102+
with:
103+
merge-multiple: true
104+
- run: "ls -al"
105+
- name: Upload XCFramework
106+
env:
107+
GH_TOKEN: ${{ github.token }}
108+
GH_REPO: ${{ github.repository }}
109+
run: |
110+
gh release upload "${{ needs.draft_release.outputs.tag }}" PowersyncKotlinRelease.zip
54111
112+
- name: "Update release description"
113+
env:
114+
GH_TOKEN: ${{ github.token }}
115+
GH_REPO: ${{ github.repository }}
116+
shell: bash
117+
run: |
118+
checksums=$(sha256sum PowersyncKotlinRelease.zip)
119+
cat > RELEASE_NOTES <<- NOTES_END
120+
File hashes:
121+
\`\`\`
122+
$checksums
123+
\`\`\`
124+
NOTES_END
125+
126+
gh release edit "${{ needs.draft_release.outputs.tag }}" -F RELEASE_NOTES
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
// swift-tools-version:5.3
2-
import PackageDescription
1+
// swift-tools-version: 5.7
32

3+
// NOTE! This is never released, we're only using this to support local Kotlin SDK builds for the
4+
// Swift SDK.
5+
import PackageDescription
46
let packageName = "PowerSyncKotlin"
57

68
let package = Package(
@@ -12,14 +14,12 @@ let package = Package(
1214
products: [
1315
.library(
1416
name: packageName,
15-
targets: [packageName]
16-
),
17+
targets: [packageName]),
1718
],
1819
targets: [
1920
.binaryTarget(
2021
name: packageName,
21-
path: "./PowerSyncKotlin/build/XCFrameworks/debug/\(packageName).xcframework"
22+
path: "build/XCFrameworks/debug/PowerSyncKotlin.xcframework"
2223
)
23-
,
2424
]
25-
)
25+
)

‎PowerSyncKotlin/build.gradle.kts

Lines changed: 22 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
1-
import co.touchlab.faktory.KmmBridgeExtension
2-
import co.touchlab.faktory.artifactmanager.ArtifactManager
3-
import co.touchlab.faktory.capitalized
41
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
5-
import java.security.MessageDigest
2+
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
63

74
plugins {
85
alias(libs.plugins.kotlinMultiplatform)
9-
alias(libs.plugins.kmmbridge)
106
alias(libs.plugins.skie)
11-
alias(libs.plugins.mavenPublishPlugin)
127
alias(libs.plugins.kotlinter)
13-
id("com.powersync.plugins.sonatype")
148
}
159

1610
kotlin {
11+
val xcf = XCFramework()
12+
1713
listOf(
1814
iosX64(),
1915
iosArm64(),
@@ -22,8 +18,14 @@ kotlin {
2218
macosX64(),
2319
).forEach {
2420
it.binaries.framework {
21+
baseName = "PowerSyncKotlin"
22+
xcf.add(this)
23+
2524
export(project(":core"))
2625
isStatic = true
26+
27+
binaryOption("bundleId", "PowerSyncKotlin")
28+
binaryOption("bundleVersion", project.version.toString())
2729
}
2830
}
2931

@@ -64,180 +66,22 @@ configurations.all {
6466
}
6567
}
6668

67-
kmmbridge {
68-
artifactManager.set(SonatypePortalPublishArtifactManager(project, repositoryName = null))
69-
artifactManager.finalizeValue()
70-
spm()
71-
}
72-
73-
// We need this so that when a user includes the package in XCode they are able to
74-
// import the package using Github
75-
if (System.getenv().containsKey("CI")) {
76-
// Setup github publishing based on GitHub action variables
77-
addGithubPackagesRepository()
78-
}
79-
80-
// This is required for KMMBridge zip to be uploaded to Sonatype (Maven Central)
81-
// Since this will only ever be used in this build file it does not make sense to make a
82-
// plugin to use this.
83-
class SonatypePortalPublishArtifactManager(
84-
val project: Project,
85-
private val publicationName: String = "KMMBridgeFramework",
86-
artifactSuffix: String = "kmmbridge",
87-
private val repositoryName: String?
88-
) : ArtifactManager {
89-
private val group: String = project.group.toString().replace(".", "/")
90-
private val kmmbridgeArtifactId =
91-
"${project.name}-$artifactSuffix"
92-
private val zipName = "powersync-$artifactSuffix"
93-
private val LIBRARY_VERSION: String by project
94-
95-
// This is the URL that will be added to Package.swift in Github package so that
96-
// KMMBridge is downloaded when a user includes the package in XCode
97-
private val MAVEN_CENTRAL_PACKAGE_ZIP_URL = "https://repo1.maven.org/maven2/com/powersync/${zipName}/${LIBRARY_VERSION}/${zipName}-${LIBRARY_VERSION}.zip"
98-
99-
override fun deployArtifact(
100-
project: Project,
101-
zipFilePath: File,
102-
version: String
103-
): String = MAVEN_CENTRAL_PACKAGE_ZIP_URL
104-
105-
override fun configure(
106-
project: Project,
107-
version: String,
108-
uploadTask: TaskProvider<Task>,
109-
kmmPublishTask: TaskProvider<Task>
110-
) {
111-
val zipXCFramework = project.tasks.named<Zip>("zipXCFramework")
112-
zipXCFramework.configure {
113-
// KMMBridge uses the Gradle Zip tasks to create XCFramework archives, but Gradle
114-
// doesn't support symlinks. XCFrameworks for macOS need to use symlinks though, so we
115-
// patch the task to generate zip files properly.
116-
doLast {
117-
val bridge = project.extensions.getByName<KmmBridgeExtension>("kmmbridge")
118-
val source = project.layout.buildDirectory.map { it.dir("XCFrameworks/${bridge.buildType.get().name}") }.get().asFile
119-
120-
val out = archiveFile.get().asFile
121-
out.delete()
69+
listOf("Debug", "Release").forEach { buildType ->
70+
tasks.register<Exec>("build$buildType") {
71+
group = "build"
72+
description = "Create an XCFramework archive for $buildType"
12273

123-
providers.exec {
124-
executable = "zip"
125-
args("-r", "--symlinks", out.absolutePath, "PowerSyncKotlin.xcframework")
126-
workingDir(source)
127-
}.result.get().assertNormalExitValue()
128-
}
129-
}
130-
131-
project.extensions.getByType<PublishingExtension>().publications.create(
132-
publicationName,
133-
MavenPublication::class.java,
134-
) {
135-
this.version = version
136-
val archiveProvider = zipXCFramework.flatMap {
137-
it.archiveFile
138-
}
139-
artifact(archiveProvider) {
140-
extension = "zip"
141-
}
142-
artifactId = kmmbridgeArtifactId
143-
}
144-
145-
// Register the task
146-
project.tasks.register<UpdatePackageSwiftChecksumTask>("updatePackageSwiftChecksum") {
147-
artifactId.set(kmmbridgeArtifactId)
148-
zipUrl.set(MAVEN_CENTRAL_PACKAGE_ZIP_URL)
149-
dependsOn("updatePackageSwift")
150-
}
151-
152-
// Make sure this task runs after updatePackageSwift
153-
project.tasks.named("kmmBridgePublish") {
154-
dependsOn("updatePackageSwiftChecksum")
155-
}
74+
val originalFramework = tasks.getByName("assemblePowerSyncKotlin${buildType}XCFramework")
75+
dependsOn(originalFramework)
15676

157-
publishingTasks().forEach {
158-
uploadTask.configure {
159-
dependsOn(it)
160-
}
161-
}
162-
try {
163-
project.tasks.named("publish").also { task ->
164-
task.configure {
165-
dependsOn(kmmPublishTask)
166-
}
167-
}
168-
} catch (_: UnknownTaskException) {
169-
project.logger.warn("Gradle publish task not found")
170-
}
171-
}
172-
173-
private fun publishingTasks(): List<TaskProvider<Task>> {
174-
val publishingExtension = project.extensions.getByType<PublishingExtension>()
175-
176-
// Either the user has supplied a correct name, or we use the default. If neither is found, fail.
177-
val publicationNameCap =
178-
publishingExtension.publications
179-
.getByName(
180-
publicationName,
181-
).name
182-
.capitalized()
183-
184-
return publishingExtension.repositories
185-
.filterIsInstance<MavenArtifactRepository>()
186-
.map { repo ->
187-
val repositoryName = repo.name.capitalized()
188-
val publishTaskName =
189-
"publish${publicationNameCap}PublicationTo${repositoryName}Repository"
190-
// Verify that the "publish" task exists before collecting
191-
project.tasks.named(publishTaskName)
192-
}
193-
}
194-
}
195-
196-
// This task is used to update Package.swift with the checksum of the zip file
197-
// located on maven central.
198-
abstract class UpdatePackageSwiftChecksumTask : DefaultTask() {
199-
@get:Input
200-
abstract val artifactId: Property<String>
201-
202-
@get:Input
203-
abstract val zipUrl: Property<String>
204-
205-
@TaskAction
206-
fun updateChecksum() {
207-
val LIBRARY_VERSION: String by project
208-
209-
val zipFile = project.file("${project.layout.buildDirectory.get()}/tmp/${artifactId.get().lowercase()}-$LIBRARY_VERSION.zip")
210-
211-
// Download the zip file
212-
zipFile.parentFile.mkdirs()
213-
project.uri(zipUrl.get()).toURL().openStream().use { input ->
214-
zipFile.outputStream().use { output ->
215-
input.copyTo(output)
216-
}
217-
}
218-
219-
// Compute the checksum
220-
val checksum =
221-
zipFile.inputStream().use { input ->
222-
val digest = MessageDigest.getInstance("SHA-256")
223-
val buffer = ByteArray(8192)
224-
var bytes = input.read(buffer)
225-
while (bytes >= 0) {
226-
digest.update(buffer, 0, bytes)
227-
bytes = input.read(buffer)
228-
}
229-
digest.digest().joinToString("") { "%02x".format(it) }
230-
}
77+
val source = project.layout.buildDirectory.map { it.dir("XCFrameworks/${buildType.lowercase()}") }.get().asFile
78+
val archiveFile = project.layout.buildDirectory.map { it.file("FrameworkArchives/PowersyncKotlin$buildType.zip") }.get().asFile
23179

232-
// Update Package.swift
233-
val packageSwiftFile = project.rootProject.file("Package.swift")
234-
val updatedContent =
235-
packageSwiftFile.readText().replace(
236-
Regex("let remoteKotlinChecksum = \"[a-f0-9]+\""),
237-
"let remoteKotlinChecksum = \"$checksum\"",
238-
)
239-
packageSwiftFile.writeText(updatedContent)
80+
archiveFile.parentFile.mkdirs()
81+
archiveFile.delete()
24082

241-
println("Updated Package.swift with new checksum: $checksum")
83+
executable = "zip"
84+
args("-r", "--symlinks", archiveFile.absolutePath, "PowerSyncKotlin.xcframework")
85+
workingDir(source)
24286
}
24387
}

‎build.gradle.kts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ plugins {
1010
alias(libs.plugins.androidApplication) apply false
1111
alias(libs.plugins.androidLibrary) apply false
1212
alias(libs.plugins.kotlinMultiplatform) apply false
13-
alias(libs.plugins.cocoapods) apply false
14-
alias(libs.plugins.kmmbridge) apply false
1513
alias(libs.plugins.skie) apply false
1614
alias(libs.plugins.kotlin.jvm) apply false
1715
alias(libs.plugins.kotlin.android) apply false

‎docs/Release.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22

33
## How to make a release
44

5-
1. Update `LIBRARY_VERSION` and `SWIFT_LIBRARY_VERSION` in `gradle.properties` in the root.
5+
1. Update `LIBRARY_VERSION` in `gradle.properties` in the root.
66
2. Add an entry to the `CHANGELOG.md`.
77
3. Make a PR and merge it.
8-
4. Once the PR is merged and in the `main` branch then manually run the Github action `Deploy to Sonatype`. This will create a release to Maven Central and will also update the version of the `powersync-kotlin` SPM package used in the Swift SDK. If the release contains changes pertaining to the Swift SDK you will need to update the `powersync-kotlin` SPM package version in that repo and make a release there as well.
8+
4. Pull `main` (which now contains your merged PR) and create a tag matching the version, e.g.
9+
`git tag v1.1.0`.
10+
5. Push that tag and manually trigger the GitHub action `Release` on that tag. This will:
11+
- Create a release to Maven Central.
12+
- Create a draft release on Github.
13+
- Build and attach an `XCFramework` zip archive for the Swift SDK to the draft release.
14+
6. Copy relevant entries from the changelog into the draft release and make it public!
15+
7. To apply this release to the Swift SDK, update the `Package.swift` file to point at the framework
16+
from that release. You can copy the SHA256 hashsum from the generated draft release notes.

‎gradle.properties

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ RELEASE_SIGNING_ENABLED=true
1818
# Library config
1919
GROUP=com.powersync
2020
LIBRARY_VERSION=1.1.0
21-
# The Swift KMM bridge artifacts are published to SPM via a unique tag version
22-
# The version is the same as the LIBRARY_VERSION, but with a suffix of +SWIFT
23-
# Please update this when updating the LIBRARY_VERSION
24-
SWIFT_LIBRARY_VERSION=1.1.0+SWIFT
2521
GITHUB_REPO=https://github.com/powersync-ja/powersync-kotlin.git
2622
# POM
2723
POM_URL=https://github.com/powersync-ja/powersync-kotlin/

‎gradle/libs.versions.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ androidxSqlite = "2.4.0"
3434

3535
# plugins
3636
android-gradle-plugin = "8.9.0"
37-
kmmBridge = "0.5.7"
3837
skie = "0.10.1"
3938
maven-publish = "0.27.0"
4039
download-plugin = "5.5.0"
@@ -125,7 +124,6 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
125124
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
126125
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
127126
skie = { id = "co.touchlab.skie", version.ref = "skie" }
128-
kmmbridge = { id = "co.touchlab.kmmbridge", version.ref = "kmmBridge" }
129127
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqlDelight" }
130128
grammarKitComposer = { id = "com.alecstrong.grammar.kit.composer", version.ref = "grammarkit-composer" }
131129
mavenPublishPlugin = { id = "com.vanniktech.maven.publish", version.ref = "maven-publish" }

0 commit comments

Comments
 (0)
Please sign in to comment.