2022-12-05 23:01:03 +00:00
module . exports = {
friendlyName : 'Get bug and PR report' ,
2023-09-02 23:59:30 +00:00
description : 'Get information about open bugs and pull requests.' ,
2022-12-05 23:01:03 +00:00
inputs : {
} ,
fn : async function ( { } ) {
if ( ! sails . config . custom . githubAccessToken ) {
throw new Error ( 'Missing GitHub access token! To use this script, a GitHub access token is required. To resolve, add a GitHub access token to your local configuration (website/config/local.js) as sails.config.custom.githubAccessToken or provide one when running this script. (ex: "sails_custom__githubAccessToken=YOUR_PERSONAL_ACCESS_TOKEN sails run get-bug-and-pr-report")' ) ;
}
let baseHeaders = {
'User-Agent' : 'Fleet average open time' ,
'Authorization' : ` token ${ sails . config . custom . githubAccessToken } `
} ;
const ONE _DAY _IN _MILLISECONDS = ( 1000 * 60 * 60 * 24 ) ;
const todaysDate = new Date ;
const threeWeeksAgo = new Date ( Date . now ( ) - ( 21 * ONE _DAY _IN _MILLISECONDS ) ) ;
const NUMBER _OF _RESULTS _REQUESTED = 100 ;
let daysSinceBugsWereOpened = [ ] ;
2024-09-03 15:52:38 +00:00
let allBugs32DaysOrOlder = [ ] ;
2023-12-11 20:38:51 +00:00
let allBugsCreatedInPastWeek = [ ] ;
let allBugsClosedInPastWeek = [ ] ;
2024-09-03 15:52:38 +00:00
let allBugsReportedByCustomersInPastWeek = [ ] ;
2022-12-05 23:01:03 +00:00
let daysSincePullRequestsWereOpened = [ ] ;
2023-05-12 22:08:00 +00:00
let daysSinceContributorPullRequestsWereOpened = [ ] ;
2022-12-05 23:01:03 +00:00
let commitToMergeTimesInDays = [ ] ;
2023-09-02 23:59:30 +00:00
let allPublicOpenPrs = [ ] ;
let publicPrsMergedInThePastThreeWeeks = [ ] ;
let allNonPublicOpenPrs = [ ] ;
let nonPublicPrsClosedInThePastThreeWeeks = [ ] ;
2022-12-05 23:01:03 +00:00
await sails . helpers . flow . simultaneously ( [
// ██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ██╗ ██╗ ██████╗ ███████╗
// ██╔═══██╗██╔══██╗██╔════╝████╗ ██║ ██╔══██╗██║ ██║██╔════╝ ██╔════╝
// ██║ ██║██████╔╝█████╗ ██╔██╗ ██║ ██████╔╝██║ ██║██║ ███╗███████╗
// ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║ ██╔══██╗██║ ██║██║ ██║╚════██║
// ╚██████╔╝██║ ███████╗██║ ╚████║ ██████╔╝╚██████╔╝╚██████╔╝███████║
// ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
//
async ( ) => {
let pageNumberForPossiblePaginatedResults = 0 ;
let allIssuesWithBugLabel = [ ] ;
// Fetch all open issues in the fleetdm/fleet repo with the bug label.
// Note: This will send requests to GitHub until the number of results is less than the number we requested.
await sails . helpers . flow . until ( async ( ) => {
// Increment the page of results we're requesting.
pageNumberForPossiblePaginatedResults += 1 ;
let issuesWithBugLabel = await sails . helpers . http . get (
` https://api.github.com/repos/fleetdm/fleet/issues ` ,
{
'state' : 'open' ,
'labels' : 'bug' ,
'per_page' : NUMBER _OF _RESULTS _REQUESTED ,
'page' : pageNumberForPossiblePaginatedResults ,
} ,
baseHeaders
) . retry ( ) ;
// Add the results to the allIssuesWithBugLabel array.
allIssuesWithBugLabel = allIssuesWithBugLabel . concat ( issuesWithBugLabel ) ;
2022-12-13 23:33:23 +00:00
// If we received less results than we requested, we've reached the last page of the results.
2022-12-05 23:01:03 +00:00
return issuesWithBugLabel . length !== NUMBER _OF _RESULTS _REQUESTED ;
} , 10000 ) ;
// iterate through the allIssuesWithBugLabel array, adding the number
2023-10-19 04:26:58 +00:00
for ( let issue of allIssuesWithBugLabel ) {
2022-12-05 23:01:03 +00:00
// Create a date object from the issue's created_at timestamp.
let issueOpenedOn = new Date ( issue . created _at ) ;
// Get the amount of time this issue has been open in milliseconds.
let timeOpenInMS = Math . abs ( todaysDate - issueOpenedOn ) ;
// Convert the miliseconds to days and add the value to the daysSinceBugsWereOpened array
let timeOpenInDays = timeOpenInMS / ONE _DAY _IN _MILLISECONDS ;
2024-09-03 15:52:38 +00:00
if ( timeOpenInDays >= 32 ) {
allBugs32DaysOrOlder . push ( issue ) ;
}
2023-12-11 20:38:51 +00:00
if ( timeOpenInDays <= 7 ) {
2024-09-03 15:52:38 +00:00
// All bugs in past week
2023-12-11 20:38:51 +00:00
allBugsCreatedInPastWeek . push ( issue ) ;
2024-09-03 15:52:38 +00:00
// Customer-reported bugs
if ( issue . labels . some ( label => label . name . indexOf ( 'customer-' ) >= 0 ) ) {
allBugsReportedByCustomersInPastWeek . push ( issue ) ;
}
2023-12-11 20:38:51 +00:00
}
2022-12-05 23:01:03 +00:00
daysSinceBugsWereOpened . push ( timeOpenInDays ) ;
}
} ,
2023-12-11 20:38:51 +00:00
// ██████╗██╗ ██████╗ ███████╗███████╗██████╗ ██████╗ ██╗ ██╗ ██████╗ ███████╗
// ██╔════╝██║ ██╔═══██╗██╔════╝██╔════╝██╔══██╗ ██╔══██╗██║ ██║██╔════╝ ██╔════╝
// ██║ ██║ ██║ ██║███████╗█████╗ ██║ ██║ ██████╔╝██║ ██║██║ ███╗███████╗
// ██║ ██║ ██║ ██║╚════██║██╔══╝ ██║ ██║ ██╔══██╗██║ ██║██║ ██║╚════██║
// ╚██████╗███████╗╚██████╔╝███████║███████╗██████╔╝ ██████╔╝╚██████╔╝╚██████╔╝███████║
// ╚═════╝╚══════╝ ╚═════╝ ╚══════╝╚══════╝╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
//
async ( ) => {
let pageNumberForPaginatedResults = 0 ;
let allIssuesWithBugLabel = [ ] ;
// Fetch all closed issues in the fleetdm/fleet repo with the bug label.
// Note: This will send requests to GitHub until the number of results is less than the number we requested.
await sails . helpers . flow . until ( async ( ) => {
// Increment the page of results we're requesting.
pageNumberForPaginatedResults += 1 ;
let issuesWithBugLabel = await sails . helpers . http . get (
` https://api.github.com/repos/fleetdm/fleet/issues ` ,
{
'state' : 'closed' ,
'labels' : 'bug' ,
'per_page' : NUMBER _OF _RESULTS _REQUESTED ,
'page' : pageNumberForPaginatedResults ,
} ,
baseHeaders
) . retry ( ) ;
// Add the results to the allIssuesWithBugLabel array.
allIssuesWithBugLabel = allIssuesWithBugLabel . concat ( issuesWithBugLabel ) ;
// Stop when we've received results from the third page.
return pageNumberForPaginatedResults === 3 ;
} , 10000 ) ;
// iterate through the allIssuesWithBugLabel array, adding the number
for ( let issue of allIssuesWithBugLabel ) {
// Create a date object from the issue's closed_at timestamp.
let issueClosedOn = new Date ( issue . closed _at ) ;
// Get the amount of time this issue has been closed in milliseconds.
let timeClosedInMS = Math . abs ( todaysDate - issueClosedOn ) ;
// Convert the miliseconds to days and add the value to the allBugsClosedInPastWeek array
let timeClosedInDays = timeClosedInMS / ONE _DAY _IN _MILLISECONDS ;
if ( timeClosedInDays <= 7 ) {
allBugsClosedInPastWeek . push ( issue ) ;
}
}
} ,
2022-12-05 23:01:03 +00:00
// ██████╗██╗ ██████╗ ███████╗███████╗██████╗ ██████╗ ██████╗ ███████╗
// ██╔════╝██║ ██╔═══██╗██╔════╝██╔════╝██╔══██╗ ██╔══██╗██╔══██╗██╔════╝
// ██║ ██║ ██║ ██║███████╗█████╗ ██║ ██║ ██████╔╝██████╔╝███████╗
// ██║ ██║ ██║ ██║╚════██║██╔══╝ ██║ ██║ ██╔═══╝ ██╔══██╗╚════██║
// ╚██████╗███████╗╚██████╔╝███████║███████╗██████╔╝ ██║ ██║ ██║███████║
// ╚═════╝╚══════╝ ╚═════╝ ╚══════╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝
//
async ( ) => {
let pageNumberForPaginatedResults = 0 ;
// Fetch the last 300 closed pull requests from the fleetdm/fleet GitHub Repo
2023-09-02 23:59:30 +00:00
// [?] https://docs.github.com/en/free-pro-team@latest/rest/pulls/pulls#list-pull-requests
2022-12-05 23:01:03 +00:00
await sails . helpers . flow . until ( async ( ) => {
// Increment the page of results we're requesting.
pageNumberForPaginatedResults += 1 ;
let closedPullRequests = await sails . helpers . http . get (
` https://api.github.com/repos/fleetdm/fleet/pulls ` ,
{
'state' : 'closed' ,
'sort' : 'updated' ,
'direction' : 'desc' ,
'per_page' : NUMBER _OF _RESULTS _REQUESTED ,
'page' : pageNumberForPaginatedResults ,
} ,
baseHeaders
) . retry ( ) ;
2023-03-06 21:57:53 +00:00
// Exclude draft PRs and filter the PRs we received from Github using the pull request's merged_at date.
2022-12-05 23:01:03 +00:00
let resultsToAdd = closedPullRequests . filter ( ( pullRequest ) => {
2023-03-06 21:57:53 +00:00
return ! pullRequest . draft && threeWeeksAgo <= new Date ( pullRequest . merged _at ) ;
2022-12-05 23:01:03 +00:00
} ) ;
// Add the filtered array of PRs to the array of all pull requests merged in the past three weeks.
2023-09-02 23:59:30 +00:00
publicPrsMergedInThePastThreeWeeks = publicPrsMergedInThePastThreeWeeks . concat ( resultsToAdd ) ;
2022-12-13 23:33:23 +00:00
// Stop when we've received results from the third page.
2022-12-05 23:01:03 +00:00
return pageNumberForPaginatedResults === 3 ;
} ) ;
// To get the timestamp of the first commit for each pull request, we'll need to send a request to the commits API endpoint.
2023-09-02 23:59:30 +00:00
await sails . helpers . flow . simultaneouslyForEach ( publicPrsMergedInThePastThreeWeeks , async ( pullRequest ) => {
2022-12-05 23:01:03 +00:00
// Create a date object from the PR's merged_at timestamp.
let pullRequestMergedOn = new Date ( pullRequest . merged _at ) ;
2023-09-02 23:59:30 +00:00
// Get commits on this PR.
// [?] https://docs.github.com/en/rest/commits/commits#list-commits
2022-12-05 23:01:03 +00:00
let commitsOnThisPullRequest = await sails . helpers . http . get ( pullRequest . commits _url , { } , baseHeaders ) . retry ( ) ;
2025-03-28 22:00:33 +00:00
if ( commitsOnThisPullRequest . length === 0 ) {
sails . log . warn ( ` A pull request # ${ pullRequest . number } ( ${ pullRequest . html _url } ) was found that has no commits. This PR will be not counted in the commit to merge time metric. ` ) ;
return ;
}
2022-12-05 23:01:03 +00:00
// Create a new Date from the timestamp of the first commit on this pull request.
let firstCommitAt = new Date ( commitsOnThisPullRequest [ 0 ] . commit . author . date ) ; // https://docs.github.com/en/rest/commits/commits#list-commits--code-samples
// Get the amount of time this issue has been open in milliseconds.
let timeFromCommitToMergeInMS = pullRequestMergedOn - firstCommitAt ;
// Convert the miliseconds to days and add the value to the daysSincePullRequestsWereOpened array.
let timeFromFirstCommitInDays = timeFromCommitToMergeInMS / ONE _DAY _IN _MILLISECONDS ;
commitToMergeTimesInDays . push ( timeFromFirstCommitInDays ) ;
} ) ;
} ,
// ██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ██████╗ ███████╗
// ██╔═══██╗██╔══██╗██╔════╝████╗ ██║ ██╔══██╗██╔══██╗██╔════╝
// ██║ ██║██████╔╝█████╗ ██╔██╗ ██║ ██████╔╝██████╔╝███████╗
// ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║ ██╔═══╝ ██╔══██╗╚════██║
// ╚██████╔╝██║ ███████╗██║ ╚████║ ██║ ██║ ██║███████║
// ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝╚══════╝
//
async ( ) => {
let pullRequestResultsPageNumber = 0 ;
2023-05-12 22:08:00 +00:00
let contributorPullRequests = [ ] ;
2022-12-05 23:01:03 +00:00
// Fetch all open pull requests in the fleetdm/fleet repo.
// Note: This will send requests to GitHub until the number of results is less than the number we requested.
2023-09-02 23:59:30 +00:00
// [?] https://docs.github.com/en/free-pro-team@latest/rest/pulls/pulls#list-pull-requests
2022-12-05 23:01:03 +00:00
await sails . helpers . flow . until ( async ( ) => {
// Increment the page of results we're requesting.
pullRequestResultsPageNumber += 1 ;
let pullRequests = await sails . helpers . http . get (
` https://api.github.com/repos/fleetdm/fleet/pulls ` ,
{
'state' : 'open' ,
'per_page' : NUMBER _OF _RESULTS _REQUESTED ,
'page' : pullRequestResultsPageNumber ,
} ,
baseHeaders
) . retry ( ) ;
// Add the results to the array of results.
2023-09-02 23:59:30 +00:00
allPublicOpenPrs = allPublicOpenPrs . concat ( pullRequests ) ;
2022-12-13 23:33:23 +00:00
// If we received less results than we requested, we've reached the last page of the results.
2022-12-05 23:01:03 +00:00
return pullRequests . length !== NUMBER _OF _RESULTS _REQUESTED ;
} , 10000 ) ;
2023-03-06 21:57:53 +00:00
2025-04-04 17:39:32 +00:00
for ( let pullRequest of allPublicOpenPrs ) {
2022-12-05 23:01:03 +00:00
// Create a date object from the PR's created_at timestamp.
let pullRequestOpenedOn = new Date ( pullRequest . created _at ) ;
// Get the amount of time this issue has been open in milliseconds.
let timeOpenInMS = Math . abs ( todaysDate - pullRequestOpenedOn ) ;
// Convert the miliseconds to days and add the value to the daysSincePullRequestsWereOpened array
let timeOpenInDays = timeOpenInMS / ONE _DAY _IN _MILLISECONDS ;
2023-03-06 21:57:53 +00:00
if ( ! pullRequest . draft ) { // Exclude draft PRs
daysSincePullRequestsWereOpened . push ( timeOpenInDays ) ;
}
2023-06-09 15:55:12 +00:00
// If not a draft, not a bot, not a PR labeled with #handbook
// Track as a contributor PR and include in contributor PR KPI
2024-02-19 22:50:54 +00:00
if ( ! pullRequest . draft && pullRequest . user . type !== 'Bot' && ! pullRequest . labels . some ( label => label . name === '#handbook' || label . name === '~ceo' || label . name === ':improve documentation' ) ) {
2023-05-12 22:08:00 +00:00
daysSinceContributorPullRequestsWereOpened . push ( timeOpenInDays ) ;
contributorPullRequests . push ( pullRequest ) ;
}
2023-03-06 21:57:53 +00:00
} //∞
2022-12-05 23:01:03 +00:00
2023-09-02 23:59:30 +00:00
} ,
// ███╗ ██╗ ██████╗ ███╗ ██╗ ██████╗ ██╗ ██╗██████╗ ██╗ ██╗ ██████╗
// ████╗ ██║██╔═══██╗████╗ ██║ ██╔══██╗██║ ██║██╔══██╗██║ ██║██╔════╝
// ██╔██╗ ██║██║ ██║██╔██╗ ██║█████╗██████╔╝██║ ██║██████╔╝██║ ██║██║
// ██║╚██╗██║██║ ██║██║╚██╗██║╚════╝██╔═══╝ ██║ ██║██╔══██╗██║ ██║██║
// ██║ ╚████║╚██████╔╝██║ ╚████║ ██║ ╚██████╔╝██████╔╝███████╗██║╚██████╗
// ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═════╝
//
async ( ) => {
2024-09-03 15:52:38 +00:00
// Fetch confidential PRs (current open, and recent closed)
for ( let repoName of [ 'confidential' ] ) {
2023-09-02 23:59:30 +00:00
// [?] https://docs.github.com/en/free-pro-team@latest/rest/pulls/pulls#list-pull-requests
let openPrs = await sails . helpers . http . get ( ` https://api.github.com/repos/fleetdm/ ${ encodeURIComponent ( repoName ) } /pulls ` , {
state : 'open' ,
'per_page' : 100 ,
page : 1 ,
} , baseHeaders ) ;
allNonPublicOpenPrs = allNonPublicOpenPrs . concat ( openPrs ) ;
// [?] https://docs.github.com/en/free-pro-team@latest/rest/pulls/pulls#list-pull-requests
let last100ClosedPrs = await sails . helpers . http . get ( ` https://api.github.com/repos/fleetdm/ ${ encodeURIComponent ( repoName ) } /pulls ` , {
state : 'closed' ,
sort : 'updated' ,
direction : 'desc' ,
'per_page' : 100 ,
page : 1 ,
} , baseHeaders ) ;
// Exclude draft PRs and filter the PRs we received from Github using the pull request's closed_at date.
nonPublicPrsClosedInThePastThreeWeeks = nonPublicPrsClosedInThePastThreeWeeks . concat (
last100ClosedPrs . filter ( ( pr ) => {
return ! pr . draft && threeWeeksAgo . getTime ( ) <= ( new Date ( pr . closed _at ) ) . getTime ( ) ;
} )
) ;
} //∞
2022-12-05 23:01:03 +00:00
}
] ) ;
// Get the averages from the arrays of results.
2023-10-19 04:26:58 +00:00
let averageNumberOfDaysBugsAreOpenFor = Math . round ( _ . sum ( daysSinceBugsWereOpened ) / daysSinceBugsWereOpened . length ) ;
2023-05-12 22:08:00 +00:00
let averageDaysContributorPullRequestsAreOpenFor = Math . round ( _ . sum ( daysSinceContributorPullRequestsWereOpened ) / daysSinceContributorPullRequestsWereOpened . length ) ;
2022-12-05 23:01:03 +00:00
2023-09-02 23:59:30 +00:00
2024-10-23 18:12:21 +00:00
// Compute Handbook PR KPIs, which are slightly simpler.
2023-09-02 23:59:30 +00:00
// FUTURE: Refactor this to be less messy.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2024-10-23 18:12:21 +00:00
let handbookOpenPrs = [ ] ;
handbookOpenPrs = handbookOpenPrs . concat ( allPublicOpenPrs . filter ( ( pr ) => ! pr . draft && _ . pluck ( pr . labels , 'name' ) . includes ( '#handbook' ) ) ) ;
handbookOpenPrs = handbookOpenPrs . concat ( allNonPublicOpenPrs . filter ( ( pr ) => ! pr . draft && _ . pluck ( pr . labels , 'name' ) . includes ( '#handbook' ) ) ) ;
2023-09-02 23:59:30 +00:00
2024-10-23 18:12:21 +00:00
let handbookPrsMergedRecently = [ ] ;
handbookPrsMergedRecently = handbookPrsMergedRecently . concat ( publicPrsMergedInThePastThreeWeeks . filter ( ( pr ) => ! pr . draft && _ . pluck ( pr . labels , 'name' ) . includes ( '#handbook' ) ) ) ;
handbookPrsMergedRecently = handbookPrsMergedRecently . concat ( nonPublicPrsClosedInThePastThreeWeeks . filter ( ( pr ) => ! pr . draft && _ . pluck ( pr . labels , 'name' ) . includes ( '#handbook' ) ) ) ;
2023-09-02 23:59:30 +00:00
2024-10-23 18:12:21 +00:00
let handbookPrOpenTime = handbookPrsMergedRecently . reduce ( ( avgDaysOpen , pr ) => {
2023-09-02 23:59:30 +00:00
let openedAt = new Date ( pr . created _at ) . getTime ( ) ;
let closedAt = new Date ( pr . closed _at ) . getTime ( ) ;
let daysOpen = Math . abs ( closedAt - openedAt ) / ONE _DAY _IN _MILLISECONDS ;
2024-10-23 18:12:21 +00:00
avgDaysOpen = avgDaysOpen + ( daysOpen / handbookPrsMergedRecently . length ) ;
2023-09-02 23:59:30 +00:00
sails . log . verbose ( 'Processing' , pr . head . repo . name , ':: #' + pr . number , 'open ' + daysOpen + ' days' , 'rolling avg now ' + avgDaysOpen ) ;
return avgDaysOpen ;
} , 0 ) ;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2024-03-04 20:26:00 +00:00
const kpiResults = [ ] ;
// NOTE: If order of the KPI sheets columns changes, the order values are pushed into this array needs to change, as well.
kpiResults . push (
averageDaysContributorPullRequestsAreOpenFor ,
2025-04-04 17:39:32 +00:00
averageNumberOfDaysBugsAreOpenFor ,
2025-04-04 18:12:36 +00:00
allBugs32DaysOrOlder . length ,
allBugsClosedInPastWeek . length ,
2025-05-27 15:29:36 +00:00
allBugsCreatedInPastWeek . length ,
2025-04-04 18:12:36 +00:00
allBugsReportedByCustomersInPastWeek . length , ) ;
2024-03-04 20:26:00 +00:00
2022-12-05 23:01:03 +00:00
// Log the results
sails . log ( `
2024-03-04 20:26:00 +00:00
CSV for copy - pasting into KPI spreadsheet :
2022-12-05 23:01:03 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -
2024-03-04 20:26:00 +00:00
$ { kpiResults . join ( ',' ) }
2022-12-05 23:01:03 +00:00
2024-09-03 15:52:38 +00:00
Note : Copy the values above , then paste into Google KPI sheet and select "Split text to columns" to split the values into separate columns .
2023-10-19 04:26:58 +00:00
2024-03-04 20:26:00 +00:00
Pull requests :
-- -- -- -- -- -- -- -- -- -- -- -- -- -
2025-04-04 17:39:32 +00:00
Average open time : $ { averageDaysContributorPullRequestsAreOpenFor } days .
2022-12-05 23:01:03 +00:00
2025-04-04 17:39:32 +00:00
Number of open pull requests in the fleetdm / fleet Github repo : $ { daysSinceContributorPullRequestsWereOpened . length }
2024-03-04 20:26:00 +00:00
2024-09-03 15:52:38 +00:00
Bugs :
2024-03-04 20:26:00 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -
Average open time ( all bugs ) : $ { averageNumberOfDaysBugsAreOpenFor } days .
2024-09-03 15:52:38 +00:00
Number of open issues with the "bug" label in fleetdm / fleet : $ { daysSinceBugsWereOpened . length }
2025-04-04 17:39:32 +00:00
Bugs older than 32 days : $ { allBugs32DaysOrOlder . length }
2023-12-11 20:38:51 +00:00
2025-04-04 17:39:32 +00:00
Bugs reported by customers in the past week : $ { allBugsReportedByCustomersInPastWeek . length }
2023-12-11 20:38:51 +00:00
2025-04-04 17:39:32 +00:00
Number of issues with the "bug" label closed in the past week : $ { allBugsClosedInPastWeek . length }
2022-12-05 23:01:03 +00:00
2025-04-04 17:39:32 +00:00
Number of issues with the "bug" label opened in the past week : $ { allBugsCreatedInPastWeek . length }
2022-12-05 23:01:03 +00:00
2024-10-23 18:12:21 +00:00
Handbook Pull requests
2023-09-02 23:59:30 +00:00
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
2024-10-23 18:12:21 +00:00
Number of open # handbook pull requests in the fleetdm Github org : $ { handbookOpenPrs . length }
2024-09-03 15:52:38 +00:00
2024-10-23 18:12:21 +00:00
Average open time ( # handbook PRs ) : $ { Math . round ( handbookPrOpenTime * 100 ) / 100 } days .
2023-09-02 23:59:30 +00:00
` );
2022-12-05 23:01:03 +00:00
}
} ;