зеркало из https://github.com/mozilla/treeherder.git
Bug 1618751 - Push Health show trunk parent and commits
This commit is contained in:
Родитель
ab28c4ccb4
Коммит
bfdd99d9b9
|
@ -3,12 +3,12 @@ import datetime
|
||||||
import responses
|
import responses
|
||||||
|
|
||||||
from treeherder.model.models import Push
|
from treeherder.model.models import Push
|
||||||
from treeherder.push_health.compare import (get_parent,
|
from treeherder.push_health.compare import (get_commit_history,
|
||||||
get_response_object)
|
get_response_object)
|
||||||
|
|
||||||
|
|
||||||
def test_get_response_object(test_push, test_repository):
|
def test_get_response_object(test_push, test_repository):
|
||||||
resp = get_response_object('1234', test_push, test_repository)
|
resp = get_response_object('1234', [1, 2], 2, test_push, test_repository)
|
||||||
assert resp['parentSha'] == '1234'
|
assert resp['parentSha'] == '1234'
|
||||||
assert resp['id'] == 1
|
assert resp['id'] == 1
|
||||||
assert resp['exactMatch'] is False
|
assert resp['exactMatch'] is False
|
||||||
|
@ -16,7 +16,7 @@ def test_get_response_object(test_push, test_repository):
|
||||||
|
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_get_parent(test_push, test_repository):
|
def test_get_commit_history_automationrelevance(test_push, test_repository):
|
||||||
test_revision = '4c45a777949168d16c03a4cba167678b7ab65f76'
|
test_revision = '4c45a777949168d16c03a4cba167678b7ab65f76'
|
||||||
parent_revision = 'abcdef77949168d16c03a4cba167678b7ab65f76'
|
parent_revision = 'abcdef77949168d16c03a4cba167678b7ab65f76'
|
||||||
Push.objects.create(
|
Push.objects.create(
|
||||||
|
@ -25,28 +25,101 @@ def test_get_parent(test_push, test_repository):
|
||||||
author='foo@bar.baz',
|
author='foo@bar.baz',
|
||||||
time=datetime.datetime.now()
|
time=datetime.datetime.now()
|
||||||
)
|
)
|
||||||
commits_url = '{}/json-pushes?version=2&full=1&changeset={}'.format(
|
|
||||||
test_repository.url, test_revision)
|
|
||||||
commits = {'pushes': {1: {'changesets': [{'parents': [parent_revision]}]}}}
|
|
||||||
|
|
||||||
responses.add(responses.GET, commits_url, json=commits, content_type='application/json', status=200)
|
autorel_commits = {'changesets': [
|
||||||
|
{
|
||||||
|
'author': 'Cheech Marin <cheech.marin@gmail.com>', 'backsoutnodes': [],
|
||||||
|
'desc': 'Bug 1612891 - Suppress parsing easing error in early returns of ConvertKeyframeSequence.\n\nWe add a stack based class and supress the exception of parsing easing\nin the destructor, to avoid hitting the potential assertions.\n\nDifferential Revision: https://phabricator.services.mozilla.com/D64268\nDifferential Diff: PHID-DIFF-c4e7dcfpalwiem7bxsnk',
|
||||||
|
'node': '3ca259f9cbdea763e64f10e286e58b271d89ab9d',
|
||||||
|
'parents': [parent_revision],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'author': 'libmozevent <release-mgmt-analysis@mozilla.com>',
|
||||||
|
'desc': 'try_task_config for https://phabricator.services.mozilla.com/D64268\nDifferential Diff: PHID-DIFF-c4e7dcfpalwiem7bxsnk',
|
||||||
|
'node': '18f68eb12ebbd88fe3a4fc3afe7df6529a0153fb',
|
||||||
|
'parents': ['3ca259f9cbdea763e64f10e286e58b271d89ab9d'],
|
||||||
|
}
|
||||||
|
], 'visible': True}
|
||||||
|
|
||||||
parent = get_parent(test_repository, test_revision, test_push)
|
autorel_url = 'https://hg.mozilla.org/{}/json-automationrelevance/{}'.format(
|
||||||
assert parent['parentSha'] == parent_revision
|
test_repository.name, test_revision)
|
||||||
assert parent['revision'] == parent_revision
|
responses.add(
|
||||||
|
responses.GET,
|
||||||
|
autorel_url,
|
||||||
|
json=autorel_commits,
|
||||||
|
content_type='application/json',
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
history = get_commit_history(test_repository, test_revision, test_push)
|
||||||
|
assert history['parentSha'] == parent_revision
|
||||||
|
assert history['revision'] == parent_revision
|
||||||
|
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_get_parent_not_found(test_push, test_repository):
|
def test_get_commit_history_json_pushes(test_push, test_repository):
|
||||||
|
test_revision = '4c45a777949168d16c03a4cba167678b7ab65f76'
|
||||||
|
parent_revision = 'abcdef77949168d16c03a4cba167678b7ab65f76'
|
||||||
|
Push.objects.create(
|
||||||
|
revision=parent_revision,
|
||||||
|
repository=test_repository,
|
||||||
|
author='foo@bar.baz',
|
||||||
|
time=datetime.datetime.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
autorel_url = 'https://hg.mozilla.org/{}/json-automationrelevance/{}'.format(
|
||||||
|
test_repository.name, test_revision)
|
||||||
|
responses.add(
|
||||||
|
responses.GET,
|
||||||
|
autorel_url,
|
||||||
|
json={},
|
||||||
|
content_type='application/json',
|
||||||
|
status=500
|
||||||
|
)
|
||||||
|
|
||||||
|
jsonpushes_commits = {
|
||||||
|
'pushes': {'108872': {'changesets': [
|
||||||
|
{
|
||||||
|
'author': 'Hiro Protagonist <hprotagonist@gmail.com>',
|
||||||
|
'desc': 'Bug 1617666 - Use a separate Debugger to improve performance of eval.',
|
||||||
|
'node': '4fb5e268cf7440332e917e431f14e8bb6dc41a0d',
|
||||||
|
'parents': [parent_revision],
|
||||||
|
}
|
||||||
|
]}}
|
||||||
|
}
|
||||||
|
commits_url = '{}/json-pushes?version=2&full=1&changeset={}'.format(
|
||||||
|
test_repository.url, test_revision)
|
||||||
|
responses.add(
|
||||||
|
responses.GET,
|
||||||
|
commits_url,
|
||||||
|
json=jsonpushes_commits,
|
||||||
|
content_type='application/json',
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
history = get_commit_history(test_repository, test_revision, test_push)
|
||||||
|
assert history['parentSha'] == parent_revision
|
||||||
|
assert history['revision'] == parent_revision
|
||||||
|
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_get_commit_history_not_found(test_push, test_repository):
|
||||||
test_revision = '4c45a777949168d16c03a4cba167678b7ab65f76'
|
test_revision = '4c45a777949168d16c03a4cba167678b7ab65f76'
|
||||||
# Does not exist as a Push in the DB.
|
# Does not exist as a Push in the DB.
|
||||||
parent_revision = 'abcdef77949168d16c03a4cba167678b7ab65f76'
|
parent_revision = 'abcdef77949168d16c03a4cba167678b7ab65f76'
|
||||||
commits_url = '{}/json-pushes?version=2&full=1&changeset={}'.format(
|
commits_url = '{}/json-pushes?version=2&full=1&changeset={}'.format(
|
||||||
test_repository.url, test_revision)
|
test_repository.url, test_revision)
|
||||||
commits = {'pushes': {1: {'changesets': [{'parents': [parent_revision]}]}}}
|
commits = {'pushes': {1: {'changesets': [
|
||||||
|
{
|
||||||
|
'author': 'Boris Chiou <boris.chiou@gmail.com>', 'backsoutnodes': [],
|
||||||
|
'desc': 'Bug 1612891 - Suppress parsing easing error in early returns of ConvertKeyframeSequence.\n\nWe add a stack based class and supress the exception of parsing easing\nin the destructor, to avoid hitting the potential assertions.\n\nDifferential Revision: https://phabricator.services.mozilla.com/D64268\nDifferential Diff: PHID-DIFF-c4e7dcfpalwiem7bxsnk',
|
||||||
|
'node': '3ca259f9cbdea763e64f10e286e58b271d89ab9d',
|
||||||
|
'parents': [parent_revision],
|
||||||
|
},
|
||||||
|
]}}}
|
||||||
|
|
||||||
responses.add(responses.GET, commits_url, json=commits, content_type='application/json', status=200)
|
responses.add(responses.GET, commits_url, json=commits, content_type='application/json', status=200)
|
||||||
|
|
||||||
parent = get_parent(test_repository, test_revision, test_push)
|
parent = get_commit_history(test_repository, test_revision, test_push)
|
||||||
assert parent['parentSha'] == parent_revision
|
assert parent['parentSha'] == parent_revision
|
||||||
assert parent['revision'] is None
|
assert parent['revision'] is None
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
"id": 630837,
|
"id": 630837,
|
||||||
"result": "fail",
|
"result": "fail",
|
||||||
"metrics": {
|
"metrics": {
|
||||||
"parent": {
|
"commitHistory": {
|
||||||
"name": "Parent",
|
"name": "Commit History",
|
||||||
"result": "none",
|
"result": "none",
|
||||||
"details": {
|
"details": {
|
||||||
"parentSha": "eeb6fd68c0223a72d8714734a34d3e6da69995e1",
|
"parentSha": "eeb6fd68c0223a72d8714734a34d3e6da69995e1",
|
||||||
|
@ -35,7 +35,40 @@
|
||||||
"running": 0,
|
"running": 0,
|
||||||
"success": 293,
|
"success": 293,
|
||||||
"testfailed": 5
|
"testfailed": 5
|
||||||
}
|
},
|
||||||
|
"revisions": [
|
||||||
|
{
|
||||||
|
"comments": "Bug 1596812 Part 1 - Update our custom nsisui.exe. r=agashlin\n\nSummary:\n\nMinify this file by removing the dialogs we don't need and hide all the\nunnecessary controls in the one we do need, so the stub installer code\ndoesn't have to do that manually (I would have removed those controls\naltogether, but the NSIS compiler errors out if you do that).\n\nDifferential Revision: https://phabricator.services.mozilla.com/D56576\n\n\n\nTest Plan:\n\nReviewers: agashlin\n\nSubscribers:\n\nBug #: 1596812\nDifferential Diff: PHID-DIFF-2cdiggedu3yo3ijopy7k",
|
||||||
|
"author": "Molly Howell <mhowell@mozilla.com>",
|
||||||
|
"revision": "298d6ae2f80cad932f8ec605395e7d263c86e734"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comments": "Bug 1596812 Part 2 - NSIS WebBrowser plugin. r=agashlin\n\nSummary:\n\nThis is all the code and build files for an NSIS plugin that enables\nrendering a web page as the content of an NSIS dialog.\n\nDocumentation and the compiled binary are in later commits in this series.\n\nDifferential Revision: https://phabricator.services.mozilla.com/D56577\n\nDepends on D56576\n\nTest Plan:\n\nReviewers: agashlin\n\nSubscribers:\n\nBug #: 1596812\nDifferential Diff: PHID-DIFF-qxemgpfgytrfewwdnp3c",
|
||||||
|
"author": "Molly Howell <mhowell@mozilla.com>",
|
||||||
|
"revision": "c639aa34f1249c0b280ad71b7a131c8a8bd40a28"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comments": "Bug 1596812 Part 3 - Compiled binary for the WebBrowser plugin. r=agashlin\n\nSummary:\n\nDifferential Revision: https://phabricator.services.mozilla.com/D56579\n\nDepends on D56577\n\nTest Plan:\n\nReviewers: agashlin\n\nSubscribers:\n\nBug #: 1596812\nDifferential Diff: PHID-DIFF-thzbkzegigfe45ytmmq3",
|
||||||
|
"author": "Molly Howell <mhowell@mozilla.com>",
|
||||||
|
"revision": "9ee489147076c8776003ff47bbf7ba4122afda44"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comments": "Bug 1596812 Part 4 - Add the WebBrowser plugin to the installer build files. r=agashlin\n\nSummary:\n\nDifferential Revision: https://phabricator.services.mozilla.com/D56580\n\nDepends on D56579\n\nTest Plan:\n\nReviewers: agashlin\n\nSubscribers:\n\nBug #: 1596812\nDifferential Diff: PHID-DIFF-fczkua6yvptm5a7nhwdj",
|
||||||
|
"author": "Molly Howell <mhowell@mozilla.com>",
|
||||||
|
"revision": "8dff5e5e9e4fa080e93fa358e42fd2422d1e2d60"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comments": "Bug 1596812 Part 5 - Add the web content files and include them in the installer build. r=agashlin\n\nSummary:\n\nDifferential Revision: https://phabricator.services.mozilla.com/D56581\n\nDepends on D56580\n\nTest Plan:\n\nReviewers: agashlin\n\nSubscribers:\n\nBug #: 1596812\nDifferential Diff: PHID-DIFF-klktifjsu4dzhdvk5464",
|
||||||
|
"author": "Molly Howell <mhowell@mozilla.com>",
|
||||||
|
"revision": "d00d10fe888660fe536a6e6e3f2a243c0a70431c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comments": "try_task_config for https://phabricator.services.mozilla.com/D56581\nDifferential Diff: PHID-DIFF-klktifjsu4dzhdvk5464",
|
||||||
|
"author": "libmozevent <release-mgmt-analysis@mozilla.com>",
|
||||||
|
"revision": "62489dedc9c4af40387b91993fa70b0e643b6b71"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"revisionCount": 6
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"linting": { "name": "Linting", "result": "pass", "details": [] },
|
"linting": { "name": "Linting", "result": "pass", "details": [] },
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
import React from 'react';
|
||||||
|
import fetchMock from 'fetch-mock';
|
||||||
|
import { render, cleanup, waitForElement } from '@testing-library/react';
|
||||||
|
|
||||||
|
import CommitHistory from '../../../ui/push-health/CommitHistory';
|
||||||
|
import pushHealth from '../mock/push_health';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fetchMock.get(
|
||||||
|
'/api/project/autoland/push/health_summary/?revision=eeb6fd68c0223a72d8714734a34d3e6da69995e1',
|
||||||
|
{ needInvestigation: 87, unsupported: 8 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
fetchMock.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('CommitHistory', () => {
|
||||||
|
const testCommitHistory = history => <CommitHistory history={history} />;
|
||||||
|
|
||||||
|
test('should show a parent commit and health icon for that parent', async () => {
|
||||||
|
const { details: commitHistory } = pushHealth.metrics.commitHistory;
|
||||||
|
const { getByText, queryByTestId } = render(
|
||||||
|
testCommitHistory(commitHistory),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
getByText('eeb6fd68c0223a72d8714734a34d3e6da69995e1'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
queryByTestId('health-status-eeb6fd68c0223a72d8714734a34d3e6da69995e1'),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
await waitForElement(() => getByText('87 items')),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show warning if not exact commit match', async () => {
|
||||||
|
const { details: commitHistory } = pushHealth.metrics.commitHistory;
|
||||||
|
|
||||||
|
commitHistory.id = 123;
|
||||||
|
commitHistory.parentSha = '00000827c820f34b3b595f887f57b4c847316fcc';
|
||||||
|
commitHistory.exactMatch = false;
|
||||||
|
|
||||||
|
const { getByText } = render(testCommitHistory(commitHistory));
|
||||||
|
expect(
|
||||||
|
getByText(
|
||||||
|
'Warning: Could not find an exact match parent Push in Treeherder.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(getByText('Closest match:')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText('eeb6fd68c0223a72d8714734a34d3e6da69995e1'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
getByText('00000827c820f34b3b595f887f57b4c847316fcc'),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
await waitForElement(() => getByText('87 items')),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not have parent PushHealthStatus if no push id', async () => {
|
||||||
|
const { details: commitHistory } = pushHealth.metrics.commitHistory;
|
||||||
|
|
||||||
|
commitHistory.id = null;
|
||||||
|
commitHistory.parentSha = '00000827c820f34b3b595f887f57b4c847316fcc';
|
||||||
|
commitHistory.exactMatch = false;
|
||||||
|
|
||||||
|
const { getByText, queryByTestId } = render(
|
||||||
|
testCommitHistory(commitHistory),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
await waitForElement(() =>
|
||||||
|
getByText(
|
||||||
|
'Warning: Could not find an exact match parent Push in Treeherder.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
queryByTestId('health-status-eeb6fd68c0223a72d8714734a34d3e6da69995e1'),
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,114 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import fetchMock from 'fetch-mock';
|
|
||||||
import { render, cleanup, waitForElement } from '@testing-library/react';
|
|
||||||
|
|
||||||
import ParentPush from '../../../ui/push-health/ParentPush';
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fetchMock.get(
|
|
||||||
'/api/project/mozilla-central/push/health_summary/?revision=76ee1827c820f34b3b595f887f57b4c847316fcc',
|
|
||||||
{ needInvestigation: 87, unsupported: 8 },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
cleanup();
|
|
||||||
fetchMock.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
const getParent = (
|
|
||||||
id = 636452,
|
|
||||||
parentSha = '76ee1827c820f34b3b595f887f57b4c847316fcc',
|
|
||||||
exactMatch = true,
|
|
||||||
) => {
|
|
||||||
return {
|
|
||||||
parentSha,
|
|
||||||
exactMatch,
|
|
||||||
revision: '76ee1827c820f34b3b595f887f57b4c847316fcc',
|
|
||||||
repository: {
|
|
||||||
id: 1,
|
|
||||||
repository_group: {
|
|
||||||
name: 'development',
|
|
||||||
description:
|
|
||||||
'Collection of repositories where code initially lands in the development process',
|
|
||||||
},
|
|
||||||
name: 'mozilla-central',
|
|
||||||
dvcs_type: 'hg',
|
|
||||||
url: 'https://hg.mozilla.org/mozilla-central',
|
|
||||||
branch: null,
|
|
||||||
codebase: 'gecko',
|
|
||||||
description: '',
|
|
||||||
active_status: 'active',
|
|
||||||
performance_alerts_enabled: false,
|
|
||||||
expire_performance_data: false,
|
|
||||||
is_try_repo: false,
|
|
||||||
tc_root_url: 'https://firefox-ci-tc.services.mozilla.com',
|
|
||||||
},
|
|
||||||
id,
|
|
||||||
jobCounts: {
|
|
||||||
completed: 8117,
|
|
||||||
pending: 236,
|
|
||||||
running: 117,
|
|
||||||
success: 8025,
|
|
||||||
retry: 40,
|
|
||||||
testfailed: 52,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('PushParent', () => {
|
|
||||||
const testPushParent = parent => <ParentPush parent={parent} />;
|
|
||||||
|
|
||||||
test('should show a parent commit and health icon for that parent', async () => {
|
|
||||||
const parent = getParent();
|
|
||||||
const { getByText, queryByTestId } = render(testPushParent(parent));
|
|
||||||
expect(
|
|
||||||
getByText('76ee1827c820f34b3b595f887f57b4c847316fcc'),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
queryByTestId('health-status-76ee1827c820f34b3b595f887f57b4c847316fcc'),
|
|
||||||
).not.toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
await waitForElement(() => getByText('87 items')),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should show warning if not exact commit match', async () => {
|
|
||||||
const parent = getParent(
|
|
||||||
123,
|
|
||||||
'00000827c820f34b3b595f887f57b4c847316fcc',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
const { getByText } = render(testPushParent(parent));
|
|
||||||
expect(
|
|
||||||
getByText('Warning: Could not find an exact match parent Push.'),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(getByText('Closest match:')).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
getByText('76ee1827c820f34b3b595f887f57b4c847316fcc'),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
getByText('00000827c820f34b3b595f887f57b4c847316fcc'),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
await waitForElement(() => getByText('87 items')),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should not have parent PushHealthStatus if no push id', async () => {
|
|
||||||
const parent = getParent(
|
|
||||||
null,
|
|
||||||
'00000827c820f34b3b595f887f57b4c847316fcc',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
const { getByText, queryByTestId } = render(testPushParent(parent));
|
|
||||||
expect(
|
|
||||||
await waitForElement(() =>
|
|
||||||
getByText('Warning: Could not find an exact match parent Push.'),
|
|
||||||
),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
queryByTestId('health-status-76ee1827c820f34b3b595f887f57b4c847316fcc'),
|
|
||||||
).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -8,7 +8,7 @@ from treeherder.webapp.api.serializers import RepositorySerializer
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_response_object(parent_sha, push, repository):
|
def get_response_object(parent_sha, revisions, revision_count, push, repository):
|
||||||
resp = {
|
resp = {
|
||||||
'parentSha': parent_sha,
|
'parentSha': parent_sha,
|
||||||
'exactMatch': False,
|
'exactMatch': False,
|
||||||
|
@ -16,6 +16,8 @@ def get_response_object(parent_sha, push, repository):
|
||||||
'repository': RepositorySerializer(repository).data,
|
'repository': RepositorySerializer(repository).data,
|
||||||
'id': None,
|
'id': None,
|
||||||
'jobCounts': None,
|
'jobCounts': None,
|
||||||
|
'revisions': revisions,
|
||||||
|
'revisionCount': revision_count,
|
||||||
}
|
}
|
||||||
if push:
|
if push:
|
||||||
resp.update({
|
resp.update({
|
||||||
|
@ -27,49 +29,75 @@ def get_response_object(parent_sha, push, repository):
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
def get_parent(repository, revision, push):
|
def commit_to_revision(commit):
|
||||||
|
return {
|
||||||
|
'comments': commit['desc'],
|
||||||
|
'author': commit['author'],
|
||||||
|
'revision': commit['node'],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_commits(repository, revision):
|
||||||
# This gets the list of revisions for the push. Treeherder only holds the the last 20 per push, so we may
|
# This gets the list of revisions for the push. Treeherder only holds the the last 20 per push, so we may
|
||||||
# not have the oldest one.
|
# not have the oldest one.
|
||||||
commits_url = '{}/json-pushes?version=2&full=1&changeset={}'.format(repository.url, revision)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
parent_resp = list(fetch_json(commits_url)["pushes"].values())[0]
|
autorel_resp = fetch_json(
|
||||||
eldest_commit = parent_resp['changesets'][0]
|
'https://hg.mozilla.org/{}/json-automationrelevance/{}'.format(
|
||||||
parent_sha = eldest_commit['parents'][0]
|
repository.name, revision))
|
||||||
parent_pushes = Push.objects.filter(revision=parent_sha)
|
|
||||||
len_parents = len(parent_pushes)
|
|
||||||
logger.error('len parents {}'.format(len_parents))
|
|
||||||
|
|
||||||
if len_parents == 1:
|
return list(autorel_resp["changesets"])
|
||||||
parent_push = parent_pushes[0]
|
except Exception:
|
||||||
return get_response_object(parent_sha, parent_push, parent_push.repository)
|
# fallback to using json-pushes
|
||||||
|
|
||||||
elif len_parents > 1:
|
try:
|
||||||
mc_pushes = parent_pushes.filter(repository__name='mozilla-central')
|
json_pushes_resp = fetch_json(
|
||||||
if len(mc_pushes):
|
'{}/json-pushes?version=2&full=1&changeset={}'.format(
|
||||||
logger.error('mc_pushes {}'.format(mc_pushes))
|
repository.url, revision))
|
||||||
# we have more than one parent push on mozilla-central. Just pick the
|
changesets = list(json_pushes_resp["pushes"].values())[0]['changesets']
|
||||||
# first one. No way to know which one is more correct.
|
changesets.reverse()
|
||||||
mc_push = mc_pushes[0]
|
|
||||||
return get_response_object(parent_sha, mc_push, mc_push.repository)
|
|
||||||
|
|
||||||
# we have more than one push that matches, but not one in m-c,
|
return changesets
|
||||||
# so let's see what we have.
|
except Exception as json_push_ex:
|
||||||
for parent in parent_pushes:
|
raise json_push_ex
|
||||||
logger.error('parent with repo {}'.format(parent.repository.name))
|
|
||||||
|
|
||||||
# This parent doesn't have its own push, so look for it in the commits table
|
|
||||||
# If there are multiple, we don't have a way to know which is the "right" one,
|
|
||||||
# so pick the first. If the only one is a commit for the push in question, then
|
|
||||||
# skip it.
|
|
||||||
commits = Commit.objects.filter(revision=revision)
|
|
||||||
for commit in commits:
|
|
||||||
if commit.push.revision != revision:
|
|
||||||
return get_response_object(parent_sha, commit.push, commit.push.repository)
|
|
||||||
|
|
||||||
# We can't find any mention of this commit, so return what we have. Hope
|
def get_commit_history(repository, revision, push):
|
||||||
# for the best that it's in the same repository as the push in question.
|
commits = get_commits(repository, revision) or []
|
||||||
return get_response_object(parent_sha, None, repository)
|
revisions = [commit_to_revision(commit) for commit in commits]
|
||||||
|
revision_count = push.commits.count()
|
||||||
|
parent_sha = commits[0]['parents'][0]
|
||||||
|
parent_pushes = Push.objects.filter(revision=parent_sha)
|
||||||
|
len_parents = len(parent_pushes)
|
||||||
|
|
||||||
except Exception as e:
|
if len_parents == 1:
|
||||||
logger.exception(e)
|
parent_push = parent_pushes[0]
|
||||||
|
return get_response_object(
|
||||||
|
parent_sha, revisions, revision_count, parent_push, parent_push.repository
|
||||||
|
)
|
||||||
|
|
||||||
|
elif len_parents > 1:
|
||||||
|
mc_pushes = parent_pushes.filter(repository__name='mozilla-central')
|
||||||
|
if len(mc_pushes):
|
||||||
|
# we have more than one parent push on mozilla-central. Just pick the
|
||||||
|
# first one. No way to know which one is more correct.
|
||||||
|
mc_push = mc_pushes[0]
|
||||||
|
return get_response_object(
|
||||||
|
parent_sha, revisions, revision_count, mc_push, mc_push.repository
|
||||||
|
)
|
||||||
|
|
||||||
|
# This parent doesn't have its own push, so look for it in the commits table
|
||||||
|
# If there are multiple, we don't have a way to know which is the "right" one,
|
||||||
|
# so pick the first. If the only one is a commit for the push in question, then
|
||||||
|
# skip it.
|
||||||
|
commits = Commit.objects.filter(revision=revision)
|
||||||
|
for commit in commits:
|
||||||
|
if commit.push.revision != revision:
|
||||||
|
return get_response_object(
|
||||||
|
parent_sha, commits, revision_count, commit.push, commit.push.repository
|
||||||
|
)
|
||||||
|
|
||||||
|
# We can't find any mention of this commit, so return what we have. Hope
|
||||||
|
# for the best that it's in the same repository as the push in question.
|
||||||
|
return get_response_object(
|
||||||
|
parent_sha, revisions, revision_count, None, repository
|
||||||
|
)
|
||||||
|
|
|
@ -14,7 +14,7 @@ from treeherder.model.models import (Job,
|
||||||
Push,
|
Push,
|
||||||
Repository)
|
Repository)
|
||||||
from treeherder.push_health.builds import get_build_failures
|
from treeherder.push_health.builds import get_build_failures
|
||||||
from treeherder.push_health.compare import get_parent
|
from treeherder.push_health.compare import get_commit_history
|
||||||
from treeherder.push_health.linting import get_lint_failures
|
from treeherder.push_health.linting import get_lint_failures
|
||||||
from treeherder.push_health.performance import get_perf_failures
|
from treeherder.push_health.performance import get_perf_failures
|
||||||
from treeherder.push_health.tests import get_test_failures
|
from treeherder.push_health.tests import get_test_failures
|
||||||
|
@ -252,7 +252,8 @@ class PushViewSet(viewsets.ViewSet):
|
||||||
|
|
||||||
# Parent link only supported for Hg at this time.
|
# Parent link only supported for Hg at this time.
|
||||||
# Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1612645
|
# Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1612645
|
||||||
parent_details = None if repository.dvcs_type == 'git' else get_parent(repository, revision, push)
|
commit_history_details = None if repository.dvcs_type == 'git' \
|
||||||
|
else get_commit_history(repository, revision, push)
|
||||||
|
|
||||||
build_failures = get_build_failures(push)
|
build_failures = get_build_failures(push)
|
||||||
build_result = 'fail' if len(build_failures) else 'pass'
|
build_result = 'fail' if len(build_failures) else 'pass'
|
||||||
|
@ -275,10 +276,10 @@ class PushViewSet(viewsets.ViewSet):
|
||||||
'id': push.id,
|
'id': push.id,
|
||||||
'result': push_result,
|
'result': push_result,
|
||||||
'metrics': {
|
'metrics': {
|
||||||
'parent': {
|
'commitHistory': {
|
||||||
'name': 'Parent Push',
|
'name': 'Commit History',
|
||||||
'result': 'none',
|
'result': 'none',
|
||||||
'details': parent_details,
|
'details': commit_history_details,
|
||||||
},
|
},
|
||||||
'linting': {
|
'linting': {
|
||||||
'name': 'Linting',
|
'name': 'Linting',
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Alert } from 'reactstrap';
|
||||||
|
|
||||||
|
import PushHealthStatus from '../shared/PushHealthStatus';
|
||||||
|
import { RevisionList } from '../shared/RevisionList';
|
||||||
|
import { getJobsUrl } from '../helpers/url';
|
||||||
|
import RepositoryModel from '../models/repository';
|
||||||
|
import Clipboard from '../shared/Clipboard';
|
||||||
|
|
||||||
|
class CommitHistory extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
clipboardVisible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
showClipboard = show => {
|
||||||
|
this.setState({ clipboardVisible: show });
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
history: {
|
||||||
|
repository,
|
||||||
|
revision,
|
||||||
|
jobCounts,
|
||||||
|
exactMatch,
|
||||||
|
parentSha,
|
||||||
|
id,
|
||||||
|
revisions,
|
||||||
|
revisionCount,
|
||||||
|
},
|
||||||
|
} = this.props;
|
||||||
|
const { clipboardVisible } = this.state;
|
||||||
|
const repoModel = new RepositoryModel(repository);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<h5>Parent Push</h5>
|
||||||
|
{!exactMatch && (
|
||||||
|
<div className="ml-4">
|
||||||
|
<div
|
||||||
|
className="mb-2 ml-3"
|
||||||
|
onMouseEnter={() => this.showClipboard(true)}
|
||||||
|
onMouseLeave={() => this.showClipboard(false)}
|
||||||
|
>
|
||||||
|
<Clipboard
|
||||||
|
description="full hash"
|
||||||
|
text={parentSha}
|
||||||
|
visible={clipboardVisible}
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
href={repoModel.getRevisionHref(parentSha)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{parentSha}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<Alert color="warning" className="m-3 font-italics">
|
||||||
|
Warning: Could not find an exact match parent Push in Treeherder.
|
||||||
|
</Alert>
|
||||||
|
{id && <div>Closest match: </div>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{id && (
|
||||||
|
<div className="ml-5">
|
||||||
|
<a
|
||||||
|
href={`${getJobsUrl({ revision, repo: repository.name })}`}
|
||||||
|
className="mx-3"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
title="Open this push in Treeherder"
|
||||||
|
>
|
||||||
|
{revision}
|
||||||
|
</a>
|
||||||
|
<PushHealthStatus
|
||||||
|
revision={revision}
|
||||||
|
repoName={repository.name}
|
||||||
|
jobCounts={jobCounts}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<h5 className="mt-4">Commit revisions</h5>
|
||||||
|
<RevisionList
|
||||||
|
revision={revision}
|
||||||
|
revisions={revisions.slice(0, 20)}
|
||||||
|
revisionCount={revisionCount}
|
||||||
|
repo={repoModel}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommitHistory.propTypes = {
|
||||||
|
history: PropTypes.shape({
|
||||||
|
repository: PropTypes.object.isRequired,
|
||||||
|
revision: PropTypes.string.isRequired,
|
||||||
|
revisionCount: PropTypes.number.isRequired,
|
||||||
|
job_counts: PropTypes.shape({
|
||||||
|
completed: PropTypes.number.isRequired,
|
||||||
|
pending: PropTypes.number.isRequired,
|
||||||
|
running: PropTypes.number.isRequired,
|
||||||
|
}),
|
||||||
|
id: PropTypes.number,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CommitHistory;
|
|
@ -30,7 +30,7 @@ import Metric from './Metric';
|
||||||
import Navigation from './Navigation';
|
import Navigation from './Navigation';
|
||||||
import TestMetric from './TestMetric';
|
import TestMetric from './TestMetric';
|
||||||
import JobListMetric from './JobListMetric';
|
import JobListMetric from './JobListMetric';
|
||||||
import ParentPush from './ParentPush';
|
import CommitHistory from './CommitHistory';
|
||||||
|
|
||||||
export default class Health extends React.PureComponent {
|
export default class Health extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -47,7 +47,7 @@ export default class Health extends React.PureComponent {
|
||||||
failureMessage: null,
|
failureMessage: null,
|
||||||
notifications: [],
|
notifications: [],
|
||||||
progressExpanded: true,
|
progressExpanded: true,
|
||||||
parentPushExpanded: false,
|
commitHistoryExpanded: false,
|
||||||
lintingExpanded: false,
|
lintingExpanded: false,
|
||||||
buildsExpanded: false,
|
buildsExpanded: false,
|
||||||
testsExpanded: false,
|
testsExpanded: false,
|
||||||
|
@ -159,14 +159,14 @@ export default class Health extends React.PureComponent {
|
||||||
notifications,
|
notifications,
|
||||||
status,
|
status,
|
||||||
progressExpanded,
|
progressExpanded,
|
||||||
parentPushExpanded,
|
commitHistoryExpanded,
|
||||||
lintingExpanded,
|
lintingExpanded,
|
||||||
buildsExpanded,
|
buildsExpanded,
|
||||||
testsExpanded,
|
testsExpanded,
|
||||||
performanceExpanded,
|
performanceExpanded,
|
||||||
searchStr,
|
searchStr,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { tests, parent, linting, builds, performance } = metrics;
|
const { tests, commitHistory, linting, builds, performance } = metrics;
|
||||||
const { currentRepo } = this.props;
|
const { currentRepo } = this.props;
|
||||||
const percentComplete = status ? getPercentComplete(status) : 0;
|
const percentComplete = status ? getPercentComplete(status) : 0;
|
||||||
const progress = {
|
const progress = {
|
||||||
|
@ -190,40 +190,45 @@ export default class Health extends React.PureComponent {
|
||||||
{!!tests && (
|
{!!tests && (
|
||||||
<Nav className="metric-buttons mb-2 pt-2 pl-3 justify-content-between w-100">
|
<Nav className="metric-buttons mb-2 pt-2 pl-3 justify-content-between w-100">
|
||||||
<span>
|
<span>
|
||||||
{[progress, linting, builds, tests, performance, parent].map(
|
{[
|
||||||
metric => (
|
progress,
|
||||||
<span key={metric.name}>
|
linting,
|
||||||
{!!metric.details && (
|
builds,
|
||||||
<Button
|
tests,
|
||||||
size="sm"
|
performance,
|
||||||
className="mr-2"
|
commitHistory,
|
||||||
color={resultColorMap[metric.result]}
|
].map(metric => (
|
||||||
title={`Click to toggle ${
|
<span key={metric.name}>
|
||||||
metric.name
|
{!!metric.details && (
|
||||||
}: ${metric.result.toUpperCase()}`}
|
<Button
|
||||||
onClick={() => this.setExpanded(metric.name, true)}
|
size="sm"
|
||||||
key={metric.name}
|
className="mr-2"
|
||||||
>
|
color={resultColorMap[metric.result]}
|
||||||
{metric.name}
|
title={`Click to toggle ${
|
||||||
{['pass', 'fail', 'indeterminate'].includes(
|
metric.name
|
||||||
metric.result,
|
}: ${metric.result.toUpperCase()}`}
|
||||||
) ? (
|
onClick={() => this.setExpanded(metric.name, true)}
|
||||||
<FontAwesomeIcon
|
key={metric.name}
|
||||||
className="ml-1"
|
>
|
||||||
icon={
|
{metric.name}
|
||||||
metric.result === 'pass'
|
{['pass', 'fail', 'indeterminate'].includes(
|
||||||
? faCheckCircle
|
metric.result,
|
||||||
: faExclamationTriangle
|
) ? (
|
||||||
}
|
<FontAwesomeIcon
|
||||||
/>
|
className="ml-1"
|
||||||
) : (
|
icon={
|
||||||
<span className="ml-1">{metric.value}</span>
|
metric.result === 'pass'
|
||||||
)}
|
? faCheckCircle
|
||||||
</Button>
|
: faExclamationTriangle
|
||||||
)}
|
}
|
||||||
</span>
|
/>
|
||||||
),
|
) : (
|
||||||
)}
|
<span className="ml-1">{metric.value}</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
</span>
|
</span>
|
||||||
<span className="mr-2">
|
<span className="mr-2">
|
||||||
<InputFilter
|
<InputFilter
|
||||||
|
@ -295,15 +300,15 @@ export default class Health extends React.PureComponent {
|
||||||
setExpanded={this.setExpanded}
|
setExpanded={this.setExpanded}
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
{parent.details && (
|
{commitHistory.details && (
|
||||||
<Row className="w-100">
|
<Row className="w-100">
|
||||||
<Metric
|
<Metric
|
||||||
name="Parent Push"
|
name="Commit History"
|
||||||
result=""
|
result=""
|
||||||
expanded={parentPushExpanded}
|
expanded={commitHistoryExpanded}
|
||||||
setExpanded={this.setExpanded}
|
setExpanded={this.setExpanded}
|
||||||
>
|
>
|
||||||
<ParentPush parent={parent.details} />
|
<CommitHistory history={commitHistory.details} />
|
||||||
</Metric>
|
</Metric>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Alert } from 'reactstrap';
|
|
||||||
|
|
||||||
import PushHealthStatus from '../shared/PushHealthStatus';
|
|
||||||
import { getJobsUrl } from '../helpers/url';
|
|
||||||
import RepositoryModel from '../models/repository';
|
|
||||||
|
|
||||||
const ParentPush = props => {
|
|
||||||
const {
|
|
||||||
parent: { repository, revision, jobCounts, exactMatch, parentSha, id },
|
|
||||||
} = props;
|
|
||||||
const repoModel = new RepositoryModel(repository);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
{!exactMatch && (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="mb-2">
|
|
||||||
<a
|
|
||||||
href={repoModel.getRevisionHref(parentSha)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{parentSha}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<Alert color="warning" className="m-3 font-italics">
|
|
||||||
Warning: Could not find an exact match parent Push.
|
|
||||||
</Alert>
|
|
||||||
{id && <div>Closest match: </div>}
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
{id && (
|
|
||||||
<React.Fragment>
|
|
||||||
<a
|
|
||||||
href={`${getJobsUrl({ revision, repo: repository.name })}`}
|
|
||||||
className="mx-1"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
title="Open this push in Treeherder"
|
|
||||||
>
|
|
||||||
{revision}
|
|
||||||
</a>
|
|
||||||
<PushHealthStatus
|
|
||||||
revision={revision}
|
|
||||||
repoName={repository.name}
|
|
||||||
jobCounts={jobCounts}
|
|
||||||
/>
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ParentPush.propTypes = {
|
|
||||||
parent: PropTypes.shape({
|
|
||||||
repository: PropTypes.object.isRequired,
|
|
||||||
revision: PropTypes.string.isRequired,
|
|
||||||
job_counts: PropTypes.shape({
|
|
||||||
completed: PropTypes.number.isRequired,
|
|
||||||
pending: PropTypes.number.isRequired,
|
|
||||||
running: PropTypes.number.isRequired,
|
|
||||||
}),
|
|
||||||
id: PropTypes.number,
|
|
||||||
}).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ParentPush;
|
|
Загрузка…
Ссылка в новой задаче