<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>monolithic kernel</title><description/><link>https://blog.mono0x.net/</link><item><title>固定した mise のバージョンを multi-gitter でまとめて更新する</title><link>https://blog.mono0x.net/2026/02/21/bump-mise-version-using-multi-gitter/</link><guid isPermaLink="true">https://blog.mono0x.net/2026/02/21/bump-mise-version-using-multi-gitter/</guid><pubDate>Fri, 20 Feb 2026 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;GitHub Actions で mise-action を使うときの再現性のために &lt;a href=&quot;/2026/01/29/jdx-mise-action/&quot;&gt;mise のバージョンを固定した&lt;/a&gt;のだが、これによって、mise のバージョンを更新する手間が生じるようになった。同じことをやっているリポジトリが複数ある場合、リポジトリの数だけ作業を繰り返す必要があり非常に面倒。&lt;/p&gt;
&lt;p&gt;同じ操作を繰り返すだけの単純作業なので、まとめて更新したいと思い調べていたところ、&lt;a href=&quot;https://github.com/lindell/multi-gitter&quot;&gt;multi-gitter&lt;/a&gt; というツールを見つけた。複数のリポジトリでコマンドを実行して、ファイルが正常に更新されたらその内容を push するという、今回の目的にはまさにぴったりなものだった。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh
MISE_VERSION=&quot;$(curl -s https://mise.jdx.dev/VERSION)&quot;
MISE_VERSION=&quot;$MISE_VERSION&quot; multi-gitter run &quot;./mise-up-internal.sh&quot; \
  --token &quot;$(gh auth token)&quot; \
  --repo org/repo --repo ... \
  -m &quot;Bump mise version to $MISE_VERSION&quot; --skip-pr
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh
perl -i -pe &quot;s/^(\s*version:\s*)[0-9.]+/\${1}$MISE_VERSION/&quot; .github/actions/setup-mise/action.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--token&lt;/code&gt; には事前に &lt;code&gt;gh auth login&lt;/code&gt; でログインして用意しておいたアクセストークンを渡している。今回は個人用なのでデフォルトブランチに直で push しているが、Pull Request を作成することもできる。&lt;/p&gt;
&lt;p&gt;これで、リポジトリがいくら増えても大丈夫そう。&lt;/p&gt;
</content:encoded></item><item><title>mermaid-rs-renderer でブラウザ依存なしで Mermaid をレンダリングする</title><link>https://blog.mono0x.net/2026/02/15/mermaid-rs-renderer/</link><guid isPermaLink="true">https://blog.mono0x.net/2026/02/15/mermaid-rs-renderer/</guid><pubDate>Sat, 14 Feb 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/1jehuang/mermaid-rs-renderer&quot;&gt;mermaid-rs-renderder&lt;/a&gt; (mmdr) という Rust で書かれたスタンドアロンの Mermaid のレンダラーを使うことで、ブラウザ依存なしで Mermaid の図をレンダリングできる。Mermaid の公式の実装では Puppeteer や Playwright が必須で、静的サイトの構築やサーバサイドレンダリングにおいては実行環境の構築に躓きやすくなるため、ブラウザ依存がなくなるのはうれしい。&lt;/p&gt;
&lt;p&gt;mmdr は Rust で書かれているので、Rust のアプリケーションであればライブラリとして組み込むこともできるが、そうでない場合は CLI のコマンドで使うことになる。&lt;/p&gt;
&lt;p&gt;この&lt;a href=&quot;/2024/02/11/astro-mermaid/&quot;&gt;ブログに組み込んでいる Mermaid のレンダリング機能&lt;/a&gt;の場合は、以下のようなコードで mermaid-cli から簡単に置き換えられた。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as child_process from &quot;node:child_process&quot;

const renderSVG = async (mermaidContent: string): Promise&amp;lt;string&amp;gt; =&amp;gt; {
  return await new Promise&amp;lt;string&amp;gt;((resolve, reject) =&amp;gt; {
    const child = child_process.execFile(&quot;mmdr&quot;, [&quot;-e&quot;, &quot;svg&quot;], (error, stdout) =&amp;gt; {
      if (error != null) {
        reject(new Error(&quot;Failed to render mermaid diagram&quot;, { cause: error }))
      } else {
        resolve(stdout)
      }
    })
    const stdin = child.stdin
    if (stdin == null) {
      reject(new Error(&quot;Failed to write to mmdr process&quot;))
      return
    }
    stdin.write(mermaidContent)
    stdin.end()
  })
}

// ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;mmdr のインストールには mise を用いている。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[tools]
&quot;github:1jehuang/mermaid-rs-renderer&quot; = &quot;0.2.0&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これも結構実行環境を選ぶ要素になってしまう気はしつつ、自分の場合は GitHub Actions なのでまあいいかなと。npm にパッケージがあるとうれしい。&lt;/p&gt;
&lt;p&gt;npm にパッケージがあってブラウザ不要という意味だと、&lt;a href=&quot;https://github.com/lukilabs/beautiful-mermaid&quot;&gt;beautiful-mermaid&lt;/a&gt; がよさそうかもとも思った。ただ、こちらは文法の互換性がそこまで高くないようで、図を修正しないとエラーになってしまったので、今回は mmdr を選んだ。&lt;/p&gt;
</content:encoded></item><item><title>Astro で1つのコードベースから複数のサイトを生成する</title><link>https://blog.mono0x.net/2026/02/01/astro-multiple-websites/</link><guid isPermaLink="true">https://blog.mono0x.net/2026/02/01/astro-multiple-websites/</guid><pubDate>Sat, 31 Jan 2026 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Astro で複数のサイトを生成していると、コンテンツは異なるがコードはほとんど同じということがある。そのような状況で、コードベースを一本化してメンテナンスを楽にする方法を検討した。&lt;/p&gt;
&lt;p&gt;公式に案内されているものがあればそれを採用したかったのだが、見つけられなかったので、あまり凝ったことはせずに、シンプルにシンボリックリンクを使った方法で実現してみた。&lt;/p&gt;
&lt;h2&gt;サイトの構成&lt;/h2&gt;
&lt;p&gt;以下のようなディレクトリ構造にして、&lt;code&gt;content&lt;/code&gt; のシンボリックリンクが示す先を切り替えることで、生成するサイトを切り替える。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.
├── sites/
│   ├── site-a/ # サイトAのコンテンツ
│   │   ├── blog/
│   │   ├── public/
│   │   └── metadata.json
│   └── site-b/ # サイトBのコンテンツ
│       ├── blog/
│       ├── public/
│       └── metadata.json
├── content -&amp;gt; sites/site-a/ # シンボリックリンク
└── src/ # 共通コード
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt; にサイト切り替えコマンドを用意している。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;scripts&quot;: {
    &quot;switch:site-a&quot;: &quot;ln -sfn sites/site-a content&quot;,
    &quot;switch:site-b&quot;: &quot;ln -sfn sites/site-b content&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ビルド前にこのコマンドを実行することで、ビルド対象を切り替える。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm run switch:site-a
pnpm run build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Astro 側は、&lt;code&gt;content&lt;/code&gt; 以下を参照してビルドするように普通に設定している。サイトごとに挙動を変えたい場合は、設定ファイルも &lt;code&gt;content&lt;/code&gt; 以下に配置して読み込めばよい。&lt;/p&gt;
&lt;h2&gt;ビルド・デプロイ&lt;/h2&gt;
&lt;p&gt;GitHub Actions の matrix 機能を使い、各サイトを並列にビルド・デプロイしている。自分の場合だと Cloudflare Workers をデプロイ先にしているので、&lt;code&gt;wrangler.toml&lt;/code&gt; にサイトごとの環境を定義しておいて、デプロイ時に指定している。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;get-sites:
  runs-on: ubuntu-latest
  outputs:
    sites: ${{ steps.set-matrix.outputs.sites }}
  steps:
    - uses: actions/checkout@v6
    - id: set-matrix
      run: |
        SITES=$(ls -1 sites | jq -R -s -c &apos;split(&quot;\n&quot;)[:-1]&apos;)
        echo &quot;sites=$SITES&quot; &amp;gt;&amp;gt; &quot;$GITHUB_OUTPUT&quot;

deploy:
  needs: get-sites
  strategy:
    matrix:
      site: ${{ fromJson(needs.get-sites.outputs.sites) }}
  runs-on: ubuntu-latest
  steps:
    # ...

    - run: pnpm run &quot;switch:$SITE&quot;
      env:
        SITE: ${{ matrix.site }}

    - run: pnpm run build

    - run: wrangler deploy --env &quot;$SITE&quot;
      env:
        CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
        CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
        SITE: ${{ matrix.site }}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[env.site-a]
name = &quot;site-a&quot;

[env.site-b]
name = &quot;site-b&quot;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>jdx/mise-action で mise のバージョンを固定する</title><link>https://blog.mono0x.net/2026/01/29/jdx-mise-action/</link><guid isPermaLink="true">https://blog.mono0x.net/2026/01/29/jdx-mise-action/</guid><pubDate>Wed, 28 Jan 2026 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;GitHub Actions におけるサプライチェーン攻撃への対策として、サードパーティのアクションを利用する際に commit SHA を指定するという方法がある。これによって、commit SHA を書き換えない限りはずっと固定のバージョンが使われることになり、ワークフローの書き換えなしでいつの間にか悪意のあるコードが実行されていた、ということを防げる。&lt;/p&gt;
&lt;p&gt;mise を GitHub Actions で使用するための公式のアクションとして、&lt;a href=&quot;https://github.com/jdx/mise-action&quot;&gt;jdx/mise-action&lt;/a&gt; がある。これを使うときも commit SHA を指定しておけば安心かというと、個人的には不十分なのではないかと考えている。というのも、jdx/mise-action では、mise のバージョンを指定しなかった場合に自動的に最新の mise がダウンロードされるようになっている。つまり、最新の mise が侵害された場合には、ワークフローの書き換えなしで悪意のあるコードが実行されることになってしまう。&lt;/p&gt;
&lt;p&gt;対策としては、mise のバージョンも固定してしまえばよい。mise のバイナリは GitHub Release からダウンロードされるようになっており、mise のリポジトリでは &lt;a href=&quot;https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases&quot;&gt;Immutable Releases&lt;/a&gt; が有効なので、バージョンを固定しておけば常に同じバイナリが使われることになる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
  with:
    version: 2026.1.8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;jdx/mise-action を何度も使う場合、すべての箇所でバージョンを指定するのはかなり面倒である。変数を使うのもよいが、Composite Action を使うのがスッキリ書けてメンテナンスもしやすいのではないかと思う。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Setup mise
description: Setup mise with version pinning
runs:
  using: composite
  steps:
    - uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
      with:
        version: 2026.1.8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使う側では &lt;code&gt;uses: jdx/mise-action@...&lt;/code&gt; の代わりに以下のようにすればよい。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- uses: ./.github/actions/setup-mise
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>chezmoi でのスクリプトキャッシュをクリーンな環境で作る</title><link>https://blog.mono0x.net/2025/11/26/chezmoi-cache/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/11/26/chezmoi-cache/</guid><pubDate>Wed, 26 Nov 2025 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;過去に、chezmoi のテンプレート機能を使ってシェルの起動を高速化する方法を紹介した。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2023/04/29/optimize-rcfiles-using-chezmoi/&quot;&gt;chezmoi のテンプレート機能を使ってシェルの起動を高速化する&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基本的にはこれでうまくいくのだが、コマンドによっては問題になる場合がある。呼び出し時の環境変数の内容を考慮して初期化処理を変えるような場合である。&lt;a href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; なんかがそれに該当する。&lt;/p&gt;
&lt;p&gt;普段使っている環境で &lt;code&gt;chezmoi&lt;/code&gt; コマンドを実行すると、その時の環境変数で &lt;code&gt;chezmoi&lt;/code&gt; コマンドが実行され、そこから起動されるプロセスにも引き継がれる。それによって、期待したスクリプトのキャッシュが生成されない場合がある。&lt;/p&gt;
&lt;p&gt;これを回避するためには、呼び出し元の環境変数の影響を排除して &lt;code&gt;chezmoi&lt;/code&gt; コマンドやそこから呼び出されるコマンドを実行できればよい。そのために、環境変数をリセットして &lt;code&gt;chezmoi&lt;/code&gt; コマンドを実行するようなラッパーを作成した。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash
# Run chezmoi in a clean environment with Homebrew environment variables set up.
set -eu

exec env -i \
  HOME=&quot;$HOME&quot; \
  HOMEBREW_PREFIX=&quot;${HOMEBREW_PREFIX:-/opt/homebrew}&quot; \
  GITHUB_ACTIONS=&quot;${GITHUB_ACTIONS:-}&quot; \
/bin/bash --noprofile --norc -s &quot;$@&quot; &amp;lt;&amp;lt; &apos;EOF&apos;
eval &quot;$(/usr/libexec/path_helper -s)&quot;
eval &quot;$(&quot;$HOMEBREW_PREFIX/bin/brew&quot; shellenv)&quot;
exec chezmoi &quot;$@&quot;
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;env&lt;/code&gt; コマンドの &lt;code&gt;-i&lt;/code&gt; オプションを指定することで、継承した環境変数を無視してコマンドを実行できる。そこから起動する &lt;code&gt;bash&lt;/code&gt; コマンドにも &lt;code&gt;--noprofile --norc&lt;/code&gt; オプションを付けて設定の読み込みを抑止している。それだけだと必要な環境変数も消えてしまうので、最低限必要になる &lt;code&gt;HOME&lt;/code&gt; や &lt;code&gt;/usr/libexec/path_helper&lt;/code&gt; の結果 (macOS の場合) や、Homebrew でインストールしたコマンドを使うための &lt;code&gt;brew shellenv&lt;/code&gt; の結果も設定したうえで &lt;code&gt;chezmoi&lt;/code&gt; コマンドを実行している。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/QuarticCat/zsh-smartcache&quot;&gt;zsh-smartcache&lt;/a&gt; とかのほうが本来の実行タイミングでキャッシュを作るので問題が少ないのかなと思いつつ、一旦これでもできてますということで。&lt;code&gt;chezmoi&lt;/code&gt; コマンドの中でやるのにふさわしい複雑な操作がでてきたら価値が出てくるかもしれない。&lt;/p&gt;
</content:encoded></item><item><title>Deno と Bun から Node に戻ってきた</title><link>https://blog.mono0x.net/2025/10/18/node-pnpm/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/10/18/node-pnpm/</guid><pubDate>Sat, 18 Oct 2025 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;個人的なコードで、ちょっとしたスクリプトでは Deno、本格的な開発では Bun というように、JS のランタイムを使い分けていたのだが、Node に戻ってきた。&lt;/p&gt;
&lt;p&gt;きっかけは、Deno だと Dependabot が使えず、Renovate を使うのにもかなり労力が必要だったこと。cooldown したかったので結構残念に感じた。&lt;/p&gt;
&lt;p&gt;もともと TypeScript を手軽に使いたくて Deno を触り始めたのだが、Node でも TypeScript を実行できるようになったことで、実は Deno である理由ってそんなにないんじゃないかと思った。Deno は周辺ツールがオールインワンなところもよかったりするのだが、Biome が進化してきたことで、こちらも Deno ならではの魅力としては映らなくなってきた。&lt;/p&gt;
&lt;p&gt;加えて、pnpm に &lt;code&gt;minimumReleaseAge&lt;/code&gt; が入ったことで、久しぶりに pnpm を触ってようと思い、Bun のコードも含めて全部 Node に揃えてしまおうと思った。&lt;/p&gt;
&lt;p&gt;# 後で気付いたけど Bun にも &lt;code&gt;minimumReleaseAge&lt;/code&gt; 来てたので、Bun に関してはあんまり移行する理由なかったかも。&lt;/p&gt;
&lt;p&gt;Bun のコードは、&lt;code&gt;package.json&lt;/code&gt; に書かずに使われていた依存があったので pnpm 化で多少の修正が必要になったものの、Astro のアプリケーション部分は何も変更せずに Node に持ってこれた。すごい。&lt;/p&gt;
&lt;p&gt;一方、Deno に関しては Deno 固有の API を使っている部分を書き換える必要があった。現在は Deno でも Node 互換の API が整ってきているのだが、昔からのコードだと厳しい。この作業を経て、今となっては、Deno で書くにしても Node の API を使っておいたほうがよいように思った。&lt;/p&gt;
&lt;p&gt;# シェルスクリプトの代替的な利用を便利にしてくれる &lt;a href=&quot;https://github.com/dsherret/dax&quot;&gt;dax&lt;/a&gt; は、&lt;code&gt;dax-sh&lt;/code&gt; というパッケージで Node でも使えるようになっていた。素晴らしい。&lt;/p&gt;
&lt;p&gt;依然として Deno ならではの点として残るのはパーミッションだろうか。これは普通に使い所はかなりある気がしている。Node も追従しているものの、Deno ほどのものではなさそう。ただ、実行時にならないと必要なパーミッションが分からないのがちょっとしたスクリプトには面倒で、結局 &lt;code&gt;-A&lt;/code&gt; みたいなことになってしまう。あらかじめ使うパーミッションを宣言しておいて、それに応じて API を使えるかどうか実行前にわかるみたいなモデルがほしい。&lt;/p&gt;
&lt;p&gt;Bun は独自の API を増やしまくっているが、先述の Deno API の話と同様で、個人的には魅力だとは感じない。Node との互換性は高いので Node とどちらでもいいと言えばいいのだが、だからこそ今回みたいなちょっとした違いでも切り替えて使えるということで、移行して試してみることにした。&lt;/p&gt;
&lt;p&gt;pnpm の面倒なところとして、pnpm を使うための準備が面倒というのがあった。手元にしても CI にしても Node を入れるだけでは使えず、追加で一手間が生じるし、その一手間も分かりやすいとは思えない。これについては、今だと &lt;a href=&quot;https://mise.jdx.dev/&quot;&gt;mise&lt;/a&gt; を使うことで楽になると気付いた。mise は言語処理系だけでなくパッケージも管理できるので、pnpm を mise でまとめて管理しておけば、&lt;code&gt;mise install&lt;/code&gt; 一発で pnpm も含めて Node 環境を構築できる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[tools]
node = &quot;24.10.0&quot;
&quot;npm:pnpm&quot; = &quot;10.18.3&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;まだとりあえずの切り替えが終わったところだが、各ランタイムが競争して進化していることが分かってよかった。今後、移行したことで何かしら困っても、また別のランタイムにすぐ移行できると分かったので、かなり身軽な気分になった。&lt;/p&gt;
</content:encoded></item><item><title>Apple のリマインダーに外部システムから項目を追加する</title><link>https://blog.mono0x.net/2025/09/18/apple-reminder/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/09/18/apple-reminder/</guid><pubDate>Thu, 18 Sep 2025 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Apple のリマインダーはアプリとしては必要十分な機能があって便利なのだが、外部システムから項目を追加しようとすると、簡単な方法が用意されていなくてやりづらい。他社のサービスだと、専用のアドレスにメールを送ったり、API を呼び出したりすることで追加できるので、似たような感覚で外部システムと連携できるとうれしい。&lt;/p&gt;
&lt;p&gt;調べてみた感じでは、いくつか方法がありそうだった。&lt;/p&gt;
&lt;h2&gt;IFTTT でメールをトリガーとしてリマインダーに追加する&lt;/h2&gt;
&lt;p&gt;iOS デバイスに IFTTT アプリをインストールしていれば、リマインダーへの追加をアクションとして実行できる。IFTTT は無料だとかなり制限が多めではあるものの、メールをトリガーとしてリマインダーに追加するだけに使うのであれば無料の範囲で使える。メールではなく Webhook を使いたいという場合は有料になる。&lt;/p&gt;
&lt;h2&gt;Microsoft アカウントをリマインダーアカウントに追加する&lt;/h2&gt;
&lt;p&gt;iOS や macOS では Microsoft アカウントでログインしてリマインダーを使うことができる。この状態では Microsoft To Do のデータを読み書きすることになるので、Microsoft To Do 側でデータを流し込めればリマインダーアプリ上でも確認できる。&lt;/p&gt;
&lt;p&gt;Activepieces というノーコードの自動化ツールが Microsoft To Do への項目の追加に対応しているので、Activepieces 側で Webhook などをトリガーとしたフローを構築し、項目を追加すればよい。&lt;/p&gt;
&lt;p&gt;これを Apple のリマインダーに項目を追加したと呼ぶかは微妙だが、アプリ上で一元管理できるという意味では使い勝手に大きな問題はない。&lt;/p&gt;
&lt;h2&gt;メール受信をトリガーとしてオートメーションを実行する&lt;/h2&gt;
&lt;p&gt;iOS のショートカットでは、特定のアドレスや件名のメールを受信したことをトリガーとしてショートカットを実行するオートメーションを設定できる。ショートカットが使えればリマインダーの操作もできる。&lt;/p&gt;
&lt;p&gt;ただ、メールも受信してしまうので邪魔という問題がある。専用のアカウントを用意して通知が飛ばないようにしておくなどの工夫が必要になりそう (他の方法を採用したので深追いはしなかった)。&lt;/p&gt;
&lt;h2&gt;Pushcut で Webhook をトリガーとしてショートカットを実行する (有料、未確認)&lt;/h2&gt;
&lt;p&gt;有料サブスクリプションの契約が必要になるので試していないが、Pushcut というアプリで Webhook をトリガーに任意のショートカットを実行できるようだった。ショートカットが使えればリマインダーの操作も理屈的にはできるはず。&lt;/p&gt;
&lt;h2&gt;個人的な選定とその理由&lt;/h2&gt;
&lt;p&gt;個人的には上から順に気に入っていて、現在は IFTTT を使っている。Microsoft アカウントで連携する場合もそこまで問題はないのだが、リマインダーが複数アカウントを使っているときの UI になって若干煩雑になるのと、Microsoft アカウントだとリストのアイコンを設定できないのが気になって、結局 IFTTT に流れた。&lt;/p&gt;
&lt;p&gt;メール受信のオートメーションは、ゴミメールが溜まる問題を解決できそうであればありな気がする。IFTTT で困ったら考える。&lt;/p&gt;
&lt;p&gt;Pushcut は他の方法で無料でどうにかなっている限りは個人的には興味が沸かない。&lt;/p&gt;
&lt;h2&gt;これで何をしているのか&lt;/h2&gt;
&lt;p&gt;サブのスマホやタブレットのバッテリー残量が低下した際に、リマインダーにそのデバイスを充電するという項目を追加する使い方をしている。これだけ聞くと、バッテリー残量が低下したらリマインダーに追加するオートメーションを組めばよいと思うかもしれないが (というか実際そう)、自分の場合は Android のスマホや別の Apple アカウントでログインした iPhone についても連携したいため、このほうな方法を採っている。&lt;/p&gt;
</content:encoded></item><item><title>GitHub Actions の Ubuntu 24.04 で Puppeteer が動かない問題</title><link>https://blog.mono0x.net/2025/09/16/puppeteer-on-github-actions-ubuntu-2404/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/09/16/puppeteer-on-github-actions-ubuntu-2404/</guid><pubDate>Tue, 16 Sep 2025 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;表題の通り、Ubuntu 24.04 (記事公開時点での latest) を使っていると、Puppeteer が動かない問題がある。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/puppeteer/puppeteer/issues/12818&quot;&gt;[Bug]: &quot;No usable sandbox!&quot; with user namespace cloning enabled · Issue #12818 · puppeteer/puppeteer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;これは、Ubuntu の AppArmor が Puppeteer がダウンロードした Chrome のバイナリを実行させないようにしているために起きている。Ubuntu 22.04 (&lt;code&gt;ubuntu-22.04&lt;/code&gt;) を使うことでも回避できるのだが、Ubuntu 24.04 を使いたい場合には、SUID sandbox という古い sandbox で実行することで回避できるようである。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pptr.dev/troubleshooting#issues-with-apparmor-on-ubuntu&quot;&gt;Troubleshooting | Puppeteer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;GitHub Actions の workflow に落とし込むと以下のようになる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jobs:
  task:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5

      - run: |
          npm ci

      # https://pptr.dev/troubleshooting#issues-with-apparmor-on-ubuntu
      - name: Prepare puppeteeer SUID sandbox
        id: sandbox
        run: |
          SANDBOX=$(find ~/.cache/puppeteer -type f -name chrome_sandbox | head -n1)
          sudo chown root:root &quot;$SANDBOX&quot;
          sudo chmod 4755 &quot;$SANDBOX&quot;
          echo &quot;sandbox_path=$SANDBOX&quot; &amp;gt;&amp;gt; &quot;$GITHUB_OUTPUT&quot;

      - run: |
          npm run task
        env:
          CHROME_DEVEL_SANDBOX: ${{ steps.sandbox.outputs.sandbox_path }}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Dependabot の cooldown 機能でサプライチェーン攻撃のリスクを緩和する</title><link>https://blog.mono0x.net/2025/09/07/dependabot-cooldown/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/09/07/dependabot-cooldown/</guid><pubDate>Sun, 07 Sep 2025 05:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今まで Bun なら &lt;code&gt;bun update&lt;/code&gt; などで手動で依存ライブラリを更新していたのだが、Dependabot を使うようにした。わざわざ手動でやるべきことでもないよねという話もあるものの、一番大きいのはサプライチェーン攻撃対策のため。&lt;/p&gt;
&lt;p&gt;適当にライブラリを更新していると攻撃コードの入ったバージョンを踏んでしまうリスクが現実的に高まっているのを感じていたので、Renovate のようにリリースしてから一定期間経過した更新だけを取り込むようにしたいと考えていた。ただ、Renovate は導入するのに手間がかかりそうで、なかなか導入するに至っていなかった。&lt;/p&gt;
&lt;p&gt;それが最近になって Dependabot で &lt;a href=&quot;https://github.blog/changelog/2025-07-01-dependabot-supports-configuration-of-a-minimum-package-age/&quot;&gt;cooldown としてサポートされた&lt;/a&gt;ことで、簡単に導入できるようになった。これによってついに導入する気になった。&lt;/p&gt;
&lt;p&gt;ただ、手間という面だと、自動化すると便利というよりも大量の Pull Request が作られて、むしろ厄介なんじゃないかという懸念もあった。あまりメンテナンスされていないリポジトリにおいて、Dependabot が作成した Pull Request が大量に並んでいるのはよく見る光景である。&lt;/p&gt;
&lt;p&gt;# Dependabot CLI を使えばローカルで cooldown ありで更新できるのかもしれないが、使っている人がそこまで多くなさそうなので試していない。&lt;/p&gt;
&lt;p&gt;これについては、&lt;a href=&quot;https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/optimizing-pr-creation-version-updates#prioritizing-meaningful-updates&quot;&gt;Groups&lt;/a&gt; という機能を使うことである程度解決できそうだった。条件に当てはまる更新をひとつの Pull Request にまとめてくれる機能で、ひとまずマイナーな更新は全部まとめてしまうようにした。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: 2
updates:
  - package-ecosystem: &quot;bun&quot; # See documentation for possible values
    directory: &quot;/&quot; # Location of package manifests
    schedule:
      interval: &quot;weekly&quot;
    cooldown:
      default-days: 14
    groups:
      dependencies:
        patterns: [&quot;*&quot;]
        update-types: [&quot;minor&quot;, &quot;patch&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;# 無いと思うけど、みんな cooldown するのが当たり前になったらどんどん cooldown 期間を延長しないと意味ないねってなったりするんだろうか……？&lt;/p&gt;
</content:encoded></item><item><title>M5Stack CoreS3 SE と GR-M02U で位置情報を取得する</title><link>https://blog.mono0x.net/2025/08/27/m5stack-cores3-pl2303-tinygpsplus/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/08/27/m5stack-cores3-pl2303-tinygpsplus/</guid><pubDate>Tue, 26 Aug 2025 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/2025/08/24/m5stack-cores3-se-with-usb-module-v12-and-gr-m02-u/&quot;&gt;前回&lt;/a&gt;作った環境で、実際に位置情報を取得するコードを動かしてみた。前回も使用した &lt;a href=&quot;https://github.com/felis/USB_Host_Shield_2.0&quot;&gt;felis/USB_Host_Shield_2.0&lt;/a&gt; に含まれている &lt;a href=&quot;https://github.com/felis/USB_Host_Shield_2.0/blob/master/examples/pl2303/pl2303_tinygps/pl2303_tinygps.ino&quot;&gt;pl2303_tinygps&lt;/a&gt; を動かせばいけるかと思いきや、それでは位置情報を得られずトラブルシューティングが必要だった。&lt;/p&gt;
&lt;p&gt;試行錯誤の結果を PlatformIO で動くようにしたのがこちら。&lt;/p&gt;
&lt;p&gt;https://github.com/mono0x/pl2303_tinygpsplus&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pio run -t upload
pio device monitor
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;--- Terminal on /dev/cu.usbmodem1101 | 9600 8-N-1
--- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H

Acquired Data
-------------
Location: xx.xxxxxx,xxx.xxxxxx
Date/Time: 2025-08-26 13:14:43.40
Altitude: x.xxm
Course: 0.00deg
Speed: 0.07km/h
Satellites: 9
HDOP: 1.41
Chars: 10236  Sentences: 100  Failed checksum: 0
-------------
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;具体的には、TinyGPS だと動かなくて、TinyGPS を改造するか TinyGPS++ に移行する必要があった。というのも、TinyGPS は NMEA データのうちの Talker ID が GP であるもののみサポートしていたのに対して、GR-M02U では GN で出力していたため。GN は複数衛星混在の情報であることを示しているようで、GR-M02U は衛星ごとに Talker ID を使い分けることはないようだ。TinyGPS++ では、Talker ID のサポートが拡大されていて問題なかった。&lt;/p&gt;
</content:encoded></item><item><title>M5Stack CoreS3 SE と USB Module with MAX3421E v1.2 で GR-M02U を扱う</title><link>https://blog.mono0x.net/2025/08/24/m5stack-cores3-se-with-usb-module-v12-and-gr-m02-u/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/08/24/m5stack-cores3-se-with-usb-module-v12-and-gr-m02-u/</guid><pubDate>Sun, 24 Aug 2025 06:00:00 GMT</pubDate><content:encoded>&lt;p&gt;もともと Android スマートフォンを GPS ロガーとして使っていたのだが、用途に対してオーバーキルすぎるとは感じていたので、他の選択肢を検討している。Raspberry Pi 系のデバイスは、試してみて動くものを作れそうな感じはしたものの、ディスプレイやボタンなどの操作系がないために、出先でのコントロールが難しそう。&lt;/p&gt;
&lt;p&gt;そこで、そのあたりをカバーできる可能性を探っていたところ、M5Stack にたどり着いた。M5Stack は、ESP32 という無線モジュール付きのマイコンに、ディスプレイ、ボタンなど UI 周りを揃えたパッケージングがされているため、ハードウェアに明るくない自分でもどうにかなるのではないかと思った。&lt;/p&gt;
&lt;p&gt;ところが、実際やってみると、インターネット上に情報が少なく、ChatGPT などもまったく役に立たないために相当に苦戦した。まだ完成どころかやっとこさ GPS の信号を受信できたという段階だが、少しでも参考になればということでメモを残しておく。&lt;/p&gt;
&lt;p&gt;本体は &lt;a href=&quot;https://www.switch-science.com/products/9690&quot;&gt;M5Stack CoreS3 SE&lt;/a&gt; で、GPS レシーバーには GR-M02U を使った。GR-M02U を接続するために、&lt;a href=&quot;https://www.switch-science.com/products/9566&quot;&gt;USB Module with MAX3421E v1.2&lt;/a&gt; を使った。&lt;/p&gt;
&lt;p&gt;# USB の GPS レシーバーを使おうとしていることで難易度が跳ね上がっていると思うので、何もないところから始めるなら &lt;a href=&quot;https://www.switch-science.com/products/10191&quot;&gt;GNSS モジュール&lt;/a&gt;を使ったほうがよさそう。こちらは解説している情報もいくつかあるっぽい。&lt;/p&gt;
&lt;p&gt;もともと、M5Stack CoreS3 SE に USB OTG、CDC 機能があるという記述を見て手に取ったのだが、調べてみても使い方がさっぱりわからなかったため、USB Module を使うことにした。開発やデバッグの際に本体側の USB を PC と繋ぎっぱなしにできるので、こちらの構成のほうが現実的だと思った。&lt;/p&gt;
&lt;p&gt;純正の USB Module を使ったとしても、引き続き使い方がわからず途方にくれることになった。DIP スイッチで構成変更できるので、CoreS3 にも対応できるという記述はあるものの、DIP スイッチをどう設定すればいいかという説明はどこにも見当たらない。&lt;/p&gt;
&lt;p&gt;困っているのは自分だけではなさそう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/m5stack/M5-Max3421E-USBShield/issues/1&quot;&gt;DIP Switch · Issue #1 · m5stack/M5-Max3421E-USBShield&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://community.m5stack.com/topic/6847/cores3-with-usb-module-v1-2&quot;&gt;CoreS3 with USB Module v1.2 | M5Stack Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;公式の &lt;a href=&quot;https://github.com/m5stack/M5-Max3421E-USBShield&quot;&gt;m5stack/M5-Max3421E-USBShield&lt;/a&gt; は使い方がわからず。いろいろ調べていく中で、&lt;a href=&quot;https://github.com/felis/USB_Host_Shield_2.0&quot;&gt;felis/USB_Host_Shield_2.0&lt;/a&gt; という M5Stack に限らない USB Host のライブラリを見ていたところ、つい最近 (2025年8月) CoreS3 対応のコードを入れてくださった方を発見。最新バージョンで試したら動作した。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/felis/USB_Host_Shield_2.0/pull/843&quot;&gt;Added the pin definitions to support the m5 usb model on m5stack s3 by zer044 · Pull Request #843 · felis/USB_Host_Shield_2.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DIP スイッチは Pull Request に書かれているように SS と INT どちらも CH2 を ON に設定した。&lt;/p&gt;
&lt;p&gt;https://i.imgur.com/BvlmodC.jpeg&lt;/p&gt;
&lt;p&gt;(CH2 のみ両方左側で、他は右側)&lt;/p&gt;
&lt;p&gt;この状態で、README に沿ってライブラリをインストールした上で、&lt;a href=&quot;https://github.com/felis/USB_Host_Shield_2.0/tree/master/examples/USB_desc&quot;&gt;examples/USB_desc&lt;/a&gt; や &lt;a href=&quot;https://github.com/felis/USB_Host_Shield_2.0/tree/master/examples/pl2303/pl2303_gps&quot;&gt;examples/pl2303/pl2303_gps&lt;/a&gt; のコードを Arduino IDE 上で動作させることができた。いろいろハマりポイントもあった。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;M5Stack 本体と USB Module のピンでの接続がギリギリのようだったので、しっかりはまるようにした
&lt;ul&gt;
&lt;li&gt;固定方法がわかっていないのでマスキングテープで抜けないようにしているが、どうしたらいいんだろう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GR-M02U を直接 USB Module に繋いでもインジケータ LED が点灯しなかったので、USB 電源補助ケーブルで給電した (手元にあった &lt;a href=&quot;https://www.iodata.jp/product/storage/dvd/upac-ut07m/&quot;&gt;UPAC-UT07M&lt;/a&gt; を使用)&lt;/li&gt;
&lt;li&gt;pl2303_gps に関しては &lt;code&gt;lc.dwDTERate&lt;/code&gt; を GR-M02U 側の Baud Rate に合わせて (自分の場合は &lt;code&gt;115200&lt;/code&gt;) 更新した
&lt;ul&gt;
&lt;li&gt;間違っていても接続さえできていれば文字化けしまくったデータが Serial に流れてくるので、そもそも動いていないこととの区別はできる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これでやっとこさやりたいことを実現するアプリケーションに取り掛かれる。険しい道のりだった。&lt;/p&gt;
&lt;p&gt;&amp;lt;del&amp;gt;でも Android スマホの便利さを痛感したのでもうそれでもいいかも……。&amp;lt;/del&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Astro で使っている BudouX のバージョンを 0.7.0 に上げた</title><link>https://blog.mono0x.net/2025/06/28/astro-budoux-0-7-0/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/06/28/astro-budoux-0-7-0/</guid><pubDate>Fri, 27 Jun 2025 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;見出し部分の折り返しを自然にするために使っている &lt;a href=&quot;https://github.com/google/budoux&quot;&gt;BudouX&lt;/a&gt; のバージョンを最新の 0.7.0 に上げた。0.6.4 から 0.7.0 に上がる際に JSDOM を &lt;a href=&quot;https://github.com/WebReflection/linkedom&quot;&gt;linkedom&lt;/a&gt; に置き換える破壊的変更が入ったことで、&lt;a href=&quot;https://h6i.org/blog/posts/astro-budoux-linebreak&quot;&gt;参考にしていたコード&lt;/a&gt;が使えなくなって困っていたのだが、Claude Code に調べてもらって解決した。&lt;/p&gt;
&lt;p&gt;結論としては、&lt;code&gt;wbr&lt;/code&gt; 要素を作るために &lt;code&gt;linkedom&lt;/code&gt; を使うようにすればよかった。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install linkedom
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import { HTMLProcessingParser, jaModel } from &quot;budoux&quot;
import { parseHTML } from &quot;linkedom&quot;

const { document } = parseHTML(&quot;&quot;)
const wbr = document.createElement(&quot;wbr&quot;)

const parser = new HTMLProcessingParser(jaModel, {
  className: &quot;budoux&quot;,
  separator: wbr,
})

export function budouxProcess(html: string) {
  return parser.translateHTMLString(html)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;他の部分は参考にしたページのものから変更なしで動いてそう。やはり、静的サイトジェネレータを使っているからにはこういった処理は静的に解決できるのが気持ちいい。&lt;/p&gt;
</content:encoded></item><item><title>Astro のブログで Embedding を計算して類似の記事を表示するようにした</title><link>https://blog.mono0x.net/2025/06/27/astro-embedding-based-similar-entries/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/06/27/astro-embedding-based-similar-entries/</guid><pubDate>Fri, 27 Jun 2025 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/2023/07/12/astro-backlinks/&quot;&gt;以前バックリンクを解析した&lt;/a&gt;ときの延長で、各記事の Embedding を計算して類似の記事を探索し、記事の末尾に表示するようにした。&lt;/p&gt;
&lt;p&gt;Astro Integration の中で強引にファイルを読んで、Remark プラグインとして類似記事の識別子を入れ込むという大まかな流れは同じ。各記事ごとに Embedding を計算する部分は &lt;a href=&quot;https://platform.openai.com/docs/guides/embeddings&quot;&gt;OpenAI の Embeddings API&lt;/a&gt; (と記事が長すぎるときの要約に &lt;a href=&quot;https://platform.openai.com/docs/api-reference/chat&quot;&gt;Chat completions API&lt;/a&gt; を少々) 利用し、計算した全記事の Embedding を &lt;a href=&quot;https://www.npmjs.com/package/hnswlib-node&quot;&gt;hnswlib-node&lt;/a&gt; に流し込んで Embedding が近い記事を探索している。&lt;/p&gt;
&lt;p&gt;ビルドのたびに Embedding を再計算していると遅すぎるしお金が掛かるので (と言っても全記事処理しても数円程度)、永続化しておいて記事ファイルのハッシュ値が変化したときのみ再計算するみたいな工夫も入れ込んでいる。&lt;/p&gt;
&lt;p&gt;うちのブログ程度にこんなものが必要あるかといえば無いとは思っているのだが、触ってみたかったもろもろの API やライブラリに触れるのと、Claude Code にコード生成させてみるお題として取り組んでみた感じ。Claude Code は取っ掛かりのわからなさや面倒くささをどうにかしてくれるという意味ではよかったものの、コードについてはなかなか納得いかなくて試行錯誤することも多かった。今はそれが新鮮だから楽しいなという感じ。長い目で見てどうかはさっぱりわからんね。&lt;/p&gt;
&lt;p&gt;生成させたコードの品質に完全には納得いっていないというのと、そこそこデカいというのもあってコードは掲載していない。前回と今回の記事の情報があれば結局ある程度コード生成させることはできるだろうし、それが各自のコードベースに基づいて行われるならそのほうが手っ取り早いのかなというのもある。&lt;/p&gt;
</content:encoded></item><item><title>VSCode や Cursor の Integrated Terminal 使用時 に Git のコミットメッセージエディタを切り替える</title><link>https://blog.mono0x.net/2025/05/11/commit-message-editor/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/05/11/commit-message-editor/</guid><pubDate>Sun, 11 May 2025 11:00:00 GMT</pubDate><content:encoded>&lt;p&gt;知人に &lt;code&gt;git commit&lt;/code&gt; で何のエディタを使っているか訊かれて、答えたら感心されたので紹介する。&lt;/p&gt;
&lt;p&gt;個人的には、ターミナルを使っているときはターミナルでそのまま編集したいし、Visual Studio Code (VSCode) や Cursor の Integrated Terminal を使っているときは、VSCode や Cursor 自体のエディタで編集したい。それを実現するために、状況に応じて起動させるエディタを分岐させるシェルスクリプトを書いて、エディタの代わりに指定している。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh
set -eu

if [ -n &quot;${CURSOR_TRACE_ID:-&quot;&quot;}&quot; ] &amp;amp;&amp;amp; [ -x &quot;$(command -v cursor)&quot; ]
then
  exec cursor --wait &quot;$@&quot;
elif [ &quot;${TERM_PROGRAM:-&quot;&quot;}&quot; = &apos;vscode&apos; ] &amp;amp;&amp;amp; [ -x &quot;$(command -v code)&quot; ]
then
  exec code --wait &quot;$@&quot;
elif [ -x &quot;$(command -v nvim)&quot; ]
then
  exec nvim &quot;$@&quot;
else
  exec vim &quot;$@&quot;
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;[core]
  editor = /path/to/commit-message-editor
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Integrated Terminal 内にいるかどうかは、環境変数を見れば判断できるらしい (当時調べて書いたはずだが忘れた)。また、コミットメッセージの編集に使う上では、&lt;code&gt;code&lt;/code&gt; や &lt;code&gt;cursor&lt;/code&gt; に &lt;code&gt;--wait&lt;/code&gt; オプションを付けておけば編集が終わるまで待機するような挙動にできる。気が利いてる。&lt;/p&gt;
</content:encoded></item><item><title>ブログのデプロイ先を Cloudflare Workers に切り替えた</title><link>https://blog.mono0x.net/2025/05/02/cloudflare-workers/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/05/02/cloudflare-workers/</guid><pubDate>Fri, 02 May 2025 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;もともと Cloudflare Pages を使っていたのだけど、今後は Cloudflare Workers がよいそうなので乗り換えてみた。といっても、Astro で生成している静的な Web サイトだし、Functions とかを使う必要性もないので特に何か恩恵があったりはしない。&lt;/p&gt;
&lt;p&gt;そして、すんなり移行できたかというとそういうわけではなくて、Pages ではビルド時に Puppeteer を使えていたのが Workers では使えなくなってしまった。Bun 1.2 のサポートでも &lt;code&gt;bun.lock&lt;/code&gt; を認識してくれない問題があったし (こちらは軽微)、今後も Cloudflare のビルド環境で悩まされるリスクを感じて、GitHub Actions 上でビルドしたものをデプロイするだけにした。&lt;/p&gt;
&lt;p&gt;こちらは特に困らなかったが、&lt;a href=&quot;https://github.com/cloudflare/wrangler-action&quot;&gt;公式の wrangler-action&lt;/a&gt; を使ったとしても. deprecated な wrangler が入る (？) っぽく、結局自前で npm の &lt;code&gt;wrangler&lt;/code&gt; を使うことになった。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: deploy
on:
  push:
    branches:
      - main
jobs:
  deploy:
    # https://github.com/puppeteer/puppeteer/issues/12818
    runs-on: ubuntu-22.04
    if: ${{ github.event.workflow_run.conclusion == &apos;success&apos; }}
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - uses: jdx/mise-action@5083fe46898c414b2475087cc79da59e7da859e8 # v2.1.11
      - run: |
          bun install --frozen-lockfile
      - run: |
          bunx puppeteer browsers install chrome
      - run: |
          bun run build # astro build
      - run: |
          bun run deploy # wrangler deploy
        env:
          CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;とはいえ無料で使えて設定 UI はわかりやすくパフォーマンスも良好と魅力の多いサービスなので、これからも使っていきたい。&lt;/p&gt;
</content:encoded></item><item><title>Everything の快適なファイル検索を Mac で実現した Cling</title><link>https://blog.mono0x.net/2025/04/13/cling/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/04/13/cling/</guid><pubDate>Sun, 13 Apr 2025 04:00:00 GMT</pubDate><content:encoded>&lt;p&gt;クロスプラットフォーム対応のソフトウェアが増加し、特定の OS 専用のソフトウェアを使う機会が減っている。そんな中でも、&lt;a href=&quot;https://www.voidtools.com/&quot;&gt;Everything&lt;/a&gt; は個人的に使っていた数少ない Windows 専用のツールのひとつ。&lt;/p&gt;
&lt;p&gt;OS にも標準のファイル検索機能はあるが、それとは比べ物にならないくらい速く、ファイルパスだけに着目したシンプルかつコアユーザーのための検索であるというのが素晴らしい。UI もキーボード操作しやすいのがうれしい。&lt;/p&gt;
&lt;p&gt;これがあるから Windows から離れられない、とまでは言わないまでも Mac に乗り換えたらそれなりに不便になると覚悟していたのだが、2025年になって &lt;a href=&quot;https://lowtechguys.com/cling/#:~:text=your%20search%20results.-,Everything%20for%20Mac,-Cling%20strives%20to&quot;&gt;Everything for Mac&lt;/a&gt; を謳うソフトウェアが出てきた。それが &lt;a href=&quot;https://lowtechguys.com/cling/&quot;&gt;Cling&lt;/a&gt; である。&lt;/p&gt;
&lt;p&gt;こちらもシンプルで美しい UI で、キーボード操作しやすい。検索も普通に速くて快適。&lt;/p&gt;
&lt;p&gt;正直なところ、まだちょっと触ってみた程度なのだが、日本語圏であまり知られていないようだったし、ChatGPT で Deep Research を掛けたときにも漏れてしまっていたので、少しでも知られて盛り上がるといいなと思い紹介した。Everything が好きな Mac ユーザーにはぜひ使ってみてほしい。&lt;/p&gt;
&lt;p&gt;気になった点としては、外付けのディスクに関してはリアルタイムでインデックスを最新に保ってくれるわけではないっぽい。わざわざこういう仕様にしているのだから対応が難しいのかもしれないけど、なんとかなったらうれしい。&lt;/p&gt;
</content:encoded></item><item><title>Astro のブログに Expressive Code を導入してリッチなコードブロックを実現する</title><link>https://blog.mono0x.net/2025/02/08/astro-expressive-code/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/02/08/astro-expressive-code/</guid><pubDate>Sat, 08 Feb 2025 03:00:00 GMT</pubDate><content:encoded>&lt;p&gt;もともと Shiki でシンタックスハイライトしつつ&lt;a href=&quot;/2023/07/10/astro-syntax-highlight-with-title/&quot;&gt;自前で表示を調整していた&lt;/a&gt;のだが、いろいろやりたいことがあって、自前で対応するよりは既存のもので済ませたいなと思うようになったので切り替えた。&lt;/p&gt;
&lt;p&gt;具体的には、クリップボードにコピーするボタンを配置したかったのと、幅が収まり切らない場合にスクロールではなく折り返し表示したかった。折り返し表示は単純なものならブラウザがやってくれるのだが、ソースコードはインデントで階層を表現しているので、それだと不十分。&lt;/p&gt;
&lt;p&gt;Expressive Code には、折り返し時に元の行のインデントを維持してくれる &lt;code&gt;preserveIndent&lt;/code&gt; というオプションがあったので、インデントしていても比較的わかりやすく表示できる。追加で行番号も出してあげると折り返ししていることが明確になるのだろうが、限られた幅を行番号に取られてしまうと折り返される頻度が上がって本末転倒な気がしたので、インデントだけで表現するところに落ち着いている。&lt;/p&gt;
&lt;p&gt;導入については、&lt;a href=&quot;https://expressive-code.com/installation/&quot;&gt;公式のドキュメント&lt;/a&gt;にもほとんど何も書いてないくらいに簡単。&lt;code&gt;astro add astro-expressive-code&lt;/code&gt; がだいたいやってくれる。&lt;/p&gt;
&lt;p&gt;ただ、MDX Integration を使っている場合だと、&lt;code&gt;integrations&lt;/code&gt; の並び順を調整して &lt;code&gt;mdx&lt;/code&gt; よりも &lt;code&gt;expressiveCode&lt;/code&gt; が手前になるようにする必要があった。これはビルド時に丁寧なエラーメッセージで教えてくれたので困るほどのことではなかった。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import mdx from &quot;@astrojs/mdx&quot;
import { defineConfig } from &quot;astro/config&quot;
import expressiveCode from &quot;astro-expressive-code&quot;

export default defineConfig({
  integrations: [
    expressiveCode({
      defaultProps: {
        wrap: true,
        preserveIndent: true,
      },
    }),
    mdx(),
    // ...
  ],
  // ...
})
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>ブログの更新を Activepieces を使って Bluesky に通知するようにした</title><link>https://blog.mono0x.net/2025/02/02/activepieces-bluesky/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/02/02/activepieces-bluesky/</guid><pubDate>Sun, 02 Feb 2025 07:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Bluesky をメインで使っている人もいくらかいるようなので、試しにやってみた。&lt;/p&gt;
&lt;p&gt;# 自分の場合は Twitter (X) の使用頻度が下がったことで、代わりに Threads や Bluesky を使うわけでもなく、Obsidian でプライベートなメモに書くようになった。思ったことをストレートに書けるし検索性も高いのでよかった。&lt;/p&gt;
&lt;p&gt;Activepieces は、ノーコードでサービス間連携などのちょっとした自動化ができるツール。IFTTT みたいな感じだけど、ノードを複数つないでよりリッチなことができるし、無料でも結構使える。オープンソース版があるので (個人的には面倒なのでやりたくないものの) セルフホスティングもできる。もともと RSS の新着をトリガーに Twitter に投稿していて、RSS の新着をトリガーにする部分の状態管理を任せられるのがうれしかったので今回も採用した。&lt;/p&gt;
&lt;p&gt;# なので、ノーコードがうれしいというよりは状態管理やサーバ管理を任せられるのがうれしくて使っている。Activepieces のグラフィカルなエディタは結構使いやすいとは思いつつ、YAML とかで書けるならそれに越したことはないという気持ち。&lt;/p&gt;
&lt;p&gt;出力部分に関しては、Twitter への投稿は Activepieces の標準機能でいけたものの、Bluesky との連携はサポートされていないので、自前で API を叩く必要があった。&lt;/p&gt;
&lt;p&gt;といっても、&lt;code&gt;com.atproto.server.createSession&lt;/code&gt; で認証して、返ってきた JWT で &lt;code&gt;com.atproto.repo.createRecord&lt;/code&gt; を呼ぶだけなので簡単。投稿時に時刻を渡してあげる必要があったので、そのためのステップも挟んでいる。ハッシュ値や HMAC の計算とかもできるので、もうちょっと使うのが難しい API でも対応できそう。&lt;/p&gt;
&lt;p&gt;参考までにフローをエクスポートしたものも貼っておく。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;created&quot;: &quot;1738479770166&quot;,
  &quot;updated&quot;: &quot;1738479770166&quot;,
  &quot;name&quot;: &quot;ブログの更新を Bluesky に通知&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;tags&quot;: [],
  &quot;pieces&quot;: [
    &quot;@activepieces/piece-rss&quot;,
    &quot;@activepieces/piece-http&quot;,
    &quot;@activepieces/piece-date-helper&quot;
  ],
  &quot;template&quot;: {
    &quot;displayName&quot;: &quot;ブログの更新を Bluesky に通知&quot;,
    &quot;trigger&quot;: {
      &quot;name&quot;: &quot;trigger&quot;,
      &quot;valid&quot;: true,
      &quot;displayName&quot;: &quot;New Item In Feed&quot;,
      &quot;type&quot;: &quot;PIECE_TRIGGER&quot;,
      &quot;settings&quot;: {
        &quot;pieceName&quot;: &quot;@activepieces/piece-rss&quot;,
        &quot;pieceVersion&quot;: &quot;0.3.7&quot;,
        &quot;pieceType&quot;: &quot;OFFICIAL&quot;,
        &quot;packageType&quot;: &quot;REGISTRY&quot;,
        &quot;input&quot;: {
          &quot;rss_feed_url&quot;: &quot;&amp;lt;RSS_URL&amp;gt;&quot;
        },
        &quot;inputUiInfo&quot;: {
          &quot;customizedInputs&quot;: {}
        },
        &quot;triggerName&quot;: &quot;new-item&quot;
      },
      &quot;nextAction&quot;: {
        &quot;name&quot;: &quot;step_1&quot;,
        &quot;skip&quot;: false,
        &quot;type&quot;: &quot;PIECE&quot;,
        &quot;valid&quot;: true,
        &quot;settings&quot;: {
          &quot;input&quot;: {
            &quot;url&quot;: &quot;https://bsky.social/xrpc/com.atproto.server.createSession&quot;,
            &quot;body&quot;: {
              &quot;data&quot;: {
                &quot;password&quot;: &quot;&amp;lt;APP_PASSWORD&amp;gt;&quot;,
                &quot;identifier&quot;: &quot;&amp;lt;USERNAME&amp;gt;.bsky.social&quot;
              }
            },
            &quot;method&quot;: &quot;POST&quot;,
            &quot;headers&quot;: {
              &quot;Content-Type&quot;: &quot;application/json; charset=UTF-8&apos;&quot;
            },
            &quot;failsafe&quot;: false,
            &quot;body_type&quot;: &quot;json&quot;,
            &quot;use_proxy&quot;: false,
            &quot;queryParams&quot;: {},
            &quot;proxy_settings&quot;: {}
          },
          &quot;pieceName&quot;: &quot;@activepieces/piece-http&quot;,
          &quot;pieceType&quot;: &quot;OFFICIAL&quot;,
          &quot;actionName&quot;: &quot;send_request&quot;,
          &quot;inputUiInfo&quot;: {
            &quot;customizedInputs&quot;: {}
          },
          &quot;packageType&quot;: &quot;REGISTRY&quot;,
          &quot;pieceVersion&quot;: &quot;0.6.1&quot;,
          &quot;errorHandlingOptions&quot;: {
            &quot;retryOnFailure&quot;: {
              &quot;value&quot;: true
            },
            &quot;continueOnFailure&quot;: {
              &quot;value&quot;: false
            }
          }
        },
        &quot;nextAction&quot;: {
          &quot;name&quot;: &quot;step_2&quot;,
          &quot;skip&quot;: false,
          &quot;type&quot;: &quot;PIECE&quot;,
          &quot;valid&quot;: true,
          &quot;settings&quot;: {
            &quot;input&quot;: {
              &quot;timeZone&quot;: &quot;Asia/Tokyo&quot;,
              &quot;timeFormat&quot;: &quot;YYYY-MM-DDTHH:mm:ssZ&quot;
            },
            &quot;pieceName&quot;: &quot;@activepieces/piece-date-helper&quot;,
            &quot;pieceType&quot;: &quot;OFFICIAL&quot;,
            &quot;actionName&quot;: &quot;get_current_date&quot;,
            &quot;inputUiInfo&quot;: {
              &quot;customizedInputs&quot;: {
                &quot;timeFormat&quot;: true
              }
            },
            &quot;packageType&quot;: &quot;REGISTRY&quot;,
            &quot;pieceVersion&quot;: &quot;0.1.6&quot;,
            &quot;errorHandlingOptions&quot;: {
              &quot;retryOnFailure&quot;: {
                &quot;value&quot;: true
              },
              &quot;continueOnFailure&quot;: {
                &quot;value&quot;: false
              }
            }
          },
          &quot;nextAction&quot;: {
            &quot;name&quot;: &quot;step_3&quot;,
            &quot;skip&quot;: false,
            &quot;type&quot;: &quot;PIECE&quot;,
            &quot;valid&quot;: true,
            &quot;settings&quot;: {
              &quot;input&quot;: {
                &quot;url&quot;: &quot;https://bsky.social/xrpc/com.atproto.repo.createRecord&quot;,
                &quot;body&quot;: {
                  &quot;data&quot;: {
                    &quot;repo&quot;: &quot;{{step_1[&apos;body&apos;][&apos;handle&apos;]}}&quot;,
                    &quot;record&quot;: {
                      &quot;text&quot;: &quot;{{trigger[&apos;title&apos;]}}&quot;,
                      &quot;embed&quot;: {
                        &quot;$type&quot;: &quot;app.bsky.embed.external&quot;,
                        &quot;external&quot;: {
                          &quot;uri&quot;: &quot;{{trigger[&apos;link&apos;]}}&quot;,
                          &quot;title&quot;: &quot;{{trigger[&apos;title&apos;]}}&quot;
                        }
                      },
                      &quot;createdAt&quot;: &quot;{{step_2[&apos;result&apos;]}}&quot;
                    },
                    &quot;collection&quot;: &quot;app.bsky.feed.post&quot;
                  }
                },
                &quot;method&quot;: &quot;POST&quot;,
                &quot;headers&quot;: {
                  &quot;Content-Type&quot;: &quot;application/json; charset=UTF-8&quot;,
                  &quot;Authorization&quot;: &quot;Bearer {{step_1[&apos;body&apos;][&apos;accessJwt&apos;]}}&quot;
                },
                &quot;failsafe&quot;: false,
                &quot;body_type&quot;: &quot;json&quot;,
                &quot;use_proxy&quot;: false,
                &quot;queryParams&quot;: {},
                &quot;proxy_settings&quot;: {}
              },
              &quot;pieceName&quot;: &quot;@activepieces/piece-http&quot;,
              &quot;pieceType&quot;: &quot;OFFICIAL&quot;,
              &quot;actionName&quot;: &quot;send_request&quot;,
              &quot;inputUiInfo&quot;: {
                &quot;customizedInputs&quot;: {
                  &quot;data&quot;: false
                }
              },
              &quot;packageType&quot;: &quot;REGISTRY&quot;,
              &quot;pieceVersion&quot;: &quot;0.6.1&quot;,
              &quot;errorHandlingOptions&quot;: {
                &quot;retryOnFailure&quot;: {
                  &quot;value&quot;: true
                },
                &quot;continueOnFailure&quot;: {
                  &quot;value&quot;: false
                }
              }
            },
            &quot;displayName&quot;: &quot;Send HTTP request&quot;
          },
          &quot;displayName&quot;: &quot;Get Current Date&quot;
        },
        &quot;displayName&quot;: &quot;Send HTTP request&quot;
      }
    },
    &quot;valid&quot;: true,
    &quot;schemaVersion&quot;: &quot;1&quot;
  },
  &quot;blogUrl&quot;: &quot;&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こんな感じで投稿できた。&lt;/p&gt;
&lt;p&gt;https://bsky.app/profile/mono0x.bsky.social/post/3lh6ik6rkms2w&lt;/p&gt;
&lt;p&gt;# 記事への投稿の埋め込みは oEmbed で普通にできた。&lt;/p&gt;
</content:encoded></item><item><title>chezmoi のスクリプト実行機能で zcompile を試した</title><link>https://blog.mono0x.net/2025/01/24/chezmoi-zcompile/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/01/24/chezmoi-zcompile/</guid><pubDate>Fri, 24 Jan 2025 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;zsh の起動を高速化するという話になるとよく &lt;code&gt;zcompile&lt;/code&gt; が出てくる。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zenn.dev/fuzmare/articles/zsh-source-zcompile-all&quot;&gt;&lt;code&gt;source&lt;/code&gt; コマンドを上書きして &lt;code&gt;zcompile&lt;/code&gt; を呼び出している事例&lt;/a&gt;なんかもあって、それもすごいと思ったのだけど、chezmoi を使って別のアプローチが取れそうだと思ったのでやってみた。&lt;/p&gt;
&lt;p&gt;chezmoi には、&lt;code&gt;chezmoi apply&lt;/code&gt; 時に任意のスクリプトを実行できる機能がある。ファイル名を &lt;code&gt;run_&lt;/code&gt; としておけば対象となり、かつ &lt;code&gt;run_after_&lt;/code&gt; のようにすれば chezmoi の処理の最後に実行してくれる。&lt;code&gt;.zshrc&lt;/code&gt; などのファイルを &lt;code&gt;chezmoi&lt;/code&gt; で配置し終えた後で &lt;code&gt;zcompile&lt;/code&gt; するスクリプトを実行すれば、&lt;code&gt;.zshrc&lt;/code&gt; などの実行時に何もせずともコンパイル済みのファイルを読み込めるし、&lt;code&gt;chezmoi apply&lt;/code&gt; の中に組み込めばコンパイル漏れもないという寸法である。&lt;/p&gt;
&lt;p&gt;具体的なスクリプトは以下のようになった。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env zsh
for file in ~/.zshenv ~/.config/zsh/.zprofile ~/.config/zsh/.zshrc ~/.config/zsh/**/*.zsh ~/.local/share/sheldon/**/*.zsh
do
  echo &quot;zcompile $file&quot;
  zcompile &quot;$file&quot;
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;パスの部分は各自の環境に合わせて変更する必要がある。自分の場合はプラグイン管理に sheldon を使っているので、そちらで管理しているプラグインも &lt;code&gt;zcompile&lt;/code&gt; するようにしてみた。&lt;/p&gt;
&lt;p&gt;試してみたところ、自分の環境では zsh の起動時間はほぼ変わらなかった。もともと 10～20 ms で起動する程度までチューニングされているし、&lt;code&gt;zsh-defer&lt;/code&gt; での非同期読み込みも多用しているので、効果が出にくかったのかもしれない。効果がないのにわざわざ複雑にする必要もないので、結局導入は見送ったのだが、chezmoi の使い方としては面白いかなと思っている。&lt;/p&gt;
</content:encoded></item><item><title>DxO PhotoLab で External Selections をまとめて削除する</title><link>https://blog.mono0x.net/2025/01/17/dxo-photolab/</link><guid isPermaLink="true">https://blog.mono0x.net/2025/01/17/dxo-photolab/</guid><pubDate>Fri, 17 Jan 2025 13:00:00 GMT</pubDate><content:encoded>&lt;p&gt;DxO PhotoLab では、Lightroom Classic などの外部アプリケーションから DxO PhotoLab に転送した場合に、External Selections のリストに項目が追加されて管理される。転送するたびに項目が増えていくが、まとめて削除する方法はなく、1件ずつポチポチしていく必要があり面倒。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forum.dxo.com/t/bulk-remove-external-selections-entries/6706&quot;&gt;Bulk remove External Selections entries - DxO PhotoLab - DxO Forum&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2019年にスレッドが作成されてから今に至るまで改善されておらず、対応には期待できないので、自分でどうにかすることにした。&lt;/p&gt;
&lt;p&gt;DxO PhotoLab のデータ構造を調べたところ、SQLite になっていて、直接データベースを操作すればよさそうだったのでやってみた。&lt;/p&gt;
&lt;p&gt;SQLite の CLI は Windows であれば scoop などでインストールする。macOS には最初から入っている。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scoop install sqlite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DxO PhotoLab が起動していない状態で以下のようなクエリを実行する。Windows と macOS でデータ構造が何もかも違っていて衝撃だった。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sqlite3 &quot;${env:AppData}\DxO\DxO PhotoLab 8\Database\PhotoLab.db&quot; &quot;DELETE FROM Projects WHERE ProjectTypeDiscriminator = &apos;EFDOPExternalSelection&apos;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sqlite3 ~/Library/DxO\ PhotoLab\ v8/DOPDatabaseV8.dopdata &quot;BEGIN; DELETE FROM ZDOPLIST; DELETE FROM Z_8PROJECTS; COMMIT&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;とりあえず自分の環境ではこれで動いた。本当に問題がないかはわからないし、ミスすると危険だと思うのでやるなら自己責任で。&lt;/p&gt;
</content:encoded></item></channel></rss>