40目前、首に真綿を感じる日々
これは 趣 Advent Calendar の12日目の記事です。 2025年もお疲れ様でした。 私自身の2025年について振り返りたいと思います。 自分時間を有効活用できない 2025年は激動の年でした。転職、子供の転園などイベントが目白押しでした。 新しい仕事、家事育児。自分の時間と呼べるものは寝る前の2時間と休日に子供が昼寝をする2時間程度しかありません。 週18時間。これが私に残された時間です。 その18時間も子供が病気で早退したとなると勤務時間を補うことに使われます。 懺悔すると、この貴重な時間をまったく有用に使えていません。 仕事と育児に疲れ、脳が考えることを拒否し個人開発は止まり読書も目が滑ります。 結果、YouTubeでお気に入りのチャンネルを見て時間を溶かしています。 あぁ、ミドルエイジクライシス すると久しぶりに友人や同僚と会っても会話が生まれません。 趣味は細分化し共通の話題がなく、新しい技術を追っていないため話題提供できません。 そうなると口から出るのは伝家の宝刀「健康」「育児」「投資」です。 飲みの場は楽しいですが、帰りの電車ではその話題を思い出して身も心もおじさんになってしまったことに虚しさを感じます。 でも幸せです 妻と子供の3人で歩いているとき急に俯瞰した目線になり「ああ、これが幸せなんだ」と感じました。 35歳を過ぎて郊外にマンションを購入し、休日は家族で車で30分のショッピングモールに行く。 自我が芽生え始めた子供に手を焼きつつ、子供が寝た瞬間を見計らって妻とスイーツを食べる。 恵まれているとしか言いようがない状況です。 LLMの衝撃 話は変わりますが、今年は猫も杓子もLLMの年でしたね。 私もその片鱗に触れて老後まで自身の市場価値が維持できなくなる、タイムリミットが早まっていると感じます。 今のところ成果物に責任持てる範囲内でLLM利用することでうまく付き合えてると思います。 この「成果物に対する責任」が人に帰属している間は食べていけそうですが、責任すら持てなくなると終わるように思います。 ここまでのまとめ 2025年の私の心理的状況をまとめると以下のようになります。 家族と過ごし子供の成長を日々感じられる幸福感 一方で、仕事と家事育児に追われ自己投資できないことへの焦燥 LLMの台頭により自身の市場価値の低下が想像以上に早くなることへの不安 2025年はこの真綿が首に巻き付いてることにようやく気づけた1年であったように思えます。 LLMが生成できないトークン LLMは確率計算上次にでるトークンを予測して文章を生成しているらしいです。 世間の大多数に流れていった人間はLLMに代替されやすいと思うのです。 今まで私は効率的に学習することを念頭に置いて、名著と言われる本を読み、メインストリームにある技術を中心に触れてきました。 このような本や技術は他の方による解説も多くLLMに聞いてもある程度精度の高い結果を返してくれるでしょう。 どうやら今の私はLLMに代替されやすい立ち位置にいるようです。 でも逆にLLMがまだ提示できないニッチな情報を持っていれば、もしかしたら酒のツマミ程度には話を聞いてくれるかもしれません。 生の情報を蒸留する なるべく他人の思考が介在する情報を避け、自分で生の情報を取得し内面化する。 そのような訓練をしていく必要がありそうです。 もちろん深堀りする努力が足りないと言われたらそれまでです。というか足りないんだと思います。 40までの2年間 これから40まであと2年あります。 私には週18時間しかありません。この中で全ての生の情報を取ることはできません。 そこでジャンルをしぼり、そのジャンルに対しては他人の思考がなるべく介在していない情報を自主的に摂取しようと思います。 この18時間もなるべく質を上げる必要があります。 YouTubeともうまく付き合えるように仕組みづくりをしていく必要がありそうです。 18時間を拡張する方向でもいくつか試してみたいです。 誰も知らないニッチな話題を提供できるようになったら、きっと飲み会の帰りも楽しくなると思います。 それでは来年もよろしくお願いします。
Git Submodule系操作まとめ
git submoduleのコマンドをよく調べるのでまとめてしまう https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E3%83%84%E3%83%BC%E3%83%AB-%E3%82%B5%E3%83%96%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB を参考にしています 1. 初期化する git submodule init 2. 更新する git submodule update 1, 2を合わせて git submodule update --init 3. diffが意味不明 yoan $ git diff diff --git a/submodule b/submodule index aaaaaaaaaa..bbbbbbbb 160000 --- a/submodule +++ b/submodule @@ -1 +1 @@ -Subproject commit aaaaaaa... +Subproject commit bbbbbbb... diff差分がよくわからないときは--submoduleオプションを追加する yoan $ git diff --submodule Submodule /submodule aaaaaaaaaaa..bbbbbbbbbbb (rewind): < Merge pull request #2 from hogehoge defaultで有効化もできる yoan $ git config --global diff.submodule log 4. 間違ったcommitになってしまう この記事を書こうと思った本命。 3の例でdiffに(rewind)が出ている (rewindは巻き戻りという意味) 実際、originではaaaaaaaaaaaが最新なのになぜかlocalでは一つ前のbbbbbbbbbbに変えようとしている この状態が何故起こったのかはよくわからないがsubmodule updateをしても無駄だった ...
Ansible
ansibleの使い方 install brew install ansible 接続確認 ❯ cat hosts [server] xxx.xxx.xxx.xxx 実行 ❯ ansible server -i hosts -m ping -u ubuntu 18.183.206.58 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" } playbook適用 ❯ cat playbook.yml --- - name: Update web servers hosts: server remote_user: ubuntu become: true tasks: - name: Run the equivalent of "apt-get update" as a separate step ansible.builtin.apt: update_cache: yes syntax-check ansible-playbook playbook.yml --syntax-check dryrun ansible-playbook playbook.yml -i hosts -C -D 適用 ansible-playbook playbook.yml -i hosts playbookの探し方 https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html ...
Journalctl の使い方
忙しい人向け journalctl -u mariadb.service -xe serviceを指定する journalctl -u mariadb.service tailする journalctl -e tail -f journalctl -f 詳細情報 journalctl -xe
Multiplayer Game Programming読本
multiplayer game programmingという本を気になる部分だけつまみ食いして読んでいる 今日はChapter 4にあった"Compression"をまとめる 英語の見出しは本文のタイトルを引用し、日本語の見出しは私がみやすさでつけたもの Compression マルチプレイのリアルタイム性のあるゲームであればあるほど、通信するオブジェクトと頻度が増え必要な通信帯域が増加する そのため、可能な限り無駄な情報は削減して通信を行う必要がある この章では低レベルな圧縮方法の紹介を行っている Sparse Array Compression 本で扱っているサンプルゲームでは名前を128byteまで許容されている。 名前の配列に128byteの配列を利用するのがプログラムとしては妥当だが、128byte使い切らないユーザーも含まれるため不要なスペースができてしまう。 そこで、名前長 + 名前というバイト列にする事で余計なスペースを除去するという圧縮方法 Entropy Encoding 以下の章ではオブジェクトの方向と回転を表す7つのfloatの圧縮方法について記載されている 参考書ではbyte配列を渡す想定をしているが、理解を助けるために下記のような構造体の形式で書いていく // 合計 28byte type XXX struct { type Location struct { x float y float z float } type Quartunion struct { qx float qy float qz float qw float } } 頻出する状態には短いデータを与えましょう。という内容 サンプルゲームではキャラがほとんどY=0のポジションにいる特性を利用してY=0ならばtrue, そうでなければfalse+floatの情報を送っている // 合計 24byte + 1bit type XXX struct { type Location struct { x float ybit bool y float z float } type Quartunion struct { qx float qy float qz float qw float } } Fixed point Locationの他の2軸の圧縮をする この内容はゲームの特性と相談になるが、整数値にすることでサイズを減らそうというもの 本では縦横に40000(おそらくpx)が表現できれば良いため、int16でよいという内容だった ...
GKEからのGCPへの認証をまとめる
GKE cluster作成時にも指定できるアクセススコープはレガシー扱いになっている https://cloud.google.com/kubernetes-engine/docs/how-to/access-scopes#what_are_access_scopes とはいえチュートリアルには--scopeを利用したものも見受けられる https://agones.dev/site/docs/installation/creating-cluster/gke/ アクセススコープは手軽だが細かい設定ができない。ServiceAccountを使うと必要最低限の権限のみ付与する事ができるので、セキュリティの品質が保たれるためGoogleとしてはServiceAccountを推している。という認識でいいのかな. GKEの権限関係で迷わないよう、サービス アカウントを使用した Google Cloud への認証を題材にやり方を整理する アクセススコープを用いた認証 ServiceAccountを用いた認証 (おまけ) ServiceAccount認証にWorkload Identityを用いる 事前準備 題材に則りpubsub topicを作成し、publishとsubscribeができるか検証する pubsub topicの作成 gcloud pubsub topics create echo gcloud pubsub subscriptions create echo-read --topic=echo ソースコード取得 git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples cd kubernetes-engine-samples cloud-pubsub/deployment以下のyamlファイルを実際に適応していく 1. アクセススコープを用いた認証 クラスタ作成 gcloud container clusters create pubsub-test --scopes=gke-default,https://www.googleapis.com/auth/pubsub --zone asia-northeast1-a scope オプションについて --scopesオプションをつけることでクラスタに権限を付与できる gke-defaultは下記のscopeをまとめたエイリアスになる Alias URI gke-default https://www.googleapis.com/auth/devstorage.read_only https://www.googleapis.com/auth/logging.write https://www.googleapis.com/auth/monitoring https://www.googleapis.com/auth/service.management.readonly https://www.googleapis.com/auth/servicecontrol https://www.googleapis.com/auth/trace.append gcloud container clusters create --helpをすると他のエイリアスもあるのでみてみると良い (webのmanにも載っている) また複数指定したいのでカンマ区切りで指定している scopeの一覧から選んで付与していく. (権限を追加する) 後から権限を追加したくなるケースはどうするか? scopeを変えたいならば、node-poolsを作り変える事しかできない # 新規作成 gcloud container node-pools create new-pools --cluster pubsub-test --scopes=gke-default,https://www.googleapis.com/auth/pubsub,https://www.googleapis.com/auth/something # 古いnodeを削除 gcloud container node-pools delete old-pools --cluster pubsub-test デプロイ kubectl apply -f cloud-pubsub/deployment/pubsub.yaml kubectl get po NAME READY STATUS RESTARTS AGE pubsub-8cb8475c8-tvxwv 1/1 Running 0 95s 確認 Publish gcloud pubsub topics publish echo --message="Hello, world\!" messageIds: - '3909513331830980' Subscribe kubectl logs -f pubsub-8cb8475c8-tvxwv Pulling messages from Pub/Sub subscription... [2022-03-21 06:37:27.265195] Received message: ID=3909513331830980 Data=b'Hello, world!' [2022-03-21 06:37:27.265294] Processing: 3909513331830980 [2022-03-21 06:37:30.267957] Processed: 3909513331830980 2. ServiceAccountを用いた認証 Googleが推奨しているのはこちら サービス アカウントを使用した Google Cloud への認証と同じ事をしているだけなので説明は省く ...
coreutilsのソースコードをDLする方法
aspのinstall $ sudo pacman asp aspの簡単な使い方 # packageの一覧 $ asp list-all # packageの取得 $ asp checkout coreutils coreutilsのダウンロード $ asp checokut coreutils $ cd coreutils $ makepkg -o # buildはせずDLだけするオプション 詰まったところ GPGキーがverifiyされない makepkgの実行中で下記のようなエラーが発生した -> Found 01-fix-fs72253.patch ==> Validating source files with sha256sums... coreutils-9.0.tar.xz ... Passed coreutils-9.0.tar.xz.sig ... Skipped 01-fix-fs72253.patch ... Passed ==> Verifying source file signatures with gpg... coreutils-9.0.tar.xz ... FAILED (unknown public key DF6FD971306037D9) ==> ERROR: One or more PGP signatures could not be verified! GNUのページに書いてあるコマンドを実行する ...
Kotest Listener
kotest kotlinでテストFWを増やしている Listenerの確認 TestListener beforeAnyとbeforeTest, afterAnyとafterTestは同じらしいので一つにまとめます import io.kotest.core.spec.style.DescribeSpec import io.kotest.matchers.shouldBe class ListenerSpec: DescribeSpec({ describe("listener hook check") { beforeContainer { println("beforeContainer") } afterContainer { println("afterContainer") } beforeEach { println("beforeEach") } afterEach { println("afterEach") } beforeTest { println("beforeAny/beforeTest") } afterTest { println("afterAny/afterTest") } beforeSpec { println("beforeSpec") } afterSpec { println("afterSpec") } context("Simple") { it("Simple test") { println("--- Simple test") 1 shouldBe 1 } } } }) beforeContainer beforeAny/beforeTest beforeEach beforeAny/beforeTest --- Simple test afterEach afterAny/afterTest afterContainer afterAny/afterTest afterSpec before, afterともにContainer > Any/Test, Each > Any/Testの流れになっている beforeとafterでstackの様にコールされるわけではない beforeSpecが呼び出されない 詳細を調べる class ListenerSpec: DescribeSpec({ // ... describe("listener hook check") { describe("Describe") { it("Simple test") { println("--- Simple test") 1 shouldBe 1 } } } }) beforeContainer beforeAny/beforeTest beforeEach beforeAny/beforeTest --- Simple test afterEach afterAny/afterTest afterContainer afterAny/afterTest afterSpec Listener順序において、describeとcontextの差異は見られない class ListenerSpec: DescribeSpec({ describe("listener hook check") { describe("Describe") { it("List 1 test") { println("--- List-1 test") 1 shouldBe 1 } it("List 2 test") { println("--- List-2 test") 1 shouldBe 1 } } } }) beforeContainer beforeAny/beforeTest beforeEach beforeAny/beforeTest --- List-1 test afterEach afterAny/afterTest beforeEach beforeAny/beforeTest --- List-2 test afterEach afterAny/afterTest afterContainer afterAny/afterTest afterSpec Containerが消えた class ListenerSpec: DescribeSpec({ // ... describe("listener hook check") { describe("Describe") { context("Context") { it("Simple test") { println("--- Simple test") 1 shouldBe 1 } } } } }) beforeContainer beforeAny beforeTest beforeContainer beforeAny beforeTest beforeEach beforeAny beforeTest --- Simple test afterEach afterAny afterTest afterContainer afterAny afterTest afterContainer afterAny afterTest afterSpec nestするとcontainer, any, testが追加される class ListenerSpec: DescribeSpec({ describe("listener hook check") { describe("Describe") { describe("Describe") { it("Simple test") { println("--- Simple test") 1 shouldBe 1 } } } } }) beforeContainer beforeAny beforeTest beforeContainer beforeAny beforeTest beforeEach beforeAny beforeTest --- Simple test afterEach afterAny afterTest afterContainer afterAny afterTest afterContainer afterAny afterTest afterSpec
vscode extensionのTreeViewを触ってみる
チュートリアルを行う https://code.visualstudio.com/api/extension-guides/tree-view チュートリアルではサイドバーにnode dependenciesを表示し、各packageのpackage.jsonを表示するサンプルを作っている。 しかしチュートリアルをそのまま試してもEditorにpackage.jsonの内容が表示されなかった。 その理由が知りたくて色々と調査した 結果として、githubのサンプルを実行するとわかるが、クリック時に発火するコマンドを指定しないと行けなかった コマンド発火のコードリーディング TreeViewをみると、チュートリアルに加えてvscode.Commandを付与している return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.None, { command: 'extension.openPackageOnNpm', title: '', arguments: [moduleName] }); 併せてopenPackageOnNpmの登録もチュートリアルから追加されている vscode.commands.registerCommand('extension.openPackageOnNpm', moduleName => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`https://www.npmjs.com/package/${moduleName}`))); 実際、これらを実装するとクリックしたらNPMのwebpageに飛ぶ挙動が確認できた TreeItemオブジェクトはcommandをフィールドに保存しているが、自クラスでフィールドを操作している形跡はない つまり、vscode本体でTreeItem.commandを呼び出していると考えられる vscode本体をコードリーディング TreeItemの定義はこちら command自体は存在している よって、vscode本体でTreeItem.commandを呼び出す仮説は正しそう https://github.com/microsoft/vscode/blob/main/src/vs/vscode.d.ts#L9136 この説明を見るに、clickをフックしてcommandを呼んでいる模様。 async resolveTreeItem(treeItemHandle: string, token: vscode.CancellationToken): Promise<ITreeItem | undefined> { if (!this.dataProvider.resolveTreeItem) { return; } const element = this.elements.get(treeItemHandle); if (element) { const node = this.nodes.get(element); if (node) { const resolve = await this.dataProvider.resolveTreeItem(node.extensionItem, element, token) ?? node.extensionItem; // Resolvable elements. Currently only tooltip and command. node.item.tooltip = this.getTooltip(resolve.tooltip); node.item.command = this.getCommand(node.disposableStore, resolve.command); return node.item; } } return; } https://github.com/microsoft/vscode/blob/8aff878db2542eea2dfb571c8bb485118bbb3113/src/vs/workbench/api/common/extHostTreeViews.ts#L376-L392 TreeItemを取得するときにクリックハンドラをgetCommandで取得していた
go getで取得したコマンドの名前を変更する
yeomanを入れて実行したところエラー ❯ yo code must specify 3 arguments 確認したところspannerのgeneratorであるyoがすでに入っていました… ❯ yo help yo is a command-line tool to generate Go code for Google Cloud Spanner. (混乱を避けるため、以降ではコードブロック以外yoはspannerのコードジェネレートツールを指します) 両方利用したかったのでspanner generatorのyoを別名に書き換える方針で決めました。 その名もgoyoです(ババーン) 本題 ではどうやったらgo getで取得したコマンド名を変えられるのか? go getのオプションを探しましたがありませんでした そこで仕方なくyoの名前自体を変更します yoはバイナリが入っていると思ったらシェルスクリプトでした (goenvを使っているとそうなる?) ❯ file ~/.anyenv/envs/goenv/shims/yo /Users/motoki.yoan/.anyenv/envs/goenv/shims/yo: Bourne-Again shell script text executable, ASCII text ❯ cat ~/.anyenv/envs/goenv/shims/yo #!/usr/bin/env bash set -e [ -n "$GOENV_DEBUG" ] && set -x program="${0##*/}" if [[ "$program" = "go"* ]]; then for arg; do case "$arg" in -c* | -- ) break ;; */* ) if [ -f "$arg" ]; then export GOENV_FILE_ARG="$arg" break fi ;; esac done fi export GOENV_ROOT="/Users/me/.anyenv/envs/goenv" exec "/Users/me/.anyenv/envs/goenv/libexec/goenv" exec "$program" "$@" programがyoを表しているように見えますが、##*/が意味不明です ...