actions/github-scriptをTypeScriptとnpmパッケージを活用して書けるようにする
はじめに
GitHub Actionsの actions/github-script 、使ってますか?
ワークフロー内でJavaScriptコードを手軽に実行できて便利ですよね。
CI/CDのコードをJavaScriptで記述できるため、Shell scriptで書くよりもリッチな構文や非同期処理が書きやすくなっています。
そんな便利なactionですが、使っているうちにだんだんと以下のような課題を感じてきました。
- TypeScriptは直接使用不可
- 外部npmパッケージの利用が困難
- 大きなスクリプトの保守性が低い
本記事では、TypeScriptとRollupを活用してこれらの制約を解決し、より保守しやすいgithub-scriptを作成する方法を紹介します。
できるようになること
-
actions/github-script
の処理をTypeScriptで型安全に書けるようになる - npmパッケージを活用できるようになる
全体像
TypeScriptとバンドラー(Rollup)を使うことで、以下の流れでスクリプトを実行できるようになります
- github-scriptのスクリプトをTypeScript、npmパッケージを活用して記述
- RollupでバンドルしてCommonJS形式に変換
- GitHub Actionsでバンドルファイルを実行
これにより、型安全性とnpmエコシステムの恩恵を受けながら、github-scriptを最大限に活用できます。
前提として、スクリプトはyamlにインラインで書くのではなく別ファイルに切り出す方法となります。
サンプルリポジトリはこちらに置いています。
プロジェクト構成例
以下のような構成でプロジェクトを作成します
.github/
├── scripts/ # ここにスクリプトファイルを置くことにする
│ └── example/
│ ├── src/
│ │ └── main.ts # TypeScriptソースコード
│ ├── dist/
│ │ └── bundle.js # ビルド結果(自動生成、gitignoreする)
│ ├── package.json # 依存関係とスクリプト
│ ├── rollup.config.js # Rollupバンドル設定
│ └── tsconfig.json # TypeScript設定
└── workflows/
└── example.yml # GitHub Actionsワークフロー
実装手順
1. パッケージ設定
package.json
{
"name": "example",
"version": "1.0.0",
"main": "dist/bundle.js",
"scripts": {
"build": "rollup -c"
},
....
}
必要なパッケージのインストール
# 型定義を補完に利用するため
$ npm install -D @actions/core @actions/github
# rollupをプラグインとともにインストール
$ npm install -D rollup rollup-plugin-esbuild @rollup/plugin-commonjs @rollup/plugin-node-resolve tslib typescript
# 利用するパッケージを追加
$ npm install -S date-fns
2. TypeScriptで処理を記述
src/main.ts
import { format, formatDistance, formatRelative, subDays } from "date-fns";
import type { getOctokit } from "@actions/github";
import type { context as ContextType } from "@actions/github";
import type * as Core from "@actions/core";
// 引数の型を定義
type GitHub = ReturnType<typeof getOctokit>;
type Context = typeof ContextType;
export default async ({
github,
context,
core,
}: {
github: GitHub;
context: Context;
core: typeof Core;
}) => {
core.info("✨ This script is written in TypeScript!");
const dateDistance = formatDistance(subDays(new Date(), 3), new Date(), {
addSuffix: true,
});
core.info(`Date distance: ${dateDistance}`);
// => Date distance: 3 days ago
const { owner, repo } = context.repo;
core.info(`Running on repository: ${owner}/${repo}`);
};
-
export default
で実行される関数をエクスポート - github-scriptから渡される
github
,context
,core
の型を明示 - npmパッケージ(この例では
date-fns
)を自由に使用可能
3. Rollup設定
rollup.config.js
import { nodeResolve } from "@rollup/plugin-node-resolve";
import esbuild from "rollup-plugin-esbuild";
const isProduction = process.env.NODE_ENV === "production";
export default {
input: "src/main.ts",
output: {
file: "dist/bundle.js",
format: "cjs", // CommonJS形式で出力する
exports: "default",
},
plugins: [
nodeResolve(),
esbuild({
sourceMap: true,
minify: isProduction,
}),
],
};
- github-scriptからはrequireで読み込むため、CommonJS形式で出力する
- esbuildでトランスパイルしている
4. tsconfig
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020"],
"module": "ESNext",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"]
}
5. GitHub Actions workflow
.github/workflows/example.yml
name: "Run TypeScript in github-script"
on:
push:
branches:
- main
workflow_dispatch:
jobs:
ts-script-job:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./.github/scripts/example
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: "npm"
cache-dependency-path: ./.github/scripts/example/package-lock.json
# 依存関係のインストール
- name: Install dependencies
run: npm install
# ビルド -> dist/bundle.js が作成される
- name: Build script
run: npm run build
# bundleされたファイルをロードして実行
- name: Run bundled TypeScript script
uses: actions/github-script@v7
with:
script: |
const script = require('./.github/scripts/example/dist/bundle.js');
return await script({github, context, core});
- github-scriptでの外部ファイルの実行方法はドキュメントを参照する
実行結果
上記のファイルをcommitしてpushすると期待通り実行されました。
おわりに
TypeScriptとRollupを組み合わせることで、github-scriptでより使いやすく保守しやすいスクリプトを作成できるようになりました。
初期設定は若干複雑ですが、GitHub APIの型定義などが利用可能になり、複雑な処理であってもGitHub Actionsに組み込みやすくなりました。
CI/CD自体のメンテナンスを安全に行うためにぜひ活用してみてください!
Discussion