Bug 1574651 - add tc_root_url to each repository (#5405)

* Bug 1574651 - add tc_root_url to each repository

* Fix issues introduced since initial creation of this feature
* Fix unit tests
* Fix retriggers in Compare Chooser of Perfherder
This commit is contained in:
Cameron Dawson 2019-09-30 16:18:07 -07:00 коммит произвёл GitHub
Родитель 6ca78bd25c
Коммит 7f72192565
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
35 изменённых файлов: 742 добавлений и 457 удалений

Просмотреть файл

@ -66,6 +66,7 @@
"redux-thunk": "2.3.0",
"taskcluster-client-web": "8.1.1",
"taskcluster-lib-scopes": "10.0.2",
"taskcluster-lib-urls": "12.0.0",
"webpack": "4.39.3",
"webpack-cli": "3.3.8",
"victory": "33.0.5"

Просмотреть файл

@ -14,6 +14,7 @@
"active_status": "active",
"performance_alerts_enabled": false,
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net",
"is_try_repo": false,
"pushLogUrl": "https://hg.mozilla.org/mozilla-central/pushloghtml",
"revisionHrefPrefix": "https://hg.mozilla.org/mozilla-central/rev/"
@ -33,6 +34,7 @@
"active_status": "active",
"performance_alerts_enabled": false,
"expire_performance_data": true,
"tc_root_url": "https://taskcluster.net",
"is_try_repo": true,
"pushLogUrl": "https://hg.mozilla.org/try/pushloghtml",
"revisionHrefPrefix": "https://hg.mozilla.org/try/rev/"
@ -52,6 +54,7 @@
"active_status": "active",
"performance_alerts_enabled": true,
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net",
"is_try_repo": false,
"pushLogUrl": "https://hg.mozilla.org/integration/autoland/pushloghtml",
"revisionHrefPrefix": "https://hg.mozilla.org/integration/autoland/rev/"

Просмотреть файл

@ -5,6 +5,7 @@ import { decisionTaskIdCache } from '../../../ui/models/push';
import { getApiUrl } from '../../../ui/helpers/url';
import paginatedJobListFixtureOne from '../mock/job_list/pagination/page_1';
import paginatedJobListFixtureTwo from '../mock/job_list/pagination/page_2';
import repositories from '../mock/repositories';
import { getProjectUrl } from '../../../ui/helpers/location';
describe('JobModel', () => {
@ -57,6 +58,7 @@ describe('JobModel', () => {
const testJobs = [
{ id: 123, push_id: 526443, job_type_name: 'foo', task_id: 'TASKID' },
];
const currentRepo = repositories[2];
beforeEach(() => {
fetchMock.mock(
@ -89,7 +91,7 @@ describe('JobModel', () => {
test('retrigger uses passed-in decisionTaskMap', async () => {
await JobModel.retrigger(
testJobs,
'autoland',
currentRepo,
notify,
1,
decisionTaskMap,
@ -101,7 +103,7 @@ describe('JobModel', () => {
});
test('retrigger calls for decision task when not passed-in', async () => {
await JobModel.retrigger(testJobs, 'autoland', notify, 1);
await JobModel.retrigger(testJobs, currentRepo, notify, 1);
expect(fetchMock.called(decisionTaskMapUrl)).toBe(true);
expect(fetchMock.called(tcTaskUrl)).toBe(false);
@ -109,7 +111,7 @@ describe('JobModel', () => {
});
test('cancel uses passed-in decisionTask', async () => {
await JobModel.cancel(testJobs, 'autoland', () => {}, decisionTaskMap);
await JobModel.cancel(testJobs, currentRepo, () => {}, decisionTaskMap);
expect(fetchMock.called(decisionTaskMapUrl)).toBe(false);
expect(fetchMock.called(tcTaskUrl)).toBe(true);
@ -117,7 +119,7 @@ describe('JobModel', () => {
});
test('cancel calls for decision task when not passed-in', async () => {
await JobModel.cancel(testJobs, 'autoland', () => {});
await JobModel.cancel(testJobs, currentRepo, () => {});
expect(fetchMock.called(decisionTaskMapUrl)).toBe(true);
expect(fetchMock.called(tcTaskUrl)).toBe(true);
@ -127,7 +129,7 @@ describe('JobModel', () => {
test('cancelAll uses passed-in decisionTask', async () => {
const decisionTask = { id: 'LVTawdmFR2-uJiWWS2NxSw', run: '0' };
await JobModel.cancelAll(526443, 'autoland', () => {}, decisionTask);
await JobModel.cancelAll(526443, currentRepo, () => {}, decisionTask);
expect(fetchMock.called(decisionTaskMapUrl)).toBe(false);
expect(fetchMock.called(tcTaskUrl)).toBe(false);
@ -135,7 +137,7 @@ describe('JobModel', () => {
});
test('cancelAll calls for decision task when not passed-in', async () => {
await JobModel.cancelAll(526443, 'autoland', () => {});
await JobModel.cancelAll(526443, currentRepo, () => {});
expect(fetchMock.called(decisionTaskMapUrl)).toBe(true);
expect(fetchMock.called(tcTaskUrl)).toBe(false);

Просмотреть файл

@ -7,6 +7,7 @@ import {
waitForElementToBeRemoved,
} from '@testing-library/react';
import projects from '../mock/repositories';
import CompareTableControls from '../../../ui/perfherder/compare/CompareTableControls';
import { compareTableText, filterText } from '../../../ui/perfherder/constants';
import CompareTable from '../../../ui/perfherder/compare/CompareTable';
@ -103,6 +104,7 @@ const compareTableControlsNode = (onPermalinkClick, userLoggedIn = false) => {
notify={() => {}}
isBaseAggregate={false}
onPermalinkClick={handlePermalinkClick}
projects={projects}
/>
);
};
@ -126,6 +128,7 @@ const compareTable = (
getJob={(repoName, jobId) => {
return { id: jobId };
}}
projects={projects}
/>,
);

Просмотреть файл

@ -10,7 +10,8 @@
"codebase": "gecko",
"repository_group": 1,
"description": "",
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -25,7 +26,8 @@
"repository_group": 1,
"description": "",
"performance_alerts_enabled": true,
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -38,7 +40,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 1,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -52,7 +55,8 @@
"codebase": "gecko",
"repository_group": 1,
"description": "",
"is_try_repo": true
"is_try_repo": true,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -67,7 +71,8 @@
"repository_group": 2,
"description": "",
"performance_alerts_enabled": true,
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -82,7 +87,8 @@
"repository_group": 2,
"description": "",
"performance_alerts_enabled": true,
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -97,7 +103,8 @@
"repository_group": 2,
"description": "",
"performance_alerts_enabled": true,
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -110,7 +117,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -123,7 +131,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -136,7 +145,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -149,7 +159,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -162,7 +173,8 @@
"active_status": "onhold",
"codebase": "jetpack",
"repository_group": 1,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -175,7 +187,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -190,7 +203,8 @@
"repository_group": 1,
"description": "",
"performance_alerts_enabled": false,
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -203,7 +217,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -216,7 +231,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -229,7 +245,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -242,7 +259,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -255,7 +273,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -268,7 +287,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -281,7 +301,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -294,7 +315,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -307,7 +329,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -320,7 +343,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -333,7 +357,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -346,7 +371,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -359,7 +385,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -372,7 +399,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -385,7 +413,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -398,7 +427,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -411,7 +441,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -424,7 +455,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -437,7 +469,8 @@
"active_status": "active",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -451,7 +484,8 @@
"codebase": "comm",
"repository_group": 8,
"description": "",
"is_try_repo": true
"is_try_repo": true,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -464,7 +498,8 @@
"active_status": "onhold",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -477,7 +512,8 @@
"active_status": "active",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -490,7 +526,8 @@
"active_status": "onhold",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -503,7 +540,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 6,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -516,7 +554,8 @@
"active_status": "onhold",
"codebase": "",
"repository_group": 1,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -529,7 +568,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -543,7 +583,8 @@
"active_status": "onhold",
"codebase": "gaia",
"repository_group": 1,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -556,7 +597,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -569,7 +611,8 @@
"active_status": "onhold",
"codebase": "taskcluster",
"repository_group": 4,
"description": "taskcluster integration test dumping ground."
"description": "taskcluster integration test dumping ground.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -583,7 +626,8 @@
"active_status": "onhold",
"codebase": "gaia",
"repository_group": 4,
"description": "Gaia master branch"
"description": "Gaia master branch",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -596,7 +640,8 @@
"active_status": "onhold",
"codebase": "gaia",
"repository_group": 4,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -609,7 +654,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 4,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -622,7 +668,8 @@
"active_status": "onhold",
"codebase": "gaia",
"repository_group": 1,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -635,7 +682,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -648,7 +696,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 5,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -661,7 +710,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -674,7 +724,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -687,7 +738,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 1,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -700,7 +752,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -713,7 +766,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -726,7 +780,8 @@
"active_status": "onhold",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -739,7 +794,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -752,7 +808,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -766,7 +823,8 @@
"active_status": "onhold",
"codebase": "bugzilla",
"repository_group": 4,
"description": "A suite of tests to check the quality of the Bugzilla codebase."
"description": "A suite of tests to check the quality of the Bugzilla codebase.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -780,7 +838,8 @@
"active_status": "onhold",
"codebase": "bugzilla",
"repository_group": 4,
"description": "A suite of tests to check the quality of the BMO codebase."
"description": "A suite of tests to check the quality of the BMO codebase.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -793,7 +852,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -806,7 +866,8 @@
"active_status": "onhold",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -820,7 +881,8 @@
"active_status": "onhold",
"codebase": "bugzilla",
"repository_group": 4,
"description": "A suite of tests to check the quality of the Bugzilla 5.0 codebase."
"description": "A suite of tests to check the quality of the Bugzilla 5.0 codebase.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -834,7 +896,8 @@
"active_status": "onhold",
"codebase": "bugzilla",
"repository_group": 4,
"description": "A suite of tests to check the quality of the Bugzilla 4.4 codebase."
"description": "A suite of tests to check the quality of the Bugzilla 4.4 codebase.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -848,7 +911,8 @@
"active_status": "onhold",
"codebase": "bugzilla",
"repository_group": 4,
"description": "A suite of tests to check the quality of the Bugzilla 4.2 codebase."
"description": "A suite of tests to check the quality of the Bugzilla 4.2 codebase.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -862,7 +926,8 @@
"active_status": "onhold",
"codebase": "bugzilla",
"repository_group": 4,
"description": "A suite of tests to check the quality of the BMO codebase."
"description": "A suite of tests to check the quality of the BMO codebase.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -875,7 +940,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -888,7 +954,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -902,7 +969,8 @@
"active_status": "onhold",
"codebase": "bugzilla",
"repository_group": 4,
"description": "A suite of tests to check the quality of the BMO codebase."
"description": "A suite of tests to check the quality of the BMO codebase.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -915,7 +983,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -928,7 +997,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -941,7 +1011,8 @@
"active_status": "onhold",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -956,7 +1027,8 @@
"codebase": "servo",
"repository_group": 10,
"description": "The Servo Parallel Browser Engine master",
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -969,7 +1041,8 @@
"active_status": "active",
"codebase": "version-control-tools",
"repository_group": 7,
"description": "Version control infrastructure and tooling."
"description": "Version control infrastructure and tooling.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -984,7 +1057,8 @@
"repository_group": 1,
"description": "The destination for automatically landed Firefox commits.",
"performance_alerts_enabled": true,
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -998,7 +1072,8 @@
"active_status": "onhold",
"codebase": "autopush",
"repository_group": 7,
"description": "Mozilla Push server and Push Endpoint."
"description": "Mozilla Push server and Push Endpoint.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1011,7 +1086,8 @@
"active_status": "active",
"codebase": "nss",
"repository_group": 7,
"description": "Network Security Services."
"description": "Network Security Services.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1025,7 +1101,8 @@
"codebase": "nss",
"repository_group": 7,
"description": "Network Security Services.",
"is_try_repo": true
"is_try_repo": true,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1039,7 +1116,8 @@
"active_status": "onhold",
"codebase": "example-addon-repo",
"repository_group": 7,
"description": "Example add-on repository for templates and best practices."
"description": "Example add-on repository for templates and best practices.",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1052,7 +1130,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 1,
"description": "Testing Servo integration with Gecko"
"description": "Testing Servo integration with Gecko",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1066,7 +1145,8 @@
"codebase": "gecko",
"repository_group": 1,
"description": "Try repo for Servo integration",
"is_try_repo": true
"is_try_repo": true,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1079,7 +1159,8 @@
"active_status": "onhold",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1092,7 +1173,8 @@
"active_status": "onhold",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1106,7 +1188,8 @@
"active_status": "active",
"codebase": "servo",
"repository_group": 7,
"description": "GPU renderer for Servo"
"description": "GPU renderer for Servo",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1120,7 +1203,8 @@
"active_status": "onhold",
"codebase": "addon-tests",
"repository_group": 5,
"description": "Tests for Add-ons"
"description": "Tests for Add-ons",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1134,7 +1218,8 @@
"active_status": "onhold",
"codebase": "fxapom",
"repository_group": 5,
"description": "Firefox Account Page Object Model"
"description": "Firefox Account Page Object Model",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1148,7 +1233,8 @@
"active_status": "onhold",
"codebase": "go-bouncer",
"repository_group": 5,
"description": "Go version of the redirector portion of bouncer"
"description": "Go version of the redirector portion of bouncer",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1162,7 +1248,8 @@
"active_status": "onhold",
"codebase": "mozillians-tests",
"repository_group": 5,
"description": "Tests for Mozillians"
"description": "Tests for Mozillians",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1176,7 +1263,8 @@
"active_status": "active",
"codebase": "snippets-service",
"repository_group": 5,
"description": "Snippets Service"
"description": "Snippets Service",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1190,7 +1278,8 @@
"active_status": "active",
"codebase": "socorro",
"repository_group": 5,
"description": "Server for collecting, processing, and displaying crash reports from clients using the Breakpad libraries"
"description": "Server for collecting, processing, and displaying crash reports from clients using the Breakpad libraries",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1204,7 +1293,8 @@
"active_status": "onhold",
"codebase": "stubattribution-tests",
"repository_group": 5,
"description": "Tests for the stub attribution service"
"description": "Tests for the stub attribution service",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1218,7 +1308,8 @@
"active_status": "onhold",
"codebase": "treeherder",
"repository_group": 5,
"description": "A system for managing CI data for Mozilla projects"
"description": "A system for managing CI data for Mozilla projects",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1231,7 +1322,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1244,7 +1336,8 @@
"active_status": "active",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1257,7 +1350,8 @@
"active_status": "active",
"codebase": "ci-admin",
"repository_group": 9,
"description": "Administrative scripts for Firefox CI"
"description": "Administrative scripts for Firefox CI",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1270,7 +1364,8 @@
"active_status": "active",
"codebase": "ci-admin",
"repository_group": 9,
"description": "Administrative configuration for Firefox CI"
"description": "Administrative configuration for Firefox CI",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1285,7 +1380,8 @@
"codebase": "servo",
"repository_group": 10,
"description": "The Servo Parallel Browser Engine try (multiple branches)",
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1300,7 +1396,8 @@
"codebase": "servo",
"repository_group": 10,
"description": "The Servo Parallel Browser Engine auto: testing PRs after review and before merging to master.",
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1315,7 +1412,8 @@
"codebase": "servo",
"repository_group": 10,
"description": "The Servo Parallel Browser Engine try, Taskcluster only",
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1330,7 +1428,8 @@
"codebase": "servo",
"repository_group": 10,
"description": "The Servo Parallel Browser Engine pull requests",
"expire_performance_data": false
"expire_performance_data": false,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1343,7 +1442,8 @@
"active_status": "active",
"codebase": "taskgraph",
"repository_group": 9,
"description": "Tools for defining graphs fo taskclutser tasks"
"description": "Tools for defining graphs fo taskclutser tasks",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1357,7 +1457,8 @@
"codebase": "taskgraph",
"repository_group": 9,
"description": "Tools for defining graphs fo taskclutser tasks",
"is_try_repo": true
"is_try_repo": true,
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1370,7 +1471,8 @@
"active_status": "active",
"codebase": "ci-admin",
"repository_group": 9,
"description": "Administrative scripts for Firefox CI"
"description": "Administrative scripts for Firefox CI",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1383,7 +1485,8 @@
"active_status": "active",
"codebase": "ci-admin",
"repository_group": 9,
"description": "Administrative configuration for Firefox CI"
"description": "Administrative configuration for Firefox CI",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1397,7 +1500,8 @@
"active_status": "active",
"codebase": "reference-browser",
"repository_group": 11,
"description": "A full-featured browser reference implementation using Mozilla Android Components"
"description": "A full-featured browser reference implementation using Mozilla Android Components",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1411,7 +1515,8 @@
"active_status": "active",
"codebase": "fenix",
"repository_group": 11,
"description": "Fenix is not your parent's Android browser"
"description": "Fenix is not your parent's Android browser",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1424,7 +1529,8 @@
"active_status": "active",
"codebase": "gecko",
"repository_group": 2,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1437,7 +1543,8 @@
"active_status": "active",
"codebase": "comm",
"repository_group": 8,
"description": ""
"description": "",
"tc_root_url": "https://taskcluster.net"
}
},
{
@ -1451,7 +1558,8 @@
"active_status": "active",
"codebase": "android-components",
"repository_group": 11,
"description": "A collection of Android libraries to build browsers or browser-like applications."
"description": "A collection of Android libraries to build browsers or browser-like applications.",
"tc_root_url": "https://taskcluster.net"
}
}
]

Просмотреть файл

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 2.2.4 on 2019-08-28 17:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('model', '0014_add_job_log_status_skipped_size'),
]
operations = [
migrations.AddField(
model_name='repository',
name='tc_root_url',
field=models.CharField(db_index=True, default='https://taskcluster.net', max_length=255),
# only apply this default when migrating; after this users must supply their
# own rootUrl (and by the time you're reading this, `taskcluster.net` is likely
# no longer operating)
preserve_default=False,
),
]

Просмотреть файл

@ -108,6 +108,7 @@ class Repository(models.Model):
performance_alerts_enabled = models.BooleanField(default=False)
expire_performance_data = models.BooleanField(default=True)
is_try_repo = models.BooleanField(default=False)
tc_root_url = models.CharField(max_length=255, null=False, db_index=True)
class Meta:
db_table = 'repository'

Просмотреть файл

@ -1,6 +1,6 @@
import { OIDCCredentialAgent, Queue } from 'taskcluster-client-web';
import { tcRootUrl, getUserSessionUrl } from './url';
import { loginRootUrl, getUserSessionUrl } from './url';
const taskcluster = (() => {
let credentialAgent = null;
@ -19,7 +19,7 @@ const taskcluster = (() => {
accessToken: JSON.parse(userSession).accessToken,
oidcProvider,
url: getUserSessionUrl(oidcProvider),
rootUrl: tcRootUrl,
rootUrl: loginRootUrl,
});
}
@ -32,7 +32,7 @@ const taskcluster = (() => {
getQueue: () =>
new Queue({
credentialAgent: tcAgent(),
rootUrl: tcRootUrl,
rootUrl: loginRootUrl,
}),
updateAgent: () => {
const userSession = localStorage.getItem('userSession');

Просмотреть файл

@ -12,7 +12,8 @@ export const hgBaseUrl = 'https://hg.mozilla.org/';
export const dxrBaseUrl = 'https://dxr.mozilla.org/';
export const tcRootUrl = 'https://taskcluster.net';
// the rootUrl of the TC deployment for which user login gets credentials
export const loginRootUrl = 'https://taskcluster.net';
export const bugsEndpoint = 'failures/';

Просмотреть файл

@ -10,6 +10,7 @@ import jsonSchemaDefaults from 'json-schema-defaults';
// https://github.com/nodeca/js-yaml/pull/462
import jsyaml from 'js-yaml/dist/js-yaml';
import { slugid } from 'taskcluster-client-web';
import tcLibUrls from 'taskcluster-lib-urls';
import {
Button,
Label,
@ -45,10 +46,10 @@ class CustomJobActions extends React.PureComponent {
}
async componentDidMount() {
const { pushId, job, notify, decisionTaskMap } = this.props;
const { pushId, job, notify, decisionTaskMap, currentRepo } = this.props;
const { id: decisionTaskId } = decisionTaskMap[pushId];
TaskclusterModel.load(decisionTaskId, job).then(results => {
TaskclusterModel.load(decisionTaskId, job, currentRepo).then(results => {
const {
originalTask,
originalTaskId,
@ -123,7 +124,7 @@ class CustomJobActions extends React.PureComponent {
selectedActionOption,
staticActionVariables,
} = this.state;
const { notify } = this.props;
const { notify, currentRepo } = this.props;
const action = selectedActionOption.value;
let input = null;
@ -151,11 +152,12 @@ class CustomJobActions extends React.PureComponent {
task: originalTask,
input,
staticActionVariables,
currentRepo,
}).then(
taskId => {
this.setState({ triggering: false });
let message = 'Custom action request sent successfully:';
let url = `https://tools.taskcluster.net/tasks/${taskId}`;
let url = tcLibUrls.ui(currentRepo.tc_root_url, `/tasks/${taskId}`);
// For the time being, we are redirecting specific actions to
// specific urls that are different than usual. At this time, we are
@ -166,7 +168,7 @@ class CustomJobActions extends React.PureComponent {
'generic-worker-windows-loaner',
];
if (loaners.includes(action.name)) {
message = 'Visit Taskcluster Tools site to access loaner:';
message = 'Visit Taskcluster site to access loaner:';
url = `${url}/connect`;
}
notify(message, 'success', { linkText: 'Open in Taskcluster', url });
@ -306,6 +308,7 @@ CustomJobActions.propTypes = {
toggle: PropTypes.func.isRequired,
decisionTaskMap: PropTypes.object.isRequired,
job: PropTypes.object,
currentRepo: PropTypes.object.isRequired,
};
CustomJobActions.defaultProps = {

Просмотреть файл

@ -97,7 +97,7 @@ class DetailsPanel extends React.Component {
};
loadBugSuggestions = () => {
const { repoName, selectedJob } = this.props;
const { currentRepo, selectedJob } = this.props;
if (!selectedJob) {
return;
@ -131,7 +131,7 @@ class DetailsPanel extends React.Component {
result: step.result,
logViewerUrl: getLogViewerUrl(
selectedJob.id,
repoName,
currentRepo.name,
step.finished_line_number,
),
}));
@ -160,7 +160,7 @@ class DetailsPanel extends React.Component {
};
selectJob = () => {
const { repoName, selectedJob } = this.props;
const { currentRepo, selectedJob } = this.props;
const push = this.findPush(selectedJob.push_id);
this.setState(
@ -178,7 +178,7 @@ class DetailsPanel extends React.Component {
'logs' in selectedJob
? Promise.resolve(selectedJob)
: JobModel.get(
repoName,
currentRepo.name,
selectedJob.id,
this.selectJobController.signal,
);
@ -193,9 +193,12 @@ class DetailsPanel extends React.Component {
this.selectJobController.signal,
);
const phSeriesPromise = PerfSeriesModel.getSeriesData(repoName, {
job_id: selectedJob.id,
});
const phSeriesPromise = PerfSeriesModel.getSeriesData(
currentRepo.name,
{
job_id: selectedJob.id,
},
);
Promise.all([
jobPromise,
@ -235,7 +238,10 @@ class DetailsPanel extends React.Component {
logParseStatus = jobLogUrls[0].parse_status;
}
const logViewerUrl = getLogViewerUrl(selectedJob.id, repoName);
const logViewerUrl = getLogViewerUrl(
selectedJob.id,
currentRepo.name,
);
const logViewerFullUrl = `${window.location.origin}/${logViewerUrl}`;
const reftestUrl = jobLogUrls.length
? getReftestUrl(jobLogUrls[0].url)
@ -252,7 +258,7 @@ class DetailsPanel extends React.Component {
];
const seriesListList = await Promise.all(
chunk(signatureIds, 20).map(signatureIdChunk =>
PerfSeriesModel.getSeriesList(repoName, {
PerfSeriesModel.getSeriesList(currentRepo.name, {
id: signatureIdChunk,
}),
),
@ -270,12 +276,12 @@ class DetailsPanel extends React.Component {
.filter(d => !d.series.parentSignature)
.map(d => ({
url: `/perf.html#/graphs?series=${[
repoName,
currentRepo.name,
d.signature_id,
1,
d.series.frameworkId,
]}&selected=${[
repoName,
currentRepo.name,
d.signature_id,
selectedJob.push_id,
d.id,
@ -314,7 +320,6 @@ class DetailsPanel extends React.Component {
render() {
const {
repoName,
user,
currentRepo,
resizedHeight,
@ -350,7 +355,6 @@ class DetailsPanel extends React.Component {
className={selectedJobFull ? 'details-panel-slide' : 'hidden'}
>
<PinBoard
repoName={repoName}
currentRepo={currentRepo}
isLoggedIn={user.isLoggedIn || false}
classificationTypes={classificationTypes}
@ -360,7 +364,6 @@ class DetailsPanel extends React.Component {
<div id="details-panel-content">
<SummaryPanel
selectedJobFull={selectedJobFull}
repoName={repoName}
currentRepo={currentRepo}
classificationMap={classificationMap}
jobLogUrls={jobLogUrls}
@ -379,7 +382,7 @@ class DetailsPanel extends React.Component {
selectedJobFull={selectedJobFull}
jobDetails={jobDetails}
perfJobDetail={perfJobDetail}
repoName={repoName}
repoName={currentRepo.name}
jobRevision={jobRevision}
suggestions={suggestions}
errors={errors}
@ -404,7 +407,6 @@ class DetailsPanel extends React.Component {
}
DetailsPanel.propTypes = {
repoName: PropTypes.string.isRequired,
currentRepo: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,
resizedHeight: PropTypes.number.isRequired,

Просмотреть файл

@ -198,14 +198,14 @@ class PinBoard extends React.Component {
};
cancelAllPinnedJobs = () => {
const { notify, repoName, pinnedJobs, decisionTaskMap } = this.props;
const { notify, currentRepo, pinnedJobs, decisionTaskMap } = this.props;
if (
window.confirm('This will cancel all the selected jobs. Are you sure?')
) {
JobModel.cancel(
Object.values(pinnedJobs),
repoName,
currentRepo,
notify,
decisionTaskMap,
);
@ -354,10 +354,10 @@ class PinBoard extends React.Component {
};
retriggerAllPinnedJobs = async () => {
const { pinnedJobs, notify, repoName, decisionTaskMap } = this.props;
const { pinnedJobs, notify, currentRepo, decisionTaskMap } = this.props;
const jobs = Object.values(pinnedJobs);
JobModel.retrigger(jobs, repoName, notify, 1, decisionTaskMap);
JobModel.retrigger(jobs, currentRepo, notify, 1, decisionTaskMap);
};
render() {
@ -650,7 +650,6 @@ PinBoard.propTypes = {
setClassificationComment: PropTypes.func.isRequired,
setSelectedJob: PropTypes.func.isRequired,
notify: PropTypes.func.isRequired,
repoName: PropTypes.string.isRequired,
currentRepo: PropTypes.object.isRequired,
failureClassificationId: PropTypes.number.isRequired,
failureClassificationComment: PropTypes.string.isRequired,

Просмотреть файл

@ -77,53 +77,62 @@ class ActionBar extends React.PureComponent {
};
createGeckoProfile = async () => {
const { user, selectedJobFull, notify, decisionTaskMap } = this.props;
const {
user,
selectedJobFull,
notify,
decisionTaskMap,
currentRepo,
} = this.props;
if (!user.isLoggedIn) {
return notify('Must be logged in to create a gecko profile', 'danger');
}
const decisionTaskId = decisionTaskMap[selectedJobFull.push_id];
TaskclusterModel.load(decisionTaskId, selectedJobFull).then(results => {
try {
const geckoprofile = getAction(results.actions, 'geckoprofile');
TaskclusterModel.load(decisionTaskId, selectedJobFull, currentRepo).then(
results => {
try {
const geckoprofile = getAction(results.actions, 'geckoprofile');
if (
geckoprofile === undefined ||
!Object.prototype.hasOwnProperty.call(geckoprofile, 'kind')
) {
return notify(
'Job was scheduled without taskcluster support for GeckoProfiles',
);
}
TaskclusterModel.submit({
action: geckoprofile,
decisionTaskId,
taskId: results.originalTaskId,
task: results.originalTask,
input: {},
staticActionVariables: results.staticActionVariables,
}).then(
() => {
notify(
'Request sent to collect gecko profile job via actions.json',
'success',
if (
geckoprofile === undefined ||
!Object.prototype.hasOwnProperty.call(geckoprofile, 'kind')
) {
return notify(
'Job was scheduled without taskcluster support for GeckoProfiles',
);
},
e => {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
},
);
} catch (e) {
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
});
}
TaskclusterModel.submit({
action: geckoprofile,
decisionTaskId,
taskId: results.originalTaskId,
task: results.originalTask,
input: {},
staticActionVariables: results.staticActionVariables,
currentRepo,
}).then(
() => {
notify(
'Request sent to collect gecko profile job via actions.json',
'success',
);
},
e => {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
},
);
} catch (e) {
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
},
);
};
retriggerJob = async jobs => {
const { user, repoName, notify, decisionTaskMap } = this.props;
const { user, notify, decisionTaskMap, currentRepo } = this.props;
if (!user.isLoggedIn) {
return notify('Must be logged in to retrigger a job', 'danger');
@ -141,11 +150,17 @@ class ActionBar extends React.PureComponent {
});
});
JobModel.retrigger(jobs, repoName, notify, 1, decisionTaskMap);
JobModel.retrigger(jobs, currentRepo, notify, 1, decisionTaskMap);
};
backfillJob = async () => {
const { user, selectedJobFull, notify, decisionTaskMap } = this.props;
const {
user,
selectedJobFull,
notify,
decisionTaskMap,
currentRepo,
} = this.props;
if (!this.canBackfill()) {
return;
@ -165,34 +180,46 @@ class ActionBar extends React.PureComponent {
const { id: decisionTaskId } = decisionTaskMap[selectedJobFull.push_id];
TaskclusterModel.load(decisionTaskId, selectedJobFull).then(results => {
try {
const backfilltask = getAction(results.actions, 'backfill');
TaskclusterModel.load(decisionTaskId, selectedJobFull, currentRepo).then(
results => {
try {
const backfilltask = getAction(results.actions, 'backfill');
return TaskclusterModel.submit({
action: backfilltask,
decisionTaskId,
taskId: results.originalTaskId,
input: {},
staticActionVariables: results.staticActionVariables,
}).then(
() => {
notify('Request sent to backfill job via actions.json', 'success');
},
e => {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
},
);
} catch (e) {
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
});
return TaskclusterModel.submit({
action: backfilltask,
decisionTaskId,
taskId: results.originalTaskId,
input: {},
staticActionVariables: results.staticActionVariables,
currentRepo,
}).then(
() => {
notify(
'Request sent to backfill job via actions.json',
'success',
);
},
e => {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
},
);
} catch (e) {
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
},
);
};
isolateJob = async () => {
const { user, selectedJobFull, notify, decisionTaskMap } = this.props;
const {
user,
selectedJobFull,
notify,
decisionTaskMap,
currentRepo,
} = this.props;
const { id: decisionTaskId } = decisionTaskMap[selectedJobFull.push_id];
if (!isTestIsolatable(selectedJobFull)) {
@ -217,63 +244,66 @@ class ActionBar extends React.PureComponent {
return;
}
TaskclusterModel.load(decisionTaskId, selectedJobFull).then(results => {
try {
const isolationtask = getAction(
results.actions,
'isolate-test-failures',
);
if (!isolationtask) {
notify(
'Request to isolate job via actions.json failed could not find action.',
'danger',
{ sticky: true },
TaskclusterModel.load(decisionTaskId, selectedJobFull, currentRepo).then(
results => {
try {
const isolationtask = getAction(
results.actions,
'isolate-test-failures',
);
return;
}
let times = 1;
let response = null;
do {
response = window.prompt(
'Enter number of times (1..100) to run isolation jobs: ',
times,
);
if (response == null) {
break;
}
times = parseInt(response, 10);
} while (Number.isNaN(times) || times < 1 || times > 100);
if (response === null) {
notify('Request to isolate job via actions.json aborted.');
return;
}
return TaskclusterModel.submit({
action: isolationtask,
decisionTaskId,
taskId: results.originalTaskId,
input: { times },
staticActionVariables: results.staticActionVariables,
}).then(
() => {
if (!isolationtask) {
notify(
'Request sent to isolate-test-failures job via actions.json',
'success',
'Request to isolate job via actions.json failed could not find action.',
'danger',
{ sticky: true },
);
},
e => {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
},
);
} catch (e) {
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
});
return;
}
let times = 1;
let response = null;
do {
response = window.prompt(
'Enter number of times (1..100) to run isolation jobs: ',
times,
);
if (response == null) {
break;
}
times = parseInt(response, 10);
} while (Number.isNaN(times) || times < 1 || times > 100);
if (response === null) {
notify('Request to isolate job via actions.json aborted.');
return;
}
return TaskclusterModel.submit({
action: isolationtask,
decisionTaskId,
taskId: results.originalTaskId,
input: { times },
staticActionVariables: results.staticActionVariables,
currentRepo,
}).then(
() => {
notify(
'Request sent to isolate-test-failures job via actions.json',
'success',
);
},
e => {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
},
);
} catch (e) {
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
},
);
};
// Can we backfill? At the moment, this only ensures we're not in a 'try' repo.
@ -308,7 +338,13 @@ class ActionBar extends React.PureComponent {
};
createInteractiveTask = async () => {
const { user, selectedJobFull, notify, decisionTaskMap } = this.props;
const {
user,
selectedJobFull,
notify,
decisionTaskMap,
currentRepo,
} = this.props;
if (!user.isLoggedIn) {
return notify(
@ -321,6 +357,7 @@ class ActionBar extends React.PureComponent {
const results = await TaskclusterModel.load(
decisionTaskId,
selectedJobFull,
currentRepo,
);
try {
@ -334,6 +371,7 @@ class ActionBar extends React.PureComponent {
notify: user.email,
},
staticActionVariables: results.staticActionVariables,
currentRepo,
});
notify(
@ -349,14 +387,14 @@ class ActionBar extends React.PureComponent {
};
cancelJobs = jobs => {
const { user, repoName, notify, decisionTaskMap } = this.props;
const { user, notify, decisionTaskMap, currentRepo } = this.props;
if (!user.isLoggedIn) {
return notify('Must be logged in to cancel a job', 'danger');
}
JobModel.cancel(
jobs.filter(({ state }) => state === 'pending' || state === 'running'),
repoName,
currentRepo,
notify,
decisionTaskMap,
);
@ -380,6 +418,7 @@ class ActionBar extends React.PureComponent {
jobLogUrls,
user,
pinJob,
currentRepo,
} = this.props;
const { customJobActionsShowing } = this.state;
@ -538,6 +577,7 @@ class ActionBar extends React.PureComponent {
<CustomJobActions
job={selectedJobFull}
pushId={selectedJobFull.push_id}
currentRepo={currentRepo}
isLoggedIn={user.isLoggedIn}
toggle={this.toggleCustomJobActions}
/>
@ -551,11 +591,11 @@ ActionBar.propTypes = {
pinJob: PropTypes.func.isRequired,
decisionTaskMap: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,
repoName: PropTypes.string.isRequired,
selectedJobFull: PropTypes.object.isRequired,
logParseStatus: PropTypes.string.isRequired,
notify: PropTypes.func.isRequired,
jobLogUrls: PropTypes.array,
currentRepo: PropTypes.object.isRequired,
isTryRepo: PropTypes.bool,
logViewerUrl: PropTypes.string,
logViewerFullUrl: PropTypes.string,

Просмотреть файл

@ -12,7 +12,6 @@ import StatusPanel from './StatusPanel';
class SummaryPanel extends React.PureComponent {
render() {
const {
repoName,
selectedJobFull,
latestClassification,
bugs,
@ -39,8 +38,8 @@ class SummaryPanel extends React.PureComponent {
<div id="summary-panel" role="region" aria-label="Summary">
<ActionBar
selectedJobFull={selectedJobFull}
repoName={repoName}
logParseStatus={logParseStatus}
currentRepo={currentRepo}
isTryRepo={currentRepo.is_try_repo}
logViewerUrl={logViewerUrl}
logViewerFullUrl={logViewerFullUrl}
@ -81,7 +80,6 @@ class SummaryPanel extends React.PureComponent {
}
SummaryPanel.propTypes = {
repoName: PropTypes.string.isRequired,
bugs: PropTypes.array.isRequired,
user: PropTypes.object.isRequired,
currentRepo: PropTypes.object.isRequired,

Просмотреть файл

@ -28,16 +28,6 @@ export default function InfraMenu() {
CI Duty
</a>
</li>
<li>
<a
className="dropdown-item"
href="https://tools.taskcluster.net/diagnostics"
target="_blank"
rel="noopener noreferrer"
>
Taskcluster Diagnostics
</a>
</li>
<li>
<a
className="dropdown-item"

Просмотреть файл

@ -140,6 +140,7 @@ class FuzzyJobFinder extends React.Component {
PushModel.triggerNewJobs(
this.state.selectedList,
this.props.decisionTaskId,
this.props.currentRepo,
)
.then(result => {
notify(result, 'success');
@ -315,6 +316,7 @@ FuzzyJobFinder.propTypes = {
decisionTaskId: PropTypes.string,
jobList: PropTypes.array,
filteredJobList: PropTypes.array,
currentRepo: PropTypes.object.isRequired,
};
FuzzyJobFinder.defaultProps = {

Просмотреть файл

@ -102,9 +102,9 @@ class Push extends React.PureComponent {
}
setSingleRevisionWindowTitle() {
const { allUnclassifiedFailureCount, repoName, push } = this.props;
const { allUnclassifiedFailureCount, currentRepo, push } = this.props;
const percentComplete = getPercentComplete(this.state.jobCounts);
const title = `[${allUnclassifiedFailureCount}] ${repoName}`;
const title = `[${allUnclassifiedFailureCount}] ${currentRepo.name}`;
document.title = `${percentComplete}% - ${title}: ${getRevisionTitle(
push.revisions,
@ -264,7 +264,7 @@ class Push extends React.PureComponent {
showUpdateNotifications = prevState => {
const { watched, jobCounts } = this.state;
const {
repoName,
currentRepo,
notificationSupported,
push: { revision, id: pushId },
notify,
@ -297,7 +297,7 @@ class Push extends React.PureComponent {
if (message) {
const notification = new Notification(message, {
body: `${repoName} rev ${revision.substring(0, 12)}`,
body: `${currentRepo.name} rev ${revision.substring(0, 12)}`,
tag: pushId,
});
@ -316,10 +316,10 @@ class Push extends React.PureComponent {
};
showRunnableJobs = async () => {
const { push, repoName, notify, decisionTaskMap } = this.props;
const { push, notify, decisionTaskMap, currentRepo } = this.props;
try {
const jobList = await RunnableJobModel.getList(repoName, {
const jobList = await RunnableJobModel.getList(currentRepo.name, {
decisionTask: decisionTaskMap[push.id],
push_id: push.id,
});
@ -352,7 +352,7 @@ class Push extends React.PureComponent {
};
showFuzzyJobs = async () => {
const { push, repoName, notify, decisionTaskMap } = this.props;
const { push, currentRepo, notify, decisionTaskMap } = this.props;
const createRegExp = (str, opts) =>
new RegExp(str.raw[0].replace(/\s/gm, ''), opts || '');
const excludedJobNames = createRegExp`
@ -366,7 +366,7 @@ class Push extends React.PureComponent {
try {
notify('Fetching runnable jobs... This could take a while...');
let fuzzyJobList = await RunnableJobModel.getList(repoName, {
let fuzzyJobList = await RunnableJobModel.getList(currentRepo.name, {
decisionTask: decisionTaskMap[push.id],
});
fuzzyJobList = [
@ -429,7 +429,6 @@ class Push extends React.PureComponent {
const {
push,
isLoggedIn,
repoName,
currentRepo,
duplicateJobsVisible,
filterModel,
@ -477,6 +476,7 @@ class Push extends React.PureComponent {
className="fuzzy-modal"
pushId={id}
decisionTaskId={decisionTaskId}
currentRepo={currentRepo}
/>
<PushHeader
push={push}
@ -487,7 +487,7 @@ class Push extends React.PureComponent {
jobCounts={jobCounts}
watchState={watched}
isLoggedIn={isLoggedIn}
repoName={repoName}
currentRepo={currentRepo}
filterModel={filterModel}
runnableVisible={runnableVisible}
showRunnableJobs={this.showRunnableJobs}
@ -509,7 +509,7 @@ class Push extends React.PureComponent {
<PushJobs
push={push}
platforms={platforms}
repoName={repoName}
repoName={currentRepo.name}
filterModel={filterModel}
pushGroupState={pushGroupState}
toggleSelectedRunnableJob={this.toggleSelectedRunnableJob}
@ -539,7 +539,6 @@ Push.propTypes = {
push: PropTypes.object.isRequired,
currentRepo: PropTypes.object.isRequired,
filterModel: PropTypes.object.isRequired,
repoName: PropTypes.string.isRequired,
isLoggedIn: PropTypes.bool.isRequired,
notificationSupported: PropTypes.bool.isRequired,
getAllShownJobs: PropTypes.func.isRequired,

Просмотреть файл

@ -53,7 +53,7 @@ class PushActionMenu extends React.PureComponent {
};
triggerMissingJobs = () => {
const { notify, revision, pushId } = this.props;
const { notify, revision, pushId, currentRepo } = this.props;
if (
!window.confirm(
@ -63,13 +63,13 @@ class PushActionMenu extends React.PureComponent {
return;
}
PushModel.triggerMissingJobs(pushId, notify).catch(e => {
PushModel.triggerMissingJobs(pushId, notify, currentRepo).catch(e => {
notify(formatTaskclusterError(e), 'danger', { sticky: true });
});
};
triggerAllTalosJobs = () => {
const { notify, revision, pushId } = this.props;
const { notify, revision, pushId, currentRepo } = this.props;
if (
!window.confirm(
@ -90,7 +90,7 @@ class PushActionMenu extends React.PureComponent {
);
}
PushModel.triggerAllTalosJobs(times, pushId, notify)
PushModel.triggerAllTalosJobs(times, pushId, notify, currentRepo)
.then(msg => {
notify(msg, 'success');
})
@ -108,13 +108,13 @@ class PushActionMenu extends React.PureComponent {
render() {
const {
isLoggedIn,
repoName,
revision,
runnableVisible,
hideRunnableJobs,
showRunnableJobs,
showFuzzyJobs,
pushId,
currentRepo,
} = this.props;
const {
topOfRangeUrl,
@ -174,7 +174,7 @@ class PushActionMenu extends React.PureComponent {
Add new jobs (Search)
</li>
)}
{triggerMissingRepos.includes(repoName) && (
{triggerMissingRepos.includes(currentRepo.name) && (
<li
title={
isLoggedIn
@ -205,7 +205,7 @@ class PushActionMenu extends React.PureComponent {
target="_blank"
rel="noopener noreferrer"
className="dropdown-item"
href={`https://bugherder.mozilla.org/?cset=${revision}&tree=${repoName}`}
href={`https://bugherder.mozilla.org/?cset=${revision}&tree=${currentRepo.name}`}
title="Use Bugherder to mark the bugs in this push"
>
Mark with Bugherder
@ -238,7 +238,7 @@ class PushActionMenu extends React.PureComponent {
<li>
<a
className="dropdown-item"
href={getPushHealthUrl({ repo: repoName, revision })}
href={getPushHealthUrl({ repo: currentRepo.name, revision })}
target="_blank"
rel="noopener noreferrer"
>
@ -251,6 +251,7 @@ class PushActionMenu extends React.PureComponent {
job={null}
pushId={pushId}
isLoggedIn={isLoggedIn}
currentRepo={currentRepo}
toggle={this.toggleCustomJobActions}
/>
)}
@ -263,7 +264,7 @@ PushActionMenu.propTypes = {
runnableVisible: PropTypes.bool.isRequired,
isLoggedIn: PropTypes.bool.isRequired,
revision: PropTypes.string.isRequired,
repoName: PropTypes.string.isRequired,
currentRepo: PropTypes.object.isRequired,
pushId: PropTypes.number.isRequired,
hideRunnableJobs: PropTypes.func.isRequired,
showRunnableJobs: PropTypes.func.isRequired,

Просмотреть файл

@ -143,6 +143,7 @@ class PushHeader extends React.Component {
hideRunnableJobs,
notify,
decisionTaskMap,
currentRepo,
} = this.props;
if (
@ -155,7 +156,11 @@ class PushHeader extends React.Component {
if (isLoggedIn) {
const { id: decisionTaskId } = decisionTaskMap[pushId];
PushModel.triggerNewJobs(selectedRunnableJobs, decisionTaskId)
PushModel.triggerNewJobs(
selectedRunnableJobs,
decisionTaskId,
currentRepo,
)
.then(result => {
notify(result, 'success');
hideRunnableJobs(pushId);
@ -177,15 +182,20 @@ class PushHeader extends React.Component {
) {
const {
notify,
repoName,
push,
isLoggedIn,
decisionTaskMap,
currentRepo,
} = this.props;
if (!isLoggedIn) return;
JobModel.cancelAll(push.id, repoName, notify, decisionTaskMap[push.id]);
JobModel.cancelAll(
push.id,
currentRepo,
notify,
decisionTaskMap[push.id],
);
}
};
@ -234,7 +244,6 @@ class PushHeader extends React.Component {
render() {
const {
repoName,
isLoggedIn,
pushId,
jobCounts,
@ -250,6 +259,7 @@ class PushHeader extends React.Component {
selectedRunnableJobs,
collapsed,
pushHealthVisibility,
currentRepo,
} = this.props;
const cancelJobsTitle = isLoggedIn
? 'Cancel all jobs'
@ -259,7 +269,7 @@ class PushHeader extends React.Component {
const authorPushFilterUrl = getJobsUrl({ ...linkParams, author });
const showPushHealthStatus =
pushHealthVisibility === 'All' ||
repoName === pushHealthVisibility.toLowerCase();
currentRepo.name === pushHealthVisibility.toLowerCase();
const watchStateLabel = {
none: 'Watch',
push: 'Notifying (per-push)',
@ -293,7 +303,7 @@ class PushHeader extends React.Component {
</span>
{showPushHealthStatus && (
<PushHealthStatus
repoName={repoName}
repoName={currentRepo.name}
pushId={pushId}
revision={revision}
jobCounts={jobCounts}
@ -324,7 +334,7 @@ class PushHeader extends React.Component {
)}
<a
className="btn btn-sm btn-push test-view-btn"
href={`/testview.html?repo=${repoName}&revision=${revision}`}
href={`/testview.html?repo=${currentRepo.name}&revision=${revision}`}
target="_blank"
rel="noopener noreferrer"
title="View details on failed test results for this push"
@ -372,7 +382,7 @@ class PushHeader extends React.Component {
isLoggedIn={isLoggedIn}
runnableVisible={runnableVisible}
revision={revision}
repoName={repoName}
currentRepo={currentRepo}
pushId={pushId}
showRunnableJobs={showRunnableJobs}
hideRunnableJobs={hideRunnableJobs}
@ -391,7 +401,6 @@ PushHeader.propTypes = {
pushTimestamp: PropTypes.number.isRequired,
author: PropTypes.string.isRequired,
revision: PropTypes.string.isRequired,
repoName: PropTypes.string.isRequired,
filterModel: PropTypes.object.isRequired,
runnableVisible: PropTypes.bool.isRequired,
showRunnableJobs: PropTypes.func.isRequired,
@ -411,6 +420,7 @@ PushHeader.propTypes = {
pushHealthVisibility: PropTypes.string.isRequired,
decisionTaskMap: PropTypes.object.isRequired,
watchState: PropTypes.string,
currentRepo: PropTypes.object.isRequired,
};
PushHeader.defaultProps = {

Просмотреть файл

@ -153,7 +153,6 @@ class PushList extends React.Component {
push={push}
isLoggedIn={isLoggedIn || false}
currentRepo={currentRepo}
repoName={repoName}
filterModel={filterModel}
notificationSupported={notificationSupported}
duplicateJobsVisible={duplicateJobsVisible}

Просмотреть файл

@ -85,13 +85,12 @@ export default class JobModel {
static async retrigger(
jobs,
repoName,
currentRepo,
notify,
times = 1,
decisionTaskIdMap = null,
) {
const jobTerm = jobs.length > 1 ? 'jobs' : 'job';
try {
notify(`Attempting to retrigger/add ${jobTerm} via actions.json`, 'info');
@ -105,49 +104,52 @@ export default class JobModel {
for (const [key, value] of Object.entries(uniquePerPushJobs)) {
const decisionTaskId = taskIdMap[key].id;
TaskclusterModel.load(decisionTaskId).then(async results => {
const actionTaskId = slugid();
const taskLabels = value.map(job => job.job_type_name);
TaskclusterModel.load(decisionTaskId, null, currentRepo).then(
async results => {
const actionTaskId = slugid();
const taskLabels = value.map(job => job.job_type_name);
let retriggerAction = results.actions.find(
action => action.name === 'retrigger-multiple',
);
let actionInput = {
requests: [{ tasks: taskLabels, times }],
};
if (!retriggerAction) {
// The `retrigger-multiple` action as introduced in Bug 1521032, to all the action
// to control whether new task are created, or existing ones re-run. We fall back
// to `add-new-jobs` to support pushing old revision to try, where the duplicating
// the release tasks impacted is unlikely to cause problems.
retriggerAction = getAction(results.actions, 'add-new-jobs');
actionInput = {
tasks: taskLabels,
let retriggerAction = results.actions.find(
action => action.name === 'retrigger-multiple',
);
let actionInput = {
requests: [{ tasks: taskLabels, times }],
};
}
if (!retriggerAction) {
// The `retrigger-multiple` action as introduced in Bug 1521032, to all the action
// to control whether new task are created, or existing ones re-run. We fall back
// to `add-new-jobs` to support pushing old revision to try, where the duplicating
// the release tasks impacted is unlikely to cause problems.
retriggerAction = getAction(results.actions, 'add-new-jobs');
actionInput = {
tasks: taskLabels,
};
}
await TaskclusterModel.submit({
action: retriggerAction,
actionTaskId,
decisionTaskId,
taskId: null,
task: null,
input: actionInput,
staticActionVariables: results.staticActionVariables,
})
.then(() =>
notify(
`Request sent to retrigger/add new jobs via actions.json (${actionTaskId})`,
),
)
.catch(error => {
notify(
`Retrigger failed with Decision task: ${decisionTaskId}: ${error}`,
'danger',
{ sticky: true },
);
});
});
await TaskclusterModel.submit({
action: retriggerAction,
actionTaskId,
decisionTaskId,
taskId: null,
task: null,
input: actionInput,
staticActionVariables: results.staticActionVariables,
currentRepo,
})
.then(() =>
notify(
`Request sent to retrigger/add new jobs via actions.json (${actionTaskId})`,
),
)
.catch(error => {
notify(
`Retrigger failed with Decision task: ${decisionTaskId}: ${error}`,
'danger',
{ sticky: true },
);
});
},
);
}
} catch (e) {
notify(
@ -158,11 +160,15 @@ export default class JobModel {
}
}
static async cancelAll(pushId, repoName, notify, decisionTask) {
static async cancelAll(pushId, currentRepo, notify, decisionTask) {
const { id: decisionTaskId } =
decisionTask || (await PushModel.getDecisionTaskId(pushId, notify));
const results = await TaskclusterModel.load(decisionTaskId);
const results = await TaskclusterModel.load(
decisionTaskId,
null,
currentRepo,
);
try {
const cancelAllTask = getAction(results.actions, 'cancel-all');
@ -172,6 +178,7 @@ export default class JobModel {
decisionTaskId,
input: {},
staticActionVariables: results.staticActionVariables,
currentRepo,
});
} catch (e) {
// The full message is too large to fit in a Treeherder
@ -182,7 +189,7 @@ export default class JobModel {
notify('Request sent to cancel all jobs via action.json', 'success');
}
static async cancel(jobs, repoName, notify, decisionTaskIdMap) {
static async cancel(jobs, currentRepo, notify, decisionTaskIdMap) {
const jobTerm = jobs.length > 1 ? 'jobs' : 'job';
const taskIdMap =
decisionTaskIdMap ||
@ -201,7 +208,11 @@ export default class JobModel {
// eslint-disable-next-line no-unused-vars
for (const job of jobs) {
const decisionTaskId = taskIdMap[job.push_id].id;
const results = await TaskclusterModel.load(decisionTaskId, job);
const results = await TaskclusterModel.load(
decisionTaskId,
job,
currentRepo,
);
try {
const cancelTask = getAction(results.actions, 'cancel');
@ -212,6 +223,7 @@ export default class JobModel {
taskId: results.originalTaskId,
input: {},
staticActionVariables: results.staticActionVariables,
currentRepo,
});
} catch (e) {
// The full message is too large to fit in a Treeherder

Просмотреть файл

@ -65,89 +65,104 @@ export default class PushModel {
return fetch(getProjectUrl(`${pushEndpoint}${pk}/`, repoName));
}
static async triggerMissingJobs(pushId, notify, decisionTask) {
static async triggerMissingJobs(pushId, notify, decisionTask, currentRepo) {
const decisionTaskId =
decisionTask || (await PushModel.getDecisionTaskId(pushId, notify)).id;
return TaskclusterModel.load(decisionTaskId).then(results => {
const actionTaskId = slugid();
return TaskclusterModel.load(decisionTaskId, null, currentRepo).then(
results => {
const actionTaskId = slugid();
try {
const missingTestsTask = getAction(
results.actions,
'run-missing-tests',
);
try {
const missingTestsTask = getAction(
results.actions,
'run-missing-tests',
);
return TaskclusterModel.submit({
action: missingTestsTask,
actionTaskId,
decisionTaskId,
taskId: null,
task: null,
input: {},
staticActionVariables: results.staticActionVariables,
}).then(
notify(
`Request sent to trigger missing jobs (${actionTaskId})`,
'success',
),
);
} catch (e) {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
});
return TaskclusterModel.submit({
action: missingTestsTask,
actionTaskId,
decisionTaskId,
taskId: null,
task: null,
input: {},
staticActionVariables: results.staticActionVariables,
currentRepo,
}).then(
notify(
`Request sent to trigger missing jobs (${actionTaskId})`,
'success',
),
);
} catch (e) {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
},
);
}
static async triggerAllTalosJobs(times, pushId, notify, decisionTask) {
static async triggerAllTalosJobs(
times,
pushId,
notify,
decisionTask,
currentRepo,
) {
const decisionTaskId =
decisionTask || (await PushModel.getDecisionTaskId(pushId, notify)).id;
return TaskclusterModel.load(decisionTaskId).then(results => {
const actionTaskId = slugid();
return TaskclusterModel.load(decisionTaskId, null, currentRepo).then(
results => {
const actionTaskId = slugid();
try {
const allTalosTask = getAction(results.actions, 'run-all-talos');
try {
const allTalosTask = getAction(results.actions, 'run-all-talos');
return TaskclusterModel.submit({
action: allTalosTask,
actionTaskId,
decisionTaskId,
taskId: null,
task: null,
input: { times },
staticActionVariables: results.staticActionVariables,
currentRepo,
}).then(
() =>
`Request sent to trigger all talos jobs ${times} time(s) via actions.json (${actionTaskId})`,
);
} catch (e) {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
},
);
}
static triggerNewJobs(jobs, decisionTaskId, currentRepo) {
return TaskclusterModel.load(decisionTaskId, null, currentRepo).then(
results => {
const actionTaskId = slugid();
const addNewJobsTask = getAction(results.actions, 'add-new-jobs');
return TaskclusterModel.submit({
action: allTalosTask,
action: addNewJobsTask,
actionTaskId,
decisionTaskId,
taskId: null,
task: null,
input: { times },
input: { tasks: jobs },
staticActionVariables: results.staticActionVariables,
currentRepo,
}).then(
() =>
`Request sent to trigger all talos jobs ${times} time(s) via actions.json (${actionTaskId})`,
`Request sent to trigger new jobs via actions.json (${actionTaskId})`,
);
} catch (e) {
// The full message is too large to fit in a Treeherder
// notification box.
notify(formatTaskclusterError(e), 'danger', { sticky: true });
}
});
}
static triggerNewJobs(jobs, decisionTaskId) {
return TaskclusterModel.load(decisionTaskId).then(results => {
const actionTaskId = slugid();
const addNewJobsTask = getAction(results.actions, 'add-new-jobs');
return TaskclusterModel.submit({
action: addNewJobsTask,
actionTaskId,
decisionTaskId,
taskId: null,
task: null,
input: { tasks: jobs },
staticActionVariables: results.staticActionVariables,
}).then(
() =>
`Request sent to trigger new jobs via actions.json (${actionTaskId})`,
);
});
},
);
}
static getHealth(repoName, revision) {

Просмотреть файл

@ -1,10 +1,10 @@
import defaults from 'lodash/defaults';
import jsone from 'json-e';
import { Auth, Hooks } from 'taskcluster-client-web';
import { Auth, Hooks, Queue } from 'taskcluster-client-web';
import { satisfiesExpression } from 'taskcluster-lib-scopes';
import taskcluster from '../helpers/taskcluster';
import { tcRootUrl } from '../helpers/url';
import { loginRootUrl } from '../helpers/url';
export default class TaskclusterModel {
static taskInContext(tagSetList, taskTags) {
@ -23,7 +23,16 @@ export default class TaskclusterModel {
task,
input,
staticActionVariables,
currentRepo,
}) {
if (currentRepo.tc_root_url !== loginRootUrl) {
// This limit could be lifted by allowing users to login to multiple TC deployments at once, using
// https://github.com/taskcluster/taskcluster-rfcs/blob/master/rfcs/0147-third-party-login.md
throw Error(
`Actions are not supported for this repository, as it does not use TC deployment ${loginRootUrl}`,
);
}
const context = defaults(
{},
{
@ -50,10 +59,10 @@ export default class TaskclusterModel {
if (action.kind === 'hook') {
const hookPayload = jsone(action.hookPayload, context);
const { hookId, hookGroupId } = action;
const auth = new Auth({ rootUrl: tcRootUrl });
const auth = new Auth({ rootUrl: currentRepo.tc_root_url });
const hooks = new Hooks({
credentialAgent: taskcluster.getAgent(),
rootUrl: tcRootUrl,
rootUrl: currentRepo.tc_root_url,
});
const decisionTask = await queue.task(decisionTaskId);
const expansion = await auth.expandScopes({
@ -73,11 +82,19 @@ export default class TaskclusterModel {
}
}
static async load(decisionTaskID, job) {
static async load(decisionTaskID, job, currentRepo) {
if (!decisionTaskID) {
throw Error("No decision task, can't find taskcluster actions");
}
if (currentRepo.tc_root_url !== loginRootUrl) {
// This limit could be lifted by allowing users to login to multiple TC deployments at once, using
// https://github.com/taskcluster/taskcluster-rfcs/blob/master/rfcs/0147-third-party-login.md
throw Error(
`Actions are not supported for this repository, as it does not use TC deployment ${loginRootUrl}`,
);
}
const queue = taskcluster.getQueue();
const actionsUrl = queue.buildUrl(
queue.getLatestArtifact,
@ -90,9 +107,8 @@ export default class TaskclusterModel {
let originalTaskPromise = Promise.resolve(null);
if (job) {
originalTaskId = job.task_id;
originalTaskPromise = fetch(
`https://queue.taskcluster.net/v1/task/${originalTaskId}`,
).then(async response => response.json());
const queue = new Queue({ rootUrl: currentRepo.tc_root_url });
originalTaskPromise = queue.task(originalTaskId);
}
return Promise.all([fetch(actionsUrl), originalTaskPromise]).then(
@ -109,7 +125,7 @@ export default class TaskclusterModel {
// The filter in the value of the actions key is an implementation
// of the specification for action context in
// https://docs.taskcluster.net/manual/using/actions/spec#action-context
// https://docs.taskcluster.net/docs/manual/design/conventions/actions/spec
// It decides if the specific action is applicable for this task.
return {
originalTask,

Просмотреть файл

@ -217,6 +217,7 @@ class CompareSubtestsView extends React.PureComponent {
}
CompareSubtestsView.propTypes = {
projects: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
validated: PropTypes.shape({
originalResultSet: PropTypes.shape({}),
newResultSet: PropTypes.shape({}),

Просмотреть файл

@ -15,6 +15,7 @@ import { compareTableText } from '../constants';
import ProgressBar from '../ProgressBar';
import { hashFunction } from '../../helpers/utils';
import JobModel from '../../models/job';
import RepositoryModel from '../../models/repository';
import TableAverage from './TableAverage';
@ -50,22 +51,24 @@ export default class CompareTable extends React.PureComponent {
retriggerJobs = async (results, times) => {
// retrigger base revision jobs
const { projects } = this.props;
this.retriggerByRevision(
results.originalRetriggerableJobId,
results.originalRepoName,
RepositoryModel.getRepo(results.originalRepoName, projects),
true,
times,
);
// retrigger new revision jobs
this.retriggerByRevision(
results.newRetriggerableJobId,
results.newRepoName,
RepositoryModel.getRepo(results.newRepoName, projects),
false,
times,
);
};
retriggerByRevision = async (jobId, repoName, isBaseline, times) => {
retriggerByRevision = async (jobId, currentRepo, isBaseline, times) => {
const { isBaseAggregate, notify, retriggerJob, getJob } = this.props;
// do not retrigger if the base is aggregate (there is a selected time range)
@ -74,8 +77,8 @@ export default class CompareTable extends React.PureComponent {
}
if (jobId) {
const job = await getJob(repoName, jobId);
retriggerJob([job], repoName, notify, times);
const job = await getJob(currentRepo.name, jobId);
retriggerJob([job], currentRepo, notify, times);
}
};

Просмотреть файл

@ -115,6 +115,7 @@ export default class CompareTableControls extends React.Component {
notify,
hasSubtests,
onPermalinkClick,
projects,
} = this.props;
const {
@ -175,6 +176,7 @@ export default class CompareTableControls extends React.Component {
isBaseAggregate={isBaseAggregate}
notify={notify}
hasSubtests={hasSubtests}
projects={projects}
/>
))
) : (
@ -191,6 +193,7 @@ CompareTableControls.propTypes = {
user: PropTypes.shape({}).isRequired,
isBaseAggregate: PropTypes.bool.isRequired,
notify: PropTypes.func.isRequired,
projects: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
hasSubtests: PropTypes.bool,
validated: PropTypes.shape({
showOnlyImportant: PropTypes.string,

Просмотреть файл

@ -196,6 +196,7 @@ export default class CompareTableView extends React.Component {
hasSubtests,
onPermalinkClick,
frameworks,
projects,
} = this.props;
const {
compareResults,
@ -312,6 +313,7 @@ export default class CompareTableView extends React.Component {
compareResults={compareResults}
isBaseAggregate={!originalRevision}
notify={this.notifyFailure}
projects={projects}
showTestsWithNoise={
testsWithNoise.length > 0 && (
<Row>
@ -352,6 +354,7 @@ CompareTableView.propTypes = {
filterByFramework: PropTypes.oneOfType([PropTypes.shape({}), PropTypes.bool]),
getDisplayResults: PropTypes.func.isRequired,
getQueryParams: PropTypes.func.isRequired,
projects: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
hasSubtests: PropTypes.bool,
onPermalinkClick: PropTypes.func,
hashFragment: PropTypes.string,

Просмотреть файл

@ -244,6 +244,7 @@ class CompareView extends React.PureComponent {
}
CompareView.propTypes = {
projects: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
validated: PropTypes.shape({
originalResultSet: PropTypes.shape({}),
newResultSet: PropTypes.shape({}),

Просмотреть файл

@ -3,6 +3,9 @@ import PropTypes from 'prop-types';
import { hot } from 'react-hot-loader/root';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import RepositoryModel from '../models/repository';
import { getRepo } from '../helpers/location';
import NotFound from './NotFound';
import Health from './Health';
@ -12,28 +15,53 @@ function hasProps(search) {
return params.get('repo') && params.get('revision');
}
const App = () => (
<BrowserRouter>
<div>
<div>
<Switch>
<Route
exact
path="/pushhealth.html"
render={props =>
hasProps(props.location.search) ? (
<Health {...props} />
) : (
<NotFound {...props} />
)
}
/>
<Route name="notfound" component={NotFound} />
</Switch>
</div>
</div>
</BrowserRouter>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
currentRepo: null,
};
}
componentDidMount() {
const repoName = getRepo();
if (repoName) {
RepositoryModel.getList().then(repos => {
const newRepo = repos.find(repo => repo.name === repoName);
this.setState({ currentRepo: newRepo });
});
}
}
render() {
const { currentRepo } = this.state;
return (
<BrowserRouter>
<div>
<div>
<Switch>
<Route
exact
path="/pushhealth.html"
render={props =>
hasProps(props.location.search) ? (
<Health currentRepo={currentRepo} {...props} />
) : (
<NotFound {...props} />
)
}
/>
<Route name="notfound" component={NotFound} />
</Switch>
</div>
</div>
</BrowserRouter>
);
}
}
App.propTypes = {
location: PropTypes.object,

Просмотреть файл

@ -42,7 +42,7 @@ class ClassificationGroup extends React.PureComponent {
};
retriggerAll = times => {
const { group, notify } = this.props;
const { group, notify, currentRepo } = this.props;
// Reduce down to the unique jobs
const jobs = group.reduce(
(acc, test) => ({
@ -53,7 +53,7 @@ class ClassificationGroup extends React.PureComponent {
);
const uniqueJobs = Object.values(jobs);
JobModel.retrigger(uniqueJobs, null, notify, times);
JobModel.retrigger(uniqueJobs, null, notify, currentRepo, times);
};
render() {
@ -68,6 +68,7 @@ class ClassificationGroup extends React.PureComponent {
user,
hasRetriggerAll,
notify,
currentRepo,
} = this.props;
const expandIcon = detailsShowing ? faMinusSquare : faPlusSquare;
@ -123,6 +124,7 @@ class ClassificationGroup extends React.PureComponent {
key={failure.key}
failure={failure}
repo={repo}
currentRepo={currentRepo}
revision={revision}
user={user}
notify={notify}
@ -139,6 +141,7 @@ ClassificationGroup.propTypes = {
group: PropTypes.array.isRequired,
name: PropTypes.string.isRequired,
repo: PropTypes.string.isRequired,
currentRepo: PropTypes.object.isRequired,
revision: PropTypes.string.isRequired,
user: PropTypes.object.isRequired,
notify: PropTypes.func.isRequired,

Просмотреть файл

@ -92,6 +92,7 @@ export default class Health extends React.PureComponent {
failureMessage,
notifications,
} = this.state;
const { currentRepo } = this.props;
const overallResult = healthData
? resultColorMap[healthData.result]
: 'none';
@ -129,6 +130,7 @@ export default class Health extends React.PureComponent {
details={metric.details}
failures={metric.failures}
repo={repo}
currentRepo={currentRepo}
revision={revision}
user={user}
notify={this.notify}
@ -149,4 +151,5 @@ export default class Health extends React.PureComponent {
Health.propTypes = {
location: PropTypes.object.isRequired,
currentRepo: PropTypes.object.isRequired,
};

Просмотреть файл

@ -36,6 +36,7 @@ export default class Metric extends React.PureComponent {
revision,
user,
notify,
currentRepo,
} = this.props;
const resultColor = resultColorMap[result];
const expandIcon = detailsShowing ? faMinusSquare : faPlusSquare;
@ -67,6 +68,7 @@ export default class Metric extends React.PureComponent {
<TestFailures
failures={failures}
repo={repo}
currentRepo={currentRepo}
revision={revision}
user={user}
notify={notify}
@ -90,6 +92,7 @@ export default class Metric extends React.PureComponent {
Metric.propTypes = {
repo: PropTypes.string.isRequired,
currentRepo: PropTypes.object.isRequired,
revision: PropTypes.string.isRequired,
result: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,

Просмотреть файл

@ -28,7 +28,7 @@ class TestFailure extends React.PureComponent {
};
retriggerJob = async job => {
const { user, repo, notify } = this.props;
const { user, notify, currentRepo } = this.props;
if (!user.isLoggedIn) {
notify('Must be logged in to retrigger a job', 'danger', {
@ -36,7 +36,7 @@ class TestFailure extends React.PureComponent {
});
return;
}
JobModel.retrigger([job], repo, notify);
JobModel.retrigger([job], currentRepo, notify);
};
render() {
@ -180,6 +180,7 @@ TestFailure.propTypes = {
key: PropTypes.string.isRequired,
}).isRequired,
repo: PropTypes.string.isRequired,
currentRepo: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,
revision: PropTypes.string.isRequired,
notify: PropTypes.func.isRequired,

Просмотреть файл

@ -5,7 +5,7 @@ import ClassificationGroup from './ClassificationGroup';
export default class TestFailures extends React.PureComponent {
render() {
const { failures, repo, revision, user, notify } = this.props;
const { failures, repo, revision, user, notify, currentRepo } = this.props;
const { needInvestigation, intermittent } = failures;
const needInvestigationLength = Object.keys(needInvestigation).length;
@ -15,6 +15,7 @@ export default class TestFailures extends React.PureComponent {
group={needInvestigation}
name="Need Investigation"
repo={repo}
currentRepo={currentRepo}
revision={revision}
className="mb-5"
headerColor={needInvestigationLength ? 'danger' : 'secondary'}
@ -26,6 +27,7 @@ export default class TestFailures extends React.PureComponent {
group={intermittent}
name="Known Intermittent"
repo={repo}
currentRepo={currentRepo}
revision={revision}
className="mb-5"
headerColor="secondary"
@ -42,6 +44,7 @@ TestFailures.propTypes = {
failures: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,
repo: PropTypes.string.isRequired,
currentRepo: PropTypes.object.isRequired,
revision: PropTypes.string.isRequired,
notify: PropTypes.func.isRequired,
};

Просмотреть файл

@ -9006,6 +9006,11 @@ taskcluster-lib-scopes@10.0.2:
resolved "https://registry.yarnpkg.com/taskcluster-lib-scopes/-/taskcluster-lib-scopes-10.0.2.tgz#ec7df9c13ba187dfb9836623fe53e9290fa6770b"
integrity sha512-imeywPQva/St/uykCxTq7QrP0Hlte3mSpV917kvdVqunrsHs0k3E90kdi9KdGyDRzncVeKbCjTZjyaDf7VndVg==
taskcluster-lib-urls@12.0.0:
version "12.0.0"
resolved "https://registry.yarnpkg.com/taskcluster-lib-urls/-/taskcluster-lib-urls-12.0.0.tgz#f56190eec9e9597d37a42ad0e7f461e8e0e6732b"
integrity sha512-OrEFE0m3p/+mGsmIwjttLhSKg3io6MpJLhYtPNjVSZA9Ix8Y5tprN3vM6a3MjWt5asPF6AKZsfT43cgpGwJB0g==
taskcluster-lib-urls@^10.0.0:
version "10.1.1"
resolved "https://registry.yarnpkg.com/taskcluster-lib-urls/-/taskcluster-lib-urls-10.1.1.tgz#67d5b9449b947e5234eafdd15c46267dde29bf74"