年中アイス

いろいろつらつら

homebrew tapのformulaファイルの置き場所

前回のエントリで、goreleaserとgithub actionsを使ったhomebrewのformulaファイルの自動生成と登録について書き、既存のツールを3つ移行しました。しかし、その度にpersonal access tokenを発行して、リポジトリのSecretsに登録するという作業が地味地味と面倒でした。*1

github actionsを使わずにgoreleaserをローカルで実行すれば、tokenの発行はローカルマシンの1回で済みますが、ワークフローを考えるとgithub actionsで済ませたいので、homebrew-tapリポジトリに入れるのではなく、ツール自身のリポジトリにformulaを置けないかと考えました。

結果は、リポジトリ直下にformulaファイルを置けば、brew tapbrew installで動きました。ただ、ケースバイケースな気もするので、私が試したhomebrew tap向けのformulaの置き方、以下3パターンを比較します。

  1. homebrew-tapリポジトリを作ってそこにformulaを集約する
  2. homebrew-toolnameリポジトリをツール個別に作ってツール用のformulaを入れる
  3. ツール自身のリポジトリにformulaを入れる

homebrew-tap/tool.rb

homebrew-tapというリポジトリを作り、そこにformulaを集約します。

reiki4040/
  └ homebrew-tap/
      ├ rnzoo.rb
      ├ rnssh.rb
      └ mogura.rb

2通りのインストールコマンドがあります。

brew install reiki4040/tap/rnzoo

または

brew tap reiki4040/rnzoo
brew install reiki4040/rnzoo

でインストールすることができます。

メリット

  • brew install reiki4040/tap/rnzooのように1コマンドでインストールが可能。
  • homebrew系のリポジトリがこれ1つで済む
  • リポジトリを見れば、他のツールの存在がわかる
  • リポジトリが分かれているので、ツール側のリポジトリにhomebrew用ファイルのコミットが混ざらない

デメリット

homebrew-tool/tool.rb

もともとツール別に作っていたリポジトリ

reiki4040/
  ├ homebrew-rnzoo/
  │   └ rnzoo.rb
  │ 
  ├ homebrew-rnssh/
  │   └ rnssh.rb
  │ 
  └ homebrew-mogura/
      └ rnssh.rb

以下でインストール可能です。

brew tap reiki4040/rnzoo
brew install reiki4040/rnzoo

メリット

  • 実はあんまりないかもしれません。organization内で、homebrewの管理をリポジトリごとバラバラに行いたい場合とかがあれば。

デメリット

tool/tool.rb

ツールのリポジトリに直接置く、今回試した方法です。*3

reiki4040/
  └ rnzoo/
      └ rnzoo.rb

インストールは以下のように2コマンド必須で、brew installで直接インストールはできませんでした。

brew tap reiki4040/rnzoo
brew install reiki4040/rnzoo

メリット

デメリット

  • homebrew系のコミットが混ざる(これをデメリット捉えるか次第)
  • brew tapしてからbrew installの必要がある(どうせコピペなので、そんなに負荷ではないかな)

最後に

いくつかパターンがあり、個別にhomebrew-toolリポジトリを作るのはそんなにメリットがないです。通常は、homebrew-tapリポジトリ作るのが一番無難かなと思います。ただ、github actionsを使い、secretsにtoken入れるの面倒(または不安)だなと言う場合は、ツールのリポジトリ直下に置くのもありかなと思います。この辺りは選択肢として知っておくのは良いと思います。

*1:トークンをリポジトリ間で使い回すという手もありますが避けました。

*2:他の外部サービスも同様?試してないです。

*3:試したのはrnzooではなく別のリポジトリですが、同じ流れで例示してます。

GoReleaser+GithubActionsを使って、releaseファイルのアップロードとhomebrew対応を自動で行う

先日、rnzooに機能を追加して、久しぶりにリリースをしようとしたら、色々動かなくなっててハマりました。しばらくやってなかったので、Rubyのバージョンアップや、hubコマンドの再設定などが必要でした。だいぶオレオレリリーススクリプトになっていたので、もうちょっと普遍的なフローでリリースできないかと思って調べました。

ちょうど先週、以下のGoReleaser+GithubActionsで決まり!というエントリが上がっていました。

tellme.tokyo

たまたま、ちょっとしたツールを作る予定もあって、それを上記エントリのやり方で構成し、追加でhombrew-tap対応を行いました。

github.com

基本的な内容は、前述のエントリにわかりやすく書いてあるので、そちらを確認、全体はリポジトリを見てみてください。個人的に失敗して対応した部分と、homebrew-tap対応を中心に書いていきます。

GithubActionsで失敗して、releaseファイルがアップロードされない

初回、リリースタグを打ってworkflowが実行されて失敗しました。以下がworkflowのエラーです。

   • GETTING AND VALIDATING GIT STATE
      • releasing v0.1.0, commit 5d807106bbd8d6dc2b9233e0fd4f3b2ec3cc6708
   ⨯ release failed after 6.29s error=git is currently in a dirty state, please check in your pipeline what can be changing the following > files:
  M go.mod
  M go.sum

内容を見ると、git がdirty stateであると。workflowでcheckoutしてきたのになぜ?と考えましたが、.goreleaser.ymlに以下の記述があります

before:
  hooks:
    - go mod tidy

そういえば、ローカルでgo mod tidyは実行していませんでした。そのため、checkout後に変更差分が出ており、それが原因で失敗していました。案の定、その変更をcommitしたら、無事完了しました

homebrew-tap対応を追加

mogura, rnzoo, rnsshなど、私のツールはhomebrew-tapで入れられるようにしています。GoReleaserはhomebrew-tap向けのファイルを生成する機能を持っています。それを参考に.goreleaser.ymlに以下を追加しました。

...

brews:
  - description: "update ECS agent with CLI."
    github:
      owner: reiki4040
      name: homebrew-tap
    homepage: "https://github.com/reiki4040/ecs-agent-updater"
    install: |
      bin.install "ecs-agent-updater"

homebrew-tapは、homebrewでインストールするための定義Formulaを持つリポジトリで、ツール自体のリポジトリとは別に作ります。今回はhomebrew-tapというリポジトリを作って、そこにFormulaを自動で追加します。*1

そしてこれを動かしてみましたが失敗しました。

• HOMEBREW TAP FORMULA     
         • writing                   formula=dist/ecs-agent-updater.rb
         • pushing                   formula=ecs-agent-updater.rb repo=reiki4040/homebrew-tap
   ⨯ release failed after 31.47s error=homebrew tap formula: failed to publish artifacts: PUT https://api.github.com/repos/reiki4040/homebrew-tap/contents/ecs-agent-updater.rb: 403 Resource not accessible by integration []

アクセス権限がない。これは予想通りというか、最小の権限しか持ってないトークンが自動で割り当てられるはずなので、当然ながら、他のリポジトリにファイルを追加することはできません。調べてみると、似たようにGithubActionsで、他のリポジトリへのアクセスをしたい場合についてのエントリがありました。

blog.marcnuri.com

やり方としては、リポジトリのsecretsにユーザアクセストークンを設定して、それをworkflow内で使うというものでした。同様に、自分のSettings > Developer Settings > Personal Access Token ここで私の場合は、public repositoryだけにチェックを入れて、tokenを生成しました。これをリポジトリのsettings > secretsにACCESS_TOKENという名前で保存し、workflow設定の参照変数を変えました。

env:
-         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 
+         GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 

これで無事に完了し、homebrew-tapファイルも生成されました

最後に

最終的には、リリースタグを打つだけで、バイナリがreleaseに追加され、homebrew-tapまで更新されて、とても楽になったことと、GithubActionsを使ったモダンな作りになりました。

rnzooなどでは、homebrewのdevelオプションでの開発版インストールにも対応していたので、そこもできるかは別途調べようと思っています。

参考

*1:今まで、各ツールごとにtapリポジトリを作ってましたが、実は1つでも良さそうなので、このフローに変更しながら集約する予定です。

CloudWatchLogsからAthena向けにS3にデータを保存する時に改行を追加してあげないといけない

前回は、自分のミスでハマった話でしたが、その後、別の仕様にハマったのでそのメモです。以下あたりを参考に、CloudWatchLogsのログをAthenaで解析する流れを検証していました。

CloudWatchLogs -(Subscription Filter)->Firehose -(delivery stream)-> S3 <-Athena

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/partitions.html *1

無事S3までログを保存することはできたものの、Athenaでパーティションにデータをロードした後、selectしたら以下のエラーが出ました。

HIVE_CURSOR_ERROR: Row is not a valid JSON Object - JSONException: Expected a ':' after a key at 6 [character 7 line 1]

何だろうと思って色々調べていくと、どうやらJSONのログは1objectごとに改行されている必要があるとちらほら。

以下のように1つ1つのobjectが改行されている必要があるため1個のobjectだけを送る場合は末端に改行コードを入れる必要がある。

さらに調べると、2017年のBlackBeltのQAにそのままの答えがありました。 QA部分抜粋

Q8. Amazon Kinesis Firehose を利用してCloudWatch LogsをS3に転送してそれをAthena で分析したいのですが、Kinesis Firehoseを通すと{json}{json}のように1行に複数のJSONオブジェクトが保存されるようです。このデータを効率的にAthenaで分析するにはどういった方法がありますか?? A8. Amazon Kinesis FirehoseにはData TransformationをAWS Lambdaで行う機能がございますので,こちらを使って所望の形式に変換すると良いです.

どうやら、CloudWatchLogsから出すと、JSON Objectが改行なしで出力されるため、Athenaで解析するには、Lambdaを作って改行を追加してあげないといけないとのこと。ただ改行入れるためだけにLambda作らなければならないのは、とても面倒ですが、現状はこうするしかないようです。この辺りは、マイクロサービス同士で組み合わさっているゆえの噛み合わせの悪さなんでしょうか。せっかくマネージドで組み合わせられるので*2、この改行は自分でLambda作らなくてもできるようにはして欲しいところです。

参考

*1:新しいAWSドキュメント、タイトルもプレビューも出ない。。。

*2:Lambdaもマネージドといえばそうなんですが