Support private repositories and private submodules · Issue #287 · actions/checkout · GitHub
Skip to content

Support private repositories and private submodules #287

Open

Description

@marcofranssen

Currently the checkout action doesn't work with private repositories using a private submodule.

As a work-around we use the following in our workflow.

steps:
      - name: clone main repository
        uses: actions/checkout@v2

      - name: clone submodule
        uses: actions/checkout@v2
        with:
          repository: our-organization/private-repo
          path: private-repo
          ref: v2
          ssh-key: ${{ secrets.SUBMODULE_SSH_KEY }}
          persist-credentials: false

It would be good if the checkout action would support some option to provide a different SSH_KEY for private submodules. E.g. SUBMODULE_SSH_KEY could be an organisation level SSH Key that allows pulling the repos.

Activity

  1. beroso commented on Jul 16, 2020

    @beroso

    This worked for me:

    #116 (comment)

  2. samuelcolvin commented on Aug 16, 2020

    @samuelcolvin

    Thanks @marcofranssen, this is just want I needed. this is a partial solution.

    For anyone else looking, deploy keys are a partial fix.

    The problem with deploy keys and a separate clone submodules step is that you need to keep the submodule ref and the ref in github actions the same, editing the setting in two places.

    Personal access tokens as suggested by @beroso work, but either involve giving access to all your repos, or creating a new machine user and adding them as a collaborator, big faff.

    It would be great if github could provide a proper and simple way to clone private submodules.

  3. samuelcolvin commented on Oct 12, 2020

    @samuelcolvin

    For anyone else having trouble with this, I have a solution that doesn't require personal access tokens but keeps the reference to the child repo commit in one place (using git submodules)

        - name: clone submodule
          uses: actions/checkout@v2
          with:
            repository: <org name>/<repo name>
            path: path
            ssh-key: ${{ secrets.SSH_KEY }}
            persist-credentials: true
    
        - name: checkout submodule
          run: |
            git submodule init
            git submodule update

    although the action checks out master, the git submodule commands check out the correct commit, this avoids having to keep the ref in github actions.

  4. elhedran commented on Oct 21, 2020

    @elhedran

    See, this is what I really want... just the persist-credentials part. then I could have

    - uses: actions/deploykey@v?
      with: ssh-key
    

    and all it does is make it so the next git command or ssh command can use that key.

  5. Aschen commented on Feb 17, 2021

    @Aschen

    Instead of using an SSH key (:scream: ) you can simply use a personal access token:

          - uses: actions/checkout@v2
            with:
              submodules: recursive
              token: ${{ secrets.ACCESS_TOKEN }}
    

    Here the ACCESS_TOKEN variable is a personal access token

  6. nixpulvis commented on Mar 6, 2021

    @nixpulvis

    For anyone else having trouble with this, I have a solution that doesn't require personal access tokens but keeps stores the reference to the child repo commit in one play (using git submodules)

    Correct me if I'm wrong, but this wont work for multiple private submodules, because each needs their own deploy key.

  7. samuelcolvin commented on Mar 7, 2021

    @samuelcolvin

    I ended up building a rudimentary private package manager for python to get round this problem.

    I think the reason github aren't fixing it is that they think the long term solution should be their package manager(s).

  8. nixpulvis commented on Mar 9, 2021

    @nixpulvis

    I ended up just embedding the private keys for read-only deploy keys in the yml workflow files directly as ENV variables then following this advice with the hard coded strings instead of secrets (which aren't available for free in GitHub Org private repos): https://rgoswami.me/posts/priv-gh-actions/

    Hacky and gross, but it works.

  9. added a commit that references this issue on Mar 13, 2021
  10. added a commit that references this issue on Apr 29, 2021
  11. davidbonnet commented on Jul 9, 2021

    @davidbonnet

    I ended up just embedding the private keys for read-only deploy keys in the yml workflow files directly as ENV variables then following this advice with the hard coded strings instead of secrets (which aren't available for free in GitHub Org private repos): https://rgoswami.me/posts/priv-gh-actions/

    Hacky and gross, but it works.

    Less hacky and gross variant, using ssh-agent instead of writing to a file inside .ssh:

          - name: Get submodules
            env:
              SSH_KEY_SUBMODULE: ${{secrets. SSH_KEY_SUBMODULE}}
            run: |
              eval `ssh-agent -s`
              ssh-add - <<< "${SSH_KEY_SUBMODULE}"; git submodule update --init --recursive
    
  12. KubaO commented on Aug 20, 2021

    @KubaO

    It's a travesty really that this doesn't work out of the box. It forces behaviors that should be discouraged - people fret about submodules (they shouldn't), people have to pay for dummy GitHub accounts just to make this work, people have to bend backwards to get something that is well within the minimally viable product spec for any CI environment. /smh

  13. jleach commented on Sep 3, 2021

    @jleach

    I tend to use deployment keys for this. Add the pub key to the submodule repo; add the pri key to the main repo as a base64 encoded secret. Tokens are fine but seem like they ave too much access. SSH keys (with: ssh-key: in the action itself don't work well for me either because I have to use a global one. This is more surgical. Then something like this works:

    set -Eeuo pipefail
    
    mkdir -p ~/.ssh
    ssh-keyscan -t rsa -H github.com >> ~/.ssh/known_hosts
    echo $GH_ACTION_DKEY > ~/.ssh/id_rsa.b64
    base64 -d -i ~/.ssh/id_rsa.b64 > ~/.ssh/id_rsa
    chmod 600 ~/.ssh/id_rsa
    
    exit 0
  14. 49 remaining items

  15. Danielku15 commented on Jan 7, 2025

    @Danielku15

    What if the checkout action allowed you to specify exactly which submodule to check out?...

    For some simple projects this might work out but in our case this would generate significant overhead. We are in a corporate environment where we're source-sharing some repositories across departments (every department is an org). Additionally we are having reusable workflows for some common CI/CD needs used across repositories.

    Requiring manual individual checkout calls for the submodules does not scale well. As we'd need to manage for every repository duplicated the submodules in the CI/CD area for auth purposes.

    Generating tokens for all orgs and passing a owner+token tuple is fairly easy achievable.

  16. p10tyr commented on May 27, 2025

    @p10tyr

    We pay for GitHub for Enterprise, and we can't even check out internal submodules. What a joke? And the secure way is to use PATs or SSH keys ? Does anybody at GitHub even know about this thread?
    I don't understand why secrets.GITHUB_TOKEN isn't a thing here??

  17. landsman commented on May 28, 2025

    @landsman

    We pay for GitHub for Enterprise, and we can't even check out internal submodules. What a joke? And the secure way is to use PATs or SSH keys ? Does anybody at GitHub even know about this thread? I don't understand why secrets.GITHUB_TOKEN isn't a thing here??

    This is become a nightmare.

    Please let the product owners of github security know! This "bug" is here 5 years already.

  18. parth-hb commented on Jun 30, 2025

    @parth-hb

    Referring to my reply on another thread with same issue here and answering the same.

    I faced same issue and struggled atleast 2 days to make it work. If you're also struggling with the same, here's a reliable and tested fix using Deploy Keys.

    Steps

    1. Generate an SSH Key

      ssh-keygen -t rsa -b 4096 -C "ci-access" -f submodule_deploy_key
    2. Add Public Key to Submodule Repo

      • Go to your submodule repo (e.g., my-org/my-redux-store)
      • Navigate to: Settings → Deploy Keys
      • Click "Add deploy key", paste the public key from submodule_deploy_key.pub
      • Make sure to enable Allow write access if required
    3. Add Private Key to Main Repo Secrets

      • Go to your main repo (my-org/my-app)
      • Add a secret like SUBMODULE_SSH_KEY
      • Paste contents of submodule_deploy_key (the private key)
    4. Modify Workflow
      Before updating submodules, add:

      - name: Checkout main repo (no submodules)
        uses: actions/checkout@v4
        with:
           submodules: false
             
      - name: Setup SSH for submodules
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SUBMODULE_SSH_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan github.com >> ~/.ssh/known_hosts
      
       - name: Init and update submodules
         run: |
          git submodule sync
          git submodule update --init --recursive
      

    This should fix the common Permission denied or Repository not found errors when dealing with submodules.


    Works great when:

    • You don’t want to use personal tokens
    • You want tight access control over a single repo
    • You prefer SSH over HTTPS
  19. jcampbell05 commented on Jul 2, 2025

    @jcampbell05

    I think if anyone wants to do it I suppose an app could be made which Github then builds in
    #287 (comment)

  20. wwangsa commented on Jul 2, 2025

    @wwangsa

    @parth-hb I had one of my repositories setup similarly as yours but I believe Github wants us to use Github Apps.
    2 weeks ago, I setup another private repo with a private submodule but this time I'm using Github Apps.

    Here are the steps for Organization (it will be similar steps for personal account)

    1. Go to Setting > Developer Settings > Create Github Apps > New Github Apps
    2. Filled the required fields then set the permissions as below then Create Github Apps
      1. Contents - Readonly
      2. Metadata - Read-only
    3. Go back to the Github Apps setting > General > Private keys > Generate private keys from the Github Apps created. A private key will be downloaded.
    4. Install the Apps to the organization
    5. Setup Github Actions' secrets. Repo > Setting > Secrets and variables > Actions : Add APP_ID with the value from Github app settings and SUBMODULE_PRIVATE_KEY where the content of the private key
    6. Here is the Actions workflow
    - name: Generate Github App token
      uses: actions/create-github-app-token@v2
      id: app-token
      with:
        app-id: ${{ secrets.APP_ID }}
        private-key: ${{ secrets.SUBMODULE_PRIVATE_KEY }}
        owner: ${{ github.repository_owner }}
        repositories: 'your-main-repo,your-submodule-repo'
      
    - name: Checkout repository
      uses: actions/checkout@v4
      with:
        token: ${{ steps.app-token.outputs.token }}
        ref: main
        submodules: 'recursive'
  21. jayshrivastava commented on Jul 6, 2025

    @jayshrivastava

    @parth-hb thanks for investigating! I followed the instructions but it doesn't seem to work. I get git@github.com: Permission denied (publickey)

    edit: I found the fix

    Image

    My PR had a source branch in a fork, which was not getting secrets.

  22. NicolasGonzalezFarabollini commented on Jul 31, 2025

    @NicolasGonzalezFarabollini

    You can bypass this issue if you use the token inside a docker instance on github actions. Yes, not the most efficient, nor elegant. But it works.

    For example, I just need to build a dependency from a private repo under same org. I make the yarn build inside the docker image, validate with token. Next, I extract the files from the docker image, and continue the build process with normal github steps.

  23. elaboy commented on Aug 14, 2025

    @elaboy

    Insane how this is still an issue...

  24. jcampbell05 commented on Aug 15, 2025

    @jcampbell05

    So I stumbled across this you can use git+http in your git path to pass in an access token. See here in Vercel guide.
    https://vercel.com/guides/using-private-dependencies-with-vercel

  25. greenmoss commented on Sep 24, 2025

    @greenmoss

    It is difficult for me to understand why Github requires this much work to check out submodules, when they exist within the same private organization as the parent.

    I've spent a day setting up a workaround. While hacking this workaround, I also accidentally leaked secrets.

    Not a good look, Github.

    EDIT including my solution

          - name: Mask Github app id
            run: |
              echo "::add-mask::$(cat /my-mounted-secrets/github_app_id)"
    
          - name: Mask Github app credentials key line by line
            run: |
              while IFS= read -r line; do
                echo "::add-mask::$line"
              done < /my-mounted-secrets/github_app_private_key
    
          - name: save Github app id
            id: gh_app_id
            run: |
              echo "value=$(cat /my-mounted-secrets/github_app_id)" >> $GITHUB_OUTPUT
    
          - name: save Github app credentials key
            id: gh_app_key
            run: |
              {
                echo "value<<EOF"
                cat /my-mounted-secrets/github_app_private_key
                echo "EOF"
              } >> $GITHUB_OUTPUT
    
          - name: Create token with access to all private repositories
            uses: actions/create-github-app-token@v1
            id: private_access_token
            with:
              app-id: ${{ steps.gh_app_id.outputs.value }}
              private-key: ${{ steps.gh_app_key.outputs.value }}
              owner: ${{ github.repository_owner }}
    
  26. landsman commented on Sep 24, 2025

    @landsman

    It is difficult for me to understand why Github requires this much work to check out submodules, when they exist within the same private organization as the parent.

    I've spent a day setting up a workaround. While hacking this workaround, I also accidentally leaked secrets.

    Not a good look, Github.

    It's really stupid, yeah!

  27. jcampbell05 commented on Jan 24, 2026

    @jcampbell05

    GitHub already created a solution for this but for some reason you can only use it for private actions

    https://docs.github.com/en/actions/how-tos/reuse-automations/share-across-private-repositories

    Can’t they just take this feature and enable it for all code

  28. poocat commented on Mar 9, 2026

    @poocat

    Struggled with this a little today. Might have found something.

    Followed @parth-hb's instructions for generating an SSH key to use as both the "deploy key" in the private submodule repo, and a secret in the public repo.

    I edited .gitmodules in the public repo to use an SSH url (i.e. git@github: instead of https://github.com/). Then, the following worked:

          - uses: actions/checkout@v4
            with:
              submodules: recursive
              ssh-key: ${{ secrets.SUBMODULE_SSH_KEY }}

    However, that is not how I want to set up my local workflow. So, I found another alternative, setting up an SSH agent and using git config to change the url after the public repo was checked out.

          - uses: webfactory/ssh-agent@v0.9.1
            with:                                                                                                                                                                                                           
              ssh-private-key: ${{ secrets.SUBMODULE_SSH_KEY }}
    
          - uses: actions/checkout@v4
    
          - name: config git to use SSH url
            run: git config --global url."git@github.com:".insteadOf "https://github.com/"
    
          - name: checkout submodules
            run: git submodule update --init --recursive
  29. maxkoryukov commented on Sep 13, 2026

    @maxkoryukov

    We pay for GitHub for Enterprise, and we can't even check out internal submodules. What a joke? And the secure way is to use PATs or SSH keys ? Does anybody at GitHub even know about this thread? I don't understand why secrets.GITHUB_TOKEN isn't a thing here??

    1 year after, sep 2026, i still need to call my shaman to configure access to submodules from the same organization (because even the org's "owner"-role members can't really help). The only "safe" alternative - create and configure org-wide github application and still - config is not much easier than creating deploy keys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions