Skip to content

CI Overhaul: Separate test, build, and release workflows with least-privilege permissions - #2

Merged
MinecraftFuns merged 3 commits into
masterfrom
copilot/overhaul-ci-pipeline
Dec 16, 2025
Merged

MinecraftFuns merged 3 commits into
masterfrom
copilot/overhaul-ci-pipeline

Conversation

Copilot AI commented Dec 16, 2025 •

Copy link
Copy Markdown

Current CI builds and releases on every master push without running tests, uses overbroad permissions, and exposes signing secrets during build. This PR separates concerns into distinct workflows with minimal privileges.

New Workflows

  • ci-tests.yml — Unit tests + lint on PRs/pushes to master, parallel jobs, contents: read only
  • ci-build.yml — Unsigned debug APK artifact on PRs/pushes, contents: read only
  • release.yml — Tag-triggered (v*.*.*) signed releases, contents: write scoped to signing job only

Build Configuration

Made signing config conditional to allow builds without keystore:

signingConfigs {
    if (System.getenv("ANDROID_KEYSTORE") != null) {
        create("multiset") {
            storeFile file(System.getenv("ANDROID_KEYSTORE"))
            // ...
        }
    }
}

Local CI Parity

  • Added scripts/ci_local.sh — runs full test suite locally with --skip-docker option
  • Handles both old (1.8.x) and new (17.x) Java version formats

Legacy Workflow

  • build-release.yaml marked deprecated with migration docs
  • Added test gate before release for backward compatibility
  • Workflow-level permissions reduced to contents: read

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • jitpack.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.9-all/6m0mbzute7p0zdleavqlib88a/gradle-8.9/lib/gradle-daemon-main-8.9.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.9-all/6m0mbzute7p0zdleavqlib88a/gradle-8.9/lib/agents/gradle-instrumentation-agent-8.9.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.9 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Your task is to overhaul the current CI pipeline to ensure a modern CI experience so developers can write code with confidence. You should follow best practices & write idiomatic code as you make edits to the codebase following the attached design docs.

Title

CI Overhaul: Comprehensive Test Coverage + Secure, Efficient Android Build/Release Pipelines

Author / Reviewers

  • Author: <name>
  • Reviewers: <tech lead>, <mobile lead>, <security/infra>

Background

Our current GitHub Actions workflow (build-release.yml) builds and publishes an Android APK but does not run tests. The repository contains unit, integration, and end-to-end (E2E) tests, but some may be outdated or inconsistent with current code.

This project will (1) bring tests up to date, (2) ensure they run locally, and (3) integrate them into CI using separate workflows that enforce separation of concerns and minimal privilege.

Goals

  • Make unit, integration, and E2E tests reliable and runnable on a clean machine.
  • Integrate tests into CI so merges and releases are gated by test success.
  • Split CI into distinct workflows (test vs build vs signing/release) with least-privilege permissions.
  • Improve efficiency by avoiding repeated work (compilation, dependency resolution) across jobs.

Non-goals

  • Major refactors of app architecture unrelated to test correctness.
  • Migrating CI providers or rewriting the build system (Gradle remains).
  • Perfecting performance micro-optimizations beyond “obvious” CI wins (caching, artifacts, job parallelism).

Current state (as-is)

build-release.yml:

  • Triggers on pushes to master.
  • Checks out code, sets up JDK 17, decodes keystore, runs ./gradlew app:assembleRelease, creates a prerelease GitHub Release, uploads app-release.apk.
  • Uses contents: write permissions for the whole job, and accesses signing secrets during build.

Key issues:

  • No tests executed (risk of releasing broken builds).
  • No PR gating.
  • Overbroad permissions and mixed responsibilities (build + signing + releasing in one job).
  • No explicit strategy for integration/E2E tests that may require Docker/services.

Proposed solution overview

Introduce three workflows with clear responsibility boundaries:

  1. CI: Tests (ci-tests.yml)
    Runs unit + integration + E2E tests on PRs and pushes. Minimal permissions (contents: read). Uses service containers / Docker Compose where needed.

  2. CI: Build (Unsigned) (ci-build.yml)
    Builds unsigned artifacts (debug/release) and uploads them as artifacts for downstream jobs. Minimal permissions (contents: read). No signing secrets.

  3. Release: Sign + Publish (release.yml)
    Runs only on tagged commits (or manual dispatch) and depends on tests (and optionally build artifacts). Has the narrowest elevated permissions required (e.g., contents: write only in the release job). Signing secrets are only exposed in the signing step/job, preferably behind protected environments.

Detailed plan

1) Test inventory and remediation

Identify and standardize how tests are invoked:

  • Unit tests: ./gradlew test (or module-specific tasks like :app:testDebugUnitTest)
  • Integration tests: define canonical Gradle tasks (examples: integrationTest, testIntegration, etc.). If these exist but are inconsistent, consolidate on a single naming scheme.
  • Android instrumentation tests (if applicable): connectedAndroidTest (requires emulator) or firebaseTestLab (out of scope unless already used).
  • E2E tests: may require Docker containers; standardize on one command (e.g., ./gradlew e2eTest or ./scripts/e2e.sh).

Remediation expectations:

  • Fix broken tests to match current APIs and runtime behavior.
  • Remove or quarantine truly obsolete tests (explicitly document why).
  • Enforce idiomatic style (Kotlin/Java test conventions, stable assertions, avoid sleeps; prefer await/poll patterns).
  • Reduce flakes: deterministic data, fixed clocks, retry only where justified, stable ports, proper teardown.

2) Local validation requirements

Engineers must be able to run the full suite locally (including Docker-required tests) from a clean checkout.

Define a single “local CI parity” entrypoint, for example:

  • ./scripts/ci_local.sh running:
    • ./gradlew --no-daemon clean test
    • docker compose up -d (only if E2E/integration requires it)
    • ./gradlew --no-daemon integrationTest (or equivalent)
    • ./gradlew --no-daemon e2eTest (or equivalent)
    • docker compose down -v

Document prerequisites:

  • JDK (17)
  • Android SDK (if needed for instrumentation)
  • Docker Desktop / Docker Engine (>=) required version
  • Any environment variables required by tests (and safe defaults for local)

3) CI workflow design (separation + minimal privilege)

A) ci-tests.yml (PR gating)

Triggers:

  • pull_request to master
  • push to master (optional, but recommended to keep master honest)

Permissions:

  • contents: read only

...


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits December 16, 2025 05:15
- Add ci-tests.yml for unit tests and lint on PRs/push
- Add ci-build.yml for unsigned APK artifacts on PRs/push
- Add release.yml for tag-triggered signed releases
- Add scripts/ci_local.sh for local CI parity
- Update build.gradle to make signing config optional
- Mark legacy build-release.yaml as deprecated and add tests

Co-authored-by: MinecraftFuns <25814618+MinecraftFuns@users.noreply.github.com>
…secret

- Improve Java version detection to handle both old (1.8) and new (17.0) formats
- Add validation for numeric version before comparison
- Add comment explaining shared password secret pattern

Co-authored-by: MinecraftFuns <25814618+MinecraftFuns@users.noreply.github.com>
Copilot AI changed the title [WIP] Overhaul CI pipeline for comprehensive test coverage CI Overhaul: Separate test, build, and release workflows with least-privilege permissions Dec 16, 2025
Copilot AI requested a review from MinecraftFuns December 16, 2025 05:21
@MinecraftFuns

Copy link
Copy Markdown

@copilot can you resolve the following warnings in the codebase by updating them to the latest non-deprecated syntax/library function (following best practices; ensure the new code is functional and don't break things), and another CI failure regarding dependency verification

Task :app:compileDebugKotlin
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AboutFragment.kt:40:9 'fun setHasOptionsMenu(p0: Boolean): Unit' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AboutFragment.kt:101:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AccountFragment.kt:186:9 'fun setHasOptionsMenu(p0: Boolean): Unit' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AccountFragment.kt:189:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AccountFragment.kt:198:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/CustomHeadersFragment.kt:64:9 'fun setHasOptionsMenu(p0: Boolean): Unit' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/CustomHeadersFragment.kt:67:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/CustomHeadersFragment.kt:74:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/LicensesFragment.kt:12:9 'fun setHasOptionsMenu(p0: Boolean): Unit' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/LicensesFragment.kt:15:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/provider/WebDavClient.kt:29:8 'class SimpleXmlConverterFactory : Converter.Factory' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/provider/WebDavClient.kt:259:25 'class SimpleXmlConverterFactory : Converter.Factory' is deprecated. Deprecated in Java.

Task :app:compileDebugJavaWithJavac
Task :app:hiltAggregateDepsDebug
Task :app:hiltJavaCompileDebug
Task :app:bundleDebugClassesToCompileJar
Task :app:generateDebugLintReportModel
Task :app:generateDebugAndroidTestLintModel
Task :app:generateDebugUnitTestLintModel FAILED

FAILURE: Build failed with an exception.
31 actionable tasks: 31 executed

  • What went wrong:
    Execution failed for task ':app:generateDebugUnitTestLintModel'.

Dependency verification failed for configuration ':app:debugUnitTestCompileClasspath'
2 artifacts failed verification:
- mockwebserver-4.12.0.jar (com.squareup.okhttp3:mockwebserver:4.12.0) from repository MavenRepo
- mockwebserver-4.12.0.module (com.squareup.okhttp3:mockwebserver:4.12.0) from repository MavenRepo
If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. For more on how to do this, please refer to https://docs.gradle.org/8.9/userguide/dependency_verification.html#sec:troubleshooting-verification in the Gradle documentation.

Copilot stopped work on behalf of MinecraftFuns due to an error December 16, 2025 23:36
@MinecraftFuns

Copy link
Copy Markdown

@copilot can you resolve the following warnings in the codebase by updating them to the latest non-deprecated syntax/library function (following best practices; ensure the new code is functional and don't break things), and another CI failure regarding dependency verification

Task :app:compileDebugKotlin
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AboutFragment.kt:40:9 'fun setHasOptionsMenu(p0: Boolean): Unit' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AboutFragment.kt:101:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AccountFragment.kt:186:9 'fun setHasOptionsMenu(p0: Boolean): Unit' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AccountFragment.kt:189:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/AccountFragment.kt:198:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/CustomHeadersFragment.kt:64:9 'fun setHasOptionsMenu(p0: Boolean): Unit' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/CustomHeadersFragment.kt:67:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/CustomHeadersFragment.kt:74:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/LicensesFragment.kt:12:9 'fun setHasOptionsMenu(p0: Boolean): Unit' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/fragments/LicensesFragment.kt:15:18 This declaration overrides a deprecated member but is not marked as deprecated itself. Please add the '@deprecated' annotation or suppress the diagnostic.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/provider/WebDavClient.kt:29:8 'class SimpleXmlConverterFactory : Converter.Factory' is deprecated. Deprecated in Java.
w: file:///home/runner/work/webdav-provider/webdav-provider/app/src/main/java/dev/rocli/android/webdav/provider/WebDavClient.kt:259:25 'class SimpleXmlConverterFactory : Converter.Factory' is deprecated. Deprecated in Java.

Task :app:compileDebugJavaWithJavac
Task :app:hiltAggregateDepsDebug
Task :app:hiltJavaCompileDebug
Task :app:bundleDebugClassesToCompileJar
Task :app:generateDebugLintReportModel
Task :app:generateDebugAndroidTestLintModel
Task :app:generateDebugUnitTestLintModel FAILED

FAILURE: Build failed with an exception.
31 actionable tasks: 31 executed

  • What went wrong:
    Execution failed for task ':app:generateDebugUnitTestLintModel'.

Dependency verification failed for configuration ':app:debugUnitTestCompileClasspath'
2 artifacts failed verification:
- mockwebserver-4.12.0.jar (com.squareup.okhttp3:mockwebserver:4.12.0) from repository MavenRepo
- mockwebserver-4.12.0.module (com.squareup.okhttp3:mockwebserver:4.12.0) from repository MavenRepo
If the artifacts are trustworthy, you will need to update the gradle/verification-metadata.xml file. For more on how to do this, please refer to https://docs.gradle.org/8.9/userguide/dependency_verification.html#sec:troubleshooting-verification in the Gradle documentation.

Copilot stopped work on behalf of MinecraftFuns due to an error December 16, 2025 23:38
@MinecraftFuns
MinecraftFuns marked this pull request as ready for review December 16, 2025 23:41
@MinecraftFuns
MinecraftFuns merged commit e3a3867 into master Dec 16, 2025
4 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants