73

I have 2 private GitHub repositories (say A and B) in the organization (say ORG). Repository A has repository B in requirements.txt:

-e git+git@github.com:ORG/B.git#egg=B

And I have the following workflow for A (in .github/workflows/test.yml):

name: Python package

on: push

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1

    - name: Install requirements
      run: |
        pip install -r requirements.txt

    - name: Test with pytest
      run: |
        pytest ./tests

As B is private, it fails on installing it.

Is it possible to install B while testing A in this workflow if they are in the same organization? How?

Yevhen Kuzmovych
  • 10,940
  • 7
  • 28
  • 48
  • This answer to a slightly different question looks very promising to me: *[How to clone multiple private repositories using GitHub Actions?](https://stackoverflow.com/a/68255302/192373)*. – Alex Cohn Nov 23 '21 at 08:20

8 Answers8

80

Since access tokens are bound to an account and have write access to all its private repos, it's a very bad solution.

Instead, use deploy keys.

Deploy keys

Deploy keys are simply SSH keys that you can use to clone a repo.

  1. Create a new SSH key pair on your computer
  2. Put the public key in the private dependency repo's Deploy keys
  3. Put the private key in the app repo's Actions secrets
  4. Delete the keys from your computer

secrets

Once it's set, you can set the private key in the GitHub Action's SSH Agent. There's no need to import a third-party GitHub Action, a 2-liner will suffice.

eval `ssh-agent -s`
ssh-add - <<< '${{ secrets.PRIVATE_SSH_KEY }}'
pip install -r requirements.txt

I found that ssh-add command here.

Nato Boram
  • 4,083
  • 6
  • 28
  • 58
  • 5
    Deploy keys were also the most convenient choice in my scenario but, for extra convenience, I managed the loading into the agent through the webfactory/ssh-agent action: https://github.com/webfactory/ssh-agent. – Dato Dec 25 '21 at 01:29
  • I'm getting "Error loading key "(stdin)": invalid format" after > eval `ssh-agent -s` – ניר Nov 07 '22 at 20:24
  • You should not drop the -----BEGIN RSA PRIVATE KEY----- prefix and sufix – ניר Nov 07 '22 at 20:45
  • 1
    Hi, I tried this solution, but it does not allow us to use the SSH key in the GH actions if a fork initiates a PR into our repo. How would we add the SSH key such that a fork PR starting the GH actions still can use the SSH key to pull the necessary repo? – ajl123 Mar 02 '23 at 22:01
  • @ajl123 The private key needs to be in the consuming repo's secrets. – Nato Boram Mar 03 '23 at 15:52
  • 1
    I try this but I got this error when cloning repo: git@github.com: Permission denied (publickey). fatal: Could not read from remote repository. whats the problem? – amir_a14 Jun 12 '23 at 13:06
  • This solution will require a unique SSH key for each dependency repository. See https://docs.github.com/en/authentication/troubleshooting-ssh/error-key-already-in-use – cpowers Sep 01 '23 at 23:09
20

I did this way!

- uses: actions/checkout@v1  
  with:
    repository: organization_name/repo_name
    token: ${{ secrets.ACCESS_TOKEN }}

You need to provide a valid token, you can generate it following this guide

Yevhen Kuzmovych
  • 10,940
  • 7
  • 28
  • 48
Duvan
  • 341
  • 2
  • 6
  • 4
    Do you mean uses two checkouts, first for repo A and second (this one) for repo B? – northtree Jul 09 '20 at 03:33
  • 15
    I wouldn't want my access token stored in the companies secrets as it might be accessed by several people. – Yevhen Kuzmovych Sep 29 '20 at 07:56
  • Were you able to figure it out eventually? @northtree – Erfan Feb 06 '21 at 23:21
  • I think that is the way to do it. I've noticed that you can add token to organization secrets (or rather your organization admin can) which you can use in actions. Haven't tested this yet – Yevhen Kuzmovych Apr 27 '21 at 14:11
  • 3
    Please do not use this solution for organizational accounts. It is suboptimal as it requires you to store your credentials to access it. The [solution provided below by Nato Boram](https://stackoverflow.com/a/70283191/12403182) is much better, as it uses deploy keys instead. – A Merii Feb 09 '22 at 15:39
  • @YevhenKuzmovych Did you find any way to clone private repo without using token. I have the same case and I don't want to share my token. – Neural Feb 08 '23 at 10:04
5

Instead of check out twice, all you need is provided the TOKEN for pip to access repo B.

    - name: Install requirements
      run: |
        git config --global url."https://${{ secrets.ACESS_TOKEN }}@github".insteadOf https://github
        pip install -r requirements.txt
Philippe Remy
  • 2,967
  • 4
  • 25
  • 39
northtree
  • 8,569
  • 11
  • 61
  • 80
5

Using deployment keys you can do

- uses: actions/checkout@v2
  with:
    ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
    repository: organization_name/repo_name

For this to work you need to

  • generate ssh keys locally
  • add pub key as deployment key to the private repo
  • add private key as a secret named SSH_PRIVATE_KEY
Michal Gallovic
  • 4,109
  • 2
  • 18
  • 25
3

Either use an SSH key with no passphrase to access repo B, or create an access token for that repo and then use the access token as your password to access that repo over HTTPS: https://USERNAME:TOKEN@github.com/ORG/B.git.

rmunn
  • 34,942
  • 10
  • 74
  • 105
  • 9
    I wouldn't want my access token stored in companies repo (or in "secrets" of the repo) as it is available to many people. There is `secrets.GITHUB_TOKEN` in actions by default that I tried to use as you suggested with HTTPS (`-e git+https://my_username:${{secrets.GITHUB_TOKEN}}@github.com/ORG/B.git#egg=B`) - it does not work (with `remote: Repository not found.`). – Yevhen Kuzmovych Aug 23 '19 at 08:38
  • I was thinking you would store the token in the [Github secrets vault](https://developer.github.com/actions/managing-workflows/storing-secrets/). Do note that the docs say "Every repository includes a GITHUB_TOKEN secret, but it's not available to an action by default. You must add the GITHUB_TOKEN secret to each action that requires access." So that might be the issue. BTW, if you end up having to store secrets in a repo, [Blackbox](https://github.com/StackExchange/blackbox) (created by Stack Exchange, BTW) is good; it uses GPG to encrypt your secrets so you can control access. – rmunn Aug 23 '19 at 09:50
  • And I don't know how to "add the GITHUB_TOKEN secret to each action that requires access" because I'm still in the waiting list for the beta, so I can't test things. I can only read the documentation and answer based on what I've understood from the docs so far, and my knowledge isn't yet complete. – rmunn Aug 23 '19 at 09:51
  • I believe you would need to add it as env variable if it is needed as an env variable (`env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}`). I'm using it directly from secrets so it's not the problem. Just to note: there is an older [workflow-actions system](https://developer.github.com/actions/), couldn't find how people solve this problem either. And thank you for your time! :) – Yevhen Kuzmovych Aug 23 '19 at 10:50
  • 2
    this doesn't work anymore since github disabled username/password authentication: ```remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.``` – nont Sep 24 '21 at 13:02
  • There is no such thing as an "access token for that repo". An access token can read EVERYTHING you have access to. Do you contract for two companies? That token you set up for one of them allows access to the other company's repos. – Roman Starkov Nov 15 '21 at 20:28
2

There is safety way to do this in Github Actions (without storing ssh-key). You have dedicated lib for that in Github Actions

- uses: webfactory/ssh-agent@v0.7.0
    with:
      ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY_VOW_SHARED }}

When you use docker you have to create ssh tunneling:

docker build --ssh default=${SSH_AUTH_SOCK} .

or docker-compose.yaml:

build:
  ssh:
    default: ${SSH_AUTH_SOCK}

And after that in Dockerfile:

RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh pip install -r requirements.txt

Remember to set DOCKER_BUILDKIT flag in ci.yaml:

env:
  DOCKER_BUILDKIT: 1
trybek_4
  • 29
  • 3
1

Disclaimer: your secret will be in clear text with this solution.

I added this line

git+https://YOUR_TOKEN_HERE@github.com/ORG/REPO_NAME.git@master#egg=REPO_NAME

to my requirements.txt and it worked. But your token will be exposed to anyone having access to this repository. It is probably best to use a secret in your repository.

Philippe Remy
  • 2,967
  • 4
  • 25
  • 39
0

If we like to access one repository from another, we could have a Github level secret (let's say ORG_GITHUB_TOKEN and ORG_GITHUB_USERNAME). If we need to have fine grained access control, we could create multiple tokens with restricted access.

In Github action, we could save them in .git-credentials and use credentials store git config. This combination can help in this situation.

Here is an example github action, where we install python dependency from another private repository.

Example is python unittest. But the approach is generic.

name: UnitTest

on:
  workflow_dispatch:
  push:

jobs:
  unittest:
    name: Unit Test
    env:
      ORG_GITHUB_USERNAME: ${{ secrets.ORG_GITHUB_USERNAME }}
      ORG_GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }}
    runs-on: ubuntu-latest
    steps:
    - name: Check out code
      uses: actions/checkout@v3

    - name: Setup Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.10'
    - name: Install dependencies
      run: |
        echo "https://$ORG_GITHUB_USERNAME:$ORG_GITHUB_TOKEN@github.com" >> $HOME/.git-credentials
        git config --global credential.helper store
        pip install poetry
        poetry config virtualenvs.create false
        poetry install --all-extras --no-root --with dev --no-interaction
        rm -rf ${HOME}/.git-credentials
    - name: Run pytest
      run: |
        python -m pytest

permissions:
  id-token: write
  contents: write
  pages: write
  repository-projects: write

Sairam Krish
  • 10,158
  • 3
  • 55
  • 67