Skip to content

Commit

Permalink
fix(git): normalize repo url without legacy url.parse (#5100)
Browse files Browse the repository at this point in the history
  • Loading branch information
merceyz authored Nov 30, 2022
1 parent 59785b0 commit 8aa8d5c
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 23 deletions.
25 changes: 25 additions & 0 deletions .yarn/versions/c2cb7806.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
releases:
"@yarnpkg/cli": patch
"@yarnpkg/plugin-git": patch

declined:
- "@yarnpkg/plugin-compat"
- "@yarnpkg/plugin-constraints"
- "@yarnpkg/plugin-dlx"
- "@yarnpkg/plugin-essentials"
- "@yarnpkg/plugin-github"
- "@yarnpkg/plugin-init"
- "@yarnpkg/plugin-interactive-tools"
- "@yarnpkg/plugin-nm"
- "@yarnpkg/plugin-npm-cli"
- "@yarnpkg/plugin-pack"
- "@yarnpkg/plugin-patch"
- "@yarnpkg/plugin-pnp"
- "@yarnpkg/plugin-pnpm"
- "@yarnpkg/plugin-stage"
- "@yarnpkg/plugin-typescript"
- "@yarnpkg/plugin-version"
- "@yarnpkg/plugin-workspace-tools"
- "@yarnpkg/builder"
- "@yarnpkg/core"
- "@yarnpkg/doctor"
29 changes: 8 additions & 21 deletions packages/plugin-git/sources/gitUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import GitUrlParse
import capitalize from 'lodash/capitalize';
import querystring from 'querystring';
import semver from 'semver';
import urlLib from 'url';

import {tryParseGitURL} from './hosted-git-info-parse';

function makeGitEnvironment() {
return {
Expand Down Expand Up @@ -126,27 +127,13 @@ export function normalizeRepoUrl(url: string, {git = false}: {git?: boolean} = {
url = url.replace(/^https:\/\/github\.com\/(?!\.{1,2}\/)([a-zA-Z0-9._-]+)\/(?!\.{1,2}(?:#|$))([a-zA-Z0-9._-]+?)\/tarball\/(.+)?$/, `https://github.com/$1/$2.git#$3`);

if (git) {
// The `git+` prefix doesn't mean anything at all for Git
url = url.replace(/^git\+([^:]+):/, `$1:`);

// The `ssh://` prefix should be removed because so URLs won't work in Git:
// ssh://git@github.com:yarnpkg/berry.git
// git@github.com/yarnpkg/berry.git
// Git only allows:
// git@github.com:yarnpkg/berry.git (no ssh)
// ssh://git@github.com/yarnpkg/berry.git (no colon)
// So we should cut `ssh://`, but only in URLs that contain colon after the hostname

let parsedUrl: urlLib.UrlWithStringQuery | null;
try {
parsedUrl = urlLib.parse(url);
} catch {
parsedUrl = null;
}
// Try to normalize the URL in a way that git accepts.
const parsedUrl = tryParseGitURL(url);
if (parsedUrl)
url = parsedUrl.href;

if (parsedUrl && parsedUrl.protocol === `ssh:` && parsedUrl.path?.startsWith(`/:`)) {
url = url.replace(/^ssh:\/\//, ``);
}
// The `git+` prefix doesn't mean anything at all for Git.
url = url.replace(/^git\+([^:]+):/, `$1:`);
}

return url;
Expand Down
67 changes: 67 additions & 0 deletions packages/plugin-git/sources/hosted-git-info-parse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Based on https://github.com/npm/hosted-git-info/blob/cf8115d6fa056fbfb0d63d4d13bde6116b2a02e0/lib/parse-url.js

/**
@license
Copyright (c) 2015, Rebecca Turner
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/

function lastIndexOfBefore(str: string, char: string, beforeChar: string) {
const startPosition = str.indexOf(beforeChar);
return str.lastIndexOf(char, startPosition > -1 ? startPosition : Infinity);
}

function safeURL(url: string) {
try {
return new URL(url);
} catch {
return undefined;
}
}

// attempt to correct an scp style url so that it will parse with `new URL()`
function correctURL(gitURL: string) {
// ignore @ that come after the first hash since the denotes the start
// of a committish which can contain @ characters
const firstAt = lastIndexOfBefore(gitURL, `@`, `#`);
// ignore colons that come after the hash since that could include colons such as:
// git@github.com:user/package-2#semver:^1.0.0
const lastColonBeforeHash = lastIndexOfBefore(gitURL, `:`, `#`);

if (lastColonBeforeHash > firstAt)
// the last : comes after the first @ (or there is no @)
// like it would in:
// proto://hostname.com:user/repo
// username@hostname.com:user/repo
// :password@hostname.com:user/repo
// username:password@hostname.com:user/repo
// proto://username@hostname.com:user/repo
// proto://:password@hostname.com:user/repo
// proto://username:password@hostname.com:user/repo
// then we replace the last : with a / to create a valid path
gitURL = `${gitURL.slice(0, lastColonBeforeHash)}/${gitURL.slice(lastColonBeforeHash + 1)}`;

if (lastIndexOfBefore(gitURL, `:`, `#`) === -1 && gitURL.indexOf(`//`) === -1)
// we have no : at all
// as it would be in:
// username@hostname.com/user/repo
// then we prepend a protocol
gitURL = `ssh://${gitURL}`;

return gitURL;
}

export function tryParseGitURL(gitURL: string) {
return safeURL(gitURL) || safeURL(correctURL(gitURL));
}
4 changes: 2 additions & 2 deletions packages/plugin-git/tests/__snapshots__/gitUtils.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ exports[`gitUtils should properly normalize git+ssh://git@github.com/yarnpkg/uti

exports[`gitUtils should properly normalize git+ssh://git@github.com:yarnpkg/berry.git#v2.1.1 ({ git: false }) 1`] = `"git+ssh://git@github.com:yarnpkg/berry.git#v2.1.1"`;

exports[`gitUtils should properly normalize git+ssh://git@github.com:yarnpkg/berry.git#v2.1.1 ({ git: true }) 1`] = `"git@github.com:yarnpkg/berry.git#v2.1.1"`;
exports[`gitUtils should properly normalize git+ssh://git@github.com:yarnpkg/berry.git#v2.1.1 ({ git: true }) 1`] = `"ssh://git@github.com/yarnpkg/berry.git#v2.1.1"`;

exports[`gitUtils should properly normalize git://github.com/yarnpkg/util-deprecate.git#v1.0.1 ({ git: false }) 1`] = `"git://github.com/yarnpkg/util-deprecate.git#v1.0.1"`;

Expand Down Expand Up @@ -86,7 +86,7 @@ exports[`gitUtils should properly normalize ssh://git@github.com/yarnpkg/util-de

exports[`gitUtils should properly normalize ssh://git@github.com:yarnpkg/berry.git#v2.1.1 ({ git: false }) 1`] = `"ssh://git@github.com:yarnpkg/berry.git#v2.1.1"`;

exports[`gitUtils should properly normalize ssh://git@github.com:yarnpkg/berry.git#v2.1.1 ({ git: true }) 1`] = `"git@github.com:yarnpkg/berry.git#v2.1.1"`;
exports[`gitUtils should properly normalize ssh://git@github.com:yarnpkg/berry.git#v2.1.1 ({ git: true }) 1`] = `"ssh://git@github.com/yarnpkg/berry.git#v2.1.1"`;

exports[`gitUtils should properly split GitHubOrg/foo-bar.js 1`] = `
{
Expand Down

0 comments on commit 8aa8d5c

Please sign in to comment.