🐕

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)を使うことで、以下の流れでスクリプトを実行できるようになります

  1. github-scriptのスクリプトをTypeScript、npmパッケージを活用して記述
  2. RollupでバンドルしてCommonJS形式に変換
  3. GitHub Actionsでバンドルファイルを実行

これにより、型安全性とnpmエコシステムの恩恵を受けながら、github-scriptを最大限に活用できます。
前提として、スクリプトはyamlにインラインで書くのではなく別ファイルに切り出す方法となります。

サンプルリポジトリはこちらに置いています。

https://github.com/ikorihn/github-script-ts-bundle

プロジェクト構成例

以下のような構成でプロジェクトを作成します

.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自体のメンテナンスを安全に行うためにぜひ活用してみてください!

GitHubで編集を提案

Discussion