1package webhook
2
3import (
4 "context"
5 "errors"
6 "fmt"
7
8 gitm "github.com/aymanbagabas/git-module"
9 "github.com/charmbracelet/soft-serve/git"
10 "github.com/charmbracelet/soft-serve/pkg/config"
11 "github.com/charmbracelet/soft-serve/pkg/db"
12 "github.com/charmbracelet/soft-serve/pkg/proto"
13 "github.com/charmbracelet/soft-serve/pkg/store"
14)
15
16// PushEvent is a push event.
17type PushEvent struct {
18 Common
19
20 // Ref is the branch or tag name.
21 Ref string `json:"ref" url:"ref"`
22 // Before is the previous commit SHA.
23 Before string `json:"before" url:"before"`
24 // After is the current commit SHA.
25 After string `json:"after" url:"after"`
26 // Commits is the list of commits.
27 Commits []Commit `json:"commits" url:"commits"`
28}
29
30// NewPushEvent sends a push event.
31func NewPushEvent(ctx context.Context, user proto.User, repo proto.Repository, ref, before, after string) (PushEvent, error) {
32 event := EventPush
33
34 payload := PushEvent{
35 Ref: ref,
36 Before: before,
37 After: after,
38 Common: Common{
39 EventType: event,
40 Repository: Repository{
41 ID: repo.ID(),
42 Name: repo.Name(),
43 Description: repo.Description(),
44 ProjectName: repo.ProjectName(),
45 Private: repo.IsPrivate(),
46 CreatedAt: repo.CreatedAt(),
47 UpdatedAt: repo.UpdatedAt(),
48 },
49 Sender: User{
50 ID: user.ID(),
51 Username: user.Username(),
52 },
53 },
54 }
55
56 cfg := config.FromContext(ctx)
57 payload.Repository.HTTPURL = repoURL(cfg.HTTP.PublicURL, repo.Name())
58 payload.Repository.SSHURL = repoURL(cfg.SSH.PublicURL, repo.Name())
59 payload.Repository.GitURL = repoURL(cfg.Git.PublicURL, repo.Name())
60
61 // Find repo owner.
62 dbx := db.FromContext(ctx)
63 datastore := store.FromContext(ctx)
64 owner, err := datastore.GetUserByID(ctx, dbx, repo.UserID())
65 if err != nil {
66 return PushEvent{}, db.WrapError(err)
67 }
68
69 handle, err := datastore.GetHandleByUserID(ctx, dbx, owner.ID)
70 if err != nil {
71 return PushEvent{}, db.WrapError(err)
72 }
73
74 payload.Repository.Owner.ID = owner.ID
75 payload.Repository.Owner.Username = handle.Handle
76
77 // Find commits.
78 r, err := repo.Open()
79 if err != nil {
80 return PushEvent{}, err
81 }
82
83 payload.Repository.DefaultBranch, err = proto.RepositoryDefaultBranch(repo)
84 // XXX: we check for ErrReferenceNotExist here because we don't want to
85 // return an error if the repo is an empty repo.
86 // This means that the repo doesn't have a default branch yet and this is
87 // the first push to it.
88 if err != nil && !errors.Is(err, git.ErrReferenceNotExist) {
89 return PushEvent{}, err
90 }
91
92 rev := after
93 if !git.IsZeroHash(before) {
94 rev = fmt.Sprintf("%s..%s", before, after)
95 }
96
97 commits, err := r.Log(rev, gitm.LogOptions{
98 // XXX: limit to 20 commits for now
99 // TODO: implement a commits api
100 MaxCount: 20,
101 })
102 if err != nil {
103 return PushEvent{}, err
104 }
105
106 payload.Commits = make([]Commit, len(commits))
107 for i, c := range commits {
108 payload.Commits[i] = Commit{
109 ID: c.ID.String(),
110 Message: c.Message,
111 Title: c.Summary(),
112 Author: Author{
113 Name: c.Author.Name,
114 Email: c.Author.Email,
115 Date: c.Author.When,
116 },
117 Committer: Author{
118 Name: c.Committer.Name,
119 Email: c.Committer.Email,
120 Date: c.Committer.When,
121 },
122 Timestamp: c.Committer.When,
123 }
124 }
125
126 return payload, nil
127}