From af38def2d21c6727c91ca998b872db43878f8e2b Mon Sep 17 00:00:00 2001 From: James Maes Date: Sun, 8 Nov 2026 07:28:03 -0600 Subject: [PATCH 1/4] ci: build on the shared ci-workflows Android workflow Source checks, JVM unit tests, scanner lint, debug and QA APK assembly and connected emulator tests run on every push via ci-workflows android.yaml. vX.Y.Z tags publish the SDK AARs to the QRun-IO Gitea Maven registry. Removes the retired CircleCI/qqq-orb configuration and the legacy GitHub workflows. Co-Authored-By: Claude Opus 5.5 --- .circleci/config.yml | 14 --- .gitea/workflows/build.yaml | 19 +++ .github/workflows/release-scanner.yml | 138 --------------------- .github/workflows/scanner.yml | 163 ------------------------- .qqq-ci.yml | 3 - AGENTS.md | 2 +- CONTRIBUTING.md | 4 +- README.md | 2 +- docs/RELEASING.md | 4 +- qqq/android/core/build.gradle.kts | 8 +- qqq/android/mobileapp/build.gradle.kts | 8 +- 11 files changed, 33 insertions(+), 332 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .gitea/workflows/build.yaml delete mode 100644 .github/workflows/release-scanner.yml delete mode 100644 .github/workflows/scanner.yml delete mode 100644 .qqq-ci.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 18fd9f1..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,14 +0,0 @@ -version: 2.1 -setup: true -orbs: - qqq-orb: kingsrook/qqq-orb@0.8 -workflows: - setup: - jobs: - - qqq-orb/generate_pipeline: - orb_version: '0.8' - filters: - branches: - ignore: automation/qbit-rc-pending - tags: - only: /.*/ diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..2bdf4ab --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,19 @@ +name: build +on: + push: + branches: ['**'] + tags: ['v*'] +jobs: + android: + uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/android.yaml@native-workflows + secrets: inherit + with: + node-version: '24.11.1' + commands: | + node scripts/ci/check-source-standards.mjs + node --test scripts/ci/scanner-echo-guards.test.mjs + tasks: | + testDebugUnitTest :scanner-host:lintDebug :scanner-host:assembleDebug + -PscannerTestBuildType=qa :scanner-host:assembleQa :scanner-host:assembleQaAndroidTest + emulator-tasks: connectedDebugAndroidTest + publish-tasks: :qqq:android:core:publishAllPublicationsToGiteaRepository :qqq:android:mobileapp:publishAllPublicationsToGiteaRepository diff --git a/.github/workflows/release-scanner.yml b/.github/workflows/release-scanner.yml deleted file mode 100644 index 30d48ad..0000000 --- a/.github/workflows/release-scanner.yml +++ /dev/null @@ -1,138 +0,0 @@ -name: Release scanner APK - -on: - push: - tags: ['scanner-v*'] - -permissions: - contents: read - -concurrency: - group: scanner-release - cancel-in-progress: false - -jobs: - verify: - uses: ./.github/workflows/scanner.yml - - package: - needs: verify - runs-on: ubuntu-24.04 - timeout-minutes: 30 - environment: scanner-release - steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - with: - persist-credentials: false - fetch-depth: 0 - - name: Require main ancestry and a strictly newer Android version - id: release-version - env: - GH_TOKEN: ${{ github.token }} - GH_REPO: ${{ github.repository }} - SCANNER_VERSION_CODE: ${{ vars.SCANNER_VERSION_CODE }} - shell: bash - run: | - set -euo pipefail - git merge-base --is-ancestor "$GITHUB_SHA" refs/remotes/origin/main - [[ "$SCANNER_VERSION_CODE" =~ ^[1-9][0-9]{0,9}$ ]] || exit 1 - (( SCANNER_VERSION_CODE <= 2100000000 )) || exit 1 - gh api --paginate "repos/$GH_REPO/releases?per_page=100" \ - --jq '.[] | select(.draft == false) | select(.tag_name | startswith("scanner-v")) | .tag_name' > "$RUNNER_TEMP/scanner-prior-tags.txt" - while IFS= read -r tag; do - [[ "$tag" =~ ^scanner-v[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.-]+)?$ ]] || exit 1 - prior_directory=$(mktemp -d "$RUNNER_TEMP/scanner-prior.XXXXXX") - gh release download "$tag" --pattern BUILD.txt --dir "$prior_directory" - previous=$(sed -n 's/^Version code: //p' "$prior_directory/BUILD.txt") - [[ "$previous" =~ ^[1-9][0-9]{0,9}$ ]] || exit 1 - (( SCANNER_VERSION_CODE > previous )) || { echo 'Version code must exceed every published scanner release'; exit 1; } - done < "$RUNNER_TEMP/scanner-prior-tags.txt" - printf 'code=%s\n' "$SCANNER_VERSION_CODE" >> "$GITHUB_OUTPUT" - - uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4 - with: - distribution: temurin - java-version: '21' - - uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4 - - name: Install Android SDK packages - run: | - "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" "platforms;android-35" "build-tools;34.0.0" "build-tools;35.0.0" "platform-tools" - - name: Build and verify signed release - env: - SCANNER_KEYSTORE_BASE64: ${{ secrets.SCANNER_KEYSTORE_BASE64 }} - SCANNER_STORE_PASSWORD: ${{ secrets.SCANNER_STORE_PASSWORD }} - SCANNER_KEY_ALIAS: ${{ secrets.SCANNER_KEY_ALIAS }} - SCANNER_KEY_PASSWORD: ${{ secrets.SCANNER_KEY_PASSWORD }} - SCANNER_CERTIFICATE_SHA256: ${{ vars.SCANNER_CERTIFICATE_SHA256 }} - SCANNER_VERSION_CODE: ${{ steps.release-version.outputs.code }} - shell: bash - run: | - set -euo pipefail - umask 077 - version="${GITHUB_REF_NAME#scanner-v}" - [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.-]+)?$ ]] || exit 1 - [[ "$SCANNER_VERSION_CODE" =~ ^[1-9][0-9]{0,9}$ ]] || exit 1 - (( SCANNER_VERSION_CODE <= 2100000000 )) || exit 1 - [[ "$SCANNER_CERTIFICATE_SHA256" =~ ^[A-Fa-f0-9]{64}$ ]] || exit 1 - for name in SCANNER_KEYSTORE_BASE64 SCANNER_STORE_PASSWORD SCANNER_KEY_ALIAS SCANNER_KEY_PASSWORD; do - [[ -n "${!name}" ]] || { echo "Missing release signing configuration: $name"; exit 1; } - done - export SCANNER_KEYSTORE="$RUNNER_TEMP/scanner-release.jks" - trap 'rm -f -- "$SCANNER_KEYSTORE"' EXIT - printf '%s' "$SCANNER_KEYSTORE_BASE64" | base64 --decode > "$SCANNER_KEYSTORE" - unset SCANNER_KEYSTORE_BASE64 - ./gradlew --no-daemon --console=plain :scanner-host:lintRelease :scanner-host:assembleRelease \ - "-PscannerVersionName=$version" "-PscannerVersionCode=$SCANNER_VERSION_CODE" - mkdir -p release - cp scanner-host/build/outputs/apk/release/scanner-host-release.apk "release/qqq-mobile-$version.apk" - cp scanner-host/build/reports/third-party/release/{aboutlibraries.json,LICENSE,NOTICE} release/ - "$ANDROID_HOME/build-tools/35.0.0/apksigner" verify --verbose --print-certs "release/qqq-mobile-$version.apk" > release/signature.txt - certificate=$(sed -n 's/^Signer #1 certificate SHA-256 digest: //p' release/signature.txt) - [[ "${certificate,,}" == "${SCANNER_CERTIFICATE_SHA256,,}" ]] || { echo 'Unexpected signing certificate'; exit 1; } - "$ANDROID_HOME/build-tools/35.0.0/aapt" dump badging "release/qqq-mobile-$version.apk" > "$RUNNER_TEMP/scanner-manifest.txt" - grep -F "package: name='io.qrun.mobile.scanner' versionCode='$SCANNER_VERSION_CODE' versionName='$version'" "$RUNNER_TEMP/scanner-manifest.txt" - if grep -q 'application-debuggable' "$RUNNER_TEMP/scanner-manifest.txt"; then - echo 'Refusing a debuggable release'; exit 1 - fi - printf 'Source: %s\nVersion: %s\nVersion code: %s\n' "$GITHUB_SHA" "$version" "$SCANNER_VERSION_CODE" > release/BUILD.txt - (cd release && sha256sum -- *.apk signature.txt BUILD.txt aboutlibraries.json LICENSE NOTICE > SHA256SUMS) - - name: Retain signed deliverable - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: scanner-release-${{ github.sha }} - path: release/ - if-no-files-found: error - retention-days: 30 - - publish: - needs: package - runs-on: ubuntu-24.04 - permissions: - contents: write - steps: - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - name: scanner-release-${{ github.sha }} - path: release - - name: Publish immutable-tag APK assets - env: - GH_TOKEN: ${{ github.token }} - GH_REPO: ${{ github.repository }} - shell: bash - run: | - set -euo pipefail - (cd release && sha256sum --check SHA256SUMS) - prerelease=() - version="${GITHUB_REF_NAME#scanner-v}" - tag_object=$(gh api "repos/$GH_REPO/git/ref/tags/$GITHUB_REF_NAME" --jq '.object.sha') - tag_type=$(gh api "repos/$GH_REPO/git/ref/tags/$GITHUB_REF_NAME" --jq '.object.type') - for ((attempt=0; attempt<5; attempt++)); do - if [[ "$tag_type" == commit ]]; then break; fi - [[ "$tag_type" == tag ]] || exit 1 - tag_json=$(gh api "repos/$GH_REPO/git/tags/$tag_object") - tag_object=$(jq -r '.object.sha' <<< "$tag_json") - tag_type=$(jq -r '.object.type' <<< "$tag_json") - done - [[ "$tag_type" == commit && "$tag_object" == "$GITHUB_SHA" ]] || { echo 'Release tag no longer identifies the verified source'; exit 1; } - if [[ "$version" == *-* ]]; then prerelease=(--prerelease); fi - gh release create "$GITHUB_REF_NAME" --verify-tag --title "QQQ Mobile $version" \ - --generate-notes "${prerelease[@]}" release/* diff --git a/.github/workflows/scanner.yml b/.github/workflows/scanner.yml deleted file mode 100644 index 8271e91..0000000 --- a/.github/workflows/scanner.yml +++ /dev/null @@ -1,163 +0,0 @@ -name: Scanner Android - -on: - push: - branches: [main, 'feature/**', 'fix/**', 'release/**', 'hotfix/**'] - pull_request: - workflow_call: - workflow_dispatch: - inputs: - emulator: - description: Run native UI and real-server scanner end-to-end tests - type: boolean - default: true - -permissions: - contents: read - -concurrency: - group: scanner-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - verify: - name: Tests, lint, and debug APK - runs-on: ubuntu-24.04 - timeout-minutes: 30 - steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - with: - persist-credentials: false - - uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4 - with: - distribution: temurin - java-version: '21' - - uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4 - with: - cache-read-only: ${{ github.event_name == 'pull_request' }} - - name: Verify source headers, whitespace, imports and JavaScript syntax - run: node scripts/ci/check-source-standards.mjs - - name: Run no-device Echo fixture guard tests - run: node --test scripts/ci/scanner-echo-guards.test.mjs - - name: Install required Android SDK packages - run: | - "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" "platforms;android-35" "build-tools;34.0.0" "build-tools;35.0.0" "platform-tools" - - name: Verify shared libraries and scanner host - run: >- - ./gradlew --no-daemon --console=plain - :qqq:android:core:testDebugUnitTest - :qqq:android:mobileapp:testDebugUnitTest - :scanner-host:testDebugUnitTest - :scanner-host:lintDebug - :scanner-host:assembleDebug - - name: Compile opt-in physical QA acceptance - run: >- - ./gradlew --no-daemon --console=plain -PscannerTestBuildType=qa - :scanner-host:assembleQa :scanner-host:assembleQaAndroidTest - - name: Hash debug APK - run: | - mkdir -p scanner-host/build/outputs/apk/debug/notices - cp scanner-host/build/reports/third-party/debug/{aboutlibraries.json,LICENSE,NOTICE} scanner-host/build/outputs/apk/debug/notices/ - (cd scanner-host/build/outputs/apk/debug && sha256sum scanner-host-debug.apk notices/* > SHA256SUMS) - - name: Upload installable development APK - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: scanner-debug-${{ github.sha }} - path: scanner-host/build/outputs/apk/debug/ - if-no-files-found: error - retention-days: 14 - - name: Upload test and lint evidence - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: scanner-checks-${{ github.sha }} - path: | - qqq/android/core/build/test-results/ - qqq/android/mobileapp/build/test-results/ - scanner-host/build/test-results/ - scanner-host/build/reports/ - retention-days: 14 - - emulator: - name: Native UI and real-server scanner E2E - if: github.event_name != 'workflow_dispatch' || inputs.emulator - needs: verify - runs-on: ubuntu-24.04 - timeout-minutes: 40 - env: - QQQ_CORE_SHA: ecf0f550a0357cec6387c2b54efca128a405a629 - SCANNER_SERVER_SHA: 10416938724c9663d66f7848ff2b1a63fe635794 - MAVEN_OPTS: -XX:ActiveProcessorCount=2 -Xmx768m - steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - with: - persist-credentials: false - - uses: actions/setup-java@cf277c60eb25467037889841efdb72551f06f6c3 # v4 - with: - distribution: temurin - java-version: '21' - - uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4 - - name: Check out the pinned QQQ core source - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - with: - repository: QRun-IO/qqq - ref: ${{ env.QQQ_CORE_SHA }} - path: .e2e-core - persist-credentials: false - - name: Check out the pinned real QQQ demo - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - with: - repository: QRun-IO/qbit-mobile-scanners - ref: ${{ env.SCANNER_SERVER_SHA }} - path: .e2e-server - persist-credentials: false - - name: Verify and build the isolated source-pinned server fixture - env: - JAVA_TOOL_OPTIONS: -XX:ActiveProcessorCount=2 -Xmx768m - run: | - test "$(git -C .e2e-core rev-parse HEAD)" = "$QQQ_CORE_SHA" - test "$(git -C .e2e-server rev-parse HEAD)" = "$SCANNER_SERVER_SHA" - fixture_m2=$(mktemp -d "$RUNNER_TEMP/scanner-fixture-m2.XXXXXX") - core_revision="4.1.0-ci.$QQQ_CORE_SHA" - (cd .e2e-core && ./mvnw --batch-mode --no-transfer-progress \ - -Dmaven.repo.local="$fixture_m2" -Drevision="$core_revision" \ - -pl qqq-bom,qqq-middleware-api -am clean install) - (cd .e2e-server && ./mvnw --batch-mode --no-transfer-progress \ - -Dmaven.repo.local="$fixture_m2" -Dqqq.version="$core_revision" clean install) - (cd .e2e-server && ./mvnw --batch-mode --no-transfer-progress -f demo/pom.xml \ - -Dmaven.repo.local="$fixture_m2" -Dqqq.version="$core_revision" clean verify \ - dependency:build-classpath -DincludeScope=runtime -Dmdep.outputFile=target/e2e-classpath.txt \ - dependency:tree -Dscope=runtime -DoutputFile=target/fixture-dependencies.txt \ - help:effective-pom -Doutput=target/fixture-effective-pom.xml) - node scripts/ci/verify-fixture-provenance.mjs .e2e-server "$fixture_m2" "$QQQ_CORE_SHA" "$SCANNER_SERVER_SHA" - - name: Enable hosted-runner hardware acceleration - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - - name: Run emulator checks - uses: ReactiveCircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2 - with: - api-level: 35 - target: google_apis - arch: x86_64 - disable-animations: true - script: | - ./gradlew --no-daemon --console=plain :qqqsampleandroidmobileapp:connectedDebugAndroidTest :scanner-host:assembleE2e :scanner-host:assembleE2eAndroidTest - node scripts/ci/scanner-e2e.mjs .e2e-server - - name: Upload emulator evidence - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: scanner-emulator-${{ github.sha }} - include-hidden-files: true - path: | - qqqsampleandroidmobileapp/build/reports/androidTests/ - qqqsampleandroidmobileapp/build/outputs/androidTest-results/ - scanner-host/build/native-e2e/ - .e2e-server/demo/target/fixture-provenance.json - .e2e-server/demo/target/fixture-dependencies.txt - .e2e-server/demo/target/fixture-effective-pom.xml - .e2e-core/**/target/surefire-reports/TEST-*.xml - .e2e-server/**/target/surefire-reports/TEST-*.xml - retention-days: 14 diff --git a/.qqq-ci.yml b/.qqq-ci.yml deleted file mode 100644 index 41d03cd..0000000 --- a/.qqq-ci.yml +++ /dev/null @@ -1,3 +0,0 @@ -profile: android -orb_version: '0.8' -default_branch: main diff --git a/AGENTS.md b/AGENTS.md index 19a22a8..5d6c01c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,4 +24,4 @@ Never log or place passwords, session cookies, or device tokens in process value ## Repository automation -The primary forge is Gitea (`git.kof22.com/QRun-IO`); GitHub is a shadow/archive, so make no ordinary GitHub writes. Protected `main` on Gitea has `.gitea/workflows/qrun-android-release.yaml` calling the shared `KofTwentyTwo/platform-ci` `android.yaml@qrun-release-v1`; pull-request checks come from central Gitea policy, not from that release workflow. It is not a scanner-APK signing pipeline, and no automatic signing CI is configured. CircleCI is managed by `kingsrook/qqq-orb` using `.qqq-ci.yml`; preserve that lane and its main-only library publication. The legacy `.github` `Scanner Android` workflow builds a debug APK artifact and never publishes libraries or production releases; it and the scanner release workflow are not a selected release path. The owner has authorized production signing with the QRun-owned key `qqq-mobile-release` (details and fingerprint in `docs/RELEASING.md`) for signed-APK/MDM distribution. Do not create replacement or additional signing keys, put key material, passwords or vault item details in the repository, or use the legacy GitHub release workflows; create no tags or releases without current authorization. Do not replace managed CI with a stale local template. +The primary forge is Gitea (`git.kof22.com/QRun-IO`); GitHub is a shadow/archive, so make no ordinary GitHub writes. `.gitea/workflows/build.yaml` calls the shared `KofTwentyTwo/ci-workflows` Android workflow (`android.yaml@v1`) on every push: source checks, JVM unit tests, scanner lint, debug and QA APK assembly, and connected tests on an emulator (android-build runners). A `vX.Y.Z` tag also publishes the SDK AARs (`qqq-android-core`, `qqq-android-mobileapp`) to the QRun-IO Gitea Maven registry. It is not a scanner-APK signing pipeline, and no automatic signing CI is configured. The owner has authorized production signing with the QRun-owned key `qqq-mobile-release` (details and fingerprint in `docs/RELEASING.md`) for signed-APK/MDM distribution. Do not create replacement or additional signing keys, or put key material, passwords or vault item details in the repository; create no tags or releases without current authorization. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 214d903..6b4ab06 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,9 +23,9 @@ The smoke script clears application data only after verifying the target is an e ## CI and delivery -Managed CircleCI remains the shared-library test and Maven publication lane. Its Android profile publishes libraries only from `main`, after its checks. Feature branches do not publish. +`.gitea/workflows/build.yaml` calls the shared `KofTwentyTwo/ci-workflows` Android workflow (`android.yaml@v1`) on every push: source checks, JVM unit tests, scanner lint, debug and QA APK assembly, and connected tests on an emulator (android-build runners). A `vX.Y.Z` tag also publishes the SDK AARs (`qqq-android-core`, `qqq-android-mobileapp`) to the QRun-IO Gitea Maven registry. Feature branches never publish. -The GitHub **Scanner Android** workflow runs shared-library/scanner JVM tests, scanner lint, and debug APK assembly for pull requests and supported branches. It uploads the development APK with SHA256 and verification reports. **Run workflow** optionally adds native emulator tests and a scanner launch screenshot. This workflow never publishes Maven artifacts, signs a production APK, or deploys to a device/store. +The emulator scanner end-to-end suite against a real QQQ server (`scripts/ci/scanner-e2e.mjs`) is not part of CI; run it locally as described in [emulator testing](docs/EMULATOR.md). Production signing and distribution require a separately configured release procedure. Do not treat the development APK as a production release. diff --git a/README.md b/README.md index d9bf3dd..4018ede 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Limits: JVM, emulator and synthetic-scan tests do not prove physical trigger, li ## CI and automation -The primary forge is Gitea (`git.kof22.com/QRun-IO`); GitHub is a shadow/archive and receives no ordinary writes. Protected `main` runs [`.gitea/workflows/qrun-android-release.yaml`](.gitea/workflows/qrun-android-release.yaml) on push and manual dispatch; it calls the shared `KofTwentyTwo/platform-ci` Android workflow (`android.yaml@qrun-release-v1`). That workflow builds, verifies and publishes the SDK libraries; it is not a scanner-APK signing or release pipeline. Pull-request checks come from central Gitea policy. The managed CircleCI profile (`.qqq-ci.yml`) and its main-only library publication are retained. The `.github` workflows are legacy and not a release path. Shared CI design is out of scope for this repository's local use: the demo above needs none of it. +The primary forge is Gitea (`git.kof22.com/QRun-IO`); GitHub is a shadow/archive and receives no ordinary writes. `.gitea/workflows/build.yaml` calls the shared `KofTwentyTwo/ci-workflows` Android workflow (`android.yaml@v1`) on every push: source checks, JVM unit tests, scanner lint, debug and QA APK assembly, and connected tests on an emulator (android-build runners). A `vX.Y.Z` tag also publishes the SDK AARs (`qqq-android-core`, `qqq-android-mobileapp`) to the QRun-IO Gitea Maven registry. It is not a scanner-APK signing or release pipeline. Shared CI design is out of scope for this repository's local use: the demo above needs none of it. ## Extension points diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 7a9a5ca..9c64374 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -41,7 +41,7 @@ Dated evidence below is preserved. As of November 4: a QRun-owned production sig ## What exists today -- **Gitea CI (actual).** `.gitea/workflows/qrun-android-release.yaml` is in the repository, runs on push to `main` (and manual dispatch) and calls the central `KofTwentyTwo/platform-ci` Android workflow at `qrun-release-v1`. A central main run (3634) passed and published the SDK AARs. It is **not** a scanner-APK signing or release pipeline: it uses no scanner signing secrets and produces no production-signed scanner APK. +- **Gitea CI (actual).** `.gitea/workflows/build.yaml` calls the shared `KofTwentyTwo/ci-workflows` Android workflow (`android.yaml@v1`) on every push: source checks, JVM unit tests, scanner lint, debug and QA APK assembly, and connected tests on an emulator (android-build runners). A `vX.Y.Z` tag also publishes the SDK AARs (`qqq-android-core`, `qqq-android-mobileapp`) to the QRun-IO Gitea Maven registry. It is **not** a scanner-APK signing or release pipeline: it uses no scanner signing secrets and produces no production-signed scanner APK. - **Gradle signing (supported, not selected).** `scanner-host/build.gradle.kts` reads four environment variables: `SCANNER_KEYSTORE`, `SCANNER_STORE_PASSWORD`, `SCANNER_KEY_ALIAS`, `SCANNER_KEY_PASSWORD`. - All four set: the `release` build type is signed with them. - None set: `assembleRelease` produces an **unsigned** APK, which is what the isolated signing step consumes. @@ -49,7 +49,7 @@ Dated evidence below is preserved. As of November 4: a QRun-owned production sig - Debug, `e2e` and `qa` builds keep the development debug signing. - Version comes from `-PscannerVersionName` (semantic version, default `0.1.0`) and `-PscannerVersionCode` (1 to 2100000000, default `1`). - **Identity.** One APK serves all customers; branding is applied at runtime from the selected server's metadata, so no per-customer key or build is needed. -- **Legacy GitHub workflows (historical only).** `.github/workflows/release-scanner.yml` and `scanner.yml` predate Gitea as primary forge. They are not a release path; do not run or follow them. The [November 1 review](RELEASE-AUTOMATION-REVIEW.md) covered those files only. +- **Legacy GitHub workflows (removed).** `.github/workflows/release-scanner.yml` and `scanner.yml` predated Gitea as primary forge and were removed with the CircleCI configuration. The [November 1 review](RELEASE-AUTOMATION-REVIEW.md) covered those files only. ## First release: 1.0.0 (versionCode 2) diff --git a/qqq/android/core/build.gradle.kts b/qqq/android/core/build.gradle.kts index 39264ea..a855937 100644 --- a/qqq/android/core/build.gradle.kts +++ b/qqq/android/core/build.gradle.kts @@ -30,10 +30,10 @@ plugins { publishing { repositories { maven { - name = "githubQqqMavenRegistry" - url = uri("https://maven.pkg.github.com/QRun-IO/qqq-maven-registry") - // username and password (a personal Github access token) should be specified as - // `githubPackagesUsername` and `githubPackagesPassword` Gradle properties + // Gitea Maven registry of the repository owner. CI sets giteaMavenUrl, giteaUsername + // and giteaPassword (ci-workflows android.yaml, publish-tasks on vX.Y.Z tags). + name = "gitea" + url = uri(providers.gradleProperty("giteaMavenUrl").getOrElse("https://git.kof22.com/api/packages/QRun-IO/maven")) credentials(PasswordCredentials::class) } } diff --git a/qqq/android/mobileapp/build.gradle.kts b/qqq/android/mobileapp/build.gradle.kts index 2e1548e..e3110e0 100644 --- a/qqq/android/mobileapp/build.gradle.kts +++ b/qqq/android/mobileapp/build.gradle.kts @@ -30,10 +30,10 @@ plugins { publishing { repositories { maven { - name = "githubQqqMavenRegistry" - url = uri("https://maven.pkg.github.com/QRun-IO/qqq-maven-registry") - // username and password (a personal Github access token) should be specified as - // `githubPackagesUsername` and `githubPackagesPassword` Gradle properties + // Gitea Maven registry of the repository owner. CI sets giteaMavenUrl, giteaUsername + // and giteaPassword (ci-workflows android.yaml, publish-tasks on vX.Y.Z tags). + name = "gitea" + url = uri(providers.gradleProperty("giteaMavenUrl").getOrElse("https://git.kof22.com/api/packages/QRun-IO/maven")) credentials(PasswordCredentials::class) } } From 8e64d9a83c3752e7c990b72af884f4dec2d40b10 Mon Sep 17 00:00:00 2001 From: James Maes Date: Sun, 8 Nov 2026 07:28:48 -0600 Subject: [PATCH 2/4] style: add the missing final newline (K22-CODE-07) The source-standards check flags scanner-device-record-photo-e2e.mjs. Co-Authored-By: Claude Opus 5.5 --- scripts/ci/scanner-device-record-photo-e2e.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/scanner-device-record-photo-e2e.mjs b/scripts/ci/scanner-device-record-photo-e2e.mjs index 416f286..bcf77dd 100644 --- a/scripts/ci/scanner-device-record-photo-e2e.mjs +++ b/scripts/ci/scanner-device-record-photo-e2e.mjs @@ -5,4 +5,4 @@ const args = process.argv.slice(2); if (args.length !== 4 || args[0] !== '--serial' || args[2] !== '--fixture' || !args[1] || !args[3]) { throw new Error('Usage: node scripts/ci/scanner-device-record-photo-e2e.mjs --serial DEVICE_SERIAL --fixture PRIVATE_RECORD_FILES_FIXTURE.json'); } -await runScannerE2e({ physical: true, serial: args[1], scenario: 'record-photos', fixture: args[3] }); \ No newline at end of file +await runScannerE2e({ physical: true, serial: args[1], scenario: 'record-photos', fixture: args[3] }); From 3bfafa5bac8be08853e3ea0c989bde7ae2207ef3 Mon Sep 17 00:00:00 2001 From: James Maes Date: Sun, 8 Nov 2026 07:45:02 -0600 Subject: [PATCH 3/4] ci: call the shared workflow from ci-workflows main Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 2bdf4ab..e7f3142 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -5,7 +5,7 @@ on: tags: ['v*'] jobs: android: - uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/android.yaml@native-workflows + uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/android.yaml@main secrets: inherit with: node-version: '24.11.1' From 21c952ea815a359dc866b48006070b762da68fdb Mon Sep 17 00:00:00 2001 From: James Maes Date: Sun, 8 Nov 2026 07:52:19 -0600 Subject: [PATCH 4/4] ci: pin the shared workflow to v1 Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index e7f3142..a103517 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -5,7 +5,7 @@ on: tags: ['v*'] jobs: android: - uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/android.yaml@main + uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/android.yaml@v1 secrets: inherit with: node-version: '24.11.1'