Skip to content
Merged
21 changes: 17 additions & 4 deletions .github/workflows/android-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,32 @@ jobs:
- name: Build Docker Image
run: docker build -t android-ci .

- name: Determine Build Target
id: target
run: |
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
echo "TASK=assembleRelease" >> $GITHUB_OUTPUT
echo "APK_PATH=app/build/outputs/apk/release/app-release-unsigned.apk" >> $GITHUB_OUTPUT
echo "ARTIFACT_NAME=app-release" >> $GITHUB_OUTPUT
else
echo "TASK=assembleDebug" >> $GITHUB_OUTPUT
echo "APK_PATH=app/build/outputs/apk/debug/app-debug.apk" >> $GITHUB_OUTPUT
echo "ARTIFACT_NAME=app-debug" >> $GITHUB_OUTPUT
fi

- name: Run Gradle Build in Docker
# Mount current directory to /app
# Use the image built in previous step
# Run './gradlew build'
# Run './gradlew <task>'
run: |
docker run --rm \
-v ${{ github.workspace }}:/app \
-w /app \
android-ci \
bash -c "chmod +x gradlew && ./gradlew build"
bash -c "chmod +x gradlew && ./gradlew ${{ steps.target.outputs.TASK }}"

- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: app-debug
path: app/build/outputs/apk/debug/app-debug.apk
name: ${{ steps.target.outputs.ARTIFACT_NAME }}
path: ${{ steps.target.outputs.APK_PATH }}
27 changes: 20 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
# Android Build with Docker and GitHub Actions
# Android CI/CD Pipeline with Docker & GitHub Actions

This repository contains a sample Android project and a tutorial on how to set up a CI/CD pipeline using Docker and GitHub Actions.
## Overview
This repository demonstrates a production-grade **DevOps** setup for mobile applications. It implements a complete CI/CD pipeline for Android using **Infrastructure as Code (IaC)** principles with Docker and **Automated Workflows** with GitHub Actions.

## Contents
## Key Features (DevOps Highlights)
- **Containerized Build Environment**: Uses a custom Dockerfile with OpenJDK 17 and Android SDK 34 to ensure ensuring 100% reproducibility across environments ("Infrastructure as Code").
- **Automated CI Pipeline**: Triggers builds on feature branches (`feat/*`) and the main branch.
- **Conditional Build Logic**: Automatically distinguishes between development builds (`debug`) and production artifacts (`release`) based on the branch.
- **Artifact Management**: Automatically packages and uploads APKs for testing and distribution.
- **Quality Checks**: Includes linting and static analysis integration.

- [App Source Code](./app/)
- [Dockerfile](./Dockerfile)
- [GitHub Actions Workflow](./.github/workflows/android-docker.yml)
- [**TUTORIAL**: Step-by-Step Guide](./TUTORIAL.md)
## Project Structure
- `.github/workflows/`: CI/CD definitions.
- `Dockerfile`: Build environment definition.
- `app/`: Android application source code.
- `TUTORIAL.md`: **[Build Guide & Interview Prep Details](./TUTORIAL.md)**.

## Quick Start
1. Fork this repository.
2. Create a feature branch: `git checkout -b feat/my-feature`.
3. Push changes to trigger a **Debug Build**.
4. Merge to `main` to trigger a **Release Build**.
103 changes: 41 additions & 62 deletions TUTORIAL.md
Original file line number Diff line number Diff line change
@@ -1,79 +1,58 @@
# Tutorial: Android GitHub Actions in Docker
# DevOps Guide: Android CI/CD with Docker

This tutorial guides you through setting up a CI/CD pipeline for your Android application using GitHub Actions and Docker. This ensures a consistent build environment and automates your build process.
This guide explains the "Why" and "How" of this project's architecture, specifically tailored for DevOps interview preparation.

## Prerequisites
## Part 1: The Architecture (Interview Concept)

- An Android project (already set up in this repository).
- Docker installed on your development machine (for local testing).
- A GitHub repository.
**Question:** "How do you ensure consistent builds across different developer machines and CI servers?"
**Answer:** "By containerizing the build environment. I use **Docker** to define the exact version of the JDK, Android SDK, and Build Tools. This verifies that 'it works on my machine' means it works everywhere."

## 1. The Docker Build Environment
### The Dockerfile Explained
[View Dockerfile](./Dockerfile)
- **`FROM eclipse-temurin:17-jdk`**: We start with a stable, lightweight Java base image.
- **`ENV ANDROID_HOME`**: We adhere to standard conventions for SDK location.
- **`sdkmanager`**: We programmatically install specific SDK versions (`platform-34`) matching our `build.gradle` config.

We use a `Dockerfile` to define an immutable build environment. This container includes:
- **OpenJDK 17**: The Java version required by the Gradle build.
- **Android Command Line Tools**: Necessary for managing the Android SDK.
- **Android SDK Components**: Specifically `platforms;android-34` and `build-tools;34.0.0` as defined in `app/build.gradle`.
## Part 2: The Pipeline (Interview Concept)

### Key Dockerfile Sections
**Question:** "Describe your CI/CD strategy for mobile apps."
**Answer:** "I implement a branching strategy where feature branches trigger flexible debug builds for rapid feedback, while the main branch triggers strict production/release builds."

```dockerfile
# Base image
FROM openjdk:17-jdk-slim
### The Workflow Explained
[View Workflow](./.github/workflows/android-docker.yml)

# Install system dependencies
RUN apt-get update && apt-get install -y curl unzip git ...

# Download Command Line Tools
RUN curl -o cmdline-tools.zip ...

# Install SDK Packages
RUN sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
```

## 2. GitHub Actions Workflow

The workflow is defined in `.github/workflows/android-docker.yml`. It triggers on pushes and pull requests to the `main` branch.

### Workflow Breakdown

1. **Checkout Code**: Retrieves your project source code.
2. **Build Docker Image**: Builds the container defined in your `Dockerfile`.
3. **Run Build**: Mounts the project source code into the container and runs `./gradlew build`.

```yaml
- name: Run Gradle Build in Docker
run: |
docker run --rm \
-v ${{ github.workspace }}:/app \
-w /app \
android-ci \
./gradlew build
#### 1. Conditional Logic
We use shell scripting within the workflow to determine the build target dynamically:
```bash
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
# Production: Build Release
echo "TASK=assembleRelease" >> $GITHUB_OUTPUT
else
# Development: Build Debug
echo "TASK=assembleDebug" >> $GITHUB_OUTPUT
fi
```

## 3. Running Locally

You can test the build environment locally using Docker before pushing to GitHub.

### Step 1: Build the Image

#### 2. Isolation
The build runs *inside* the Docker container:
```bash
docker build -t android-ci .
docker run --rm -v $(pwd):/app ... ./gradlew $TASK
```
This ensures the CI runner's pre-installed tools don't interfere with our build.

### Step 2: Run the Build
#### 3. Artifact Retention
We use `actions/upload-artifact` to save the correct APK (`app-debug.apk` or `app-release-unsigned.apk`) allowing QA teams to download and test immediately without access to the code.

```bash
docker run --rm -v $(pwd):/app -w /app android-ci ./gradlew assembleDebug
```
## Part 3: Android Configuration Basics for DevOps

This command:
- `--rm`: Removes the container after it exits.
- `-v $(pwd):/app`: Maps your current directory to `/app` inside the container.
- `-w /app`: Sets the working directory to `/app`.
- `android-ci`: Uses the image you just built.
- `./gradlew assembleDebug`: Runs the Gradle task to build the debug APK.
**Question:** "What files do you need to configure in an Android project?"
**Answer:**
1. **`app/build.gradle`**: Defines the *compileSdk* (must match Dockerfile) and *versionCode* (should be auto-incremented in CI).
2. **`gradle.properties`**: Controls build performance (JVM args) and library compatibility (`android.useAndroidX=true`).
3. **`local.properties`**: **IGNORED** by git. Used for local SDK paths. In CI, we use environment variables instead.

## Conclusion
## Part 4: Common Interview Troubleshooting

By containerizing your build environment, you eliminate "it works on my machine" issues and simplify your CI configuration. This setup forms the foundation for more advanced pipelines, including running tests and deploying to the Play Store.
- **"Gradle not found"**: Often means the wrapper script (`gradlew`) relies on a system gradle that isn't installed. *Fix: Install Gradle in the Docker image or use the wrapper correctly.*
- **"Permission denied"**: `gradlew` script often loses executable permission in git. *Fix: `chmod +x gradlew` or `git update-index --chmod=+x`.*
- **"License not accepted"**: The Android SDK requires explicit license acceptance. *Fix: Run `yes | sdkmanager --licenses` in the Dockerfile.*