おそらくはそれさえも平凡な日々

ヘンリーに入社しました

1月から株式会社ヘンリーに入社しました。ヘンリーは「社会課題を解決し続け、より良いセカイを創る」というミッションを掲げ、現在はHenryというレセコン一体型クラウド電子カルテサービスを主力として医療DXに取り組んでいるスタートアップです。

https://corp.henry-app.jp/

ヘンリーのことはあまり知らなかったのですが、ずっと一緒に働きたいと思っていたエンジニアの一人である縣さんが所属しており、今回私が転職活動を始めたのを彼が早々に察知して誘ってくれたのがきっかけです。

彼と話したところ、開発に色々課題は抱えつつも前向きに、楽しそうに働いていると感じたのが印象的でした。ナイスガイな彼がそれくらい魅力を感じているのであれば、良いチームで面白い社会課題を解いているだろうなと。

その後、2週間で様々なポジションの7名とお話しました。どの方もモチベーション高く、顧客や事業やプロダクトに取り組んでいるのが感じられ、やはり良いチームだと確信でき、事業やプロダクトにも魅力を感じられそうだったので、入社することに決めました。

比較的年齢が若い会社ですが、CEOの一人も含め、働きながら子育てしている社員も多く、健康に活力を持って生きながら、柔軟にモチベーション高く働ける環境を志向している点も、ミッションや事業と言行一致していて魅力的でした。

人数もまだ30人程度で、リモートワーク中心で入社する上で、自分にとってちょうど良く感じる組織規模だったというのもあります。

入社して

入社して早くも2週間近く経ちましたが、ちょうど全社員参加のワークショップがあってチームメンバーや業務をキャッチアップできて助かりました。各メンバーのモチベーションの高さも改めて感じられ、これくらいの人数のスタートアップの良さを感じましたし、人がこれから増える中でもそれを維持したいですね。月末にはオフラインの新年会もあるようですし、良いタイミングで入社できて嬉しいです。

入社後驚いたことの一つに、営業チームの各メンバーがHenryをSaaSとして売っていくことをしっかりと理解していたことがあります。カスタマイズを極力避け、自分たちの製品がフィットする病院をターゲティングしてリストアップし営業をかけている点が非常にクレバーだと感じました。開発側としても迷いを減らして生産性を上げられるので嬉しいポイントです。

とはいえ、医療というドメインはなかなか難しく、入社したら予想通り情報量も多くて大変です。私自身も自分が直接のユーザーにならないサービスを開発するのはSIer時代以来でだいぶ久しぶりのことで、その点もチャレンジングではあります。

ただ、私も歳を重ねるに連れて、医療機関にお世話になる機会も増え、医療は、今後一層自分ゴト化せざるを得ないドメインです。また、電子カルテ自体も実はまだ普及率が50%程度にとどまり、業界にDXの余白が多く残っている状況で、事業にもまだまだ伸びしろがあります。日本は世界一の高齢化先進国の一つになることが宿命付けられているので、その分野で世界をリードできるチャンスもあります。そんな中で良い仲間と有意義な社会課題を面白く解いていくことを楽しみにしています。

また、今はレセコン一体型電子カルテがメインサービスで、しばらくはそれが注力事業ですが、長いスパンでは医療ドメインや隣接分野でも新規サービスを展開していきたいと考えているようですし、新規事業がどんどん生まれる会社になると嬉しいですね。

技術

システムとしてはいくつかのマイクロサービスに分割されていてバックエンドは主にKotlin、BFFにNode.js, TypeScript、フロントはReact, TypeScriptです。BFFのプロトコルは後ろはgRPCで前はGraphQLです。クラウドはGCPで主にCloud Runを実行環境としています。

結構モダンな技術選定ではありますが、その分苦労している点もあるようです。お陰様でお客様も付き始め、その過程で当初想定していなかったことや、運用の難しさなどにも当然直面しています。当初の設計も振り返りが必要なタイミングだとも感じているようです。

また、当然品質が大事な製品なので、QAフローを厚くやりつつも、そこが開発サイクルのボトルネックにならないよう全員がQAを意識して開発に取り組む必要性も感じています。言うのは簡単ですが、これは開発に携わるメンバー全員が感じている大きな課題意識です。

採用

ということで、各職種絶賛採用中なので、お問い合わせいただけると嬉しいです。

https://jobs.henry-app.jp/

また、1/24(火)にオフラインのエンジニアミートアップをやるのでヘンリーが少しでも面白そうだと思ったらお申し込みください。ピザやビールを片手にラフに技術について雑談できればと思います。そういう機会も久しぶりなので楽しみですね。


https://henry.connpass.com/event/271976/

お待ちしています!

退職

12月末でLaunchableを退職します。実際には11月30日が業務最終日で12月は求職活動をしていました。幸い12月中に転職先を決めることができ、1月から次の会社で働きます。次の会社は年明けにお知らせします。

求職活動中は多くの方や会社から連絡をいただき本当にありがたかったです。全てにお返事をすることができず申し訳ありませんが、直接お知らせできなかった方にはこちらでお知らせとなることをご了承ください。

以上でお知らせは終わりで、以降は単なる中年男性のしょうもない独白です。


退職は非常に残念で、Launchableでまだまだやりたいことはあり、これからというところでもあったのですが、言ってしまえば西海岸外資の洗礼を受けたということです。

今回の僕の挑戦はあっけなく終りを迎え、ほろ苦い体験となりました。とはいえ間違いなく良い経験にはなりました。Launchableの事業は引き続き応援していきます。ありがたいことにSOもいくらか付与されているので、折角なので行使できる分はするつもりです。未上場外国株式を持てる機会もなかなか無いですし。

今回の退職は僕の能力不足起因ではないと会社からは伝えてもらったし、よくある決り文句ではなく実際にそう思ってくれているのも分かったのですが、僕としてはやっぱり実力不足だったなと受け止めています。

錯覚資産に自分が錯覚させられていた話

僕はアウトプットが多い分、実力に対して過大評価されているとは常々思っています。必要以上に自分を大きく見せたくない自分としては居心地の悪さを感じつつも、過度にへりくだることは自分を評価してくれる人を貶める態度なので、その評価に見合うように頑張ろうと思ってやってきました。

それに評価していただくと思わぬ面白い機会をいただくことも増え、その機会に乗って結果を出すことが自分の成長につながるのも感じていました。過大評価に自分の実力を合わせていくということで、それもまた自分の持ち味だとも捉えていました。「過大評価よりも過小評価されることを恐れたほうが良い」といったことをkazuhoさんが以前どこかでおっしゃっていたように記憶していますが、自分を大きく見せたくない人間に取って、それは余計留意すべきポイントだとも思っています。

そういった自分のスタイルを受け止めつつも、周りからの評価に対して詐欺師的なジレンマを感じたり、「有名エンジニアは実は仕事ができない」みたいな言説が定期的にSNSで話題になるたびに心にチクリと痛みを感じることもありました。

とはいえ、やはり、あまりにもうまく行き過ぎていた。特に30歳以降、キャリアを望めば望む方向に進める感じがありました。それは、偶然歯車が噛み合ってうまく動き出した。つまり運が良かったからだ、とは思っていたはずです。しかし、努力は必ず報われる、はずがない、という当たり前のことがもしかしたら自分には当てはまらないのではないかと傲慢になっていたのかも知れません。過大評価が自分の実力だと勘違いしてしまうことが怖いと思っていたのですが、結局勘違いしていたのではないか。

努力をしていたつもりでもその土俵で戦えなくなることはあるし思い通りにならないからこそ人生は面白いのでした。今回の挫折は良い薬になり、自我の肥大にブレーキをかけるきっかけにもなりました。

20代の頃は苦労したし、その経験もあって、自分のような頭が良いだけで社会性のない人間がいつ転落するかわからない。そのように思っていました。しかし、近年はその感覚が薄れていたし、転落のような劇的な変化が自分に起こりうると思ってしまう事自体、中二病的な恥ずかしい感覚なのかもしれない、とも思うようにもなっていました。しかし、改めて自分はどうなるかわからないし、程よい危機感を持って生きる必要があると思い直しました。

しかし、ここまで書いて読み返してみると、だいぶ気持ち悪い文章ですね…。退職が決まった12月頭に書いたらもっとひどい文になっただろうと思い、すぐには書き出さず、少し落ち着いた今なら大丈夫だろうと思って書き出したらこの有様です。

今回の求職活動

それくらいショックを受けていたのですが、12月に求職活動を始めながら何人か知人に相談にのってもらい、そして、ありがたいことに色々な会社から声をかけてもいただきました。皆様その節は本当にありがとうございました。その中で、何社か選考に進み、12月にオファーを出してくださった会社の中から次の会社を決めました。

当初は焦って決めたくないし、1月は休みたいとも思っていて、2月から働く想定でした。その勤務開始時期も応募した会社にはそれぞれ伝えていたにもかかわらず、12月で決めてしまい、他社の選考を中断したのは申し訳なかったです。

次の会社が、拙速な判断にならないように何度も面接のステップを踏まえつつ、意思決定早く選考からオファーまで進めてくださったので、こちらも早くお返事したくなったのと、他社も含めた求職活動の中で元気が出てきたので1月から働く気になったというところです。

妻からも「求職活動を引き伸ばして選択肢を増やしても迷いが増えるだけだし、他の会社に選考コストを無駄に払わさせることにもなるから、そこが良いと思ったのならさっさと決めても良いんじゃないの?」のようなことを言われ、僕も最後は直感で決めるのが良いと思っているのでさっさと決めてしまって動くことにしました。

採用ツールやフロー

ちなみに、求職活動をしていることをTwitterでは告知しませんでした。連絡が多くなりすぎて捌けなくなるだろうと思ったからです。なので、広報範囲を絞る意味で、まずは登録した後あまり活用していなかったYOUTRUSTで転職ステータスを変更し、メッセージも投稿しました。それで集まらなかったら、facebookやLinkedInでも告知するつもりでしたが、YOUTRUSTだけで返事しきれないほどの連絡をいただきました。ステータス変更だけで多くのメッセージがすぐさま届きました。各社の人事仕事してるし、エンジニア採用事情が大変なのだな、と感じました。

Twitterやfacebookでのコネクションは維持したいと思いつつも、もはや誰とつながっているかわからないのでキャリアSNSという切り口でソーシャルグラフが再構築されている点、条件面含めた希望面もある程度具体的に書いても角が立たない雰囲気がある点など、YOUTRUSTは有用でした。

そういえば同時期に転職活動をしている人がTwitterサークル機能を使って転職を告知していて、なるほどそうやって広報先を絞る方法があったか、と膝を打ちました。

それと、10年近くぶりに日本のエンジニアの通常の採用選考を受けましたが、選考フローは以前と比べてどこも格段に洗練されていました。エンジニア採用が難しくなっていることも影響しているのでしょう。

事前準備が必要な技術課題が課されるケースも多くありました。ただ、そうすると選考のリードタイムが伸びてしまう悩ましさもあると感じました。しかし、採用は失敗するとダメージが大きいので、ちゃんと見極めるためには大事なステップですし、有名人だからといって変に特別扱いせずフェアにやるべきだし、むしろ技術的にマッチしない有名人を雇ってしまうとダメージがもっと大きくなりますし、ジレンマですね…。

なんにせよ、数年前まではエンジニア採用に積極的に関わっていて採用活動は嫌いではないし、次の会社でも採用に関わることもありそうなので、良い会社の選考を受けられたことは今後の参考になるありがたい体験となりました。

ミドルエイジクライシス

それに、選考を受け、その中で自分の考えや過去のエピソードなどを話せたのは、自分の思考を整理して自分を見つめ直す良い機会にもなりました。ミドルエイジクライシスとか言い出すの嫌だなーと思ってたのですが、今後のキャリアも人生も長いし、変化の早い時代の中で定期的に自分を振り返るのは必須だなと思うようになりました。

僕はわがままで自分の欲望を大事にしたいし、つらいと感じたら何かが間違っていて、その苦しさに陶酔してはいけないと考えています。などと言いながらそれなりに苦悩しながら生きている現実もあります。

以前、yamazさんに「松木くんは悩める人だから」と言われたことがあり、それから「これは自分のパーソナリティで、ずっと付き合っていくものなのだな」と肯定的に受け入れられるようになったことがありました。その後補足で「それはむしろ才能なんじゃないか」的なことも言われ、自分の持ち味なのだとも思うようにもなりました。

一流と超一流の間

ということで、僕のことを結構すごいエンジニアだと思ってくださっている方も多いでしょうし、僕自身もそんなに悪くないエンジニアだとは自己認識するようにしていますが、他の多くの皆様同様に悩みを抱えていたりするわけです。人生を楽しみたいと思い、もちろんかなり楽しみつつも、何故か苦しんでもいる。人生そんなもんです。虚勢を張って自分を良く見せすぎても余計自分を苦しめるだけなのです。

僕は自分を一流エンジニアだと思うようにしていますが、超一流では決してありません。そこには自分の中で明確な線引きがあり、それを決して超えられないとも感じているのですが、そのあたりの言語化はそのうち試みるかも知れません。

とはいえ、OSSやTwitter、ブログ等のアウトプットをレバレッジにする自分のスタイルは変えるつもりはないし、変えられないとも思っています。それを所属企業の広報的にも活かしていくのが僕の強みの一つだとも考えているので。

ただ、このブログを含め文章については日本語のアウトプットが大半なので、そこに閉じていないで英語を使うチャレンジは増やしたい。Launchableでそれを狙っていましたが残念ながらうまくいきませんでした。とはいえ、ここ数年、英語学習量、会話や読み書きの頻度を増やす中で英語は少しづつながら着実に成長している手応えは感じているし、面白味も感じています。これはまた、ソフトウェアエンジニアリングと同じで、ライフワーク的に続けていくものだと思うようになりました。

ということで、キャリアの惑いは尽きないものですが、自分も含め、幸い世のソフトウェアエンジニアの仲間たちが変化に富んだ多様な生き方を示してくれています。一緒に考えながら、基本的には愉快に、時には悩みながら面白い人生を歩んでいきたいと思っています。

直近2社は短期間の所属になってしまったので、次は長く勤めたい。僕の志向として短期的に発揮できる技術だけではなく、自分の能力を長期的な事業貢献に繋げたいし、そこでバリューを発揮できるとも考えています。会社としてもそういう動きを僕に求めるでしょうし、次はそういう手応えを感じられる充実した働き方を長く継続できるようにしたい。

ササミの漬け - お手軽高タンパク常備菜

バズレシピアドベントカレンダーの24日目です。リュウジさんの鶏胸肉のレモン漬けをベースにして、ササミの漬けを定期的に作っているので紹介です。

多めに仕込んで、常備菜として一週間程度で食べきっています。お手軽高タンパク食でアレンジも効いて美味しいので、食事に一品足したいときとか、料理したくない時や間食でぱっと食べるのに便利です。

アウトライン

リュウジさんのレシピと異なる点は、ササミを使っている点、ブライン液に漬けて吸水する工程がある点、漬け汁に酢とワサビを加えて長期保存が効くことを期待している点です。

ササミは、自分の場合500g強のパックが近くのスーパーに安めに売っていて、それをまとめて仕込むのが都合が良いのと、切りやすいこと、胸肉と違って皮の処理を考える必要がないことなどの理由で採用しています。

ブライン液とは水に対して5%の分量の砂糖と塩を溶かした溶液です。肉に吸水させる方法は色々試したのですが、これに浸すシンプルな方法に行きつきました。P2B Hausのよだれどりが感動的に美味しく、お店の人に調理方法を聞いた時に教えてもらいました。

材料

漬け汁は元のレシピで使われている「これ!うま!!つゆ」があれば素直に使うと良いでしょう。ウチにはヤマキの白だしと創味のつゆが常備されているので使っているだけです。

味の濃薄の好みもあると思うのでアレンジするといいでしょう。ワサビは結構入れてしまっても大丈夫です。私は刺身パックの余った小袋を2,3個まとめて使ったりしています。風味付けにもなりますし、ワサビが苦手な妻も美味しく食べています。

手順

1. ササミをそぎ切りにする

切りやすい厚さでよいですが、厚みによっては茹で時間を多少調整する必要が出てくるかも知れません。筋取りはしていませんが、丁寧にやりたいならどうぞ。

2. 切ったササミをブライン液に一晩以上つける

ブライン液は浸透圧等の関係で分量が大事らしいのでしっかり計量しましょう。特に塩分は5%を超えず、3~5%の範囲に収まっていると良いようです。また、この工程は吸水が目的なので、それ以外の調味料を加える必要はありません。

漬け時間は冷蔵庫で半日~丸一日。

3. 片栗粉を絡める

冷蔵庫から出して水を切り、ボウルのまま片栗粉をふりかけて手で絡めます。片栗粉は大さじ2としていますが全体に絡めばいいので厳密に計る必要はありません。

この片栗粉が水晶鶏のようなプルンとした食感を生み出します。

4. 沸騰したお湯でさっと茹でる

沸騰したお湯に肉をまとめて投入し、たまに軽く混ぜながら火の通りが均一になるようにします。茹で時間は様子を見ながら大体2-3分。実際は肉を投入後に再沸騰を待ち、そこから30秒-1分程度で茹であげとしています。

元のレシピの茹で時間は1分程度になっていますが、僕の腕前の問題で切り方が厚めなのと、このレシピでは冷蔵庫で漬けておいた肉を取り出して直ぐに茹でることになるので、長めに茹でています。常温に戻す工程をいれても良いかも知れませんが、手間ですし、戻す時間によって逆に茹で上がりがブレそうなのでやっていません。

茹で汁も旨味が出て美味しくなっているので、味を整えてスープなどにしても良いでしょう。

5. 保存容器に入れ漬け汁に浸す

茹で上がった肉を保存容器にあけ、漬け汁を注ぎます。保存容器はこのジップロック コンテナー正方形 700mlがジャストサイズで気に入っています。漬け汁を注ぎすぎると蓋を閉めるときに溢れてしまうので気をつけましょう。

実は私は実際には漬け汁を注ぐのではなく事前に水以外の調味料を保存容器に入れておいて、そこに肉をあけてから水を加えて水位を調整するという工程を取っています。洗い物を増やしたくないので。

6. 仕上げ

元のレシピにもある通り、蓋を閉める前に、黒胡椒をお好みでこれでもかと振りかけましょう。

薄切りレモンを乗せられると嬉しいですが、無くても構いません。酸味の強い系の柑橘の皮が余っていれば乗せるのはオススメです。私は柚や小夏(日向夏)を使いましたが良かったです。

あとは冷蔵庫で数時間置けば食べられます。私は1週間弱を目処に食べきるようにしています。ラー油や柚子胡椒などを加えて食べても美味しいです。

まとめ

簡単にでき、高タンパクで多めに仕込んでおけるので、ダイエッターやトレーニーにとっても嬉しい一品となっております。是非お試しください。

tagpr を機能強化してv1.0.0をリリースしました

前回のtagprの紹介エントリの反響が思ったより大きくて喜んでいます。ということで、積み残し及び、要望やヒントから着想を得て幾つかの機能を追加実装しv1.0.0をリリースしました。

https://github.com/Songmu/tagpr/releases/tag/v1.0.0

ありがたいことに、public repoだけでも十数名の方に既にご利用いただいているようで、フィードバックから細かいエッジケースの修正も幾つかおこなって敲かれたため、だいぶ使い物になるソフトウェアになったと言って良いでしょう。

ということで、追加された機能等について解説していきます。

uses: Songmu/tagpr@v1

これまではワークフロー上で uses: Songmu/tagpr@main とブランチ直指定してもらっていましたが、uses: Songmu/tagpr@v1 と指定できるようになりました。こっちのほうが、tagpr自体のリリースタイミング的にも若干安全なので、既にお使いの方にはお手数ですが切り替えを推奨します。

併せてMarketplaceへの公開もしました。GitHub ActionsのカスタムワークフローのMarketplace公開は初めてなので嬉しいですね。

https://github.com/marketplace/actions/automate-pull-request-generation-and-tagging-for-releases-using-tagpr

CHANGELOG.md を自動変更しないオプション

CHANGELOG.mdを自動更新しない設定を .tagpr 設定ファイルに書けるようになりました。

[tagpr]
    changelog = false

前回の紹介エントリを書いたらすぐにsiketyanさんからpull requestいただきました。ありがとうございます!

https://github.com/Songmu/tagpr/pull/86

Conventional Labelsの強化

リリース対象のpull request一覧を拾い、それらのラベルに"major"か"minor"が含まれていた場合、それを考慮して次バージョン番号候補をtagprが生成するpull request上に出力するようにしました。

これは便利だと思います。

GitHub Releases作成挙動の変更オプション

tagprはデフォルトでtagを打つと同時に、GitHub Releasesも作成しますが、この挙動を設定可能にしました。tagpr.release設定キーに、true, false, draftの何れかを指定できます。

[tagpr]
    release = false # [true/false/draft]

GitHub Actions用出力により後続のタスクとの連携が容易に

tagprがtagを打った後、別途ジョブを動かしたいケースは多いでしょう。実行ファイルやコンテナをビルドしてパッケージングし、それらを何らかのリポジトリにアップロードしたり、deployフローを動かしたりなど。

これは、tagをトリガーにして別ワークフローを動かすのが良いと思いますが、前のエントリで説明したように、GitHub tokenの権限周りの仕様の関係でトークン発行処理を組む必要が出てくるなど少し複雑になります。

ですので、tagpr実行後に直接後続のタスクを動かせれば一つのワークフローの責務は多めになりますが、お手軽です。

それを実現しやすくするために、GitHub Actions用の出力を出すようにしました。具体的にはtagpull_requestというoutputsを出力します。このtagが空か否かを見ることで後続のフローを動かすかどうかの判定を簡単におこなえます。具体的には以下のような具合です。

- uses: actions/checkout@v3
- id: tagpr
  uses: Songmu/tagpr@v1
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: ./.github/actions/release
  with:
    tag: ${{ steps.tagpr.outputs.tag }}
  if: "steps.tagpr.outputs.tag != ''"

このサンプルでは、後続のリリースフローをリポジトリローカルのカスタムアクションにまとめています。.github/actions/release/action.yml に処理を記述することになるでしょう。

このようにしておくと便利な点としては、上記のtagprのワークフローが途中で意図せず失敗してしまった場合に、リリースフローをリカバリするために手動でタグを打つワークフローを別途用意しやすくなることです。

name: release
on:
  push:
    tags:
    - "v[0-9]+.[0-9]+.[0-9]+"
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: ./.github/actions/release
      with:
        tag: ${{ github.ref }} # Note that github.ref is formatted like refs/tag/v1.2.3

tagprがタグを打った時、secrets.GITHUB_TOKEN を使っている限りはこのワークフローがトリガーされることはありませんが、手動でタグを打った場合にはこのワークフローが動くという具合です。

FUNDING.yml

FUNDING.ymlを配置してGitHub Sponsorsへの導線をつけました。ワンショットスポンサーのメニューもあるので、是非スポンサーをご検討下さい。

https://github.com/sponsors/Songmu?frequency=one-time

スポンサーのみならず、引き続きpull requestや感想や要望等お待ちしております!

Shodo校正APIの結果をpull requestのレビューコメントに自動反映する

Shodo AI校正APIのベータ版に申し込み、利用させていただいています。Shodo、日本語校正のSaaSということで注目していて、今回のAPIベータ版の話がTLに流れてきたので申し込み、当選することができました。

日本語校正サービス・ソフトウェアは商用のものの質はやはり高く、例えば老舗ジャストシステムのJust Right!などは友人のライターが利用していて、評判の良さも聞いています。

ただ、それらはいかんせん良いお値段します。もちろん、良質なサービスに対価を払うことはやぶさかではありませんが、私のようなホビーライターからするとちょっと厳しいお値段です。

ただ、私もブログは細々と継続していて、不定期で有償の記事や執筆をお受けすることもあるため、何らかの校正サービスを使いたいと思っては常々思っていました。

その点、スポットで従量課金的に利用できるSaaSモデルであるShodoは魅力的です。また、APIがあればCI/CDに組み込むのも容易です。私はGitHubで文書を管理することが多いため、そこも嬉しいポイントです。インストールライセンス型のパッケージでは実現しづらい部分でしょう。

action-shodo-lint

ということで、ShodoのAPIを利用して、pull rquestにレビューコメント及び変更提案をしてくれるGitHub Acitonsのカスタムアクションを作りました。

https://github.com/Songmu/action-shodo-lint

ブランチを切ってpull requestを作ると、デフォルトブランチとの差分があるマークダウン(.md)ファイルを校正APIにかけ、以下のような変更提案をしてくれます。

ワークフローも以下のように簡単に書けます。

name: action-shodo-lint
on: [pull_request]
jobs:
  shodo:
    name: runner / shodo
    runs-on: ubuntu-latest
    steps:
    - uses: Songmu/action-shodo-lint@main
      with:
        github_token: ${{ secrets.github_token }}
        api_token: ${{ secrets.SHODO_API_TOKEN }}
        api_root: 'https://api.shodo.ink/@{your_organization}/{your_project}/'

Shodo 校正API自体がベータであるため、このアクションも割と実験的なものにはなりますが、現在のベータ利用者はすぐにご利用いただけます。

スクリーンショットからわかる通り、内部的にはreviewdogを使っています。また、今回別で作った、Go版のShodo API CLIツールであるgoshodoも活用しています。

goshodo

https://github.com/Songmu/goshodo

公式のPython CLIがありますが、個人的にはGoのほうが早く書けるのと、とりあえずやりたいことを実現するためにGoで別のCLIを作りました。

この goshodo は公式の shodo コマンドの lint サブコマンド相当の機能のみ備え、かつ、それを拡張しています。

具体的には、複数ファイルを引数に持てるようにしたことと、-f checkstyle オプションを指定することでCheckstyle形式のXMLを標準出力に出せるようにしたところが拡張点です。

なにがしかの規格に準拠したフォーマットで出力を出せれば、他のツールと連携しやすくなります。実際、reviewdogもcheckstyle入力フォーマットを受け付けているため、とりあえずそれで出せるようにしました。

action.shを見てもらうとわかりますが、やっていることは以下の一行がほぼ全てです。

echo "$files" | xargs goshodo lint -f checkstyle | \
  reviewdog -f="checkstyle" \
    -name="shodo" \
    -reporter=github-pr-review \
...

checkstyle を選んだのは action-textlinttextlintとreviewdogの連携で使われていたため、それに倣った形です。

ただ、checkstyleフォーマットにも不満があって、具体的には指摘の開始位置を指定できるのですが、仕様上終端を指定できない点が困ります。

なので、現状action-shodo-lintでは無理やりsuggestionsを出力しているのですが、それは校正範囲とその提案が一行に収まる場合のみに限っています。

このあたりの課題感については、reviewdog repository内の以下のドキュメントにまとまっており、読み応えがあるのでおすすめです。

Reviewdog Diagnostic Format (RDFormat)

これを読んだ結論として変更提案をより良く出したい場合には、以下のフォーマットへの対応を検討すると良さそうです。

Shodo APIやCLIへのフィードバック

ベータ版を使わせて頂いている身として、この場でフィードバックを何点か書いてこの記事を終了しようと思います。

校正結果APIのレスポンスの"after"

https://github.com/zenproducts/developers.shodo.ink/blob/master/docs/api.md

レスポンスには必要な情報が網羅されていて良かったです。

一点だけ対応してほしい点として、推奨される置き換えテキストが格納されているafterフィールドに「トル」という校正指示が入るケースがあるのが気になりました。校正後の置き換えテキストは空文字列にしつつ、校正指示文言は別のフィールドに格納されている方がプログラムから扱うには嬉しいです。

また、カラム位置やインデックス番号がすべてUTF-8文字数になっているのはプロダクトの思想の問題だと思うので構わないと思います。ただ、合字などの扱いがどのようになるのかは調べていませんが、気になりました。

校正ルールのAPI管理

校正ルールを設定ファイルやAPIで管理できると、GitHubを用いたような執筆フローにより組み込みやすいと思いました。

公式CLIのlint出力

公式のCLIの出力は、デフォルトでは人間が読みやすい出力になっていて、それは問題ないと思います。

それに加えて、私がgoshodoで拡張したように、複数ファイルを受け取れるようにして、-f オプションで各種出力フォーマットを選択できるようになると嬉しいと思いました。textlintが多くの出力フォーマットに対応していて参考になります。

公式CLIのXDG_CONFIG_HOME対応

これは本当に些細な指摘で好みの話ですが、公式CLIは現状 ~/.shodo/credentials に秘匿情報を書き込みますが、近年はホームディレクトリが汚れないように、 $XDG_CONFIG_HOME/shodo, ~/.config/shodoにこういった情報を書き込むのがセオリーになってきています。特にこだわりがなければそのように変更してみるのはいかがでしょうか。

ref. https://wiki.archlinux.jp/index.php/XDG_Base_Directory

何にせよShodoのことは応援しています!

GitHubのリリースノート自動生成機能からCHANGELOG.mdを生成する

tl;dr

本題

GitHubには、リリースノートを自動生成する機能がある。これは、リリース間でマージされたpull requestのタイトルを一覧し、リリース項目としてGitHub Releases上に出力してくれるものです。リポジトリ上に.github/release.yml設定ファイルを配置すれば、pull requestの作者やラベルを元にグルーピングしたり非表示にするといった出力内容のカスタマイズもできる。

このあたりの実際の運用方法については以下のBlog記事を参考にすると良いと思うし、僕も参考にさせてもらいました。

また、r7kamuraさんの記事にも書かれていますが、このリリースノートはAPIからMarkdownを得ることもできます。そして、このAPI出力を、keep a changelog形式に近いMarkdownに変換し、CHANGELOG.mdを出力するgh2changelogというツールを作った。これはtagpr を作る中の副産物で、別ツールとして切り出したものでもあります。

https://github.com/Songmu/gh2changelog

ちなみに、keep a changelog形式は、これもまたr7kamuraさんの記事に書かれていますが、Changelogのガイドラインとして2015年頃に提案され、受け入れられてポピュラーなフォーマットになったものです。参考リンクを載せておきます。

pull requestのタイトルをChangelogに転記するコンセプト

個人的にもともと、Changelogは作りたいが、書く手間をあまり取られたくない、しかしcommit messageを一覧してChangelogするのは粒度がマチマチで見づらくなるし、後から書き換えるのも煩雑になるで避けたい。なのでpull requestのタイトルを一覧してChangelogとするのは良い落とし所だと考えていました。

実際、そのコンセプトを実現するためにghch というツールを以前作り、今でも活用しています。ありがたいことに200 starsを超え、僕のOSSの中では代表的なものになっています。

そして、冒頭に書いたGitHub公式のリリースノート生成機能は去年追加されたのですが、これも既に書いたとおり、まさにpull requestのタイトルベースでChangelogを生成するものでした。

公式がそういう機能を出してきたのであれば乗るのが正道であり、そこで作ったのがgh2changelogです。なのでghchの一部機能の後継ツールでもあります。

.github/release.ymlでのカスタマイズも嬉しい点で、keep a changelog形式で提案されているAdd, Changed, Deprecatedといったタイプ別に、ラベルを用いたカテゴリ設定を記述すれば、理想的なCHANGELOG.mdを出力できます。

ちなみに、gh2changelogは敢えてのkeep a changelog形式決め打ちの出力であり、ghchはJSON出力もできて自由度が高いツールです。コンセプトが異なるため、完全にgh2changelogghchを置き換える訳ではありません。

CHANGELOG.md欲しい?

GitHub Releasesが熟れてきたし、そっちのリリースノートに更新内容が書いてあれば、もはやCHANGELOG.mdを配置する必要は無いし、むしろ二重管理になるのも困るという意見は正しいと思います。

なので、CHANGELOG.mdを配置するというのは僕個人のちょっとしたこだわりでしかないとも思います。ただ、以下のような利点から配置するようにしています。自動生成する分には二重管理の問題も解消されているので。

ということで、私同様にChangelogが好きな方は、一度gh2changelogをご利用いただけると嬉しいです。

リリース用のpull requestを自動作成し、マージされたら自動でタグを打つtagpr

常々GitHubにtag requestが欲しいと言ってきましたが、それを実現するツールを作りました。OSSなど、バージョニングとリリースが伴うソフトウェア開発のリリースエンジニアリングをとにかく楽にしたいという動機です。既に自分が管理している幾つかのOSSでは導入して便利に利用しています。

https://github.com/Songmu/tagpr

アイデア

基本の発想は以下のようにシンプルです。

リリース用のpull requestを自動で作りマージボタンを以てリリースと為す、というのは、みんな(僕が)大好き git-pr-release と同じ発想です。git-pr-releaseではブランチをdevelopとmainに分けることでマージをリリースとしていましたが、tagpr ではmainブランチ一本で、マージコミットにタグを打つ点が異なります。

タグが打たれるので、それをトリガーにして成果物の生成などを別途行うと良いでしょう。

CHANGELOG.mdについては、GitHubのリリースノート生成機能を利用しているため、.github/release.ymlで表示が調整できます。

ref. https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes

導入

リリース時にsemver形式のtagを打つフローのリポジトリであれば、今すぐ導入できます。GitHub Actionsでの実行を前提としており、以下のワークフローを配置するだけで導入完了です。

# .github/workflows/tagpr.yml
name: tagpr
on:
  push:
    branches: ["main"]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: Songmu/tagpr@main
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

tagが打たれた後に、更に別のAcitonを動かしたいときには GITHUB_TOKEN の扱いについて気をつけるべき点があり、それは後で説明します。

実際のフロー

tagpr の実際のフローをもう少し詳細に説明すると以下のようになります。

  1. (自動) mainブランチが進むとリリース用のpull requestが作られる
    • mainブランチの先頭からブランチングされる
    • mainブランチが更に進むと自動で追随する
  2. 1で出来たブランチに必要なら更新を加える
  3. pull requestを好きなタイミングでマージする
  4. (自動) タグが打たれ、GitHub Releasesも作られる

作られるpull requestは以下のようなものです。

pull requestの内容 https://github.com/Songmu/tagpr/pull/69

作った動機

個人のOSS開発上の課題意識から。OSS開発のリリース作業は、定形作業ではあるのですが、細々した作業が発生し、案外漏れが出たり、属人的にもなりやすい。例えば以下のような作業です。

特に、リリースに必要なファイル変更は、機能開発とは関係ないちょっとした変更なのでリリース担当者がサッと済ませてしまうことも多く、案外暗黙知となります。それで、他の人が見様見真似でやると必要なファイル更新が漏れる事故などが起こりがち。

これは、ちゃんと引き継げばいいと言うよりかは、引き継がなくても恙無くリリースできる状況を作るのが理想です。

なので、これもpull requestにしてレビュー可能な状況を作るとともに、pull request及びその変更も可能な限り自動で作られ、それがマージされたらリリースされるようにしたい。それを実現したのがこのtagprというわけです。

リポジトリに訪れた人も、そのpull requestを見れば、未リリース項目が把握でき、マージすれば新バージョンがリリースされることがわかるという、見える化・透明性の向上効果もあります。

リリース作業時にリポジトリ上で追加で変更したいファイルがある点や、tagを以てリリース完了としたいという点で git-pr-release とユースケースが異なります。

pull requestのタイトルと本文

上記のスクショを見てもらうとわかるように、pull requestの本文に、"What's Changed"で始まるリリース項目が自動で一覧されるのがお役立ちです。これはGitHub標準のリリースノート生成機能を利用しています。

このタイトルと本文は、設定ファイル.tagprtagpr.template キーにGo形式のテンプレートを指定することでカスタマイズ可能です。

pull requestのcommit内容

tagprはpull request生成時に標準で以下のファイル更新を行います。

また、初回実行時に以下のファイルが存在しない場合、最小限設定済の雛形を生成します。

リリース作業時に他にも自動更新させたいプロジェクト固有の項目がある場合、tagpr.command 設定にコマンドを指定し、commit前に任意のコマンド実行ができます。

また、リリース前の変更作業を手動でおこないたい場合、pull requestのブランチを直接編集し、commitを重ねてしまって構いません。

後はこのpull requestをマージすれば、マージコミットに自動でtagが打たれます。merge commitを作る形式でもsquash mergeでも構いません。

tagと同時にGitHub Releaseも作るのですが、これをオフにするオプションも設けるかもしれません。

次バージョン指定方法

tagprのバージョンインクリメント挙動はデフォルトではpull request上で単にpatchバージョンをインクリメントするだけです。しかし、major, minorバージョンを上げたいことも当然あるでしょう。これには二つの対応方法があります。

後者は、versionfileを利用しない運用の際に採用すると良いでしょう。また、リリース対象項目のpull requestに付与されたラベル等を見て、major,minor,patchのどれをインクリメントするかを推測する機能を将来的に追加したい。

secrets.GITHUB_TOKEN が次のActionsをキックしない問題

tagprは自動でgit tagを打つところまでやってくれるため、そこから別のGitHub Actionsのワークフローをトリガーしたいこともあるでしょう。成果物の生成やアップロード等。

しかし、tagprの動作にGitHub Actionsが自動で提供してくれる secrets.GITHUB_TOKEN を利用すると、後続のワークフローは動きません。これは、ワークフローの意図しない再帰的な連続実行を防ぐための仕様です。

ref. https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow

このため、後続のワークフローをトリガーしたい場合、tagprを別の権限で実行する必要があります。以下は、secrets.GH_PAT にpersonal access tokenを指定した例です。git push --tags も別権限で行いたいので、actions/checkoutにもこのtokenを指定しています。secretsはリポジトリ設定から別途指定してください。

name: tagpr
on:
  push:
    branches:
    - main
jobs:
  tagpr:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
      with:
        token: ${{ secrets.GH_PAT }}
    - uses: Songmu/tagpr@main
      env:
        GITHUB_TOKEN: ${{ secrets.GH_PAT }}

簡単のためにpersonal access tokenの例を書きましたが、GitHub Apps等を用いてtokenを都度生成するような手法を使ったほうが安全でしょう。

まとめ

他の細かい設定等についてはドキュメントをご確認ください。長々と書きましたが導入は簡単で便利です。実際個人的に便利に使っていますし、結構頑張って作ったので使ってもらえると大変喜びます。

とはいえ主張が強いツールなので、色々適用しづらい場合もあるかとは思います。そのあたりはフィードバックがあると大変嬉しいです。

ちなみに、今回のtagprを含め、これまでOSSのリリースエンジニアリングに関わるOSSを幾つか自作したり開発に携わったりしてきたので、どうやら私はこの分野が好きなようです。

そのあたりの話を来月のGo Conference mini 2022 Autumn IN SENDAIでお話する予定です。「OSSの最適なリリースエンジニアリングを求める旅路」というタイトルです。

私は仙台に行く予定ですが、現地枠もオンライン参加枠もまだあるようなので是非お申し込みください。仙台で交流できることも楽しみにしています。

https://sendaigo.connpass.com/event/256463/

git-pr-releaseとGitHub Actionsでワンクリックデプロイを実現する

突然ですが、git-pr-releaseのなんちゃってコラボレーターである私が僭越ながら、その王道の使い方を皆様に伝授していきます。何番煎じかの記事ではありますが、現代の、特にGitHub Actions出現以降の使い方をまとめたいというのが動機です。

git-pr-releaseはGitHubを業務開発で利用している場合に便利なツールで、デフォルトブランチにマージされたpull requestをリリース項目として一覧し、それをpull request化してくれるものです。これにより以下のことが実現できます。

導入

git-pr-releaseはCIで自動実行するのが基本です。今はGitHub Actionsがあるので、なんと、以下の1枚のYAMLをGitHub上のリポジトリに配置するだけです。

# .github/workflows/git-pr-release.yaml
name: git-pr-release
on:
  push:
    branches:
    - develop
jobs:
  git-pr-release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0 # git-pr-release needs the git histories
      - uses: actions/setup-ruby@v1
        with:
          ruby-version: 3.1
      - run: gem install --no-document git-pr-release
      - run: git-pr-release --squashed
        env:
          GIT_PR_RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GIT_PR_RELEASE_BRANCH_PRODUCTION: main
          GIT_PR_RELEASE_BRANCH_STAGING: develop
          GIT_PR_RELEASE_LABELS: pr-release
          # GIT_PR_RELEASE_TEMPLATE: .github/git-pr-release.erb
          # TZ: Asia/Tokyo

もちろん環境によって多少は調整が必要です。GIT_PR_RELEASE_BRANCH_STAGING にデフォルト開発ブランチを、GIT_PR_RELEASE_BRANCH_PRODUCTION に本番ブランチ名を指定してください。このブランチについては少し後に説明します。

成功イメージ

トピックブランチがマージされるなどしてデフォルト開発ブランチのコミットが進むと、GitHub Actionsにより以下のようにデフォルトブランチから本番ブランチへのpull requestが自動で作られます。マージされたpull reqeustの一覧がリリース項目として表示されており、開発ブランチのコミットが進む度にリストは自動で更新されます。

折を見てこのpull requestをマージすれば、mainブランチのコミットが進み、それによりdeployされるという寸法です。deployフローは各自のものをお使いください。

前提とするブランチングモデル

git-pr-releaseは以下のようなブランチングモデルを前提としています。ここではmainとdevelopという名前にしていますが、名前は先に述べたように設定可能です。

メインで開発を進めるdevelopブランチと並走するmainブランチがあります。developからmainへのマージによりmainブランチのコミットが進むことがdeploymentのトリガーとなり、deployフローが走り始めることを想定しています。

mainは基本的にはdevelopからのマージでしかコミットは進みませんし、マージされた時点ではdevelopとmainの内容は等しくなります。

このブランチングモデルは、物議を醸しがちなgit flowの簡易版とも言えるもので、割とポピュラーなのではないでしょうか。developが進んだ時点でステージングやQA環境を自動デプロイしている組織も多いでしょう。

他のブランチングモデルとしては、本番ブランチ(ここではmain)を分けて作らずに、デフォルトブランチにマージされた時点で本番deployを走らせるようなフローや、トピックブランチのレビューが通った時点でそのトピックブランチを本番deployし、それが成功したらデフォルトブランチにマージするようなフローもあるでしょう。

そういったスタイルに比べて、このモデルのデリバリー速度は少し落ちるかもしれませんが、ステージング環境が用意できること、リリースのタイミングを制御できること、複数のフィーチャーブランチが近いタイミングでマージされた時にdeployが混雑せずリリースフローを直列化できるところなどにメリットがあるでしょう。

GitHub Actions用のYAMLの解説

冒頭のYAML上のいくつかのポイントについて解説していきます。

shallow cloneとgitの履歴

git-pr-releaseはリリース項目の抽出のためにgit logを使います。なのでshallow cloneで履歴が少ないケースでは上手く動きません。ですので、clone時にshallow cloneを防ぐ設定が必要です。

- uses: actions/checkout@v3
  with:
    fetch-depth: 0 # git-pr-release needs the git histories

実は、shallow clone時に自動でunshallowする挙動 が近々リリースされるので、この設定せずとも動くようになりますが、余計なfetchが減らせるので、この設定を入れるメリットはまだほんの少しだけあります。

squash mergeのための--squashed オプション指定

pull requestのmergeにsquash mergeを使っている場合、--squashed オプションを指定してください。

squashしないプロジェクトではこのオプションを付ける必要はありませんが、付けても多少APIリクエストが増える可能性があるだけなので、常につけて大きな損はないでしょう。

ちなみに、開発ブランチへのコミットがすべてマージコミットである場合には--squashed オプションによる追加のAPIリクエストは一切発生しません。なので、このオプションを標準挙動にしてしまっても良いのではないかとも思っていますが、そのあたりはメンテナ陣と議論が必要ですね。

pull requestへのラベル自動付与

GIT_PR_RELEASE_LABALS 環境変数にpull requestに自動付与するラベルを設定できます。カンマ区切りで複数指定も可能です。これは後でリリースを抽出して振り返りたい場合等に便利です。あまり言及されていることは少ないのですが、設定することをおすすめします。

TZ環境変数によるタイムゾーン設定

標準ではpull requestのタイトルに時刻が表示されますが、GitHub Actions上で実行する場合UTC表示になります。開発チームにわかりやすいタイムゾーンで時刻表示したい場合、TZ環境変数を設定すればお手軽に表示を変更できます。

JSTで表示したい場合GitHub ActionsのYAMLの適切な箇所に以下のようなenv設定書くことになるでしょう。

TZ: Asia/Tokyo

独自テンプレートの活用

pull requestのタイトル及び本文はテンプレートでカスタマイズできます。GIT_PR_RELEASE_TEMPLATE 環境変数でファイルパスを指定します。erb形式のテンプレートで、デフォルトの内容は以下です。一行目がpull requestのタイトルになり二行目以降が本文になります。

Release <%= Time.now %>
<% pull_requests.each do |pr| -%>
<%=  pr.to_checklist_item %>
<% end -%>

最近の自分のユースケースだと、リリース方法、手順、ロールバック方法等を冒頭に載せているのと、mergeされたpull requestをそれぞれチェックリストとして表示する必要性は感じなくなったので、以下のようにしています。

## How to deploy
...

## Items to be Released
<% pull_requests.each do |pr| -%>
- #<%=  pr.number %> <%= pr.mention %>
<% end -%>

テンプレートが意図通り出力されるかどうかの確認には、手元で git-pr-release --dry-run を実行すると良いでしょう。

落ち穂拾い

マージボタンに対するワークフロー

個人的にはあまり厳密にやらないのですが、git-pr-releaseが作成したpull requestのマージを、特定メンバーのレビューや何らかのチェックが通るまではブロックしたいこともあるでしょう。そういった場合はBranch ProtectionやCODEOWNERS機能を活用すると良いです。

リリース障害時の切り戻しはどうするの?

リリースしたコードに不具合があり、手動で切り戻しが必要になった時にどうするか。

ロールバックが作り込まれていればそれを使えばよいですが、個人的には本番ブランチを変更してcommitを進めてリリースしてしまうのがお手軽だと思っています。具体的にはgit-pr-releaseのpull requestをrevertしたりhotfixを入れるなどする。

「本番ブランチのcommitが進めば新しいリリースが作られてdeployが始まる」というメンタルモデルに寄せてしまったほうが覚えることや混乱が少ないというのが理由。障害時に普段使わない不慣れなロールバック作業をすると余計なミスが生まれるリスクも高まります。

もちろん、直前に稼働していたコンテナイメージなどの成果物を再利用できたほうが切り戻しが早いというのはあります。そのためにロールバックを整えるのも手ですが、例えば、成果物を特定するためのidentityにコミットハッシュを使うのではなく、git rev-parse HEAD^{tree} でツリーオブジェクトのハッシュ値を使えば、revert commitでも直前の成果物をつかってくれる仕組みを組むことが可能でしょう。

参考: 同じソースツリーでテストが通っていたらテストをスキップする

さて、本番ブランチにrevertやhotfixを入れると、開発ブランチと本番ブランチの内容に食い違いが出てしまいます。そういった時にどうするか。

本番ブランチと開発ブランチの食い違い問題

結論を書くと、普段とは逆向きに、本番ブランチから開発ブランチに向けてmergeしてしまえば良いです。

git-pr-releaseは前述の通り、本番ブランチと開発ブランチの内容はマージ時点で同一になることを想定しています。そして、通常時は開発ブランチから本番ブランチへのマージでしか本番ブランチのコミットは進まないため、それがほぼ担保されます。

しかし、前項のような緊急オペレーションを行なったなどの理由で乖離が発生する可能性があります。こういったときは、気持ち悪く感じるかもしれませんが、本番ブランチから開発ブランチに、通常とは逆向きのマージをしてしまえば大体問題ないです。ここはちょっと手作業が必要になってしまいますが。

それで問題が起きたことは、これまで10年近く複数プロジェクトに渡って長らく使ってきましたが、特にありません。いざとなったら本番ブランチを消すなりリネームして新たに開発ブランチから作り直しても良いと思います。また、厳密には本番ブランチと開発ブランチに乖離が無いかのチェックを簡単なスクリプトでCIしても良いのかもしれませんが、やったことはなく、困ったこともありません。

設定ファイル .git-pr-release

git-pr-releaseには設定ファイルがあります。リポジトリルートに .git-pr-release というファイル名で配置して、git-config形式で設定を記述します。

ここに書ける設定はすべてここまでで解説した GIT_PR_RELEASE_* 環境変数で代用することができます。特にこの記事ではGitHub ActionsのYAML一枚でわかりやすく完結させるために、このファイルは使っていません。

ただ、手元で git-pr-release を動かす場合、具体的にはテンプレートの動作確認のために --dry-run をつけて動かしたいなどの場合、設定ファイルがあったほうが都合が良いこともあります。

また、リポジトリルートにこのファイルがあることで、git-pr-release を利用していることがわかりやすいという話もあります。ただ、近年はリポジトリルートにファイルが散乱しがちなので、逆に置きたくないという向きもあるでしょう。

カスタムアクション化しないの?

メンテナンス体制との兼ね合いで、メインメンテナ(@onk)的に提供する気は無いようです。

実際、冒頭のYAMLを見てもらえれば分かりますが、やってることは gem install git-pr-release && git-pr-release だけなので、いちいちカスタムアクションをかぶせるのも大袈裟とも言えます。

有志の方でカスタムアクションを作られてる方もいて、僕もそれを利用していたこともありましたが、最新機能を使いたくなったこともあり、最近はそれらを使わない素朴な形になりました。

都度インストールすると、オーバーヘッドやRubyGemsのダウンリスクなどが気になるかもしれませんが、gem install git-pr-release の所要時間は2秒程度ですし、そういった些細な事象を気にして複雑度を上げる必要もないと考えます。

Go化などは考えないのか?

git-pr-release はRuby製のツールですが、オリジナル作者の @motemen や私は最近はGo Hackerでもあります。なので、私も個人的にGoで作り直すことを考えたこともありましたが、必要ないと思うようになりました。

Rubyによる実行オーバーヘッドは些細なものです。それに、言語自体のパフォーマンスよりもリモートリポジトリやAPIへのアクセスが実行時間に対して支配的です。それに、git-pr-release 自体の実行時間は差分の量にもよりますが精々10秒程度です。そこが開発スピードの全体ボトルネックになることは考えづらいでしょう。

erbでテンプレートが書ける点もRuby製ならではの嬉しさと言えます。

git-pr-release は長年使われており、メンテナンスもされ、より安定して使いやすくなっていっています。例えば、squash merge関連の機能が整備されたこと等が、ここ一年の地味に嬉しい機能拡張として挙げられます。そのあたりも後発ツールに比べた強みがあるので、是非ご利用ください。(ポジショントーク)

--no-fetch オプションは現状全く意味がない

余談ですが、 --no-fetch というオプションがあるのですが、これは現状全く動いていません。このオプションを指定してもなんと何も動作が変わらないのです。

これについては、以下のissueで過去の経緯や、今後どうするかを議論しているので興味がある方は御覧ください。

https://github.com/x-motemen/git-pr-release/pull/33

Shokzの骨伝導イヤホンは会議でも運動でも入浴でもマルチに使えて快適

Amazonプライムデー開催中ですし、Shokzの宣伝エントリでも書きます。Shokzの骨伝導イヤホンはこれまで4本自分で購入していましたが、4月にありがたいことにShokz様からOpenRun Proを頂き、宣伝ツイートもさせていただいたので、この先を読みすすめる場合、そういう利害関係がありPR的な側面が出てしまうことをご承知おきください。

この後長々書きますが、まずは試してみたいという方は、エントリーモデルを一つ買うと良いでしょう。マイクが必要な会議でもこれで十分です。

ちなみに、Shokz製品との出会いは多くのWebエンジニアがそうであるように、例によってlestrratさんのエントリです。一度使ってみたら様々なシーンでメリットがあることが分かりました。まとめてみると以下のような点です。

在宅勤務で

一日中つけっぱなしでもストレスがないのは大きい。メガネと似たような感じ。耳も塞がないので圧迫感が不快になったり、周りの音が聞こえなくてふと不安になることもなく、実際家族の様子も分かります。

Web会議もブームマイクついてないモデルでもちゃんと通話できます。たまに自分の声を録音して聞くこともありますが問題を感じたことはありません。

ただ、モデルにもよりますが連続稼働時間が6~10時間なので、本当に一日中連続で利用していると電池切れすることはあります。ISUCON参加したときは電池切れしました。会議の多い人はこまめに充電が必要でしょう。ちなみにブームマイクがついたモデルのOpenCommは16時間もつようです。

音楽に関してはそれなりですが全然聞けるレベルです。耳栓をつければ音の響きなどもより感じられるようになります。ただ、没頭して作業したい場合にはノイズキャンセリングのオーバーイヤーヘッドフォンをつけています。

ちなみに、今はフルリモート勤務ですが、前職でオフィスによく行っていた頃も、周りの音が聞こえるので重宝していました。

ちなみに、macOSのMontereyをお使いの場合、Web会議中に突然ミュートになるという事象が発生することがあるようなので、その場合は以下のエントリを参考にしてください。

Montereyと(After)Shokzの相性問題とその解決方法

運動や散歩や家事で

Shokz製品には耳栓が付属しています。これがエポックメイキングで、耳栓をつけると周りの音が大きくても快適に音楽や音声メディアを聞けます。これはカナル型のようなメリットも享受できると言えますし、イヤーチップと違って耳栓は自分にあったものを安価に用意できる点も優位点です。

水仕事や部屋でローラーを漕ぐときに重宝していて、少し音がする場合であっても、耳栓をつければ快適にコンテンツを楽しめます。外出時も電車などでは助かります。歩いているや外で運動している時に両耳を塞いでいると危ないので、つけないか、片耳だけつけるなどしています。

風呂で

防水性能が高いので風呂で気にせず使っています。特に、現旧ハイエンドのOpenRun ProやAeropexはマグネット式の充電で水が入り込みそうな穴が空いていないのも安心感があります。入浴しながらpodcastやaudibleが聞けるのはかなり良い体験です。

ただ、カタログ上のスペックの防水性能は旧ハイエンドのAeropexが一番高く(IP67)、OpenRun Proは一段落ちて(IP55)、他の製品と横並びです。なので、OpenRun Proは石鹸等がついてしまったりするとちょっと良くないのかもしれないのですが、そこまで神経質にならずに使っていて、問題にもなっていません。

私の使い分け

マルチポイントに対応していて、一本で2台接続先を設定できますが、2台では足りないので以下のように使い分けています。

それとは別に妻も一本つかっています。

マルチポイントに関しては、Macbook ProとiPhoneを頻繁に切り替えて使う場合でも、新フラッグシップのOpenRun Proでは問題なく使えています。またOpenRun Proの場合アプリで簡単にマルチポイントの設定ができる点も良かったです。旧モデルではマルチポイントは少し不安定になることがありました。

充電ケーブル

Aeropex, OpenRun Proはマグネット式の独自の充電ケーブルで、OpenMoveはUSB Type-Cでの充電です。このあたりは好みがあるでしょう。ただ、マグネット式の充電のモデルの場合にケーブルが2本付属してくるのは抜かりない配慮だと感じました。私は1本はデスクに置き、1本持ち歩き用のケースに忍ばせています。

既に述べたとおりマグネット式の場合、防水性能が高そうなのが安心ポイントです。なので上位モデルは軒並みマグネット式です。

メガネやマスクとの干渉について

最近は外出時にマスクも必要なので、耳の上がどうしても混雑してしまうのは事実です。ただ、つけてる間はそれほどストレスは感じません。ただ、つけ外しする時に絡まってめんどくさかったりするので、最近たまに外出時にコンタクトレンズをつけるようになりました。

まとめ

ということで非常に快適に使っているのおすすめです。以下に私が使っているモデルのリンクを貼ります。

また、ブームマイク付きのOpenCommはどれくらい会議で違うのかは少し気になっています。長時間バッテリーがもつのも魅力です。また、既存モデルは仰向けに寝た時にツルが少し気になってしまうので、OpenRun Miniも少し気になっています。気が向いたら購入してまたエントリを書くかもしれません。

GitHub Actionsのmatrixを動的に生成してGoの最新安定バージョンでテストする

Goのライブラリを提供している場合、Goの最新の安定バージョンでテストしたくなることがあるでしょう。具体的にはマイナーバージョンの直近2バージョン、今だと1.18と1.17です。GitHub Actions定義への記述は以下のようになるでしょう。

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        go-version: ['1.17', '1.18']
    steps:
    - uses: actions/setup-go@v3
      with:
        go-version: ${{ matrix.go-version }}
    - run: go test ./...

しかしこのようにベタに書いてしまうと、Goのバージョンが上がったときにチマチマ上げるのが地味にめんどくさい。なのでこれを動的に生成したい。

これは事前にGoの安定バージョン一覧を取得するjobを走らせ、その出力からmatrix用のリストを生成すれば実現できます。具体的には以下のようにした。

jobs:
  go-versions:
    runs-on: ubuntu-latest
    outputs:
      versions: ${{ steps.versions.outputs.value }}
    steps:
      - id: versions
        run: |
          versions=$(curl -s 'https://go.dev/dl/?mode=json' | jq -c 'map(.version[2:])')
          echo "::set-output name=value::${versions}"
  test:
    needs: go-versions
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        go-version: ${{fromJson(needs.go-versions.outputs.versions)}}
    steps:
    - uses: actions/setup-go@v3
      with:
        go-version: ${{ matrix.go-version }}
    - run: go test ./...

go-versions というジョブで https://go.dev/dl/?mode=json からGoの安定バージョン一覧を取得してそれをjqで加工し、GitHub Actionsのset-output で出力を作っている。

% curl -s 'https://go.dev/dl/?mode=json' | jq -c 'map(.version[2:])'
["1.18.2","1.17.10"]

その出力を test ジョブの中で使っているという具合。test.needsgo-versions を依存として記述している。

ref. https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter

実際に拙作の prompter というライブラリに 導入してみた がちゃんと動いている。

本当はカスタムアクションを提供できると嬉しいと思ったのだが、一旦このような形でささっとやってみた。

https://go.dev/dl/?mode=json は決め打ちで最新安定のマイナー2バージョンしか返さない。それで多くの場合は十分だろうが、例えば3バージョンテストしたいとか、次バージョンのRCが出たらそれも早めにテストしたいなどのニーズもあろうかと思う。

その場合は、 https://go.dev/dl/?mode=json&include=all から一覧を取得してテストしたいバージョンを抜き出せると嬉しそうである。

そういった機構も含めたカスタムアクションがあると良さそうなので、僕もそのうち気が向いたらやるかも知れないが、誰かチャレンジしてくれると嬉しい。