GitHub action to check out repo as a git submodule

Viewed 1047

Background

I have a project which contains a git sub-module, both are hosted on GitHub.
The sub-module sources are built as part of the project, and changes in the sub-module could affect the containing project.

My goal is to make sure the sub-module does not break the containing project.
For that purpose I'm trying to create a GitHub action on the submodule repo that does the following upon push/pull:

  • Clone the containing project
  • Check out the submodule with the specific push/PR SHA
  • Build the project with the submodule

The problem

The standard Checkout v2 action does not support such workflow.
Instead, I'm cloning and checking out the submodule according to $GITHUB_SHA, like this:

- name: Checkout project
  run: git clone <Project>
- name: Update submodules
  run: git submodule update --init --recursive
- name: Checkout submodule
  working-directory: ./<submodule dir>
      run: |
        git fetch
        git checkout $GITHUB_SHA
- name: Build
  run : make

This works well for push, but not for pull requests.
On PRs I'm getting something like this:

fatal: reference is not a tree: 48fd1d918a25e7544969d13949b1d436f525412c

The SHA that is provided by $GITHUB_SHA in case of PRs is simply nowhere to be found.

Example

Questions

  • Why is $GITHUB_SHA wrong in case of PRs? What does it represent in case of PRs?
  • Is there a better way to achieve what I'm trying to do?

Clarification

If it wasn't clear - the problematic PR was on the repo of the sub-module, not on the main (containing) project repo.
The action is running on the sub-module repo, and checkouts both the containing and the sub module.

The issue is related to the fact that the $GITHUB_SHA of the PR (of the submodule) does not seem to represent a commit on the submodule, although I expected it would.


Update 1

I've made another attempt:
Tried this as the Checkout submodule "run" step:

        git fetch ${{ github.event.repository.git_url }}
        git fetch ${{ github.event.pull_request.head.repo.clone_url }}
        git checkout ${{ github.sha }} || git checkout ${{ github.event.pull_request.head.sha }}

On PR, checkout of both SHAs is still failing with:

fatal: reference is not a tree

So fetching from the original repo where the PR came from - didn't help.
The pull_request.head.sha looks correct (it's the right SHA this time), but git checkout even for that fails! No idea why.


Update 2

Eventually I found a workaround to do it!
Here is the "Checkout submodule" run step:

      run: |
        git fetch --force ${{ github.event.repository.git_url }} "+refs/heads/*:refs/remotes/origin/*"
        git fetch --force ${{ github.event.repository.git_url }} "+refs/pull/*/head:refs/remotes/origin/pr/*"
        git checkout ${{ github.sha }} || git checkout ${{ github.event.pull_request.head.sha }}

So what I'm doing is:

  • Fetching heads from git_url
  • Fetching all PRs from git_url
  • Trying to checkout github.sha. This works for push but not for PR
  • If the above failed, checkout github.event.pull_request.head.sha. This works for PR but not for push....

Same questions, however, remain -

  • Why is github.sha wrong in case of PRs? What does it represent in case of PRs?
  • Is there a better way to achieve what I'm trying to do, instead of fetching all PRs from the remote and trying out both github.sha and github.event.pull_request.head.sha?
1 Answers

I found the answer here:
https://frontside.com/blog/2020-05-26-github-actions-pull_request/#how-does-pull_request-affect-actionscheckout

Apparently, in case of PR, github.sha (or $GITHUB_SHA) represents the SHA of the resulting commit that was created from merging the base to the head, and not the SHA of the base change commit itself.

To obtain it, I needed to fetch refs/pull/*/merge instead of refs/pull/*/head.

Now this run step makes more sense:

        git fetch --force ${{ github.event.repository.git_url }} "+refs/heads/*:refs/remotes/origin/*"
        git fetch --force ${{ github.event.repository.git_url }} "+refs/pull/*/merge:refs/remotes/origin/pr/*"
        git checkout ${{ github.sha }}

The first fetch would fetch heads, for push actions. The second would fetch the merge commits for PRs.
Both are represented by github.sha.

Related