Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support private repositories and private submodules #287

Open
marcofranssen opened this issue Jun 23, 2020 · 50 comments
Open

Support private repositories and private submodules #287

marcofranssen opened this issue Jun 23, 2020 · 50 comments

Comments

@marcofranssen
Copy link

marcofranssen commented Jun 23, 2020

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.

@beroso
Copy link

beroso commented Jul 16, 2020

This worked for me:

#116 (comment)

@samuelcolvin
Copy link

samuelcolvin commented Aug 16, 2020

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.

@samuelcolvin
Copy link

samuelcolvin commented Oct 12, 2020

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.

@elhedran
Copy link

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.

@Aschen
Copy link

Aschen commented Feb 17, 2021

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

@nixpulvis
Copy link

nixpulvis commented Mar 6, 2021

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.

@samuelcolvin
Copy link

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).

@nixpulvis
Copy link

nixpulvis commented Mar 9, 2021

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.

e1004 added a commit to e1004/teamiclink that referenced this issue Mar 13, 2021
kawamataryo added a commit to kawamataryo/zenn-articles that referenced this issue Apr 29, 2021
diff --git a/.cache/kvs-node-localstorage/proofdict-lastUpdated b/.cache/kvs-node-localstorage/proofdict-lastUpdated
index eff9476..cefbfec 100644
--- a/.cache/kvs-node-localstorage/proofdict-lastUpdated
+++ b/.cache/kvs-node-localstorage/proofdict-lastUpdated
@@ -1 +1 @@
-1619384079626
\ No newline at end of file
+1619692984086
\ No newline at end of file
diff --git a/articles/improve-dependabot-pr.md b/articles/improve-dependabot-pr.md
index 1187eb3..8ebb47a 100644
--- a/articles/improve-dependabot-pr.md
+++ b/articles/improve-dependabot-pr.md
@@ -1,86 +1,22 @@
 ---
-title: "GitHub Actions で Dependabot プルリクエストの滞留を防ぐ仕組みづくり"
+title: "GitHub Actions で Dependabot のプルリクエストの滞留を防ぐ仕組みづくり"
 emoji: "🛤"
 type: "tech"
 topics: ["githubactions", "github", "dependabot"]
-published: false
+published: true
 ---

 自動的にライブラリのアップデートのプルリクエストを作ってくれる[Dependabot](https://dependabot.com/)はとても便利です。ただ、何かと通常の開発タスクに追われライブラリアップデートのプルリクエストは滞留しがちです。それを解決するための仕組みはないかなと思い、試行錯誤してみたので書きます。
-
-# Dependabotのプルリクエスト作成時にランダムにレビュアーをアサイン
-
-私のチームのプルリクエスト作成からマージまでの流れは以下です。
-
-1. プルリクエスト作成時に任意のチームメンバーを 1 人選びレビュアーにアサイン
-2. レビュアーがレビュー後マージ
-
-基本的には、レビュアーにアサインされたものをレビューするという運用なので、レビュアーのアサインがない Dependabot のプルリクエストは後回しになりがちです。
-
-それを改善するために、Dependabot のプルリクエストのみ自動的にレビュアーをアサインする仕組みを GitHub Actions を使って作りました。
-以下がコードです。
-
-```yml
-name: Reviewer assign action
-
-on:
-  pull_request_target:
-    types: [opened]
-
-jobs:
-  reviewer-assign:
-    timeout-minutes: 10
-    runs-on: ubuntu-18.04
-    if: contains(github.head_ref, 'dependabot/npm_and_yarn') || contains(github.head_ref, 'dependabot/pip')
-    steps:
-      # ランダムでレビュアーをアサイン
-      - name: Assign reviewer
-        uses: hkusu/review-assign-action@v1.0.0
-        with:
-          reviewers: taro, jiro, masaki, ichiro
-          max-num-of-reviewers: 1
-      # ライブラリアップデートロールをアサイン
-      - if: contains(github.head_ref, 'npm_and_yarn')
-        run: echo ROLL_USER=kawamataryo >> $GITHUB_ENV
-      - if: contains(github.head_ref, 'pip')
-        run: echo ROLL_USER=shiro >> $GITHUB_ENV
-      - name: Assign roll user
-        uses: hkusu/review-assign-action@v1.0.0
-        with:
-          reviewers: ${{ env.ROLL_USER }}
-          assignees: ${{ env.ROLL_USER }}
-```
-
-プルリクエストの自動レビュアーアサインには、[Review Assign Action](https://github.com/hkusu/review-assign-action) を利用しています。
-
-https://github.com/marketplace/actions/review-assign-action
-
-上記のコードだと、最初の `name: Assign reviewer` のステップで `reviewers` に指定されているユーザーから、ランダムに 1 人がレビュアーにアサインされます。
-そして、その後の `name: Assign roll user` の方で、ライブラリの種類(ここでは npm か pip)によって専任の担当者を決めています。これはライブラリアップデートという役割を持つメンバーがチームにいて、その者をランダムなレビュアーとは別に必ずアサインするためです。このように GitHub Actions の `if` 構文を使うことで条件によって動的にアサイン対象を変えることも可能です。
-
-
-また、Dependabot のプルリクエストのみを対象にするために、`jobs.xxx.if`で dependabot の作成ブランチのみ true を返すように指定しています。これで、通常のプルリクエストは対象にならず、Dependabot のプルリクエストのみこの GitHub Actions が実行されます。
-
-
-:::message
-Dependabot の標準の設定でも、レビュアーやアサイナーの設定はできるのですが、複数人の候補からランダムに 1 人を選ぶというのはできないので GitHub Actions で対応しています。
-もし必ず固定メンバーをアサインということなら、[こちら](https://docs.github.com/en/code-security/supply-chain-security/configuration-options-for-dependency-updates#reviewers)で設定可能です。
-:::
-
-:::message
-Dependabot と同様の機能を持つ[Renovate](https://github.com/renovatebot/renovate) の場合は、指定メンバーの中からのランダムアサインも可能なようです。
-:::
-
 # 静的アセットのビルド差分からレビューの必要性を判断

-今のチームではバックエンドは Python、フロントエンドが Vue.js という構成なので、Node.js はランタイムで利用せず、あくまで静的アセット(JS, CSS, Image)のビルドのみです。
-そのため、npm モジュールのライブラリアップデートは**プルリクエストブランチでビルドされた静的アセットが、master ブランチでビルドされた静的アセットと差分がなければプロダクトの動きは変わららないはず**です。
-なので、差分をみれば詳細なレビューが必要かどうか判断できます。差分もなく CI も通っていればほぼノールックでマージしてよいはず。
+今のチームのプロダクトでは静的アセット(JS, CSS, Image)のビルドにのみ Node.js を利用しています。
+そのため、npm モジュールのライブラリアップデート時に**プルリクエストのブランチでビルドされた静的アセットが、master ブランチでビルドされた静的アセットと差分がなければプロダクトの動きは変わららないはず**です。
+なので、そのビルド差分の有無をみれば詳細なレビューが必要かどうか判断できます。差分もなく CI も通っていればほぼ動作確認�は不要で、Change log の確認だけでマージしてもよいでしょう。

-※ 差分が出ない場合の例: Test 系、Lint 系、ビルド系のライブラリ、Tree Shaking で除去される部分のコードの変更
+※ 差分が出ない場合の例: Test 系、Lint 系、ビルド系のライブラリ、Tree Shaking で除去される部分のコードの変更など

 :::message
-この考え・仕組みは前職の開発チームで[@mugi_uno](https://twitter.com/mugi_uno)が作ってくれたスクリプトをそのまま参考にしています。ありがとうございます🙏
+この考え・仕組みは前職の開発チームで[@mugi_uno](https://twitter.com/mugi_uno)が作ってくた仕組みを参考にしています。感謝🙏
 :::

 その差分比較を毎回手動で行うのは面倒なので、GitHub Actions で自動実行できるようにしました。
@@ -190,15 +126,78 @@ jobs:
 ![](https://storage.googleapis.com/zenn-user-upload/1ft5n4j30866cze0ddak9wo3kxs8)

 **差分がある場合**
-![](https://storage.googleapis.com/zenn-user-upload/496c6u9lw58mjgz4drgj7vancfnz)
+![](https://storage.googleapis.com/zenn-user-upload/co3e550t6fzpaym7belne7s3q60r)

 差分がないとコメントされた場合は、気軽にマージできます。

+# Dependabotのプルリクエスト作成時にランダムにレビュアーをアサイン
+
+私のチームのプルリクエスト作成からマージまでの流れは以下です。
+
+1. プルリクエスト作成時に任意のチームメンバーを 1 人選びレビュアーにアサイン
+2. レビュアーがレビュー後マージ
+
+基本的には、レビュアーにアサインされたものをレビューするという運用なので、レビュアーのアサインがない Dependabot のプルリクエストは後回しになりがちです。
+
+それを改善するために、Dependabot のプルリクエストのみ自動的にレビュアーをアサインする仕組みを GitHub Actions を使って作りました。
+以下がコードです。
+
+```yml
+name: Reviewer assign action
+
+on:
+  pull_request_target:
+    types: [opened]
+
+jobs:
+  reviewer-assign:
+    timeout-minutes: 10
+    runs-on: ubuntu-18.04
+    if: contains(github.head_ref, 'dependabot/npm_and_yarn') || contains(github.head_ref, 'dependabot/pip')
+    steps:
+      # ランダムでレビュアーをアサイン
+      - name: Assign reviewer
+        uses: hkusu/review-assign-action@v1.0.0
+        with:
+          reviewers: taro, jiro, masaki, ichiro
+          max-num-of-reviewers: 1
+      # ライブラリアップデートロールをアサイン
+      - if: contains(github.head_ref, 'npm_and_yarn')
+        run: echo ROLL_USER=kawamataryo >> $GITHUB_ENV
+      - if: contains(github.head_ref, 'pip')
+        run: echo ROLL_USER=shiro >> $GITHUB_ENV
+      - name: Assign roll user
+        uses: hkusu/review-assign-action@v1.0.0
+        with:
+          reviewers: ${{ env.ROLL_USER }}
+          assignees: ${{ env.ROLL_USER }}
+```
+
+プルリクエストの自動レビュアーアサインには、[Review Assign Action](https://github.com/hkusu/review-assign-action) を利用しています。
+
+https://github.com/marketplace/actions/review-assign-action
+
+上記のコードだと、最初の `name: Assign reviewer` のステップで `reviewers` に指定されているユーザーから、ランダムに 1 人がレビュアーにアサインされます。
+そして、その後の `name: Assign roll user` の方で、ライブラリの種類(ここでは npm か pip)によって専任の担当者を決めています。これはライブラリアップデートという役割を持つメンバーがチームにいて、その者をランダムなレビュアーとは別に必ずアサインするためです。このように GitHub Actions の `if` 構文を使うことで条件によって動的にアサイン対象を変えることも可能です。
+
+
+また、Dependabot のプルリクエストのみを対象にするために、`jobs.xxx.if`で dependabot の作成ブランチのみ true を返すように指定しています。これで、通常のプルリクエストは対象にならず、Dependabot のプルリクエストのみこの GitHub Actions が実行されます。
+
+
+:::message
+Dependabot の標準の設定でも、レビュアーやアサイナーの設定はできるのですが、複数人の候補からランダムに 1 人を選ぶというのはできないので GitHub Actions で対応しています。
+もし必ず固定メンバーをアサインということなら、[こちら](https://docs.github.com/en/code-security/supply-chain-security/configuration-options-for-dependency-updates#reviewers)で設定可能です。
+:::
+
+:::message
+Dependabot と同様の機能を持つ[Renovate](https://github.com/renovatebot/renovate) の場合は、指定メンバーの中からのランダムアサインも可能なようです。
+:::
+
 # 詰まったところ

-実装上で色々踏み抜いたので書きます。
+実装上で色々詰まった部分があったのでまとめます。

-## 対象ブランチで動作しない・・
+### 対象ブランチで動作しない・・

 当初対象ブランチの指定方法を間違え、以下のように`pull_request.branches`でブランチ名を指定していました。

@@ -216,7 +215,7 @@ on:

 https://zenn.dev/ryo_kawamata/articles/github-actions-specific-branch

-## Dependabot作成のPRだけ、403でコメント・レビュアーアサインが落ちる・・
+### Dependabot作成のPRだけ、403でコメント・レビュアーアサインが落ちる・・

 「もう完璧に動くやろ!」とメインブランチにマージした後に気がついたのですが、Dependabot の作ったプルリクエストの場合のみ、同じ GitHub Actions でも書き込み系の操作で 403 エラーが発生しました。これは、Dependabot のみ`GITHUB_TOKEN`で取れるトークンが読み取り専ようになるため起こるようです。

@@ -225,13 +224,13 @@ https://zenn.dev/ryo_kawamata/articles/github-actions-specific-branch
 dependabot/dependabot-core#3253

 :::message
-こういう面倒な点を考慮すると、Renovate を使ったほうが良いのかもしれない・・
+こういう面倒な点を考慮すると、Renovate を使ったほうが良いのかもしれないです。
 :::

-## Runの中でエラーでもないのになぜか毎回終了する・・
+### Runの中でエラーでもないのになぜか毎回終了する・・

 差分を取るために GitHub Actions の run でコマンドを実行しているのですが、なぜか`git diff`で差分がある場合のみ、コマンドがそこで終了するという現象に悩まされました。
-原因は、`git diff`で差分がある場合、終了コードが`1`になるためでした。GitHub Actions の run は終了コードが`1`となるコマンドが実行されるとそこで処理を停止するようです。
+原因は、Git 管理対象外のファイルに`git diff`を行った際に差分がある場合、終了コードが`1`になるためでした。GitHub Actions の run は終了コードが`1`となるコマンドが実行されるとそこで処理を停止するようです。

 今回はコマンドでは、最後に`|| true`をつけることで回避しています。

@@ -241,9 +240,22 @@ git diff --compact-summary /tmp/current /tmp/master > $RESULT_FILE || true
 #...
 ```

+### プロジェクト内のSubmoduleのcloneに失敗・・
+今回この仕組を導入したプロジェクトが、プライベートリポジトリの Git Submodule を含むプロジェクトだったため `actions/checkout@v2` の通常の submodule の設定だけではうまく行かず詰まりました。結局、以下 Issue を参考に、Personal Access Token を設定することで回避しました。
+
+actions/checkout#287
+
+```yaml
+      - name: Checkout current branch
+        uses: actions/checkout@v2
+        with:
+          ref: ${{ github.event.pull_request.head.sha }}
+          token: ${{ secrets.PAT }}
+          submodules: 'recursive'
+```

 # おわりに
-以上、「GitHub Actions で Dependabot のプルリクエスト滞留問題を解決する仕組み作り」でした。まだ、運用を始めたばかりで道半ばというところですが、この仕組を使って良い感じにバージョンアップを進められればと思っています。
+以上、「GitHub Actions で Dependabot のプルリクエスト滞留問題を解決する仕組み作り」でした。まだ、運用を始めたばかりで道半ばというところですが、この仕組を使って良い感じにバージョンアップを進められればなと思っています。

 # 参考
@davidbonnet
Copy link

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

@KubaO
Copy link

KubaO commented Aug 20, 2021

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

@jleach
Copy link

jleach commented Sep 3, 2021

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

@td-krzysiek
Copy link

td-krzysiek commented Sep 4, 2021

something like this worked for multiple private submodules.

    steps:
      - uses: actions/checkout@v2
        with:
          ssh-key: ${{secrets.SSH_KEY}}

      - name: Checkout submodules
        env:
          GIT_SSH_COMMAND: "ssh -o StrictHostKeyChecking=no"
        run: |
          eval `ssh-agent -s`

          echo "${{secrets.SSH_KEY_SUBMODULE1}}" | ssh-add -
          git submodule update --init -- src/submodule1
          ssh-add -D

          echo "${{secrets.SSH_KEY_SUBMODULE2}}" | ssh-add -
          git submodule update --init -- src/submodule2
          ssh-add -D

          eval `ssh-agent -k`

@fearphage
Copy link

ssh -o StrictHostKeyChecking=no

This is insecure and not recommended.

The secure alternative is ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts

@td-krzysiek
Copy link

@fearphage true, although ssh-keyscan will accept incorrect key in case of the spoofed server. I don't like the ssh-keyscan solution because on self hosted servers this appends to the same file on each trigger.

One solution to this problem is to use ssh -o StrictHostKeyChecking=accept-new which has similar effect as ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts, expect it doesn't append to known_hosts file if a key is already there.

From the security perspective it may be better to ssh-keyscan once, and save public keys in an action. Something like this:

tmpdir=$(mktemp -d)
echo "${{ secrets.GITHUB_PUBLIC }}" >> $tmpdir/known_hosts

ssh -o  UserKnownHostsFile=$tmpdir/known_hosts

Then can remove temporary directory to clean things up.

@fearphage
Copy link

I don't like the ssh-keyscan solution because on self hosted servers this appends to the same file on each trigger.

That sounds pretty bad in general. I have no experience using self-hosted runners, but the whole point of GitHub Actions (in my mind) is that every run is from a clean room environment. Reusing a dirty machine/image sounds like a security hole in and of itself.

aalemayhu added a commit to 2anki/server that referenced this issue Sep 25, 2021
@fairmonk
Copy link

my repository and its submodules don't support SSH ( only HTTPS ). Is there a solution for this protocol to retrieve submodules in GitHub Actions ?

@kenmorse
Copy link

@fairmonk – the trick here is to specify the submodule via a relative path when initially configuring it, that way it works when the parent module is checked out via either scheme. See https://stackoverflow.com/a/44630028/31629 for more info.

@fairmonk
Copy link

@fairmonk – the trick here is to specify the submodule via a relative path when initially configuring it, that way it works when the parent module is checked out via either scheme. See https://stackoverflow.com/a/44630028/31629 for more info.

Using relative URL didn't help here.
I ended up using gh, here is a good article on how to do it: https://medium.com/@alexander.sirenko/using-github-access-token-with-submodules-5038b6d639e8

@ScottTylerHall349
Copy link

Using a PAT or SSH key means that it needs to be generated for a specific user's account. This is not a viable solution, because if that person leaves the project, and we remove their access to the project, then we need to regenerate the PAT and SSH key for another user. This is the problem we have been having, or am I misunderstanding this and there is a better way?

NyxAlexandra added a commit to NyxAlexandra/quartz that referenced this issue Oct 5, 2022
@stepanjakl
Copy link

Hi, I thought I would chip in with a solution that has been working pretty well for me.

You can use webfactory/ssh-agent action to provide individual deploy keys for multiple submodule repositories like so:

...
    steps:
      - uses: actions/checkout@v3

      - name: Add SSH private keys for submodule repositories
        uses: webfactory/ssh-agent@0.7.0
        with:
          ssh-private-key: |
            ${{ secrets.SSH_PRIVATE_KEY_SUBMODULE_1 }}
            ${{ secrets.SSH_PRIVATE_KEY_SUBMODULE_2 }}

      - run: git submodule update --init --recursive --remote
...

@kenmorse
Copy link

You can use webfactory/ssh-agent action to provide individual deploy keys for multiple submodule repositories…

Nice to have options, @stepanjakl!

It seems this functionality really should be baked into the core product though and hopefully that will happen.

@matthijskooijman
Copy link

matthijskooijman commented Nov 15, 2022

The most appropriate solution is IMHO using GitHub Apps with https://github.com/marketplace/actions/github-app-token. This allows for appropriate permissions to checkout the main repo and submodules. No "machine" account and such.
This should be a built-in option.

Thanks for the link, it was the right starting point to make this work with Github Apps, which indeed seems a fairly clean way to approach this, because it:

  • Does not need a dummy bot user (so does not take up a seat in a paid plan and no need to manage a password and log in as a dummy user to set things up)
  • Allows access to the entire github API if needed (not just repo access like with SSH keys).

The downside is that you need to setup and manage a Github App, and it might not be entirely obvious (to others or your future self) that the permissions for an action are set up through an app (but that's fixable by documentation, I guess).

I made this work with the below workflow file, which I'll share here in case it is useful for others too. The comment at the top has a description of how this works and what must be configured, in the workflow itself, the first two steps are relevant to this approach, the rest is just regular workflow stuff.

# This workflow handles tagged releases.
#
# Whenever a tag is pushed, this produces a clean build, creates
# a github release for the thag and attaches the build results.
#
# Authentication is a bit complicated. Actions automatically get a token
# that can access the repo they run in, but this token cannot access any
# other private repositories within the organization.
# See also https://github.com/actions/checkout/issues/287
#
# To fix this, a Github App is used. These are often publically hosted
# by a third party and users can install the app in their repo or
# organization, giving the app itself access to (selected) info in that
# repo or organization. The server the app runs on must be configured
# with a private key to authenticate to Github, with which it can
# request a short-lived "installation token" for each place where the
# app is installed. This installation token can then be used to talk to
# the normal Github API on behalf of that installation (repo or
# organization).
#
# In this case, instead of running on some server, the "app" runs inside
# a github action, using Github secrets to make the private key
# available to the action. The app (as registered in Github) is also not
# shared between many users, but you register an app just for your own
# organization, giving that app (and thus actions running your
# organization) access to all (or specific) private repositories in the
# organization.
#
# This approach only works for accessing private repositories in the
# same organization as where the workflow runs, and only if the app has
# been granted access to the repo where the workflow runs, since the
# github-app-token step uses the workflow repository to select which
# installation token to request. Cross-organization access could be made
# to work, but needs additional work.
#
# To be able to use this workflow, the following needs to be set up:
#
#  - Create a Github App https://docs.github.com/en/developers/apps/building-github-apps/creating-a-github-app
#    - Go to organization settings
#    - Developer settings -> OAuth apps
#    - New Github App
#    - Under "Permissions", add "Repository -> Content -> Read-only"
#
#  - Create a private key for the app (in the app's settings).
#
#  - Create two secrets accessible by this workflow (e.g. organization
#    secrets, with appropriate permisions):
#    - REPO_READONLY_GITHUB_APP_KEY containing the generated private key
#    - REPO_READONLY_GITHUB_APP_ID containing the App ID (shown in the
#      app's settings).
#
#  - Install the app on the organization
#    - In the created app's settings -> Install App
#    - Grant (read-only) access to all repositories (or selected repositories if preferred).
name: Release Workflow
on:
  push:
    tags:
    - '*'
jobs:
  build_and_publish:
    name: Build and publish release
    runs-on: ubuntu-latest
    steps:
      - name: Generate token
        # Use the Github App private key to request an installation
        # token with read-only access to the organization's private
        # repositories. This token is then used in the checkout step
        # (but not in subsequent steps that create releases and upload
        # assets, those still use the default token that has write
        # access to the current repository).
        id: generate_token
        uses: tibdex/github-app-token@v1
        with:
          app_id: ${{ secrets.REPO_READONLY_GITHUB_APP_ID }}
          private_key: ${{ secrets.REPO_READONLY_GITHUB_APP_KEY }}
          # Limit permissions to what we need (these need to be
          # configured in the app settings as well).
          permissions: >-
            {"contents": "read"}

      - name: Checkout tag
        uses: actions/checkout@v3
        with:
          submodules: recursive
          token: ${{ steps.generate_token.outputs.token }}

      - name: build
        run: |
          make

      - name: release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            build-result.zip

edit: It seems Github now offers its own action that can replace the tibdex/github-app-token action. I have not tested this, but see #287 (comment) for an example how to use it (it is pretty much drop-in it seems).

@stepanjakl
Copy link

I also realized that you can simply do this using the checkout action to include multiple submodule deploy keys. I am surprised it hasn't been mentioned here yet.

...
    steps:
      - uses: actions/checkout@v3
         with:
           ssh-key: |
             ${{ secrets.SSH_PRIVATE_KEY_SUBMODULE_1 }}
             ${{ secrets.SSH_PRIVATE_KEY_SUBMODULE_2 }}
           submodules: 'recursive'
...

Just remember the keys must be generated with a link/comment to the repository e.g.

ssh-keygen -t ed25519 -C "git@github.com:owner/repo.git"

The GH checkout can then connect the key with the correct repository.

@revero-doug
Copy link

this may be one of the few things GitLab does much better IMO, but I can appreciate (enough to suggest this be configurable rather than a new default, but not in spirit) the "but that's not secure!" protests, anyway here goes -- GitLab CI pipelines inherit permissions from the user who's action kicked off the pipeline. The ability to at least configure this behavior on a repo level seems like table stakes for GitHub to implement in core, and bikeshedding in this checkout action (read: not core) issue for another 2.5 years doesn't seem like the move.

@ArindamRayMukherjee
Copy link

Instead of using an SSH key (😱 ) 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

Using a PAT in one github action can affect other github actions apparently. For example if you have a tagging/versioning step that commits to the same branch by tagging it, the default GITHUB_TOKEN prevents recursive pipeline triggers.
After trying the fixes here that advocate the use of a PAT to download a submodule, in my case, the PAT stayed on for the step that committed the tag. This causes the pipeline to go into recursive builds repeatedly tagging and releasing.

@jquesnelle
Copy link

Hi, I thought I would chip in with a solution that has been working pretty well for me.

You can use webfactory/ssh-agent action to provide individual deploy keys for multiple submodule repositories like so:

...
    steps:
      - uses: actions/checkout@v3

      - name: Add SSH private keys for submodule repositories
        uses: webfactory/ssh-agent@0.7.0
        with:
          ssh-private-key: |
            ${{ secrets.SSH_PRIVATE_KEY_SUBMODULE_1 }}
            ${{ secrets.SSH_PRIVATE_KEY_SUBMODULE_2 }}

      - run: git submodule update --init --recursive --remote
...

This worked for me, except that --remote was causing it to checkout the incorrect ref (master of the submodule, not the referenced commit). Just doing git submodule update --init --recursive got me the desired behavior

@iggyzap
Copy link

iggyzap commented Feb 13, 2023

I also realized that you can simply do this using the checkout action to include multiple submodule deploy keys. I am surprised it hasn't been mentioned here yet.

I've checked that solution first, unfortunately these ssh keys needs to be added as deployment keys to repository that contains submodules and for some reason multiple keys failed to work :(

You approach with ssh-agent config worked!

@edmondop
Copy link

edmondop commented Mar 9, 2023

Is this really required? Are there security reasons why a much better UX can't be provided to developers? The workflow runs with the identity of the user who triggered it, if this person has checkout privileges on submodules can't his token be authorized to checkout submodules?

@stepanjakl
Copy link

I can confirm that this method no longer works reliably - it returned the following error in my case:

ERROR: Repository not found.
Error: fatal: Could not read from remote repository.

I agree with @edmondop, utilizing user's (or even organization's) access permissions automatically should be a way to go. Ideally, there could be an option to set action permissions within the GH's UI. As an avid user GH actions, I really appreciate how they simplify repository management. But I believe that simplifying those kind of low-level configurations/processes would be of a great benefit.

@Hubro
Copy link

Hubro commented May 2, 2023

I also realized that you can simply do this using the checkout action to include multiple submodule deploy keys. I am surprised it hasn't been mentioned here yet.

...
    steps:
      - uses: actions/checkout@v3
         with:
           ssh-key: |
             ${{ secrets.SSH_PRIVATE_KEY_SUBMODULE_1 }}
             ${{ secrets.SSH_PRIVATE_KEY_SUBMODULE_2 }}
           submodules: 'recursive'
...

Just remember the keys must be generated with a link/comment to the repository e.g.

ssh-keygen -t ed25519 -C "git@github.com:owner/repo.git"

The GH checkout can then connect the key with the correct repository.

After an hour of wasting my time on this, I can confirm this does not work, at least not for self-hosted GitHub Enterprise.

@troberti
Copy link

troberti commented May 2, 2023

Right now we do not use Github actions because the configuration for private submodules is just too cumbersome. We would also like this to see this working 'out of the box'. We already setup repo access with Github Teams, and then we want to stop thinking about it.

@landsman
Copy link

landsman commented Jun 29, 2023

This is really sad story. I want to generate access token just in-memory for GitHub Action, because I'm in the same organisation for got sake! 🤦 https://docs.github.com/en/actions/security-guides/automatic-token-authentication

Please GitHub do something with this.

@king-of-poppk
Copy link

Using a PAT or SSH key means that it needs to be generated for a specific user's account.

@ScottTylerHall349 You can create repo-specific SSH keys using deploy keys.

@landsman
Copy link

@king-of-poppk yeah, but why do you even have to do it? It's in your organisation...

@king-of-poppk
Copy link

@ScottTylerHall349 You can create repo-specific SSH keys using deploy keys.

You can also create a dedicated service account if you prefer a PAT or in general managing SSH keys there. Sure it's really bad because it means one more seat to pay for on enterprise.

@king-of-poppk yeah, but why do you even have to do it? It's in your organisation...

Access might be more fine-grained than all-or-nothing within an org.

@magmanu
Copy link

magmanu commented Nov 28, 2023

I endorse @matthijskooijman's solution.
Using a Github App token is safer and more robust than using somebody's PAT.

Also: now there's an official action to handle GH App tokens.
Here's how my workflow ended up:

jobs:
  test-submodules:
    runs-on: ubuntu-latest
    steps:
    - name: Get token from Github App
      uses: actions/create-github-app-token@v1
      id: app_token
      with:
        app-id: ${{ secrets.APP_ID }}
        private-key: ${{ secrets.APP_PEM }}
        # owner is required, otherwise the creds will fail the checkout step
        owner: ${{ github.repository_owner }}

    - name: Checkout from GitHub
      uses: actions/checkout@v4
      with:
        submodules: true
        token: ${{ steps.app_token.outputs.token }}
    
    - name: Print .gitmodules
      run: cat .gitmodules

In the GH app side:

  • provide it with contents permission - minimum
  • install the app in the submodule repo AND in the repo where the workflow is (otherwise checkout will fail to clone the main repo)

@asbjornu
Copy link

asbjornu commented Dec 19, 2023

Thank you for the provided workaround, @magmanu! 🙏🏼

GitHub needs to do something to make this easier, though. The below discussions are related and point to a solution where the issued GITHUB_TOKEN needs to support tweaking on a per-job basis to include permissions up to the level of the user that initiated the run.

@doutv
Copy link

doutv commented Feb 29, 2024

Deploy key solution:
https://gist.github.com/doutv/54098c2c283ed8141ba961c88a2d5bb0

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Clone main repository
      uses: actions/checkout@v4

    - name: Add SSH private keys for submodule repositories
      uses: webfactory/ssh-agent@v0.9.0
      with:
        ssh-private-key: ${{ secrets.READ_ONLY_DEPLOY_KEY }}

    - name: Clone submodules
      run: git submodule update --init --recursive --remote

Read ssh-agent Usage section to learn how to:

  1. create a deploy key for your private git repo
  2. set secrets in your parent repo
    https://github.com/marketplace/actions/webfactory-ssh-agent

Also, in .gitmodules, change the repo url to the ssh format git@:

[submodule "xxx"]
	path = xxx
	url = git@github.com:username/xxx

@landsman
Copy link

Doing this via ssh-agent is just overkill. It should be possible to do this via token. GitHub please introduce this feature.

@brad-technologik
Copy link

Thanks @doutv, using ssh-agent is unfortunate but worked for me.

Simply defining ssh-key with my deploy key for the submodule failed the fetch altogether, so it seems the only workaround it to split the submodule fetch separately.

+1 for this feature. I'm coming from GitLab where this type of this was easy to achieve.

@flopana
Copy link

flopana commented Apr 18, 2024

After aggregating solutions here and reading some documentation I've came up with a clean solution.
See webfactory/ssh-agent docs

My use case is the following, in an action in Repo A I need to clone Repo B that has a submodule Repo C. All three are private in my organization.

- name: Setup SSH Agent
  uses: webfactory/ssh-agent@v0.9.0
  with:
    ssh-private-key: |
      ${{ secrets.REPO_B_DEPLOY_KEY }}
      ${{ secrets.REPO_C_DEPLOY_KEY }}

- name: Clone Repo B
  uses: actions/checkout@v4
  with:
    ref: master
    repository: my-org/Repo-B
    submodules: 'true' # This will automatically fetch the submodule Repo C
    path: repo_b

IMPORTANT

This works because webfactory/ssh-agent creates a git-config setting that redirects git requests to the proper URL and also creates an ssh config that configures the ssh keys for each corresponding repository. This only works if you have the repository url in the comment of the corresponding key.

For example:
ssh-keygen -q -b 4096 -C git@github.com:my-org/Repo-B.git -f REPO_B_DEPLOY_KEY -t rsa

If you generate the key with puttygen export the private key with Conversions->Export OpenSSH key (force new file format), this will include the comment with the repo url in the private key. Otherwise the ssh-agent action can't set up the ssh config and this won't work.

@landsman
Copy link

I would love to see official solution from GitHub. It's should be based on Action permissions, org config of tmp token.

@flopana
Copy link

flopana commented Apr 18, 2024

I would love to see official solution from GitHub. It's should be based on Action permissions, org config of tmp token.

Second this but I think "my" solution is not really hacky or sketchy and also scalable. I can now really fine tune permissions by the use of some ssh keys.

@matthijskooijman
Copy link

matthijskooijman commented Apr 18, 2024

Second this but I think "my" solution is not really hacky or sketchy and also scalable. I can now really fine tune permissions by the use of some ssh keys.

AFAICS the ssh-keys approach does have limitations, in particular you can either use:

  1. A single (or multiple if you want) SSH key for a regular github account (i.e. a personal account, or a "bot" account that you create for this purpose) that you can give access to any repositories it needs. Using regular accounts is a bit hacky IMHO.
  2. "Deploy keys", which are repository-specific (so one key per repository). Not hacky, but a bit cumbersome to manage (I think - I have not tried it), especially because this also needs some scripting to ensure the right key is used for the right repository (git-over-ssh does not support selecting the right key automatically due to how it is implemented).

Did I get that right?

For completeness, one alternative is to use github app tokens as I previously proposed (original #287 (comment) and followup #287 (comment) showing there is now an official GH action to support this flow). Possibly a bit more hassle to set up, but possibly a bit easier to manage than per-repository deploy SSH-keys (or maybe not - haven't tried that solution).

@flopana
Copy link

flopana commented Apr 18, 2024

@matthijskooijman What I've done is I created a deploy key for each of the two repositories in my scenario and added the private keys for that to the secrets in the repo that the action runs in.

After that I can simply use ${{ secrets.REPO_B_DEPLOY_KEY }} this private key without any scripting.

I guess if you have a ton of repositories this gets pretty cumbersome.

I haven't tried the GH App approach, I guess this one could be useful if you have many repositories where you need to do this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests