Commit c6c5cb3409
Verified · cmc ci/build: error ci/test: error
.gitbay/wiki/Parity.org +9 −6
| @@ -48,6 +48,8 @@ browser-only and the iOS build screen unable to say more than the log. | ||
| 48 | 48 | | create from a fork | yes | yes | yes | |
| 49 | 49 | | retarget | yes | yes | yes | |
| 50 | 50 | | milestone | yes | yes | yes | |
| 51 | | labels | yes | yes | no | | |
| 52 | | filter by label | yes | yes | no | | |
| 51 | 53 | | request a review | yes | yes | yes | |
| 52 | 54 | | choose body markup | yes | yes | yes | |
| 53 | 55 | | stacked merge requests | yes | yes | yes | |
| @@ -118,12 +120,13 @@ reviews, since an approval was of the diff against the old branch. | ||
| 118 | 120 | | org milestones: create, list, close, reopen | yes | list | yes | |
| 119 | 121 | | closes across repositories | yes | yes | yes | |
| 120 | 122 | |
| 121 | Labels are created on the fly by =issue label --add= and managed by | |
| 122 | =label list=, =label set <label> --color rrggbb= and =label remove=, | |
| 123 | which takes the label off every issue. The web paints the stored colour | |
| 124 | on every chip and derives one from the name when none is set. The set | |
| 125 | itself is at =/<owner>/<repo>/labels=, linked from the issue list: | |
| 126 | create, recolour and remove, dispatching the same commands. | |
| 123 | Labels are created on the fly by =issue label --add= and =mr label | |
| 124 | --add=, and managed by =label list=, =label set <label> --color rrggbb= | |
| 125 | and =label remove=, which takes the label off every issue and merge | |
| 126 | request. One set serves both. The web paints the stored colour on every | |
| 127 | chip and derives one from the name when none is set. The set itself is | |
| 128 | at =/<owner>/<repo>/labels=, linked from the issue list: create, | |
| 129 | recolour and remove, dispatching the same commands. | |
| 127 | 130 | |
| 128 | 131 | Org labels and milestones are managed on the CLI, the API and the iOS |
| 129 | 132 | client's org screen; =/<org>/-/labels= and =/<org>/-/milestones= show |
.gitbay/wiki/Users.org +8 −6
| @@ -370,8 +370,9 @@ gitbay milestone close v1.0 / reopen v1.0 | ||
| 370 | 370 | #+end_src |
| 371 | 371 | |
| 372 | 372 | An org holds labels and milestones every repository under it sees |
| 373 | beside its own. =issue label --add=, =issue milestone= and =mr | |
| 374 | milestone= resolve the org's row first; a repository cannot create a | |
| 373 | beside its own. =issue label --add=, =mr label --add=, =issue | |
| 374 | milestone= and =mr milestone= resolve the org's row first; a repository | |
| 375 | cannot create a | |
| 375 | 376 | label or milestone with a name its org holds. Creating an org label or |
| 376 | 377 | milestone whose name repositories under the org already use folds them |
| 377 | 378 | in: their issues and merge requests move to the org's row. Org admins |
| @@ -394,14 +395,14 @@ form prefills its textarea, and =gitbay issue templates= lists them. | ||
| 394 | 395 | |
| 395 | 396 | Lists narrow the same way on every surface: =issue list --label bug |
| 396 | 397 | --assignee bob --author alice --milestone v1= (or =--milestone none=), |
| 397 | =mr list --author bob --milestone v1=; the web's issue and merge request | |
| 398 | =mr list --label bug --author bob --milestone v1=; the web's issue and merge request | |
| 398 | 399 | lists take the same names as query parameters, and each active filter |
| 399 | 400 | shows with a link that drops it. |
| 400 | 401 | |
| 401 | 402 | Labels take a colour: =gitbay label set bug --color cf222e=; =label |
| 402 | list= shows each with its colour and how many issues carry it, and | |
| 403 | =label remove= takes one off every issue. =issue label --add= still | |
| 404 | creates a colourless label on the fly. | |
| 403 | list= shows each with its colour and how many issues and merge requests | |
| 404 | carry it, and =label remove= takes one off all of them. =issue label | |
| 405 | --add= and =mr label --add= still create a colourless label on the fly. | |
| 405 | 406 | |
| 406 | 407 | * Merge requests |
| 407 | 408 | |
| @@ -411,6 +412,7 @@ gitbay mr create other/upstream --source you/fork:feature --target main --title | ||
| 411 | 412 | gitbay mr list / show 4 / diff 4 |
| 412 | 413 | gitbay mr checkout 4 # local branch mr/4 from the MR head |
| 413 | 414 | gitbay mr review 4 --approve # or --request-changes / --comment |
| 415 | gitbay mr label 4 --add bug --remove wontfix | |
| 414 | 416 | gitbay mr merge 4 [--strategy ff|merge|squash|rebase] |
| 415 | 417 | gitbay mr close 4 |
| 416 | 418 | #+end_src |
e2e/labelweb_test.go +2 −2
| @@ -39,8 +39,8 @@ func TestLabelsWeb(t *testing.T) { | ||
| 39 | 39 | t.Fatal("label recolour failed") |
| 40 | 40 | } |
| 41 | 41 | out, _, _ := inst.ssh(t, aliceKey, "", "label", "list", "alice/app", "--json") |
| 42 | if !strings.Contains(out, `{"name":"bug","color":"#cf222e","issues":1}`) || | |
| 43 | !strings.Contains(out, `{"name":"docs","color":"#1f6feb","issues":0}`) { | |
| 42 | if !strings.Contains(out, `{"name":"bug","color":"#cf222e","issues":1,"mrs":0}`) || | |
| 43 | !strings.Contains(out, `{"name":"docs","color":"#1f6feb","issues":0,"mrs":0}`) { | |
| 44 | 44 | t.Fatalf("labels not as posted:\n%s", out) |
| 45 | 45 | } |
| 46 | 46 | |
e2e/mrweb_test.go +82
| @@ -502,3 +502,85 @@ func TestMRSupersedes(t *testing.T) { | ||
| 502 | 502 | t.Fatalf("!2 still says it supersedes after clearing:\n%s", body2) |
| 503 | 503 | } |
| 504 | 504 | } |
| 505 | ||
| 506 | // TestMRWebLabels labels a merge request from the browser and filters the | |
| 507 | // list by it (#231). The CLI is the check that the page dispatched | |
| 508 | // mr label rather than writing its own rows. | |
| 509 | func TestMRWebLabels(t *testing.T) { | |
| 510 | inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n") | |
| 511 | aliceKey := inst.newKey(t, "alice") | |
| 512 | bobKey := inst.newKey(t, "bob") | |
| 513 | inst.admin(t, "admin", "user", "create", "alice", | |
| 514 | "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified") | |
| 515 | inst.admin(t, "admin", "user", "create", "bob", | |
| 516 | "--key", bobKey+".pub", "--email", "bob@example.test", "--verified") | |
| 517 | if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/app"); code != 0 { | |
| 518 | t.Fatalf("repo create: %s", errOut) | |
| 519 | } | |
| 520 | env := inst.gitEnv(aliceKey) | |
| 521 | work := t.TempDir() | |
| 522 | mustGit(t, work, env, "clone", inst.sshURL("alice/app"), "w") | |
| 523 | dir := filepath.Join(work, "w") | |
| 524 | os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a\n"), 0o644) | |
| 525 | mustGit(t, dir, env, "checkout", "-q", "-b", "main") | |
| 526 | mustGit(t, dir, env, "add", ".") | |
| 527 | mustGit(t, dir, env, "commit", "-q", "-m", "base") | |
| 528 | mustGit(t, dir, env, "push", "-q", "origin", "main") | |
| 529 | mustGit(t, dir, env, "checkout", "-q", "-b", "topic") | |
| 530 | os.WriteFile(filepath.Join(dir, "b.txt"), []byte("b\n"), 0o644) | |
| 531 | mustGit(t, dir, env, "add", ".") | |
| 532 | mustGit(t, dir, env, "commit", "-q", "-m", "topic work") | |
| 533 | mustGit(t, dir, env, "push", "-q", "origin", "topic") | |
| 534 | if _, errOut, code := inst.ssh(t, aliceKey, "", "mr", "create", "alice/app", | |
| 535 | "--source", "topic", "--target", "main", "--title", "feature"); code != 0 { | |
| 536 | t.Fatalf("mr create: %s", errOut) | |
| 537 | } | |
| 538 | ||
| 539 | alice := inst.login(t, aliceKey) | |
| 540 | mrURL := inst.base() + "/alice/app/mrs/1" | |
| 541 | ||
| 542 | // The form is on the page, and applying it lands in the CLI's view. | |
| 543 | if _, body := browserGet(t, alice, mrURL); !strings.Contains(body, `/mrs/1/label`) { | |
| 544 | t.Fatalf("MR page has no label form:\n%s", body) | |
| 545 | } | |
| 546 | if status, _ := browserPost(t, alice, mrURL+"/label", url.Values{"add": {"bug ui"}}); status != 200 { | |
| 547 | t.Fatalf("label post: %d", status) | |
| 548 | } | |
| 549 | out, _, _ := inst.ssh(t, aliceKey, "", "mr", "show", "alice/app", "1", "--json") | |
| 550 | for _, want := range []string{`"bug"`, `"ui"`} { | |
| 551 | if !strings.Contains(out, want) { | |
| 552 | t.Fatalf("label %s did not land:\n%s", want, out) | |
| 553 | } | |
| 554 | } | |
| 555 | _, body := browserGet(t, alice, mrURL) | |
| 556 | if n := strings.Count(body, `class="chip label"`); n != 2 { | |
| 557 | t.Fatalf("MR page shows %d label chips, want 2:\n%s", n, body) | |
| 558 | } | |
| 559 | ||
| 560 | // Removing works the same way. | |
| 561 | if status, _ := browserPost(t, alice, mrURL+"/label", url.Values{"remove": {"ui"}}); status != 200 { | |
| 562 | t.Fatalf("label remove: %d", status) | |
| 563 | } | |
| 564 | if out, _, _ := inst.ssh(t, aliceKey, "", "mr", "show", "alice/app", "1", "--json"); strings.Contains(out, `"ui"`) { | |
| 565 | t.Fatalf("label not removed:\n%s", out) | |
| 566 | } | |
| 567 | ||
| 568 | // The list narrows by label, and says which one it is narrowed by. | |
| 569 | _, body = browserGet(t, alice, inst.base()+"/alice/app/mrs?label=bug") | |
| 570 | if !strings.Contains(body, ">feature<") || !strings.Contains(body, `label: <span class="chip label"`) { | |
| 571 | t.Fatalf("web label filter:\n%s", body) | |
| 572 | } | |
| 573 | _, body = browserGet(t, alice, inst.base()+"/alice/app/mrs?label=ui") | |
| 574 | if strings.Contains(body, ">feature<") { | |
| 575 | t.Fatalf("removed label still lists the merge request:\n%s", body) | |
| 576 | } | |
| 577 | ||
| 578 | // A reader gets the chips and no form. | |
| 579 | _, body = browserGet(t, inst.login(t, bobKey), mrURL) | |
| 580 | if !strings.Contains(body, `class="chip label"`) { | |
| 581 | t.Fatalf("reader sees no labels:\n%s", body) | |
| 582 | } | |
| 583 | if strings.Contains(body, `/mrs/1/label`) { | |
| 584 | t.Fatalf("reader sees the label form:\n%s", body) | |
| 585 | } | |
| 586 | } | |