1
0
Fork 0
mirror of https://code.forgejo.org/actions/git-backporting synced 2025-03-15 06:36:59 +01:00

fix: --no-squash on single-commit GitLab MR (#93)

Due to off-by-one error in GitLabMapper, --no-squash was not effective
on an MR containing single commit. Fix by using the same condition as
GitHubMapper.
This commit is contained in:
Ratchanan Srirattanamet 2024-02-20 20:24:08 +07:00 committed by GitHub
parent b7df1f80dc
commit 300fa91a8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 376 additions and 165 deletions

212
dist/cli/index.js vendored
View file

@ -1165,8 +1165,8 @@ class GitLabMapper {
sourceRepo: await this.mapSourceRepo(mr), sourceRepo: await this.mapSourceRepo(mr),
targetRepo: await this.mapTargetRepo(mr), targetRepo: await this.mapTargetRepo(mr),
// if commits list is provided use that as source // if commits list is provided use that as source
nCommits: (commits && commits.length > 1) ? commits.length : 1, nCommits: (commits && commits.length > 0) ? commits.length : 1,
commits: (commits && commits.length > 1) ? commits : this.getSha(mr) commits: (commits && commits.length > 0) ? commits : this.getSha(mr)
}; };
} }
getSha(mr) { getSha(mr) {
@ -5847,6 +5847,29 @@ var Writable = (__nccwpck_require__(2781).Writable);
var assert = __nccwpck_require__(9491); var assert = __nccwpck_require__(9491);
var debug = __nccwpck_require__(1133); var debug = __nccwpck_require__(1133);
// Whether to use the native URL object or the legacy url module
var useNativeURL = false;
try {
assert(new URL());
}
catch (error) {
useNativeURL = error.code === "ERR_INVALID_URL";
}
// URL fields to preserve in copy operations
var preservedUrlFields = [
"auth",
"host",
"hostname",
"href",
"path",
"pathname",
"port",
"protocol",
"query",
"search",
];
// Create handlers that pass events from native requests // Create handlers that pass events from native requests
var events = ["abort", "aborted", "connect", "error", "socket", "timeout"]; var events = ["abort", "aborted", "connect", "error", "socket", "timeout"];
var eventHandlers = Object.create(null); var eventHandlers = Object.create(null);
@ -5856,19 +5879,20 @@ events.forEach(function (event) {
}; };
}); });
// Error types with codes
var InvalidUrlError = createErrorType( var InvalidUrlError = createErrorType(
"ERR_INVALID_URL", "ERR_INVALID_URL",
"Invalid URL", "Invalid URL",
TypeError TypeError
); );
// Error types with codes
var RedirectionError = createErrorType( var RedirectionError = createErrorType(
"ERR_FR_REDIRECTION_FAILURE", "ERR_FR_REDIRECTION_FAILURE",
"Redirected request failed" "Redirected request failed"
); );
var TooManyRedirectsError = createErrorType( var TooManyRedirectsError = createErrorType(
"ERR_FR_TOO_MANY_REDIRECTS", "ERR_FR_TOO_MANY_REDIRECTS",
"Maximum number of redirects exceeded" "Maximum number of redirects exceeded",
RedirectionError
); );
var MaxBodyLengthExceededError = createErrorType( var MaxBodyLengthExceededError = createErrorType(
"ERR_FR_MAX_BODY_LENGTH_EXCEEDED", "ERR_FR_MAX_BODY_LENGTH_EXCEEDED",
@ -5879,6 +5903,9 @@ var WriteAfterEndError = createErrorType(
"write after end" "write after end"
); );
// istanbul ignore next
var destroy = Writable.prototype.destroy || noop;
// An HTTP(S) request that can be redirected // An HTTP(S) request that can be redirected
function RedirectableRequest(options, responseCallback) { function RedirectableRequest(options, responseCallback) {
// Initialize the request // Initialize the request
@ -5900,7 +5927,13 @@ function RedirectableRequest(options, responseCallback) {
// React to responses of native requests // React to responses of native requests
var self = this; var self = this;
this._onNativeResponse = function (response) { this._onNativeResponse = function (response) {
self._processResponse(response); try {
self._processResponse(response);
}
catch (cause) {
self.emit("error", cause instanceof RedirectionError ?
cause : new RedirectionError({ cause: cause }));
}
}; };
// Perform the first request // Perform the first request
@ -5909,10 +5942,17 @@ function RedirectableRequest(options, responseCallback) {
RedirectableRequest.prototype = Object.create(Writable.prototype); RedirectableRequest.prototype = Object.create(Writable.prototype);
RedirectableRequest.prototype.abort = function () { RedirectableRequest.prototype.abort = function () {
abortRequest(this._currentRequest); destroyRequest(this._currentRequest);
this._currentRequest.abort();
this.emit("abort"); this.emit("abort");
}; };
RedirectableRequest.prototype.destroy = function (error) {
destroyRequest(this._currentRequest, error);
destroy.call(this, error);
return this;
};
// Writes buffered data to the current native request // Writes buffered data to the current native request
RedirectableRequest.prototype.write = function (data, encoding, callback) { RedirectableRequest.prototype.write = function (data, encoding, callback) {
// Writing is not allowed if end has been called // Writing is not allowed if end has been called
@ -6025,6 +6065,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
self.removeListener("abort", clearTimer); self.removeListener("abort", clearTimer);
self.removeListener("error", clearTimer); self.removeListener("error", clearTimer);
self.removeListener("response", clearTimer); self.removeListener("response", clearTimer);
self.removeListener("close", clearTimer);
if (callback) { if (callback) {
self.removeListener("timeout", callback); self.removeListener("timeout", callback);
} }
@ -6051,6 +6092,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
this.on("abort", clearTimer); this.on("abort", clearTimer);
this.on("error", clearTimer); this.on("error", clearTimer);
this.on("response", clearTimer); this.on("response", clearTimer);
this.on("close", clearTimer);
return this; return this;
}; };
@ -6109,8 +6151,7 @@ RedirectableRequest.prototype._performRequest = function () {
var protocol = this._options.protocol; var protocol = this._options.protocol;
var nativeProtocol = this._options.nativeProtocols[protocol]; var nativeProtocol = this._options.nativeProtocols[protocol];
if (!nativeProtocol) { if (!nativeProtocol) {
this.emit("error", new TypeError("Unsupported protocol " + protocol)); throw new TypeError("Unsupported protocol " + protocol);
return;
} }
// If specified, use the agent corresponding to the protocol // If specified, use the agent corresponding to the protocol
@ -6202,15 +6243,14 @@ RedirectableRequest.prototype._processResponse = function (response) {
} }
// The response is a redirect, so abort the current request // The response is a redirect, so abort the current request
abortRequest(this._currentRequest); destroyRequest(this._currentRequest);
// Discard the remainder of the response to avoid waiting for data // Discard the remainder of the response to avoid waiting for data
response.destroy(); response.destroy();
// RFC7231§6.4: A client SHOULD detect and intervene // RFC7231§6.4: A client SHOULD detect and intervene
// in cyclical redirections (i.e., "infinite" redirection loops). // in cyclical redirections (i.e., "infinite" redirection loops).
if (++this._redirectCount > this._options.maxRedirects) { if (++this._redirectCount > this._options.maxRedirects) {
this.emit("error", new TooManyRedirectsError()); throw new TooManyRedirectsError();
return;
} }
// Store the request headers if applicable // Store the request headers if applicable
@ -6244,33 +6284,23 @@ RedirectableRequest.prototype._processResponse = function (response) {
var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers); var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
// If the redirect is relative, carry over the host of the last request // If the redirect is relative, carry over the host of the last request
var currentUrlParts = url.parse(this._currentUrl); var currentUrlParts = parseUrl(this._currentUrl);
var currentHost = currentHostHeader || currentUrlParts.host; var currentHost = currentHostHeader || currentUrlParts.host;
var currentUrl = /^\w+:/.test(location) ? this._currentUrl : var currentUrl = /^\w+:/.test(location) ? this._currentUrl :
url.format(Object.assign(currentUrlParts, { host: currentHost })); url.format(Object.assign(currentUrlParts, { host: currentHost }));
// Determine the URL of the redirection
var redirectUrl;
try {
redirectUrl = url.resolve(currentUrl, location);
}
catch (cause) {
this.emit("error", new RedirectionError({ cause: cause }));
return;
}
// Create the redirected request // Create the redirected request
debug("redirecting to", redirectUrl); var redirectUrl = resolveUrl(location, currentUrl);
debug("redirecting to", redirectUrl.href);
this._isRedirect = true; this._isRedirect = true;
var redirectUrlParts = url.parse(redirectUrl); spreadUrlObject(redirectUrl, this._options);
Object.assign(this._options, redirectUrlParts);
// Drop confidential headers when redirecting to a less secure protocol // Drop confidential headers when redirecting to a less secure protocol
// or to a different domain that is not a superdomain // or to a different domain that is not a superdomain
if (redirectUrlParts.protocol !== currentUrlParts.protocol && if (redirectUrl.protocol !== currentUrlParts.protocol &&
redirectUrlParts.protocol !== "https:" || redirectUrl.protocol !== "https:" ||
redirectUrlParts.host !== currentHost && redirectUrl.host !== currentHost &&
!isSubdomain(redirectUrlParts.host, currentHost)) { !isSubdomain(redirectUrl.host, currentHost)) {
removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers); removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers);
} }
@ -6285,23 +6315,12 @@ RedirectableRequest.prototype._processResponse = function (response) {
method: method, method: method,
headers: requestHeaders, headers: requestHeaders,
}; };
try { beforeRedirect(this._options, responseDetails, requestDetails);
beforeRedirect(this._options, responseDetails, requestDetails);
}
catch (err) {
this.emit("error", err);
return;
}
this._sanitizeOptions(this._options); this._sanitizeOptions(this._options);
} }
// Perform the redirected request // Perform the redirected request
try { this._performRequest();
this._performRequest();
}
catch (cause) {
this.emit("error", new RedirectionError({ cause: cause }));
}
}; };
// Wraps the key/value object of protocols with redirect functionality // Wraps the key/value object of protocols with redirect functionality
@ -6321,27 +6340,16 @@ function wrap(protocols) {
// Executes a request, following redirects // Executes a request, following redirects
function request(input, options, callback) { function request(input, options, callback) {
// Parse parameters // Parse parameters, ensuring that input is an object
if (isString(input)) { if (isURL(input)) {
var parsed; input = spreadUrlObject(input);
try {
parsed = urlToOptions(new URL(input));
}
catch (err) {
/* istanbul ignore next */
parsed = url.parse(input);
}
if (!isString(parsed.protocol)) {
throw new InvalidUrlError({ input });
}
input = parsed;
} }
else if (URL && (input instanceof URL)) { else if (isString(input)) {
input = urlToOptions(input); input = spreadUrlObject(parseUrl(input));
} }
else { else {
callback = options; callback = options;
options = input; options = validateUrl(input);
input = { protocol: protocol }; input = { protocol: protocol };
} }
if (isFunction(options)) { if (isFunction(options)) {
@ -6380,27 +6388,57 @@ function wrap(protocols) {
return exports; return exports;
} }
/* istanbul ignore next */
function noop() { /* empty */ } function noop() { /* empty */ }
// from https://github.com/nodejs/node/blob/master/lib/internal/url.js function parseUrl(input) {
function urlToOptions(urlObject) { var parsed;
var options = { /* istanbul ignore else */
protocol: urlObject.protocol, if (useNativeURL) {
hostname: urlObject.hostname.startsWith("[") ? parsed = new URL(input);
/* istanbul ignore next */
urlObject.hostname.slice(1, -1) :
urlObject.hostname,
hash: urlObject.hash,
search: urlObject.search,
pathname: urlObject.pathname,
path: urlObject.pathname + urlObject.search,
href: urlObject.href,
};
if (urlObject.port !== "") {
options.port = Number(urlObject.port);
} }
return options; else {
// Ensure the URL is valid and absolute
parsed = validateUrl(url.parse(input));
if (!isString(parsed.protocol)) {
throw new InvalidUrlError({ input });
}
}
return parsed;
}
function resolveUrl(relative, base) {
/* istanbul ignore next */
return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
throw new InvalidUrlError({ input: input.href || input });
}
if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
throw new InvalidUrlError({ input: input.href || input });
}
return input;
}
function spreadUrlObject(urlObject, target) {
var spread = target || {};
for (var key of preservedUrlFields) {
spread[key] = urlObject[key];
}
// Fix IPv6 hostname
if (spread.hostname.startsWith("[")) {
spread.hostname = spread.hostname.slice(1, -1);
}
// Ensure port is a number
if (spread.port !== "") {
spread.port = Number(spread.port);
}
// Concatenate path
spread.path = spread.search ? spread.pathname + spread.search : spread.pathname;
return spread;
} }
function removeMatchingHeaders(regex, headers) { function removeMatchingHeaders(regex, headers) {
@ -6426,17 +6464,25 @@ function createErrorType(code, message, baseClass) {
// Attach constructor and set default properties // Attach constructor and set default properties
CustomError.prototype = new (baseClass || Error)(); CustomError.prototype = new (baseClass || Error)();
CustomError.prototype.constructor = CustomError; Object.defineProperties(CustomError.prototype, {
CustomError.prototype.name = "Error [" + code + "]"; constructor: {
value: CustomError,
enumerable: false,
},
name: {
value: "Error [" + code + "]",
enumerable: false,
},
});
return CustomError; return CustomError;
} }
function abortRequest(request) { function destroyRequest(request, error) {
for (var event of events) { for (var event of events) {
request.removeListener(event, eventHandlers[event]); request.removeListener(event, eventHandlers[event]);
} }
request.on("error", noop); request.on("error", noop);
request.abort(); request.destroy(error);
} }
function isSubdomain(subdomain, domain) { function isSubdomain(subdomain, domain) {
@ -6457,6 +6503,10 @@ function isBuffer(value) {
return typeof value === "object" && ("length" in value); return typeof value === "object" && ("length" in value);
} }
function isURL(value) {
return URL && value instanceof URL;
}
// Exports // Exports
module.exports = wrap({ http: http, https: https }); module.exports = wrap({ http: http, https: https });
module.exports.wrap = wrap; module.exports.wrap = wrap;

212
dist/gha/index.js vendored
View file

@ -1135,8 +1135,8 @@ class GitLabMapper {
sourceRepo: await this.mapSourceRepo(mr), sourceRepo: await this.mapSourceRepo(mr),
targetRepo: await this.mapTargetRepo(mr), targetRepo: await this.mapTargetRepo(mr),
// if commits list is provided use that as source // if commits list is provided use that as source
nCommits: (commits && commits.length > 1) ? commits.length : 1, nCommits: (commits && commits.length > 0) ? commits.length : 1,
commits: (commits && commits.length > 1) ? commits : this.getSha(mr) commits: (commits && commits.length > 0) ? commits : this.getSha(mr)
}; };
} }
getSha(mr) { getSha(mr) {
@ -7578,6 +7578,29 @@ var Writable = (__nccwpck_require__(2781).Writable);
var assert = __nccwpck_require__(9491); var assert = __nccwpck_require__(9491);
var debug = __nccwpck_require__(1133); var debug = __nccwpck_require__(1133);
// Whether to use the native URL object or the legacy url module
var useNativeURL = false;
try {
assert(new URL());
}
catch (error) {
useNativeURL = error.code === "ERR_INVALID_URL";
}
// URL fields to preserve in copy operations
var preservedUrlFields = [
"auth",
"host",
"hostname",
"href",
"path",
"pathname",
"port",
"protocol",
"query",
"search",
];
// Create handlers that pass events from native requests // Create handlers that pass events from native requests
var events = ["abort", "aborted", "connect", "error", "socket", "timeout"]; var events = ["abort", "aborted", "connect", "error", "socket", "timeout"];
var eventHandlers = Object.create(null); var eventHandlers = Object.create(null);
@ -7587,19 +7610,20 @@ events.forEach(function (event) {
}; };
}); });
// Error types with codes
var InvalidUrlError = createErrorType( var InvalidUrlError = createErrorType(
"ERR_INVALID_URL", "ERR_INVALID_URL",
"Invalid URL", "Invalid URL",
TypeError TypeError
); );
// Error types with codes
var RedirectionError = createErrorType( var RedirectionError = createErrorType(
"ERR_FR_REDIRECTION_FAILURE", "ERR_FR_REDIRECTION_FAILURE",
"Redirected request failed" "Redirected request failed"
); );
var TooManyRedirectsError = createErrorType( var TooManyRedirectsError = createErrorType(
"ERR_FR_TOO_MANY_REDIRECTS", "ERR_FR_TOO_MANY_REDIRECTS",
"Maximum number of redirects exceeded" "Maximum number of redirects exceeded",
RedirectionError
); );
var MaxBodyLengthExceededError = createErrorType( var MaxBodyLengthExceededError = createErrorType(
"ERR_FR_MAX_BODY_LENGTH_EXCEEDED", "ERR_FR_MAX_BODY_LENGTH_EXCEEDED",
@ -7610,6 +7634,9 @@ var WriteAfterEndError = createErrorType(
"write after end" "write after end"
); );
// istanbul ignore next
var destroy = Writable.prototype.destroy || noop;
// An HTTP(S) request that can be redirected // An HTTP(S) request that can be redirected
function RedirectableRequest(options, responseCallback) { function RedirectableRequest(options, responseCallback) {
// Initialize the request // Initialize the request
@ -7631,7 +7658,13 @@ function RedirectableRequest(options, responseCallback) {
// React to responses of native requests // React to responses of native requests
var self = this; var self = this;
this._onNativeResponse = function (response) { this._onNativeResponse = function (response) {
self._processResponse(response); try {
self._processResponse(response);
}
catch (cause) {
self.emit("error", cause instanceof RedirectionError ?
cause : new RedirectionError({ cause: cause }));
}
}; };
// Perform the first request // Perform the first request
@ -7640,10 +7673,17 @@ function RedirectableRequest(options, responseCallback) {
RedirectableRequest.prototype = Object.create(Writable.prototype); RedirectableRequest.prototype = Object.create(Writable.prototype);
RedirectableRequest.prototype.abort = function () { RedirectableRequest.prototype.abort = function () {
abortRequest(this._currentRequest); destroyRequest(this._currentRequest);
this._currentRequest.abort();
this.emit("abort"); this.emit("abort");
}; };
RedirectableRequest.prototype.destroy = function (error) {
destroyRequest(this._currentRequest, error);
destroy.call(this, error);
return this;
};
// Writes buffered data to the current native request // Writes buffered data to the current native request
RedirectableRequest.prototype.write = function (data, encoding, callback) { RedirectableRequest.prototype.write = function (data, encoding, callback) {
// Writing is not allowed if end has been called // Writing is not allowed if end has been called
@ -7756,6 +7796,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
self.removeListener("abort", clearTimer); self.removeListener("abort", clearTimer);
self.removeListener("error", clearTimer); self.removeListener("error", clearTimer);
self.removeListener("response", clearTimer); self.removeListener("response", clearTimer);
self.removeListener("close", clearTimer);
if (callback) { if (callback) {
self.removeListener("timeout", callback); self.removeListener("timeout", callback);
} }
@ -7782,6 +7823,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
this.on("abort", clearTimer); this.on("abort", clearTimer);
this.on("error", clearTimer); this.on("error", clearTimer);
this.on("response", clearTimer); this.on("response", clearTimer);
this.on("close", clearTimer);
return this; return this;
}; };
@ -7840,8 +7882,7 @@ RedirectableRequest.prototype._performRequest = function () {
var protocol = this._options.protocol; var protocol = this._options.protocol;
var nativeProtocol = this._options.nativeProtocols[protocol]; var nativeProtocol = this._options.nativeProtocols[protocol];
if (!nativeProtocol) { if (!nativeProtocol) {
this.emit("error", new TypeError("Unsupported protocol " + protocol)); throw new TypeError("Unsupported protocol " + protocol);
return;
} }
// If specified, use the agent corresponding to the protocol // If specified, use the agent corresponding to the protocol
@ -7933,15 +7974,14 @@ RedirectableRequest.prototype._processResponse = function (response) {
} }
// The response is a redirect, so abort the current request // The response is a redirect, so abort the current request
abortRequest(this._currentRequest); destroyRequest(this._currentRequest);
// Discard the remainder of the response to avoid waiting for data // Discard the remainder of the response to avoid waiting for data
response.destroy(); response.destroy();
// RFC7231§6.4: A client SHOULD detect and intervene // RFC7231§6.4: A client SHOULD detect and intervene
// in cyclical redirections (i.e., "infinite" redirection loops). // in cyclical redirections (i.e., "infinite" redirection loops).
if (++this._redirectCount > this._options.maxRedirects) { if (++this._redirectCount > this._options.maxRedirects) {
this.emit("error", new TooManyRedirectsError()); throw new TooManyRedirectsError();
return;
} }
// Store the request headers if applicable // Store the request headers if applicable
@ -7975,33 +8015,23 @@ RedirectableRequest.prototype._processResponse = function (response) {
var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers); var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
// If the redirect is relative, carry over the host of the last request // If the redirect is relative, carry over the host of the last request
var currentUrlParts = url.parse(this._currentUrl); var currentUrlParts = parseUrl(this._currentUrl);
var currentHost = currentHostHeader || currentUrlParts.host; var currentHost = currentHostHeader || currentUrlParts.host;
var currentUrl = /^\w+:/.test(location) ? this._currentUrl : var currentUrl = /^\w+:/.test(location) ? this._currentUrl :
url.format(Object.assign(currentUrlParts, { host: currentHost })); url.format(Object.assign(currentUrlParts, { host: currentHost }));
// Determine the URL of the redirection
var redirectUrl;
try {
redirectUrl = url.resolve(currentUrl, location);
}
catch (cause) {
this.emit("error", new RedirectionError({ cause: cause }));
return;
}
// Create the redirected request // Create the redirected request
debug("redirecting to", redirectUrl); var redirectUrl = resolveUrl(location, currentUrl);
debug("redirecting to", redirectUrl.href);
this._isRedirect = true; this._isRedirect = true;
var redirectUrlParts = url.parse(redirectUrl); spreadUrlObject(redirectUrl, this._options);
Object.assign(this._options, redirectUrlParts);
// Drop confidential headers when redirecting to a less secure protocol // Drop confidential headers when redirecting to a less secure protocol
// or to a different domain that is not a superdomain // or to a different domain that is not a superdomain
if (redirectUrlParts.protocol !== currentUrlParts.protocol && if (redirectUrl.protocol !== currentUrlParts.protocol &&
redirectUrlParts.protocol !== "https:" || redirectUrl.protocol !== "https:" ||
redirectUrlParts.host !== currentHost && redirectUrl.host !== currentHost &&
!isSubdomain(redirectUrlParts.host, currentHost)) { !isSubdomain(redirectUrl.host, currentHost)) {
removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers); removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers);
} }
@ -8016,23 +8046,12 @@ RedirectableRequest.prototype._processResponse = function (response) {
method: method, method: method,
headers: requestHeaders, headers: requestHeaders,
}; };
try { beforeRedirect(this._options, responseDetails, requestDetails);
beforeRedirect(this._options, responseDetails, requestDetails);
}
catch (err) {
this.emit("error", err);
return;
}
this._sanitizeOptions(this._options); this._sanitizeOptions(this._options);
} }
// Perform the redirected request // Perform the redirected request
try { this._performRequest();
this._performRequest();
}
catch (cause) {
this.emit("error", new RedirectionError({ cause: cause }));
}
}; };
// Wraps the key/value object of protocols with redirect functionality // Wraps the key/value object of protocols with redirect functionality
@ -8052,27 +8071,16 @@ function wrap(protocols) {
// Executes a request, following redirects // Executes a request, following redirects
function request(input, options, callback) { function request(input, options, callback) {
// Parse parameters // Parse parameters, ensuring that input is an object
if (isString(input)) { if (isURL(input)) {
var parsed; input = spreadUrlObject(input);
try {
parsed = urlToOptions(new URL(input));
}
catch (err) {
/* istanbul ignore next */
parsed = url.parse(input);
}
if (!isString(parsed.protocol)) {
throw new InvalidUrlError({ input });
}
input = parsed;
} }
else if (URL && (input instanceof URL)) { else if (isString(input)) {
input = urlToOptions(input); input = spreadUrlObject(parseUrl(input));
} }
else { else {
callback = options; callback = options;
options = input; options = validateUrl(input);
input = { protocol: protocol }; input = { protocol: protocol };
} }
if (isFunction(options)) { if (isFunction(options)) {
@ -8111,27 +8119,57 @@ function wrap(protocols) {
return exports; return exports;
} }
/* istanbul ignore next */
function noop() { /* empty */ } function noop() { /* empty */ }
// from https://github.com/nodejs/node/blob/master/lib/internal/url.js function parseUrl(input) {
function urlToOptions(urlObject) { var parsed;
var options = { /* istanbul ignore else */
protocol: urlObject.protocol, if (useNativeURL) {
hostname: urlObject.hostname.startsWith("[") ? parsed = new URL(input);
/* istanbul ignore next */
urlObject.hostname.slice(1, -1) :
urlObject.hostname,
hash: urlObject.hash,
search: urlObject.search,
pathname: urlObject.pathname,
path: urlObject.pathname + urlObject.search,
href: urlObject.href,
};
if (urlObject.port !== "") {
options.port = Number(urlObject.port);
} }
return options; else {
// Ensure the URL is valid and absolute
parsed = validateUrl(url.parse(input));
if (!isString(parsed.protocol)) {
throw new InvalidUrlError({ input });
}
}
return parsed;
}
function resolveUrl(relative, base) {
/* istanbul ignore next */
return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
throw new InvalidUrlError({ input: input.href || input });
}
if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
throw new InvalidUrlError({ input: input.href || input });
}
return input;
}
function spreadUrlObject(urlObject, target) {
var spread = target || {};
for (var key of preservedUrlFields) {
spread[key] = urlObject[key];
}
// Fix IPv6 hostname
if (spread.hostname.startsWith("[")) {
spread.hostname = spread.hostname.slice(1, -1);
}
// Ensure port is a number
if (spread.port !== "") {
spread.port = Number(spread.port);
}
// Concatenate path
spread.path = spread.search ? spread.pathname + spread.search : spread.pathname;
return spread;
} }
function removeMatchingHeaders(regex, headers) { function removeMatchingHeaders(regex, headers) {
@ -8157,17 +8195,25 @@ function createErrorType(code, message, baseClass) {
// Attach constructor and set default properties // Attach constructor and set default properties
CustomError.prototype = new (baseClass || Error)(); CustomError.prototype = new (baseClass || Error)();
CustomError.prototype.constructor = CustomError; Object.defineProperties(CustomError.prototype, {
CustomError.prototype.name = "Error [" + code + "]"; constructor: {
value: CustomError,
enumerable: false,
},
name: {
value: "Error [" + code + "]",
enumerable: false,
},
});
return CustomError; return CustomError;
} }
function abortRequest(request) { function destroyRequest(request, error) {
for (var event of events) { for (var event of events) {
request.removeListener(event, eventHandlers[event]); request.removeListener(event, eventHandlers[event]);
} }
request.on("error", noop); request.on("error", noop);
request.abort(); request.destroy(error);
} }
function isSubdomain(subdomain, domain) { function isSubdomain(subdomain, domain) {
@ -8188,6 +8234,10 @@ function isBuffer(value) {
return typeof value === "object" && ("length" in value); return typeof value === "object" && ("length" in value);
} }
function isURL(value) {
return URL && value instanceof URL;
}
// Exports // Exports
module.exports = wrap({ http: http, https: https }); module.exports = wrap({ http: http, https: https });
module.exports.wrap = wrap; module.exports.wrap = wrap;

View file

@ -41,8 +41,8 @@ export default class GitLabMapper implements GitResponseMapper<MergeRequestSchem
sourceRepo: await this.mapSourceRepo(mr), sourceRepo: await this.mapSourceRepo(mr),
targetRepo: await this.mapTargetRepo(mr), targetRepo: await this.mapTargetRepo(mr),
// if commits list is provided use that as source // if commits list is provided use that as source
nCommits: (commits && commits.length > 1) ? commits.length : 1, nCommits: (commits && commits.length > 0) ? commits.length : 1,
commits: (commits && commits.length > 1) ? commits : this.getSha(mr) commits: (commits && commits.length > 0) ? commits : this.getSha(mr)
}; };
} }

View file

@ -504,6 +504,50 @@ describe("cli runner", () => {
); );
}); });
test("single commit without squash", async () => {
addProcessArgs([
"-tb",
"target",
"-pr",
"https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/1",
"--no-squash",
]);
await runner.execute();
const cwd = process.cwd() + "/bp";
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITLAB, undefined, "https://my.gitlab.host.com/api/v4");
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
expect(GitCLIService.prototype.clone).toBeCalledWith("https://my.gitlab.host.com/superuser/backporting-example.git", cwd, "target");
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-e4dd336");
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "e4dd336a4a20f394df6665994df382fb1d193a11", undefined, undefined);
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-e4dd336");
expect(GitLabClient.prototype.createPullRequest).toBeCalledTimes(1);
expect(GitLabClient.prototype.createPullRequest).toBeCalledWith({
owner: "superuser",
repo: "backporting-example",
head: "bp-target-e4dd336",
base: "target",
title: "[target] Update test.txt",
body: expect.stringContaining("**Backport:** https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/1"),
reviewers: ["superuser"],
assignees: [],
labels: [],
comments: [],
}
);
});
test("multiple commits without squash", async () => { test("multiple commits without squash", async () => {
addProcessArgs([ addProcessArgs([
"-tb", "-tb",

View file

@ -427,6 +427,48 @@ describe("gha runner", () => {
); );
}); });
test("single commit without squash", async () => {
spyGetInput({
"target-branch": "target",
"pull-request": "https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/1",
"no-squash": "true",
});
await runner.execute();
const cwd = process.cwd() + "/bp";
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITLAB, undefined, "https://my.gitlab.host.com/api/v4");
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
expect(GitCLIService.prototype.clone).toBeCalledWith("https://my.gitlab.host.com/superuser/backporting-example.git", cwd, "target");
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-e4dd336");
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "e4dd336a4a20f394df6665994df382fb1d193a11", undefined, undefined);
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-e4dd336");
expect(GitLabClient.prototype.createPullRequest).toBeCalledTimes(1);
expect(GitLabClient.prototype.createPullRequest).toBeCalledWith({
owner: "superuser",
repo: "backporting-example",
head: "bp-target-e4dd336",
base: "target",
title: "[target] Update test.txt",
body: expect.stringContaining("**Backport:** https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/1"),
reviewers: ["superuser"],
assignees: [],
labels: [],
comments: [],
}
);
});
test("multiple commits without squash", async () => { test("multiple commits without squash", async () => {
spyGetInput({ spyGetInput({
"target-branch": "target", "target-branch": "target",

View file

@ -1,7 +1,7 @@
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory"; import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
import { Moctokit } from "@kie/mock-github"; import { Moctokit } from "@kie/mock-github";
import { TARGET_OWNER, REPO, MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, NOT_FOUND_PR_NUMBER, MULT_COMMITS_PR_FIXTURE, MULT_COMMITS_PR_COMMITS, NEW_PR_URL, NEW_PR_NUMBER } from "./github-data"; import { TARGET_OWNER, REPO, MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, NOT_FOUND_PR_NUMBER, MULT_COMMITS_PR_FIXTURE, MULT_COMMITS_PR_COMMITS, NEW_PR_URL, NEW_PR_NUMBER } from "./github-data";
import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, NESTED_NAMESPACE_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, NESTED_PROJECT_EXAMPLE, SUPERUSER} from "./gitlab-data"; import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, NESTED_NAMESPACE_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, NESTED_PROJECT_EXAMPLE, SUPERUSER, MERGED_SQUASHED_MR_COMMITS } from "./gitlab-data";
// high number, for each test we are not expecting // high number, for each test we are not expecting
// to send more than 3 reqs per api endpoint // to send more than 3 reqs per api endpoint
@ -30,6 +30,8 @@ export const getAxiosMocked = (url: string) => {
data = NESTED_PROJECT_EXAMPLE; data = NESTED_PROJECT_EXAMPLE;
} else if (url.endsWith("users?username=superuser")) { } else if (url.endsWith("users?username=superuser")) {
data = [SUPERUSER]; data = [SUPERUSER];
} else if (url.endsWith("merge_requests/1/commits")) {
data = MERGED_SQUASHED_MR_COMMITS;
} else if (url.endsWith("merge_requests/2/commits")) { } else if (url.endsWith("merge_requests/2/commits")) {
data = OPEN_PR_COMMITS; data = OPEN_PR_COMMITS;
} }

View file

@ -689,6 +689,29 @@ export const CLOSED_NOT_MERGED_MR = {
} }
}; };
export const MERGED_SQUASHED_MR_COMMITS = [
{
"id":"e4dd336a4a20f394df6665994df382fb1d193a11",
"short_id":"e4dd336a",
"created_at":"2023-06-29T09:59:10.000Z",
"parent_ids":[
],
"title":"Add new file",
"message":"Add new file",
"author_name":"Super User",
"author_email":"superuser@email.com",
"authored_date":"2023-06-29T09:59:10.000Z",
"committer_name":"Super User",
"committer_email":"superuser@email.com",
"committed_date":"2023-06-29T09:59:10.000Z",
"trailers":{
},
"web_url":"https://gitlab.com/superuser/backporting-example/-/commit/e4dd336a4a20f394df6665994df382fb1d193a11"
},
];
export const OPEN_PR_COMMITS = [ export const OPEN_PR_COMMITS = [
{ {
"id":"974519f65c9e0ed65277cd71026657a09fca05e7", "id":"974519f65c9e0ed65277cd71026657a09fca05e7",