package api import ( "code.gitea.io/gitea/modules/structs" gitea "code.gitea.io/sdk/gitea" "fmt" "github.com/google/go-github/v59/github" "time" ) func stringPtr(s string) *string { return &s } func numberPtr(n int) *int { return &n } func intPtr(n int) *int { return &n } func int64Ptr(n int64) *int64 { return &n } func boolPtr(b bool) *bool { return &b } func timePtr(t time.Time) *github.Timestamp { timestamp := github.Timestamp{Time: t} return ×tamp } func timePtrIfNotNil(t *time.Time) *github.Timestamp { if t == nil { return nil } return timePtr(*t) } func convertUser(user *gitea.User) *github.User { if user == nil { return &github.User{} } return &github.User{ Login: stringPtr(user.UserName), ID: int64Ptr(user.ID), } } func convertCoreUser(user *structs.User) *github.User { if user == nil { return &github.User{} } return &github.User{ Login: stringPtr(user.UserName), ID: int64Ptr(user.ID), } } func convertUsers(users []*gitea.User) []*github.User { var ghUsers []*github.User for _, user := range users { ghUsers = append(ghUsers, convertUser(user)) } return ghUsers } func convertCoreUsers(users []*structs.User) []*github.User { var ghUsers []*github.User for _, user := range users { ghUsers = append(ghUsers, convertCoreUser(user)) } return ghUsers } func convertMilestone(milestone *gitea.Milestone) *github.Milestone { if milestone == nil { return &github.Milestone{} } return &github.Milestone{ Title: stringPtr(milestone.Title), } } func convertCoreMilestone(milestone *structs.Milestone) *github.Milestone { if milestone == nil { return &github.Milestone{} } return &github.Milestone{ Title: stringPtr(milestone.Title), } } func convertLabels(labels []*gitea.Label) []*github.Label { if labels == nil { return make([]*github.Label, 0) } var ghLabels []*github.Label for _, label := range labels { if label == nil { continue } ghLabels = append(ghLabels, &github.Label{ Name: stringPtr(label.Name), }) } return ghLabels } func convertCoreLabels(labels []*structs.Label) []*github.Label { if labels == nil { return make([]*github.Label, 0) } var ghLabels []*github.Label for _, label := range labels { if label == nil { continue } ghLabels = append(ghLabels, &github.Label{ Name: stringPtr(label.Name), }) } return ghLabels } func convertPRBranch(branch *gitea.PRBranchInfo) *github.PullRequestBranch { if branch == nil { return &github.PullRequestBranch{} } return &github.PullRequestBranch{ Label: stringPtr(branch.Name), Ref: stringPtr(branch.Ref), SHA: stringPtr(branch.Sha), Repo: convertRepo(branch.Repository), } } func convertCorePRBranch(branch *structs.PRBranchInfo) *github.PullRequestBranch { if branch == nil { return &github.PullRequestBranch{} } return &github.PullRequestBranch{ Label: stringPtr(branch.Name), Ref: stringPtr(branch.Ref), SHA: stringPtr(branch.Sha), Repo: convertCoreRepo(branch.Repository), } } func convertRepo(repo *gitea.Repository) *github.Repository { if repo == nil { return &github.Repository{} } return &github.Repository{ ID: int64Ptr(repo.ID), Name: stringPtr(repo.Name), Owner: convertUser(repo.Owner), FullName: stringPtr(repo.FullName), Description: stringPtr(repo.Description), Homepage: stringPtr(repo.Website), HTMLURL: stringPtr(repo.HTMLURL), CloneURL: stringPtr(repo.CloneURL), GitURL: stringPtr(repo.CloneURL), SSHURL: stringPtr(repo.SSHURL), DefaultBranch: stringPtr(repo.DefaultBranch), CreatedAt: timePtr(repo.Created), UpdatedAt: timePtr(repo.Updated), Private: boolPtr(repo.Private), Fork: boolPtr(repo.Fork), Size: intPtr(repo.Size), StargazersCount: intPtr(repo.Stars), SubscribersCount: intPtr(repo.Watchers), ForksCount: intPtr(repo.Forks), Watchers: intPtr(repo.Watchers), WatchersCount: intPtr(repo.Stars), OpenIssuesCount: intPtr(repo.OpenIssues), Archived: boolPtr(repo.Archived), } } func convertCoreRepo(repo *structs.Repository) *github.Repository { if repo == nil { return &github.Repository{} } return &github.Repository{ ID: int64Ptr(repo.ID), Name: stringPtr(repo.Name), Owner: convertCoreUser(repo.Owner), FullName: stringPtr(repo.FullName), Description: stringPtr(repo.Description), Homepage: stringPtr(repo.Website), HTMLURL: stringPtr(repo.HTMLURL), CloneURL: stringPtr(repo.CloneURL), GitURL: stringPtr(repo.CloneURL), SSHURL: stringPtr(repo.SSHURL), DefaultBranch: stringPtr(repo.DefaultBranch), CreatedAt: timePtr(repo.Created), UpdatedAt: timePtr(repo.Updated), Private: boolPtr(repo.Private), Fork: boolPtr(repo.Fork), Size: intPtr(repo.Size), StargazersCount: intPtr(repo.Stars), SubscribersCount: intPtr(repo.Watchers), ForksCount: intPtr(repo.Forks), Watchers: intPtr(repo.Watchers), WatchersCount: intPtr(repo.Stars), OpenIssuesCount: intPtr(repo.OpenIssues), Archived: boolPtr(repo.Archived), } } func convertPullRequest(request *structs.PullRequest) *github.PullRequest { if request == nil { return &github.PullRequest{} } pr := &github.PullRequest{ ID: int64Ptr(request.ID), Number: intPtr(int(request.Index)), State: stringPtr(string(request.State)), Title: stringPtr(request.Title), Body: stringPtr(request.Body), CreatedAt: timePtr(*request.Created), UpdatedAt: timePtr(*request.Updated), ClosedAt: timePtrIfNotNil(request.Closed), MergedAt: timePtrIfNotNil(request.Merged), Merged: boolPtr(request.HasMerged), Mergeable: boolPtr(request.Mergeable), MergeCommitSHA: request.MergedCommitID, URL: stringPtr(request.URL), HTMLURL: stringPtr(request.HTMLURL), DiffURL: stringPtr(request.DiffURL), PatchURL: stringPtr(request.PatchURL), Comments: intPtr(request.Comments), Assignee: convertCoreUser(request.Assignee), Assignees: convertCoreUsers(request.Assignees), Milestone: convertCoreMilestone(request.Milestone), Labels: convertCoreLabels(request.Labels), } // Convert PR branch info if request.Head != nil { pr.Head = convertCorePRBranch(request.Head) } if request.Base != nil { pr.Base = convertCorePRBranch(request.Base) } return pr } func convertChanges(changes *structs.ChangesPayload) *github.EditChange { if changes == nil { return &github.EditChange{} } return &github.EditChange{ Title: &github.EditTitle{ From: stringPtr(changes.Title.From), }, Body: &github.EditBody{ From: stringPtr(changes.Body.From), }, Base: &github.EditBase{ Ref: &github.EditRef{ From: stringPtr(changes.Ref.From), }, }, } } func convertLabel(label *gitea.Label) *github.Label { if label == nil { return &github.Label{} } return &github.Label{ ID: int64Ptr(label.ID), Name: stringPtr(label.Name), Color: stringPtr(label.Color), Description: stringPtr(label.Description), URL: stringPtr(label.URL), } } func convertCoreLabel(label *structs.Label) *github.Label { if label == nil { return &github.Label{} } return &github.Label{ ID: int64Ptr(label.ID), Name: stringPtr(label.Name), Color: stringPtr(label.Color), Description: stringPtr(label.Description), URL: stringPtr(label.URL), } } func convertCommitFile(file *gitea.ChangedFile) *github.CommitFile { if file == nil { return &github.CommitFile{} } return &github.CommitFile{ Filename: stringPtr(file.Filename), PreviousFilename: stringPtr(file.PreviousFilename), Additions: intPtr(file.Additions), Deletions: intPtr(file.Deletions), Changes: intPtr(file.Changes), Status: stringPtr(file.Status), BlobURL: stringPtr(file.ContentsURL), RawURL: stringPtr(file.RawURL), } } func convertGitTree(tree *gitea.GitTreeResponse) *github.Tree { if tree == nil { return &github.Tree{} } return &github.Tree{ SHA: stringPtr(tree.SHA), Truncated: boolPtr(tree.Truncated), Entries: convertGitEntries(tree.Entries), } } func convertGitEntries(entries []gitea.GitEntry) []*github.TreeEntry { if entries == nil { return make([]*github.TreeEntry, 0) } var ghEntries []*github.TreeEntry for _, entry := range entries { ghEntries = append(ghEntries, convertGitEntry(&entry)) } return ghEntries } func convertGitEntry(s *gitea.GitEntry) *github.TreeEntry { if s == nil { return &github.TreeEntry{} } return &github.TreeEntry{ Path: stringPtr(s.Path), Mode: stringPtr(s.Mode), Type: stringPtr(s.Type), Size: intPtr(int(s.Size)), SHA: stringPtr(s.SHA), URL: stringPtr(s.URL), } } func convertIssueComment(comment *gitea.Comment, reactions []*gitea.Reaction) *github.IssueComment { if comment == nil { return &github.IssueComment{} } return &github.IssueComment{ ID: int64Ptr(comment.ID), Body: stringPtr(comment.Body), CreatedAt: timePtr(comment.Created), UpdatedAt: timePtr(comment.Updated), User: convertUser(comment.Poster), Reactions: convertReactions(reactions), } } func convertReactions(reactions []*gitea.Reaction) *github.Reactions { if reactions == nil { return &github.Reactions{} } return &github.Reactions{ TotalCount: intPtr(len(reactions)), PlusOne: intPtr(countReaction(reactions, "+1")), MinusOne: intPtr(countReaction(reactions, "-1")), Laugh: intPtr(countReaction(reactions, "laugh")), Confused: intPtr(countReaction(reactions, "confused")), Heart: intPtr(countReaction(reactions, "heart")), Rocket: intPtr(countReaction(reactions, "rocket")), Eyes: intPtr(countReaction(reactions, "eyes")), } } func countReaction(reactions []*gitea.Reaction, reactionType string) int { count := 0 for _, reaction := range reactions { if reaction.Reaction == reactionType { count++ } } return count } func translatePrAction(action structs.HookIssueAction, prefix bool) string { translatedAction := "" switch action { case structs.HookIssueOpened: translatedAction = "opened" case structs.HookIssueClosed: translatedAction = "closed" case structs.HookIssueReOpened: translatedAction = "reopened" case structs.HookIssueEdited: translatedAction = "edited" case structs.HookIssueAssigned: translatedAction = "assigned" case structs.HookIssueUnassigned: translatedAction = "unassigned" case structs.HookIssueLabelUpdated: // GitHub does not have a direct "label_updated" event; use "labeled" as the closest action translatedAction = "labeled" // Assuming you handle the update as adding a label case structs.HookIssueLabelCleared: // GitHub does not have a direct "label_cleared" event; use "unlabeled" as the closest action translatedAction = "unlabeled" // Assuming you handle the clearing as removing a label case structs.HookIssueSynchronized: translatedAction = "synchronize" case structs.HookIssueMilestoned: translatedAction = "milestoned" case structs.HookIssueDemilestoned: translatedAction = "demilestoned" case structs.HookIssueReviewed: // GitHub does not have a direct "reviewed" event for PRs; this might be closest to a review submitted translatedAction = "review_submitted" // This is not a direct GitHub event, consider how best to map this action case structs.HookIssueReviewRequested: translatedAction = "review_requested" case structs.HookIssueReviewRequestRemoved: translatedAction = "review_request_removed" default: // Fallback for any unhandled actions translatedAction = "unknown_action" } if prefix { translatedAction = fmt.Sprintf("pull_request.%s", translatedAction) } return translatedAction } func convertRelease(release *gitea.Release) *github.RepositoryRelease { if release == nil { return &github.RepositoryRelease{} } return &github.RepositoryRelease{ ID: int64Ptr(release.ID), TagName: stringPtr(release.TagName), TargetCommitish: stringPtr(release.Target), Name: stringPtr(release.Title), Body: stringPtr(release.Note), Draft: boolPtr(release.IsDraft), Prerelease: boolPtr(release.IsPrerelease), CreatedAt: timePtr(release.CreatedAt), PublishedAt: timePtr(release.PublishedAt), Assets: convertReleaseAttachments(release.Attachments), URL: stringPtr(release.URL), ZipballURL: stringPtr(release.ZipURL), TarballURL: stringPtr(release.TarURL), HTMLURL: stringPtr(release.HTMLURL), } } func convertReleaseAttachments(attachments []*gitea.Attachment) []*github.ReleaseAsset { if attachments == nil { return make([]*github.ReleaseAsset, 0) } var ghAttachments []*github.ReleaseAsset for _, attachment := range attachments { ghAttachments = append(ghAttachments, convertReleaseAttachment(attachment)) } return ghAttachments } func convertReleaseAttachment(attachment *gitea.Attachment) *github.ReleaseAsset { if attachment == nil { return &github.ReleaseAsset{} } return &github.ReleaseAsset{ ID: int64Ptr(attachment.ID), Name: stringPtr(attachment.Name), Size: intPtr(int(attachment.Size)), DownloadCount: intPtr(int(attachment.DownloadCount)), BrowserDownloadURL: stringPtr(attachment.DownloadURL), } }