Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: 2

# Observe libsignal releases automatically. Dependabot watches Maven Central for
# new org.signal:libsignal-* versions and opens a PR bumping the single
# `libsignal` ref in gradle/libs.versions.toml. Because libsignal-client and
# libsignal-android both use version.ref = "libsignal", that one bump moves BOTH
# the JVM/Android Maven coords and (via ci.yml rebuilding the .a from source) the
# Apple native track in lockstep. The PR then triggers ci.yml, which clones
# signalapp/libsignal at the new tag, rebuilds libsignal_ffi.a, and runs the
# real-crypto tests on JVM + iOS — so a bad/ABI-changing version goes red on the
# PR instead of in a release.
updates:
- package-ecosystem: gradle
directory: "/"
schedule:
interval: daily
# Only libsignal must stay in lockstep with the native binary; let the rest
# be bumped manually so unrelated deps don't churn the (heavy) native CI.
allow:
- dependency-name: "org.signal:libsignal-client"
- dependency-name: "org.signal:libsignal-android"
commit-message:
prefix: chore
labels:
- libsignal-bump
open-pull-requests-limit: 1
70 changes: 70 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: CI

# Validates every PR / push — and crucially, proves a libsignal version bump
# actually works end-to-end BEFORE it is merged and tagged for release.
#
# The native libsignal_ffi.a is never committed: this job rebuilds it FRESH from
# Signal's source at the pinned version (cached so it only rebuilds when the
# version changes), embeds it into the Apple klib, and runs the real-crypto
# tests on JVM and the iOS simulator. If Signal ships a version whose FFI ABI
# changed, this job goes red here instead of breaking a published release.
on:
pull_request:
push:
branches: [main, master]
workflow_dispatch:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
runs-on: macos-14 # builds Apple natively + cross-compiles jvm/android/linux/wasm
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

- uses: gradle/actions/setup-gradle@v4

- uses: dtolnay/rust-toolchain@stable

# Single source of truth — the catalog version drives BOTH tracks.
- name: Resolve libsignal version
id: ls
run: |
v=$(grep -oE '^libsignal = "[^"]+"' gradle/libs.versions.toml | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
echo "version=$v" >> "$GITHUB_OUTPUT"
echo "libsignal=$v"

# Cache the built .a per version: a normal PR restores instantly; the first
# PR after a version bump pays the Rust build once, then everyone benefits.
- name: Cache libsignal_ffi.a
id: ffi-cache
uses: actions/cache@v4
with:
path: krypton-protocol/libs/apple
key: libsignal-ffi-${{ steps.ls.outputs.version }}-apple-v1

- name: Build fresh libsignal_ffi.a from Signal source (cache miss)
if: steps.ffi-cache.outputs.cache-hit != 'true'
run: scripts/build-libsignal-ffi.sh ${{ steps.ls.outputs.version }}

# Hard gate: native .a version must equal the JVM/Android Maven version.
- name: Verify native/Maven libsignal versions match
run: ./gradlew :krypton-protocol:verifyLibsignalVersion

# Prove the (possibly new) libsignal works on both tracks.
- name: JVM real-crypto tests
run: ./gradlew :krypton-protocol:jvmTest

- name: iOS simulator real-crypto tests
run: ./gradlew :krypton-protocol:iosSimulatorArm64Test

# Make sure every published target still compiles against this version.
- name: Compile all published targets
run: ./gradlew :krypton:assemble
70 changes: 70 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Release

# Publishes the whole Krypton KMP library to Maven Central.
#
# Why a single macOS runner: it can build EVERY Kotlin target — all Apple arches
# natively, and it cross-compiles JVM, Android, Linux, Windows and wasm. The only
# Krypton publishes only cinterop bindings (no Signal binary embedded — see
# SECURITY.md / "Bring your own libsignal"). This job builds the Apple .a FRESH
# from libsignal's pinned source ONLY to run the real-crypto tests before
# publishing; the published Apple klibs stay binding-only and Apple consumers
# fetch libsignal themselves. JVM/Android libsignal comes from Maven.
#
# Trigger: push a tag like `v0.1.0`, or run manually. Set these repo secrets:
# MAVEN_CENTRAL_USERNAME / MAVEN_CENTRAL_PASSWORD — Central Portal token
# SIGNING_KEY — ASCII-armored GPG private key (in-memory signing)
# SIGNING_PASSWORD — passphrase for that key
on:
push:
tags: ['v*']
workflow_dispatch:

jobs:
publish:
runs-on: macos-14 # Apple Silicon: native Apple builds + cross-compiles the rest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

- uses: gradle/actions/setup-gradle@v4

- uses: dtolnay/rust-toolchain@stable

# libsignal version is the single source of truth in the catalog; the native
# .a MUST match it (verifyLibsignalVersion enforces this below).
- name: Resolve libsignal version
id: ls
run: |
v=$(grep -oE '^libsignal = "[^"]+"' gradle/libs.versions.toml | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
echo "version=$v" >> "$GITHUB_OUTPUT"
echo "libsignal=$v"

# The Rust build of libsignal is slow; cache the produced .a per version so it
# only rebuilds when the libsignal version actually changes.
- name: Cache libsignal_ffi.a
id: ffi-cache
uses: actions/cache@v4
with:
path: krypton-protocol/libs/apple
key: libsignal-ffi-${{ steps.ls.outputs.version }}-apple-v1

- name: Build fresh libsignal_ffi.a (cache miss)
if: steps.ffi-cache.outputs.cache-hit != 'true'
run: scripts/build-libsignal-ffi.sh ${{ steps.ls.outputs.version }}

# Guard: native .a version must equal the JVM/Android Maven version, or an
# iOS user and an Android user could silently fail to talk to each other.
- name: Verify native/Maven libsignal versions match
run: ./gradlew :krypton-protocol:verifyLibsignalVersion

- name: Publish + release to Maven Central
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache --stacktrace
12 changes: 10 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@ build/
local.properties
.idea
*.class
*.jar
*.log
.DS_Store
.cxx
captures
.externalNativeBuild

# Prebuilt native crypto binaries (build artifacts — exceed GitHub size limits)
# Prebuilt native crypto binaries (build artifacts — rebuilt fresh in CI,
# exceed GitHub's size limits, and don't belong in source control).
*.a
*.o
# Vendored libsignal jars (~100 MB) — these come from Maven, never commit them.
krypton-protocol/libs/**/*.jar

# Generated Xcode project — the source of truth is project.yml.
# Run `xcodegen generate` in samples/composeApp/iosApp to recreate it.
samples/composeApp/iosApp/iosApp.xcodeproj/

# NOTE: the Gradle wrapper jar (gradle/wrapper/gradle-wrapper.jar) is intentionally
# committed so `./gradlew` works on a fresh clone — do not add a blanket *.jar rule.
Loading
Loading