Skip to content

Commit

Permalink
Enable update-pr-from-base-branch on forks (#2185)
Browse files Browse the repository at this point in the history
  • Loading branch information
fregante committed Jul 4, 2019
1 parent 6d852ce commit 9022674
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 35 deletions.
2 changes: 1 addition & 1 deletion readme.md
Expand Up @@ -104,7 +104,7 @@ GitHub Enterprise is also supported. More info in the options.
- [Add co-authors when merging PRs with multiple committers.](https://user-images.githubusercontent.com/1402241/51468821-71a42100-1da2-11e9-86aa-fc2a6a29da84.png)
- [Search for issues and PRs separately in the top search.](https://user-images.githubusercontent.com/1402241/52181103-35a09f80-2829-11e9-9c6f-57f2e08fc5b2.png)
- [View the source of Markdown files.](https://user-images.githubusercontent.com/1402241/54814836-7bc39c80-4ccb-11e9-8996-9ecf4f6036cb.png)
- [Update a PR from the base branch to ensure it build correctly before merging the PR itself (same-repo branches only).](https://user-images.githubusercontent.com/1402241/57941992-f2170080-7902-11e9-8f8a-594aad983559.png)
- [Update a PR from the base branch to ensure it build correctly before merging the PR itself.](https://user-images.githubusercontent.com/1402241/57941992-f2170080-7902-11e9-8f8a-594aad983559.png)

### More actions

Expand Down
64 changes: 34 additions & 30 deletions source/features/update-pr-from-base-branch.tsx
Expand Up @@ -5,7 +5,7 @@ import features from '../libs/features';
import * as api from '../libs/api';
import * as icons from '../libs/icons';
import observeEl from '../libs/simplified-element-observer';
import {getRepoURL} from '../libs/utils';
import {getRepoURL, getDiscussionNumber} from '../libs/utils';

let observer: MutationObserver;

Expand All @@ -17,12 +17,10 @@ function getBranches(): {base: string; head: string} {
}

export async function mergeBranches(): Promise<AnyObject> {
const prBranches = getBranches();
return api.v3(`repos/${getRepoURL()}/merges`, {
method: 'POST',
body: {
head: prBranches.base,
base: prBranches.head
return api.v3(`repos/${getRepoURL()}/pulls/${getDiscussionNumber()}/update-branch`, {
method: 'PUT',
headers: {
Accept: 'application/vnd.github.lydian-preview+json'
},
ignoreHTTPStatus: true
});
Expand All @@ -33,25 +31,31 @@ async function handler(event: DelegateEvent): Promise<void> {
button.disabled = true;
button.textContent = 'Updating branch…';
button.classList.remove('tooltipped');
observer.disconnect();

const response = await mergeBranches();
if (!response.status || response.status < 300) {
if (response.ok) {
button.remove();
observer.disconnect();
} else if (typeof response.message === 'string') {
button.textContent = response.message;
} else if (response.message && response.message.toLowerCase().startsWith('merge conflict')) {
// Only shown on Draft PRs
button.replaceWith(
<a href={location.pathname + '/conflicts'} className="btn float-right">{icons.alert()} Resolve conflicts</a>
);
} else {
button.textContent = response.message || 'Error';
button.prepend(icons.alert(), ' ');
if (response.message === 'Merge conflict') {
// Only shown on Draft PRs
button.replaceWith(
<a href={location.pathname + '/conflicts'} className="btn float-right">{icons.alert()} Resolve conflicts</a>
);
} else {
throw new api.RefinedGitHubAPIError('update-pr-from-base-branch: ' + response.message);
}
throw new api.RefinedGitHubAPIError('update-pr-from-base-branch: ' + JSON.stringify(response));
}
}

function createButton(base: string, head: string): HTMLElement {
return (
<button type="button" className="btn float-right rgh-update-pr-from-master tooltipped tooltipped-n" aria-label={`Merge the ${base} branch into ${head}`}>
Update branch
</button>
);
}

async function addButton(): Promise<void> {
if (select.exists('.rgh-update-pr-from-master')) {
return;
Expand All @@ -64,16 +68,21 @@ async function addButton(): Promise<void> {
}

const {base, head} = getBranches();

// Draft PRs already have this info on the page
const [outOfDateContainer] = select.all('.completeness-indicator-problem + .status-heading')
.filter(title => (title.textContent!).includes('out-of-date'));
if (outOfDateContainer) {
outOfDateContainer.append(createButton(base, head));
return;
}

const {status} = await api.v3(`repos/${getRepoURL()}/compare/${base}...${head}`);
if (status !== 'diverged') {
return;
}

select('.mergeability-details .merge-message')!.append(
<button type="button" className="btn float-right rgh-update-pr-from-master tooltipped tooltipped-n" aria-label={`Merge the ${base} branch into ${head}`}>
Update branch
</button>
);
select('.mergeability-details .merge-message')!.append(createButton(base, head));
}

function init(): void | false {
Expand All @@ -90,18 +99,13 @@ function init(): void | false {
return false;
}

// API doesn't support cross-fork merges
if (getBranches().base.includes(':')) {
return false;
}

observer = observeEl('.discussion-timeline-actions', addButton)!;
delegate('.discussion-timeline-actions', '.rgh-update-pr-from-master', 'click', handler);
}

features.add({
id: __featureName__,
description: 'Button to update a PR from the base branch to ensure it build correctly before merging the PR itself (same-repo branches only)',
description: 'Button to update a PR from the base branch to ensure it build correctly before merging the PR itself.',
include: [
features.isPRConversation
],
Expand Down
14 changes: 10 additions & 4 deletions source/libs/api.ts
Expand Up @@ -60,8 +60,9 @@ const api4 = location.hostname === 'github.com' ?

interface GHRestApiOptions {
ignoreHTTPStatus?: boolean;
method?: 'GET' | 'POST';
method?: 'GET' | 'POST' | 'PUT';
body?: undefined | JsonObject;
headers?: HeadersInit;
}

interface GHGraphQLApiOptions {
Expand All @@ -82,7 +83,7 @@ export const v3 = mem(async (
query: string,
options: GHRestApiOptions = v3defaults
): Promise<AnyObject> => {
const {ignoreHTTPStatus, method, body} = {...v3defaults, ...options};
const {ignoreHTTPStatus, method, body, headers} = {...v3defaults, ...options};
const {personalToken} = await settings;

const response = await fetch(api3 + query, {
Expand All @@ -91,16 +92,21 @@ export const v3 = mem(async (
headers: {
'User-Agent': 'Refined GitHub',
Accept: 'application/vnd.github.v3+json',
...headers,
...(personalToken ? {Authorization: `token ${personalToken}`} : {})
}
});
const textContent = await response.text();

// The response might just be a 200 or 404, it's the REST equivalent of `boolean`
const apiResponse: JsonObject = textContent.length > 0 ? JSON.parse(textContent) : {status: response.status};
const apiResponse: JsonObject = textContent.length > 0 ? JSON.parse(textContent) : {};

if (response.ok || ignoreHTTPStatus) {
return apiResponse;
return {
status: response.status,
...apiResponse,
ok: response.ok
};
}

throw getError(apiResponse);
Expand Down

0 comments on commit 9022674

Please sign in to comment.