Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Automating Android Workflows with Github Actions

Automating Android Workflows with Github Actions

Companion slides for my talk about Android CI/CD powered by Github Actions, including platform models, CI topologies and unique challenges we can find when automating Android pipelines withing this platform.

Delivered in the following events :

- GDG Curitiba Android Meetup #08 (online) - August / 2020
- TDC São Paulo Trilha Android (online) - August/2020
- DevCommunity Summit (online) - October/2021
- Droidcon Berlin - October/2021

Ubiratan Soares
PRO

August 26, 2020
Tweet

More Decks by Ubiratan Soares

Other Decks in Programming

Transcript

  1. AUTOMATING YOUR
    ANDROID WORKFLOWS
    Ubiratan Soares
    October / 2021
    GITHUB ACTIONS

    View Slide

  2. CRASHING
    COURSE !!!

    View Slide

  3. Step
    Step
    Step
    Job
    . . .
    Workflow
    Runner
    Event

    View Slide

  4. Job
    . . .
    Workflow
    Push
    Pull Request
    Release
    REST API
    Tag
    .
    .
    .
    Job
    . . .
    Job
    . . .

    View Slide

  5. Pull Request
    Build
    . . .
    Internal Library
    • 1 Event
    • 1 Job triggered

    View Slide

  6. Tag
    Build
    . . .
    Internal Library
    Publish
    . . .
    • 1 Event
    • 1 Job triggered
    • 1 Job chained

    View Slide

  7. /
    .github/workflows
    compliance-checks.yaml
    ci.yaml
    etc.yaml

    View Slide

  8. What are your
    thoughts about YAML?
    Software Engineer

    View Slide

  9. Job
    Runner

    View Slide

  10. Step
    Job
    Runner Shell Script Command

    View Slide

  11. Step
    Job
    Runner
    Shell Script Command
    Step Composite Action (Bash)

    View Slide

  12. Step
    Job
    Runner
    Shell Script Command
    Step Composite Action (Bash)
    Step Nested Composite Action

    View Slide

  13. Step
    Job
    Runner
    Shell Script Command
    Step Composite Action (Bash)
    Step Nested Composite Action
    Step Code-defined

    View Slide

  14. Action Type | Runner Ubuntu MacOS Windows
    JS YES YES YES
    Docker YES NO NO
    Composite/Simple YES YES YES
    Composite/Nested YES 👀 👀
    Step
    Job
    Step
    Runner
    . . .
    by

    View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. name: Android CI
    on:
    pull_request:
    push:
    branches:
    - 'master'
    - '4.**'
    - '5.**'
    jobs:
    build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11
    - name: Validate Gradle Wrapper
    uses: gradle/wrapper-validation-action@v1
    - name: Remove Android 31 (S)
    run: $ANDROID_HOME/tools/bin/sdkmanager --uninstall "platforms;android-31"
    - name: Build with Gradle
    run: ./gradlew qa
    - name: Archive reports for failed build
    if: ${{ failure() }}
    uses: actions/upload-artifact@v2
    with:
    name: reports
    path: '*/build/reports' github.com/signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  20. name: Android CI
    on:
    pull_request:
    push:
    branches:
    - 'master'
    - '4.**'
    - '5.**'
    jobs:
    build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11 github.com/signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  21. name: Android CI
    on:
    pull_request:
    push:
    branches:
    - 'master'
    - '4.**'
    - '5.**'
    jobs:
    build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11 github.com/signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  22. name: Android CI
    on:
    pull_request:
    push:
    branches:
    - 'master'
    - '4.**'
    - '5.**'
    jobs:
    build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11 github.com/signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  23. name: Android CI
    on:
    pull_request:
    push:
    branches:
    - 'master'
    - '4.**'
    - '5.**'
    jobs:
    build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11 signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  24. steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11
    - name: Validate Gradle Wrapper
    uses: gradle/wrapper-validation-action@v1
    - name: Remove Android 31 (S)
    run: $ANDROID_HOME/tools/bin/sdkmanager --uninstall "platforms;android-31"
    - name: Build with Gradle
    run: ./gradlew qa
    - name: Archive reports for failed build
    if: ${{ failure() }}
    uses: actions/upload-artifact@v2
    with:
    name: reports
    path: '*/build/reports'
    github.com/signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  25. steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11
    - name: Validate Gradle Wrapper
    uses: gradle/wrapper-validation-action@v1
    - name: Remove Android 31 (S)
    run: $ANDROID_HOME/tools/bin/sdkmanager --uninstall "platforms;android-31"
    - name: Build with Gradle
    run: ./gradlew qa
    - name: Archive reports for failed build
    if: ${{ failure() }}
    uses: actions/upload-artifact@v2
    with:
    name: reports
    path: '*/build/reports'
    github.com/signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  26. steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11
    - name: Validate Gradle Wrapper
    uses: gradle/wrapper-validation-action@v1
    - name: Remove Android 31 (S)
    run: $ANDROID_HOME/tools/bin/sdkmanager --uninstall "platforms;android-31"
    - name: Build with Gradle
    run: ./gradlew qa
    - name: Archive reports for failed build
    if: ${{ failure() }}
    uses: actions/upload-artifact@v2
    with:
    name: reports
    path: '*/build/reports'
    github.com/signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  27. steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11
    - name: Validate Gradle Wrapper
    uses: gradle/wrapper-validation-action@v1
    - name: Remove Android 31 (S)
    run: $ANDROID_HOME/tools/bin/sdkmanager --uninstall "platforms;android-31"
    - name: Build with Gradle
    run: ./gradlew qa
    - name: Archive reports for failed build
    if: ${{ failure() }}
    uses: actions/upload-artifact@v2
    with:
    name: reports
    path: '*/build/reports'
    github.com/signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  28. name: Android CI
    on:
    pull_request:
    push:
    branches:
    - 'master'
    - '4.**'
    - '5.**'
    jobs:
    build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
    uses: actions/setup-java@v1
    with:
    java-version: 11
    - name: Validate Gradle Wrapper
    uses: gradle/wrapper-validation-action@v1
    - name: Remove Android 31 (S)
    run: $ANDROID_HOME/tools/bin/sdkmanager --uninstall "platforms;android-31"
    - name: Build with Gradle
    run: ./gradlew qa
    - name: Archive reports for failed build
    if: ${{ failure() }}
    uses: actions/upload-artifact@v2
    with:
    name: reports
    path: '*/build/reports'
    • Multi-triggers
    • 1 Job
    • 5 mandatory Steps
    • 1 conditional Step
    github.com/signalapp/Signal-Android/blob/master/.github/workflows/android.yml

    View Slide

  29. jobs:
    build:
    # Something before
    steps:
    - uses: actions/cache@v1

    View Slide

  30. name: 'Cache'
    [. . .]
    inputs:
    path:
    description: 'A list of files, directories, and wildcard patterns to cache and restore'
    required: true
    key:
    description: 'An explicit key for restoring and saving the cache'
    required: true
    restore-keys:
    description: 'An ordered list of keys to use for restoring the cache if no cache hit occurred'
    required: false
    outputs:
    cache-hit:
    description: 'A boolean value to indicate an exact match was found for the primary key'
    https://github.com/actions/cache/blob/master/action.yml

    View Slide

  31. name: 'Cache'
    [. . .]
    inputs:
    path:
    description: 'A list of files, directories, and wildcard patterns to cache and restore'
    required: true
    key:
    description: 'An explicit key for restoring and saving the cache'
    required: true
    restore-keys:
    description: 'An ordered list of keys to use for restoring the cache if no cache hit occurred'
    required: false
    outputs:
    cache-hit:
    description: 'A boolean value to indicate an exact match was found for the primary key'
    https://github.com/actions/cache/blob/master/action.yml

    View Slide

  32. View Slide

  33. SCALING
    PIPELINES

    View Slide

  34. Step
    Step
    Job
    . . .
    Workflow
    Runner
    Github-hosted Runner vCPUs RAM SSD
    Ubuntu 2 7 GB 14 GB
    MacOS 3 14 GB 14 GB
    Windows 2 7 GB 14 GB
    by

    View Slide

  35. Github-hosted Runner
    Self-hosted Runner

    View Slide

  36. Step
    Job 1
    Step
    Step
    Job 2
    Step
    Step
    Job N
    Step
    . . .
    RUNNER RUNNER RUNNER
    Step
    Single Job
    . . .
    RUNNER
    vs
    Step
    Step
    Step
    Step
    Step
    🤔

    View Slide

  37. Build Matrix

    View Slide

  38. View Slide

  39. View Slide

  40. View Slide

  41. name: Pre Merge Checks
    on:
    push:
    branches:
    - main
    pull_request:
    branches:
    - '**'
    jobs:
    gradle:
    if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
    strategy:
    fail-fast: false
    matrix:
    os: [ubuntu-latest, macos-latest, windows-latest]
    jdk: [8, 11, 17]
    runs-on: ${{ matrix.os }}
    env:
    JDK_VERSION: ${{ matrix.jdk }}
    GRADLE_OPTS: -Dorg.gradle.daemon=false

    View Slide

  42. name: Pre Merge Checks
    on:
    push:
    branches:
    - main
    pull_request:
    branches:
    - '**'
    jobs:
    gradle:
    if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
    strategy:
    fail-fast: false
    matrix:
    os: [ubuntu-latest, macos-latest, windows-latest]
    jdk: [8, 11, 17]
    runs-on: ${{ matrix.os }}
    env:
    JDK_VERSION: ${{ matrix.jdk }}
    GRADLE_OPTS: -Dorg.gradle.daemon=false

    View Slide

  43. steps:
    - name: Checkout Repo
    uses: actions/checkout@v2
    - name: Cache Gradle Folders
    uses: actions/cache@v2
    with:
    path: |
    ~/.gradle/caches/
    ~/.gradle/wrapper/
    key: cache-gradle-${{ matrix.os }}-${{ matrix.jdk }}-${{ hashFiles('gradle/libs.versions.toml')
    restore-keys: |
    cache-gradle-${{ matrix.os }}-${{ matrix.jdk }}-
    cache-gradle-${{ matrix.os }}-
    cache-gradle-
    - name: Setup Java
    uses: actions/setup-java@v2
    with:
    java-version: ${{ matrix.jdk }}
    distribution: 'adopt'
    - name: Build detekt
    run: ./gradlew build moveJarForIntegrationTest -x detekt

    View Slide

  44. steps:
    - name: Checkout Repo
    uses: actions/checkout@v2
    - name: Cache Gradle Folders
    uses: actions/cache@v2
    with:
    path: |
    ~/.gradle/caches/
    ~/.gradle/wrapper/
    key: cache-gradle-${{ matrix.os }}-${{ matrix.jdk }}-${{ hashFiles('gradle/libs.versions.toml')
    restore-keys: |
    cache-gradle-${{ matrix.os }}-${{ matrix.jdk }}-
    cache-gradle-${{ matrix.os }}-
    cache-gradle-
    - name: Setup Java
    uses: actions/setup-java@v2
    with:
    java-version: ${{ matrix.jdk }}
    distribution: 'adopt'
    - name: Build detekt
    run: ./gradlew build moveJarForIntegrationTest -x detekt

    View Slide

  45. Matrix (A, B)
    Step
    Step
    Job
    . . .
    Step
    Step
    Job
    . . .
    Step
    Step
    Job
    . . .
    Step
    Step
    Job
    . . .
    A0, B0 A1, B0 A2, B0 An, Bm
    . . .

    View Slide

  46. View Slide

  47. View Slide

  48. Custom
    Topologies

    View Slide

  49. Event Job 2
    Job 3
    Job 5
    Job 6
    Job 7
    Job 4
    Job 1

    View Slide

  50. jobs:
    first:
    steps:
    # Your steps here
    second:
    needs: first
    steps:
    # Your steps here
    Step
    first
    Step
    Step
    second
    Step
    Step
    Sequential Jobs

    View Slide

  51. jobs:
    first:
    steps:
    # Steps for first job
    second:
    steps:
    # Steps for second job
    third:
    needs: [first, second]
    steps:
    # Steps for third job
    Step
    first
    Step
    Step
    second
    Step
    third
    Step
    Step
    Fan-in to Job

    View Slide

  52. Step
    first
    Step
    Step
    second
    Step
    third
    Step
    Step
    jobs:
    first:
    steps:
    # Steps for first job
    second:
    needs: first
    steps:
    # Steps for second job
    third:
    needs: first
    steps:
    # Steps for third job
    Fan-out from Job

    View Slide

  53. View Slide

  54. View Slide

  55. https://github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml
    acceptance_tests:
    runs-on: ubuntu-20.04
    timeout-minutes: 15
    needs: [assemble_apk, espresso_prepare, unit_tests, static_analysis]
    steps:
    - name: Project Checkout
    uses: actions/[email protected]
    - name: Fetch Instrumentation artefacts
    uses: actions/[email protected]
    - name: Run Espresso tests over emulator.wtf
    run: ./scripts/emulator-wtf.sh ${{ secrets.EMULATOR_WTF_TOKEN }}
    - name: Archive execution results
    if: success()
    uses: actions/[email protected]
    with:
    name: emulator-wtf-results
    path: emulator-wtf-results

    View Slide

  56. https://github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml
    acceptance_tests:
    runs-on: ubuntu-20.04
    timeout-minutes: 15
    needs: [assemble_apk, espresso_prepare, unit_tests, static_analysis]
    steps:
    - name: Project Checkout
    uses: actions/[email protected]
    - name: Fetch Instrumentation artefacts
    uses: actions/[email protected]
    - name: Run Espresso tests over emulator.wtf
    run: ./scripts/emulator-wtf.sh ${{ secrets.EMULATOR_WTF_TOKEN }}
    - name: Archive execution results
    if: success()
    uses: actions/[email protected]
    with:
    name: emulator-wtf-results
    path: emulator-wtf-results

    View Slide

  57. STUDY
    CASE

    View Slide

  58. https://github.com/dotanuki-labs/norris
    +
    📸
    📸
    📸

    View Slide

  59. View Slide

  60. /
    .gitignore
    src/androidTest//ScreenshotTests.kt
    screenshots/debug
    your.nice.package.SomeTestName.png
    your.nice.package.AnotherTestName.png
    your.nice.package.OneMoreTest.png
    .
    .
    .

    View Slide

  61. /
    .gitignore
    src/androidTest//ScreenshotTests.kt
    screenshots/debug
    your.nice.package.SomeTestName.png
    your.nice.package.AnotherTestName.png
    your.nice.package.OneMoreTest.png
    .
    .
    .

    View Slide

  62. /
    .gitignore
    src/androidTest//ScreenshotTests.kt
    screenshots/debug
    your.nice.package.SomeTestName.png
    your.nice.package.AnotherTestName.png
    your.nice.package.OneMoreTest.png
    .
    .
    .

    View Slide

  63. Hooking over 3rd party Gradle plugins / tasks

    View Slide

  64. What if I told you … there is another way !!!

    View Slide

  65. 🔥
    • Have the 2 sets of images available beforehand
    • Define target device at pipeline time
    • Copy images into convention folder according
    device spec
    • Spawn emulator according device spec
    • Invoke target Gradle task and run tests !!!

    View Slide

  66. View Slide

  67. View Slide

  68. name: Main
    jobs:
    # Other jobs
    screenshot_tests:
    runs-on: macOS-10.15
    needs: [acceptance_tests]
    timeout-minutes: 20
    strategy:
    fail-fast: true
    matrix:
    device: ['nexus4', 'pixel']
    github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml#L125

    View Slide

  69. name: Main
    jobs:
    # Other jobs
    screenshot_tests:
    runs-on: macOS-10.15
    needs: [acceptance_tests]
    timeout-minutes: 20
    strategy:
    fail-fast: true
    matrix:
    device: ['nexus4', 'pixel']
    github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml#L125

    View Slide

  70. name: Main
    jobs:
    # Other jobs
    screenshot_tests:
    runs-on: macOS-10.15
    needs: [acceptance_tests]
    timeout-minutes: 20
    strategy:
    fail-fast: true
    matrix:
    device: ['nexus4', 'pixel']
    github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml#L125

    View Slide

  71. steps:
    - name: Project Checkout
    uses: actions/[email protected]
    - name: Synchronize screenshots
    run: ./scripts/screenshots-sync.sh ${{ matrix.device }}
    - name: Assign emulator profile
    id: emulator-profile
    uses: ./.github/actions/assign-emulator-profile
    with:
    device: ${{ matrix.device }}
    - name: Run Screenshot tests
    uses: reactivecircus/[email protected]
    with:
    api-level: 28
    target: 'google_apis'
    profile: ${{ steps.emulator-profile.outputs.assigned }}
    script: ./gradlew clean executeScreenTests --no-daemon --stacktrace
    github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml#L125

    View Slide

  72. steps:
    - name: Project Checkout
    uses: actions/[email protected]
    - name: Synchronize screenshots
    run: ./scripts/screenshots-sync.sh ${{ matrix.device }}
    - name: Assign emulator profile
    id: emulator-profile
    uses: ./.github/actions/assign-emulator-profile
    with:
    device: ${{ matrix.device }}
    - name: Run Screenshot tests
    uses: reactivecircus/[email protected]
    with:
    api-level: 28
    target: 'google_apis'
    profile: ${{ steps.emulator-profile.outputs.assigned }}
    script: ./gradlew clean executeScreenTests --no-daemon --stacktrace
    github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml#L125

    View Slide

  73. steps:
    - name: Project Checkout
    uses: actions/[email protected]
    - name: Synchronize screenshots
    run: ./scripts/screenshots-sync.sh ${{ matrix.device }}
    - name: Assign emulator profile
    id: emulator-profile
    uses: ./.github/actions/assign-emulator-profile
    with:
    device: ${{ matrix.device }}
    - name: Run Screenshot tests
    uses: reactivecircus/[email protected]
    with:
    api-level: 28
    target: 'google_apis'
    profile: ${{ steps.emulator-profile.outputs.assigned }}
    script: ./gradlew clean executeScreenTests --no-daemon --stacktrace
    github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml#L125
    Internal GHA
    “nexus4”
    “Nexus 4”

    View Slide

  74. steps:
    - name: Project Checkout
    uses: actions/[email protected]
    - name: Synchronize screenshots
    run: ./scripts/screenshots-sync.sh ${{ matrix.device }}
    - name: Assign emulator profile
    id: emulator-profile
    uses: ./.github/actions/assign-emulator-profile
    with:
    device: ${{ matrix.device }}
    - name: Run Screenshot tests
    uses: reactivecircus/[email protected]
    with:
    api-level: 28
    target: 'google_apis'
    profile: ${{ steps.emulator-profile.outputs.assigned }}
    script: ./gradlew clean executeScreenTests --no-daemon --stacktrace
    github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml#L125
    Internal GHA
    “nexus4”
    “Nexus 4”

    View Slide

  75. #! /usr/bin/env bash
    set -e
    readonly features=(
    "search"
    "facts"
    )
    readonly device="$1"
    sync() {
    echo -e "Syncing screenshots for device profile : $device"
    for feature in "${features[@]}"
    do
    origin="features/$feature/screenshots/$device"
    destination="features/$feature/screenshots/debug"
    rm -rf $destination
    echo -e "✔ Cleaned contents at $destination"
    cp -R $origin $destination
    echo -e "✔ Copied images from $origin$ to $destination"
    done
    }
    github.com/dotanuki-labs/norris/blob/master/scripts/screenshots-sync.sh

    View Slide

  76. #! /usr/bin/env bash
    set -e
    readonly features=(
    "search"
    "facts"
    )
    readonly device="$1"
    sync() {
    echo -e "Syncing screenshots for device profile : $device"
    for feature in "${features[@]}"
    do
    origin="features/$feature/screenshots/$device"
    destination="features/$feature/screenshots/debug"
    rm -rf $destination
    echo -e "✔ Cleaned contents at $destination"
    cp -R $origin $destination
    echo -e "✔ Copied images from $origin$ to $destination"
    done
    }
    github.com/dotanuki-labs/norris/blob/master/scripts/screenshots-sync.sh

    View Slide

  77. View Slide

  78. View Slide

  79. ANDROID
    TIPS++

    View Slide

  80. Emulators on CI

    View Slide

  81. View Slide

  82. View Slide

  83. View Slide

  84. Definining
    Timeouts

    View Slide

  85. name: Main
    on:
    pull_request:
    push:
    branches:
    - master
    jobs:
    static_analysis:
    runs-on: ubuntu-20.04
    timeout-minutes: 10
    steps:
    # Previous steps
    - name: Check code formatting
    run: ./gradlew ktlintCheck detekt —stacktrace
    https://github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml

    View Slide

  86. name: Main
    on:
    pull_request:
    push:
    branches:
    - master
    jobs:
    static_analysis:
    runs-on: ubuntu-20.04
    timeout-minutes: 10
    steps:
    # Previous steps
    - name: Check code formatting
    run: ./gradlew ktlintCheck detekt —stacktrace
    https://github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml

    View Slide

  87. View Slide

  88. View Slide

  89. Gradle Caching

    View Slide

  90. JOB
    Save
    Restore
    Cloud Storage
    JOB
    Save
    Restore
    Cloud Storage
    JOB
    Workflow Run Workflow Run Workflow Run

    View Slide

  91. Hard questions around CI caching
    • How do we compute entry-keys that maximize cache hits?
    • What should be cached per execution?
    • Should we segregate cached files per Job?
    • Etc
    🤔

    View Slide

  92. Github Actions
    Required
    Configuration
    Layered
    Caching
    Tasks
    Caching
    Distribution
    Caching
    Segregation
    per Job
    actions/setup-java (built-in)
    Easiest
    (Zero conf)
    No Yes Yes No
    actions/cache
    Hard
    (All custom)
    No
    Opt-in per
    Configuration
    Opt-in per
    Configuration
    Possible
    gradle/gradle-build-action
    Easy
    (Sensible Defaults)
    No Yes Yes Yes
    burrunan/gradle-cache-action
    Easy
    (Sensible Defaults)
    Yes Yes Yes Yes

    View Slide

  93. Food for thought

    View Slide

  94. Task | Condition Merged PR Push Cherry pick Tag Nightly
    Static Analysis ✓ ✓ ✓
    Unit tests ✓ ✓ ✓ ✓
    Espresso tests ✓ ✓ ✓ ✓
    Smoke Tests ✓ ✓ ✓ ✓
    Security Analysis ✓ ✓ ✓ ✓
    Build ✓ ✓ ✓ ✓
    Artifacts generation ✓ ✓ ✓ ✓
    Artifact distribution ✓ ✓

    View Slide

  95. Decision Matrix Merged PR Push Cherry pick Tag Nightly
    Static Analysis ✓ ✓ ✓
    Unit tests ✓ ✓ ✓ ✓
    Espresso tests ✓ 🤔 ✓ ✓ 🤔
    Smoke Tests
    🤔 🤔 ✓ ✓ 🤔
    Security Analysis
    🤔 🤔 🤔 ✓
    Build ✓ ✓ 🤔 ✓ ✓
    Artifacts generation ✓ ✓ ✓ ✓
    Artifact distribution ✓ ✓

    View Slide

  96. Code Changes Tests Build Deployable
    Deploy Release
    Continuous Integration
    Continuous Delivery
    Monitoring
    E2E Tests

    View Slide

  97. Code Changes Tests Build Deployable
    CD ???
    CI

    View Slide

  98. FINAL
    REMARKS

    View Slide

  99. TL;DR
    • Open-source model for reusable Actions is quite powerful
    • Pipelines can scale both horizontally and vertically
    • Awesome option for open-source projects
    • Unique challenges for big Android projects

    View Slide

  100. UBIRATAN
    SOARES
    Brazilian Computer Scientist
    Senior Software Engineer @ N26
    GDE for Android and Kotlin
    @ubiratanfsoares
    ubiratansoares.dev

    View Slide

  101. THANKS

    View Slide