173 lines
5.8 KiB
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
|
|
}
|
|
}
|