Skip to main content

· 2 min read
Shunsuke Suzuki

buildflow というツールを開発しているので buildflow というタグをつけて何回かに分けてブログを書こうかなと思います。

この記事では buildflow の設定ファイルを分割する方法について説明します。

buildflow では一部の設定項目について他のファイルのパスを指定して読み込むということが出来ます。 1 つのファイルに全部の設定を書いていると、ファイルが大きくなってメンテナンス性が悪くなったり、 コードオーナーが曖昧になったりするので、そういう場合は分割すると良いでしょう。 コードオーナーが異なる複数のサービスで共通の設定ファイルを用いる場合、ファイルを分割して GitHub の CODEOWNERS を設定するのもよいでしょう。 あまりないかもしれませんが、ファイルを分割すると同じファイルを読み込んで再利用も出来ます。

また、 Tengo script を独立したファイルに分割すると、 test が可能になります。 Tengo script をテストするためのツールとして tengo-tester というツールも開発しているので、そちらをお使いください。

以下のようなファイル読み込みの設定があります。

  • phase.import
  • task.import:
  • task.input_file
  • task.output_file
  • task.when_file
  • command.command_file
  • command.env[].value_file
  • write_file.template_file

ファイルのパスは、絶対パスか、実行中の build の設定ファイルが存在するディレクトリからの相対パスになります。

· 3 min read
Shunsuke Suzuki

buildflow というツールを開発しているので buildflow というタグをつけて何回かに分けてブログを書こうと思います。

この記事では buildflow の Tengo script やテンプレートにパラメータとして渡される変数について紹介します。

buildflow では Tengo script はテンプレートが使える設定項目が多くあります。それらの設定には共通のフォーマットのパラメータが渡されます。

  • PR: Pull Request の情報: GitHub API のレスポンス body
  • Files: Pull Request で更新されたファイルの一覧: GitHub API のレスポンス body
  • Phases: 対象の Phase よりも前の Phase の結果
  • Phase: 対象の Phase
  • Tasks: 対象の Phase の Task の結果
  • Task: 対象の Task
  • Item: dynamic task のパラメータとして渡される
  • Meta: 設定 meta

Phase

  • Status: Phase の実行結果
    • succeeded
    • failed
    • skipped
  • Tasks: Phase の task の実行結果
  • Meta: phase の 設定 meta

Task

type によらず共通

  • Type: task の type
  • Name: task 名
  • Status: Task の実行結果
    • queue
    • running
    • succeeded
    • failed
    • skipped
  • Meta: task の meta の設定
  • Input:
  • Output:

command

  • ExitCode
  • StdOut
  • StdErr
  • CombinedOutput

read_file

  • File
    • Text: ファイルの内容
    • Data: read_file の format を指定した場合、パースされた結果

write_file

  • File
    • Text: ファイルの内容

Template でパラメータを参照する

例えば command の場合

---
phases:
- name: main
tasks:
- name: hello
command:
command: 'echo "{{.Task.Name}}"'

Tengo script でパラメータを参照する

例えば task foo が成功した場合のみ、 task bar を実行したい場合

---
phases:
- name: main
tasks:
- name: foo
command:
command: echo hello
- name: bar
command:
command: echo hello
dependency:
- foo
when: |
task := {}
for t in Tasks {
if t.Name == "foo" {
task = t
break
}
}
result := task.Status == "succeeded"

このように他の task の実行結果や PR の情報などを使ってビルドの挙動を変えることが出来ます。

· 3 min read
Shunsuke Suzuki

buildflow というツールを開発しているので buildflow というタグをつけて何回かに分けてブログを書こうと思います。

この記事では buildflow の task の基本的な設定項目などについて説明します。 数が多いので、個々の設定の詳細はまた別の記事に書きます。

task には幾つか type がありますが、全ての type に共通するパラメータが以下になります。

  • name: task 名。 unique である必要はない。 Go の text/template が使える
  • when: task を実行するか否か。 真偽値か Tengo script
    • when_file で外部ファイルを読み込める
  • dependency: task の依存関係の定義。 task 名のリストか、 Tengo script
  • items: dynamic task の設定。 loop を使って複数の task を動的に生成できる
    • 任意の list か map か、 Tengo script
  • input: Tengo script で task のコマンドのパラメータを生成できる
    • input_file で外部ファイルを読み込める
  • output: Tengo script で task の実行結果を整形できる。他の task が参照して挙動を変えたりできる
    • output_file で外部ファイルを読み込める
  • meta: ユーザーが自由にパラメータを定義できる map

上記の設定は name 以外はオプションです。

task の type としては以下のものがあります。

  • command: 外部コマンドを実行
  • read_file: ファイルを読み込む。ファイルの内容を他の task で参照できる
  • write_file: ファイルを書き込む

command の設定

  • shell, shell_opts: コマンドの実行シェル。デフォルトは /bin/sh -c
  • command: コマンド。 Go の text/template で処理される
    • command_file で外部ファイルを読み込める
  • stdin: コマンドの標準入力。 Go の text/template で処理される
    • stdin_file で外部ファイルを読み込める
  • env: 環境変数。環境変数名と値は Go の text/template で処理される

read_file の設定

  • path: ファイルのパス。Go の text/template で処理される
  • format: ファイルのフォーマット。オプション。 json と yaml をサポート。指定するとパースした結果を他の task が参照できる

write_file の設定

  • path: ファイルのパス。Go の text/template で処理される
  • template: ファイルの内容。Go の text/template で処理される
    • template_file で外部ファイルを読み込める

· 3 min read
Shunsuke Suzuki

buildflow というツールを開発しているので buildflow というタグをつけて何回かに分けてブログを書こうと思います。

この記事では buildflow の概念である build, phase, task について書きたいと思います。

buildflow には Build, Phase, Task という概念があります。 CircleCI の Pipeline, Workflow, Job みたいなものと思ってもらえるとよいと思います。

$ buildflow run

で 1 つの build が実行されます。 build は複数の phase からなり、 phase が 1 つずつ順に実行されます。 phase は複数の task からなり、 task が全て終了すると、その phase も終了となります。 task は並列に実行したり、依存関係を定義したりできます。 task では外部コマンドを実行したりできます。

設定ファイルでは phases, tasks をそれぞれ配列で指定します。

---
phases:
- name: setup
tasks:
- name: hello
command:
command: echo hello
- name: foo
command:
command: echo foo
- name: build
tasks:
- name: hello
command:
command: echo hello
- name: foo
command:
command: echo foo
dependency:
- hello
- name: post build
tasks:
- name: hello
command:
command: echo hello

上の例では 3 つの phase setup, build, post build が順に実行されます。 デフォルトではどれかの phase が失敗するとそれ以降の phase は実行されません(この挙動は変えられます)。

task も phase 同様配列で指定しますが、配列の順序に意味はありません。 依存関係がない限り、並列で実行されますし、実行順序は不定です。

· 2 min read
Shunsuke Suzuki

buildflow というツールを開発しているので buildflow というタグをつけて何回かに分けてブログを書こうかなと思います。

この記事では buildflow で Tengo というスクリプト言語をどのように使っているか書きたいと思います。

https://github.com/d5/tengo

buildflow の設定では task.when や task.dependency, task.input などで Tengo script が使えますが、 1 つの共通のルールがあります。 result という変数を宣言し、 script の実行結果をその変数に持たせるというルールです。 これは Tengo の仕様とかではなく、 buildflow 特有のルールです。 もっとも単純な例だと次のような感じです。

result := true

task.input, output などだと result の値が Task.Input, Task.Output として参照できるようになります。

---
phases:
- name: main
tasks:
- name: hello
input: |
result := {
foo: "bar"
}
command:
command: 'echo "{{.Task.Input.foo}}"'
when: "result := true"

Tengo の標準ライブラリ

Tengo には標準ライブラリがあります。 buildflow では全ての標準ライブラリが使えます。

· 5 min read
Shunsuke Suzuki

buildflow というツールを開発しているので紹介します。 buildflow というタグをつけて何回かに分けてブログを書こうかなと思います。 1本目のこの記事では

  • どんなツールか
  • Hello World
  • 特徴

について簡単に説明します。

どんなツールか

https://github.com/suzuki-shunsuke/buildflow

ワークフローを実行するための CLI ツールです。 ワークフローエンジンと言うと Airflow とか Azkaban, Argo Workflows のようなツールをイメージするかと思いますが、 それらとは目的も機能も違います。 一部の CI サービスではワークフローのローカル実行をサポートしてたりしますが、そんなイメージで良いかもしれません。 buildflow では task と task の依存関係を設定ファイルに定義し、コマンドを実行するとローカルでタスクが実行されます。 そういうとタスクランナーといったほうがいいのかもしれませんが、個別のタスクを指定して実行するような機能はないので、タスクランナーとも違う気がします。

CI サービス上で実行することを目的として開発しています(汎用的なツールなので他の目的でも使えるとは思います)。

Hello World

まだどんなツールかピンと来てない人もいるかもしれないので、簡単な Hello World をやってみましょう。

GitHub Releases からバイナリをダウンロードしてください。

次のような設定ファイル .buildflow.yaml を用意します。

---
phases:
- name: main
tasks:
- name: hello
command:
command: echo hello

次のコマンドを実行すると task が実行されます。

$ buildflow run

==============
= Phase: main =
==============
07:50:46UTC | hello | + /bin/sh -c echo hello
07:50:46UTC | hello |
07:50:46UTC | hello | hello
07:50:46UTC | hello |

================
= Phase Result: main =
================
status: succeeded
task: hello
status: succeeded
exit code: 0
start time: 2020-10-17T07:50:46Z
end time: 2020-10-17T07:50:46Z
duration: 4.317259ms
+ /bin/sh -c echo hello
hello

特徴

task を依存関係に基づいて並列実行できます。加えて以下のような特徴があります。

  • ワンバイナリで動く。他に依存するものがない
    • Go で書かれています
  • 他のタスクの実行結果によってタスクの挙動を変えられる
    • 標準(エラー)出力、 exit code, etc
    • dynamic に task を生成することも可能
  • Tengo というスクリプト言語を用いて柔軟な設定を書ける
    • 設定ファイルは YAML ですが、一部の設定に Tengo というスクリプト言語が使えます
    • Tengo の処理系も buildflow に内包されているので、 Tengo の処理系をインストールする必要はありません
  • CI で実行時に Pull Request (以下 PR) の情報に基づいて処理を変えられる
    • GitHub 前提(GitLab や BitBucket はサポート外)
    • 自動で PR の情報を取得

Tengo は Go で実行されたスクリプト言語です。 https://github.com/d5/tengo を参照してください。 なぜ Tengo を採用したかとかは別に書きたいと思います。

Tengo は Python や Ruby, Go といった言語に比べれば言語仕様がコンパクトであり、 よく知らなくてもなんとなく読めるし、簡単にかけると思っています。 加えて、そもそも buildflow の中での Tengo の役割は限定されており、 Tengo をゴリゴリ書く必要はないと思います。

PR の情報に基づき、例えば以下のようなことが出来ます。

  • 特定のラベルがついたら task を実行する
  • 特定のファイルが PR で更新されたら task を実行する
  • 特定のユーザー(botとか)からの PR では task を実行しない

これらのロジックは自分がよく欲しくなるので、 buildflow を使えば簡単に実装できるようにしました。

他にも様々な機能がありますので、また別の記事で紹介できたらと思います。

· 2 min read
Shunsuke Suzuki

tfnotify が terraform の標準出力のパースに失敗してコメントを投稿できないことがあります。

コメントを投稿できなくてもビルドのログには残るのですが、やはりコメントを投稿できると便利なので、tfnotify がパースエラーでコメントの投稿に失敗したら、 github-comment でコメントを投稿するようにしました。

なお、この記事を書いている時点のバージョンは tfnotify v0.7.0, github-comment v1.9.0 です。

例えば tfnotify plan がパースエラーになった場合、 cannot parse plan result というメッセージが標準エラー出力されます。 そこで標準エラー出力に cannot parse plan result が含まれていたら github-comment でコメントするようにします。

terraform plan | github-comment exec -k plan -- tfnotify plan

.github-comment.yml

# 細かく template を分けているが、別に分けなくてもよい
templates:
# header は CodeBuild の場合
header: '{{Env "TARGET"}} [Build link]({{Env "CODEBUILD_BUILD_URL"}})'
exit_code: ':{{if eq .ExitCode 0}}white_check_mark{{else}}x{{end}}: Exit Code {{.ExitCode}}'
join_command: |
```
$ {{.JoinCommand}}
```
hidden_combined_output: |
<details>

<pre><code>{{.CombinedOutput}}</code></pre>

</details>
exec_default: |
{{template "header" .}}

{{template "exit_code" .}}

{{template "join_command" .}}

{{template "hidden_combined_output" .}}
exec:
plan:
- when: |
Stderr contains "cannot parse plan result"
template: |
{{template "exec_default" .}}

tfnotify apply の場合は cannot parse apply result というメッセージを出力するので 次のようになります。

terraform apply -auto-approve | github-comment exec -k apply -- tfnotify apply
exec:
apply:
- when: |
Stderr contains "cannot parse apply result"
template: |
{{template "exec_default" .}}

tfnotify のコードを確認

· 6 min read
Shunsuke Suzuki

AWS CodeBuild を検証しているんですが、結構良いですね。 現状 Jenkins や CircleCI で実行しているジョブや CI/CD を一部移行したいなと思いました。

一部と言っているのは、単純に全部いきなり移行するのは難しいから共存する前提で考えるくらいの意味です。

なお、これを書いている時点ではまだ軽く検証しているだけなので、CodeBuild の理解は浅いです。

特徴としては

  • Managed
  • AWS 以外のサービスに AWS の credential を登録しなくて良い
    • Secret を AWS Secrets Manager で管理できる
    • Secret を至るところに設定するのではなく、 AWS Secrets Manager か何かで一元管理するのが理想
    • Role 作って Build Project の Service Role として指定するだけなら credential を扱う必要がなくて楽で安心
  • VPC 内で実行できる
  • GitHub 連携も簡単
    • Webhook の設定で PR の細かなイベント(merge とか reopen とか)に対応しているのも良い
    • PR を merge したときも PR で変更されたファイルによって Webhook をフィルタできるのが良い
  • 変更されたファイルによって実行するBuild Project を変更するようなロジックを実現できる(monorepo で特に有効)

といった点が挙げられます。

変更検知について

上記の特徴についてはだいたい「まぁそうだね」というふうに思ってもらえるのではないかと思いますが、変更検知の部分について補足します。 monorepo をやっているとサービス A 関連のジョブはサービス A 関連のコードが変更された場合のみ実行したいというのがあります。 なのですが、 CircleCI だと自分の知る限りそれはできません。

https://circleci.com/docs/2.0/configuration-reference/

なので現状 Job を実行してからサービス A 関連のコードが変更されているかチェックし、変更されていなかったら skip ないし circleci step halt で Job を終了しています。 ただ、差分検知の部分を自分で実装しないといけないし、 circleci step halt で終了するにしても Job の起動自体にそこそこ時間がかかったりするし(これだけでも金銭的コストがかかっているはず)、 Job の実行時間や error rate のモニタリングでもノイズとして混じってきてしまいます。

CodeBuild の場合、1つのリポジトリに複数の Build Project を設定でき、 Build Project ごとに buildspec を指定できます。 Build Project の Webhook の FILE_PATH の設定で特定のファイルが変更された場合のみビルドを実行することもできます。 なのでサービス A 用の build spec 及び Build Project を作成し、サービス A のコードが変更されたときのみその Build Project が実行されるように Webhook を設定すれば良さそうです。

気になる点

  • FILE_PATH で 1 個の正規表現しか設定できないので、場合によっては複雑な正規表現を書かないといけない
  • Terraform で管理するのが結構面倒くさい
    • いい感じにモジュール化とかテンプレート化しないとだめかも
  • Pull Request のラベルがついていたらこのジョブを実行する、みたいな複雑なことは一工夫必要かも
    • Webhook の設定だと無理なので、 build は実行しつつラベルが設定されていなかったら skip とかしないといけない

おまけ: CodeBuild 関連のニュース

以前検証したときより良くなってる気がするなと思って更新をチェックしたらやっぱ色々更新があるみたいですね。

https://aws.amazon.com/jp/new/?whats-new-content-all.sort-by=item.additionalFields.postDateTime&whats-new-content-all.sort-order=desc&awsf.whats-new-developer-tools=general-products%23aws-codebuild

· 9 min read
Shunsuke Suzuki

GitHub の issue や pull request, commit にコメントを投稿する CLI ツールを作りました(結構前の話ですが)。

https://github.com/suzuki-shunsuke/github-comment

このブログの執筆時点で最新は v1.5.0 です。

Go 製なので、 GitHub Releases からダウンロードしてくれば簡単にインストールできます。

想定している主な用途は、 CI/CD の 結果をコメントで通知することで DX を向上することです。 例えば CI がこけたらこけたコマンドとエラーメッセージを通知するなどです。

github-comment には

  • init: 設定ファイルの雛形を生成する
  • post: コメントを投稿する
  • exec: 外部コマンドを実行し、その結果を元にコメントを投稿する

という 3 つのサブコマンドがあります。

コメントの投稿には GitHub の Access Token が必要です。 コマンドライン引数 -token でも渡せますが、環境変数として設定しましょう。

$ export GITHUB_TOKEN=xxx # GITHUB_ACCESS_TOKEN も可

post コマンド

こんな感じでコメントを投稿できます。

$ github-comment post -org suzuki-shunsuke -repo github-comment -pr 1 -template test

パラメータの数が多いですが、いくつかの Platform では環境変数から自動でパラメータを補完してくれます。

  • Drone
  • CircleCI
  • GitHub Actions

そうするとこれでよくなります。

$ github-comment post -template test

コメントは Go の text/template で処理されます。 {{.Org}} {{.Repo}} といった感じでパラメータを参照できます。

$ github-comment post -template "{{.Org}}/{{.Repo}} test"
  • PRNumber
  • Org
  • Repo
  • SHA1
  • TemplateKey
  • Vars

exec コマンド

コマンドの実行結果(標準出力、標準エラー出力、 exit code) を元にコメントを投稿したい場合に、 exec コマンドが使えます。

$ github-comment exec -template "{{.ExitCode}} {{.Stdout}}" -- echo hello

コマンドを実行した上でコメントを投稿します。テンプレートの変数として、 post でも渡されるパラメータの他にコマンドの実行結果が渡されます。

  • ExitCode
  • Stdout
  • Stderr
  • CombinedOutput
  • Command: exec.Command.String
  • JoinCommand: コマンド引数(配列)をスペース " " でつないだ文字列

github-comment exec の標準入力は実行するコマンドに渡されますし、 github-comment exec の exit code は実行したコマンドの exit code になります。

設定ファイル

上記の例では投稿するコメントを -template で渡していましたが、ごく短いコメント以外は設定ファイルに記述したほうが良いでしょう。 設定ファイルは github-comment init で雛形を生成できます。

$ github-comment init

こんな感じで複数のテンプレートを記述できます。

post:
default: |
foo
hello: |
hello
...

そして post 実行時に -template-key (-k) で使用するテンプレートを指定します。 -template-k 両方指定しない場合、デフォルトで default テンプレートが使用されます。

$ github-comment -k hello

exec の設定

exec の設定はもう少し複雑です。 それはコマンドの実行結果によって使用するテンプレートを変えたり、あるいはコメントを投稿しなかったりできるようにするためです。 一つのテンプレートキーに対し、複数のテンプレートを配列で設定します。

exec:
default:
- when: ExitCode == 0
template: |
success
- when: ExitCode != 0
template: |
failed
...
...

そしてテンプレート文字列とは別に when という、そのテンプレートを使う条件を設定します。 この条件は antonmedv/expr によって処理されます。 テンプレートの text/template とはまた別の構文なのがややこしいですね。 条件の評価結果は当然真偽値ではないといけません。 評価結果が true ならばそれを使ってコメントし、あとは無視されます。 false なら次のテンプレートを評価します。 全部マッチしなければコメントは投稿されませんし、エラーにもなりません。 dont_comment: true とすると、その条件にマッチした場合はコメントを投稿せずに終了します(後続のテンプレートも無視されます)。

exec:
hello:
- when: ExitCode != 0
dont_comment: true
- when: true
template: |
Hello, world

テンプレートの再利用

templates を使うと複数のテンプレートでヘッダーなどを共通化して再利用できます。

templates:
header: "# {{.Org}}/{{.Repo}}"
post:
default: |
{{template "header" .}}

Go の text/template に馴染みがないとわかりにくいかと思いますが、

templates:
テンプレート名: テンプレート

でテンプレートを定義して

{{template "テンプレート名" .}}

でテンプレートを参照できます。

テンプレートの変数をコマンドライン引数で渡す

-var 変数名:値 でパラメータを渡せます。 {{.Var.変数名}} で参照できます。

$ github-comment post -var name:foo -template "Hello, {{.Var.name}}"

設定ファイルで変数を定義する

設定ファイルで変数を定義できます。任意の型の変数を定義できます。 {{.Var}}.変数名 で参照できます。

vars:
foo: bar
zoo:
foo: hello

{{.Var.zoo.foo}}

post コマンドの標準入力でテンプレートを渡す

github-comment post の標準入力でテンプレートを渡せます。 exec の場合はそうはならない(実行するコマンドに渡される)ので注意してください。

$ echo foo | github-comment post

パラメータの補完

先に述べたとおり、いくつかの Platform では環境変数から自動でパラメータを補完してくれます。

CircleCI

プラットフォームの判別: CIRCLECI の有無

パラメータソース
.OrgCIRCLE_PROJECT_USERNAME
.RepoCIRCLE_PROJECT_REPONAME
.PRNumberCIRCLE_PULL_REQUEST
.SHA1CIRCLE_SHA1

Drone

プラットフォームの判別: DRONE の有無

パラメータソース
.OrgDRONE_REPO_OWNER
.RepoDRONE_REPO_NAME
.PRNumberDRONE_PULL_REQUEST
.SHA1DRONE_COMMIT_SHA1

GitHub Actions

プラットフォームの判別: GITHUB_ACTIONS の有無

パラメータソース
.OrgGITHUB_REPOSITORY
.RepoGITHUB_REPOSITORY
.PRNumberGITHUB_EVENT_PATH
.SHA1GITHUB_SHA1

exec のパラメータの Command と JoinCommand

実行したコマンドを示すパラメータとして Command と JoinCommand があります。 これらは似てますが、微妙に違います。

Command は exec.Cmd の .String() から取得されるのですが、コマンドが絶対パスになったりするので、あまり望ましくないこともあるでしょう。

例えば echo hello の場合 /usr/local/opt/coreutils/libexec/gnubin/echo foo となったりします。

一方 JoinCommand はコマンド文字列を単にスペースでつないだものになります。

exec.Cmd の .String() のドキュメントに書いてあるとおり、 .Command と .JoinCommand はそのまま shell で実行するのには適さない形式なので注意してください。

String returns a human-readable description of c. It is intended only for debugging. In particular, it is not suitable for use as input to a shell. The output of String may vary across Go releases.

設定ファイルのパス

設定ファイルのパスは --config -c オプションで指定できます。 何も指定しない場合、カレントディレクトリからルートディレクトリに向かって .github-comment.yml, .github-comment.yaml を探索し、最初に見つかったものを使います。

設定ファイルで .Org, .Repo を指定する

設定ファイルでコメント先のリポジトリを指定できます。 Platform で補完される場合や、明示的にパラメータで指定する場合は不要です。

base:
org: suzuki-shunsuke
repo: github-comment

· 3 min read
Shunsuke Suzuki

多分車輪の再発明だとは思いますが、簡単にツールをインストールするための CLI ツールを作りました。 tarball や zip をダウンロードして展開して指定したパスにインストールするツールです。

https://github.com/suzuki-shunsuke/clap

Go で書かれています。 ツールの名前(clap)には特別な意味や理由はなく、なんとなくです。

CI で何かしらのツールをインストールすることがままあって、そのためのシェルスクリプトを都度書くのが割と面倒なのでツール化しました。

このブログを書いている時点でバージョンは v0.1.0-1 で、最低限の機能しかありませんが、9割型ニーズを満たせるかなと思います。

使い方は以下のようになっています。

$ clap <URL> <インストールするファイルのアーカイブ内での相対パス>:<インストール先> [<インストールするファイルのアーカイブ内での相対パス>:<インストール先>...]

例えば conftest を /usr/local/bin にインストールする場合次のようになります。

CONFTEST_VERSION=0.18.2
clap install https://github.com/instrumenta/conftest/releases/download/v${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz conftest:/usr/local/bin/conftest
chmod a+x /usr/local/bin/conftest

パーミッションの付与はやってくれないので必要に応じてやってください。 ファイルの圧縮形式は URL から自動で判別してくれます。

上記の conftest のインストールを今までは次のようなシェルスクリプトを書いていました。

#!/usr/bin/env bash

set -eu

CONFTEST_VERSION=0.18.2

dirpath=$(mktemp -d)
pushd "$dirpath"
TARFILE=conftest_${CONFTEST_VERSION}_Linux_x86_64.tar.gz
curl -OL https://github.com/instrumenta/conftest/releases/download/v${CONFTEST_VERSION}/${TARFILE}
tar xvzf $TARFILE
mv conftest /usr/local/bin/conftest
chmod a+x /usr/local/bin/conftest
popd
rm -R "$dirpath"

地味に面倒ですね。これをツール毎に書いて、しかも圧縮形式によって微妙に変えないといけません。

clap 自体のインストールはどうするかというと、 GitHub Releases で tarball の他にバイナリ単体でも配布しているので簡単にインストールできます。

CLAP_VERSION=0.1.0-1
curl -L -o /usr/local/bin/clap https://github.com/suzuki-shunsuke/clap/releases/download/v${CLAP_VERSION}/clap_${CLAP_VERSION}_linux_amd64
chmod a+x /usr/local/bin/clap

以上、簡単な紹介でした。