fix. harden Go review flow
Fix runner bootstrap and auth handling, preserve queued SHAs, make event/job acceptance atomic, fence stale runs, correct retries and prompts, add fake end-to-end coverage, and fix deployment defaults. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+38
-17
@@ -66,13 +66,17 @@ func (w *Worker) process(ctx context.Context, job domain.Job, run domain.ReviewR
|
||||
if err != nil {
|
||||
return w.fail(ctx, job, run, err)
|
||||
}
|
||||
if job.HeadSHA != "" && job.HeadSHA != "unknown" {
|
||||
// The queued SHA is immutable job input. The PR may have advanced while queued.
|
||||
pr.HeadSHA = job.HeadSHA
|
||||
}
|
||||
if pr.IsFork && !w.settings.AllowUntrustedForks {
|
||||
message := "Skipped review for fork PR because `ALLOW_UNTRUSTED_FORKS=false`."
|
||||
_, postErr := w.gitea.PostIssueComment(ctx, job.Repo, job.PRNumber, message)
|
||||
if postErr != nil {
|
||||
return w.fail(ctx, job, run, postErr)
|
||||
}
|
||||
return w.store.FinishJob(ctx, job.ID, run.ID, true, true, &domain.ReviewResult{Verdict: "correct", Confidence: 1, Summary: message, Findings: []domain.Finding{}}, nil)
|
||||
return w.finish(ctx, job.ID, run.ID, true, true, &domain.ReviewResult{Verdict: "correct", Confidence: 1, Summary: message, Findings: []domain.Finding{}}, nil)
|
||||
}
|
||||
cfg := review.MissingRepoConfig()
|
||||
text, configured, cfgErr := w.gitea.GetFileContent(ctx, job.Repo, ".codex-review.yml", pr.HeadSHA)
|
||||
@@ -85,12 +89,13 @@ func (w *Worker) process(ctx context.Context, job domain.Job, run domain.ReviewR
|
||||
return w.fail(ctx, job, run, err)
|
||||
}
|
||||
}
|
||||
review.ApplyServerMaxDiff(&cfg, w.settings.MaxDiffBytes)
|
||||
if !cfg.Enabled {
|
||||
_, postErr := w.gitea.PostIssueComment(ctx, job.Repo, job.PRNumber, review.FormatDisabledAck())
|
||||
if postErr != nil {
|
||||
return w.fail(ctx, job, run, postErr)
|
||||
}
|
||||
return w.store.FinishJob(ctx, job.ID, run.ID, true, true, &domain.ReviewResult{Verdict: "correct", Confidence: 1, Summary: review.FormatDisabledAck(), Findings: []domain.Finding{}}, nil)
|
||||
return w.finish(ctx, job.ID, run.ID, true, true, &domain.ReviewResult{Verdict: "correct", Confidence: 1, Summary: review.FormatDisabledAck(), Findings: []domain.Finding{}}, nil)
|
||||
}
|
||||
review.ResolveMode(&cmd, cfg)
|
||||
result, err := w.runner.Run(ctx, pr, cmd, cfg)
|
||||
@@ -105,13 +110,13 @@ func (w *Worker) process(ctx context.Context, job domain.Job, run domain.ReviewR
|
||||
if err := w.store.UpsertBotComment(ctx, job.Repo, job.PRNumber, "codex-review", commentID, pr.HeadSHA); err != nil {
|
||||
return w.fail(ctx, job, run, err)
|
||||
}
|
||||
return w.store.FinishJob(ctx, job.ID, run.ID, true, false, &result, nil)
|
||||
return w.finish(ctx, job.ID, run.ID, true, false, &result, nil)
|
||||
}
|
||||
func (w *Worker) processNonReview(ctx context.Context, job domain.Job, run domain.ReviewRun, cmd domain.ParsedCommand) error {
|
||||
switch cmd.Name {
|
||||
case "ignore":
|
||||
result := domain.ReviewResult{Verdict: "correct", Confidence: 1, Summary: "Ignore command acknowledged. No review run executed.", Findings: []domain.Finding{}}
|
||||
return w.store.FinishJob(ctx, job.ID, run.ID, true, true, &result, nil)
|
||||
return w.finish(ctx, job.ID, run.ID, true, true, &result, nil)
|
||||
case "explain":
|
||||
latest, err := w.store.LatestSuccessfulReview(ctx, job.Repo, job.PRNumber)
|
||||
if err != nil {
|
||||
@@ -127,7 +132,7 @@ func (w *Worker) processNonReview(ctx context.Context, job domain.Job, run domai
|
||||
if _, err := w.gitea.PostIssueComment(ctx, job.Repo, job.PRNumber, message); err != nil {
|
||||
return w.fail(ctx, job, run, err)
|
||||
}
|
||||
return w.store.FinishJob(ctx, job.ID, run.ID, true, true, &domain.ReviewResult{Verdict: "correct", Confidence: 1, Summary: message, Findings: []domain.Finding{}}, nil)
|
||||
return w.finish(ctx, job.ID, run.ID, true, true, &domain.ReviewResult{Verdict: "correct", Confidence: 1, Summary: message, Findings: []domain.Finding{}}, nil)
|
||||
case "help":
|
||||
comments, err := w.gitea.GetIssueComments(ctx, job.Repo, job.PRNumber)
|
||||
if err != nil {
|
||||
@@ -141,7 +146,7 @@ func (w *Worker) processNonReview(ctx context.Context, job domain.Job, run domai
|
||||
if _, err := w.gitea.PostIssueComment(ctx, job.Repo, job.PRNumber, message); err != nil {
|
||||
return w.fail(ctx, job, run, err)
|
||||
}
|
||||
return w.store.FinishJob(ctx, job.ID, run.ID, true, true, &domain.ReviewResult{Verdict: "correct", Confidence: 1, Summary: "Help/status summary posted.", Findings: []domain.Finding{}}, nil)
|
||||
return w.finish(ctx, job.ID, run.ID, true, true, &domain.ReviewResult{Verdict: "correct", Confidence: 1, Summary: "Help/status summary posted.", Findings: []domain.Finding{}}, nil)
|
||||
}
|
||||
return w.fail(ctx, job, run, fmt.Errorf("unsupported worker command %q", cmd.Name))
|
||||
}
|
||||
@@ -150,25 +155,41 @@ func (w *Worker) fail(ctx context.Context, job domain.Job, run domain.ReviewRun,
|
||||
if errorText == "" {
|
||||
errorText = "review failed"
|
||||
}
|
||||
if _, postErr := w.gitea.PostIssueComment(ctx, job.Repo, job.PRNumber, review.FailureComment(job.HeadSHA, errorText)); postErr != nil {
|
||||
finalCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
if _, postErr := w.gitea.PostIssueComment(finalCtx, job.Repo, job.PRNumber, review.FailureComment(job.HeadSHA, errorText)); postErr != nil {
|
||||
w.logger.Error("post failure comment", "job_id", job.ID, "error", postErr)
|
||||
}
|
||||
return w.store.FinishJob(ctx, job.ID, run.ID, false, false, nil, fmt.Errorf("%s", errorText))
|
||||
return w.store.FinishJob(finalCtx, job.ID, run.ID, false, false, nil, fmt.Errorf("%s", errorText))
|
||||
}
|
||||
func (w *Worker) finish(ctx context.Context, jobID, runID int64, success, skipped bool, result *domain.ReviewResult, err error) error {
|
||||
if ctx.Err() != nil {
|
||||
finalCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
return w.store.FinishJob(finalCtx, jobID, runID, success, skipped, result, err)
|
||||
}
|
||||
return w.store.FinishJob(ctx, jobID, runID, success, skipped, result, err)
|
||||
}
|
||||
func commandFromJob(job domain.Job, aliases map[string]bool) domain.ParsedCommand {
|
||||
if parsed, ok := commands.Parse(job.TriggerCommentBody, aliases); ok {
|
||||
return parsed
|
||||
}
|
||||
args := strings.Fields(job.CommandArgs)
|
||||
return domain.ParsedCommand{Name: job.Command, Raw: job.TriggerCommentBody, Arguments: args, Mode: "summary", Full: contains(args, "--full")}
|
||||
}
|
||||
func contains(items []string, needle string) bool {
|
||||
for _, item := range items {
|
||||
if item == needle {
|
||||
return true
|
||||
var args []string
|
||||
if json.Unmarshal([]byte(job.CommandArgs), &args) != nil {
|
||||
args = strings.Fields(job.CommandArgs)
|
||||
}
|
||||
cmd := domain.ParsedCommand{Name: job.Command, Raw: job.TriggerCommentBody, Arguments: args, Mode: "summary"}
|
||||
if job.Command == "review" {
|
||||
for _, arg := range args {
|
||||
switch arg {
|
||||
case "security", "performance", "tests":
|
||||
cmd.Mode, cmd.ModeExplicit = arg, true
|
||||
case "--full":
|
||||
cmd.Mode, cmd.ModeExplicit, cmd.Full = "full", true, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return cmd
|
||||
}
|
||||
func helpComment(comments []map[string]any, bot string, pending int) string {
|
||||
bot = strings.ToLower(strings.TrimSpace(bot))
|
||||
@@ -197,7 +218,7 @@ func helpComment(comments []map[string]any, bot string, pending int) string {
|
||||
lines = append(lines, fmt.Sprintf("- @%s: %s", user, body))
|
||||
}
|
||||
}
|
||||
lines[11] = fmt.Sprintf("Discussion summary (%d comments, human `%d`, bot `%d`):", len(comments), human, bots)
|
||||
lines[12] = fmt.Sprintf("Discussion summary (%d comments, human `%d`, bot `%d`):", len(comments), human, bots)
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
func sleep(ctx context.Context, duration time.Duration) {
|
||||
|
||||
Reference in New Issue
Block a user