GitHub Enterprise 使わなくてもデプロイを承認制にできるよ??
こんにちは!営業製作所でエンジニアをやっている秋山です 🍔
本記事では、GitHub Enterprise を使用せずに、デプロイを承認制にする方法を解説します。
対象読者:
- GitHub Enterprise を導入していない開発者
背景と目的
弊社プロダクトはリリースして間もないということもあり運用がまだ固まっておらず、誰でも本番環境へデプロイができる状態でした。
これは今後プロダクトを運用していくにあたって好ましい状態ではありません。
ただ、弊社の GitHub プランは Enterprise ではないためデプロイを承認制にすることができませんでした。
金銭的にも安く済ませるに越したことはないため、GitHub Enterprise に入らずにデプロイを承認制する方法を考える必要がありました。
どのように実現するか?
🍳 用意するもの
GitHub Actions
・開発環境へのデプロイ
・ステージング環境へのデプロイ
・本番環境へのデプロイ
Slack チャンネル
・開発環境
・ステージング環境
・本番環境
🚀 フローの概要
- GHA : 開発環境へデプロイ
- Slack : 開発環境チャンネルにデプロイ完了通知
開発環境チャンネルに QA 完了ボタンを表示 - QA 完了 ✨
- Slack : 開発環境チャンネルの QA 完了ボタンを押下
- Slack : ステージング/本番チャンネルに[デプロイ]ボタンを表示
- Slack : [デプロイ]ボタンを押下
- GHA : ステージング/本番へデプロイ
利用する機能
では次に実装にまつわる話に移ります。
主に使用した機能やツールはこちらになります。
- GitHub Actions
- Slack Workflow
- Slack Custom Function
なぜ Slack Custom Function を使うのか?
標準の Slack Workflow では実現できないことが 2 点ありました。
- 1 つのアクションから同時に 2 チャンネルにメッセージを送れない
- 開発環境で QA 確認完了後「ステージング環境」「本番環境」の 2 チャンネルに送りたい
- 外部 API を呼び出すことができない
- Slack から GitHub Actions を実行したい
これを解決するためにSlack Custom Functionを使用する必要がありました。
Slack Custom Function ってなに?
先述したフローに当てはめると5, 6 番目にあたるステップがカスタムファンクションを使わないと実現できない機能です。
- GHA : 開発環境へデプロイ
- Slack : 開発環境チャンネルにデプロイ完了通知
開発環境チャンネルに QA 完了ボタンを表示- QA 完了 ✨
- Slack : 開発環境チャンネルの QA 完了ボタンを押下
- Slack : ステージング/本番チャンネルに[デプロイ]ボタンを表示
- Slack : [デプロイ]ボタンを押下
- GHA : ステージング/本番へデプロイ
Slack Custom Function
の詳細を知りたい方はこちらから
↓↓↓↓↓↓↓
実装例
次に実装の話に移ります。
承認制デプロイを実現するためには大きく分けて 3 つ実装する必要があります。
-
GitHub Actions
- 開発環境デプロイ後 Slack に通知する部分の実装する
- SlackからAPI経由で実行するワークフロー
-
Slack Workflow
- 開発環境デプロイ後の通知を受けて開発環境チャンネルにメッセージを送信するように設定する
- 開発環境チャンネルでボタンが押されたら後述する Slack Custom Function を呼び出すように設定する
-
Slack Custom Function
- 開発環境チャンネルで「QA 確認完了」ボタンを押されるとステージングチャンネル, 本番チャンネルに「デプロイボタン」付きメッセージが飛ぶように実装する
- デプロイボタンを押すと GitHub Actions が実行するように実装する
それぞれの詳細は次の項目で紹介します。
GitHub Actions 作成
-
開発環境デプロイ後 Slack に通知する部分の実装する
後述で作成する Slack Workflow の URL にPOST
リクエストを行います。
この際に後続処理で必要になる変数を渡すことも可能です。
👇 実装例(一部抜粋)send_slack_notification: runs-on: ubuntu-latest steps: - name: Send API Request run: | curl -X POST ${{ secrets.SLACK_WEBHOOK_URL }} \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${{ secrets.SLACK_WEBHOOK_TOKEN }}" \ -d slackに渡したいパラメータがあればここに記述
-
Slack から API 経由で実行されるようにする部分の実装する
repository_dispatch を使用することで API 経由でWorkflowを実行できるようになります。
types で受け取ったパラメータを元に実行するワークフローを指定することも可能です。
👇 実装例(一部抜粋)on: repository_dispatch: types: [production]
Slack Workflow 実装
なるべく実装コストを減らすために Slack Workflow で実現できる部分は Slack Workflow を使用します。
作成する workflow はいたってシンプルで以下の機能を持った workflow を作成します。
- 開発環境デプロイ後の通知を受け取れるように Webhook から発火されるようにする
- 開発環境チャンネルに「QA 確認完了」ボタンを通知する
- 「QA 確認完了」ボタンを押下したらCustom Functionを呼び出す
※もちろん最初から最後まで Custom Function で作成することも可能です。
Slack Workflow
の詳細はこちらから参照できます。
↓↓↓↓↓↓↓
Slack Custom Function 実装
事前準備
slack cli を使えるようにします。
curl -fsSL https://downloads.slack-edge.com/slack-cli/install.sh | bash
slack コマンドが使用可能になっているか確認します。
slack help
slack にログインします。
slack login
Slack Custom Function の使い方
下記コマンドでアプリを新規作成します。
slack create my-app
テンプレートをもとに作成すると下記のようなファイルが自動生成されます。
.
├── LICENSE
├── README.md
├── assets
│ └── default_new_app_icon.png
├── deno.jsonc
├── functions
│ └── post_issue_message.ts
├── import_map.json
├── manifest.ts
├── slack.json
├── triggers
│ └── submit_issue.ts
└── workflows
└── submit_issue.ts
この workflow をローカルで動作確認する場合は下記コマンドを使用します。
これで Slack の画面から試すことが可能になります。
slack run
ローカルで動作確認しつつテンプレートを編集していくと実装のイメージがつきやすそうですね。
Slack Custom Function 実装
下記機能を実装します。
- 開発環境用チャンネルで「QA 確認完了」ボタンを押下後、ステージング用チャンネルと本番環境用チャンネルに「デプロイ」ボタンを通知
- 「デプロイ」ボタンが押されたら GItHub Actions を実行する
実際に作成したカスタムファンクションはこちらのリポジトリから参照できます。
↓↓↓↓↓↓↓
上記リポジトリから一部抜粋して説明していきます。
Slack Workflow から呼び出すメソッドを定義します。
受け取りたい変数を定義します。
export const PostDeployMessage = DefineFunction({
callback_id: "post_deploy_message",
title: "デプロイボタン表示をするメッセージ",
description:
"各環境向けにデプロイボタンを表示するためのメッセージを投稿します",
source_file: "functions/post_deploy_message.ts",
input_parameters: {
properties: {
commitHash: {
description: "デプロイするコミットのハッシュ",
type: Schema.types.string,
},
githubRepositoryOwner: {
description: "デプロイ対象リポジトリのオーナー",
type: Schema.types.string,
},
githubRepository: {
description: "デプロイ対象リポジトリ",
type: Schema.types.string,
},
sendToSlackChannelIdStaging: {
description: "ステージング:Slackに通知するチャンネルID",
type: Schema.types.string,
},
sendToSlackChannelIdProduction: {
description: "本番:Slackに通知するチャンネルID",
type: Schema.types.string,
},
},
required: [
"commitHash",
"githubRepositoryOwner",
"githubRepository",
"sendToSlackChannelIdStaging",
"sendToSlackChannelIdProduction",
],
},
});
次に開発環境用チャンネルで「QA 確認完了」ボタンが押された後にステージング環境用のチャンネル、本番環境用のチャンネルにメッセージを送る処理を実装します。
それぞれのチャンネルには下記のようにメッセージを送信しています。
- コミットハッシュ
- デプロイボタン
- キャンセルボタン
export default SlackFunction(PostDeployMessage, async ({ inputs, client }) => {
await postMessage(client, {
commitHash: inputs.commitHash,
channel: inputs.sendToSlackChannelIdStaging,
repository: inputs.githubRepository,
});
await postMessage(
client,
{
commitHash: inputs.commitHash,
channel: inputs.sendToSlackChannelIdProduction,
repository: inputs.githubRepository,
},
true
);
return { completed: false };
});
const postMessage = async (
client: SlackAPIClient,
postParams: {
commitHash: string;
channel: string;
repository: string;
},
prod: boolean = false
) => {
await client.chat.postMessage({
channel: postParams.channel,
blocks: [
{
type: "section",
text: {
type: "plain_text",
text: `コミット:${postParams.commitHash}`,
},
},
{
type: "actions",
elements: [
{
type: "button",
text: {
type: "plain_text",
text: `🚀デプロイ(${prod ? "本番" : "ステージング"})`,
},
action_id: `${postParams.channel}-deploy`,
},
],
},
{
type: "actions",
elements: [
{
type: "button",
text: {
type: "plain_text",
text: `🚧キャンセル`,
},
action_id: `${postParams.channel}-cancel`,
},
],
},
],
});
};
これだけでは「デプロイ」ボタンを押しても何も実行されません・
なので次は「デプロイ」ボタンが押下された時に実行される関数を実装します。
ここでは GitHub の API を使用して GitHub Actions を実行させる必要があるので下記のような実装になります。
const dispatchGithubActions = async (
params: DispatchGithubActionsParams,
env: Env
) => {
return await fetch(
`https://api.github.com/repos/${params.owner}/${params.repository}/dispatches`,
{
method: "POST",
headers: {
Authorization: `Bearer ${env.GITHUB_TOKEN}`,
Accept: "application/vnd.github.v3+json",
},
body: JSON.stringify({
event_type: `${params.environment}`, // "staging | production"
client_payload: {
// ここに渡したい引数を設定
},
}),
}
)
.then((response) => response.ok)
.catch((error) => {
console.error(error);
throw new Error("Failed to dispatch Github Actions");
});
};
ポイントはbodyのevent_typeと GitHub Actions 側のrepository_dispatchの types で指定している名称を合わせる必要がある点です。
運用する時は「staging」「production」の環境名を指定しています。
Slack Workflow から Slack Custom Function を呼び出す
実装が一通り終わりローカルでの動作確認が完了したらデプロイして実装完了となります。
slack deploy
まとめ
今回は Slack を活用することでGitHub Enterprise プランを使わずにデプロイを承認制にする方法を紹介しました。
Slack Custom Function を初めて作ってみましたが、自分でコードを書く分自由度が高く色んなことに使えそうと思いました。
Discussion