Issue Checks

# Contains jobs corresponding to issue related checks.

name: Issue Checks

on:
  issues:
    types: [closed, reopened]

permissions:
  issues: write

jobs:
  script_check:
    name: Closed TODO Issue Check
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2

      - name: Set up Bazel
        uses: abhinavsingh/setup-bazel@v3
        with:
          version: 6.5.0

      - name: TODO Issue Resolved Check
        id: todoIssueResolvedCheck
        if: ${{ github.event.action == 'closed' }}
        run: |
          bazel run //scripts:todo_issue_resolved_check -- $(pwd) ${{ github.event.issue.number }} ${{ github.sha }}

      - name: Reopen Issue
        if: failure() && steps.todoIssueResolvedCheck.outcome == 'failure'
        env:
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          gh issue reopen ${{ github.event.issue.number }}

      - name: Check for duplicate comment
        id: duplicateCommentCheck
        if: failure() && steps.todoIssueResolvedCheck.outcome == 'failure'
        env:
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          gh issue view ${{ github.event.issue.number }} --json comments --jq '.comments[-1].body' > $(pwd)/latest_comment.txt
          bazel run //scripts:todo_issue_comment_check -- $(pwd) latest_comment.txt script_failures.txt

      - name: Add Comment
        if: failure() && steps.duplicateCommentCheck.outcome == 'failure'
        env:
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          gh issue comment ${{ github.event.issue.number }} -F $(pwd)/script_failures.txt


Unit Tests (Robolectric -- Gradle)

# Contains jobs corresponding to Gradle tests (particularly Robolectric unit tests).

name: Unit Tests (Robolectric -- Gradle)

# Controls when the action will run. Triggers the workflow on pull request
# events or push events in the develop branch.
on:
  workflow_dispatch:
  pull_request:
  push:
    branches:
      # Push events on develop branch
      - develop

# This workflow has the following jobs:
# robolectric_tests: Robolectric tests for all modules except the app module
# app_tests: Non-flaky Robolectric tests for the app module
jobs:
  robolectric_tests:
    name: Non-app Module Robolectric Tests
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-20.04]
    steps:
      - uses: actions/checkout@v2
      - uses: actions/cache@v2
        id: cache
        with:
          path: ~/.gradle
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-jars-{{ checksum "build.gradle" }}

      - name: Set up JDK 1.9
        uses: actions/setup-java@v1
        with:
          java-version: 1.9

      - name: Check Java version & path
        run: |
          which java
          java -version
          echo "Java home: $JAVA_HOME"
          $JAVA_HOME/bin/java -version

      - name: Install Dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: ./gradlew --full-stacktrace androidDependencies -Dorg.gradle.java.home=$JAVA_HOME

      - name: Check Gradle environment
        run: ./gradlew -Dorg.gradle.java.home=$JAVA_HOME -v

      - name: Build App
        # We require 'sudo' to avoid an error of the existing android sdk. See https://github.com/actions/starter-workflows/issues/58
        run: sudo ./gradlew --full-stacktrace assembleDebug -Dorg.gradle.java.home=$JAVA_HOME

      - name: Utility tests
        if: always()
        # We require 'sudo' to avoid an error of the existing android sdk. See https://github.com/actions/starter-workflows/issues/58
        run:  sudo ./gradlew --full-stacktrace :utility:testDebugUnitTest -Dorg.gradle.java.home=$JAVA_HOME
      - name: Upload Utility Test Reports
        uses: actions/upload-artifact@v2
        if: ${{ always() }} # IMPORTANT: Upload reports regardless of status
        with:
          name: utility reports
          path: utility/build/reports

      - name: Data tests
        if: always()
        # We require 'sudo' to avoid an error of the existing android sdk. See https://github.com/actions/starter-workflows/issues/58
        run: sudo ./gradlew --full-stacktrace :data:testDebugUnitTest -Dorg.gradle.java.home=$JAVA_HOME
      - name: Upload Data Test Reports
        uses: actions/upload-artifact@v2
        if: ${{ always() }} # IMPORTANT: Upload reports regardless of status
        with:
          name: data reports
          path: data/build/reports

      - name: Domain tests
        if: always()
        # We require 'sudo' to avoid an error of the existing android sdk. See https://github.com/actions/starter-workflows/issues/58
        run:  sudo ./gradlew --full-stacktrace :domain:testDebugUnitTest -Dorg.gradle.java.home=$JAVA_HOME
      - name: Upload Domain Test Reports
        uses: actions/upload-artifact@v2
        if: ${{ always() }} # IMPORTANT: Upload reports regardless of status
        with:
          name: domain reports
          path: domain/build/reports

      - name: Testing tests
        if: always()
        # We require 'sudo' to avoid an error of the existing android sdk. See https://github.com/actions/starter-workflows/issues/58
        run:  sudo ./gradlew --full-stacktrace :testing:testDebugUnitTest -Dorg.gradle.java.home=$JAVA_HOME
      - name: Upload Testing Test Reports
        uses: actions/upload-artifact@v2
        if: ${{ always() }} # IMPORTANT: Upload reports regardless of status
        with:
          name: testing reports
          path: testing/build/reports

  run_app_module_test:
    name: Run app module Robolectric test shard
    runs-on: ubuntu-20.04
    strategy:
      fail-fast: false
      matrix:
        shard: [shard0, shard1, shard2, shard3, shard4, shard5, shard6, shard7]
    steps:
      - uses: actions/checkout@v2

      - uses: actions/cache@v2
        with:
          path: ~/.gradle/caches
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-jars-{{ checksum "build.gradle" }}

      - name: Set up JDK 1.9
        uses: actions/setup-java@v1
        with:
          java-version: 1.9

      - name: Check Java version & path
        run: |
          which java
          java -version
          echo "Java home: $JAVA_HOME"
          $JAVA_HOME/bin/java -version

      - name: Check Gradle environment
        run: ./gradlew -Dorg.gradle.java.home=$JAVA_HOME -v

      - name: Robolectric tests - App Module
        # We require 'sudo' to avoid an error of the existing android sdk. See https://github.com/actions/starter-workflows/issues/58
        run: |
          sudo ./gradlew --full-stacktrace :app:testDebugUnitTest --${{ matrix.shard }} -Dorg.gradle.java.home=$JAVA_HOME
      - name: Upload App Test Reports
        uses: actions/upload-artifact@v2
        if: ${{ always() }} # IMPORTANT: Upload reports regardless of status
        with:
          name: app reports ${{ matrix.shard }}
          path: app/build/reports

  app_tests:
    name: App Module Robolectric Tests
    needs: run_app_module_test
    if: ${{ always() }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-20.04]
    steps:
      - name: Check tests passed (for tests that ran)
        if: ${{ needs.run_app_module_test.result != 'success' }}
        run: exit 1


Oppiabot

# Contains jobs corresponding to Oppiabot.

name: Oppiabot

on:
  pull_request_target:
    branches:
      - develop

jobs:
  oppiabot:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: CLA Check
        uses: oppia/oppiabot@1.4.0
        with:
          repo-token: ${{secrets.GITHUB_TOKEN}}
        env:
          SHEETS_TOKEN: ${{ secrets.SHEETS_TOKEN }}
          SHEETS_CRED: ${{ secrets.SHEETS_CRED }}
          SPREADSHEET_ID: ${{ secrets.SPREADSHEET_ID }}


Repository Messaging

# Contains jobs corresponding to repository-wide messaging.

name: Repository Messaging

on:
  pull_request_target:
    types: [ opened ]

jobs:
  holiday_message:
    runs-on: ubuntu-latest
    # NOTE TO DEVELOPERS: Update the body text and set this to 'true' to enable automatic messaging on all new PRs.
    if: false
    steps:
      - name: Add holiday comment
        uses: peter-evans/create-or-update-comment@v1
        with:
          issue-number: ${{ github.event.pull_request.number }}
          body: |
            Thanks for submitting this pull request! Some main reviewers
            have taken time off for the next few weeks, so it may take a
            little while before we can look at this PR. We appreciate your
            patience while some of our team members recharge. We'll be fully
            returning on 10 January 2022.


Static Checks

# Contains jobs corresponding to static checks (such as syntax correctness & prohibited patterns).

name: Static Checks

on:
  workflow_dispatch:
  pull_request:
  push:
    branches:
      - develop

jobs:
  check_codeowners:
    name: Check CODEOWNERS & Repository files
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2

      - uses: mszostok/codeowners-validator@v0.5.1
        with:
          checks: "duppatterns,files,syntax"
          experimental_checks: "notowned"

  check_base_branch:
    name: Check base branch
    runs-on: ubuntu-20.04
    if: github.event_name == 'pull_request'
    steps:
      - name: "Branch is not based on develop or release branch"
        if: ${{ github.base_ref != 'develop' && !startsWith(github.base_ref, 'release-') }}
        run: |
          echo "Current base branch: $BASE_BRANCH"
          echo "Note: this check is expected to fail for chained PRs so that they can't accidentally be merged. PRs should only ever be merged directly into develop or a release branch."
          exit 1
        env:
          BASE_BRANCH: ${{ github.base_ref }}

      - name: "Branch verified as based on develop/release branch"
        if: ${{ github.base_ref == 'develop' || startsWith(github.base_ref, 'release-') }}
        run: |
          echo "Branch is correctly branched off of valid base branch to merge PRs into: $BASE_BRANCH"
        env:
          BASE_BRANCH: ${{ github.base_ref }}

  linters:
    name: Lint Tests
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2

      - name: Create oppia android tools directory
        run: mkdir -p $HOME/oppia-android-tools

      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11

      - name: Download Checkstyle
        run: |
          cd $HOME/oppia-android-tools
          bash /home/runner/work/oppia-android/oppia-android/scripts/checkstyle_download.sh

      - name: Download Ktlint
        run: |
          cd $HOME/oppia-android-tools
          bash /home/runner/work/oppia-android/oppia-android/scripts/ktlint_download.sh

      - name: Download Buf
        run: |
          cd $HOME/oppia-android-tools
          bash /home/runner/work/oppia-android/oppia-android/scripts/buf_download.sh

      - name: Download Buildifier
        run: |
          cd $HOME/oppia-android-tools
          bash /home/runner/work/oppia-android/oppia-android/scripts/buildifier_download.sh

      - name: Java lint check
        run: |
          bash /home/runner/work/oppia-android/oppia-android/scripts/checkstyle_lint_check.sh $HOME

      - name: Kotlin lint check
        run: |
          bash /home/runner/work/oppia-android/oppia-android/scripts/ktlint_lint_check.sh $HOME

      - name: Protobuf lint check
        run: |
          bash /home/runner/work/oppia-android/oppia-android/scripts/buf_lint_check.sh $HOME

      - name: Bazel lint check
        run: |
          bash /home/runner/work/oppia-android/oppia-android/scripts/buildifier_lint_check.sh $HOME

  script_checks:
    name: Script Checks
    runs-on: ubuntu-20.04
    env:
      CACHE_DIRECTORY: ~/.bazel_cache
    steps:
      - uses: actions/checkout@v2

      - name: Set up Bazel
        uses: abhinavsingh/setup-bazel@v3
        with:
          version: 6.5.0

      - uses: actions/cache@v2
        id: scripts_cache
        with:
          path: ${{ env.CACHE_DIRECTORY }}
          key: ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-scripts-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-scripts-
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-

      # This check is needed to ensure that Bazel's unbounded cache growth doesn't result in a
      # situation where the cache never updates (e.g. due to exceeding GitHub's cache size limit)
      # thereby only ever using the last successful cache version. This solution will result in a
      # few slower CI actions around the time cache is detected to be too large, but it should
      # incrementally improve thereafter.
      - name: Ensure cache size
        env:
          BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
        run: |
          # See https://stackoverflow.com/a/27485157 for reference.
          EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
          CACHE_SIZE_MB=$(du -smc $EXPANDED_BAZEL_CACHE_PATH | grep total | cut -f1)
          echo "Total size of Bazel cache (rounded up to MBs): $CACHE_SIZE_MB"
          # Use a 4.5GB threshold since actions/cache compresses the results, and Bazel caches seem
          # to only increase by a few hundred megabytes across changes for unrelated branches. This
          # is also a reasonable upper-bound (local tests as of 2021-03-31 suggest that a full build
          # of the codebase (e.g. //...) from scratch only requires a ~2.1GB uncompressed/~900MB
          # compressed cache).
          if [[ "$CACHE_SIZE_MB" -gt 4500 ]]; then
            echo "Cache exceeds cut-off; resetting it (will result in a slow build)"
            rm -rf $EXPANDED_BAZEL_CACHE_PATH
          fi

      - name: Configure Bazel to use a local cache
        env:
          BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
        run: |
          EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
          echo "Using $EXPANDED_BAZEL_CACHE_PATH as Bazel's cache path"
          echo "build --disk_cache=$EXPANDED_BAZEL_CACHE_PATH" >> $HOME/.bazelrc
        shell: bash

      - name: Regex Patterns Validation Check
        if: always()
        run: |
          bazel run //scripts:regex_pattern_validation_check -- $(pwd)

      - name: XML Syntax Validation Check
        if: always()
        run: |
          bazel run //scripts:xml_syntax_check -- $(pwd)

      - name: Testfile Presence Check
        if: always()
        run: |
          bazel run //scripts:test_file_check -- $(pwd)

      - name: Accessibility label Check
        if: always()
        run: |
          bazel run //scripts:accessibility_label_check -- $(pwd) scripts/assets/accessibility_label_exemptions.pb app/src/main/AndroidManifest.xml

      - name: KDoc Validation Check
        if: always()
        run: |
          bazel run //scripts:kdoc_validity_check -- $(pwd) scripts/assets/kdoc_validity_exemptions.pb

      - name: Todo Check
        if: always()
        env:
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          bazel run //scripts:todo_open_check -- $(pwd) scripts/assets/todo_open_exemptions.pb

      - name: String Resource Validation Check
        if: always()
        run: |
          bazel run //scripts:string_resource_validation_check -- $(pwd)

  # Note that caching is intentionally not enabled for this check since licenses should always be
  # verified without any potential influence from earlier builds (i.e. always from a clean build to
  # ensure the results exactly match the current state of the repository).
  third_party_dependencies_check:
    name: Maven Dependencies Checks
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2

      - name: Set up Bazel
        uses: abhinavsingh/setup-bazel@v3
        with:
          version: 6.5.0

      - name: Maven Repin Check
        if: always()
        run: |
          REPIN=1 bazel run @unpinned_maven//:pin

      - name: Maven Dependencies Update Check
        if: always()
        run: |
          bazel run //scripts:maven_dependencies_list_check -- $(pwd) third_party/maven_install.json scripts/assets/maven_dependencies.pb

      - name: License Texts Check
        if: always()
        run: |
          bazel run //scripts:license_texts_check -- $(pwd)/app/src/main/res/values/third_party_dependencies.xml


Stats Checks & Reports

# Contains jobs corresponding to stats, including build stats due to changes in a PR.

name: Stats Checks & Reports

on:
  workflow_dispatch:
  schedule:
    - cron: "30 02 * * *"

permissions:
  pull-requests: write

jobs:
  find_open_pull_requests:
    name: Find open PRs
    runs-on: ubuntu-20.04
    outputs:
      matrix: ${{ steps.compute-pull-request-matrix.outputs.matrix }}
    env:
      GH_TOKEN: ${{ github.token }}
    steps:
      - uses: actions/checkout@v4

      - name: Compute PR matrix
        id: compute-pull-request-matrix
        # Remove spaces to ensure the matrix output is on one line. Reference:
        # https://stackoverflow.com/a/3232433.
        run: |
          CURRENT_OPEN_PR_INFO="$(gh pr list --json number,baseRefName,headRefName,headRepository,headRepositoryOwner | tr -d '[:space:]')"
          echo "matrix={\"prInfo\": $CURRENT_OPEN_PR_INFO}" >> "$GITHUB_OUTPUT"

  build_stats:
    name: Build Stats
    needs: find_open_pull_requests
    runs-on: ubuntu-20.04
    # Reduce parallelization due to high build times, and allow individual PRs to fail.
    strategy:
      fail-fast: false
      max-parallel: 5
      matrix: ${{ fromJson(needs.find_open_pull_requests.outputs.matrix) }}
    env:
      ENABLE_CACHING: false
      CACHE_DIRECTORY: ~/.bazel_cache
    steps:
      - name: Compute PR head owner/repo reference
        env:
          PR_HEAD_REPO: ${{ matrix.prInfo.headRepository.name }}
          PR_HEAD_REPO_OWNER: ${{ matrix.prInfo.headRepositoryOwner.login }}
        run: |
          echo "PR_HEAD=$PR_HEAD_REPO_OWNER/$PR_HEAD_REPO" >> "$GITHUB_ENV"
      - name: Print PR information for this run
        env:
          PR_BASE_REF_NAME: ${{ matrix.prInfo.baseRefName }}
          PR_HEAD_REF_NAME: ${{ matrix.prInfo.headRefName }}
          PR_NUMBER: ${{ matrix.prInfo.number }}
        run: |
          echo "PR $PR_NUMBER is merging into $PR_BASE_REF_NAME from https://github.com/$PR_HEAD branch $PR_HEAD_REF_NAME."

      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11

      - name: Set up Bazel
        uses: abhinavsingh/setup-bazel@v3
        with:
          version: 6.5.0

      # For reference on this & the later cache actions, see:
      # https://github.com/actions/cache/issues/239#issuecomment-606950711 &
      # https://github.com/actions/cache/issues/109#issuecomment-558771281. Note that these work
      # with Bazel since Bazel can share the most recent cache from an unrelated build and still
      # benefit from incremental build performance (assuming that actions/cache aggressively removes
      # older caches due to the 5GB cache limit size & Bazel's large cache size).
      - uses: actions/cache@v2
        id: cache
        with:
          path: ${{ env.CACHE_DIRECTORY }}
          key: ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-binary-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-binary-
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-

      # This check is needed to ensure that Bazel's unbounded cache growth doesn't result in a
      # situation where the cache never updates (e.g. due to exceeding GitHub's cache size limit)
      # thereby only ever using the last successful cache version. This solution will result in a
      # few slower CI actions around the time cache is detected to be too large, but it should
      # incrementally improve thereafter.
      - name: Ensure cache size
        env:
          BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
        run: |
          # See https://stackoverflow.com/a/27485157 for reference.
          EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
          CACHE_SIZE_MB=$(du -smc $EXPANDED_BAZEL_CACHE_PATH | grep total | cut -f1)
          echo "Total size of Bazel cache (rounded up to MBs): $CACHE_SIZE_MB"
          # Use a 4.5GB threshold since actions/cache compresses the results, and Bazel caches seem
          # to only increase by a few hundred megabytes across changes for unrelated branches. This
          # is also a reasonable upper-bound (local tests as of 2021-03-31 suggest that a full build
          # of the codebase (e.g. //...) from scratch only requires a ~2.1GB uncompressed/~900MB
          # compressed cache).
          if [[ "$CACHE_SIZE_MB" -gt 4500 ]]; then
            echo "Cache exceeds cut-off; resetting it (will result in a slow build)"
            rm -rf $EXPANDED_BAZEL_CACHE_PATH
          fi

      - name: Configure Bazel to use a local cache
        env:
          BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
        run: |
          EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
          echo "Using $EXPANDED_BAZEL_CACHE_PATH as Bazel's cache path"
          echo "build --disk_cache=$EXPANDED_BAZEL_CACHE_PATH" >> $HOME/.bazelrc
        shell: bash

      # This checks out the actual true develop branch separately to ensure that the stats check is
      # run from the latest develop rather than the base branch (which might be different for
      # chained PRs).
      - name: Check out develop repository
        uses: actions/checkout@v4
        with:
          path: develop

      - name: Set up build environment
        uses: ./develop/.github/actions/set-up-android-bazel-build-environment

      - name: Check Bazel environment
        run: |
          cd develop
          bazel info

      - name: Check out base repository and branch
        env:
          PR_BASE_REF_NAME: ${{ matrix.prInfo.baseRefName }}
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ env.PR_BASE_REF_NAME }}
          path: base

      - name: Check out head repository and branch
        env:
          PR_HEAD_REF_NAME: ${{ matrix.prInfo.headRefName }}
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          repository: ${{ env.PR_HEAD }}
          ref: ${{ env.PR_HEAD_REF_NAME }}
          path: head

      # Note that Bazel is shutdown between builds since multiple Bazel servers will otherwise end
      # up being active (due to multiple repositories being used) and this can quickly overwhelm CI
      # worker resources.
      - name: Build Oppia dev, alpha, beta, and GA (feature branch)
        run: |
          cd head
          git log -n 1
          bazel build -- //:oppia_dev //:oppia_alpha //:oppia_beta //:oppia_ga
          cp bazel-bin/oppia_dev.aab ../develop/oppia_dev_with_changes.aab
          cp bazel-bin/oppia_alpha.aab ../develop/oppia_alpha_with_changes.aab
          cp bazel-bin/oppia_beta.aab ../develop/oppia_beta_with_changes.aab
          cp bazel-bin/oppia_ga.aab ../develop/oppia_ga_with_changes.aab
          bazel shutdown

      - name: Build Oppia dev, alpha, beta, and GA (base branch)
        run: |
          cd base
          git log -n 1
          bazel build -- //:oppia_dev //:oppia_alpha //:oppia_beta //:oppia_ga
          cp bazel-bin/oppia_dev.aab ../develop/oppia_dev_without_changes.aab
          cp bazel-bin/oppia_alpha.aab ../develop/oppia_alpha_without_changes.aab
          cp bazel-bin/oppia_beta.aab ../develop/oppia_beta_without_changes.aab
          cp bazel-bin/oppia_ga.aab ../develop/oppia_ga_without_changes.aab
          bazel shutdown

      - name: Run stats analysis tool (develop branch)
        run: |
          cd develop
          git log -n 1
          bazel run //scripts:compute_aab_differences -- \
            $(pwd)/brief_build_summary.log $(pwd)/full_build_summary.log \
            dev $(pwd)/oppia_dev_without_changes.aab $(pwd)/oppia_dev_with_changes.aab \
            alpha $(pwd)/oppia_alpha_without_changes.aab $(pwd)/oppia_alpha_with_changes.aab \
            beta $(pwd)/oppia_beta_without_changes.aab $(pwd)/oppia_beta_with_changes.aab \
            ga $(pwd)/oppia_ga_without_changes.aab $(pwd)/oppia_ga_with_changes.aab

      # Reference: https://github.com/peter-evans/create-or-update-comment#setting-the-comment-body-from-a-file.
      # Also, for multi-line env values, see: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings.
      - name: Extract reports for uploading & commenting
        env:
          PR_NUMBER: ${{ matrix.prInfo.number }}
        id: compute-comment-body
        run: |
          {
            echo 'comment_body<<EOF'
            cat $GITHUB_WORKSPACE/develop/brief_build_summary.log
            echo EOF
          } >> "$GITHUB_OUTPUT"
          FULL_BUILD_SUMMARY_FILE_NAME="full_build_summary_pr_$PR_NUMBER.log"
          FULL_BUILD_SUMMARY_FILE_PATH="$GITHUB_WORKSPACE/develop/$FULL_BUILD_SUMMARY_FILE_NAME"
          echo "FULL_BUILD_SUMMARY_FILE_NAME=$FULL_BUILD_SUMMARY_FILE_NAME" >> "$GITHUB_ENV"
          echo "FULL_BUILD_SUMMARY_FILE_PATH=$FULL_BUILD_SUMMARY_FILE_PATH" >> "$GITHUB_ENV"
          cp "$GITHUB_WORKSPACE/develop/full_build_summary.log" "$FULL_BUILD_SUMMARY_FILE_PATH"

      - name: Add build stats summary comment
        env:
          PR_NUMBER: ${{ matrix.prInfo.number }}
        uses: peter-evans/create-or-update-comment@v1
        with:
          issue-number: ${{ env.PR_NUMBER }}
          body: ${{ steps.compute-comment-body.outputs.comment_body }}

      - uses: actions/upload-artifact@v2
        with:
          name: ${{ env.FULL_BUILD_SUMMARY_FILE_NAME }}
          path: ${{ env.FULL_BUILD_SUMMARY_FILE_PATH }}


Unit Tests (Robolectric - Bazel)

# Contains jobs corresponding to unit tests. Integration (e.g. emulator or screenshot testing) or End-to-End tests should not be triggered here.

name: Unit Tests (Robolectric - Bazel)

# Controls when the action will run. Triggers the workflow on pull request
# events or push events in the develop branch.
on:
  workflow_dispatch:
  pull_request:
  push:
    branches:
      # Push events on develop branch
      - develop

jobs:
  bazel_compute_affected_targets:
    name: Compute affected tests
    runs-on: ubuntu-20.04
    outputs:
      matrix: ${{ steps.compute-test-matrix.outputs.matrix }}
      can_skip_tests: ${{ steps.compute-test-matrix.outputs.can_skip_tests }}
    env:
      CACHE_DIRECTORY: ~/.bazel_cache
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0

      - name: Set up Bazel
        uses: abhinavsingh/setup-bazel@v3
        with:
          version: 6.5.0

      - uses: actions/cache@v2
        id: scripts_cache
        with:
          path: ${{ env.CACHE_DIRECTORY }}
          key: ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-scripts-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-scripts-
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-

      # This check is needed to ensure that Bazel's unbounded cache growth doesn't result in a
      # situation where the cache never updates (e.g. due to exceeding GitHub's cache size limit)
      # thereby only ever using the last successful cache version. This solution will result in a
      # few slower CI actions around the time cache is detected to be too large, but it should
      # incrementally improve thereafter.
      - name: Ensure cache size
        env:
          BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
        run: |
          # See https://stackoverflow.com/a/27485157 for reference.
          EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
          CACHE_SIZE_MB=$(du -smc $EXPANDED_BAZEL_CACHE_PATH | grep total | cut -f1)
          echo "Total size of Bazel cache (rounded up to MBs): $CACHE_SIZE_MB"
          # Use a 4.5GB threshold since actions/cache compresses the results, and Bazel caches seem
          # to only increase by a few hundred megabytes across changes for unrelated branches. This
          # is also a reasonable upper-bound (local tests as of 2021-03-31 suggest that a full build
          # of the codebase (e.g. //...) from scratch only requires a ~2.1GB uncompressed/~900MB
          # compressed cache).
          if [[ "$CACHE_SIZE_MB" -gt 4500 ]]; then
            echo "Cache exceeds cut-off; resetting it (will result in a slow build)"
            rm -rf $EXPANDED_BAZEL_CACHE_PATH
          fi

      - name: Configure Bazel to use a local cache
        env:
          BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
        run: |
          EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
          echo "Using $EXPANDED_BAZEL_CACHE_PATH as Bazel's cache path"
          echo "build --disk_cache=$EXPANDED_BAZEL_CACHE_PATH" >> $HOME/.bazelrc
        shell: bash

      - name: Compute test matrix
        id: compute-test-matrix
        env:
          compute_all_targets: ${{ contains(github.event.pull_request.title, '[RunAllTests]') }}
          # See: https://docs.github.com/en/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request. Defer to origin/develop outside a PR (such as develop's main CI runs).
          base_commit_hash: ${{ github.event.pull_request.base.sha || 'origin/develop' }}
        # https://unix.stackexchange.com/a/338124 for reference on creating a JSON-friendly
        # comma-separated list of test targets for the matrix.
        run: |
          bazel run //scripts:compute_affected_tests -- $(pwd) $(pwd)/affected_targets.log $base_commit_hash compute_all_tests=$compute_all_targets
          TEST_BUCKET_LIST=$(cat ./affected_targets.log | sed 's/^\|$/"/g' | paste -sd, -)
          echo "Affected tests (note that this might be all tests if configured to run all or on the develop branch): $TEST_BUCKET_LIST"
          echo "::set-output name=matrix::{\"affected-tests-bucket-base64-encoded-shard\":[$TEST_BUCKET_LIST]}"
          if [[ ! -z "$TEST_BUCKET_LIST" ]]; then
            echo "::set-output name=can_skip_tests::false"
          else
            echo "::set-output name=can_skip_tests::true"
            echo "No tests are detected as affected by this change. If this is wrong, you can add '[RunAllTests]' to the PR title to force a run."
          fi

  bazel_run_test:
    name: Run Bazel Test
    needs: bazel_compute_affected_targets
    if: ${{ needs.bazel_compute_affected_targets.outputs.can_skip_tests != 'true' }}
    runs-on: ubuntu-20.04
    strategy:
      fail-fast: false
      max-parallel: 10
      matrix: ${{ fromJson(needs.bazel_compute_affected_targets.outputs.matrix) }}
    env:
      ENABLE_CACHING: false
      CACHE_DIRECTORY: ~/.bazel_cache
    steps:
      - uses: actions/checkout@v2

      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11

      - name: Set up Bazel
        uses: abhinavsingh/setup-bazel@v3
        with:
          version: 6.5.0

      - uses: actions/cache@v2
        id: scripts_cache
        with:
          path: ${{ env.CACHE_DIRECTORY }}
          key: ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-scripts-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-scripts-
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-

      - name: Set up build environment
        uses: ./.github/actions/set-up-android-bazel-build-environment

      - name: Configure Bazel to use a local cache (for scripts)
        env:
          BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
        run: |
          EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
          echo "Using $EXPANDED_BAZEL_CACHE_PATH as Bazel's cache path"
          echo "build --disk_cache=$EXPANDED_BAZEL_CACHE_PATH" >> $HOME/.bazelrc
        shell: bash

      - name: Extract test caching bucket & targets
        env:
          AFFECTED_TESTS_BUCKET_BASE64_ENCODED_SHARD: ${{ matrix.affected-tests-bucket-base64-encoded-shard }}
        run: |
          # See https://stackoverflow.com/a/29903172 for cut logic. This is needed to remove the
          # user-friendly shard prefix from the matrix value.
          AFFECTED_TESTS_BUCKET_BASE64=$(echo "$AFFECTED_TESTS_BUCKET_BASE64_ENCODED_SHARD" | cut -d ";" -f 2)
          bazel run //scripts:retrieve_affected_tests -- $AFFECTED_TESTS_BUCKET_BASE64 $(pwd)/test_bucket_name $(pwd)/bazel_test_targets
          TEST_CATEGORY=$(cat ./test_bucket_name)
          BAZEL_TEST_TARGETS=$(cat ./bazel_test_targets)
          echo "Test category: $TEST_CATEGORY"
          echo "Bazel test targets: $BAZEL_TEST_TARGETS"
          echo "TEST_CACHING_BUCKET=$TEST_CATEGORY" >> $GITHUB_ENV
          echo "BAZEL_TEST_TARGETS=$BAZEL_TEST_TARGETS" >> $GITHUB_ENV

      # For reference on this & the later cache actions, see:
      # https://github.com/actions/cache/issues/239#issuecomment-606950711 &
      # https://github.com/actions/cache/issues/109#issuecomment-558771281. Note that these work
      # with Bazel since Bazel can share the most recent cache from an unrelated build and still
      # benefit from incremental build performance (assuming that actions/cache aggressively removes
      # older caches due to the 5GB cache limit size & Bazel's large cache size).
      - uses: actions/cache@v2
        id: test_cache
        with:
          path: ${{ env.CACHE_DIRECTORY }}
          key: ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-tests-${{ env.TEST_CACHING_BUCKET }}-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-tests-${{ env.TEST_CACHING_BUCKET }}-
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-tests-
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-binary-
            ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-

      # This check is needed to ensure that Bazel's unbounded cache growth doesn't result in a
      # situation where the cache never updates (e.g. due to exceeding GitHub's cache size limit)
      # thereby only ever using the last successful cache version. This solution will result in a
      # few slower CI actions around the time cache is detected to be too large, but it should
      # incrementally improve thereafter.
      - name: Ensure cache size
        env:
          BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
        run: |
          # See https://stackoverflow.com/a/27485157 for reference.
          EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
          CACHE_SIZE_MB=$(du -smc $EXPANDED_BAZEL_CACHE_PATH | grep total | cut -f1)
          echo "Total size of Bazel cache (rounded up to MBs): $CACHE_SIZE_MB"
          # Use a 4.5GB threshold since actions/cache compresses the results, and Bazel caches seem
          # to only increase by a few hundred megabytes across changes for unrelated branches. This
          # is also a reasonable upper-bound (local tests as of 2021-03-31 suggest that a full build
          # of the codebase (e.g. //...) from scratch only requires a ~2.1GB uncompressed/~900MB
          # compressed cache).
          if [[ "$CACHE_SIZE_MB" -gt 4500 ]]; then
            echo "Cache exceeds cut-off; resetting it (will result in a slow build)"
            rm -rf $EXPANDED_BAZEL_CACHE_PATH
          fi

      - name: Configure Bazel to use a local cache (for tests)
        env:
          BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
        run: |
          EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
          echo "Using $EXPANDED_BAZEL_CACHE_PATH as Bazel's cache path"
          echo "build --disk_cache=$EXPANDED_BAZEL_CACHE_PATH" >> $HOME/.bazelrc
        shell: bash

      # See explanation in bazel_build_app for how this is installed.
      - name: Install git-secret (non-fork only)
        if: ${{ env.ENABLE_CACHING == 'true' && ((github.ref == 'refs/heads/develop' && github.event_name == 'push') || (github.event.pull_request.head.repo.full_name == 'oppia/oppia-android')) }}
        shell: bash
        run: |
          cd $HOME
          mkdir -p $HOME/gitsecret
          git clone https://github.com/sobolevn/git-secret.git git-secret
          cd git-secret && make build
          PREFIX="$HOME/gitsecret" make install
          echo "$HOME/gitsecret" >> $GITHUB_PATH
          echo "$HOME/gitsecret/bin" >> $GITHUB_PATH

      - name: Decrypt secrets (non-fork only)
        if: ${{ env.ENABLE_CACHING == 'true' && ((github.ref == 'refs/heads/develop' && github.event_name == 'push') || (github.event.pull_request.head.repo.full_name == 'oppia/oppia-android')) }}
        env:
          GIT_SECRET_GPG_PRIVATE_KEY: ${{ secrets.GIT_SECRET_GPG_PRIVATE_KEY }}
        run: |
          cd $HOME
          # NOTE TO DEVELOPERS: Make sure to never print this key directly to stdout!
          echo $GIT_SECRET_GPG_PRIVATE_KEY | base64 --decode > ./git_secret_private_key.gpg
          gpg --import ./git_secret_private_key.gpg
          cd $GITHUB_WORKSPACE
          git secret reveal

      # See https://www.cyberciti.biz/faq/unix-for-loop-1-to-10/ for for-loop reference.
      - name: Build Oppia Tests (with caching, non-fork only)
        if: ${{ env.ENABLE_CACHING == 'true' && ((github.ref == 'refs/heads/develop' && github.event_name == 'push') || (github.event.pull_request.head.repo.full_name == 'oppia/oppia-android')) }}
        env:
          BAZEL_REMOTE_CACHE_URL: ${{ secrets.BAZEL_REMOTE_CACHE_URL }}
          BAZEL_TEST_TARGETS: ${{ env.BAZEL_TEST_TARGETS }}
        run: |
          # Attempt to build 5 times in case there are flaky builds.
          # TODO(#3759): Remove this once there are no longer app test build failures.
          i=0
          # Disable exit-on-first-failure.
          set +e
          while [ $i -ne 5 ]; do
            i=$(( $i+1 ))
            echo "Attempt $i/5 to build test targets"
            bazel build --keep_going --remote_http_cache=$BAZEL_REMOTE_CACHE_URL --google_credentials=./config/oppia-dev-workflow-remote-cache-credentials.json -- $BAZEL_TEST_TARGETS
          done
          # Capture the error code of the final command run (which should be a success if there isn't a real build failure).
          last_error_code=$?
          # Reenable exit-on-first-failure.
          set -e
          # Exit only if the most recent exit was a failure (by using a subshell).
          (exit $last_error_code)

      - name: Build Oppia Tests (without caching, or on a fork)
        if: ${{ env.ENABLE_CACHING == 'false' || ((github.ref != 'refs/heads/develop' || github.event_name != 'push') && (github.event.pull_request.head.repo.full_name != 'oppia/oppia-android')) }}
        env:
          BAZEL_TEST_TARGETS: ${{ env.BAZEL_TEST_TARGETS }}
        run: |
          # Attempt to build 5 times in case there are flaky builds.
          # TODO(#3759): Remove this once there are no longer app test build failures.
          i=0
          # Disable exit-on-first-failure.
          set +e
          while [ $i -ne 5 ]; do
            i=$(( $i+1 ))
            echo "Attempt $i/5 to build test targets"
            bazel build --keep_going -- $BAZEL_TEST_TARGETS
          done
          # Capture the error code of the final command run (which should be a success if there isn't a real build failure).
          last_error_code=$?
          # Reenable exit-on-first-failure.
          set -e
          # Exit only if the most recent exit was a failure (by using a subshell).
          (exit $last_error_code)
      - name: Run Oppia Tests (with caching, non-fork only)
        if: ${{ env.ENABLE_CACHING == 'true' && ((github.ref == 'refs/heads/develop' && github.event_name == 'push') || (github.event.pull_request.head.repo.full_name == 'oppia/oppia-android')) }}
        env:
          BAZEL_REMOTE_CACHE_URL: ${{ secrets.BAZEL_REMOTE_CACHE_URL }}
          BAZEL_TEST_TARGETS: ${{ env.BAZEL_TEST_TARGETS }}
        run: |
          # Attempt to build 5 times in case there are flaky builds.
          # TODO(#3970): Remove this once there are no longer app test build failures.
          i=0
          # Disable exit-on-first-failure.
          set +e
          while [ $i -ne 5 ]; do
            i=$(( $i+1 ))
            echo "Attempt $i/5 to run test targets"
            bazel test --keep_going --remote_http_cache=$BAZEL_REMOTE_CACHE_URL --google_credentials=./config/oppia-dev-workflow-remote-cache-credentials.json -- $BAZEL_TEST_TARGETS
          done
          # Capture the error code of the final command run (which should be a success if there isn't a real build failure).
          last_error_code=$?
          # Reenable exit-on-first-failure.
          set -e
          # Exit only if the most recent exit was a failure (by using a subshell).
          (exit $last_error_code)
      - name: Run Oppia Tests (without caching, or on a fork)
        if: ${{ env.ENABLE_CACHING == 'false' || ((github.ref != 'refs/heads/develop' || github.event_name != 'push') && (github.event.pull_request.head.repo.full_name != 'oppia/oppia-android')) }}
        env:
          BAZEL_TEST_TARGETS: ${{ env.BAZEL_TEST_TARGETS }}
        run: |
          # Attempt to build 5 times in case there are flaky builds.
          # TODO(#3970): Remove this once there are no longer app test build failures.
          i=0
          # Disable exit-on-first-failure.
          set +e
          while [ $i -ne 5 ]; do
            i=$(( $i+1 ))
            echo "Attempt $i/5 to run test targets"
            bazel test --keep_going -- $BAZEL_TEST_TARGETS
          done
          # Capture the error code of the final command run (which should be a success if there isn't a real build failure).
          last_error_code=$?
          # Reenable exit-on-first-failure.
          set -e
          # Exit only if the most recent exit was a failure (by using a subshell).
          (exit $last_error_code)

  # Reference: https://github.community/t/127354/7.
  check_test_results:
      name: Check Bazel Test Results
      needs: [bazel_compute_affected_targets, bazel_run_test]
      if: ${{ always() }}
      runs-on: ubuntu-20.04
      steps:
        # This step will be skipped if there are no tests to run, so the overall job should pass.
        - name: Check tests passed (for tests that ran)
          if: ${{ needs.bazel_compute_affected_targets.outputs.can_skip_tests != 'true' && needs.bazel_run_test.result != 'success' }}
          run: exit 1


Deploy to Wiki

name: Deploy to Wiki
on:
  push:
    branches:
      - develop
    paths:
      - 'wiki/**'
  # Triggers this workflow when the wiki is changed
  # (see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#gollum).
  gollum:

jobs:
  wiki-deploy:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-20.04]
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Add remote
        run: |
          git filter-branch --subdirectory-filter wiki/ -- --all
          git remote set-url origin https://github.com/oppia/oppia-android.wiki.git
          git checkout -b master
          git push origin master --force


Automatic Workflow Canceller

name: Automatic Workflow Canceller

# This workflow should be triggered in one of three situations:
# 1. Manual workflow dispatch via https://github.com/oppia/oppia-android/actions.
# 2. Upon creation of a PR & updates to that PR.
#
# Note that the action being used here automatically accounts for the current branch & the commit
# hash of the tip of the branch to ensure it doesn't cancel previous workflows that aren't related
# to the branch being evaluated.
on:
  workflow_dispatch:
  pull_request:

jobs:
  cancel:
    name: Cancel Previous Runs
    runs-on: ubuntu-20.04
    steps:
      # See https://github.com/styfle/cancel-workflow-action for details on this workflow.
      - uses: styfle/cancel-workflow-action@0.6.0
        with:
          workflow_id: main.yml
          access_token: ${{ github.token }}


Build

name: Build

on:
  workflow_call:

defaults:
  run:
    shell: bash -l {0}

env:
  flavour: oss
  config: debug
  api-level: 28

jobs:

  Build:
    name: Build
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      statuses: write
      pull-requests: write
      contents: write

    steps:
      - uses: actions/checkout@v4

      - name: Fetch Dependabot metadata
        id: dependabot-metadata
        uses: dependabot/fetch-metadata@v2.1.0
        if: ${{ github.event_name == 'pull_request' && github.actor == 'dependabot[bot]' }}
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

      - name: Setup NPM
        run: |
          nvm install
          npm install --global gulp-cli
          nvm use
          npm ci

      - name: Danger
        uses: docker://ghcr.io/danger/danger-kotlin:1.2.0
        if: ${{ github.event_name == 'pull_request' }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'adopt'
          cache: gradle

      - name: Validate Gradle Wrapper
        uses: gradle/actions/wrapper-validation@v3

      - name: Build
        run: |
          ./gradlew assemble${{ env.flavour }}${{ env.config }} -PdisablePreDex

      - name: Tokens check
        if: ${{ github.event_name != 'pull_request' || github.actor != 'dependabot[bot]' || github.event_name == 'pull_request' && github.actor == 'dependabot[bot]' && !contains(steps.dependabot-metadata.outputs.dependency-names, 'bpk-') }}
        run: |
          ./gradlew generateTokens -PdisablePreDex
          ./scripts/check-no-changes.sh

      - name: Token update
        id: tokenUpdate
        if: ${{ github.event_name == 'pull_request' && github.actor == 'dependabot[bot]' && contains(steps.dependabot-metadata.outputs.dependency-names, 'bpk-')}}
        run: |
          ./gradlew generateTokens -PdisablePreDex
          changedFiles=`git status --porcelain` && echo "CHANGED_FILES=${changedFiles//$'\n'/'%0A'}" >> $GITHUB_OUTPUT

      - name: Token commit
        if: ${{ github.event_name == 'pull_request' && github.actor == 'dependabot[bot]' && steps.tokenUpdate.outputs.CHANGED_FILES != '' && contains(steps.dependabot-metadata.outputs.dependency-names, 'bpk-') }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          git config --local user.email "github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git remote set-url origin https://x-access-token:$GITHUB_TOKEN@github.com/${{ github.repository }}
          git fetch origin $GITHUB_HEAD_REF
          git checkout $GITHUB_HEAD_REF
          git add .
          git diff-index --quiet HEAD || git commit -m "Update tokens"
          git push

      - name: Detekt check
        run: ./gradlew detekt -PdisablePreDex

      - name: Lint check
        run: ./gradlew lint${{ env.flavour }}${{ env.config }} -PdisablePreDex

      - name: Unit Tests
        run: ./gradlew test${{ env.flavour }}${{ env.config }}UnitTest -PdisablePreDex

  Android:
    name: Android tests
    runs-on: ubuntu-latest
    timeout-minutes: 30

    steps:

      - name: Checkout
        uses: actions/checkout@v4

      - name: Enable KVM group perms
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'adopt'
          cache: gradle

      - name: AVD cache
        uses: actions/cache@v4
        id: avd-cache
        with:
          path: |
            ~/.android/avd/*
            ~/.android/adb*
          key: avd-${{ env.api-level }}

      - name: create AVD and generate snapshot for caching
        if: steps.avd-cache.outputs.cache-hit != 'true'
        uses: reactivecircus/android-emulator-runner@v2
        with:
          profile: Nexus 4
          sdcard-path-or-size: 512M
          api-level: ${{ env.api-level }}
          force-avd-creation: false
          emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
          avd-name: avd-x86-default-28
          disable-animations: false
          script: echo "Generated AVD snapshot for caching."

      - name: Android Tests
        uses: reactivecircus/android-emulator-runner@v2
        with:
          profile: Nexus 4
          sdcard-path-or-size: 512M
          api-level: ${{ env.api-level }}
          force-avd-creation: false
          avd-name: avd-x86-default-28
          emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
          disable-animations: true
          script: |
            # uninstall first to avoid signature error. see https://github.com/ReactiveCircus/android-emulator-runner/issues/319
            adb uninstall net.skyscanner.backpack || :
            adb uninstall net.skyscanner.backpack.test || :
            adb uninstall net.skyscanner.backpack.compose.test || :
            adb uninstall net.skyscanner.backpack.common.test || :

            ./gradlew :Backpack:connected${{ env.config }}AndroidTest :backpack-compose:connected${{ env.config }}AndroidTest :backpack-common:connected${{ env.config }}AndroidTest

  Screenshots:
    name: Screenshots tests
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      contents: write
    timeout-minutes: 20
    steps:

      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'adopt'
          cache: gradle

      - name: Screenshot Tests
        id: screenshotTests
        run: |
          set -e
          ./scripts/record_screenshot_tests.sh

          changedFiles=`git status --porcelain` && echo "CHANGED_FILES=${changedFiles//$'\n'/'%0A'}" >> $GITHUB_OUTPUT

      - name: Check snapshot changes
        id: checkSnapshotChanges
        if: ${{ github.event_name != 'pull_request' }}
        run: ./scripts/check-no-changes.sh

      - name: Commit changes
        if: ${{ github.event_name == 'pull_request' && steps.screenshotTests.outputs.CHANGED_FILES != '' }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          git config --local user.email "github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git remote set-url origin https://x-access-token:$GITHUB_TOKEN@github.com/${{ github.repository }}
          git fetch origin $GITHUB_HEAD_REF
          git checkout $GITHUB_HEAD_REF
          git add app/screenshots/*
          git diff-index --quiet HEAD || git commit -m "Updated snapshots"
          git push


label-check

name: label-check

on:
  pull_request_target:
    types: [opened, labeled, unlabeled, synchronize]

jobs:
  label-check:
    runs-on: ubuntu-latest

    steps:
      - uses: docker://agilepathway/pull-request-label-checker:latest
        with:
          one_of: major,minor,patch,dependencies,skip-changelog
          repo_token: ${{ secrets.GITHUB_TOKEN }}


Main

name: Main

on:
  push:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

defaults:
  run:
    shell: bash -l {0}

jobs:

  Build:
    name: Build
    permissions:
      statuses: write
      pull-requests: write
      contents: write
    uses: ./.github/workflows/_build.yml

  ReleaseDraft:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: read
    steps:
      - name: Draft release notes
        uses: release-drafter/release-drafter@v6
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


PR

name: PR

on:
  pull_request:
    branches: [ main ]

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

defaults:
  run:
    shell: bash -l {0}

jobs:

  Build:
    name: Build
    permissions:
      statuses: write
      pull-requests: write
      contents: write
    uses: ./.github/workflows/_build.yml

  Dependabot:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    if: ${{ github.actor == 'dependabot[bot]' }}
    steps:
      - name: Fetch Dependabot metadata
        id: dependabot-metadata
        uses: dependabot/fetch-metadata@v2.1.0
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"
      - name: Add bpk label
        if: contains(steps.dependabot-metadata.outputs.dependency-names, 'bpk-')
        run: gh pr edit "$PR_URL" --add-label "bpk" --remove-label "javascript"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_URL: ${{github.event.pull_request.html_url}}


Release

name: Release

on:
  release:
    types: [ published ]

defaults:
  run:
    shell: bash -l {0}

env:
  flavour: oss

jobs:

  Build:
    name: Build
    permissions:
      statuses: write
      pull-requests: write
      contents: write
    uses: ./.github/workflows/_build.yml

  Deploy:
    name: Deploy
    runs-on: ubuntu-latest
    environment: Publishing
    needs: [Build]
    timeout-minutes: 30

    steps:

      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'adopt'

      - name: Create Sonatype key
        run: base64 -d <<< $SONATYPE_SINGING_KEY_BASE64 > sonatype.gpg
        env:
          SONATYPE_SINGING_KEY_BASE64: ${{ secrets.SONATYPE_SINGING_KEY_BASE64 }}

      - name: Building release APK
        run: |
          openssl aes-256-cbc -K $KEYSTORE_DECRYPT_KEY -iv $KEYSTORE_DECRYPT_IV -in backpack.android.demo.key.enc -out backpack.android.demo.key -d;
          ./gradlew :app:assemble${{ env.flavour }}Release -PdisablePreDex
        env:
          KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
          KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
          KEYSTORE_ALIAS_PASSWORD: ${{ secrets.KEYSTORE_ALIAS_PASSWORD }}
          MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }}
          KEYSTORE_DECRYPT_KEY: ${{ secrets.KEYSTORE_DECRYPT_KEY }}
          KEYSTORE_DECRYPT_IV: ${{ secrets.KEYSTORE_DECRYPT_IV }}

      - name: Build Dokka docs
        run: ./gradlew dokkaHtmlMultiModule

      - name: Publish artifacts
        run: >
          ./gradlew checkMavenCredentials
          publishMavenPublicationToSonatypeRepository
          closeAndReleaseSonatypeStagingRepository
          -Pversion=${{ github.event.release.tag_name }}
        env:
          SONATYPE_SINGING_KEY_ID: ${{ secrets.SONATYPE_SINGING_KEY_ID }}
          SONATYPE_SINGING_KEY_PASSWORD: ${{ secrets.SONATYPE_SINGING_KEY_PASSWORD }}
          SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
          OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
          OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}

      - name: Remove Sonatype key
        run: rm -rf sonatype.gpg

      - name: Deploy Dokka docs
        uses: peaceiris/actions-gh-pages@v4
        with:
          deploy_key: ${{ secrets.DOKKA_DEPLOY_KEY }}
          publish_dir: dokka/
          keep_files: true
          external_repository: backpack/android
          publish_branch: main

      - name: Publish Release APK to AppCenter
        uses: wzieba/AppCenter-Github-Action@v1.3.4
        with:
          appName: Backpack/Backpack-Android
          token: ${{secrets.APP_CENTER_TOKEN}}
          group: Everyone
          file: app/build/outputs/apk/oss/release/app-${{ env.flavour }}-release.apk
          releaseNotes: Version ${{github.event.release.tag_name}}

  SupernovaPublish:
    runs-on: ubuntu-latest
    environment: Publishing
    steps:
      - name: Update Supernova docs
        run: npx @supernovaio/cli publish-documentation --apiKey=${{ secrets.SUPERNOVA_API_KEY }} --designSystemId=${{ secrets.SUPERNOVA_DESIGN_SYSTEM_ID }}


Build

name: Build

on:
  push:
    paths-ignore:
      - 'README.md'
      - 'ci/**'
      - 'docs-generator/**'
      - '.editorconfig'
      - 'LICENSE.txt'
  pull_request:
    paths-ignore:
      - 'README.md'
      - 'ci/**'
      - 'docs-generator/**'
      - '.editorconfig'
      - 'LICENSE.txt'

jobs:
  Build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'corretto'
      #          cache: 'gradle'
      #          check-latest: true

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

      - name: Build With Gradle
        run: ./gradlew assemble --parallel --console=plain --no-daemon

      - name: Upload Test Results
        uses: actions/upload-artifact@v4
        if: ${{ always() }}
        with:
          name: build-report
          path: |
            **/build/**/test-results/**/* |
            **/build/**/reports/**/*


Docs Generation

name: Docs Generation

on:
  push:
    branches:
      - master

jobs:
  GenerateDocs:
    runs-on: ubuntu-latest

    permissions:
      contents: write

    steps:
      - name: Checkout base code
        uses: actions/checkout@v4
        with:
          path: kinference

      - name: Checkout wiki
        uses: actions/checkout@v4
        with:
          repository: ${{github.repository}}.wiki
          path: kinference-wiki

      - name: Python setup
        uses: actions/setup-python@v5
        with:
          python-version: 3.9

      - name: Install dependencies
        working-directory: kinference
        run: |
          python -m pip install -r docs-generator/requirements.txt

      - name: Generate docs
        working-directory: kinference
        run: |
          python docs-generator/main.py --core_doc_file $GITHUB_WORKSPACE/kinference-wiki/KInference-Core-supported-operators.md --tfjs_doc_file $GITHUB_WORKSPACE/kinference-wiki/KInference-TensorFlow.js-supported-operators.md

      - name: Commit updated documentation
        uses: stefanzweifel/git-auto-commit-action@v5
        with:
          repository: kinference-wiki
          commit_message: 'CI: Update documentation'


Heavy Tests

name: Heavy Tests

on:
  push:
    paths-ignore:
      - 'README.md'
      - 'ci/**'
      - 'docs-generator/**'
      - '.editorconfig'
      - 'LICENSE.txt'
  pull_request:
    paths-ignore:
      - 'README.md'
      - 'ci/**'
      - 'docs-generator/**'
      - '.editorconfig'
      - 'LICENSE.txt'

jobs:
  JVM:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'corretto'
      #          cache: 'gradle'
      #          check-latest: true

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

      - name: Cache test data
        uses: actions/cache@v4
        with:
          path: test-data
          key: test-data-${{ hashFiles('buildSrc/src/main/**/DefaultS3Deps.kt') }}

      - name: JVM Heavy Test
        env:
          AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
          AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
        run: ./gradlew jvmHeavyTest --console=plain -Pci --no-daemon

      - name: Upload Test Results
        uses: actions/upload-artifact@v4
        if: ${{ always() }}
        with:
          name: heavy-test-report-jvm
          path: |
            **/build/**/test-results/**/* |
            **/build/**/reports/**/*

  JS-IR:
    runs-on: ubuntu-latest
    container:
      image: registry.jetbrains.team/p/ki/containers-ci/ci-corretto-17-firefox:1.0.1
      options: --user 1001

    steps:
      - uses: actions/checkout@v4

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

      - name: Cache test data
        uses: actions/cache@v4
        with:
          path: test-data
          key: test-data-${{ hashFiles('buildSrc/src/main/**/DefaultS3Deps.kt') }}

      - name: JS IR Heavy Test
        env:
          AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
          AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
        run: xvfb-run --auto-servernum ./gradlew jsHeavyTest --console=plain -Pci --no-daemon

      - name: Upload Test Results
        uses: actions/upload-artifact@v4
        if: ${{ always() }}
        with:
          name: heavy-test-report-js
          path: |
            **/build/**/test-results/**/* |
            **/build/**/reports/**/*


Tests

name: Tests

on:
  push:
    paths-ignore:
      - 'README.md'
      - 'ci/**'
      - 'docs-generator/**'
      - '.editorconfig'
      - 'LICENSE.txt'
  pull_request:
    paths-ignore:
      - 'README.md'
      - 'ci/**'
      - 'docs-generator/**'
      - '.editorconfig'
      - 'LICENSE.txt'

jobs:
  JVM:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'corretto'
      #          cache: 'gradle'
      #          check-latest: true
      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v3

      - name: JVM Tests
        run: ./gradlew jvmTest --parallel --console=plain --no-daemon -Pci

      - name: Upload Test Results
        uses: actions/upload-artifact@v4
        if: ${{ always() }}
        with:
          name: test-report-jvm
          path: |
            **/build/**/test-results/**/* |
            **/build/**/reports/**/*

  JS-IR:
    runs-on: ubuntu-latest
    container:
      image: registry.jetbrains.team/p/ki/containers-ci/ci-corretto-17-firefox:1.0.1
      options: --user 1001

    steps:
      - uses: actions/checkout@v4

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

      - name: JS IR Test
        run: xvfb-run --auto-servernum ./gradlew jsTest --parallel --console=plain --no-daemon -Pci

      - name: Upload Test Results
        uses: actions/upload-artifact@v4
        if: ${{ always() }}
        with:
          name: test-report-js
          path: |
            **/build/**/test-results/**/* |
            **/build/**/reports/**/*


[Release] Step 3: Deploy (Autorun)

name: "[Release] Step 3: Deploy (Autorun)"

# Run when push on master or create tag with version [v*] for only changes to the test_runner/**
on:
  workflow_dispatch:
  push:
    paths:
      - 'test_runner/**'
      - 'firebase_api/test_api/**'
    branches:
      - 'master'
    tags: v*

# TODO: update workflow permissions to least privilege after auditing all the APIs used
# permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
#   contents: read

jobs:
  release:
    runs-on: ubuntu-latest

    steps:
    - name: Harden Runner
      uses: step-security/harden-runner@v2
      with:
        egress-policy: audit
        # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

    - uses: actions/checkout@v4
    - uses: gradle/wrapper-validation-action@v1

    - name: Get all git tags
      run: |
        git fetch --prune --unshallow --tags -f
        git tag

    - name: Download flankScripts and add it to PATH
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        ./gradlew :flank-scripts:download
        echo "./flank-scripts/bash" >> $GITHUB_PATH

    - name: Set env variables
      run: |
        echo "MVN_REVISION=$(echo $GITHUB_SHA)" >> $GITHUB_ENV

          if [[ $GITHUB_REF == refs/tags/* ]]
            then
              echo "MVN_VERSION=$(echo ${GITHUB_REF:11})" >> $GITHUB_ENV
              echo "RELEASE_TAG=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV
            else
              echo "RELEASE_TAG=flank-snapshot" >> $GITHUB_ENV
              echo "MVN_VERSION=master-SNAPSHOT" >> $GITHUB_ENV
          fi;

    - name: Store version variables to file
      run: |
          echo "$GITHUB_SHA" > ./test_runner/src/main/resources/revision.txt
          echo "$RELEASE_TAG" > ./test_runner/src/main/resources/version.txt

    - name: Gradle Build Flank
      uses: gradle/gradle-build-action@v2.11.1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        HEAD_REF: ${{ github.head_ref }}
      with:
        arguments: "clean test_runner:build test_runner:shadowJar"

    - name: Delete old release
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        gh --version
        flankScripts github delete_release --git-tag=$RELEASE_TAG

    - name: Delete old tag
      if: ${{ env.RELEASE_TAG == 'flank-snapshot' }}
      run: flankScripts github delete_old_tag --git-tag=$RELEASE_TAG --username=$GITHUB_ACTOR --token=${{ secrets.GITHUB_TOKEN }}

    - name: Release snapshot
      if: ${{ env.RELEASE_TAG == 'flank-snapshot' }}
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: flankScripts github make_release --input-file=./test_runner/build/libs/flank.jar --git-tag=$RELEASE_TAG --commit-hash=$GITHUB_SHA --snapshot

    - name: Release stable
      if: startsWith(github.ref, 'refs/tags/v')
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: flankScripts github make_release --input-file=./test_runner/build/libs/flank.jar --git-tag=$RELEASE_TAG --token=${{ secrets.GITHUB_TOKEN }}

    - name: Append checksum to release
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        sha256sum ./test_runner/build/libs/flank.jar > flank.sha256
        gh release upload $RELEASE_TAG flank.sha256

    - name: Gradle Upload to MavenCentral
      uses: gradle/gradle-build-action@v2.11.1
      env:
        PGP_SIGNING_KEY: ${{ secrets.PGP_SIGNING_KEY }}
      with:
        arguments: :test_runner:publishMavenJavaPublicationToMavenCentralRepository -PMVN_CENTRAL_USER=${{ secrets.MVN_CENTRAL_USER }} -PMVN_CENTRAL_PASSWORD=${{ secrets.MVN_CENTRAL_PASSWORD }}

    - name: Gradle close staging on MavenCentral
      uses: gradle/gradle-build-action@v2.11.1
      if: startsWith(github.ref, 'refs/tags/v')
      with:
        arguments: closeAndReleaseRepository -PMVN_CENTRAL_USER=${{ secrets.MVN_CENTRAL_USER }} -PMVN_CENTRAL_PASSWORD=${{ secrets.MVN_CENTRAL_PASSWORD }}

    - name: Gradle Upload to GitHub packages
      uses: gradle/gradle-build-action@v2.11.1
      if: startsWith(github.ref, 'refs/tags/v')
      env:
        PGP_SIGNING_KEY: ${{ secrets.PGP_SIGNING_KEY }}
      with:
        arguments: ":test_runner:publishMavenJavaPublicationToGitHubPackagesRepository -PGITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}"


[Code Health] Run update dependencies

name: "[Code Health] Run update dependencies"

on:
  workflow_dispatch:

# TODO: update workflow permissions to least privilege after auditing all the APIs used
# permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
#   contents: read

jobs:
  get_date:
    runs-on: ubuntu-latest
    outputs:
      date: ${{ steps.date.outputs.date }}
    steps:
      - name: Get current date
        id: date
        run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT

  update_dependencies:
    runs-on: macos-latest
    needs: [ get_date ]
    steps:
      - uses: actions/checkout@v4
      - uses: tibdex/github-app-token@v2
        id: generate-token
        with:
          app_id: ${{ secrets.FLANK_RELEASE_APP_ID }}
          private_key: ${{ secrets.FLANK_RELEASE_PRIVATE_KEY }}

      - name: Gradle dependency updates raport
        uses: gradle/gradle-build-action@v2.11.1
        with:
          arguments: dependencyUpdates -DoutputFormatter=json -DoutputDir=.

      - name: Download flankScripts and add it to PATH
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          ./gradlew :flank-scripts:download
          echo "./flank-scripts/bash" >> $GITHUB_PATH

      - name: Update dependencies
        run: |
          flankScripts dependencies update

      - name: Commit files and create Pull request
        id: pr
        uses: peter-evans/create-pull-request@v5
        with:
          token: ${{ steps.generate-token.outputs.token }}
          commit-message: "[Automatic PR] Dependencies update"
          signoff: false
          branch: "dependencies-update-${{ needs.get_date.outputs.date }}"
          title: "build: Dependencies updates [${{ needs.get_date.outputs.date }}]"
          body: "Dependencies updates"
          labels: |
            automated pr
            dependencies
          reviewers: adamvduke,jakeouellette,tonybaroneee
          draft: false

  update_firebase_api:
    runs-on: macos-latest
    needs: [ get_date ]
    steps:
      - uses: actions/checkout@v4
      - uses: tibdex/github-app-token@v2
        id: generate-token
        with:
          app_id: ${{ secrets.FLANK_RELEASE_APP_ID }}
          private_key: ${{ secrets.FLANK_RELEASE_PRIVATE_KEY }}

      - name: Setup python
        uses: actions/setup-python@v5
        with:
          python-version: '2.x'

      - name: Download flankScripts and add it to PATH
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          ./gradlew :flank-scripts:download
          echo "./flank-scripts/bash" >> $GITHUB_PATH

      - name: Update Java Client
        run: |
          pip install google-apis-client-generator
          flankScripts firebase update_api
          flankScripts firebase generate_client

      - name: Commit files and create Pull request
        id: pr
        uses: peter-evans/create-pull-request@v5
        with:
          token: ${{ steps.generate-token.outputs.token }}
          commit-message: "[Automatic PR] Firebase API Client update"
          signoff: false
          branch: "firebase-api-client-update-${{ needs.get_date.outputs.date }}"
          title: "build: Firebase API Client update [${{ needs.get_date.outputs.date }}]"
          body: "Firebase Api update"
          labels: |
            automated pr
            firease_api
          reviewers: adamvduke,jakeouellette,tonybaroneee
          draft: false

  check_for_sdk_updates:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - name: Download flankScripts and add it to PATH
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          ./gradlew :flank-scripts:download
          echo "./flank-scripts/bash" >> $GITHUB_PATH

      - name: Run SDK check
        run: |
          flankScripts firebase check_for_sdk_updates \
          --github-token=${{ secrets.GITHUB_TOKEN }} \

  trigger-pointer:
    runs-on: ubuntu-latest
    needs: [ check_for_sdk_updates ]
    if: always()
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@v2
        with:
          egress-policy: audit
          # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

      - uses: tibdex/github-app-token@v2
        id: generate-token
        with:
          app_id: ${{ secrets.FLANK_RELEASE_APP_ID }}
          private_key: ${{ secrets.FLANK_RELEASE_PRIVATE_KEY }}

      - name: Dispatch repository event
        uses: peter-evans/repository-dispatch@v2
        with:
          token: ${{ steps.generate-token.outputs.token }}
          event-type: dependencies-pointer


[Docs] Publish docs site (via MkDocs)

name: "[Docs] Publish docs site (via MkDocs)"

on:
  workflow_dispatch:
  push:
    branches: [master]
    paths: ['docs/**']

permissions:  # added using https://github.com/step-security/secure-workflows
  contents: read

jobs:
  deploy:
    permissions:
      contents: write  # for mkdocs gh-deploy to publish docs
    runs-on: ubuntu-latest
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@v2
        with:
          egress-policy: audit
          # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: 3.x
      - run: pip install mkdocs-material
      - run: mkdocs gh-deploy --force


Pointer - Integration Tests

# Workflow is unable to retrieve data about previous workflows (the same file).
# This workflow is a way to preserve last run date for full_suite_integration_tests.yml workflow
# which is available from within that workflow
name: "Pointer - Integration Tests"
on:
  repository_dispatch:
    types: [ integration-pointer]

permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
  contents: read

jobs:
  point:
    runs-on: ubuntu-latest
    steps:
      - run: |
          echo "Integration tests pointer updated: $(date +'%Y-%m-%d')"


Pointer - Dependencies Update

# Workflow is unable to retrieve data about previous workflows (the same file).
# This workflow is a way to preserve last run date for update_dependencies_and_client.yml workflow
# which is available from within that workflow
name: "Pointer - Dependencies Update"
on:
  repository_dispatch:
    types: [ dependencies-pointer ]

permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
  contents: read

jobs:
  point:
    runs-on: ubuntu-latest
    steps:
      - run: |
          echo "Dependencies pointer updated: $(date +'%Y-%m-%d')"


name: "[Code Health] Run Scorecards security analysis"
on:
  # Only the default branch is supported.
  branch_protection_rule:
  schedule:
    - cron: '41 12 * * 3'
  push:
    branches: [ "master" ]

# Declare default permissions as read only.
permissions: read-all

jobs:
  analysis:
    name: Scorecards analysis
    runs-on: ubuntu-latest
    permissions:
      # Needed to upload the results to code-scanning dashboard.
      security-events: write
      # Used to receive a badge.
      id-token: write
      # Needs for private repositories.
      contents: read
      actions: read

    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@v2
        with:
          egress-policy: audit
          # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

      - name: "Checkout code"
        uses: actions/checkout@v4 # tag=v3.0.0
        with:
          persist-credentials: false

      - name: "Run analysis"
        uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # tag=v2.3.1
        with:
          results_file: results.sarif
          results_format: sarif
          # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if:
          # - you want to enable the Branch-Protection check on a *public* repository, or
          # - you are installing Scorecards on a *private* repository
          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
          # repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}

          # Publish the results for public repositories to enable scorecard badges. For more details, see
          # https://github.com/ossf/scorecard-action#publishing-results.
          # For private repositories, `publish_results` will automatically be set to `false`, regardless
          # of the value entered here.
          publish_results: true

      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
      # format to the repository Actions tab.
      - name: "Upload artifact"
        uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # tag=v4.2.0
        with:
          name: SARIF file
          path: results.sarif
          retention-days: 5

      # Upload the results to GitHub's code scanning dashboard.
      - name: "Upload to code-scanning"
        uses: github/codeql-action/upload-sarif@v3.23.1 # tag=v1.0.26
        with:
          sarif_file: results.sarif


[Code Health] Run CodeQL Analysis

# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "[Code Health] Run CodeQL Analysis"

on:
  push:
    branches: [ "master" ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ "master" ]
  schedule:
    - cron: '32 22 * * 6'

permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
  contents: read

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'java' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

    steps:
    - name: Harden Runner
      uses: step-security/harden-runner@v2
      with:
        egress-policy: audit
        # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

    - name: Checkout repository
      uses: actions/checkout@v4

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3.23.1
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.

        # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
        # queries: security-extended,security-and-quality

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v3.23.1

    # ℹ Command-line programs to run using the OS shell.
    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

    #   If the Autobuild fails above, remove it and uncomment the following three lines.
    #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

    # - run: |
    #   echo "Run, Build Application using script"
    #   ./location_of_script_within_repo/buildscript.sh

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3.23.1
      with:
        category: "/language:${{matrix.language}}"


ubuntu-workflow

name: ubuntu-workflow

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - '*'

permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
  contents: read

jobs:
  build:
    permissions:
      actions: write  # for styfle/cancel-workflow-action to cancel/stop running workflows
      contents: read  # for actions/checkout to fetch code
    runs-on: ubuntu-latest
    outputs:
      build-scan-url: ${{ steps.build.outputs.build-scan-url }}
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@v2
        with:
          egress-policy: audit
          # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.12.0
        with:
          access_token: ${{ github.token }}
      - uses: actions/checkout@v4
        with:
          submodules: true
      - uses: gradle/wrapper-validation-action@v1
      - uses: actions/cache@v4
        with:
          path: ~/.gradle/caches
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      - id: build
        name: Gradle clean build
        uses: gradle/gradle-build-action@v2.11.1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          HEAD_REF: ${{ github.head_ref }}
        with:
          arguments: "clean build"

  publish-scan-url:
    needs: [ build ]
    permissions:
      pull-requests: write # to allow creating or updating a comment
    if: ${{ github.event_name == 'pull_request' }}
    runs-on: ubuntu-latest
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@v2
        with:
          egress-policy: audit
          # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

      - name: Get current time
        uses: 1466587594/get-current-time@v2
        id: current-time
        with:
          format: 'YYYY-MM-DD HH:mm:ss'
          utcOffset: "+00:00"

      - name: Find Comment
        uses: peter-evans/find-comment@v2
        id: fc
        with:
          issue-number: ${{ github.event.pull_request.number }}
          comment-author: 'github-actions[bot]'
          body-includes: Buildscan url for ubuntu-workflow run

      - name: Create comment
        if: ${{ steps.fc.outputs.comment-id == 0 }}
        uses: peter-evans/create-or-update-comment@v3
        with:
          issue-number: ${{ github.event.pull_request.number }}
          body: |
            **Timestamp:** ${{ steps.current-time.outputs.formattedTime }}
            **Buildscan url for ubuntu-workflow run [${{ github.run_id }}](https://github.com/Flank/flank/actions/runs/${{ github.run_id }})**
            ${{ needs.build.outputs.build-scan-url }}

      - name: Update comment
        if: ${{ steps.fc.outputs.comment-id != 0 }}
        uses: peter-evans/create-or-update-comment@v3
        with:
          comment-id: ${{ steps.fc.outputs.comment-id }}
          edit-mode: replace
          body: |
            **Timestamp:** ${{ steps.current-time.outputs.formattedTime }}
            **Buildscan url for ubuntu-workflow run [${{ github.run_id }}](https://github.com/Flank/flank/actions/runs/${{ github.run_id }})**
            ${{ needs.build.outputs.build-scan-url }}


name: "[Release] Step 1: Generate release notes"

on:
  workflow_dispatch:

# limit the permissions of the GITHIUB_TOKEN to reading repository contents
# the subsequent pull request uses a token from the 'generate-token' step
permissions:
  contents: read

jobs:
  generateReleaseNotes:
    runs-on: macos-latest

    steps:
    - uses: actions/checkout@v4
    - uses: gradle/wrapper-validation-action@v1
    - name: Get all git tags
      run: |
        git fetch --prune --unshallow --tags
        git tag

    - name: Download flankScripts and add it to PATH
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        ./gradlew :flank-scripts:download
        echo "./flank-scripts/bash" >> $GITHUB_PATH

    - name: Set next release tag variable
      run: |
        TAG=$(flankScripts release next_tag --token=${{ secrets.GITHUB_TOKEN }})
        echo "NEXT_RELEASE_TAG=$(echo $TAG)" >> $GITHUB_ENV

    - name: Append release note
      run: |
        flankScripts release generate_release_notes --token=${{ secrets.GITHUB_TOKEN }}

    - uses: tibdex/github-app-token@v2
      id: generate-token
      with:
        app_id: ${{ secrets.FLANK_RELEASE_APP_ID }}
        private_key: ${{ secrets.FLANK_RELEASE_PRIVATE_KEY }}

    - name: Commit files and create Pull request
      id: pr
      uses: peter-evans/create-pull-request@v5
      with:
        token: ${{ steps.generate-token.outputs.token }}
        commit-message: "[Automatic PR] Generate release notes"
        signoff: false
        branch: 'release/${{ env.NEXT_RELEASE_TAG }}'
        title: 'chore: release notes for ${{ env.NEXT_RELEASE_TAG }}'
        body: "Auto generated release notes for `${{ env.NEXT_RELEASE_TAG }}` by @${{ github.actor }}"
        labels: |
          automated pr
          release
        reviewers: adamvduke,jakeouellette,tonybaroneee
          owners
        draft: false


[Tests] Run integration tests

name: "[Tests] Run integration tests"

on:
  schedule:
    - cron: '0 0 * * 1-5' # At 00:00 on every day-of-week from Monday through Friday
  workflow_dispatch:      # or manually

# TODO: update workflow permissions to least privilege after auditing all the APIs used
# permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
#   contents: read

jobs:
  cancel_previous:
    permissions:
      actions: write  # for styfle/cancel-workflow-action to cancel/stop running workflows
    runs-on: macos-latest
    steps:
      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.12.0
        with:
          access_token: '${{ secrets.GITHUB_TOKEN }}'

  should_run_it:
    runs-on: macos-latest
    outputs:
      run_integration_tests: ${{ steps.should-run-step.outputs.should_run }}
      pr_number: ${{ steps.pr_number.outputs.number }}
    steps:
      - name: Check if integrations tests should run
        id: should-run-step
        env:
          run_it: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' }}
        run: |
          echo "event name: ${{ github.event_name }}"
          echo "should_run=${{ env.run_it }}" >> $GITHUB_OUTPUT

  run-it-full-suite:
    needs: [ should_run_it ]
    if: needs.should_run_it.outputs.run_integration_tests == 'true'
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ macos-latest, windows-latest, ubuntu-latest ]
      fail-fast: false
    outputs:
      job_status: ${{ job.status }}
      build-scan-url: ${{ steps.run-it.outputs.build-scan-url }}
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@v2
        with:
          egress-policy: audit
          # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

      - name: Checkout code
        uses: actions/checkout@v4

      - uses: actions/cache@v4
        with:
          path: ~/.gradle/caches
          key: macos-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            macos-gradle-

      - name: Prepare Google Service Account
        env:
          GCLOUD_KEY: ${{ secrets.GCLOUD_KEY }}

        run: |
            if [ "$RUNNER_OS" == "Windows" ]; then
                GCLOUD_DIR=$HOMEPATH\\.config\\gcloud\\
                mkdir -p $GCLOUD_DIR
                echo $GCLOUD_KEY > key.txt
                certutil -decode key.txt $GCLOUD_DIR\application_default_credentials.json
            else
                GCLOUD_DIR="$HOME/.config/gcloud/"
                mkdir -p "$GCLOUD_DIR"
                echo "$GCLOUD_KEY" | base64 --decode > "$GCLOUD_DIR/application_default_credentials.json"
            fi
        shell: bash

      - name: Gradle integration tests
        uses: gradle/gradle-build-action@v2.11.1
        id: run-it
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          HEAD_REF: ${{ github.ref }}
        with:
          arguments: "integrationTests"

      - name: Export Variables for FlankScripts
        if: ${{ always() }}
        uses: UnlyEd/github-action-store-variable@v3.0.15
        with:
          variables: |
            ${{ runner.os }}=${{ job.status }}
            ${{ runner.os }}-bs=${{steps.run-it.outputs.build-scan-url}}

  process-results:
    needs: [ run-it-full-suite ]
    runs-on: macos-latest
    steps:
      - name: Import variable
        uses: UnlyEd/github-action-store-variable@v3.0.15
        with:
          delimiter: ','
          variables: |
            Windows,Windows-bs,macOS,macOS-bs,Linux,Linux-bs

      - name: Checkout code
        uses: actions/checkout@v4
      - name: Download flankScripts and add it to PATH
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          ./gradlew :flank-scripts:download
          echo "./flank-scripts/bash" >> $GITHUB_PATH

      - name: Process IT results
        run: |
          flankScripts integration_tests process_results \
          --global-result=${{ needs.run-it-full-suite.outputs.job_status }} \
          --run-result='${{ toJSON(env) }}' \
          --github-token=${{ secrets.GITHUB_TOKEN }} \
          --run-id=${{ github.run_id }}

  trigger-pointer:
    runs-on: ubuntu-latest
    needs: [ process-results ]
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@v2
        with:
          egress-policy: audit
          # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

      - uses: tibdex/github-app-token@v2
        id: generate-token
        with:
          app_id: ${{ secrets.FLANK_RELEASE_APP_ID }}
          private_key: ${{ secrets.FLANK_RELEASE_PRIVATE_KEY }}

      - name: Repository Dispatch
        uses: peter-evans/repository-dispatch@v2
        with:
          token: ${{ steps.generate-token.outputs.token }}
          event-type: integration-pointer


name: "[Release] Step 2: Tag next release (Autorun)"

on:
  workflow_dispatch:
  pull_request:
    types: [ closed ]

# TODO: update workflow permissions to least privilege after auditing all the APIs used
# permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
#   contents: read

jobs:
  create_release:
    runs-on: ubuntu-latest
    if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release') && contains(github.event.pull_request.user.login, 'google-flank-bot[bot]') && contains(github.event.pull_request.title, 'release notes for')
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@v2
        with:
          egress-policy: audit
          # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

      - uses: actions/checkout@v4
      - uses: tibdex/github-app-token@v2
        id: generate-token
        with:
          app_id: ${{ secrets.FLANK_RELEASE_APP_ID }}
          private_key: ${{ secrets.FLANK_RELEASE_PRIVATE_KEY }}

      - name: Download flankScripts and add it to PATH.
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          ./gradlew :flank-scripts:download
          echo "./flank-scripts/bash" >> $GITHUB_PATH
      - name: Set next release tag variable
        run: |
          TAG=$(flankScripts release next_tag --token=${{ secrets.GITHUB_TOKEN }})
          echo "NEXT_RELEASE_TAG=$(echo $TAG)" >> $GITHUB_ENV

      - name: Push tag
        id: tag_version
        uses: mathieudutour/github-tag-action@v5
        with:
          github_token: ${{ steps.generate-token.outputs.token }}
          custom_tag: ${{ env.NEXT_RELEASE_TAG }}
          tag_prefix: ''


wsl-workflow

name: wsl-workflow

on:
  workflow_dispatch:

jobs:
  integration-tests-wsl:
    permissions:
      actions: write  # for styfle/cancel-workflow-action to cancel/stop running workflows
      contents: read  # for actions/checkout to fetch code
    runs-on: windows-2019
    defaults:
      run:
        shell: wsl-bash {0}

    steps:
    - name: Cancel Previous Runs
      uses: styfle/cancel-workflow-action@0.12.0
      with:
        access_token: ${{ secrets.GITHUB_TOKEN }}

    - uses: Vampire/setup-wsl@v2
      with:
        distribution: Ubuntu-20.04
        additional-packages:
          dos2unix

    - uses: actions/checkout@v4
    - uses: gradle/wrapper-validation-action@v1
    - uses: actions/cache@v4
      with:
        path: ~/.gradle/caches
        key: ${{ runner.os }}-2-gradle-${{ hashFiles('**/*.gradle*') }}
        restore-keys: |
          ${{ runner.os }}-2-gradle-

    - name: Set GCLOUD_KEY for WSL
      shell: cmd
      run: |
          echo ${{ secrets.GCLOUD_KEY }} > gcloud_key.txt

    - name: Configure WSL for flank
      run: |
          find . -type f -print0 | xargs -0 -n 1 -P 4 dos2unix
          chmod +x gradlew
          sudo apt-get -y install openjdk-8-jdk

    - name: Gradle clean build
      run: |
          export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
          export HEAD_REF=${{ github.ref }}
          ./gradlew clean build

    - name: Prepare Google Service Account
      run: |
          GCLOUD_KEY=$(cat gcloud_key.txt)
          GCLOUD_DIR="$HOME/.config/gcloud/"
          mkdir -p "$GCLOUD_DIR"
          echo "$GCLOUD_KEY" | base64 --ignore-garbage --decode > "$GCLOUD_DIR/application_default_credentials.json"

    - name: Gradle Integration Tests Android
      uses: gradle/gradle-build-action@v2.11.1
      env:
        GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
      with:
        arguments: "--info :integration_tests:test --tests IntegrationTests.shouldMatchAndroidSuccessExitCodeAndPattern -Dflank-path=../test_runner/build/libs/flank.jar -Dyml-path=./src/test/resources/flank_android.yml"

    - name: Gradle Integration Tests iOS
      uses: gradle/gradle-build-action@v2.11.1
      env:
        GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
      with:
        arguments: "--info :integration_tests:test --tests IntegrationTests.shouldMatchIosSuccessExitCodeAndPattern -Dflank-path=../test_runner/build/libs/flank.jar -Dyml-path=./src/test/resources/flank_ios.yml"


macOS-workflow

name: macOS-workflow

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - '*'

permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
  contents: read

jobs:
  build:
    permissions:
      actions: write  # for styfle/cancel-workflow-action to cancel/stop running workflows
      contents: read  # for actions/checkout to fetch code
    runs-on: macos-latest

    steps:
    - name: Cancel Previous Runs
      uses: styfle/cancel-workflow-action@0.12.0
      with:
        access_token: ${{ secrets.GITHUB_TOKEN }}
    - uses: actions/checkout@v4
    - uses: gradle/wrapper-validation-action@v1
    - uses: actions/setup-java@v4
      with:
        java-version: 15
    - uses: actions/cache@v4
      with:
        path: ~/.gradle/caches
        key: ${{ runner.os }}-2-gradle-${{ hashFiles('**/*.gradle*') }}
        restore-keys: |
          ${{ runner.os }}-2-gradle-

    - name: Gradle clean build
      uses: gradle/gradle-build-action@v2.11.1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        HEAD_REF: ${{ github.head_ref }}
      with:
        arguments: "clean build"


windows-workflow

name: windows-workflow

on:
  pull_request:
    branches:
      - '*'

permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
  contents: read

jobs:
  build:
    permissions:
      actions: write  # for styfle/cancel-workflow-action to cancel/stop running workflows
      contents: read  # for actions/checkout to fetch code
    runs-on: windows-latest

    steps:
    - name: Cancel Previous Runs
      uses: styfle/cancel-workflow-action@0.12.0
      with:
        access_token: ${{ secrets.GITHUB_TOKEN }}
    - uses: actions/checkout@v4
    - uses: gradle/wrapper-validation-action@v1
    - uses: actions/cache@v4
      with:
        path: ~/.gradle/caches
        key: ${{ runner.os }}-2-gradle-${{ hashFiles('**/*.gradle*') }}
        restore-keys: |
          ${{ runner.os }}-2-gradle-

    - name: Gradle clean build
      uses: gradle/gradle-build-action@v2.11.1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        HEAD_REF: ${{ github.head_ref }}
      with:
        arguments: "clean build"


Release Flank Scripts

name: "Release Flank Scripts"

on:
  push:
    paths:
      - 'flank-scripts/**'
    branches:
      - 'master'

# TODO: update workflow permissions to least privilege after auditing all the APIs used
# permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
#   contents: read

jobs:
  release:
    runs-on: ubuntu-latest

    steps:
    - name: Harden Runner
      uses: step-security/harden-runner@v2
      with:
        egress-policy: audit
        # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

    - uses: actions/checkout@v4
    - uses: gradle/wrapper-validation-action@v1

    - name: Gradle Upload to Github packages and Github release
      uses: gradle/gradle-build-action@v2.11.1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        arguments: "flank-scripts:releaseFlankScripts -PGITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}"


Release Flank Wrapper

name: "Release Flank Wrapper"

on:
  push:
    paths:
      - 'flank_wrapper/**'
    branches:
      - 'master'

# TODO: update workflow permissions to least privilege after auditing all the APIs used
# permissions:  # limit the permissions of the GITHIUB_TOKEN to reading repository contents
#   contents: read

jobs:
  release:
    runs-on: ubuntu-latest

    steps:
    - name: Harden Runner
      uses: step-security/harden-runner@v2
      with:
        egress-policy: audit
        # TODO: change to 'egress-policy: block' after auditing a number of runs and updating the allowed-endpoints option accordingly

    - uses: actions/checkout@v4
    - uses: gradle/wrapper-validation-action@v1

    - name: Gradle Upload to Github packages and Github release
      uses: gradle/gradle-build-action@v2.11.1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        arguments: "flank_wrapper:releaseFlankWrapper -PGITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}"