Gitlab CI on RaspberryPi4

# 概要
Gitlab-Runner を RaspberryPi4 で動作させ RaspberryPi4 上で GitlabCI を走らせてみます
RaspberryPi4 上で動作させることで gpio を使ってジョブやパイブラインの結果をインタラクティブにしてみます
今回は実行結果に応じて光らせる LED の種類を変更してみます

# 環境
* RaspberryPi4 8GRAM
  * Linux raspberrypi 5.10.17-v8+ #1414 SMP PREEMPT Fri Apr 30 13:23:25 BST 2021 aarch64 GNU/Linux
* フルカラーLED (RGBフルカラーLED　5mm　OSTA5131A　カソードコモン)

# Gitlab-Runnerのインストールと登録
* curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
* sudo apt-get install gitlab-runner
* sudo gitlab-runner register

Gitlab の URL とトークンを入力して登録を完了します
gitlab-runner ユーザが gpio を制御できるようにグループに追加します

* sudo usermod -aG gpio gitlab-runner

# フルカラーLEDのシールド
ユニバーサル基板にハンダします
必要なピン数分だけピンヘッダを準備していますが余裕があればシールドみたいにすべてのピンヘッダをつけても OK です
使用しているのは 11,12,13,14(GND) になります
抵抗はすべて220オームを使用しています

表

![gitlab_runner_on_gitlab1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35879/a298597d-0567-d155-47aa-fcd6c20a49d4.png)

裏

![gitlab_runner_on_gitlab2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35879/7a342333-6224-2154-d7e2-11217f22b372.png)

取り付けた感じは以下のようなになります

![gitlab_runner_on_gitlab3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35879/2219c2fa-4c0c-9eae-5dce-37c57bf3b4eb.png)

# フルカラーLEDを点滅させるスクリプト
PWM を使います
カラーコードをデューティー比に変換し ChangeDutyCycle で各色の明るさを調整することで色の調整をしています

* vim /home/pi/work/python/blink.py

```python
# -*- coding: utf-8 -*-
"""フルカラーのLEDを制御するモジュール."""
import RPi.GPIO as GPIO
import time
import logging
import sys

from logging import getLogger


class Blink():
    """フルカラーのLEDを赤or緑点滅するクラス."""

    # 各ピン定義
    RED = 11
    GREEN = 13
    BLUE = 12

    def __init__(self, color=GREEN):
        """ピンを初期化します."""
        logging.basicConfig(level=logging.INFO)
        self.logger = getLogger(__name__)
        # RGB のカラーコードの設定
        if color == self.RED:
            self.color = 0xFF0000
        elif color == self.GREEN:
            self.color = 0x00FF00
        pin_def = {
            self.RED: 2000,  # red
            self.GREEN: 2000,  # green
            self.BLUE: 5000,  # blue
        }
        GPIO.setmode(GPIO.BOARD)
        # 制御するピンを管理するdict
        self.pins = {
            self.RED: None,
            self.GREEN: None,
            self.BLUE: None,
        }
        # ピンの初期化
        for pin_num, freq in pin_def.items():
            GPIO.setup(pin_num, GPIO.OUT)
            pin = GPIO.PWM(pin_num, freq)
            pin.start(0)
            self.pins[pin_num] = pin
        self.logger.info('Started pwm pins for a full color led.')

    def calc_duty(self, x, in_min, in_max, out_min, out_max):
        """デューティー比を計算します."""
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min

    def blinkColor(self, col):
        """各ピンに色を設定し指定の色を光らせます."""
        # 左ビットシフト
        red = (col & 0x110000) >> 16
        green = (col & 0x001100) >> 8
        blue = (col & 0x000011) >> 0
        # デューティー比に変換 (0-100)
        red = self.calc_duty(red, 0, 255, 0, 100)
        green = self.calc_duty(green, 0, 255, 0, 100)
        blue = self.calc_duty(blue, 0, 255, 0, 100)
        # ピンにデューティー比を設定
        self.pins[self.RED].ChangeDutyCycle(red)
        self.pins[self.GREEN].ChangeDutyCycle(green)
        self.pins[self.BLUE].ChangeDutyCycle(blue)

    def blink(self):
        """フルカラーLEDを点滅させます."""
        try:
            self.logger.info('Started blinking a full color led.')
            self.blinkColor(self.color)
            time.sleep(5)
        except KeyboardInterrupt:
            pass
        finally:
            self.cleanup()

    def cleanup(self):
        """クリーンアップ処理を実行します."""
        for pin in self.pins.values():
            pin.stop()
        GPIO.cleanup()
        self.logger.info('The pin cleanup process was completed.')


if __name__ == "__main__":
    arg_color = sys.argv[1]
    if arg_color == "green":
        color = Blink.GREEN
    elif arg_color == "red":
        color = Blink.RED
    else:
        sys.exit(1)
    test = Blink(color)
    test.blink()
```

RaspberryPi 上で `python /home/pi/work/python/blink.py red` と実行して赤色が点灯するのを確認しましょう

# .gitlab-ci.ymlの作成
必ず成功するような CI になっています
成功した場合には緑を点灯し失敗した場合には赤を点灯させるようになっています
動作確認で失敗時の挙動も確認します

* vim .gitlab-ci.yml

```yaml
stages:
  - stage1
  - blink_red
  - blink_green

.echo1_script: &echo1_script
  - "export MSG=hello"
  - "echo ${MSG} >> msg.txt"
  - "export MSG=HELLO"

send_alert:
  stage: blink_red
  script:
    - "python /home/pi/work/python/blink.py red"
  when: on_failure

echo1:
  stage: stage1
  script:
    - *echo1_script
  artifacts:
    paths:
      - msg.txt

send_success:
  stage: blink_green
  script:
    - "python /home/pi/work/python/blink.py green"
```

# 動作確認
CI が成功した場合は緑色が点灯し CI が失敗した場合は赤色が点灯するのが確認できます

<iframe width="560" height="315" src="https://www.youtube.com/embed/s7i89ctmrC0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# 最後に
RaspberryPi4 の gpio だと 20mA くらいの出力電流が限界なので LED あたりだと簡単に制御できます
MOSFET やトランジスタを使えば出力電流を上げて 5V 駆動の DC モータやサーボモータあたりであれば制御できると思います

ただその場合は簡単な保護回路 (逆起電力など) も必要になるのでご注意ください