GithubのプライベートリポジトリをGemfileで参照する方法
背景
当社では、社内で共通に使いたい機能をgemに切り出し、機能の利用側のGemfileでプライベートリポジトリを参照しています。
gem "some_internal_library", git: "https://github.com/precena-dev/some_internal_library.git", tag: "v1.0.0"
ローカル端末でのみ利用する場合はgitのURLはgit@ やssh@ で始まるURLを使えば問題なくbundle installできます。しかしCI/CD環境でもbundle installするため、httpsで始まるURLで登録しています。
このプライベートリポジトリをbundle install時に参照する方法について記載します。
Personal Access Tokenを使う方法について
こちらのドキュメントに記載があるように、BUNDLE_GITBHUB__COM の環境変数にgithubのPersonal Access Token(PAT)を登録することでbundle install時にプライベートリポジトリを参照する方法があります。しかし、GithubはPATの利用を推奨していません。ドキュメントに非推奨の「GitHub recommends that you use fine-grained personal access tokens instead」といった言及がされています。したがって、当記事ではPAT以外を利用した方法について記載します。
ローカル端末の場合
Github CLIを使う方法
まずCLIをインストールしておきます。Macの場合はbrewコマンドでインストールできます。
% brew install gh
次に以下のコマンドを実行するとブラウザが開きますので、Githubの認可を行います。
% gh auth login
これで、bundle installが成功します。
git configを使う方法
以下のコマンドを実行します。参考
% git config url.git@github.com:.insteadOf https://github.com/
そうすると、sshで参照するようになるためbundle installが成功します。
CI/CD環境の場合
github actionsやAWS codebuildなどのCI/CD環境について記載します。
Github Appsの作成
まず、https://github.com/organizations/<your_organization_name>/settings/apps/new から、Github Appsを作ります。組織内でのみ利用したいため、「Where can this GitHub App be installed?」の項目は「Only on this account」にチェックしておきます。
パーミッションについては、プライベートリポジトリを参照してbundle installするだけであれば、「Contents:Read-only」を選択するだけで良いでしょう。
作成後にPrivate Keyを作れるようになりますので、ひとつ作成して秘密鍵をダウンロードしておきます。
画面上部に表示されているAppIDを控えます
作成したGithub Appのリポジトリへの導入
Github App 左メニューのInstall Appを選択し、歯車アイコンをクリックします。
必要なリポジトリを選択し、Saveします。
Github Actions
控えておいたGITHUB_APP_IDおよびGITHUB_APP_PRIVATE_KEYを、下図のようにActionのsecretsに、登録しておきます。
Github App経由でtokenを取得します。その値を、環境変数BUNDLE_GITHUB__COM
に設定します。以下にGithub Actionsの設定例を掲載します。
steps:
- name: Generate github token
id: generate_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
・・・中略・・・
- name: Set up Ruby
env:
BUNDLE_GITHUB__COM: x-access-token:${{ steps.generate_token.outputs.token }}
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
# change this to (see https://github.com/ruby/setup-ruby#versioning):
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
AWS CodeBuild
Github Actionsとやっていることは同じです。Github Appsを用意し、GITHUB_APP_IDとGITHUB_APP_PRIVATE_KEYを使って、Access Tokenを取得します。ただしgithub actionsのように公開された再利用可能ワークフローがないため、自前でスクリプトを実行してtokenを取得します。
GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEYに加え、GITHUB_APP_INSTALLATION_IDをActionのsecretsに登録しておきます。installation idは各リポジトリのSettingsメニューの下部Github Appsを選択し、Github Appsの一覧のConfigureボタンを押した先のURLに含まれています。https://github.com/organizations/<organization_name>/settings/installations/<installation_id> の形式です。このinstallation_idを控えてください。
以下が、buildspec.ymlから呼ぶスクリプトです。
# buildspec.ymlから呼ぶscript.
# github app経由でtokenを取得する
npm install axios jsonwebtoken
node ./get_github_token.js > $BUNDLE_GITHUB__COM
get_github_token.jsの内容は以下のとおりです。クラスメソッドさんのブログ記事より流用、改変しています。
// github app経由でtokenを取得するスクリプト
// 成果物のtokenは標準出力に出す
// https://dev.classmethod.jp/articles/register-github-app-and-get-access-token/ を改変
const jwt = require("jsonwebtoken")
const axios = require("axios")
const githubAppId = process.env.GITHUB_APP_ID;
const githubAppPrivateKey = process.env.GITHUB_APP_PRIVATE_KEY;
const githubAppInstallationId = process.env.GITHUB_APP_INSTALLATION_ID;
const payload = {
exp: Math.floor(Date.now() / 1000) + 60, // JWT expiration time
// ちょっとだけ時間を手前にしておくとアクセストークンの発行に失敗し辛いらしい。
// https://qiita.com/icoxfog417/items/fe411b94b8e7ae229e3e#github-apps%E3%81%AE%E8%AA%8D%E8%A8%BC
iat: Math.floor(Date.now() / 1000) - 10, // Issued at time
iss: githubAppId
}
const cert = githubAppPrivateKey;
const token = jwt.sign(payload, cert, { algorithm: 'RS256'});
axios.default.post(`https://api.github.com/app/installations/${githubAppInstallationId}/access_tokens`, null, {
headers: {
Authorization: "Bearer " + token,
Accept: "application/vnd.github.machine-man-preview+json"
}
})
.then(res => {
// 標準出力に出たものをシェルスクリプトでリダイレクトして使う想定
console.log(`x-access-token:${res.data.token}`);
})
.catch(res => {
console.error('error');
console.error(res);
throw new Error(res.data);
})
本サイトの更新情報は、X(旧Twitter)の株式会社プレセナ・ストラテジック・パートナーズエンジニア公式で発信しています。ご確認ください。
最終更新
役に立ちましたか?