Compare commits

...
Author SHA1 Message Date
chichuan a0c64e5ef4 fix(ci): restore eval dispatch PR comments 2026-08-11 23:57:12 +08:00
github-actions[bot] 7a140e59c3 Merge pull request #946 from DingTalk-Real-AI/codex/minutes-shortcuts
feat(minutes): align and expand shortcut workflows
2026-08-11 23:20:21 +08:00
2 changed files with 118 additions and 30 deletions
+15 -29
View File
@@ -34,8 +34,8 @@ jobs:
timeout-minutes: 5
permissions:
contents: read
issues: write
pull-requests: read
# 该 job 仅处理 PR;评论写入也限定在 PR Conversation 这一权限域。
pull-requests: write
steps:
- name: Check out default branch tooling
uses: actions/checkout@v4
@@ -69,12 +69,10 @@ jobs:
PARSE_ERROR: ${{ steps.parse.outputs.error }}
run: |
body="❌ /eval 命令解析失败:${PARSE_ERROR}"
jq -n --arg body "$body" '{body: $body}' | curl --fail --silent --show-error \
-X POST \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
--data @- \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" > /dev/null
gh api --method POST \
"repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \
--raw-field body="$body" \
> /dev/null
exit 1
- name: Verify reviewed PR head
@@ -103,13 +101,9 @@ jobs:
set -euo pipefail
placeholder_body="🛰️ /eval 请求已通过权限与版本校验,正在生成可验证的评测请求。"
response="$(
jq -n --arg body "$placeholder_body" '{body: $body}' \
| curl --fail --silent --show-error \
-X POST \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
--data @- \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments"
gh api --method POST \
"repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \
--raw-field body="$placeholder_body"
)"
comment_id="$(
printf '%s' "$response" \
@@ -277,13 +271,9 @@ jobs:
fi
body="<!-- eval-dispatch: ${marker_json} -->"$'\n'"🛰️ /eval 已受理:产品集 \`${PRODUCTS}\`${cases_note},评测对象 \`${PR_HEAD_SHA}\`。"$'\n'"受控评测服务将在数分钟内处理,完成后由 bot 回贴报告。"
response="$(
jq -n --arg body "$body" '{body: $body}' \
| curl --fail --silent --show-error \
-X PATCH \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
--data @- \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/comments/${DISPATCH_COMMENT_ID}"
gh api --method PATCH \
"repos/${GITHUB_REPOSITORY}/issues/comments/${DISPATCH_COMMENT_ID}" \
--raw-field body="$body"
)"
printf '%s' "$response" \
| jq -e \
@@ -299,12 +289,8 @@ jobs:
DISPATCH_COMMENT_ID: ${{ steps.placeholder.outputs.comment_id }}
run: |
failure_body="❌ /eval 请求准备失败,未生成可消费的评测请求。请稍后重试。"
jq -n --arg body "$failure_body" '{body: $body}' \
| curl --fail --silent --show-error \
-X PATCH \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
--data @- \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/comments/${DISPATCH_COMMENT_ID}" \
gh api --method PATCH \
"repos/${GITHUB_REPOSITORY}/issues/comments/${DISPATCH_COMMENT_ID}" \
--raw-field body="$failure_body" \
> /dev/null \
|| true
+103 -1
View File
@@ -44,6 +44,84 @@ func TestEvalDispatchWorkflowUsesRepositoryPermissionAndReviewedSHA(t *testing.T
}
}
func TestEvalDispatchWorkflowCanWritePRConversationComments(t *testing.T) {
t.Parallel()
root, err := filepath.Abs(filepath.Join("..", ".."))
if err != nil {
t.Fatalf("resolve repository root: %v", err)
}
data, err := os.ReadFile(filepath.Join(root, ".github", "workflows", "eval-dispatch.yml"))
if err != nil {
t.Fatalf("read eval-dispatch workflow: %v", err)
}
workflow := string(data)
dispatchStart := strings.Index(workflow, "jobs:\n dispatch:")
if dispatchStart < 0 {
t.Fatal("eval-dispatch workflow missing dispatch job")
}
stepsOffset := strings.Index(workflow[dispatchStart:], "\n steps:")
if stepsOffset < 0 {
t.Fatal("eval-dispatch workflow missing dispatch steps")
}
permissions := workflow[dispatchStart : dispatchStart+stepsOffset]
if !containsTrimmedLine(permissions, "pull-requests: write") {
t.Error("dispatch job must grant write permission for PR conversation comments")
}
if containsTrimmedLine(permissions, "pull-requests: read") {
t.Error("dispatch job still limits pull requests to read-only")
}
if containsTrimmedLine(permissions, "issues: write") {
t.Error("dispatch job must keep a single write domain instead of granting issue-wide writes")
}
commentWrites := []struct {
step string
method string
endpoint string
}{
{
step: "Reply usage on parse failure",
method: "POST",
endpoint: "issues/${PR_NUMBER}/comments",
},
{
step: "Create dispatch placeholder",
method: "POST",
endpoint: "issues/${PR_NUMBER}/comments",
},
{
step: "Finalize dispatch marker",
method: "PATCH",
endpoint: "issues/comments/${DISPATCH_COMMENT_ID}",
},
{
step: "Mark dispatch preparation failure",
method: "PATCH",
endpoint: "issues/comments/${DISPATCH_COMMENT_ID}",
},
}
for _, write := range commentWrites {
write := write
t.Run(write.step, func(t *testing.T) {
t.Parallel()
step := evalDispatchWorkflowStep(t, workflow, write.step)
if !strings.Contains(step, "gh api --method "+write.method) {
t.Errorf("step %q must use gh api so GitHub's HTTP error message remains visible", write.step)
}
if !strings.Contains(step, write.endpoint) {
t.Errorf("step %q missing comment endpoint %q", write.step, write.endpoint)
}
for _, forbidden := range []string{"curl --fail", "Authorization: Bearer", "2>/dev/null"} {
if strings.Contains(step, forbidden) {
t.Errorf("step %q hides or manually transports API diagnostics via %q", write.step, forbidden)
}
}
})
}
}
func TestEvalDispatchWorkflowPublishesArtifactBoundRequest(t *testing.T) {
t.Parallel()
@@ -102,7 +180,7 @@ func TestEvalDispatchWorkflowPublishesArtifactBoundRequest(t *testing.T) {
"ARTIFACT_ID: ${{ steps.artifact.outputs.artifact-id }}",
"ARTIFACT_DIGEST: ${{ steps.artifact.outputs.artifact-digest }}",
"<!-- eval-dispatch: ${marker_json} -->",
"-X PATCH",
"gh api --method PATCH",
"issues/comments/${DISPATCH_COMMENT_ID}",
} {
if !strings.Contains(workflow, want) {
@@ -120,6 +198,30 @@ func TestEvalDispatchWorkflowPublishesArtifactBoundRequest(t *testing.T) {
}
}
func evalDispatchWorkflowStep(t *testing.T, workflow, name string) string {
t.Helper()
marker := " - name: " + name + "\n"
start := strings.Index(workflow, marker)
if start < 0 {
t.Fatalf("eval-dispatch workflow missing step %q", name)
}
rest := workflow[start+len(marker):]
if end := strings.Index(rest, "\n - name: "); end >= 0 {
return rest[:end]
}
return rest
}
func containsTrimmedLine(text, want string) bool {
for _, line := range strings.Split(text, "\n") {
if strings.TrimSpace(line) == want {
return true
}
}
return false
}
func TestEvalPollValidatePython(t *testing.T) {
t.Parallel()