-
Notifications
You must be signed in to change notification settings - Fork 0
Release: develop を main にマージ #905
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from 64 commits
Commits
Show all changes
75 commits
Select commit
Hold shift + click to select a range
d0ba5d6
Merge pull request #798 from otomatty/main
otomatty e2a1751
fix(mobile): widen bottom-nav item padding and add bottom margin (#799)
otomatty 117d46d
chore(deps): bump the minor-and-patch group with 2 updates (#800)
dependabot[bot] 05357a8
feat(api): add Sentry error capture (#810)
otomatty c9a3696
feat: add api_errors table and service for Sentry error aggregation (…
otomatty e351898
feat: Add Sentry webhook receiver and admin error management API (#812)
otomatty 5bb767e
feat: Add API errors admin page with real-time polling and Sentry int…
otomatty 9237966
feat(api): repository_dispatch + AI analysis callback (#805) (#814)
otomatty 7dae05e
feat: Add Claude AI error analysis workflow (Epic #616 Phase 2) (#815)
otomatty 4252952
feat: implement SSE streaming for real-time API error updates (#816)
otomatty 3375128
feat: implement auto-file GitHub Issue on high/medium severity (Epic …
otomatty 8363b55
feat(api): add email-only notifier for high/medium API errors (#809) …
otomatty 64586ab
Add thumbnail garbage collection on page deletion (#819)
otomatty cfa1452
feat(api): add default note ("マイノート") foundation (#821)
otomatty 49ba374
chore(deps): bump tauri (#822)
dependabot[bot] fe64082
feat(api): migrate personal pages into default note and drop note_pag…
otomatty b30fa8f
Retire /home, introduce /notes/me landing with default note resolutio…
otomatty ff076dd
feat: implement clipUrl hand-off for Chrome extension via /notes/me (…
otomatty c516e0d
feat: add note switcher dropdown to header (issue #827) (#834)
otomatty cd7c22f
feat(frontend): generalize HomePageCount to NotePageCount(noteId) (#8…
otomatty 0d68034
Update Zedi Web Clipper to use /notes/me endpoint (#836)
otomatty d85ae21
feat(frontend): warn before flipping default note to public/unlisted …
otomatty d8fd505
Unify note and personal page grids with context-aware PageGrid (#838)
otomatty d027318
Refactor thumbnail deletion to use shared GC service with referential…
otomatty 5f502ec
chore(deps): bump actions/create-github-app-token from 2 to 3 (#840)
dependabot[bot] 1410a02
chore(deps): bump @anthropic-ai/sdk from 0.92.0 to 0.95.1 in the mino…
dependabot[bot] 7dfaa15
chore(deps-dev): bump @commitlint/cli from 20.5.3 to 21.0.0 (#842)
dependabot[bot] 3d6b02c
chore(deps): bump react-day-picker from 9.14.0 to 10.0.0 (#843)
dependabot[bot] fdba957
chore(deps): resolve dependabot conflicts for genai 2.0.1 and config-…
otomatty 718984d
feat(notes): title switcher, settings redesign, remove share modal (#…
otomatty 4651cbf
Dedupe note API calls and optimize page listing queries (#855)
otomatty c67d23c
perf(notes): Epic #847 phases 4-6 — shared image cache, virtualizatio…
otomatty 0d8511a
fix(api): coerce MAX(pages.updated_at) to Date in GET /api/notes/:id …
otomatty 9f6a0d1
feat(pdf): foundation for local PDF knowledge ingestion (#389) (#858)
otomatty 478fce4
fix(api): restore content_preview in GET /api/notes/:id (#860 Phase 0…
otomatty 12151a7
feat(api): keyset pagination + index for note pages (#860 Phase 1/2) …
otomatty b6230c7
feat: implement keyset cursor pagination for note pages (issue #860 P…
otomatty 27a33e7
feat: add note-scoped SSE event channel for page list (#860 Phase 4) …
otomatty cc57e30
chore(notes): drop pages[] from note shell; add /page-titles + /pages…
otomatty e1598e6
fix(editor): suppress slash menu inside inline code and code blocks (…
otomatty e527235
feat(pdf): implement pdf.js viewer with highlights and derive-page se…
otomatty 5f99095
test(e2e): add PDF knowledge ingestion E2E suite for #863 (#871)
otomatty be80392
feat(search): surface PDF highlight bodies in Global Search (#864) (#…
otomatty 922f9e9
fix(pdf): disable isEvalSupported in getPdfDocument (#872) (#874)
otomatty a79b499
feat(note): remove note-scoped page search bar from NoteView (#875)
otomatty e68479c
fix(hocuspocus): repair WebSocket auth after note_pages removal (#876)
otomatty 933a826
fix(api): add missing page snapshots migration (#877)
otomatty 8f0527a
ci: Add Drizzle schema drift detection across entire repository (#879)
otomatty 72679d5
feat(notes): add read-only share-settings entry for editor/viewer (#6…
otomatty 74fcd6d
feat: implement issue #880 Phase B & C - wiki link graph sync (#882)
otomatty 56e263b
fix: Add ON DELETE CASCADE to user FKs in invite link tables (#885)
otomatty f742cf1
refactor: unify page-detail toolbar into shared PageEditorHeader (#886)
otomatty dcdc83d
fix: Move WikiLink mark normalization to server-side (Issue #880 Phas…
otomatty f2388b0
Add metadata-only PUT endpoint and read-only GET endpoint for pages (…
otomatty 0221bd8
feat(#890): align NotePageView toolbar with /pages/:id (history/expor…
otomatty d2e4e71
refactor(note): introduce NotePagePublicView for read-only guests (#8…
otomatty 7885a59
refactor: consolidate page editor into note-centric architecture (Iss…
otomatty 15aebce
refactor(api): retire GET/PUT /api/pages/:id/content (Issue #889 Phas…
otomatty eae01d1
feat: Add `/health` endpoint with Postgres pool saturation monitoring…
otomatty 1329e28
feat: Add tag filter bar to note page list (#897)
otomatty aa1a22e
fix: align NoteMeRedirect skeleton with PageGrid layout (#898)
otomatty 8da5d91
chore(deps): bump cloudflare/wrangler-action from 3 to 4
dependabot[bot] b40f7d1
chore(deps): bump the minor-and-patch group with 2 updates
dependabot[bot] 5773b9d
chore(deps): sync bun.lock with package.json changes
github-actions[bot] 8c75aa8
chore(deps): bump pdfjs-dist from 4.10.38 to 5.7.284
dependabot[bot] 90bdd03
chore(deps): sync bun.lock with package.json changes
github-actions[bot] 4b27153
feat(editor): replace collaboration loading spinner with skeleton UI …
otomatty 8e42cdb
refactor: remove FAB "add existing page" feature and orphan copy hook…
otomatty c1417f9
refactor(page): remove mobile-only delete button from PageCard (#904)
otomatty 86e3f5e
feat: add ErrorScreen component with ErrorBoundary integration (#907)
otomatty 0d11e14
fix: PR #905 レビュー指摘のフォローアップ (#908)
otomatty baa1c1f
Merge pull request #899 from otomatty/dependabot/github_actions/devel…
otomatty 692e434
Merge pull request #900 from otomatty/dependabot/npm_and_yarn/develop…
otomatty d678880
Merge pull request #901 from otomatty/dependabot/npm_and_yarn/develop…
otomatty 212edb7
fix: clarify admin error response payload (#910)
otomatty File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
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
Large diffs are not rendered by default.
Oops, something went wrong.
557 changes: 557 additions & 0 deletions
557
.github/actions/claude-analyze/__tests__/autoIssue.test.mjs
Large diffs are not rendered by default.
Oops, something went wrong.
4 changes: 4 additions & 0 deletions
4
.github/actions/claude-analyze/__tests__/fixtures/invalid-bad-severity.json
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "severity": "critical", | ||
| "ai_summary": "Severity is not one of the allowed enum values." | ||
| } |
4 changes: 4 additions & 0 deletions
4
.github/actions/claude-analyze/__tests__/fixtures/invalid-missing-summary.json
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "severity": "medium", | ||
| "ai_root_cause": "ai_summary is missing" | ||
| } |
9 changes: 9 additions & 0 deletions
9
.github/actions/claude-analyze/__tests__/fixtures/invalid-suspected-file.json
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "severity": "high", | ||
| "ai_summary": "Suspected files entry is missing the required path.", | ||
| "ai_suspected_files": [ | ||
| { | ||
| "reason": "no path means this entry is invalid" | ||
| } | ||
| ] | ||
| } |
12 changes: 12 additions & 0 deletions
12
.github/actions/claude-analyze/__tests__/fixtures/invalid-too-many-files.json
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| { | ||
| "severity": "high", | ||
| "ai_summary": "Claude returned 6 suspected files, exceeding the documented 5-entry cap.", | ||
| "ai_suspected_files": [ | ||
| { "path": "src/a.ts" }, | ||
| { "path": "src/b.ts" }, | ||
| { "path": "src/c.ts" }, | ||
| { "path": "src/d.ts" }, | ||
| { "path": "src/e.ts" }, | ||
| { "path": "src/f.ts" } | ||
| ] | ||
| } |
17 changes: 17 additions & 0 deletions
17
.github/actions/claude-analyze/__tests__/fixtures/valid-high.json
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "severity": "high", | ||
| "ai_summary": "Database migration failed mid-flight, leaving rows with NULL note_id and breaking page lookups.", | ||
| "ai_root_cause": "Migration 0042 added a NOT NULL constraint without backfilling. Existing rows pre-dating the migration have NULL and the SELECT path crashes.", | ||
| "ai_suggested_fix": "Backfill note_id from pages.owner_id where NULL, then re-apply the NOT NULL constraint in a follow-up migration.", | ||
| "ai_suspected_files": [ | ||
| { | ||
| "path": "server/api/drizzle/0042_add_note_id.sql", | ||
| "reason": "Introduced the NOT NULL constraint without a backfill step.", | ||
| "line": 12 | ||
| }, | ||
| { | ||
| "path": "server/api/src/services/pageService.ts", | ||
| "reason": "SELECT path that throws when note_id is NULL." | ||
| } | ||
| ] | ||
| } |
7 changes: 7 additions & 0 deletions
7
.github/actions/claude-analyze/__tests__/fixtures/valid-low-nulls.json
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "severity": "low", | ||
| "ai_summary": "Transient network blip while contacting the third-party clipper service. Retried automatically.", | ||
| "ai_root_cause": null, | ||
| "ai_suggested_fix": null, | ||
| "ai_suspected_files": null | ||
| } |
123 changes: 123 additions & 0 deletions
123
.github/actions/claude-analyze/__tests__/schema.test.mjs
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| /** | ||
| * Fixture-driven tests for the Claude analysis output schema. Issue #806. | ||
| * | ||
| * 実行方法 / How to run: | ||
| * `node --test .github/actions/claude-analyze/__tests__/schema.test.mjs` | ||
| * | ||
| * vitest を新たに追加するのは workspace の test:run が肥大化するので、 | ||
| * Node 24 の組み込みテストランナーを使う。CI への組み込みは README 参照。 | ||
| * | ||
| * Uses Node 24's built-in test runner instead of adding a new vitest workspace | ||
| * — keeps the action self-contained and avoids touching the monorepo's | ||
| * `test:run` aggregator. CI wiring guidance lives in the action README. | ||
| */ | ||
| import { test } from "node:test"; | ||
| import assert from "node:assert/strict"; | ||
| import { readFile } from "node:fs/promises"; | ||
| import path from "node:path"; | ||
|
|
||
| import { | ||
| analysisOutputSchema, | ||
| parseAndValidate, | ||
| SEVERITIES, | ||
| suspectedFileSchema, | ||
| } from "../schema.mjs"; | ||
|
|
||
| const FIXTURES = path.join(import.meta.dirname, "fixtures"); | ||
|
|
||
| /** | ||
| * @param {string} name | ||
| * @returns {Promise<string>} | ||
| */ | ||
| async function loadFixtureRaw(name) { | ||
| return readFile(path.join(FIXTURES, name), "utf8"); | ||
| } | ||
|
|
||
| test("SEVERITIES matches the server-side ApiErrorSeverity enum", () => { | ||
| assert.deepEqual([...SEVERITIES], ["high", "medium", "low", "unknown"]); | ||
| }); | ||
|
|
||
| test("valid-high.json passes the schema and round-trips through parseAndValidate", async () => { | ||
| const raw = await loadFixtureRaw("valid-high.json"); | ||
| const parsed = JSON.parse(raw); | ||
| assert.equal(analysisOutputSchema.safeParse(parsed).success, true); | ||
| const validated = parseAndValidate(raw); | ||
| assert.equal(validated.severity, "high"); | ||
| assert.equal(Array.isArray(validated.ai_suspected_files), true); | ||
| assert.equal(validated.ai_suspected_files?.length, 2); | ||
| assert.equal(validated.ai_suspected_files?.[0]?.path.includes("0042_add_note_id"), true); | ||
| }); | ||
|
|
||
| test("valid-low-nulls.json accepts explicit nulls for optional fields", async () => { | ||
| const raw = await loadFixtureRaw("valid-low-nulls.json"); | ||
| const validated = parseAndValidate(raw); | ||
| assert.equal(validated.severity, "low"); | ||
| assert.equal(validated.ai_root_cause, null); | ||
| assert.equal(validated.ai_suggested_fix, null); | ||
| assert.equal(validated.ai_suspected_files, null); | ||
| }); | ||
|
|
||
| test("invalid-bad-severity.json is rejected with a severity-mention message", async () => { | ||
| const raw = await loadFixtureRaw("invalid-bad-severity.json"); | ||
| assert.throws(() => parseAndValidate(raw), /severity/i); | ||
| }); | ||
|
|
||
| test("invalid-missing-summary.json is rejected when ai_summary is absent", async () => { | ||
| const raw = await loadFixtureRaw("invalid-missing-summary.json"); | ||
| assert.throws(() => parseAndValidate(raw), /ai_summary/); | ||
| }); | ||
|
|
||
| test("invalid-suspected-file.json is rejected when an entry has no path", async () => { | ||
| const raw = await loadFixtureRaw("invalid-suspected-file.json"); | ||
| assert.throws(() => parseAndValidate(raw), /path/); | ||
| }); | ||
|
|
||
| test("invalid-too-many-files.json is rejected when ai_suspected_files exceeds 5 entries", async () => { | ||
| const raw = await loadFixtureRaw("invalid-too-many-files.json"); | ||
| assert.throws(() => parseAndValidate(raw), /ai_suspected_files.*5|at most 5/); | ||
| }); | ||
|
|
||
| test("parseAndValidate strips Claude's ```json``` fence and prose preamble", () => { | ||
| const wrapped = [ | ||
| "Sure, here is the analysis:", | ||
| "```json", | ||
| JSON.stringify({ | ||
| severity: "medium", | ||
| ai_summary: "wrapped in fence", | ||
| ai_root_cause: null, | ||
| ai_suggested_fix: null, | ||
| ai_suspected_files: null, | ||
| }), | ||
| "```", | ||
| ].join("\n"); | ||
| const validated = parseAndValidate(wrapped); | ||
| assert.equal(validated.severity, "medium"); | ||
| assert.equal(validated.ai_summary, "wrapped in fence"); | ||
| }); | ||
|
|
||
| test("parseAndValidate throws when no JSON object is present", () => { | ||
| assert.throws(() => parseAndValidate("nope, no braces here"), /JSON object/); | ||
| }); | ||
|
|
||
| test("parseAndValidate throws on empty input", () => { | ||
| assert.throws(() => parseAndValidate(""), /empty/); | ||
| }); | ||
|
|
||
| test("suspectedFileSchema requires a non-empty path", () => { | ||
| assert.equal(suspectedFileSchema.safeParse({ path: "" }).success, false); | ||
| assert.equal(suspectedFileSchema.safeParse({ path: "src/foo.ts" }).success, true); | ||
| }); | ||
|
|
||
| test("suspectedFileSchema rejects non-integer line numbers", () => { | ||
| assert.equal(suspectedFileSchema.safeParse({ path: "src/foo.ts", line: 12.5 }).success, false); | ||
| assert.equal(suspectedFileSchema.safeParse({ path: "src/foo.ts", line: 12 }).success, true); | ||
| }); | ||
|
|
||
| test("analysisOutputSchema rejects unknown top-level keys (strict mode)", () => { | ||
| const bad = { | ||
| severity: "low", | ||
| ai_summary: "ok", | ||
| extra_field: "should not be here", | ||
| }; | ||
| assert.equal(analysisOutputSchema.safeParse(bad).success, false); | ||
| }); |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| # `.github/actions/claude-analyze` — Composite action that runs the Claude | ||
| # AI error analysis script and PUTs the validated result back to the API | ||
| # callback endpoint. Epic #616 Phase 2 / issue #806. | ||
| # | ||
| # This action is invoked by `.github/workflows/analyze-error.yml` on | ||
| # `repository_dispatch` (`event_type: analyze-error`). See README.md for the | ||
| # full client_payload contract and a workflow_dispatch dry-run recipe. | ||
| name: Claude analyze API error | ||
| description: > | ||
| Analyze a Sentry-reported API error with Claude and PUT the structured | ||
| result back to the Zedi API callback endpoint. / Sentry が検知した API エラーを | ||
| Claude で解析し、構造化結果を Zedi API のコールバックへ書き戻す。 | ||
|
|
||
| inputs: | ||
| api_error_id: | ||
| description: "`api_errors.id` (UUID) for the row to update." | ||
| required: true | ||
| sentry_issue_id: | ||
| description: Sentry issue id from the dispatch client_payload. | ||
| required: true | ||
| title: | ||
| description: Short error title from Sentry. | ||
| required: true | ||
| route: | ||
| description: Route where the error fired (may be empty). | ||
| required: false | ||
| default: "" | ||
| callback_base_url: | ||
| description: > | ||
| Base URL for the API callback (e.g. https://api.example.com). | ||
| The action appends `/api/webhooks/github/ai-result/<id>`. | ||
| required: true | ||
| installation_token: | ||
| description: GitHub App installation access token used as the bearer for the callback PUT. | ||
| required: true | ||
| anthropic_api_key: | ||
| description: Anthropic API key for the Claude call. | ||
| required: true | ||
| model: | ||
| description: Override Claude model id (defaults to claude-sonnet-4-6). | ||
| required: false | ||
| default: "" | ||
| dry_run: | ||
| description: When "true", skip the Anthropic call and emit a stub payload (for workflow_dispatch testing). | ||
| required: false | ||
| default: "false" | ||
| skip_callback: | ||
| description: When "true", validate locally but do not PUT to the API (pairs with dry_run for fixture validation). | ||
| required: false | ||
| default: "false" | ||
| skip_issue: | ||
| description: > | ||
| When "true", skip the auto-issue (create / comment) step. Pairs with | ||
| `dry_run` / `skip_callback` for fixture validation. Epic #616 Phase 3. | ||
| required: false | ||
| default: "false" | ||
|
|
||
| outputs: | ||
| severity: | ||
| description: AI-assigned severity (high | medium | low | unknown). | ||
| value: ${{ steps.analyze.outputs.severity }} | ||
| output_path: | ||
| description: Path to the validated analysis JSON written by the script. | ||
| value: ${{ steps.analyze.outputs.output_path }} | ||
| issue_action: | ||
| description: > | ||
| Outcome of the auto-issue step: `skipped`, `created`, or `commented`. | ||
| Empty when `skip_issue=true`. Epic #616 Phase 3 / Issue #808. | ||
| value: ${{ steps.auto-issue.outputs.action }} | ||
| issue_number: | ||
| description: GitHub Issue number created or commented on (empty when skipped). | ||
| value: ${{ steps.auto-issue.outputs.issue_number }} | ||
| issue_html_url: | ||
| description: HTML URL of the created Issue or recurrence comment (empty when skipped). | ||
| value: ${{ steps.auto-issue.outputs.issue_html_url }} | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
| - name: Setup Node | ||
| uses: actions/setup-node@v6 | ||
| with: | ||
| node-version-file: .nvmrc | ||
|
|
||
| - name: Setup Bun | ||
| uses: oven-sh/setup-bun@v2 | ||
| with: | ||
| bun-version: "1.3" | ||
|
|
||
| - name: Install dependencies | ||
| shell: bash | ||
| run: bun install --frozen-lockfile | ||
|
|
||
| - name: Run analyze script | ||
| id: analyze | ||
| shell: bash | ||
| env: | ||
| CLAUDE_ANALYZE_API_ERROR_ID: ${{ inputs.api_error_id }} | ||
| CLAUDE_ANALYZE_SENTRY_ISSUE_ID: ${{ inputs.sentry_issue_id }} | ||
| CLAUDE_ANALYZE_TITLE: ${{ inputs.title }} | ||
| CLAUDE_ANALYZE_ROUTE: ${{ inputs.route }} | ||
| CLAUDE_ANALYZE_REPOSITORY: ${{ github.repository }} | ||
| ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }} | ||
| CLAUDE_MODEL: ${{ inputs.model }} | ||
| CLAUDE_ANALYZE_DRY_RUN: ${{ inputs.dry_run }} | ||
| CLAUDE_ANALYZE_OUTPUT: ${{ runner.temp }}/analyze-output.json | ||
| run: | | ||
| node "${{ github.action_path }}/analyze.mjs" | ||
| echo "output_path=${CLAUDE_ANALYZE_OUTPUT}" >> "$GITHUB_OUTPUT" | ||
| # severity を outputs に拾う。Node スクリプトに依存させず、jq で抜き出す。 | ||
| # Pull severity into outputs via jq — keeps the script's only contract | ||
| # the JSON file, no out-of-band stdout protocol to maintain. | ||
| SEVERITY=$(jq -r '.severity' "${CLAUDE_ANALYZE_OUTPUT}") | ||
| echo "severity=${SEVERITY}" >> "$GITHUB_OUTPUT" | ||
| echo "::notice title=AI severity::${SEVERITY}" | ||
|
|
||
| - name: PUT analysis to API callback | ||
| if: inputs.skip_callback != 'true' | ||
| shell: bash | ||
| env: | ||
| CALLBACK_URL: ${{ inputs.callback_base_url }}/api/webhooks/github/ai-result/${{ inputs.api_error_id }} | ||
| INSTALLATION_TOKEN: ${{ inputs.installation_token }} | ||
| OUTPUT_PATH: ${{ steps.analyze.outputs.output_path }} | ||
| run: | | ||
| set -euo pipefail | ||
| # API のコールバックに PUT。失敗時は最大 2 回までリトライ(issue #806 の | ||
| # 「workflow 内で 1〜2 回まで」要件)。HTTP ステータスを判定し、 | ||
| # 5xx / ネットワークエラーのみリトライ、4xx は即失敗(auth 不正など)。 | ||
| # | ||
| # PUT to the API callback. Retry up to 2 attempts (issue #806's "1〜2 回 | ||
| # まで"). Only retry on transient 5xx / network errors; 4xx (auth, bad | ||
| # payload) is final so misconfiguration surfaces immediately. | ||
| attempt=1 | ||
| max_attempts=2 | ||
| while true; do | ||
| echo "PUT ${CALLBACK_URL} (attempt ${attempt}/${max_attempts})" | ||
| # `--connect-timeout` で TCP 接続待ちを 10 秒、`--max-time` でリクエスト | ||
| # 全体を 30 秒に制限する。ハングした session が retry ループの背圧になる | ||
| # のを防ぐ(Job timeout の 10 分を一発で食い潰すのを避ける目的)。 | ||
| # `--connect-timeout` caps TCP connect at 10 s; `--max-time` caps the | ||
| # total request at 30 s. Without these, a hung session would block the | ||
| # retry loop and could burn the entire 10-minute job timeout on a | ||
| # single PUT. | ||
| http_status=$(curl -sS -o /tmp/callback-response.txt -w "%{http_code}" \ | ||
| -X PUT "${CALLBACK_URL}" \ | ||
| -H "Authorization: Bearer ${INSTALLATION_TOKEN}" \ | ||
| -H "Content-Type: application/json" \ | ||
| --connect-timeout 10 \ | ||
| --max-time 30 \ | ||
| --data-binary "@${OUTPUT_PATH}" || echo "000") | ||
| echo "HTTP ${http_status}" | ||
| if [ "${http_status}" -ge 200 ] && [ "${http_status}" -lt 300 ]; then | ||
| cat /tmp/callback-response.txt | ||
| echo | ||
| echo "Callback succeeded." | ||
| break | ||
| fi | ||
| if [ "${http_status}" -ge 400 ] && [ "${http_status}" -lt 500 ]; then | ||
| echo "::error title=Callback rejected (${http_status})::$(cat /tmp/callback-response.txt)" | ||
| exit 1 | ||
| fi | ||
| if [ "${attempt}" -ge "${max_attempts}" ]; then | ||
| echo "::error title=Callback failed after ${max_attempts} attempts (${http_status})::$(cat /tmp/callback-response.txt)" | ||
| exit 1 | ||
| fi | ||
| attempt=$((attempt + 1)) | ||
| sleep 5 | ||
| done | ||
|
|
||
| - name: Auto-file or comment GitHub Issue | ||
| # Epic #616 Phase 3 / Issue #808: severity が high / medium のときだけ | ||
| # Issue を起票し、同一 sentry_issue_id に既存オープン Issue があれば | ||
| # コメント追記で再発を表現する(連続 100 回でも 1 件に集約)。`skip_issue` | ||
| # は `workflow_dispatch` のドライラン用フラグ。 | ||
| # | ||
| # Files a GitHub Issue when AI severity is high / medium, or appends a | ||
| # recurrence comment when an open Issue with the matching | ||
| # `sentry-issue:<id>` label already exists. Honors `skip_issue` so | ||
| # workflow_dispatch dry runs cannot accidentally create Issues. | ||
| if: inputs.skip_issue != 'true' | ||
| id: auto-issue | ||
| shell: bash | ||
| env: | ||
| AUTO_ISSUE_OUTPUT_PATH: ${{ steps.analyze.outputs.output_path }} | ||
| AUTO_ISSUE_SENTRY_ISSUE_ID: ${{ inputs.sentry_issue_id }} | ||
| AUTO_ISSUE_API_ERROR_ID: ${{ inputs.api_error_id }} | ||
| AUTO_ISSUE_TITLE: ${{ inputs.title }} | ||
| AUTO_ISSUE_ROUTE: ${{ inputs.route }} | ||
| AUTO_ISSUE_REPOSITORY: ${{ github.repository }} | ||
| AUTO_ISSUE_TOKEN: ${{ inputs.installation_token }} | ||
| AUTO_ISSUE_WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| run: node "${{ github.action_path }}/autoIssueRunner.mjs" | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.