-
Notifications
You must be signed in to change notification settings - Fork 204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix various rate limiting issues #1424
Changes from all commits
80b3922
84109da
0ef0b9a
3d64d66
b4831c0
66f7e2a
5d9bb97
2a0f270
baab296
d6e7be2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
import { graphql } from "@octokit/graphql"; | ||
import { enterpriseCompatibility } from "@octokit/plugin-enterprise-compatibility"; | ||
import path from "path"; | ||
import { retry } from "@octokit/plugin-retry"; | ||
|
@@ -11,6 +10,7 @@ import endent from "endent"; | |
import on from "await-to-js"; | ||
import join from "url-join"; | ||
import { gt, lt } from "semver"; | ||
import prettyMs from "pretty-ms"; | ||
|
||
import { Memoize as memoize } from "typescript-memoize"; | ||
|
||
|
@@ -128,9 +128,7 @@ export default class Git { | |
? this.options.graphqlBaseUrl || join(new URL(this.baseUrl).origin, "api") | ||
: this.baseUrl; | ||
this.logger.veryVerbose.info(`Initializing GitHub with: ${this.baseUrl}`); | ||
const GitHub = Octokit.plugin(enterpriseCompatibility) | ||
.plugin(retry) | ||
.plugin(throttling); | ||
const GitHub = Octokit.plugin(enterpriseCompatibility, retry, throttling); | ||
this.github = new GitHub({ | ||
baseUrl: this.baseUrl, | ||
auth: this.options.token, | ||
|
@@ -144,15 +142,20 @@ export default class Git { | |
); | ||
|
||
if (opts.request.retryCount < 5) { | ||
this.logger.verbose.log(`Retrying after ${retryAfter} seconds!`); | ||
this.logger.log.log( | ||
`Retrying after ${prettyMs(retryAfter * 1000)}!` | ||
); | ||
return true; | ||
} | ||
}, | ||
/** does not retry, only logs an error */ | ||
onAbuseLimit: (_: number, opts: ThrottleOpts) => { | ||
/** wait after abuse */ | ||
onAbuseLimit: (retryAfter: number, opts: ThrottleOpts) => { | ||
this.logger.log.error( | ||
`Went over abuse rate limit ${opts.method} ${opts.url}` | ||
`Went over abuse rate limit ${opts.method} ${ | ||
opts.url | ||
}, retrying in ${prettyMs(retryAfter * 1000)}.` | ||
); | ||
return true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will now actually retry after abuse limit is hit |
||
}, | ||
}, | ||
}); | ||
|
@@ -488,6 +491,7 @@ export default class Git { | |
} | ||
|
||
/** Search to GitHub project's issue and pull requests */ | ||
@memoize() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a single run if the search query is the same it should be safe to memoize. The |
||
async searchRepo( | ||
options: RestEndpointMethodTypes["search"]["issuesAndPullRequests"]["parameters"] | ||
) { | ||
|
@@ -505,10 +509,11 @@ export default class Git { | |
} | ||
|
||
/** Run a graphql query on the GitHub project */ | ||
@memoize() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing goes for graphql queries. Easy to memoize. |
||
async graphql<T>(query: string) { | ||
this.logger.verbose.info("Querying Github using GraphQL:\n", query); | ||
|
||
const data = await graphql<T>(query, { | ||
const data = await this.github.graphql<T>(query, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Switching to |
||
baseUrl: this.graphqlBaseUrl, | ||
request: { agent: this.options.agent }, | ||
headers: { | ||
|
@@ -687,7 +692,7 @@ export default class Git { | |
}); | ||
|
||
this.logger.veryVerbose.info(`Got response from PR #${pr}\n`, result); | ||
this.logger.verbose.info(`Got commits for PR #${pr}.`); | ||
this.logger.verbose.info(`Got commits for PR #${pr}`); | ||
|
||
return result; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -283,6 +283,7 @@ export default class Release { | |
* @param from - Tag or SHA to start at | ||
* @param to - Tag or SHA to end at (defaults to HEAD) | ||
*/ | ||
@memoize() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was iffy on adding memoization here. But since |
||
async getCommits(from: string, to = "HEAD"): Promise<IExtendedCommit[]> { | ||
this.logger.verbose.info(`Getting commits from ${from} to ${to}`); | ||
|
||
|
@@ -611,12 +612,9 @@ export default class Release { | |
hash: commit.hash, | ||
}); | ||
} else if (commit.authorEmail) { | ||
const author = await this.git.getUserByEmail(commit.authorEmail); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
resolvedAuthors.push({ | ||
email: commit.authorEmail, | ||
name: commit.authorName, | ||
...author, | ||
hash: commit.hash, | ||
}); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,9 +14,6 @@ export default class FirstTimeContributorPlugin implements IPlugin { | |
|
||
/** Tap into auto plugin points. */ | ||
apply(auto: Auto) { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const cache: Record<string, Record<string, any>> = {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rely on |
||
|
||
auto.hooks.onCreateChangelog.tap(this.name, (changelog) => { | ||
const base = new URL(changelog.options.baseUrl).origin; | ||
|
||
|
@@ -26,46 +23,30 @@ export default class FirstTimeContributorPlugin implements IPlugin { | |
return `${name}${username ? (name ? ` (${link})` : link) : ""}`; | ||
}; | ||
|
||
/** Get the PRs made by a user */ | ||
const getContributions = async (username: string) => { | ||
if (cache[username]) { | ||
return cache[username]; | ||
} | ||
|
||
const response = await auto.git?.graphql<Record<string, any>>(` | ||
{ | ||
search(first: 2, type: ISSUE, query: "repo:${auto.git?.options.owner}/${auto.git?.options.repo} is:pr is:merged author:${username}") { | ||
issueCount | ||
} | ||
} | ||
`); | ||
|
||
if (response) { | ||
cache[username] = response; | ||
} | ||
|
||
return response; | ||
}; | ||
|
||
changelog.hooks.addToBody.tapPromise( | ||
this.name, | ||
async (notes, commits) => { | ||
const newContributors = ( | ||
await Promise.all( | ||
flatMap(commits, (c) => c.authors).map(async (author) => { | ||
if (!author.username || author.type === "Bot") { | ||
return; | ||
} | ||
const newContributors: ICommitAuthor[] = []; | ||
|
||
// prettier-ignore | ||
const prs = await getContributions(author.username) | ||
for (const author of flatMap(commits, (c) => c.authors)) { | ||
if (!author.username || author.type === "Bot") { | ||
continue; | ||
} | ||
|
||
if (prs && prs.search.issueCount <= 1) { | ||
return author; | ||
// prettier-ignore | ||
// eslint-disable-next-line no-await-in-loop | ||
const prs = await auto.git?.graphql<Record<string, any>>(` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Call graphql API serially so we can use the memoized the graphql call |
||
{ | ||
search(first: 2, type: ISSUE, query: "repo:${auto.git?.options.owner}/${auto.git?.options.repo} is:pr is:merged author:${author.username}") { | ||
issueCount | ||
} | ||
}) | ||
) | ||
).filter((a): a is ICommitAuthor => Boolean(a)); | ||
} | ||
`) | ||
|
||
if (prs && prs.search.issueCount <= 1) { | ||
newContributors.push(author); | ||
} | ||
} | ||
|
||
if (!newContributors.length) { | ||
return notes; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this because getting all the commits in a canary release with no recent release can lead to hitting the rate limits. With canaries this is especially easy to hit because they are made so often.