188

I have a monorepo with two workflows:

.github/workflows/test.yml

name: test

on: [push, pull_request]

jobs:
  test-packages:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: test packages
        run: |
          yarn install
          yarn test
...

.github/workflows/deploy.yml

name: deploy

on:
  push:
    tags:
      - "*"

jobs:
  deploy-packages:
    runs-on: ubuntu-latest
    needs: test-packages
    steps:
      - uses: actions/checkout@v1
      - name: deploy packages
        run: |
          yarn deploy
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
...

This doesn't work, I can't reference a job in another workflow:

### ERRORED 19:13:07Z

- Your workflow file was invalid: The pipeline is not valid. The pipeline must contain at least one job with no dependencies.

Is there a way to create a dependency between workflows?

What I want is to run test.yml then deploy.yml on tags, and test.yml only on push and pull requests. I don't want to duplicate jobs between workflows.

Greg Dubicki
  • 5,983
  • 3
  • 55
  • 68
Guillaume Vincent
  • 13,355
  • 13
  • 76
  • 103

5 Answers5

190

Now it's possible to have dependencies between workflows on Github Actions using workflow_run.

Using this config, the Release workflow will work when the Run Tests workflow is completed.

name: Release
on:
  workflow_run:
    workflows: ["Run Tests"]
    branches: [main]
    types: 
      - completed
Ahmed AbouZaid
  • 2,151
  • 1
  • 13
  • 9
  • 151
    It's important to know that the `Release` workflow in your example will run whenever the `Run Tests` workflow completes, regardless of whether it succeeds or fails. You need to inspect `github.event.workflow_run.conclusion` if workflow outcome is important (which is usually is). More details here: https://github.community/t/workflow-run-completed-event-triggered-by-failed-workflow/128001 – John Blackbourn Dec 08 '20 at 19:49
  • 3
    Can we give two workflow names in workflows? I have to trigger this after completion of two other workflows. – lakshmiravali rimmalapudi Oct 11 '21 at 09:11
  • @lakshmiravalirimmalapudi You can give two names, but that only means that it will trigger when *either* of those workflows completes, not when both of them complete. – Michael Mior Sep 09 '22 at 19:27
  • 3
    To anyone looking to use this, it only runs on the default branch and not the branch that actually triggered the workflow that this one depends on ( see notes about this [here](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run) ) – Dave Johansen Feb 03 '23 at 18:37
  • @DaveJohansen So to be clear, even if "Run Tests" was run on a branch (created for a PR, and assuming there were no branches: filter in the above), the "Release" workflow is run from main/master/etc.? – Brian Mar 13 '23 at 17:12
  • Yes, that is correct. It's always run on the default branch regardless of the branch that the task is started for – Dave Johansen Mar 14 '23 at 18:50
82

This is possible with a combination of workflow_call and needs. Adding workflow_call to the on values of a job allows it to be called by other jobs. You can then call that job from the other workflow, and use needs to force future steps to depend on the success of that job. You can read about making workflows callable here: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onworkflow_call and requiring a step to succeed before triggering future steps here: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds

So for your case, the following should work:

# cat .github/workflows/tests.yml
name: tests
on: [workflow_call]  # allow this workflow to be called from other workflows
jobs:
  ...

# cat .github/workflows/deploy.yml
name: deploy
on:
  push:
    tags:  # trigger the deploy job on tag creation
    - *
jobs:
  tests:
    uses: ./.github/workflows/tests.yml  # use the callable tests job to run tests
  deploy:
    name: deploy
    needs: [tests]  # require tests to pass before deploy runs
    ...
bstpierre
  • 30,042
  • 15
  • 70
  • 103
jlhasson
  • 1,516
  • 1
  • 15
  • 23
  • 4
    This is a great solution. Very clean, and in my opinion, *the* solution. Especially as this will stop other steps from running – Spencer Pollock Dec 08 '22 at 07:07
  • 2
    Beware, secrets and params are not automatically forwarded. You can use and the magic `secrets: inherit` syntax at the same level as `uses:`. Refer to https://docs.github.com/en/actions/using-workflows/reusing-workflows#using-inputs-and-secrets-in-a-reusable-workflow – Cyril Duchon-Doris Apr 28 '23 at 14:50
  • The drawback to this approach is that workflows included by other workflows like this will not get an entry in their respective workflow run logs, but only in the logs of the calling run. – Appleshell May 19 '23 at 15:30
46

You can (also) do it by combining workflow_run and if.


Using the below config, the deploy workflow will start only when all of these conditions are true:

  1. after the test workflow is completed,
  2. if the test workflow was successful,
  3. Tere was a tag pushed to the default branch,

Assuming that the default branch is main:

name: deploy

on:
  # the 1st condition
  workflow_run:
    workflows: ["tests"]
    branches: [main]
    types:
      - completed

jobs:
  deploy-packages:
    # the 2nd condition
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    (...)

....BUT unfortunately the 3rd condition cannot be checked this way as the deploy workflow is triggered in a context of the HEAD of the default branch, without the knowledge about the tag that may be pointing there.

So doing something like:

    if: ${{ github.event.workflow_run.conclusion == 'success' }} && startsWith(github.ref, 'refs/tags/') }}

...will NOT work.


I will update this answer when I find out the workaround for this issue.

Greg Dubicki
  • 5,983
  • 3
  • 55
  • 68
9

It seems that the Wait n Check action is currently the optimal workaround for this missing feature, as it declares in its README:

It allows to work around a GitHub Actions limitation of non-interdependent workflows (we can only depend on jobs inside a single workflow).

UPDATE: see also my other answer for a partial solution using workflow_run.

Greg Dubicki
  • 5,983
  • 3
  • 55
  • 68
  • That action just seems to wait like a timeout and not actually waiting on a workflow finishing before letting the succinct jobs starting. – Maxie Berkmann Apr 10 '21 at 17:26
  • @MaxieBerkmann It does wait for another workflow to finish. But it does it by repeatedly checking on some interval to see if the job has finished, which is why you see `wait-interval` as an option. – Michael Mior Sep 09 '22 at 19:31
0

You can accomplish this without any third party tools using reusable workflows

example:

Create a workflow under .github/workflows called callable.yml

callable.yml:

  on:
  workflow_call:

  jobs:
    some_job:
      runs-on: ubuntu-latest
      name: some_job

Then call it from a main workflow (we'll call it ci.yml for this example)

ci.yml:

name: Call a reusable workflow

on:
  pull_request:
    branches:
      - main

    jobs:
      call-workflow:
        uses: ./.github/workflows/callable.yml

the ./.github/ notation is used to indicate a local workflow, but you can use them from other repos as well.

More details found in the docs:

https://docs.github.com/en/actions/using-workflows/reusing-workflows

Larry E
  • 19
  • 3