Import IKAR messenger client and server

This commit is contained in:
sevenhill
2026-10-07 22:06:07 +03:00
commit 69ea5091eb
1951 changed files with 424546 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
<!--
Стиль оформления PR (заполняйте по этому шаблону).
Заголовок PR:
feat: краткое перечисление фич + scope → целевая ветка
Примеры:
feat: чаты, группы, файлы, real-time + security-фиксы #17 → dev/0.5.0
fix(ios): исключить Firebase из сборки iOS → dev/0.5.0
Префиксы: feat / fix / refactor / chore / docs (можно со scope, напр. fix(ios)).
Удалите неиспользуемые разделы. Описание — на русском языке.
-->
## Описание
<!-- 1–3 предложения: что переносим и зачем. Укажите исходную и целевую ветку. -->
**Объём:** N файлов, +X / −Y.
## Что вошло
<!-- Группируйте по областям с эмодзи. Оставляйте только релевантные блоки. -->
### 💬 Чаты и сообщения
-
### 👤 Профиль и контакты
-
### 🔒 Безопасность
<!-- При наличии: ссылка на issue и номера находок. -->
-
### 🛠 Прочее
-
## Коммиты
<!-- Список `hash` + краткое описание. -->
- `xxxxxxx`
## Проверка
- [ ] `flutter analyze`
- [ ] Сборка `qlyra` flavor
- [ ] Ручная проверка затронутых сценариев
- [ ] Регресс по безопасности (если затронута)
+17
View File
@@ -0,0 +1,17 @@
name: Build E2EE test library
description: Build and test the pinned C crypto core and expose it to Flutter tests.
runs:
using: composite
steps:
- shell: bash
run: |
cmake -S native/crypto-core -B build/native-tests/crypto-core -DCMAKE_BUILD_TYPE=Debug
cmake --build build/native-tests/crypto-core --config Debug --parallel 2
ctest --test-dir build/native-tests/crypto-core -C Debug --output-on-failure
if [ "$RUNNER_OS" = "Windows" ]; then
echo "QLYRA_E2EE_LIBRARY=$GITHUB_WORKSPACE/build/native-tests/crypto-core/Debug/komet_crypto.dll" >> "$GITHUB_ENV"
elif [ "$RUNNER_OS" = "macOS" ]; then
echo "QLYRA_E2EE_LIBRARY=$GITHUB_WORKSPACE/build/native-tests/crypto-core/libkomet_crypto.dylib" >> "$GITHUB_ENV"
else
echo "QLYRA_E2EE_LIBRARY=$GITHUB_WORKSPACE/build/native-tests/crypto-core/libkomet_crypto.so" >> "$GITHUB_ENV"
fi
+26
View File
@@ -0,0 +1,26 @@
name: Setup Rust
description: >
Install the Rust toolchain with platform targets and a build cache, so
cargokit can compile the native libraries during the Flutter build. kolibri
comes from pub.dev and builds inside the pub cache, so only the in-repo crate
is listed here; the shared cargo registry and git checkouts are cached anyway.
inputs:
targets:
description: Comma-separated rustup targets for the platform being built.
required: true
runs:
using: composite
steps:
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ inputs.targets }}
- name: Cache Rust build
uses: Swatinem/rust-cache@v2
with:
workspaces: |
native/qlyra_crypto/rust
native/komet_tlottie/rust
+63
View File
@@ -0,0 +1,63 @@
name: Android Emulator Smoke
on:
pull_request:
branches:
- main
- 'dev/**'
workflow_dispatch:
jobs:
launch-qlyra:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: gradle
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: stable
cache: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
- name: Install dependencies
run: flutter pub get
- name: Build smoke APK
run: flutter build apk --debug --flavor ikar
- name: Verify launch and warm resume on emulator
uses: ReactiveCircus/android-emulator-runner@v2
with:
api-level: 35
arch: x86_64
profile: pixel_6
disable-animations: true
script: |
adb install -r build/app/outputs/flutter-apk/app-ikar-debug.apk
python3 scripts/check-android-resume.py --output artifacts/android-resume-smoke
adb shell pidof ru.ikar.app
- name: Upload Android resume diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: android-resume-smoke
path: artifacts/android-resume-smoke
if-no-files-found: ignore
+170
View File
@@ -0,0 +1,170 @@
name: Build Android (FCM)
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- 'lib/**'
- 'android/**'
- 'pubspec.yaml'
- 'pubspec.lock'
- 'native/**'
- 'third_party/**'
- 'scripts/verify_android_signing.sh'
- '.github/workflows/build-android-fcm.yml'
jobs:
build-android-fcm:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
cache: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
- name: Get dependencies
run: flutter pub get
- name: Verify formatting
run: dart format --output=none --set-exit-if-changed lib test
- name: Flutter analyze
run: flutter analyze --no-fatal-infos lib test
- name: Install native plugin build dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev
- name: Prepare native plugin runtime for tests
run: dart run tool/prepare_plugin_runtime.dart
- name: Build E2EE test library
uses: ./.github/actions/setup-e2ee
- name: Build native crypto test library
run: cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
- name: Flutter tests
run: flutter test
- name: Configure Gradle
run: |
mkdir -p ~/.gradle
echo "org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m" >> ~/.gradle/gradle.properties
echo "kotlin.daemon.jvmargs=-Xmx1536m" >> ~/.gradle/gradle.properties
echo "org.gradle.caching=true" >> ~/.gradle/gradle.properties
echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties
echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties
- name: Setup release signing
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: |
if [ -z "$KEYSTORE_BASE64" ]; then
echo "Release signing secrets are required."
exit 1
fi
echo "$KEYSTORE_BASE64" | base64 -d > android/app/qlyra-release.jks
{
echo "storePassword=$KEYSTORE_PASSWORD"
echo "keyPassword=$KEY_PASSWORD"
echo "keyAlias=$KEY_ALIAS"
echo "storeFile=qlyra-release.jks"
} > android/key.properties
echo "Release signing configured."
- name: Build Universal APK
run: flutter build apk --release --flavor oneme --obfuscate --split-debug-info=build/symbols
- name: Build Split APKs
run: flutter build apk --release --split-per-abi --flavor oneme --obfuscate --split-debug-info=build/symbols
- name: Verify APK release certificates
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: bash scripts/verify_android_signing.sh
- name: Upload Universal APK
uses: actions/upload-artifact@v4
with:
name: qlyra-android-fcm-universal
path: build/app/outputs/flutter-apk/app-oneme-release.apk
if-no-files-found: error
retention-days: 30
- name: Upload arm64-v8a APK
uses: actions/upload-artifact@v4
with:
name: qlyra-android-fcm-arm64-v8a
path: build/app/outputs/flutter-apk/app-arm64-v8a-oneme-release.apk
if-no-files-found: error
retention-days: 30
- name: Upload armeabi-v7a APK
uses: actions/upload-artifact@v4
with:
name: qlyra-android-fcm-armeabi-v7a
path: build/app/outputs/flutter-apk/app-armeabi-v7a-oneme-release.apk
if-no-files-found: error
retention-days: 30
- name: Upload x86_64 APK
uses: actions/upload-artifact@v4
with:
name: qlyra-android-fcm-x86_64
path: build/app/outputs/flutter-apk/app-x86_64-oneme-release.apk
if-no-files-found: error
retention-days: 30
- name: Build App Bundle
run: flutter build appbundle --release --flavor oneme --obfuscate --split-debug-info=build/symbols
- name: Verify App Bundle release certificate
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: bash scripts/verify_android_signing.sh
- name: Upload App Bundle artifact
uses: actions/upload-artifact@v4
with:
name: qlyra-android-fcm-aab
path: build/app/outputs/bundle/onemeRelease/app-oneme-release.aab
if-no-files-found: error
retention-days: 30
- name: Upload debug symbols
uses: actions/upload-artifact@v4
with:
name: qlyra-android-fcm-symbols
path: build/symbols
if-no-files-found: error
retention-days: 90
+168
View File
@@ -0,0 +1,168 @@
name: Build Android (Google Play)
on:
workflow_dispatch:
jobs:
build-android:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
cache: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
- name: Get dependencies
run: flutter pub get
- name: Verify formatting
run: dart format --output=none --set-exit-if-changed lib test
- name: Flutter analyze
run: flutter analyze --no-fatal-infos lib test
- name: Install native plugin build dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev
- name: Prepare native plugin runtime for tests
run: dart run tool/prepare_plugin_runtime.dart
- name: Build E2EE test library
uses: ./.github/actions/setup-e2ee
- name: Build native crypto test library
run: cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
- name: Flutter tests
run: flutter test
- name: Configure Gradle
run: |
mkdir -p ~/.gradle
echo "org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m" >> ~/.gradle/gradle.properties
echo "kotlin.daemon.jvmargs=-Xmx1536m" >> ~/.gradle/gradle.properties
echo "org.gradle.caching=true" >> ~/.gradle/gradle.properties
echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties
echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties
- name: Setup release signing
env:
KEYSTORE_BASE64: ${{ secrets.PLAY_KEYSTORE_BASE64 || secrets.KEYSTORE_BASE64 }}
KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD || secrets.KEYSTORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD || secrets.KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS || secrets.KEY_ALIAS }}
run: |
if [ -z "$KEYSTORE_BASE64" ]; then
echo "Release signing secrets are required."
exit 1
fi
echo "$KEYSTORE_BASE64" | base64 -d > android/app/qlyra-release.jks
{
echo "storePassword=$KEYSTORE_PASSWORD"
echo "keyPassword=$KEY_PASSWORD"
echo "keyAlias=$KEY_ALIAS"
echo "storeFile=qlyra-release.jks"
} > android/key.properties
echo "Release signing configured."
- name: Build Universal APK
run: flutter build apk --release --flavor store --obfuscate --split-debug-info=build/symbols
- name: Verify store manifest
shell: bash
run: |
set -euo pipefail
manifest=$(find build/app/intermediates -path '*storeRelease*' -path '*merged_manifests*' -name AndroidManifest.xml -print -quit)
test -n "$manifest"
if grep -Eq 'REQUEST_INSTALL_PACKAGES|android:host="(www\.)?max\.ru"|firebaseinitprovider' "$manifest"; then
echo '::error::Store manifest contains forbidden entries'
exit 1
fi
- name: Build Split APKs
run: flutter build apk --release --split-per-abi --flavor store --obfuscate --split-debug-info=build/symbols
- name: Verify APK release certificates
env:
KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD || secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS || secrets.KEY_ALIAS }}
run: bash scripts/verify_android_signing.sh
- name: Upload Universal APK
uses: actions/upload-artifact@v4
with:
name: qlyra-play-universal
path: build/app/outputs/flutter-apk/app-store-release.apk
if-no-files-found: error
retention-days: 30
- name: Upload arm64-v8a APK
uses: actions/upload-artifact@v4
with:
name: qlyra-play-arm64-v8a
path: build/app/outputs/flutter-apk/app-arm64-v8a-store-release.apk
if-no-files-found: error
retention-days: 30
- name: Upload armeabi-v7a APK
uses: actions/upload-artifact@v4
with:
name: qlyra-play-armeabi-v7a
path: build/app/outputs/flutter-apk/app-armeabi-v7a-store-release.apk
if-no-files-found: error
retention-days: 30
- name: Upload x86_64 APK
uses: actions/upload-artifact@v4
with:
name: qlyra-play-x86_64
path: build/app/outputs/flutter-apk/app-x86_64-store-release.apk
if-no-files-found: error
retention-days: 30
- name: Build App Bundle
run: flutter build appbundle --release --flavor store --obfuscate --split-debug-info=build/symbols
- name: Verify App Bundle release certificate
env:
KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD || secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS || secrets.KEY_ALIAS }}
run: bash scripts/verify_android_signing.sh
- name: Upload App Bundle artifact
uses: actions/upload-artifact@v4
with:
name: qlyra-play-aab
path: build/app/outputs/bundle/storeRelease/app-store-release.aab
if-no-files-found: error
retention-days: 30
- name: Upload debug symbols
uses: actions/upload-artifact@v4
with:
name: qlyra-play-symbols
path: build/symbols
if-no-files-found: error
retention-days: 90
+170
View File
@@ -0,0 +1,170 @@
name: Build Android
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- 'lib/**'
- 'android/**'
- 'pubspec.yaml'
- 'pubspec.lock'
- 'native/**'
- 'third_party/**'
- 'scripts/verify_android_signing.sh'
- '.github/workflows/build-android.yml'
jobs:
build-android:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
cache: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
- name: Get dependencies
run: flutter pub get
- name: Verify formatting
run: dart format --output=none --set-exit-if-changed lib test
- name: Flutter analyze
run: flutter analyze --no-fatal-infos lib test
- name: Install native plugin build dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev
- name: Prepare native plugin runtime for tests
run: dart run tool/prepare_plugin_runtime.dart
- name: Build E2EE test library
uses: ./.github/actions/setup-e2ee
- name: Build native crypto test library
run: cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
- name: Flutter tests
run: flutter test
- name: Configure Gradle
run: |
mkdir -p ~/.gradle
echo "org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m" >> ~/.gradle/gradle.properties
echo "kotlin.daemon.jvmargs=-Xmx1536m" >> ~/.gradle/gradle.properties
echo "org.gradle.caching=true" >> ~/.gradle/gradle.properties
echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties
echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties
- name: Setup release signing
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: |
if [ -z "$KEYSTORE_BASE64" ]; then
echo "Release signing secrets are required."
exit 1
fi
echo "$KEYSTORE_BASE64" | base64 -d > android/app/qlyra-release.jks
{
echo "storePassword=$KEYSTORE_PASSWORD"
echo "keyPassword=$KEY_PASSWORD"
echo "keyAlias=$KEY_ALIAS"
echo "storeFile=qlyra-release.jks"
} > android/key.properties
echo "Release signing configured."
- name: Build Universal APK
run: flutter build apk --release --flavor qlyra --obfuscate --split-debug-info=build/symbols
- name: Build Split APKs
run: flutter build apk --release --split-per-abi --flavor qlyra --obfuscate --split-debug-info=build/symbols
- name: Verify APK release certificates
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: bash scripts/verify_android_signing.sh
- name: Upload Universal APK
uses: actions/upload-artifact@v4
with:
name: qlyra-android-universal
path: build/app/outputs/flutter-apk/app-qlyra-release.apk
if-no-files-found: error
retention-days: 30
- name: Upload arm64-v8a APK
uses: actions/upload-artifact@v4
with:
name: qlyra-android-arm64-v8a
path: build/app/outputs/flutter-apk/app-arm64-v8a-qlyra-release.apk
if-no-files-found: error
retention-days: 30
- name: Upload armeabi-v7a APK
uses: actions/upload-artifact@v4
with:
name: qlyra-android-armeabi-v7a
path: build/app/outputs/flutter-apk/app-armeabi-v7a-qlyra-release.apk
if-no-files-found: error
retention-days: 30
- name: Upload x86_64 APK
uses: actions/upload-artifact@v4
with:
name: qlyra-android-x86_64
path: build/app/outputs/flutter-apk/app-x86_64-qlyra-release.apk
if-no-files-found: error
retention-days: 30
- name: Build App Bundle
run: flutter build appbundle --release --flavor qlyra --obfuscate --split-debug-info=build/symbols
- name: Verify App Bundle release certificate
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: bash scripts/verify_android_signing.sh
- name: Upload App Bundle artifact
uses: actions/upload-artifact@v4
with:
name: qlyra-android-aab
path: build/app/outputs/bundle/qlyraRelease/app-qlyra-release.aab
if-no-files-found: error
retention-days: 30
- name: Upload debug symbols
uses: actions/upload-artifact@v4
with:
name: qlyra-android-symbols
path: build/symbols
if-no-files-found: error
retention-days: 90
+115
View File
@@ -0,0 +1,115 @@
name: Build iOS
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- 'lib/**'
- 'ios/**'
- 'pubspec.yaml'
- '.github/workflows/build-ios.yml'
pull_request:
paths:
- 'lib/**'
- 'ios/**'
- 'pubspec.yaml'
- '.github/workflows/build-ios.yml'
env:
FLUTTER_VERSION: '3.44.3'
jobs:
build-ios:
runs-on: macos-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios
- name: Get dependencies
run: flutter pub get
- name: Build iOS (no codesign)
run: |
flutter config --no-enable-swift-package-manager
flutter build ios --release --no-codesign
- name: Package unsigned IPA
run: |
mkdir -p dist build/ios/Payload
cp -R build/ios/iphoneos/Runner.app build/ios/Payload/
(cd build/ios && zip -qr ../../dist/Qlyra-ios-unsigned.ipa Payload)
rm -rf build/ios/Payload
- name: Upload unsigned iOS artifact
uses: actions/upload-artifact@v4
with:
name: qlyra-ios-unsigned
path: dist/Qlyra-ios-unsigned.ipa
if-no-files-found: error
retention-days: 30
- name: Install ldid
run: brew install ldid
- name: Pseudo-sign with ldid
run: |
set -euo pipefail
APP="build/ios/iphoneos/Runner.app"
ENT="$(mktemp -t ent).plist"
cat > "$ENT" <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>get-task-allow</key><true/>
<key>application-identifier</key><string>ru.qlyra.app</string>
<key>keychain-access-groups</key>
<array><string>ru.qlyra.app</string></array>
</dict>
</plist>
PLIST
if [ -d "$APP/Frameworks" ]; then
find "$APP/Frameworks" -type f -name "*.dylib" -print0 | xargs -0 -I{} ldid -S "{}"
find "$APP/Frameworks" -type d -name "*.framework" | while read -r fw; do
bin="$fw/$(basename "$fw" .framework)"
[ -f "$bin" ] && ldid -S "$bin"
done
fi
if [ -d "$APP/PlugIns" ]; then
find "$APP/PlugIns" -type d -name "*.appex" | while read -r ext; do
bin="$ext/$(basename "$ext" .appex)"
[ -f "$bin" ] && ldid -S"$ENT" "$bin"
done
fi
ldid -S"$ENT" "$APP/Runner"
- name: Package IPA
run: |
mkdir -p dist build/ios/Payload
cp -R build/ios/iphoneos/Runner.app build/ios/Payload/
(cd build/ios && zip -qr ../../dist/Qlyra-ios-pseudosigned.ipa Payload)
- name: Upload iOS artifact
uses: actions/upload-artifact@v4
with:
name: qlyra-ios-pseudosigned
path: dist/Qlyra-ios-pseudosigned.ipa
if-no-files-found: error
retention-days: 30
+67
View File
@@ -0,0 +1,67 @@
name: Build Linux
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- 'lib/**'
- 'linux/**'
- 'pubspec.yaml'
- '.github/workflows/build-linux.yml'
pull_request:
paths:
- 'lib/**'
- 'linux/**'
- 'pubspec.yaml'
- '.github/workflows/build-linux.yml'
env:
FLUTTER_VERSION: '3.44.3'
jobs:
build-linux:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Install Linux desktop dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev libsecret-1-dev libjsoncpp-dev libmpv-dev mpv libopus-dev libogg-dev
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: x86_64-unknown-linux-gnu
- name: Get dependencies
run: flutter pub get
- name: Build Linux
run: flutter build linux --release
- name: Package Linux
run: |
mkdir -p dist
tar -C build/linux/x64/release/bundle -czf dist/Qlyra-linux-x64.tar.gz .
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: qlyra-linux-x64
path: dist/Qlyra-linux-x64.tar.gz
if-no-files-found: error
retention-days: 30
+59
View File
@@ -0,0 +1,59 @@
name: Build macOS
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- 'lib/**'
- 'macos/**'
- 'pubspec.yaml'
- '.github/workflows/build-macos.yml'
pull_request:
paths:
- 'lib/**'
- 'macos/**'
- 'pubspec.yaml'
- '.github/workflows/build-macos.yml'
env:
FLUTTER_VERSION: '3.44.3'
jobs:
build-macos:
runs-on: macos-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-darwin,x86_64-apple-darwin
- name: Install create-dmg
run: brew install create-dmg
- name: Build signed DMG
run: |
flutter config --no-enable-swift-package-manager
./scripts/build_dmg.sh
- name: Upload macOS DMG artifact
uses: actions/upload-artifact@v4
with:
name: qlyra-macos
path: dist/*.dmg
if-no-files-found: error
retention-days: 30
+63
View File
@@ -0,0 +1,63 @@
name: Build Windows
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- 'lib/**'
- 'windows/**'
- 'pubspec.yaml'
- '.github/workflows/build-windows.yml'
pull_request:
paths:
- 'lib/**'
- 'windows/**'
- 'pubspec.yaml'
- '.github/workflows/build-windows.yml'
env:
FLUTTER_VERSION: '3.44.3'
jobs:
build-windows:
runs-on: windows-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: x86_64-pc-windows-msvc
- name: Get dependencies
run: flutter pub get
- name: Build Windows
run: flutter build windows --release
- name: Package Windows
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path dist | Out-Null
Compress-Archive -Path build/windows/x64/runner/Release/* -DestinationPath dist/Qlyra-windows-x64.zip
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: qlyra-windows-x64
path: dist/Qlyra-windows-x64.zip
if-no-files-found: error
retention-days: 30
+131
View File
@@ -0,0 +1,131 @@
name: Flutter Dev CI
on:
pull_request:
branches:
- 'dev/**'
jobs:
analyze-and-build-android:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
cache: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
- name: Install dependencies
run: flutter pub get
- name: Verify formatting
run: dart format --output=none --set-exit-if-changed lib test
- name: Flutter analyze
run: flutter analyze --no-fatal-infos lib test
- name: Install native plugin build dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev
- name: Prepare native plugin runtime for tests
run: dart run tool/prepare_plugin_runtime.dart
- name: Build E2EE test library
uses: ./.github/actions/setup-e2ee
- name: Build native crypto test library
run: cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
- name: Flutter tests with coverage
run: flutter test --coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: flutter-dev-coverage
path: coverage/lcov.info
if-no-files-found: error
- name: Configure Gradle
run: |
mkdir -p ~/.gradle
echo "org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m" >> ~/.gradle/gradle.properties
echo "kotlin.daemon.jvmargs=-Xmx1536m" >> ~/.gradle/gradle.properties
echo "org.gradle.caching=true" >> ~/.gradle/gradle.properties
echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties
echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties
- name: Build debug APK
run: flutter build apk --debug --flavor qlyra
build-ios:
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
cache: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios
- name: Install dependencies
run: flutter pub get
- name: Verify formatting
run: dart format --output=none --set-exit-if-changed lib test
- name: Install native plugin build dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev
- name: Prepare native plugin runtime for tests
run: dart run tool/prepare_plugin_runtime.dart
- name: Build E2EE test library
uses: ./.github/actions/setup-e2ee
- name: Build native crypto test library
run: cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
- name: Flutter tests
run: flutter test
- name: Build iOS (no codesign)
run: |
flutter config --no-enable-swift-package-manager
flutter build ios --release --no-codesign
+168
View File
@@ -0,0 +1,168 @@
name: Flutter Main CI
on:
pull_request:
branches:
- 'main'
jobs:
android:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
cache: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
- name: Install dependencies
run: flutter pub get
- name: Verify formatting
run: dart format --output=none --set-exit-if-changed lib test
- name: Flutter analyze
run: flutter analyze --no-fatal-infos lib test
- name: Install native plugin build dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libclang-dev
- name: Prepare native plugin runtime for tests
run: dart run tool/prepare_plugin_runtime.dart
- name: Build E2EE test library
uses: ./.github/actions/setup-e2ee
- name: Build native crypto test library
run: cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
- name: Flutter tests with coverage
run: flutter test --coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: flutter-main-coverage
path: coverage/lcov.info
if-no-files-found: error
- name: Configure Gradle
run: |
mkdir -p ~/.gradle
echo "org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m" >> ~/.gradle/gradle.properties
echo "kotlin.daemon.jvmargs=-Xmx1536m" >> ~/.gradle/gradle.properties
echo "org.gradle.caching=true" >> ~/.gradle/gradle.properties
echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties
echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties
- name: Build debug APK
run: flutter build apk --debug --flavor qlyra
linux:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
- name: Install Linux desktop dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev libsecret-1-dev libjsoncpp-dev
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: x86_64-unknown-linux-gnu
- name: Install dependencies
run: flutter pub get
- name: Build Linux
run: flutter build linux --release
windows:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: x86_64-pc-windows-msvc
- name: Install dependencies
run: flutter pub get
- name: Build Windows
run: flutter build windows --release
apple:
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios,aarch64-apple-darwin,x86_64-apple-darwin
- name: Install dependencies
run: flutter pub get
- name: Build iOS
run: |
flutter config --no-enable-swift-package-manager
flutter build ios --release --no-codesign
- name: Fix ogg_opus_player macOS libs
run: ./scripts/fix_opus_macos.sh
- name: Build macOS
run: flutter build macos --release
+22
View File
@@ -0,0 +1,22 @@
name: Native crypto
on:
pull_request:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
jobs:
native-crypto:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
cache: true
- uses: dtolnay/rust-toolchain@stable
- run: flutter pub get
- run: cargo test --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
- run: cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
- run: flutter test --no-pub test/chat_crypto_roundtrip_test.dart test/outbox_queue_test.dart test/message_persistence_test.dart
+25
View File
@@ -0,0 +1,25 @@
name: Dependency Vulnerability Scan
on:
pull_request:
branches:
- main
- 'dev/**'
merge_group:
branches:
- main
schedule:
- cron: '30 2 * * 1'
workflow_dispatch:
permissions:
actions: read
contents: read
security-events: write
jobs:
scan:
uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.5.0
with:
scan-args: |-
--lockfile=./pubspec.lock
+328
View File
@@ -0,0 +1,328 @@
name: Pre-release (dev/0.5.0)
on:
workflow_dispatch:
inputs:
build_number:
description: 'Override build number (e.g. 13). Leave empty to use the auto run number.'
required: false
default: ''
push:
branches:
- 'dev/0.5.0'
permissions:
contents: write
env:
FLUTTER_VERSION: '3.44.3'
jobs:
android:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
flavor: [qlyra, oneme]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
cache: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
- name: Get dependencies
run: flutter pub get
- name: Verify native crypto and message delivery
run: |
cargo test --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
flutter test --no-pub test/chat_crypto_roundtrip_test.dart test/outbox_queue_test.dart test/message_persistence_test.dart test/messages_delivery_test.dart
- name: Configure Gradle
run: |
mkdir -p ~/.gradle
echo "org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m" >> ~/.gradle/gradle.properties
echo "kotlin.daemon.jvmargs=-Xmx1536m" >> ~/.gradle/gradle.properties
echo "org.gradle.caching=true" >> ~/.gradle/gradle.properties
echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties
echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties
- name: Setup release signing
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: |
if [ -z "$KEYSTORE_BASE64" ]; then
echo "Release signing secrets are required."
exit 1
fi
echo "$KEYSTORE_BASE64" | base64 -d > android/app/qlyra-release.jks
{
echo "storePassword=$KEYSTORE_PASSWORD"
echo "keyPassword=$KEY_PASSWORD"
echo "keyAlias=$KEY_ALIAS"
echo "storeFile=qlyra-release.jks"
} > android/key.properties
echo "Release signing configured."
- name: Build APKs and App Bundle (${{ matrix.flavor }})
run: |
flutter build apk --release --flavor ${{ matrix.flavor }} --obfuscate --split-debug-info=build/symbols
flutter build apk --release --split-per-abi --flavor ${{ matrix.flavor }} --obfuscate --split-debug-info=build/symbols
flutter build appbundle --release --flavor ${{ matrix.flavor }} --obfuscate --split-debug-info=build/symbols
- name: Collect Android artifacts (${{ matrix.flavor }})
run: |
F=${{ matrix.flavor }}
mkdir -p dist
cp build/app/outputs/flutter-apk/app-$F-release.apk dist/Qlyra-android-$F-universal.apk
cp build/app/outputs/flutter-apk/app-arm64-v8a-$F-release.apk dist/Qlyra-android-$F-arm64-v8a.apk
cp build/app/outputs/flutter-apk/app-armeabi-v7a-$F-release.apk dist/Qlyra-android-$F-armeabi-v7a.apk
cp build/app/outputs/flutter-apk/app-x86_64-$F-release.apk dist/Qlyra-android-$F-x86_64.apk
cp build/app/outputs/bundle/${F}Release/app-$F-release.aab dist/Qlyra-android-$F.aab
tar -czf dist/Qlyra-android-$F-symbols.tar.gz -C build/symbols .
- name: Verify APK release certificates
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: bash scripts/verify_android_signing.sh
- name: Upload Android artifacts (${{ matrix.flavor }})
uses: actions/upload-artifact@v4
with:
name: android-${{ matrix.flavor }}
path: dist/*
if-no-files-found: error
retention-days: 7
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: x86_64-pc-windows-msvc
- name: Get dependencies
run: flutter pub get
- name: Build Windows
run: flutter build windows --release
- name: Package Windows
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path dist | Out-Null
Compress-Archive -Path build/windows/x64/runner/Release/* -DestinationPath dist/Qlyra-windows-x64.zip
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: windows
path: dist/*
if-no-files-found: error
retention-days: 7
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Install Linux desktop dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev libsecret-1-dev libjsoncpp-dev libmpv-dev mpv libopus-dev libogg-dev
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: x86_64-unknown-linux-gnu
- name: Get dependencies
run: flutter pub get
- name: Build Linux
run: flutter build linux --release
- name: Package Linux
run: |
mkdir -p dist
tar -C build/linux/x64/release/bundle -czf dist/Qlyra-linux-x64.tar.gz .
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: linux
path: dist/*
if-no-files-found: error
retention-days: 7
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-darwin,x86_64-apple-darwin
- name: Get dependencies
run: flutter pub get
- name: Fix opus/ogg libs for macOS
run: ./scripts/fix_opus_macos.sh
- name: Build macOS
run: |
flutter config --no-enable-swift-package-manager
flutter build macos --release
- name: Package macOS
run: |
mkdir -p dist
APP=$(find build/macos/Build/Products/Release -maxdepth 1 -name "*.app" | head -1)
ditto -c -k --keepParent "$APP" dist/Qlyra-macos.zip
- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: macos
path: dist/*
if-no-files-found: error
retention-days: 7
ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios
- name: Get dependencies
run: flutter pub get
- name: Build iOS (no codesign)
run: |
flutter config --no-enable-swift-package-manager
flutter build ios --release --no-codesign
- name: Package unsigned IPA
run: |
mkdir -p dist build/ios/Payload
cp -R build/ios/iphoneos/Runner.app build/ios/Payload/
(cd build/ios && zip -qr ../../dist/Qlyra-ios-unsigned.ipa Payload)
rm -rf build/ios/Payload
- name: Install ldid
run: brew install ldid
- name: Pseudo-sign with ldid
run: |
set -euo pipefail
APP="build/ios/iphoneos/Runner.app"
ENT="$(mktemp -t ent).plist"
cat > "$ENT" <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>platform-application</key><true/>
<key>get-task-allow</key><true/>
<key>application-identifier</key><string>ru.qlyra.app</string>
<key>keychain-access-groups</key>
<array><string>ru.qlyra.app</string></array>
<key>com.apple.private.security.no-container</key><true/>
</dict>
</plist>
PLIST
if [ -d "$APP/Frameworks" ]; then
find "$APP/Frameworks" -type f -name "*.dylib" -print0 | xargs -0 -I{} ldid -S "{}"
find "$APP/Frameworks" -type d -name "*.framework" | while read -r fw; do
bin="$fw/$(basename "$fw" .framework)"
[ -f "$bin" ] && ldid -S "$bin"
done
fi
if [ -d "$APP/PlugIns" ]; then
find "$APP/PlugIns" -type d -name "*.appex" | while read -r ext; do
bin="$ext/$(basename "$ext" .appex)"
[ -f "$bin" ] && ldid -S"$ENT" "$bin"
done
fi
ldid -S"$ENT" "$APP/Runner"
- name: Package IPA
run: |
mkdir -p dist build/ios/Payload
cp -R build/ios/iphoneos/Runner.app build/ios/Payload/
(cd build/ios && zip -qr ../../dist/Qlyra-ios-pseudosigned.ipa Payload)
- name: Upload iOS artifact
uses: actions/upload-artifact@v4
with:
name: ios
path: dist/*
if-no-files-found: error
retention-days: 7
release:
needs: [android, windows, linux, macos, ios]
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Compute version
id: ver
run: |
BRANCH_VERSION="${GITHUB_REF_NAME#dev/}"
BUILD="${{ github.event.inputs.build_number }}"
BUILD="${BUILD:-$GITHUB_RUN_NUMBER}"
echo "tag=v${BRANCH_VERSION}-dev.${BUILD}" >> "$GITHUB_OUTPUT"
echo "name=Qlyra ${BRANCH_VERSION}-dev (build ${BUILD})" >> "$GITHUB_OUTPUT"
- name: List artifacts
run: ls -lhR dist
- name: Create pre-release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.ver.outputs.tag }}
name: ${{ steps.ver.outputs.name }}
target_commitish: ${{ github.sha }}
prerelease: true
generate_release_notes: true
fail_on_unmatched_files: true
files: dist/*
+351
View File
@@ -0,0 +1,351 @@
name: Release (main)
on:
workflow_dispatch:
push:
branches:
- main
permissions:
contents: write
env:
FLUTTER_VERSION: '3.44.3'
jobs:
android:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
flavor: [qlyra, oneme]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
cache: true
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
- name: Get dependencies
run: flutter pub get
- name: Verify native crypto and message delivery
run: |
cargo test --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
flutter test --no-pub test/chat_crypto_roundtrip_test.dart test/outbox_queue_test.dart test/message_persistence_test.dart test/messages_delivery_test.dart
- name: Configure Gradle
run: |
mkdir -p ~/.gradle
echo "org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m" >> ~/.gradle/gradle.properties
echo "kotlin.daemon.jvmargs=-Xmx1536m" >> ~/.gradle/gradle.properties
echo "org.gradle.caching=true" >> ~/.gradle/gradle.properties
echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties
echo "org.gradle.configureondemand=true" >> ~/.gradle/gradle.properties
- name: Setup release signing
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: |
if [ -z "$KEYSTORE_BASE64" ]; then
echo "Release signing secrets are required."
exit 1
fi
echo "$KEYSTORE_BASE64" | base64 -d > android/app/qlyra-release.jks
{
echo "storePassword=$KEYSTORE_PASSWORD"
echo "keyPassword=$KEY_PASSWORD"
echo "keyAlias=$KEY_ALIAS"
echo "storeFile=qlyra-release.jks"
} > android/key.properties
echo "Release signing configured."
- name: Build APKs and App Bundle (${{ matrix.flavor }})
run: |
flutter build apk --release --flavor ${{ matrix.flavor }} --obfuscate --split-debug-info=build/symbols
flutter build apk --release --split-per-abi --flavor ${{ matrix.flavor }} --obfuscate --split-debug-info=build/symbols
flutter build appbundle --release --flavor ${{ matrix.flavor }} --obfuscate --split-debug-info=build/symbols
- name: Collect Android artifacts (${{ matrix.flavor }})
run: |
F=${{ matrix.flavor }}
mkdir -p dist
cp build/app/outputs/flutter-apk/app-$F-release.apk dist/Qlyra-android-$F-universal.apk
cp build/app/outputs/flutter-apk/app-arm64-v8a-$F-release.apk dist/Qlyra-android-$F-arm64-v8a.apk
cp build/app/outputs/flutter-apk/app-armeabi-v7a-$F-release.apk dist/Qlyra-android-$F-armeabi-v7a.apk
cp build/app/outputs/flutter-apk/app-x86_64-$F-release.apk dist/Qlyra-android-$F-x86_64.apk
cp build/app/outputs/bundle/${F}Release/app-$F-release.aab dist/Qlyra-android-$F.aab
tar -czf dist/Qlyra-android-$F-symbols.tar.gz -C build/symbols .
- name: Verify APK release certificates
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: bash scripts/verify_android_signing.sh
- name: Upload Android artifacts (${{ matrix.flavor }})
uses: actions/upload-artifact@v4
with:
name: android-${{ matrix.flavor }}
path: dist/*
if-no-files-found: error
retention-days: 7
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: x86_64-pc-windows-msvc
- name: Get dependencies
run: flutter pub get
- name: Build Windows
run: flutter build windows --release
- name: Package Windows
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path dist | Out-Null
Compress-Archive -Path build/windows/x64/runner/Release/* -DestinationPath dist/Qlyra-windows-x64.zip
- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: windows
path: dist/*
if-no-files-found: error
retention-days: 7
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Install Linux desktop dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev libsecret-1-dev libjsoncpp-dev
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: x86_64-unknown-linux-gnu
- name: Get dependencies
run: flutter pub get
- name: Build Linux
run: flutter build linux --release
- name: Package Linux
run: |
mkdir -p dist
tar -C build/linux/x64/release/bundle -czf dist/Qlyra-linux-x64.tar.gz .
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: linux
path: dist/*
if-no-files-found: error
retention-days: 7
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-darwin,x86_64-apple-darwin
- name: Get dependencies
run: flutter pub get
- name: Build macOS
run: |
flutter config --no-enable-swift-package-manager
flutter build macos --release
- name: Package macOS
run: |
mkdir -p dist
APP=$(find build/macos/Build/Products/Release -maxdepth 1 -name "*.app" | head -1)
ditto -c -k --keepParent "$APP" dist/Qlyra-macos.zip
- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: macos
path: dist/*
if-no-files-found: error
retention-days: 7
ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: 'stable'
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios
- name: Get dependencies
run: flutter pub get
- name: Build iOS (no codesign)
run: |
flutter config --no-enable-swift-package-manager
flutter build ios --release --no-codesign
- name: Install ldid
run: brew install ldid
- name: Pseudo-sign with ldid
run: |
set -euo pipefail
APP="build/ios/iphoneos/Runner.app"
ENT="$(mktemp -t ent).plist"
cat > "$ENT" <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>platform-application</key><true/>
<key>get-task-allow</key><true/>
<key>application-identifier</key><string>ru.qlyra.app</string>
<key>keychain-access-groups</key>
<array><string>ru.qlyra.app</string></array>
<key>com.apple.private.security.no-container</key><true/>
</dict>
</plist>
PLIST
if [ -d "$APP/Frameworks" ]; then
find "$APP/Frameworks" -type f -name "*.dylib" -print0 | xargs -0 -I{} ldid -S "{}"
find "$APP/Frameworks" -type d -name "*.framework" | while read -r fw; do
bin="$fw/$(basename "$fw" .framework)"
[ -f "$bin" ] && ldid -S "$bin"
done
fi
if [ -d "$APP/PlugIns" ]; then
find "$APP/PlugIns" -type d -name "*.appex" | while read -r ext; do
bin="$ext/$(basename "$ext" .appex)"
[ -f "$bin" ] && ldid -S"$ENT" "$bin"
done
fi
ldid -S"$ENT" "$APP/Runner"
- name: Package IPA
run: |
mkdir -p dist build/ios/Payload
cp -R build/ios/iphoneos/Runner.app build/ios/Payload/
(cd build/ios && zip -qr ../../dist/Qlyra-ios-pseudosigned.ipa Payload)
- name: Upload iOS artifact
uses: actions/upload-artifact@v4
with:
name: ios
path: dist/*
if-no-files-found: error
retention-days: 7
release:
needs: [android, windows, linux, macos, ios]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Compute version
id: ver
run: |
PUBSPEC_VERSION=$(grep '^version:' pubspec.yaml | awk '{print $2}' | cut -d'+' -f1)
echo "tag=v${PUBSPEC_VERSION}+${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT"
echo "name=Qlyra ${PUBSPEC_VERSION} (сборка ${GITHUB_RUN_NUMBER})" >> "$GITHUB_OUTPUT"
- name: List artifacts
run: ls -lhR dist
- name: Create release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.ver.outputs.tag }}
name: ${{ steps.ver.outputs.name }}
target_commitish: ${{ github.sha }}
prerelease: false
generate_release_notes: true
fail_on_unmatched_files: true
body: |
Автоматическая сборка из ветки `${{ github.ref_name }}` @ `${{ github.sha }}`.
## Какой файл качать
### Android — версия Qlyra (без push-уведомлений)
- **`Qlyra-android-qlyra-arm64-v8a.apk`** — рекомендуется для большинства современных телефонов (64-битный ARM).
- `Qlyra-android-qlyra-universal.apk` — универсальный APK, работает везде, но весит больше.
- `Qlyra-android-qlyra-armeabi-v7a.apk` — для старых 32-битных устройств.
- `Qlyra-android-qlyra-x86_64.apk` — для эмуляторов и редких x86-устройств.
- `Qlyra-android-qlyra.aab` — App Bundle для публикации в Google Play (не для ручной установки).
### Android — версия OneMe (с push-уведомлениями через Firebase)
- **`Qlyra-android-oneme-arm64-v8a.apk`** — рекомендуется для большинства современных телефонов.
- `Qlyra-android-oneme-universal.apk` — универсальный APK.
- `Qlyra-android-oneme-armeabi-v7a.apk` — для старых 32-битных устройств.
- `Qlyra-android-oneme-x86_64.apk` — для эмуляторов и x86-устройств.
- `Qlyra-android-oneme.aab` — App Bundle для Google Play.
### Windows
- **`Qlyra-windows-x64.zip`** — портативная сборка для Windows 10/11 (x64). Распаковать и запустить `Qlyra.exe`.
### Linux
- **`Qlyra-linux-x64.tar.gz`** — сборка для 64-битного Linux. Требуются `libgtk-3`, `libsecret-1`, `libjsoncpp`. Распаковать и запустить бинарник.
### macOS
- **`Qlyra-macos.zip`** — `.app` для macOS. Распаковать и перетащить в `/Applications`. Приложение не нотаризовано — при первом запуске разрешите через «Системные настройки → Защита и безопасность».
### iOS
- **`Qlyra-ios-pseudosigned.ipa`** — IPA с псевдо-подписью (ldid).
⚠️ Не устанавливается через App Store. Подходит для:
- jailbreak (Filza, rootless),
- TrollStore,
- сайдлоада через AltStore / Sideloadly (потребуется ваш Apple ID для повторной подписи).
files: dist/*
+42
View File
@@ -0,0 +1,42 @@
name: Tests
on:
push:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: tests-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
submodules: recursive
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.44.3'
channel: 'stable'
cache: true
- name: Build crypto core
run: make -C native/crypto-core shared
- name: Build Qlyra crypto
run: cargo build --locked --manifest-path native/qlyra_crypto/rust/Cargo.toml
- name: Install dependencies
run: flutter pub get
- name: Flutter test
run: flutter test