denoで他の人にもすぐ使ってもらえるCLIツールを書こう(OSSでも仕事でも)

ちょっとしたCLIツール書いた際にどのように配布していますか?

利用者目線で考えると、OSSにしてパッケージ管理ツールを用意すればすぐ使ってもらえそうです。

とはいえ制作者目線で見ると、たった1ファイルくらいのものの場合、リポジトリを用意して設定ファイルを置いて〜というのもちょっと面倒なこともあります。

また社内向けには認証が必要なので、「git cloneしてディレクトリ移動して実行して〜たまに最新にして〜」などを案内するなども必要なこともありました。

そういった手間が必要な課題を解決するため、最近は社内向けのツールを含めdenoを使い始めました。

結論

スクリプトのtsファイルを書いたら以下のコマンドの形式で伝えれば完成です。

DENO_AUTH_TOKENS=$(gh auth token)@raw.githubusercontent.com deno https://raw.githubusercontent.com/cohalz/tools/e53a15512b87886b24621c67196700de9218afb9/hello.ts

上のコマンドを実行するとHello, Deno!と表示されるはずです。

これはパブリックリポジトリでも組織プライベートリポジトリでもGitHubのリポジトリに置いて指定してやればすぐ使ってもらえます。(パブリックリポジトリの場合はDENO_AUTH_TOKENSは不要)

この呼び出しを基本としていくつか用途に合わせてオプションを変えてやれば良いです。

信頼できるスクリプトであればDeno 2.6で追加されたdx(deno x)コマンドを利用しても良いと思います。

deno.com

解説など

denoコマンド(deno run)はURL指定でそのままスクリプトを実行できます。

docs.deno.com

ちなみにURLを指定した場合は、初回実行のみダウンロードが実行され以降はキャッシュから実行されます。

キャッシュファイルの場所は deno info | grep 'Remote modules cache'とかで確認できます。

最新の内容に更新する

-r(--reload)オプションをつけることでキャッシュを無視して再ダウンロードが可能です。

このreloadオプションをを使うことで、提供者がツールを更新したらすぐ利用者にも新しいものを使って欲しい場合に便利です。URLにはmainブランチなどを指定しておいて提示するコマンドも deno -r オプションをつけることで毎回最新のスクリプトを実行してもらえます。

逆に勝手に更新して欲しくない場合はスクリプトのURLをコミットハッシュなどにしておくなど推奨です。

プライベートリポジトリのコードを実行する

最初に提示したように以下の変数を設定することでプライベートリポジトリのコードをすぐ実行できます。

DENO_AUTH_TOKENS=$(gh auth token)@raw.githubusercontent.com

DENO_AUTH_TOKENS はdenoで認証が必要な場合にこの変数にトークンを含めることで解決できるというものです。(ドキュメント)

これはモジュールだけでなく、ツールの実行にも使えます。

これに gh auth token というGitHub CLIで利用しているトークンを表示するコマンドを合わせることでトークンを発行したり管理する手間なくプライベートリポジトリのコードを実行することができます。(ドキュメント)

もし権限がない場合には gh auth status とかで現在のトークンの権限が確認できます。

作ったもの紹介

せっかくなので最近denoで書いたOSSのCLIツールも紹介します。READMEなど整備できてないですがすぐ使えます。

github.com

mackerel-alert-stats.ts

期間を指定してその期間にあったMackerelのアラートの履歴を集計し、合計時間やMTTR、稼働率(アラート合計時間を期間で割ったもの)をCosense(Scrapbox)形式で出してくれるツール。 前の期間との比較も括弧内に出してくれるので、定期的に見て変化があったかを確認する用途に使えます。(本当はグラフとかにしたいですが)

table:2025/12/5 17:01:55 ~ 2025/12/19 17:01:55の集計 (前期間: 2025/11/21 17:01:55 ~ 2025/12/5 17:01:55)
監視名   回数  MTTR(分)   稼働率(%)
Alert1  8(+0)   3(+2)   99.88(-0.08)
Alert2  2(-4)   5(-5)   99.95(+0.25)

mackerel/list_monitors.ts

Mackerelの監視ルールを「スコープもしくは監視名」か「通知グループに紐づいたもの」で絞り込んで表示できるツール。

org内に複数サービスがあった際に、このサービスどういう監視があるんだっけ?とかチームで管理してる監視ルールってどれだっけ?とかを確認するのに使えます。

github/make_team_maintainer.ts

GitHubのorgとユーザを指定して、org内でそのユーザが所属するチーム全てにmaintainer権限を設定するスクリプト。

GitHubでは組織でOwnerからMemberに変更する際にチームのmaintainer権限も失うケースがあるため、再設定をするスクリプトです。これ自体は需要は少ないけどチーム一覧に何かするみたいなのを作りたい時に使えるかも?

以下の記事の対応を進めている際に作りました。

developer.hatenastaff.com

おわり

そんな感じでdenoでCLIツールを書く話でした。リポジトリ構成やファイル名なども気にせず雑多にスクリプトを置いてすぐ実行してもらえるという気軽さが気に入ってます。

denoは権限を明示的に許可しないとファイル操作や通信もできない状態なので、「外部のよくわからないスクリプトを実行して事故になってしまう」みたいなリスクも一段階落とせるのも嬉しいですね。

というわけで はてなエンジニア Advent Calendar 2025 の1月1日の記事でした。

前日のエントリーはこちらです。 developer.hatenastaff.com

クリアした美少女ゲームとか2025

去年はこちら。今年は全年齢とかもやったので「とか」です。

cohalz.co

アメイジング・グレイス -What color is your attribute?-

学が無くて騙される(一敗)。中盤からあまりに面白すぎる。キリエ。

猫撫ディストーション

当時から久しぶりにやって2回目。当時はやってなかったExodusもやったけどだいぶわかりやすくて良かった。

見上げてごらん、夜空の星を

この大空に、翼をひろげてよりもキャラの魅力が増して好き。突然名前呼ばれてビビる。

9-nine-

ep3から面白すぎる。 新章でみゃーこが救われて良かった。

春季限定ポコ・ア・ポコ!

桐谷華も藤咲ウサも初期の作品だけどキャラが良い。

すきま桜とうその都会

これも当時から久しぶりにやって2回目。ロケットパンチのシーン覚えてたけど意外と短かった。

はぴねす! えもーしょん

テンション高い佐本二厘(男)と安玖深音の妹を楽しむ。

恋×シンアイ彼女

エロゲ版喜嶋先生の静かな世界。

魔女こいにっき

聖ルートが完全に夜明け前より瑠璃色なのオマージュがわかって良い。多分他のルートもなんかのオマージュな気がする。変な作品すぎて大好き。

ななついろ★ドロップス

光の少女マンガで心が浄化される。

カタハネ

中盤からの雰囲気が最高すぎる。実質ポケモンZA。

処女はお姉さまに恋してる

ドキドキ女装ものかと思ったらほんわか日常系アニメだった。

キミのとなりで恋してる!

声が良い。

時計仕掛けのレイライン -黄昏時の境界線-

キャラの魅力出すことに関して優等生すぎる。小倉結衣の声が聞こえるとエロゲやってる感が出る。

ショコラ ~maid cafe ”curio”~ Re-order

丸戸節がもうすでに出ている。翠〜。

魔法少女ノ魔女裁判

cohalz.co

2010年くらいに自我があった人間としてはあまりに良すぎた。関連作品としてはわかってる限りではダンガンロンパ、まどマギ、シュタゲ、シークレットゲーム、うみねこ、逆転裁判あたり?

終わった後のロスがすごいしいまだに実況を見ている。

天神乱漫 LUCKY or UNLUCKY!?

佐奈かわいい。この時期の作品のシリアスのミスマッチなぁ。

夏ノ雨

翠〜。

蒼の彼方のフォーリズム

グラフィック頑張りすぎて解散したのでは?と訝しむ。EXTRA1はどういうお笑い?

ダンガンロンパ

魔法少女ノ魔女裁判の元ネタということでやった。みんなすごい真面目に議論するじゃん。ネタバレ耐性がかなり低いので注意。

シンソウノイズ ~受信探偵の事件簿~

ちゃんとミステリーしてて良かった。単純に出来が良い。

もののあはれは彩の頃。

中盤があまりに痛快すぎる。パーティーゲームの実況を見ている気分で楽しい。

ダンガンロンパ2

なんで七海のこと誰も教えてくれなかったんですか?無印やったらこれもやらないとな感じで非常に満足。ラストは唐突にその性癖出すんだ!?ってなってびびった。

金色ラブリッチェ

夜明け前より瑠璃色なかと思ったらパルフェだったみたいな作品。3人に勝てるわけないだろ!

おわり

好きな作品5選は上からこんな感じかなぁ。

  • アメイジング・グレイス -What color is your attribute?-
  • カタハネ
  • ショコラ
  • 魔女こいにっき
  • もののあはれは彩の頃。

Mackerelのアラート履歴をグラフアノテーションに投稿してダッシュボードで確認する

この記事は Mackerel Advent Calendar 2025 の16日目の記事です。15日目は id:kazeburoJSONL ログから柔軟にメトリクスを生成する ― mackerel-plugin-jsonl を作った話 でした。

みなさまMackerelのアラート履歴はどのように確認していますか?

  • Web上からアラートの一覧を眺める?
  • ツールで集計して以前との変化を確認する?

それとは別に、会議でダッシュボードを見る際にこんな状況になったことはありませんか?

  • メトリックとイベントの突き合わせが早押しクイズになってしまう
    • 「そのメトリックが変化したのはその日に障害があったのと関連していて...」など
  • メトリックの変化をの理由を調べるアクションは出るものの、監視ルールの見直しなどはあまり話題に上がらない

その結果監視ルールの見直しもあまりできず、その他のアクションを出す会話もよくわかっている人頼みになってしまいがちでした。

こういった課題を解決するために、アラートの履歴をグラフアノテーションとして投稿し、ダッシュボード上で確認する取り組みを始めました。

(ちなみにダッシュボードでアノテーションを表示できる機能は2023/12にリリースされました。)

mackerel.io

ダッシュボードでアラートをアノテーションに投稿すると見た目はこんな感じになります。

アノテーションの詳細には実際のアラートのURLを書くことでリンクになりそのまま当時のアラートへすぐ飛ぶこともできます。

もしprepalertも利用していれば、その先のアラートを開くだけでログや集計結果を確認でき一時調査もその場で済ませることができます。

これらにより、誰でもそのタイミングで何があったのかを調べやすくなり、効率よく改善を進めることがやりやすくなります。

目的別にアノテーションを分類する

実際には監視ルールには即時対応が必要なものだったり、キャパシティプランニングの通知目的であったりなど重要度が様々なものが存在します。

また、アラートがマイクロサービスなどサブコンポーネントにどのように影響を与え、逆にサブコンポーネントのアラートが本体にどのように影響を与えているかというのを知りたい場合もあります。

そういった監視ルールのアラートを同じように一箇所のサービス・ロールに追加することでアラートの傾向自体は分かりやすくなりますが、障害時などに大量のアラートでアノテーションが埋まってしまい、振り返るのが難しくなるというデメリットもありました。

そんな中、最近では以下のように監視名を設定していました。

監視ルールの prefix 用途
Alert:* SLO自体もしくはSLOに大きな影響があり営業時間外でもすぐに対応が必要
Issue:* SLOにすぐには影響がなく、営業日などに対応すれば良い
Info:* キャパシティプランニングや通知目的

この監視ルールをもとに投稿先を分類することで、アノテーションが混ざらないようにし振り返りやすくすることを行いました。

今回はこのような分類で投稿先のロールを変えるようにしました。

  • *-Only
    • *に書いてあるアラートのみ表示
    • (例: Alert-OnlyならAlert:*から始まるアラートのみを投稿)
  • *-Level
    • *のレベル以上のアラートのみ表示(ログレベルのようなイメージ)
    • (例: Issue-LevelならIssueとAlertのアラートのみを投稿)

監視ルールと投稿先の対応先はこんな感じになっています。

監視ルールのprefix アノテーション投稿先ロール
Alert:* Alert-Only, Alert-Level, Issue-Level, Info-Level
Issue:* Issue-Only, Issue-Level, Info-Level
Info:* Info-Only, Info-Level

ダッシュボードを定期的に確認する際に、用途に応じて表示するアノテーションのロールを切り替えることで効率よくアラートを確認できます。

例えば、

  • Alert-Levelでアノテーションを表示して、SLOに関連するアラート設定になっているか確認する
    • SLI/SLOの状況と比較してアラートが多すぎる/少なすぎるを確認する
  • Issue/Infoのみのアノテーションを表示して、キャパシティプランニングのアラートを確認する
    • 重要なアラートをあえて表示しないことで埋もれずに確認できる

こういったようにアラートとメトリックを適切に紐づけて確認することで、メトリックの変化があった際にアラートのアノテーションで関連がすぐわかるのはもちろん、逆にアノテーションがない時に「アラート設定していないけど大丈夫?」みたいな会話を何も考えなくとも誰もが素早くできるようになるというメリットもあると思います。

EventBridge + Step Functionsで実装する

最初はこの機能をprepalertで実現しようとしていましたが、実現したいことの単純さもありEventBridge + Step Functionsで実装することにしました。

まずは、公式ドキュメントの通りにEventBridgeの設定を有効にしておきます。

mackerel.io

その後、こんな感じの条件でアラートがクローズした際にStep Functionsが実行されるようにしておきます。

{
  "source": [{
    "prefix": "aws.partner/mackerel.io"
  }],
  "detail": {
    "alert": {
      "isOpen": [false]
    }
  }
}

Step Functionsの定義はたったこれだけで、あとはアラートが来ると監視名を元に、適切な投稿先にアノテーションが投稿されるようになります。

{
  "StartAt": "PostGraphAnnotation",
  "States": {
    "PostGraphAnnotation": {
      "QueryLanguage": "JSONata",
      "End": true,
      "Type": "Task",
      "Arguments": {
        "ApiEndpoint": "https://api.mackerelio.com/api/v0/graph-annotations",
        "Authentication": {
          "ConnectionArn": "ConnectionArn"
        },
        "Method": "POST",
        "RequestBody": {
          "title": "{% $states.input.detail.alert.monitorName %}",
          "description": "{% $states.input.detail.alert.url %}",
          "from": "{% $states.input.detail.alert.openedAt %}",
          "to": "{% $states.input.detail.alert.closedAt %}",
          "service": "annotation-service",
          "roles": "{% $contains($states.input.detail.alert.monitorName, /^Alert:/) ? ['Alert-Only', 'Alert-Level', 'Issue-Level', 'Info-Level'] : $contains($states.input.detail.alert.monitorName, /^Issue:/) ? ['Issue-Only', 'Issue-Level', 'Info-Level'] : $contains($states.input.detail.alert.monitorName, /^Info:/) ? ['Info-Only', 'Info-Level'] : [] %}"
        }
      },
      "Resource": "arn:aws:states:::http:invoke"
    }
  }
}

今回は単純にJSONataで入力を変換することで1ステップだけで実現することができましたが、もっと細かく場合分けをしたい場合でも、Step FunctionsのChoiceやJSONataなどを組み合わせることでユースケースにあった形で実現ができると思います。

こんな感じでStep FunctionsのJSONataサポートもあり、Mackerelのイベントから何かするみたいなのをLambda要らずで簡単に実現できるようになりました。アラートに限らずMackerelのイベントを色々とStep Functionsに送ってみて運用をイージーにしてみてはいかがでしょうか。

魔法少女ノ魔女裁判の感想など(ネタバレあり)

久しぶりに凄く刺さる作品だったので感想を書いてみる。

store.steampowered.com

ネタバレ少なめ感想(攻略順など)

ADV、デスゲーム、魔法少女もの、裁判系のゲーム等のエッセンスをうまく取り入れて高い水準でまとまっている作品で、今後ADVを語る上で外せない作品になっていると思う。

こういった作品を多くやってきた自分が「こういうジャンルはこういう要素が不可欠だよね」と思ったものが必要十分な量で入っていて非常に満足度が高い。

内容自体は過激だけどCGはある程度配慮されたものになっているのでそういったゲームの入門にも?良いと思う。

攻略順について

  • BADエンド選択肢は先に選ぶと良いかも、そういう意味でヒントは有効にしたままでも良いとは思う
  • 裁判の時間切れエンドはクリアしてからでも問題ない、CGがある場合もあるので裁判の最初にすぐ飛べるようセーブしておくと良さそう

以下各章のネタバレあり細かい感想

1章

ヒロまじ??って思いながら進めていたと思う。

トリックはあんなに何本もくっつけて武器にできるのか??と今でも思ってるけど動けなかったならまぁ可能なのかなぁ。

裁判パートは怪しい人はいつつも決定的な証拠がないみたいな流れは楽しかったなぁ。配信アーカイブだ!リンゴだ!はあんまちゃんと覚えてなくて難しかったけど。

2章

ホラー映画のくだりが結構印象に残ってたのもあって犯人はすぐわかってしまった。 動機のところでハッとなったけど確かに思い返すと違和感はめちゃくちゃあったなぁと思って事前に気づきたかったなぁとなった。

3章

ピクニックパートで、あぁどっちか死ぬんだなとは思っていたけど犯人もいたとは全然想像していなかった。

裁判パートの「それでも犯人だって言いますか?」でズームするシーン大好きすぎる。

シェリーのラストシーンで泣かされるとは思ってなかった。

4章

自殺じゃないか?という議論から最後に安全圏だったエマに向いて自供パートに入ったのは衝撃的だった。処刑用BGMが主人公にかかるというのは憎い。それでも処刑時にナノカが下からやってきて真実を教えてやってくるのでは!と思ったら悪い?意味で裏切られた。

炙りビンを始めとしたココの全く緊張感のないツッコミラッシュが楽しくてわざと選択肢を外しても良いくらい。

5章

特にラストシーンでメルルが溶鉱炉に沈んでいくシーンは涙無しには見られなかった。

(表向きの)ラストバトルで推理パートはめちゃくちゃ難しかったが燃えた。今までトリックにしか使ってなかった他人の魔法を信じて解決に導くというのは熱い。

ココやマーゴのラストシーンで一枚絵がなかったのはちょっとどうなの??とこの時には思ったけどその気持ちを第6章以降に持っていって消化するつもりだったのかなと後で思った。

地の文のトリックは他の人のプレイを見るまでちゃんと理解できてなくてその時だけじゃなくて「ずっと」そうだったのを後から知ってハッとなった。

ところで関係ないけど直近で時計仕掛けのレイラインをやっていたのもあってユキの声聞いて安玖深音じゃないか??とちょっと思った(けど違った)というのもあった。(ななついろ★ドロップスにも安玖深音のユキちゃんいるし...)

6章

Cパートか?と思ったら全然終わらなくて2nd OPが流れた時は本当に痺れたしココの魔女化っぽい一枚絵っぽいものが写った時は先が気になりすぎてあまりに嬉しかった。

デスゲームといえば複数ルート!

6~8章の裁判パートはそれまでのパートからさらに難易度が上がっているのも良かった。魔法を使わないトリックやモノマネの強化まであって単純に面白さが強化されて非常に体験が良かった。

7章

1周目主人公であるエマとのバチバチの議論からの一緒に真犯人を見つける流れは美しすぎる。

ラストはデスゲームであるあるな救いという感じでこれも大好き。

8章

今まで激しく対立していた相手と共犯になり「こんなんひっくり返せるのか?」「エマが身代わりになるのか??」とかハラハラして進められた。死んだふりだったというのは全然わからなかったし犯人を詰めるまでも長いので非常に満足感が高い。

これまでの事件の蝶の話がここにきて繋がってくるのも良かった。

9章

裁判パート自体はトリックに重量が出てくるので簡単に予想できた上に短かったので不満はあったけど、ハンナ自体が頭の切れるキャラではなく理由も衝動的で杜撰だったのを考えるとまぁリアリティはあるのかなと納得はできるな〜。 それにしてもシェリーが本当にいい役すぎる。

最終章

9章で消化不良だった裁判パートを大ボリュームでやってくれて満足。

最後だからかはっちゃけて思いっきり「異議あり!」ってついに言っちゃったのあまりに良かった。 最も魔女から遠く最も人間離れしたシェリーが最後に立ちはだかってくるのもいい演出だと思う。欲を言えばあっさりだなとは思ったけど既に十分な見せ場はあったしエマも残ってるしなぁ。でも顔はあまりに怖いよ...。

5章ではあまりに救いのなかったメルルが救われたのも良かった。

スタッフロール中に各キャラのその後が描かれるのは良いゲーム。

ところで

魔法少女ノ魔女裁判のことを知ったのはこれがきっかけ。

この人は2021年頃から推している人で、デスゲームをテーマにした曲を歌ったり、

www.youtube.com

様々なADVの曲を歌ったりとADVが大好きなことをずっと公言していた。

www.youtube.com

そういった中で自分も同じくADVを愛するものとして力になろうと本人が好きな絵師にイラストを発注してもらったり、

ADV界隈のラジオに出演した際にはお便りを送って読まれたりなど微力ながら応援してきた。

特にラジオで送ったお便りで「今後比良坂芽衣名義でADVに出演したい気持ちはありますか?」と聞いていて「もちろん」との回答が返ってきたのと、パーソナリティであるAyumi.さんも協力してくれるという非常に今後が期待できる状態になっていた。

今回の声優オーディションにそれが直接影響したかはわからないけれど、少なくとも本人にとってADVへの出演を強く望む人が一人いるということは伝わっていた状態ではあったと思う。

そういった中で魔法少女ノ魔女裁判の声優として出演が決まり、実際にプレイして素晴らしい作品だったことは本当に今まで推してきて良かったなと思えた瞬間だった。

死ぬ前に推せ!

Fargate Spotの中断通知をAWS Step FunctionsだけでMackerelのアノテーションに記録する

Mackerelでグラフの変化とFargate Spotの中断イベントを合わせて確認したくなったので、Step Functionsで利用できるようになったJSONataを使ってLambdaを使わずStep FunctionsだけでMackerelに送れるようにしてみました。

ECSのクラスタ名と同じMackerelのサービス名にイベント時のタイミングでアノテーションを投稿する実装はこんな感じになります。

{
  "QueryLanguage": "JSONata",
  "StartAt": "Call HTTPS APIs",
  "States": {
    "Call HTTPS APIs": {
      "Type": "Task",
      "Resource": "arn:aws:states:::http:invoke",
      "Arguments": {
        "Headers": {
          "Content-Type": "application/json"
        },
        "InvocationConfig": {
          "ConnectionArn": "ConnectionArn"
        },
        "Method": "POST",
        "ApiEndpoint": "https://api.mackerelio.com/api/v0/graph-annotations",
        "RequestBody": {
          "title": "SpotInterruption",
          "from": "{% $floor($toMillis($states.input.time) / 1000) %}",
          "to": "{% $floor($toMillis($states.input.time) / 1000) %}",
          "service": "{% $replace($states.input.detail.clusterArn, /.*?\\/(.*)/, \"$1\") %}"
        }
      },
      "End": true
    }
  }
}

実際に投稿された様子

Fargate Spotの中断イベントはEventBridgeで拾うことができるので条件を設定し、対象のStep Functionsを実行すれば動かせます。

zenn.dev

気をつけポイントはあまりなく、EventBridgeのイベントのtime はISO 8601の形式でやってきて、MackerelのAPIはepoch秒なので変換してあげる必要があるくらいですがJSONataで実現可能です。

文字列操作もいろんな関数が用意されていて、正規表現で置換するなどもStep Functionsだけできるようになってます。

docs.jsonata.org

今回は簡単のためにECSのクラスタ名とMackerelのサービス名が同じ場合を想定しましたが、そうでない場合もECSのクラスタにMackerelのサービス名のタグをつけておいて、イベントがあったクラスタのARNからそのタグを取得してそこに送信みたいなフローもLambdaいらずで簡単にできそうですね。

あわせて読みたい

Step FunctionsからMackerelにリクエストを送る実装はこの記事が大いに参考になりました。

blog.utgw.net

記事内のLambdaで使っている Date.now()Math.random() 相当の関数はJSONataにもあるのでこの例も全てStep Functionsだけで実現可能になってそうですね。

3/1-2の週末日記

2日間ともPokémon GO Tour:イッシュ地方に参加したのでその記録。

ポケモンGO自体は11月のPokémon GO ワイルドエリア:福岡の時になんも調べず復帰してという感じだったので中身見てちゃんとやるのは初めて。

1日目

友達を誘って一緒にキュレムを倒しまくるなどした。

早めにキュレムの100%が出て合体したり、マラカッチの色違いは出たりしたもののそのくらいで図鑑は埋まった程度。

チームチャレンジでナゲキが出るというのでやったものの、対戦するタスクが不具合でカウントされなかったりずっとクイタランとアイアントしかでないみたいな状態になったりしたので最終的にその辺にいたシャドウナゲキレイドをやるなどした。

終盤はキラチャームを使ってお互いに合体背景キュレムのキラを作ったり色違いキラを交換しあったりと特別な交換をしまくった。

2日目

ソロで午後から参戦した。

キュレムは大体揃っていたので午後からゆっくり対戦用の個体など野生中心に捕まえていた。

色違いも1日目に比べて急に出るようになってバスラオやプルリルの全種色違いや終了間際にはタスクからシンボラーの背景付き色違いも出て相当運が来ていた。

ブルンゲルなんかは対戦でもずっと使ってたのでかなり嬉しい。

途中赤坂駅あたりで天気の判定が変わるのに気づくなどもあり往復しまくってたら初日以上に歩くことになった。

終わりはビアキチでオムレツと肉吸いが食べられるキャンペーンやってて行ったけど冷静に考えると6時間歩きっぱからそのまま2時間近くたちっぱで飲み続けていたのだいぶ元気があった。

という感じで最近あんまりブログ書けてなかったなとなったのでリハビリがてら書いてみようとなったのだった。

Fujiwara Tech Conference 2025でfujiwara-wareやecspressoのCI/CDについて話しました&補足など

connpass.com

資料はこちらです。

紹介できなかったところや発表・懇親会中に質問があった内容について補足します。

fujiwara-wareへの貢献

以前にecspresso MeetUpでも紹介しているのでそちらもご確認ください。

speakerdeck.com

個人的に一番好きな貢献はdiffコマンドによるUnified Diff形式の出力で、v1の時はオプションですがv2からはデフォルトで有効にしてもらって自分を含め多くの人が使う機能を用意できたなと思ってます。

github.com

ステージング環境を停止・再開する部分について

再開する際になぜ scale --tasks=1 になってないのかという質問がありました。

これはリポジトリをマスターとして扱いためで、desiredCountも含めコード管理しています。単にdeployコマンドを実行することでdesiredCountが1でない場合や、その他の設定を変更したいときにマージ前のPR上で試してそのままずっと忘れてなんか設定が変だけど?みたいなことを防げます。

ecspressoリポジトリのディレクトリ名規則について

各ECSサービスをサービス名/コンポーネント名/環境名/ という単位で分けてディレクトリを作っています。これらのディレクトリの中でそれぞれecspressoのファイルがあるという感じです。

.
├── blog
│   ├── app
│   │   ├── base
│   │   ├── production
│   │   └── staging
│   └── proxy
│       └── production
└── bookmark
    └── app
        └── production

ここでいうサービス名はECSサービスではなく外部から見たサービスという意味で、コンポーネント名はそのサービスを構成するコンポーネントになっています。

環境名はそのまま本番かステージングかみたいな名前が入り、各環境で共通化したいファイルがあったらbaseディレクトリを別途作ってそれぞれでimportしています。

この命名規則にすることでリポジトリのルートを見るだけでどのサービスがあるかわかり、各サービスにどのコンポーネントがるかわかるようになるという感じです。

ちなみにこれらの名前はECSクラスタやECSサービス名と同じになっている必要はなく、リポジトリ上で管理しやすい名前にしています。

普段の開発の流れとデプロイ対象の特定をまとめるとこんな感じです。

  • アプリケーションリポジトリでmainを変更
    • blog/app/staging 以下のイメージタグファイルを更新&自動マージ
  • アプリケーションリポジトリでリリース作業を開始
    • blog/app/production 以下のイメージタグファイルを更新&手動マージ
  • blog/app/base を何か手動で変更
    • blog/app/stagingblog/app/production もデプロイ対象に

このルールを完全に真似する必要はないですが、何か命名規則を決めることで例えば staging のものだけを対象に実行するみたいなことを入れやすくなります。

jsonnetのimportのルールについて

jsonnetの共通化はいくらでもできてしまいますがこれもルールを設けています。

  • importは各コンポーネントのbaseディレクトリからのみして良い
    • 他の環境からimport禁止
    • 他のコンポーネントやサービスからimport禁止
  • ecspresso.jsonnet のみトップレベルからimportしても良い

例えば blog/proxy/production の設定は blog/proxy/staging からimportすることはしないし別のblog/app/production みたいな別コンポーネントからimportしないようにしています。

これはfluent-bitであったりx-rayであったりのサイドカーでも同様です。

現状はこれらサイドカーの変更頻度も低い他、コンテナ定義ごとにファイルを分けているのあってそこまで手間にはなっていないです。(タグなどの書き換えなどもエディタの検索で十分)

発表でもあったように、多少の手間を許容してとにかく「間違ってデプロイされてしまう/デプロイできてない」みたいな事故をとにかく防ぐ方針でやっています。

ただ今後OTel Collectorなどデフォルトで全サービスに入れたいみたいなことが発生した際に手間ではあるのでそこは課題の一つかなと思います。

解決策としてはトップレベルに common などのディレクトリを作って、そこからimportするみたいな感じになりそうですが、これもまた影響のあるサービスを検出する部分がさらに複雑になってしまうのでその処理も考慮する必要があります。

ecspresso.jsonnet のimport

これに関してはどのECSサービスも同じ設定でECSの設定にもほぼ影響しないのでimportしています。

{
  required_version: '= v' + importstr '.ecspresso-version',
  region: 'ap-northeast-1',
  service_definition: 'service-def.jsonnet',
  task_definition: 'task-def.jsonnet',
  timeout: '15m0s',
  plugins: [{
    name: 'cloudformation'
  }],
}

このファイルをecspresso用リポジトリのトップレベルなどに置いておき、各サービスから参照しています。

local base = import '../../../base-ecspresso.libsonnet';

base {
  cluster: 'blog',
  service: 'app',
}

ecspressoの各設定にはクラスタ名やサービス名を書くだけで済むようになり、他に追加したいものがあればそれだけ書けば良いようになって便利です。

その他感想など

スライドでも軽く話したけど、リリースのフローの作り込みは作り込みが激しいのとpush型が基本になっていて、この辺どうにかできればみたいなのをCNDWでも会話していました。PipeCDのLTを聞いてだいぶ良さそうだったので検討してみたいですね。