From 45983466d34a46777f73344019eb803628a1699e Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sat, 24 Oct 2026 09:08:24 -0500 Subject: [PATCH] fix(deprecation): address DEP0169 --- lib/get-git-auth-url.js | 23 ++++++++++++++++++----- test/hide-sensitive.test.js | 4 ++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/get-git-auth-url.js b/lib/get-git-auth-url.js index 4beccfe3..b9b0e2ae 100644 --- a/lib/get-git-auth-url.js +++ b/lib/get-git-auth-url.js @@ -1,4 +1,4 @@ -import { format, parse } from "node:url"; +import { format } from "node:url"; import { isNil } from "lodash-es"; import hostedGitInfo from "hosted-git-info"; import debugAuthUrl from "debug"; @@ -6,6 +6,17 @@ import { verifyAuth } from "./git.js"; const debug = debugAuthUrl("semantic-release:get-git-auth-url"); +/** + * Get the protocol of a repository URL, if it has one (`null` for scp-like and shorthand URLs) + * + * @param {String} repositoryUrl User-given repository URL + * + * @return {String|null} The protocol, including the trailing `:` + */ +function getProtocol(repositoryUrl) { + return URL.canParse(repositoryUrl) ? new URL(repositoryUrl).protocol : null; +} + /** * Machinery to format a repository URL with the given credentials * @@ -22,12 +33,14 @@ function formatAuthUrl(protocol, repositoryUrl, gitCredentials) { const [match, auth, host, basePort, path] = /^(?!.+:\/\/)(?:(?.*)@)?(?.*?):(?\d+)?:?\/?(?.*)$/.exec(repositoryUrl) || []; - const { port, hostname, ...parsed } = parse( + const { port, hostname, pathname, search, hash } = new URL( match ? `ssh://${auth ? `${auth}@` : ""}${host}${basePort ? `:${basePort}` : ""}/${path}` : repositoryUrl ); return format({ - ...parsed, + pathname, + search, + hash, auth: gitCredentials, host: `${hostname}${protocol === "ssh:" ? "" : port ? `:${port}` : ""}`, protocol: protocol && /http[^s]/.test(protocol) ? "http" : "https", @@ -81,14 +94,14 @@ export default async (context) => { let { repositoryUrl } = context.options; const info = hostedGitInfo.fromUrl(repositoryUrl, { noGitPlus: true }); - const { protocol, ...parsed } = parse(repositoryUrl); + const protocol = getProtocol(repositoryUrl); if (info && info.getDefaultRepresentation() === "shortcut") { // Expand shorthand URLs (such as `owner/repo` or `gitlab:owner/repo`) repositoryUrl = info.https(); } else if (protocol && protocol.includes("http")) { // Replace `git+https` and `git+http` with `https` or `http` - repositoryUrl = format({ ...parsed, protocol: protocol.includes("https") ? "https" : "http", href: null }); + repositoryUrl = repositoryUrl.replace(/^[^:]+:/, protocol.includes("https") ? "https:" : "http:"); } // Test if push is allowed without transforming the URL (e.g. is ssh keys are set up) diff --git a/test/hide-sensitive.test.js b/test/hide-sensitive.test.js index 34f80b91..e293296d 100644 --- a/test/hide-sensitive.test.js +++ b/test/hide-sensitive.test.js @@ -1,4 +1,4 @@ -import { format, parse } from "node:url"; +import { format } from "node:url"; import test from "ava"; import { repeat } from "lodash-es"; import hideSensitive from "../lib/hide-sensitive.js"; @@ -87,7 +87,7 @@ test("Exclude environment variables with value shorter than SECRET_MIN_SIZE from // `lib/get-git-auth-url.js` embeds credentials in the repository URL with `url.format()`, which // encodes the `auth` field like `encodeURIComponent()` except that `:` separators are kept as is. // Build the URLs asserted below the same way, so they match what a failing git command prints. -const gitAuthUrl = (auth) => format({ ...parse("https://host.com/owner/repo.git"), auth }); +const gitAuthUrl = (auth) => format({ protocol: "https", host: "host.com", pathname: "/owner/repo.git", auth }); for (const character of ["@", "/", "?", "&", "=", "#", " "]) { test(`Mask GIT_CREDENTIALS containing "${character}" in a git authentication URL`, (t) => {