diff --git a/app/bugdash.mjs b/app/bugdash.mjs
index f06b449..0b5635b 100644
--- a/app/bugdash.mjs
+++ b/app/bugdash.mjs
@@ -12,6 +12,7 @@ import * as REO from "tabs/reo";
import * as Stalled from "tabs/stalled";
import * as Tracked from "tabs/tracked";
import * as Triage from "tabs/triage";
+import * as Uplift from "tabs/uplift";
import * as Tooltips from "tooltips";
import { _ } from "util";
@@ -34,6 +35,7 @@ window.addEventListener("DOMContentLoaded", async () => {
REO.initUI();
Tracked.initUI();
Beta.initUI();
+ Uplift.initUI();
Overview.initUI();
Tooltips.initUI();
diff --git a/app/buglist.mjs b/app/buglist.mjs
index 4723e51..0bce859 100644
--- a/app/buglist.mjs
+++ b/app/buglist.mjs
@@ -136,6 +136,7 @@ export function append({
description,
query,
include,
+ fetchBugs,
template,
augment,
order,
@@ -162,6 +163,7 @@ export function append({
$root: $root,
query: query,
includeFn: include,
+ fetchBugs: fetchBugs,
$timestampTemplate: _(`#bug-row-timestamp-${template || "creation"}`),
augmentFn: augment,
order: "default",
@@ -257,7 +259,9 @@ export async function refresh(id) {
// execute query
let response;
try {
- response = await Bugzilla.rest(buglist.url);
+ response = buglist.fetchBugs
+ ? await buglist.fetchBugs(buglist)
+ : await Bugzilla.rest(buglist.url);
} catch (_error) {
setErrorState(buglist);
return;
diff --git a/app/buglists/uplift.mjs b/app/buglists/uplift.mjs
new file mode 100644
index 0000000..c09cdfa
--- /dev/null
+++ b/app/buglists/uplift.mjs
@@ -0,0 +1,102 @@
+import * as BugList from "buglist";
+import * as Bugzilla from "bugzilla";
+import * as Global from "global";
+
+const FLOOR_VERSION = 5;
+const AFFECTING_VALUES = "affected,fix-optional,?";
+const CHUNK_SIZE = 40;
+
+function buildAnchor(anchorVersion) {
+ return {
+ resolution: ["---", "FIXED"],
+ f1: `cf_status_firefox${anchorVersion}`,
+ o1: "anyexact",
+ v1: "fixed,verified",
+ };
+}
+
+function buildChunkQuery(anchorVersion, startVersion, endVersion) {
+ const query = {
+ ...buildAnchor(anchorVersion),
+ f2: "OP",
+ j2: "OR",
+ };
+ let idx = 3;
+ for (let v = startVersion; v < endVersion; v++) {
+ query[`f${idx}`] = `cf_status_firefox${v}`;
+ query[`o${idx}`] = "anyexact";
+ query[`v${idx}`] = AFFECTING_VALUES;
+ idx++;
+ }
+ query[`f${idx}`] = "CP";
+ return query;
+}
+
+function appendUpliftList({
+ $container,
+ usesComponents,
+ channel,
+ channelTitle,
+ anchorVersion,
+}) {
+ BugList.append({
+ id: `uplift-${channel}-affecting-prior`,
+ $container: $container,
+ title: `${anchorVersion} (${channelTitle}) Fixed, Affecting Prior Versions`,
+ description:
+ "Candidates for uplift/backport consideration.\n" +
+ "Bugs with all of the following:\n" +
+ `- status-firefox${anchorVersion} set to fixed or verified\n` +
+ `- any status-firefox${FLOOR_VERSION}..${anchorVersion - 1} set to ` +
+ "affected, fix-optional, or ?\n" +
+ "Bugs are ordered by creation date, oldest first.",
+ query: buildAnchor(anchorVersion),
+ usesComponents: usesComponents,
+ fetchBugs: async (buglist) => {
+ const components = buglist.usesComponents
+ ? Global.selectedComponents()
+ : undefined;
+ const urls = [];
+ for (
+ let start = FLOOR_VERSION;
+ start < anchorVersion;
+ start += CHUNK_SIZE
+ ) {
+ const end = Math.min(start + CHUNK_SIZE, anchorVersion);
+ urls.push(
+ Bugzilla.queryURL(
+ buildChunkQuery(anchorVersion, start, end),
+ components,
+ ),
+ );
+ }
+ const responses = await Promise.all(urls.map((url) => Bugzilla.rest(url)));
+ const byId = new Map();
+ for (const response of responses) {
+ for (const bug of response.bugs) {
+ byId.set(bug.id, bug);
+ }
+ }
+ return { bugs: Array.from(byId.values()) };
+ },
+ });
+}
+
+export function init($container, usesComponents) {
+ const releases = Global.releaseData();
+ const channels = [
+ {
+ channel: "nightly",
+ channelTitle: "Nightly",
+ anchorVersion: Number(releases.nightly.version),
+ },
+ {
+ channel: "beta",
+ channelTitle: "Beta",
+ anchorVersion: Number(releases.beta.version),
+ },
+ ];
+ for (const c of channels) {
+ appendUpliftList({ $container, usesComponents, ...c });
+ }
+}
diff --git a/app/tabs.mjs b/app/tabs.mjs
index a32221f..53612e1 100644
--- a/app/tabs.mjs
+++ b/app/tabs.mjs
@@ -76,6 +76,10 @@ function addTabs() {
name: "beta",
title: "Beta",
},
+ {
+ name: "uplift",
+ title: "Potential Uplifts",
+ },
]);
}
diff --git a/app/tabs/uplift.mjs b/app/tabs/uplift.mjs
new file mode 100644
index 0000000..0bf565d
--- /dev/null
+++ b/app/tabs/uplift.mjs
@@ -0,0 +1,10 @@
+import * as BugList from "buglist";
+import * as Uplift from "buglists/uplift";
+import { _ } from "util";
+
+export function initUI() {
+ const $content = _("#uplift-content");
+
+ const $group = BugList.newGroup($content);
+ Uplift.init($group, false);
+}
diff --git a/index.html b/index.html
index 907f43a..fc56c0d 100644
--- a/index.html
+++ b/index.html
@@ -37,8 +37,8 @@
-
+