LoginSignup
1
0

More than 1 year has passed since last update.

GitHub ActionsでArduinoプロジェクトに自動テスト環境を構築する

Last updated at Posted at 2023-01-11

最近またArduinoを再開したので、開発環境アップデートの第1弾として、CI環境の見直しを行いました。
昔々、まだGitHub Actionsとかいう超便利な代物が無かった時代、自作のArduinoライブラリはTravisCIに突っ込んで自動テストを回していました。
今はGitHub Actionsもあることですし、手始めに移行してみることにしました。

公式ワークフロー

GitHub Actionsは、再利用可能なワークフローをマーケットプレイス経由で簡単に組み込めるのが最高に便利ですよね。
Arduinoも 公式 がワークフローを公開してくれています。

公式ワークフローは2種類あります。

  • 実行環境インストール、ボードやライブラリの追加、コンパイルまで全自動 (上記)
  • 実行環境インストールのみ (arduino/setup-arduino-cli)

複雑な処理を行う場合は単体版の方が便利かと思いますが、今回はテストを回したいだけなので、全自動版を使用します。

リポジトリへ組み込む

早速、自分のリポジトリへワークフローを追加していきます。

まずディレクトリ構成ですが、他の一般的なワークフローと同じく、以下のような構成をとります。

  • .github/
    • workflows/
      • test.yaml
      • release.yaml
      • ...

そして僕はYAMLが 何で存在するのか理解できないレベルで生理的に無理 苦手なので、基本的にワークフローはJSONで書いてから yq というフォーマット変換ツールへ通してYAMLを 仕方無く 生み出しています。

  • .github/
    • workflows_json/
      • to-yaml.sh
      • test.json
      • release.json
      • ...
to-yaml.sh
set -uC
cd ${0%/*}
yq -P -I 4 ./${1}.json | head -c -1 | tee ../workflows/${1}.yaml &> /dev/null
# test.json -> test.yaml
./to-yaml.sh test

ワークフローの記述

まずは見ていただければ、やってることは何となく分かるかと思います。

test.json
test.json
{
    "name": "Test",
    "on": {
        "push": {
            "branches": [
                "dev"
            ],
            "paths-ignore": [
                ".git*",
                "**.md",
                "*.properties"
            ]
        },
        "pull_request": {
            "branches": [
                "master",
                "dev"
            ],
            "paths-ignore": [
                ".git*",
                "**.md",
                "*.properties"
            ]
        }
    },
    "jobs": {
        "test": {
            "name": "Test: ${{matrix.board.name}}",
            "runs-on": "ubuntu-latest",
            "strategy": {
                "fail-fast": true,
                "matrix": {
                    "board": [{
                        "vendor": "arduino",
                        "arch": "samd",
                        "name": "arduino_zero_native"
                    }, {
                        "vendor": "adafruit",
                        "arch": "samd",
                        "name": "adafruit_trinket_m0"
                    }, {
                        "vendor": "teensy",
                        "arch": "avr",
                        "name": "teensy41"
                    }],
                    "include": [{
                        "index": "https://downloads.arduino.cc/packages/package_index.json",
                        "board": {
                            "vendor": "arduino"
                        }
                    }, {
                        "index": "https://adafruit.github.io/arduino-board-index/package_adafruit_index.json",
                        "board": {
                            "vendor": "adafruit"
                        }
                    }, {
                        "index": "https://www.pjrc.com/teensy/package_teensy_index.json",
                        "board": {
                            "vendor": "teensy"
                        }
                    }]
                }
            },
            "steps": [{
                "name": "clone repository",
                "uses": "actions/checkout@v3"
            }, {
                "name": "install arduino and run test",
                "uses": "arduino/compile-sketches@v1",
                "with": {
                    "fqbn": "${{matrix.board.vendor}}:${{matrix.board.arch}}:${{matrix.board.name}}",
                    "platforms": "- name: ${{matrix.board.vendor}}:${{matrix.board.arch}}\n  source-url: ${{matrix.index}}"
                }
            }]
        }
    }
}

要点だけ掻い摘んで解説します。

matrix.board

GitHub Actionsの並列実行機能で、大量のボードを同時並行でテストできます。

テストしたいボードの FQBN を分解して記述します。
FQBNとは 製造者:アーキテクチャ:ボード名 で構成された、Arduinoにおけるボード識別子のことです。
それら3要素を、それぞれ matrix.board 配列のオブジェクトプロパティとして以下のように記述します。

  • vendor ... 製造者
  • arch ... アーキテクチャ
  • name ... ボード名

FQBNは製造者によって予め定義されているため、事前に確認しておく必要があります。

matrix.include

上記の並列実行において、特定のコンテキストにのみプロパティを追加したい場合、ここでパターンマッチと追加するプロパティを記述できます。

Arduinoに慣れ親しんだ方ならご存知かと思いますが、サードパーティ製のボードを使用する場合は、製造者が提供しているボード定義を追加する必要があります。
全コンテキストにおいて全ボード定義を追加するのも不可能ではないですが、不必要なボード定義までダウンロードすることになり、ネットワーク負荷増大に繋がるため、コンテキストに必要なボード定義のみを追加するために、この機能を使います。

例えば、FQBNが adafruit:samd:adafruit_trinket_m0 の場合、必要なボード定義はAdafruit社の package_adafruit_index.json です。
そこで、以下のように matrix.include を指定することで、特定の vendor の時のみその製造者が提供しているボード定義の index プロパティを追加できます。

test.json(抜粋)
{
    "index": "https://adafruit.github.io/arduino-board-index/package_adafruit_index.json",
    // ↓ で "matrix.board.vendor" が "adafruit" の時のみ "matrix.index" に ↑ を追加する
    "board": {
        "vendor": "arduino"
    }
}

arduino/compile-sketches@v1

今回の目玉です。
全自動で全てよろしくやってくれる本体です。

デフォルトのテスト対象は examples/ の中身全部となります。

他のテスト対象を指定したい場合や、依存関係ライブラリが存在する場合は、別途 with プロパティ内でオプション設定できます。

ここでは2個のオプションを指定します。

with.fqbn

ボードのFQBN、つまり matrix.board の3要素を全てコロンで繋げた値となります。

with.platforms

少し厄介です。

この中身はGitHub Actionsワークフローそのものではなく、実行中のワークフロー内で別に解析されるため、YAML文字列で書く必要があります。つらい。
ここで指定するのは、インストールしたいボードのプラットフォームと、そのボードが定義されているインデックスのURLです。

プラットフォームとは、具体的に言うとコンパイラのことで、これは製造者がアーキテクチャ毎に用意しているため、識別子は 製造者:アーキテクチャ となります。

例えば、FQBNが adafruit:samd:adafruit_trinket_m0 の場合、プラットフォームは adafruit:samd となります。
そしてボード定義は board.include で拡張された matrix.index に格納されています。

実行してみる

今回のワークフローでは dev ブランチにpushしたとき、テストが走るようになっています。
master ブランチへは直接プッシュできないよう保護しているので、基本的に細かい修正は dev で、全く新しい機能を追加するときは dev から新たに feat-xxx ブランチを切る感じで運用してます。

お恥ずかしながら...ここで実際のワークフローの挙動を確認できます。

まとめ

自動テストが出来るようになることで、開発効率が圧倒的に良くなります。
皆さんも是非GitHub Actionsを活用してArduino開発を加速させてみてください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0