Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GitHub action to check out repo as a git submodule

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

  • The action
  • A failing run

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?
like image 207
Amir Gonnen Avatar asked Oct 18 '25 15:10

Amir Gonnen


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.

like image 171
Amir Gonnen Avatar answered Oct 21 '25 04:10

Amir Gonnen