見出し画像

SingerでGitLabのデータをサクッと抜いて可視化してみた

はじめに

こんにちは。テスト自動化アーキテクトの森川です。
本日はETLツールを用いてGitリポジトリから定量的な情報の抽出と可視化をします。リポジトリ管理はGitLab、ETLツールにはSingerを用います。

きっかけ

エンジニアを評価するための参考値としてリポジトリの情報を抽出したい。
クラウド版やエンタープライズ版ならばGitLab謹製の集計機能が使えるようだが、あいにく対象プロダクトはオンプレ・コミュニティ版GitLabだった。
Findy Teamというサービスをご紹介いただいた。とても良さげだったが、これまたオンプレGitLabには対応していないらしい。
(2022/12 記事執筆時点)

これは自力でやってみたほうが早そうだ。

Singerとは

Singerはあらゆるデータを「Taps」と呼ばれるスクリプトで取り込み、「Targets」と呼ばれるスクリプトでDWH(データウェアハウス)に書き込むOSSのETLフレームワーク。

すべてのデータをJson形式でやりとりし、言語はPythonで構成される。
OSS上にさまざまなサービスの「Taps」と「Targets」が公開されていて、エコシステムとして確立している。

singer-io/getting-started: This repository is a getting started guide to Singer.

Tapsにはtap-gitlabを用いてGitLab APIからデータを抽出する。
DWHとしてはpostgtes dbを用意したのでTargetsにはsinger-target-postgresを用いる。

数多あるETLツールからSingerを選んだのは、この手の領域ではPythonの相性がよさそうだと思ったから。それだけです。

構成イメージ

※ BIツールにはMetabaseを採用した。

tap-gitlabで抽出できるAPI

tap-gitlabで抽出できるGitLab APIは限られていて、以下の8本のみ

  • Branches

  • Commits

  • Issues

  • Projects

  • Project milestones

  • Users

  • Groups

  • Group Milestones

GitLabの公開APIは大量にあり、MergeRequestやGitlab Runnerまわりなど既に足りない雰囲気。

REST API | GitLab

APIを追加する方法については、あとで少し触れましょう。

環境構築

検証は以下の環境で行った。

環境

  • WSL : Ubuntu22.02LTS

  • Python: 3.9.13

  • singer-python: 5.0.4

  • tap-gitlab: v0.5.1

  • target-postgres: 0.2.4

※ postgre dbとmetabaseはそれぞれ起動済

ディレクトリ

├── CHANGELOG.md
├── Dockerfile
├──...
├── config         👈 定義ファイル達が入る
│   ├── config.json
│   └── db-config.json
├── docker-compose.yml
├── setup.cfg
├── setup.py
└── tap_gitlab
    ├── __init__.py 👈 コアファイル
    └── schemas    👈 データスキーマ達が入る
        ├── branches.json
        ...
        └── users.json

セットアップ

  • singer-io/tap-gitlabをClone

    • 直接pip installでもインストールできます。git cloneしているのは後で手をいれるため

  • Dockerfileを作成

FROM python:3.9
USER root

RUN apt-get -y update
RUN apt-get -y install locales vim && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

RUN pip install strict-rfc3339 \
    singer-target-postgres
  • docker-compose.ymlを作成

version: '3.3'
services:
  python3:
    restart: always
    build: .
    container_name: 'singer-gitlab'
    working_dir: '/root/opt'
    tty: true
    volumes:
      - $PWD/config:/root/opt/config
      - $PWD/tap_gitlab:/root/opt/tap_gitlab
      - $PWD/setup.py:/root/opt/setup.py
  • config/config.jsonにGitLabの情報を記載する

{
    "api_url": "GitLab url",
    "private_token": "GitLab Access Token",
    "groups": "グループ名",
    "projects": "プロジェクト名(グループ名/プロジェクト名)",
    "start_date": "抽出の下限日"
}
  • 事前に権限付与済のAPI Tokenを取得しておきましょう

  • グループ名、プロジェクト名は半角スペースで列挙可能

  • config/db-config.jsonにpostgres DBの接続情報を記載する

{
    "postgres_host": "db host",
    "postgres_port": 5432,
    "postgres_database": "db_name",
    "postgres_username": "username",
    "postgres_password": "password",
    "postgres_schema": "db_schema"
}

インストール

# コンテナ起動
docker-compose up -d
# 中に入る
docker exec -it singer-gitlab bash
# install
cd /root/opt/tap-gitlab
pip install -e .

インストールに成功すると下記のようになります。

Installing collected packages: tap-gitlab
  Attempting uninstall: tap-gitlab
    Found existing installation: tap-gitlab 0.5.1
    Uninstalling tap-gitlab-0.5.1:
      Successfully uninstalled tap-gitlab-0.5.1
  Running setup.py develop for tap-gitlab
Successfully installed tap-gitlab-0.5.1

データ抽出

まずJson出力してみます。

docker exec -it singer-gitlab sh -c "tap-gitlab -c config/config.json"

InfoログとJsonデータが出力され最後に"INFO Sync complete"となれば正常です。

次にDBに出力します。パイプでtarget-postgresをつなぐだけです。

docker exec -it singer-gitlab sh -c "tap-gitlab -c config/config.json | target-postgres -c config/db-config.json"

postgores db上に12個の新規テーブルが作成されていれば成功。

可視化してみる

Metabaseとpostgres dbを接続して適当にクエリを設定してビジュアライズします。

ETLで生成されるデータベーススキーマはschema内のJsonに準ずるのでそちらを見ながらクエリを組めば良いだけです。

Metabaseの使い方については数多のブログサイトがありますのでそちらをご覧ください。

参考:OSSのBIツール「Metabase」の使い方

APIが足りない

コミット数やブランチ数は取得できましたが、肝心のMerge Request情報が無いです。

自力で足してみましょう。

段取りとしては以下の通り。

  • APIのデータスキーマJsonを作成してschemaに追加

  • tap_gitlab/__init__.pyにスキーマ定義とAPIをTapするメソッドを追加

  • その他修正

まずGitLabのMerge Requests APIを直に叩いてスキーマを調べます。

Merge requests API | GitLab

大量のプロパティを持っているので必要なデータのみに絞ります。

tap_gitlab/schema/merge_requests.json

{
    "type": "object",
    "properties": {
        "id": {
            "type": "integer"
        },
        "iid": {
            "type": "integer"
        },
        "project_id": {
            "type": "integer"
        },
        ...中略
        "merge_when_pipeline_succeeds": {
            "type": [
                "null",
                "boolean"
            ]
        }
    }
}

"tap_gitlab/init.py"に追記していきます。

# スキーマ読み込みを追記(L70あたり)
    'merge_requests': {
        'url': '/projects/{}/merge_requests',
        'schema': load_schema('merge_requests'),
        'key_properties': ['id'],
    },
# API読み込みメソッドの追加(L230 "sync_group"の後ろ)
def sync_merge_request(project):
    url = get_url('merge_requests', project['id'])
    with Transformer(pre_hook=format_timestamp) as transformer:
        for row in gen_request(url):
            flatten_id(row, 'merged_by') 👈 ユーザ情報は入れ子なのでフラットなユーザIDに強制変換
            flatten_id(row, 'closed_by')
            flatten_id(row, 'merge_user')
            flatten_id(row, 'assignee')
            flatten_id(row, 'author')
            transformed_row = transformer.transform(
                row, RESOURCES['merge_requests']['schema']
            )

            if row['updated_at'] >= get_start('project_{}'.format(project['id'])):
                singer.write_record(
                    'merge_requests', transformed_row, time_extracted=utils.now()
                )

Metabaseで可視化してみるとこんな感じです。

ここから月別推移や、チーム別の数値を見ていくと、定量的な評価のネタになる気がしますね。

※ コードはGitHubにあげています。本家にもプルリク済。

enabeld to tap merge requests · tomoo-morikawa/tap-gitlab@d46f0b3

ついでにMerge Requetsのchanges APIを用いて変更コード行数をTapする変更も載せておきます。

こちらはMRごとにAPIクエリが実行されるので負荷や所要時間は要注意です。

count change lines on merge request · tomoo-morikawa/tap-gitlab@0e67436

Tapの実装ベストプラクティスについてはこちらに詳しく書かれています。

getting-started/BEST_PRACTICES.md at master · singer-io/getting-started

所感

Singerと公開されているTaps/Targetsを用いることで、データ抽出からDBテーブル生成まで簡単にできることがわかりました。

足りないAPIについてもスキーマJsonと簡単な抽出コードを書くだけなのはお手軽で良いです。

今後の課題としてはDevOpsのFour Keysメトリクスのような複合的な定量値や、複数リポジトリを対象としたデータ抽出(今の仕組みだとDBテーブルの共存ができない。)をしてみたいです。

そのためにはJiraとの連携やCATのようなテスト管理ツールとの連携も必要になりそうです。

今回の構成とは逆に、JiraのダッシュボードにETLで取得したデータを表示させる、なんていうのもありかもしれません。

Singerのエコシステムは250以上のさまざまなサービスと連携可能ですので可能性は広がりますね。

「可視化してみた」と謳っておきながら、Metabaseまわりの説明が薄かったので、そこは次回に記事にてご説明させていただきます。

ご容赦くださいませ。

実運用の際に気をつけたいこと

  • 実行タイミング: cronで定時実行されるようにしたい

  • 断面データ: ETLツールは断面データを可視化できることが強みの一つ。データの種類によっては推移を追いかけるようにカスタマイズしたほうがよいでしょう

  • 負荷: Tap対象にはそれなりの負荷がかかります。時間帯や取得範囲の設定などの配慮が必要でしょう

尚、このあたりのお世話をおまかせできるサービスが世の中にはたくさんあります。

SingerベースだとMeltanoというオールインワンなサービスが公開されています。

参考


執筆者プロフィール:森川 知雄
中堅SIerでテスト管理と業務ツール、テスト自動化ツール開発を10数年経験。
SHIFTでは、GUIテストの自動化ツールRacine(ラシーヌ)の開発を担当。
GUIテストに限らず、なんでも自動化することを好むが、ルンバが掃除しているところを眺めるのは好まないタイプ。さまざま案件で自動化、効率化によるお客様への価値創出を日々模索している。
2021年から技術イベントSHIFT EVOLVEの運営を主担当。司会は大の苦手

お問合せはお気軽に
https://service.shiftinc.jp/contact/

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/

PHOTO:UnsplashDima Solomin