From d29001528392aef731746c724b3f1bb5c288f5d1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 09:44:45 +0000 Subject: [PATCH 1/3] feat: add @octokit/plugin-retry to handle retriable server errors (#4298) Add the retry plugin to automatically retry requests that fail with server errors (5xx status codes). Configure the plugin to exclude 429 (rate limit) from retries since that is already handled by the throttling plugin. - Add @octokit/plugin-retry dependency - Register retry plugin in Octokit client - Export retryOptions with doNotRetry list excluding 429 - Apply retryOptions in GitHubHelper constructor --- src/github-helper.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/github-helper.ts b/src/github-helper.ts index 1779f7c..85439dd 100644 --- a/src/github-helper.ts +++ b/src/github-helper.ts @@ -1,7 +1,12 @@ import * as core from '@actions/core' import {Inputs} from './create-pull-request' import {Commit, GitCommandManager} from './git-command-manager' -import {Octokit, OctokitOptions, throttleOptions} from './octokit-client' +import { + Octokit, + OctokitOptions, + retryOptions, + throttleOptions +} from './octokit-client' import pLimit from 'p-limit' import * as utils from './utils' @@ -52,6 +57,7 @@ export class GitHubHelper { options.baseUrl = 'https://api.github.com' } options.throttle = throttleOptions + options.retry = retryOptions this.octokit = new Octokit(options) } From ed42fa5aa2dc33950da9cd94229abd0d64657d1d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 09:44:46 +0000 Subject: [PATCH 2/3] feat: add @octokit/plugin-retry to handle retriable server errors (#4298) Add the retry plugin to automatically retry requests that fail with server errors (5xx status codes). Configure the plugin to exclude 429 (rate limit) from retries since that is already handled by the throttling plugin. - Add @octokit/plugin-retry dependency - Register retry plugin in Octokit client - Export retryOptions with doNotRetry list excluding 429 - Apply retryOptions in GitHubHelper constructor --- src/octokit-client.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/octokit-client.ts b/src/octokit-client.ts index 8c2c02a..11b0e3a 100644 --- a/src/octokit-client.ts +++ b/src/octokit-client.ts @@ -2,6 +2,7 @@ import * as core from '@actions/core' import {Octokit as OctokitCore} from '@octokit/core' import {paginateRest} from '@octokit/plugin-paginate-rest' import {restEndpointMethods} from '@octokit/plugin-rest-endpoint-methods' +import {retry} from '@octokit/plugin-retry' import {throttling} from '@octokit/plugin-throttling' import {fetch} from 'node-fetch-native/proxy' export {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods' @@ -11,6 +12,7 @@ export {OctokitOptions} from '@octokit/core/dist-types/types' export const Octokit = OctokitCore.plugin( paginateRest, restEndpointMethods, + retry, throttling, autoProxyAgent ) @@ -32,6 +34,11 @@ export const throttleOptions = { } } +export const retryOptions = { + // 429 is handled by the throttling plugin, so we exclude it from retry + doNotRetry: [400, 401, 403, 404, 410, 422, 429, 451] +} + // Octokit plugin to support the standard environment variables http_proxy, https_proxy and no_proxy function autoProxyAgent(octokit: OctokitCore) { octokit.hook.before('request', options => { From 2c1fadb1a7cf192a0b9b951046f811562c2a7a42 Mon Sep 17 00:00:00 2001 From: Raj-StepSecurity Date: Sun, 1 Feb 2026 15:22:16 +0530 Subject: [PATCH 3/3] conflicted commits cherry-picked --- dist/index.js | 212 ++++++++++++++++++++++++++++--------- package-lock.json | 40 +++++-- package.json | 1 + src/create-pull-request.ts | 8 +- 4 files changed, 201 insertions(+), 60 deletions(-) diff --git a/dist/index.js b/dist/index.js index 1601075..583ab68 100644 --- a/dist/index.js +++ b/dist/index.js @@ -458,7 +458,12 @@ function createPullRequest(inputs) { // deleted after being merged or closed. Without this the push using // '--force-with-lease' fails due to "stale info." // https://github.com/step-security/create-pull-request/issues/633 - yield git.exec(['remote', 'prune', branchRemoteName]); + try { + yield git.exec(['remote', 'prune', branchRemoteName]); + } + catch (error) { + core.warning(`Failed to prune remote '${branchRemoteName}': ${error.message}`); + } } core.endGroup(); // Apply the branch suffix if set @@ -1390,6 +1395,7 @@ class GitHubHelper { options.baseUrl = 'https://api.github.com'; } options.throttle = octokit_client_1.throttleOptions; + options.retry = octokit_client_1.retryOptions; this.octokit = new octokit_client_1.Octokit(options); } parseRepository(repository) { @@ -1839,14 +1845,15 @@ var __importStar = (this && this.__importStar) || (function () { }; })(); Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.throttleOptions = exports.Octokit = void 0; +exports.retryOptions = exports.throttleOptions = exports.Octokit = void 0; const core = __importStar(__nccwpck_require__(7484)); -const core_1 = __nccwpck_require__(767); +const core_1 = __nccwpck_require__(708); const plugin_paginate_rest_1 = __nccwpck_require__(3779); const plugin_rest_endpoint_methods_1 = __nccwpck_require__(9210); +const plugin_retry_1 = __nccwpck_require__(9735); const plugin_throttling_1 = __nccwpck_require__(6856); const proxy_1 = __nccwpck_require__(3459); -exports.Octokit = core_1.Octokit.plugin(plugin_paginate_rest_1.paginateRest, plugin_rest_endpoint_methods_1.restEndpointMethods, plugin_throttling_1.throttling, autoProxyAgent); +exports.Octokit = core_1.Octokit.plugin(plugin_paginate_rest_1.paginateRest, plugin_rest_endpoint_methods_1.restEndpointMethods, plugin_retry_1.retry, plugin_throttling_1.throttling, autoProxyAgent); exports.throttleOptions = { onRateLimit: (retryAfter, options, _, retryCount) => { core.debug(`Hit rate limit for request ${options.method} ${options.url}`); @@ -1861,6 +1868,10 @@ exports.throttleOptions = { core.warning(`Requests may be retried after ${retryAfter} seconds.`); } }; +exports.retryOptions = { + // 429 is handled by the throttling plugin, so we exclude it from retry + doNotRetry: [400, 401, 403, 404, 410, 422, 429, 451] +}; // Octokit plugin to support the standard environment variables http_proxy, https_proxy and no_proxy function autoProxyAgent(octokit) { octokit.hook.before('request', options => { @@ -42077,7 +42088,7 @@ module.exports = fetch; /***/ }), -/***/ 767: +/***/ 708: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -42597,46 +42608,8 @@ var endpoint = withDefaults(null, DEFAULTS); // EXTERNAL MODULE: ./node_modules/fast-content-type-parse/index.js var fast_content_type_parse = __nccwpck_require__(8739); -;// CONCATENATED MODULE: ./node_modules/@octokit/request-error/dist-src/index.js -class RequestError extends Error { - name; - /** - * http status code - */ - status; - /** - * Request options that lead to the error. - */ - request; - /** - * Response object if a response was received - */ - response; - constructor(message, statusCode, options) { - super(message); - this.name = "HttpError"; - this.status = Number.parseInt(statusCode); - if (Number.isNaN(this.status)) { - this.status = 0; - } - if ("response" in options) { - this.response = options.response; - } - const requestCopy = Object.assign({}, options.request); - if (options.request.headers.authorization) { - requestCopy.headers = Object.assign({}, options.request.headers, { - authorization: options.request.headers.authorization.replace( - /(?= 400) { octokitResponse.data = await getResponseData(fetchResponse); - throw new RequestError(toErrorMessage(octokitResponse.data), status, { + throw new dist_src/* RequestError */.G(toErrorMessage(octokitResponse.data), status, { response: octokitResponse, request: requestOptions }); @@ -46056,6 +46029,98 @@ legacyRestEndpointMethods.VERSION = VERSION; //# sourceMappingURL=index.js.map +/***/ }), + +/***/ 9735: +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { + +"use strict"; +__nccwpck_require__.r(__webpack_exports__); +/* harmony export */ __nccwpck_require__.d(__webpack_exports__, { +/* harmony export */ VERSION: () => (/* binding */ VERSION), +/* harmony export */ retry: () => (/* binding */ retry) +/* harmony export */ }); +/* harmony import */ var bottleneck_light_js__WEBPACK_IMPORTED_MODULE_0__ = __nccwpck_require__(3251); +/* harmony import */ var _octokit_request_error__WEBPACK_IMPORTED_MODULE_1__ = __nccwpck_require__(1015); +// pkg/dist-src/version.js +var VERSION = "0.0.0-development"; + +// pkg/dist-src/error-request.js +async function errorRequest(state, octokit, error, options) { + if (!error.request || !error.request.request) { + throw error; + } + if (error.status >= 400 && !state.doNotRetry.includes(error.status)) { + const retries = options.request.retries != null ? options.request.retries : state.retries; + const retryAfter = Math.pow((options.request.retryCount || 0) + 1, 2); + throw octokit.retry.retryRequest(error, retries, retryAfter); + } + throw error; +} + +// pkg/dist-src/wrap-request.js + + +async function wrapRequest(state, octokit, request, options) { + const limiter = new bottleneck_light_js__WEBPACK_IMPORTED_MODULE_0__(); + limiter.on("failed", function(error, info) { + const maxRetries = ~~error.request.request.retries; + const after = ~~error.request.request.retryAfter; + options.request.retryCount = info.retryCount + 1; + if (maxRetries > info.retryCount) { + return after * state.retryAfterBaseValue; + } + }); + return limiter.schedule( + requestWithGraphqlErrorHandling.bind(null, state, octokit, request), + options + ); +} +async function requestWithGraphqlErrorHandling(state, octokit, request, options) { + const response = await request(request, options); + if (response.data && response.data.errors && response.data.errors.length > 0 && /Something went wrong while executing your query/.test( + response.data.errors[0].message + )) { + const error = new _octokit_request_error__WEBPACK_IMPORTED_MODULE_1__/* .RequestError */ .G(response.data.errors[0].message, 500, { + request: options, + response + }); + return errorRequest(state, octokit, error, options); + } + return response; +} + +// pkg/dist-src/index.js +function retry(octokit, octokitOptions) { + const state = Object.assign( + { + enabled: true, + retryAfterBaseValue: 1e3, + doNotRetry: [400, 401, 403, 404, 410, 422, 451], + retries: 3 + }, + octokitOptions.retry + ); + if (state.enabled) { + octokit.hook.error("request", errorRequest.bind(null, state, octokit)); + octokit.hook.wrap("request", wrapRequest.bind(null, state, octokit)); + } + return { + retry: { + retryRequest: (error, retries, retryAfter) => { + error.request.request = Object.assign({}, error.request.request, { + retries, + retryAfter + }); + return error; + } + } + }; +} +retry.VERSION = VERSION; + + + /***/ }), /***/ 6856: @@ -46295,6 +46360,55 @@ throttling.triggersNotification = triggersNotification; +/***/ }), + +/***/ 1015: +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { + +"use strict"; +/* harmony export */ __nccwpck_require__.d(__webpack_exports__, { +/* harmony export */ G: () => (/* binding */ RequestError) +/* harmony export */ }); +class RequestError extends Error { + name; + /** + * http status code + */ + status; + /** + * Request options that lead to the error. + */ + request; + /** + * Response object if a response was received + */ + response; + constructor(message, statusCode, options) { + super(message); + this.name = "HttpError"; + this.status = Number.parseInt(statusCode); + if (Number.isNaN(this.status)) { + this.status = 0; + } + if ("response" in options) { + this.response = options.response; + } + const requestCopy = Object.assign({}, options.request); + if (options.request.headers.authorization) { + requestCopy.headers = Object.assign({}, options.request.headers, { + authorization: options.request.headers.authorization.replace( + /(?=6" } }, + "node_modules/@octokit/plugin-retry": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.2.1.tgz", + "integrity": "sha512-wUc3gv0D6vNHpGxSaR3FlqJpTXGWgqmk607N9L3LvPL4QjaxDgX/1nY2mGpT37Khn+nlIXdljczkRnNdTTV3/A==", + "dependencies": { + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", + "dependencies": { + "@octokit/openapi-types": "^25.1.0" + } + }, "node_modules/@octokit/plugin-throttling": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.6.1.tgz", @@ -2480,7 +2508,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2995,7 +3022,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001640", "electron-to-chromium": "^1.4.820", @@ -3859,7 +3885,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -4106,7 +4131,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -5707,7 +5731,6 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -7249,7 +7272,6 @@ "integrity": "sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -8128,7 +8150,6 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8446,7 +8467,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index e80101d..1e62be6 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@octokit/plugin-paginate-rest": "^11.6.0", "@octokit/plugin-rest-endpoint-methods": "^13.5.0", "@octokit/plugin-throttling": "^9.6.1", + "@octokit/plugin-retry": "^7.2.1", "axios": "^1.13.2", "node-fetch-native": "^1.6.7", "p-limit": "^6.2.0", diff --git a/src/create-pull-request.ts b/src/create-pull-request.ts index 999bd13..b5b236f 100644 --- a/src/create-pull-request.ts +++ b/src/create-pull-request.ts @@ -127,7 +127,13 @@ export async function createPullRequest(inputs: Inputs): Promise { // deleted after being merged or closed. Without this the push using // '--force-with-lease' fails due to "stale info." // https://github.com/step-security/create-pull-request/issues/633 - await git.exec(['remote', 'prune', branchRemoteName]) + try { + await git.exec(['remote', 'prune', branchRemoteName]) + } catch (error) { + core.warning( + `Failed to prune remote '${branchRemoteName}': ${(error as Error).message}` + ) + } } core.endGroup()