こんにちは。@m_koshikawa です。
GitHub・GitLab・Gitea。名前は知っているし、それぞれ触ったこともある。でも使い分けているかと聞かれると、なんとなくGitHubに寄せている方が多いのではないでしょうか。
私はAPC技術ブログとQiitaに記事を書き続けています。ネタの管理、投稿先への振り分け、公開後のステータス更新といったワークフローをGitHubのIssues・Projects・Actionsで仕組み化しました。その過程は「ブログ管理をエンジニアリングしてみた — Markdownの限界をGitHub Issues + Actionsで超えた話」に書いています。
GitHubに寄せたことで管理は楽になりました。しかし全てをGitHubで回そうとすると、詰まるポイントが3つ見えてきました。この記事では、その3つと、GitLab・Giteaで解決した実体験を共有します。
詰まり①: セキュリティスキャンに月$49かかる
ブログ管理リポジトリにはPythonスクリプトが20本近くあります。Qiita投稿スクリプト、プロジェクト整合性チェックツール、会議分析ツールなど。これらのスクリプトにAPIキーのハードコードや既知の脆弱性パターンがないかチェックしたい。
GitHubでSAST(静的セキュリティスキャン)を回すにはGitHub Advanced Securityが必要です。価格は$49/ユーザー/月。個人の技術ブログ管理にこのコストは見合いません。
GitLabセルフホスト版にはSASTが無料で内蔵されています。.gitlab-ci.yml に2行書くだけです。
include:
- template: Security/SAST.gitlab-ci.yml
Windows PCのWSL2にDocker Engine(無料)でGitLabを立て、SASTを回しています。初回起動に数分かかりますが、一度立ち上がれば安定して動きます。pushするたびに自動でセキュリティスキャンが走り、コストは0円です。
詰まり②: Project V2 APIにClassic PATが必要
GitHub Projectsのカンバンボードでブログのネタを管理しています。Issue作成時にProjectへ自動追加し、公開時にステータスとURLを自動更新する仕組みをGitHub Actionsで構築しました。
ところが、GitHub ProjectsのGraphQL API(V2)にアクセスするには、Fine-grained Personal Access Tokenが使えません。Fine-grained PATのAccount permissionsにProjectsの項目が存在しないのです。Classic PATで project + repo スコープを付ける必要があります。
Classic PATはスコープが粗く、リポジトリ全体への書き込み権限が付いてきます。Fine-grained PATの「必要最小限の権限」という思想とは逆行します。GitHubがProjectsのFine-grained対応を進めてくれるまで、この制約は残ります。
これはGitLab/Giteaで解決する問題ではありませんが、GitHubだけに依存するリスクとして記録しておきます。APIの制約に引っかかったときに代替手段を持っているかどうかが、ワークフローの柔軟性を左右します。
詰まり③: ローカルで完結したい作業がクラウド前提
記事を書いている途中でこまめにcommit/pushしたい。しかしGitHub Actionsはpushのたびに動きます。まだ書きかけの記事でCIが走り、lintエラーが出て、失敗通知が飛んでくる。
Giteaをローカルに立てて、日常のpush先にしています。brew install gitea で即起動。Raspberry Pi 5でも動くほど軽量です。書きかけの記事を気軽にpushでき、CIは走りません。記事が仕上がったらGitHubとGitLabにpushする、という流れです。
# 日常の作業(Giteaへ)
git push gitea main
# 記事完成時(GitHub + GitLabへ)
git push github-personal main
git push gitlab main
3サービスの得意領域
実際に使い分けて見えた、各サービスの得意領域です。
| 観点 | GitHub | GitLab | Gitea |
|---|---|---|---|
| Issue/カンバン管理 | 強い | あるが弱い | APIがない |
| CI/CDのトリガー柔軟性 | Issueコメント等が得意 | pushベースが基本 | Actionsあるが実績少 |
| セキュリティスキャン | 有料($49/user/月) | 無料で内蔵 | なし |
| セルフホストの手軽さ | Enterprise Server(高額) | Docker一発(RAM 4GB+) | brew一発(超軽量) |
| AIエージェント連携 | Copilot連携が自然 | CI内でAPIを叩ける | 特になし |
| コスト(セルフホスト) | — | 無料 | 無料 |
私の使い分け
| 関心事 | どこ | 理由 |
|---|---|---|
| ネタ管理(カンバン) | GitHub | Issues + Projectsが強力 |
| 公開時の自動処理 | GitHub Actions | Issueコメントがトリガー |
| セキュリティスキャン | GitLab CI | 無料で内蔵 |
| AIによる記事レビュー | GitLab CI | ローカルRunnerで高速・コスト0 |
| 日常のcommit/push | Gitea | 軽量・CI不発火・気軽に使える |
3つのリモートを持ち、用途に応じてpush先を選びます。1つに統一しようとしたから詰まっていました。
GitLab CIで実際に回しているパイプライン
GitLabにpushするたびに3つのジョブが走ります。
| ジョブ | 内容 | 実行 |
|---|---|---|
| semgrep-sast | Semgrepによるセキュリティスキャン | 自動 |
| article-lint | 記事の品質チェック(太字+カギ括弧等) | 自動 |
| ai-review | Claude APIで読者視点のレビュー | 手動トリガー |
SASTと記事lintはpushのたびに自動実行されます。AIレビューは手動トリガーにしています。毎pushで走らせるとAPIコストがかかるため、レビューに出す前に自分の判断で実行します。
構築にかかった時間は、GitLabのDocker起動から3ジョブの成功確認まで約2時間でした。
環境構築ガイド
GitLabとGiteaのセットアップ方法をOS別にまとめます。Docker Desktopは使いません。
GitLab CE セルフホスト
macOS の場合
colima(OSSのDocker互換ランタイム)を使います。
brew install colima docker docker-compose
colima start --cpu 4 --memory 8
mkdir -p ~/gitlab && cd ~/gitlab
# docker-compose.yml を作成(詳細はリポジトリ参照)
docker compose up -d
# http://localhost:8080 でアクセス
Windows(WSL2)の場合
WSL2上のDocker Engine(無料・OSS)を使います。
# WSL2にDocker Engineをインストール(未導入の場合)
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
mkdir -p ~/gitlab && cd ~/gitlab
# docker-compose.yml を作成(詳細はリポジトリ参照)
docker compose up -d
# http://localhost でアクセス
Gitea
macOS の場合
brew install gitea
brew services start gitea
# http://localhost:3000 でアクセス
Windows(WSL2)の場合
docker run -d --name gitea \
-p 3000:3000 -p 2223:22 \
-v gitea_data:/data \
gitea/gitea:latest
# http://localhost:3000 でアクセス
docker-compose.ymlの全文やGitLab Runnerの登録手順は公開リポジトリに記載しています。
ハマりポイント
同じことをやる方のために、ハマったポイントを残しておきます。
-
GitLab CIの
.gitlab-ci.ymlで|ブロックと通常行を混在させるとパースエラーになります。scriptは1つの|ブロックにまとめるのが安全です - Docker DesktopとDocker Engineは別物です。会社でDocker Desktopの利用が制限されている場合でも、macOSならcolima、WSL2ならDocker Engine(CE)で代替できます。いずれも無料・OSSです
- Claude APIのモデルIDはプランによって使えるものが異なります。Sonnet系が404を返す場合はHaikuで試してください
ローカルGitLabは本番CIの「砂場」
ここまで読んで「結局GitLabに移行しろという話か」と思われたかもしれません。違います。
エンタープライズではGitHub一択が管理コスト面で圧倒的に優位です。チームメンバーにGitLabを使わせるのは現実的ではありません。本番はGitHubです。
ではなぜローカルにGitLabを立てるのか。本番のCI/CDを壊さずに試行錯誤できる砂場として使うためです。
今回の構築では、7回pushして5回失敗しました。YAML構文エラー、Claude APIのモデルID間違い、スクリプトの分離不足。これがGitHub Actionsだったら、失敗した5回分も従量課金されます。ローカルGitLabなら何回失敗しても0円です。
砂場で検証して本番に持っていく
GitLabには、検証後にGitHub本番へ提案できる機能があります。
| GitLabの機能 | 検証する内容 | GitHubでの展開先 |
|---|---|---|
| Secret Detection | APIキー漏洩の専用スキャン | 全社リポジトリのCIポリシーに提案 |
| Code Quality | スクリプトの品質スコア可視化 | PRレビューの判断基準に |
| Approval Rules | SAST/レビュー通過をマージ条件に | Branch Protection Rulesに反映 |
ローカルで「これは効く」と確認できたものを、根拠付きでGitHub本番に提案する。動くものを作るだけでなく、どこにお金をかけてどこを無料で済ませるかを設計するのもエンジニアのスキルです。
チームから見える世界
最終的な構成はこうなります。
メンバーはGitHubにpushするだけです。GitLabの存在を知る必要はありません。越川がローカルGitLabで検証し、効果が確認できたCIポリシーをGitHubに反映する。砂場と本番の関係です。
まとめ
GitHubだけで詰まったから他に逃げたのではありません。ローカルで検証して本番に持っていく仕組みを作りました。
- GitHub: 本番。管理・CI/CD・チームの全てがここに集約される
- GitLab: 砂場。CIポリシーの検証と試行錯誤を無料で回す
- Gitea: 手元の作業場。書きかけのコードを気軽にpushする
3つのGitサービスは対立するものではなく、砂場と本番の関係で共存します。
この記事で紹介した .gitlab-ci.yml、GitHub Actions、スクリプト類は以下のリポジトリで公開しています。
参考
- git-family-workflow — この記事の実装例(公開リポジトリ)
- GitLab SAST ドキュメント
- Gitea 公式サイト
- GitHub Projects V2 API
- ブログ管理をエンジニアリングしてみた — Markdownの限界をGitHub Issues + Actionsで超えた話 — 筆者の過去記事