jq 1.8.0をリリースしました

jq 1.8.0をリリースしました。 github.com

前回のアップデート記事はこちら。 itchyny.hatenablog.com

変更の詳細はリリースノートに譲るとして、推しの新機能や修正を紹介します。

jqのクエリが間違っている時に、どこが問題なのかわからないというのはよくあることです。 今回のリリースで、エラーが起きている場所がわかりやすくなりました。

 $ jq -n '1 + $foo + 2'
jq: error: $foo is not defined at <top-level>, line 1, column 5:
    1 + $foo + 2
        ^^^^
jq: 1 compile error

エラーの位置がわかりやすいのはとても便利ですね。

jqでは、asキーワードを使って値を変数に束縛できます。 しかし、その結合が演算子よりも強く、以下のようなクエリで直感的でない挙動をしていました。 今回のリリースで、asキーワードの結合を修正し、より分かりやすい挙動に変更しました。

 $ jq -nc '[-1 as $x | 1,$x]'
[1,-1]    # これまでは [-1,-1]
 $ jq -nc '1 | . + 2 as $x | -$x'
-3        # これまでは -1

この修正はだいぶ前に自分が書いて放置されていたのですが、最近になってようやくマージできました。 変数束縛を使っている場合は、既存の結合に依存していないかご注意ください。

jqはJQ_COLORS環境変数を使って、出力の色を設定できますが、短いANSIシーケンスしか設定できませんでした。 今回のリリースで、JQ_COLORS環境変数にtruecolorのように長い色の指定を設定できるようになりました。

export JQ_COLORS="38;2;255;173;173:38;2;255;214;165:38;2;253;255;182:38;2;202;255;191:38;2;155;246;255:38;2;160;196;255:38;2;189;178;255:38;2;255;198;255"
jq -nc '[null,false,true,42,{"a":"bc"}]'

綺麗ですね。 ぜひ推しの配色を設定してみてください。

まだ紹介したい機能や修正はありますが、全部紹介していると長くなりすぎるのでここまでにして、あとはリリースノートをご参照ください。 重大な脆弱性の修正も含まれていますので、jqを使っている方はぜひアップデートをお願いします。

2024年を振り返って

サイボウズに入社して三年が経ちました。 夏ごろまではインフラ移行やリリースの自動化・高速化などに携わり、秋からは新しいサブチームを立ち上げてEMとしてチームをリードする立場になりました。 プロダクトの根幹となる領域を担当し、他のサブチームの認知負荷を下げるという立場で、苦労しながらもなんとかチームを軌道に乗せることができたと思っています。

今年の初めにはS3にファイルをキャッシュするGitHub Actionを作って公開しました。 こちらは業務で関わっているリポジトリで必要になったために自作したのですが、今では様々なビルド成果物や依存パッケージのキャッシュに使っています。 弊社はこういう感じで手を動かすタイプの人があまりいないので、こういう動きをしてもいいのかなと迷うことも多いです (こういうオープンネスもあまり評価されない)。 でも結果オーライということでこのアクションに関しては作って良かったなと思っています。 itchyny.hatenablog.com

他のOSS活動としては、去年に引き続きgojqとjqのメンテをやっていました。 jqへの貢献としては、日々のissueやPRの対応に加えて、新しいフィルターの実装やバグ修正などを行いました。

メンテナがみんな忙しくて次のリリースはいつになるやという感じですが、引き続きやっていくので応援よろしくお願いします。

今年の初めには台湾に旅行に行きました。 久しぶりの海外旅行で初めは緊張していましたが、なんとかなりました。 有名な夜市は外国人観光客が多く英語が通じましたし、九份にはツアーで行ったので困りませんでした。 ただ、台北でも夜市から少しハズレにある個人店でご飯を食べようとすると、コミュニケーションに困る場面がありました。 台湾茶にはかなりハマり、今でも普段の食事で台湾茶を飲む日があるくらいです。 今年は他には広島や岡山、名古屋に旅行に行きました。 来年は夏に北海道とか行きたいですね。

最近息子が産まれて、今は妻と共に育児に専念しています。 息子がじっと自分を見つめてくるのを見ていると、何があってもこの子を大事に育て上げなければという気持ちが湧いてきます。 カラフルな色が描かれている絵本を見せたり、ジブリの曲を歌ってあげたりしているのですが、あまり反応もなく良い刺激になっているのかはよく分かりません。 もう少し大きくなれば笑ってくれるようになるのでしょうか。これからの成長がとても楽しみです。

今年は株式投資に目覚めた年でした。 以前から個別株の売買は多少していたものの全く何も分からないまま適当にトレードしており、八月の暴落が起きた時にかなりの損失を出してしまいました。 それをきっかけに真面目に勉強を始め、今はテクニカル分析を中心とした短期トレードにハマっています。 株価は需給によって上下すること、あらゆる事実や予測が織り込まれること、様々な時間軸と資金力で取引する人がいることなどが分かってきました。 来年は良い成績が納められるよう(相場に焼かれないよう)、ぼちぼち頑張っていきたいと思います。

今年もよくドラマを見ていました。 特に『アンメット-ある脳外科医の日記』『海のはじまり』『ライオンの隠れ家』『海に眠るダイヤモンド』は印象に残っています。

来年は子の成長を見守りながら、趣味の幅を広げたり新しいことに挑戦していきたいです。

百瀬弥生「分かる、好きな人の子どもって、こんなにかわいいんだ!って。やっぱ似てるとこ、ちょいちょいあるしね。」

海のはじまり 第6話

dependabotのPRに自動でビルド成果物をコミットする

ビルド成果物をリポジトリに含める必要がある場合、どのタイミングでコミットするかが問題になります。 例えばGitHub Actionsのように、JavaScriptの成果物をリポジトリに含める必要があるようなケースです。 リリースを打つ時にビルド成果物をコミットするという方法もありますが、この記事ではメインブランチへのマージ時にはビルドしないといけないという前提があることにします。

人間がPRを出す場合は手元からビルドして成果物も一緒にコミットできますが、dependabotのようなbotにはビルドをさせることができません。 しかし、GitHub Actionsを使えば、dependabotのPRに対してもビルド成果物をコミットすることができます。

name: CI

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha || github.sha }}
          token: ${{ secrets.DEPENDABOT_TOKEN || secrets.GITHUB_TOKEN }}
          fetch-depth: ${{ github.actor == 'dependabot[bot]' && 2 || 1 }}
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - name: Install Dependencies
        run: npm ci
      - name: Run tests
        run: npm run test
      - name: Build
        run: npm run build
      - name: Check for dist changes
        id: dist-changes
        run: git diff --exit-code
      - name: Push dist changes for dependabot
        if: github.actor == 'dependabot[bot]' && failure() &&
          steps.dist-changes.outcome == 'failure'
        run: |
          git config user.name "$(git show -s --format=%an)"
          git config user.email "$(git show -s --format=%ae)"
          git commit --all --amend --no-edit
          git push --force origin "HEAD:${GITHUB_HEAD_REF}"
  • GITHUB_TOKENの権限を強めたくないので、permissionsは読み取り権限のみにしています。Dependabot secretを使うことで、dependabotのPRでしか利用できないシークレットを設定できます。このシークレットには該当リポジトリへのwrite権限を付与したアクセストークンを設定しておきます。
  • pull_requestイベントでactions/checkoutを使うと、マージコミットをチェックアウトしてしまいます。今回はPRにコミットを積みたいので、refgithub.event.pull_request.head.shapushイベント時のフォールバックを指定します。
  • あとはテストとビルドを行います。そしてビルド成果物が変更されているかを確認し、もし変更があればdependabotのPRにコミットします。今回はパッケージの更新コミットにamendしたかったので、push --forceで更新しています。また、fetch-depthも2にしています。もしamendしない場合はpush --forceも不要でfetch-depthを指定しないでも問題ありません。

ポイントはチェックアウトするときのrefと、Dependabot secretを使うことでしょうか。 GitHub Actionsがそうなのですが、ビルド成果物をコミットしなければならないというシチュエーションはそもそも珍しいかもしれません。 しかし、Dependabot secretの存在を知っておくと、dependabotのPRにのみ利用できるシークレットを設定できるのでとても便利ですね。

それではまた!

バイナリエディタ bed のコマンドラインの機能を強化しました

先日、自分の作っているバイナリエディタbedhomebrew/coreに入ったことをご報告しました。

その後、コードを眺めていると色々と直したいところが出てきたり、欲しい機能の実装イメージが沸いたりして、また活発に開発するようになりました。 今でもそれなりに使われていることを意識すると、急にメンテする気力が湧いてくるんですよね。不思議です。 個人OSSの継続にはモチベーションが大事です。フィードバックは大歓迎です。

最近、コマンドライン周りの機能強化をやっています。

  • コマンドの実行履歴を上下キー (と<C-n>, <C-p>) で辿れるようにしました。実装としては、実行したコマンドをスライスに追加していく (のと重複を削除する) だけです。少し面倒なのが、コマンド実行 (:) と検索 (/, ?) で別の履歴として保存しないといけないことです。
  • コマンドライン環境変数の補完に対応しました。例えば、$GOPATHのように環境変数ディレクトリを指しているとき、 :e $GOP<TAB>:e $GOPATH/ まで補完して、さらにその中のファイルを候補として表示してくれます。
  • 新しいコマンドとして :cd:pwd を実装しました。:cdは作業中のディレクトリを変更するコマンドで、:pwdはそれを表示するコマンドです。:cd - で前にいた場所に戻るといった細かい (けど使う人には便利な) 機能も実装しています。

全く別の文脈 (とあるVimプラグイン) で作業ディレクトリのことを考えていて、bedでもVimみたいにディレクトリを移動できればファイルを開くのが楽になるなと思い:cdの実装を考え始めたのがきっかけでした。 初めはos.Chdirを呼ぶだけだと思っていたのですが、これまでコマンド実行中にディレクトリが移動することがなかったので、既存の機能に影響して苦労しました。 例えば :w test で保存したウィンドウは、その後 :w で同じファイルを上書きします。 しかし、このtestが作業中のディレクトリからの相対パスになっていたので、:cdを作ったことで挙動が変わってしまいました。 各ウィンドウで絶対パスを管理することで、この問題は解決しました。

コードを読み返していると、当時勢いで書いた部分が多く、色々な箇所をリファクタリングしたくなってきました。 今回は、コマンドラインのパーサーを大きく書き直しました。 昔は[]runeを引き回して次のオフセットを返すようなパーサーをよく書いていたのですが、Goのプラクティス的には文字列を渡してパースしたものと残りの文字列を返す方が良いということがわかってきました。 strings.CutPrefixはとても便利ですね。 他にもやたら状態遷移するコードを好んで書いていたのですが、よほど複雑なものでない限りはコードが読みにくくなるだけなのでやめました。 ここ数年でもだいぶコーディングの好みが変わってきているのを感じます。

テストもだいぶ書き直して、読みやすく安定したものになりました。 bedのテストを見返すと、UIが送ったイベントをウィンドウが処理するのを待つためにsleepするみたいなテストが存在していました。 例えば、UIが:w:qのイベントを送った時に、ファイル保存が完了するよりも前に:qを処理してしまうと、保存がうまくいきません。 でもテストの中でsleepするのは良くないですよね。何を待っているのかわからなくなりますし、その時間で処理が完了する保証もなく、不安定になってしまいます。 UIが送ったイベントに対しては再描画のリクエストがUIに返ってくるので、そのイベントの受信を待つよう修正することでsleepを撲滅することができました。 テストで何かを待ちたくなったら、sleepするより良い待ち方はないのか、本当にsleepする時間内に完了する保証があるのか考える必要があります。

Vimの操作に慣れていて、バイナリファイルを軽く確認したい、軽く編集もしたいという人に、bedはとてもオススメできるツールです。 ぜひ使ってみてください。

バイナリエディタ bed が homebrew/core に入りました

数年前に、趣味でbedというバイナリエディタを作ったことがありました。 当時はメモリーに乗らないような大きなファイルを描画したり、編集できるようにするロジックを考えるのが楽しく、とても熱中していました。 itchyny.hatenablog.com 基本的な機能をある程度作ってからは急速にやる気がなくなってしまい、細々とリファクタリングやパフォーマンス改善などをしてメンテナンスをしていました。

bedは元々itchyny/homebrew-tapでHomebrew formulaを配信していたのですが、最近homebrew/coreに入りました。 github.com これで、次のコマンドでbedをインストールできるようになりました。

brew install bed

私の作ったものでhomebrew/coreに入ったプロダクトが二つになりました。 一つ目は、もちろんgojqです。 誰かが自分のプロダクトを良いものだと思ってくれて、より多くの人に使われるように動いてくれるのは嬉しいことですね。