Skip to content
Merged
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
120 changes: 120 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: CI

# Until this existed, nothing ran on a pull request except CodeQL's default
# scan: unit tests and the submodule pointer were only exercised by the tag
# build, so a regression was reviewable but not detectable. Runs on pushes to
# main too, so a direct commit is covered by the same gate as a PR.
on:
pull_request:
push:
branches: [main]

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

jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

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

- uses: android-actions/setup-android@v3

- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: gradle-${{ runner.os }}-

# setup.sh fetches the ONNX Runtime and LiteRT AARs the native build
# links against. It is idempotent — it compares the installed version.txt
# against the versions pinned in the script — so a cache hit makes it a
# no-op rather than something to skip.
- name: Cache native dependencies
uses: actions/cache@v4
with:
path: |
ort
litert
key: native-deps-${{ runner.os }}-${{ hashFiles('setup.sh') }}

- name: Run setup
run: ./setup.sh

# The SDK suite covers the download manifest, the app suite covers the
# demo's own logic including text insertion, and control-demo matches
# what the release build runs. None of them ran on a PR before.
- name: Unit tests
run: ./gradlew :sdk:test :app:testDebugUnitTest :control-demo:testDebugUnitTest

# Compiles the JNI bridge against the pinned speech-core, so a submodule
# that does not build with the current bridge fails here rather than at
# release time.
- name: Assemble
run: ./gradlew :sdk:assembleRelease :app:assembleDebug

- name: Upload test reports
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports
path: |
sdk/build/reports/tests/
app/build/reports/tests/
retention-days: 7

submodule-ancestry:
# A pull request that carries an older speech-core pointer than main
# regresses the engine silently: it is one line in the diff and reads as
# noise. This makes it a failing check instead of something a reviewer has
# to notice.
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: speech-core must not move backwards
run: |
set -euo pipefail
git fetch --quiet origin "${{ github.base_ref }}"
base=$(git rev-parse "origin/${{ github.base_ref }}:speech-core")
head=$(git rev-parse "HEAD:speech-core")
echo "base ${{ github.base_ref }}: $base"
echo "head: $head"

if [ "$base" = "$head" ]; then
echo "unchanged"
exit 0
fi

# The pointer may legitimately advance. It may not retreat, and the
# commit has to exist upstream — a pointer only reachable from a fork
# or a local branch breaks every later checkout of this repository.
git clone --quiet --filter=blob:none --no-checkout \
https://github.com/soniqo/speech-core.git /tmp/speech-core
cd /tmp/speech-core

if ! git cat-file -e "$head^{commit}" 2>/dev/null; then
echo "::error::speech-core $head does not exist upstream"
exit 1
fi

if git merge-base --is-ancestor "$head" "$base"; then
echo "::error::speech-core moves backwards: $head is an ancestor of $base on ${{ github.base_ref }}"
exit 1
fi

echo "speech-core advances or diverges forward"
Comment on lines +82 to +120
Loading