Colima on ARM Macでaarch64とx86_64のDocker環境を切り替えて使う

Colimaにはプロファイル( profile )という概念があり、--profile によってVM環境を切り替えることができます。

また --arch オプションでVMのイメージを切り替えることも可能です。

上記を利用してaarch64とx86_64のDocker環境を両方準備してみます。

方針

ARM Macなので基本的には aarch64(ARM)をデフォルトにします

profile ISA
default aarch64
x64 x86_64

各環境を作成

まずaarch64の環境を作成します。

$ colima start --cpu 8 --memory 8 --disk 128 --mount '~/src/:w' --mount '~/tmp:w' --dns 8.8.8.8

次にx86_64の環境を x64 というプロファイル名をつけて作成します。

$ colima start --profile x64 --arch x86_64 --cpu 8 --memory 8 --disk 128 --mount '~/src/:w' --mount '~/tmp:w' --dns 8.8.8.8

この時点で以下のようになっています

$ colima list
PROFILE    STATUS     ARCH       CPUS    MEMORY    DISK
default    Running    aarch64    8       8GiB      128GiB
x64        Running    x86_64     8       8GiB      128GiB

ColimaはLimaのラッパーなので limactl でも確認してみます。

$ limactl list
NAME          STATUS     SSH                ARCH       CPUS    MEMORY    DISK      DIR
colima        Running    127.0.0.1:52650    aarch64    8       8GiB      128GiB    /Users/k1low/.lima/colima
colima-x64    Running    127.0.0.1:58188    x86_64     8       8GiB      128GiB    /Users/k1low/.lima/colima-x64

docker context list をみるとホストのdockerがどのDockerのエンドポイントを向いているのかがわかります。

$ docker context list
NAME            DESCRIPTION                               DOCKER ENDPOINT                               KUBERNETES ENDPOINT   ORCHESTRATOR
colima          colima                                    unix:///Users/k1low/.colima/docker.sock
colima-x64 *    colima [profile=x64]                      unix:///Users/k1low/.colima-x64/docker.sock
default         Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                                         swarm
desktop-linux                                             unix:///Users/k1low/.docker/run/docker.sock

colima-x64* がついているので、今は x86_64 環境を向いています。

contextの切り替えは docker context use [NAME] で切り替え可能です。

これで、Colima on ARM Macでaarch64とx86_64のDocker環境を切り替えて使えるようになりました。

--profile=x64 の環境ではx86_64のDockerイメージしかない mysql:5.7 なども動きます。

ベンチマーク

せっかくなのでベンチマークも取ってみます。

aarch64 環境

$ docker context use colima
colima
Current context is now "colima"
$ docker container run -it --rm --name debian-bullseye debian:bullseye /bin/bash
Unable to find image 'debian:bullseye' locally
bullseye: Pulling from library/debian
c7869242ae9a: Pull complete
Digest: sha256:10b622c6cf6daa0a295be74c0e412ed20e10f91ae4c6f3ce6ff0c9c04f77cbf6
Status: Downloaded newer image for debian:bullseye
root@faabf0fd5dcf:/# apt -y update && apt -y install sysbench
[...]
root@faabf0fd5dcf:/# uname -a
Linux faabf0fd5dcf 5.10.93-0-virt #1-Alpine SMP Thu, 27 Jan 2022 09:34:38 +0000 aarch64 GNU/Linux
root@faabf0fd5dcf:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
root@faabf0fd5dcf:/# sysbench cpu --cpu-max-prime=10000 --threads=8 --time=30 run
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 8
Initializing random number generator from current time


Prime numbers limit: 10000

Initializing worker threads...

Threads started!

CPU speed:
    events per second: 64490.28

General statistics:
    total time:                          30.0003s
    total number of events:              1934753

Latency (ms):
         min:                                    0.09
         avg:                                    0.12
         max:                                   21.01
         95th percentile:                        0.46
         sum:                               239149.80

Threads fairness:
    events (avg/stddev):           241844.1250/1938.40
    execution time (avg/stddev):   29.8937/0.01

root@faabf0fd5dcf:/#

x86_64 環境

$ docker context use colima-x64
colima-x64
Current context is now "colima-x64"
$ docker container run -it --rm --name debian-bullseye debian:bullseye /bin/bash
[...]
root@be6276f3da26:/# uname -a
Linux be6276f3da26 5.10.93-0-virt #1-Alpine SMP Thu, 27 Jan 2022 09:34:38 +0000 x86_64 GNU/Linux
root@be6276f3da26:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
root@be6276f3da26:/# sysbench cpu --cpu-max-prime=10000 --threads=8 --time=30 run
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 8
Initializing random number generator from current time


Prime numbers limit: 10000

Initializing worker threads...

Threads started!

CPU speed:
    events per second:  1077.72

General statistics:
    total time:                          30.0053s
    total number of events:              32340

Latency (ms):
         min:                                    3.38
         avg:                                    7.41
         max:                                   37.02
         95th percentile:                       13.22
         sum:                               239622.11

Threads fairness:
    events (avg/stddev):           4042.5000/46.19
    execution time (avg/stddev):   29.9528/0.01

root@be6276f3da26:/#

x86_64 on ARM はやはり遅いか......

参考

blog.amedama.jp

awsdoに ~/.aws/(config|credentials)の設定情報がなくてもAssumeRoleできるようにするためのオプションと、AssumeRoleしたロールでAWSコンソールにログインするオプションを追加した

1年以上前からの久しぶりのアップデートです。

k1low.hatenablog.com

--role-arn --source-profile

複数のAWSアカウントを横断して作業することがあり、AssumeRoleのための設定を~/.aws/(config|credentials)に書くのすら面倒になってきたので、設定なしでAssumeRoleができるようにするためのオプション --role-arn--source-profile を追加しました。

委任元のアカウントAのロール arn:aws:iam::AAAAAAAAAAAAAA:role/example-role を委任先のアカウントBに委任できるようにしている場合、

委任先のアカウントBの設定が ~/.aws/(config|credentials) にある場合は( profile-b )、

$ awsdo --role-arn=arn:aws:iam::AAAAAAAAAAAAAA:role/example-role --source-profile=profile-b -- aws ec2 describe-instance-status

と書けばいいし、アカウントBの設定がなくてもクレデンシャルがあれば

$ env AWS_ACCESS_KEY_ID=BBBBBbbbBBbBb AWS_SECRET_ACCESS_KEY=bbbbBBBbBBBBBBBB awsdo --role-arn=arn:aws:iam::AAAAAAAAAAAAAA:role/example-role -- aws ec2 describe-instance-status

と書けばAssumeRoleをした上でコマンドを実行できます。

設定を書かなくてもAssumeRoleできるということは、シェルスクリプトの一部だったりとかCIで実行したりなどがやりやすくなります。

例えばGitHub Actionsだと以下のようなワークフローでOIDC Connectして認証しながらさらにAssumeRoleしてコマンド実行ができたりします(未検証)。

name: AWS example workflow
on:
  push
permissions:
  id-token: write
  contents: read
jobs:
  assumeRole:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT }}:role/example-role
          aws-region: ${{ secrets.AWS_REGION }}
      - name: Run as ${{ secrets.AWS_ACCOUNT }}
        run: |
          aws sts get-caller-identity
      - name: Setup awsdo
        run: |
          export AWSDO_VERSION=X.X.X
          curl -L https://git.io/dpkg-i-from-url | bash -s -- https://github.com/k1LoW/awsdo/releases/download/v$AWSDO_VERSION/awsdo_$AWSDO_VERSION-1_amd64.deb
      - name: Run as ${{ secrets.AWS_ANOTHER_ACCOUNT }} using awsdo
        run: |
          awsdo --role-arn=arn:aws:iam::${{ secrets.AWS_ANOTHER_ACCOUNT }}:role/another-example-role -- aws sts get-caller-identity

--login

AssumeRoleしたロールでAWSマネジメントコンソール(Web)にログインできるオプションです。

$ AWS_PROFILE=myaws awsdo --login

もともと aws-vault にある機能で、便利だったので実装を参考にして awsdo にも追加しました。

というわけで

どんどんAssumeRoleしていきます。

github-script-ruby@v2でRubyのバージョンを指定できるようにした

github-script-rubyが何かについてはペパボのテックブログに書きましたので是非ご覧ください。

tech.pepabo.com

github-script-rubyは、簡単にいうと actions/github-scriptRuby版です。

今回は、github-script-ruby独自の機能としてRubyのバージョンを指定できるようにしました。

具体的には ruby-version:Rubyのバージョンを書くとそのバージョンで動きます。 簡単なサンプルとしては以下のような感じです。

- name: 'Hello Ruby version'
  uses: k1LoW/github-script-ruby@v2
  with:
    script: |
      repo = "#{context.repo.owner}/#{context.repo.repo}"
      number = context.issue.number
      comment = "Hello using Ruby v#{RUBY_VERSION}"
      github.add_comment(repo, number, comment)
    ruby-version: 2.7.5

あまり古いバージョンのRubyで動かすことにモチベーションはないですが、現状2.4.10までは動いています(コードを対応させればもう少し古いバージョンでも動くと思います)。

さて、どのように実現したのかというと ruby/setup-ruby の仕組みを利用させてもらうことで実現しています。

ruby/setup-ruby はどのように ruby-version: の機能を実現しているか

ruby/setup-rubyのREADME.mdに以下のように書かれています。

This action downloads a prebuilt ruby and adds it to the PATH.

The prebuilt releases are generated by ruby-builder and on Windows by RubyInstaller2. mingw and mswin builds are generated by ruby-loco. ruby-head is generated by ruby-dev-builder, jruby-head is generated by jruby-dev-builder, truffleruby-head is generated by truffleruby-dev-builder and truffleruby+graalvm is generated by graalvm-ce-dev-builds. The full list of available Ruby versions can be seen in ruby-builder-versions.js for Ubuntu and macOS and in windows-versions.js for Windows.

つまり、あらかじめruby/ruby-builder をはじめとする各リポジトリでプレビルドしたRubyをReleasesのAssetとして置いておき、ruby/setup-ruby側では ruby-version: で指定されたバージョンのRubyをダウンロード・展開し設置するという形で「任意のバージョンのRubyのセットアップ」を実現しています*1

github-script-ruby@v2 でも ruby-version: の機能を実現する

github-script-ruby@v2 でもruby/setup-rubyで使用しているAssetを使用させてもらっています。

まず、さまざまな処理系やバージョンにする必要はないと考えて「MRIのみ」かつ「パッチバージョンまで指定する」という制約をつけています。 そうすると対象となるのは ruby/ruby-builder で管理されているプレビルドRubyだけになります。

github-script-ruby@v2をstepで実行するタイミングで ruby-version: が指定されていれば ruby/ruby-builder からプレビルドRubyをダウンロードして設置して使用します。 また、bundlerが標準添付されていないバージョンの場合はbundlerもインストールします。

https://github.com/k1LoW/github-script-ruby/blob/6852aaffa26d2b760ff60c79b7086ef9a248a918/scripts/entrypoint.sh#L9-L23

これだけで簡単な ruby-version: の機能が実現できました。

その他にも

v2で、gemfile-path: (Gemfileのパスを指定する)や command:github-script-rubyの環境で任意のコマンドを実行する)なども追加しました。

これで、stepだけで独立した任意のバージョンのRuby環境を手に入れることができます。

(必要かどうかはおいておいて)例えば、

- name: 'Capistrano deploy'
  uses: k1LoW/github-script-ruby@v2
  with:
    gemfile-path: path/to/Gemfile
    command: bundle exec cap deploy --gemfile=path/to/Gemfile
    ruby-version: 2.7.5

みたいなこともできたりします。

というわけで、v1からあった gemfile:(Gemfileを書ける) や pre-command:github-script-rubyの環境で任意のコマンドを実行する)に続いて、さらに元ネタであったactions/github-scriptからさらに逸脱してきた感じです。

何か面白いユースケースを思いついたら是非教えてください。

*1:実際にはダウンロード以外にもキャッシュを使ったりruby-buildでビルドしたりなどをしています

2021年の振り返りと2022年の抱負

2021年の振り返り

2021年も2020年から引き続き内に籠った年だった気がします。

また、厄年だったことは全く関係ないのですが「エンジニアとしてこの先生き残るには」ということを考えることが多かったと思います。

私はこれまであまり将来を深く考えることはあまりなかったのですが、子供の著しい成長を間近で見ていると「ところで私は成長できているのだろうか」という現状に対する思いと「今後子供が十分に成長するまでサポートし続けることができるだろうか」という将来に対する思いとがごちゃ混ぜになって「うっ」となることが何回かありました。世の親をやっている人々はすごい。

一方で「自分が何が好きなのか」は言語化できました。

私はいろいろ好きだし大抵のものには意義を見出せる質なのですが、特に「開発者のための開発」が好きです。

その言語化を得て冷静に振り返ってみると「結局自分がやり続けたこととか成功体験の積み重ねの結果の"好き"なんだなあ」とも思いました。

会社では全方位でいろいろやらせてもらえる(結果は求められる)ので、引き続きやっていきたいです。

2021年の抱負は「インプットができるようにする」でした。

結果はどうだったかというと「できてないな」というのが正直な感想です。

「手持ちのカードが増えない」という事実は結構自分の心を乱している要因にもなっています。せめて強いカードを持っていればいいのですが難しい。

増えていない原因も自分なので、これはもう自分で動き出す以外は方法はないです。がんばれ。

OSS

今年はGitHub関係のものが多かった印象があります。GitHub Actionsは本当に良いですね。

今年最も力を入れたOSSは何かと問われたら octocov だと思います。

個人的にはtblsに近いレベルで「うまく設計がまとまったな」という感覚があります。うまくまとまると「小さな世界」を作れた感覚があって好きです。これを求めて懲りずにツールを作っている面もありそう。

octocovは2022年ももう少しだけやり残した実装を続けると思います。そしたらまたアイデアが降ってくるまでは手を離すことができそうです。

発表

継続的ドキュメンテーション関係の発表が多かったです。もう個人的には研究課題みたいになっています。もしくは趣味。

2022年の抱負

今年は「挑戦する」です。1年が終わった時に「今年はこれをやったな〜」と言いたい。

コンフォートゾーンから抜けるのが本当に苦手なので(むしろ嫌がる)、なんでもいいので、失敗でもいいので「やったな〜」を成し遂げたい。

チャンス(特に自身の感情の高まり)を逃さずに2022年もコードを書いていきたいです。

あ、あと分割キーボードを今年こそ手に入れたい。

今年もよろしくお願いします。

せめてリポジトリの各ディレクトリの概要説明だけでも欲しい思ったので dirmap というツールを作ってみた

既存の開発に参加するときや、0->1の開発をしているとき、いつも「せめてリポジトリの各ディレクトリの概要説明だけでも欲しい」と思っていました。

既存のプロジェクトに参加するときは「プロジェクトの理解をする側」、0->1のプロジェクトで開発をしているときは「説明をする側の立場」で、です。

Ruby on Railsのような基本のディレクトリレイアウト決まっていてもそのプロジェクトの独自性がでてきますし、Goのようにスタンダードなレイアウトがないのであればなおさら初見ではわかりません。

「じゃあREADME.mdにでも書いておけばいい」というのはその通りです。

ただ、概要説明であっても一度書いたら終わりではなく、更新は必要になります。特に0->1のプロジェクトの初期ではディレクトリレイアウトすら途中で変わるということはままあります。

(ここらへんは「継続的ドキュメンテーション」として私の興味のある分野です。私のSpeaker Deckのドキュメント系の発表は大体が継続的ドキュメンテーションにつながっています)

そこで、できるだけ簡単に各ディレクトリの概要説明を実現するために dirmap というツールを作ってみました。

github.com

dirmap

dirmapはディレクトリを再帰的に走査して各ディレクトリの「概要を説明していそうな箇所の文字列」を取得し「各ディレクトリの概要説明=ディレクトリマップ」を生成するツールです。

たとえば dirmap のリポジトリルートで dirmap generate を実行すると以下のような tree に近い出力を得ることができます。

$ dirmap generate
.
├── .github/
│   └── workflows/
├── cmd/ ... Commands.
├── config/ ... Configuration file.
├── matcher/ ... Implementation to find the string that will be the overview from the code or Markdown.
├── output/ ... Output format of the directory map.
├── scanner/ ... Implementation of scanning the target directory and its overview from the file system based on the configuration.
├── scripts/ ... scripts for Dockerfile.
└── version/ ... Version.

この Commands.scripts for Dockerfile. のような概要っぽい文字列はどこから取得しているかというと、「そのディレクトリにあるMarkdownファイルの見出しではない最初のパラグラフ」や「そのディレクトリにあるGoのソースコードに書かれているgodocのpackages Overviewにあたる部分」などです。

このように「そのディレクトリにあるファイルからそのディレクトリの説明となる文章を抽出すれば、各ディレクトリの概要説明の管理も最小限になるだろう」という算段です。

「概要っぽい文字列」の取得ルールは設定ファイルを渡すことで変更も可能です。built-inな markdown markdownHeading godoc 以外にも正規表現マッチもサポートしています。

設定ファイルは dirmap init で生成可能です。書かれている設定は dirmap のデフォルト設定(設定ファイルなしの場合の挙動と同じ)になります。

$ dirmap init
Create .dirmap.yml
# $ cat .dirmap.yml
targets:
- file: .dirmap.md
  matcher: markdown
- file: README.md
  matcher: markdown
- file: doc.go
  matcher: godoc
- file: "*.go"
  matcher: godoc

ディレクトリの概要は targets: に指定した走査条件に最初にマッチした文字列になります。

dirmap generate で出力できるフォーマットは今の所「treeライクなASCIIの出力」と「Markdown(GFM)のテーブル形式」の2つです(以下はテーブル形式)。

$ dirmap generate -t table
| Directory | Overview |
| --- | --- |
| .github/ |  |
| .github/workflows/ |  |
| cmd/ | Commands. ( [ref](cmd/doc.go) ) |
| config/ | Configuration file ( [ref](config/config.go) ) |
| matcher/ | Implementation to find the string that will be the overview from the code or Markdown. ( [ref](matcher/matcher.go) ) |
| output/ | Output format of the directory map ( [ref](output/output.go) ) |
| scanner/ | Implementation of scanning the target directory and its overview from the file system based on the configuration. ( [ref](scanner/scanner.go) ) |
| scripts/ | Scripts for Dockerfile ( [ref](scripts/.dirmap.md) ) |
| version/ | Version ( [ref](version/version.go) ) |

とりあえずdirmapを使って各ディレクトリの概要説明をいれつつ、新しくプロジェクトに入るメンバーのために開発者用ドキュメントをメンテナンスしてみようと思います。

Goのプロジェクトならgodocのpackage overviewの箇所だけ書いておけばいいだけなので楽です(他の言語も良いドキュメントフォーマットが存在するなら対応したいと思っています。ぜひ教えてください)。

これだけで開発開始までのオーバーヘッドが大幅に減るとは思ってはいませんが、継続的ドキュメンテーションの1つとして試してみようと思います。

ディレクトリ内のPDFとEPUBをタイトル付きでリストアップする ebk ls を作った

私は買った電子書籍を1つのディレクトリに保存しているのですが、ファイル名はダウンロードしたときのそのままにしていることが多いので、良く「あの書籍はどのファイルだっけ?」が発生していました。

電子書籍リーダーで管理すればいいとは思うのですが、それを整備することすらしていない有様です。

とりあえず「あの書籍はどのファイルだっけ?」や「あの書籍って買ってたっけ?」をなんとか解消したいと思っていました。

PDFファイルやEPUBファイルの電子書籍のタイトルだけでも一覧表示できれば上記が解消すると思っていたので、重い腰をあげて作りました。

github.com

ebk

上に貼ったツイート以上の情報はないのですが、 ebk ls [DIR] を実行するとディレクトリ内のPDFとEPUBのタイトルとファイル名をデリミタ(デフォルトは :)で区切って出力します。

$ ebk ls ~/path/to/books/ | grep oreilly
NFS & NIS 第2版:oreilly-4-87311-078-5e.pdf
ウェブオペレーション:oreilly-978-4-87311-493-4e.pdf
SQLアンチパターン:oreilly-978-4-87311-589-4e.epub
[...]
Go言語による並行処理:oreilly-978-4-87311-846-8e.epub
入門 監視:oreilly-978-4-87311-864-2e.epub
入門 Prometheus:oreilly-978-4-87311-877-2e.epub
みんなでアジャイル:oreilly-978-4-87311-909-0e.epub
ユニコーン企業のひみつ:oreilly-978-4-87311-946-5e.epub

使い方としては peco などのツールと合わせて使うことを想定していて、macOSだと以下のようなコマンドで、選択した電子書籍電子書籍リーダーで開くことができます。

$ open $(ebk ls /path/to/books/ --with-path -d '\t' | peco | awk -F '\t' '{print $2}')

f:id:k1LoW:20211122072335p:plain

タイトルを取得できなかった場合は ??? で表示されます。

というわけで

個人的にはもう満足していて ebk ls 以外のコマンドは作らなそうですが、また電子書籍メタデータで何かしたくなったら ebk に追加しようと思います。

私は、ebk のおかげで早速間違って二重購入するのを防げました。作った甲斐がありました。もっとちゃんと管理しろというお話ですが。

もし同じように「あの書籍はどのファイルだっけ?」に困っている方がいましたら使ってみてください。

go-githubのClientをいい感じに組み立ててインスタンスを生成するだけのgo-github-clientを作った

GitHubGitHub Actionsが好きで、いろいろツールを作ったりするのですが、毎回毎回go-githubのClientのインスタンス生成のために何行かコードを書いています。

最初の頃は GITHUB_TOKEN のことだけを考えていていたのが、その後GitHub Enterpriseのエンドポイントも考えるようになり、GitHub Actionsの登場からはActions上の環境変数をうまく使いたくもなり、GitHub CLI extensionがでてきてからは、GH_* という環境変数も考慮する必要がでてきています。

その都度過去のツールのコードをまるっとコピペしつつもgo-githubのClientインスタンス生成コードは変わってきています。

また、go-githubはセマンティックバージョニングを採用している(?)のか、既にv39まできています。 go-githubのClientを引数に受け取るような外部パッケージがあったとき、その外部パッケージが採用しているgo-githubのメジャーバージョンに合わせないといけないのも苦でした。

毎回コピペコードを書いたり、過去ツールの実装を直したりするのも嫌になってきたので、go-github-clientとしてコード片をパッケージ化しました。

github.com

go-github-client

使い方は go-github のメジャーバージョンに合わせて go-github-client/[VERSION]/factory をimportして使うだけです。

package main

import (
    "context"
    "fmt"

    "github.com/k1LoW/go-github-client/v39/factory"
)

func main() {
    ctx := context.Background()
    c, _ := factory.NewGithubClient()
    u, _, _ := c.Users.Get(ctx, "k1LoW")
    fmt.Printf("%s\n", u.GetLocation())
}

factory.NewGithubClient() 内で、環境変数からトークンやエンドポイントの解決をし生成したClientインスタンスを返します。

また、Functional Option Patternを採用しているので、外部からトークンやエンドポイントなどを差し込むことも可能です。

例えば、go-githubをモック化したい場合は、 *http.Client を外部から差し込むことで解決します。

package main

import (
    "context"
    "testing"

    "github.com/google/go-github/v39/github"
    "github.com/k1LoW/go-github-client/v39/factory"
    "github.com/migueleliasweb/go-github-mock/src/mock"
)

func TestUsingMock(t *testing.T) {
    mockedHTTPClient := mock.NewMockedHTTPClient(
        mock.WithRequestMatch(
            mock.GetUsersByUsername,
            github.User{
                Name: github.String("foobar"),
            },
        ),
    )
    c, err := factory.NewGithubClient(factory.HTTPClient(mockedHTTPClient))
    if err != nil {
        t.Fatal(err)
    }

    ctx := context.Background()
    user, _, err := c.Users.Get(ctx, "myuser")
    if err != nil {
        t.Fatal(err)
    }
    got := user.GetName()
    if want := "foobar"; got != want {
        t.Errorf("got %v\nwant %v", got, want)
    }
}

バージョニング

バージョニングは少し特殊です。

このgo-github-clientパッケージ自体がgo-githubのClientを生成するだけのものなので、基本的にgo-githubバージョンと合わせてタグやディレクトリを切っています。

ただ、go-github-client自体の機能追加もあるので、それにはパッチバージョンに加算することで対応します。

表で表すと以下のような感じです。

バージョン 構成
Major google/go-github のmajorバージョン
Minor google/go-github のminorバージョン
Patch google/go-github のpatchバージョンにk1LoW/go-github-clientの更新を加算

まあちょっとひどいですが、利便性を考えて割り切ります。

というわけで

ただのコード片ですが、これからもGitHub APIを叩くツールや実装は作りそうなので、個人的にはこれを使っていこうかと思っています。

そういえば昔もawsecretsというgemを作ったりしていて、成長していないなーなどと思いました。