0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub AppとPAT(Classic)によるPrivateパッケージインストールの比較と実装

Posted at

はじめに

GitHub Packages は非常に便利で、npm パッケージを配布する際の最初の選択肢の1つではないでしょうか。
一方で、npm には GitHub Packagesを経由せずとも、GitHub にホストされた repository の URL を直接指定してインストールする機能もあります。

それぞれ下記のように指定します。

{
  "name": "from-github-package",
  "dependencies": {
    "some-package": "^0.0.1"
  }
}
{
  "name": "from-github-repository",
  "dependencies": {
    "some-package": "github:<org-name>/<repository-name>"
  }
}

一般に前者の方が優れています。

  • CDN から配信されるため高速
  • バージョンを固定できるため冪等性が高い

後者の欠点はいくつかありますが、最大の問題は repository から直接インストールするため、ビルド済みの成果物を Git 管理する必要が出てくる点だと思います。

prepare を使えば成果物を Git 管理から外すことも可能ですが、インストール時にビルドが必要になるなど運用が煩雑になります。
また成果物がビルド環境に依存するため、必ずしも冪等とは言えなくなります。

Private なパッケージをインストールする代表的な方法として、PAT(Classic)を使う方法GitHub App を使う方法があります。

後者を使いたい場面が多いのですが、1つ制約がありました。
というのも私が確認したところ、GitHub App 経由では GitHub Packages からのインストールができないようです

ここで、公式ドキュメントには次のように書かれています。

GitHub Packages では、personal access token (classic) を使用した認証のみがサポートされています。

つまり、fine-grained PAT では GitHub Packages からインストールできないということです。
また、GitHub App の認証と fine-grained PAT の認証は同等の権限モデルを持つため、同じ制約に直面します。

The permissions available to fine-grained personal access tokens are the same permissions available to GitHub Apps, and repository targeting works the same too.

以上から、GitHub App 単体では GitHub Packages の認証には向かない、という結論を導きました。

本記事の目的

上で触れた 2 つの方法(PAT(Classic)/GitHub App)それぞれで Private パッケージをインストールする方法を示します。

最後に、GitHub Actions の外で Private repository をインストールする特殊ケースの対応例も紹介します。

PAT(Classic)を用いる方法

この方法では、GitHub Packages 経由でパッケージをインストールできます。package.json は例えば次のようになります。

package.json
{
    "name": "from-github-package",
    "dependencies": {
        "some-package": "^0.0.1"
    }
}

インストール実行ルートに .npmrc を置き、権限を付与した PAT (Classic) を使う方法がよく使われます。必要な権限の例は次の通りです。

  • repo
  • read:packages

.npmrc の例:

.npmrc
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
@org-name:registry=https://npm.pkg.github.com

GitHub Actions での設定例は次の通りです。

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    registry-url: https://npm.pkg.github.com/
    scope: "@org-name"

- name: Install dependencies
  run: npm install
  env:
    NODE_AUTH_TOKEN: ${{ secrets.PAT }}

行っていることはローカルと同じで、actions/setup-node@v4が自動的に.npmrcを生成し、NODE_AUTH_TOKEN環境変数を参照します。

PAT(Classic)が利用可能である場合、この方法が最もシンプルで確実です。

GitHub App を用いる方法

こちらは repository を直接 clone してインストールする方式になります。
package.json は次のように指定します。

package.json
{
    "name": "from-github-repository",
    "dependencies": {
        "some-package": "github:<org-name>/<repository-name>"
    }
}

単なる repository の clone なので、GitHub App で発行したトークンを使って Git の URL を置換する方法が使えます。
組織にアプリをインストールする際は、対象 repository に対して「コンテンツ(repository)へのアクセス許可」を与えてください。

ワークフローの主要な流れは次のようになります。

- name: Generate GitHub App Token
  id: app-token
  uses: actions/create-github-app-token@v2
  with:
    app-id: ${{ secrets.APP_ID }}
    private-key: ${{ secrets.APP_SECRET }}
    owner: ${{ github.repository_owner }}
    repositories: |
      some-package

- name: Replace GitHub URL with Token
  run: |
    git config --global url."https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/".insteadOf "https://github.com/"

- name: Checkout repository
  uses: actions/checkout@v4
  with:
    persist-credentials: false

- name: Install dependencies
  run: |
    npm install

persist-credentials: false は必須です。デフォルトでは actions/checkoutGITHUB_TOKEN.git/config に設定するため、これが git config --global の設定より優先され、権限不足エラーになります。

(参考)https://zenn.dev/link/comments/3efc9e0e576881

以上が GitHub App を用いた一般的な流れです。

GitHub Actions 外で Private repository をインストールする例

課題

ここからは付録です。
というのも、Firebase Functions の自動デプロイで遭遇した問題についてです。

Firebase の自動デプロイ時、 npm install は Cloud Build 上で実行されます。つまり、GitHub Actions 上で npm install を実行しても、Cloud Build の実行環境には反映されません。したがって、デプロイに .npmrc を含めるなどの対応が必要になることがあります。

PAT (Classic) が利用可能であれば、上記の .npmrc を含める方法が最も簡単です。

どうしても GitHub App を使いたい場合、GitHub Package の利用は諦めるしかありません。
ですが、Replace GitHub URL with Token で実行した URL の書き換えは Cloud Build には引き継がれません。

さて、どうすればいいのでしょうか。

解法

Cloud Build に URL 置換の設定が引き継がれない都合上、インストール対象パッケージの URL を一時的に package.json に直接書き換えるしかありません。

- name: Temporarily update package.json
  run: |
    npm pkg set dependencies.@org-name/some-package="git+https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/org-name/some-package.git"

この方法で、GitHub App のトークンが有効な間は該当 URL からインストールできます。

まとめ

Private パッケージ をインストールする場合、現時点では PAT (Classic) を使った GitHub Packages を利用することが最も確実です。複雑なビルドプロセスを持つ TypeScript パッケージなどではなおさらです。

この記事の解法はあくまで応急処置的なものであり、根本的な解決策ではありません
特にFirebase Functions のバンドルの中に、有効期限があるとはいえ、token が含まれた package.json が残るのは好ましくありません。
また 1 例に過ぎないため、他にも良い方法があるかもしれません。

GitHub App を使う場合は、将来的に GitHub がこの制約を解消することを期待しています。

何か誤りや改善点があれば、ご教示いただけると幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?