Karabiner-Elementsの設定JSONにCIを設定する


はじめに

Karabiner-ElementsとはKarabinerの後継であり、macOSのキーバインド変更を行うためのアプリケーションである。特定のアプリケーションでのみ動作するキーバインドや、あるいは特定のキーと併用された場合にのみ動作をするといった複雑な設定を与えることができ、このような複雑な設定をComplex modificationsと呼ぶ1。このComplex modificationsはJSON形式のファイルで管理することができ、GitHubとTravis CIを使うことでこのJSONに対してCIができる。この記事ではこのようなCIを構成する方法について解説する。
また、筆者が作成したものが下記のリポジトリーに置かれている。

Karabiner CLI

実はKarabiner-ElementsにはCLIが同梱されており、下記のように使うことができる。

$ "/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_cli" --help
A command line utility of Karabiner-Elements.
Usage:
  karabiner_cli [OPTION...]

      --select-profile arg      Select a profile by name.
      --set-variables arg       Json string: {[key: string]: number}
      --copy-current-profile-to-system-default-profile
                                Copy the current profile to system default
                                profile.
      --remove-system-default-profile
                                Remove the system default profile.
      --lint-complex-modifications complex_modifications.json
                                Check complex_modifications.json
      --version                 Displays version.
      --version-number          Displays version_number.
      --help                    Print help.

Examples:
  karabiner_cli --select-profile 'Default profile'
  karabiner_cli --set-variables '{"cli_flag1":1, "cli_flag2":2}'

CIではこのCLIの--lint-complex-modificationsオプションを利用する。

Travis CIでの手順

GitHub上のリポジトリーにComplex modificationsのJSONファイルが設置されているとして、次のような方法で行う。

  1. Travis CIのmacOS環境にhomebrew + caskでKarabiner-Elementsをインストール
  2. Bashスクリプトを利用してフォルダー内のJSONを探索
  3. karabiner_cliで(2)のJSONを検査

このような感じであり、具体的には次のような.travis.ymlを用いた。

.travis.yml
os: osx

osx_image: xcode11

language: generic

install:
  - brew cask install karabiner-elements

script:
  - ./test.sh

このあとでtest.shからkarabiner_cliを起動して、それの終了コードを検査するだけである。

test.sh
#!/usr/bin/env bash

set -eo pipefail

SCRIPT_DIRECTORY="complex_modifications"

for FILENAME in `ls ./$SCRIPT_DIRECTORY`; do
  '/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_cli' \
    --lint-complex-modifications \
    "${SCRIPT_DIRECTORY}/${FILENAME}"

  if [ $? != 0 ]; then
      exit 1
  fi
done

まとめ(余談)

最初、Karabiner-ElementsのCIを作りたいと思ったときはopenコマンドとkarabiner://URLを利用するというのを考えていた。Karabiner-Elementsはkarabiner://からインターネットやローカルにあるJSONファイルをインポートすることができるので、これでロードできたらOKというようなCIを作ろうとした。しかし、そのためにはどうしてもKarabinerのGUIを操作する必要があったので、次のようなAppleScriptを利用して強引に操作するという作戦を実行した。具体的にはopen "karabiner://......"を実行すると次のようなWindowが出現する。

注目してほしいのは、これがもしJSONに問題がある場合はImportボタンが押せないことである。

これを利用して次のようなAppleScriptを用意した。これは(1)Importボタンを押してみて、次に(2)Cancelボタンを押してみる。もしここで(2)のCancelボタンを押すことに成功したということは、(1)でImportボタンが押せなかったことを意味する。したがって(2)が成功したならば、JSONは壊れていると判断する。

tell application "Karabiner-Elements" to activate

tell application "System Events"
    tell process "Karabiner-Elements"
        tell window "Karabiner-Elements Preferences"
            click button "Import" of sheet 1
            delay 1
            set result to ""
            try
                click button "Cancel" of sheet 1
            on error
                set result to "Success"
            end try

            if (result is not "Success") then
                error number -1
            end if
        end tell
    end tell
end tell

これをTravis CI上で実行したものの失敗に終った。今のmacOSはSystem Eventsといった何かしらのアプリケーションが、それとは違う別のアプリケーションを操作する場合は、アクセシビリティーのセキュリティをユーザーが許可しなければならない。かつてはこのセキュリティー権限を管理しているデータベースファイルをsqlite3コマンドで操作することができたらしいが、現在は絶対にGUIで操作しなければならない。もし自動でこの権限を操作できてしまうならば、マリシャスなアプリケーションはまず自動でセキュリティーを解除し、そのあとで他のアプリケーションを操作してしまうため、ここにGUIを求めるのはセキュリティー上の理由において正しいと思う。ともかくこういういった理由でこの作戦は失敗か、あるいはTravis CIの中の人にお願いしてApple Scriptのインタープリターにあらかじめセキュリティー許可を与えてもらうといった方法をとるしかなく、かなり途方に暮れていた。そのあとでもう一度調べなおしてみたところ参考文献の情報に出会い、この記事の方法でCIができることとなった。

参考文献

この他にもAppleScript関連のサイトをいくつか読んだはずだが、どれを読んだのかもう分からなくなってしまった……


  1. Complex modificationsと対比して、「abにする」といったキーバインドのことをSimple modificationsと言う。