Amazon ECRのライフサイクルポリシーのタグプレフィックスリストはOR条件ではなくAND条件

突然のAWSの話題です。

Amazon ECR (Elastic Container Registry)はDockerコンテナレジストリのサービスです。コンテナイメージをどんどんプッシュしていくと無限に溜まっていってそれだけストレージコストが掛かってしまうので、不要になったイメージは適度に削除したいものです。ECRではそのためにライフサイクルポリシーという仕組みがあり、

  • タグの有無
  • タグプレフィックスリスト
  • イメージの数、もしくはイメージがプッシュされてからの時間

といった条件を指定してルールのリストを作り、各ルールにマッチしたイメージは保護し、どのルールにもマッチしなくなったイメージは削除する、ということができます。

ここ最近ECRを触っていて見かけた事例に、タグプレフィックスリストの使い方が間違っているものがありました。タグプレフィックスリストはドキュメントに次のように説明があります。

Only used if you specified "tagStatus": "tagged". You must specify a comma-separated list of image tag prefixes on which to take action with your lifecycle policy. For example, if your images are tagged as prod, prod1, prod2, and so on, you would use the tag prefix prod to specify all of them. If you specify multiple tags, only the images with all specified tags are selected.

リストに複数のプレフィックスを指定した場合、全てのプレフィックスを満たしたイメージが選択される、つまりOR条件ではなくAND条件ということです。

例えばproduction環境とstaging環境へのデプロイ用のイメージは保護しよう、という時になんとなく使うとこのリストを["production-", "staging-"]と指定してしまうかもしれませんが、これだと両方を満たしたイメージがなくどちらも保護されない・もしくは古いイメージを削除できない、ということになってしまいます。この場合は["production-"]を指定するルールと、["staging-"]を指定するルールを別々に作るのが正解です。

例として「Filtering on multiple tags in a single rule」というのも挙げられていますが、意外とこういうのは見落としがちかもしれません。ドキュメントが用意されているものはすっ飛ばさずにちゃんと読んで、余計な回り道をしないようにしていきたい今日この頃です。

GitHub Actionsのpathsによる条件付き実行にまつわる落とし穴と困りごと

paths と言っているのはこれです。

https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestpaths

When using the push and pull_request events, you can configure a workflow to run when at least one file does not match paths-ignore or at least one modified file matches the configured paths. Path filters are not evaluated for pushes to tags.

GitHub ActionsでファイルAが更新されているがファイルBが更新されていない時だけ実行する、を実現する

GitHub Actionsの on: pushon: pull_rerequest のトリガーには paths/paths_ignore という機能があり、特定のファイルパスが変更された時だけワークフローを実行できます。

# 例えばiOSとAndroidのmonorepoで、ios以下が変更された時だけiOSのテストを実行したい
on:
  push:
    paths:
      - 'ios/**'

paths/paths_ignoreには配列で複数のパス指定・globパターン指定ができ、そのうちのどれか1つにでもマッチするとワークフローが実行されます。しかしこの機能には1つ弱点があり、pathspaths_ignore を同時に使うことができません。つまり、ファイルAが変更され・かつ・ファイルBが変更されていない、という条件が記述できません(と認識しています)。

You can exclude paths using two types of filters. You cannot use both of these filters for the same event in a workflow.

今回はこれを乗り越える方法の紹介です。trilom/file-changes-actionというアクションを使用して、ジョブ内で自分でファイル差分をチェックしてみましょう。ここではiOSアプリのリポジトリで、Podfileが更新されているがPodfile.lockが更新されていない時、ということにしてみます。

name: Update Podfile.lock

on:
  pull_request:
    paths:
      - Podfile

jobs:
  precheck:
    runs-on: ubuntu-latest
    outputs:
      files_changed: ${{ steps.file_changes.outputs.files }}
    steps:
      - uses: actions/checkout@v2
      - id: file_changes
        uses: trilom/file-changes-action@v1.2.3

  update:
    runs-on: macos-latest
    needs: precheck
    if: contains(fromJson(needs.precheck.outputs.files_changed), 'Podfile.lock') == false
    steps:
      - uses: actions/checkout@v2
      - run: pod install
      - uses: stefanzweifel/git-auto-commit-action@v4.2.0
        with:
          commit_message: Update Podfile.lock
          file_pattern: Podfile.lock

まずは前提条件としてpathsPodfileを指定して、「Podfileが更新されている」ことをワークフローレベルでフィルタリングします。

ここからが本題です。precheckジョブで、trilom/file-changes-actionを使ってファイル差分を取得します。そしてそのステップのoutputジョブのoutputに保存して、後続のジョブからアクセスできるようにします。ここでジョブを分割しているのは

  • 後続の処理が複数ステップに渡る場合、それぞれのステップにifを書くのが面倒・メンテナンスしにくい
  • 今回の例では後続の処理にmacOSが必要だが、macOSは課金が高額(Ubuntuの10倍!)なので、ファイル差分のチェックはUbuntuで行う

ためです。

updateジョブはneedsを使ってprecheckジョブの終了後に直列で実行されるようにします(デフォルトでは複数のジョブは並列で実行されます)。次にジョブレベルのifで、Podfile.lockが変更されていない、という条件を書きます。needs contextを使うと、前段のジョブの実行結果やoutputsにアクセスできます。ここではprecheckジョブでoutputsに保存したtrilom/file-changes-actionの出力結果を使用し、contains関数で変更されたファイル名の配列にPodfile.lockが含まれるかを確認します。

後は実処理として pod installしてPodfile.lockを更新してから、stefanzweifel/git-auto-commit-actionを使ってプルリクエストを作成しています。

こうして「Podfileが更新されているがPodfile.lockが更新されていない時」という条件を実現できました。めでたしめでたし。色んなアクションを組み合わせて柔軟なジョブ制御を実現していきましょう。

`MenuItem.onNavDestinationSelected`と`android:menuCategory="secondary"`の関係

https://developer.android.com/reference/kotlin/androidx/navigation/ui/package-summary#(android.view.MenuItem).onNavDestinationSelected(androidx.navigation.NavController)

By default, the back stack will be popped back to the navigation graph's start destination. Menu items that have android:menuCategory="secondary" will not pop the back stack.

https://developer.android.com/jetpack/androidx/releases/navigation#1.0.0-alpha09 で経緯が分かる。

MenuItems with menuCategory="secondary" will no longer pop the back stack when used with NavigationUI methods. b/120104424

難しい!!!

GitHub ActionsのXcode 10.3ではSwiftPMのビルドがエラーになる

GitHub ActionsではmacOSも実行環境としてサポートされており、現在のOS指定はmacos-latest/macos-10.15の1つのみです。以前はmacos-10.14もサポートされていたのですが少し前に非推奨となり削除されてしまいました。

その際に使用できるXcodeのバージョンも整理されてしまい、GitHub ActionsではXcode 11以降しか使えなくなっていたのですが、ユーザーからの要望によりmacos-10.15に再度Xcode 10.3が追加されました。

さて、この復活したXcode 10.3なのですが、Swiftで書かれたOSSをテストしようとすると実は問題がありました。xcodebuildコマンドでのXcodeプロジェクト・ターゲットとしてのビルドは問題ありませんが、SwiftPMでのビルド swift build をすると次のようなエラーになってしまいます。

This copy of libswiftCore.dylib requires an OS version prior to 10.14.4.
Exited with signal code 6

ということで、GitHub Actionsで複数のXcodeバージョンを使ったmatrixビルドをする場合、Xcode 10.3のSwiftPMでの swift build/swift testだけは除外して、それだけTravis CIでテストするなどの回避策が必要となっています。おしまい。

Swift 5.2では関数・メソッドの最後の引数がデフォルト引数の場合、その1つ前のクロージャの引数をtrailing closureとして呼べるようになった

タイトルがほぼ全てです。

func foo(_ closure: () -> Void, defaultArg: Int = 0) {
  closure()
}

foo { print("foo") }

このコードはSwift 5.2では動作しますが、Swift 5.1以前ではコンパイルエラーになります。

Nimbleに挙がったissueでそのことに気付きました。

Discordのswift-developers-japanで話してみたところ、apple/swiftこのコミットが該当の変更であることを分かりました。

さらに芋づるで分かったことがあって、一時期のSwiftのDevelopment SnapshotでNimbleのビルドが通らなくなったり、そのリグレッションがきっかけでapple/swift-source-compat-suiteにNimbleが追加されたのもこの辺りに関連した変更の影響でした。

色々繋がっていて面白いですね、めでたしめでたし。

GitHub ActionsでのJestのテスト実行時に失敗箇所をPRへのアノテーションとして表示するレポーター

このような便利なパッケージがありました。

GitHub Actionsにはworkflow commandsと呼ばれる、echoコマンドと特定の形式の文字列を使うことでActionsの処理に一部介入できる機能があります。その中にはファイルの特定の箇所(行・カラム)にdebug/warning/errorメッセージをPRのアノテーションとして追加できるコマンドがあります。jest-github-actions-reporterではJestのレポーターの実装としてそのSetting an error messageコマンドを使い、Jestのテストの失敗結果をerrorアノテーションとしてPRの差分表示上で簡単に結果が閲覧できるようになっています。

こういうやり方があるのはRenovateのこのPRで気付きました。

きっとそのためのJestのレポーターをパッケージとして作成・公開している人がいるだろうと探してみたら、案の定存在した次第です。

GitHub Actionsで日々の暮らしを楽にしていきましょう。