2026-09-04 · githubactions · cli · ci · typescript
自作ツールを composite GitHub Action で配る(npm 公開なし)
小さな CLI を作ったとき、他の人に使ってもらう方法として npm 公開を考えがちです。 でもアカウントも公開手続きも要らない配布経路があります。GitHub Action です。
- uses: あなたの名前/ツール名@v1
この1行で使えるようになります。実際にやったので、手順とハマりどころを書きます。
composite か Docker か
Action には3種類(JavaScript / Docker / composite)ありますが、 Node で動くツールなら composite 一択でした。
| composite | Docker | |
|---|---|---|
| 起動 | 速い | イメージのビルド or 取得が要る |
| 実行環境 | ランナーのもの | 隔離される |
| 書き方 | シェルのステップを並べるだけ | Dockerfile が要る |
Docker の隔離が要るのは、システムに手を入れるツールや、 特殊な依存を固めたい場合です。Node だけで完結するツールに Docker を使うと、 起動時間を払うだけで見返りがありません。
全文
action.yml をリポジトリの直下に置きます。
name: 'mdlinkcheck'
description: 'Find broken relative links in Markdown files.'
author: 'your-name'
branding:
icon: 'link-2'
color: 'orange'
inputs:
path:
description: 'Directory or file to check.'
required: false
default: '.'
format:
description: 'Output format: text or json.'
required: false
default: 'text'
runs:
using: 'composite'
steps:
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Build
shell: bash
working-directory: ${{ github.action_path }}
run: |
npm ci --no-audit --no-fund
npm run build
- name: Check links
shell: bash
run: |
node "${{ github.action_path }}/bin/mdlinkcheck.js" \
"${{ inputs.path }}" --format "${{ inputs.format }}"
これだけです。
ハマりどころ
shell: は毎ステップ必須
composite の run ステップは、1つずつ shell: を書かないとエラーになります。
通常のワークフローだと省略できるので、忘れます。
${{ github.action_path }} を使う
action が置かれたディレクトリの絶対パスです。利用者のリポジトリで実行されるので、 カレントディレクトリは利用者側になります。
# NG: 利用者のリポジトリで npm ci しようとする
run: npm ci
# OK
working-directory: ${{ github.action_path }}
run: npm ci
ツールの実行も同様に絶対パスで指定します。
run: node "${{ github.action_path }}/bin/tool.js" "${{ inputs.path }}"
終了コードはそのまま伝わる
シェルステップが非ゼロで終われば、ステップが失敗します。
|| true を付けたり continue-on-error を書いたりしなければ、
ツールの終了コードがそのまま CI の合否になります。
これが効くので、ツール側で終了コードをちゃんと設計しておく価値があります。
0 問題なし
1 検出した(CI を落としたい)
2 使い方が間違っている(パスが無い、オプションが不正)
dist/ をコミットするかどうか
JavaScript action だと dist/ をコミットする必要がありますが、
composite なら action 側で npm ci && npm run build すればいいので不要です。
そのぶん実行のたびにビルドが走ります。数秒なので許容しました。
気になるなら actions/cache を挟めます。
バージョンタグを打つ
@main を案内すると、利用者は壊れる変更を勝手に取り込むことになります。
git tag -a v1.0.0 -m "v1.0.0"
git push origin v1.0.0
# v1 は「動くタグ」。最新の 1.x を指し続ける
git tag -f v1
git push -f origin v1
利用者には @v1 を案内し、固定したい人には @v1.0.0 があると伝えます。
リリースのたびに v1 を進めます。
git tag -f v1 && git push -f origin v1
公開タグが本当に動くかを CI で確かめる
ここが一番効きました。
自分のリポジトリで uses: ./ として動作確認するのは簡単ですが、
それが通ってもタグが指すコミットが壊れていれば利用者は動きません。
./ はローカルのチェックアウトを見ているだけだからです。
そこで、CI に「公開しているタグを参照して実行する」ジョブを足します。
jobs:
use-the-action:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./ # ローカル
with:
path: .
published-tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: your-name/your-tool@v1 # 公開タグ
with:
path: .
これで、タグの張り替えを間違えた瞬間に自分の CI が落ちます。 利用者のリポジトリで落ちる前に気づけます。
自作ツールを自分自身に対して実行できる種類のもの(linter、フォーマッタ、 リンクチェッカーなど)なら、この形が特に相性がいいです。 「使える」が主張ではなく、毎回検査されている状態になります。
Marketplace に出すかどうか
action.yml に branding を書いておくと、GitHub の UI から
Marketplace への公開を案内されます。出さなくても uses: では使えるので、
急ぐ必要はありません。
出すと検索経路が1つ増えるので、リポジトリの説明とトピックを整えたうえで 出しておく価値はあります。
まとめ
- Node で動くツールなら composite。Docker の隔離が要らないなら払う理由がない
shell:は毎ステップ必須。忘れる- パスは
${{ github.action_path }}。カレントは利用者側のリポジトリ - 終了コードがそのまま CI の合否になる。ツール側で設計しておく
@mainを案内しない。v1の動くタグとv1.0.0の固定タグを打つ- 公開タグを参照するジョブを CI に置く。
uses: ./が通っても、 タグが壊れていれば利用者は動かない
この形で配っている小さな CLI(Markdown のリンク切れ検出)を MIT で公開しています。
Claude Code に設計・実装・レビューを別々の人格として分担させ、GitHub Issue と
ブランチを軸に並列開発を回すための設定一式を MIT で公開しています。
コピーして ./setup.sh を叩けば動きます。技術スタックには依存しません。
https://github.com/quintetkit/quartet
このワークフローだけで実際にツールを 1 つ作りました。Issue の分割から PR、 レビュー、マージまで記録が全部残っています。うまくいかなかった箇所も消していません。
https://github.com/quintetkit/mdlinkcheck
UI 設計人格・レビュー基準・Issue 単位の並列実行スクリプト・実践ガイド 10 章を 足したものは製品ページにあります。