Slide 1

Slide 1 text

ファインディでの GitHub Actions活⽤事例 1 2024/08/22 GitHub Actionsの最適化どうしてる?開発者体験を向上させる運⽤術 ファインディ株式会社 フロントエンドテックリード 新福 宜侑    @puku0x

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

3 GitHub Actions

Slide 4

Slide 4 text

4 ラベル設定 担当者アサイン Pull Request 作成

Slide 5

Slide 5 text

担当者アサインの⾃動化 5

Slide 6

Slide 6 text

担当者アサインの⾃動化 6 ● ファインディでは↓を利⽤しているプロジェクトが多い ○ https://github.com/kentaro-m/auto-assign-action ○ ⾃動化しているのはAssigneeのみ - uses: kentaro-m/[email protected] with: repo-token: ${{ secrets.GITHUB_TOKEN }} addAssignees: author runOnDraft: true

Slide 7

Slide 7 text

7 ラベル設定 担当者アサイン Pull Request 作成

Slide 8

Slide 8 text

ラベルの⾃動化 8

Slide 9

Slide 9 text

ラベルの⾃動化 9 ● actions/labeler を利⽤した⾃動付与 ○ https://github.com/actions/labeler ● ラベルは↓を参考に作成 ○ https://github.com/azu/github-label-setup

Slide 10

Slide 10 text

.github/labeler.yml 10 'Type: Feature': - head-branch: ['^feat'] 'Type: Bug': - head-branch: ['^fix', '^bug'] 'Type: Refactoring': - head-branch: ['^refactor'] '施策対応': - head-branch: ['.*-pj-.*'] ブランチ名から ラベルを⾃動付与

Slide 11

Slide 11 text

モノレポの場合に便利な設定 11 'Scope: App1: - changed-files: - any-glob-to-any-file: - apps/app1/**/* - libs/app1/**/* 'Scope: App2: - changed-files: - any-glob-to-any-file: - apps/app2/**/* - libs/app2/**/* ファイルの変更で ラベルを⾃動付与

Slide 12

Slide 12 text

actions/labeler が対応していないもの 12 run: | pr_title=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Accept: application/vnd.github.v3+json" \ "https://api.github.com/repos/Findy/***/pulls/${{ github.event.pull_request.number }}" \ | jq -r '.title') if [[ $pr_title =~ ^feat ]]; then pr_type='Feature' fi curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Accept: application/vnd.github.v3+json" -d '{"labels": ["Type: '"$pr_type"'"]}' \ "https://api.github.com/repos/Findy/***/issues/${{ github.event.pull_request.number }}/labels" PRタイトルから ラベルを⾃動付与

Slide 13

Slide 13 text

13 CI実⾏ ラベル設定

Slide 14

Slide 14 text

14 CIにどれくらい時間が かかっていますか?

Slide 15

Slide 15 text

15 開発あるある 機能追加 コード量↑ CI時間↑

Slide 16

Slide 16 text

16 CIが遅いと... レビューが遅くなる どうせ◯分 かかるし...

Slide 17

Slide 17 text

17 レビューが遅いと... コンフリクトが発⽣しやすくなる ブランチの⽣存期間が延びる

Slide 18

Slide 18 text

18 開発者はコーディングと同程度CI待ちに時間を割く https://github.blog/news-insights/research/survey-reveals-ais-impact-on-the-developer-experience/

Slide 19

Slide 19 text

19 CIの⾼速化は必須

Slide 20

Slide 20 text

キャッシュの活⽤ 20

Slide 21

Slide 21 text

21 actions/cache

Slide 22

Slide 22 text

キャッシュの活⽤ 22 - uses: actions/setup-node@v4 id: node with: node-version: 20 - uses: actions/cache@v4 id: cache with: path: node_modules key: ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.node.outputs.node-version }}-npm-${{ hashFiles('**/pac - if: steps.cache.outputs.cache-hit != 'true' run: npm ci キャッシュヒットした場合に 依存ライブラリのインストールを省略

Slide 23

Slide 23 text

23

Slide 24

Slide 24 text

24 node_modulesのキャッシュ キーを適切に設定していれば node_modules はキャッシュして良い

Slide 25

Slide 25 text

キャッシュキー詳細 25 ${{ runner.arch }}-${{ runner.os }}-node-${{ node-version }}-npm-${{ hashFiles('**/package-lock.json') }} CPUアーキテクチャ X64 や ARM64 など Node.js バージョン OS種別 ロックファイルの ハッシュ値 パッケージ マネージャー種別

Slide 26

Slide 26 text

~/.npm等もキャッシュする場合 26 - uses: actions/cache@v4 id: cache with: path: node_modules key: ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.node.outputs.node-version }}-npm-${{ hashFiles('**/package-lock.json') }} - uses: actions/cache@v4 if: steps.cache.outputs.cache-hit != 'true' with: path: | ~/.npm ~/.cache/Cypress key: ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.node.outputs.node-version }}-npm-${{ hashFiles('**/package-lock.json') }} restore-keys: ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.node.outputs.node-version }}-npm- - if: steps.cache.outputs.cache-hit != 'true' run: npm ci

Slide 27

Slide 27 text

27 キャッシュ容量⾜りない問題

Slide 28

Slide 28 text

28 キャッシュ消費の低減 - uses: actions/cache@v4 if: github.actor != 'dependabot[bot]' id: cache with: path: node_modules key: ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.node.outputs.node-version }}-npm-${{ hashFiles('**/pack ● 案1. actions/cache/restore を使う ○ actions/cache/save を使うワークフローは別途⽤意(デフォルトブランチ) ● 案2. キャッシュする条件を追加 ○ botが作るPRはキャッシュしない等

Slide 29

Slide 29 text

ジョブの並列化 29

Slide 30

Slide 30 text

ジョブの並列化 30 ● matrixを使った並列化 ○ parallel_split_test ○ ファイルサイズ(≒実⾏時間)でソート Findy Tech Blog -RailsのCIのテスト実行時間を 10分から5分に高速化した話 - https://tech.findy.co.jp/entry/2024/03/04/100000 strategy: matrix: ci_node_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] steps: run: | TEST_FILES="$(find ./spec -type f -name "*_spec.rb" -exec ls -l {} + | sort -n -k 5 | awk '{print $9}' | xargs ./script echo $TEST_FILES bundle exec parallel_split_test $TEST_FILES --format progress --color

Slide 31

Slide 31 text

Larger runners 31

Slide 32

Slide 32 text

Larger Runner 32 ● GitHub Teamプランまたは GitHub Enterprise Cloudプランのみ ○ https://docs.github.com/actions/using-github-hosted-runners/about-github-hosted-runners ● CIが実⾏されるホストマシンのスペックを上げる ○ プライベートリポジトリのデフォルトはCPU2コア/メモリ7GB ■ 最⼤CPU64コア/メモリ256GB ■ Armベースのランナー(ベータ版)も利⽤可 ● 3割程度安い、ruby/setup-ruby等は未対応 ● 料⾦はスペックに⽐例 ○ スペックを上げればCI時間が短縮される

Slide 33

Slide 33 text

33 早いは正義

Slide 34

Slide 34 text

34 CIの時間は10分が⽬安 https://speakerdeck.com/ham0215/ciha5fen-yi-nei-su-zao-ikai-fa-saikuruwozhi-eruci

Slide 35

Slide 35 text

35 リリース CI実⾏

Slide 36

Slide 36 text

36 リリースするバージョンを決めて PR作ってリリースノート書いて 検証環境に上げてレビューして 本番環境にデプロイして... わかりました リリースよろしく

Slide 37

Slide 37 text

リリース⾃動化 37

Slide 38

Slide 38 text

38 セマンティックバージョニング ● Conventional Commitsに準拠 https://www.conventionalcommits.org 例: feat(lang): add polish language ● コミットメッセージを基に新しいバージョンを⾃動採番 ○ リリースノートの作成も⾃動

Slide 39

Slide 39 text

リリース⾃動化 39 1. 新しいバージョンの採番ワークフローを実⾏ 2. バージョニング⽤Pull Request(⾃動⽣成)をマージ 3. リリース⽤Pull Request(⾃動⽣成)をレビュー後、マージ 4. 本番デプロイ https://dev.to/puku0x/github-actions-1mi5

Slide 40

Slide 40 text

40 リリース時QAチェック項⽬の抽出 ● PR作成時にチェックを⼊れる ● リリースPRのテンプレートに↑チェックされた項⽬を追加 def category_by_pull(pull) return :planner_qa_pr if body.include? "- [x] 企画側QA" return :developer_qa_pr if body.include? "- [x] 開発側QA" return :other_pr end ## リリース時QAチェック項⽬ ### 企画QA #{pull_id_lines(:planner_qa_pr, true)} ### 開発QA #{pull_id_lines(:developer_qa_pr, true)}

Slide 41

Slide 41 text

定期実⾏ジョブ 41

Slide 42

Slide 42 text

定期実⾏ジョブ 42 カバレッジレポートや定期的なリリース on: schedule: - cron: '30 0 * * 1-5' # 平⽇09:30 (JST) 分 時 ⽇ ⽉ 曜⽇ ※60⽇以上リポジトリにアクティビティが無い場合は⾃動で無効化されます

Slide 43

Slide 43 text

43 休みの⽇は働きたくない

Slide 44

Slide 44 text

祝⽇判定 44 check: outputs: is_holiday: ${{ steps.check_holiday.outputs.result }} steps: - run: npm install @holiday-jp/holiday_jp - uses: actions/github-script@v7 id: check_holiday env: TZ: 'Asia/Tokyo' # タイムゾーン固定必須 with: script: | const holidayJp = require('@holiday-jp/holiday_jp'); return holidayJp.isHoliday(new Date()); some_job: needs: check if: needs.check.outputs.is_holiday == 'false' 祝⽇判定 ジョブ本体

Slide 45

Slide 45 text

リポジトリ間の連携 45

Slide 46

Slide 46 text

46 GraphQLの スキーマ更新しました バックエンドの最新版Pullして graphql-codegenしてPR作って... わかりました

Slide 47

Slide 47 text

47 repository_dispatch

Slide 48

Slide 48 text

リポジトリ間の連携(1) 48 バックエンド側はフロントエンド側にイベント送信 on: push: branches: - main paths: - schema.json jobs: steps: - uses: actions/create-github-app-token@v1 id: app-token - run: | curl -X POST H "Authorization: token ${{ steps.app-token.outputs.token }}" \ -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/Findy/フロントエンド側リポジトリ/dispatches \ -d '{"event_type":"trigger-graphql-codegen"}'

Slide 49

Slide 49 text

リポジトリ間の連携(2) 49 フロントエンド側は repository_dispatch をトリガに起動 on: repository_dispatch: types: [trigger-graphql-codegen] jobs: steps: - uses: actions/create-github-app-token@v1 id: app-token - uses: actions/checkout@v4 with: repository: Findy/バックエンド側リポジトリ - run: | 〜schema.jsonコピー&graphql-codegen実⾏&PR作成など〜 バックエンド側 からのイベント

Slide 50

Slide 50 text

⾃作Actions 50

Slide 51

Slide 51 text

51 GitHub利⽤状況の取得 https://github.com/starfish719/check-actions-billing-info - uses: starfish719/[email protected] id: check with: accessToken: ${{ secrets.PERSONAL_ACCESS_TOKEN }} user: 'ユーザー名' - name: result run: | echo total_minutes_used-${{ steps.check.outputs.total_minutes_used }} echo total_paid_minutes_used-${{ steps.check.outputs.total_paid_minutes_used }} echo included_minutes-${{ steps.check.outputs.included_minutes }} ※利⽤イメージ

Slide 52

Slide 52 text

52 GitHub Project v2の⾃動化 https://github.com/nipe0324/update-project-v2-item-field ● リードタイム計測⽤に開発 ● 当時Project v2対応のものが⾒つからなかったため⾃作 ○ Statusの変更はProject Workflowでも良さそう? if: ${{ github.event_name == 'pull_request' }} steps: - uses: nipe0324/update-project-v2-item-fi[email protected] with: project-url: https://github.com/orgs/Findy/projects/***/views/*** field-name: 'Status' field-value: 'In Progress'

Slide 53

Slide 53 text

53 リードタイム計測&Projectに反映 https://github.com/nipe0324/update-project-v2-item-field - uses: nipe0324/update-project-v2-item-fi[email protected] if: ${{ github.event.action == 'closed' }} with: project-url: https://github.com/orgs/Findy/projects/***/views/*** field-name: 'リードタイム' field-value-script: | const endDate = new Date(item.fieldValues['開発完了⽇']); const startDate = new Date(item.fieldValues['開発着⼿⽇'] ?? endDate); const days = Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 3600 * 24)) return days;

Slide 54

Slide 54 text

54 まとめ ● ファインディではCI/CDにGitHub Actionsを活⽤ ○ CI以外にも活⽤できる! ● 今後の予定(検証中) ○ DependabotのPRマージ⾃動化 ■ 公式で対応して欲しい ○ RDSバージョンアップ → Terraform⾃動反映 ○ リリース⽤ツール⾒直し ■ Release Please、Release Drafter、Nx Release…

Slide 55

Slide 55 text

GitHub Actionsはいいぞ! 55

Slide 56

Slide 56 text

We’re hiring! https://careers.findy.co.jp/ 56