RenovateでPrivate Submodulesを扱う


## RenovateはPrivate Submoduleに対応していない

Renovateは独自にcloneしたリポジトリに対して実行されます。
そのためGitHub Actions側でSubmoduleをcloneしても意味がありません。

また残念なことに[cloneSubmodules](https://docs.renovatebot.com/configuration-options/#clonesubmodules)設定はPrivate Submoduleに対応していないと明記されています。

> Important: private submodules aren't supported by Renovate, unless the underlying `ssh` layer already has the correct permissions.

これではパッケージ管理ツールからSubmoduleをPath指定している場合に困ります。
Submoduleをやめるという方法もありますが、Renovateの都合でプロジェクト構造を制限されるのは避けたいところです。

色々試した結果なんとかなったので共有します。

## 要件

- Personal access tokenではなくGitHub Appによる認証がしたい。
    - 個人のPATを会社で使いたくない。
- Submoduleは複数あり、SSHとHTTPが混在している。
    - 上記公式ドキュメントに書かれているようなSSHレイヤーでの解決では不十分。

DependabotもPersonal access tokenでの認証はできないようでした。

## Self-hosted Renovate

今までは[Renovate GitHub App](https://github.com/apps/renovate)を使用してMend-hostedを利用していましたが、今回の設定を適用するにはSelf-hostedに乗り換える必要があります。
ここでは[GitHub Action Renovate](https://github.com/renovatebot/github-action)を使用します。

## 設定

### GitHub App

以下の通り[Renovateが指定する権限](https://docs.renovatebot.com/modules/platform/github/#running-as-a-github-app)を付与する必要があります。

| Permission        | Scope            |
| ----------------- | ---------------- |
| Checks            | `read` + `write` |
| Commit statuses   | `read` + `write` |
| Contents          | `read` + `write` |
| Issues            | `read` + `write` |
| Pull requests     | `read` + `write` |
| Workflows         | `read` + `write` |
| Administration    | `read`           |
| Dependabot alerts | `read`           |
| Members           | `read`           |
| Metadata          | `read`           |

インストール後の権限変更は、インストール画面で権限の追加を承認する必要があります。

### GitHub Repository

既存のRenovate GitHub Appは無効化しましょう。
そしてRenovate Actionで使用できるよう、Actions secrets and variablesに以下のように値を追加します。

| 種別        | 値           | 名前                 |
| --------- | ----------- | ------------------ |
| Variables | App ID      | GH_APP_ID          |
| Secrets   | Private key | GH_APP_PRIVATE_KEY |

### GitHub Actions

以下の内容を`.github/workflows/renovate.yml`としてリポジトリに保存します。

```yml
name: Renovate
on:
  schedule:
    # 15分ごとに実行
    - cron: "0/15 * * * *"
  workflow_dispatch:
jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
      - name: Generate github token
        uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ vars.GH_APP_ID }}
          private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
          owner: ${{ github.repository_owner }}

      - uses: actions/checkout@v4
        with:
          submodules: "recursive"
          token: ${{ steps.app-token.outputs.token }}
          persist-credentials: false

      - name: Self-hosted Renovate
        uses: renovatebot/github-action@v41.0.13
        with:
          configurationFile: self_hosted_renovate.js
          token: ${{ steps.app-token.outputs.token }}
          env-regex: "^(?:RENOVATE_\\w+|LOG_LEVEL|GITHUB_COM_TOKEN|NODE_OPTIONS|GIT_CONFIG_\\w+)$"
        env:
          GIT_CONFIG_COUNT: "2"
          GIT_CONFIG_KEY_0: "url.https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/.insteadOf"
          GIT_CONFIG_VALUE_0: "https://github.com/"
          GIT_CONFIG_KEY_1: "url.https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/.insteadOf"
          GIT_CONFIG_VALUE_1: "git@github.com:"
```

env部分の`GIT_CONFIG_*`がキモであり、`git clone`する際にGitHub App Tokenを使用するようGitレベルで設定します。

上記の、

> Important: private submodules aren't supported by Renovate, unless the underlying `ssh` layer already has the correct permissions.

と似たような、Renovate外での解決というわけです。

### Renovate configuration

以下の内容を**編集して**`self_hosted_renovate.js`としてリポジトリに保存します。

```js
module.exports = {
  dependencyDashboard: true,
  onboarding: false,
  requireConfig: "optional",
  branchPrefix: "self-hosted-renovate/",
  // 既存のRenovateブランチが邪魔してうまく動かない場合はコメントアウト。
  // branchPrefixOld: "ignore-old-renovate/",
  platform: "github",
  repositories: [
    // 適用リポジトリに編集してコメントアウト。
    // "org_name/repository"
  ],
  cloneSubmodules: true,
};
```

## 最後に

もっとスマートな方法があれば知りたいです。