くりにっき

フルスタックキュアエンジニアです

#fukuokark05 に参加した

福岡Rubyist会議05 に参加した感想。

regional.rubykaigi.org

全体を通しての感想

今回のテーマが"最近、何してる?"なので、トークを通じて普段他の人がどんなことをしてるのが知れたのが面白かったです。

こういう地域RubyKaigiらしさいいよね...

自分にとって(地域RubyKaigiじゃない方の)RubyKaigiは日々の活動を発表する生活発表会なので(異論は認める)、福岡Rubyist会議05はRubyKaigiっぽさがあってよかったです。

トークの個別感想

特に印象に残ったのをピックアップ

Rubyist としてアメリカで 10 年戦ったらどうなったか

トークで話されてた https://github.com/jugyo/react-hcl を聞いて Carinaの型システムについて - Gosuke Miyashita のことを思い出した。みんなAlt Terraform作りがち

Panel Discussion:「コミュニティの垣根を越えよう」

普段Rubyのコミュニティに参加することが多いので外部から見た感想とかが聞けるのがよかった。

Keynote:mruby in the 8 bit world: mruby VM on Zilog Z80

発表時の本番トラブルに対してフォールバック先が用意されていてよかった。(インフラエンジニア視点)

おまけ:福岡飯

モツ鍋

牧のうどん

福岡でしか食べれないやつ

むっちゃん万十

福岡でしか食べれないやつその2。20年以上前から変わらない味

↑は博多駅のバスターミナルのむっちゃん万十。

実は翌日に西新の方のむっちゃん万十にも行ったんですが移転しててビックリしました。

博多ラーメン

www.instagram.com

鯛めし

www.instagram.com

RubyKaigi 2026で不採択だったProposalを公開します

RubyKaigi 2026 に出したProposalが不採択になったので出したProposalを公開します。

出したProposal

Title

Managing 100+ repositories with OSS

Abstract

I'll talk about an OSS tool I built specifically to maintain a massive number of other OSS repositories.

I currently maintain 80+ OSS repositories. Including private ones, that's 100+ repositories in total, spanning various languages like Ruby, Go, and Terraform across both https://github.com/ and https://gitlab.com/ .

For example, every year on December 25th, a new version of Ruby is released. Even a simple task like adding the new version to the build matrix requires updating nearly 50 different repositories.

To solve this kind of tedious "grunt work"—or what we in Japan call "sashimi tanpopo"—I created https://github.com/sue445/sashimi_tanpopo .

In this talk, I’ll introduce this tool and the ecosystem of utilities I’ve built to streamline these tasks.

日本語(Geminiで英訳&添削する前の原文)

大量のOSSをメンテするために作ったOSSの話をします。

私は現在80個以上のOSSをメンテしています。プライベートリポジトリも含めればメンテしているリポジトリは100個を超えます。言語はRubyやGoやTerraformなど様々です。管理してるリポジトリもGitHubとGitLabに分かれています。

毎年12月25日には新しいRubyのバージョンが出ますが、各gemのリポジトリのビルドマトリクスに新しいRubyのバージョンを追加するだけでも50個近いリポジトリを編集する必要があります。

このような刺し身タンポポ作業を解決するために作った https://github.com/sue445/sashimi_tanpopo やその関連ツール群について話します。

Detail

https://github.com/sue445/sashimi_tanpopo というgemを作った話をします。

アウトラインはこういうのを考えてますが変わるかもしれません。

https://sue445.hatenablog.com/entry/2025/10/26/121451 に書いたことがベースになりそうですがブログ公開から時間が経ってそれなりにsashimi_tanpopoを活用してきたのでその辺の知見も話せると思います。

sashimi_tanpopoだけだと時間が余りそうな気がするので、時間が許すなら複数のリポジトリでコピペしまくってたGitHub Actionsのworkflowファイルを一元管理していい感じにした https://github.com/sue445/workflows についても話そうかなと思ってます。

Pitch

OSS活動をしてる人には大きく分けて「1つor少数のOSSを深く狭くメンテするタイプ」と「たくさんのOSSを浅く広くメンテするタイプ」の2種類がいると思っています。

私は後者のタイプです。浅く広くと言っても自分の代表作の https://github.com/sue445/rubicure は毎年新機能を追加しつつ10年以上コツコツメンテしてます。

前者の人の話はRubyKaigiなどでもよく聞きますが、後者の人の話はあまり聞かない気がします。

たくさんのOSSをメンテする人にはその人なりの問題があるので https://github.com/sue445/sashimi_tanpopo を作って解決しました。

たくさんのOSSをメンテしてる人にしか分からない苦労を共有しつつ、自分のようにたくさんのリポジトリをメンテしてる人やチームに解決策を持ち帰ってもらおうと思っています。

また、Detailにも書いた https://github.com/itamae-kitchen/itamae は僕がメンテしてるOSSの1つなので、内部実装やメリデメを熟知してます。

Itamaeのメンテナ自ら「こういうケースではItamaeは向かない(だからこそ再実装する必要があった)」ってのは説得力があると思います。

DependabotのSecurity Alertで作られたPRをいい感じにする

tl;dr;

自分が管理してるOSSで Dependabot Security Alert で作られたPRに対して自動でタイトルにCVE IDをつけたりsecurityラベルをつけられるようにしました

モチベーション

PRのタイトルにCVE IDを書いておくとリリースノートでオートリンクされて便利です。

今まではDependabot Security Alertが作ったPRに対して手でタイトルを修正をしてたのですが、自動化の機運が高まったのでやりました。

技術的なこと

dependabot/fetch-metadata で alert-lookup: true をつければGHSA ID( https://github.com/advisories 上のID)がとれるので、GHSA IDからさらにCVE IDを取得してます。

あとラベルをつけておくと .github/release.yml でいい感じに分類できるようになるのでsecurityラベルをつけるようにしました。

workflow抜粋

- uses: actions/create-github-app-token@v2
  id: app-token
  with:
    app-id: ${{ secrets.app-id }}
    private-key: ${{ secrets.private-key }}
    permission-contents: write
    permission-issues: write # to create security label
    permission-pull-requests: write
    permission-vulnerability-alerts: read

- name: Dependabot metadata
  id: metadata
  uses: dependabot/fetch-metadata@v2
  with:
    github-token: ${{ steps.app-token.outputs.token }}
    alert-lookup: true

- name: Create `security` label if it doesn't exist
  if: steps.metadata.outputs.ghsa-id != ''
  run: |
    if ! gh label list --json name --jq '.[].name' | grep -qx "security"; then
      gh label create "security" --color "ffc107" --description "Security related issues or PRs"
      echo "[INFO] Created security label"
    fi
  env:
    GH_TOKEN: ${{ steps.app-token.outputs.token }}

- name: Add security label and GHSA-ID (or CVE-ID if possible) to PR
  if: steps.metadata.outputs.ghsa-id != ''
  run: |
    # Only prepend GHSA-ID or CVE-ID if the title does not already start with a GHSA or CVE prefix
    if [[ ! "${PR_TITLE}" =~ ^\[(GHSA|CVE)-[0-9A-Za-z-]+\] ]]; then
      # When multiple CVE identifiers exist for a GHSA advisory, we intentionally use a single CVE ID for the PR title prefix.
      CVE_ID=$(gh api "/advisories/$GHSA_ID" --jq '.identifiers[] | select(.type=="CVE") | .value' 2>/dev/null || echo "")
      if [ -n "${CVE_ID}" ]; then
        PR_TITLE="[${CVE_ID}] ${PR_TITLE}"
      else
        PR_TITLE="[${GHSA_ID}] ${PR_TITLE}"
      fi
    fi
    gh pr edit "$PR_URL" --title "${PR_TITLE}" --add-label "security"
  env:
    PR_URL: ${{ github.event.pull_request.html_url }}
    PR_TITLE: ${{ github.event.pull_request.title }}
    GH_TOKEN: ${{ steps.app-token.outputs.token }}
    GHSA_ID: ${{ steps.metadata.outputs.ghsa-id }}

細かいけど、 actions/create-github-app-token で permision-xxxx みたいな引数を明示的に渡すことで必要な権限をyaml上で宣言できて便利ですね。

workflow全文は https://github.com/sue445/workflows/blob/main/.github/workflows/dependabot-security-alert.yml を見てください。

注意点

Security advisoryの情報は secret.GITHUB_TOKENでは取得できません。

https://github.com/dependabot/fetch-metadata のREADMEには alert-lookup: true を使うにはPAT(Personal Access Token)を使えって書いてたけど調べたらGitHub AppでDependabot alertsのRead-onlyをつけることで取得できたのでPATを使うよりはこっちの方がセキュアかと思います。*1

あとGitHub Appを利用する場合にはGitHub Appの証明書をsecretにセットしてから利用することになると思いますが、Dependabotが参照できる必要があるのでAction Secretではなく Dependabot Secrets にセットする必要があります。

苦労したポイント

Dependabot security alertによるPR作成がworkflowのトリガになっているため、動作確認が非常に大変でした。(自分がメンテしてるOSSで使ってるライブラリで脆弱性が発生しないと動作確認ができない)

One more thing

privateリポジトリも含めると僕は100個以上のリポジトリをメンテしてるんですが、全てのリポジトリにさっきのような長大なworkflowファイルを置きたくなかったのでworkflow本体は https://github.com/sue445/workflows に置きつつ他のリポジトリからはそれをincludeする方式にしました。(最近のマイブーム)

各リポジトリでは下記のようなyamlを書くだけで使えるようにしてます。

name: dependabot-security-alert

on:
  pull_request:
    types:
      - opened
      - synchronize # PR branch is rebased

jobs:
  auto-merge:
    uses: sue445/workflows/.github/workflows/dependabot-security-alert.yml@main
    secrets:
      # TODO: Set secrets to Dependabot secrets
      app-id: ${{ secrets.GH_APP_ID }}
      private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
      # slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

https://github.com/sue445/workflows?tab=readme-ov-file#dependabot-security-alert

1つのworkflowファイルから複数の Reuse workflows をincludeすることができるので、実際には下記のように DependabotのPRをいい感じに自動マージするworkflow もセットで使っています。

name: dependabot-manager

on:
  pull_request:
    types:
      - opened
      - synchronize # PR branch is rebased

jobs:
  dependabot-auto-merge:
    uses: sue445/workflows/.github/workflows/dependabot-auto-merge.yml@main
    secrets:
      # TODO: Set secrets to Dependabot secrets
      app-id: ${{ secrets.GH_APP_ID }}
      private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
      slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

  dependabot-security-alert:
    uses: sue445/workflows/.github/workflows/dependabot-security-alert.yml@main
    secrets:
      # TODO: Set secrets to Dependabot secrets
      app-id: ${{ secrets.GH_APP_ID }}
      private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
      slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

2026/2/20 23:50追記

https://github.com/dependabot/fetch-metadata の alert-lookup でApp Tokenも使えることを追記するパッチを投げた

github.com

*1:追記にも書いたけどApp Tokenと一緒に使う例をパッチで投げました

terraform-plan-changes-countを作った

terraform-plan-changes-countについて

github.com

Terraformのplanに含まれるchangesの件数を取得するためのGitHub ActionsのActionです

下記の例が全て

steps:
  - uses: hashicorp/setup-terraform@v3

  - run: terraform plan -out=tfplan

  - name: Run terraform-plan-changes-count
    id: plan-changes
    uses: sue445/terraform-plan-changes-count@v0
    with:
      plan-path: tfplan

  - name: Print outputs
    run: |
      echo "CREATE_COUNT=${CREATE_COUNT}"
      echo "UPDATE_COUNT=${UPDATE_COUNT}"
      echo "DELETE_COUNT=${DELETE_COUNT}"
      echo "TOTAL_COUNT=${TOTAL_COUNT}"
    env:
      CREATE_COUNT: ${{ steps.plan-changes.outputs.create }}
      UPDATE_COUNT: ${{ steps.plan-changes.outputs.update }}
      DELETE_COUNT: ${{ steps.plan-changes.outputs.delete }}
      TOTAL_COUNT:  ${{ steps.plan-changes.outputs.total }}

モチベーション

僕は個人でTerraformのリポジトリを10個以上メンテしています。

Terraformの本体やProviderをバージョンアップするPRは毎月自動で作られるのですが、planの結果を見て1つずつマージしていくのも大変です。

これらのPRの場合基本的にplanのchanges(create, update, delete)の件数が0件なことがほとんどなので、ほとんどのケースで自動マージできます。

そのためGitHub Actions上でplanのchangesの件数を取得しやすくするために作りました。

例:dependabotが作ったPRでplanのchangesの件数が0件だった場合に自動マージする例

jobs:
  terraform:
    permissions:
      contents: write
      pull-requests: write

    steps:
      - name: Run terraform-plan-changes-count
        id: plan-changes
        uses: sue445/terraform-plan-changes-count@v0
        with:
          plan-path: tfplan

      - name: Auto-merge Dependabot PR when there are no plan changes
        run: |
          gh pr review --approve "$PR_URL"
          gh pr merge --auto --merge "$PR_URL"
        if: github.event_name == 'pull_request' && steps.plan-changes.outputs.total == '0' && github.actor == 'dependabot[bot]'
        env:
          PR_URL: ${{ github.event.pull_request.html_url }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

元ネタ

GitLabだと下記のような方法でplanのchangesの件数を取得してMergeRequest上で表示することができます。

docs.gitlab.com

ここに書かれてるスクリプトをGitHub Actionsでいい感じに使いたくて作りました。

2025年に作ったものまとめ

レギュレーション

  • 既存OSSのバージョンアップを含めたらきりがないので2025年に新しく作ったものだけをカウント

go-gem-wrapper

自分の中で今年のベストオブOSSといったら間違いなくgo-gem-wrapper

github.com

go-gem-wrapperでRubyKaigiにも登壇させてもらったしBundler v4の新機能としても取り込まれた

inside.pixiv.blog

sue445.hatenablog.com

connpass_api_v2-ruby

Connpass API v2が出た直後に作ったやつ

github.com

sue445.hatenablog.com

x_post_sanitizer

個人ツールのために作ったやつ

github.com

sue445.hatenablog.com

sashimi_tanpopo

OSSメンテのために作ったOSS。これのおかげで日々のOSSメンテが割と楽になってる

github.com

sue445.hatenablog.com

sue445/workflows

https://github.com/r7kamura/workflows にインスパイヤされて作った、自分がメンテしてるOSSのリポジトリで使ってる汎用workflow集。

github.com

既存リポジトリのworkflow移行は死ぬほどつらかったけど60個近いリポジトリでコピペしまくってたyamlを1箇所に集約できたのは個人的にはかなりの文明開化。

草

基本的にGitHubメインだけどGitLabにも出してるOSSがあるのでそっちもちょいちょい草が生えています。

https://github.com/sue445

https://gitlab.com/sue445

yardで複数ディレクトリに同名ファイルがある場合にファイル一覧をいい感じにしたい

tl;dr;

ファイルに @title を埋め込むかyard用の中間ファイルを作る

コンテキスト

例えば下記のようなファイル構成があったとします

  • README.md
  • sub-dir/README.md

https://github.com/lsegal/yard でドキュメントを作るとファイル一覧で同じものが並ぶのでなんとかしたかったというのが発端

方法1: ファイルに @title を埋め込む

https://rubydoc.info/gems/yard/file/docs/GettingStarted.md#adding-meta-data-to-extra-files を参照。

これでもいいんだけどGitHubとかで見た時にyardのタグが見えるので見栄えが悪いのがちょっと難点。

方法2: yard用の中間ファイルを作る

.yard/sub-dir-README.md のような名前で sub-dir/README.md をincludeするような中間ファイルを作成。

{include:file:sub-dir/README.md}

.yardoptsなどで下記のように中間ファイルのパスを渡す。

.yard/sub-dir-README.md

こうすることで sub-dir/README.md にyardのタグを書かずにファイル一覧をいい感じにできます。

go-gem-wrapperでこの問題に困ってこっちの方法を採用しました。

github.com

マージした後で気付いたけど .yard/go_gem_README.md に @title 書いてもよかったかも。

中間ファイルが増えるのもちょっと微妙だけど自分はこっちの方がまだマシかなという判断。異論は認める

dependabot-auto-mergeを移行した

dependabot-auto-merge(下記で書いたやつ)を新しい仕組みに移行しました。

sue445.hatenablog.com

発端

dependabot-auto-mergeはDependabotの @dependabot merge コマンドに依存してるんですが、2026/1/27に廃止予定なので移行する必要がありました。

github.blog

やったこと

色々調べたんですが以前 id:ohbarye さんが書かれてものが自分のやりたかったことに近かったので参考にさせてもらいました。

blog.smartbank.co.jp

しかし移行予定のリポジトリが20個近くあったため、workflowファイルをコピペしていくとロジックを修正したくなった時に全リポジトリを修正してまわる必要があって大変なのは明らかでした。

そのため、この機会にworkflowの実装を https://github.com/sue445/workflows (自分がメンテしてるリポジトリ全部にいれる汎用workflow集)に移行しました。

最終的に完成した新dependabot-auto-mergeは https://github.com/sue445/workflows/blob/1e4d54c9399e7229b37c60255eb937714481d704/.github/workflows/dependabot-auto-merge.yml

各リポジトリに下記のようなworkflowは書いてまわる必要があるものの、workflowの本体は1箇所に集約されてるので便利。

name: dependabot-auto-merge

on:
  pull_request:
    types:
      - opened
      - synchronize # PR branch is rebased

jobs:
  auto-merge:
    uses: sue445/workflows/.github/workflows/dependabot-auto-merge.yml@main
    secrets:
      # TODO: Set secrets to Dependabot secrets
      app-id: ${{ secrets.GH_APP_ID }}
      private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
      slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

基本的に常に最新を使っていいのでmainブランチ指定にしてるんだけど、Require actions to be pinned to a full-length commit SHA *1 が使えなくなるのが難点っすね...