2019年5月20日月曜日

Walterbot の「View log」 がすごく便利!

ブログズミ: [Wercker] Walterbot + Slack を試してみた

この記事を書いた時点では「View log」はなかったので紹介してませんでしたが、
これがすごく便利なので改めて紹介します。
(Walterbot については↑を参考にしてください)

View Log ボタンでエラーの周辺ログが見れる!!


「View Log」ボタンを押すと以下のようにログが見れます。


iutest の Wercker でのテストは
fused したコード(1ファイル化したもの)をさらに圧縮して(js の min.js みたいな) Wandbox に投げつけてコンパイル&実行しているので、エラーがすごく見づらいのですが・・・

ともあれ、すごく便利なので Wercker 使いの方は、ぜひ Walterbot を使いましょう!!

2019年5月13日月曜日

[C++] g++ 9.1.0 で variant が valueless_by_exception にならないケースに遭遇した

iutest の開発をしていたら、variant が valueless になったときのテストが失敗するようになった。


テストコードはこれ。
valueless_by_exception と出力されるのを期待しているが、実際には 0.2 という空になる前の値を出力している。

{
    PrintToLogChecker ck("valueless_by_exception");
    ::std::variant<int, ::std::string, float> v = 0.2f;
    try
    {
        struct S { operator int() { throw 42; } };
        v.emplace<0>(S());
    }
    catch(...)
    {
    }
    IUTEST_SUCCEED() << ::iutest::PrintToString(v);
}

この valueless にするコードは、
https://ja.cppreference.com/w/cpp/utility/variant/valueless_by_exception からとってきたもので、今までは valueless になっていたのを確認している。

関連するコードをいじってはいないので、環境周りの違いだと思い確認したところ、g++ 9.1.0 では valueless にならないことを確認しました。
また、valueless な状態を作る方法を探していたら、
https://cpprefjp.github.io/reference/variant/variant/valueless_by_exception.html に AlwaysThrow でのサンプルがあったので、そちらを使って確認したところ g++ 9.1.0 でも valueless になりました。

(typo がひでぇ)



よって、テストコードとりあえず以下のように修正しました。
{
    PrintToLogChecker ck("valueless_by_exception");
    ::std::variant<int, float, AlwaysThrow> v = 0.2f;
    try
    {
        struct S { operator int() { throw 42; } };
        v.emplace<0>(S());
    }
    catch(...)
    {
        IUTEST_INFORM_TRUE(v.valueless_by_exception());
    }
    if( !v.valueless_by_exception() )
    {
        try
        {
            v = AlwaysThrow();
        }
        catch(...)
        {
           IUTEST_INFORM_TRUE(v.valueless_by_exception());
        }
    }
    IUTEST_SUCCEED() << ::iutest::PrintToString(v);
}

これはバグ?
cpprefjpcppreference.com も、サンプルのコメントを読むと、確実に valueless な状態にはなるとは限らないように読み取れるのだが、(規格的に)これはそういうもんなのだろうか?それとも g++ 9.1.0 のバグなのだろうか?

一旦ここまでで記事公開させていただきます。
続報あれば、追記していきますmm



2019年5月7日火曜日

[Git] マージするときのリネーム上限とリネーム判定の抑制

最近、テスト結果を蓄積する試みをしていたら、git の挙動でハマったので備忘録として残しておきます。

git は意図的なリネームが可能ですが、意図しないリネームが発生したりすることがあります。
これは、リネームは削除と追加がまとまったもので(ほぼ)同じ内容のファイルが削除と追加(別名で)されたときに、git はリネームとして処理することがあります。
なので、こちらとしては望んでいない場合でもリネームとして扱われてしまうことがあります。
(ほとんどの人はこんなケースには遭遇しない気がするけど・・・)

で、なにが起きたかというと、
リネームが大量に検出されて、マージするときのリネーム上限に引っかかってしまいました。



というか、上限なんてあったんだ!という感じでしたが、git config merge.renameLimit で上限解放できるらしいです。
git config merge.renameLimit 9999


そもそもリネームじゃねぇっ!という場合は、merge.renames でリネーム処理を行わないようにもできます。
git config merge.renames false


たぶん、こういうケースにあたる人は少ない気がするけど、
merge.renameLimit と merge.renames config の紹介でした。
では。

2019年4月23日火曜日

[Cirrus CI] OSX VM 数制限対策としてマトリックスを直列にしてみた




OSX VM は1つしか使えない制限があり、その制限により CI が FAIL することがあったので対策しました。
もともとの YAML はこんな感じでした(一部抜粋)
cirrus_osx_test_task:
  only_if: *default-condition
  auto_cancellation: true
  osx_instance:
    image: mojave-xcode-10.1
  env:
    matrix:
      STDFLAG: -std=c++14
      STDFLAG: -std=c++17
  test_script: cd test && make -j4 showcxxversion default && make test


c++14 と c++17 コンパイルを並列していました。
単純にどちらかだけにしたら制限に引っかかることはなくなりますが、できれば減らしたくない条件です。
そこで、並列に実行するのではなく、直列で実行するようにしました。
修正した YAML はこんな感じです。
cirrus_osx_14_test_task: &osx_task
  only_if: *default-condition
  auto_cancellation: true
  osx_instance:
    image: mojave-xcode-10.1
  env:
    STDFLAG: -std=c++14
  test_script: cd test && make -j4 showcxxversion default && make test

cirrus_osx_17_test_task:
  <<: *osx_task
  env:
    STDFLAG: -std=c++17
  depends_on:
    - cirrus_osx_14_test
depends_on で依存を貼って、直列にしています。 YAML エイリアスが使えるため直列にしても記述量はさほど増えませんでした。メンテナンス性が問題になることはないでしょう。 ただし、実行時間が長くなるデメリットはあります。が、iutest の現状の開発ペースでは特に問題にならないのでこのようにしました。

今回は、これで以上です。
では。

2019年4月9日火曜日

Slack のステータス(障害情報)を通知する

ついこの間 Slack の障害が発生し、メッセージが正しく届かなかったり、スタンプが押せなかったりなどの影響がありましたね。
Slack の状態はステータスページで確認できます。また、Twitter アカウントもあります。


あとは、Atom feed と RSS も用意されているので、お好きな方法で状況を確認できます。


ただ、ステータスページを都度都度見るのはめんどくさいですし、twitter もタイムラインをずっと見てるわけじゃないので気づかなそう。RSS や Atom feed はリーダーを使ってない。。。

RPA で任意のツールに通知する
というわけで、RPA 組みました。
今回使ったのは Integromat です。最近のお気に入りです。

出来上がったシナリオは3オペレーションになりました。(こだわらなければ2ステップでできます)


Trigger は RSS で、Slack Status の RSS url をセットします。


間にある Text Parser はステータスからアイコンのファイル名を摘出するためのステップです。
アイコン画像にこだわらなければ、このステップは不要です。


最後は通知するステップです。Slack で通知してますが、ここはなんでも良いです。お好きなもので通知してください。
接続設定(slack ならスペースとチャンネル)をしたら、本文をセットしましょう。
RSS で取得した内容が使えるので、その内容を流しています。(特に決まりはないので、ここもお好きに編集してください。)


Slack ではアイコンを指定できるので、Text Parser で取得したステータスからアイコンの url をセットしました。



最後に、Slack Status は 30 分更新のようなので、Trigger の Interval も 30 分にします。


これで完成です。
シナリオを有効にしたら、以下のように通知が飛んできます。


最後に
Integromat はシナリオの Export/Import が可能です。
今回紹介したシナリオの blueprint を Gist で公開しているので、こちらを Import して始めることもできます。
Integromat 便利なので是非使ってみてください。では~


2019年4月2日火曜日

Integromat で Gmail のメールを開いたときに文字化けする問題の回避方法

みなさんは RPA に何を使われてますか?最近はたくさんのツールが存在しますね。
RPA ツールというと個人的には IFTTT を代表に上げるのですが、RPA ツールで検索するとちょっと自分のイメージと違うツールがたくさん出てくるのでなにか「IFTTT や Zapier のようなサービスをまとめた」いい言葉があればなーと思っているこの頃です。


前置きはこのくらいにして、本題に入ります。
最近 Integromat をよく使っていたのですが、ついにフリープランを卒業しベーシックプラン($9/月)をはじめました。Zapier が年契約で $18.33/月 なので、安くて助かってます。
Integromat はフリープランでも複雑なシナリオが書けるので大変オススメなサービスです。




ただ、少し困ったことが起きました。
メールを受信したことをトリガーにその内容を通知するシナリオを書こうとしたところ、日本語が正しく読めなかったのです。


具体的な症状としては、iso-2022-jp エンコーディングなメールが読めません。utf-8 は大丈夫でした。
とはいえ、メール配信側にお願いして utf-8 にしてもらうという解決方法は難しそうです。

Zapier はどうなのかというと、iso-2022-jp のメールも問題なく読めます。


じゃ、Zapier にお願いしよう
Zapier はフリープランでも 5 Zap までなら無料で使えるので、Zapier でメールのトリガーだけお願いして、あとは Integromat でやる作戦です。

Zapier 側の設定は Gmail トリガーと Webhook アクションです。
とりあえず、payload には Subject と Body を入れました。


Integromat 側は Gmail トリガーをやめて、Webhook トリガーに変更。それ以降は Integromat の世界で好き勝手にいじれます。


これでなんとか作りたいシナリオができそうです。

最後に
Integromat に Feature Request を出したので、ユーザーの方はぜひ vote をお願いしますmm
https://www.integromat.com/en/requests/feature-requests/p/gmail-encoding-iso-2022-jp

(メールの日本語が読めないことがあります・・・だと正直、日本で布教しにくい・・・)

2019年3月27日水曜日

git gc --aggressive するそのまえに

Git が遅い!!
そんなときに検索をするとだいたい
「git gc しましょう」だったり、「git gc --aggressive --prune=now」だったりします。

が、試してみても劇的に速くなることってあんまりなくないですか?
特に --aggressive オプションを付けると git gc 自体がくそ長く、その割に効果は感じられなくて残念な結果になることが多いです。
※個人的な感想です

なんか遅い。もう少し深掘りしてみませんか?

GIT_TRACE=1 GIT_TRACE_PERFORMANCE=1
git コマンドは GIT_TRACE を有効にすると、中で何をしているのか追跡できるようになります。
GIT_TRACE=1 git st


git st はエイリアスなので、git status コマンドが呼ばれている様子がわかりますね。
さらに、GIT_TRACE_PERFORMANCE=1 をしておくと、処理時間の計測までしてくれます。

GIT_TRACE=1 GIT_TRACE_PERFORMANCE=1 git st


処理を追えるようになったので、あとはボトルネックになってる部分を見ていけばいいわけです。
(TRACE には他にも項目があります。詳細はこちら→ Git - 環境変数

事例
上に載せたスクリーンショットは、サブモジュールがあるリポジトリでの git status コマンドの計測なのですが、見ていただくとサブモジュールに対してもステータスを確認しにいっているのがわかります。普段なにげなく使っていたコマンドですが、サブモジュールを見に行ってるとは知りませんでした。

サブモジュールの使い方にもよると思いますが、
データ(アセット)をサブモジュールにしているリポジトリでの git status がすごく遅くなっていたため、TRACE するとサブモジュールの status に時間がかかってました。(でかいからな)

自分の場合は、そのサブモジュールで作業することはなく興味のない情報だったので、以下の設定で無視するようにしました。
git config diff.ignoreSubmodule dirty

これで 90% くらい速くなりました。

最後に
速度を求めるなら、ボトルネックを知らなければ効率的な解決はできませんよね!

2019年3月20日水曜日

[CI] 帰ってきたクラウド版 Drone



以前サービスされていた Drone.io のクラウド版が帰ってきてました。
というわけで、(ちょうどとある CI からジョブを引っ越しさせたいと思っていたので)
早速使ってみました。

セットアップ
まずはサインナップからですが省略します。

ダッシュボードにリポジトリがリストアップされているので、CI したいリポジトリを選んで「ACTIVATE」を押します。


リポジトリの「SETTINGS」が開くので、もう一度「ACTIVATE」を押します。


すぐに ACTIVATE は完了し、設定が表示されます。
いくつか設定できる項目がありますが、最初に気にしておくところは「Project visibility」くらいだと思います。


Drone は YAML config のみのようなので、つづいて yaml を作成をしていきます。

パイプライン作成
それでは、.drone.yml ファイルを書いていきましょう。
フォーマットは公式ドキュメントを見るのが一番良いのでそちらを参照してください。
Pipeline

iutest では以下のように書いてみました。
kind: pipeline
name: benchmark_test

steps:
- name: bench
  image: gcc
  commands:
    - cd test
    - make -C benchmark
    - make bench

steps に配列で処理を定義できる感じですね。簡単!

これをリポジトリルートに置いたら commit/push しましょう。
自動でビルドが開始されます。(反応もいい感じ)




複数のパイプライン
Drone では複数のパイプラインを定義できます。

kind: pipeline
name: test1

※1つ目のパイプラインの処理

---
kind: pipeline
name: test2

※2つ目のパイプラインの処理


このように --- で区切ることで複数のパイプラインが作成できます
それぞれのパイプラインの書き方は特に変わらずそのままです。
(※YAML anchor/alias はこれを越えられないみたいです)




Plugin を使う
Drone ではプラグインを使うことで、複雑な処理を書くことなくパラメータの設定だけでツールやサービスを簡単に利用できる仕組みになっています。
どんなプラグインがあるかはこちらにまとまっています。
http://plugins.drone.io/

今回は Slack Plugin を使ってステータス通知をしてみました。

steps:
- name: slack
  image: plugins/slack
  settings:
    webhook:
      from_secret: SLACK_WEBHOOK
    channel: ci
    username: drone
    icon_url: https://raw.githubusercontent.com/drone/brand/master/logos/png/drone-logo-dark_256.png
  when:
    status: [ success, failure ]


Webhook URL は見えないように from_secret: SLACK_WEBHOOK にしています。
こちらは「SETTINGS」ページの「Secrets」のところで生成できます。


動かしてみるとこのように通知が飛んできました!

簡単ですね。

skip コメント
つづいて、CI のスキップコメントメッセージについてまとめているので、そちらの更新も行いました。
https://github.com/srz-zumix/ci-skip


ドキュメントは見当たらなかったのですが、他の CI サービスと同様に [ci skip] [skip ci] が使えます。
また、***no_ci*** も使えるようです。

Badge
シメは、お決まりのバッジですね。
「SETTINGS」 タブを開くと最下部に「Badges」があるのでそこから取得できます。




一点だけ注意があって、ブランチ指定をした場合に生成される URL が ?ref=/refs/heads/develop のようにパラメータが付きますが、執筆時にはこの URL だと「build: none」と表示されてしまってました。

https://0-8-0.docs.drone.io/badges/
0.8.0 のドキュメントですが、こちらを参考に ?branch=develop に変えたところ、イメージが取得できました。


最後に
クラウド版は一度閉じてはいましたが、その間もオンプレミスで進化を続けていただけあって、簡単にかつ素早く動いたので良かったです。
(いつから戻ってきたんだろ)

最近、他の CI サービスの・・・で苦労してたので、シンプルに使える Drone がまた使えるようになったのはすごく助かりますね。
オンプレミス版を Jenkins の代わりとして使ってる企業も多いようなので、ぜひ一度使ってみてください。

ではでは。

2019年3月11日月曜日

Codacy は Dockerfile の検証もしてくれてた

Codacy
Python とか Ruby とかの静的解析をしてくれるサービスは多いですが、Dockerfile も見てくれているとは!
Codacy というか Hadolint



1.13以降はMAINTAINERの代わりにLABELを使うようにしよう - Qiita
MAINTAINER じゃなくて LABEL maintainer "srz_zumix <...>" のようにするとイイらしいっす!









あ、 Visual Studio Code でも出てた


Dockerfile の書き方でこうしたほうがいいよってのがわかるので便利ですね!

2019年3月5日火曜日

EditorConfig の設定に従っているかをテストしてみた

どうも、先日 .editorconfig の記述に typo をずっと放置していて驚愕した僕です。
ブログズミ: .editorconfig ファイル自体の Lint を CI で回す

.editorconfig "自体"の Lint ができたので、今回は .editorconfig に従っているかチェックしたいと思います。
こんな方にオススメ
.editorconfig を開発途中から導入した場合
iutest もそうですが、仕事のプロジェクトでも最初から .editorconfig が用意されているケースがなく、あとから .editorconfig を追加するケースをよく目にしています。
.editorconfig を導入したあとは、対応しているエディタで作業していれば、.editorconfig に従っていない記述が混入することはないはずですが、開発途中に導入された場合は変更が加わらない限り補完されません。

従ってないことでクリティカルな問題が出ることは少ないと思いますが、作業者が変更したところ以外に差分が出るとレビューの際にじゃまになったりするので、やる意味はあるかなと思います。

.editorconfig に対応していないエディタからのコミッターがいる
このケースは存在するのだろうか?という疑問は置いておいて…
git の pre-commit とかに仕込んでおくと、対応している人と対応してない人が無駄に差分を出し合うことがなくなるかなーと思います。

使った Lint ツール
今回の検証では、2 つの Lint ツールを使いました。
使ってみた結果、一長一短あったため 2 つとも採用しています。
Editorconfig Checker
最初に見つけたツールが、Editorconfig Checker です。
https://github.com/editorconfig-checker
複数の言語に対応しているようで、JavaScript/Go/PHP/Java のリポジトリがありました。
今回は最初に見つけたのが npm package だったのでそちらを使うことにしました。

eclint
もう1つは eclint です。
https://github.com/jedmao/eclint
こちらも npm package があり、Editorconfig checker を npm で取ってきたこともあり、すぐに使えそうだったので試してみました。


Editorconfig Checker の使い方と注意
npm install などセットアップについては省略します。

使い方は単純で、解析したいパスを渡すだけです。
オプションで除外パスの指定もできるので、エディタで編集しないようなファイルを除外しつつチェックができます。
また、デフォルトで .git ディレクトリ以下や画像ファイルの拡張子は除外してくれるのと、--dotfiles or -d オプションで dotfiles を除外できるので、単純にルートディレクトリを解析するでも問題ないかもしれません。

以下は、iutest でのオプションの抜粋です。
Visual Studio のソリューション・プロジェクトファイルだけ除外しました。
CI サービス上で実行する想定なので、git 管理外のファイル除外には対応してません。

"scripts": {
    "eccheck": "editorconfig-checker --exclude-regexp \"\\.sln|\\..*proj\" ."
  },

これで npm run eccheck を実行すれば検証してくれます。
このような形でレポートされます。


注意点
Editorconfig Checker は、文字コード(charset)の検出ができないです。
BOM のつけ忘れを検出したいなーと思ったのですが、これができなかったのがもう1つツールを使うことにした理由です。

eclint の使い方と注意
続いて eclint です。
こちらも npm install などのセットアップについては省略します。
eclint も基本的には検証したいパスを引数に渡すだけで使用できます。
Editorconfig Checker とは違い除外オプションはないので、ワイルドカードでの指定や、find コマンドなどを使ってファイルのリストを渡すなどの工夫が必要です。

以下は、iutest でのオプションの抜粋です。
"scripts": {
    "eclint": "npm-run-all eclint:*",
    "eclint:src": "eclint check ./src",
    "eclint:include": "find ./include -type f -not -name iutest_ver.hpp | xargs eclint check -s 2",
    "eclint:root": "find . -maxdepth 1 -type f | xargs eclint check"
  },

これで npm run eclint を実行すれば検証されます。
(除外指定をしつつワンライナーで書くのが手間だったので npm-run-all を使いました)
このような形でレポートされます。


Editorconfig Checker ではできなかった charset の検出ができました。

注意点
さて、charset の検出はできましたが、いいことだけではありません。
eclint はインデント判定もちゃんとしてくれるのですが、これがちょっとノイズになってしまいました。
「インデント」ではなく「整列」のために空白を入れたものまで警告されるためです。

具体的には以下のようなコードです。
enum Level
    {
          LOG_INFO
        , LOG_WARNING
        , LOG_ERROR
        , LOG_FATAL
        , LOG_LEVEL_NUM
    };
私は列挙や関数引数を改行して書く場合、「,」を行末ではなく行頭に書くようにしています。
そして、視認性の観点から最初の「,」のいらない要素に関しては、名前の先頭が揃うように空白を挿入しています。

これがインデント幅 4 の設定に適合せず警告されるのです。
個人的に、これは許容したいため 「-s 2」 オプションをつけて、インデント幅 2 として検証をしています。

また、コメント中の整形であれば以下の設定(もしくはオプション指定)で無視することができます。


ただ、それでも無視しきれないケースがあったため、iutest_ver.hpp だけ検証対象から除外しています。
(Editorconfig Checker は幸か不幸か、インデントの検証がされないようなので iutest_ver.hpp は Editorconfig Checker で検証するって感じです)

おまけ: GitHub Actions
GitHub Actions の使い方が少しわかってきたので、こちらの検証は GitHub Actions で行うようにしました。今回は、特に自分ではアクションを作らず、既存の npm アクションを使ってます。

action "lint:editorconfig" {
  uses = "actions/npm@master"
  args = ["apt-get update && apt-get install -y git && npm install && npm run lint:editorconfig"]
  runs = "bash -c"
}

最初に git をインストールしているのは npm アクションのコンテナに git がインストールされていないため、git 依存のあるパッケージを取ってこれないためです。
npm の ENTRYPOINT は npm コマンドになっているので、runs に bash を指定して install から npm run までするようにしました。
この点だけ手間取りましたが、あとは簡単に組めました。



まとめ



自動化しましょう!

2019年2月25日月曜日

GitHub Actions を試してみた





1ヶ月ほど前から使えるようになったので、ちょこちょこ弄ってました。

割と手こずったのでハマったところを書いておこうと思ったのですが、ちゃんとドキュメント読めよ。に落ち着いたので、ここに書くことはなくなりました。。。
ので、駄文です。

とりあえずやってみた
とりあえず、GitHub Actions でなにかをさせることができました。
「なにか」については、別途ブログにします。
今回は、GitHub Actions を使ってみた感想と、詰まったところを書きます。
使い方とかはすでに書かれている方も多いと思うので、ここでは書きません。


いろいろ手間取った
とりあえず、なにかさせることはできるようになったのですが、そちらは既存のアクションを使っています。最初、アクションを自分で用意してやっていたのですが、いきなり見よう見まねでやったのがよくなかったです。。。

サンプルリポジトリを用意して、ドキュメントを理解しながらやるべきでした。。。
https://github.com/srz-zumix/github_actions_sample


今後やりたいこと
GitHub Actions ならではなことができたらなーと思ってます。
ただ単に他の CI サービスと同じことをさせるのではなく、なにか便利なことができればなと。

では。

2019年2月19日火曜日

[Azure Pipelines] ファイルがなかった場合にステップをスキップする

Add an exists() on Task Custom Condition - Developer Community



とあるファイルがある場合にだけ実行する Job/Step が欲しくなったのですが、condition に簡単に書けたら嬉しかったのですが、今の所ない模様。

こんな感じできたら・・・
- script: |
    echo "xml がある場合にしたいこと"
  condition: exist('./test/*.xml')


状態を variable に記録して条件とする
ワンステップではできないものの、比較的簡単にやりたいことは実現できます。

condition で variables を参照してスキップさせることはできるので、ファイルが存在しない場合にスキップ用の variable をセットするようにしました。

- script: |
    TEST_RESULTS=`find ./test -maxdepth 1 -name *.xml 2>/dev/null`
    if [ $? -ne 0 ] || [ -z "$TEST_RESULTS" ]; then echo "##vso[task.setvariable variable=XmlFileNotExist]true"; fi

- script: |
    echo "xml がある場合にしたいこと"
  condition: ne(variables['XmlFileNotExist'], 'true')



この方法であれば、「ファイルが存在するかどうか」以外でも、条件付けできそうですね。

2019年2月12日火曜日

Windows + Git で実行権限をつけて commit する

何回かハマって、何回も調べ直してるので、記憶の定着・備忘録のために自分のブログにも書き残しておくことにした。

Windows + Git Bash では 644 で commit される
chmod +x としておいても commit したときに書き換えられます。
$ git commit -m 'sample action a'
[master e432bd4] sample action a
 4 files changed, 21 insertions(+), 4 deletions(-)
 create mode 100644 action-a/Dockerfile
 create mode 100644 action-a/README.md
 create mode 100644 action-a/entrypoint.sh

この状態でもう一回 chmod +x しても差分として出てこない。

git update-index --add --chmod=+x
パーミッションの変更は上記コマンドで行います。

しょっちゅう忘れるので alias にしました。
git config --global alias.addx 'update-index --add --chmod=+x'

これで、addx エイリアスから chmod +x できるようになりました。



+x/-x 両方に対応したエイリアス
せっかくなのでもう少し汎用的なエイリアスも用意しました。
git config --global alias.chmod '!f(){ cd ${GIT_PREFIX:-.} && git update-index --add --chmod=$1 ${@:2:($#-1)};};f'

git chmod +x hoge
git chmod -x foo bar
のように使えます。

2019年2月4日月曜日

.editorconfig ファイル自体の Lint を CI で回す



[*.{cpp,hpp,ipp}}
charset = utf-8-bom
単純な typo ですが、今までずーと気づかずに config が仕事してくれてると思い込んでました。。。
直すのは簡単です。簡単ですが、また同じ失敗をする可能性があります。(にんげんだもの)

というわけで、テスト書きました。

テスト方法
.editorconfig の記法は概ね ini ファイルと一致します。
なので、適当な ini ファイルパーサーに読み込ませてエラーが起きないかどうか?で検証できそうです。

iutest は C++ のテスティングフレームワークですが、ツール系には Python も使用しています。
Python には ConfigParser があるのでそちらでテストを書きました。

import sys
import os

try:
    from configparser import ConfigParser
except ImportError:
    from ConfigParser import SafeConfigParser as ConfigParser


class EditorConfig(object):
    def __init__(self, path):
        self.name = os.path.basename(path)
        self.fp = open(path)
        self.first_head = True

    def readline(self):
        if self.first_head:
            self.first_head = False
            return '[global]\n'
        return self.fp.readline()

    def __iter__(self):
        return self

    def __next__(self):
        line = self.readline()
        if not line:
            raise StopIteration
        return line

    next = __next__  # For Python 2 compatibility.


def main():
    path = sys.argv[1]
    ini = ConfigParser()
    if not os.path.exists(path):
        sys.stderr.write('%s not found...' % path)
        sys.exit(2)
    ini.readfp(EditorConfig(path))
    sys.exit(0)

if __name__ == '__main__':
    main()

基本的には ini ファイルをパーサーにかけるだけなんですが、1点だけポイントがあります。
EditorConfig は、ディレクトリを上がって .editorconfig を探索しますが、root = true を最初に書いておくとそこで探索が打ち切られます。
この root = true が ini ファイルとしてはセクションのない要素のためエラーになってしまいます。
そのため、最初に [global] というダミーセクションを挿入して ConfigParser に渡してます

CI に組み込む
Python が実行できる CI サービスであればなんでも問題ありません。
今回 iutest では Codeship で cpplint やインクルードガードのチェックや絶対パスが混入してないかの、雑多なテストを行っているのでそちらに組み込みました。



こんな感じに検出されます!
これでもう安心ですね。