@fitlab-ai/agent-infra 0.9.11 → 0.9.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/README.zh-CN.md +13 -13
- package/dist/bin/internal-cli.js +29 -19
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/defaults.json +4 -0
- package/dist/lib/git/workflow.js +43 -1
- package/dist/lib/init.js +1 -0
- package/dist/lib/internal/git-workflow.js +8 -1
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +9 -9
- package/dist/lib/internal/platform-context.js +2 -2
- package/dist/lib/internal/platform-issue.js +8 -9
- package/dist/lib/internal/platform-pr-review.js +10 -10
- package/dist/lib/internal/platform-pr.js +68 -23
- package/dist/lib/internal/platform-release-notes.js +3 -3
- package/dist/lib/internal/pr-review-grade.js +11 -9
- package/dist/lib/internal/release-workflow.js +4 -4
- package/dist/lib/internal/sandbox-control.js +30 -4
- package/dist/lib/internal/task-activity.js +3 -3
- package/dist/lib/internal/task-artifact.js +76 -24
- package/dist/lib/internal/task-create.js +2 -2
- package/dist/lib/internal/task-delivery.js +59 -0
- package/dist/lib/internal/task-event.js +6 -3
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-invalidation.js +60 -0
- package/dist/lib/internal/task-ledger.js +9 -6
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-override.js +4 -4
- package/dist/lib/internal/task-review.js +3 -3
- package/dist/lib/internal/task-verify.js +6 -6
- package/dist/lib/internal/task-warning.js +3 -3
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +8 -2
- package/dist/lib/platform/github-provider.js +903 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +94 -41
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +551 -0
- package/dist/lib/platform/pr-checks.js +124 -106
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +523 -49
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +487 -0
- package/dist/lib/platform/provider-contract.js +95 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +504 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +1050 -502
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +22 -12
- package/dist/lib/platform/verification-sync.js +118 -239
- package/dist/lib/pr-review/evidence-grading.js +30 -9
- package/dist/lib/run/index.js +63 -23
- package/dist/lib/sandbox/config.js +5 -0
- package/dist/lib/sandbox/control/client.js +96 -15
- package/dist/lib/sandbox/control/executor.js +3 -3
- package/dist/lib/sandbox/control/lifecycle.js +29 -7
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +398 -25
- package/dist/lib/sandbox/control/state.js +204 -1
- package/dist/lib/server/process-state.js +2 -2
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +35 -7
- package/dist/lib/task/capabilities.js +239 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-intent.js +100 -0
- package/dist/lib/task/commit-operation.js +207 -102
- package/dist/lib/task/control-authority.js +8 -5
- package/dist/lib/task/create-service.js +8 -8
- package/dist/lib/task/create.js +33 -7
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +3 -48
- package/dist/lib/task/delivery-target.js +97 -0
- package/dist/lib/task/delivery.js +153 -0
- package/dist/lib/task/events.js +297 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +61 -36
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/lifecycle.js +106 -9
- package/dist/lib/task/local-artifact-finalization.js +374 -0
- package/dist/lib/task/orchestration.js +100 -151
- package/dist/lib/task/pr-delivery-fact.js +183 -0
- package/dist/lib/task/review-finalization.js +4 -8
- package/dist/lib/task/review-fingerprint.js +32 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-engine.js +89 -38
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/update.js +9 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/defaults.json +4 -0
- package/lib/git/workflow.ts +38 -1
- package/lib/init.ts +4 -1
- package/lib/internal/git-workflow.ts +7 -1
- package/lib/internal/platform-checks.ts +3 -3
- package/lib/internal/platform-comment.ts +8 -8
- package/lib/internal/platform-context.ts +2 -2
- package/lib/internal/platform-issue.ts +7 -8
- package/lib/internal/platform-pr-review.ts +9 -9
- package/lib/internal/platform-pr.ts +53 -21
- package/lib/internal/platform-release-notes.ts +3 -3
- package/lib/internal/pr-review-grade.ts +10 -8
- package/lib/internal/release-workflow.ts +4 -4
- package/lib/internal/sandbox-control.ts +27 -3
- package/lib/internal/task-activity.ts +3 -3
- package/lib/internal/task-artifact.ts +65 -22
- package/lib/internal/task-create.ts +2 -2
- package/lib/internal/task-delivery.ts +53 -0
- package/lib/internal/task-event.ts +6 -3
- package/lib/internal/task-finalization.ts +3 -3
- package/lib/internal/task-invalidation.ts +39 -0
- package/lib/internal/task-ledger.ts +11 -8
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-override.ts +6 -6
- package/lib/internal/task-review.ts +3 -3
- package/lib/internal/task-verify.ts +6 -6
- package/lib/internal/task-warning.ts +3 -3
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +10 -3
- package/lib/platform/github-provider.ts +910 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +97 -47
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +645 -0
- package/lib/platform/pr-checks.ts +129 -107
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +562 -52
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +536 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +529 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +1126 -505
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +21 -13
- package/lib/platform/verification-sync.ts +118 -293
- package/lib/pr-review/evidence-grading.ts +31 -11
- package/lib/run/index.ts +57 -22
- package/lib/sandbox/config.ts +7 -0
- package/lib/sandbox/control/client.ts +105 -15
- package/lib/sandbox/control/executor.ts +4 -4
- package/lib/sandbox/control/lifecycle.ts +30 -8
- package/lib/sandbox/control/protocol.ts +113 -0
- package/lib/sandbox/control/server.ts +431 -23
- package/lib/sandbox/control/state.ts +251 -1
- package/lib/server/process-state.ts +2 -2
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +34 -8
- package/lib/task/capabilities.ts +262 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-intent.ts +137 -0
- package/lib/task/commit-operation.ts +186 -88
- package/lib/task/control-authority.ts +11 -8
- package/lib/task/create-service.ts +8 -8
- package/lib/task/create.ts +34 -9
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +8 -64
- package/lib/task/delivery-target.ts +130 -0
- package/lib/task/delivery.ts +160 -0
- package/lib/task/events.ts +296 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +73 -46
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/lifecycle.ts +93 -4
- package/lib/task/local-artifact-finalization.ts +563 -0
- package/lib/task/orchestration.ts +110 -146
- package/lib/task/pr-delivery-fact.ts +237 -0
- package/lib/task/review-finalization.ts +4 -14
- package/lib/task/review-fingerprint.ts +33 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-engine.ts +95 -43
- package/lib/task/verification-review.ts +2 -0
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +11 -1
- package/lib/version.ts +15 -1
- package/package.json +7 -1
- package/templates/.agents/rules/README.en.md +4 -2
- package/templates/.agents/rules/README.zh-CN.md +4 -2
- package/templates/.agents/rules/create-issue.en.md +11 -2
- package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
- package/templates/.agents/rules/issue-pr-commands.en.md +4 -17
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +28 -5
- package/templates/.agents/rules/issue-sync.zh-CN.md +64 -5
- package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
- package/templates/.agents/rules/local-artifact-repair.en.md +53 -0
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +53 -0
- package/templates/.agents/rules/milestone-inference.en.md +12 -2
- package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
- package/templates/.agents/rules/pr-sync.en.md +28 -13
- package/templates/.agents/rules/pr-sync.zh-CN.md +39 -5
- package/templates/.agents/rules/release-commands.en.md +42 -3
- package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
- package/templates/.agents/rules/security-alerts.en.md +17 -2
- package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
- package/templates/.agents/rules/sync-content-generation.en.md +8 -0
- package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
- package/templates/.agents/rules/task-management.en.md +1 -1
- package/templates/.agents/rules/task-management.zh-CN.md +1 -1
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/scripts/security-alerts.github.sh +151 -0
- package/templates/.agents/scripts/security-alerts.sh +31 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +14 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -3
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +32 -10
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +35 -13
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/commit/SKILL.en.md +3 -3
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/config/verify.json +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +4 -4
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +4 -4
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +5 -4
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +5 -4
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -13
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +30 -13
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +11 -10
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +11 -11
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/create-task/SKILL.en.md +1 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
- package/templates/.agents/skills/plan-task/SKILL.en.md +14 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +11 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +6 -5
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +6 -5
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +10 -9
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +10 -9
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +6 -5
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +6 -5
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +4 -0
- package/templates/.agents/skills/watch-pr/SKILL.en.md +6 -6
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +23 -2
- package/templates/.agents/templates/task.zh-CN.md +23 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/feature-development.en.yaml +9 -8
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/refactoring.en.yaml +9 -8
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
- package/templates/.github/workflows/pr-label.yml +7 -26
- package/templates/.agents/rules/create-issue.github.en.md +0 -14
- package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
- package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
- package/templates/.agents/rules/issue-sync.github.en.md +0 -40
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
- package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
- package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
- package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
- package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
- package/templates/.agents/rules/pr-sync.github.en.md +0 -43
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
- package/templates/.agents/rules/release-commands.github.en.md +0 -44
- package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -44
- package/templates/.agents/rules/security-alerts.github.en.md +0 -43
- package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
- /package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +0 -0
- /package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +0 -0
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
|
|
4
4
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
5
|
-
import {
|
|
6
|
-
inspectPlatformRequiredChecks,
|
|
7
|
-
registerPlatformCapabilities
|
|
8
|
-
} from './adapters.ts';
|
|
9
5
|
import type { PlatformCheckSnapshot } from './adapters.ts';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import type { GitHubClient } from './github-client.ts';
|
|
6
|
+
import { resolvePlatformProviderContext } from './context.ts';
|
|
7
|
+
import type { PlatformClient } from './context.ts';
|
|
13
8
|
import { inspectPlatformPullRequest } from './pull-requests.ts';
|
|
14
9
|
import { platformResult } from './types.ts';
|
|
15
10
|
import type { PlatformResult } from './types.ts';
|
|
16
11
|
import type { PullRequestSnapshot } from './pull-requests.ts';
|
|
12
|
+
import { readPrDeliveryFact } from '../task/pr-delivery-fact.ts';
|
|
13
|
+
import {
|
|
14
|
+
providerError,
|
|
15
|
+
providerOperationContext,
|
|
16
|
+
providerStatus,
|
|
17
|
+
resourceIdentity,
|
|
18
|
+
resourceIdentityNumber,
|
|
19
|
+
unsupportedProviderOperation
|
|
20
|
+
} from './provider-bridge.ts';
|
|
17
21
|
|
|
18
22
|
type CheckBucket = 'pass' | 'fail' | 'pending' | 'cancel';
|
|
19
23
|
type CheckState = 'passed' | 'failed' | 'pending' | 'timed-out' | 'cancelled' | 'no-required';
|
|
@@ -29,8 +33,9 @@ type ChecksResult = PlatformResult & {
|
|
|
29
33
|
resolution?: { status: 'resolved' | 'missing' | 'ambiguous'; runId: number | null; jobId: number | null };
|
|
30
34
|
logs?: { runId: number; jobId?: number; text: string };
|
|
31
35
|
};
|
|
32
|
-
type
|
|
33
|
-
type
|
|
36
|
+
type GitHubClient = PlatformClient;
|
|
37
|
+
type InspectionOptions = { cwd?: string; client?: PlatformClient; runtimeVersion?: string };
|
|
38
|
+
type SharedOptions = { cwd?: string; client?: PlatformClient };
|
|
34
39
|
|
|
35
40
|
function classifyRequiredChecks(required: CheckSnapshot[]): ChecksSnapshot {
|
|
36
41
|
if (required.length === 0) return { state: 'no-required', required };
|
|
@@ -80,8 +85,8 @@ function checksResult(status: PlatformResult['status'], overrides: Partial<Check
|
|
|
80
85
|
};
|
|
81
86
|
}
|
|
82
87
|
|
|
83
|
-
function normalizeBucket(value: { bucket?: string; state?: string; conclusion?: string }): CheckBucket {
|
|
84
|
-
const raw = String(value.bucket || value.conclusion || value.state || '').toLowerCase();
|
|
88
|
+
function normalizeBucket(value: { bucket?: string; status?: string; state?: string; conclusion?: string }): CheckBucket {
|
|
89
|
+
const raw = String(value.bucket || value.status || value.conclusion || value.state || '').toLowerCase();
|
|
85
90
|
if (['pass', 'success', 'successful', 'neutral'].includes(raw)) return 'pass';
|
|
86
91
|
if (['fail', 'failure', 'failed', 'error', 'timed_out', 'action_required'].includes(raw)) return 'fail';
|
|
87
92
|
if (['cancel', 'cancelled', 'canceled', 'skipped', 'stale'].includes(raw)) return 'cancel';
|
|
@@ -105,84 +110,81 @@ function normalizeChecks(value: unknown): CheckSnapshot[] {
|
|
|
105
110
|
});
|
|
106
111
|
}
|
|
107
112
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
: { ok: false, error: inspected.error };
|
|
118
|
-
}
|
|
119
|
-
});
|
|
113
|
+
function inspectGitHubRequiredChecks(client: GitHubClient, repository: string, number: number, cwd: string) {
|
|
114
|
+
const inspected = client.json<unknown>([
|
|
115
|
+
'pr', 'checks', String(number), '--repo', repository,
|
|
116
|
+
'--json', 'name,state,bucket,link,workflow,startedAt,completedAt'
|
|
117
|
+
], { cwd });
|
|
118
|
+
return inspected.ok
|
|
119
|
+
? { ok: true as const, value: normalizeChecks(inspected.value) }
|
|
120
|
+
: { ok: false as const, error: inspected.error };
|
|
121
|
+
}
|
|
120
122
|
|
|
121
|
-
function resolvedTask(taskRef: string, options: InspectionOptions) {
|
|
123
|
+
async function resolvedTask(taskRef: string, options: InspectionOptions) {
|
|
122
124
|
const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
|
|
123
125
|
if (!resolved.ok) return { ok: false as const, output: checksResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }) };
|
|
124
|
-
const frontmatter =
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
const
|
|
126
|
+
const frontmatter = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
|
|
127
|
+
const fact = readPrDeliveryFact(frontmatter, options.runtimeVersion);
|
|
128
|
+
if (fact.status === 'invalid') return { ok: false as const, output: checksResult('failed', { error: { code: fact.error.code, message: fact.error.message, retryable: false } }) };
|
|
129
|
+
const prIdentity = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
130
|
+
const prNumber = resourceIdentityNumber(prIdentity);
|
|
131
|
+
if (!prIdentity) return { ok: false as const, output: checksResult('failed', { error: { code: fact.status === 'missing' ? 'PR_DELIVERY_FACT_MISSING' : 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false } }) };
|
|
132
|
+
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
133
|
+
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
130
134
|
if (!context.platform.repository || !['no-op', 'degraded'].includes(context.status)) return { ok: false as const, output: checksResult(context.status, { platform: context.platform, capabilities: context.capabilities, error: context.error }) };
|
|
131
|
-
const inspected = inspectPlatformPullRequest(taskRef, { cwd: resolved.repoRoot, client });
|
|
135
|
+
const inspected = await inspectPlatformPullRequest(taskRef, { cwd: resolved.repoRoot, client: options.client });
|
|
132
136
|
if (!inspected.pullRequest) return { ok: false as const, output: checksResult(inspected.status, { platform: context.platform, capabilities: context.capabilities, error: inspected.error }) };
|
|
133
|
-
return { ok:
|
|
137
|
+
if (!loaded.ok) return { ok: false as const, output: checksResult('failed', { platform: context.platform, capabilities: context.capabilities, error: context.error }) };
|
|
138
|
+
return { ok: true as const, resolved, prNumber, prIdentity, client: options.client, context, pullRequest: inspected.pullRequest, provider: loaded.value.provider, loadedContext: loaded.value };
|
|
134
139
|
}
|
|
135
140
|
|
|
136
|
-
function inspectRequiredChecks(taskRef: string, options: InspectionOptions = {}): ChecksResult {
|
|
137
|
-
const base = resolvedTask(taskRef, options);
|
|
141
|
+
async function inspectRequiredChecks(taskRef: string, options: InspectionOptions = {}): Promise<ChecksResult> {
|
|
142
|
+
const base = await resolvedTask(taskRef, options);
|
|
138
143
|
if (!base.ok) return base.output;
|
|
139
144
|
return inspectChecksForResolvedTask(base, options);
|
|
140
145
|
}
|
|
141
146
|
|
|
142
|
-
function inspectChecksForResolvedTask(
|
|
143
|
-
base: Extract<ReturnType<typeof resolvedTask
|
|
147
|
+
async function inspectChecksForResolvedTask(
|
|
148
|
+
base: Extract<Awaited<ReturnType<typeof resolvedTask>>, { ok: true }>,
|
|
144
149
|
options: InspectionOptions
|
|
145
|
-
): ChecksResult {
|
|
150
|
+
): Promise<ChecksResult> {
|
|
146
151
|
const repository = base.context.platform.repository!;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
retryable: false
|
|
159
|
-
};
|
|
160
|
-
return checksResult(error.retryable ? 'blocked' : 'failed', {
|
|
161
|
-
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
152
|
+
{
|
|
153
|
+
const inspected = base.provider.checks?.inspectRequired
|
|
154
|
+
? await base.provider.checks.inspectRequired({
|
|
155
|
+
context: providerOperationContext(base.loadedContext),
|
|
156
|
+
changeRequest: base.prIdentity,
|
|
157
|
+
headSha: base.pullRequest.head.sha
|
|
158
|
+
})
|
|
159
|
+
: unsupportedProviderOperation(base.provider, 'checks.inspectRequired');
|
|
160
|
+
if (!inspected.ok) return checksResult(providerStatus(inspected.error), {
|
|
161
|
+
platform: base.context.platform,
|
|
162
|
+
capabilities: base.context.capabilities,
|
|
162
163
|
resource: { kind: 'pull-request', number: base.prNumber },
|
|
163
|
-
pullRequest: base.pullRequest,
|
|
164
|
+
pullRequest: base.pullRequest,
|
|
165
|
+
error: providerError(inspected.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
|
|
166
|
+
});
|
|
167
|
+
const required = normalizeChecks(inspected.value);
|
|
168
|
+
const classified = classifyRequiredChecks(required);
|
|
169
|
+
const status = classified.state === 'passed' || classified.state === 'no-required'
|
|
170
|
+
? 'no-op'
|
|
171
|
+
: classified.state === 'failed' || classified.state === 'cancelled' ? 'failed' : 'blocked';
|
|
172
|
+
return checksResult(status, {
|
|
173
|
+
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
174
|
+
resource: { kind: 'pull-request', number: base.prNumber }, pullRequest: base.pullRequest,
|
|
175
|
+
checks: classified,
|
|
176
|
+
error: status === 'no-op' ? null : {
|
|
177
|
+
code: classified.state === 'pending' ? 'REQUIRED_CHECKS_PENDING' : `REQUIRED_CHECKS_${classified.state.toUpperCase()}`,
|
|
178
|
+
message: `Required checks are ${classified.state}`, retryable: classified.state === 'pending'
|
|
179
|
+
}
|
|
164
180
|
});
|
|
165
181
|
}
|
|
166
|
-
const classified = classifyRequiredChecks(inspected.value);
|
|
167
|
-
const status = classified.state === 'passed' || classified.state === 'no-required'
|
|
168
|
-
? 'no-op'
|
|
169
|
-
: classified.state === 'failed' || classified.state === 'cancelled' ? 'failed' : 'blocked';
|
|
170
|
-
return checksResult(status, {
|
|
171
|
-
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
172
|
-
resource: { kind: 'pull-request', number: base.prNumber },
|
|
173
|
-
pullRequest: base.pullRequest, checks: classified,
|
|
174
|
-
error: status === 'no-op' ? null : {
|
|
175
|
-
code: classified.state === 'pending' ? 'REQUIRED_CHECKS_PENDING' : `REQUIRED_CHECKS_${classified.state.toUpperCase()}`,
|
|
176
|
-
message: `Required checks are ${classified.state}`,
|
|
177
|
-
retryable: classified.state === 'pending'
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
182
|
}
|
|
181
183
|
|
|
182
|
-
function inspectPullRequestReadiness(taskRef: string, options: InspectionOptions = {}): ChecksResult {
|
|
183
|
-
const base = resolvedTask(taskRef, options);
|
|
184
|
+
async function inspectPullRequestReadiness(taskRef: string, options: InspectionOptions = {}): Promise<ChecksResult> {
|
|
185
|
+
const base = await resolvedTask(taskRef, options);
|
|
184
186
|
if (!base.ok) return base.output;
|
|
185
|
-
const checked = inspectChecksForResolvedTask(base, options);
|
|
187
|
+
const checked = await inspectChecksForResolvedTask(base, options);
|
|
186
188
|
const classifiedCodes = new Set(['REQUIRED_CHECKS_PENDING', 'REQUIRED_CHECKS_FAILED', 'REQUIRED_CHECKS_CANCELLED']);
|
|
187
189
|
if (checked.error && !classifiedCodes.has(checked.error.code)) return checked;
|
|
188
190
|
const readiness = classifyPullRequestReadiness({
|
|
@@ -224,7 +226,7 @@ async function watchPullRequestReadiness(taskRef: string, options: InspectionOpt
|
|
|
224
226
|
readiness: { state: 'cancelled', headSha: last?.pullRequest?.head.sha ?? '' },
|
|
225
227
|
error: { code: 'PR_READINESS_CANCELLED', message: 'Pull request readiness watch was cancelled', retryable: false }
|
|
226
228
|
});
|
|
227
|
-
last = inspectPullRequestReadiness(taskRef, options);
|
|
229
|
+
last = await inspectPullRequestReadiness(taskRef, options);
|
|
228
230
|
if (last.readiness?.state !== 'pending' || last.error?.code !== 'PR_READINESS_PENDING') return last;
|
|
229
231
|
const elapsed = now() - started;
|
|
230
232
|
if (elapsed >= options.deadlineSeconds * 1000) return checksResult('blocked', {
|
|
@@ -237,33 +239,40 @@ async function watchPullRequestReadiness(taskRef: string, options: InspectionOpt
|
|
|
237
239
|
}
|
|
238
240
|
}
|
|
239
241
|
|
|
240
|
-
function resolvePlatformCheckRun(taskRef: string, options: SharedOptions & { checkName: string; detailsUrl?: string }): ChecksResult {
|
|
241
|
-
const base = resolvedTask(taskRef, options);
|
|
242
|
+
async function resolvePlatformCheckRun(taskRef: string, options: SharedOptions & { checkName: string; detailsUrl?: string }): Promise<ChecksResult> {
|
|
243
|
+
const base = await resolvedTask(taskRef, options);
|
|
242
244
|
if (!base.ok) return base.output;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
{
|
|
246
|
+
const resolved = base.provider.checks?.resolveRun
|
|
247
|
+
? await base.provider.checks.resolveRun({
|
|
248
|
+
context: providerOperationContext(base.loadedContext),
|
|
249
|
+
changeRequest: base.prIdentity,
|
|
250
|
+
checkName: options.checkName,
|
|
251
|
+
...(options.detailsUrl ? { detailsUrl: options.detailsUrl } : {})
|
|
252
|
+
})
|
|
253
|
+
: unsupportedProviderOperation(base.provider, 'checks.resolveRun');
|
|
254
|
+
if (!resolved.ok) return checksResult(providerStatus(resolved.error), {
|
|
255
|
+
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
256
|
+
resource: { kind: 'pull-request', number: base.prNumber }, pullRequest: base.pullRequest,
|
|
257
|
+
error: providerError(resolved.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
|
|
258
|
+
});
|
|
259
|
+
const runId = Number(resolved.value.runId);
|
|
260
|
+
if (!Number.isSafeInteger(runId) || runId <= 0) return checksResult('failed', {
|
|
261
|
+
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
262
|
+
resource: { kind: 'pull-request', number: base.prNumber }, pullRequest: base.pullRequest,
|
|
263
|
+
error: { code: 'CHECK_RUN_IDENTITY_INVALID', message: 'Provider returned an invalid check run identity', retryable: false }
|
|
264
|
+
});
|
|
265
|
+
const jobId = resolved.value.jobId ? Number(resolved.value.jobId) : null;
|
|
266
|
+
return checksResult('no-op', {
|
|
267
|
+
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
268
|
+
resource: { kind: 'pull-request', number: base.prNumber }, pullRequest: base.pullRequest,
|
|
269
|
+
resolution: { status: 'resolved', runId, jobId: jobId !== null && Number.isSafeInteger(jobId) && jobId > 0 ? jobId : null }, error: null
|
|
270
|
+
});
|
|
249
271
|
}
|
|
250
|
-
const listed = base.client.json<{ check_runs?: Array<{ name?: string; details_url?: string }> }>(['api', `repos/${base.context.platform.repository}/commits/${base.pullRequest.head.sha}/check-runs`], { cwd: base.resolved.repoRoot });
|
|
251
|
-
if (!listed.ok) return checksResult(listed.error.retryable ? 'blocked' : 'failed', { error: listed.error });
|
|
252
|
-
const candidates = (listed.value.check_runs || []).flatMap((check) => {
|
|
253
|
-
if (check.name !== options.checkName || !check.details_url) return [];
|
|
254
|
-
const identity = parseRunJobIdentity(check.details_url);
|
|
255
|
-
return identity ? [{ id: identity.runId, jobId: identity.jobId, name: check.name, headSha: base.pullRequest.head.sha }] : [];
|
|
256
|
-
});
|
|
257
|
-
const resolution = resolveRunCandidate(candidates, base.pullRequest.head.sha, options.checkName);
|
|
258
|
-
return checksResult(resolution.status === 'resolved' ? 'no-op' : 'failed', {
|
|
259
|
-
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
260
|
-
resource: { kind: 'pull-request', number: base.prNumber }, pullRequest: base.pullRequest,
|
|
261
|
-
resolution,
|
|
262
|
-
error: resolution.status === 'resolved' ? null : { code: resolution.status === 'ambiguous' ? 'CHECK_RUN_AMBIGUOUS' : 'CHECK_RUN_NOT_FOUND', message: `Check run is ${resolution.status}`, retryable: false }
|
|
263
|
-
});
|
|
264
272
|
}
|
|
265
273
|
|
|
266
274
|
function fetchCheckLogText(client: GitHubClient, args: string[], cwd: string) {
|
|
275
|
+
if (!client.text) return { ok: false as const, error: { code: 'PLATFORM_CLIENT_TEXT_UNAVAILABLE', message: 'Platform client does not support text responses', retryable: false } };
|
|
267
276
|
const fetched = client.text(args, { cwd });
|
|
268
277
|
if (fetched.ok || args[0] !== 'api' || !/response contains terminal escape sequences/i.test(fetched.error.message)) {
|
|
269
278
|
return fetched;
|
|
@@ -271,20 +280,32 @@ function fetchCheckLogText(client: GitHubClient, args: string[], cwd: string) {
|
|
|
271
280
|
return client.text([...args, '--allow-escape-sequences'], { cwd });
|
|
272
281
|
}
|
|
273
282
|
|
|
274
|
-
function fetchPlatformCheckLogs(taskRef: string, options: SharedOptions & { run: number; job?: number }): ChecksResult {
|
|
275
|
-
const base = resolvedTask(taskRef, options);
|
|
283
|
+
async function fetchPlatformCheckLogs(taskRef: string, options: SharedOptions & { run: number; job?: number }): Promise<ChecksResult> {
|
|
284
|
+
const base = await resolvedTask(taskRef, options);
|
|
276
285
|
if (!base.ok) return base.output;
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
286
|
+
{
|
|
287
|
+
const fetched = base.provider.checks?.fetchLogs
|
|
288
|
+
? await base.provider.checks.fetchLogs({
|
|
289
|
+
context: providerOperationContext(base.loadedContext),
|
|
290
|
+
runId: String(options.run),
|
|
291
|
+
...(options.job ? { jobId: String(options.job) } : {})
|
|
292
|
+
})
|
|
293
|
+
: unsupportedProviderOperation(base.provider, 'checks.fetchLogs');
|
|
294
|
+
if (!fetched.ok) return checksResult(providerStatus(fetched.error), {
|
|
295
|
+
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
296
|
+
resource: { kind: 'pull-request', number: base.prNumber }, pullRequest: base.pullRequest,
|
|
297
|
+
error: providerError(fetched.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
|
|
298
|
+
});
|
|
299
|
+
if (!fetched.value.text) return checksResult('failed', {
|
|
300
|
+
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
301
|
+
error: { code: 'CHECK_LOGS_MISSING', message: 'No failed logs are available', retryable: false }
|
|
302
|
+
});
|
|
303
|
+
return checksResult('no-op', {
|
|
304
|
+
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
305
|
+
resource: { kind: 'pull-request', number: base.prNumber }, pullRequest: base.pullRequest,
|
|
306
|
+
logs: { runId: options.run, ...(options.job ? { jobId: options.job } : {}), text: fetched.value.text }, error: null
|
|
307
|
+
});
|
|
308
|
+
}
|
|
288
309
|
}
|
|
289
310
|
|
|
290
311
|
export {
|
|
@@ -292,6 +313,7 @@ export {
|
|
|
292
313
|
classifyRequiredChecks,
|
|
293
314
|
fetchCheckLogText,
|
|
294
315
|
fetchPlatformCheckLogs,
|
|
316
|
+
inspectGitHubRequiredChecks,
|
|
295
317
|
inspectRequiredChecks,
|
|
296
318
|
inspectPullRequestReadiness,
|
|
297
319
|
parseRunJobIdentity,
|
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
3
|
-
import { resolvePlatformContext } from './context.ts';
|
|
1
|
+
import { resolvePlatformProviderContext } from './context.ts';
|
|
2
|
+
import type { PlatformClient } from './context.ts';
|
|
4
3
|
import { platformResult } from './types.ts';
|
|
5
4
|
import type { PlatformOperation, PlatformResult } from './types.ts';
|
|
5
|
+
import {
|
|
6
|
+
providerError,
|
|
7
|
+
providerOperationContext,
|
|
8
|
+
providerStatus,
|
|
9
|
+
providerResourceToken,
|
|
10
|
+
unsupportedProviderOperation
|
|
11
|
+
} from './provider-bridge.ts';
|
|
12
|
+
import { isResourceIdentity, resourceIdentityNumber, reviewMarker as resourceReviewMarker } from './resource-identity.ts';
|
|
13
|
+
import type { ResourceIdentity } from './resource-identity.ts';
|
|
6
14
|
|
|
7
15
|
export type PrReviewEvent = 'COMMENT' | 'APPROVE' | 'REQUEST_CHANGES';
|
|
8
|
-
export type PrReviewIdentity = { scope: string; round: number; commitSha: string };
|
|
9
|
-
export type PrReviewListEntry = { id: number; commitId: string; body: string; url: string };
|
|
16
|
+
export type PrReviewIdentity = { scope: string; round: number; commitSha: string; resource?: ResourceIdentity };
|
|
17
|
+
export type PrReviewListEntry = { id: number | string; commitId: string; body: string; url: string };
|
|
10
18
|
export type PrReviewListResult = PlatformResult & { reviews: PrReviewListEntry[] };
|
|
11
19
|
|
|
12
|
-
type RemoteReview = {
|
|
13
|
-
id?: number;
|
|
14
|
-
node_id?: string;
|
|
15
|
-
commit_id?: string;
|
|
16
|
-
body?: string | null;
|
|
17
|
-
state?: string;
|
|
18
|
-
html_url?: string;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
20
|
const REVIEW_EVENTS: readonly PrReviewEvent[] = ['COMMENT', 'APPROVE', 'REQUEST_CHANGES'];
|
|
22
21
|
// Marker-safe scope: a task id (`TASK-YYYYMMDD-HHMMSS`) or a bare PR number
|
|
23
22
|
// (`pr{N}`). Any other value (e.g. one containing `\r\n` or `-->`) would break
|
|
@@ -25,7 +24,10 @@ const REVIEW_EVENTS: readonly PrReviewEvent[] = ['COMMENT', 'APPROVE', 'REQUEST_
|
|
|
25
24
|
const REVIEW_SCOPE_PATTERN = /^(?:pr\d+|TASK-\d{8}-\d{6})$/;
|
|
26
25
|
|
|
27
26
|
export function reviewMarker(identity: PrReviewIdentity): string {
|
|
28
|
-
|
|
27
|
+
const scope = identity.resource && isResourceIdentity(identity.resource) && identity.resource.kind !== 'number'
|
|
28
|
+
? resourceReviewMarker(identity.resource)
|
|
29
|
+
: identity.scope;
|
|
30
|
+
return `<!-- review-pr:${scope}:r${identity.round} -->`;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export function reviewedCommitMarker(sha: string): string {
|
|
@@ -44,105 +46,98 @@ function hasUsableContext(context: PlatformResult): boolean {
|
|
|
44
46
|
return (context.status === 'no-op' || context.status === 'degraded') && context.platform.repository !== null;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
return !Number.isInteger(prNumber) || prNumber <= 0;
|
|
49
|
-
}
|
|
49
|
+
type PrToken = string | number;
|
|
50
50
|
|
|
51
|
-
export function listPrReviews(prNumber:
|
|
52
|
-
const
|
|
53
|
-
const context =
|
|
51
|
+
export async function listPrReviews(prNumber: PrToken, options: { cwd?: string; client?: PlatformClient } = {}): Promise<PrReviewListResult> {
|
|
52
|
+
const loaded = await resolvePlatformProviderContext({ cwd: options.cwd || process.cwd(), client: options.client });
|
|
53
|
+
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
54
54
|
if (!hasUsableContext(context)) {
|
|
55
55
|
return { ...platformResult(context.status, { platform: context.platform, capabilities: context.capabilities, error: context.error }), reviews: [] };
|
|
56
56
|
}
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
57
|
+
const identity = loaded.ok ? (() => { try { return providerResourceToken(loaded.value.provider, 'pull-request', String(prNumber)); } catch { return null; } })() : null;
|
|
58
|
+
if (!identity) {
|
|
59
59
|
return {
|
|
60
60
|
...platformResult('failed', {
|
|
61
61
|
platform: context.platform, capabilities: context.capabilities,
|
|
62
|
-
error: { code: '
|
|
62
|
+
error: { code: 'PLATFORM_IDENTITY_TOKEN_INVALID', message: 'Pull request token is invalid', retryable: false }
|
|
63
63
|
}),
|
|
64
64
|
reviews: []
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
67
|
+
const identityNumber = resourceIdentityNumber(identity);
|
|
68
|
+
if (loaded.ok) {
|
|
69
|
+
const fetched = loaded.value.provider.reviews?.list
|
|
70
|
+
? await loaded.value.provider.reviews.list({ context: providerOperationContext(loaded.value), changeRequest: identity })
|
|
71
|
+
: unsupportedProviderOperation(loaded.value.provider, 'reviews.list');
|
|
72
|
+
if (!fetched.ok) return {
|
|
73
|
+
...platformResult(providerStatus(fetched.error), {
|
|
74
|
+
platform: context.platform, capabilities: context.capabilities,
|
|
75
|
+
resource: { kind: 'pull-request', number: identityNumber, identity }, error: providerError(fetched.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
|
|
76
|
+
}), reviews: []
|
|
77
|
+
};
|
|
69
78
|
return {
|
|
70
|
-
...platformResult(
|
|
79
|
+
...platformResult('no-op', {
|
|
71
80
|
platform: context.platform, capabilities: context.capabilities,
|
|
72
|
-
resource: { kind: 'pull-request', number:
|
|
81
|
+
resource: { kind: 'pull-request', number: identityNumber, identity }, error: null
|
|
73
82
|
}),
|
|
74
|
-
reviews:
|
|
83
|
+
reviews: fetched.value.map((review) => ({ id: review.id, commitId: review.commitSha || '', body: review.body, url: review.displayUrl || '' }))
|
|
75
84
|
};
|
|
76
85
|
}
|
|
77
|
-
|
|
78
|
-
const reviews: PrReviewListEntry[] = flat
|
|
79
|
-
.map((review) => ({
|
|
80
|
-
id: review.id ?? 0,
|
|
81
|
-
commitId: review.commit_id ?? '',
|
|
82
|
-
body: review.body ?? '',
|
|
83
|
-
url: review.html_url ?? ''
|
|
84
|
-
}))
|
|
85
|
-
.filter((review) => review.body !== '');
|
|
86
|
-
return {
|
|
87
|
-
...platformResult('no-op', {
|
|
88
|
-
platform: context.platform, capabilities: context.capabilities,
|
|
89
|
-
resource: { kind: 'pull-request', number: prNumber }, error: null
|
|
90
|
-
}),
|
|
91
|
-
reviews
|
|
92
|
-
};
|
|
86
|
+
return { ...platformResult('failed', { platform: context.platform, capabilities: context.capabilities, error: context.error }), reviews: [] };
|
|
93
87
|
}
|
|
94
88
|
|
|
95
|
-
export function publishPrReview(options: {
|
|
89
|
+
export async function publishPrReview(options: {
|
|
96
90
|
cwd?: string;
|
|
97
|
-
client?:
|
|
91
|
+
client?: PlatformClient;
|
|
98
92
|
dryRun?: boolean;
|
|
99
|
-
prNumber:
|
|
93
|
+
prNumber: PrToken;
|
|
100
94
|
identity: PrReviewIdentity;
|
|
101
95
|
event: PrReviewEvent;
|
|
102
96
|
body: string;
|
|
103
|
-
}): PlatformResult {
|
|
104
|
-
const
|
|
105
|
-
const context =
|
|
97
|
+
}): Promise<PlatformResult> {
|
|
98
|
+
const loaded = await resolvePlatformProviderContext({ cwd: options.cwd || process.cwd(), client: options.client });
|
|
99
|
+
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
106
100
|
if (!hasUsableContext(context)) {
|
|
107
101
|
return platformResult(context.status, { platform: context.platform, capabilities: context.capabilities, error: context.error });
|
|
108
102
|
}
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
103
|
+
const identity = loaded.ok ? (() => { try { return providerResourceToken(loaded.value.provider, 'pull-request', String(options.prNumber)); } catch { return null; } })() : null;
|
|
104
|
+
if (!identity) {
|
|
111
105
|
return platformResult('failed', {
|
|
112
106
|
platform: context.platform, capabilities: context.capabilities,
|
|
113
|
-
error: { code: '
|
|
107
|
+
error: { code: 'PLATFORM_IDENTITY_TOKEN_INVALID', message: 'Pull request token is invalid', retryable: false }
|
|
114
108
|
});
|
|
115
109
|
}
|
|
110
|
+
const identityNumber = resourceIdentityNumber(identity);
|
|
116
111
|
if (!REVIEW_EVENTS.includes(options.event)) {
|
|
117
112
|
return platformResult('failed', {
|
|
118
113
|
platform: context.platform, capabilities: context.capabilities,
|
|
119
|
-
resource: { kind: 'pull-request', number:
|
|
114
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
120
115
|
error: { code: 'REVIEW_EVENT_INVALID', message: `review event must be one of ${REVIEW_EVENTS.join('|')}`, retryable: false }
|
|
121
116
|
});
|
|
122
117
|
}
|
|
123
118
|
if (!REVIEW_SCOPE_PATTERN.test(options.identity.scope) || !/^[0-9a-f]{7,40}$/i.test(options.identity.commitSha)) {
|
|
124
119
|
return platformResult('failed', {
|
|
125
120
|
platform: context.platform, capabilities: context.capabilities,
|
|
126
|
-
resource: { kind: 'pull-request', number:
|
|
121
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
127
122
|
error: { code: 'REVIEW_IDENTITY_INVALID', message: 'review scope must be a task id or pr{N}; commitSha is required', retryable: false }
|
|
128
123
|
});
|
|
129
124
|
}
|
|
130
125
|
|
|
131
|
-
const marker = reviewMarker(options.identity);
|
|
132
|
-
const listed = listPrReviews(options.prNumber, { cwd: options.cwd, client });
|
|
126
|
+
const marker = reviewMarker({ ...options.identity, resource: identity });
|
|
127
|
+
const listed = await listPrReviews(options.prNumber, { cwd: options.cwd, client: options.client });
|
|
133
128
|
if (listed.status === 'failed' || listed.status === 'blocked') return listed;
|
|
134
129
|
const existing = listed.reviews.find((review) => firstLine(review.body) === marker) ?? null;
|
|
135
130
|
if (existing) {
|
|
136
131
|
if (existing.commitId === options.identity.commitSha) {
|
|
137
132
|
return platformResult('no-op', {
|
|
138
133
|
platform: context.platform, capabilities: context.capabilities,
|
|
139
|
-
resource: { kind: 'pull-request', number:
|
|
134
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
140
135
|
operations: [{ name: 'review:publish', status: 'no-op', reasonCode: null }], error: null
|
|
141
136
|
});
|
|
142
137
|
}
|
|
143
138
|
return platformResult('failed', {
|
|
144
139
|
platform: context.platform, capabilities: context.capabilities,
|
|
145
|
-
resource: { kind: 'pull-request', number:
|
|
140
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
146
141
|
operations: [{ name: 'review:publish', status: 'failed', reasonCode: 'REVIEW_MARKER_CONFLICT' }],
|
|
147
142
|
error: { code: 'REVIEW_MARKER_CONFLICT', message: 'A review with the same marker targets a different commit; start a new round', retryable: false }
|
|
148
143
|
});
|
|
@@ -159,43 +154,51 @@ export function publishPrReview(options: {
|
|
|
159
154
|
if (options.dryRun) {
|
|
160
155
|
return platformResult('planned', {
|
|
161
156
|
platform: context.platform, capabilities: context.capabilities,
|
|
162
|
-
resource: { kind: 'pull-request', number:
|
|
157
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
163
158
|
operations: [{ name: 'review:publish', status: 'planned', reasonCode: null }], error: null
|
|
164
159
|
});
|
|
165
160
|
}
|
|
166
161
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
162
|
+
if (loaded.ok) {
|
|
163
|
+
const published = loaded.value.provider.reviews?.publish
|
|
164
|
+
? await loaded.value.provider.reviews.publish({
|
|
165
|
+
context: providerOperationContext(loaded.value),
|
|
166
|
+
changeRequest: identity,
|
|
167
|
+
identity: options.identity,
|
|
168
|
+
event: options.event,
|
|
169
|
+
body: wrappedBody,
|
|
170
|
+
mutation: { idempotencyKey: `review:publish:${marker}` }
|
|
171
|
+
})
|
|
172
|
+
: unsupportedProviderOperation(loaded.value.provider, 'reviews.publish');
|
|
173
|
+
if (!published.ok) {
|
|
174
|
+
if (published.error.retryable) {
|
|
175
|
+
const reconciled = await listPrReviews(options.prNumber, { cwd: options.cwd, client: options.client });
|
|
176
|
+
const found = reconciled.reviews.find((review) => firstLine(review.body) === marker);
|
|
177
|
+
if (found) return platformResult('applied', {
|
|
178
178
|
changed: true, platform: context.platform, capabilities: context.capabilities,
|
|
179
|
-
resource: { kind: 'pull-request', number:
|
|
179
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
180
180
|
operations: [{ name: 'review:publish', status: 'applied', reasonCode: 'CREATE_RECONCILED' }], error: null
|
|
181
181
|
});
|
|
182
|
+
return platformResult('blocked', {
|
|
183
|
+
platform: context.platform, capabilities: context.capabilities,
|
|
184
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
185
|
+
operations: [{ name: 'review:publish', status: 'failed', reasonCode: 'REVIEW_CREATE_OUTCOME_UNKNOWN' }],
|
|
186
|
+
error: { code: 'REVIEW_CREATE_OUTCOME_UNKNOWN', message: published.error.message, retryable: true }
|
|
187
|
+
});
|
|
182
188
|
}
|
|
183
|
-
return platformResult(
|
|
189
|
+
return platformResult(providerStatus(published.error), {
|
|
184
190
|
platform: context.platform, capabilities: context.capabilities,
|
|
185
|
-
resource: { kind: 'pull-request', number:
|
|
186
|
-
operations: [{ name: 'review:publish', status: 'failed', reasonCode:
|
|
187
|
-
error:
|
|
191
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
192
|
+
operations: [{ name: 'review:publish', status: 'failed', reasonCode: published.error.code }],
|
|
193
|
+
error: providerError(published.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
|
|
188
194
|
});
|
|
189
195
|
}
|
|
190
|
-
return platformResult('
|
|
196
|
+
return platformResult('applied', {
|
|
197
|
+
changed: published.value.changed,
|
|
191
198
|
platform: context.platform, capabilities: context.capabilities,
|
|
192
|
-
resource: { kind: 'pull-request', number:
|
|
193
|
-
operations: [{ name: 'review:publish', status: '
|
|
199
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
200
|
+
operations: [{ name: 'review:publish', status: 'applied', reasonCode: null }], error: null
|
|
194
201
|
});
|
|
195
202
|
}
|
|
196
|
-
return platformResult('
|
|
197
|
-
changed: true, platform: context.platform, capabilities: context.capabilities,
|
|
198
|
-
resource: { kind: 'pull-request', number: options.prNumber },
|
|
199
|
-
operations: [{ name: 'review:publish', status: 'applied', reasonCode: null }], error: null
|
|
200
|
-
});
|
|
203
|
+
return platformResult('failed', { platform: context.platform, capabilities: context.capabilities, error: context.error });
|
|
201
204
|
}
|