幾つか CircleCI Orb を作っていたのですが、記事にしていませんでした。
どれも必要になって作ったもので、いずれもプロダクションで万単位で実行されれているのでいい感じで使えそうなら幸いです。
目次
TL;DR
- NuGet Cache を制御するなら guitarrapc/nuget-cache
- Shallow Clone を制御するなら guitarrapc/git-shallow-clone
- Chatwork にメッセージを投げるなら guitarrapc/chatwork
- CircleCI は、公式Orb でやっているやり方をドキュメントに反映してほしいです。Orbを初めて作るときに把握から始まるのは結構ツライ
Orbs とは
CircleCI は Job や Command にまとめていた処理を Orb として共有できます。
これって結構強力でいい仕組みで、Executor を含んだり含まないことができます。
- CircleCI でYAMLが特定の処理のためにやけに長くなってしまった (Executor 依存がないとよりよい)
- 特定の操作を行いたい
特定のシーンにスコープを絞れば Executor 依存してもいいし、特定の処理に絞って汎用的に使いたいなら Executor 依存しないようにするといいでしょう。
CircleCI でやっていこうと思ったら、何かしたいときは Orb をチェックするのは標準的に行うといい流れを作ろうとしているし、実際そうだと思います。
既存のOrbs で提案があれば適当にPR出したりもいいでしょう。 どのみち config.yml は公開されますしね。
公開したOrbs
3つ作って公開しています。
- https://circleci.com/orbs/registry/orb/guitarrapc/git-shallow-clone
- https://circleci.com/orbs/registry/orb/guitarrapc/nuget-cache
- https://circleci.com/orbs/registry/orb/guitarrapc/chatwork
どれも当初は commands として定義していたのですが、ほかのプロジェクトでも使えるし公開しました。
guitarrapc/git-shallow-clone
https://circleci.com/orbs/registry/orb/guitarrapc/git-shallow-clone
git shallow clone をするOrb は実はいくつかあります。 有名なのは、ganta/git でしょうか。
しかしこれらのOrb はどれも Fork プロジェクトは考慮されていません。 また、GitHubや BitBucket が死んでることを想定していないので、案外失敗します。
guitarrapc/git-shallow-clone は、Fork プロジェクトにも対応しつつ、実際に頻繁に使っても問題ないまでブラッシュアップしてあります。
利用はいたってシンプルです。
orbs: git: guitarrapc/git-shallow-clone@1.0.1 version: 2.1 workflows: build: jobs: - git/checkout_shallow
keyscan_bitbucket と keyscan_github で、実際のリポジトリを keyscan するか選べます。 デフォルトは、鍵の更新がしばらくないのでハードコードしてあります。
depth と fetch_depth で、深さを指定できます。 Shallow Clone は、depth で 1指定しておいて、fetch depth では少し深く 5~10指定しないとブランチとかをいい感じで処理できないのでその対策も入っています。 clone時にsingle branchを使ってると気にせずにすむのですが。
clone を高速に終わらせて、汎用的に使いたいならお勧めです。 毎日結構な回数動いてて安定しているので、大概のケースでいけるかと。
この Orb はテストを書くのが面倒でしたが、ひとこねしたテストも書けるのは CircleCI 強かった。
guitarrapc/nuget-cache
https://circleci.com/orbs/registry/orb/guitarrapc/nuget-cache
.NET Core のビルドで困るのが、nuget package の restoreサイズは大きく時間がかかるということです。 別に一切気にしないという手もありますが、キャッシュしておくほうが爆速になるのでやるでしょう。
ただ、cache をするにはキャッシュキーを決める必要があります。 利用するパッケージに変更があったらキャッシュを切り替えたいわけです。 .NET Core なら csprojの変化を見るのが一番間違いがないでしょう。(Directory.Build.props は考慮したくないですね!)
guitarrapc/nuget-cache を使うと、任意のファイルを拾って md5 を取得してキャッシュキーに利用しつつ、cache のsave/restoreが可能になります。
想定している流れ場、nuget_cache/nuget_restore_cache して、dotnet restore してnuget_cache/nuget_save_cacheです。
orb でいい感じのキーで nuget-restore して、dotnet cli でdotnet restore するのはキャッシュがあれば秒で終わります。 キャッシュがずれていたら適切に dotnet restoreでリストアされるので、あとはいい感じのキーでsave してあげれば次回以降のビルドで生きます。
orbs: nuget-cache: guitarrapc/nuget-cache@0.1.1 version: 2.1 executors: dotnet31: docker: - image: 'mcr.microsoft.com/dotnet/core/sdk:3.1' environment: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true NUGET_XMLDOC_MODE: skip jobs: build: executor: dotnet31 steps: - checkout - nuget_cache/nuget_restore_cache: cache_key: test_save_cache project: nuget-cache-orb - run: dotnet restore - nuget_cache/nuget_save_cache: cache_key: test_save_cache project: nuget-cache-orb project_cache: false - run: dotnet build
シンプルですが、こういうのが適当にできるのがいいなぁということで作りました。 いろんなプロジェクト構成に対応できるように、cache_key や project、working fidrectoryや target file pattern も指定できるようにしてあります。
実際、外部リポジトリを使った複雑なプロジェクト構成でも機能しているのでだいたいうまくいくはずです。
guitarrapc/chatwork
Chatwork のルームにメッセージを送信します。 さくっとメッセージを送るだけなら、Chatwork API を使うのがいいでしょう。(こんなことで OAuthしたくない)
ということで、curl でサクッと送ります。
orb: chatwork: guitarrac/chatwork@0.0.2 version: 2.1 workflow: build: jobs: - chatwork/notify: message: Hello+Chatwork%21 room_id: '12345'
環境変数 CHATWORK_TOKEN
に API Token だけ仕込んでおけばサクッと使えます。
また、when に対応してあるので、「on_success
成功時 (デフォルト) 」、「on_fail
失敗時」、「always
いつでも」の状況に合わせた送信ができます。
Orb を作る
基本は公式を見ましょう。
namespace とか、circleci cli の基本操作は沿っておく必要があります。
ただし、公式 CircleCI Orb はいずれもこの構成とはずれています。ひどい。 ということで、実際どうやって構成しているのかを把握します。
Orb の構成を把握する
Orb を使うのはともかく、作るとなると「作ること自体は簡単」です。 ただ、「テストやCI/CD」まで含めると面倒ですが、テンプレート化してしまえばあとは流れ作業で作れます。
このテンプレート化がドキュメントがなく、CircleCI 公式Orb でもどれもバラバラなので把握が面倒です。
公式を見つつどんな構造化把握します。
基本構造
公式Orbもそうなのですが、Orb のディレクトリは基本的に次の構造になるはずです。
. ├── .circleci │ └── config.yml ├── LICENSE.md ├── README.md ├── src │ ├── commands │ │ └── your_command.yml │ ├── examples │ │ └── your_command.yml │ └── @orb.yml └── tests ├── your_test.yml └── your_test_n.yml
シンプルに、@orb.yml に全部定義というのもいいと思います。
.circleci
Orb自体のCI/CDは CircleCI 自身で行います。後述。
src/@orb.yml
Orb のDescription と依存する Orb などを書きます。
例えば、次のように書くと
version: 2.1 description: > Build images and push them to the Amazon Elastic Container Registry. See this orb's source: https://github.com/circleci-public/aws-ecr-orb orbs: aws-cli: circleci/aws-cli@0.1.13
Orbのページでは次のように表示されます。
実際のところ、@orb.yml
じゃなくても orb.yaml
で動作します。
私は orb.yml
にしていますが、公式はみんな @orb.yml
なのでそっちにしておけばいいでしょう。
src/commands/
commandsフォルダの中に置いたファイル名がコマンド名になります。 いいですね、この割り切り。
ecr-login というコマンドなら、ecr-login.yml というファイル名にします。
description: "Authenticate into the Amazon ECR service" parameters: region: type: env_var_name default: AWS_REGION description: > Name of env var storing your AWS region information, defaults to AWS_REGION steps: - run: name: Log into Amazon ECR command: | # aws ecr get-login returns a login command w/ a temp token LOGIN_COMMAND=$(aws ecr get-login --no-include-email --region $<<parameters.region>>) # save it to an env var & use that env var to login $LOGIN_COMMAND
すると、ecr-login としてコマンド定義が公開される。
src/examples/
コマンドのサンプルを定義します。 examplesフォルダの中に置いたファイル名がExample名になります。
なのでコマンドごとにファイルを用意したり、Workflow として一連の流れを定義したりです。
たとえば Workflow simple-build-and-push-image
を定義して、それを Orb画面で simple-build-and-push
として表示したいなら次の定義になります。
description: Log into AWS, build and push image to Amazon ECR usage: version: 2.1 orbs: aws-ecr: circleci/aws-ecr@x.y.z workflows: build_and_push_image: jobs: # build and push image to ECR - aws-ecr/build-and-push-image: # required if any necessary secrets are stored via Contexts context: myContext # AWS profile name, defaults to "default" profile-name: myProfileName # name of env var storing your AWS Access Key ID, defaults to AWS_ACCESS_KEY_ID aws-access-key-id: ACCESS_KEY_ID_ENV_VAR_NAME # name of env var storing your AWS Secret Access Key, defaults to AWS_SECRET_ACCESS_KEY aws-secret-access-key: SECRET_ACCESS_KEY_ENV_VAR_NAME # name of env var storing your AWS region, defaults to AWS_REGION region: AWS_REGION_ENV_VAR_NAME # name of env var storing your ECR account URL, defaults to AWS_ECR_ACCOUNT_URL account-url: AWS_ECR_ACCOUNT_URL_ENV_VAR_NAME # name of your ECR repository repo: myECRRepository # set this to true to create the repository if it does not already exist, defaults to "false" create-repo: true # ECR image tags (comma separated string), defaults to "latest" tag: latest,myECRRepoTag # name of Dockerfile to use, defaults to "Dockerfile" dockerfile: myDockerfile # path to Dockerfile, defaults to . (working directory) path: pathToMyDockerfile
Orb画面は、simple-build-and-push とファイル名になります。
tests
テストを定義しておきます。
ここは CircleCI 上で解釈されて実行されるので、いい感じにファイルを置きます。 もちろん CircleCI 上で config.yaml を走らせることもできて、その場合はconfig.yaml の記法に沿っておく必要があります。
公式Orbは案外tests フォルダを持っていないのですが、devに挙げたorbで挙動をテストするようにしています。
testsフォルダを設けている例として、mafuyuk さんや私のOrb を例にします。
例えば、mafuyuk/terraform-orb は terraform 操作を行う Orbです。 その test.yml は次のようになっています。
version: 2.1 jobs: build: executor: name: terraform/default tag: 0.11.11 steps: - checkout - run: name: Create Sample Terraform command: | mv test/backend.tf backend.tf mv test/main.tf main.tf mv test/provider.tf provider.tf mv test/variables.tf variables.tf # - terraform/init # - terraform/plan # - terraform/apply: # use-plan: false
コメントアウトされていますが、ニュアンスはわかります。
テストのときは、cache のようなファイル操作ができないのは circleci cli の制約を食らうのでそこは注意です。 なんだそれ感はある。
もし、外部リポジトリをクローンするなどSSH Key を渡したりといった込み入ったことをする必要があるなら、こっちのOrbが参考になるでしょう。
ポイントは、テストをどう走らせるかです。
test: # ref: https://github.com/ganta/git-orb/blob/master/.circleci/config.yml # Workaround for passing a SSH key with orb-tools/local-test-build command # https://github.com/CircleCI-Public/orb-tools-orb/blob/330d11bb0cdef3c2f24cb4e2c595ca01e55bfd8c/src/%40orb.yml#L107-L134 commands: local-test-build: parameters: path: type: string steps: - run: cp << parameters.path >> tmp-config-src/config.yml - run: mkdir -pv $(dirname tmp-config-src.yml) - run: circleci config pack tmp-config-src > uncompiled-config.yml - run: cat uncompiled-config.yml - run: circleci config process uncompiled-config.yml > config.yml # Cannot access "~/.ssh" from working directory. # pass spin up env's CIRCLE_TAG to local circleci command. - run: command: | cp ~/.ssh/id_rsa ./id_rsa circleci local execute --checkout-key ./id_rsa -c config.yml -e CIRCLE_TAG=${CIRCLE_TAG} | \ tee local_build_output.txt /dev/stderr | \ tail -n 1 | \ grep "Success"
Orb のCI/CD をする
Orbを作ったら、それをいい感じでCircleCI 上で Orb のCI/CD をしたいでしょう。
CI/CDでは当然、Orb のLint、テストをしたり、Orb Dev に挙げて動作を事前テストしたり、Devから Production に Promote したりしたいです。 Promote はGitHub Tag に準じるといいでしょう。
つまりこれ。
- CircleCI Orbで提供する処理を定義する
- CircleCI Orbで何ができるのかDescriptionやExampleを定義する
- Orb のCIを行う
- Orb のDevへのCDを行う
- Orb のProduction への CD (Promote) を行う
CircleCI-Public/orb-tools-orb
Orbリリースをするためのツールとして CircleCI が用意しているのが CircleCI-Public/orb-tools-orb です。
これを使うと、YAML のLintチェックから、パッケージング、テストの実行、GitHub Release 連動、DevへのCD、ProductionへのCD まで一通りできます、便利! ドキュメントがなく Example だけで動作もわかりにくいですが、がんばれ。
9.x 系
orb-tools-orb ですが、9.0.0でコマンドを悉く変えています。 9系はコミットでの制御に切り替える方針に伴って破壊的変更をしていて、コマンド自体が消えたりパラメーターが丸っと変わっています。
9.0.0 をやってるケースもあるので、気が向いたらあげよう。
CircleCI 公式の各種Orb は8.27.3 以前のバージョンで止まっていたりします。 対応確定するまでは 8.27.3 をターゲットで私は書いています。(8.27.6時点でもうダメだったりする)
基本的なパターン
公式Orb を参考にするといいでしょう。
circleci-orbs/config.yml at master · CircleCI-Public/circleci-orbs
slack-orb/config.yml at staging · CircleCI-Public/slack-orb
aws-ecr-orb/config.yml at master · CircleCI-Public/aws-ecr-orb
8.27.3 をターゲットにします。
定義
公式Orb 各種がそうですが、概ね次の流れです。
lint_pack-validate_publish-dev
PR へのプッシュでトリガーします。
- orb-tools/lint
- orb-tools/pack
- orb-tools/publish-dev
- orb-tools/trigger-integration-workflow
- orb-tools/trigger-integration-workflow
integration_tests-prod_deploy
テストはintegration タグやmaster-タグでトリガーします。 dev-promote-prodは、各種master-patch / master-minor / master-major のタグでトリガーされます。
- 各種テスト
- orb-tools/dev-promote-prod (patch)
- orb-tools/dev-promote-prod (minor)
- orb-tools/dev-promote-prod (major)
挙動は config.yaml だけ見ても全然分からず、動かしたりCircleCI-Public/orb-tools-orb をみるしかないでしょう。
リリースの流れ
PR を作って、CI と dev への CD が走るので通るのを確認、でマージすると Orb にpush されます。
Renovate を使うという手も。
Orbのテンプレートにする
適当に guitarrapc/chatwork-orb あたりはシンプルなのでテンプレートにしやすいでしょう。
Orbのnamespace を用意したら、これを fork したり適当にベースにして、OrbのコマンドとExample と orb.yml を変えてテスト書けば行けるはずです。(orb名やテストの置換はしましょう)
TIPS
既存の Orb で時々使いにくいやつがあるので、そうならないようにメモ。
- Jobとしての提供なら、Orb でExecutor 指定しよう
- 案外こういうのは多いので、どう使いたいか次第でまず決めるといい
- Stepsとしての提供なら、Orb はなるべく依存小さくするほうがいい
- Bash依存とか避けたほういいのは間違いない
- コマンドある前提で組んでしまって、コマンドないならサクッとエラーになるのでユーザーに任せるほうがユーザーにとっても幸せ