# Fan-in to a single required GitHub Action

DevFeed: [Fan-in to a single required GitHub Action](<https://devfeed.tech/articles/fan-in-to-a-single-required-github-action-20936.md>)

Original publisher: [Read original article](<https://jakewharton.com/fan-in-to-a-single-required-github-action/>)

Published: 2025-05-07T00:00:00Z

Content type: tutorial

Language: en

Sources: [Jake Wharton](<https://devfeed.tech/sources/jake-wharton.md>)

Topics: [GitHub Actions](<https://devfeed.tech/topics/github-actions.md>), [GitHub](<https://devfeed.tech/topics/github.md>), [Pull Request](<https://devfeed.tech/topics/pull-request.md>), [issue tracker](<https://devfeed.tech/topics/issue-tracker.md>)

Tags: [github](<https://devfeed.tech/tags/github.md>), [github-actions](<https://devfeed.tech/tags/github-actions.md>), [jobs](<https://devfeed.tech/tags/jobs.md>), [pull-request](<https://devfeed.tech/tags/pull-request.md>)

## AI overview

This article explains how to fan in multiple GitHub Actions jobs into one required status check for pull requests. It addresses the problem of skipped dependent jobs being reported as successful and shows how to make a final job run unless canceled, verify the results of its required jobs, and use that job as the sole required check. It also describes a simpler failure-only alternative with fewer extension options.

## Source excerpt

It doesn't take long for a project to spawn multiple jobs in their GitHub Actions. Parallelization can lead to huge speedups for PRs. Job grouping makes it easier to conditionally enable or disable multiple steps. Each time you add a new job, however, you have to mark it as required in branch protection to prevent failing PRs from accidentally merging. Being a clever person, you might create a final job which lists all the other jobs as required, and then mark that as the single required job. jobs: # ... final-status: needs: - build - unit-tests - emulator-tests - screenshot-tests # ... Unfortunately, this does not work in practice. GitHub will skip the 'final-status' job if any of its 'needs' fail, and skipped jobs are treated as passing according to the docs: A job that is skipped will report its status as "Success". It will not prevent a pull request from merging, even if it is a required check. To work around this undesirable behavior, first, change the job to always run (unless canceled): final-status: + if: ${{ !cancelled() }} needs: - build - unit-tests - emulator-tests - screenshot-tests ... Next, add a step which ensures the status of each 'needs' job was successful: steps: - name: Check run: | results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}') if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then echo "One or more required jobs failed" exit 1 fi Finally, you can mark this job the only required one. It will now successfully reflect the status of all jobs. You can also hang additional steps on it, or even entire subsequent jobs (provided they aren't needed for PRs). I'm using this setup on a few repos such as Mosaic where you can also see a downstream 'publish' job which only runs on the integration branch. An alternative is to have a final job which only runs when one of its 'needs' fails and then to fail itself. An example of this strategy was posted on the Actions issue tracker. This approach is simpler, but precludes any additional steps or jobs