Skip to content

ci: CI にテスト・型チェック・ビルドを追加 - #85

Merged
susumutomita merged 1 commit into
mainfrom
ci/add-test-typecheck-build
Nov 30, 2025
Merged

susumutomita merged 1 commit into
mainfrom
ci/add-test-typecheck-build

Conversation

@susumutomita

@susumutomita susumutomita commented Nov 30, 2025 •

Copy link
Copy Markdown
Owner

概要

CI ワークフローにテスト、型チェック、ビルドを追加し、make before-commit と同等のチェックを実行するようにした。

変更内容

CI ワークフロー

  • make typecheck を追加(全フロントエンドアプリの型チェック)
  • make test を追加(カバレッジ付きテスト)
  • make build を追加(ビルド確認)
  • install_ci を拡張して全フロントエンドアプリとバックエンドサービスの依存関係をインストール

Keycloak / tenant-management-service

  • tenant-management-service クライアントを追加
  • serviceAccountsEnabled を有効化
  • realm-admin 権限を付与
  • docker-compose に Keycloak 環境変数を追加

テスト

  • CI で typecheck, test, build が実行されることを確認

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Enhanced CI/CD pipeline with type checking, automated testing, and build validation steps to ensure code quality.
    • Streamlined dependency installation process for multi-application environments.
    • Updated infrastructure configuration to support enhanced service account management and authorization capabilities.

✏️ Tip: You can customize this high-level summary in your review settings.

CI ワークフロー:
- make typecheck を追加
- make test(カバレッジ付き)を追加
- make build を追加
- install_ci を拡張して全アプリの依存関係をインストール

Keycloak / tenant-management-service:
- tenant-management-service クライアントを追加
- serviceAccountsEnabled を有効化
- realm-admin 権限を付与
- docker-compose に Keycloak 環境変数を追加

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 30, 2025 •

Copy link
Copy Markdown

Walkthrough

The pull request updates CI/CD infrastructure and tenant-management provisioning. It adds type checking, testing, and build steps to the CI workflow; refactors the Makefile's install_ci target to iterate over multiple frontend apps and install problem-management dependencies; adds Keycloak environment variables to the tenant-management service; and extends the Keycloak setup script to enable service accounts and grant realm-admin role to the tenant-management-service.

Changes

Cohort / File(s) Summary
CI/CD Pipeline Configuration
.github/workflows/ci.yml
Adds three new steps to the ci job: Type check, Run tests, and Build; positioned after Format check and before docker-build job.
Build Tooling
Makefile
Refactors install_ci target to iterate over FRONTEND_APPS with per-app installation and CI flags; adds installation of PROBLEM_MANAGEMENT_DIR; includes per-app progress messages and error handling.
Docker Infrastructure
docker-compose.yml
Adds three environment variables to tenant-management service: KEYCLOAK_URL, KEYCLOAK_CLIENT_ID, and KEYCLOAK_CLIENT_SECRET.
Keycloak Provisioning
infrastructure/docker/keycloak/scripts/setup-keycloak.sh
Enables service accounts in client creation payload; adds tenant-management-service admin role grant flow including client UUID retrieval, service-account-user lookup, realm-management client retrieval, and role-mapping assignment; logic duplicated in two locations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Keycloak setup script duplication: The tenant-management-service provisioning and role assignment logic is duplicated in two places; verify this is intentional and maintainable.
  • Makefile loop logic: Confirm the loop over FRONTEND_APPS properly handles all applications and failure cases.
  • Environment variable completeness: Verify the three Keycloak env vars added to docker-compose are sufficient for tenant-management service authentication flow.

Possibly related PRs

Poem

🐰 New steps bloom in CI's garden bed,
Apps install in loop, no longer spread,
Keycloak grants roles with service accounts true,
Infrastructure grows, with each config anew! 🌱

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title in Japanese translates to 'ci: Add test, type-check, and build to CI' which accurately summarizes the main changes—adding three new workflow steps (test, type-check, build) to the CI pipeline.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ci/add-test-typecheck-build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@susumutomita
susumutomita enabled auto-merge (squash) November 30, 2025 22:05
@claude

claude Bot commented Nov 30, 2025

Copy link
Copy Markdown

コードレビュー

このPRでは、CIワークフローに typecheck、test、build を追加し、make before-commit と同等のチェックを実現しています。また、Keycloak に tenant-management-service クライアントを追加し、realm-admin 権限を付与する実装も含まれています。

✅ 良い点

  1. CI の強化: 型チェック、テスト、ビルドを CI に追加し、品質保証を強化している
  2. 一貫性: make before-commit と CI の検証内容を統一している
  3. プロダクションレディ: Keycloak の Service Account を有効化し、適切な権限を付与している
  4. 環境変数の追加: docker-compose.yml に tenant-management-service の Keycloak 接続情報を追加している

🔍 改善提案

1. セキュリティ: ハードコードされたシークレット

重大度: 高

docker-compose.yml と setup-keycloak.sh で KEYCLOAK_CLIENT_SECRET=secret がハードコードされています。

docker-compose.yml:105

  • KEYCLOAK_CLIENT_SECRET=secret

setup-keycloak.sh:138
create_client "tenant-management-service" "http://localhost:3004/*" "http://localhost:3004" "secret"

推奨事項:

  • 環境変数から読み取るようにする
  • 本番環境では強力なシークレットを生成する
  • ドキュメントに警告を追加する

2. エラーハンドリング: Keycloak セットアップの失敗処理

重大度: 中

setup-keycloak.sh の権限付与セクション(142-174行目)で、エラーが発生した場合のハンドリングが不十分です。

問題点:

  • CLIENT_UUID が取得できない場合、警告のみで続行する
  • SERVICE_ACCOUNT_USER_ID が取得できない場合も同様
  • これらの失敗は致命的なのに、スクリプトは成功として扱われる

推奨事項:
重要な処理が失敗した場合は exit 1 する、または失敗カウンターを導入し、最後に総合判定する

3. Makefile: install_ci の改善

重大度: 低

Makefile:54-55 で PROBLEM_MANAGEMENT_DIR のインストールのみを実行していますが、将来的に他のバックエンドサービスが追加された場合、修正が必要になります。

推奨事項:
フロントエンドと同様にループ構造を使用する、または BACKEND_SERVICES 変数を定義して拡張性を確保する

4. CI ワークフロー: テスト結果の可視化

重大度: 低

テストとカバレッジを実行していますが、結果をアーティファクトとして保存していません。

推奨事項:

  • カバレッジレポートを GitHub Actions のアーティファクトとして保存
  • PR コメントにカバレッジサマリーを追加する

5. ドキュメント: 変更内容の説明不足

重大度: 低

PR 本文に「tenant-management-service クライアントを追加」とありますが、なぜこのクライアントが必要なのか、どのように使用されるのかの説明がありません。

推奨事項:
README または docs に tenant-management-service のアーキテクチャ説明を追加

🧪 テストカバレッジ

CLAUDE.md によると、カバレッジ 100% が必須条件ですが、このPRではテストコードの追加がありません。

確認事項:

  • 既存のテストが tenant-management-service の新機能をカバーしているか
  • Keycloak 統合のテストが存在するか

📋 チェックリスト(CLAUDE.md の 2.7 より)

  • モックデータやスタブが残っていないか
  • すべての API が実際のデータベースに接続しているか ※ 本PRはインフラ変更のみ
  • 認証・認可チェックがすべてのエンドポイントにあるか ※ 権限付与を実装
  • バリデーションがサーバーサイドで実装されているか ※ 本PRは該当なし
  • エラーハンドリングが適切に実装されているか ⚠️ 改善余地あり(上記2参照)
  • ログが適切に記録されているか ※ 本PRは該当なし
  • テストカバレッジが 100% か ⚠️ 確認必要
  • 型エラーがないか(TypeScript strict mode)
  • セキュリティ脆弱性がないか(OWASP Top 10)⚠️ ハードコードシークレット(上記1参照)

🎯 総評

CI の強化とKeycloak の Service Account 設定は正しい方向性です。ただし、以下の点に対応することを推奨します:

  1. 必須: ハードコードされたシークレットを環境変数化する(セキュリティ)
  2. 推奨: Keycloak セットアップのエラーハンドリングを改善する(信頼性)
  3. 任意: Makefile の拡張性を向上させる(保守性)

CI が Green になることを確認後、上記の修正を行うことをお勧めします。


🤖 Generated with Claude Code

@susumutomita
susumutomita merged commit f0a3320 into main Nov 30, 2025
7 of 8 checks passed
@susumutomita
susumutomita deleted the ci/add-test-typecheck-build branch November 30, 2025 22:07

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 882f954 and 13b3c02.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml (1 hunks)
  • Makefile (1 hunks)
  • docker-compose.yml (1 hunks)
  • infrastructure/docker/keycloak/scripts/setup-keycloak.sh (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: susumutomita/TenkaCloud PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T06:19:22.178Z
Learning: テスト実行前に make before-commit コマンドを実行し、lint_text、format_check、typecheck、test、build がすべて成功することを確認する。
📚 Learning: 2025-11-30T06:19:22.178Z
Learnt from: CR
Repo: susumutomita/TenkaCloud PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T06:19:22.178Z
Learning: テスト実行前に make before-commit コマンドを実行し、lint_text、format_check、typecheck、test、build がすべて成功することを確認する。

Applied to files:

  • Makefile
  • .github/workflows/ci.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: claude-review
  • GitHub Check: ci
🔇 Additional comments (3)
Makefile (1)

50-56: LGTM! The install_ci refactoring correctly iterates over all frontend apps and backend services with proper error handling and supply chain security flags.

.github/workflows/ci.yml (1)

53-63: LGTM! The three new CI steps (Type check, Run tests, Build) are logically sequenced after Format check and correctly invoke the Makefile targets. This mirrors the local make before_commit workflow, ensuring consistency between developer machines and CI. Per learnings, this validates the full CI pipeline as specified.

infrastructure/docker/keycloak/scripts/setup-keycloak.sh (1)

116-116: LGTM! Adding serviceAccountsEnabled: true correctly enables service accounts for clients, which is necessary for the tenant-management-service to authenticate with Keycloak using credentials.

Comment thread docker-compose.yml
Comment on lines +103 to +105
- KEYCLOAK_URL=http://keycloak:8080
- KEYCLOAK_CLIENT_ID=tenant-management-service
- KEYCLOAK_CLIENT_SECRET=secret

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Harden Keycloak credentials against plaintext exposure. The hardcoded KEYCLOAK_CLIENT_SECRET=secret should be environment-variable based, consistent with other services in the compose file (e.g., AUTH_SECRET, AUTH_KEYCLOAK_SECRET). Even for local development, storing credentials as environment variables improves separation of concerns and prevents accidental commits of real secrets.

Apply this diff to externalize the credentials:

     environment:
       - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/tenkacloud?schema=public
       - KEYCLOAK_URL=http://keycloak:8080
-      - KEYCLOAK_CLIENT_ID=tenant-management-service
-      - KEYCLOAK_CLIENT_SECRET=secret
+      - KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-tenant-management-service}
+      - KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET:-secret}

Alternatively, if these values should always be fixed for local development, consider documenting this decision in a comment explaining the security implications.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- KEYCLOAK_URL=http://keycloak:8080
- KEYCLOAK_CLIENT_ID=tenant-management-service
- KEYCLOAK_CLIENT_SECRET=secret
- KEYCLOAK_URL=http://keycloak:8080
- KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-tenant-management-service}
- KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET:-secret}
🤖 Prompt for AI Agents
In docker-compose.yml around lines 103 to 105, the Keycloak client secret is
hardcoded as KEYCLOAK_CLIENT_SECRET=secret; replace it with an
environment-variable reference (for example use the existing
AUTH_KEYCLOAK_SECRET or a new KEYCLOAK_CLIENT_SECRET env var) and optionally
provide a safe local default expansion for development. Update the service's
environment list to pull the secret from the external env (and document the
chosen behavior in a short comment if you decide the value should remain fixed
for local dev) so credentials are not stored in plaintext in the compose file.

Comment on lines 135 to +138
create_client "control-plane-ui" "http://localhost:3000/*" "http://localhost:3000" "secret"
create_client "admin-app" "http://localhost:3001/*" "http://localhost:3001" "secret"
create_client "participant-app" "http://localhost:3002/*" "http://localhost:3002" "secret"
create_client "tenant-management-service" "http://localhost:3004/*" "http://localhost:3004" "secret"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use environment variables for tenant-management credentials. The script now receives KEYCLOAK_CLIENT_ID and KEYCLOAK_CLIENT_SECRET from docker-compose.yml but continues to hardcode "tenant-management-service" and "secret". This creates a security inconsistency and prevents flexible credential management. Leverage the environment variables to align with the broader setup.

Apply this diff to use environment variables:

 # クライアント作成 (Secret は 'secret' で統一)
 create_client "control-plane-ui" "http://localhost:3000/*" "http://localhost:3000" "secret"
 create_client "admin-app" "http://localhost:3001/*" "http://localhost:3001" "secret"
 create_client "participant-app" "http://localhost:3002/*" "http://localhost:3002" "secret"
-create_client "tenant-management-service" "http://localhost:3004/*" "http://localhost:3004" "secret"
+TENANT_MGMT_CLIENT_ID="${KEYCLOAK_CLIENT_ID:-tenant-management-service}"
+TENANT_MGMT_CLIENT_SECRET="${KEYCLOAK_CLIENT_SECRET:-secret}"
+create_client "${TENANT_MGMT_CLIENT_ID}" "http://localhost:3004/*" "http://localhost:3004" "${TENANT_MGMT_CLIENT_SECRET}"

Then update the admin-grant flow to use the variable:

-CLIENT_UUID=$(curl -s "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients?clientId=tenant-management-service" \
+CLIENT_UUID=$(curl -s "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients?clientId=${TENANT_MGMT_CLIENT_ID}" \
   -H "Authorization: Bearer ${ACCESS_TOKEN}" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
in infrastructure/docker/keycloak/scripts/setup-keycloak.sh around lines
135-138, the client creation call hardcodes the tenant-management service id and
secret; replace the literal "tenant-management-service" and "secret" with the
environment variables KEYCLOAK_CLIENT_ID and KEYCLOAK_CLIENT_SECRET (use the
shell variable expansions already provided by docker-compose) so the client is
created with the configured values, and update the subsequent admin-grant flow
in this script to reference the same KEYCLOAK_CLIENT_ID variable instead of the
hardcoded name so both creation and grant steps use the injected credentials
consistently.

create_client "control-plane-ui" "http://localhost:3000/*" "http://localhost:3000" "secret"
create_client "admin-app" "http://localhost:3001/*" "http://localhost:3001" "secret"
create_client "participant-app" "http://localhost:3002/*" "http://localhost:3002" "secret"
create_client "tenant-management-service" "http://localhost:3004/*" "http://localhost:3004" "secret"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling and avoid silent failures in the admin-grant flow. The curl operations in lines 145–166 lack error checking. If any network call fails (e.g., malformed response, network timeout), the script continues silently. With set -e only catching non-zero exit codes, failed command substitutions $(...) won't halt execution.

Apply this diff to add explicit error handling:

 # Get Client UUID
-CLIENT_UUID=$(curl -s "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients?clientId=tenant-management-service" \
-  -H "Authorization: Bearer ${ACCESS_TOKEN}" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
+CLIENT_UUID=$(curl -s -f "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients?clientId=tenant-management-service" \
+  -H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
 
 if [ -n "$CLIENT_UUID" ]; then
   # Get Service Account User ID
-  SERVICE_ACCOUNT_USER_ID=$(curl -s "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${CLIENT_UUID}/service-account-user" \
-    -H "Authorization: Bearer ${ACCESS_TOKEN}" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
+  SERVICE_ACCOUNT_USER_ID=$(curl -s -f "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${CLIENT_UUID}/service-account-user" \
+    -H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
 
   if [ -n "$SERVICE_ACCOUNT_USER_ID" ]; then
     # Get realm-management Client UUID
-    REALM_MANAGEMENT_ID=$(curl -s "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients?clientId=realm-management" \
-      -H "Authorization: Bearer ${ACCESS_TOKEN}" | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
+    REALM_MANAGEMENT_ID=$(curl -s -f "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients?clientId=realm-management" \
+      -H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null | grep -o '"id":"[^"]*' | head -1 | sed 's/"id":"//')
 
     # Get realm-admin Role info
-    ROLE_INFO=$(curl -s "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${REALM_MANAGEMENT_ID}/roles/realm-admin" \
-      -H "Authorization: Bearer ${ACCESS_TOKEN}")
+    ROLE_INFO=$(curl -s -f "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients/${REALM_MANAGEMENT_ID}/roles/realm-admin" \
+      -H "Authorization: Bearer ${ACCESS_TOKEN}" 2>/dev/null)
 
     # Assign role
-    curl -s -X POST "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users/${SERVICE_ACCOUNT_USER_ID}/role-mappings/clients/${REALM_MANAGEMENT_ID}" \
+    curl -s -f -X POST "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users/${SERVICE_ACCOUNT_USER_ID}/role-mappings/clients/${REALM_MANAGEMENT_ID}" \
       -H "Authorization: Bearer ${ACCESS_TOKEN}" \
       -H "Content-Type: application/json" \
       -d "[${ROLE_INFO}]" > /dev/null

The -f flag causes curl to exit with status 22 on HTTP errors (4xx/5xx), and the script's set -e will then halt execution on failure.

Also applies to: 142-174

🤖 Prompt for AI Agents
in infrastructure/docker/keycloak/scripts/setup-keycloak.sh around lines 138 and
142-174, the admin-grant curl calls currently perform command substitutions
without checking for HTTP or network errors, which can lead to silent failures;
modify each curl invocation to use the -f flag (or test HTTP status explicitly)
and check return values of command substitutions, e.g., capture responses/status
in variables and test for non-empty/expected fields and non-zero curl exit
codes, and fail fast (exit 1) with an explanatory error message when a call
fails so set -e will reliably stop the script on HTTP/network errors.

susumutomita added a commit that referenced this pull request Jun 18, 2026
…(ADR-037 Slice 3) (#1901)

Picks up TenkaCloudChallenge #85 (708705f): SCHEMA.json now allows
disruptions[].recurrence, so a problem can declare a score-gated recurring
disruption past `make validate-problems`. Completes the ADR-037 Slice 3 pairing
with the platform engine (PR-1900). Additive; all existing problems still validate.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant