nabeo がピーしているブログ (仮)

どーも、nabeop です

Node.js の grpc パッケージのインストール時に python で gyp が見つからなくてエラーになる時の対処

手元環境の python を 3.12 系に更新したあとに Node.js で grpc パッケージを入れようとすると

│ Traceback (most recent call last):
│   File "/Users/nabeo/.cache/node/corepack/pnpm/8.15.3/dist/node_modules/node-gyp/gyp/gyp_main.…
│     import gyp  # noqa: E402
│     ^^^^^^^^^^
│   File "/Users/nabeo/.cache/node/corepack/pnpm/8.15.3/dist/node_modules/node-gyp/gyp/pylib/gyp…
│     import gyp.input
│   File "/Users/nabeo/.cache/node/corepack/pnpm/8.15.3/dist/node_modules/node-gyp/gyp/pylib/gyp…
│     from distutils.version import StrictVersion
│ ModuleNotFoundError: No module named 'distutils'

こういうエラーで失敗してしまう。エラーメッセージでググるpython 3.12 から distutils は削除されたので、python の packaging を入れなさいという issue が nodejs/node-gyp であった。

もしくは npm_config_python 環境変数python 3.11 のパスを設定すると回避もできるらしい。とりあえずの対処としては npm_config_python 環境変数で逃げるでもよいけど、これからのことを考えるといつまでも python 3.11 に依存したくないので、python 3.12 でちゃんと使えるようにしたい。

homebrew では python の packaging は python-packaging として提供されていて、このエラーが発生時している時点でちゃんと入っていた。

% brew info python-packaging
==> python-packaging: stable 24.0 (bottled)
Core utilities for Python packages
https://packaging.pypa.io/
/opt/homebrew/Cellar/python-packaging/24.0 (53 files, 372KB) *
  Poured from bottle using the formulae.brew.sh API on 2024-03-25 at 10:36:03
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/p/python-packaging.rb
License: Apache-2.0 or BSD-2-Clause
==> Dependencies
Build: python-flit-core ✘, python@3.11 ✔, python@3.12 ✔
==> Analytics
install: 19,387 (30 days), 63,842 (90 days), 205,027 (365 days)
install-on-request: 821 (30 days), 1,497 (90 days), 3,796 (365 days)
build-error: 24 (30 days)
% brew list python-packaging
/opt/homebrew/Cellar/python-packaging/24.0/lib/python3.11/ (23 files)
/opt/homebrew/Cellar/python-packaging/24.0/lib/python3.12/ (23 files)
%

実は python の setuptools も必要というオチだったので、brew install python-setuptools で setuptools も入れて、python 3.12 の環境でも Node.js の grpc パッケージのインストールができるようになった。

git の alias の紹介、あるいは、init.defaultBranch を設定しておくと生活が豊かになる話

git の操作は基本的に Emacs から Magit を使っているけど、ちょっとした操作とかはターミナルから git コマンドを直接実行することもある。で、よく使うコマンドは git alias でいい感じにしておくとタイプ数が少なくなったり、シュッと使えて体験がよくなる。

というわけで、僕の ~/.gitconfigalias セクションにはこんな感じで設定している

[alias]
  aliases = config --get-regexp alias
  log-graph = log --graph --date=short --pretty=format:'%Cgreen%h %cd %Cblue%cn %Creset%s'
  log-all = log --graph --all --color --pretty='%x09%h %cn%x09%s %Cred%d%Creset'
  log-pr = !git log --stat --no-merges $( git merge-base origin/$( git config --get init.defaultBranch ) HEAD )...HEAD
  diff-pr = !git diff $( git merge-base origin/$( git config --get init.defaultBranch ) HEAD )...HEAD
  remotes = remote -v
  f = fetch origin --prune
  r = rebase
  s = status
  c = switch
  cd = !git switch $( git config --get init.defaultBranch )
  co = checkout
  sui = submodule update --init
  pf = push --force-with-lease --force-if-includes
  wc = whatchanged
  ls = log --stat --no-merges
  lp = log -p

git config --get init.defaultBranch というコマンドは git のデフォルトブランチを取得しているんだけど、現代ではリポジトリごとになることが普通だと思う。とくに新しく作業ブランチを作成する時にリポジトリごとに異なるデフォルトブランチを気にする必要なく、git cd しておけば良いので、事故を防ぐことができて便利に使っている。

で、肝心のデフォルトブランチの設定は GitHubリポジトリだったら、gh コマンドからリポジトリに設定されているデフォルトブランチを gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name' で取得できるので、以下のような関数を .zshrc に登録している。

set-default-branch () {
        git rev-parse 2> /dev/null || return 0
        defaultBranch=$( gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name' )
        [ -z ${defaultBranch} ] && return 0
        git config --local init.defaultBranch ${defaultBranch}
        echo "set init.defaultBranch : ${defaultBranch}"
}

Github Actions と blogsync を使ってはてなブログの記事を管理する

前々からこのブログを Github で管理したいなーと思っていたけど、なかなか手をつける気になっていなかった。なぜかこの連休中に機運が高まったのでえいやで Github Actions と blogsync を使った仕組みを導入した。

大筋では

  • 新しいエントリを追加する P-R を作る
  • 作業ブランチでの作業中は下書き状態にしておく
  • P-R をメインブランチにマージするときに公開する

といった運用を想定している。

特に P-R ベースでの作業フローはブログの執筆者のほかに公開前に投稿内容をチェックするような体制を想定している。

仕組み

Github Actions のワークフローは以下の3つを実装している。

  • 下書き状態で新しい記事をはてなブログに投稿するワークフロー : blogsync-post.yaml
    • blogsync post --draft を実行している
    • P-R の最初に動くことを想定している
    • blogsync post を実行した後は blogsync/created ラベルを P-R につける
  • はてなブログに投稿している記事を更新するワークフロー : blogsync-push.yaml
    • blogsync push を実行している
    • P-R 中に記事のコミットがつまれたときに
  • 下書き状態の記事を公開状態にするワークフロー : blogsync-publish.yaml
    • 記事のメタデータDraft の値を変更して blogsync push を実行している
    • P-R がマージされたときに実行される

blogsync がはてなブログAtom Pub API を使うときの認証情報は設定ファイルか環境変数で扱うことができる。リポジトリに秘密情報は含めたくないので環境変数を使うことになるんだけど、複数の執筆者が想定される環境では、Github の世界とはてなの世界でユーザのマッピングが必要になる。今回は json ファイルで Github の login 名とはてな ID (とその API Key を保存しているシークレットの名前) を定義して、ワークフロー中で取得している。ワークフロー中での取得している様子は以下のとおり。

- name: hatenaid
  id: hatena_id
  env:
    PR_AUTHOR: ${{ github.event.pull_request.user.login }}
  run: |
    echo hatena_id=$(jq --arg github_login ${PR_AUTHOR} '.[$github_login].hatena_id' map.json) | tr -d '"' >> $GITHUB_OUTPUT
    echo password_secret_name=$(jq --arg github_login ${PR_AUTHOR} '.[$github_login].password_secret_name' map.json) | tr -d '"' >> $GITHUB_OUTPUT

ひとまず、今の状態でしばらく使ってみていまいちなところとかはチマチマ直していくかという気分になっている。今のところ blogsync pull に相当するワークフローを作っていないのではてなブログの管理画面などで変更した内容を取り込むときには個別で頑張る必要があるはず。

追記

この記事を公開するための準備ではじめて使ったら P-R にラベルを付与するところで、

GraphQL: Resource not accessible by integration (addLabelsToLabelable)

というエラーが出て失敗してしまった。これは Github token のデフォルトの権限が参照しか付与されていないからだった。リポジトリの Settings から Workflow permissions が「Read repository contents and packages permissions」になっているので「Read and write permissions」に変更するか、ワークフローの定義で個別に指定すればよいはず。面倒なのでデフォルトの権限を「Read and write permissions」にしちゃったけど、本来は contents: writepull-requests: writeissues: write の権限があれば十分なはず。

ある github organization のリポジトリを手元に持ってくる時に使っているアレ

github apigh api で呼び出していい感じに

  • 手元になければ ghq get して、デフォルトブランチを設定する
  • アーカイブ済みのリポジトリは消していいかを聞いてから消す

ということをしてくれる。

#!/usr/bin/env zsh

for repo in $( gh api --paginate --jq '.[] | select( .archived != true ) | .full_name' "/orgs/${org_name}/repos" ); do
  repodir=$( ghq list -p -e "${repo}" )
  if [ "x${repodir}" == "x" ]; then
    ghq get "git@github.com:${repo}.git"
    pushd "${repodir}"
    git config --local init.defaultBranch $( gh api "/repos/${repo}" --jq '.default_branch' )
    popd
  fi
done

for repo in $( gh api --paginate --jq '.[] | select( .archived == true ) | .full_name' "/orgs/${org_name}/repos" ); do
  repodir=$( ghq list -p -e "${repo}" )
  if [ "x${repodir}" != "x" ]; then
    echo "remove ${repo}? (y/N): "; read -q && rm -rf "${repodir}"
  fi
done

${org_name} は目的の Organization を入れる。

cdktf 再入門

どうも、型にハマらない人生を歩みたいけど、コードを書く時は型が欲しい id:nabeop です。

1年くらい前に cdktf に入門したあとは changelog は追っていたけど、使う機会がなかった。 ちょうど AWS アカウント単位で検証環境を複数作ることになったので、cdktf に再入門してみた。

最初に触ったときは v0.0.18 で現時点では v0.9.0 だった。

続きを読む

AWS Certified Security - Specialty に合格しました

会社の資格取得補助を利用して、AWS Certified Security - Specialty の資格を取得できた。

一昨年は AWS Certified Solutions Architect – Associate に合格したけど、去年は AWS Certified Advanced Networking - Specialty に落ちちゃったので、今年こそは絶対に取るぞと気合を入れていたのでなんとか取得できて嬉しい。

AWS Certified Security - Specialty は AWS が認定している資格の1つで公式の説明では以下のようになっています。

この資格は、組織がクラウドイニシアチブを実装するための重要なスキルを持つ人材を特定して育成するのに役立ちます。AWS Certified Security – Specialty を取得することで、AWS クラウドにおけるデータやワークロードのセキュリティ確保に関する専門知識が認定されます。 https://aws.amazon.com/jp/certification/certified-security-specialty/?ch=sec&sec=rmg&d=1

詳しくは公式の説明を読んでもらうとわかると思うけど、要するに AWS のサービスのうちセキュリティ分野に特化した専門知識を持っているかを試される、という感じです。最近は AWS アカウント周りの整備をやっていてセキュリティ周りもスコープに入っていたので仕事でやっていることの再確認も兼ねてちょうど良い感じだった。

AWS Certified Security - Specialty の取得を考えている人の参考になるかもしれないので、資格取得のためにやった準備とかをまとめておこうと思う。

会社で資格取得補助をやっているという関係で毎年この時期に資格取得のための勉強会が立ち上がるので、その流れにのって週一回に1時間ほど同じに用に資格取得を目指す同僚とオンラインで黙々と勉強をしていました。このエントリも勉強会の時間に書いています。

すでに AWS については3年程の実務経験があって、直近では AWS アカウントのセキュリティやガバナンス強化の設計をしていたので、ある程度の専門知識はありそう(むしろ無いと困る)という感じだったので、初手で模試を受けてみました。Solutions Architect の Associate に合格していたので、クーポンが使えるのでお得だったんだけど、僕がうけた模試は結果がわかるけど、具体的にどの問題が不正解だったのかがわからないタイプだった。一緒に勉強会に参加していた id:wtatsuru さんは AWS Skill Builder の模試を受けていて、こっちは解答と解説がでていたそうなので、失敗したなーと思っています。

wtatsuru.hatenadiary.com

最初に受けた模試の結果では総合スコアが 75% で合格ラインには達していそうだったけど、スコアが低い分野があったので、スコアが低かった分野を中心に勉強をするという方針を立てた。

勉強の教材は AWS Certified Security - Specialty に特化した参考書が会社に転がっていたので、一通り目を通してみて、スコアが低かった分野は Blackbelt で知識を保管するという作戦です。

会社に転がっていた参考書はこれ

試験はリモートでの試験も選択可能だったけど、会社の近くに試験会場があったし、リモート受験して苦労している同僚もみていたので、試験会場で直接受けた。試験の内容は模試と同じような雰囲気だったのでどこかの時点で模試は受けておいた方が良いと思う。あと、AWS におけるセキュリティといえば IAM や KMS 、GuardDuty や Security Hub とかが思い浮かぶけど、試験ではそのようなセキュリティサービスに加えて、CloudFront などのサービスでセキュリティを担保するため構成が問われるような問題も出ていた。

来年も勉強会は開催されると思うので、次回は DevOps Engineer Professional か Solutions Architect - Professional あたりに挑戦するかと思っている。

OpenSSH で複数の公開鍵を使い分ける

人間、生きていると複数の公開鍵を優先順位をつけて使い分けたくなる時があります。例えば、OpenSSH 8.8/8.8p1 で ssh-rsa 鍵による署名が無効になるので、この機会に ssh-rsa 鍵を捨てたいけど、本当に全部のサーバを置き換えたか自信がないので、古い鍵にフォールバックできるようにしておきたい、などです。

ssh_config (5) では以下のように IdentityFile を複数定義することができると書いてあります。

 IdentityFile
         Specifies a file from which the user's DSA, ECDSA, Ed25519 or RSA authentication identity is read.  The default is
         ~/.ssh/id_dsa, ~/.ssh/id_ecdsa, ~/.ssh/id_ed25519 and ~/.ssh/id_rsa.  Additionally, any identities represented by the
         authentication agent will be used for authentication unless IdentitiesOnly is set.  If no certificates have been explic-
         itly specified by CertificateFile, ssh(1) will try to load certificate information from the filename obtained by appending
         -cert.pub to the path of a specified IdentityFile.

         Arguments to IdentityFile may use the tilde syntax to refer to a user's home directory or the tokens described in the
         TOKENS section.

         It is possible to have multiple identity files specified in configuration files; all these identities will be tried in
         sequence.  Multiple IdentityFile directives will add to the list of identities tried (this behaviour differs from that of
         other configuration directives).

         IdentityFile may be used in conjunction with IdentitiesOnly to select which identities in an agent are offered during
         authentication.  IdentityFile may also be used in conjunction with CertificateFile in order to provide any certificate
         also needed for authentication with the identity.

「all these identities will be tried in sequence.」なので、~/.ssh/config で以下のように書いたら ~/.ssh/id_ed25519~/.ssh/id_rsa/ の順番で鍵を使ってくれそうです。

IdentityFile ~/.ssh/id_ed25519
IdentityFile ~/.ssh/id_rsa

これは真でもあり偽でもあります。

具体的には ssh-agent に ~/.ssh/id_rsa が登録されている状態で、設定ファイルを上記のように書き換えた場合、既に ssh-agent に登録されていた ~/.ssh/id_rsa が使用する鍵ペアの公開鍵として使われ、 次に ~/.ssh/id_ed25519 の鍵ペアが使われていました。

このため、ssh-add -D して ssh-agent に記憶喪失になってもらうか、ssh-agent をリスタートさせる必要がありました。

ssh-ed25519 公開鍵を受け付けない ssh サーバを準備して意図した優先順位で鍵ペアが使われているかを確認したら、ssh -v の出力から意図通りに ssh-rsa にフォールバックしてくれることを確認しました。

フォールバックに成功した例:

# id_ed25519 -> id_rsa の順番で使いたいと表明している
debug1: Will attempt key: /Users/nabeop/.ssh/id_ed25519 ED25519 SHA256:PMVZkeWRos6nqGxKtIJXfAlJK6H2z1GOjxnWPTm9/T8 explicit agent
debug1: Will attempt key: /Users/nabeop/.ssh/id_rsa RSA SHA256:wMZ0uFG/ig9QLVRaamjzL6JKCDmB6SuqM4V5lpaqkqU explicit agent
# id_rsa にフォールバックした
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering public key: /Users/nabeop/.ssh/id_ed25519 ED25519 SHA256:PMVZkeWRos6nqGxKtIJXfAlJK6H2z1GOjxnWPTm9/T8 explicit agent
debug1: Authentications that can continue: publickey
debug1: Offering public key: /Users/nabeop/.ssh/id_rsa RSA SHA256:wMZ0uFG/ig9QLVRaamjzL6JKCDmB6SuqM4V5lpaqkqU explicit agent
debug1: Server accepts key: /Users/nabeop/.ssh/id_rsa RSA SHA256:wMZ0uFG/ig9QLVRaamjzL6JKCDmB6SuqM4V5lpaqkqU explicit agent
Authenticated to 127.0.0.1 using "publickey".

ssh-ed25519 が使えた例:

# id_ed25519 -> id_rsa の順番で使いたいと表明している
debug1: Will attempt key: /Users/nabeop/.ssh/id_ed25519 ED25519 SHA256:PMVZkeWRos6nqGxKtIJXfAlJK6H2z1GOjxnWPTm9/T8 explicit agent
debug1: Will attempt key: /Users/nabeop/.ssh/id_rsa RSA SHA256:wMZ0uFG/ig9QLVRaamjzL6JKCDmB6SuqM4V5lpaqkqU explicit
# id_ed25519 が使われた
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering public key: /Users/nabeop/.ssh/id_ed25519 ED25519 SHA256:PMVZkeWRos6nqGxKtIJXfAlJK6H2z1GOjxnWPTm9/T8 explicit agent
debug1: Server accepts key: /Users/nabeop/.ssh/id_ed25519 ED25519 SHA256:PMVZkeWRos6nqGxKtIJXfAlJK6H2z1GOjxnWPTm9/T8 explicit agent
Authenticated to 127.0.0.1 using "publickey".