circleci config pack で分割した yml から設定ファイル (config.yml) を作成してみた
circleci config pack で分割した yml から設定ファイル (config.yml) を作成してみた背景
最近 CircleCI に入門しました。config.yml を書いているうちに記述量は少ないもののそれでも Slack 通知のテンプレなど記述すると見通しが悪くなるので、なんとかしたいと思って調べてみるとCircleCI CLI
にcircleci config pack
というコマンドがあり、分割された yml をパッケージ化できることを知ったので使ってみました。
因みに CircleCI2.1 の想定です。
サンプルリポジトリ
サンプルとなるソースをリポジトリにまとめてみたのでこれを参考に紹介していきます。
CircleCI CLI のインストール
なにわともわれこれがないと進まないので以下のリンクを参照して circleci
コマンドを使えるようにします。
因みに自分は brew
を使ってインストールしました。
brew install circleci
# Mac 版の Docker を既にインストールしている場合は、
brew install --ignore-dependencies circleci
circleci config pack コマンドについて
コマンドは
circleci config pack <ディレクトリ名>
となります。
分割した yml を格納しているディレクトリを指定します。
以下のドキュメントに詳しく書かれています。
CLI の pack コマンドを使用すると、複数のファイルをまとめて 1 つの YAML ファイルを作成できます。 pack コマンドには、ディレクトリ ツリー内の複数ファイルにまたがる YAML ドキュメントを解析する FYAML が実装されています。 これは、容量の大きな Orbs のソース コードを分割している場合に特に利便性が高く、Orbs の YAML 構成のカスタム編成を行うことができます。 circleci config pack は、ディレクトリ構造とファイルの内容に基づいて、ファイル システム ツリーを 1 つの YAML ファイルに変換します。 pack コマンドを使用するときのファイルの名前や編成に応じて、最終的にどのような orb.yml が出力されるかが決まります。 以下のフォルダー構造を例に考えます。
以上、ドキュメントに書かれているように、ファイル名やディレクトリ構成などの制約が結構強いです。
完成予定の config.yml
見ての通り Slack のテンプレートが幅をきかせてます。
onfig.yml
version: 2.1
orbs:
aws-cli: circleci/aws-cli@1.3.1
aws-s3: circleci/aws-s3@2.0.0
slack: circleci/slack@4.3.1
commands:
install_yarn_version:
description: Install specific Yarn version
steps:
- run:
command: |
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.22.10
echo 'export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"' >> $BASH_ENV
name: Install specific Yarn version
notify_slack_fail:
steps:
- slack/notify:
custom: |
{
"text": "CircleCI job failed.",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Job Failed. :red_circle:",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Job*: ${CIRCLE_JOB}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Project*:\\n$CIRCLE_PROJECT_REPONAME"
},
{
"type": "mrkdwn",
"text": "*Branch*:\\n$CIRCLE_BRANCH"
},
{
"type": "mrkdwn",
"text": "*Author*:\\n$CIRCLE_USERNAME"
}
],
"accessory": {
"type": "image",
"image_url": "https://assets.brandfolder.com/otz5mn-bw4j2w-6jzqo8/original/circle-logo-badge-black.png",
"alt_text": "CircleCI logo"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Mentions*:\\n$SLACK_PARAM_MENTIONS"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
event: fail
notify_slack_pass:
steps:
- slack/notify:
custom: |
{
"text": "CircleCI job succeeded!",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Job Succeeded. :white_check_mark:",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Job*: ${CIRCLE_JOB}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Project*:\\n$CIRCLE_PROJECT_REPONAME"
},
{
"type": "mrkdwn",
"text": "*Branch*:\\n$CIRCLE_BRANCH"
},
{
"type": "mrkdwn",
"text": "*Commit*:\\n$CIRCLE_SHA1"
},
{
"type": "mrkdwn",
"text": "*Author*:\\n$CIRCLE_USERNAME"
}
],
"accessory": {
"type": "image",
"image_url": "https://assets.brandfolder.com/otz5mn-bw4j2w-6jzqo8/original/circle-logo-badge-black.png",
"alt_text": "CircleCI logo"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
event: pass
restore_node_modules_cache:
steps:
- restore_cache:
keys:
- node_modules-{{ .Branch }}-packages-{{ checksum "yarn.lock" }}
name: Restore node_modules cache
restore_yarn_cache:
steps:
- restore_cache:
keys:
- yarn-{{ .Branch }}-packages-{{ checksum "yarn.lock" }}
name: Restore Yarn cache
run_yarn_install:
description: Install dependencies
steps:
- run:
command: yarn install --frozen-lockfile
name: Install dependencies
save_node_modules_cache:
description: Save node_modules cache
steps:
- save_cache:
key: node_modules-{{ .Branch }}-packages-{{ checksum "yarn.lock" }}
name: Save node_modules cache
paths:
- node_modules
save_yarn_cache:
description: Save Yarn cache
steps:
- save_cache:
key: yarn-{{ .Branch }}-packages-{{ checksum "yarn.lock" }}
name: Save Yarn cache
paths:
- ~/.cache/yarn
executors:
default:
docker:
- environment:
TZ: Asia/Tokyo
image: circleci/node:14.15.5
working_directory: ~/repo
jobs:
deploy_dev:
executor: default
steps:
- checkout
- attach_workspace:
at: .
- aws-s3/sync:
arguments: --delete
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-region: AWS_DEFAULT_REGION
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
from: ./dist/
to: s3://dev.7890987.xyz
- aws-cli/setup:
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-region: AWS_DEFAULT_REGION
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
profile-name: default
- run:
command: |
aws cloudfront create-invalidation --distribution-id "$CLOUDFRONT_DISTRIBUTION_ID" --paths '/*'
name: Restore CloudFront cache
- notify_slack_fail
- notify_slack_pass
prepare:
executor: default
steps:
- checkout
- install_yarn_version
- restore_yarn_cache
- restore_node_modules_cache
- run_yarn_install
- save_yarn_cache
- save_node_modules_cache
- run:
command: yarn build
name: Build
- persist_to_workspace:
paths:
- node_modules
- dist
root: .
- notify_slack_fail
slack:
executor: default
steps:
- run:
command: echo test
name: Send Notification to Slack
- notify_slack_pass
workflows:
version: 2
deploy_dev:
jobs:
- prepare:
filters:
branches:
only:
- develop
- deploy_dev:
filters:
branches:
only:
- develop
requires:
- prepare
release:
jobs:
- prepare:
filters:
branches:
only:
- main
tags:
only: /^v[0-9]+\.[0-9]+\.[0-9]+$/
- slack:
filters:
branches:
only:
- main
tags:
only: /^v[0-9]+\.[0-9]+\.[0-9]+$/
requires:
- prepare
ディレクトリ構成とマッピング
.circleci
ディレクトリとは別に分割したファイルだけ管理する circleci
ディレクトリを作成しています。
$ tree
.
└── circleci
├── @orb.yml
├── commands
│ ├── @commands.yml
│ └── @slack.yml
├── executors
│ └── default.yml
├── jobs
│ ├── deploy_dev.yml
│ ├── deploy_prod.yml
│ └── prepare.yml
└── workflows
├── @workflows.yml
├── develop.yml
└── release.yml
上記のディレクトリ構成の場合、以下のようにマッピングされます。
@ で始まるファイルの内容は、その親フォルダーのレベルにマージされます。
# ここに @orb.yml の内容が表示されます
commands:
# ここに @commands.yml の内容が表示されます
# ここに @slack.yml の内容が表示されます
executors:
default: # この default フォルダーは default.ymlに @ が付いていない為です。
# ここに default.yml の内容が表示されます
jobs:
deploy_dev:
# ここに deploy_dev.yml の内容が表示されます
deploy_prod:
# ここに deploy_prod.yml の内容が表示されます
prepare:
# ここに prepare.yml の内容が表示されます
workflows:
# ここに @workflows.yml の内容が表示されます
develop:
# ここに develop.yml の内容が表示されます
release:
# ここに release.yml の内容が表示されます
ファイルの中身
circleci/@orb.yml
version: 2.1
orbs:
aws-cli: circleci/aws-cli@1.3.1
aws-s3: circleci/aws-s3@2.0.0
slack: circleci/slack@4.3.1
circleci のバージョンと orb の宣言のみにしてます。
circleci/commands/@commands.yml
install_yarn_version:
description: 'Install specific Yarn version'
steps:
- run:
name: Install specific Yarn version
command: |
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.22.10
echo 'export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"' >> $BASH_ENV
restore_yarn_cache:
steps:
- restore_cache:
name: Restore Yarn cache
keys:
- yarn-{{ .Branch }}-packages-{{ checksum "yarn.lock" }}
save_yarn_cache:
description: 'Save Yarn cache'
steps:
- save_cache:
name: Save Yarn cache
key: yarn-{{ .Branch }}-packages-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
run_yarn_install:
description: 'Install dependencies'
steps:
- run:
name: Install dependencies
command: yarn install --frozen-lockfile
restore_node_modules_cache:
steps:
- restore_cache:
name: Restore node_modules cache
keys:
- node_modules-{{ .Branch }}-packages-{{ checksum "yarn.lock" }}
save_node_modules_cache:
description: 'Save node_modules cache'
steps:
- save_cache:
name: Save node_modules cache
key: node_modules-{{ .Branch }}-packages-{{ checksum "yarn.lock" }}
paths:
- node_modules
yarn のインストールやキャッシュ周りのコマンドを集約してます。
circleci/commands/@slack.yml
notify_slack_pass:
steps:
- slack/notify:
event: pass
custom: |
{
"text": "CircleCI job succeeded!",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Job Succeeded. :white_check_mark:",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Job*: ${CIRCLE_JOB}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Project*:\\n$CIRCLE_PROJECT_REPONAME"
},
{
"type": "mrkdwn",
"text": "*Branch*:\\n$CIRCLE_BRANCH"
},
{
"type": "mrkdwn",
"text": "*Commit*:\\n$CIRCLE_SHA1"
},
{
"type": "mrkdwn",
"text": "*Author*:\\n$CIRCLE_USERNAME"
}
],
"accessory": {
"type": "image",
"image_url": "https://assets.brandfolder.com/otz5mn-bw4j2w-6jzqo8/original/circle-logo-badge-black.png",
"alt_text": "CircleCI logo"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
notify_slack_fail:
steps:
- slack/notify:
event: fail
custom: |
{
"text": "CircleCI job failed.",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Job Failed. :red_circle:",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Job*: ${CIRCLE_JOB}"
}
]
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Project*:\\n$CIRCLE_PROJECT_REPONAME"
},
{
"type": "mrkdwn",
"text": "*Branch*:\\n$CIRCLE_BRANCH"
},
{
"type": "mrkdwn",
"text": "*Author*:\\n$CIRCLE_USERNAME"
}
],
"accessory": {
"type": "image",
"image_url": "https://assets.brandfolder.com/otz5mn-bw4j2w-6jzqo8/original/circle-logo-badge-black.png",
"alt_text": "CircleCI logo"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Mentions*:\\n$SLACK_PARAM_MENTIONS"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View Job"
},
"url": "${CIRCLE_BUILD_URL}"
}
]
}
]
}
Slack 周りをここに集約してます。これが一番邪魔だったんでスッキリしました。
circleci/executors/default.yml
working_directory: ~/repo
docker:
- image: circleci/node:14.15.5
environment:
TZ: Asia/Tokyo
今は default だけですが、環境毎の Executor ファイルをこのディレクトリで管理。
circleci/jobs/prepare.yml
executor: default
steps:
- checkout
- install_yarn_version
- restore_yarn_cache
- restore_node_modules_cache
- run_yarn_install
- save_yarn_cache
- save_node_modules_cache
- run:
name: Build
command: yarn build
- persist_to_workspace:
root: .
paths:
- node_modules
- dist
- notify_slack_fail
circleci/jobs/deploy_dev.yml と circleci/jobs/deploy_prod.yml
executor: default
steps:
- checkout
- attach_workspace:
at: .
- aws-s3/sync:
from: ./dist/
to: 's3://buecketname'
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
aws-region: AWS_DEFAULT_REGION
arguments: --delete
- aws-cli/setup:
profile-name: default
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
aws-region: AWS_DEFAULT_REGION
- run:
name: Restore CloudFront cache
command: |
aws cloudfront create-invalidation --distribution-id "$CLOUDFRONT_DISTRIBUTION_ID" --paths '/*'
- notify_slack_fail
- notify_slack_pass
circleci/workflows/@workflows.yml
version: 2
Workflow のバージョンだけの為のファイル
circleci/workflows/develop.yml
jobs:
- prepare:
filters:
branches:
only:
- develop
- deploy_dev:
requires:
- prepare
filters:
branches:
only:
- develop
circleci/workflows/release.yml
jobs:
- prepare:
filters:
tags:
only: /^v[0-9]+\.[0-9]+\.[0-9]+$/
branches:
only:
- main
- deploy_prod:
requires:
- prepare
filters:
tags:
only: /^v[0-9]+\.[0-9]+\.[0-9]+$/
branches:
only:
- main
config.yml の作成
以下のコマンドを実行します。
# circleci config pack <ディレクトリ名>
circleci config pack circleci
すると統合された結果が出力されます。
ただ、出力される内容はお世辞にも綺麗ではないです。。。
なのでリダイレクトを使うなどして config.yml
に直接上書きしてしまうのが手っ取り早いかと思います。
circleci config pack circleci >| .circleci/config.yml
また、config.yml を作成後は config.yml
のバリデーションをお勧めします。
自分の場合、インデント周りでエラーがでまくりでした。
circleci validate
個人開発したサービス