CI/CD 目指すはメンテフリー🗽~GitHub Actionsによる自動アップデートとWebhooksによるDiscordへの自動通知~
Changelog
- 10/16/2024
- 初版公開
- 10/21/2024
-
main
ブランチのRulesetsに関する項を追加 - 定期マージのworkflow(例)を修正
-
はじめに
この記事ではCI/CDをフル活用して、メンテナンスコストを限りなくゼロに近づける方法を紹介する。
対象とする読者
- メンテナンスコストを限りなくゼロに近づけたい(メンテナンスフリー)
- ライブラリやフレームワークのアップデートを自動化したい
- アップデートやデプロイの通知を受け取りたい
- Staging/Production環境とCI/CDを上手く組み合わせたい
CI/CD とは?
CI/CDの必要性やDevOpsについて:CI/CDのリンク
前準備(前提)
- 環境として開発(
develop
ブランチ)と本番(main
ブランチ)がある - 一般的なCI (lint, test, build)は既に用意されている
アップデートの自動化
まずはアップデートの自動化を行う。
CI (GitHub Actions)
例)Lint workflow
name: Lint
on:
pull_request:
branches:
- develop
jobs:
lint:
name: npm run lint
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Cache packages
uses: actions/cache@v4
id: cache_npm_packages
env:
cache-name: cache-npm-packages
with:
path: "**/node_modules"
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
- name: Install packages
if: ${{ steps.cache_npm_packages.outputs.cache-hit != 'true' }}
run: npm i
- name: Run lint
run: npm run lint
Dependabot
アップデートの自動化にはDependabotを使う。
セキュリティアップデート
Settings > Code security
バージョンアップデート
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
target-branch: "develop"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
target-branch: "develop"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major", "version-update:semver-minor"]
例)グループ化すると関連するパッケージをまとめたPRにできる
...
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major", "version-update:semver-minor"]
groups:
next:
applies-to: version-updates
update-types:
- "patch"
- "minor"
patterns:
- "@next/*"
- "next"
- "eslint-config-next"
lint:
applies-to: version-updates
update-types:
- "patch"
- "minor"
patterns:
- "@typescript-eslint/*"
- "eslint*"
- "prettier*"
- "stylelint*"
exclude-patterns:
- "eslint-config-next"
deps:
applies-to: version-updates
update-types:
- "patch"
- "minor"
dependency-type: production
patterns:
- "*"
exclude-patterns:
- "@next/*"
- "next"
- "eslint-config-next"
- "@typescript-eslint/*"
- "eslint*"
- "prettier*"
- "stylelint*"
dev-deps:
applies-to: version-updates
update-types:
- "patch"
- "minor"
dependency-type: development
patterns:
- "*"
exclude-patterns:
- "@next/*"
- "next"
- "eslint-config-next"
- "@typescript-eslint/*"
- "eslint*"
- "prettier*"
- "stylelint*"
GitHub Settings
Settings > Rulesets
Rulesetsの設定により、CI (lint)をパスしたPRのみマージできるようにする。コード品質の検証やテストに失敗した場合にマージを止めることができる。
Settings > General > Pull Requests
Allow auto-merge
を有効にすると、先ほどRulesets設定した条件(CIやPRのRequirements)をパスした場合に自動マージできる。
Settings > Actions > Workflow permissions
GitHub Actions
# https://docs.github.com/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions
name: Dependabot auto-merge
on: pull_request
permissions:
contents: write
pull-requests: write
jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve a PR
run: gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Enable auto-merge for Dependabot PRs
if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' }}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
CD(リリース・デプロイの自動化)
定期的なマージと、Vercel, Netlify, Cloudflare Pages等のPaaSや、デプロイ用のworkflowを組み合わせることでリリース(デプロイ)を自動化する。
定期マージ
前述したアップデートの自動化はdevelop
ブランチに対して行う。これらの変更をmain
ブランチ/本番環境(prod)にデプロイするには、定期的に変更をマージするworkflowを作る。
例)毎週月曜日の00:00(JST)に定期マージするworkflow
name: Schedule merge from develop into main
on:
schedule:
- cron: "0 15 * * 0" # Every Monday at 00:00 JST
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
merge:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: develop
- name: Diff develop and main
id: diff
run: git diff origin/main origin/develop --exit-code
continue-on-error: true
- name: Create a PR
id: pull-request
# If diff is exist, create a PR
if: ${{ steps.diff.outcome == 'failure' }}
run: |
PR_URL=$(gh pr create -B main -t "release(prod): weekly merge from develop into main" -l 'Type: Release' -b "Regular merge every Monday at 00:00.")
echo "PR_URL=$PR_URL" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Enable auto-merge for PRs
# Only merge if PR has diff
if: ${{ steps.diff.outcome == 'failure' }}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{ steps.pull-request.outputs.PR_URL }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
例)mainブランチのRulesets
例)Vercel Ignored Build Stepのscript(なくてもよい)
#!/bin/bash
BRANCH=$VERCEL_GIT_COMMIT_REF
echo "🚀 Starting build for branch $BRANCH"
# Only build for main, preview, and develop branches
if [ "$BRANCH" != "main" ] && [ "$BRANCH" != "preview" ] && [ "$BRANCH" != "develop" ]; then
echo "🛑 Skipping build for branch $BRANCH"
exit 0
fi
# Only build if the commit does not include .md files
if git diff --name-only HEAD~1 HEAD | grep -q '\.md$'; then
echo "🛑 Skipping build because commit includes .md files"
exit 0
fi
# Proceed with the build
echo "✅ Proceeding with the build"
exit 1
リリース(デプロイ)
Vercel, Netlify, Cloudflare Pages等のPaaSを使う場合は、main
ブランチをProduction Branchに設定する。
デプロイ用のworkflowを作る場合は、GitHub Actionsでmain
ブランチへのpush
イベントをトリガーにすると良い。
通知の自動化
GitHubのIssuesやPull requests、GitHub Actionsのworkflowsやリリース(デプロイ)の通知を自動化する。
Discord Webhook
Server Settings (Edit Channel) > Integrations > Webhooks > New Webhook
GitHub Webhook
Settings > Webhooks > Add webhook
Webhook URLに/github
を付けることで、Discordが公式に対応しているGitHubイベントを通知できる。
それ以外のGitHubイベントはGitHub Actionsでイベントに対応するworkflowsを作る必要がある。これにはcurl
でDiscord Webhookの実行もしくは、Discord Webhook Actionを使う。
Discord Webhook Action (GitHub Actions)
Discord Webhook Actionを使って、Discordが対応していないGitHubイベントの通知を行う。(curl
を使わない場合)
GitHub Settings
Settings > Actions secrets and variables > New repository secrets
GitHub Actions
例)CI (workflows)の通知
# https://github.com/marketplace/actions/discord-webhook-action
name: Discord Notify Workflow
on:
workflow_run:
workflows: ["Lint"]
types: [requested, completed]
branches:
- "*"
jobs:
discord-notify-workflow:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v6.0.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL_WORKFLOW }}
username: ${{ github.event.workflow_run.actor.login }}
avatar-url: ${{ github.event.workflow_run.actor.avatar_url }}
embed-title: "[${{ github.repository }}] Workflow ${{ github.event.workflow_run.name }}: ${{ github.event.workflow_run.pull_requests.number || github.event.workflow_run.head_branch }} 🤖"
embed-description: "Branch: ${{ github.repository }}/${{ github.event.workflow_run.head_branch }}\nWorkflow: ${{ github.event.workflow_run.name }}\nStatus: ${{ job.status }} ${{ job.status == 'success' && '✅' || job.status == 'failure' && '🛑' || '🟨' }}\nEvent: ${{ github.event.workflow_run.event }}"
embed-color: ${{ job.status == 'success' && '2278750' || job.status == 'failure' && '15680580' || '15381256' }}
embed-author-icon-url: ${{ github.event.workflow_run.actor.avatar_url }}
embed-author-name: ${{ github.event.workflow_run.actor.login }}
embed-author-url: ${{ github.event.workflow_run.actor.html_url }}
embed-url: ${{ github.event.workflow_run.html_url }}
例)Deploy (Release)の通知
# https://github.com/marketplace/actions/discord-webhook-action
name: Discord Notify Deploy
on: deployment
jobs:
discord-notify-deploy:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v6.0.0
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL_WORKFLOW }}
username: ${{ github.event.deployment.creator.login }}
avatar-url: ${{ github.event.deployment.creator.avatar_url }}
embed-title: "[${{ github.repository }}] Deploy ${{ github.event.deployment.environment }} 🛠️"
embed-description: "Repository: ${{ github.repository }}\nDeploy: ${{ github.event.deployment.environment }}\nStatus: ${{ job.status }} ${{ job.status == 'success' && '✅' || job.status == 'failure' && '🛑' || '🟨' }}\n${{github.event.deployment.description}}"
embed-color: ${{ job.status == 'success' && '2278750' || job.status == 'failure' && '15680580' || '15381256' }}
embed-author-icon-url: ${{ github.event.deployment.creator.avatar_url }}
embed-author-name: ${{ github.event.deployment.creator.login }}
embed-author-url: ${{ github.event.deployment.creator.html_url }}
embed-url: ${{ github.event.deployment.url }}
Webhookは認証機構がないためURLを外部に公開しないこと。
通知(表示例)
こんな感じのembeddedでリッチなカードの通知が来る。
例)CI (workflows)の表示
さいごに
お役に立てれば幸いです!
以上『CI/CDの完全自動化でリアルライフを充実させよう!』のコーナーでした 🎃
About us
アプリ開発サークルは『価値を創造し、価値を届ける』という理念のもと、企画・開発・デザイン・プロモーション・運営の垣根を超えた活動をチームで目指す、東京国際工科専門職大学の公認サークルです!
東京国際工科専門職大学の学生さんで、少しでもご興味がございましたら、お気軽にアプリ開発サークルまでお問い合わせください!
✨発言は個人の見解であり、所属する組織を代表するものではありません✨
Discussion