GitHubリポジトリにあるソースコードをインストールして使う
始めに
共通のコードを使い回すためにnpmパッケージ化が案として上がると思いますが、全世界にpublishするほどのものではない場合があると思います。publishせずに使用する方法の一つとして、GitHubリポジトリのURLを指定してinstallする手段がありますが、リポジトリは基本ビルド前のソースを配置するものであり、使いづらそうに見えます。しかし、GitHub Actionsを駆使することでビルド済みのパッケージをタグ付きでpushすることができ、そこをinstall先とすることで十分運用できそうに感じたのでその辺についてまとめてみました。
プロジェクト構成
この記事では以下のプロジェクト構成で検証しました。Publicでみれるものはリンクをつけていますが、Privateリポジトリからインストールする検証の都合上見れないページについてはリンクはつけておりません。
npm packageへのインストールは git+ssh://git@github.com:wintyo-ssh-packages/utils.git#dist-v0.0.0
のようにsshで指定することでアクセス権限のあるGitHubユーザは自動でinstallできるようにしています。
-
wintyo-ssh-packages (検証する用で用意したGitHub Organization)
- wintyo-ssh-packages/test-use-packages (他リポジトリで作成したパッケージを使用するリポジトリ)
- wintyo-ssh-packages/utils (publicなパッケージリポジトリ)
- wintyo-ssh-packages/private-utils (privateなパッケージリポジトリ。privateなためリンクはなし)
パッケージ作成
まずはinstallするためのパッケージを作成します。
パッケージコードの用意
ディレクトリ構成は重要なところだけピックアップすると以下のようになります。
├── dist (ビルド後の成果物の出力先)
├── src (ビルド前のソースコード)
│ └── index.ts (ビルド時のエントリーポイント)
└── package.json (パッケージ情報)
今回は検証のためなのでシンプルなメソッドだけexportします。
/**
* 加算メソッド
* @param x - 数値
* @param y - 数値
* @returns - 足した値
*/
export const add = (x: number, y: number) => {
return x + y;
};
package.jsonは以下のように書いています。name
がimport {} from 'xxx'
で使われる名前で、main
がインポート時に参照されるファイルになります。
{
"name": "@wintyo-ssh-packages/utils",
"version": "0.0.0",
"description": "utility package",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc"
},
"author": "wintyo <wintyo1024@gmail.com>",
"license": "MIT",
"devDependencies": {
"typescript": "^4.9.5"
}
}
tsconfig.jsonは以下のようにしています。自動生成したものから必要そうな項目を指定しているのでもしかしたら不要なものとかがいくつか入っているかもしれないですが、そこはご了承ください。
{
"compilerOptions": {
/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */,
"rootDir": "./src" /* Specify the root folder within your source files. */,
"baseUrl": "./src" /* Specify the base directory to resolve non-relative module names. */,
/* Emit */
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
"declarationMap": true, /* Create sourcemaps for d.ts files. */
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
/* Interop Constraints */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
"strictNullChecks": true /* When type checking, take into account 'null' and 'undefined'. */,
/* Completeness */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
パッケージコードをビルドしてdistブランチにpushする
前セクションで用意したコードをビルドしてdistブランチにpushします。同じリポジトリにpushする場合はGitHub Pagesで同じみのpeaceiris/actions-gh-pages
を使うと楽に実装できます。このActionにブランチ名やタグ名、push対象になるディレクトリを指定できるので適切なパラメータを設定します。ディレクトリについては必要なコードだけpushすれば良いためdist,src,package.jsonをpublishディレクトリにコピーしてそれをpushするようにしています。
name: publish
on:
push:
branches:
- main
tags:
- 'v*.*.*'
permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-22.04
timeout-minutes: 10
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18.12.1'
- name: Install
run: yarn install
- name: Build
run: yarn build
- name: Prepare Publish Contents
run: |
mkdir publish
cp -r dist publish
cp -r src publish
cp package.json publish
- name: Prepare tag
id: prepare_tag
if: startsWith(github.ref, 'refs/tags/')
run: |
echo "DEPLOY_TAG_NAME=dist-${TAG_NAME}" >> "${GITHUB_OUTPUT}"
env:
TAG_NAME: ${{ github.ref_name }}
- name: Publish
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./publish
publish_branch: dist
tag_name: ${{ steps.prepare_tag.outputs.DEPLOY_TAG_NAME }}
tag_message: 'Publication ${{ github.ref_name }}'
これでdist
ブランチにどんどん成果物がpushされていきます。v0.0.0
のようにタグを振るとdist-v0.0.0
で成果物もタグを振ってpushされるようになります。これで yarn add git+ssh://git@github.com:wintyo-ssh-packages/utils.git#dist-v0.0.0
のように書くとバージョン指定でinstallすることができます。
Privateパッケージも用意する
上記の作業をPrivateリポジトリも行います。
作成したパッケージを使用する
手元で使用する
手元で使用する場合は yarn add git+ssh://git@github.com:wintyo-ssh-packages/utils.git#dist-v0.0.0
のようにssh指定のGitHub URLでインストールすることができます。認証もGitHubと同じものが使われるのでプライベートリポジトリもアクセス権限のあるGitHubアカウントであれば問題なくインストールできます。
インストールできたらいつも使っているような書き方でimportできます。
import { add } from "@wintyo-ssh-packages/utils";
console.log(add(1, 2));
GitHub Actions上で使用する
CIではプライベートリポジトリはそのままだと認証エラーになってしまうため一工夫が必要です。今回はGitHub Actionsでやる方法を2つ紹介します。
SSH Keyを登録して使用する
一つ目の方法は、SSH KeyをGitHub Actionsに登録してインストールする方法です。手元でやる場合は .ssh/id_rsa
をみてSSH認証しているので、同じようにGitHub Actions側でもできるようにします。
ここの記事などを参考に公開鍵・秘密鍵を生成します。公開鍵は記事の通りGitHubアカウントに対してSSH keyを登録します。
秘密鍵についてはGitHub Actionsのsecretsに登録し、この変数を使ってGitHub Actions上で鍵として保存し、そのあとyarn installします。具体的には以下のようになります。
name: Install by ssh
on: push
jobs:
install-by-ssh:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version-file: ".node-version"
cache: "yarn"
- name: SSH Setting
run: |
mkdir -p /home/runner/.ssh/
echo -e "$SSH_KEY" > /home/runner/.ssh/id_rsa
chmod 600 /home/runner/.ssh/id_rsa
env:
SSH_KEY: ${{ secrets.WINTYO_SSH_KEY }}
- name: Install
run: yarn install --frozen-lockfile
- name: Run
run: yarn start
PATで使用する
二つ目の方法はPersonal Access Token(PAT)を使って認証する方法です。
まずはPATを発行します。classicの方選択して、repo
に権限を付与して作成します。これをGitHubのsecretsに登録します。
ssh指定したものをトークン付きのhttpsに切り替える場合はgit configで以下のように設定します。
git config --global url."https://x-access-token:[トークン]@github.com/[アカウント名]".insteadOf "git@github.com:[アカウント名]"
なので上記の設定をinstall前に書くことで動きます。まとめると、以下のようなGitHub Actionになります。
name: Install by PAT
on: push
jobs:
install-by-pat:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v3
with:
token: ${{ secrets.WINTYO_PAT }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version-file: ".node-version"
cache: "yarn"
- name: Setup ssh url
run: |
git config --global url."https://x-access-token:${{ secrets.WINTYO_PAT }}@github.com/wintyo-ssh-packages".insteadOf "git@github.com:wintyo-ssh-packages"
- name: Install
run: yarn install --frozen-lockfile
- name: Run
run: yarn start
一つ注意がありまして、checkout時にもtokenを設定する必要があります。以下でも書かれてましたが、checkoutで使ったトークン(デフォルトだとGITHUB_TOKEN
)の方が優先的に見られてしまうようです。
終わりに
以上がGitHub上にpushしたソースをパッケージとして使う方法でした。基本的にリポジトリはビルド前のコードが置いてあるのでパッケージとして使いづらいかなと思っていましたが、gh-pages用のActionsを上手く使い回すことでビルドしたコードも簡単にpushすることができ、npm publishするのとそこまで変わらない手間で済みそうでした。利用者側はGitHubリポジトリにアクセスできればプライベートであっても普通にinstallすることができて、使い勝手も良さそうです。
GitHub Packagesも検討していましたが、そちらは残念ながらSSH認証ができず手元でもトークンの設定など一手間が必要で断念しました。この辺が解消されたらGitHub Packagesもありですがひとまずはリポジトリを直接参照するやり方でいこうかなと思っています。
Discussion