GitHubでソースコードを管理しているアプリにおいて、利用ライブラリのアップデートを自動で行う方法として Dependabot や Renovate などがあります。
Renovateを使う場合、Mend社が提供している Renovate GitHub App (Mend-hosted GitHub App)を使うのが簡単です。
また、Renovateでは、Gemfileなどに記載されているライブラリの更新とともに、Gemfile.lockなどのロックファイルも更新します。
Use Cases - Renovate Docs
ただし、ロックファイルの更新方法はパッケージマネージャによって異なります。例えば、 プライベートリポジトリなどのアクセスできないところにライブラリがある場合です。
npmでは、アクセスできないライブラリを無視してロックファイルを更新します。
一方、Bundlerではエラーとみなします。その結果、ロックファイルを更新するRenovateの処理 renovate/artifacts が失敗してしまい、次のプルリクのようになります。
https://github.com/thinkami-renovate-sandbox/main-app-with-renovate/pull/13
renovate/artifacts のエラーを解消するには、プライベートリポジトリにアクセスするための情報をMend Cloudに渡せば良さそうでした。
Configure Mend for GitHub.com to resolve your private dependencies
他にも方法がないか調べたところ、GitHub Actionによる Self-Hosted Renovate がありました。GitHub の中で色々完結できそうなので、GitHub Action方式でいくことにしました。
次に、その環境で Renovate に渡すためのトークンとして
のいずれかを使う方法がありました。
PATの場合、個人アカウントを用意する必要があり、かつ、個人アカウントに紐づく、有効期限の設定が必要など、GitHub Actionsでは使い勝手が悪いです。一方、GitHub App installation access tokenはGitHub Actionsで使うのにちょうど良さそうです。詳細は以下の記事が参考になりました。
GitHub Appsトークン解体新書:GitHub ActionsからPATを駆逐する技術
そこで、GitHub App installation access token + GitHub ActionsによるSelf-Hosted Renovateで、プライベートリポジトリのgemやnpmパッケージが含まれるGemfileの更新を試してみたことから、その内容をメモします。
目次
環境
以下の内容を1つのGitHub Organizationで管理しているとします。
- プライベートリポジトリで管理している次のパッケージ
- npmパッケージ
- gem
- パブリックリポジトリで管理しているアプリ
- 上記のパッケージを以下で管理
- package.json
- パッケージマネージャは yarn 1.x 系
- Gemfile
- パッケージマネージャは Bundler
- package.json
- 上記のパッケージを以下で管理
なお、今回の各パッケージはソースコードからインストールするものとします。
GitHub Package によるプライベートパッケージ管理の環境からインストールする場合は、別の方法になりそうです。詳しくはRenovateのドキュメントを参照してください。
Private Packages - Renovate Docs
検証で使うパッケージとアプリについて
今回は次のようなパッケージとアプリを用意しました。なお、完成したらそれぞれGitHubへpushしておきます。
npmパッケージ
今回はインストールできるかどうかだけが分かればよいので、ミニマムなnpmパッケージとして index.js と package.json を用意します。
index.js
export const sayHello = () => { console.log('Hello World!'); };
package.json
{ "name": "private_npm_lib", "version": "1.0.0", "main": "index.js", "type": "module" }
gem
gemもミニマムなものを用意します。まず bundle gem private-bundler-app でgemを生成します。
% bundle gem private-bundler-app
Creating gem 'private-bundler-app'...
Initializing git repo in /path/to/private-bundler-app
create private-bundler-app/Gemfile
create private-bundler-app/lib/private/bundler/app.rb
create private-bundler-app/lib/private/bundler/app/version.rb
create private-bundler-app/sig/private/bundler/app.rbs
create private-bundler-app/private-bundler-app.gemspec
create private-bundler-app/Rakefile
create private-bundler-app/README.md
create private-bundler-app/bin/console
create private-bundler-app/bin/setup
create private-bundler-app/.gitignore
続いて、 lib/private/bundler/app.rb ファイルを追加します。
puts 'Hello, World!'
最後に、 private_bundler_app.gemspec をいい感じに書いて完成です。
アプリ
今回は以下の組み合わせが更新できればOKとします。
- Gemfile と Gemfile.lock
- package.json と yarn.lock
そのため、アプリ用のファイルはつくらず、Gemfileとpackage.jsonだけ用意します。
Gemfile
パブリックgemとして colorize を、プライベートgemとして上記で作成した private_bundler_app をそれぞれ Gemfile に定義します。
source "https://rubygems.org" gem 'private_bundler_app', git: 'https://github.com/thinkami-renovate-sandbox/private-bundler-app.git' gem 'colorize', '0.5.5'
package.json
こちらもパブリックパッケージとして uuid を、プライベートパッケージとして private-npm-app をそれぞれ package.json に定義します。
{ "name": "my-app-with-self-hosted-renovate", "version": "1.0.0", "main": "index.js", "dependencies": { "private_npm_lib": "https://github.com/thinkami-renovate-sandbox/private-npm-app.git", "uuid": "1.4.0" } }
最後にインストールです。
まずは、gemです。
% bundle install Fetching https://github.com/thinkami-renovate-sandbox/private-bundler-app.git Fetching gem metadata from https://rubygems.org/....... Resolving dependencies... Bundle complete! 2 Gemfile dependencies, 5 gems now installed. Use `bundle info [gemname]` to see where a bundled gem is installed.
続いて、npmパッケージをyarnでインストールします。 uuidのバージョンが古いためwarningが出ていますが、今回は気にしないことにします。
% yarn install yarn install v1.22.22 info No lockfile found. [1/4] 🔍 Resolving packages... warning uuid@1.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. [2/4] 🚚 Fetching packages... [3/4] 🔗 Linking dependencies... [4/4] 🔨 Building fresh packages... success Saved lockfile. ✨ Done in 3.94s.
全体像
Self-HostedなRenovateをGitHub Actionで作る場合、以下が必要です。
GitHub Appでは、以下の権限を管理します。
GitHub Actionでは以下を行います。
それぞれ見ていきます。
GitHub App の用意
Organizationで GitHub App を作成
Organizationの Settings > Developer settings > GitHub App から、 New GitHub App ボタンを押します。
以下の項目をデフォルトから変更します。
| 項目 | 値 |
|---|---|
| GitHub App name | 任意の名前 |
| Homepage URL | 任意のURL |
| Indentifying ... - Expire user authorization tokens | チェックを外す |
| Webhook - Active | チェックを外す |
また、Permissionsには、GitHubでのSelf-Hosted Renovateに必要なものを選択します。
GitHub and GitHub Enterprise Server - Renovate Docs
なお、上記の公式ドキュメントとは異なり、今回は workflow の更新は行わないため、workflowへのPermissionは与えていません。
| 項目 | 権限 |
|---|---|
| Administration | Read-only |
| Checks | Read and write |
| Commit statuses | Read and write |
| Contents | Read and write |
| Dependabot alerts | Read-only |
| Issues | Read and write |
| Metadata | Read-only |
| Pull requests | Read and write |
最後に保存して完了です。
App IDの確認と、Private keyの生成
GitHub ActionでGitHub Appからインストールアクセストークンを取得する際、GitHub Appの App ID と Private key が必要です。
GitHub Actions ワークフローで GitHub App を使用して認証済み API 要求を作成する - GitHub Docs
そこで、作成したGitHub Appの Generalページから、以下の作業を行います。
App IDの確認
上の方の About の近くにある App ID を確認し、値を控えておきます。
Private keyの生成
Generate a private key からPrivate keyを生成します。
生成したキーはダウンロードしておきます。
作成したGitHub Appをリポジトリへ割り当て
次に、作成したGitHub Appをリポジトリへと割り当てます。
割当先は
の各リポジトリです。Gemfileを持つアプリのリポジトリの他、Gemfileから参照されるリポジトリも指定する必要があります。
ここで指定しておくことで、あとで作成するGitHub App のインストールアクセストークンでプライベートリポジトリへアクセスできます。
まず、Organizationの Settings > Third-party Access GitHub Apps より、上記で作成したGitHub AppをOrganizationへインストールします。
インストールする際のRepository accessでは Only select repositories を選択し、
の各リポジトリを指定して保存します。
アプリリポジトリでの作業
Secretsの設定
Secretsを設定するため、アプリのリポジトリの Settings > Secrets and variables > Actions を開きます。
Repository secretsに以下の2つを追加します。なお、追加して保存すると値は再確認できなくなります。
| Name | 値 |
|---|---|
| RENOVATE_APP_ID | 上記で確認したGitHub Appの App ID |
| RENOVATE_APP_PRIVATE_KEY | 上記で生成したGitHub AppのPrivate keyの中身 |
Self-HostedなRenovateの設定ファイルを作成
Self-HostedなRenovateでは、以下の3ファイルが必要です。
config.js- Renovateの設定のうち、Self-Hostedで必要な設定
renovate.json5- Renovateの設定
.github/workflows/renovate.yml- GitHub ActionsでRenovateを動かすための設定
ちなみに、 config.js と renovate.json5 は両方ともRenovate向けの設定ファイルです。
両者の違いは以下です。
- config.js
- グローバル設定
- Renovate Git App では準備不要
- 設定できるオプションはグローバル設定に加え、リポジトリ固有のオプションも設定可能
- renovate.json5
今回のGitHub ActionsによるSelf-hosted Renovateでは、1つのGitHub Actionを1つのリポジトリでしか使いません。そのため、設定を config.js に集約することもできます。
ただ、「Renovate GitHub Appを利用している環境は renovate.json5 へ設定し、Self-hosted Renovateの場合は config.js へ設定する」だと分かりづらいため、今回は config.js の設定は必要最低限とし、基本は renovate.json5 ファイルに書くことにします。
Renovateには他にも設定できるものがあります。詳しくは以下の公式ドキュメントを参照してください。
Overview - Renovate Docs
では、それぞれのファイルについて詳しく見ていきます。
config.js
renovate.json5ファイルに書いても有効にならない設定を、この config.js へ設定します。今回の場合、renovate.json5ファイルでは「BundlerがGitHubのプライベートリポジトリへアクセスするときのトークン(BUNDLE_GITHUB__COM)を環境変数に設定する」ができませんでした。
そこで、 env オプションのキー BUNDLE_GITHUB__COM に対して、GitHub Actionから渡されてきた値 process.env.BUNDLE_GITHUB__COM を設定しています。
なお、 BUNDLE_GITHUB__COM については、 Bundlerのbundle configのページに詳細が記載されています。
Bundler: bundle config
env: { BUNDLE_GITHUB__COM: process.env.BUNDLE_GITHUB__COM, },
config.js の全体はこちら。
module.exports = { $schema: "https://docs.renovatebot.com/renovate-schema.json", // renovate.json5 があれば、そちらの設定も利用する // https://docs.renovatebot.com/self-hosted-configuration/#requireconfig requireConfig: "optional", // Git設定 gitAuthor: "Renovate Bot <renovate@example.com>", // Bundler用の環境変数設定。 env: { BUNDLE_GITHUB__COM: process.env.BUNDLE_GITHUB__COM, }, };
renovate.json5
今回はマイナー・パッチアップデートのプルリクを作る感じで設定します。
{ // Renovateの基本設定 $schema: "https://docs.renovatebot.com/renovate-schema.json", // すべて実行する mode: "full", // ベース設定を継承 extends: [ "config:recommended" ], // automergeを無効化 automerge: false, // パッケージルール packageRules: [ { // すべてのパッケージに対して matchPackageNames: ["*"], // メジャーアップデートを無効化 matchUpdateTypes: ["major"], enabled: false }, { // すべてのパッケージに対して matchPackageNames: ["*"], // マイナー・パッチアップデートのみ有効 matchUpdateTypes: ["minor", "patch"], enabled: true } ], // 作成するプルリクは、いったん無制限 prConcurrentLimit: 0, prHourlyLimit: 0, // タイムゾーン設定(日本時間) timezone: "Asia/Tokyo", }
.github/workflows/renovate.yml
GitHub ActionsでRenovateを動かすため、GitHub Action用の設定を用意します。
Jobのステップとしては以下です。
actions/checkoutでチェックアウトactions/create-github-app-tokenでGitHub Appからトークンを生成renovatebot/github-actionでSelf-hostedなRenovateを実行
ここでは、ステップ2と3を詳しく見ていきます。
actions/create-github-app-token でGitHub Appからトークンを生成
公式のGitHub Actions actions/create-github-app-token を使い、GitHub Appからインストールトークンを取得します。
actions/create-github-app-token: GitHub Action for creating a GitHub App Installation Access Token
READMEにはサンプルコードも掲載されています。今回は、GitHub Appがインストールされた全リポジトリに対してアクセスを可能としたいため、「Create a token for all repositories in the current owner's installation」を参考にします。
ちなみに、よりセキュアにするには、アクセスするリポジトリを絞れる「Create a token for multiple repositories in the current owner's installation」のほうがよいかもしれませんが、今回は上記の設定とします。
使い方としてはサンプルコードにある通り、環境変数に設定した App IDとSecret keyの他、リポジトリのオーナー(ここではorganization)を指定します。
なお、今回はわかりやすさのためGitHub Actionは @v2.0.6 のようにバージョン指定していますが、セキュアにするにはハッシュを指定する方が良いでしょう。
- name: Generate token id: generate-token uses: actions/create-github-app-token@v2.0.6 with: app-id: ${{ secrets.RENOVATE_APP_ID }} private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }} owner: ${{ github.repository_owner }}
renovatebot/github-action でSelf-hostedなRenovateを実行
RenovateのGitHub ActionにあるREADMEを参考に設定します。 renovatebot/github-action
今回は環境変数まわりで色々設定するため、READMEにある Environment Variables | renovatebot/github-actioを参考にしながら設定していきます。
- name: Self-hosted Renovate # https://github.com/renovatebot/github-action uses: renovatebot/github-action@v42.0.5 env: LOG_LEVEL: ${{ github.event.inputs.logLevel || 'info' }} RENOVATE_DRY_RUN: ${{ github.event.inputs.dryRun || 'false' }} RENOVATE_REPOSITORIES: ${{ github.repository }} BUNDLE_GITHUB__COM: "x-access-token:${{ steps.generate-token.outputs.token }}" with: configurationFile: config.js token: ${{ steps.generate-token.outputs.token }} env-regex: "^(?:RENOVATE_\\w+|LOG_LEVEL|GITHUB_COM_TOKEN|BUNDLE_GITHUB__COM)$"
まず、 env で BUNDLE_GITHUB__COM に値をセットしています。この env でセットした値が config.js の process.env で取得できるためです。
GitHub App でインストールアクセストークンを使ってリポジトリへとアクセスするには、 x-access-token: というprefixが必要です。
GitHub App インストールとしての認証 - GitHub Docs
そこで、一つ前のステップで取得したインストールアクセストークン steps.generate-token.outputs.token に対してprefixを設定し、 BUNDLE_GITHUB__COM の値としています。
また、Renovate (config.js) に BUNDLE_GITHUB__COM というキーの環境変数を渡すため、 with で env-regex を指定しています。
Passing other environment variables | renovatebot/github-action
他に、設定ファイルとしてリポジトリルートにある config.js を渡すよう、 configurationFile へ指定しています。
configurationFile | renovatebot/github-action
ファイル全体
GitHubリポジトリの Action タブから手動実行できるよう、 workflow_dispatch を定義しています。
実運用で使う場合は、スケジューリングすることになるでしょう。
name: Renovate on: # 手動実行 workflow_dispatch: inputs: dryRun: description: 'Dry Run' type: boolean required: false default: false logLevel: description: 'Log Level' type: choice required: false default: 'info' options: - debug - info - warn - error jobs: renovate: runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@v4 - name: Generate token id: generate-token # https://github.com/actions/create-github-app-token uses: actions/create-github-app-token@v2.0.6 with: app-id: ${{ secrets.RENOVATE_APP_ID }} private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }} owner: ${{ github.repository_owner }} - name: Self-hosted Renovate # https://github.com/renovatebot/github-action uses: renovatebot/github-action@v42.0.5 env: LOG_LEVEL: ${{ github.event.inputs.logLevel || 'info' }} RENOVATE_DRY_RUN: ${{ github.event.inputs.dryRun || 'false' }} RENOVATE_REPOSITORIES: ${{ github.repository }} BUNDLE_GITHUB__COM: "x-access-token:${{ steps.generate-token.outputs.token }}" with: configurationFile: config.js token: ${{ steps.generate-token.outputs.token }} env-regex: "^(?:RENOVATE_\\w+|LOG_LEVEL|GITHUB_COM_TOKEN|BUNDLE_GITHUB__COM)$"
動作確認
ここまでで作成してきた各ファイルをコミット、GitHubへpush、 main ブランチへマージした上で、動作確認を行います。
Actionsから作成したworkflowを動かすと、成功しました。
https://github.com/thinkami-renovate-sandbox/my-app-with-self-hosted-renovate/actions/runs/15598133809
また、Renovateが作成したプルリクも、プライベートリポジトリにアクセスできたようで、エラーになることなく作成されています。
https://github.com/thinkami-renovate-sandbox/my-app-with-self-hosted-renovate/pull/3
ソースコード
アプリのリポジトリをGitHubに上げました。動作確認の都合上、プライベートリポジトリは公開していません。
https://github.com/thinkami-renovate-sandbox/my-app-with-self-hosted-renovate
設定ファイルを作成したときのプルリクはこちら。
https://github.com/thinkami-renovate-sandbox/my-app-with-self-hosted-renovate/pull/1
Renovateが作成した、プライベートリポジトリにあるgemを含むGemfileを更新するプルリクはこちら。
https://github.com/thinkami-renovate-sandbox/my-app-with-self-hosted-renovate/pull/3
なお、色々試行錯誤したリポジトリも紹介します。あくまでいろいろ試したときの内容なので、参考まで。
https://github.com/thinkami-renovate-sandbox/main-app-with-self-hosted-renovate
他に、Mend-hosted Renovateを使っているリポジトリはこちら。
https://github.com/thinkami-renovate-sandbox/main-app-with-renovate