幾つかCircleCI Orbを作っていたのですが、記事にしていませんでした。
どれも必要になって作ったもので、いずれもプロダクションで万単位が実行されれているのでいい感じで使えそうなら幸いです。
概要
- 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依存とか避けたほういいのは間違いない
- コマンドある前提で組んでしまって、コマンドないならサクッとエラーになるのでユーザーに任せるほうがユーザーにとっても幸せ
REF
https://qiita.com/na0ya/items/0118434ff1bb6e3bb3c0