| name | Release Engineering |
| description | Expert capabilities for managing Upnext application releases, CI/CD pipelines, and versioning. |
Release Engineering Skill
This skill provides a comprehensive guide for managing the release lifecycle of the Upnext Android application. It encapsulates logic for GitHub Actions, Fastlane, Semantic Versioning, and troubleshooting.
🚀 Triggering a Release
The release pipeline is fully automated via GitHub Actions, prioritizing stability and reducing noise.
1. Nightly Scheduled Builds (CD)
Automated deployments run daily at 2:00 AM UTC via .github/workflows/deploy.yml. This batches all dependency updates and merges from the day into a single release.
Triggers:
- Schedule:
cron: '0 2 * * *'
- Manual:
workflow_dispatch (Run via GitHub Actions UI)
Process:
- Versioning: Bumps
VERSION_CODE and VERSION_NAME in version.properties.
- Build: Generates signed Release Bundle (
.aab).
- Distribution: Fastlane uploads to Google Play Internal Track.
- Tagging: Pushes a git tag (e.g.,
v2025.1.4-215).
Critical Implementation Detail (GitHub Releases):
For scheduled builds, the workflow runs on the main branch ref (refs/heads/main), not the tag ref.
To ensure a GitHub Release is attached to the newly created tag:
- Fastlane updates
version.properties.
- The workflow explicitly reads
VERSION_NAME and VERSION_CODE from the updated file.
- It constructs the tag name (
v{NAME}-{CODE}) and passes it to the softprops/action-gh-release action.
Do not rely on github.ref for scheduled release tagging.
2. Pull Request Verification (CI)
Every Pull Request to main undergoes strict quality checks via .github/workflows/pull_request.yml.
Unified verify Job:
To radically optimize GitHub Actions minutes consumption, all verification tasks are executed within a single runner matrix step using Gradle's internal dependency graph to share Daemon state and avoid redundant :app:compileDebugKotlin invocations:
ktlintCheck (Code Style)
detekt (Static Analysis)
lintDebug (Android best practices, permissions, security)
testDebugUnitTest (Unit Tests)
assembleDebug (Compilation validation)
assembleRelease (Verifies R8/ProGuard shrinking without crashing)
Note on Signing in Pull Requests:
PR builds do not have access to production signing keys. app/build.gradle is configured to fallback to debug signing automatically when the release keystore is missing. This allows assembleRelease to verify compilation and shrinking logic in CI without needing secrets.
⏱️ CI/CD Minute Optimization
To prevent excessive consumption of GitHub Actions minutes, especially from automated dependency bots like Renovate, the following strategies are implemented:
1. Conditional Deployment (deploy.yml)
The nightly scheduled build incorporates a check_changes pre-job.
- It evaluates the commit history over the last 24 hours.
- If no new commits were merged into
main, it skips the expensive deploy job entirely.
- Manual triggers (
workflow_dispatch) bypass this check and force a deployment.
2. Renovate & PR Auto-Cancellation (renovate.json & pull_request.yml)
Dependency updates can cause massive CI fan-out. To mitigate this:
rebaseWhen: conflicted: Renovate is configured to only rebase PRs if there is a git conflict, preventing it from auto-rebasing 10+ PRs whenever a single PR is merged into main.
- Update Grouping: Minor and patch dependency updates are grouped into a single PR (
all-minor-patch), reducing PR volume.
prConcurrentLimit: 3: Restricts the maximum number of simultaneous Renovate PRs.
- Concurrency Cancellation:
pull_request.yml includes a concurrency block (cancel-in-progress: true). If a PR receives new commits (e.g. during a manual rebase), GitHub instantly cancels the running tests for the old commit, saving minutes.
🛠 Manual Release & Debugging
You can run the release logic locally using Fastlane to verify builds before pushing.
Prerequisites
fastlane installed.
key.properties present (or environment variables set).
service-account.json for Google Play API access.
screenshot-framer (Python library) installed for generating marketing screenshots (do not use fastlane frameit).
Commands
Run Internal Track Deployment locally:
fastlane deploy_internal
Note: This will perform the build, signing, and upload to Google Play, and will commit/tag version bumps.
Run Specific CI Checks Locally:
./gradlew ktlintCheck
./gradlew detekt
./gradlew lintDebug
./gradlew assembleRelease
📦 Versioning Strategy
Upnext uses Calendar Versioning (CalVer): YYYY.M.Patch
- Major: Year (e.g., 2025)
- Minor: Month (e.g., 1 for January)
- Patch: Incremental number for releases within that month.
File: version.properties
VERSION_CODE=214
VERSION_NAME=2025.1.4
Automated Bumping:
The Fastfile (deploy_internal lane) automatically:
- Increments
VERSION_CODE for every build.
- Sets
VERSION_NAME to YYYY.M.1 (if new month) or YYYY.M.Patch+1 (if same month).
🔧 Troubleshooting Guide
1. HTTP 403 / "Title Unknown" (Trakt API)
- Symptom: App runs, but data fails to load or showing titles are missing.
- Cause: Missing Trakt API Client ID/Secret in
local.properties or Secrets.
- Fix: Ensure
TRAKT_CLIENT_ID and TRAKT_CLIENT_SECRET are set in GitHub Actions Secrets.
2. Release Build "Missing Keystore"
3. Missing Resources / Permissions (Lint Failures)
- Symptom:
lintDebug fails with "Missing permission" (e.g., WAKE_LOCK) or "Resource not found".
- Cause: Multi-module projects (
core:common, core:data) often need their own AndroidManifest.xml and resource definitions to satisfy lint, even if the app module provides them.
- Fix: Add
AndroidManifest.xml with <uses-permission> to the specific module (e.g., core/common/src/main/AndroidManifest.xml) or define the missing resource in that module.
4. Git Identity Issues
- Symptom: "Please tell me who you are" during CI/CD.
- Fix: The
deploy.yml configures git user/email before tagging using github-actions[bot].
📂 Key Files
.github/workflows/deploy.yml: Nightly CD workflow.
.github/workflows/pull_request.yml: PR CI workflow.
fastlane/Fastfile: Build automation script.
version.properties: App version definition.
app/build.gradle: Build & signing configuration.