dariocurr / checkout-called

Checkout the called repository at the same ref it was called
https://github.com/marketplace/actions/checkout-called-repository
MIT License
1 stars 0 forks source link

🐛 .referenced_workflows[0] is not reliable when mutliple reusable workflows are used #5

Open Plonk42 opened 1 week ago

Plonk42 commented 1 week ago

Description

I have a workflow that is using a reusable workflow, which in turn is also using another reusable workflow.

When using GitHub API for workflow runs, it seems the order of referenced_workflows is not fixed, and so we cannot assume that .referenced_workflows[0] is the correct workflow to consider.

Reproduction steps

I stumbled upon this by running a second attempt of a failed run. Here is my setup:

This results in 3 entries in the referenced_workflows list, but the order is not constant. See the following outputs of 2 different runs of the same workflow at the same version:

  "run_attempt": 1,
  "referenced_workflows": [
    {
      "path": "myOrg/myRepo1/.github/workflows/myWorkflow1.yml@version1",
      "sha": "sha1",
      "ref": "refs/heads/version1"
    },
    {
      "path": "myOrg/myRepo1/.github/workflows/myWorkflow2.yml@sha1",
      "sha": "sha1",
      "ref": "refs/heads/version1"
    },
    {
      "path": "myOrg/myRepo2/.github/workflows/myWorkflow3.yml@version2",
      "sha": "sha2",
      "ref": "refs/heads/version2"
    }
  ]
  "run_attempt": 2,
  "referenced_workflows": [
    {
      "path": "myOrg/myRepo2/.github/workflows/myWorkflow3.yml@version2",
      "sha": "sha2",
      "ref": "refs/heads/version2"
    },
    {
      "path": "myOrg/myRepo1/.github/workflows/myWorkflow1.yml@version1",
      "sha": "sha3",
      "ref": "refs/heads/version1"
    },
    {
      "path": "myOrg/myRepo1/.github/workflows/myWorkflow2.yml@sha3",
      "sha": "sha3",
      "ref": "refs/heads/version1"
    }
  ],

When myWorkflow1.yml@version1 is not the first in the list, this action will checkout the wrong repo (at the wrong version).

Version

latest

Screenshots

No response

Logs

No response

Fix

In https://github.com/canonical/get-workflow-version-action/blob/main/get_workflow_version/main.py#L122-L127, the repo and the file to consider are explicitly required as input so you can ensure to get the right entry in referenced_workflows. But this is partially defeating the purpose of this action that is trying to do that automatically...

Plonk42 commented 1 week ago

Here is a proposed fix, introducing a new parameter for the workflow file to look for. Sadly I'm not sure we can get this to work without this additional parameter.

diff --git a/action.yml b/action.yml
index 17a504c..e3884a5 100644
--- a/action.yml
+++ b/action.yml
@@ -4,6 +4,13 @@ branding:
   color: gray-dark
 description: Checkout the called repository at the same ref it was called
 inputs:
+  workflow:
+    description: >
+      The workflow file to look for. This is **required** when multiple reusable workflows
+      are called since GitHub API does not guarantee any ordering for `referenced_workflows[]`.
+      This is optional when a single reusable workflow is called. The first (and only)
+      workflow listed by the GitHub API is then used.
+    default: ""
   token:
     description: >
       Personal access token (PAT) used to fetch the repository. The PAT is configured
@@ -101,7 +108,15 @@ runs:
       shell: bash
       run: |
         content=$(curl -L -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ inputs.token }}" -H "X-GitHub-Api-Version: 2022-11-28" ${{ github.api_url }}/repos/${{ github.repository }}/actions/runs/${{ github.run_id }})
-        data=$(echo $content | jq -r '.referenced_workflows[0]')
+
+        # return the first referenced workflow if none is specified
+        if [[ -z "${{ inputs.workflow }}" ]]; then
+          data=$(echo $content | jq -r '.referenced_workflows[0]')
+        else
+          # 'first' is defensive in case there is more than one match for the specified workflow. Hopefully this should not happen...
+          data=$(echo $content | jq --arg path "${{ inputs.workflow }}" '[.referenced_workflows[] | select(.path | contains($path))] | first')
+        fi
+
         if [ "$data" != "null" ];
         then
           ref=$(echo $data | jq -r  '.sha')

Edit: note that this is a substring search. So you can use the full workflow reference like myOrg/myRepo/.github/workflows/myWorkflow.yml to be on the safe side, or only myWorkflow.yml if you don't want to specify the repo, at the risk of picking a wrong workflow with the same name, but from another repo.

dariocurr commented 1 week ago

I added the argument 8559e746840fae890af00f2e821aaaff240d4eb2. Please verify that everything is working as expected