Skip to content

Commit 8f83492

Browse files
authored
Link projects not in the report and keep Left behind rows together on narrow screens (#59)
1 parent 6f8c370 commit 8f83492

3 files changed

Lines changed: 31 additions & 17 deletions

File tree

‎docs/page.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ Those issues show their key and summary too, and the services they miss sit unde
112112

113113
A service that needs a tag has a **Pick** button for it, which picks the tag without opening the service. `no tag yet`
114114
links to create the next tag, and `not merged`, `merged into <branch>`, and `in prod or not merged` link to the merge
115-
requests or commits. When any line needs a tag, a button next to the title picks every needed tag at once, the newest
116-
one per service. A needed tag can ship more tasks that need tags of their own, so the button also picks those, and its
117-
count includes them.
115+
requests or commits. A service `not in this report` links to its project in GitLab. When any line needs a tag, a
116+
button next to the title picks every needed tag at once, the newest one per service. A needed tag can ship more tasks
117+
that need tags of their own, so the button also picks those, and its count includes them.
118118

119119
Changes already in production and closed merge requests are not listed. Other relations, such as `blocks`,
120120
`relates to`, or a parent, do not hold a release back; they show only when you hover over the issue's key.

‎release_scope/_static/index.html‎

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@
134134
.gap-dep-head, .gap-line { padding-left: 16px; }
135135
.gap-line.nested { padding-left: 32px; }
136136
.gap-line button { padding: 0 8px; font-size: 12px; }
137+
.gap-summary { flex: 1 1 0; min-width: 8em; }
138+
.gap-dep-head > .status { margin-left: auto; }
137139
.row-key.via-jira > a { text-decoration: underline dashed; text-underline-offset: 3px; }
138140
.link-icon { width: 13px; height: 13px; margin-right: 2px; vertical-align: -2px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
139141
.issue-row .summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@@ -334,7 +336,7 @@ <h2 x-text="t('services')"></h2>
334336
</template>
335337
<template x-for="entry in unlisted" :key="entry.project">
336338
<div class="service unlisted">
337-
<b :title="entry.project" x-text="shortName(entry.project)"></b>
339+
<b :title="entry.project"><a :href="entry.url" x-text="shortName(entry.project)"></a></b>
338340
<span class="cell cell-outside" x-text="t('notInReport')"></span>
339341
<span class="muted" x-text="t('linkedTo')"></span>
340342
<template x-for="key in entry.keys" :key="key"><span class="key" x-text="key"></span></template>
@@ -394,7 +396,7 @@ <h2 x-text="t('release')"></h2>
394396
<div class="gap-head">
395397
<span class="dot" :class="'dot-' + (task.issue ? task.issue.status_category : 'new')" :title="task.issue && task.issue.status"></span>
396398
<a :href="task.url" x-text="task.key"></a>
397-
<span x-text="task.issue ? task.issue.summary : ''"></span>
399+
<span class="gap-summary" x-text="task.issue ? task.issue.summary : ''"></span>
398400
</div>
399401
<template x-for="(item, index) in gapRows(task)" :key="index">
400402
<div>
@@ -403,15 +405,20 @@ <h2 x-text="t('release')"></h2>
403405
<span class="muted" x-text="relationLabel(item.dep.relation)"></span>
404406
<span class="dot" :class="'dot-' + (item.dep.issue.status_category || 'new')" :title="item.dep.issue.status"></span>
405407
<a :href="item.dep.issue.url" x-text="item.dep.key"></a>
406-
<span class="muted" x-text="item.dep.issue.summary"></span>
408+
<span class="muted gap-summary" x-text="item.dep.issue.summary"></span>
407409
<template x-if="!item.dep.lines.length">
408410
<span :class="statusClass(item.dep.issue)" x-text="item.dep.issue.status"></span>
409411
</template>
410412
</div>
411413
</template>
412414
<template x-if="item.line">
413415
<div class="gap-line" :class="{ nested: item.nested }">
414-
<span :title="item.line.project" x-text="shortName(item.line.project)"></span>
416+
<template x-if="item.line.url">
417+
<a :href="item.line.url" :title="item.line.project" x-text="shortName(item.line.project)"></a>
418+
</template>
419+
<template x-if="!item.line.url">
420+
<span :title="item.line.project" x-text="shortName(item.line.project)"></span>
421+
</template>
415422
<span class="cell" :class="'cell-' + (item.line.state === 'tag' ? 'untagged' : item.line.state)" x-text="item.line.reason"></span>
416423
<template x-if="item.line.state === 'tag'">
417424
<button type="button" @click="pickGap(item.line)" x-text="t('gapPick', { tag: item.line.tag })"></button>
@@ -864,8 +871,8 @@ <h2 x-text="t('release')"></h2>
864871
}
865872
}
866873
for (const [key, issue] of Object.entries(issues)) {
867-
const outside = new Set(issue.links.map((link) => link.project).filter((project) => !projects.has(project)));
868-
for (const project of outside) reach[key].push({ project, state: "outside" });
874+
const outside = new Map(issue.links.filter((link) => !projects.has(link.project)).map((link) => [link.project, link.project_url]));
875+
for (const [project, url] of outside) reach[key].push({ project, state: "outside", url });
869876
}
870877
return reach;
871878
};
@@ -899,7 +906,7 @@ <h2 x-text="t('release')"></h2>
899906
const report = await response.json();
900907
for (const service of report.services) {
901908
const tag = service.release && service.release.tag ? service.release.tag.name : "";
902-
this.picks[service.project] = service.candidates.some((item) => item.tag.name === tag) ? tag : "";
909+
this.picks[service.project] = this.candidateOf(service, tag) ? tag : "";
903910
}
904911
this.reach = reachOf(report);
905912
this.names = namesOf(report);
@@ -973,7 +980,7 @@ <h2 x-text="t('release')"></h2>
973980
return this.pickable
974981
.map((service) => ({
975982
service,
976-
candidate: service.candidates.find((item) => item.tag.name === picks[service.project]),
983+
candidate: this.candidateOf(service, picks[service.project]),
977984
}))
978985
.filter((item) => item.candidate);
979986
},
@@ -1114,7 +1121,7 @@ <h2 x-text="t('release')"></h2>
11141121
},
11151122
postGap(task) {
11161123
const line = (item, indent) =>
1117-
`${indent}- ${item.project}: ${[item.reason, ...this.gapChanges(item).map((change) => `[${change.label}](${change.url})`)].join(" ")}`;
1124+
`${indent}- ${item.url ? `[${item.project}](${item.url})` : item.project}: ${[item.reason, ...this.gapChanges(item).map((change) => `[${change.label}](${change.url})`)].join(" ")}`;
11181125
return [
11191126
`- [${task.key}](${task.url})${task.issue ? " " + task.issue.summary : ""}`,
11201127
...task.lines.map((item) => line(item, " ")),
@@ -1153,7 +1160,7 @@ <h2 x-text="t('release')"></h2>
11531160
for (const key of this.chosen) {
11541161
for (const item of this.reach[key]) {
11551162
if (!plan.has(item.project)) {
1156-
plan.set(item.project, { project: item.project, state: item.state, keys: [], best: null, untagged: [], elsewhere: [] });
1163+
plan.set(item.project, { project: item.project, state: item.state, url: item.url, keys: [], best: null, untagged: [], elsewhere: [] });
11571164
}
11581165
const entry = plan.get(item.project);
11591166
entry.keys.push(key);
@@ -1259,7 +1266,7 @@ <h2 x-text="t('release')"></h2>
12591266
if (wanted.size) {
12601267
for (const service of this.pickable) {
12611268
const tag = wanted.get(service.project) || "";
1262-
const known = !tag || service.candidates.some((item) => item.tag.name === tag);
1269+
const known = !tag || this.candidateOf(service, tag);
12631270
this.picks[service.project] = known ? tag : "";
12641271
if (known) wanted.delete(service.project);
12651272
}
@@ -1278,7 +1285,10 @@ <h2 x-text="t('release')"></h2>
12781285
history.replaceState(null, "", hash ? `#${hash}` : location.pathname + location.search);
12791286
},
12801287
pickOf(service) {
1281-
return service.candidates.find((item) => item.tag.name === this.picks[service.project]);
1288+
return this.candidateOf(service, this.picks[service.project]);
1289+
},
1290+
candidateOf(service, tag) {
1291+
return service.candidates.find((item) => item.tag.name === tag);
12821292
},
12831293
pickHint(service) {
12841294
return service.candidates.length ? this.t("pickTag") : "";

‎tests/test_browser.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ def test_demo_names_a_service_by_its_repo_and_keeps_the_group_only_when_two_shar
340340
outside: typing.Final = page.locator(".service.unlisted b")
341341
expect(outside).to_have_text(["mobile"])
342342
expect(outside).to_have_attribute("title", "partners/mobile")
343+
expect(outside.get_by_role("link")).to_have_attribute("href", "https://gitlab.example.com/partners/mobile")
343344
expect(page.locator(".issue-row", has=page.locator("code", has_text="SHOP-44")).locator(".cell")).to_have_text(
344345
["shop/admin not merged", "legacy/admin not in this report"]
345346
)
@@ -374,6 +375,9 @@ def test_demo_a_hand_pick_lists_what_it_leaves_behind_under_each_task(
374375
]
375376
)
376377
expect(export.locator(".gap-dep-head")).to_have_text(["is blocked by SHOP-44 Only managers may export orders"])
378+
expect(export.get_by_role("link", name="mobile")).to_have_attribute(
379+
"href", "https://gitlab.example.com/partners/mobile"
380+
)
377381
expect(export.get_by_role("link", name="!15")).to_have_attribute(
378382
"href", "https://gitlab.example.com/shop/admin/-/merge_requests/15"
379383
)
@@ -444,10 +448,10 @@ def test_demo_release_post_lists_what_the_picks_leave_behind(page: Page, demo_si
444448
re.escape(
445449
"\n\n**Left behind (3)**\n"
446450
"- [SHOP-38](https://jira.example.com/browse/SHOP-38) Export orders as CSV\n"
447-
" - partners/mobile: not in this report\n"
451+
" - [partners/mobile](https://gitlab.example.com/partners/mobile): not in this report\n"
448452
" - is blocked by [SHOP-44](https://jira.example.com/browse/SHOP-44) Only managers may export orders\n"
449453
" - shop/admin: not merged [!15](https://gitlab.example.com/shop/admin/-/merge_requests/15)\n"
450-
" - legacy/admin: not in this report\n"
454+
" - [legacy/admin](https://gitlab.example.com/legacy/admin): not in this report\n"
451455
"- [SHOP-35]"
452456
)
453457
)

0 commit comments

Comments
 (0)