feat: implement gt mq reject command for manual MR rejection
- Add gt mq command group with reject subcommand - Add FindMR method to locate MRs by ID or branch - Add RejectMR method to reject MRs with reason - Add notifyWorkerRejected for optional mail notification - Add tests for rejection state transitions Closes gt-svi.5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -551,6 +551,88 @@ Thank you for your contribution!`,
|
||||
router.Send(msg)
|
||||
}
|
||||
|
||||
// Common errors for MR operations
|
||||
var (
|
||||
ErrMRNotFound = errors.New("merge request not found")
|
||||
)
|
||||
|
||||
// FindMR finds a merge request by ID or branch name.
|
||||
// Returns nil if not found.
|
||||
func (m *Manager) FindMR(idOrBranch string) (*MergeRequest, error) {
|
||||
queue, err := m.Queue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range queue {
|
||||
// Match by ID
|
||||
if item.MR.ID == idOrBranch {
|
||||
return item.MR, nil
|
||||
}
|
||||
// Match by branch name (with or without polecat/ prefix)
|
||||
if item.MR.Branch == idOrBranch {
|
||||
return item.MR, nil
|
||||
}
|
||||
if "polecat/"+idOrBranch == item.MR.Branch {
|
||||
return item.MR, nil
|
||||
}
|
||||
// Match by worker name (partial match for convenience)
|
||||
if strings.Contains(item.MR.ID, idOrBranch) {
|
||||
return item.MR, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrMRNotFound
|
||||
}
|
||||
|
||||
// RejectMR manually rejects a merge request.
|
||||
// It closes the MR with rejected status and optionally notifies the worker.
|
||||
// Returns the rejected MR for display purposes.
|
||||
func (m *Manager) RejectMR(idOrBranch string, reason string, notify bool) (*MergeRequest, error) {
|
||||
mr, err := m.FindMR(idOrBranch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Verify MR is open or in_progress (can't reject already closed)
|
||||
if mr.IsClosed() {
|
||||
return nil, fmt.Errorf("%w: MR is already closed with reason: %s", ErrClosedImmutable, mr.CloseReason)
|
||||
}
|
||||
|
||||
// Close with rejected reason
|
||||
if err := mr.Close(CloseReasonRejected); err != nil {
|
||||
return nil, fmt.Errorf("failed to close MR: %w", err)
|
||||
}
|
||||
mr.Error = reason
|
||||
|
||||
// Optionally notify worker
|
||||
if notify {
|
||||
m.notifyWorkerRejected(mr, reason)
|
||||
}
|
||||
|
||||
return mr, nil
|
||||
}
|
||||
|
||||
// notifyWorkerRejected sends a rejection notification to a polecat.
|
||||
func (m *Manager) notifyWorkerRejected(mr *MergeRequest, reason string) {
|
||||
router := mail.NewRouter(m.workDir)
|
||||
msg := &mail.Message{
|
||||
From: fmt.Sprintf("%s/refinery", m.rig.Name),
|
||||
To: fmt.Sprintf("%s/%s", m.rig.Name, mr.Worker),
|
||||
Subject: "Merge request rejected",
|
||||
Body: fmt.Sprintf(`Your merge request has been rejected.
|
||||
|
||||
Branch: %s
|
||||
Issue: %s
|
||||
Reason: %s
|
||||
|
||||
Please review the feedback and address the issues before resubmitting.`,
|
||||
mr.Branch, mr.IssueID, reason),
|
||||
Priority: mail.PriorityNormal,
|
||||
}
|
||||
router.Send(msg)
|
||||
}
|
||||
|
||||
// findTownRoot walks up directories to find the town root.
|
||||
func findTownRoot(startPath string) string {
|
||||
path := startPath
|
||||
|
||||
Reference in New Issue
Block a user