-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
[No QA] fixed quickly Commenting on PRs hit api limit #2769
Conversation
I am sure it works as I have checked it with dummy promises but How should I test it in GH action context? |
We unit test what we can, and sometimes test in other test repos, but unfortunately with our GH Actions we also just do a lot of live-testing by just merging PRs and seeing if it works 😅 |
So @parasharrajat I'm going to approve this but we need to hold on the merge for a bit because something is seriously wrong with our deploy checklist at the moment and I'm pretty sure it would affect our ability to test this PR |
Also, keep in mind we won't really know if this works until we run a production deploy, which has been happening on average every two weeks for a while now, but could theoretically happen every day if there are no bugs on staging but not prod. |
I will wait. Just ping on the issue if it fails which is very unlikely |
Updated. I added Throttling based on https://github.com/actions/toolkit/tree/cac7db2d19f7a7e48fe1ec608fc99bc1786a858e/packages/github#extending-the-octokit-instance and removed the manual 1-sec waiting. To be sure we have to wait until it runs. Just ping here if there is an issue. |
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.
Nice work, just some minor comments!
prList.forEach((pr) => { | ||
githubUtils.createComment(github.context.repo.repo, pr, message, octokit) | ||
function commentPR(pr) { | ||
return githubUtils.createComment(GitHub.context.repo.repo, pr, message, octokit) |
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.
I see that it was there before, but the octokit
parameter is unnecessary / unused on this function. GithubUtils
takes an octokit instance in its constructor.
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.
oh Yeah. I didn't notice.
const prList = ActionUtils.getJSONInput('PR_LIST', {required: true}); | ||
const isProd = ActionUtils.getJSONInput('IS_PRODUCTION_DEPLOY', {required: true}); | ||
const version = core.getInput('DEPLOY_VERSION', {required: true}); | ||
const token = core.getInput('GITHUB_TOKEN', {required: true}); | ||
const octokit = github.getOctokit(token); | ||
const octokit = new OctokitThrottled(getOctokitOptions(token, { |
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.
This looks like some pretty generic code to set up the throttling, so can we move it to the GithubUtils
constructor ?
We could make throttled
an optional parameter that sets up throttling in the constructor, or just do it by default. Thoughts? Is there any time we wouldn't want throttling when we hit a rate limit?
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.
Throttling is good for all cases. Should we retry after Abuse Limit? It will by default wait for the Abuse locking period.
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.
I guess for now we can not retry after the Abuse Limit – using throttling everywhere we shouldn't reach that limit unless something weird is going on.
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.
Agree.
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.
@roryabraham GithubUtils
expects the first parameter as octokit
instance but when we apply throttling, we have to create a new Octokit instance with the throttle plugin applied.
const OctokitThrottled = GitHub.plugin(throttling);
const octokit = new OctokitThrottled(getOctokitOptions(token, {
Now how do you want me to handle this?
- We Pass a second parameter
{throttle: Boolean}.
which ignores the first parameter's octokit instance and creates new with the above logic. - We check for throttle property in the first parameter and if this property is found we Assume that we have passed the options as the first parameter and we will create our own Instance and if this property does not exist we assume that the first param was
octokit
instance so do whatever we do now.
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.
I guess what I would do is just get rid of the octokit
parameter in the GithubUtils
constructor and just set it up like you have here, directly in the constructor when someone creates a GithubUtils
instance. Then in all the actions that call new GithubUtils()
just get rid of the octokit
parameter. As a bonus step you could even make GithubUtils
a singleton, such that all the methods become static and internally it will initialize the octokit
instance if it needs to.
However, the only thing I'm unsure of in this approach is that when you do this:
const core = require('@actions/core');
const github = require('@actions/github');
const octokit = github.getOctokit(core.getInput('GITHUB_TOKEN', {required: true}));
You get a pre-authenticated octokit
client. I'm not sure if that happens the same using this:
const {GitHub, getOctokitOptions} = require('@actions/github/lib/utils');
because you're not providing that GITHUB_TOKEN
which is provided from the GH workflow that's calling the action.
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.
We pass the token here const octokit = new OctokitThrottled(getOctokitOptions(token <----, {
I checked github.getOctokit(
source. It basically does the the same.
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.
Oh, nice 👍
So yeah I'd recommend that you just instantiate the octokit
instance in the GithubUtils
constructor. It's up to you if you want to make the class a singleton
@roryabraham Were you expecting something like these changes? Please review. |
It seems Token is not being passed. Could you please help? |
@parasharrajat try replacing |
Also, if you find it more complex, then also just feel free to move the octokit initialization to the regular-old constructor and leave the non-static functions as non-static. |
@roryabraham I don't think this is complex. Seems it's better. If we update the test.yml file to include previously test file was using I have one thing to bypass this.
This bypasses the Octokit default instance creation. Should I do this instead? |
@parasharrajat Actually I think what we want to do is just mock |
Sorry, @roryabraham. But I do not seem to understand this. As per me we are using the real Octokit instance. const {Octokit} = require('@octokit/rest');
const GithubUtils = require('../../.github/libs/GithubUtils'); on the top of the file.
Agree to this, I think we can pass the Octokit instance from GithubUtils.octokitInternal = new Octokit(); After this, I am sure the test will pass. We are only mocking the Octokit at one place. I think I should try with these changes and see if tests are passing. Anyways, it's not going to affect any real functionality. |
@parasharrajat Okay, I'm not sure I'm following your solution, so feel free to update the PR so I can see what you mean. There are many examples of mocked octokit methods throughout the So the solution I'm suggesting is that you don't change the implementation of // This is a mock Octokit object. When we need to mock something in Octokit, we would just do something like:
// moctokit.issues.listForRepo = jest.fn().mockResolvedValue({data: []});
const moctokit = {};
beforeAll(() => {
GithubUtils.octokitInternal = moctokit;
}); And then change all the instances like this one to: // Before
GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: []});
// After
moctokit.issues.listForRepo = jest.fn().mockResolvedValue({data: []}); |
@roryabraham Updated. |
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.
Okay, this is looking good.
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
Great news! It seems this didn't catastrophically break all our GH Actions, which means that it probably works as expected! 🙃 |
Please review @roryabraham
Details
Added following changes to
markPullRequestsAsDeployed
GH action:Fixed Issues
Fixes #2759