2019年4月23日火曜日

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




OSX VM は1つしか使えない制限があり、その制限により CI が FAIL することがあったので対策しました。
もともとの YAML はこんな感じでした(一部抜粋)
1
2
3
4
5
6
7
8
9
10
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 はこんな感じです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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月16日火曜日

Docker Toolbox でホストのカレントディレクトリをマウントするときの備忘録

Windows + Docker Toolbox だとちょっとしたコツが必要なのでメモ

  • C:\Users 配下
    • -v "/$(pwd)":/target
      ※ ルートの / が windows のオプションとして認識されちゃうので //c/ みたいになるように / を余分に付ける
  • それ以外
    • VM の設定をいじる必要がある
    • めんどくさいので mklink したほうが早い

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 便利なので是非使ってみてください。では~

{
"name": "Slack Status RSS",
"flow": [
{
"id": 1,
"module": "rss:TriggerNewArticle",
"version": 4,
"parameters": {
"url": "https://status.slack.com/feed/rss",
"gzip": true,
"include": [],
"password": "",
"username": "",
"maxResults": 2
},
"mapper": {},
"metadata": {
"designer": {
"x": 0,
"y": 0
},
"parameters": [
{
"name": "url",
"type": "url",
"label": "URL",
"required": true
},
{
"name": "maxResults",
"type": "number",
"label": "Maximum number of returned items",
"required": true
},
{
"name": "username",
"type": "text",
"label": "User name"
},
{
"name": "password",
"type": "text",
"label": "Password"
},
{
"name": "include",
"type": "select",
"label": "Process RSS fields",
"multiple": true,
"validate": {
"enum": [
"google-merchant-center"
]
}
},
{
"name": "gzip",
"type": "boolean",
"label": "Request compressed content",
"required": true
}
],
"interface": [
{
"name": "title",
"type": "text",
"label": "Title"
},
{
"name": "description",
"type": "text",
"label": "Description"
},
{
"name": "summary",
"type": "text",
"label": "Summary"
},
{
"name": "author",
"type": "text",
"label": "Author"
},
{
"name": "url",
"type": "url",
"label": "URL"
},
{
"name": "dateUpdated",
"type": "date",
"label": "Date updated"
},
{
"name": "dateCreated",
"type": "date",
"label": "Date created"
},
{
"name": "comments",
"type": "url",
"label": "Comments"
},
{
"name": "image",
"spec": [
{
"name": "title",
"type": "text",
"label": "Name"
},
{
"name": "url",
"type": "url",
"label": "URL"
}
],
"type": "collection",
"label": "Image"
},
{
"name": "categories",
"spec": {
"type": "text"
},
"type": "array",
"label": "Categories"
},
{
"name": "source",
"spec": [
{
"name": "title",
"type": "text",
"label": "Name"
},
{
"name": "url",
"type": "url",
"label": "URL"
}
],
"type": "collection",
"label": "Source"
},
{
"name": "enclosures",
"spec": [
{
"name": "url",
"type": "url",
"label": "URL"
},
{
"name": "type",
"type": "text",
"label": "Type"
},
{
"name": "length",
"type": "number",
"label": "Length"
}
],
"type": "array",
"label": "Enclosures"
},
{
"help": "Other RSS fields. All values are in text format.",
"name": "rssFields",
"spec": [
{
"name": "title",
"type": "text",
"label": "title"
},
{
"name": "description",
"type": "text",
"label": "description"
},
{
"name": "pubdate",
"type": "text",
"label": "pubdate"
},
{
"name": "link",
"type": "text",
"label": "link"
},
{
"name": "guid",
"type": "text",
"label": "guid"
}
],
"type": "collection",
"label": "RSS fields"
}
]
}
},
{
"id": 3,
"module": "regexp:Parser",
"version": 1,
"parameters": {
"global": false,
"pattern": "(?<status>.*):.*",
"multiline": false,
"sensitive": true,
"continueWhenNoRes": false
},
"mapper": {
"text": "{{1.title}}"
},
"metadata": {
"designer": {
"x": 300,
"y": 0
},
"parameters": [
{
"name": "pattern",
"type": "text",
"label": "Pattern",
"required": true
},
{
"name": "global",
"type": "boolean",
"label": "Global match",
"required": true
},
{
"name": "sensitive",
"type": "boolean",
"label": "Case sensitive",
"required": true
},
{
"name": "multiline",
"type": "boolean",
"label": "Multiline",
"required": true
},
{
"name": "continueWhenNoRes",
"type": "boolean",
"label": "Continue the execution of the route even if the module finds no matches",
"required": true
}
],
"expect": [
{
"name": "text",
"type": "text",
"label": "Text"
}
],
"interface": [
{
"name": "status",
"type": "text",
"label": "status"
}
]
}
},
{
"id": 2,
"module": "slack:ActionCreateMessage",
"version": 2,
"parameters": {
"account": 0
},
"mapper": {
"text": "{{1.title}}\n{{1.description}}\n{{1.url}}\n{{1.dateUpdated}}",
"username": "Slack Status",
"parse": false,
"linkNames": true,
"iconType": "url",
"attachments": [],
"type": "channel",
"iconUrl": "https://status.slack.com/img/v2/{{3.status}}.png",
"channelId": ""
},
"metadata": {
"designer": {
"x": 600,
"y": 0
},
"restore": {
"account": {
"label": ""
},
"channelId": {
"mode": "chose",
"label": "slack_status"
},
"parse": {
"mode": "chose"
},
"unfurlLinks": {
"mode": "chose"
},
"unfurlMedia": {
"mode": "chose"
},
"linkNames": {
"mode": "chose"
},
"iconType": {
"label": "Icon URL"
},
"attachments": {
"mode": "chose",
"items": []
},
"type": {
"label": "to a selected channel"
}
},
"parameters": [
{
"name": "account",
"label": "Connection",
"type": "account",
"required": true
}
],
"expect": [
{
"name": "text",
"label": "Text",
"type": "text",
"multiline": true
},
{
"name": "username",
"label": "User name",
"type": "text"
},
{
"name": "parse",
"label": "Parse message text",
"type": "boolean",
"required": true
},
{
"name": "unfurlLinks",
"label": "Unfurl links",
"type": "boolean"
},
{
"name": "unfurlMedia",
"label": "Unfurl media",
"type": "boolean"
},
{
"name": "linkNames",
"label": "Link names",
"type": "boolean",
"required": true
},
{
"name": "iconType",
"label": "Displaying the icon",
"type": "select",
"required": true,
"validate": {
"enum": [
"none",
"url",
"emoji"
]
}
},
{
"name": "attachments",
"label": "Message attachments",
"type": "array",
"labels": {
"add": "Add an attachment",
"edit": "Edit an attachment",
"field": "Attachment"
},
"spec": [
{
"name": "fallback",
"label": "Fallback",
"type": "text",
"required": true
},
{
"name": "color",
"label": "Color",
"type": "text"
},
{
"name": "pretext",
"label": "Text above the formatted data",
"type": "text"
},
{
"name": "authorName",
"label": "Author name",
"type": "text"
},
{
"name": "authorLink",
"label": "Author link",
"type": "url"
},
{
"name": "authorIcon",
"label": "Author icon (URL)",
"type": "url"
},
{
"name": "title",
"label": "Title",
"type": "text"
},
{
"name": "titleLink",
"label": "Title link",
"type": "url"
},
{
"name": "text",
"label": "Text inside the message attachment",
"type": "text"
},
{
"name": "imageUrl",
"label": "Image (URL)",
"type": "text"
},
{
"name": "thumbUrl",
"label": "Thumbnail (URL)",
"type": "text"
},
{
"name": "footer",
"label": "Footer",
"type": "text"
},
{
"name": "footerIcon",
"label": "Footer icon (URL)",
"type": "url"
},
{
"name": "ts",
"label": "Timestamp",
"type": "number"
},
{
"name": "fields",
"label": "Fields inside the message attachment",
"type": "array",
"labels": {
"add": "Add a field",
"edit": "Edit a field",
"field": "Field inside a message attachment"
},
"spec": [
{
"name": "title",
"label": "Description",
"type": "text",
"required": true
},
{
"name": "value",
"label": "Value",
"type": "text",
"required": true
},
{
"name": "short",
"label": "Display side-by-side with other values",
"type": "boolean",
"required": true
}
]
}
]
},
{
"name": "type",
"label": "Where to send the message ",
"type": "select",
"required": true,
"validate": {
"enum": [
"channel",
"group",
"im"
]
}
},
{
"name": "iconUrl",
"label": "A valid URL that displays the icon",
"type": "url",
"required": true
},
{
"name": "channelId",
"label": "Channel",
"type": "select",
"required": true
}
],
"advanced": true
}
}
],
"metadata": {
"version": 1,
"scenario": {
"roundtrips": 1,
"maxErrors": 3,
"autoCommit": false,
"sequential": false,
"confidential": false,
"dataloss": false,
"dlq": false
},
"designer": {
"orphans": []
},
"zone": "eu1.integromat.com"
}
}
view raw blueprint.json hosted with ❤ by GitHub

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

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