mirror of
https://github.com/softprops/action-gh-release.git
synced 2025-06-28 22:26:02 +00:00
124 lines
4.8 KiB
JavaScript
124 lines
4.8 KiB
JavaScript
// @ts-ignore
|
|
import BottleneckLight from "bottleneck/light";
|
|
import { VERSION } from "./version";
|
|
import { wrapRequest } from "./wrap-request";
|
|
import triggersNotificationPaths from "./generated/triggers-notification-paths";
|
|
import { routeMatcher } from "./route-matcher";
|
|
// Workaround to allow tests to directly access the triggersNotification function.
|
|
const regex = routeMatcher(triggersNotificationPaths);
|
|
const triggersNotification = regex.test.bind(regex);
|
|
const groups = {};
|
|
// @ts-ignore
|
|
const createGroups = function (Bottleneck, common) {
|
|
// @ts-ignore
|
|
groups.global = new Bottleneck.Group({
|
|
id: "octokit-global",
|
|
maxConcurrent: 10,
|
|
...common,
|
|
});
|
|
// @ts-ignore
|
|
groups.search = new Bottleneck.Group({
|
|
id: "octokit-search",
|
|
maxConcurrent: 1,
|
|
minTime: 2000,
|
|
...common,
|
|
});
|
|
// @ts-ignore
|
|
groups.write = new Bottleneck.Group({
|
|
id: "octokit-write",
|
|
maxConcurrent: 1,
|
|
minTime: 1000,
|
|
...common,
|
|
});
|
|
// @ts-ignore
|
|
groups.notifications = new Bottleneck.Group({
|
|
id: "octokit-notifications",
|
|
maxConcurrent: 1,
|
|
minTime: 3000,
|
|
...common,
|
|
});
|
|
};
|
|
export function throttling(octokit, octokitOptions = {}) {
|
|
const { enabled = true, Bottleneck = BottleneckLight, id = "no-id", timeout = 1000 * 60 * 2, // Redis TTL: 2 minutes
|
|
connection, } = octokitOptions.throttle || {};
|
|
if (!enabled) {
|
|
return;
|
|
}
|
|
const common = { connection, timeout };
|
|
// @ts-ignore
|
|
if (groups.global == null) {
|
|
createGroups(Bottleneck, common);
|
|
}
|
|
const state = Object.assign({
|
|
clustering: connection != null,
|
|
triggersNotification,
|
|
minimumAbuseRetryAfter: 5,
|
|
retryAfterBaseValue: 1000,
|
|
retryLimiter: new Bottleneck(),
|
|
id,
|
|
...groups,
|
|
},
|
|
// @ts-ignore
|
|
octokitOptions.throttle);
|
|
if (typeof state.onAbuseLimit !== "function" ||
|
|
typeof state.onRateLimit !== "function") {
|
|
throw new Error(`octokit/plugin-throttling error:
|
|
You must pass the onAbuseLimit and onRateLimit error handlers.
|
|
See https://github.com/octokit/rest.js#throttling
|
|
|
|
const octokit = new Octokit({
|
|
throttle: {
|
|
onAbuseLimit: (retryAfter, options) => {/* ... */},
|
|
onRateLimit: (retryAfter, options) => {/* ... */}
|
|
}
|
|
})
|
|
`);
|
|
}
|
|
const events = {};
|
|
const emitter = new Bottleneck.Events(events);
|
|
// @ts-ignore
|
|
events.on("abuse-limit", state.onAbuseLimit);
|
|
// @ts-ignore
|
|
events.on("rate-limit", state.onRateLimit);
|
|
// @ts-ignore
|
|
events.on("error", (e) => console.warn("Error in throttling-plugin limit handler", e));
|
|
// @ts-ignore
|
|
state.retryLimiter.on("failed", async function (error, info) {
|
|
const options = info.args[info.args.length - 1];
|
|
const isGraphQL = options.url.startsWith("/graphql");
|
|
if (!(isGraphQL || error.status === 403)) {
|
|
return;
|
|
}
|
|
const retryCount = ~~options.request.retryCount;
|
|
options.request.retryCount = retryCount;
|
|
const { wantRetry, retryAfter } = await (async function () {
|
|
if (/\babuse\b/i.test(error.message)) {
|
|
// The user has hit the abuse rate limit. (REST only)
|
|
// https://developer.github.com/v3/#abuse-rate-limits
|
|
// The Retry-After header can sometimes be blank when hitting an abuse limit,
|
|
// but is always present after 2-3s, so make sure to set `retryAfter` to at least 5s by default.
|
|
const retryAfter = Math.max(~~error.headers["retry-after"], state.minimumAbuseRetryAfter);
|
|
const wantRetry = await emitter.trigger("abuse-limit", retryAfter, options);
|
|
return { wantRetry, retryAfter };
|
|
}
|
|
if (error.headers != null &&
|
|
error.headers["x-ratelimit-remaining"] === "0") {
|
|
// The user has used all their allowed calls for the current time period (REST and GraphQL)
|
|
// https://developer.github.com/v3/#rate-limiting
|
|
const rateLimitReset = new Date(~~error.headers["x-ratelimit-reset"] * 1000).getTime();
|
|
const retryAfter = Math.max(Math.ceil((rateLimitReset - Date.now()) / 1000), 0);
|
|
const wantRetry = await emitter.trigger("rate-limit", retryAfter, options);
|
|
return { wantRetry, retryAfter };
|
|
}
|
|
return {};
|
|
})();
|
|
if (wantRetry) {
|
|
options.request.retryCount++;
|
|
// @ts-ignore
|
|
return retryAfter * state.retryAfterBaseValue;
|
|
}
|
|
});
|
|
octokit.hook.wrap("request", wrapRequest.bind(null, state));
|
|
}
|
|
throttling.VERSION = VERSION;
|
|
throttling.triggersNotification = triggersNotification;
|