2020年3月31日火曜日

[CircleCI] CLI を Windows で使う

昨今の CI サービスは CLI ツールが用意されていることが多いです。
ジョブを実行したり、コンフィルのバリデーションをしたりでます。
だいたいは、ジョブを本番に push するまえにテストするために使うことを考えて作られていると思います。

CircleCI も例にもれず CLI コマンドがあります。
CircleCI のローカル CLI の使用 - CircleCI

インストール方法はこちらに記載されています。
ぱっと見 Mac/Linux のみで Windows 版はないようにも見えますが、ちゃんと存在しました。
手動でのダウンロード」に「GitHub リリース」へのリンクがあり、そちらから Windows 版もダウンロードできます。


ダウンロードしたら任意のパスに展開し、パスを通せば普通に使えます。

例)バリデーションエラーの様子

修正前
workflows:
  default-test:
    jobs:
      - parallel1:
        i: 0
      - parallel2:
        i: 4

修正後
workflows:
  default-test:
    jobs:
      - parallel1:
          i: 0
      - parallel2:
          i: 4

↑の違いわかりますでしょうか?
i の位置に注目してください。


CircleCI の YAML はなんか他のサービスと比べてインデントが厳しい印象・・
こちらのエラーもインデントがずれていただけでした。これ気づくの難しい・・


正解は i のインデントレベルが 1 段違うだけでした。

最後に
コンフィグをいじる際に何度も本番環境でトライアンドエラーを繰り返すとクレジットを消費してしまってもったいないので、これからはローカルでバリデーションしてから push するようにしたいと思います。

今回は以上です。
では。

2020年3月27日金曜日

[CircleCI] iutest のパイプラインを更新した&お詫びと訂正

さて今週も iutest の CI パイプライン更新を紹介します。
先週はこちら「ブログズミ: [Azure Pipelines] iutest のパイプラインを Multi-stage pipelines に変更した
今週は CircleCI です。

YAML の整理
まずは CircleCI のサンプルを参考にして、YAML をキレイに変更しました。
もともと gcc/clang でそれぞれ 4 パターンビルドしていたジョブを、アンカー・エイリアスを駆使して gcc/clang 別々のジョブに分けました。



以下、YAML の Before/After になります。
Before
version: 2
default: &default
shell: /bin/bash --login
docker:
- image: circleci/buildpack-deps:cosmic-browsers
jobs:
optimize_test:
working_directory: ~/srz-zumix/iutest
parallelism: 4
<<: *default
environment:
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
MAKE_OPTION: -j4 OUTPUTXML=1
MAKE_TARGET: all_tests
MAKE_RUN_TARGET: run_all_tests
steps:
- checkout
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
- restore_cache: &restore_cache
keys:
# This branch if available
- v2-dep-{{ .Branch }}-
# Default branch if not
- v2-dep-master-
# Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly
- v2-dep-
- run: &setup-step
name: SetUp
command: |
sudo apt-get -y --allow-unauthenticated update
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
wget -q -O - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository -y 'deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic-9 main'
sudo apt-get -y --allow-unauthenticated update
sudo apt-get -qq install clang-9
sudo apt-get -qq install g++-8
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 90
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-9 60
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-9 60
- save_cache: &save_cache
key: v2-dep-{{ .Branch }}-{{ epoch }}
paths:
# This is a broad list of cache paths to include many possible development environments
# You can probably delete some of these entries
- vendor/bundle
- ~/virtualenvs
- ~/.m2
- ~/.ivy2
- ~/.bundle
- ~/.go_workspace
- ~/.gradle
- ~/.cache/bower
# Test
- run:
name: check compiler version
working_directory: test
command: |
if [ "$CIRCLE_NODE_INDEX" == "0" ]; then
make CXX=g++ showcxxversion;
make CXX=clang++ showcxxversion;
fi
- run:
name: g++ minimum_tests and show feature / spec
command: if [ "$CIRCLE_NODE_INDEX" == "0" ]; then make minimum_tests && ./minimum_tests --spec --feature; fi
working_directory: test
environment:
CXX: g++
- run:
name: clang++ minimum_tests and show feature / spec
command: if [ "$CIRCLE_NODE_INDEX" == "0" ]; then make minimum_tests && ./minimum_tests --spec --feature; fi
working_directory: test
environment:
CXX: clang++
- run:
name: g++ build
command: |
make clean
case $CIRCLE_NODE_INDEX in 0) make $MAKE_TARGET $MAKE_OPTION OPTIMIZE=-O2 ;; 1) make $MAKE_TARGET $MAKE_OPTION OPTIMIZE=-Os ;; 2) make $MAKE_TARGET $MAKE_OPTION OPTIMIZE=-O3 ;; 3) make $MAKE_TARGET $MAKE_OPTION OPTIMIZE=-O4 ;; esac
working_directory: test
environment:
CXX: g++
- run:
name: g++ test
command: |
make $MAKE_RUN_TARGET
working_directory: test
environment:
CXX: g++
- run:
name: clang++ build
command: |
make clean
case $CIRCLE_NODE_INDEX in 0) make $MAKE_TARGET $MAKE_OPTION $MAKE_ADD_OPTION OPTIMIZE=-O2 ;; 1) make $MAKE_TARGET $MAKE_OPTION $MAKE_ADD_OPTION OPTIMIZE=-Os ;; 2) make $MAKE_TARGET $MAKE_OPTION $MAKE_ADD_OPTION OPTIMIZE=-O3 ;; 3) make $MAKE_TARGET $MAKE_OPTION $MAKE_ADD_OPTION OPTIMIZE=-Oz ;; esac
cp *.json $CIRCLE_ARTIFACTS/
working_directory: test
environment:
CXX: clang++
MAKE_ADD_OPTION: CXXFLAGS=-ftime-trace
- run:
name: clang++ test
command: |
make $MAKE_RUN_TARGET
working_directory: test
environment:
CXX: clang++
- run:
name: move result xml
command: |
if [ "$CIRCLE_NODE_INDEX" == "0" ]; then
mkdir -p $CIRCLE_TEST_REPORTS/junit/;
find . -type f -regex ".*/test/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;;
fi
# Teardown
- store_test_results:
path: /tmp/circleci-test-results
# Save artifacts
- store_artifacts:
path: /tmp/circleci-artifacts
- store_artifacts:
path: /tmp/circleci-test-results
workflows:
version: 2
default-test:
jobs:
- optimize_test:
filters:
branches:
only:
- master
- develop
- /^feature.*/
- /^fix.*/
- /^circleci.*/
view raw config.yml hosted with ❤ by GitHub


After
version: 2.1
aliases:
- &branch-filter
branches:
only:
- master
- develop
- /^feature.*/
- /^fix.*/
- /^circleci.*/
- &default
working_directory: ~/srz-zumix/iutest
shell: /bin/bash --login
docker:
- image: circleci/buildpack-deps:disco-curl
environment:
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
MAKE_OPTION: -j4 OUTPUTXML=1
MAKE_TARGET: default
MAKE_RUN_TARGET: test
# MAKE_TARGET: all_tests
# MAKE_RUN_TARGET: run_all_tests
- &restore_cache
keys:
# This branch if available
- v2-dep-{{ .Branch }}-
# Default branch if not
- v2-dep-master-
# Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly
- v2-dep-
- &setup-step
name: SetUp
command: |
sudo apt-get -y --allow-unauthenticated update
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
wget -qO - http://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository -y 'deb http://apt.llvm.org/disco/ llvm-toolchain-disco-9 main'
sudo apt-get -y --allow-unauthenticated update
sudo apt-get -qq install clang-9
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-9 60 --slave /usr/bin/clang++ clang++ /usr/bin/clang++-9
sudo apt-get -qq install g++-8
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 90 --slave /usr/bin/g++ g++ /usr/bin/g++-8
- &save_cache
key: v2-dep-{{ .Branch }}-{{ epoch }}
paths:
# This is a broad list of cache paths to include many possible development environments
# You can probably delete some of these entries
- vendor/bundle
- ~/virtualenvs
- ~/.m2
- ~/.ivy2
- ~/.bundle
- ~/.go_workspace
- ~/.gradle
- ~/.cache/bower
- &check-version-step
name: check compiler version
working_directory: test
command: |
if [ "$CIRCLE_NODE_INDEX" == "0" ]; then
make showcxxversion;
fi
- &min-tests-and-spec-step
name: minimum_tests and show feature / spec
command: if [ "$CIRCLE_NODE_INDEX" == "0" ]; then make minimum_tests && ./minimum_tests --spec --feature; fi
working_directory: test
- &build-step
name: build
command: |
make clean
case $CIRCLE_NODE_INDEX in \
0) make $MAKE_TARGET $MAKE_OPTION $MAKE_ADD_OPTION OPTIMIZE=-O2 ;; \
1) make $MAKE_TARGET $MAKE_OPTION $MAKE_ADD_OPTION OPTIMIZE=-O3 ;; \
2) make $MAKE_TARGET $MAKE_OPTION $MAKE_ADD_OPTION OPTIMIZE=-Os ;; \
3) make $MAKE_TARGET $MAKE_OPTION $MAKE_ADD_OPTION OPTIMIZE=$FAST_OPTIMIZE ;; \
esac
cp *.json $CIRCLE_ARTIFACTS/ 2>/dev/null || true
working_directory: test
- &test-step
name: run test
command: |
make $MAKE_RUN_TARGET
working_directory: test
- &move-result-step
name: move result xml
command: |
if [ "$CIRCLE_NODE_INDEX" == "0" ]; then
mkdir -p $CIRCLE_TEST_REPORTS/junit/;
find . -type f -regex ".*/test/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;;
fi
- &build-test-steps
- checkout
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
- restore_cache: *restore_cache
- run: *setup-step
- save_cache: *save_cache
# Test
- run: *check-version-step
- run: *min-tests-and-spec-step
- run: *build-step
- run: *test-step
- run: *move-result-step
# Teardown
- store_test_results:
path: /tmp/circleci-test-results
# Save artifacts
- store_artifacts:
path: /tmp/circleci-artifacts
- store_artifacts:
path: /tmp/circleci-test-results
jobs:
gcc_optimize_test:
parallelism: 4
<<: *default
environment:
CXX: g++
FAST_OPTIMIZE: -Og
# FAST_OPTIMIZE: -Ofast # https://github.com/srz-zumix/iutest/issues/396
steps: *build-test-steps
clang_optimize_test:
parallelism: 4
<<: *default
environment:
CXX: clang++
FAST_OPTIMIZE: -Ot
MAKE_ADD_OPTION: CXXFLAGS=-ftime-trace
steps: *build-test-steps
workflows:
version: 2.1
default-test:
jobs:
- gcc_optimize_test:
filters: *branch-filter
- clang_optimize_test:
filters: *branch-filter
view raw config.yml hosted with ❤ by GitHub


ファイルの先頭部分はあまり変わってないですが、ファイルの末尾あたりにあるジョブの定義とワークフローの定義が YAML のアンカー・エイリアスを使ってとてもシンブルになっていると思います。

CI が定めた名前のキーではなく、任意のキーでアンカーを作って再利用するやり方はとてもいいなーと思いました。
(CI サービスによってはスキーマにないキーを許容しないサービスもあった気もするが・・)

GitHub PR の Checks に表示する
PR の Checks に表示するには「CircleCI Checks」をインストールする必要があったみたいだったので、遅ればせながらインストールしました。

「Organization Settings」の「VCS」に「Manage GitHub Checks」ボタンがあるのでそちらでインストールできます。
権限確認と適用する範囲を決めたら完了です。



このように表示されます。



お詫びと訂正


本書で、Circle CI の 4 並列制限がなくなったと書いていましたが、あれは真っ赤なウソでした。
お詫びして訂正致します。

今回並列数を増やそうとして気づきました。。




実際には、コンテナの並列数は今までどおり 4 つまでとなっているようです。(ただ、これは CircleCI 1.0 のころの設定画面なので CircleCI 2.0 になってどう管理されいるのか、まだ良くわかってません)
また、OSS プランはベースがあくまでも Free プランなので、ジョブの並列数は Pricing 記載の通り 1 つまでの制限になっているようです。
ジョブが1つずつ実行されていることを確認しました。



これを機に、各 CI サービスの並列実行のサンプルリポジトリを作成することとしました。
近いうちに CI Specs に付け加える予定です。

以上。

2020年3月16日月曜日

[Azure Pipelines] iutest のパイプラインを Multi-stage pipelines に変更した

技術書典8 (技術書典 応援祭)で本を出しましたが、本を書いてていくつかのサービスで新しい機能に追従できてないことが発覚したため、ちょこちょこと更新をしていっています。
今回は、iutest の「Azure Pipeline」を「Multi-stage pipelines」に移行しました。

移行結果
まずはじめに移行してどうなったのか見ていただこうと思います。
Before

After


ながいので一部分だけのキャプチャですが、なんとなーく見やすくなったような気がしますね。
テストの種類ごとにステージを分けて、ステージの依存と並列化をしました。

Preview features の有効化
Multi-stage pipelines は現在 Preview feature のため、設定で有効になっている必要があります。
アカウントのボタンを開くと「Preview features」メニューがありそこから変更ができます。


(組織の管理者権限があれば、個人か組織どちらの設定をするか選択できます。)
ここで「Multi-stage pipelines」が「On」になっているか確認してください。
(自分の場合有効化した記憶がないのですが、もしかして Multi-stage な YAML を push すると自動で有効になる??この機能が発表されたのが去年の 5 月なので、以前に有効化していた可能性もなにきしもあらず)




dependsOn 設定で依存と並列
ステージは dependsOn で依存関係を定義できます。iutest では Test/ConfigDisabledTest/ConfigNoFeatureTest が Precheck ステージに依存してます。
また、依存先が同じステージは並列に実行されます。つまり、下記 YAML では Test/ConfigDisabledTest/ConfigNoFeatureTest が並列になります。
stages:
  - stage: Precheck
    jobs:
      - job: syntax_test
        pool:
          vmImage: ubuntu-16.04
        steps:
          - script: make -C test -j4 syntax_tests_run
            displayName: 'syntax test'
  - stage: Test
    dependsOn: Precheck
    jobs:
      - template: .ci/azure_pipelines/template-make-test.yml
        parameters:
          name: default
  - stage: ConfigDisabledTest
    dependsOn: Precheck
    jobs:
      - template: .ci/azure_pipelines/template-make-disabled-test.yml
        parameters:
          name: disabled
  - stage: ConfigNoFeatureTest
    dependsOn: Precheck
    jobs:
      - template: .ci/azure_pipelines/template-make-nofeature-test.yml
        parameters:
          name: nofeature

依存するステージがない複数のステージを並列にしたい場合は以下のように記述します。
dependsOn がないと Test1 → Test2 の順で逐次実行されますが、「dependsOn: []」とすることで Test1/Test2 が並列実行されます。
dependsOn がないと1つ前に書かれているステージに暗黙的に依存すると考えてるとよさそうです。
stages:
- stage: Test1

- stage: Test2
  dependsOn: []

最後に
Multi-stage pipelines にしたことでステージ単位でグルーピングでき、見た目がわかりやすくなったと思います。また、依存関係もステージ単位でできようになり楽になりました。

今回は以上です。では。



宣伝

2020年3月6日金曜日

[技術書典8] 技術書典 応援祭・BOOTH にて販売を開始しました

表題のとおり、技術書典 応援祭BOOTH にて拙著
「あつまれ CI サービス タダではじめる継続的インテグレーション生活」の販売を開始しました。

過去6年間で使ってきた「無料で使える CI サービス」の中から厳選した 13 サービスを一冊にまとめました。
第 I 部では YAML の使用事例とともに1つ1つサービスを紹介します。
第 II 部では各トピックごとに CI サービスを比較検証した内容を紹介します。

技術書典 応援祭
https://techbookfest.org/product/4931278088437760


BOOTH
https://srz-zumix.booth.pm/items/1879985


感想や issue お待ちしておりますmm
https://github.com/srz-zumix/techbookfest8

2020年3月3日火曜日

[技術書典] PDF から必要なページを切り出す

自分用備忘録。

見本ように表紙や目次を提出することがある。
その際に出来上がった PDF から必要なページだけ取り出したいと思ったときに毎回検索して調べているので、メモです。

「Chrome で PDF を開いて必要なページを指定して印刷する」

以上。


【宣伝】