Repository: codecov/umbrella
Pull request #88: chore(ci): internalize reusable workflows from gha-workflows
Author: login: matt-codecov
Created: 2025-04-30T00:06:13Z | Merged: 2025-04-30T18:49:33Z

this PR copies the reusable workflows we use 100% unchanged to umbrella. reusable workflows start with `_` by convention.

being in a monorepo has already required some divergence and complexity, and as we embrace the monorepo more, even more junk will have to be added to retain interoperability with other repos. also, merging into gha-workflows, cutting a release, and updating a tag in umbrella is a big pain (that's why we went for a monorepo to begin with). giving umbrella its own copies of these workflows which we can customize will help us declutter and move fast

second in a series of related CI changes. i am testing my personal build of git-branchless with stacked PR support, please humor me

<!-- %%% BRANCHLESS STACK INFO START %%% -->
<details open><summary> Stack info:</summary>

| Stack 1 |
|-|
|<ul><li>https://github.com/codecov/umbrella/pull/87</li><li>➡️https://github.com/codecov/umbrella/pull/88</li><li>https://github.com/codecov/umbrella/pull/89</li><li>https://github.com/codecov/umbrella/pull/90</li></ul>|

</details>
<!-- %%% BRANCHLESS STACK INFO END %%% -->


Diff:
diff --git a/.github/workflows/_build-app.yml b/.github/workflows/_build-app.yml
new file mode 100644
index 0000000000..1f207d727b
--- /dev/null
+++ b/.github/workflows/_build-app.yml
@@ -0,0 +1,94 @@
+name: Build App
+
+on:
+  workflow_call:
+    inputs:
+      repo:
+        type: string
+        required: true
+      cache_requirements:
+        type: boolean
+        default: true
+      output_directory:
+        type: string
+        default: .
+      reqs_cache_key:
+        type: string
+        required: false
+      cache_file:
+        type: string
+        default: "requirements.txt"
+      make_target_prefix:
+        type: string
+        default: ""
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  build:
+    name: Build App
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 2
+          submodules: 'recursive'
+      - id: "auth"
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' }}
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' }}
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Cache Requirements
+        id: cache-requirements
+        if: inputs.cache_requirements
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-requirements
+        with:
+          path: |
+            ${{ inputs.output_directory }}/requirements.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.reqs_cache_key || format('{0}-{1}', hashFiles(format('{0}/**/{1}', inputs.output_directory, inputs.cache_file)), hashFiles(format('{0}/**/docker/Dockerfile.requirements', inputs.output_directory))) }}
+          restore-keys: |
+            ${{ runner.os }}-${{ env.cache-name }}-
+
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+
+      - name: Load requirements from cache
+        if: ${{ steps.cache-requirements.outputs.cache-hit == 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.requirements
+
+      - name: Build/pull requirements
+        if: ${{ steps.cache-requirements.outputs.cache-hit != 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.requirements
+          make ${{ inputs.make_target_prefix }}save.requirements
+
+      - name: Push Requirements
+        if: ${{ steps.cache-requirements.outputs.cache-hit != 'true' && !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}push.requirements
+      - name: Build app
+        run: |
+          make ${{ inputs.make_target_prefix }}build.app
+          make ${{ inputs.make_target_prefix }}save.app
+
diff --git a/.github/workflows/_create-release-pr.yml b/.github/workflows/_create-release-pr.yml
new file mode 100644
index 0000000000..0c4d7ce171
--- /dev/null
+++ b/.github/workflows/_create-release-pr.yml
@@ -0,0 +1,54 @@
+name: Create PR for Release
+
+on:
+  workflow_call:
+    inputs:
+      mainBranch:
+        description: 'Name of main branch'
+        required: false
+        type: string
+        default: 'main'
+jobs:
+  create-release-pr:
+    name: Create PR
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          submodules: 'recursive'
+
+      - name: Import GPG key
+        id: import-gpg
+        uses: crazy-max/ghaction-import-gpg@v6
+        with:
+          gpg_private_key: ${{ secrets.RELEASER_GPG_PRIVATE_KEY }}
+          git_user_signingkey: true
+          git_commit_gpgsign: true
+          git_config_global: true
+
+      - name: Create release branch
+        run: git checkout -b release/${{ github.event.inputs.versionName }}
+      - name: Update version file
+        id: make-commit
+        run: |
+          echo ${{ github.event.inputs.versionName }} > VERSION
+          echo -n $(tr -d '\n' < VERSION) > VERSION
+          git add VERSION
+          git commit -S --message "Prepare release ${{ github.event.inputs.versionName }}"
+          echo "commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
+
+      - name: Push release branch
+        run: git push origin release/${{ github.event.inputs.versionName }}
+      - name: Create pull request into main
+        uses: thomaseizinger/create-pull-request@1.3.1
+        with:
+          github_token: ${{ secrets.GH_RELEASE_TOKEN }}
+          head: release/${{ github.event.inputs.versionName }}
+          base: ${{ inputs.mainBranch }}
+          title: Release ${{ github.event.inputs.versionName }}
+          reviewers: ${{ github.event.issue.user.login }}
+          body: |
+            Release PR for ${{ github.event.inputs.versionName }}
+            I've updated the version name and committed: ${{ steps.make-commit.outputs.commit }}.
+
diff --git a/.github/workflows/_create-release.yml b/.github/workflows/_create-release.yml
new file mode 100644
index 0000000000..866f2e43df
--- /dev/null
+++ b/.github/workflows/_create-release.yml
@@ -0,0 +1,29 @@
+name: Create GitHub Release
+
+on:
+  workflow_call:
+    inputs:
+      tag_to_prepend:
+        type: string
+        required: false
+        default: ''
+        description: 'Tag prepend value. Ex: self-hosted-'
+jobs:
+  create-release:
+    name: Create Github Release
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - id: get-release-vars
+        name: Configure Release Vars
+        run: |
+          echo "release_version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
+
+      - name: Create GitHub Release
+        env:
+          GITHUB_TOKEN: ${{ secrets.CODECOV_RELEASE_PAT }}
+        run: |
+          gh release create ${{ inputs.tag_to_prepend }}${{ steps.get-release-vars.outputs.release_version }} --title "Release ${{ steps.get-release-vars.outputs.release_version }}" --notes "Autogenerated for ${{ steps.get-release-vars.outputs.release_version }}. Created for ${{ github.event.pull_request.html_url }}" --generate-notes
+
diff --git a/.github/workflows/_lint.yml b/.github/workflows/_lint.yml
new file mode 100644
index 0000000000..f30ff8700d
--- /dev/null
+++ b/.github/workflows/_lint.yml
@@ -0,0 +1,20 @@
+name: Run Lint
+
+on:
+  workflow_call:
+    inputs:
+      make_target_prefix:
+        type: string
+        default: ""
+
+jobs:
+  lint:
+    name: Run Lint
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Install dependencies
+        run: make ${{ inputs.make_target_prefix }}lint.install
+      - name: Check
+        run: make ${{ inputs.make_target_prefix }}lint.check
+
diff --git a/.github/workflows/_push-env.yml b/.github/workflows/_push-env.yml
new file mode 100644
index 0000000000..824d796221
--- /dev/null
+++ b/.github/workflows/_push-env.yml
@@ -0,0 +1,162 @@
+name: Push Image for Environment
+
+on:
+  workflow_call:
+    inputs:
+      environment:
+        required: true
+        type: string
+      repo:
+        type: string
+        required: true
+      create_sentry_release:
+        type: boolean
+        required: false
+        default: true
+      push_rolling:
+        type: boolean
+        default: false
+        description: "Whether to push the rolling image"
+      push_release:
+        type: boolean
+        default: false
+        description: "Whether to push the release image"
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+      sentry_project:
+        type: string
+        required: false
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  push-environment:
+    name: Push ${{ inputs.environment }} Image
+    runs-on: ubuntu-latest
+    environment: ${{ inputs.environment }}
+    if: github.repository_owner == 'codecov' && !inputs.push_rolling && !inputs.push_release
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Get Release SHA
+        env:
+          SHA: ${{ github.sha }}
+        id: sha
+        run: echo short_sha="${SHA:0:7}" >> $GITHUB_OUTPUT
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - id: "auth"
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Push ${{ inputs.environment }}
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.${{ inputs.environment }}
+          make ${{ inputs.make_target_prefix }}push.${{ inputs.environment }}
+      - name: Push latest
+        if: inputs.environment == 'production'
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.latest
+          make ${{ inputs.make_target_prefix }}push.latest
+
+      - name: Create Sentry release
+        if: inputs.create_sentry_release
+        uses: getsentry/action-release@v1
+        env:
+          SENTRY_AUTH_TOKEN: ${{ secrets.CODECOV_SENTRY_RELEASE_TOKEN }}
+          SENTRY_ORG: ${{ secrets.CODECOV_SENTRY_ORG }}
+          SENTRY_PROJECT: ${{ inputs.sentry_project != '' && inputs.sentry_project || secrets.CODECOV_SENTRY_PROJECT }}
+        with:
+          environment: ${{ inputs.environment }}
+          version: ${{ inputs.environment }}-release-${{ steps.sha.outputs.short_sha }}
+          ignore_missing: true
+          output_directory: ${{ inputs.output_directory }}
+  rolling:
+    name: Push Rolling Image
+    if: inputs.push_rolling == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push Rolling
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.rolling
+          make ${{ inputs.make_target_prefix }}push.rolling
+  release:
+    name: Push Release Image
+    if: inputs.push_release == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push release
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.release
+          make ${{ inputs.make_target_prefix }}push.release
+
diff --git a/.github/workflows/_run-tests.yml b/.github/workflows/_run-tests.yml
new file mode 100644
index 0000000000..48e13a0266
--- /dev/null
+++ b/.github/workflows/_run-tests.yml
@@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading integration test coverage (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/integration.coverage.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading unit test results (${{ matrix.name }})
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/unit.junit.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          # The coverage action will have installed codecovcli with pip. The
+          # actual binary will be found in $PATH.
+          binary: codecovcli
+          working-directory: ${{ inputs.output_directory }}
+
+      - name: Uploading integration test results (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/integration.junit.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          # The coverage action will have installed codecovcli with pip. The
+          # actual binary will be found in $PATH.
+          binary: codecovcli
+          working-directory: ${{ inputs.output_directory }}
+
diff --git a/.github/workflows/_self-hosted.yml b/.github/workflows/_self-hosted.yml
new file mode 100644
index 0000000000..19cd74faa1
--- /dev/null
+++ b/.github/workflows/_self-hosted.yml
@@ -0,0 +1,172 @@
+name: Self Hosted Build and Release
+on:
+  workflow_call:
+    inputs:
+      push_rolling:
+        type: boolean
+        default: false
+        description: "Whether to push the rolling image"
+      push_release:
+        type: boolean
+        default: false
+        description: "Whether to push the release image"
+      repo:
+        type: string
+        required: true
+      cache_requirements:
+        type: boolean
+        default: true
+      cache_file:
+        type: string
+        default: "requirements.txt"
+      output_directory:
+        type: string
+        default: .
+      reqs_cache_key:
+        type: string
+        required: false
+      make_target_prefix:
+        type: string
+        default: ""
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  build-self-hosted:
+    name: Build Self Hosted App
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          submodules: 'recursive'
+
+      - id: "auth"
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Cache Requirements
+        id: cache-requirements
+        if: inputs.cache_requirements
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-requirements
+        with:
+          path: |
+            ${{ inputs.output_directory }}/requirements.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.reqs_cache_key || format('{0}-{1}', hashFiles(format('{0}/**/{1}', inputs.output_directory, inputs.cache_file)), hashFiles(format('{0}/**/docker/Dockerfile.requirements', inputs.output_directory))) }}
+          restore-keys: |
+            ${{ runner.os }}-${{ env.cache-name }}-
+
+      - name: Cache Self hosted
+        id: cache-self-hosted
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-self-hosted
+        with:
+          path: |
+            ${{ inputs.output_directory }}/self-hosted-runtime.tar
+            ${{ inputs.output_directory }}/self-hosted.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+
+      - name: Load requirements from cache
+        if: ${{ steps.cache-requirements.outputs.cache-hit == 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.requirements
+
+      - name: Build/pull requirements
+        if: ${{ steps.cache-requirements.outputs.cache-hit != 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.requirements
+
+      - name: Load built image
+        if: ${{ steps.cache-self-hosted.outputs.cache-hit == 'true' }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.self-hosted
+
+      - name: Build self hosted
+        if: ${{ steps.cache-self-hosted.outputs.cache-hit != 'true' }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.self-hosted
+          make ${{ inputs.make_target_prefix }}save.self-hosted
+
+  self-hosted:
+    name: Push Self Hosted Image
+    needs: [build-self-hosted]
+    if: inputs.push_rolling == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    environment: self-hosted
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache Self hosted
+        id: cache-self-hosted
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-self-hosted
+        with:
+          path: |
+            ${{ inputs.output_directory }}/self-hosted-runtime.tar
+            ${{ inputs.output_directory }}/self-hosted.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          make ${{ inputs.make_target_prefix }}load.self-hosted
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push Self Hosted Rolling
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.self-hosted-rolling
+          make ${{ inputs.make_target_prefix }}push.self-hosted-rolling
+  self-hosted-release:
+    name: Push Self Hosted Release Image
+    needs: [build-self-hosted]
+    if: inputs.push_release == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    environment: self-hosted
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache Self hosted
+        id: cache-self-hosted
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-self-hosted
+        with:
+          path: |
+            ${{ inputs.output_directory }}/self-hosted-runtime.tar
+            ${{ inputs.output_directory }}/self-hosted.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          make ${{ inputs.make_target_prefix }}load.self-hosted
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push self hosted release
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.self-hosted-release
+          make ${{ inputs.make_target_prefix }}push.self-hosted-release
+
diff --git a/.github/workflows/api-ci.yml b/.github/workflows/api-ci.yml
index 60d6db979b..10167d2ff2 100644
--- a/.github/workflows/api-ci.yml
+++ b/.github/workflows/api-ci.yml
@@ -20,7 +20,7 @@ jobs:
   api-lint:
     name: Run Lint (API)
     if: ${{ inputs.skip == false }}
-    uses: codecov/gha-workflows/.github/workflows/lint.yml@v1.2.36
+    uses: ./.github/workflows/_lint.yml
     with:
       make_target_prefix: api.
 
@@ -53,7 +53,7 @@ jobs:
     name: Build App (API)
     if: ${{ inputs.skip == false }}
     needs: [api-compute-reqs-cache-key]
-    uses: codecov/gha-workflows/.github/workflows/build-app.yml@v1.2.36
+    uses: ./.github/workflows/_build-app.yml
     secrets: inherit
     with:
       repo: ${{ vars.CODECOV_API_IMAGE_V2 || vars.CODECOV_API_IMAGE_V2_SELF_HOSTED || 'codecov/self-hosted-api' }}
@@ -65,7 +65,7 @@ jobs:
     name: Test (API)
     if: ${{ inputs.skip == false }}
     needs: [api-build]
-    uses: codecov/gha-workflows/.github/workflows/run-tests.yml@v1.2.36
+    uses: ./.github/workflows/_run-tests.yml
     secrets: inherit
     with:
       run_integration: false
@@ -79,7 +79,7 @@ jobs:
     name: Build Self Hosted (API)
     if: ${{ inputs.skip == false }}
     needs: [api-compute-reqs-cache-key, api-build, api-test]
-    uses: codecov/gha-workflows/.github/workflows/self-hosted.yml@v1.2.36
+    uses: ./.github/workflows/_self-hosted.yml
     secrets: inherit
     with:
       repo: ${{ vars.CODECOV_API_IMAGE_V2 || vars.CODECOV_API_IMAGE_V2_SELF_HOSTED || 'codecov/self-hosted-api' }}
@@ -91,7 +91,7 @@ jobs:
     name: Push Staging Image (API)
     needs: [api-build, api-test]
     if: ${{ inputs.skip == false && github.event_name == 'push' && github.event.ref == 'refs/heads/staging' && github.repository_owner == 'codecov' }}
-    uses: codecov/gha-workflows/.github/workflows/push-env.yml@v1.2.37
+    uses: ./.github/workflows/_push-env.yml
     secrets: inherit
     with:
       environment: staging
@@ -104,7 +104,7 @@ jobs:
     name: Push Production Image (API)
     needs: [api-build, api-test]
     if: ${{ inputs.skip == false && github.event_name == 'push' && github.event.ref == 'refs/heads/main' && github.repository_owner == 'codecov' }}
-    uses: codecov/gha-workflows/.github/workflows/push-env.yml@v1.2.37
+    uses: ./.github/workflows/_push-env.yml
     secrets: inherit
     with:
       environment: production
@@ -118,7 +118,7 @@ jobs:
     needs: [api-compute-reqs-cache-key, api-build-self-hosted, api-test]
     secrets: inherit
     if: ${{ inputs.skip == false && github.event_name == 'push' && github.event.ref == 'refs/heads/main' && github.repository_owner == 'codecov' }}
-    uses: codecov/gha-workflows/.github/workflows/self-hosted.yml@v1.2.36
+    uses: ./.github/workflows/_self-hosted.yml
     with:
       push_rolling: true
       repo: ${{ vars.CODECOV_API_IMAGE_V2 || vars.CODECOV_API_IMAGE_V2_SELF_HOSTED || 'codecov/self-hosted-api' }}
diff --git a/.github/workflows/self-hosted-release-pr.yml b/.github/workflows/self-hosted-release-pr.yml
index a01cb559fe..4a71f80c60 100644
--- a/.github/workflows/self-hosted-release-pr.yml
+++ b/.github/workflows/self-hosted-release-pr.yml
@@ -10,5 +10,5 @@ on:
 jobs:
   create-release-pr:
     name: Create PR for Release ${{ github.event.inputs.versionName }}
-    uses: codecov/gha-workflows/.github/workflows/create-release-pr.yml@v1.2.21
+    uses: ./.github/workflows/_create-release-pr.yml
     secrets: inherit
diff --git a/.github/workflows/self-hosted-release.yml b/.github/workflows/self-hosted-release.yml
index 5905f78f23..2b625c09a5 100644
--- a/.github/workflows/self-hosted-release.yml
+++ b/.github/workflows/self-hosted-release.yml
@@ -14,7 +14,7 @@ jobs:
   create-release:
     name: Tag Release ${{ github.head_ref }} and Push Docker image to Docker Hub
     if: ${{ github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/') && github.repository_owner == 'codecov' }}
-    uses: codecov/gha-workflows/.github/workflows/create-release.yml@v1.2.21
+    uses: ./.github/workflows/_create-release.yml
     with:
       tag_to_prepend: self-hosted-
     secrets: inherit
@@ -40,7 +40,7 @@ jobs:
   push-worker-image:
     needs: [create-release]
     if: ${{ github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/') && github.repository_owner == 'codecov' }}
-    uses: codecov/gha-workflows/.github/workflows/self-hosted.yml@v1.2.36
+    uses: ./.github/workflows/_self-hosted.yml
     secrets: inherit
     with:
       push_release: true
@@ -52,7 +52,7 @@ jobs:
   push-api-image:
     needs: [create-release]
     if: ${{ github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/') && github.repository_owner == 'codecov' }}
-    uses: codecov/gha-workflows/.github/workflows/self-hosted.yml@v1.2.36
+    uses: ./.github/workflows/_self-hosted.yml
     secrets: inherit
     with:
       push_release: true
diff --git a/.github/workflows/shared-ci.yml b/.github/workflows/shared-ci.yml
index ce077a4f04..bcba0c6f2a 100644
--- a/.github/workflows/shared-ci.yml
+++ b/.github/workflows/shared-ci.yml
@@ -15,7 +15,7 @@ jobs:
   shared-lint:
     name: Run Lint (Shared)
     if: ${{ inputs.skip == false }}
-    uses: codecov/gha-workflows/.github/workflows/lint.yml@v1.2.36
+    uses: ./.github/workflows/_lint.yml
     with:
       make_target_prefix: shared.
 
diff --git a/.github/workflows/worker-ci.yml b/.github/workflows/worker-ci.yml
index 120fc26bb1..bf4f2e0d9c 100644
--- a/.github/workflows/worker-ci.yml
+++ b/.github/workflows/worker-ci.yml
@@ -20,7 +20,7 @@ jobs:
   worker-lint:
     name: Run Lint (Worker)
     if: ${{ inputs.skip == false }}
-    uses: codecov/gha-workflows/.github/workflows/lint.yml@v1.2.36
+    uses: ./.github/workflows/_lint.yml
     with:
       make_target_prefix: worker.
 
@@ -53,7 +53,7 @@ jobs:
     name: Build App (Worker)
     if: ${{ inputs.skip == false }}
     needs: [worker-compute-reqs-cache-key]
-    uses: codecov/gha-workflows/.github/workflows/build-app.yml@v1.2.36
+    uses: ./.github/workflows/_build-app.yml
     secrets: inherit
     with:
       repo: ${{ vars.CODECOV_WORKER_IMAGE_V2 || vars.CODECOV_WORKER_IMAGE_V2_SELF_HOSTED || 'codecov/self-hosted-worker' }}
@@ -65,7 +65,7 @@ jobs:
     name: Test (Worker)
     if: ${{ inputs.skip == false }}
     needs: [worker-build]
-    uses: codecov/gha-workflows/.github/workflows/run-tests.yml@v1.2.36
+    uses: ./.github/workflows/_run-tests.yml
     secrets: inherit
     with:
       repo: ${{ vars.CODECOV_WORKER_IMAGE_V2 || vars.CODECOV_WORKER_IMAGE_V2_SELF_HOSTED || 'codecov/self-hosted-worker' }}
@@ -78,7 +78,7 @@ jobs:
     name: Build Self Hosted (Worker)
     if: ${{ inputs.skip == false }}
     needs: [worker-compute-reqs-cache-key, worker-build, worker-test]
-    uses: codecov/gha-workflows/.github/workflows/self-hosted.yml@v1.2.36
+    uses: ./.github/workflows/_self-hosted.yml
     secrets: inherit
     with:
       repo: ${{ vars.CODECOV_WORKER_IMAGE_V2 || vars.CODECOV_WORKER_IMAGE_V2_SELF_HOSTED || 'codecov/self-hosted-worker' }}
@@ -90,7 +90,7 @@ jobs:
     name: Push Staging Image (Worker)
     needs: [worker-build, worker-test]
     if: ${{ inputs.skip == false && github.event_name == 'push' && (github.event.ref == 'refs/heads/main' || github.event.ref == 'refs/heads/staging') && github.repository_owner == 'codecov' }}
-    uses: codecov/gha-workflows/.github/workflows/push-env.yml@v1.2.37
+    uses: ./.github/workflows/_push-env.yml
     secrets: inherit
     with:
       environment: staging
@@ -103,7 +103,7 @@ jobs:
     name: Push Production Image (Worker)
     needs: [worker-build, worker-test]
     if: ${{ inputs.skip == false && github.event_name == 'push' && github.event.ref == 'refs/heads/main' && github.repository_owner == 'codecov' }}
-    uses: codecov/gha-workflows/.github/workflows/push-env.yml@v1.2.37
+    uses: ./.github/workflows/_push-env.yml
     secrets: inherit
     with:
       environment: production
@@ -117,7 +117,7 @@ jobs:
     needs: [worker-compute-reqs-cache-key, worker-build-self-hosted, worker-test]
     secrets: inherit
     if: ${{ inputs.skip == false && github.event_name == 'push' && github.event.ref == 'refs/heads/main' && github.repository_owner == 'codecov' }}
-    uses: codecov/gha-workflows/.github/workflows/self-hosted.yml@v1.2.36
+    uses: ./.github/workflows/_self-hosted.yml
     with:
       push_rolling: true
       repo: ${{ vars.CODECOV_WORKER_IMAGE_V2 || vars.CODECOV_WORKER_IMAGE_V2_SELF_HOSTED || 'codecov/self-hosted-worker' }}


Reviews:
user: login: Swatinem
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T08:34:31Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-30T18:36:28Z

Review comments:
diff_hunk: @@ -0,0 +1,94 @@
+name: Build App
+
+on:
+  workflow_call:
+    inputs:
+      repo:
+        type: string
+        required: true
+      cache_requirements:
+        type: boolean
+        default: true
+      output_directory:
+        type: string
+        default: .
+      reqs_cache_key:
+        type: string
+        required: false
+      cache_file:
+        type: string
+        default: "requirements.txt"
+      make_target_prefix:
+        type: string
+        default: ""
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  build:
+    name: Build App
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 2
+          submodules: 'recursive'
+      - id: "auth"
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' }}
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' }}
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Cache Requirements
+        id: cache-requirements
+        if: inputs.cache_requirements
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-requirements
+        with:
+          path: |
+            ${{ inputs.output_directory }}/requirements.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.reqs_cache_key || format('{0}-{1}', hashFiles(format('{0}/**/{1}', inputs.output_directory, inputs.cache_file)), hashFiles(format('{0}/**/docker/Dockerfile.requirements', inputs.output_directory))) }}
+          restore-keys: |
+            ${{ runner.os }}-${{ env.cache-name }}-
+
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+
+      - name: Load requirements from cache
+        if: ${{ steps.cache-requirements.outputs.cache-hit == 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.requirements
+
+      - name: Build/pull requirements
+        if: ${{ steps.cache-requirements.outputs.cache-hit != 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.requirements
+          make ${{ inputs.make_target_prefix }}save.requirements
+
+      - name: Push Requirements
+        if: ${{ steps.cache-requirements.outputs.cache-hit != 'true' && !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}push.requirements
+      - name: Build app
+        run: |
+          make ${{ inputs.make_target_prefix }}build.app
+          make ${{ inputs.make_target_prefix }}save.app
path: .github/workflows/_build-app.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/21)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+name: Create PR for Release
+
+on:
+  workflow_call:
+    inputs:
+      mainBranch:
+        description: 'Name of main branch'
+        required: false
+        type: string
+        default: 'main'
+jobs:
+  create-release-pr:
+    name: Create PR
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          submodules: 'recursive'
+
+      - name: Import GPG key
+        id: import-gpg
+        uses: crazy-max/ghaction-import-gpg@v6
+        with:
+          gpg_private_key: ${{ secrets.RELEASER_GPG_PRIVATE_KEY }}
+          git_user_signingkey: true
+          git_commit_gpgsign: true
+          git_config_global: true
+
+      - name: Create release branch
+        run: git checkout -b release/${{ github.event.inputs.versionName }}
+      - name: Update version file
+        id: make-commit
+        run: |
+          echo ${{ github.event.inputs.versionName }} > VERSION
+          echo -n $(tr -d '\n' < VERSION) > VERSION
+          git add VERSION
+          git commit -S --message "Prepare release ${{ github.event.inputs.versionName }}"
+          echo "commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
+
+      - name: Push release branch
+        run: git push origin release/${{ github.event.inputs.versionName }}
+      - name: Create pull request into main
+        uses: thomaseizinger/create-pull-request@1.3.1
+        with:
+          github_token: ${{ secrets.GH_RELEASE_TOKEN }}
+          head: release/${{ github.event.inputs.versionName }}
+          base: ${{ inputs.mainBranch }}
+          title: Release ${{ github.event.inputs.versionName }}
+          reviewers: ${{ github.event.issue.user.login }}
+          body: |
+            Release PR for ${{ github.event.inputs.versionName }}
+            I've updated the version name and committed: ${{ steps.make-commit.outputs.commit }}.
path: .github/workflows/_create-release-pr.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/18)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,29 @@
+name: Create GitHub Release
+
+on:
+  workflow_call:
+    inputs:
+      tag_to_prepend:
+        type: string
+        required: false
+        default: ''
+        description: 'Tag prepend value. Ex: self-hosted-'
+jobs:
+  create-release:
+    name: Create Github Release
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - id: get-release-vars
+        name: Configure Release Vars
+        run: |
+          echo "release_version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
+
+      - name: Create GitHub Release
+        env:
+          GITHUB_TOKEN: ${{ secrets.CODECOV_RELEASE_PAT }}
+        run: |
+          gh release create ${{ inputs.tag_to_prepend }}${{ steps.get-release-vars.outputs.release_version }} --title "Release ${{ steps.get-release-vars.outputs.release_version }}" --notes "Autogenerated for ${{ steps.get-release-vars.outputs.release_version }}. Created for ${{ github.event.pull_request.html_url }}" --generate-notes
path: .github/workflows/_create-release.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/20)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,20 @@
+name: Run Lint
+
+on:
+  workflow_call:
+    inputs:
+      make_target_prefix:
+        type: string
+        default: ""
+
+jobs:
+  lint:
+    name: Run Lint
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Install dependencies
+        run: make ${{ inputs.make_target_prefix }}lint.install
+      - name: Check
+        run: make ${{ inputs.make_target_prefix }}lint.check
path: .github/workflows/_lint.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/19)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,162 @@
+name: Push Image for Environment
+
+on:
+  workflow_call:
+    inputs:
+      environment:
+        required: true
+        type: string
+      repo:
+        type: string
+        required: true
+      create_sentry_release:
+        type: boolean
+        required: false
+        default: true
+      push_rolling:
+        type: boolean
+        default: false
+        description: "Whether to push the rolling image"
+      push_release:
+        type: boolean
+        default: false
+        description: "Whether to push the release image"
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+      sentry_project:
+        type: string
+        required: false
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  push-environment:
+    name: Push ${{ inputs.environment }} Image
+    runs-on: ubuntu-latest
+    environment: ${{ inputs.environment }}
+    if: github.repository_owner == 'codecov' && !inputs.push_rolling && !inputs.push_release
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Get Release SHA
+        env:
+          SHA: ${{ github.sha }}
+        id: sha
+        run: echo short_sha="${SHA:0:7}" >> $GITHUB_OUTPUT
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - id: "auth"
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Push ${{ inputs.environment }}
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.${{ inputs.environment }}
+          make ${{ inputs.make_target_prefix }}push.${{ inputs.environment }}
+      - name: Push latest
+        if: inputs.environment == 'production'
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.latest
+          make ${{ inputs.make_target_prefix }}push.latest
+
+      - name: Create Sentry release
+        if: inputs.create_sentry_release
+        uses: getsentry/action-release@v1
+        env:
+          SENTRY_AUTH_TOKEN: ${{ secrets.CODECOV_SENTRY_RELEASE_TOKEN }}
+          SENTRY_ORG: ${{ secrets.CODECOV_SENTRY_ORG }}
+          SENTRY_PROJECT: ${{ inputs.sentry_project != '' && inputs.sentry_project || secrets.CODECOV_SENTRY_PROJECT }}
+        with:
+          environment: ${{ inputs.environment }}
+          version: ${{ inputs.environment }}-release-${{ steps.sha.outputs.short_sha }}
+          ignore_missing: true
+          output_directory: ${{ inputs.output_directory }}
+  rolling:
path: .github/workflows/_push-env.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/22)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,162 @@
+name: Push Image for Environment
+
+on:
+  workflow_call:
+    inputs:
+      environment:
+        required: true
+        type: string
+      repo:
+        type: string
+        required: true
+      create_sentry_release:
+        type: boolean
+        required: false
+        default: true
+      push_rolling:
+        type: boolean
+        default: false
+        description: "Whether to push the rolling image"
+      push_release:
+        type: boolean
+        default: false
+        description: "Whether to push the release image"
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+      sentry_project:
+        type: string
+        required: false
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  push-environment:
+    name: Push ${{ inputs.environment }} Image
+    runs-on: ubuntu-latest
+    environment: ${{ inputs.environment }}
+    if: github.repository_owner == 'codecov' && !inputs.push_rolling && !inputs.push_release
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Get Release SHA
+        env:
+          SHA: ${{ github.sha }}
+        id: sha
+        run: echo short_sha="${SHA:0:7}" >> $GITHUB_OUTPUT
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - id: "auth"
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Push ${{ inputs.environment }}
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.${{ inputs.environment }}
+          make ${{ inputs.make_target_prefix }}push.${{ inputs.environment }}
+      - name: Push latest
+        if: inputs.environment == 'production'
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.latest
+          make ${{ inputs.make_target_prefix }}push.latest
+
+      - name: Create Sentry release
+        if: inputs.create_sentry_release
+        uses: getsentry/action-release@v1
+        env:
+          SENTRY_AUTH_TOKEN: ${{ secrets.CODECOV_SENTRY_RELEASE_TOKEN }}
+          SENTRY_ORG: ${{ secrets.CODECOV_SENTRY_ORG }}
+          SENTRY_PROJECT: ${{ inputs.sentry_project != '' && inputs.sentry_project || secrets.CODECOV_SENTRY_PROJECT }}
+        with:
+          environment: ${{ inputs.environment }}
+          version: ${{ inputs.environment }}-release-${{ steps.sha.outputs.short_sha }}
+          ignore_missing: true
+          output_directory: ${{ inputs.output_directory }}
+  rolling:
+    name: Push Rolling Image
+    if: inputs.push_rolling == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push Rolling
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.rolling
+          make ${{ inputs.make_target_prefix }}push.rolling
+  release:
path: .github/workflows/_push-env.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/26)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,162 @@
+name: Push Image for Environment
+
+on:
+  workflow_call:
+    inputs:
+      environment:
+        required: true
+        type: string
+      repo:
+        type: string
+        required: true
+      create_sentry_release:
+        type: boolean
+        required: false
+        default: true
+      push_rolling:
+        type: boolean
+        default: false
+        description: "Whether to push the rolling image"
+      push_release:
+        type: boolean
+        default: false
+        description: "Whether to push the release image"
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+      sentry_project:
+        type: string
+        required: false
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  push-environment:
+    name: Push ${{ inputs.environment }} Image
+    runs-on: ubuntu-latest
+    environment: ${{ inputs.environment }}
+    if: github.repository_owner == 'codecov' && !inputs.push_rolling && !inputs.push_release
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Get Release SHA
+        env:
+          SHA: ${{ github.sha }}
+        id: sha
+        run: echo short_sha="${SHA:0:7}" >> $GITHUB_OUTPUT
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - id: "auth"
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Push ${{ inputs.environment }}
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.${{ inputs.environment }}
+          make ${{ inputs.make_target_prefix }}push.${{ inputs.environment }}
+      - name: Push latest
+        if: inputs.environment == 'production'
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.latest
+          make ${{ inputs.make_target_prefix }}push.latest
+
+      - name: Create Sentry release
+        if: inputs.create_sentry_release
+        uses: getsentry/action-release@v1
+        env:
+          SENTRY_AUTH_TOKEN: ${{ secrets.CODECOV_SENTRY_RELEASE_TOKEN }}
+          SENTRY_ORG: ${{ secrets.CODECOV_SENTRY_ORG }}
+          SENTRY_PROJECT: ${{ inputs.sentry_project != '' && inputs.sentry_project || secrets.CODECOV_SENTRY_PROJECT }}
+        with:
+          environment: ${{ inputs.environment }}
+          version: ${{ inputs.environment }}-release-${{ steps.sha.outputs.short_sha }}
+          ignore_missing: true
+          output_directory: ${{ inputs.output_directory }}
+  rolling:
+    name: Push Rolling Image
+    if: inputs.push_rolling == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push Rolling
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.rolling
+          make ${{ inputs.make_target_prefix }}push.rolling
+  release:
+    name: Push Release Image
+    if: inputs.push_release == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push release
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.release
+          make ${{ inputs.make_target_prefix }}push.release
path: .github/workflows/_push-env.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/27)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/23)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading integration test coverage (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/integration.coverage.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading unit test results (${{ matrix.name }})
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/unit.junit.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          # The coverage action will have installed codecovcli with pip. The
+          # actual binary will be found in $PATH.
+          binary: codecovcli
+          working-directory: ${{ inputs.output_directory }}
+
+      - name: Uploading integration test results (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/integration.junit.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          # The coverage action will have installed codecovcli with pip. The
+          # actual binary will be found in $PATH.
+          binary: codecovcli
+          working-directory: ${{ inputs.output_directory }}
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/25)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Excessive Secrets Exposure

All organization and repository secrets are passed to the workflow runner in [secrets\[matrix.codecov_token_secret\]](1)

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/32)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Excessive Secrets Exposure

All organization and repository secrets are passed to the workflow runner in [secrets\[matrix.codecov_url_secret\]](1)

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/33)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading integration test coverage (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/integration.coverage.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Excessive Secrets Exposure

All organization and repository secrets are passed to the workflow runner in [secrets\[matrix.codecov_token_secret\]](1)

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/34)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading integration test coverage (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/integration.coverage.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Excessive Secrets Exposure

All organization and repository secrets are passed to the workflow runner in [secrets\[matrix.codecov_url_secret\]](1)

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/35)
created_at: 2025-04-30T18:36:27Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading integration test coverage (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/integration.coverage.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading unit test results (${{ matrix.name }})
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/unit.junit.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Excessive Secrets Exposure

All organization and repository secrets are passed to the workflow runner in [secrets\[matrix.codecov_token_secret\]](1)

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/36)
created_at: 2025-04-30T18:36:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading integration test coverage (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/integration.coverage.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading unit test results (${{ matrix.name }})
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/unit.junit.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Excessive Secrets Exposure

All organization and repository secrets are passed to the workflow runner in [secrets\[matrix.codecov_url_secret\]](1)

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/37)
created_at: 2025-04-30T18:36:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading integration test coverage (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/integration.coverage.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading unit test results (${{ matrix.name }})
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/unit.junit.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          # The coverage action will have installed codecovcli with pip. The
+          # actual binary will be found in $PATH.
+          binary: codecovcli
+          working-directory: ${{ inputs.output_directory }}
+
+      - name: Uploading integration test results (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/integration.junit.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Excessive Secrets Exposure

All organization and repository secrets are passed to the workflow runner in [secrets\[matrix.codecov_token_secret\]](1)

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/38)
created_at: 2025-04-30T18:36:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,179 @@
+name: Run Tests
+
+on:
+  workflow_call:
+    inputs:
+      run_integration:
+        required: false
+        type: boolean
+        default: true
+      repo:
+        type: string
+        required: true
+      flag_prefix:
+        type: string
+        default: ''
+      pytest_rootdir:
+        type: string
+        default: .
+      output_directory:
+        type: string
+        default: .
+      make_target_prefix:
+        type: string
+        default: ""
+    outputs:
+      tests_passed:
+        # Silly issue with returning job results as workflow outputs
+        # https://github.com/actions/runner/issues/2495
+        value: ${{ fromJSON(toJSON(jobs.test)).result }}
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  test:
+    name: Test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache App
+        id: cache-app
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-app
+        with:
+          path: |
+            ${{ inputs.output_directory }}/app.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          docker load --input ${{ inputs.output_directory }}/app.tar
+      - name: Install docker compose
+        run: |
+          sudo curl -SL https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
+          sudo chmod +x /usr/local/bin/docker-compose
+      - name: Bring test env up
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.up
+      - name: Prepare for tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.prepare
+          make ${{ inputs.make_target_prefix }}test_env.check_db
+      - name: Run unit tests
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_unit PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+      - name: Run integration tests
+        if: ${{ !cancelled() && inputs.run_integration == true }}
+        run: |
+          make ${{ inputs.make_target_prefix }}test_env.run_integration PYTEST_ROOTDIR=${{ inputs.pytest_rootdir }}
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+          path: ${{ inputs.output_directory }}/*.coverage.xml
+
+      - uses: actions/upload-artifact@v4
+        if: ${{ !cancelled() }}
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+          path: ${{ inputs.output_directory }}/*junit*.xml
+
+  upload:
+    name: Upload to Codecov
+    runs-on: ubuntu-latest
+    needs: [test]
+    strategy:
+      matrix:
+        include:
+          - codecov_url_secret: CODECOV_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN
+            name: prod
+          - codecov_url_secret: CODECOV_STAGING_URL
+            codecov_token_secret: CODECOV_ORG_TOKEN_STAGING
+            name: staging
+          - codecov_url_secret: CODECOV_QA_URL
+            codecov_token_secret: CODECOV_QA_ORG
+            name: qa
+          - codecov_url_secret: CODECOV_PUBLIC_QA_URL
+            codecov_token_secret: CODECOV_PUBLIC_QA_TOKEN
+            name: public qa
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+
+      - name: Download coverage
+        id: download_coverage
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-coveragefiles
+
+      - name: Download test results
+        id: download_test_results
+        uses: actions/download-artifact@v4
+        with:
+          name: ${{ inputs.flag_prefix }}-junitfiles
+
+      - name: Uploading unit test coverage (${{ matrix.name }})
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/unit.coverage.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading integration test coverage (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/codecov-action@v5
+        with:
+          files: ${{ steps.download_coverage.outputs.download-path }}/integration.coverage.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          # Strange workaround: API has a `codecov` directory in the repo root
+          # which conflicts with the action's `codecov` binary
+          use_pypi: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          recurse_submodules: true
+
+      - name: Uploading unit test results (${{ matrix.name }})
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/unit.junit.xml
+          flags: ${{ format('{0}unit', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
+          # The coverage action will have installed codecovcli with pip. The
+          # actual binary will be found in $PATH.
+          binary: codecovcli
+          working-directory: ${{ inputs.output_directory }}
+
+      - name: Uploading integration test results (${{ matrix.name }})
+        if: ${{ inputs.run_integration == true }}
+        uses: codecov/test-results-action@v1
+        with:
+          files: ${{ steps.download_test_results.outputs.download-path }}/integration.junit.xml
+          flags: ${{ format('{0}integration', inputs.flag_prefix) }}
+          disable_search: true
+          token: ${{ secrets[matrix.codecov_token_secret] }}
+          url: ${{ secrets[matrix.codecov_url_secret] }}
path: .github/workflows/_run-tests.yml
user: login: github-advanced-security[bot]
body: ## Excessive Secrets Exposure

All organization and repository secrets are passed to the workflow runner in [secrets\[matrix.codecov_url_secret\]](1)

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/39)
created_at: 2025-04-30T18:36:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,172 @@
+name: Self Hosted Build and Release
+on:
+  workflow_call:
+    inputs:
+      push_rolling:
+        type: boolean
+        default: false
+        description: "Whether to push the rolling image"
+      push_release:
+        type: boolean
+        default: false
+        description: "Whether to push the release image"
+      repo:
+        type: string
+        required: true
+      cache_requirements:
+        type: boolean
+        default: true
+      cache_file:
+        type: string
+        default: "requirements.txt"
+      output_directory:
+        type: string
+        default: .
+      reqs_cache_key:
+        type: string
+        required: false
+      make_target_prefix:
+        type: string
+        default: ""
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  build-self-hosted:
+    name: Build Self Hosted App
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          submodules: 'recursive'
+
+      - id: "auth"
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Cache Requirements
+        id: cache-requirements
+        if: inputs.cache_requirements
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-requirements
+        with:
+          path: |
+            ${{ inputs.output_directory }}/requirements.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.reqs_cache_key || format('{0}-{1}', hashFiles(format('{0}/**/{1}', inputs.output_directory, inputs.cache_file)), hashFiles(format('{0}/**/docker/Dockerfile.requirements', inputs.output_directory))) }}
+          restore-keys: |
+            ${{ runner.os }}-${{ env.cache-name }}-
+
+      - name: Cache Self hosted
+        id: cache-self-hosted
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-self-hosted
+        with:
+          path: |
+            ${{ inputs.output_directory }}/self-hosted-runtime.tar
+            ${{ inputs.output_directory }}/self-hosted.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+
+      - name: Load requirements from cache
+        if: ${{ steps.cache-requirements.outputs.cache-hit == 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.requirements
+
+      - name: Build/pull requirements
+        if: ${{ steps.cache-requirements.outputs.cache-hit != 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.requirements
+
+      - name: Load built image
+        if: ${{ steps.cache-self-hosted.outputs.cache-hit == 'true' }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.self-hosted
+
+      - name: Build self hosted
+        if: ${{ steps.cache-self-hosted.outputs.cache-hit != 'true' }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.self-hosted
+          make ${{ inputs.make_target_prefix }}save.self-hosted
+
+  self-hosted:
path: .github/workflows/_self-hosted.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/24)
created_at: 2025-04-30T18:36:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,172 @@
+name: Self Hosted Build and Release
+on:
+  workflow_call:
+    inputs:
+      push_rolling:
+        type: boolean
+        default: false
+        description: "Whether to push the rolling image"
+      push_release:
+        type: boolean
+        default: false
+        description: "Whether to push the release image"
+      repo:
+        type: string
+        required: true
+      cache_requirements:
+        type: boolean
+        default: true
+      cache_file:
+        type: string
+        default: "requirements.txt"
+      output_directory:
+        type: string
+        default: .
+      reqs_cache_key:
+        type: string
+        required: false
+      make_target_prefix:
+        type: string
+        default: ""
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  build-self-hosted:
+    name: Build Self Hosted App
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          submodules: 'recursive'
+
+      - id: "auth"
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Cache Requirements
+        id: cache-requirements
+        if: inputs.cache_requirements
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-requirements
+        with:
+          path: |
+            ${{ inputs.output_directory }}/requirements.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.reqs_cache_key || format('{0}-{1}', hashFiles(format('{0}/**/{1}', inputs.output_directory, inputs.cache_file)), hashFiles(format('{0}/**/docker/Dockerfile.requirements', inputs.output_directory))) }}
+          restore-keys: |
+            ${{ runner.os }}-${{ env.cache-name }}-
+
+      - name: Cache Self hosted
+        id: cache-self-hosted
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-self-hosted
+        with:
+          path: |
+            ${{ inputs.output_directory }}/self-hosted-runtime.tar
+            ${{ inputs.output_directory }}/self-hosted.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+
+      - name: Load requirements from cache
+        if: ${{ steps.cache-requirements.outputs.cache-hit == 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.requirements
+
+      - name: Build/pull requirements
+        if: ${{ steps.cache-requirements.outputs.cache-hit != 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.requirements
+
+      - name: Load built image
+        if: ${{ steps.cache-self-hosted.outputs.cache-hit == 'true' }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.self-hosted
+
+      - name: Build self hosted
+        if: ${{ steps.cache-self-hosted.outputs.cache-hit != 'true' }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.self-hosted
+          make ${{ inputs.make_target_prefix }}save.self-hosted
+
+  self-hosted:
+    name: Push Self Hosted Image
+    needs: [build-self-hosted]
+    if: inputs.push_rolling == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    environment: self-hosted
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache Self hosted
+        id: cache-self-hosted
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-self-hosted
+        with:
+          path: |
+            ${{ inputs.output_directory }}/self-hosted-runtime.tar
+            ${{ inputs.output_directory }}/self-hosted.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          make ${{ inputs.make_target_prefix }}load.self-hosted
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push Self Hosted Rolling
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.self-hosted-rolling
+          make ${{ inputs.make_target_prefix }}push.self-hosted-rolling
+  self-hosted-release:
path: .github/workflows/_self-hosted.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/28)
created_at: 2025-04-30T18:36:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,172 @@
+name: Self Hosted Build and Release
+on:
+  workflow_call:
+    inputs:
+      push_rolling:
+        type: boolean
+        default: false
+        description: "Whether to push the rolling image"
+      push_release:
+        type: boolean
+        default: false
+        description: "Whether to push the release image"
+      repo:
+        type: string
+        required: true
+      cache_requirements:
+        type: boolean
+        default: true
+      cache_file:
+        type: string
+        default: "requirements.txt"
+      output_directory:
+        type: string
+        default: .
+      reqs_cache_key:
+        type: string
+        required: false
+      make_target_prefix:
+        type: string
+        default: ""
+
+env:
+  AR_REPO: ${{ inputs.repo }}
+
+jobs:
+  build-self-hosted:
+    name: Build Self Hosted App
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          submodules: 'recursive'
+
+      - id: "auth"
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        name: "Authenticate to Google Cloud"
+        uses: "google-github-actions/auth@v2.1.2"
+        with:
+          token_format: "access_token"
+          workload_identity_provider: ${{ secrets.CODECOV_GCP_WIDP }}
+          service_account: ${{ secrets.CODECOV_GCP_WIDSA }}
+
+      - name: Docker configuration
+        if: ${{ !github.event.pull_request.head.repo.fork && github.repository_owner == 'codecov' && inputs.cache_requirements }}
+        run: |-
+          echo ${{steps.auth.outputs.access_token}} | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev
+
+      - name: Cache Requirements
+        id: cache-requirements
+        if: inputs.cache_requirements
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-requirements
+        with:
+          path: |
+            ${{ inputs.output_directory }}/requirements.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.reqs_cache_key || format('{0}-{1}', hashFiles(format('{0}/**/{1}', inputs.output_directory, inputs.cache_file)), hashFiles(format('{0}/**/docker/Dockerfile.requirements', inputs.output_directory))) }}
+          restore-keys: |
+            ${{ runner.os }}-${{ env.cache-name }}-
+
+      - name: Cache Self hosted
+        id: cache-self-hosted
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-self-hosted
+        with:
+          path: |
+            ${{ inputs.output_directory }}/self-hosted-runtime.tar
+            ${{ inputs.output_directory }}/self-hosted.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+
+      - name: Load requirements from cache
+        if: ${{ steps.cache-requirements.outputs.cache-hit == 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.requirements
+
+      - name: Build/pull requirements
+        if: ${{ steps.cache-requirements.outputs.cache-hit != 'true' && inputs.cache_requirements }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.requirements
+
+      - name: Load built image
+        if: ${{ steps.cache-self-hosted.outputs.cache-hit == 'true' }}
+        run: |
+          make ${{ inputs.make_target_prefix }}load.self-hosted
+
+      - name: Build self hosted
+        if: ${{ steps.cache-self-hosted.outputs.cache-hit != 'true' }}
+        run: |
+          make ${{ inputs.make_target_prefix }}build.self-hosted
+          make ${{ inputs.make_target_prefix }}save.self-hosted
+
+  self-hosted:
+    name: Push Self Hosted Image
+    needs: [build-self-hosted]
+    if: inputs.push_rolling == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    environment: self-hosted
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache Self hosted
+        id: cache-self-hosted
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-self-hosted
+        with:
+          path: |
+            ${{ inputs.output_directory }}/self-hosted-runtime.tar
+            ${{ inputs.output_directory }}/self-hosted.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          make ${{ inputs.make_target_prefix }}load.self-hosted
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push Self Hosted Rolling
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.self-hosted-rolling
+          make ${{ inputs.make_target_prefix }}push.self-hosted-rolling
+  self-hosted-release:
+    name: Push Self Hosted Release Image
+    needs: [build-self-hosted]
+    if: inputs.push_release == true && github.repository_owner == 'codecov' && !github.event.pull_request.head.repo.fork
+    runs-on: ubuntu-latest
+    environment: self-hosted
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          submodules: 'recursive'
+      - name: Cache Self hosted
+        id: cache-self-hosted
+        uses: actions/cache@v4
+        env:
+          cache-name: ${{ inputs.repo }}-self-hosted
+        with:
+          path: |
+            ${{ inputs.output_directory }}/self-hosted-runtime.tar
+            ${{ inputs.output_directory }}/self-hosted.tar
+          key: ${{ runner.os }}-${{ env.cache-name }}-${{ github.run_id }}
+      - name: Load built image
+        run: |
+          make ${{ inputs.make_target_prefix }}load.self-hosted
+      - name: Log in to Docker Hub
+        uses: docker/login-action@v3.1.0
+        with:
+          username: ${{ secrets.CODECOV_DEVOPS_DOCKER_USERNAME }}
+          password: ${{ secrets.CODECOV_DEVOPS_DOCKER_PASSWORD }}
+      - name: Push self hosted release
+        run: |
+          make ${{ inputs.make_target_prefix }}tag.self-hosted-release
+          make ${{ inputs.make_target_prefix }}push.self-hosted-release
path: .github/workflows/_self-hosted.yml
user: login: github-advanced-security[bot]
body: ## Workflow does not contain permissions

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

[Show more details](https://github.com/codecov/umbrella/security/code-scanning/29)
created_at: 2025-04-30T18:36:28Z
author_association: NONE

PR comments:
user: login: seer-by-sentry[bot]
created_at: 2025-04-30T00:07:17Z
body: ### :white_check_mark: Sentry found no issues in your recent changes :white_check_mark:

author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-04-30T00:09:52Z
body: ## [Codecov](https://app.codecov.io/gh/codecov/umbrella/pull/88?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.25%. Comparing base [(`6aaad25`)](https://app.codecov.io/gh/codecov/umbrella/commit/6aaad259d379038d7f8e0cf6c12186cf41118efa?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) to head [(`f067294`)](https://app.codecov.io/gh/codecov/umbrella/commit/f0672947dbb3b915560cc182f4b55528f36d81dd?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov).
> Report is 2 commits behind head on main.

:white_check_mark: All tests successful. No failed tests found.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main      #88   +/-   ##
=======================================
  Coverage   94.25%   94.25%           
=======================================
  Files        1210     1210           
  Lines       45688    45688           
  Branches     1449     1449           
=======================================
  Hits        43065    43065           
  Misses       2317     2317           
  Partials      306      306           
```

| [Flag](https://app.codecov.io/gh/codecov/umbrella/pull/88/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | Coverage Δ | |
|---|---|---|
| [apiunit](https://app.codecov.io/gh/codecov/umbrella/pull/88/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `96.41% <ø> (ø)` | |
| [shared-docker-uploader](https://app.codecov.io/gh/codecov/umbrella/pull/88/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `88.82% <ø> (ø)` | |
| [workerintegration](https://app.codecov.io/gh/codecov/umbrella/pull/88/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `61.19% <ø> (ø)` | |
| [workerunit](https://app.codecov.io/gh/codecov/umbrella/pull/88/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov) | `91.33% <ø> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/codecov/umbrella/pull/88?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov).

author_association: NONE
user: login: codecov-notifications[bot]
created_at: 2025-04-30T00:09:59Z
body: ## [Codecov](https://stage-app.codecov.dev/gh/codecov/umbrella/pull/88?dropdown=coverage&src=pr&el=h1) Report
All modified and coverable lines are covered by tests :white_check_mark:

:white_check_mark: All tests successful. No failed tests found.


:loudspeaker: Thoughts on this report? [Let us know!](https://about.codecov.io/pull-request-comment-report/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=codecov)

author_association: NONE
user: login: codspeed-hq[bot]
created_at: 2025-04-30T00:14:44Z
body: <!-- __CODSPEED_PERFORMANCE_REPORT_COMMENT__ -->
<!-- __CODSPEED_INSTRUMENTATION_PERFORMANCE_REPORT_COMMENT__ -->

## [CodSpeed Performance Report](https://codspeed.io/codecov/umbrella/branches/branchless%2Fpr88)

### Merging #88 will **not alter performance**

<sub>Comparing <code>branchless/pr88</code> (f067294) with <code>main</code> (6aaad25)</sub>



### Summary

`✅ 9` untouched benchmarks  




author_association: NONE