iop/packages/go/agenttask/review.go

173 lines
5.8 KiB
Go

package agenttask
import (
"context"
"fmt"
)
func (m *Manager) reviewSubmission(
ctx context.Context,
project ProjectRecord,
work WorkRecord,
submission Submission,
) (bool, error) {
result, err := m.reviewer.Review(ctx, ReviewRequest{
Project: project, Work: work, Submission: submission,
IdempotencyKey: reviewKey(project.ProjectID, work.Unit.ID, work.Attempt, submission.ArtifactID),
})
if err != nil {
m.blockWork(ctx, project.ProjectID, work.Unit.ID, WorkStateBlocked, Blocker{
Code: BlockerReviewFailed, Message: err.Error(), Retryable: true,
})
return false, nil
}
if result.ProjectID != project.ProjectID || result.WorkUnitID != work.Unit.ID ||
result.AttemptID != work.AttemptID || result.ArtifactID != submission.ArtifactID {
m.blockWork(ctx, project.ProjectID, work.Unit.ID, WorkStateBlocked, Blocker{
Code: BlockerArtifactMismatch, Message: "official review result identity mismatch",
})
return false, nil
}
var cmdID CommandID
var wfRev WorkflowRevision
if project.Intent != nil {
cmdID = project.Intent.CommandID
wfRev = project.Intent.WorkflowRevision
}
if result.Verdict == ReviewVerdictPass {
if result.ChangeSet == nil || result.ChangeSet.ArtifactID != submission.ArtifactID {
m.blockWork(ctx, project.ProjectID, work.Unit.ID, WorkStateBlocked, Blocker{
Code: BlockerArtifactMismatch, Message: "PASS review is missing an exact artifact change-set identity",
})
return false, nil
}
if err := validateIdentity("change_set", string(result.ChangeSet.ID)); err != nil {
m.blockWork(ctx, project.ProjectID, work.Unit.ID, WorkStateBlocked, Blocker{
Code: BlockerArtifactMismatch, Message: err.Error(),
})
return false, nil
}
if err := validateIdentity("change_set_revision", result.ChangeSet.Revision); err != nil {
m.blockWork(ctx, project.ProjectID, work.Unit.ID, WorkStateBlocked, Blocker{
Code: BlockerArtifactMismatch, Message: err.Error(),
})
return false, nil
}
}
committedReview := result
if result.ChangeSet != nil {
changeSet := *result.ChangeSet
committedReview.ChangeSet = &changeSet
}
if err := m.changeWork(ctx, project.ProjectID, work.Unit.ID, func(work *WorkRecord) error {
work.Review = &committedReview
return nil
}); err != nil {
return false, err
}
m.emit(ctx, Event{
Type: EventReviewResult, ProjectID: project.ProjectID, WorkspaceID: project.WorkspaceID,
WorkUnitID: work.Unit.ID, CommandID: cmdID, WorkflowRevision: wfRev, AttemptID: work.AttemptID, Ordinal: work.DispatchOrdinal,
Detail: string(result.Verdict),
})
switch result.Verdict {
case ReviewVerdictPass:
err := m.changeWork(ctx, project.ProjectID, work.Unit.ID, func(work *WorkRecord) error {
if err := transitionWork(work, WorkStatePendingIntegration); err != nil {
return err
}
changeSet := *result.ChangeSet
work.ChangeSet = &changeSet
if work.Locators == nil {
work.Locators = make(map[LocatorKind]LocatorRecord)
}
work.Locators[LocatorChangeSet] = locatorForChangeSet(project, *work, changeSet)
work.Blocker = nil
resetFailure(work, FailureStageReview)
return nil
})
return false, err
case ReviewVerdictWarn, ReviewVerdictFail:
var rework bool
var exhausted bool
var nextAttempt AttemptID
err := m.changeWork(ctx, project.ProjectID, work.Unit.ID, func(work *WorkRecord) error {
failure := m.recordFailure(work, FailureStageReview, Blocker{
Code: BlockerReviewFailed,
Message: fmt.Sprintf("official review ended with %s: %s", result.Verdict, result.Message),
Retryable: result.Rework,
})
if failure.Code == BlockerFailureBudgetExhausted {
if err := transitionWork(work, WorkStateBlocked); err != nil {
return err
}
work.Blocker = &failure
exhausted = true
return nil
}
if result.Rework && work.Attempt < m.config.MaxReworkAttempts {
if err := transitionWork(work, WorkStateReady); err != nil {
return err
}
work.Attempt++
work.AttemptID = attemptID(work.Unit.ID, work.Attempt)
work.Target = nil
work.Isolation = nil
work.Submission = nil
work.ChangeSet = nil
work.Locators = make(map[LocatorKind]LocatorRecord)
work.Blocker = nil
rework = true
nextAttempt = work.AttemptID
return nil
}
code := BlockerReviewFailed
if result.Rework {
code = BlockerReviewReworkExhausted
}
if err := transitionWork(work, WorkStateBlocked); err != nil {
return err
}
work.Blocker = &Blocker{
Code: code,
Message: fmt.Sprintf("official review ended with %s: %s", result.Verdict, result.Message),
}
return nil
})
if err != nil {
return false, err
}
if rework {
m.emit(ctx, Event{
Type: EventFollowup, ProjectID: project.ProjectID, WorkspaceID: project.WorkspaceID,
WorkUnitID: work.Unit.ID, CommandID: cmdID, WorkflowRevision: wfRev, AttemptID: nextAttempt,
Ordinal: work.DispatchOrdinal, Detail: string(result.Verdict),
})
return true, nil
}
if exhausted {
m.emit(ctx, Event{
Type: EventBlocked, ProjectID: project.ProjectID, WorkspaceID: project.WorkspaceID,
WorkUnitID: work.Unit.ID, AttemptID: work.AttemptID,
State: WorkStateBlocked, Detail: string(BlockerFailureBudgetExhausted),
})
return false, nil
}
m.emit(ctx, Event{
Type: EventBlocked, ProjectID: project.ProjectID, WorkspaceID: project.WorkspaceID,
WorkUnitID: work.Unit.ID, AttemptID: work.AttemptID,
State: WorkStateBlocked, Detail: string(BlockerReviewReworkExhausted),
})
return false, nil
case ReviewVerdictUserReview:
m.blockWork(ctx, project.ProjectID, work.Unit.ID, WorkStateTerminalDeferred, Blocker{
Code: BlockerUserReview, Message: result.Message,
})
return false, nil
default:
m.blockWork(ctx, project.ProjectID, work.Unit.ID, WorkStateBlocked, Blocker{
Code: BlockerReviewFailed, Message: "official review returned an unknown verdict",
})
return false, nil
}
}