Skip to content
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

WNMGDS-3037 Adds script to compute percentage of nightly tests that pass #3320

Closed

Conversation

tamara-corbalt
Copy link
Collaborator

@tamara-corbalt tamara-corbalt commented Nov 15, 2024

Summary

This script analyzes jenkins nightly build data to compute a general pass rate and outputs a list of failed builds for manual inspection.
Note: It utilizes the Puppeteer library to simulate logging into Jenkins as a workaround, pending resolution of issues with setting up a proper API token.

// Sample output for a range of builds
% npx ts-node get-nightly-build-pass-rate.ts 750 3
Logging in to Jenkins...
Login successful.
Analyzing builds from 748 to 750...
Fetching data for build #748...
Fetching data for build #749...
Fetching data for build #750...
Analysis complete.
Pass Count: 3
Fail Count: 0
Pass-to-Fail Rate: 100.00%
Failed Builds: 

Jira ticket

How to test

  • Pull down the branch
  • Ensure you are connected to the Zscaler VPN.
  • Navigate to scripts directory:cd scripts
  • Create a .env file in the scripts directory with the following variables:
JENKINS_ID=<your-eua-id>
JENKINS_PASSWORD=<your-password>
JENKINS_LOGIN_URL=<Jenkins login URL>
JENKINS_BASE_API_URL=<Jenkins base API URL>
  • Run yarn install
  • Run npx ts-node get-nightly-build-pass-rate.ts <build number> <numberOfDays>
    • Replace with the latest jenkins build number you want to inspect.
    • Replace with the range of days to analyze (optional; defaults to 1 if omitted).

Checklist

  • Prefixed the PR title with the Jira ticket number as [WNMGDS-####] Title or [NO-TICKET] if this is unticketed work.
  • Selected appropriate Type (only one) label for this PR, if it is a breaking change, label should only be Type: Breaking
  • Selected appropriate Impacts, multiple can be selected.
  • Selected appropriate release milestone

@tamara-corbalt tamara-corbalt added Type: Internal This item relates to internal tooling/maintenance Type: Added Indicates a new feature. labels Nov 15, 2024
@tamara-corbalt tamara-corbalt added this to the 12.1.0 milestone Nov 15, 2024
@tamara-corbalt tamara-corbalt marked this pull request as draft November 15, 2024 19:27
@tamara-corbalt tamara-corbalt marked this pull request as ready for review November 18, 2024 19:05
}

let data: { result?: string } | null = null;
try {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Planning to move the block that handles parsing and processing build data into a separate function in the future. This will make the code more modular and easier to maintain.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This seems pretty straightforward and not super long, I think you could leave it inlined but a separate function is fine too!

@pwolfert
Copy link
Contributor

pwolfert commented Nov 18, 2024

Would the private toolkit repo be a more appropriate place for this?

If you're using Puppeteer instead of Playwright, there's no gain in shared dependencies by putting it in this repo anyway. That is, you'd have to install a browser-testing tool in the toolkit, but you're installing a new one anyway.

@tamara-corbalt
Copy link
Collaborator Author

Would the private toolkit repo be a more appropriate place for this?

If you're using Puppeteer instead of Playwright, there's no gain in shared dependencies by putting it in this repo anyway. That is, you'd have to install a browser-testing tool in the toolkit, but you're installing a new one anyway.

Hey @pwolfert, that's a great call-out, and I completely agree. I think this script would be a better fit for our private toolkit repo. I'll work on moving it over. Thanks for pointing that out! 😊


const loginAndAnalyzeBuilds = async (
latestBuildNumber: number,
numberOfDays: number,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're more likely to have the start date of a PI on hand rather than the number of days back we want to go. Do you think you could change this to accept a PI start date?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think accepting a date as input might introduce some challenges. Are you suggesting that the user manually identify the build number for the start of the PI based on that date? I feel that could make things more difficult, especially since we'll likely be running this script after a PI concludes.

It seems easier to locate the build number for the end of the PI—jenkins and our slack channel make it straightforward to find the most recent build. Backtracking to determine the exact build corresponding to a PI's start date would likely require more effort on the user's part. That said, if there’s an automated way to map dates to build numbers, I’d be happy to explore that!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I'm just suggesting that instead of saying the number of days back you want to go back to have a user specify a date to go back to. You could do the calculation by replacing const startBuildNumber = latestBuildNumber - numberOfDays + 1; with something more complicated but a little more natural for users:

const daysInAMillisecond = .000000011574
const now = Date.now()
const then = new Date(process.agv[3]) <- or whatever the date input argument is
const diff = Math.abs(now - then)
const numberOfDays = Math.floor(daysInAMillisecond * diff) <- or Math.ceil(), depends on how we need to round

const startBuildNumber = latestBuildNumber - numberOfDays + 1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also asking you to work with Dates and Time in Javascript, so it's ok if that's just a categorical 'no' 😆

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jack-ryan-nava-pbc, Ohhh I misunderstood! I think I see the appeal in using a specific date to calculate the starting build number—it definitely aligns more naturally with how we think about time. However, since our PIs are typically a fixed duration (I should double check this assumption), sticking to a set number of days might keep things simpler and more predictable for our use case?

Copy link
Collaborator

@jack-ryan-nava-pbc jack-ryan-nava-pbc Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, a fixed number of days wouldn't work if the PI isn't complete yet right?

Copy link
Collaborator Author

@tamara-corbalt tamara-corbalt Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, a fixed number of days wouldn't work if the PI isn't complete yet right?

@jack-ryan-nava-pbc Ah, I see your point now! I think I might not have been super clear earlier. When I mentioned a set number of days, I was referring to the fact that PIs are typically a fixed duration (e.g., 54 days for PI4). Since this script is mostly being used in retrospect, I thought it might be easier to find the final build number and work backward a set number of builds, rather than trying to pinpoint the exact date for the start of the PI.

If you feel strongly about using dates, let’s move this discussion over to pr-75 since I’m planning to close this one out.

page.click('button[type="submit"]'),
page.waitForNavigation({ waitUntil: 'networkidle2' }),
]);
console.log('Login successful.');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checking but pretty much everything before here will be removed once you get access to the Jenkins REST API?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's the plan. Once we sort out the issue with API credentials, we can probably dispense with Puppeteer, which feels like a heavy-handed tool for what this script could be. At that point, we’d likely switch to something simpler, like node-fetch, to handle the requests.

continue;
}

const responseBody = await response.text();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you call response.json() here and remove the later call to JSON.parse? Maybe not since Puppeteer is returning HTML?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I didn't think it was guaranteed that the response would always be JSON. Since we're retrieving page content, it might return HTML or plain text, so I felt using response.text() and handling JSON parsing in a try/catch block was a safer approach for now. We can revisit and potentially refactor this once we move away from Puppeteer.

}

let data: { result?: string } | null = null;
try {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This seems pretty straightforward and not super long, I think you could leave it inlined but a separate function is fine too!

console.log(`Pass Count: ${passCount}`);
console.log(`Fail Count: ${failCount}`);
console.log(`Pass-to-Fail Rate: ${passToFailRate.toFixed(2)}%`);
console.log(`Failed Builds: ${failedBuilds.join(', ')}`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking great! I would love to have these results written to a file. Do you think you could write these out to a CSV with 4 columns? PI number, pass count, fail count, ratio and maybe a list of the builds that were not accounted for for whatever reason. Does that sound like a good structure/reasonable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, good call! I'd like to see better formatting for this output.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jack-ryan-nava-pbc, After giving it some more thought, I think it might be best to hold off on finalizing the output format. I discussed some possible new features with @kim-cmsds earlier, and I foresee that we might be adding some elements down the road that could impact how we structure our data output. So to avoid revisions, I'd like to delay this for now. What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! I'm totally onboard.

}

if (!data || !data.result) {
console.log(`Build #${buildNumber}: Data unavailable or missing result.`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we track all these weird data unavailable cases? Like if the server returns a 500 or if there are missing data on a build for some other reason?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add more descriptive logging for unavailable cases

@tamara-corbalt
Copy link
Collaborator Author

Moving this script into experience-toolkit repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Added Indicates a new feature. Type: Internal This item relates to internal tooling/maintenance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants