home / skills / hitoshura25 / claude-devtools / android-workflow-internal

android-workflow-internal skill

/skills/android-workflow-internal

This skill generates two GitHub Actions workflows for Android: CI build/tests and manual internal releases with version control.

npx playbooks add skill hitoshura25/claude-devtools --skill android-workflow-internal

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
10.9 KB
---
name: android-workflow-internal
description: Generate GitHub Actions workflows for CI and internal testing track deployment (Option D - Split Workflows)
category: android
version: 2.0.0
inputs:
  - package_name: Android app package name
outputs:
  - .github/workflows/build.yml
  - .github/workflows/release-internal.yml
verify: "yamllint .github/workflows/build.yml .github/workflows/release-internal.yml"
---

# Android Internal Track Workflows (Option D - Split)

Generates **two separate** GitHub Actions workflows following Option D architecture:
1. **build.yml** - CI only (build & test on every push/PR)
2. **release-internal.yml** - Manual releases with version management

**Clear separation of concerns:**
- CI validates code quality automatically
- Releases are always intentional (manual trigger)
- Version management integrated into release process
- No accidental deployments

## Prerequisites

- Service account setup complete
- Package name known

## Inputs

| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| package_name | Yes | - | App package name |

## Process

### Step 1: Verify Fastlane Setup

Ensure Fastlane is configured by running `/devtools:android-fastlane-setup` first.

Verify setup:
```bash
bundle exec fastlane lanes
```

Expected output should show `deploy_internal` lane.

### Step 2: Verify Metadata Structure

Ensure Fastlane metadata exists:
```bash
ls fastlane/metadata/android/en-US/
```

If missing, run `/devtools:android-fastlane-setup`.

### Step 3: Create Workflow Directory

```bash
mkdir -p .github/workflows
```

### Step 4: Create Build Workflow (CI)

Create `.github/workflows/build.yml`:

```yaml
name: Build & Test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2

      - name: Set up JDK 17
        uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00  # v4.7.0
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Setup Gradle cache
        uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57  # v4.2.0
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            .gradle/configuration-cache
          key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            gradle-${{ runner.os }}-

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4

      - name: Build
        run: ./gradlew assembleRelease

      - name: Run Unit Tests
        run: ./gradlew test

      - name: Upload Test Results
        if: always()
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02  # v4.6.0
        with:
          name: test-results
          path: app/build/reports/tests/
          retention-days: 7
```

**Purpose:** CI only - validates code quality, no deployment.

---

### Step 5: Create Release Workflow (Manual)

Create `.github/workflows/release-internal.yml`:

```yaml
name: Release to Internal Track

on:
  workflow_dispatch:
    inputs:
      version_bump:
        description: 'Version bump type'
        required: true
        type: choice
        options:
          - patch
          - minor
          - major
        default: 'patch'

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write  # For pushing commits and tags
    steps:
      - name: Checkout code
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
        with:
          fetch-depth: 0  # Full history for tags
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Set up JDK 17
        uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00  # v4.7.0
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Setup Gradle cache
        uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57  # v4.2.0
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            .gradle/configuration-cache
          key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            gradle-${{ runner.os }}-

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4

      - name: Calculate Version
        id: version
        run: |
          chmod +x scripts/gradle-version.sh
          scripts/gradle-version.sh generate ${{ inputs.version_bump }}

      - name: Update version.properties
        run: |
          scripts/gradle-version.sh update ${{ steps.version.outputs.version }}

      - name: Commit Version Bump
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add version.properties
          git commit -m "chore: release v${{ steps.version.outputs.version }}"
          git push origin main

      - name: Decode Keystore
        env:
          ENCODED_KEYSTORE: ${{ secrets.SIGNING_KEY_STORE_BASE64 }}
        run: |
          echo $ENCODED_KEYSTORE | base64 -d > app/release.jks

      - name: Build Release Bundle
        env:
          SIGNING_KEY_STORE_PATH: ${{ github.workspace }}/app/release.jks
          SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
          SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
          SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
        run: ./gradlew bundleRelease

      - name: Upload Artifact
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02  # v4.6.0
        with:
          name: release-${{ steps.version.outputs.tag }}
          path: app/build/outputs/bundle/release/app-release.aab
          retention-days: 90

      - name: Create Git Tag
        run: |
          git tag -a ${{ steps.version.outputs.tag }} -m "Release ${{ steps.version.outputs.version }}"
          git push origin ${{ steps.version.outputs.tag }}

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2'
          bundler-cache: true

      - name: Create Service Account File
        run: echo "${{ secrets.SERVICE_ACCOUNT_JSON_PLAINTEXT }}" > service-account.json

      - name: Deploy with Fastlane
        env:
          SIGNING_KEY_STORE_PATH: ${{ github.workspace }}/app/release.jks
          SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
          SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
          SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
          PLAY_STORE_SERVICE_ACCOUNT: service-account.json
        run: bundle exec fastlane deploy_internal

      - name: Cleanup
        if: always()
        run: |
          rm -f app/release.jks
          rm -f service-account.json

      - name: Release Summary
        run: |
          echo "## Release Complete" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
          echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
          echo "| Version | ${{ steps.version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Tag | ${{ steps.version.outputs.tag }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Version Code | ${{ steps.version.outputs.version-code }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Track | Internal |" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### Next Steps" >> $GITHUB_STEP_SUMMARY
          echo "1. Open Play Console - Internal Testing" >> $GITHUB_STEP_SUMMARY
          echo "2. Verify the new version is available" >> $GITHUB_STEP_SUMMARY
          echo "3. Add internal testers if needed" >> $GITHUB_STEP_SUMMARY
```

**Purpose:** Manual releases with version management - always intentional, never automatic.

**Key differences from combined approach:**
- ✅ Clear separation: CI validates code, Release deploys
- ✅ Version management integrated into release workflow
- ✅ Commits version bump before building
- ✅ Creates git tags as part of release process
- ✅ Release is always manual (workflow_dispatch only)
- ✅ Build/test runs on every PR and push (no deployment)
- ✅ No accidental deployments on regular commits

## Verification

**MANDATORY:** Validate both workflows:

```bash
# Validate YAML syntax
yamllint .github/workflows/build.yml
yamllint .github/workflows/release-internal.yml

# Verify workflows exist
test -f .github/workflows/build.yml && echo "✓ Build workflow created"
test -f .github/workflows/release-internal.yml && echo "✓ Release workflow created"

# Verify Fastlane commands
grep "fastlane deploy_internal" .github/workflows/release-internal.yml
```

**Expected output:**
- ✓ Build workflow created
- ✓ Release workflow created
- Fastlane deploy command found

## Outputs

| Output | Location | Description |
|--------|----------|-------------|
| Build workflow | .github/workflows/build.yml | CI: build & test on every push/PR |
| Release workflow | .github/workflows/release-internal.yml | Manual releases with version management |

## How to Use

### Continuous Integration (Automatic)

Every push to main or PR triggers **build.yml**:
- Builds the app
- Runs unit tests
- Uploads test results

**No deployment** - just validation.

### Release to Internal Track (Manual)

1. Go to **Actions** → **Release to Internal Track**
2. Click **Run workflow**
3. Select version bump type (patch/minor/major)
4. Click **Run workflow**

**What happens:**
1. Calculates next version from git tags
2. Updates `version.properties`
3. Commits version bump
4. Creates git tag
5. Builds release bundle
6. Deploys to Play Store internal track

## Workflow Separation Benefits

✅ **Clear intent:** CI validates, releases deploy
✅ **No accidents:** Releases are always manual
✅ **Version control:** Version bumps committed before release
✅ **Git tags:** Automatic tag creation on release
✅ **Fast CI:** Build/test runs quickly without deployment overhead
✅ **Audit trail:** Clear history of releases vs. regular commits

## Troubleshooting

### "YAML syntax error"
**Cause:** Invalid YAML format
**Fix:** Check indentation (use spaces, not tabs)

### "Workflow not triggering"
**Cause:** Build workflow not running on push
**Fix:** Verify branch name in workflow matches your main branch (main or master)

### "Version calculation fails"
**Cause:** version-manager.sh script not found or not executable
**Fix:** Run `/devtools:version-management` first to setup version scripts

## Completion Criteria

- [ ] `.github/workflows/build.yml` exists and is valid
- [ ] `.github/workflows/release-internal.yml` exists and is valid
- [ ] Fastlane configured (`bundle exec fastlane lanes` works)
- [ ] Metadata exists in `fastlane/metadata/android/en-US/`
- [ ] Version management scripts available
- [ ] Build workflow runs on push/PR
- [ ] Release workflow available for manual trigger

Overview

This skill generates two focused GitHub Actions workflows for Android: a CI-only build & test workflow and a manual release workflow for the Play Store internal track. It follows Option D (Split Workflows) to enforce separation of concerns, version management, and intentional releases. The workflows integrate Gradle, JDK setup, caching, Fastlane deployment, and automated version bumping and tagging.

How this skill works

It creates .github/workflows/build.yml that runs on pushes and pull requests to main and performs checkout, JDK setup, Gradle cache/setup, assembleRelease, unit tests, and uploads test reports. It also creates .github/workflows/release-internal.yml triggered only by workflow_dispatch to perform a controlled release: calculate and commit version bumps, build the release bundle, decode keystore from secrets, run Fastlane deploy_internal, upload artifacts, create a git tag, and write a release summary. Both workflows include validation and cleanup steps.

When to use it

  • You want strict separation between CI validation and deployments
  • You need manual, auditable releases to the Play Store internal track
  • You require automated version bumping and tag creation during releases
  • You have Fastlane and version scripts already configured
  • You want faster CI runs without deployment overhead

Best practices

  • Ensure Fastlane setup and metadata exist before enabling workflows
  • Store signing keys and service account JSON in repository secrets (base64 as needed)
  • Keep fetch-depth: 0 in release workflow to allow tagging and version calculation
  • Validate YAML syntax with yamllint after creating workflows
  • Run bundle exec fastlane lanes locally to confirm deploy_internal lane exists

Example use cases

  • Run build.yml on every PR to catch regressions and upload test reports
  • Trigger Release to Internal Track manually when a new internal tester build is needed
  • Use version bump input (patch/minor/major) to control semantic versioning during release
  • Automate tag creation and push version commit as part of release audit trail
  • Upload AAB artifacts for QA while preventing accidental public deployments

FAQ

What prerequisites are required?

You must have Fastlane configured with a deploy_internal lane, fastlane metadata in fastlane/metadata/android/en-US/, version scripts, and service account + signing secrets in GitHub.

Why separate CI and release workflows?

Separation prevents accidental deployments, speeds up CI by removing deployment steps, and ensures releases are intentional with version bumps and tags tracked in git.