Skip to content

Commit

Permalink
Update deep link generation (#9)
Browse files Browse the repository at this point in the history
* add context to created links, fix sp

* fix PR comparison links from same origin

* have github trees detect if commit, still broken for gl

* fix comparisons with the same origin

* update tests

* update github comparison link when not comparing and typo fix
  • Loading branch information
miggy-e authored Aug 17, 2023
1 parent b478e19 commit 6cccff5
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 43 deletions.
79 changes: 59 additions & 20 deletions src/hosts/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function injectionScope(url: string) {
private _timer: ReturnType<typeof setTimeout> | undefined;
private _observer: MutationObserver | undefined;

constructor(private uri: URL) {}
constructor(private uri: URL) { }

inject(): void {
document.addEventListener('pjax:end', () => {
Expand Down Expand Up @@ -42,7 +42,7 @@ export function injectionScope(url: string) {

try {
const label = 'Open with GitKraken';
const url = this.tranformUrl('gkdev', 'open');
const url = this.transformUrl('gkdev', 'open');

const [, , , type] = this.uri.pathname.split('/');
switch (type) {
Expand All @@ -69,7 +69,7 @@ export function injectionScope(url: string) {

break;
case 'pull': {
const compareUrl = this.tranformUrl('gkdev', 'compare');
const compareUrl = this.transformUrl('gkdev', 'compare');

insertions.set('[data-target="get-repo.modal"] #local-panel ul li:first-child', {
html: /*html*/ `<li data-gk class="Box-row Box-row--hover-gray p-3 mt-0 rounded-0">
Expand Down Expand Up @@ -165,15 +165,20 @@ export function injectionScope(url: string) {
}
}

private tranformUrl(target: LinkTarget, action: 'open' | 'compare'): string {
private transformUrl(target: LinkTarget, action: 'open' | 'compare'): string {
let [, owner, repo, type, ...rest] = this.uri.pathname.split('/');

if (target === 'gkdev') {
const redirectUrl = this.tranformUrl('vscode', action);
const redirectUrl = new URL(this.transformUrl('vscode', action));
console.debug('redirectUrl', redirectUrl);
const deepLinkUrl =
MODE === 'production' ? 'https://gitkraken.dev/link' : 'https://dev.gitkraken.dev/link';
return new URL(`${deepLinkUrl}/${encodeURIComponent(btoa(redirectUrl))}`).toString();
const deepLink = new URL(`${deepLinkUrl}/${encodeURIComponent(btoa(redirectUrl.toString()))}`);
deepLink.searchParams.set('referrer', 'extension');
if (redirectUrl.searchParams.get('pr')) {
deepLink.searchParams.set('context', 'pr');
}
return deepLink.toString();
}

const repoId = '-';
Expand Down Expand Up @@ -213,10 +218,18 @@ export function injectionScope(url: string) {
}
break;
}
case 'tree':
// TODO@eamodio this is naive as it assumes everything after the tree is the branch, but it could also contain a path
url = new URL(`${target}://repolink/${repoId}/branch/${rest.join('/')}`);
case 'tree': {
// TODO@eamodio this is naive as it assumes everything after the tree is the branch or commit, but it could also contain a path
const prButtonForBranchPage = document.querySelector(
'.btn-link.no-underline.color-fg-muted',
);
if (prButtonForBranchPage && prButtonForBranchPage.textContent?.includes('Contribute')) {
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/b/${rest.join('/')}`);
} else {
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/c/${rest.join('/')}`);
}
break;
}
default:
url = new URL(`${target}://repolink/${repoId}`);
break;
Expand All @@ -229,9 +242,20 @@ export function injectionScope(url: string) {
case 'commit':
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/c/${rest.join('/')}`);
break;
case 'compare':
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/compare/${rest.join('/')}`);
case 'compare': {
let comparisonTarget = rest.join('/');
if (!comparisonTarget) {
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}`);
break;
}
const sameOrigin = !comparisonTarget.includes(':');
if (sameOrigin) {
const branches = comparisonTarget.split('...').map(branch => `origin/${branch}`);
comparisonTarget = branches.join('...');
}
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/compare/${comparisonTarget}`);
break;
}
case 'pull': {
const [prNumber] = rest;

Expand All @@ -244,12 +268,20 @@ export function injectionScope(url: string) {
const baseTreeUrl =
document.querySelector<HTMLAnchorElement>('.commit-ref.base-ref a')?.href;
if (baseTreeUrl) {
const [, , , , ...baseBranch] = new URL(baseTreeUrl).pathname.split('/');
const [, baseOwner, baseRepo, , ...baseBranch] = new URL(
baseTreeUrl,
).pathname.split('/');

let baseBranchString = baseBranch.join('/');
let prBranchString = prBranch.join('/');

if (prOwner === baseOwner && prRepo === baseRepo) {
baseBranchString = `origin/${baseBranchString}`;
prBranchString = `origin/${prBranchString}`;
}

url = new URL(
`${target}://eamodio.gitlens/link/r/${repoId}/compare/${baseBranch.join(
'/',
)}...${prBranch.join('/')}`,
`${target}://eamodio.gitlens/link/r/${repoId}/compare/${baseBranchString}...${prBranchString}`,
);
}
}
Expand Down Expand Up @@ -280,10 +312,18 @@ export function injectionScope(url: string) {
}
break;
}
case 'tree':
case 'tree': {
// TODO@eamodio this is naive as it assumes everything after the tree is the branch, but it could also contain a path
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/b/${rest.join('/')}`);
const prButtonForBranchPage = document.querySelector(
'.btn-link.no-underline.color-fg-muted',
);
if (prButtonForBranchPage && prButtonForBranchPage.textContent?.includes('Contribute')) {
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/b/${rest.join('/')}`);
} else {
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/c/${rest.join('/')}`);
}
break;
}
default:
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}`);
break;
Expand All @@ -302,9 +342,8 @@ export function injectionScope(url: string) {
}

private getGitKrakenSvg(size: number, classes?: string, style?: string) {
return /*html*/ `<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="${size}" height="${size}" fill="var(--color-btn-text)" viewBox="0 0 32 32"${
classes ? ` class="${classes}"` : ''
} style="pointer-events:none; ${style ?? ''}">
return /*html*/ `<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="${size}" height="${size}" fill="var(--color-btn-text)" viewBox="0 0 32 32"${classes ? ` class="${classes}"` : ''
} style="pointer-events:none; ${style ?? ''}">
<path d="M0 16C0 7.177 7.177 0 16 0s16 7.177 16 16-7.177 16-16 16S0 24.823 0 16Zm28.226-4.714a.607.607 0 0 0-.269-.317l-.004-.001c-.123-.07-.268-.095-.409-.07a.613.613 0 0 0-.5.6c.002.073.014.144.04.214.502 1.337.757 2.741.756 4.17a11.835 11.835 0 0 1-10.188 11.729v-5.396c.284-.058.566-.134.84-.226v4.67l.111-.027a11.128 11.128 0 0 0 6.042-3.864 10.939 10.939 0 0 0 2.406-6.884 11.052 11.052 0 0 0-5.703-9.685.618.618 0 0 0-.476-.046.61.61 0 0 0-.113 1.113 9.846 9.846 0 0 1-1.031 17.733v-3.954a1.666 1.666 0 0 0 1.104-1.387 1.67 1.67 0 0 0-.768-1.606c.237-2.17.927-2.598 1.433-2.913.302-.186.561-.348.561-.817v-.668c0-.692-.687-2.307-2.224-4.353-2.127-2.834-3.34-3.13-3.659-3.153-.12-.012-.223-.007-.337 0h-.012c-.321.023-1.534.32-3.664 3.153-1.539 2.046-2.227 3.661-2.227 4.353v.666c0 .47.26.63.56.817.506.313 1.197.742 1.433 2.912a1.664 1.664 0 0 0-.779 1.423c0 .692.456 1.33 1.117 1.572v3.955a9.837 9.837 0 0 1-4.364-3.51 9.784 9.784 0 0 1-1.752-5.604c0-3.578 1.95-6.88 5.088-8.62a.609.609 0 0 0-.294-1.14h-.001a.593.593 0 0 0-.29.076 11.057 11.057 0 0 0-5.71 9.684c0 2.53.833 4.91 2.407 6.885a11.131 11.131 0 0 0 6.041 3.864l.111.027v-4.669c.275.092.557.168.84.226v5.395A11.834 11.834 0 0 1 4.154 15.884c0-1.43.255-2.833.76-4.17a.612.612 0 0 0-.017-.464.597.597 0 0 0-.34-.316.611.611 0 0 0-.655.155.61.61 0 0 0-.125.202 13.133 13.133 0 0 0-.744 6.067A13.135 13.135 0 0 0 5.128 23.1a13.134 13.134 0 0 0 4.477 4.162 13.14 13.14 0 0 0 5.88 1.672l.093.003v-6.565a17.775 17.775 0 0 0 .479.012c.08-.002.233-.006.362-.012v6.565l.093-.003a13.156 13.156 0 0 0 5.878-1.676 13.152 13.152 0 0 0 4.477-4.163 13.145 13.145 0 0 0 2.097-5.741 13.146 13.146 0 0 0-.738-6.068ZM13.664 20.01a.977.977 0 0 0-.436-1.442.978.978 0 0 0-1.329.71.978.978 0 0 0 .583 1.09.974.974 0 0 0 1.183-.357h-.002Zm6.343-.994a.971.971 0 0 0-1.14-.475.978.978 0 0 0-.69 1.025.975.975 0 0 0 .968.881.974.974 0 0 0 .861-1.431h.001Z" />
</svg>`;
}
Expand Down
74 changes: 54 additions & 20 deletions src/hosts/gitlab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function injectionScope(url: string) {
private _timer: ReturnType<typeof setTimeout> | undefined;
private _observer: MutationObserver | undefined;

constructor(private readonly uri: URL) {}
constructor(private readonly uri: URL) { }

inject(): void {
this.render();
Expand All @@ -34,15 +34,18 @@ export function injectionScope(url: string) {
break;
}
case 'compare': {
insertions.set('form.js-requires-input .gl-display-flex:last-child .btn-confirm', {
html: /*html*/ `<a data-gk type="button" class="gl-button btn btn-defualt btn-md" href="${url}" target="_blank">
<span class="gl-button-text">
${this.getGitKrakenSvg(22, 's16 gl-icon gl-button-icon', undefined)}
Compare with GitKraken
</span>
</a>`,
position: 'afterend',
});
if (rest.length) {
// only insert if we know what we're comparing
insertions.set('form.js-requires-input .gl-display-flex:last-child .btn-confirm', {
html: /*html*/ `<a data-gk type="button" class="gl-button btn btn-defualt btn-md" href="${url}" target="_blank">
<span class="gl-button-text">
${this.getGitKrakenSvg(22, 's16 gl-icon gl-button-icon', undefined)}
Compare with GitKraken
</span>
</a>`,
position: 'afterend',
});
}

break;
}
Expand Down Expand Up @@ -189,11 +192,16 @@ export function injectionScope(url: string) {
let { owner, repo, type, rest } = this.parseUrl(this.uri.pathname);

if (target === 'gkdev') {
const redirectUrl = this.transformUrl('vscode', action);
const redirectUrl = new URL(this.transformUrl('vscode', action));
console.debug('redirectUrl', redirectUrl);
const deepLinkUrl =
MODE === 'production' ? 'https://gitkraken.dev/link' : 'https://dev.gitkraken.dev/link';
return new URL(`${deepLinkUrl}/${encodeURIComponent(btoa(redirectUrl))}`).toString();
const deepLink = new URL(`${deepLinkUrl}/${encodeURIComponent(btoa(redirectUrl.toString()))}`);
deepLink.searchParams.set('referrer', 'extension');
if (redirectUrl.searchParams.get('pr')) {
deepLink.searchParams.set('context', 'pr');
}
return deepLink.toString();
}

const repoId = '-';
Expand Down Expand Up @@ -246,6 +254,8 @@ export function injectionScope(url: string) {
// and selecting the correct deeplink route to use (/branch/... for branches, /c/... for commits) is an
// unsolveable problem. yes, full length shas are pretty easy to differentiate from branch names, but GitLab
// supports shortened shas which screws up everything
// the below line would be a good check, but isn't loaded with the initial page load, so we can't use it
// document.querySelector('[title="Copy commit SHA"]')?.getAttribute('data-clipboard-text') === rest.join('/')
url = new URL(`${target}://repolink/${repoId}/branch/${rest.join('/')}`);
break;
}
Expand All @@ -264,7 +274,20 @@ export function injectionScope(url: string) {
break;
}
case 'compare': {
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/compare/${rest.join('/')}`);
// get the comparison target if not already provided
let comparisonTarget = rest.join('/');
if (!comparisonTarget) {
// TODO get the current state of the comparison pickers
// currently defaulting to a link to the repo
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}`);
break;
}
const sameOrigin = !comparisonTarget.includes(':');
if (sameOrigin) {
const branches = comparisonTarget.split('...').map(branch => `origin/${branch}`);
comparisonTarget = branches.join('...');
}
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/compare/${comparisonTarget}`);
break;
}
case 'merge_requests': {
Expand All @@ -285,12 +308,22 @@ export function injectionScope(url: string) {
'.merge-request-details .detail-page-description a.gl-font-monospace:nth-of-type(3)',
)?.href;
if (baseTreeUrl) {
const { rest: baseBranch } = this.parseUrl(new URL(baseTreeUrl).pathname);
const {
owner: baseOwner,
repo: baseRepo,
rest: baseBranch,
} = this.parseUrl(new URL(baseTreeUrl).pathname);

let baseBranchString = baseBranch.join('/');
let prBranchString = prBranch.join('/');

if (prOwner === baseOwner && prRepo === baseRepo) {
baseBranchString = `origin/${baseBranchString}`;
prBranchString = `origin/${prBranchString}`;
}

url = new URL(
`${target}://eamodio.gitlens/link/r/${repoId}/compare/${baseBranch.join(
'/',
)}...${prBranch.join('/')}`,
`${target}://eamodio.gitlens/link/r/${repoId}/compare/${baseBranchString}...${prBranchString}`,
);
}
}
Expand Down Expand Up @@ -327,6 +360,8 @@ export function injectionScope(url: string) {
// and selecting the correct deeplink route to use (/branch/... for branches, /c/... for commits) is an
// unsolveable problem. yes, full length shas are pretty easy to differentiate from branch names, but GitLab
// supports shortened shas which screws up everything
// the below line would be a good check, but isn't loaded with the initial page load, so we can't use it
// document.querySelector('[title="Copy commit SHA"]')?.getAttribute('data-clipboard-text') === rest.join('/')
url = new URL(`${target}://eamodio.gitlens/link/r/${repoId}/b/${rest.join('/')}`);
break;
}
Expand All @@ -349,9 +384,8 @@ export function injectionScope(url: string) {
}

private getGitKrakenSvg(size: number, classes?: string, style?: string) {
return /*html*/ `<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="${size}" height="${size}" fill="var(--color-btn-text)" viewBox="0 0 32 32"${
classes ? ` class="${classes}"` : ''
} style="pointer-events:none; ${style ?? ''}">
return /*html*/ `<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="${size}" height="${size}" fill="var(--color-btn-text)" viewBox="0 0 32 32"${classes ? ` class="${classes}"` : ''
} style="pointer-events:none; ${style ?? ''}">
<path d="M0 16C0 7.177 7.177 0 16 0s16 7.177 16 16-7.177 16-16 16S0 24.823 0 16Zm28.226-4.714a.607.607 0 0 0-.269-.317l-.004-.001c-.123-.07-.268-.095-.409-.07a.613.613 0 0 0-.5.6c.002.073.014.144.04.214.502 1.337.757 2.741.756 4.17a11.835 11.835 0 0 1-10.188 11.729v-5.396c.284-.058.566-.134.84-.226v4.67l.111-.027a11.128 11.128 0 0 0 6.042-3.864 10.939 10.939 0 0 0 2.406-6.884 11.052 11.052 0 0 0-5.703-9.685.618.618 0 0 0-.476-.046.61.61 0 0 0-.113 1.113 9.846 9.846 0 0 1-1.031 17.733v-3.954a1.666 1.666 0 0 0 1.104-1.387 1.67 1.67 0 0 0-.768-1.606c.237-2.17.927-2.598 1.433-2.913.302-.186.561-.348.561-.817v-.668c0-.692-.687-2.307-2.224-4.353-2.127-2.834-3.34-3.13-3.659-3.153-.12-.012-.223-.007-.337 0h-.012c-.321.023-1.534.32-3.664 3.153-1.539 2.046-2.227 3.661-2.227 4.353v.666c0 .47.26.63.56.817.506.313 1.197.742 1.433 2.912a1.664 1.664 0 0 0-.779 1.423c0 .692.456 1.33 1.117 1.572v3.955a9.837 9.837 0 0 1-4.364-3.51 9.784 9.784 0 0 1-1.752-5.604c0-3.578 1.95-6.88 5.088-8.62a.609.609 0 0 0-.294-1.14h-.001a.593.593 0 0 0-.29.076 11.057 11.057 0 0 0-5.71 9.684c0 2.53.833 4.91 2.407 6.885a11.131 11.131 0 0 0 6.041 3.864l.111.027v-4.669c.275.092.557.168.84.226v5.395A11.834 11.834 0 0 1 4.154 15.884c0-1.43.255-2.833.76-4.17a.612.612 0 0 0-.017-.464.597.597 0 0 0-.34-.316.611.611 0 0 0-.655.155.61.61 0 0 0-.125.202 13.133 13.133 0 0 0-.744 6.067A13.135 13.135 0 0 0 5.128 23.1a13.134 13.134 0 0 0 4.477 4.162 13.14 13.14 0 0 0 5.88 1.672l.093.003v-6.565a17.775 17.775 0 0 0 .479.012c.08-.002.233-.006.362-.012v6.565l.093-.003a13.156 13.156 0 0 0 5.878-1.676 13.152 13.152 0 0 0 4.477-4.163 13.145 13.145 0 0 0 2.097-5.741 13.146 13.146 0 0 0-.738-6.068ZM13.664 20.01a.977.977 0 0 0-.436-1.442.978.978 0 0 0-1.329.71.978.978 0 0 0 .583 1.09.974.974 0 0 0 1.183-.357h-.002Zm6.343-.994a.971.971 0 0 0-1.14-.475.978.978 0 0 0-.69 1.025.975.975 0 0 0 .968.881.974.974 0 0 0 .861-1.431h.001Z" />
</svg>`;
}
Expand Down
6 changes: 3 additions & 3 deletions src/tests/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test('should create a link to open a repository in the repo page', async ({ page
const gkLinkElement = page.getByRole('link', { name: 'Open with GitKraken' });
const gkLink = await gkLinkElement.getAttribute('href');
expect(gkLink).toBe(
'https://dev.gitkraken.dev/link/dnNjb2RlOi8vZWFtb2Rpby5naXRsZW5zL2xpbmsvci8tP3VybD1odHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZtaWNyb3NvZnQlMkZ2c2NvZGUuZ2l0',
'https://dev.gitkraken.dev/link/dnNjb2RlOi8vZWFtb2Rpby5naXRsZW5zL2xpbmsvci8tP3VybD1odHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZtaWNyb3NvZnQlMkZ2c2NvZGUuZ2l0?referrer=extension',
);
});

Expand All @@ -31,7 +31,7 @@ test.skip('should create a link to open a branch for a PR', async ({ page }) =>
const gkLink = await gkLinkElement.getAttribute('href');
expect(gkLink).toBe(
// todo: get the correct link
'dev.gitkraken.dev/link/dnNjb2RlOi8vZWFtb2Rpby5naXRsZW5zL2xpbmsvci8tP3VybD1odHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZtaWNyb3NvZnQlMkZ2c2NvZGUuZ2l0',
'dev.gitkraken.dev/link/dnNjb2RlOi8vZWFtb2Rpby5naXRsZW5zL2xpbmsvci8tP3VybD1odHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZtaWNyb3NvZnQlMkZ2c2NvZGUuZ2l0?referrer=extension',
);
});

Expand All @@ -44,6 +44,6 @@ test('should create a link to open a commit', async ({ page }) => {
const gkLinkElement = page.getByLabel('Open with GitKraken', { exact: true });
const gkLink = await gkLinkElement.getAttribute('href');
expect(gkLink).toBe(
'https://dev.gitkraken.dev/link/dnNjb2RlOi8vZWFtb2Rpby5naXRsZW5zL2xpbmsvci8tL2MvYmViNTAzYWU4MTMwM2Q2M2FiYjgyMWYyYjBjNjZlNDE2MzNiNDcwNT91cmw9aHR0cHMlM0ElMkYlMkZnaXRodWIuY29tJTJGZ2l0a3Jha2VuJTJGdnNjb2RlLWdpdGxlbnMuZ2l0',
'https://dev.gitkraken.dev/link/dnNjb2RlOi8vZWFtb2Rpby5naXRsZW5zL2xpbmsvci8tL2MvYmViNTAzYWU4MTMwM2Q2M2FiYjgyMWYyYjBjNjZlNDE2MzNiNDcwNT91cmw9aHR0cHMlM0ElMkYlMkZnaXRodWIuY29tJTJGZ2l0a3Jha2VuJTJGdnNjb2RlLWdpdGxlbnMuZ2l0?referrer=extension',
);
});

0 comments on commit 6cccff5

Please sign in to comment.