Sync comments
This commit is contained in:
parent
57a8bb5a5e
commit
2a21f6ba50
|
|
@ -54,7 +54,7 @@ func main() {
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
go sessionStore.Cleanup(ctx, 30*time.Minute)
|
go sessionStore.Cleanup(ctx, 30*time.Minute)
|
||||||
go forgejosync.SyncUnsyncedTickets(ctx, db, forgejoClient)
|
go forgejosync.SyncUnsynced(ctx, db, forgejoClient)
|
||||||
|
|
||||||
publicRouter := publichandlers.NewRouter(publichandlers.Dependencies{
|
publicRouter := publichandlers.NewRouter(publichandlers.Dependencies{
|
||||||
DB: db,
|
DB: db,
|
||||||
|
|
|
||||||
|
|
@ -17,42 +17,18 @@ const (
|
||||||
maxRetries = 10
|
maxRetries = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// SyncUnsyncedTickets runs a background loop that finds tickets without a
|
// SyncUnsynced runs a background loop that syncs tickets and comments
|
||||||
// Forgejo issue number and creates issues for them, retrying with exponential
|
// that are missing their Forgejo counterparts, retrying with exponential
|
||||||
// backoff. It stops when ctx is cancelled or all tickets are synced.
|
// backoff. It stops when ctx is cancelled or everything is synced.
|
||||||
func SyncUnsyncedTickets(ctx context.Context, db *gorm.DB, client *forgejo.Client) {
|
func SyncUnsynced(ctx context.Context, db *gorm.DB, client *forgejo.Client) {
|
||||||
backoff := initialBackoff
|
backoff := initialBackoff
|
||||||
|
|
||||||
for attempt := 0; attempt < maxRetries; attempt++ {
|
for attempt := 0; attempt < maxRetries; attempt++ {
|
||||||
var tickets []models.Ticket
|
ticketsFailed := syncUnsyncedTickets(db, client)
|
||||||
if err := db.Preload("Repo").Preload("User").
|
commentsFailed := syncUnsyncedComments(db, client)
|
||||||
Where("forgejo_issue_number IS NULL").
|
|
||||||
Find(&tickets).Error; err != nil {
|
|
||||||
log.Error().Err(err).Msg("sync: failed to query unsynced tickets")
|
|
||||||
if !sleep(ctx, backoff) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
backoff = nextBackoff(backoff)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(tickets) == 0 {
|
if !ticketsFailed && !commentsFailed {
|
||||||
log.Info().Msg("sync: all tickets synced")
|
log.Info().Msg("sync: everything synced successfully")
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info().Int("count", len(tickets)).Msg("sync: found unsynced tickets")
|
|
||||||
|
|
||||||
allSynced := true
|
|
||||||
for _, ticket := range tickets {
|
|
||||||
if err := syncTicket(ctx, db, client, ticket); err != nil {
|
|
||||||
log.Error().Err(err).Str("ticket_id", ticket.ID.String()).Msg("sync: failed to sync ticket")
|
|
||||||
allSynced = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if allSynced {
|
|
||||||
log.Info().Msg("sync: all tickets synced successfully")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,11 +38,70 @@ func SyncUnsyncedTickets(ctx context.Context, db *gorm.DB, client *forgejo.Clien
|
||||||
backoff = nextBackoff(backoff)
|
backoff = nextBackoff(backoff)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warn().Msg("sync: gave up after max retries, remaining unsynced tickets will be retried on next restart")
|
log.Warn().Msg("sync: gave up after max retries, remaining items will be retried on next restart")
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncTicket(_ context.Context, db *gorm.DB, client *forgejo.Client, ticket models.Ticket) error {
|
// syncUnsyncedTickets syncs tickets without a Forgejo issue number.
|
||||||
// Look up or create the "customer" label
|
// Returns true if any tickets failed to sync.
|
||||||
|
func syncUnsyncedTickets(db *gorm.DB, client *forgejo.Client) bool {
|
||||||
|
var tickets []models.Ticket
|
||||||
|
if err := db.Preload("Repo").Preload("User").
|
||||||
|
Where("forgejo_issue_number IS NULL").
|
||||||
|
Find(&tickets).Error; err != nil {
|
||||||
|
log.Error().Err(err).Msg("sync: failed to query unsynced tickets")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tickets) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Int("count", len(tickets)).Msg("sync: found unsynced tickets")
|
||||||
|
|
||||||
|
anyFailed := false
|
||||||
|
for _, ticket := range tickets {
|
||||||
|
if err := syncTicket(db, client, ticket); err != nil {
|
||||||
|
log.Error().Err(err).Str("ticket_id", ticket.ID.String()).Msg("sync: failed to sync ticket")
|
||||||
|
anyFailed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return anyFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncUnsyncedComments syncs comments that belong to tickets with a Forgejo
|
||||||
|
// issue but are missing their own Forgejo comment ID.
|
||||||
|
// Returns true if any comments failed to sync.
|
||||||
|
func syncUnsyncedComments(db *gorm.DB, client *forgejo.Client) bool {
|
||||||
|
var comments []models.TicketComment
|
||||||
|
if err := db.Preload("Ticket").Preload("Ticket.Repo").Preload("User").
|
||||||
|
Where("forgejo_comment_id IS NULL").
|
||||||
|
Find(&comments).Error; err != nil {
|
||||||
|
log.Error().Err(err).Msg("sync: failed to query unsynced comments")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(comments) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Int("count", len(comments)).Msg("sync: found unsynced comments")
|
||||||
|
|
||||||
|
anyFailed := false
|
||||||
|
for _, comment := range comments {
|
||||||
|
if comment.Ticket.ForgejoIssueNumber == nil {
|
||||||
|
// Parent ticket hasn't been synced yet; skip until next round
|
||||||
|
anyFailed = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := syncComment(db, client, comment); err != nil {
|
||||||
|
log.Error().Err(err).Str("comment_id", comment.ID.String()).Msg("sync: failed to sync comment")
|
||||||
|
anyFailed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return anyFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
func syncTicket(db *gorm.DB, client *forgejo.Client, ticket models.Ticket) error {
|
||||||
var labelIDs []int64
|
var labelIDs []int64
|
||||||
label, err := client.GetOrCreateLabel(ticket.Repo.ForgejoOwner, ticket.Repo.ForgejoRepo, "customer", "#0075ca")
|
label, err := client.GetOrCreateLabel(ticket.Repo.ForgejoOwner, ticket.Repo.ForgejoRepo, "customer", "#0075ca")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -92,6 +127,23 @@ func syncTicket(_ context.Context, db *gorm.DB, client *forgejo.Client, ticket m
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func syncComment(db *gorm.DB, client *forgejo.Client, comment models.TicketComment) error {
|
||||||
|
repo := comment.Ticket.Repo
|
||||||
|
forgejoComment, err := client.CreateComment(repo.ForgejoOwner, repo.ForgejoRepo, *comment.Ticket.ForgejoIssueNumber, forgejo.CreateCommentRequest{
|
||||||
|
Body: comment.Body + "\n\n---\n*Comment by: " + comment.User.Email + "*",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Model(&comment).Update("forgejo_comment_id", forgejoComment.ID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Str("comment_id", comment.ID.String()).Int64("forgejo_comment_id", forgejoComment.ID).Msg("sync: comment synced")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func sleep(ctx context.Context, d time.Duration) bool {
|
func sleep(ctx context.Context, d time.Duration) bool {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue