1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package domain
4
5import (
6 "strconv"
7 "strings"
8 "time"
9)
10
11// A merge request is a "pull request" for the workflows that run on it (CI-3,
12// D5): ermite keeps two refs per request, as GitHub does.
13//
14// refs/pull/<n>/head the head of the source branch
15// refs/pull/<n>/merge a test merge of the head into the target, when it merges cleanly
16//
17// They are written by ermite only: a push to them is refused (see IsPullRef),
18// and they are not branches nor tags, so no listing shows them. Git serves them
19// to fetch like any ref.
20
21// PullRefPrefix starts the refs of the merge requests.
22const PullRefPrefix = "refs/pull/"
23
24// PullHeadRef is the ref that holds the head of merge request number.
25func PullHeadRef(number int64) string { return PullRefPrefix + strconv.FormatInt(number, 10) + "/head" }
26
27// PullMergeRef is the ref that holds the test merge of merge request number.
28func PullMergeRef(number int64) string {
29 return PullRefPrefix + strconv.FormatInt(number, 10) + "/merge"
30}
31
32// IsPullRefName reports whether ref is under refs/pull/, whatever follows.
33// Pushes are refused on all of that namespace.
34func IsPullRefName(ref string) bool { return strings.HasPrefix(ref, PullRefPrefix) }
35
36// IsPullRef reports whether ref is exactly refs/pull/<n>/head or
37// refs/pull/<n>/merge, the two refs ermite maintains.
38func IsPullRef(ref string) bool {
39 rest, ok := strings.CutPrefix(ref, PullRefPrefix)
40 if !ok {
41 return false
42 }
43 num, kind, ok := strings.Cut(rest, "/")
44 if !ok || (kind != "head" && kind != "merge") {
45 return false
46 }
47 n, err := strconv.ParseInt(num, 10, 64)
48 return err == nil && n > 0 && strconv.FormatInt(n, 10) == num
49}
50
51// PullRequestActions are the activity types of the pull_request event that
52// ermite can produce. A merge request cannot be reopened yet, so "reopened"
53// never occurs, but a workflow may list it.
54const (
55 PullOpened = "opened"
56 PullSynchronize = "synchronize"
57 PullReopened = "reopened"
58 PullClosed = "closed"
59)
60
61// DefaultPullRequestTypes are the types of a pull_request that lists none.
62var DefaultPullRequestTypes = []string{PullOpened, PullSynchronize, PullReopened}
63
64// TestMergeCommit returns the text of the commit that tests the merge of head
65// into base: the tree the merge produced, parents base then head, and GitHub's
66// message. It is not signed.
67func TestMergeCommit(tree, base, head ObjectID, who Identity, at time.Time) []byte {
68 sig := signatureLine(Signature{Name: who.Name, Email: who.Email, Time: at.UTC()})
69 var b strings.Builder
70 b.WriteString("tree " + tree.String() + "\n")
71 b.WriteString("parent " + base.String() + "\n")
72 b.WriteString("parent " + head.String() + "\n")
73 b.WriteString("author " + sig + "\n")
74 b.WriteString("committer " + sig + "\n")
75 b.WriteString("\nMerge " + head.String() + " into " + base.String() + "\n")
76 return []byte(b.String())
77}