背景#
倉庫の移行やパイプラインの権限の問題により、パイプラインで使用される倉庫がページ上で直接 PR を提案できなくなりました。そのため、協力者の同僚が SaaS バックエンドコードを修正してリリースする必要がある場合、毎回フロントエンドがローカルでコードをマージする必要があり、非常に煩雑で非効率的です。現在、毎回プルする倉庫とブランチは固定されているため、Node を使ってスクリプトを書き、API を呼び出すことでコードを自動的にプルすることを考えました。
技術スタックの選択#
現在の要件は非常にシンプルで、単純な同期を行うことです。実際には以下のコマンドラインを実行します:
git checkout dev
git pull --no-rebase
git pull bkop dev_databus --no-rebase
git push
これに基づいて、主に以下のいくつかの重要な技術を採用しました:- Node.js: サーバーサイドで動作する JavaScript ランタイム環境。
- Express: 迅速で、すぐに使える Web アプリケーションフレームワークで、API サービスを支えます。
- Child Process: シェルコマンドを実行するために使用します。
- PM2: 内蔵の負荷分散機能を持つ Node アプリケーションのプロセスマネージャーです。
解決策の概要#
ディレクトリ構造と依存関係のインストール#
まずプロジェクトディレクトリを作成し、その中で新しい npm プロジェクトを初期化し、必要な依存関係をインストールします。
mkdir git-auto-pilot && cd $_
npm init -y
npm install express child-process-promise
ここでは、エラーハンドリングの優雅さを向上させ、Promise 構文をサポートするために、child-process-promise
ライブラリを使用しています。
自動化スクリプトの作成#
次に、自動化ロジックの核心部分を実装します —— シンプルな Express アプリケーションを書き、HTTP インターフェースを通じて一連の Git 操作をトリガーします。
主な機能モジュール#
Express アプリの設定#
必要なモジュールをインポートし、基本的な Express アプリを設定します。
// ファイルパス: index.js
const express = require('express')
const cp = require('child-process-promise')
const path = require('path')
const app = express()
const directory = 'xxx' // ファイルパス
更新操作の GET ルート#
すべての Git 操作を実行するために/update
ルートを定義します。
app.get('/update', async (req, res) => {
try {
await executeGitCommand('checkout dev')
await executeGitCommand('pull --no-rebase')
await executeGitCommand('pull bkop dev_databus --no-rebase')
await executeGitCommand('push')
const recentCommits = await executeGitCommand('log -n 5 --pretty=format:"%h %s"')
res.send(`
<h1>更新とプッシュが成功しました!</h1>
<h2>最近のコミット:</h2>
<pre>${recentCommits.stdout}</pre>
`)
} catch(error) {
console.error(`更新操作の実行に失敗しました: ${error}`)
res.status(500).send(`<pre>エラー: ${error.stderr || error.stack}</pre>`)
}
})
function executeGitCommand(command) {
const fullCommand = `git ${command}`
console.log(`コマンドを実行中: ${fullCommand} in ${directory}`)
return cp.exec(fullCommand, {cwd: directory})
}
このルートでは、git の操作を順番に実行し、各コマンドで例外をキャッチしてプログラムの堅牢性を確保しています。
Express サービスの起動#
最後のステップは、アプリケーションを起動してポートをリッスンさせることです。
const PORT = process.env.PORT || 30035;
app.listen(PORT, () =>
console.log(`サーバーは http://localhost:${PORT}/ で実行中です`)
);
PM2 を使用したデーモンプロセス管理#
アプリケーションがバックグラウンドで安定して実行できるように、PM2 を使用して管理します。これにより、ターミナルを開いたままにする必要がなくなります。
pm2 start index.js --name "git-auto-pilot"
これで、ターミナルを閉じたり、マシンを再起動しても、このサービスは PM2 によって自動的に復活します。
まとめ#
Node と PM2 の組み合わせを使用することで、多くのシンプルで実用的な自動化スクリプトを書くことができます。現在、このスクリプトは衝突が発生した場合の処理ができませんが、私の大部分の問題を解決し、手動でコードをマージする手間を大幅に削減しています。