チーム全体の生産性を加速する!開発ナレッジを活かした自動化の実践
開発現場では、日々の作業の中で手作業による負担が蓄積し、チーム全体の生産性が低下することがあります。そこで重要になるのが「自動化」です。本記事では、開発ナレッジを活かしてチーム全体の生産性を向上させるための自動化の具体的な実践例を紹介します。
対象読者
- GitHub Actionsを使って手作業を自動化したい方
- 開発チームの生産性向上に関心があるマネージャーやリーダー
- 継続的インテグレーション/デリバリー(CI/CD)の導入を検討しているエンジニア
自動化はチーム全体の生産性を向上させる
自動化の導入は、個人の作業効率を向上させるだけでなく、チーム全体の生産性向上にも貢献します。
-
手作業の削減による効率化
- 繰り返し発生する作業を自動化することで、開発者が本来の業務に集中できるようになります
- CIとして実装することでその自動化をチームメンバーも利用できるようになります
-
ミスを防ぐための仕組み作り
- 手動作業によるヒューマンエラーを防ぎ、一貫した品質を保つことができます
設計レビューに関する自動化
背景
私が所属するチームでは、PRを出す前に設計レビューを行うことがあります。
自動化前の課題
- Issueに設計や実装方針について記載
- 書けたらレビュー依頼を行う
- 同期レビュー: 昼礼の際にレビューをしてもらいたいIssueのリンクを議事録に記載
- 非同期レビュー: Slackで「<issueのリンク> こちら非同期レビューをお願いいたします」と依頼
- 合意が取れたら
planned
ラベルを付与
自動化後の改善
ほとんどのレビューは非同期レビューとして行われますが、Slackにリンクを貼る作業が面倒だったため、以下の自動化を導入しました。
レビュー依頼を自動化
新たに設計レビュー中であることを示す plan: planning
ラベルを作成し、そのラベルを付けることでSlackに通知が行くようにしました。
設計レビュー依頼をSlackに通知する自動化の例
# このワークフローは、"plan: planning" ラベルが付けられたIssueをSlackに通知するためのものです。
name: On Planning
on:
issues:
types:
- labeled
jobs:
# "plan: planning" ラベルがつけられたらslackに通知
notify-planning:
if: ${{ endsWith(github.event.label.name, 'planning') }}
runs-on: ubuntu-latest
steps:
- name: Post to a Slack channel if planning label is added
uses: slackapi/slack-github-action@v2.0.0
with:
token: ${{ secrets.SLACK_BOT_TOKEN }}
method: chat.postMessage
# sec-app
payload: |
channel: <channel-id>
text: "<!subteam^group-id>\n<${{ github.event.issue.html_url }}|${{ github.event.issue.title }}>\n非同期プランニングをお願いいたします!"
これにより非同期レビューの流れは以下のように変わりました。
- Issueに設計や実装方針について記載
- 書けたら
plan: planning
ラベルを付与 - 合意が取れたら
plan: planned
ラベルを付与
レビュー催促を自動化
非同期でレビュー依頼を行った場合、忘れてしまうこともあるため、plan: planning
ラベルが付いたIssueがあれば毎朝催促を行うようにしました。
プランニング中のIssueをSlackでリマインドする自動化の例
name: Check Planning
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * 1-5"
jobs:
hoge:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Find planning issues
run: |
MESSAGE=$(gh issue list --label "plan: planning" --json title,url,assignees --jq '.[] | "<\(.url)|\(.title)> from \(.assignees.[].login), "' | sed 's/, /\\n\\n/g')
{
echo 'MESSAGE<<EOF'
echo -e $MESSAGE
echo EOF
} >> "$GITHUB_ENV"
- name: Post to a Slack channel if exists
uses: slackapi/slack-github-action@v2.0.0
if: ${{ env.MESSAGE != '' }}
with:
token: ${{ secrets.SLACK_BOT_TOKEN }}
method: chat.postMessage
# sec-app
payload: |
channel: <channel-id>
text: "<!subteam^group-id>\nプランニング中のIssueがあります。確認をお願いします。\n${{ env.MESSAGE }}"
作業イシューのコピーの自動化
背景
私が所属するモバイルチームでは、iOSとAndroidそれぞれのリポジトリが存在し、共通する課題管理のためのリポジトリもあります。
自動化前の課題
- 共通の課題は共通リポジトリに親Issueを作り、それぞれのリポジトリに移動して手作業でIssueを作る必要がある
自動化後の改善
- 親Issueで
/copy
というコメントをすることで、作業が自動化されます
工夫した点
- 単純なスラッシュコマンドで起動する
- イシューコピー用のPersonal Access Tokenを作成し、用途を限定させる
name: Create Child Issues
on:
issue_comment:
types:
- created
jobs:
create-child-issue:
if: ${{ startsWith(github.event.comment.body, '/copy') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: copy issue to each repositories
env:
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_URL: ${{ github.event.issue.html_url }}
GH_TOKEN: ${{ secrets.MOBILE_TEAM_ISSUE_COPIER_TOKEN }}
run: |
TEMPLATE=$(cat .github/workflows/child-issue-template.md)
BODY=$(echo -e "## 要件\n- 親issue: $ISSUE_URL\n\n$TEMPLATE")
gh api repos/owner/android-repo/issues \
--header "Authorization: Bearer $GH_TOKEN" \
-f title="$ISSUE_TITLE" \
-f body="$BODY"
gh api repos/owner/ios-repo/issues \
--header "Authorization: Bearer $GH_TOKEN" \
-f title="$ISSUE_TITLE" \
-f body="$BODY"
## スケジュール
- テスト実施日、リリースはいつ?
## 既存仕様
### 概要
- そのままテストコードや動作確認テストに落ちる粒度
### クラス図・シーケンス図など
| | |
|---|---|
| <img src="" width=400> | <img src="" width=400> |
## 要件・仕様
### 詳細
- そのままテストコードや動作確認テストに落ちる粒度
### Links
- []()
## 確認用アカウント情報
| id(mail) | password |
|---|---|
| | |
## 実装方針、設計
### 概要
-
### クラス図・シーケンス図など
| | |
|---|---|
| <img src="" width=400> | <img src="" width=400> |
## TODO
- [ ] 作業開始commit `git commit --allow-empty -m '対応開始'`
リリース作業の半自動化
背景
リリース作業は手順が多く、ミスが発生しやすいため、自動化を導入しました。
自動化前の課題
- 手動でリリース用のブランチを作成し、バージョンをインクリメントする必要があった
- リリースPRをマージした後手動でデプロイする必要があった
自動化後の改善
- リリース用のIssueを作成し、
/release
とコメントするだけでPRを作成できるようになりました - リリースPRのマージ後にリリースの作成、タグの作成、デプロイまで自動化されました
工夫した点
- 完全自動化ではなくスラッシュコマンドを起点とした半自動化に留めたこと
- リリース作業は週1で行なっているので、cronを使って完全に自動化することも可能ですが、hotfixのような作業が発生すると手作業で行った方が良いため、エントリーポイントだけは人の手で行うようにすることで柔軟性を持たせています
ワークフローファイル
name: Release Command
on:
issue_comment:
types:
- created
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
env:
# アクセストークンを作っておかないとPR作成をトリガーとするCIが動かない
GH_TOKEN: ${{ secrets.BOT_PERSONAL_ACCESS_TOKEN }}
if: ${{ startsWith(github.event.comment.body, '/release') }}
steps:
- uses: actions/checkout@v4
- name: Get current versionCode from libs.versions.toml
run: |
VERSION_CODE=$(grep 'versionCode' ./gradle/libs.versions.toml | awk -F '"' '{print $2}')
NEW_VERSION_CODE=$((VERSION_CODE + 1))
echo "VERSION_CODE=$VERSION_CODE" >> $GITHUB_ENV
echo "NEW_VERSION_CODE=$NEW_VERSION_CODE" >> $GITHUB_ENV
- name: Get current versionName from libs.versions.toml
run: |
VERSION_NAME=$(grep 'versionName' ./gradle/libs.versions.toml | awk -F '"' '{print $2}')
MAJOR=$(echo $VERSION_NAME | cut -d'.' -f1)
MINOR=$(echo $VERSION_NAME | cut -d'.' -f2)
NEW_VERSION_NAME="$MAJOR.$((MINOR + 1))"
echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_ENV
echo "NEW_VERSION_NAME=$NEW_VERSION_NAME" >> $GITHUB_ENV
- name: Create and checkout release branch
run: |
git checkout -b release/$NEW_VERSION_NAME
- name: Update versionCode/versionName in libs.versions.toml
run: |
sed -i "s/versionCode = \"$VERSION_CODE\"/versionCode = \"$NEW_VERSION_CODE\"/" ./gradle/libs.versions.toml
sed -i "s/versionName = \"$VERSION_NAME\"/versionName = \"$NEW_VERSION_NAME\"/" ./gradle/libs.versions.toml
- name: Commit and push updated version
run: |
git config --local user.name "GitHub Action"
git config --local user.email "actions@github.com"
git add ./gradle/libs.versions.toml
git commit -m "chore: Bump version to $NEW_VERSION_NAME ($NEW_VERSION_CODE)"
git push --set-upstream origin release/$NEW_VERSION_NAME
- name: Get the lowest open Milestone number
run: |
LOWEST_MILESTONE_TITLE=$(gh api repos/${{ github.repository }}/milestones?state=open --jq '. | sort_by(.number) | .[0] .title')
echo "LOWEST_MILESTONE_TITLE=$LOWEST_MILESTONE_TITLE" >> $GITHUB_ENV
- name: Create Pull Request to Master
run: |
PR_TITLE="Merge release/${NEW_VERSION_NAME} into master"
gh pr create \
--base master \
--head release/"$NEW_VERSION_NAME" \
--title "$PR_TITLE" \
--body-file ./.github/workflows/merge_release_into_master_template.md \
--label "maintenance" \
--milestone "$LOWEST_MILESTONE_TITLE"
- name: Create Pull Request to Develop
run: |
PR_TITLE="Merge release/${NEW_VERSION_NAME} into develop"
gh pr create \
--base develop \
--head release/"$NEW_VERSION_NAME" \
--title "$PR_TITLE" \
--body-file ./.github/workflows/merge_release_into_develop_template.md \
--label "maintenance" \
--milestone "$LOWEST_MILESTONE_TITLE"
本番リリースを作成 & Firebase App Distributionにデプロイ & Google Playにデプロイ
ワークフローファイル
name: on-merge-into-master
on:
push:
branches:
- master
jobs:
generate-aab:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4.7.0
with:
distribution: 'temurin'
java-version: 17
architecture: x64
cache: gradle
- name: Get Version Name
id: extract_version
run: |
VERSION_NAME=$(grep 'versionName ' gradle/libs.versions.toml | cut -d '"' -f 2)
echo "Version Name is $VERSION_NAME"
echo "version_name=${VERSION_NAME}" >> $GITHUB_OUTPUT
- name: Decode Keystore
run: echo ${{ secrets.KEYSTORE_BASE64 }} | base64 --decode > ./app/release.keystore
- name: Generate AAB
run: ./gradlew :app:bundleProdRelease
env:
RELEASE_KEYSTORE_STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
RELEASE_KEYSTORE_KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
RELEASE_KEYSTORE_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
- name: Temporaly uploading for other steps
uses: actions/upload-artifact@v4
with:
name: aabProdRelease
path: app/build/outputs/bundle/prodRelease
retention-days: 1
outputs:
version_name: ${{ steps.extract_version.outputs.version_name }}
create-release:
needs: generate-aab
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION_NAME: ${{needs.generate-aab.outputs.version_name}}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download signed aab
uses: actions/download-artifact@v4
with:
name: aabProdRelease
- name: Create Tag And Release
run: |
git tag $VERSION_NAME master
git push origin $VERSION_NAME
gh release create ${VERSION_NAME} --title Release_${VERSION_NAME} --generate-notes ./app-prod-release.aab
# Firebase App Distribution にデプロイ
distribute-release:
needs: [generate-aab, create-release]
runs-on: ubuntu-latest
env:
VERSION_NAME: ${{needs.generate-aab.outputs.version_name}}
steps:
- uses: actions/checkout@v4
- name: Download signed aab
uses: actions/download-artifact@v4
with:
name: aabProdRelease
- name: title into release notes.
run: |
message="message for release"
echo -e "$message" >> ./.github/workflows/release_notes.txt
- name: Upload apk to Firebase Distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1
with:
appId: ${{secrets.RELEASE_FIREBASE_APP_ID}}
token: ${{secrets.FIREBASE_TOKEN}}
serviceCredentialsFile: path/to/google-services.json
groups: android_develop_testers,android_testers
file: ./app-prod-release.aab
releaseNotesFile: ./.github/workflows/release_notes.txt
# google play に内部テストとしてデプロイ
upload-google-play:
needs: generate-aab
runs-on: ubuntu-latest
env:
VERSION_NAME: ${{needs.generate-aab.outputs.version_name}}
steps:
- uses: actions/checkout@v4
- name: Download signed aab
uses: actions/download-artifact@v4
with:
name: aabProdRelease
- name: Create service_account.json
run: echo '${{ secrets.PLAY_PUBLISH_SERVICE_ACCOUNT_JSON }}' > service_account.json
- name: Upload aab to Play Console
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJson: service_account.json
packageName: <package.name>
releaseFiles: ./app-prod-release.aab
releaseName: Release_${{ env.VERSION_NAME }}
status: draft
track: internal
まとめ
自動化を導入することで、開発チーム全体の生産性を向上させることができます。特に、レビュー依頼やリリース作業の自動化によって、開発者がより価値のある作業に集中できるようになります。今後も継続的に自動化を進め、さらなる効率化を目指していきます。
参考文献

株式会社 カラビナテクノロジーは「命綱や支点を素早く確実に繋ぐカラビナ。そんなカラビナのような役割をテクノロジーで実現したい」という想いのもと、福岡で設立。 主にシステム開発・アプリ開発・ Webサイト制作を行っています。採用情報→karabiner.tech/career
Discussion