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 committed Nov 30, 2022
1 parent 60f1dae commit ee23198
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
@@ -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
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 @@ -140,27 +141,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
@@ -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
Expand Up @@ -46,7 +46,7 @@ exports[`gitUtils should properly normalize git+ssh://git@github.com/TooTallNate

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/TooTallNate/util-deprecate.git#v1.0.1 ({ git: false }) 1`] = `"git://github.com/TooTallNate/util-deprecate.git#v1.0.1"`;

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

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`] = `
Object {
Expand Down

0 comments on commit ee23198

Please sign in to comment.