やりたいこと
GitHubのissueやプルリクエストのコメントに任意のスクリプトを書いてGitHub Actionsで実行したいです。
これができると色々と自動化できます。
例えば、
- プルリクをマージした時のプレビュー
- npmのパッケージをアップデートして自動でプルリク
- SSHでGitHub Actions内に入ってデバッグ
- Python/Ruby/Go/Deno/Scala .... 任意言語の実行
- LGTMの画像をコメント
- ...
などなどアイデア次第でGitHubコメントする手軽さであらゆることを自動化できます。
npm
でもapt
でもpip
でもgit
でも色々コメントからGitHub完結で実行できます。
できたもの
comment-runと呼んでます。
GitHub: https://github.com/nwtgck/actions-comment-run
上記のREADMEにここで紹介するいくつかの実行可能なコメントを載せています。コピペしたい時に便利です。
自分のリポジトリにcomment-runを導入する
.github/workflows/comment-run.yml
として以下を設置するだけです。
name: "Comment run"
on:
issue_comment:
types: [created, edited]
jobs:
comment-run:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
with:
# 0 indicates all history
fetch-depth: 0
- uses: nwtgck/actions-comment-run@v1.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
allowed-associations: '["OWNER"]'
以下でコマンドをリポジトリのルートで叩けば一発で導入できます。
mkdir -p .github/workflows/ && cd .github/workflows/ && wget https://gist.githubusercontent.com/nwtgck/a9b291f6869db42ecc3d9e30d0a0494c/raw/comment-run.yml && cd -
pushすれば自動導入されます。GitHub側で設定することは特にないです。
comment-runを使ってみる
まず新しくissueを立てます。(既存のissueでもOKです。)
その中で以下のようにコメントしてみます。
以下はコピペ用です。書き始めを@github-actions run
にします。```js
のコードブロック内が実行できます。
@github-actions run
```js
console.log("hello, world");
```
以下のように"hello, world"が出ているのがわかります。
LGTM画像をコメントしよう
もう少し使えそうな使用例の紹介です。
以下のコメントでLGTM.in/gからランダムで画像がコメントされます。
実際のコメント:https://github.com/nwtgck/actions-comment-run/pull/1#issuecomment-596302174
コメント内でdetails
タグを使えばすっきりと自動化のコメントを投稿できます。
よく使うコメントをGitHubに覚えさせる
GitHubにはSaved repliesという機能があり(@peaceiris氏に教えてもらいました)、
これでcomment-run用のコメントを覚えさせると自動化が捗ります。
Saved repliesの記憶のさせ方は、
まず以下のSettingsに行きます。
そして、Saved repliesを選んで記憶させたいものを記入できます。
覚えさせたコメントを使いたいときは、
以下のコメント投稿欄の右上にある「↖️」アイコンを押すと選べます。
npmパッケージを更新して自動でプルリクを作る
使用例です。
Dependabotは便利ですが、大量に更新があるときにまとめて更新してくれると助かることがあります。更新の自動プルリク作成がGitHub完結で行えます。
以下のマークダウンのコメントを投稿するだけです。
@github-actions run
```js
function exec(cmd) {
console.log(execSync(cmd).toString());
}
// Config
const baseBranchName = context.payload.repository.default_branch;
const gitUserEmail = "github-actions[bot]@users.noreply.github.com";
const gitUserName = "github-actions[bot]";
const prBranchName = "comment-run/npm-update";
exec(`git config --global user.email "${gitUserEmail}"`);
exec(`git config --global user.name "${gitUserName}"`);
exec(`git fetch --all`);
exec(`git checkout ${baseBranchName}`);
exec(`git checkout -b ${prBranchName}`);
const pakcageJson = JSON.parse(require('fs').readFileSync('package.json'));
const depStr = Object.keys(pakcageJson.dependencies || {}).join(' ');
const devDepStr = Object.keys(pakcageJson.devDependencies || {}).join(' ');
exec(`npm i ${depStr} ${devDepStr}`);
exec("git status");
exec("git add package*json");
exec(`git commit -m "chore(deps): update npm dependencies"`);
exec(`git push -fu origin ${prBranchName}`);
(async () => {
await githubClient.pulls.create({
base: baseBranchName,
head: prBranchName,
owner: context.repo.owner,
repo: context.repo.repo,
title: "chore(deps): update npm dependencies",
body: "update npm dependencies",
});
})();
```
以下のように自動でプルリクを作ってくれます。
上記のコードをちょっと変えればコミットメッセージやプルリクのタイトルを日本語にしたりできます。
この自動プルリクの可能性は、npmに限らずあらゆるパッケージマネージャや自動化ツールと連携してローカル環境に行かずに自動でプルリクエストできるところだと思います。
具体的には上記のコードのexec(`npm i ${depStr} ${devDepStr}`)
の部分を他のコマンドに変えれば良いでしょう。例えばVue CLIを使っている人ならvue upgrade
したやつをプルリクしてくれるのは便利かなと思います。
プルリクをマージした時のプレビュー用のブランチを作る
comment-runの使用例です。
GitHub Actionsはセキュリティ上Secretsをforkした人が作ったプルリクには渡さないようにしています。(トークンとかがメンテナ以外が見れたりすると危険ですからね。)
それだとSecretsなしのGitHub Actionsしか実行できず機能的に制限されてしまいます。これが、それを解決するための一つの方法になります。
メンテナがプルリクエストのコードを見て問題ないときは以下の@github-actions run ...
を実行するとマージした時のブランチを適当な名前でpushしてくれます。そのpushされたブランチへのURLが自動でコメントされます。
そのpushされたブランチではSecretsが引き渡された他のGitHub Actionsが動いてくれたりします。
comment-runのセキュリティ的な話
コメントしたリポジトリにadmin/write権限のあるユーザーのみが実行できるようになっています。
それに加え、デフォルトでは@github-actions run
でコメントを実行できるのはOWNER
だけになっています。
例えば、最初の設定のYAMLでallowed-associations: '["OWNER", "MEMBER"]'
に変更すればMEMBERも@github-actions run
も実行できるようになります。
organizationに参加している人はMEMBERだったりします。
他には以下の値を取りうるはずです。
- COLLABORATOR
- CONTRIBUTOR
- FIRST_TIMER
- FIRST_TIME_CONTRIBUTOR
- MEMBER
- NONE
- OWNER
(参考: https://developer.github.com/v4/enum/commentauthorassociation/)
comment-runの環境は通常のGitHub Actionsの実行環境と同じだけの能力があります。つまりGitHub Actionsを書くときと同じように@github-actions run
コメントを注意して書く必要があります。GitHub Actions用のGitHubのトークンが使えるのでそのリポジトリに対してのwrite権限があります。GitHub Actions用の秘密にすべき環境変数を設定している場合は表示しないように気をつけましょう。
SSHしてGitHub Actions内に入る
comment-runの使用例に戻ります。
頑張ってSSHする方法は色々あると思います。ここでは @Cryolite氏提案の「Piping Server を介した双方向パイプによる,任意のネットワークコネクションの確立 - Qiita」で紹介されていた方法を使います。
コメントを実行すると"New SSH session"というコメントが自動で投稿されそこに書かれているコマンドを別々のターミナルで実行するとSSHできます。
コードがほしいときは、実際のコメント: https://github.com/nwtgck/actions-comment-run/pull/2#issuecomment-596020242
SSHデバッグ専用のGitHub Actionsならhttps://github.com/mxschmitt/action-tmateが有名だと思います。
上記のcomment-runも改良すればtmateを使った方法にできると思います。専用のActionsを使わずにcomment-runを一つ導入するだけで色々できるところが魅力です。
comment-runは複数のコードブロック、任意の言語が実行できる
例えばGitHub Actionsの環境でpip
が使えます。
以下のようにpip
でnumpyをインストールしてPythonが実行できます。
重要なことは「comment-runはJavaScriptとshebang(#!から始まるやつ)に対応している」ということです。JavaScript以外には一つずつ言語に対応せずにshebangに対応するだけでシンプルさを保っています。shebangに対応するあらゆる言語などを実行できます。もう少し色んな言語を動かす例を見ていきます。
Denoを動かす
以下がDenoを動かす例です。
実際のコメント: https://github.com/nwtgck/actions-comment-run/pull/1#issuecomment-596170740
npmと違ってライブラリをURLでimportできるところがcomment-runとの相性が良さそうなところです。
Goを動かす
以下でGo言語が動きます。
実際のコメント: https://github.com/nwtgck/actions-comment-run/pull/1#issuecomment-596176678
残念ながらgo run
はshebangに対応しておらずgorun
を使います。
Haskellを動かす
以下でHaskellが動きます。
実際のコメント: https://github.com/nwtgck/actions-comment-run/pull/1#issuecomment-596176131
Scalaを動かす
以下でAmmoniteを使ってScalaを動かします。Denoと同じでbuild.sbtなどを使うのと違い他のライブラリを一つのファイルでimportしやすくcomment-runと相性が良いと思います。
実際のコメント: https://github.com/nwtgck/actions-comment-run/pull/1#issuecomment-596180626
comment-runはコードブロックをシーケンシャルに実行する
comment-runの仕様の話です。
comment-runは```js
のコードブロックと shebang(#!のやつ) があるコードブロックを上から下へシーケンシャルに一つずつ実行します。
内部的には```js
はJavaScript Actionの環境でeval()
します。そのためcomment-runのJavaScript Actionの環境にある変数が利用できます。(利用できることを保証している変数に関してはのちに追記したいと思います)
shebangが付いているものは一時ファイルとして保存して、実行権限をつけて実行します。
ソースコードとしてGistやGitHubで管理したい
いろんな言語が動くというcomment-runの可能性を提示しましたが、JavaScriptを使うのが一番comment-runと相性がいいです。GitHubにコメントしたりtokenが使えるのはJavaScriptの環境だからです(環境変数やファイル経由で他の言語に引き渡す手はあると思います)。
そこで、
だんだんとロジックが複雑になるとソースコードとしてちゃんと管理したくなります。そこで簡単にGistで管理するTIPSです。
単純にGistに保存してそれをfetch()
してeval()
で実行するだけです。GitHubでもRawを押してコードのURLを使えばGitHubでもこの方法を使うことができます。
Gist: https://gist.githubusercontent.com/nwtgck/c8accac2bcd9939a6604a3500247012d/raw/comment-run-merge-preview.js
既存のnpmパッケージ利用とTypeScript利用したいしちゃんとコード管理したい
だいぶ欲張りですが全部叶えたいです。
まず既存のnpmパッケージを利用したいです。例えば以下のようにnpm i -S ...
好きにパッケージを入れられます。
この方法は短い実行コメントの時や初期だと良いと思います。実行コメントが育ってきてだいぶ使ってきて動作が安定してきたら、ちゃんと依存関係を固定したり毎回npm i
が走る時間を削減したくなってきます。
また、実行コメントが成長してくると、複雑なロジックやAPIなどの変化に強くするためにも型が欲しくなってきてTypeScriptで書きたくなってきます。
なおかつ、ソースコードを管理して他の人や再利用や改善がしやすい方法の紹介です。
仕組みとしてはTypeScriptで書いてnccでbundleしてできた.jsをGitHub Pagesにホストします。
利用する時は以下のようにGitHub Pagesからfetch()して使います。
@github-actions run
```js
(async () => {
const url = "https://nwtgck.github.io/comment-run-scripts/hello-world-comment.js";
const js = await (await fetch(url)).text();
eval(js);
})();
```
peaceiris/actions-gh-pagesでビルド後の.jsをGitHub Pagesにホストすることで、通常のTypeScript Actionのようにビルド結果をGit管理する必要がなくなることも利点です。
ソースコードはここにhttps://github.com/nwtgck/comment-run-scriptsあります。同じ用途でも個人でカスタマイズしたい場合やセキュリティ的な関係で自分の管理下のGitHub Pagesにしたい場合などは利用者はforkして運用するのがいいかなと思っています。
ディレクトリ構成は./hello-world-commentや./merge-previewなどがリポジトリ直下にあり、それらは独立したnpmパッケージなっています。各ディレクトリに入って好きにnpm i
することができます。
comment-runで利用可能なことを保証しているJavaScriptの変数
以下のREADMEにまとめました。
https://github.com/nwtgck/actions-comment-run/tree/6bea6fb49b8a3e2253f5e2e9ebfded12caf17113#available-variables-in-the-js-comment-context