actions / toolkit

The GitHub ToolKit for developing GitHub Actions.
https://github.com/features/actions
MIT License
4.94k stars 1.42k forks source link

make reference accessible in reusable workflow #1264

Open kagahd opened 1 year ago

kagahd commented 1 year ago

Describe the enhancement

A reusable workflow should be able to access the reference that it was called for.

Context

A reusable workflow in public repos may be called by appending a reference which can be a SHA, a release tag, or a branch name, as for example: {owner}/{repo}/.github/workflows/{filename}@{ref}

Githubs documentation states:

When a reusable workflow is triggered by a caller workflow, the github context is always associated with the caller workflow.

The problem

Since the github context is always associated with the caller workflow, the reusable workflow cannot access the reference, for example the tag v1.0.0. However, knowing the reference is important when the reusable workflow needs to checkout the repository in order to make use of composite actions.

Code snippet

Assume that the caller workflow is being executed from within the main branch and calls the ref v1.0.0. of a reusable workflow:

name: Caller workflow
on:
  workflow_dispatch:

jobs:
  caller:
    uses: owner/public-repo/.github/workflows/reusable-workflow.yml@v1.0.0

Here is the reusable workflow that uses a composite action:

name: reusable workflows
on:
  workflow_call:

jobs:
  first-job:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3.1.0
        with:
          repository: owner/public-repo
          ref: ${{ github.ref_name }}

      - name: composite action
        uses: ./actions/my-composite-action

In the above code snippet, ${{ github.ref_name }} is main instead of v1.0.0 because github context is always associated with the caller workflow. Therefore, the composite actions code is based on main and not on v1.0.0.

Proposal

Introduce a new github context variable caller_ref which reflects the reference indicated by the caller. The reusable workflow could then use it as follows:

name: reusable workflows
on:
  workflow_call:

jobs:
  first-job:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3.1.0
        with:
          repository: owner/public-repo
          ref: ${{ github.caller_ref }}

      - name: composite action
        uses: ./actions/my-composite-action

Or even shorter without the need to checkout:

name: reusable workflows
on:
  workflow_call:

jobs:
  first-job:
    runs-on: ubuntu-latest
    steps:
      - name: composite action
        uses: ./actions/my-composite-action@${{ github.caller_ref }}

Now, the composite action would be using v1.0.0. as indicated by the caller.

Related to this FR:

cbrzn commented 1 year ago

I have the same issue, and would love some help!

carlcsaposs-canonical commented 1 year ago

Ran into the same issue, here's a workaround: https://github.com/canonical/data-platform-workflows/blob/main/.github/workflows/_get_workflow_version.yaml

To solve this, we're now using this composite action (that implements @maxbergs's solution)

https://github.com/canonical/get-workflow-version-action

Example usage:

# Reusable workflow (e.g. build_charm.yaml)
on:
  workflow_call:

jobs:
  foo:
    runs-on: ubuntu-latest
    steps:
      - name: Get workflow version
        id: workflow-version
        uses: canonical/get-workflow-version-action@v1
        with:
          repository-name: canonical/data-platform-workflows
          file-name: build_charm.yaml
      # Use the version. For example:
      - name: Install Python CLI
        run: pipx install git+https://github.com/canonical/data-platform-workflows@'${{ steps.workflow-version.outputs.sha }}'#subdirectory=python/cli
mjhipp commented 1 year ago

I am also interested in a solution for this. My use case is I have a repository within my org where I put shared workflows as well as composite actions. I often want to use composite actions within my reusable workflow in the same repository.

I see that you can do this with reusable workflows, but not yet composite actions: https://github.blog/changelog/2022-01-25-github-actions-reusable-workflows-can-be-referenced-locally/

mjhipp/shared-actions
├── actions
│   └── my-composite-action
│       └── action.yml
└── workflows
    └── my-reusable-workflow.yml
name: My Reusable Workflow
on:
  workflow_call:
jobs:
  my-reusable-workflow:
    runs-on: ubuntu-latest:
    steps:
      - uses: mjhipp/shared-actions/actions/my-composite-action@{???}

I would like to use whatever version I am using for the shared workflow for the called composite action, from the same repo as the called workflow. I could use 'main', but I like to pin version tags in my actions for safety.

jrosend commented 1 year ago

I'd love this new reference to be created as well. I'm facing the same problem.

dstuck commented 1 year ago

Presumably the github.job_workflow_sha which is "For jobs using a reusable workflow, the commit SHA for the reusable workflow file" should solve this but it appears that this variable isn't ever populated: https://github.com/actions/runner/issues/2417

GustavoKatel commented 1 year ago

I am also interested in a solution for this. My use case is I have a repository within my org where I put shared workflows as well as composite actions. I often want to use composite actions within my reusable workflow in the same repository.

I see that you can do this with reusable workflows, but not yet composite actions: https://github.blog/changelog/2022-01-25-github-actions-reusable-workflows-can-be-referenced-locally/

mjhipp/shared-actions
├── actions
│   └── my-composite-action
│       └── action.yml
└── workflows
    └── my-reusable-workflow.yml
name: My Reusable Workflow
on:
  workflow_call:
jobs:
  my-reusable-workflow:
    runs-on: ubuntu-latest:
    steps:
      - uses: mjhipp/shared-actions/actions/my-composite-action@{???}

I would like to use whatever version I am using for the shared workflow for the called composite action, from the same repo as the called workflow. I could use 'main', but I like to pin version tags in my actions for safety.

I have the same use case and having this implemented would be great for pinning version between the reusable and the composite action

cezar-tech commented 1 year ago

Also having this need in my enterprise

apascual commented 1 year ago

This is a basic feature need for using reusable workflows. We need a way to keep reusable workflows and the rest of the associated scripting in the same repository so we can version or do branch development. Please, provide a solution to access some context of the reused workflow, ideally access to local files and current branch/tag.

lcharest-godaddy commented 1 year ago

Piling on. My org really needs this.

carloseduardosx commented 1 year ago

I'm also interested in a solution for this.

My org has a central repo with our Reusable Workflows and Shared Action, and it would be essential for us to call the Shared Action using the same revision of the Reusable Workflow.

corest commented 1 year ago

+1 for this For now I came up with workaround I'm sharing below.

So I have repository org/workflows with structure:

.github/workflows/shared.yml
actions/action1
actions/action2

.github/workflows/shared.yml is using action/action1 and action/action2 actions inside via e.g. uses: ./action/action.

Repository app calls shared workflow via uses: org/workflows/.github/workflows/shared.yml@v1.0.0. So we need to know value v1.0.0 in shared workflow to properly checkout reference with local actions.

Here is how it is done in .github/workflows/shared.yml:

jobs:

  action1:
    name: Extract metadata
    runs-on: ubuntu-latest
    steps:

      - name: Checkout calling repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: Get workflow reference
        id: workflows-ref
        run: |
          workflow_ref=${{ github.workflow_ref }}
          repository="${{ github.repository }}/"
          repo_ref="@${{ github.ref }}"
          workflow_path=${workflow_ref#"$repository"}
          workflow_path=${workflow_path%"$repo_ref"}

          ref=$(cat ${workflow_path} | grep pull-request-java-app | cut -d"@" -f2)
          echo "ref=${ref}" >> ${GITHUB_OUTPUT}

      - name: Checkout workflows
        uses: actions/checkout@v4
        with:
          ref: ${{ steps.workflows-ref.outputs.ref }} # use ref
          repository: org/workflows
          token: ${{ secrets.CIJOBTOKENWRITE }}
          path: workflows # checkout into separate folder

      - name: action1
        id: action1
        uses: ./workflows/actions/action1
maxbergs commented 11 months ago

The workaround to this problem that we found was the most clean and easy was to simply do a curl request to get the SHA from the workflow run and use that in ref (https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#get-a-workflow-run).

Example (I don't think I missed anything):

jobs:
  Metadata:
    name: Extract metadata
    runs-on: ubuntu-latest
    permissions:
      actions: read    
    outputs:
      caller-sha: ${{ steps.workflows-ref.outputs.caller-sha }}
    steps:
      - name: Get workflow reference
        id: workflows-ref
        run: |
          sha=$(curl -L -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/<owner>/<repo>/actions/runs/${{ github.run_id }} | jq -r '.referenced_workflows[0] | .sha')
          echo "caller-sha=$sha" >> $GITHUB_OUTPUT
  Test:
    needs: [Metadata]
    uses: ./.github/workflows/test.yml
    secrets: inherit
    with:
      version: ${{ needs.Metadata.outputs.caller-sha }}          

And then in ./.github/workflows/test.yml:

on:
  workflow_call:
    inputs:
      version:
        required: true
        type: string
jobs:
  Test:
    runs-on: ubuntu-latest
    steps:
      - name: Check out actions
        uses: actions/checkout@v4
        with:
          repository: <repo> 
          ref: ${{ input.version }}
          token: <secrets.TOKEN> ##you need a PAT-token with read rights to the repo
pdmtt commented 8 months ago

I'm facing this problem too.

Our reusable workflow needs to checkout its own repository in order to execute a Python script. Allowing access to the called workflow repo's reference would enable versioning its behaviour.

Any developments on this? Thanks!

shermanericts commented 7 months ago

The workaround to this problem that we found was the most clean and easy was to simply do a curl request to get the SHA from the workflow run and use that in ref (https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#get-a-workflow-run).

Example (I don't think I missed anything):

jobs:
  Metadata:
    name: Extract metadata
    runs-on: ubuntu-latest
    permissions:
      actions: read    
    outputs:
      caller-sha: ${{ steps.workflows-ref.outputs.caller-sha }}
    steps:
      - name: Get workflow reference
        id: workflows-ref
        run: |
          sha=$(curl -L -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/<owner>/<repo>/actions/runs/${{ github.run_id }} | jq -r '.referenced_workflows[0] | .sha')
          echo "caller-sha=$sha" >> $GITHUB_OUTPUT
  Test:
    needs: [Metadata]
    uses: ./.github/workflows/test.yml
    secrets: inherit
    with:
      version: ${{ needs.Metadata.outputs.caller-sha }}          

And then in ./.github/workflows/test.yml:

on:
  workflow_call:
    inputs:
      version:
        required: true
        type: string
jobs:
  Test:
    runs-on: ubuntu-latest
    steps:
      - name: Check out actions
        uses: actions/checkout@v4
        with:
          repository: <repo> 
          ref: ${{ input.version }}
          token: <secrets.TOKEN> ##you need a PAT-token with read rights to the repo

This was a life-saver for me today. Thank you!

Ilyes512 commented 7 months ago

I don't understand how such obvious requirement is missing... this is one of the first things I immediately needed when extracting workflows and composite actions to the same repo.

It seems like there is not enough dogfeeding happening at Github. :/

earlye commented 5 months ago

This is definitely a thing that still matters. We're in the process of building out shared workflows that are composed of, well, composite actions in the same repository, and having to manually specify a branch as we modify those composite actions in the workflow file is tedious and error prone. Would be much better to be able to say:

  - uses: {path-to-workflow}/@${{ github.referenced_workflows[0].sha }}
carlcsaposs-canonical commented 5 months ago

In case it helps anyone, we created a composite action that implements @maxbergs's solution

https://github.com/canonical/get-workflow-version-action

Example usage:

# Reusable workflow (e.g. build_charm.yaml)
on:
  workflow_call:

jobs:
  foo:
    runs-on: ubuntu-latest
    steps:
      - name: Get workflow version
        id: workflow-version
        uses: canonical/get-workflow-version-action@v1
        with:
          repository-name: canonical/data-platform-workflows
          file-name: build_charm.yaml
      # Use the version. For example:
      - name: Install Python CLI
        run: pipx install git+https://github.com/canonical/data-platform-workflows@'${{ steps.workflow-version.outputs.sha }}'#subdirectory=python/cli
ckmoga commented 3 months ago

We have reusable workflows to build docker images and deploy microservices. The docker image workflow is triggered when a version is created and we use ${GITHUB_REF#refs/*/} to extract it. From what people say above, we must write a composite action to handle this before calling the reusable workflow. This is ok when you have few repos but it becomes daunting when you have 40+ microservices.

We currently copy & paste the workflows because there is not much benefit to using the reusable workflows. Is there is no other way to do this more efficiently, other than composite actions?

carlcsaposs-canonical commented 3 months ago

@ckmoga I don't understand exactly what you're trying to accomplish, but it doesn't sound like you're affected by this issue

GITHUB_REF (or ${{ github.ref }}) is available in a reusable workflow

This issue is about accessing the ref that a reusable workflow was called with—i.e. the version of the reusable workflow—not about accessing the ref of the caller repository that triggered the workflow run

ckmoga commented 3 months ago

@carlcsaposs-canonical it is possible that I am missing something but I cannot get my release number using ${GITHUB_REF#refs/*/} It is empty but in ordinary workflow it is correct

carlcsaposs-canonical commented 3 months ago

@ckmoga In that case, I think you're encountering a different issue

fwiw, I was able to access GITHUB_REF in a reusable workflow: https://github.com/carlcsaposs-canonical/test-reusable-workflow-githubref/actions/runs/9835427768/job/27149037144 https://github.com/carlcsaposs-canonical/test-reusable-workflow-githubref/tree/main/.github/workflows

ckmoga commented 3 months ago

OK @carlcsaposs-canonical I got it working with $GITHUB_REF_NAME. ${{ github.ref }} throws unknown reference somehow. I think my issue was the curly brackets: ${GITHUB_REF}

stf976 commented 1 month ago

In case it helps anyone, we created a composite action that implements @maxbergs's solution

It's not obvious to me what the advantage of this solution is over directly passing the called workflow repo and ref as workflow inputs. Especially since for the action you need the repo and file name to get the ref.

The repo and file name of the called workflow might change, hence should not be hard-coded in the called workflow file.

You already know the repo, ref, and file name in the caller. So you could pass the repo and file name to the called workflow to execute the action, or just pass repo and ref and skip the action step.

carlcsaposs-canonical commented 1 month ago

It's not obvious to me what the advantage of this solution is over directly passing the called workflow repo and ref as workflow inputs. Especially since for the action you need the repo and file name to get the ref.

For us, it's helpful because the repository name and workflow name are determined by the reusable workflow, but the ref is determined by the caller. (So it's easy to hard code repository name & workflow name in the reusable workflow)

We prefer having one source of truth instead of duplicating it as an input (which could lead to mistakes), and because we use tools like Renovate to update our refs (which are not easily capable of also updating that input)

But that's just what works for us—if passing the ref as an input works for you, there's no need to use an action that calls the GitHub API

dariocurr commented 1 month ago

Hi guys, following this discussion and looking for a solution I created the following basic action to facilitate what @maxbergs provided us: dariocurr/checkout-called.

I hope it will be useful. The only difficult part, IMHO, might be creating the token with the correct set of permissions

randallt21 commented 3 weeks ago

@dariocurr Thank you so much for creating this action! Worked perfectly.

Leaving this comment so others know to use it.