dorny / paths-filter

Conditionally run actions based on files modified by PR, feature branch or pushed commits
MIT License
2.14k stars 239 forks source link

How to use paths-filter in reusable github action workflows #192

Open hex0cter opened 1 year ago

hex0cter commented 1 year ago

When using this action as a step in a reusable github action workflow, the git ref is the branch name of the calling repository instead of the called repository.

For example, in repo B, there is a reusable workflow b.yaml, which uses below as a step:

      - uses: dorny/paths-filter@v2
        id: filter
        with:
          base: ${{ github.ref_name }}
          filters: |
            backend:
              - 'backend/**'
            frontend:
              - 'frontend/**'

In repo A there is another workflow a.yaml that calls B/b.yaml. Now if someone pushes a commit which triggers a.yaml, which then triggers b.yaml, dorny/paths-filter will fail because the github context comes from repository A instead repository B, because the branch name from repo A doesn't exist in repo B.

Is there any solution for this?

Reference: https://docs.github.com/en/actions/using-workflows/reusing-workflows#using-outputs-from-a-reusable-workflow

When a reusable workflow is triggered by a caller workflow, the github context is always associated with the caller workflow. The called workflow is automatically granted access to github.token and secrets.GITHUB_TOKEN. For more information about the github context, see "Contexts."

Crisfole commented 1 year ago

You want this to run in the context of the called repository? That's not a reusable workflow then, you're talking about workflow-dispatch. I've used benc-uk/workflow-dispatch for this before.

A reusable workflow is a workflow that can apply to other repositories in their own context. ("Deploy to Fruggle", "Retarget PR", "Open New Issue"). You can have a workflow that dispatches to another workflow in its own repository, but you'll need to share sensitive token data, so this is only appropriate for internal stuff.

hex0cter commented 1 year ago

I wanted to use the workflow_call event instead of workflow_dispatch event in the workflow where paths-filter is used. But that didn't seem to be possible due to the context problem.

Now I have figured I could use the input parameters of workflow_call event to pass whatever is needed to the called workflow.

Crisfole commented 1 year ago

@hex0cter my workflows these days look like a lot of workflow_call handlers that all call each other:

name: Production Deploy

permissions:
  id-token: write
  contents: read
  actions: write
  pull-requests: read

on:
  pull_request:
    types: [closed]
    branches: [main]

jobs:
    deploy:
      if: github.event.pull_request.merged == true
      uses: ./.github/workflows/deploy.yml
      secrets: inherit
      with:
        environment: production

That workflow uses paths-filter to determine which other workflow_call workflows to run/skip:

name: Deploy

permissions:
  id-token: write
  contents: read
  actions: write
  pull-requests: read

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string

jobs:
  changes:
    name: "Detect Changes"
    runs-on: ubuntu-latest
    permissions:
      pull-requests: read
    steps:
      - uses: dorny/paths-filter@v2
        id: changes
        with:
          filters: |
            infrastructure:
              - 'infrastructure/**'
            migrate:
              - 'database/**'
            code:
              - 'src/**'
              - 'package*.json'
    outputs:
      infrastructure: ${{ steps.filter.outputs.infrastructure }}
      migrate: ${{ steps.filter.outputs.migrate}}
      code: ${{ steps.filter.outputs.code}}

  migrate:
    name: "Run Database Migrations"
    needs: changes
    if: needs.changes.outputs.migrate == 'true'
    uses: ./.github/workflows/migrate.yml
    secrets: inherit
    with:
      environment: ${{ inputs.environment }}

  infrastructure:
    name: "Update Infrastructure"
    needs: changes
    if: needs.changes.outputs.infrastructure == 'true'
    uses: ./.github/workflows/update-infrastructure.yml
    secrets: inherit
    with:
      environment: ${{ inputs.environment }}

  deploy:
    name: "Deploy the Application"
    needs: [changes, migrate, infrastructure]
    if: always() && needs.changes.outputs.code == 'true' && (needs.migrate.result == 'success' || needs.migrate.result == 'skipped') && (needs.infrastructure.result == 'success' || needs.infrastructure.result == 'skipped')
    uses: ./.github/workflows/deploy-application.yml
    secrets: inherit
    with:
      environment: ${{ inputs.environment }}
hex0cter commented 1 year ago

Thanks @Crisfole. In my case the workflow is triggered when a pull request is opened rather than closed. I guess the github context is different there.

Crisfole commented 1 year ago

Right, I'm just demoing how I manage it all! The combo of workflow calls and environment as an argument is pretty powerful.