はいどーも。こんばんは。CircleCI のシーバです
CircleCI Config SDK
何日か前に CircleCI Config SDK が発表されましたー!わーい!やったー!
って喜んでみたものの、どういうことかあんまりよくわかってない!(えー)
ので、とりあえず触ってみることにした。TypeScript や JavaScript で CircleCI の設定ファイルを生成できるみたい
仕組み
例えば Job はこんな感じで定義できる
const testJob = new CircleCI.Job("test", dockerNode.reuse());
testJob.addStep(new CircleCI.commands.Checkout());
testJob.addStep(new CircleCI.commands.Run({
command: "npm install && npm run test"
}));
TypeScript で SDK が作られてるので型の情報が出てきて便利。同じ感じで Workflow とかも定義して、最終的にはその定義を YAML として書き出すことができる
ほうほうそれで?
書き出すことができるのは分かったけど、どんな風に使うんだろう?って思いながらぼーっとブログを読みながら考えてみた。こういうことかな?
- SDK を使って設定ファイルを生成する関数を作ると、似たような YAML を何度も手で書かなくて済むようにできる
- さらに、モジュール化して NPM レジストリにアップロードすることで、その関数やツールを共有できる
ふむふむ。面白いかもしれない
設定ファイル生成のタイミング
静的に config.yml を生成してその生成された設定ファイルをコミットすることもできるし、Dynamic Config と組み合わせて CircleCI の実行時に動的に生成することもできる
静的生成は、プロジェクトの雛形を生成するときに対話形式で config.yml を生成するようなツールを作るのに使えそうかなぁ。動的生成は、実行時の情報を使って設定ファイルを生成できて便利そう
とりあえず触ってみよう
ということで、雰囲気だけは分かったので、やってみよう。動的な生成の方で。まずは、モジュールを作って NPM レジストリにアップロードしてみるー
ブログの記事に書いてあるサンプルにちょこちょこ手を入れてこんな感じになった
const CircleCI = require("@circleci/circleci-config-sdk");
const fs = require('fs');
const nodeConfig = new CircleCI.Config();
// Node executor
const dockerNode = new CircleCI.executors
.DockerExecutor("cimg/node:lts")
.toReusable("docker-node");
nodeConfig.addReusableExecutor(dockerNode);
// Test Job
const testJob = new CircleCI.Job("test", dockerNode.reuse());
testJob.addStep(new CircleCI.commands.Checkout());
testJob.addStep(new CircleCI.commands.Run({
command: "npm install && npm run test"
}));
nodeConfig.addJob(testJob);
// Deploy Job
const deployJob = new CircleCI.Job("deploy", dockerNode.reuse());
deployJob.addStep(new CircleCI.commands.Checkout());
deployJob.addStep(new CircleCI.commands.Run({ command: "npm run deploy" }));
nodeConfig.addJob(deployJob);
// Workflow
const nodeWorkflow = new CircleCI.Workflow("node-test-deploy");
nodeConfig.addWorkflow(nodeWorkflow);
const wfTestJob = new CircleCI.workflow.WorkflowJob(testJob);
nodeWorkflow.jobs.push(wfTestJob);
const wfDeployJob = new CircleCI.workflow.WorkflowJob(deployJob, {
requires: ["test"], filters: { branches: { ignore: ["/.*/"] } }
});
nodeWorkflow.jobs.push(wfDeployJob);
/**
* Exports a CircleCI config for a node project
*/
module.exports = function writeNodeConfig(deployTag, configPath) {
wfTestJob.parameters = {
...wfTestJob.parameters, filters: { tags: { only: deployTag } }
};
wfDeployJob.parameters.filters.tags = { only: deployTag };
fs.writeFile(configPath, nodeConfig.stringify(), (err) => {
if (err) console.error(err);
})
}
型情報があるから書きやすいのは分かるんだけど、JS で書いて読みやすいのかなぁ?って思ってたら、実際に書いて眺めてみるとわりと悪くない
んで、これを使って試しに設定ファイルを生成してみると
❯ node
Welcome to Node.js v18.2.0.
Type ".help" for more information.
> const writeNodeConfig = require('.');
undefined
> writeNodeConfig('/v.*/', './config.yml')
undefined
こういう YAML ファイルができた
❯ cat config.yml # This configuration has been automatically generated by the CircleCI Config SDK. # For more information, see https://github.com/CircleCI-Public/circleci-config-sdk-ts # SDK Version: 0.0.0-development version: 2.1 setup: false executors: docker-node: docker: - image: cimg/node:lts resource_class: medium jobs: test: executor: name: docker-node steps: - checkout - run: command: npm install && npm run test deploy: executor: name: docker-node steps: - checkout - run: command: npm run deploy workflows: node-test-deploy: jobs: - test: filters: tags: only: /v.*/ - deploy: requires: - test filters: branches: ignore: - /.*/ tags: only: /v.*/
ふむふむ。んで、NPM レジストリにアップロードしておいた
それを使うプロジェクト
次は、今アップロードしたモジュールを使って CircleCI を動かすプロジェクトを作る
Dynamic Config を使うので、こういう構成にして

ちょっと分かりにくいけど、.circleci/dynamic の下に、ビルド用の JS プロジェクトが置いてある。下の方にある package.json はビルド対象のプロジェクトのやつ
ビルド用の index.js はこれだけ
const path = require("path");
const writeNodeConfig = require("@bufferings/hello-circleci-config-sdk");
writeNodeConfig("/v.*/", path.join(__dirname, "../dynamicConfig.yml"));
なるほどなぁ。これだけかー
で Dynamic Config の setup 用の config.yml は、こうなる
version: 2.1 orbs: continuation: circleci/continuation@0.3.1 node: circleci/node@5.0.2 setup: true jobs: generate-config: executor: node/default steps: - checkout - node/install-packages: app-dir: .circleci/dynamic - run: name: Generate config command: node .circleci/dynamic/index.js - continuation/continue: configuration_path: .circleci/dynamicConfig.yml workflows: dynamic-workflow: jobs: - generate-config
CircleCI でプロジェクトを登録して、プロジェクト設定 > Advanced > Dynamic Config をオンにして実行

動いたーヽ(=´▽`=)ノ
感想
- JS で書くのは思ってたより心地よい。型情報があるのでサクサク書ける
- NPM レジストリにアップロードして共通化するのはとても便利そう
- アップロードしない場合でも、たくさんのサービスを持ってて同じような YAML を書きまくってるモノレポみたいな環境だと、いっこ関数を定義しておいてそれをうまく使えばとても楽そう
一方で
- YAML だと読めば何をするかが分かる安心感があるけど、SDK で生成する場合は結局どうなるのか分かりにくいだろうなと思う
- なので、あまり複雑なことをやりすぎると手に負えなくなりそう
でも
- 複雑な処理をうまく関数で包んで隠すことができると、また新しい考えが生まれてきそう
ってところかな。実際に使うときは TypeScript で ES Modules なスタイルで書きたい気持ち
ぜひ使ってみてくださいー!