AWS CDKアプリを作る時のテンプレートリポジトリを作った

AWS CDKの昨今

最近は仕事でもプライベートでもAWSのリソース配備にはAWS CDKを使っている。
ちなみに最近はCDK for Terraformが出るなど、AWSに限らないツールキットを指向している雰囲気があるので「仕事は {ここにAWS以外のクラウドプロバイダが入る} だからな〜」「AWS以外も含めたハイブリッドクラウド環境だからな〜」という人も一度触ってみるとおもしろいのではないでしょうか。

ちなみにCDK for Terraformのようなものを作る布石はaws/constructsという前は@aws-cdk/core以下にあった階層や依存関係を表現する基礎的なクラス群が独立したライブラリとして提供されはじめたころからあったと思われる。

CDKはCloud Development Kitという名の通り従来のIaCというところから一歩進んだ「クラウド開発キット」であり、マネージドサービスを使っていくとインフラかアプリケーションかという区別が曖昧になっていくなら、構築支援するライブラリ・ツールキットもそれらを無理に分け隔てる必要はないのでは? という着想が垣間見える。実際使っていると隔たりが限りなく取り払われていく感触がある。
たとえばAWS CDKには手元のファイルをS3にアップロードしたり、手元のディレクトリでdocker buildしてECRにイメージをpushするライブラリなどがあったりする。Lambdaのコードを手元からアップロードするのはとても便利。

アプリケーションを上から下までプログラムで構築していくという考え方が徹底している。そういった話は1年前くらいの発表で触れていて、変わりない。

speakerdeck.com

CDKのベターデフォルト

cdk initで雛形はできるけれど、いくつかの点で不満がある。

  • ESLintが同梱されていない
    • ここに空白あったほうがいいですね、とか議論していい時代はもう終わった
  • renovateが設定されていない
    • CDKは頻繁に更新されるのでこれ無しではすぐに滅ぶ
    • アップデートはさほど苦しくないので自動化を徹底することが大事
  • GitHub Actionsの設定がない
  • TypeScriptのコンパイルが必須

などなど。

ESLintとかはともかく、TypeScriptのコンパイルを必須とするのは個人的にはCDKのような、Vanilla JSへ変換することが必須だったりアセットの整合性を追跡したいような要件がない場合には冗長だと感じる。
なので--noEmitとts-nodeで良いじゃん派。cdk deployとかして古いな〜 → コンパイルしていなかった、というようなできごとで失う時間のほうが多いと考える。

ので、良いかんじのtsconfigなどを込みにしたリポジトリを作った:

github.com

含めてあるものは上記の通りで、

  • ESLintと良いかんじの設定
  • renvoateの設定
  • 良いかんじのtsconfig

など。

テストなどはcdk initで出力されるものを基本的に踏襲している。

公開されたシンボルにJSDoc/TSDocを書くことをESLintでルール化したい

公開しているライブラリほどじゃないにせよ、チームで開発しているTypeScriptで書いたコードにちらほらドキュメンテーションしていきたい。

それESLintで

とりあえず今までろくに書いていなかった部分はいまさっき書き足すPull Requestを作ったけれど、今後手を入れる時にもちゃんと書いていってほしい。
で、こういうルールの徹底を人間がレビューで「ドキュメントおねがいします」って言うのは不毛。2020年代にやっていいことではない。

TypeScript/JavaScriptでやろうとなったらESLintでどうにかしたい。eslint-plugin-jsdocがJSDocに関連したルールを提供している。
JSDocはTSDocと基本的なフォーマットは同じで細かいタグのセマンティクスは差異がありそう。
そしてなんとeslint-plugin-jsdocはTypeScriptもサポートしている *1

ちなみにTypeScriptでドキュメント書く時はけっきょくどのフォーマットがええんや (なんかesdocっていうのもあったような) と思っていたらちょうど良いところに:
blog.pokutuna.com

最高。

ほどほどに始めたい

とはいえ社内でのみ開発されるアプリケーションのドキュメントをがちがちに書けというのも大仰なルールだし、そもそもTypeScriptで担えている型情報とかは書かなくて良いということにしたい。

前者は公開されたシンボルにだけドキュメントが書いてあれば良いということでよさそう。公開というのはexportされている、もしくは可視性がpublicであるフィールド・メソッドという定義にする。

eslint-plugin-jsdocにはそのものずばりpublicOnlyというオプションがrequire-jsdocルールにある。

そんなこんなで始めたESLintのルールが以下:

  "eslintConfig": {
    "extends": [
      "eslint:recommended",
      "plugin:@typescript-eslint/eslint-recommended",
      "plugin:@typescript-eslint/recommended",
      "plugin:@typescript-eslint/recommended-requiring-type-checking",
      "plugin:jsdoc/recommended"
    ],
    "plugins": [
      "@typescript-eslint",
      "jsdoc"
    ],
    "parser": "@typescript-eslint/parser",
    "env": {
      "node": true,
      "es6": true
    },
    "parserOptions": {
      "ecmaVersion": 2018,
      "sourceType": "module",
      "createDefaultProgram": true,
      "project": "./tsconfig.json"
    },
    "rules": {
      "jsdoc/require-jsdoc": [
        "error",
        {
          "publicOnly": true,
          "require": {
            "ArrowFunctionExpression": true,
            "ClassDeclaration": true,
            "MethodDefinition": true
          },
          "checkConstructors": false
        }
      ],
      "jsdoc/require-param-type": 0,
      "jsdoc/require-returns": 0
    }

むすび

*1:大半のルールにおいては。一部のルールはまだサポートされていない

react-type-safe-render: 第二引数にnullを許容しない安全なReactDOM.renderの型定義を書いた

@types/react-domで `ReactDOM.render` の第2引数 (container) はnullを許容しており、`document.getElementById` の返り値の型に合わせているそう。
しかしランタイムがnullを許容しているわけではなくnullを渡すと実行時例外になる。

TypeScriptを使っているのにこういうことが起きるのはなかなか悲しいので、nullを許容しないより安全なrender代替をexportするライブラリと、ReactDOM.renderを禁止するeslint pluginを作った。

GitHub - aereal/react-type-safe-render: Provides `safeRender` function that is more type-safe version of `ReactDOM.render` with no additional implementations

npm i -S @aereal/react-type-safe-render
yarn add @aereal/react-type-safe-render
npm i -D @aereal/eslint-plugin-react-type-safe-render
yarn add -D @aereal/eslint-plugin-react-type-safe-render

実装はとても素朴でRendererの定義から丁寧にnullを取り除いただけ。
関数の実体はReactDOM.renderそのものなので、実行時にペナルティは無い。

またESLintのプラグインを初めて作った。

@types/eslintにはプラグイン自体の型定義がなくてドキュメントを見ながら作っていたら、疲れていたのかしょっちゅうミスしてだいぶ時間がかかってしまった……。

最初はno-restricted-importsのruleを定義を持つconfigを配れば良いじゃんと思っていたのだけれども、利用者でno-restricted-importsを使いたい時に競合して上書きしあってしまうので実用的ではなく、似たような実装のruleを別に作ることにした。

けっこう楽しかったのでおもしろESLintプラグインを作って遊んでいきたい。

株式会社はてなを退職

2020年8月14日付けで退職する運びとなった。
入社が2012年なので勤続丸8年を迎え社内でも古株の方になってきつつある。Web業界にしてはわりと長くいたほうだと思う。
自分自身でもこんなに長く籍を置くとは思っていなかったので驚いている。

はてなに入社してから3039日

退職を決めた理由は主に2つ。

金沢移住

1つめは、現在住んでいる京都を離れて金沢で暮らしたいと考えたから。

数年前に観光で訪れた金沢を歩いてから一目惚れしてしまい、自分がここで生活する想像をするうちに単なる夢想から具体的に実現することを考えはじめた。
これを書いている時点で、株式会社はてなの事業拠点は東京と京都のみであり、在宅勤務は育児や介護、その他会社が認めるに足る理由があるケースのみ認められている。
平時は週数日程度スポットでの在宅勤務はマネージャーと合意した上では認められている。またコロナ禍においては在宅勤務推奨となっている。ただし、継続的にフルリモート勤務というのは上記の通り認められる事例は限定的になっている。
このまま在職しつづけても実現が難しそうと判断したのが、転職を決意した最大の動機といえる。

制度設計や判断について特に言うべきことはない。単に自分のニーズに合わなかったというだけのことだと思う。

幸か不幸かコロナ禍でリモートワークを半ば余儀なくされ当事者は拡大しているので、議論が進む機会となることを願っている。

一応、制度の拡大について提案してみたが諦めた。
世の事例を調べるに、ローカルワーク *1 に慣れたそれなりに成熟している企業がリモートワークに切り替える際には、

  • トップダウン
  • 合理性や生産性の観点で評価するというより、働きやすさ・自己実現の促進という観点を優先させた

事例がほとんどだった。

つまり、トップがある物差しを差し置いて別の物差しを守ろうとする強い判断を起こしたというヒロイックな事例しか見つからなかった。
「働きやすさを守る経営者」というと美しく見えるが一長一短であると思うし、どちらが良いという話でもない。

ここで重視したのは、生産性がローカルとリモートでどれくらい変化しうるとか、それをどう埋め合わせるための仮説や検証をこうしたとか、経営判断に繋がる十分な強度を持った提案の根拠として十分な仮説や先行事例を当時は見つけることができなかったということ。

ただしこれらは転職活動を始めた当時の調べによるものであって、現在は状況が変わっているのでこれまで在宅勤務の導入に踏み切っていなかった企業がうまくバランスさせながら導入を試みる事例や方法論がより多く世に出ていくことと思う。

そして、当社は企業全体の生産性と働きやすさをバランスさせる制度を前提としており、なかなか骨が折れそうなので諦めることにした。
実際、全体最適を考えてリモートワークを拡大しようとすると慎重にならざるをえないと自分も思う。しかし、今自分が選びたいのは組織全体にとって善となることより、個人の願望を十分なスピードで叶えることだった。

また、リモートワークが前提となった世界でではどううまくやっていくか? という問題に取り組みたいと考えた。
過去に失敗した事例はいくらでもあるし容易な道ではないことは承知しているが、それでもフロンティアに立ってどう解決するかに自分の時間と情熱を捧げたい。

後述するように、他の動機もあって留まることにこだわることはないと考えた。

ちなみに、在宅勤務ではなく通勤手当の拡大という線でも提案したがこちらも諦めた。在宅勤務の拡大より合理性の面でより望み薄 *2 だと考えていたので、これも仕方がないと思う。

スーパーリセット

2つめは、大きな変化を欲したから。

端的に言えば老害なりかけの自分を一旦捨てたかったから。

blog.kentarok.org

8年勤めて、様々なことに慣れた。仕事の進め方や手続きで不明なこともない。
中心に据えられている事業分野で求められるであろう技術・スキルはそれなりに備えていると思うし、業界が発展してもキャッチアップしつづける自信もある。
それなりに信頼も得ている。自分の発言はそれなりに尊重してもらえる。

でも、ふとした時に自分ってそんなに大した人間なのか? って思う瞬間が増えた。これって、単に時間を重ねただけの結果なんじゃないの?
ここに至るまでにもっと短い時間で済ませられたり、同じ時間をかけたらもっと遠いところに至れる可能性はなかったの?

ちゃんと自分は成長するべくして成長したのか。エンジンはへたっていないか、惰性に任せきりではなかったか、そういった自問自答に自信をもって答えられる根拠は少ない。
単に周りに恵まれただけで、自力は大しことがないのではないか。

自分は仕事以外にも時間を使いたい趣味はいろいろあるし、仕事一筋でも大好きというわけでもない。働かずに暮らせるなら今すぐそうしたいと思う。
しかしそれも難しいので、ならば生活の中で少なくない時間を使う仕事はとてもおもしろいものであってほしい。おもしろい仕事は難しい。難しい仕事を果たすためには技量と信頼がいる。
そういえば面接で当時社長だったjkondoさんに「僕は仕事がしたくないです。仕事を仕事だと思ってするつまらないことは絶対にしたくないです」って言ったおぼえがある。

株式会社はてなに留まってもそういう仕事ができる可能性は十分にあると思う。しかし自分がへたっていないか、ちゃんとおもしろい仕事を楽しみつづけるだけの体力が今もあるのか、それを維持できる体なのか、ということを試したい。
そのために、自分の身ひとつ以外は無いところに身を移してみたいと考えた。

8年ちょっとでやったこと

Resume - aereal

転職活動を始める際にさっと職務経歴書にまとめた。ここに書くことに苦労しないくらい色々とおもしろいことがやれたのは本当に良かった。
複数の相手にURLだけ伝えれば良い・見るであろうぎりぎりまで手直しできる、などのメリットがあるのでPDFとかを都度送るのは非合理だなと思った。

新サービス立ち上げから独自ドメインHTTPS配信、オンプレミスからクラウドへの移行などなど。Webのソフトウェアエンジニアとして一通りここは通りたいと思っていた分野を一巡りできたのではないかと思う。

特に独自ドメインHTTPS配信システムを作るのは良い経験になった。RFCで定められたプロトコルに従って実装を書いたり、そもそもTLSまで降りてプロトコルを学んだり、それまでのWebアプリケーションエンジニアから少し踏み出したエンジニアリングができた。

これから

そんなこんなで転職活動を始め活動を終了した。8月から新しい会社で働く。
金沢で暮らしたいという意思を伝えて理解いただいたうえでオファーをいただいたので確実に移住への大きな一歩を踏み出した。

次もWeb業界でソフトウェアエンジニアをやる。試用期間が終わるまでは所属は明らかにはしないと思う。クビになったらダサくて恥ずかしいので。

転職活動はかなり消耗したのでしばらくやりたくない。まず初めての転職だということもあるし、おまけにCOVID-19の拡大と重なってしまい現職の仕事も転職活動もだいぶ大変になった。
内定が出たGWごろは、楽しみだった旅行もライブもすべて無くなってしまい気分が完全に落ちていたので手放しで喜べなかったどころか、途端に不安になってしまったりかなり参っていた。

最後に。大学を中退したどこの馬の骨ともわからない人間が、10年近く経って贅沢に自己実現やキャリアのことを考えられているのも伸び伸びと仕事ができる環境があってのことだったと思う。
また、お世話になった方は数知れずいるものの、id:hitode909さんは自分が入社する以前からよく気にかけてくださり、大学もアルバイトも辞めて実家に帰るか野垂れ死ぬか考えていた時に「(はてなに) アルバイト応募してみませんか」と声をかけてもらえなければこうしていることはなかったでしょう。
入社してからもソフトウェアの話、仕事の話、おもしろ技術の話、様々に刺激を受けお世話になりました。数年来hitodeさんが務めたチームのリーダーを引き継ぐと決めた時にやっと同じ土俵に立てたように思い、とても感慨深くまたやっと自立できたと思える瞬間でした。
こういう話はしたことがなかった気がするけれど、ずっと意識していたのです。

在職中は他にも挙げきれないほどの方々にたいへんお世話になりました。本当にありがとうございます。引き続きインターネットでお会いしましょう。

*1:同一拠点で勤務すること。オフラインと表現することもあるが、リモートとより対称がとれていそうなのでこの表現を選ぶ。

*2:近距離通勤手当を福利厚生として支給している

JWEは毎回ランダムなInitialization Vectorを使用するので得られるトークンは毎回異なる

タイトルですべて言い切ってしまった。

JWE (= JSON Web Encryption) にはInitialization Vectorというフィールドが含まれており、これは暗号化処理ごとにランダムな値が使われる。
なので、同じペイロード・同じ秘密鍵を使って暗号化しても暗号化したトークンは異なる値になる。

もともとJWS (= JSON Web Signing) で署名したトークンを使っていて完全一致でテストしていたコードが暗号化するようになって必ず落ちるようになりハマって調べた。

複合して元のメッセージが得られれば良いので暗号化した文字列が毎回異なっていても良い、というのは暗号化において一般的なのかもしれないと後から思い至ったけれど、同じ入力・秘密鍵を与えて変わると思っておらず、テストの書き方がおかしいのだと思い込んで時間をつぶしてしまった……。

scrapbox.io

Scrapboxにもまとめた。

cloud.google.com/goのリクエストがX-Rayでトレースできないので土日を潰して調べて完全に理解した

cloud.google.com/goはGo向けのGCPAPIライブラリ。

一部を除き単にHTTPリクエストを送っているのでX-Rayでトレースする *1 のは造作もないだろうと思ったら思いがけず難航し、だらだら調査していたら2日かかってしまった。

詳しいメモと結論はScrapboxにまとめた。

scrapbox.io

読んだのは

の3つ。

VSCodeでdelveを使ってデバッグする体験がだいぶ良くて捗った。というかこれがなかったらつらくて諦めていたと思う。

土日を潰したけれど結果的によく使う大きなライブラリのコードリーディングをする良い機会になった。特にgolang.org/x/oauth2は雰囲気だけ知っていたけれど、まとまった時間をとって読んだりメモしたことはなかったので。

あと複数のライブラリを行き来しながらメモを書くのにScrapboxがかなり役に立った。
最初は調査用メモのページに実行パスをあれこれ書いていたけれど、階層が深くなっていくし同じメソッドを複数回呼ばれている時に表現しようがなくて困ったけれど、各関数ごとにページをわけたらすっきりする。
ページのリネームが簡単にできるので、書く時は `Cred()` とかにしておいて、後からパッケージ名を含めた完全修飾名にして検索しやすくするのも苦ではなかった。しかも後から補完が効いて便利だし。

早くにoauth2のissueを見つけて、てっきりこれがすべての原因だと思っていたけれどデバッガを使っているうちにトークン取得リクエストだけではなくAPI呼び出しキャプチャするaws-xray-sdk-goの実装が呼ばれないのは変だということに気がつくまでに時間がかかった。
これに気がついてからはあたりをつけて調べは早くに済んだけれど。

*1:github.com/aws/aws-xray-sdk-goを使って

GAEのapp.yamlに秘密の情報を含めたい時

app.yamlに公開したくない情報を含める - aereal-techより。

環境固有の値とかapp.yamlに書いてコミットしたくないけど、CD = Continuos Deliveryするためには必要でどうしよう、という時に思いついた策。

valid JSON is valid YAMLなので、雛形となるJSONリポジトリに置きjqで編集して秘密の情報を含めたJSONをapp.yamlとして出力する。
GitHub Actionsだとよくjqを使ってJSONをいじったりするけどYAMLだしなあ……と悩んでいた時にJSON is YAMLじゃんということに気がついた。

実際の例:

GCPだとSecrets Managerを使うのが筋なんだろうけれど、複数のシークレットを取得する方法がなくてシークレットの数だけAPI呼び出しが必要になる。
GAEでアプリ起動時に呼び出すのもばかばかしいし、個人の小規模なプロジェクトだからGAE用のビルドイメージを置いたGCSをうっかり覗かれて漏洩ということも考えにくいしまあいいかと思っている。

あと基本的にGitHub Actionsからのみデプロイするので秘密の情報の管理がGitHub Actionsのsecretsに集約されるので都合が良い。

AWS Systems Manager Parameters Storeは暗号化オプション付きでありながら複数の値を取得できて使い勝手が良いのでGCPにもこういうソリューションがほしい。

人間が関わりあう時間の価値を最大化するためのオートメーション

コードを書かない問題解決

ソフトウェアエンジニアとしての価値観とか仕草みたいなのはだいぶid:hitode909さんの影響を受けており、その中でも自分が大きく変わったと思うのは、問題解決の手段としてソフトウェアにこだわりすぎないという視点を手に入れたこと。

自分は本来的にはコードを書くのが大好き人間というかyak shaving大好き人間なので、コードを書きはじめて実現に至るまでのすべての寄り道を楽しいと思う性質だけど、一方、チームや組織という文脈でそれが常に正しくて価値が最大かというとそうではない。
……ということに気が付かされたのはひとでさんと仕事していて自分がソフトウェアを作るという方法にこだわって悩んでいると「とりあえずスプレッドシートで運用してみませんか」とか提案してもらえることがあり、最初はえーコード書きたいよと思っていたものの実際にやってみるとそれくらいで事足りる頻度の作業だったり、そもそも思っていたのと違うものを求められていたことがわかったり、と様々な示唆を得ることができた。

ひとでさんがなんでもかんでも手作業でやろうという考え方をしているかというとそうではなくて、酔っ払うといつもソフトウェア構築に関する良い話で盛り上がれる。
MVPを作る最短経路に対するアンテナが高く、かつその手法に拘泥しないというプロフェッショナル精神が高いのだと理解して、自分も心掛けるようになった。
自分はソフトウェアエンジニアであって、コーダーより広い職責を持つ人間なので、職責を果たすための手札は多いに越したことはない。

余談だけれども、ソフトウェア知識を得るためのメリハリをつけるために趣味ではおもしろさ・新しさに全振りするということを決めている。
そういった考え方をScrapboxの趣味プロダクトにまとめた。

オートメーション筋

それはそれとして、最近ソフトウェアで自動化するという手札をもっと研いでも良いのではと改めて実感しはじめた。
身の周りで人間が工夫して運用しているままのものがけっこういろいろあって、工夫しているだけあっていろんなパターンを実現できていて (できてしまっていて) これを後から自動化するのも大変そうだな……と思って見ている。

その時は人間がなんとかするのがMVPだと思っていたけれど、継続的に運用を改善したり見直す仕組みがないと大変なままなのでだったら最初からソフトウェアになっていたほうが柔軟性と引き換えに見通しが良くできたのでは、とかあれこれ考えたりした。
考えはじめたきっかけは在宅勤務が原則となったこともあるし、単にもっと前から人が増えてきたり、とかいろいろ。

そう物事は単純ではないけれどひとつ考えたこととして、最短経路というのは自分たちの手札によっても決まるのでそれを磨くというのも状況を改善する手段になるのでは、ということ。
徒歩で生活している人と車で生活している人にとって「近い」という感覚は異なると思う。

なので最近はTerraformとGitHub Appsを覚えようと思って実装しながら調べ物をしている。
ScrapboxのGitHub Appsを中心に認可方法について主にまとめている。App側がステートレスかつ安全に振る舞えるようよく設計されているな〜と感心している。

人間らしい暮らし

人間とコラボレーションする時には人間らしい時間が流れていると良いなと思う。
事務的・機械的なことを人間とやりとりしていると虚しい気持ちになってしまう。

在宅勤務・リモートワークが広がって人と人のリンクがオンラインになる (非同期、レイテンシが大きい、情報チャンネルが選択的に制限される) 中で、そういう虚しい時間だけが増えると他人と関わる時間が苦痛になりそうで嫌だなと思いはじめた。

コードレビューの話でも空白の話とかはlinterとかに任せて設計とかの話をしようという考え方があって、それもコラボレーションする時にその方法と相手じゃないとできないことに価値を持たせようということなのだと思っている。

自分は、人間同士が直接やりとりするのは常に最高でも好きでもなくて適材適所であるというスタンスに立っていて、だからこそ人間と関わる時間は実り多いものにしたい。

以上、引き継ぎとかをしたり、周りで人が増えてきて伝授する方法も変えなければ、という話題を見聞きして思ったことでした。

aereal.orgをNext.jsのSSGとContentfulに移行した

VPSCloud Bucketのホスティング機能→Firebase Hosting + Gatsbyという変遷を辿っていた自分のサイトをNext.jsのSSG (= Server-side generation) とHeadless CMSであるcontentfulに移行した。

aereal.org

最近出たNext.js 9.3でSSGサポートがちょっと良くなった:

These new methods have many advantages over the getInitialProps model as there is a clear distinction between what will become SSG vs SSR.

  • getStaticProps (Static Generation): Fetch data at build-time.
  • getStaticPaths (Static Generation): Specify dynamic routes to prerender based on data.
  • getServerSideProps (Server-Side Rendering): Fetch data on each request.
Blog - Next.js 9.3 | Next.js

かいつまむと、いままでgetInitialPropsという汎用的なAPIしか無かったのでこれを使うとNext.jsのstatic optimizationが効かなかったのだけれど、9.3で3つに分割されて最適化が効く範囲が広がった。

もうひとつ、Gatsbyを使うモチベーションとしてGraphQL + Apollo Toolingの組み合わせによるTypeScriptの型定義生成が便利で、GraphQLを直接サポートするHeadless CMSの類というとほぼGatsby一択だったという点がある。
元々contentfulは知っていてすこし触っていて好感触だったけれども、この型定義の生成がなくなるデメリットを受け入れられるほどではないなあと諦めていた。

しかしふと調べるとcontentful-typescript-codegenというツールがあってcontentfulのcontent modelから生成することができるようだった。実際、触ってちゃんと動くのでこれでよさそうとなり移行することにした。

あとついでに職務経歴書も書きはじめた。

作業メモ:

aereal.orgをGatsbyからNext.js + Contentfulに移行 - aereal-tech

趣味プロダクトのデザインドキュメントをScrapboxに書き始めた

scrapbox.io

仕事と趣味が曖昧な方の人間なのであれこれ触ったり作ったりするのが好きなんだけど、これまでは作ったものをGitHubに置いてREADMEを書いてそれだけで終わっていてなんかもったいなあと感じていた。

GitHubに置くからにはと、リポジトリをいきなり見に行っても情報が十分に凝集しているようにと書く一方で自分という文脈を踏まえた情報は意図的に書いてこなかったし、見栄を張ってREADMEを英語で書き続けているからどうしても微妙なニュアンスのテキストは書けずにいたりしたので、もうちょっとアウトプットの濃度を上げられないかとたまに考えていた。

最近、仕事でデザインドキュメントを書いていこうというムーブメントが社内で高まっていて、これはソフトウェアエンジニアのアウトプットとしてコード以外のかたちとして考えられる中でかなりおもしろい形式だと思って、他チームの書いたものを楽しく読んだりしている。
で、これを趣味プロダクトでやらない手はないなと思った。ので書き始めた。

Scrapboxに書いていくうちに、どんどんこれまで考えてきたことがかたちになっていて不思議な体験だった。

読んでほしいのは自分なりの趣味プロダクトの定義です。

scrapbox.io

ECS taskの停止理由をMackerelのグラフアノテーションとして記録する

github.com

記録するLambdaを作るAWS CDKのリソースライブラリを作った。

いますぐ npm i -S @aereal/cdk-ecs-task-retirement-events-mackerel-annotator !

f:id:aereal:20200204174634p:plain
こういうアノテーションが作られる

モチベーションと利点はScrapboxに書いたように:

[ECS]のtask停止は[Mackerel]にホストの退役と登録というかたちで通知されるが、その契機となった変化が何なのかがわからない。
[CloudWatch Events]を購読する[Lambda]が、コンテナの停止理由をMackerelのアノテーションとして付与する。
Mackerelのアノテーションはサービス (とロール) に紐付き、揮発していくコンテナより長いライフサイクルで残るため、連続的な変化を追いやすい。

cdk-ecs-task-retirement-events-mackerel-annotator - aereal-tech

……というところ。

どうぞご利用ください。

zsh: /usr/libexec/path_helperから逃れる方法 2020年版

macOSには /usr/libexec/path_helper というスクリプトがあり、これは /etc/zprofile で実行されOSがPATHをよしなに設定するというものだが、ユーザーが追加したパスより優先度高く追加するようになっており、かなり傍迷惑。
/usr/local/binなどより任意のパスを優先させたいのに〜〜〜ということがよくある。

Homebrewでzshを入れる際、過去には --disable-etcdir というオプションをconfigure時に渡すオプションとかがあったけどなくなっており、仕方なくforkして--disable-etcdirを付けたり、といろいろあったのだが、/etc/zprofileを読まないよう設定することもできて、これがおそらく最もポータブルな対処法でありそう。

unsetopt GLOBAL_RCSするだけ。

Commands are first read from /etc/zshenv; this cannot be overridden. Subsequent behaviour is modified by the RCS and GLOBAL_RCS options

zsh: 5 Files

注意したいのは /etc/zshenv は必ず読み込まれ、これはunsetopt GLOBAL_RCSでも回避できないこと。

2019

仕事

はてなブログ タグ

staff.hatenablog.com

initial commitの日付が1年くらい前だった。ほぼかかりきりだったので1年以上やっていたことになる。

上から下まで本当にいろいろやれて良かった。要素技術やらキーワードを挙げ連ねると:

  • Next.js
  • TypeScript
  • Go
  • GraphQL
  • styled-components
  • Atomic Design
  • AWS CDK

……などなど。引きの良いものが並んでいるけど、単に映えることだけを考えて挙げたり選んだわけではなく、必要とされるものや過去の反省を活かして最適な選択肢を模索した結果、これらが妥当と判断したので、すべての要素について選んだ根拠を自信をもって説明できる。 また、矛盾するようだけれどもすべてが最適だと考えているわけでもなく妥協した点や今後より良い選択肢が出そうな点についても心当たりがある。そういったリスク含めて説明できるし責任を負えると判断して作れたのは良い経験だと思う。

一方、こうした判断の壁打ちの機会が特に初期は乏しかったので、後から加わったメンバーからツッコミはなかったものの、それは既にできあがったからじゃないの、っていう疑念が無いでもない。 別にチームメンバーからのフィードバックを信用していないわけではないけれど、なんとなくこういうことって「便りのないことは元気な証拠」とも言い難い側面があるなあと思うので不安になっているだけだと思う。

たぶん今、自分が最も困っていてかつ解決すべきなのは、チームでお山の大将に限りなく近付いていることだと思う。自分の考えを理詰めで整理して伝えることが得意だと自負しているが、それが「なんか違う気がする」くらいの指摘を呼ぶ素地が無いように見えているんだと思う。 別に自分が受ける批判・指摘の全てに一切の瑕疵があってはならないなんて思っていないけれど、普段チームを引っ張るような役割の人間が常に75点くらいの仕事をしていたら、50点くらいでも赤点のような気がする、って周りに思わせてしまうっていうのは人の心として理解はできる。

じゃあ仕事の面で自分も甘さを見せれば良いのかというとそうではない気がするし、そうだとしてもかなりやりたくない。一辺倒では解決できない問題だと思うので、気長にトライアンドエラーしていくことになると思う。

テックスキルとしては、デザイナーとAtomic Designの落とし込みやJSSの選択について初期にかなりがっつり会話できたのは良い経験だった。あれこれ比較して当初から本命だったstyled-componentsに落ち着いたものの、比較検討できたことで選択に自信がついた。

そういったフロントエンドに近いところから始まり、インフラ設計はほぼひとりで、構築は後から加わったSREと一緒にやった。VPCなどネットワーク設計より上のことは一通りやったし、だいたい勘も付いた。

カメラマン

developer.hatenastaff.com

他社もインタビューコンテンツに力を入れている中、何がフックになるかといったらTwitter Cardsとかに出てくるサムネイルが大きいんじゃないかと思い、写真係やりますよって言い出して長命なシリーズになっている。

プロの方が絶対に腕は良いけど、小回りが利くことは強みではないか。

登壇

speakerdeck.com

speakerdeck.com

speakerdeck.com

仕事関係が2つ、完全な趣味が1つ。数は少なめだったけど、仕事の方はまだ出せそうなネタがいくつかあるのできっちり出しておきたい。

趣味のほうは、リメイクということで個人的にはちょっといただけない感じがするものの、動くデモが作れたのでトントン。

ソフトウェア

わりと仕事でやっているプロダクトで必要になったので書いてOSSにした、っていうのが多い。

go-http-replay

Go版vcr

「外部APIにアクセスするテストを書く時にスタブをしたいけど、手作りするより本物のレスポンスを使いたいよね?」「でもいちいちcurlして保存して……はだるいよね?」 → じゃあ初回だけ実際にリクエストして、そのレスポンスを保存して使い回そう! というライブラリ

Goには httputil.DumpResponse / httputil.ReadResponse っていう便利グッズがあるのでこれを使っているだけ。 http.TransportというPerlでいうLWP::Protocol的なやつを差し替えるだけでやっている。 72行くらいしか無いけど、Goのnet/httpのおもしろさが詰まっていると思うので気が向いたら読んでみてください。

go-envschema

環境変数JSONのobjectと見なせるやん? だったらJSON Schemaで検証できるやん? という発想によるライブラリ。

詳しくは 大コンテナ時代を生きのこるためのJSON Schema

go-dsn

TypeScriptで書いたオブジェクトを https://github.com/go-sql-driver/mysql のDSN (Data Source Name) 文字列にするNPMパッケージ。

AWS CDKでRDSのhostname/portを結合してコンテナの環境変数に渡すために作った。

cdk-mackerel-container-agent

CDKで書いた TaskDefinitionmackerel-container-agentをsidecar containerとして良い感じに差し込むグッズ。 mackerel-container-agentが対応している環境変数などをいちいちドキュメントを見に行かなくてもIDEで補完してくれるようにという目的で書いた。

ベータ版の頃はクラスタがFargateとEC2かによって設定の感じが変わるのでまあまあ賢いコードが書いてあったけど、今は単にデフォルト値を与えるくらい。 CDKがDeveloper Previewのころからライブラリにしていたので毎週のbreaking changeでスナップショットが壊れては仲間たちが丁寧に jest -u してくれた思い出のライブラリです。

action-aws-assume-role

GitHub Actionsで使えるaction.

文字通り AssumeRole して得られたsession tokenやらを環境変数にexportするグッズ。

gqlgen-tracer-xray

gqlgenでAWS X-Rayのトレースを良い感じに取るプラグイン。ほとんどopentracing版を真似しただけ。

migrate-gh-repo

2つのGitHubリポジトリでissueやらラベルやらを移行するスクリプト

似たようなツールはいろいろあるけど、ほとんどべき等になっているところとあまり賢くないが故に柔軟性が高いのが売り。

古いリポジトリのissueへのリンクを残すだけの仕様になので、たとえば複数リポジトリを1つのリポジトリへmonorepo化しつつ移行する場合にも、同じissue numberにコメントでリンクが書き連ねられるだけなので、N:1の移行も問題なくできる。

技術的には設定にCUEを使っているのがおもしろポイント。

総括

仕事に全振りした1年だった。それなりに得るものもあったけど、どうしてもやったことが仕事の文脈だけだとおもしろみが無いなあと感じてしまう。 来年は文字通りの意味でワークライフバランスの良いおもしろ技術探索をやっていく。

VSCodeでJSONを編集する時にJSON schemaを指定して補完を効かせる

JSON editing in Visual Studio Code

tsconfig.jsonなどはデフォルトで補完が効くが、これはsettings.jsonで追加できる。

たとえばrenovate.jsonを編集する時はこういう設定を入れておくと便利。

  "json.schemas": [
    {
      "fileMatch": ["renovate.json"],
      "url": "https://raw.githubusercontent.com/renovatebot/renovate/master/renovate-schema.json"
    }
  ]

SlackとGitHub Deployments APIで疎結合なChatOpsを実現する


下記で紹介している /github deploy というコマンドは2021年4月9日の更新で現行のSlack連携からは削除された。

Removed deploy command and notification support: Today, the functionality provided by deploy command is very limited and doesn't address all the scenarios. We are removing deploy command and notifications support as part of this version. We want to relook at the scenarios and build a more holistic experience that customers need.

integrations/slack: Bring your code to the conversations you care about with the GitHub and Slack integration

this.aereal.org

Deployments APIが出た頃に上記記事を書いてもう5年が経とうしている中、最近いろいろなグッズが進化してタイトルにあるような良い塩梅のChatOpsが実現できそうなのでそれのメモ。

登場人物

Deployments APIGitHub Actions

このAPIだけではなんらかの仕事は行われず、ただWebhookのイベントが通知されるだけのAPIになっている。
この説明だけ聞くと「ふーん」というかんじだけれども、各種Webhookイベントに応じてなんらかのタスクが実行でき、しかもCI用に払い出されたAPIキーが振ってくるというGitHub Actionsの存在でできることの幅や手軽さが変わってくる。

実際のWorkflow YAMLを見るのが早いと思う: https://github.com/aereal/actions-playground/blob/master/.github/workflows/deploy.yml
on: deployment を書いておけば、Deploymentが作られた時にこのYAMLの内容が実行される。

内容の詳細は:

……となっている。

この例では活用していないが、Deployment APIのパラメータにはtaskやenvironmentがあるのでステージング環境か、アプリケーションのデプロイの他にDBのスキーマ更新などの付随するタスクも表現できたりなど、いろいろ使い出はありそうに見える。

Slackからデプロイ

では肝心のDeploymentはどうやって作るの、ということだけど、これまでは丁寧にcurlするしかなかったのが最近SlackのGitHub連携から作れるようになった。

/github deploy OWNER/REPOを実行するとこういう画面が出てきて:

n

リビジョンなどを埋めるとAPIが呼ばれる。

上記GitHub Actionsが設定されていれば、ピタゴラスイッチが動き出すという寸法。

Slackにはこういう通知が出る:

f:id:aereal:20191029121824p:plain

Deployment viewに期待

最近ひっそりとdeployment viewというものがベータリリースされている。これまで紹介してきたactions-playgroundの画面だとこういうかんじ: https://github.com/aereal/actions-playground/deployments

f:id:aereal:20191029121952p:plain

今は本当にログしか出てきていなくて味気が無いけれども、GitHub公式でこういうビューを作ろうとしているのはちょっと期待。

まあ仮にお蔵入りになってしまうとしてもDeployments APIを使ってちょっとしたビューを作るのはわりと楽だろう。

なぜDeployments APIなのか

こういうChatOpsの類はずっと前からいくらでもあるし、Deployments APIの何が良いの? という向きもあるかもしれませんが、以下の点でとても魅力的だと思っています:

  • チャット (Slack) 側の仕事内容が少ない = 移植性が高くコントローラブルな要素が多い
  • チャットを介さない実行が容易
  • Commit Statusなど既存のGitHubエコシステムと深く統合されて、今までの暮らしがスポイルされず、むしろ進化させられる

チャット側の仕事が少ないというのは、これまで見てきたように単にHTTP APIを呼び出しているだけなので、なにかうまく動かないということがそもそも起きにくい・起きたとしてもトレースがしやすい・実装の移植がしやすい、という点が嬉しい。
なんでもChatOpsにしたはいいがいざSlackが落ちると何もできないという事態に陥ることなく、いざとなればcurlを叩けば良いというのもポイントが高い。

またWebhookなどイベントベースのピタゴラスイッチはどこがホットスポットなのかわかりづらく全容を把握しづらくなってしまうことも多いけれど、Deployments APIとActionsの組み合わせだとGitHubが中央にあるので、とりあえずリポジトリを見れば概形がわかるというのも差別化ポイントになりうると思う。

さらにCommit Statusなど既に広く使われている機能などと統合されているというのも魅力。たとえばDeploymentにrequired_contextsを指定できるので、prchecklistのチェックが完了しなければデプロイが進められない、という制約も簡単に実現・移行できる。

そもそもなぜDeployments APIの話をしているのか

git の develop ブランチは必要なのか、またはリリースtagについて - Togetter

ここらへんでGit FlowなのかGitHub Flowなのかみたいな話が盛り上がり、そういえば最近Deployments APIが盛り上がっとるなということを思い出したのがきっかけ。

id:Songmuさんにこういうリプライもした:

実際、いま新しくやっているプロジェクトでもGitHub Flowに近いワークフローになっている。
コンテナワークロードになると同じリビジョンから作られる成果物を複数環境にデプロイが容易になるはずなので、アプリケーションの内容は同じなのに複数リビジョンが付くGit Flowは困ることが増えるなあ、という印象を抱いていた。

とはいえ上記Togetterまとめにあるとおりgit-pr-releaseは様々な点で便利なのは間違いない。が、本質は差分の可視化といったところなどにあるはずでPull RequestやGit Flow的なブランチ運用が根幹にあるものではないと思っていた。

そんなモヤモヤを抱く中でDeployments APIが進化してきたので注目しているというかんじ。