Merge pull request 'Add the forge kind open: every open pull request into trunk (issue 1279, ADR 0443)' (#42) from fix/1279-consumers-from-now into main
This commit was merged in pull request #42.
This commit is contained in:
+48
-6
@@ -41,8 +41,19 @@ const (
|
||||
ForgePull = "pull"
|
||||
ForgeMerged = "merged"
|
||||
ForgeRefused = "refused"
|
||||
ForgeOpen = "open"
|
||||
)
|
||||
|
||||
// OpenPull names an open pull request into the trunk: the repository as `<owner>/<name>` and the number.
|
||||
type OpenPull struct {
|
||||
Repository string `json:"repository"`
|
||||
Number int `json:"number"`
|
||||
}
|
||||
|
||||
// OpenEvent is the name of an open message: `forge.open`. It names no repository: it says every open pull request of
|
||||
// every repository the forge bridge reads (novox/hq ADR 0443 §5).
|
||||
func OpenEvent() string { return "forge." + ForgeOpen }
|
||||
|
||||
// The actions of a pull message.
|
||||
const (
|
||||
PullOpened = "opened"
|
||||
@@ -106,18 +117,21 @@ func checkRepository(repository string) error {
|
||||
|
||||
func checkForgeKind(kind string) error {
|
||||
switch kind {
|
||||
case ForgePull, ForgeMerged, ForgeRefused:
|
||||
case ForgePull, ForgeMerged, ForgeRefused, ForgeOpen:
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("delivery: the forge kind %q is not pull, merged or refused", kind)
|
||||
return fmt.Errorf("delivery: the forge kind %q is not pull, merged, refused or open", kind)
|
||||
}
|
||||
|
||||
// ForgeEvent is the name of a forge message of the given kind: `forge.<owner>.<name>.<kind>`.
|
||||
func ForgeEvent(repository, kind string) (string, error) {
|
||||
if err := checkRepository(repository); err != nil {
|
||||
if err := checkForgeKind(kind); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := checkForgeKind(kind); err != nil {
|
||||
if kind == ForgeOpen {
|
||||
return "", fmt.Errorf("delivery: an open message names no repository, its event is %s", OpenEvent())
|
||||
}
|
||||
if err := checkRepository(repository); err != nil {
|
||||
return "", err
|
||||
}
|
||||
owner, name, _ := strings.Cut(repository, "/")
|
||||
@@ -126,6 +140,9 @@ func ForgeEvent(repository, kind string) (string, error) {
|
||||
|
||||
// ParseForgeEvent is the inverse of ForgeEvent.
|
||||
func ParseForgeEvent(event string) (repository, kind string, err error) {
|
||||
if event == OpenEvent() {
|
||||
return "", ForgeOpen, nil
|
||||
}
|
||||
parts := strings.Split(event, ".")
|
||||
if len(parts) != 4 || parts[0] != "forge" {
|
||||
return "", "", fmt.Errorf("delivery: %q is not a forge event, forge.<owner>.<name>.<kind>", event)
|
||||
@@ -259,13 +276,20 @@ type Forge struct {
|
||||
Root string `json:"root,omitempty"`
|
||||
Act string `json:"act,omitempty"`
|
||||
Words string `json:"words,omitempty"`
|
||||
Open []OpenPull `json:"open,omitempty"`
|
||||
}
|
||||
|
||||
func (f Forge) check() error {
|
||||
if err := checkRepository(f.Repository); err != nil {
|
||||
if err := checkForgeKind(f.Kind); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkForgeKind(f.Kind); err != nil {
|
||||
if f.Kind == ForgeOpen {
|
||||
return f.checkOpen()
|
||||
}
|
||||
if len(f.Open) > 0 {
|
||||
return fmt.Errorf("delivery: only an open message names open pull requests")
|
||||
}
|
||||
if err := checkRepository(f.Repository); err != nil {
|
||||
return err
|
||||
}
|
||||
switch f.Kind {
|
||||
@@ -322,6 +346,24 @@ func (f Forge) check() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkOpen refuses an open message that names a repository, a branch, a head or anything of the other kinds, or
|
||||
// an open pull request that is not `<owner>/<name>` and a number.
|
||||
func (f Forge) checkOpen() error {
|
||||
if f.Repository != "" || f.Branch != "" || f.Number != 0 || f.Head != "" || f.Action != "" || len(f.Dirs) > 0 || f.DirsKnown ||
|
||||
f.Merge != "" || len(f.Trees) > 0 || f.Root != "" || f.Act != "" || f.Words != "" {
|
||||
return fmt.Errorf("delivery: an open message carries only the open pull requests it names")
|
||||
}
|
||||
for _, p := range f.Open {
|
||||
if err := checkRepository(p.Repository); err != nil {
|
||||
return err
|
||||
}
|
||||
if p.Number <= 0 {
|
||||
return fmt.Errorf("delivery: the open pull request of %s has no number", p.Repository)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeSource is s as JSON at Version; it refuses a source with no repository or an empty head.
|
||||
func EncodeSource(s Source) ([]byte, error) {
|
||||
if err := s.check(); err != nil {
|
||||
|
||||
@@ -65,6 +65,41 @@ func TestSourceKeys(t *testing.T) { // 1200/AC1
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgeOpenKind(t *testing.T) { // 1279/AC5
|
||||
ev := OpenEvent()
|
||||
if ev != "forge.open" {
|
||||
t.Fatalf("open event = %q", ev)
|
||||
}
|
||||
repo, kind, err := ParseForgeEvent(ev)
|
||||
if err != nil || repo != "" || kind != ForgeOpen {
|
||||
t.Fatalf("parse open event = %q %q %v", repo, kind, err)
|
||||
}
|
||||
// An empty list is a message too: nothing is open.
|
||||
data, err := EncodeForge(Forge{Kind: ForgeOpen, Open: []OpenPull{}})
|
||||
if err != nil {
|
||||
t.Fatalf("empty open: %v", err)
|
||||
}
|
||||
if f, err := DecodeForge(data); err != nil || f.Kind != ForgeOpen || len(f.Open) != 0 {
|
||||
t.Errorf("empty open round trip: %+v %v", f, err)
|
||||
}
|
||||
for name, f := range map[string]Forge{
|
||||
"a repository": {Kind: ForgeOpen, Repository: "a/b", Open: []OpenPull{{Repository: "a/b", Number: 1}}},
|
||||
"a bad entry": {Kind: ForgeOpen, Open: []OpenPull{{Repository: "a", Number: 1}}},
|
||||
"a zero number": {Kind: ForgeOpen, Open: []OpenPull{{Repository: "a/b"}}},
|
||||
"a head": {Kind: ForgeOpen, Head: "x", Open: []OpenPull{{Repository: "a/b", Number: 1}}},
|
||||
} {
|
||||
if _, err := EncodeForge(f); err == nil {
|
||||
t.Errorf("open with %s accepted", name)
|
||||
}
|
||||
}
|
||||
if _, err := EncodeForge(Forge{Kind: ForgePull, Repository: "a/b", Number: 1, Head: "h", Action: "updated", Open: []OpenPull{{Repository: "a/b", Number: 1}}}); err == nil {
|
||||
t.Error("a pull message naming open pull requests accepted")
|
||||
}
|
||||
if _, err := ForgeEvent("a/b", ForgeOpen); err == nil {
|
||||
t.Error("an open event for a repository accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeadsHash(t *testing.T) { // 1200/AC2
|
||||
a := Head{Repository: "novox/mesh-sdk", Number: 36, Head: "aaaa"}
|
||||
b := Head{Repository: "novox/mesh-control", Number: 7, Head: "bbbb"}
|
||||
@@ -113,6 +148,7 @@ func sampleForges() []Forge {
|
||||
{Kind: ForgeMerged, Repository: "novox/mesh-sdk", Number: 36, Head: "aaaa", Merge: "cccc",
|
||||
Trees: map[string]string{"modules/a": "t1"}, Root: "t0"},
|
||||
{Kind: ForgeRefused, Repository: "novox/mesh-sdk", Act: "novox/mesh-sdk.36.aaaa.view", Words: "403 forbidden"},
|
||||
{Kind: ForgeOpen, Open: []OpenPull{{Repository: "novox/mesh-sdk", Number: 36}, {Repository: "novox/mesh-control", Number: 7}}},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user