🛠️
個人開発に Android Lint と Dependabot を入れて得た効果
はじめに
個人開発でも 品質と保守性を自動で守る仕組み を先に用意しておくと、実装に集中できてモチベーションを落としにくくなります。本稿では、個人アプリ VtuberCamera に導入した Android Lint と Dependabot の設定と運用、導入効果をまとめます。
導入背景
- ライブラリ更新や警告チェックを 手作業で追いかけ続けるのは負担が大きい。自動化して抜け漏れを防ぎたい。
- 個人開発は「とりあえず動けばOK」で放置しがち。最低限の品質ゲートをつくりたい。
- 依存関係は放置するとすぐ陳腐化し、最悪ビルド不能になる。早めの小さな更新を習慣化したい。
- YAML を 1 ファイル置くだけで GitHub Actions が動作するため、初期コストが低い。
Android Lint — PR にコメントで返す仕組み
PR 作成/更新時に ./gradlew lintDebug を実行し、結果 XML を Python スクリプトでエラーフォーマット(efm)に変換して reviewdog で PR コメントとして通知します。ビルドは失敗させず(-fail-on-error=false)、まずは 気づけることを最優先 にしています。
lint
name: Android Lint Review
on:
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
jobs:
lint:
runs-on: ubuntu-24.04
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Run Android Lint
run: ./gradlew lintDebug || true
- name: Install reviewdog
uses: reviewdog/action-setup@v1
with:
reviewdog_version: latest
- name: Convert Android Lint XML to efm lines
if: always()
run: |
set -euo pipefail
python3 <<'PY' > lint.out
import glob, xml.etree.ElementTree as ET, sys
files = glob.glob("**/lint-results-*.xml", recursive=True)
if not files:
sys.exit(0)
sev_map = {"Error":"e", "Fatal":"e", "Warning":"w", "Information":"i", "Informational":"i", "Ignore":"i"}
for xml_path in files:
try:
root = ET.parse(xml_path).getroot()
except Exception as e:
print(f"# failed to parse {xml_path}: {e}", file=sys.stderr)
continue
for issue in root.findall("issue"):
iid = issue.get("id","UNKNOWN")
msg = issue.get("message","").replace("\n"," ").strip()
sev = sev_map.get(issue.get("severity","Warning"), "w")
locs = issue.findall("location")
if not locs:
f = issue.get("file", xml_path)
ln = issue.get("line","1")
print(f"{f}:{ln}: {sev}: [{iid}] {msg}")
continue
for loc in locs:
f = loc.get("file", xml_path)
ln = loc.get("line","1")
col = loc.get("column")
if col:
print(f"{f}:{ln}:{col}: {sev}: [{iid}] {msg}")
else:
print(f"{f}:{ln}: {sev}: [{iid}] {msg}")
PY
wc -l lint.out || true
- name: Report lint results with reviewdog
if: always()
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
reviewdog \
-efm="%f:%l:%c: %t: %m" \
-efm="%f:%l: %t: %m" \
-name="Android Lint" \
-reporter=github-pr-review \
-level=warning \
-fail-on-error=false \
< lint.out
この設計のポイント
- コメント駆動の修正:PR 上で指摘箇所が即座に見える。
- 開発を止めない:まずは警告レベルで回し、しきい値や必須化は後から段階的に。
-
ノイズ制御:必要になったら
lint.xmlでルールの有効/無効や severity を調整。

PR 上で Android Lint の結果が reviewdog によってコメントとして表示される様子
Dependabot — 依存更新を「まとめて、計画的に」
Gradle と GitHub Actions の 2 系列を毎週月曜にチェック。関連ライブラリをグループ化して 1 本の PR に束ね、レビューと検証のコストを抑えています。破壊的変更が入りやすいものは ignore でメジャー更新を除外。
dependabot
version: 2
updates:
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "09:00"
timezone: "Asia/Tokyo"
groups:
androidx:
patterns: ["androidx.*"]
exclude-patterns: ["androidx.compose.*"]
compose:
patterns: ["androidx.compose.*", "*compose*"]
camerax:
patterns: ["androidx.camera:*", "*camera*"]
navigation:
patterns: ["androidx.navigation:*"]
kotlin:
patterns: ["org.jetbrains.kotlin*", "*kotlin*"]
material:
patterns: ["com.google.android.material:*", "*material*"]
ignore:
- dependency-name: "com.android.tools.build:gradle"
update-types: ["version-update:semver-major"]
- dependency-name: "androidx.compose.compiler:compiler"
update-types: ["version-update:semver-major"]
- dependency-name: "androidx.core:core-ktx"
update-types: ["version-update:semver-major"]
open-pull-requests-limit: 8
labels: ["dependencies","android","vtuberCamera"]
assignees: ["your_account_name"]
commit-message:
prefix: "deps"
prefix-development: "deps-dev"
include: "scope"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "10:00"
timezone: "Asia/Tokyo"
labels: ["github-actions","ci-cd"]
assignees: [""]
open-pull-requests-limit: 3
commit-message:
prefix: "ci"
include: "scope"
// CODEOWNERS
# vtuberCamera プロジェクトのコードオーナー設定
# このファイルは、プルリクエストの自動レビュアー割り当てに使用されます
# Dependabotのプルリクエストもこの設定に基づいてレビュアーが割り当てられます
# デフォルト: すべてのファイルのオーナー
* @your_account_name
# Android関連の依存関係とビルド設定
*.gradle @your_account_name
gradle.properties @your_account_name
build.gradle.kts @your_account_name
app/build.gradle.kts @your_account_name
# GitHub Actions設定
.github/ @your_account_name
# プロジェクト設定ファイル
*.xml @your_account_name
*.yml @your_account_name
*.yaml @your_account_name
*.json @your_account_name
# Kotlin/Javaソースコード
*.kt @your_account_name
*.java @your_account_name
# リソースファイル
app/src/main/res/ @your_account_name
# マニフェストファイル
app/src/main/AndroidManifest.xml @your_account_name
この設計のポイント
- グループ化で PR 氾濫を抑制:AndroidX、Compose、CameraX、Navigation、Kotlin、Material を用途別に分離。
-
壊れやすいところは除外:AGP、Compose Compiler、
core-ktxのメジャー更新は手動確認。 - 定期運用:毎週月曜の朝に Gradle、10:00 に Actions をチェック。習慣化して「やり忘れゼロ」。
YAML は AI に 8:2 で作ってもらった
初期ドラフトは AI に生成してもらい、リポジトリ事情に合わせて最小の手直しだけを実施。Dependabot は一発で稼働、Lint は reviewdog のエラーハンドリングを含めた再生成で安定運用に到達しました。
導入して得られた効果
- 品質の底上げ:警告や潜在バグに即気づける。
- 保守の安心感:小さな更新を積み重ね、ビルド崩壊や大規模追従を避けられる。
- 集中力の維持:定型作業を自動化し、機能開発に時間を使える。
- モチベーションの維持:週次で PR が届き、進捗が可視化されることで「前に進んでいる感」を得やすい。
今後の展望
- Lint のしきい値を段階的に引き上げ、将来的にはエラーでビルド失敗へ。
- 変更影響が大きい依存(Kotlin、AGPなど)の検証手順テンプレート化。
-
lint.xmlとbaselineの整備でノイズをさらに削減。
個人開発こそ、自動化で"守り"を固めて"攻め"に集中する 価値があります。同じ悩みを持つ方の参考になれば幸いです。
関連記事
この記事で触れたVtuberCameraの開発記録です。AIツールを取り入れながらリビルドした2週間の体験談。
Copilotを業務で使って得た教訓。Android開発での失敗談と学び。
Discussion