Update SDK distribution spec to add kmp publishing plan (#1929)

* Update SDK distribution specifications and add remote publishing plan

* fix(spec): align SD-06 plan with SD-04 conventions

- Rename gpr.key to gpr.token for consistency with SD-04
- Move publish workflow from out-of-scope into scope
- Add workflow section (Section 4) matching publish-android-sdk.yml pattern
- Fix access model description (public repo, auth still required)
- Add workflow to files-to-modify and definition-of-done

---------

Co-authored-by: Javier Cortejoso <javier.cortejoso@gmail.com>
This commit is contained in:
Seshanth.S
2026-04-08 12:01:17 +05:30
committed by GitHub
parent 06a513918f
commit 0f3cef7f56
4 changed files with 247 additions and 3 deletions

View File

@@ -133,3 +133,4 @@ Any other domain request returns a `DOMAIN_NOT_FOUND` error response.
| [Native Shells Lite](../native-shells-lite/SPEC.md) | Sibling — serves non-KMP consumers |
| [Paused Native Shells (KMP)](../../paused/native-shells/SPEC.md) | Historical KMP work — validated foundation |
| [Build Pipeline](../build-pipeline/SPEC.md) | Downstream — bundles webview-app into native assets |
| [SDK Distribution — SD-06](../sdk-distribution/SPEC.md) | Downstream — remote publishing after KR-03 validates artifacts |

View File

@@ -21,7 +21,7 @@ After KR-01 (Android parity) and KR-02 (iOS parity), you need to validate that t
### Out of Scope
- Actual publishing to external Maven/SPM registries (that's a follow-up, equivalent to paused NS-08)
- Actual publishing to external Maven/SPM registries (tracked as [SD-06](../../sdk-distribution/plans/SD-06-kmp-remote-publishing.md))
- `packages/native-shell-android/` and `packages/native-shell-ios/` — do not modify
- CI/CD pipeline changes
- Android dependency trimming or Android manifest slimming inside `packages/kmp-sdk/` — that ownership belongs to KR-01; KR-03 validates the result

View File

@@ -1,6 +1,6 @@
# SDK Distribution — Implementation Spec
> Last updated: 2026-03-30
> Last updated: 2026-04-06
> Owner: SDK / Platform
> Parent: `../../OVERVIEW.md`
> Status: Active
@@ -44,6 +44,7 @@
| Native Shells (Lite) — NSL-02 | Upstream | Active | iOS shell must exist before switching URL loading |
| `packages/webview-app/` | Upstream | Active | Source of the hosted web app |
| Build Pipeline | Sibling | Active | Bundle script remains for local dev only after SD-03 |
| KMP Revival — KR-03 | Upstream | Active | KMP artifacts validated locally before remote publish |
## Ownership Boundaries
@@ -52,6 +53,7 @@
| `packages/native-shell-android/` | SDK Distribution | Config + URL loading changes only |
| `packages/native-shell-ios/` | SDK Distribution | Config + URL loading changes only |
| `packages/webview-app/` | SDK Distribution | Hosting setup only (no source changes) |
| `packages/kmp-sdk/` | SDK Distribution | Publishing config only (SD-06) |
| Bridge handlers | Native Shells | Not modified by this workstream |
## Backlog
@@ -63,6 +65,7 @@
| SD-03 | WebView app hosting setup | Ready | High | — | [plans/SD-03-hosting-setup.md](./plans/SD-03-hosting-setup.md) | — |
| SD-04 | Android Maven publishing | Ready | Medium | SD-01 | [plans/SD-04-android-maven-publishing.md](./plans/SD-04-android-maven-publishing.md) | — |
| SD-05 | iOS publishing (SPM + CocoaPods) | Ready | Medium | SD-02 | [plans/SD-05-ios-spm-publishing.md](./plans/SD-05-ios-spm-publishing.md) | — |
| SD-06 | KMP remote publishing (Maven+SPM) | Ready | Medium | KR-03 | [plans/SD-06-kmp-remote-publishing.md](./plans/SD-06-kmp-remote-publishing.md) | — |
Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done`
@@ -70,7 +73,7 @@ Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done`
1. **SD-03** first — hosting must be live before native shells can load it
2. **SD-01 + SD-02** in parallel — Android and iOS URL loading changes
3. **SD-04 + SD-05** in parallel — publishing cleanup after URL loading works
3. **SD-04 + SD-05 + SD-06** in parallel — all publishing (SD-06 runs once KR-03 completes)
## Active Plans
@@ -81,6 +84,7 @@ Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done`
| [plans/SD-03-hosting-setup.md](./plans/SD-03-hosting-setup.md) | SD-03 | Ready |
| [plans/SD-04-android-maven-publishing.md](./plans/SD-04-android-maven-publishing.md) | SD-04 | Ready |
| [plans/SD-05-ios-spm-publishing.md](./plans/SD-05-ios-spm-publishing.md) | SD-05 | Ready |
| [plans/SD-06-kmp-remote-publishing.md](./plans/SD-06-kmp-remote-publishing.md) | SD-06 | Ready |
## Completion Checklist
@@ -108,3 +112,4 @@ Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done`
| [Build Pipeline](../build-pipeline/SPEC.md) | Sibling — bundle script retained for local dev only |
| [WebView Spec](../webview/SPEC.md) | Upstream — produces the web app being hosted |
| [SDK Core Spec](../sdk-core/SPEC.md) | Sibling — engine consumed by hosted web app |
| [KMP Revival](../kmp-revival/SPEC.md) | Upstream — KR-03 validates artifacts before SD-06 publishes |

View File

@@ -0,0 +1,238 @@
## KMP Remote Publishing (Maven + SPM)
> Last updated: 2026-04-06
> Status: Ready
- Workstream: sdk-distribution
- Backlog IDs: SD-06
- Owner: TBD
- Branch: TBD
- PR: TBD
### Why
The KMP SDK (`packages/kmp-sdk/`) already has `maven-publish` configured and produces both AAR and XCFramework artifacts (validated by KR-03). What's missing is the remote publishing configuration: a Maven repository target for Android consumers and a hosted XCFramework URL for iOS/SPM consumers. Without this, integrators must build from source or use local artifacts.
### Scope
- Add remote Maven repository configuration to `packages/kmp-sdk/shared/build.gradle.kts`
- Switch `createXCFramework` task from debug to release variants
- Update `packages/kmp-sdk/Package.swift` from local path to remote URL + checksum
- Add `publish-kmp-sdk.yml` workflow (manual dispatch with dry-run, matching SD-04 pattern)
### Out of Scope
- Maven Central account setup / GPG signing (ops task, not code)
- CDN or hosting infrastructure for XCFramework ZIP
- KMP source code changes (owned by kmp-revival workstream)
- Native shell publishing (SD-04, SD-05)
- NFC / biometric handler registration or dependency changes
- NFCPassportReader fork accessibility (known limitation, does not block 3-domain distribution)
### Preconditions
- KR-03 complete (build artifacts validated locally, `publishToMavenLocal` succeeds, XCFramework builds)
### Files to Modify
- `packages/kmp-sdk/shared/build.gradle.kts` — Add `publishing { repositories { maven { ... } } }` block for remote Maven repo. Switch `createXCFramework` task dependencies from `linkDebugFramework*` to `linkReleaseFramework*` and update framework paths from `debugFramework` to `releaseFramework`.
- `packages/kmp-sdk/Package.swift` — Change `.binaryTarget` from local `path:` to remote `url:` + `checksum:`.
- `.github/workflows/publish-kmp-sdk.yml` — Add manual dispatch workflow with dry-run mode (matching SD-04 pattern).
### Files NOT to Modify
- `packages/kmp-sdk/shared/src/` — No source code changes
- `packages/native-shell-android/` — Separate publishing (SD-04)
- `packages/native-shell-ios/` — Separate publishing (SD-05)
- `packages/webview-app/` — Upstream, do not change
- `packages/self-sdk-swift/` — iOS provider package, unchanged
### Implementation Details
#### 1. Add remote Maven repository configuration
**File:** `packages/kmp-sdk/shared/build.gradle.kts`
The `maven-publish` plugin is already applied (line 7). `publishLibraryVariants("release")` is already set (line 17). The Gradle module name defaults to `shared` (the module directory name), which would publish as `xyz.self.sdk:shared`. Override the `artifactId` to `self-sdk-kmp` so the final Maven coordinate is `xyz.self.sdk:self-sdk-kmp:0.1.0`. Add a `publishing` block with the artifactId override and remote repository target:
```kotlin
publishing {
publications.withType<MavenPublication> {
artifactId = "self-sdk-kmp"
}
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/selfxyz/self")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("gpr.token") as String? ?: System.getenv("GITHUB_TOKEN")
}
}
}
}
```
This adds ~13 LOC. The `groupId` (`xyz.self.sdk`) and `version` (`0.1.0`) are already set at lines 10-11. The published Maven coordinate will be `xyz.self.sdk:self-sdk-kmp:0.1.0`.
#### 2. Switch XCFramework to release variants
**File:** `packages/kmp-sdk/shared/build.gradle.kts`
Update the `createXCFramework` task (lines 145-175):
- Change `dependsOn` from `linkDebugFrameworkIosArm64` / `linkDebugFrameworkIosSimulatorArm64` to `linkReleaseFrameworkIosArm64` / `linkReleaseFrameworkIosSimulatorArm64`
- Update framework paths from `debugFramework` to `releaseFramework`
```kotlin
tasks.register("createXCFramework") {
group = "build"
description = "Creates XCFramework for iOS distribution"
dependsOn(
":shared:linkReleaseFrameworkIosArm64",
":shared:linkReleaseFrameworkIosSimulatorArm64",
)
doLast {
val buildDir = layout.buildDirectory.get().asFile
val frameworkPath = "$buildDir/bin/iosArm64/releaseFramework/SelfSdk.framework"
val simulatorFrameworkPath = "$buildDir/bin/iosSimulatorArm64/releaseFramework/SelfSdk.framework"
val xcframeworkPath = "$buildDir/xcframework/SelfSdk.xcframework"
// Remove existing XCFramework if present
project.delete(xcframeworkPath)
project.exec {
commandLine(
"xcodebuild",
"-create-xcframework",
"-framework", frameworkPath,
"-framework", simulatorFrameworkPath,
"-output", xcframeworkPath,
)
}
println("XCFramework created at: $xcframeworkPath")
}
}
```
~3 LOC changed (debug → release in 3 places).
#### 3. Update Package.swift for remote distribution
**File:** `packages/kmp-sdk/Package.swift`
Change the `.binaryTarget` from a local path to a remote URL with checksum:
```swift
targets: [
.binaryTarget(
name: "SelfSdk",
url: "https://github.com/selfxyz/self/releases/download/kmp-sdk-v0.1.0/SelfSdk.xcframework.zip",
checksum: "<SHA256_CHECKSUM>"
)
]
```
The checksum is computed from the zipped XCFramework:
```bash
cd packages/kmp-sdk
zip -r SelfSdk.xcframework.zip shared/build/xcframework/SelfSdk.xcframework
swift package compute-checksum SelfSdk.xcframework.zip
```
The actual URL and checksum values will be set during the first release. Use placeholder values with a `// TODO: Update on first release` comment until then.
#### 4. Add publish workflow
**File:** `.github/workflows/publish-kmp-sdk.yml`
Match the SD-04 pattern (`publish-android-sdk.yml`): manual `workflow_dispatch` with `version` input and `dry-run` toggle (defaults to on). Steps: checkout → setup JDK 17 → cache Gradle → set version → build → test (`./gradlew :shared:jvmTest`) → publish (`publishToMavenLocal` for dry-run, `publishAllPublicationsToGitHubPackagesRepository` for real).
### Validation
```bash
cd packages/kmp-sdk
# Verify remote Maven repo is configured
./gradlew tasks --all | grep -i publish
# Should show publishAllPublicationsToGitHubPackagesRepository (or similar)
# Verify release XCFramework builds
./gradlew createXCFramework
ls -la shared/build/xcframework/SelfSdk.xcframework
# Verify Package.swift is valid
swift package dump-package
# Existing tests still pass
./gradlew :shared:jvmTest
```
### Consumer Setup
The Maven package on GitHub Packages requires authentication even though the repo is public. Consumers need a GitHub token with `read:packages` scope (any GitHub account works — no org/repo access required).
#### Android (Gradle)
Add the GitHub Packages Maven repository to `settings.gradle.kts` (or root `build.gradle.kts`):
```kotlin
dependencyResolutionManagement {
repositories {
maven {
url = uri("https://maven.pkg.github.com/selfxyz/self")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("gpr.token") as String? ?: System.getenv("GITHUB_TOKEN")
}
}
}
}
```
Then add the dependency:
```kotlin
implementation("xyz.self.sdk:self-sdk-kmp:0.1.0")
```
Consumers need a GitHub personal access token with `read:packages` scope. Set in `~/.gradle/gradle.properties`:
```properties
gpr.user=GITHUB_USERNAME
gpr.token=ghp_YOUR_TOKEN
```
Or set `GITHUB_ACTOR` / `GITHUB_TOKEN` environment variables in CI.
#### iOS (SPM)
The XCFramework ZIP hosted on GitHub Releases can also be private. If the repo is private, consumers must authenticate for `swift package resolve` to download the binary target. Xcode handles this via the logged-in GitHub account in **Xcode > Settings > Accounts**. CI environments need a `netrc` entry or `GITHUB_TOKEN` for authentication.
### Known Limitations
- **GitHub Packages Maven:** Consumers need a GitHub token with `read:packages` scope to resolve dependencies (see Consumer Setup above). If public access is required, migration to Maven Central is a follow-up.
### Definition of Done
- [ ] `publishing` block added to `build.gradle.kts` with `artifactId = "self-sdk-kmp"` and GitHub Packages repository
- [ ] `createXCFramework` uses release variants (not debug)
- [ ] `Package.swift` uses `.binaryTarget(url:checksum:)` instead of `path:`
- [ ] `./gradlew :shared:jvmTest` passes
- [ ] `./gradlew createXCFramework` produces a release XCFramework
- [ ] `swift package dump-package` succeeds
- [ ] `publish-kmp-sdk.yml` workflow added with dry-run mode
- [ ] Backlog row updated
- [ ] Plan status updated
### Estimated PR Size
~50-80 LOC changed. Well within the 1k-3k target.
### Status Log
- 2026-04-06: Plan created. Based on NS-03 audit findings and KR-03 deferred publishing scope.