fix(observability): preserve exporter capability and map phase logs #77
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Comment Commands | ||
| # Issue comment 経由で発火するコマンド群を 1 ワークフローに集約。 | ||
| # 1 コメントごとに作られる workflow run の数を 4 -> 1 に減らし、 | ||
| # Actions ダッシュボードに大量の Skipped run が並ぶ現象を抑える。 | ||
| # | ||
| # 各 job は独自の `if` で対象コマンドと著者条件を判定し、 | ||
| # 該当しないコメントでは即座に Skipped になる。 | ||
| on: | ||
| issue_comment: | ||
| types: [created] | ||
| jobs: | ||
| # ===================================================================== | ||
| # /review — TAKT PR Review (formerly takt-review.yml) | ||
| # ===================================================================== | ||
| review: | ||
| if: | | ||
| github.event.issue.pull_request != null && | ||
| ( | ||
| github.event.comment.body == '/review' || | ||
| startsWith(github.event.comment.body, '/review ') || | ||
| startsWith(github.event.comment.body, '/review | ||
| ') | ||
| ) && | ||
| contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) | ||
| concurrency: | ||
| group: comment-cmd-review-${{ github.event.issue.number }} | ||
| cancel-in-progress: true | ||
| runs-on: ubuntu-latest | ||
| environment: takt-review | ||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| steps: | ||
| - name: 受付リアクション | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| await github.rest.reactions.createForIssueComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| comment_id: context.payload.comment.id, | ||
| content: 'eyes', | ||
| }); | ||
| - name: PR の head SHA を取得 | ||
| id: pr | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| result-encoding: string | ||
| script: | | ||
| const { data: pr } = await github.rest.pulls.get({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| pull_number: context.issue.number, | ||
| }); | ||
| return pr.head.sha; | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ steps.pr.outputs.result }} | ||
| fetch-depth: 0 | ||
| - name: API キー確認 | ||
| run: | | ||
| if [ -z "$ANTHROPIC_API_KEY" ]; then | ||
| echo "::error::ANTHROPIC_API_KEY is not set" | ||
| exit 1 | ||
| fi | ||
| env: | ||
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 20 | ||
| - name: Claude Code & TAKT インストール | ||
| run: | | ||
| npm install -g @anthropic-ai/claude-code | ||
| npm install -g takt | ||
| - name: TAKT Review 実行 | ||
| run: takt --pipeline --skip-git --pr ${{ github.event.issue.number }} -w review-takt-default | ||
| env: | ||
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GH_REPO: ${{ github.repository }} | ||
| - name: レビュー結果をPRコメントに投稿 | ||
| if: always() | ||
| run: | | ||
| REPORT_DIR=$(ls -td .takt/runs/*/reports 2>/dev/null | head -1) | ||
| if [ -n "$REPORT_DIR" ]; then | ||
| SUMMARY=$(find "$REPORT_DIR" -name "*review-summary*" -type f | head -1) | ||
| if [ -n "$SUMMARY" ]; then | ||
| gh pr comment ${{ github.event.issue.number }} --body-file "$SUMMARY" | ||
| else | ||
| echo "レビューサマリーが見つかりません" | ||
| fi | ||
| else | ||
| echo "レポートディレクトリが見つかりません" | ||
| fi | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GH_REPO: ${{ github.repository }} | ||
| - name: レビューレポートをアーティファクトに保存 | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: takt-review-reports | ||
| path: .takt/runs/*/reports/ | ||
| if-no-files-found: ignore | ||
| # ===================================================================== | ||
| # /resolve — CC Resolve (formerly cc-resolve.yml) | ||
| # ===================================================================== | ||
| resolve: | ||
| if: | | ||
| github.event.issue.pull_request && | ||
| contains(github.event.comment.body, '/resolve') && | ||
| github.event.comment.author_association == 'OWNER' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| actions: write | ||
| contents: write | ||
| pull-requests: write | ||
| steps: | ||
| - name: Acknowledge | ||
| run: | | ||
| gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \ | ||
| -f content=rocket | ||
| gh pr comment ${{ github.event.issue.number }} --repo ${{ github.repository }} \ | ||
| --body "🚀 cc-resolve started: [View logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Check if fork PR | ||
| id: pr | ||
| run: | | ||
| PR_REPO=$(gh pr view ${{ github.event.issue.number }} --repo ${{ github.repository }} \ | ||
| --json headRepositoryOwner,headRepository \ | ||
| --jq '"\(.headRepositoryOwner.login)/\(.headRepository.name)"') | ||
| BRANCH=$(gh pr view ${{ github.event.issue.number }} --repo ${{ github.repository }} \ | ||
| --json headRefName -q .headRefName) | ||
| echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT" | ||
| if [ "$PR_REPO" != "${{ github.repository }}" ]; then | ||
| echo "::error::Fork PR はサポートしていません。contributor 側で解決してください。" | ||
| exit 1 | ||
| fi | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ steps.pr.outputs.branch }} | ||
| fetch-depth: 0 | ||
| - name: Configure git | ||
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| - name: Merge main (detect conflicts) | ||
| id: merge | ||
| run: | | ||
| git fetch origin main | ||
| # --no-commit --no-ff: コンフリクトの有無にかかわらず常にマージ状態を保持する | ||
| # これにより最後の git commit が必ずマージコミット(親2つ)を作る | ||
| if git merge --no-commit --no-ff origin/main 2>/dev/null; then | ||
| echo "conflicts=false" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "conflicts=true" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| # コミット済みのコンフリクトマーカーを検出 | ||
| STALE_MARKERS=$(grep -rl '<<<<<<<' --include='*.ts' --include='*.js' --include='*.json' --include='*.yaml' --include='*.yml' --include='*.md' . 2>/dev/null | grep -v node_modules | grep -v .git || echo "") | ||
| if [ -n "$STALE_MARKERS" ]; then | ||
| echo "stale_markers=true" >> "$GITHUB_OUTPUT" | ||
| { | ||
| echo "stale_marker_files<<MARKER_EOF" | ||
| echo "$STALE_MARKERS" | ||
| echo "MARKER_EOF" | ||
| } >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "stale_markers=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 20 | ||
| - name: Install Claude Code | ||
| run: npm install -g @anthropic-ai/claude-code | ||
| - name: Resolve | ||
| run: | | ||
| claude -p --dangerously-skip-permissions "$(cat <<'PROMPT' | ||
| このPRのコンフリクトを解決してください。 | ||
| ## 状況判定 | ||
| まず現在の状態を確認してください。以下の2つをすべてチェックする。 | ||
| 1. `git status` でマージコンフリクト(Unmerged paths)の有無を確認 | ||
| 2. ファイル中にコミット済みのコンフリクトマーカー(`<<<<<<<`)が残っていないか `grep -r '<<<<<<<' --include='*.ts' --include='*.js' --include='*.json' .` で確認 | ||
| **重要**: git status がクリーンでも、ファイル内にコンフリクトマーカーがテキストとしてコミットされている場合がある。必ず grep で確認すること。 | ||
| どちらも該当しなければ「コンフリクトなし」と報告して終了。 | ||
| --- | ||
| ## コンフリクト解決 | ||
| Git merge/rebase/cherry-pick のコンフリクト、およびファイル内に残存するコンフリクトマーカーを、差分分析に基づいて解決する。 | ||
| **原則: 差分を読み、疑い、判断根拠を書いてから解決する。妄信的に片方を採用しない。** | ||
| ### 1. コンフリクト状態を確認する | ||
| ```bash | ||
| git status | ||
| ``` | ||
| - merge / rebase / cherry-pick のどれが進行中か特定する | ||
| - `.git/MERGE_HEAD` があれば merge | ||
| - `.git/rebase-merge/` があれば rebase | ||
| - `.git/CHERRY_PICK_HEAD` があれば cherry-pick | ||
| ### 2. コンテキストを把握する | ||
| 以下を**並列で**実行: | ||
| - `git log --oneline HEAD -5` で HEAD 側(現在のブランチ)の最近の変更を確認 | ||
| - `git log --oneline MERGE_HEAD -5` で取り込み側の最近の変更を確認(merge の場合) | ||
| - 両ブランチの関係性(どちらがベースでどちらが新しいか)を理解する | ||
| ### 3. コンフリクトファイルを列挙する | ||
| ```bash | ||
| git diff --name-only --diff-filter=U | ||
| ``` | ||
| 加えて、コミット済みマーカーがあるファイルも対象に含める: | ||
| ```bash | ||
| grep -rl '<<<<<<<' --include='*.ts' --include='*.js' --include='*.json' . | grep -v node_modules | ||
| ``` | ||
| ファイル数と種類(ソースコード / 設定ファイル / ロックファイル等)を報告する。 | ||
| ### 4. 各ファイルを分析する | ||
| **ここが核心。ファイルごとに以下を必ず実行する。省略しない。** | ||
| 1. ファイル全体を読む(コンフリクトマーカー付きの状態) | ||
| 2. 各コンフリクトブロック(`<<<<<<<` 〜 `>>>>>>>`)について: | ||
| - HEAD 側の内容を具体的に読む | ||
| - theirs 側の内容を具体的に読む | ||
| - 差分が何を意味するか分析する(バージョン番号?リファクタ?機能追加?型変更?) | ||
| - 判断に迷う場合は `git log --oneline -- {file}` で変更履歴を確認する | ||
| 3. **判断を書く**(以下の形式で必ず出力すること): | ||
| ```markdown | ||
| ### ファイル: path/to/file.ts | ||
| #### コンフリクト 1 (L30-45) | ||
| - HEAD 側: {具体的な内容を書く} | ||
| - theirs 側: {具体的な内容を書く} | ||
| - 分析: {差分が何を意味するか} | ||
| - 判断: {HEAD / theirs / 両方統合} を採用({理由}) | ||
| ``` | ||
| **疑うべきポイント:** | ||
| - 「〇〇側が新しいから」だけで判断していないか? HEAD 側に独自の意図ある変更はないか? | ||
| - theirs を採用すると、HEAD 側でしか行っていない作業が消えないか? | ||
| - 両方の変更を統合すべきケースではないか? | ||
| - package-lock.json のような機械生成ファイルでも、バージョンの意味を確認したか? | ||
| ### 5. 解決を実施する | ||
| ステップ4の分析結果に基づいて解決する: | ||
| - 片方採用が明確な場合: `git checkout --ours {file}` / `git checkout --theirs {file}` を使ってよい(**分析済みファイルのみ**) | ||
| - 両方の変更を統合する場合: コンフリクトマーカーを除去し、両方の内容を適切に結合する | ||
| - 解決したファイルを `git add {file}` でマークする | ||
| 解決後、`<<<<<<<` を検索し、マーカーの取り残しがないか確認する。 | ||
| --- | ||
| ## 波及影響確認 | ||
| **コンフリクトを解決しただけでは終わらない。** 対象外ファイルにも影響が出ていないか検証する。 | ||
| - ビルド確認(`npm run build`、`./gradlew build` 等、プロジェクトに応じて) | ||
| - テスト確認(`npm test`、`./gradlew test` 等) | ||
| - 対象外ファイルが、変更と矛盾していないか確認する | ||
| - 例: 関数シグネチャを変更したのに、テストが旧シグネチャを期待している | ||
| - 例: import パスを変更したのに、別ファイルが旧パスを参照している | ||
| 問題が見つかった場合はここで修正する。 | ||
| --- | ||
| ## 結果を報告する | ||
| 全ファイルの解決結果をサマリーテーブルで報告する: | ||
| ```markdown | ||
| ## コンフリクト解決サマリー | ||
| | ファイル | コンフリクト数 | 採用 | 理由 | | ||
| |---------|-------------|------|------| | ||
| | path/to/file.ts | 2 | theirs | リファクタリング済み | | ||
| 波及修正: {対象外ファイルの修正内容。なければ「なし」} | ||
| ビルド: OK / NG | ||
| テスト: OK / NG ({passed}/{total}) | ||
| ``` | ||
| --- | ||
| ## 絶対原則 | ||
| - **差分を読まずに解決しない。** ファイルの中身を確認せずに `--ours` / `--theirs` を適用しない | ||
| - **盲従しない。** HEAD 側に独自の意図がないか必ず疑う | ||
| - **判断根拠を省略しない。** 各コンフリクトに「何が・なぜ・どちらを」の3点を書く | ||
| - **波及を確認する。** 対象外ファイルもビルド・テストで検証する | ||
| ## 禁止事項 | ||
| - 分析なしで `git checkout --ours .` / `git checkout --theirs .` を実行しない | ||
| - 「とりあえず片方」で全ファイルを一括解決しない | ||
| - コンフリクトマーカー (`<<<<<<<`) が残ったままにしない | ||
| - `git merge --abort` を実行しない | ||
| - `git reset` を実行しない(MERGE_HEAD が消えてマージコミットが作れなくなる) | ||
| - `.git/MERGE_HEAD` を保持したまま作業すること | ||
| PROMPT | ||
| )" --verbose | ||
| env: | ||
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Commit and push | ||
| id: push | ||
| run: | | ||
| git add -A | ||
| # MERGE_HEAD があればマージコミット、なければ通常コミット | ||
| if [ -f .git/MERGE_HEAD ]; then | ||
| git commit -m "merge: integrate main into PR branch" | ||
| elif ! git diff --cached --quiet; then | ||
| git commit -m "fix: resolve merge conflicts" | ||
| fi | ||
| AHEAD=$(git rev-list --count origin/${{ steps.pr.outputs.branch }}..HEAD 2>/dev/null || echo "0") | ||
| if [ "$AHEAD" -gt 0 ]; then | ||
| echo "Pushing $AHEAD commit(s)" | ||
| git push | ||
| echo "pushed=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "Nothing to push" | ||
| echo "pushed=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| - name: Trigger CI | ||
| if: steps.push.outputs.pushed == 'true' | ||
| run: | | ||
| gh workflow run ci.yml --ref "${{ steps.pr.outputs.branch }}" | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Report result | ||
| if: always() | ||
| run: | | ||
| PR_NUMBER=${{ github.event.issue.number }} | ||
| RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | ||
| if [ "${{ job.status }}" = "success" ]; then | ||
| gh pr comment "$PR_NUMBER" --repo ${{ github.repository }} --body "✅ cc-resolve completed. [View logs](${RUN_URL})" | ||
| else | ||
| gh pr comment "$PR_NUMBER" --repo ${{ github.repository }} --body "❌ cc-resolve failed. [View logs](${RUN_URL})" | ||
| fi | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| # ===================================================================== | ||
| # /ci — CI Trigger (formerly ci-trigger.yml) | ||
| # ===================================================================== | ||
| ci: | ||
| if: | | ||
| github.event.issue.pull_request && | ||
| contains(github.event.comment.body, '/ci') && | ||
| github.event.comment.author_association == 'OWNER' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| statuses: write | ||
| steps: | ||
| - name: Get PR ref | ||
| id: pr | ||
| run: | | ||
| PR=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} \ | ||
| --jq '{head_ref: .head.ref, head_sha: .head.sha}') | ||
| echo "ref=$(echo "$PR" | jq -r .head_ref)" >> "$GITHUB_OUTPUT" | ||
| echo "sha=$(echo "$PR" | jq -r .head_sha)" >> "$GITHUB_OUTPUT" | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ steps.pr.outputs.ref }} | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 20 | ||
| cache: npm | ||
| - name: Set pending status | ||
| run: | | ||
| for context in "CI / lint" "CI / test" "CI / e2e-mock"; do | ||
| gh api repos/${{ github.repository }}/statuses/${{ steps.pr.outputs.sha }} \ | ||
| -f state=pending -f context="$context" \ | ||
| -f target_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | ||
| done | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - run: npm ci | ||
| - run: npm run build | ||
| - name: Lint | ||
| id: lint | ||
| run: npm run lint | ||
| continue-on-error: true | ||
| - name: Test | ||
| id: test | ||
| run: npm run test | ||
| continue-on-error: true | ||
| - name: E2E Mock | ||
| id: e2e | ||
| run: npm run test:e2e:mock | ||
| continue-on-error: true | ||
| - name: Report statuses | ||
| if: always() | ||
| run: | | ||
| report_status() { | ||
| local context="$1" result="$2" | ||
| local state="success" | ||
| if [ "$result" != "success" ]; then state="failure"; fi | ||
| gh api repos/${{ github.repository }}/statuses/${{ steps.pr.outputs.sha }} \ | ||
| -f state="$state" -f context="$context" \ | ||
| -f target_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" | ||
| } | ||
| report_status "CI / lint" "${{ steps.lint.outcome }}" | ||
| report_status "CI / test" "${{ steps.test.outcome }}" | ||
| report_status "CI / e2e-mock" "${{ steps.e2e.outcome }}" | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Fail if any step failed | ||
| if: always() | ||
| run: | | ||
| if [ "${{ steps.lint.outcome }}" != "success" ] || \ | ||
| [ "${{ steps.test.outcome }}" != "success" ] || \ | ||
| [ "${{ steps.e2e.outcome }}" != "success" ]; then | ||
| exit 1 | ||
| fi | ||
| # ===================================================================== | ||
| # @takt — TAKT Action (formerly takt-action.yml) | ||
| # ===================================================================== | ||
| takt: | ||
| if: | | ||
| contains(github.event.comment.body, '@takt') && | ||
| github.event.comment.author_association == 'OWNER' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| issues: write | ||
| contents: write | ||
| pull-requests: write | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: nrslib/takt-action@main | ||
| with: | ||
| anthropic_api_key: ${{ secrets.TAKT_ANTHROPIC_API_KEY }} | ||
| model: ${{ vars.TAKT_MODEL }} | ||
| github_token: ${{ secrets.GITHUB_TOKEN }} | ||
| log_output: ${{ vars.TAKT_LOG_OUTPUT || 'false' }} | ||
| - name: Notify Slack | ||
| if: always() | ||
| uses: slackapi/slack-github-action@v2.0.0 | ||
| with: | ||
| webhook-type: incoming-webhook | ||
| webhook: ${{ secrets.SLACK_WEBHOOK_URL }} | ||
| payload: | | ||
| { | ||
| "text": "${{ job.status == 'success' && '✅' || '⚠️' }} TAKT Action ${{ job.status }}", | ||
| "blocks": [ | ||
| { | ||
| "type": "section", | ||
| "text": { | ||
| "type": "mrkdwn", | ||
| "text": "*${{ job.status == 'success' && '✅' || '⚠️' }} TAKT Action ${{ job.status }}*\n<${{ github.event.issue.html_url }}|Issue #${{ github.event.issue.number }}>: ${{ github.event.issue.title }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View logs>" | ||
| } | ||
| }, | ||
| { | ||
| "type": "context", | ||
| "elements": [ | ||
| { | ||
| "type": "mrkdwn", | ||
| "text": "Triggered by @${{ github.event.comment.user.login }}" | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||