зеркало из https://github.com/mozilla/treeherder.git
Bug 1575941 - Fix error message when task action not available
This commit is contained in:
Родитель
d4631afe64
Коммит
9b095eb04b
|
@ -1,5 +1,5 @@
|
|||
import { displayNumber } from '../../ui/perfherder/helpers';
|
||||
import { getRevisionUrl } from '../../ui/helpers/url';
|
||||
import { displayNumber } from '../../../ui/perfherder/helpers';
|
||||
import { getRevisionUrl } from '../../../ui/helpers/url';
|
||||
|
||||
describe('getRevisionUrl helper', () => {
|
||||
test('escapes some html symbols', () => {
|
|
@ -0,0 +1,19 @@
|
|||
import { getAction } from '../../../ui/helpers/taskcluster';
|
||||
|
||||
describe('taskcluster helper', () => {
|
||||
const results = [{ name: 'foo' }, { name: 'bar' }, { name: 'baz' }];
|
||||
|
||||
test('getAction finds the right action', () => {
|
||||
const action = getAction(results, 'baz');
|
||||
|
||||
expect(action).toEqual({ name: 'baz' });
|
||||
});
|
||||
|
||||
test('getAction throws exception when action is missing', () => {
|
||||
const results = [{ name: 'foo' }, { name: 'bar' }, { name: 'baz' }];
|
||||
|
||||
expect(() => getAction(results, 'meh')).toThrow(
|
||||
"'meh' action is not available for this task. Available: foo, bar, baz",
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,42 @@
|
|||
import { fetchMock } from 'fetch-mock';
|
||||
|
||||
import PushModel from '../../../ui/models/push';
|
||||
import { getProjectUrl } from '../../../ui/helpers/location';
|
||||
|
||||
describe('PushModel', () => {
|
||||
afterEach(() => {
|
||||
fetchMock.reset();
|
||||
});
|
||||
|
||||
describe('taskcluster actions', () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.mock(
|
||||
getProjectUrl('/push/decisiontask/?push_ids=548880', 'autoland'),
|
||||
{ '548880': { id: 'U-lI3jzPTkWFplfJPz6cJA', run: '0' } },
|
||||
);
|
||||
});
|
||||
|
||||
test('getDecisionTaskId', async () => {
|
||||
const decisionTaskId = await PushModel.getDecisionTaskId(
|
||||
548880,
|
||||
() => {},
|
||||
);
|
||||
|
||||
expect(decisionTaskId).toStrictEqual({
|
||||
id: 'U-lI3jzPTkWFplfJPz6cJA',
|
||||
run: '0',
|
||||
});
|
||||
});
|
||||
|
||||
test('getDecisionTaskMap', async () => {
|
||||
const decisionTaskMap = await PushModel.getDecisionTaskMap(
|
||||
[548880],
|
||||
() => {},
|
||||
);
|
||||
|
||||
expect(decisionTaskMap).toStrictEqual({
|
||||
'548880': { id: 'U-lI3jzPTkWFplfJPz6cJA', run: '0' },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -46,4 +46,18 @@ const taskcluster = (() => {
|
|||
};
|
||||
})();
|
||||
|
||||
export const getAction = (actionArray, actionName) => {
|
||||
const action = actionArray.find(result => result.name === actionName);
|
||||
|
||||
if (!action) {
|
||||
throw Error(
|
||||
`'${actionName}' action is not available for this task. Available: ${actionArray
|
||||
.map(act => act.name)
|
||||
.join(', ')}`,
|
||||
);
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
||||
|
||||
export default taskcluster;
|
||||
|
|
|
@ -21,6 +21,7 @@ import TaskclusterModel from '../../../models/taskcluster';
|
|||
import CustomJobActions from '../../CustomJobActions';
|
||||
import { notify } from '../../redux/stores/notifications';
|
||||
import { pinJob } from '../../redux/stores/pinnedJobs';
|
||||
import { getAction } from '../../../helpers/taskcluster';
|
||||
|
||||
import LogUrls from './LogUrls';
|
||||
|
||||
|
@ -87,39 +88,41 @@ class ActionBar extends React.PureComponent {
|
|||
notify,
|
||||
);
|
||||
TaskclusterModel.load(decisionTaskId, selectedJobFull).then(results => {
|
||||
const geckoprofile = results.actions.find(
|
||||
result => result.name === 'geckoprofile',
|
||||
);
|
||||
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 });
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
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',
|
||||
);
|
||||
},
|
||||
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 });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -164,18 +167,13 @@ class ActionBar extends React.PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
selectedJobFull.build_system_type === 'taskcluster' ||
|
||||
selectedJobFull.reason.startsWith('Created by BBB for task')
|
||||
) {
|
||||
const { id: decisionTaskId } = await PushModel.getDecisionTaskId(
|
||||
selectedJobFull.push_id,
|
||||
notify,
|
||||
);
|
||||
TaskclusterModel.load(decisionTaskId, selectedJobFull).then(results => {
|
||||
const backfilltask = results.actions.find(
|
||||
result => result.name === 'backfill',
|
||||
);
|
||||
const { id: decisionTaskId } = await PushModel.getDecisionTaskId(
|
||||
selectedJobFull.push_id,
|
||||
notify,
|
||||
);
|
||||
TaskclusterModel.load(decisionTaskId, selectedJobFull).then(results => {
|
||||
try {
|
||||
const backfilltask = getAction(results.actions, 'backfill');
|
||||
|
||||
return TaskclusterModel.submit({
|
||||
action: backfilltask,
|
||||
|
@ -193,10 +191,10 @@ class ActionBar extends React.PureComponent {
|
|||
notify(formatTaskclusterError(e), 'danger', { sticky: true });
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
notify('Unable to backfill this job type!', 'danger', { sticky: true });
|
||||
}
|
||||
} catch (e) {
|
||||
notify(formatTaskclusterError(e), 'danger', { sticky: true });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
isolateJob = async () => {
|
||||
|
@ -228,13 +226,11 @@ class ActionBar extends React.PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
selectedJobFull.build_system_type === 'taskcluster' ||
|
||||
selectedJobFull.reason.startsWith('Created by BBB for task')
|
||||
) {
|
||||
TaskclusterModel.load(decisionTaskId, selectedJobFull).then(results => {
|
||||
const isolationtask = results.actions.find(
|
||||
result => result.name === 'isolate-test-failures',
|
||||
TaskclusterModel.load(decisionTaskId, selectedJobFull).then(results => {
|
||||
try {
|
||||
const isolationtask = getAction(
|
||||
results.actions,
|
||||
'isolate-test-failures',
|
||||
);
|
||||
|
||||
if (!isolationtask) {
|
||||
|
@ -283,10 +279,10 @@ class ActionBar extends React.PureComponent {
|
|||
notify(formatTaskclusterError(e), 'danger', { sticky: true });
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
notify('Unable to isolate this job type!', '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.
|
||||
|
@ -337,11 +333,10 @@ class ActionBar extends React.PureComponent {
|
|||
notify,
|
||||
);
|
||||
const results = await TaskclusterModel.load(decisionTaskId, job);
|
||||
const interactiveTask = results.actions.find(
|
||||
result => result.name === 'create-interactive',
|
||||
);
|
||||
|
||||
try {
|
||||
const interactiveTask = getAction(results.actions, 'create-interactive');
|
||||
|
||||
await TaskclusterModel.submit({
|
||||
action: interactiveTask,
|
||||
decisionTaskId,
|
||||
|
|
|
@ -90,7 +90,7 @@ class PushActionMenu extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
PushModel.triggerAllTalosJobs(times, pushId)
|
||||
PushModel.triggerAllTalosJobs(times, pushId, notify)
|
||||
.then(msg => {
|
||||
notify(msg, 'success');
|
||||
})
|
||||
|
|
|
@ -7,6 +7,7 @@ import { formatTaskclusterError } from '../helpers/errorMessage';
|
|||
import { addAggregateFields } from '../helpers/job';
|
||||
import { getProjectUrl } from '../helpers/location';
|
||||
import { getData } from '../helpers/http';
|
||||
import { getAction } from '../helpers/taskcluster';
|
||||
|
||||
import PushModel from './push';
|
||||
import TaskclusterModel from './taskcluster';
|
||||
|
@ -112,9 +113,7 @@ export default class JobModel {
|
|||
// 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 = results.actions.find(
|
||||
action => action.name === 'add-new-jobs',
|
||||
);
|
||||
retriggerAction = getAction(results.actions, 'add-new-jobs');
|
||||
actionInput = {
|
||||
tasks: taskLabels,
|
||||
};
|
||||
|
@ -140,7 +139,11 @@ export default class JobModel {
|
|||
});
|
||||
}
|
||||
} catch (e) {
|
||||
notify(`Unable to retrigger/add ${jobTerm}`, 'danger', { sticky: true });
|
||||
notify(
|
||||
`Unable to retrigger/add ${jobTerm}. ${formatTaskclusterError(e)}`,
|
||||
'danger',
|
||||
{ sticky: true },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,11 +153,10 @@ export default class JobModel {
|
|||
notify,
|
||||
);
|
||||
const results = await TaskclusterModel.load(decisionTaskId);
|
||||
const cancelAllTask = results.actions.find(
|
||||
result => result.name === 'cancel-all',
|
||||
);
|
||||
|
||||
try {
|
||||
const cancelAllTask = getAction(results.actions, 'cancel-all');
|
||||
|
||||
await TaskclusterModel.submit({
|
||||
action: cancelAllTask,
|
||||
decisionTaskId,
|
||||
|
@ -204,11 +206,10 @@ export default class JobModel {
|
|||
job.taskcluster_metadata = tcMetadataMap[job.id];
|
||||
const decisionTaskId = taskIdMap[job.push_id].id;
|
||||
const results = await TaskclusterModel.load(decisionTaskId, job);
|
||||
const cancelTask = results.actions.find(
|
||||
result => result.name === 'cancel',
|
||||
);
|
||||
|
||||
try {
|
||||
const cancelTask = getAction(results.actions, 'cancel');
|
||||
|
||||
await TaskclusterModel.submit({
|
||||
action: cancelTask,
|
||||
decisionTaskId,
|
||||
|
|
|
@ -5,6 +5,8 @@ import { thMaxPushFetchSize } from '../helpers/constants';
|
|||
import { getData } from '../helpers/http';
|
||||
import { getProjectUrl, getUrlParam } from '../helpers/location';
|
||||
import { createQueryParams, pushEndpoint } from '../helpers/url';
|
||||
import { formatTaskclusterError } from '../helpers/errorMessage';
|
||||
import { getAction } from '../helpers/taskcluster';
|
||||
|
||||
import TaskclusterModel from './taskcluster';
|
||||
|
||||
|
@ -71,24 +73,32 @@ export default class PushModel {
|
|||
|
||||
return TaskclusterModel.load(decisionTaskId).then(results => {
|
||||
const actionTaskId = slugid();
|
||||
const missingTestsTask = results.actions.find(
|
||||
action => action.name === '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',
|
||||
),
|
||||
);
|
||||
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 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -100,31 +110,34 @@ export default class PushModel {
|
|||
|
||||
return TaskclusterModel.load(decisionTaskId).then(results => {
|
||||
const actionTaskId = slugid();
|
||||
const allTalosTask = results.actions.find(
|
||||
action => action.name === 'run-all-talos',
|
||||
);
|
||||
|
||||
return TaskclusterModel.submit({
|
||||
action: allTalosTask,
|
||||
actionTaskId,
|
||||
decisionTaskId,
|
||||
taskId: null,
|
||||
task: null,
|
||||
input: { times },
|
||||
staticActionVariables: results.staticActionVariables,
|
||||
}).then(
|
||||
() =>
|
||||
`Request sent to trigger all talos jobs ${times} time(s) via actions.json (${actionTaskId})`,
|
||||
);
|
||||
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,
|
||||
}).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) {
|
||||
return TaskclusterModel.load(decisionTaskId).then(results => {
|
||||
const actionTaskId = slugid();
|
||||
const addNewJobsTask = results.actions.find(
|
||||
action => action.name === 'add-new-jobs',
|
||||
);
|
||||
const addNewJobsTask = getAction(results.actions, 'add-new-jobs');
|
||||
|
||||
return TaskclusterModel.submit({
|
||||
action: addNewJobsTask,
|
||||
|
|
Загрузка…
Ссылка в новой задаче