android cicd pipeline setup
| Type | Skill |
| Plugin | awl-android · v0.0.19 |
| Invoke | /awl-android:cicd-pipeline-setup |
| Source | plugins/awl-android/skills/cicd-pipeline-setup/SKILL.md |
When Claude uses it
Abschnitt betitelt „When Claude uses it“Sets up or updates the GitLab CI/CD pipeline of an Android or KMP app on the AWL android component from devops/ci-cd-templates (unit tests with Kover coverage, .aab bundles, CI-side release signing, Updraft, Play Store publish, CycloneDX SBOM with Vulnerability Tracker upload, secret detection). Use when the user says “add .gitlab-ci.yml”, “set up CI for the Android app”, “wire the android component”, “enable coverage in CI”, “sign release builds in CI”, “add SBOM to the pipeline”, or “the pipeline is missing a stage”. NOT for iOS-only pipelines (use the ios-lint and ios-fastlane components), for GitHub Actions, or for Kubernetes deployments.
Trigger phrases: add .gitlab-ci.yml · set up CI for the Android app · wire the android component · enable coverage in CI · sign release builds in CI · add SBOM to the pipeline · the pipeline is missing a stage
Definition
Abschnitt betitelt „Definition“The android component at devops/ci-cd-templates is input-driven: a short include in .gitlab-ci.yml replaces hand-written jobs. The project has to live on gitlab.appswithlove.net, because $CI_SERVER_FQDN/devops/ci-cd-templates/... only resolves there.
Source of truth when anything here looks off: https://gitlab.appswithlove.net/devops/ci-cd-templates/-/blob/main/templates/android/README.md and template.yml next to it.
Workflow
Abschnitt betitelt „Workflow“| Step | Do | Detail |
|---|---|---|
| 0 | Ask which features to enable | this file |
| 1 | Map flavors and build types to variants | this file |
| 2 | Write .gitlab-ci.yml |
this file |
| 3 | Wire CI-side signing and CI variables | references/signing.md |
| 4 | Configure Kover, SBOM, Updraft, Play publish as opted in | references/sbom.md, other skills |
| 5 | Verify tasks and open an MR | this file |
Read references/component.md when you need the full input table, every job with its rules, or what signing_from_ci and enable_coverage do under the hood.
What the component provides
Abschnitt betitelt „What the component provides“Stages test, build, validate, release. Jobs:
testUnitruns on merge requests intodev_branch. Defaults totest<dev_variant>UnitTest; widen withtest_task. Withenable_coverage: trueit appendskoverXmlReportCustom koverPrintCoverageCustomto the same Gradle invocation and reports the line percentage on the MR.bundleDevandbundleProdbuild.aabfiles on push todev_branchandprod_branch.updraftDevandupdraftProdupload on push todev_branch;updraftDevManualandupdraftProdManualare manual buttons onprod_branch.publishStoreis a manualpublish<prod_variant>Bundleonprod_branch.sbomrunscyclonedxBominvalidate, and the includedtrack-vulntracker-uploadcomponent uploads the JSON inrelease.secret_detectionruns GitLab’s built-in template on every pipeline unlessenable_secret_detection: false.
All build, Updraft, and publish jobs decode the release keystore from CI variables when signing_from_ci: true.
0. Ask before writing
Abschnitt betitelt „0. Ask before writing“Use askUserQuestionTool for a multi-select: Kover coverage on MRs, CI-side release signing, Updraft dev, Updraft prod, Play Store publish, SBOM plus Vulnerability Tracker. The AWL KMP template enables coverage, signing, and SBOM by default, so propose those three as preselected.
Only ask about features the project cannot already do. A missing Updraft or CycloneDX plugin turns a flag into setup work, so say so.
Without a production branch the prod jobs simply never fire. No action needed.
1. Map flavors and build types to variants
Abschnitt betitelt „1. Map flavors and build types to variants“The component drives Gradle by variant name (bundle<Variant>, updraftBundle<Variant>, publish<Variant>Bundle). Variant is <Flavor><BuildType> in upper camel case.
Read productFlavors and buildTypes in the app module (androidApp/build.gradle.kts for KMP, app/build.gradle.kts for a plain app):
| Project | module_name |
dev_variant |
prod_variant |
test_task |
|---|---|---|---|---|
KMP template, flavors dev/prod |
androidApp |
DevDebug |
ProdRelease |
"testDevDebugUnitTest :shared:testAndroidHostTest" |
| Plain app, no flavors | app |
Debug |
Release |
leave empty |
Flavors stage/prod |
app |
StageDebug |
ProdRelease |
leave empty |
The KMP shared module has no bare test task under the AGP KMP library plugin, so list :shared:testAndroidHostTest explicitly. Confirm the bundle tasks exist:
./gradlew :<module_name>:tasks --all --no-daemon | grep -iE "^bundle(<DevVariant>|<ProdVariant>) "Also confirm the Gradle wrapper sits at the repo root and a runner tagged android-dev exists.
2. Write .gitlab-ci.yml
Abschnitt betitelt „2. Write .gitlab-ci.yml“This is the KMP template’s file, minus the template-only when: never overrides:
include: - component: $CI_SERVER_FQDN/devops/ci-cd-templates/android@main inputs: module_name: androidApp dev_variant: DevDebug prod_variant: ProdRelease enable_sbom: true enable_coverage: true signing_from_ci: true test_task: "testDevDebugUnitTest :shared:testAndroidHostTest"
# iOS in a KMP repo: lint on MRs, TestFlight on merge to main. - component: $CI_SERVER_FQDN/devops/ci-cd-templates/ios-lint@main - component: $CI_SERVER_FQDN/devops/ci-cd-templates/ios-fastlane@main
# Top-level `stages` does not merge across includes; the last component wins.# Declare the union so Android (build/validate/release) and iOS (deploy/warm) jobs both have stages.stages: - test - build - validate - release - deploy - warmDrop the two iOS includes and the deploy and warm stages for an Android-only app. Keep the explicit stages block whenever more than one component is included. The iOS side (Tuist, fastlane lanes, xcov coverage) belongs to the ios-lint and ios-fastlane components; this skill does not configure it.
Add enable_updraft_dev, enable_updraft_prod, or enable_publish_store only when the user opted in. Pin @main to a tag if the templates repo publishes one; reproducible pipelines beat floating includes.
3. Signing and CI variables
Abschnitt betitelt „3. Signing and CI variables“With signing_from_ci: true the component decodes RELEASE_KEYSTORE to $CI_PROJECT_DIR/release-keystore.jks and exports RELEASE_STORE_FILE before Gradle runs. The Gradle signing config therefore has to read RELEASE_STORE_FILE, RELEASE_KEY_ALIAS, RELEASE_STORE_PASSWORD, and RELEASE_KEY_PASSWORD from the environment, with keystore.properties as the local fallback. Gradle snippet, variable table, 1Password flow, and versionCode handling: references/signing.md.
4. Optional features
Abschnitt betitelt „4. Optional features“- Coverage: needs the Kover
customvariant in every module sokoverPrintCoverageCustomandkoverXmlReportCustomexist. Use thecode-coverageskill. - SBOM: needs the CycloneDX plugin in the root build and a
cyclonedxBomtask. Seereferences/sbom.md. - Updraft: needs the Updraft plugin with per-variant URLs read from
UPDRAFT_URL_DEVandUPDRAFT_URL_PROD. Use theupdraft-android plugin integrationskill. - Play Store: needs a
publish<prod_variant>Bundletask (Gradle Play Publisher) and service-account credentials. Use theplay-console-service-accountskill.
5. Verify
Abschnitt betitelt „5. Verify“bundle<DevVariant>andbundle<ProdVariant>resolve (step 1).- With coverage:
./gradlew koverPrintCoverageCustomprintsapplication line coveragefor each module. - With SBOM:
./gradlew cyclonedxBom --no-daemonwritesbuild/reports/project-sbom.json. - With signing: the four
RELEASE_*variables exist, protected and masked, andRELEASE_KEYSTOREis a file variable. - Open an MR into
dev_branchand confirmtestUnitandsecret_detectionrun. Push todev_branchand confirmbundleDev,bundleProd, and, if enabled,sbomplus the vulntracker upload run.

