Diary of a Perpetual Student

Perpetual Student: A person who remains at university far beyond the normal period

Windows の OS 名をレジストリから取得するのはやめよう

3行

  • Windows の OS 名をレジストリから取得すると、Win 11 が Win 10 として表示されてしまう
  • 例えば、WMI を使って取得する方法が正攻法っぽい
  • Go から WMI を触るサンプルコードもあるよ

Win 10 が Win 11 として Mackerel に登録されている

自分は OSS の動作確認のために Windows を使いたくなったときに、手元の Macbook から別のホストにリモートデスクトップで繋いで使っている*1。また、繋ぐ前に WOL マジックパケットを送ってスタンバイ状態から復帰させるようにしている。

ここで、手元にマシンがない状態だと WOL によりちゃんと起動したかどうかがわからない。そこで、mackerel-agent をインストールしていて、Mackerel 上でメトリックが取れているかどうかでマシンの生存確認をするようにしている。

そんな中、ふと気づいた。ホスト OS は Windows 11 のはずなのに、Mackerel の Web コンソールには Windows 10 として表示されていることに。

Windows なんもわからん……と思いながら、詳しい人が教えてくれたら良いなという楽観的発想でまず issue を立てた。

github.com

mackerel-agent の実装はどうなっているのか

mackerel-agent が Windows の OS 名やバージョンを取得している実装は以下の部分である。

https://github.com/mackerelio/mackerel-agent/blob/7417ef2be05e88c815ddc8a4002ba32a6b74d16c/spec/windows/kernel.go#L25-L44

osname, _, err := windows.RegGetString(
        windows.HKEY_LOCAL_MACHINE, registryKey, `ProductName`)

これらの情報はレジストリから取得していて、例えば OS 名であれば HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\ProductName から取得した値を利用している。

つまり、先ほどの不具合は、OS が Windows 11 でもこのレジストリの値が依然として "Windows 10" であることによるものだった。

この問題については Windows の Q&A フォーラムでもスレッドが立っていた。

learn.microsoft.com

Windows 10 は最後の OS だ」と言い切ってしまったが故にこのような混沌が生み出されたのかもしれない。

どう直すか

レジストリを使った取得方法では正しい OS 名を取得できないため、実装を改めなければならない。改修方針案はいくつかあった:

  • OS 名は、レジストリ ProductName と CurrentVersion の両者から判定する
    • つまり、ProductName が Windows 10 で CurrentVersion が m.n.o 以上だったら Windows 11 とする、といった感じ
  • PowerShell のコマンド Get-Computerinfo を実行し、パースする
    • ConvertTo-Json コマンドも利用すれば program-friendly な JSON 形式で OS 名を取得可能
  • WMI の Win32_OperatingSystem クラスにある Caption というプロパティ値を OS 名とする

レジストリによる取得の延長的な改修に関しては、mackerel-agent の該当実装部分にやたらと分岐が増えて負債となり得るので、避けたいと思った。Windows 11 がこんな状況では、Windows 12 以降どうなるか先が思いやられる。仕様が現実に追いついていない不安定なものに依存したくはない。

PowerShell のコマンドを実行する方式もぱっと見悪くはないと思ったのだが、チーム内で話したところできれば避けたい、という結論になった。これは、PoweShell の動作が重いこと、ConvertTo-Json を利用できない OS がありそうなことなどによる。

そこで、3つ目に挙げた WMI (Windows Management Instrumentation) を利用する手法に着目した。WMI とは Windows OS を管理・監視するために作られたインタフェースのようだ。WQL という SQL-like な言語を使ってシステム情報を問い合わせることができる。

Win32_OperatingSystem クラスの中に Caption というプロパティがあり、「オペレーティング システムのバージョン」が含まれているようだ。試しに Windows 11 のホストで値を取得すると"Microsoft Windows 11 Pro" と結果が返ってくる。これこそが求めていた文字列である。

learn.microsoft.com

Go のプログラムから WMI に問い合わせる

ここまでできたら、あとは mackerel-agent から WMI というインタフェースをどう触るか、ということを考えれば良い。mackerel-agent は Go 言語で開発されているので、この要件は「Go のプログラムから」と読み替えてもよくなる。

実は mackerel-agent には、すでに go の WMI ライブラリ github.com/StackExchange/wmi が利用されている。しかし、このリポジトリを見るともうメンテされていないようなので、 fork 先の github.com/yusufpapurcu/wmi を使うことにする。

こうして、自分で実験的なアプリケーションを作ってみた。

github.com

//go:build windows

package windows

import "github.com/yusufpapurcu/wmi"

// cf.) https://learn.microsoft.com/windows/win32/cimwin32prov/win32-operatingsystem
type Win32_OperatingSystem struct {
    Caption    string
    Version    string
    CSDVersion string
}

type WinVer struct {
    OSName  string
    Version string
    Release string
}

func GetWinVer() (*WinVer, error) {
    var dst []Win32_OperatingSystem
    q := wmi.CreateQuery(&dst, "")
    if err := wmi.Query(q, &dst); err != nil {
        return nil, err
    }
    winVer := WinVer{}
    for _, v := range dst {
        winVer.OSName = v.Caption
        winVer.Version = v.Version
        winVer.Release = v.CSDVersion
        break //nolint
    }
    return &winVer, nil
}

今回利用した wmi ライブラリで特徴的なのが、結果を格納するために作る構造体の構造からそのまま WQL クエリが作られるところだ。つまり、Win32_OperatingSystem という構造体の名前やフィールド名を任意に変えることはできない。

GitHub Actions に Windows の runner があるので、これを利用してテストを動かしてみた。OSName が Microsoft Windows Server 2019 Datacenter として取得できていることが確認できる。

mackerel-agent にこれを組み込むにはまだ考えることがあるので未着手だが、近いうちに取り組もうと思う。

(2022-02-03 追記) 上記方針での改修が取り込まれた mackerel-agent v0.75.0 がリリースされました

まとめ

Windows の OS 名を取得したい時には、レジストリを参照するのではなく WMI を利用しよう。もしくは、「もっと良い方法があるよ」という有益情報をお持ちのWindows に詳しい方はぜひ教えていただきたい。

*1:そのために家のネットワークに VPN で繋げるようにしてあるし、OS も Windows Pro をわざわざ買っている。

next-gen Slack App で AWS のサービスと接続するとき気をつけたいこと3選

年末から Slack App の次世代プラットフォームを試してみています。現在はオープンベータ版となっており、有料の Slack ワークスペースでしか利用できません。

api.slack.com

次世代プラットフォームはこれまでの Slack App を取り巻くエコシステムと大きく変わっています。全部は紹介しきれないのですが、これまでと変わったところを挙げておきます:

  • Slack App を動かすためのインフラを Slack 側が用意してくれる
  • 開発用にローカルにサーバを立てて、Slack のアプリから参照し動作確認できる
  • アプリの設定(この読み取り権限が必要、など)を TypeScript のコードとして表現できる
  • CLI が用意されていて、これを叩いてデプロイなどが可能

試しに作ったもの

今回はお試しとして「ask-anywhere」という Slack App を next-gen platform を利用して作りました。

はてなの Slack ワークスペースには「#ask-hogehoge」という形でいろんな部署に問い合わせることができるチャンネル群があります。ここで、例えば、総務のチャンネルで質問したら、実は経理部門管轄の話で別のチャンネルに案内された、という様子をよく見かけます。

質問を投げるとどの窓口チャンネルに書けば良いか教えてくれるインフォメーションセンターのような Slack App があれば便利かなあと思って作ってみました。動作イメージは以下の通りです:

また、自然言語で入力された質問の解釈には Amazon Lex を利用しました。Alexa と同じ技術を使って、テキストや音声での会話を行う Bot を簡単に作れるサービスです。

aws.amazon.com

構成図は以下の通りになります:

next-gen platform の Slack App で気をつけたいこと

Slack のインフラ上で完結できれば簡単だったのですが、AWS のサービスとの接続を考えると面倒なことがいくつかあったので紹介します。

OIDC できない

GitHub Actions から AWS のリソースにアクセスするときにはアクセスキーの漏洩を嫌って OIDC 認証するのが一般的だと思います。

zenn.dev

これと同様のことを next-gen platform でもやりたかったのですが、残念ながら今はできません。Lex bot との会話をするための IAM ユーザを作り、アクセスキーとシークレットを発行する必要があります。

また、こういった秘匿情報は App にハードコーディングせず、環境変数に格納することが推奨されています。

api.slack.com

deno の公式 aws-sdk がない

next-gen Slack App のランタイムは現在 deno しか選べません。

Amazon Lex とやりとりするために aws-sdk を利用したいのですが、現在 deno 用の公式 aws-sdk は用意されていません。issue は立っているものの Open の状態です。

仕方ないので非公式のものを利用します。今回の実装では、先ほどの issue の discussion 中で紹介されていた aws_api for Deno を利用しました。

import { ApiFactory } from "https://deno.land/x/aws_api@v0.6.0/client/mod.ts";
import { LexRuntimeV2 } from "https://aws-api.deno.dev/v0.3/services/lexruntimev2.ts?docs=full";

...

const api = new ApiFactory({
  credentials: {
    awsAccessKeyId: env["aws_access_key_id"],
    awsSecretKey: env["aws_secret_access_key"],
  },
  region: "ap-northeast-1",
});
const lex = api.makeNew(LexRuntimeV2);
// lex bot に入力したテキストを認識させる
const response = await lex.recognizeText({
  ...
});

外部との通信がそのままではできない

deno はデフォルトの設定だとネットワークアクセスができないことをご存知でしょうか?deno で動く Slack App でも同じようなことが言えて、外部にアクセスする際には許可するドメインを manifest.ts ファイルの outgoingDomains に記す必要があります。

export default Manifest({
  ...
  outgoingDomains: [
    "aws-api.deno.dev",
    "runtime-v2-lex.ap-northeast-1.amazonaws.com",
  ],
  ...
});

sdk による通信など、どのドメインとやりとりしているか自分で分からない場合は、エラーログを見てちまちまとリストに足していけば良いです。例えば、以下のようなエラーが出たら outgoingDomains に aws-api.deno.dev を足す、という感じです。

slack next platform Requires net access to "aws-api.deno.dev", run again with the --allow-net flag

まとめ

next-gen platform の Slack App は(有料ワークスペース限定ですが)現在無料で利用できます。自前でバックエンドの Lambda や ECS を立てたりする必要がないので、インフラ面に関しては気が楽ですね。

一方で、ユースケースによってはやっぱり他のクラウドサービスとも接続したいと思うこともあるでしょう。そんな時には本記事が参考になれば幸いです。

ベータ版のうちに遊ぶと色々面白いと思うので、ぜひ次世代プラットフォームの波に乗っていきましょう。

選択は多目的最適化の重みづけによって説明されたい

軽音楽部の学生のシールドケーブルの巻き方がやけに小さかったのを見て小言を言ったという記録。


ケーブルというのは円柱の形をしている。

(ここでプリングルスのパッケージを手に取り、ひねる。)

円柱を曲げるには無理やり力を入れなければならない上、このように皺が寄ってしまう。

これと同様に、巻くという行為によって少なからずケーブルへの負担がかかる。

すなわち、ケーブルをまっすぐ伸ばしたまま保管するのが、ケーブルへのダメージを限りなく抑える方法として最も良いということになる。

しかし、実際問題そうやって保管している人はほぼいない。そのままでは取り回しにくいという理由もあるが、こうすると保管に必要なスペースが大きくなってしまうという問題がある。

保管に必要なスペースを限りなく小さくしようとしたときに、皆さんがやっているように半径を小さく小さくして巻く、という手法が考えられる。

つまり、保管スペースを小さくしたいというのと、ケーブルに負荷をかけたくないというのはトレードオフの関係にある。

(面倒なのでとりあえずリニアな関係ということにしておく、本来は入れ物に入らないというのはあってはならないことなので制約を指示関数化したものになるはずだし、負荷がリニアなこともないだろう。)

この2つの指標を共に、両者同じぐらいの重要度だということにして最小化しようとすると、直感的には「入れ物に入るギリギリの大きさで巻く」のが最も良いということになる。

本当は持ちやすさとかもっと複雑に考えなければならない軸はあるが、まずはこの2つがトレードオフの関係にあるということを意識してケーブルを巻いてみたらどうだろうか。


こういった話を(もう少し簡単にして)高校生にした。

僕は単一原因論的に物事を決めるような大人になって欲しくないと常々思っている。世の中の言説は、ある点が満たされないからこの主張は間違っている、という雑な否定に満たされている。

多角的に物事を見た上で、最終的には軸ごとの重みづけをどうするか、という議論に帰着すると思う。しかし、ある人にとってその重みのパラメータは完全に固定してしまっている。(もしくは、ある軸の評価関数が完全に制約と化してしまっている。)ある軸に全振りしていることに気づいていないのだ。

いかにそういった仮定・思い込みから一歩引けるかというのを、普段の仕事でも考えている。一方で、そういった思考を他人に促すというのはとても難しい。

技術的なアウトプットに疲弊したので振り返る

2022年は技術的なエントリをそれなりの数書いてきた。特に12月は計10本のエントリを出し、まだ世に出せていないネタもたくさんあるという状況である。

ところが、当人の感覚としては暖簾に腕押しだなあと思っている。ということでちゃんと振り返ってみることにする。

stats

2022年で一番ブクマが多かった記事はblog.arthur1.devである。

他の記事はこの半数にも満たない、というか、片手で数えられるブクマ数でさえありがたい、という状況である。

書くという行為によって自分自身の理解が確かなものになることに意義は感じているものの、せっかく書いているのだから多くの人に読んでほしいし、それが読者のためになればなお良いと思っている。

もっとシビアに言ってしまうと、書いても読んでもらえない状況がずっと続くならいつか自分は筆を取るのをやめてしまうだろうという懸念がある。モチベーション維持のために今のうちに何とかしたい。

この現況についてはいろんな仮説を立てている。

仮説

ボトムアップで描かれた記事はとっつきにくい?

自分は普段何かを説明するときに、全てを理解してもらうために0から話し始めることが多い。これは論文書きの仕草から来ている。論理の飛躍があった瞬間にその論述の信頼性は大きく下がってしまう。

どんどん事実が提示され最終的に主題に辿り着いていくスタイルだと、文章が長くなり、読み物として読みづらいものになるのかなと思う。

小学生のうちは円周率を魔法の数字 3.14 として考えるように、fundamental な部分の厳密性をあえて欠く、という選択肢も考えるべきである。一番伝えたいメッセージを決めた上でメリハリをつけて説明量を調節したい。他の文献に譲る、記事を複数に分ける、という素朴な工夫ももっとできるはずだ。

ネタがニッチすぎる?

選んでいるネタがニッチすぎて需要がないのかなとも思う。前項と関連して、細かすぎるネタを選んでいるからこそその説明にかかる時間が長くなってしまい、ボトムアップで説明されたむやみに重厚な、でも需要に乏しい記事ができてしまうのかもしれない。

逆に、自分が当たり前のように知っていた知識を他の人がエントリにするとバズっている、という経験が何度かあった。哺乳類の自分にとっては「陸上での呼吸の仕方」を共有してもしょうがないと思っているのだが、実は地球には魚類もたくさんいて「知らなかった、すごい!」という反応をする、といった具合である。

現在あるセミナーへの登壇準備を進めていて話す内容を考えているところなのだが、こういう仮説を立てている自分として一般受けしそうなソフトスキル寄りの話に逃げたい、というバイアスが掛かるようになってしまった。

掴みが弱い?

エントリを読む/読まないを選択するときに、以下の情報を参考にするだろう:

  • タイトル
  • ogp 画像
  • 本文の最初の数行

ここでのアピール力が足りないのではと思っている。

タイトルに関してはここ最近ある程度気を使っていて、人間の感性に添った表現を心がけている。もちろん、昨今の YouTube のようなサムネ詐欺はやりたくないという美学はある。

存在感がない?

自分の Web エンジニアリング界隈での存在感は 0 に近い。社のエンジニアたちを見ているとそのあたりすごいなと思う。

現在の Web というプラットフォームはスケールフリーなネットワーク(次数の分布が指数関数)になっていて、ごく少数のノードが大きな影響力を持っている。これが Memex 構想を提唱した Vannever Bush が望んだ未来であろうか(反語)と思うこともある。Web が好きだからこそ、 hypertext の一実装でありながら、文書自体の関連性(つまりエッジの中身)よりもネットワークの構造自体が支配的に働いたり、メディアの持ち主が消してしまうと有益な情報が失われてしまったりする Web を憎んでいる。

愚痴はさておき、世の中を変えるほどの力はないのでなんとか順応して食い込んでいくしかない。今のアウトプットを地道に続けたらうまくいく、という単純なものではないことだけはわかっている。

2023年のアウトプット

ということで、2023年は以下のアクションを取ってみることにする。

  • もっと他人の記事を読む
    • 内容だけでなく、自分が面白いと思った記事の伝え方を観察する
  • 全てを自分が説明せず、適切に他の記事に譲る
    • このエントリが役に立った、という情報自体が有益であるはずだ

続き:

blog.arthur1.dev

ent + gqlgen で怠惰に GraphQL API を作る

この記事ははてなエンジニア Advent Calendar 2022 の 2023年1月4日の記事です。


Go 言語の ORM である ent をご存知でしょうか? ent はコード生成を用いて型安全に RDB のクエリを記述できる ORM です。エンティティ間の関係を Edge として定義するのが特色となっており、その結果、簡単にグラフ構造データのスキーマモデリングができます。

entgo.io

そんな ent には GraphQL インテグレーションという機能があります。これを使うと、ent のスキーマ定義を用いて簡単に RDB をデータストアとした GraphQL API サーバを作れるのでご紹介します。

ent GraphQL インテグレーションの仕組み

Go 言語で GraphQL API を開発するとき、最初に名前が上がるのが gqlgen というライブラリでしょう。GraphQL のスキーマファイルを食わせてやると、あとは resolver を手で書くだけの状態になった GraphQL API のコードが生成される、というものです。

gqlgen.com

ent によるコード生成時に SchemaGenerator というものを用いると、同時に GraphQL の スキーマファイル(データやクエリの型情報が入っている)を書き出すことができます。このファイルを gqlgen に食わせてやると、 resolver の雛形が生成されます。この未実装部分に ent で生成した CRUD API を利用して実装を埋め込んでいきます。すると、あっという間に GraphQL API の出来上がりです。

// Decks is the resolver for the decks field.
func (r *queryResolver) Decks(ctx context.Context) ([]*ent.Deck, error) {
    panic(fmt.Errorf("not implemented: Decks - decks"))
    // ここの panic を消して、代わりに ent で生成される ORM Client を使った実装を書く
    // return r.Client.Deck.Query().All(ctx)
}

雑多な説明になってしまいましたが、簡単にいうと以下のコーディングだけで GraphQL API が作れます。便利

  • ent のスキーマ定義ファイル
  • ent が吐き出した CRUD API を利用した Resolver の実装(関数を呼び出して return するだけ)

登場人物と関係図

以下のように generate.go を用意すると、この一連のコード生成を go generate . 一発で行えます。

//go:generate go run -mod=mod ./ent/entc.go
//go:generate go run -mod=mod github.com/99designs/gqlgen

参考

詳しい説明は今回はしません。気が向いたら手順をエントリにまとめていこうと思います。

自分が参考にしたのは以下のドキュメントです。

entgo.io

entgo.io

感想

id:arthur-1 の個人開発プロジェクトに活用してみたので、使ってみた感想を述べていきます。

github.com

よかったこと

レールに従っているだけで、DB のスキーマ、ORM のクライアント、そして GraphQL API が簡単に作れるのはとてもよかったです。

GraphQL API が手軽にできてしまうと、フロントエンドの実装に集中することができます。もともとこのアーキテクチャが「面倒な実装は全部サーバサイドにやらせよう」という雰囲気のものですが、その面倒な部分を ent に丸投げできてしまいます。

さらに、できた GraphQL API がオモチャではないのも良いです。Cursor 形式のページネーションにも対応していたり、dataloader を自前で実装しなくても n+1 問題を自然と回避できたりするので、production として使う上で気になるパフォーマンス上の懸念もあまりなさそうです。

0 からサービスを作り上げたいときに GraphQL を活用したいと考えている場合、手早くスタートする方法としてピッタリではないかと思います。

よくなかったこと

これは ent の GraphQL インテグデーションというより ent 本体の問題なのですが、生成されるデータベースのテーブル定義が「マジかよ」という感じでした。

同じ型のエンティティ間の多対多の関係を定義した時に、その関係を保持する中間テーブルのカラム名が、本来想定されたものと入れ替わってしまうという不具合があります。

ent が生成する CRUD API を通してやりとりする場合には問題ありませんが、データベースに初期データを入れてテストしたいときなどに、カラム名が入れ替わっていることに気をつけなければなりません。

この問題は既知ですが、今だに解決されていません。

github.com

migration に atlas を使うと直るかも?

まとめ

GraphQL のエコシステムに乗っかっていくと、リクエストの数やサイズを減らしたり、決まった型がついた状態でデータを fetch できたりと、嬉しいことが多いと思います。一方で、自由に投げられるクエリを処理しなければならないため、サーバの実装はかなり難解なものになっています。

ent の GraphQL インテグレーションを利用すると、すでに定義したエンティティとその間の関係の情報を生かし、自然な形で GraphQL の API サーバを作ることができます。

ささっと GraphQL API を作り、その嬉しさを手早く体感しませんか?というお話でした。


prev: 2023年1月3日分は id:ikesyo さんの 『Production Ready GraphQL』の「Anemic GraphQL」 - いけだや技術ノート でした

next: 2023年1月5日分は id:hogashi さんの aタグで#topにリンクするとページ先頭にスクロールするのは仕様 - hogashi.* です

GitHub Actions の workflow の lint を Github Actions で動かす

GitHub Actions で色々なコードの test や lint を動かしていると思います。

その際に workflow を yml で定義するのですが、この workflow ファイルはちゃんとチェックしてますか?

  • 「見よう見まねで yml を書いて push したけど、キー名が間違っていて動かなかった」
  • ::set-output などの deprecated な機能を今更使ってしまった」

こんな経験はありませんか?

Github Actions に関するその問題、Github Actions を使えば解決できます!

解決策

actionlint という Github Actions の workflow ファイルをチェックしてくれる lint ツールがあります。

github.com

こいつを Github Actions 上で動かすだけです。

動作させてみた例を以下に載せておきます。

github.com

deprecated な機能を利用して怒られた例

やり方

以下のように 2 つのファイルを用意して push しましょう。

.github/workflows/ci-actions.yml

on:
  push:
    paths:
      - .github/workflows/*.yml
      - .github/actionlint.yml
      - .github/actionlint-matcher.json

name: actionlint
jobs:
  actionlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install actionlint
        shell: bash
        run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
      - name: Add problem matcher
        run: echo "::add-matcher::.github/actionlint-matcher.json"
      - name: run actionlint
        run: ./actionlint
        shell: bash

.github/actionlint-matcher.json

以下からコピーして配置してください。

github.com

{
  "problemMatcher": [
    {
      "owner": "actionlint",
      "pattern": [
        {
          "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$",
          "file": 1,
          "line": 2,
          "column": 3,
          "message": 4,
          "code": 5
        }
      ]
    }
  ]
}

解説

この workflow は以下のような流れで実行されます。

  1. github から actionlint を install するコマンドを実行
  2. actionlint の出力から annotation (この行がエラーだよという表示)を作るためのテンプレートを設定する
  3. actionlint を実行する

上級 Tips

手元では検出されないエラーが GHA 上で動かすと検出される

actionlint は手元でもインストール実行できるコマンドで、VSCode の拡張として使っている人たちもいるでしょう。このとき、手元環境だとエラーが検出されないのに、Github Actions 上で動かすとエラーが表示されることがあります。

この原因は、shellcheck というコマンドがインストールされているかどうかの差異にあります。

actionlint は、shellcheck がインストールされているなら、run: などで yml 上に書いているシェルスクリプトに対して shellcheck を適用した結果を教えてくれます。

shellcheck は Github Actions の ubuntu-latest runner にデフォルトでインストールされているのに対し、手元環境ではインストールしてない人もいるでしょう。

If you want to enable shellcheck integration, install shellcheck command. Note that shellcheck is pre-installed on Ubuntu worker.

actionlint/usage.md at main · rhysd/actionlint · GitHub より引用)

custom runner や self-hosted runner の名前がエラー扱いになる

基本 runs-on: ubuntu-latest として用意された runner を用いて動かすことが多いですが、大規模なプロジェクトでは custom runner や self-hosted runner を利用している場合もあるでしょう。

このとき、runs-on: hoge のように、デフォルトで用意されていない runner を指定すると、以下のように怒られます。

label "hoge" is unknown. available labels are "windows-latest", "windows-2022", ...

これをエラーとして検出しないようにするには、actionlint の設定ファイルを以下のように設置する必要があります。

.github/actionlint.yml

self-hosted-runner:
  labels:
    - hoge

cf.) actionlint/config.md at main · rhysd/actionlint · GitHub

まとめ

GitHub Actions のチェックが GitHub Actions でできました!!


余談

このエントリは社内でやっている技術勉強会「忘年LT大会」向けのものですが、せっかくなので public な場で書き留めておきました。

id:arthur-1ウロボロス的なものが好きで、はてなエンジニア Advent Calendar に「Advent Calendar の監視をする」というネタで投稿したのですが全くウケませんでした。今回もまた懲りずに同じ過ちを繰り返したわけであります。

blog.arthur1.dev