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 · 54 comments
Open

Support private repositories and private submodules #287

marcofranssen opened this issue Jun 23, 2020 · 54 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?

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

@jcampbell05
Copy link

jcampbell05 commented Jun 17, 2024

I too ran into this, one way to balance security concerns would be if github could detect you have submodules and install keys onto your agent which only allow the clone of that specific commit and can only be redeemed once / expire after an hour

@king-of-poppk
Copy link

I too ran into this, one way to balance security concerns would be if github could detect you have submodules and install keys onto your agent which only allow the clone of that specific commit and can only be redeemed once / expire after an hour

How would it check that you have the permission to read from that submodule? That also needs to be automated somehow. And it is complex: The permission is not necessarily tied to a "committer" or "pusher", there are many different triggers. Also you cannot just assume a "commiter" or a "pusher" gives consent to cloning from a repo that was potentially added as a submodule by someone else (who does not necessarily have access to that repo).

@jcampbell05
Copy link

jcampbell05 commented Jul 15, 2024

How would it check that you have the permission to read from that submodule? That also needs to be automated somehow. And it is complex: The permission is not necessarily tied to a "committer" or "pusher", there are many different triggers. Also you cannot just assume a "commiter" or a "pusher" gives consent to cloning from a repo that was potentially added as a submodule by someone else (who does not necessarily have access to that repo).

How I imagine it would be like this:

  1. Github Action detects .gitsubmodule
  2. Clone all public ones
  3. For private repos do the following process:
    a) For every organisation there could be virtual user (or at least the built in ability to make them without whole process of setting up a fake email etc) scoped to your organisation called "Organization:ActionBot" similar to how dependabot works now
    b) Grant this virtual user read-only permission to each repo you wish to be able to clone
    c) Actions assumes the permission of this vurtual user and generate a short lived read-only SSH key for the CI session for the virtual user
    d) This SSH key will authenticate with Github as the virtual user and allow the cloning of repos that this virtual user is added to

@landsman
Copy link

@nebuk89 can you please look on this as well? I believe that's a similar case as this one https://github.com/orgs/community/discussions/46566#discussioncomment-10889835. Is it?

Current ways are such a overkill, UX unfriendly for company like GitHub.

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