窓を作っては壊していた人のブログ

この謎のブログタイトルの由来を知るものはもういないだろう

GitHub Actionsのon pushでmaster branchだけ処理したい!

GitHub Actionsを使って、PRをmasterへmergeしたらbuildが走ってdeployする、みたいなことをしたかった時にハマった出来事です。 build & deploy周りの話は

【備忘録】Scala で Azure Functions やってみる(deploy編) - 窓を作っては壊していた人のブログ

の件です。

ネタバレ

  • PRのmergeと同時に行っていたブランチの削除がtriggerだった
  • deletedフラグを監視することでブランチの削除由来のアクションを抑制できる

on pushイベントが2回走ってる!

f:id:yamachu_co:20190329022649p:plain
2回走るon push to masterくん

PRをmasterにmergeしたらon pushイベントがどうしたことか2回走っています。 on pushイベント自体はブランチに関係なくcommitがpushされると走るのでその挙動自体には問題はありません。

問題なのはactions/binに実装されているfilter/branchでmasterを指定しただけだと効果がないということです。 というのも走っているactionsのどちらもmasterブランチに紐付いているからです。

なんとかしてmasterへのmergeだけを取り出せないか、やってみました。

案1. on pushイベントのevent.json内のref要素を見てfilter

on pushイベントは2回発火していましたが、GitHub Actionsでfilterなどをする時などに使うevent.jsonの中身は異なっていました。 diffを見ていて気になったのがref要素が違う、ということでした。

ブランチは両方masterではあるのですが、ref要素を見てみると

// PRにしたブランチ名は custom-filter
"ref":"refs/heads/custom-filter"
// こっちがmaster
"ref":"refs/heads/master"

の様な違いがあります。

であればこの要素を見てfilterかければ良さそうです。 ということでやってみたのが以下のPRです。

Custom filter by yamachu · Pull Request #9 · yamachu/play-actions · GitHub

中身としては actions/bin で使われているfilter系のスクリプトほぼそのままです。 外部からどの要素を使うか流し込めるようになっています。

action "filter head" {
  uses = "actions/bin/filter@master"
  runs = "/github/workspace/.github/custom-filter.sh"
  args = "refs/heads/master"
  env = {
    CUSTOM_JQ_FILTER = ".ref"
  }
}

これでon pushが2回走ろうがmasterのみをハンドリングできるようになりました。

案2. on pushイベントのevent.json内のdeleted要素を見てfilter

案1で満足していましたが、結局なぜ2回走っているのかはわかりませんでした。 そんな中

このツイートに対してリプライが飛んできて、どうもブランチを消したタイミングで2個目が走っているとの情報をいただきました。 情報非常に感謝です。 実際にmergeした後にブランチを削除しないと、確かに1つしかon pushイベントは飛んでいないのです。 完全に盲点だった………

え、ドキュメントに書いてあったっけ、と思い公式ドキュメントを見てみると

developer.github.com

………あまりそういう風には読み取れ…ないんだけど…自分の英語能力がアレなのかな…みたいになってしまいました。

ともかくそういうものである、ということにして進めていきます。

案1の時と同じ様にmergeのタイミングで取得できたevent.jsonと削除タイミングで取得できたevent.jsonを比較してみます。 すると

// 削除した時
"deleted":true
// mergeした時
"deleted":false

ビンゴです、完全に理解した。

ということで書き直してみます。

Update main.workflow by yamachu · Pull Request #10 · yamachu/play-actions · GitHub

action "filter not deleted event" {
  uses = "actions/bin/filter@master"
  runs = "/github/workspace/.github/custom-filter.sh"
  args = "false"
  env = {
    CUSTOM_JQ_FILTER = ".deleted"
  }
}

これで正攻法で戦えている気がします。

結局on pushでmaster branchのみを引っ張るには

  • filterでbranch masterを行う
  • "deleted": falseなものを通す

この2つをWorkflowに入れることで可能になりそうです。

今は外側からどの要素を取得するかみたいなスクリプトを自分で書いて入れていますが

github.com

リポジトリを見てみたらPRが上がっているようです。 mergeされるともう少し楽になりそうですね。

良きGitHub Actionsライフを!

【備忘録】Scala で Azure Functions やってみる(deploy編)

連載系のやつではないですが、引き続きScalaでAzure Functionsをやってみます。 ローカルに開発環境を整えてビルドを行う部分についてはこちらをご覧ください。

Scala で Azure Functions やってみる(ローカル動作編) - 窓を作っては壊していた人のブログ

前回の記事の最後あたりでdeployについて記載しました。 今回はdeploy方法について触れていこうと思います。

正直今回は備忘録レベルとリポジトリ見ろみたいな扱いです。 またGitHub Actionsを使ってみたかった、という色が非常に強いです。

github.com


自分が今までに試したAzure Functionsのデプロイ方法は3つあります。

  1. Azure Functions Core Tools(& VSCodeの拡張)
  2. curlを使ったzip deploy
  3. GitHub Actionsを使ったdeploy

それぞれ自分の感じたメリット・デメリットがあり、これがベストというものはありませんでした。 一つ一つ少しずつメモを残していきます。

Azure Functions Core Tools

メリット

  • 特にハマりどころがなく、host.json のあるディレクトリで func azure functionapp publish <Function name> を叩くだけの優しいコマンド
  • VSCodeの拡張と組み合わせるとGUIで出来るのでパット見わかりやすい

デメリット

  • ビルド環境とデプロイ環境を引き剥がすのが面倒
  • Docker上で動かすのが非常に面倒
  • 個人開発ならどうにでもなりそうだけど、チームでとなると大変そう

ローカルで動かして、そのままデプロイみたいなことが出来るのである意味完結している。 testとかもやってみたいな自動化したくなるとめんどくさそう(という印象がある)。

この辺上手くやる方法があるのであればぜひ知りたいところ…

curl

メリット

  • zipで固めて投げるだけ
  • curl と zip が使える環境ならどこでも使えそう
  • publish用のuser名とpassword(Azureのコンソール上からダウンロード出来る)を手に入れればすぐ使える
  • ローカルでもCI上でもいい感じに使えるハイブリッドな方法

デメリット

  • 機微情報ではあるので扱いに注意

とはいいつつもCIサービスはだいたいSecretEnvとかSecretValとか対応してるしちゃちゃっとTravisとかでやっちゃうのであれば普通に選択肢に入る。

https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file

ScalaFunctions/Makefile at master · yamachu/ScalaFunctions · GitHub

自分は上記リンクみたいにMakefileのタスクとして定義した。 Travisからは値を渡してそのタスクを叩くだけ。

GitHub Actions

メリット

  • Azure/github-actionsにAzure関係のActionsが用意されているので、それをそのまま使うだけ

デメリット

  • 手元で動かして試してみるみたいなことが出来ない(GitHub Actionsをローカルで動かしてみるって出来るんですかね…?)

Azure Functionsにデプロイするのであれば必要なのは loginアクションfunctionsアクションの2つです。

まずはloginに使用するAPP_IDやPASSWORDなどを発行するため、新規のservice principalを作ります。

コマンドライン上で

az ad sp create-for-rbac -n "ここにdeployに使用するdeployプロファイル名みたいなの" \\
    --scopes /subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_NAME}

いい感じに値を埋めて実行すると、

{
  "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "displayName": "ここにdeployに使用するdeployプロファイル名みたいなの",
  "name": "http://ここにdeployに使用するdeployプロファイル名みたいなの",
  "password": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

こんな感じの結果が出力されます。 loginアクションで使うのは appIdpasswordtenant なのでどこかにメモっておきましょう。

service principalに関する詳しいドキュメントは以下を参照

Azure CLI で Azure サービス プリンシパルを使用する | Microsoft Docs

上記の手順で手に入れた各種値をloginアクションに追加します。 これでlogin周りは完了。

あとはfunctionsですが、これはJava以外はハマることはないでしょう。

Javaの場合は次のセクションに記載してある問題がありfunctionsアクションをそのまま使うことが出来ません。 そのため今回はオレオレスクリプトを用意し、entrypointを差し替えることを行いました。

ScalaFunctions/azure-functions-deploy.sh at master · yamachu/ScalaFunctions · GitHub

やっていることは単純で、WEBSITE_RUN_FROM_PACKAGE変数の値を1にする処理をスキップしただけです。 詳しくはリポジトリを参照してください。

これでGitHub Actionsからでもデプロイが可能になります。

ハマった場所

現状対象のAzure Functionsの展開方法がパッケージからになっている状態(WEBSITE_RUN_FROM_PACKAGE=1の状態)だと展開が正常に行われないという現象が起こっているように見られます。

Deploying Java Jar to Web App completes successfully but the Web App does not start - Developer Community

Unable to Deploy · Issue #181 · Azure/azure-functions-java-worker · GitHub

WEBSITE_RUN_FROM_PACKAGE=0にしたら動いたみたいな解決方法になっているため、curlAzureのGitHub Actionsを使ってのzipdeployでハマる可能性があります。(AzureのGitHub ActionsはここWEBSITE_RUN_FROM_PACKAGE=1にしているためそのまま使うと……)

おそらくこの辺りのIssueに関連しているのかなと思いますが、深くはまだ探っていない状態です。

完全にメモ書きになってしまった。