diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe848d4bf9..817cf18540 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,11 +29,11 @@ jobs: - run: npm run format-check - run: npm run lint - run: npm run test - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v6 with: name: dist path: dist - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v6 with: name: action.yml path: action.yml @@ -50,12 +50,12 @@ jobs: with: ref: main - if: matrix.target == 'built' || github.event_name == 'pull_request' - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: dist path: dist - if: matrix.target == 'built' || github.event_name == 'pull_request' - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: action.yml path: . @@ -119,12 +119,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: actions/download-artifact@v6 + - uses: actions/download-artifact@v7 with: name: dist path: dist - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 with: token: ${{ secrets.ACTIONS_BOT_TOKEN }} commit-message: 'build: update distribution' diff --git a/README.md b/README.md index 3ff6bbab66..c3f17b7fd6 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,10 @@ Create Pull Request action will: # Make changes to pull request here - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 ``` -You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v7.x.x` +You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v8.x.x` ### Workflow permissions @@ -131,7 +131,7 @@ If you want branches to be deleted immediately on merge then you should use GitH For self-hosted runners behind a corporate proxy set the `https_proxy` environment variable. ```yml - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 env: https_proxy: http://: ``` @@ -153,7 +153,7 @@ Note that in order to read the step outputs the action step must have an id. ```yml - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 - name: Check outputs if: ${{ steps.cpr.outputs.pull-request-number }} run: | @@ -216,7 +216,7 @@ File changes that do not match one of the paths will be stashed and restored aft ```yml - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 with: add-paths: | *.java @@ -230,7 +230,7 @@ Note that the repository must be checked out on a branch with a remote, it won't ```yml steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Create commits run: | git config user.name 'Peter Evans' @@ -243,7 +243,7 @@ Note that the repository must be checked out on a branch with a remote, it won't - name: Uncommitted change run: date +%s > report.txt - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 ``` ### Auto-merge @@ -263,14 +263,14 @@ jobs: createPullRequest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Make changes to pull request run: date +%s > report.txt - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 with: token: ${{ secrets.PAT }} commit-message: Update report diff --git a/dist/index.js b/dist/index.js index a0c3f55743..56100057fa 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/peter-evans/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) { @@ -1819,14 +1825,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}`); @@ -1841,6 +1848,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 => { @@ -4511,7 +4522,7 @@ class HttpClient { } const usingSsl = parsedUrl.protocol === 'https:'; proxyAgent = new undici_1.ProxyAgent(Object.assign({ uri: proxyUrl.href, pipelining: !this._keepAlive ? 0 : 1 }, ((proxyUrl.username || proxyUrl.password) && { - token: `${proxyUrl.username}:${proxyUrl.password}` + token: `Basic ${Buffer.from(`${proxyUrl.username}:${proxyUrl.password}`).toString('base64')}` }))); this._proxyAgentDispatcher = proxyAgent; if (usingSsl && this._ignoreSslError) { @@ -4625,11 +4636,11 @@ function getProxyUrl(reqUrl) { })(); if (proxyVar) { try { - return new URL(proxyVar); + return new DecodedURL(proxyVar); } catch (_a) { if (!proxyVar.startsWith('http://') && !proxyVar.startsWith('https://')) - return new URL(`http://${proxyVar}`); + return new DecodedURL(`http://${proxyVar}`); } } else { @@ -4688,6 +4699,19 @@ function isLoopbackAddress(host) { hostLower.startsWith('[::1]') || hostLower.startsWith('[0:0:0:0:0:0:0:1]')); } +class DecodedURL extends URL { + constructor(url, base) { + super(url, base); + this._decodedUsername = decodeURIComponent(super.username); + this._decodedPassword = decodeURIComponent(super.password); + } + get username() { + return this._decodedUsername; + } + get password() { + return this._decodedPassword; + } +} //# sourceMappingURL=proxy.js.map /***/ }), @@ -32203,7 +32227,7 @@ module.exports = fetch; /***/ }), -/***/ 767: +/***/ 708: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { "use strict"; @@ -32723,46 +32747,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 }); @@ -36182,6 +36168,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: @@ -36421,6 +36499,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( + /(?=24.4.0" @@ -62,9 +63,10 @@ } }, "node_modules/@actions/http-client": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.1.tgz", - "integrity": "sha512-KhC/cZsq7f8I4LfZSJKgCvEwfkE8o1538VoBeoGzokVLLnbFDEAdFD3UhoMklxo2un9NJVBdANOresx7vTHlHw==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", + "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", + "license": "MIT", "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" @@ -1374,6 +1376,38 @@ "@octokit/core": ">=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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "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", @@ -7234,9 +7268,9 @@ } }, "node_modules/prettier": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.3.tgz", - "integrity": "sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", "peer": true, @@ -8179,9 +8213,9 @@ } }, "node_modules/ts-jest": { - "version": "29.4.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz", - "integrity": "sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==", + "version": "29.4.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", + "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", "dev": true, "license": "MIT", "dependencies": { @@ -8474,9 +8508,9 @@ } }, "node_modules/undici": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz", - "integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index b957b29860..32572fb849 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@octokit/core": "^6.1.6", "@octokit/plugin-paginate-rest": "^11.6.0", "@octokit/plugin-rest-endpoint-methods": "^13.5.0", + "@octokit/plugin-retry": "^7.2.1", "@octokit/plugin-throttling": "^9.6.1", "node-fetch-native": "^1.6.7", "p-limit": "^6.2.0", @@ -58,9 +59,9 @@ "jest-circus": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "js-yaml": "^4.1.1", - "prettier": "^3.7.3", - "ts-jest": "^29.4.5", + "prettier": "^3.7.4", + "ts-jest": "^29.4.6", "typescript": "^5.9.3", - "undici": "^6.22.0" + "undici": "^6.23.0" } } diff --git a/src/create-pull-request.ts b/src/create-pull-request.ts index 10b037751f..2e3f0e0a29 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/peter-evans/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() diff --git a/src/github-helper.ts b/src/github-helper.ts index 1779f7c165..85439dddf0 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) } diff --git a/src/octokit-client.ts b/src/octokit-client.ts index 8c2c02addf..11b0e3a6fa 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 => {