Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
163 changes: 163 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
name: Release builds

on:
workflow_dispatch:
push:
tags: ['v*']

permissions:
contents: read

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
version:
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.version.outputs.version }}
code: ${{ steps.version.outputs.code }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: version
name: Validate release version
shell: bash
run: |
python3 - <<'PY'
import os, re, subprocess, tomllib
with open('Cargo.toml', 'rb') as f:
version = tomllib.load(f)['workspace']['package']['version']
if not re.fullmatch(r'\d+\.\d+\.\d+', version):
raise SystemExit('Only stable X.Y.Z versions supported by this workflow')
if os.environ['GITHUB_REF_TYPE'] == 'tag' and os.environ['GITHUB_REF_NAME'] != f'v{version}':
raise SystemExit('Tag must match Cargo workspace version')
code = subprocess.check_output(['git', 'rev-list', '--count', 'HEAD'], text=True).strip()
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f'version={version}\ncode={code}\n')
PY

linux:
needs: version
runs-on: ubuntu-24.04
env:
VERSION: ${{ needs.version.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Install build and packaging dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential pkg-config cmake ninja-build clang libclang-dev \
libgtk-3-dev libasound2-dev libssl-dev libdbus-1-dev libabsl-dev \
libxkbcommon-dev libxcb1-dev dpkg-dev desktop-file-utils
- name: Install Rust
run: rustup toolchain install stable --profile minimal && rustup default stable
- name: Build tarball and Debian package
run: |
desktop-file-validate packaging/linux/tinline.desktop
./scripts/package_linux_tarball.sh
./scripts/package_deb.sh
- name: Inspect and install Debian package on build runner
run: |
dpkg-deb --info dist/deb/*.deb
dpkg-deb --contents dist/deb/*.deb
sudo apt-get install -y ./dist/deb/*.deb
ldd /usr/bin/tinline | tee dist/linux-libraries.txt
! grep -q 'not found' dist/linux-libraries.txt
- uses: actions/upload-artifact@v4
with:
name: linux-release
path: |
dist/deb/*
dist/releases/**/*
dist/linux-libraries.txt
if-no-files-found: error

android:
needs: version
runs-on: ubuntu-24.04
environment: release
env:
VERSION: ${{ needs.version.outputs.version }}
VERSION_CODE: ${{ needs.version.outputs.code }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- uses: android-actions/setup-android@v3
- name: Install native build dependencies and Android SDK
run: |
sudo apt-get update
sudo apt-get install -y build-essential cmake ninja-build pkg-config clang libclang-dev
sdkmanager 'platforms;android-36' 'build-tools;36.0.0' 'ndk;28.2.13676358'
- name: Install Rust and cargo-ndk
run: |
rustup toolchain install stable --profile minimal
rustup default stable
rustup target add aarch64-linux-android x86_64-linux-android
cargo install cargo-ndk --version 4.1.2 --locked
- name: Restore release keystore
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: |
test -n "$KEYSTORE_BASE64" || { echo 'Missing Android release keystore secret'; exit 1; }
umask 077
printf '%s' "$KEYSTORE_BASE64" | base64 --decode > "$RUNNER_TEMP/tinline-release.jks"
- name: Build signed release APK
working-directory: android
env:
ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
export ORG_GRADLE_PROJECT_RELEASE_STORE_FILE="$RUNNER_TEMP/tinline-release.jks"
./gradlew --no-daemon --console=plain :app:assembleRelease \
-PrequireReleaseSigning=true -PversionName="$VERSION" -PversionCode="$VERSION_CODE" \
-Pabis=x86_64,arm64-v8a
- name: Verify APK signature and collect artifact
run: |
"$ANDROID_HOME/build-tools/36.0.0/apksigner" verify --verbose --print-certs \
android/app/build/outputs/apk/release/app-release.apk
mkdir -p dist/android
cp android/app/build/outputs/apk/release/app-release.apk "dist/android/tinline-v${VERSION}.apk"
cd dist/android
sha256sum *.apk > SHA256SUMS
- name: Remove keystore
if: always()
run: rm -f "$RUNNER_TEMP/tinline-release.jks"
- uses: actions/upload-artifact@v4
with:
name: android-release
path: dist/android/*
if-no-files-found: error

draft-release:
if: github.ref_type == 'tag'
needs: [version, linux, android]
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create draft release and attach packages
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }}
run: |
if gh release view "$TAG" >/dev/null 2>&1; then
test "$(gh release view "$TAG" --json isDraft --jq .isDraft)" = true || {
echo 'Refusing to replace artifacts on a published release'; exit 1;
}
else
gh release create "$TAG" --verify-tag --draft --title "Tinline $TAG" \
--notes 'Candidate Linux tarball, Debian package, and signed Android APK. Test before publishing. Linux build baseline: Ubuntu 24.04; older distros are not verified.'
fi
mapfile -d '' files < <(find artifacts -type f -print0)
gh release upload "$TAG" "${files[@]}" --clobber
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
target
dist/
android/.gradle/
android/build/
android/app/build/
Expand Down
10 changes: 7 additions & 3 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ android {
targetSdk = 36
// Play needs a higher code on every upload; scripts/build_android.py --bundle passes the commit count.
versionCode = (findProperty("versionCode") as String?)?.toInt() ?: 1
versionName = "0.1.0"
versionName = (findProperty("versionName") as String?) ?: "0.1.0"
// Only ship ABIs that have libp2pcore: libraries like JNA bring 32-bit/mips copies, which
// would make Play offer the app to devices where the core can't load.
ndk { abiFilters += (findProperty("abis") as String? ?: "x86_64,arm64-v8a").split(",").map { it.trim() } }
Expand All @@ -26,6 +26,10 @@ android {
// RELEASE_KEY_PASSWORD in ~/.gradle/gradle.properties (or -P). Without them the release build
// is signed with the DEBUG key so it can be installed locally for testing -- NOT publishable.
val relStore = findProperty("RELEASE_STORE_FILE") as String?
if ((findProperty("requireReleaseSigning") as String?) == "true") {
listOf("RELEASE_STORE_FILE", "RELEASE_STORE_PASSWORD", "RELEASE_KEY_ALIAS", "RELEASE_KEY_PASSWORD")
.forEach { if ((findProperty(it) as String?).isNullOrBlank()) throw GradleException("Missing release signing property: $it") }
}
signingConfigs {
if (relStore != null) create("release") {
storeFile = file(relStore)
Expand Down Expand Up @@ -107,7 +111,7 @@ val cargoNdkBuild = tasks.register<RustExec>("cargoNdkBuild") {
cmd.set(buildList {
add("cargo"); add("ndk")
rustAbis.forEach { add("-t"); add(it) }
addAll(listOf("-P", "28", "-o", jniOut.get().asFile.absolutePath, "build", "-p", "p2pcore"))
addAll(listOf("-P", "28", "-o", jniOut.get().asFile.absolutePath, "build", "--locked", "-p", "p2pcore"))
if (rustRelease) add("--release")
})
inputs.files(rustInputs).withPropertyName("rustSources").withPathSensitivity(PathSensitivity.RELATIVE)
Expand All @@ -123,7 +127,7 @@ val uniffiBindgen = tasks.register<RustExec>("uniffiBindgen") {
workDir.set(repoRoot.absolutePath)
extraEnv.set(envMap)
val so = File(repoRoot, "target/x86_64-linux-android/$profileDir/libp2pcore.so")
cmd.set(listOf("cargo", "run", "-q", "-p", "p2pcore", "--bin", "uniffi-bindgen", "--",
cmd.set(listOf("cargo", "run", "--locked", "-q", "-p", "p2pcore", "--bin", "uniffi-bindgen", "--",
"generate", "--library", so.absolutePath, "--language", "kotlin",
"--out-dir", uniffiOut.get().asFile.absolutePath))
inputs.files(rustInputs).withPropertyName("rustSources").withPathSensitivity(PathSensitivity.RELATIVE)
Expand Down
5 changes: 5 additions & 0 deletions android/app/src/debug/kotlin/com/osvauld/p2p/DebugReceiver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ class DebugReceiver : BroadcastReceiver() {
app.refresh()
testLog("added name=${c.name} did=${c.did}")
}
"clip" -> {
val cm = app.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
cm.setPrimaryClip(android.content.ClipData.newPlainText("test", i.getStringExtra("text") ?: node.myTicket()))
testLog("clip set")
}
"contacts" -> node.contacts().forEach { testLog("contact name=${it.name} did=${it.did}") }
"call" -> {
val who = i.getStringExtra("who") ?: ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ private fun Gallery(which: String, app: P2pApp, act: ComponentActivity) {
"home_search" -> HomeContent(contacts, true, false, emptyList(), {}, {}, none, {}, {}, none, emptyList(), null, startSearching = true, startQuery = "jo")
"home_empty" -> HomeContent(emptyList(), true, false, emptyList(), {}, {}, none, {}, {}, none, emptyList(), null)
"home_offline" -> HomeContent(contacts, false, false, listOf(Need.Mic), {}, {}, none, {}, {}, none, emptyList(), null)
"mycode" -> AddContactScreen(app, false, none, {}, {})
"scan" -> AddContactScreen(app, true, none, {}, {})
"paste" -> PasteCardScreen("OSVC2:MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPE======", {}, none, none)
"mycode" -> AddContactScreen(app, false, onClose = none, onCall = {}, onVerify = {})
"scan" -> AddContactScreen(app, true, onClose = none, onCall = {}, onVerify = {})
"paste" -> PasteCardScreen(app, "OSVC2:MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPE======", {}, none, {})
"adding" -> AddingScreen(maya, none)
"added" -> AddedScreen(maya, arjun, none, none, none)
"failed" -> FailedScreen(maya, null, none, none)
Expand Down
7 changes: 7 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,19 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|keyboard|keyboardHidden|uiMode|density|fontScale|layoutDirection|locale"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- "Share -> Tinline" from a messenger: a contact card in the text opens "Add <name>?". -->
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity
android:name=".IncomingCallActivity"
Expand Down
Loading