I prefer to have any authentication identifiers be as narrow as possible when they’re defined in a pipeline or similar, and this proved to be a challenge when it comes to private git submodules inside a GitHub Action pipeline.
A common way to solve this is by defining a personal access token (a PAT), but this has a severe limitation: it needs access to both the project you’re deploying and the external submodule. But why does that matter? Because it means that you can’t configure the PAT under an organisation and re-use it across all your projects without making the PAT have access to all the repositories as well.
You effectively end up with a super-PAT that isn’t limited to just deploying the submodule.
Instead, you have to do it rather manually and as a two-step process. First, check out your project as you’d usually do with the checkout action, and then do the manual dance of configuring ssh with your deploy key and fetch submodules explicitly with git.
You’ll end up with something resembling these two workflow steps (based on a few posts on Stack Overflow and suggestions from a few LLMs):
- name: Checkout and setup
uses: actions/checkout@v6.0.1
with:
submodules: false
- run: |
mkdir -p ~/.ssh
touch ~/.ssh/id_deploy_key
chmod 0600 ~/.ssh/id_deploy_key
echo '${{ secrets.LIBRARY_DEPLOY_PRIVATE_KEY }}' > ~/.ssh/id_deploy_key
GIT_SSH_COMMAND="ssh -i ~/.ssh/id_deploy_key" git submodule update --init --recursive
shell: bash
This is slightly verbose, but it works nicely in practice. Stick the deploy key in an organization secret under the name LIBRARY_DEPLOY_PRIVATE_KEY, and it’ll be available to any workflow that needs access to your private submodule.
And if it leaks – it just affects your common library, which shouldn’t contain any valuable secrets (.. outside of your code, at least) and your generic library code.
The best way would be that actions itself supported providing a deploy key for a given submodule, but it doesn’t seem like GitHub wants to support that from the tickets I read while researching this.