valid,invalid

関心を持てる事柄について

YAMLのAnchorとAliasを使ってconfigをDRYに書く

あらすじ

  • ふだん無意識に読み飛ばしているが使おうと思ったときに出てこなかった
  • YAML の anchor と alias を使うと色々 DRY に書ける
  • DRYに書いた

Anchor/Alias

YAML では &name (Anchor) で名前をつけて *name (Alias) で参照することができる*1

Example1: 重複排除

こんな感じの CircleCI 用の config.yml。

version: 2
jobs:
  bundle_npm_dependencies:
    docker:
      - image: circleci/node:8.7.0
    steps:
      - checkout
      - restore_cache: # <= 1つめ
          keys:
            - key-name-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - key-name-{{ arch }}
      - run: yarn install --pure-lockfile
      - save_cache:
          key:  key-name-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
          paths:
            - node_modules

  test:
    docker:
      - image: circleci/node:8.7.0
    steps:
      - checkout
      - restore_cache: # <= 2つめ
          keys:
            - key-name-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - key-name-{{ arch }}

  deploy:
    docker:
      - image: circleci/node:8.7.0
    steps:
      - checkout
      - restore_cache: # <= 3つめ
          keys:
            - key-name-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - key-name-{{ arch }}
      - deploy:
          command: ./scripts/${CIRCLE_JOB}.sh

中身はともかく restore_cache のステップが複数箇所存在する。キャッシュを invalidate するために key name を変える場合は3箇所漏れ無く更新しないといけない。

これを anchor & alias 使い、初出箇所で参照可能にする。

version: 2
jobs:
  bundle_npm_dependencies:
    docker:
      - image: circleci/node:8.7.0
    steps:
      - checkout
      - restore_cache: &restore_cache # <= anchor 作成
          keys:
            - key-name-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - key-name-{{ arch }}
      - run: yarn install --pure-lockfile
      - save_cache:
          key:  key-name-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
          paths:
            - node_modules

  test:
    docker:
      - image: circleci/node:8.7.0
    steps:
      - checkout
      - *restore_cache # <= 参照

  deploy:
    docker:
      - image: circleci/node:8.7.0
    steps:
      - checkout
      - *restore_cache # <= 参照
      - deploy:
          command: ./scripts/${CIRCLE_JOB}.sh

宣言箇所がわかりづらいのでファイルの先頭にまとめたりする。

version: 2
anchors:
  - cache_key: &cache_key key-name-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
  - restore_cache: &restore_cache
      keys:
        - *cache_key
        - key-name-{{ arch }}
  - image_name: &image_name circleci/node:8.7.0

jobs:
  bundle_npm_dependencies:
    docker:
      - image: *image_name
    steps:
      - checkout
      - *restore_cache
      - run: yarn install --pure-lockfile
      - save_cache:
          key: *cache_key
          paths:
            - node_modules

  test:
    docker:
      - image: *image_name
    steps:
      - checkout
      - *restore_cache

  deploy:
    docker:
      - image: *image_name
    steps:
      - checkout
      - *restore_cache
      - deploy:
          command: ./scripts/${CIRCLE_JOB}.sh

Example2: 環境変数として定義した値を参照

CircleCI の environment で設定した環境変数を使いまわしたいとき。

# NG例
version: 2
jobs:
  test:
    docker:
      - image: image_name
        environment:
          ARTIFACT_PATH: "/tmp/artifacts"
    steps:
      - run: mkdir -p ${ARTIFACT_PATH}
      - store_artifacts:
          path: ${ARTIFACT_PATH}

shell command の中に書かれた値は変数展開されるが、そうでない箇所は当然ながらただの文字列として解釈されてしまう。JSON に parse するとわかる。

{
  "version": 2, 
  "jobs": {
    "test": {
      "docker": [
        {
          "environment": {
            "ARTIFACT_PATH": "/tmp/artifacts"
          }, 
          "image": "image_name"
        }
      ], 
      "steps": [
        {
          "run": "mkdir -p ${ARTIFACT_PATH}"
        }, 
        {
          "store_artifacts": {
            "path": "${ARTIFACT_PATH}" # <= ココ
          }
        }
      ]
    }
  }
}

こういうときにも anchor & alias は使える。

version: 2
jobs:
  test:
    docker:
      - image: image_name
        environment:
          ARTIFACT_PATH: &artifact_path "/tmp/artifacts"
    steps:
      - run: mkdir -p ${ARTIFACT_PATH}
      - store_artifacts:
          path: *artifact_path

値の一部として展開することはできないので run: mkdir -p *artifact_path はNG。

その他

  • Online YAML Parser 便利
  • そもそも YAML の仕様書ってどこにあるんだ => YAML™ Specification Index のようだが読む気が起きない
  • DRYに書いたからといって読みやすくなるわけではないことも多いので留意

*1:記号はC言語由来?