Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a GitHub project badge for Hacktoberfest [GithubHacktoberfest] (#…
- Loading branch information
1 parent
33a6c53
commit 76cb943
Showing
2 changed files
with
234 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
'use strict' | ||
|
||
const gql = require('graphql-tag') | ||
const Joi = require('@hapi/joi') | ||
const moment = require('moment') | ||
const { metric, maybePluralize } = require('../text-formatters') | ||
const { nonNegativeInteger } = require('../validators') | ||
const { GithubAuthV4Service } = require('./github-auth-service') | ||
const { | ||
documentation: githubDocumentation, | ||
transformErrors, | ||
} = require('./github-helpers') | ||
|
||
const documentation = ` | ||
<p> | ||
This badge is designed for projects hosted on GitHub which are | ||
participating in | ||
<a href="https://hacktoberfest.digitalocean.com">Hacktoberfest</a>, | ||
an initiative to encourage participating in open-source projects. The | ||
badge can be added to the project readme to encourage potential | ||
contributors to review the suggested issues and to celebrate the | ||
contributions that have already been made. | ||
The badge displays three pieces of information: | ||
<ul> | ||
<li> | ||
The number of suggested issues. By default this will count open | ||
issues with the <strong>hacktoberfest</strong> label, however you | ||
can pick a different label (e.g. | ||
<code>?suggestion_label=good%20first%20issue</code>). | ||
</li> | ||
<li> | ||
The number of pull requests opened in October. This excludes any | ||
PR with the <strong>invalid</strong> label. | ||
</li> | ||
<li>The number of days left of October.</li> | ||
</ul> | ||
</p> | ||
${githubDocumentation} | ||
` | ||
|
||
const schema = Joi.object({ | ||
data: Joi.object({ | ||
repository: Joi.object({ | ||
issues: Joi.object({ | ||
totalCount: nonNegativeInteger, | ||
}).required(), | ||
}).required(), | ||
search: Joi.object({ | ||
issueCount: nonNegativeInteger, | ||
}).required(), | ||
}).required(), | ||
}).required() | ||
|
||
const queryParamSchema = Joi.object({ | ||
suggestion_label: Joi.string(), | ||
}).required() | ||
|
||
module.exports = class GithubHacktoberfestCombinedStatus extends GithubAuthV4Service { | ||
static get category() { | ||
return 'issue-tracking' | ||
} | ||
|
||
static get route() { | ||
return { | ||
base: 'github/hacktoberfest/2019', | ||
pattern: ':user/:repo', | ||
queryParamSchema, | ||
} | ||
} | ||
|
||
static get examples() { | ||
return [ | ||
{ | ||
title: 'GitHub Hacktoberfest combined status', | ||
namedParams: { | ||
user: 'snyk', | ||
repo: 'snyk', | ||
}, | ||
staticPreview: this.render({ | ||
suggestedIssueCount: 12, | ||
contributionCount: 8, | ||
daysLeft: 15, | ||
}), | ||
documentation, | ||
}, | ||
{ | ||
title: | ||
'GitHub Hacktoberfest combined status (suggestion label override)', | ||
namedParams: { | ||
user: 'tmrowco', | ||
repo: 'tmrowapp-contrib', | ||
}, | ||
queryParams: { | ||
suggestion_label: 'help wanted', | ||
}, | ||
staticPreview: this.render({ | ||
suggestedIssueCount: 12, | ||
contributionCount: 8, | ||
daysLeft: 15, | ||
}), | ||
documentation, | ||
}, | ||
] | ||
} | ||
|
||
static get defaultBadgeData() { | ||
return { | ||
label: 'hacktoberfest', | ||
color: 'orange', | ||
} | ||
} | ||
|
||
static render({ suggestedIssueCount, contributionCount, daysLeft }) { | ||
if (daysLeft === undefined) { | ||
// The global cutoff time is 11/1 noon UTC. | ||
// https://github.com/badges/shields/pull/4109#discussion_r330782093 | ||
// We want to show "1 day left" on the last day so we add 1. | ||
daysLeft = moment('2019-11-01 12:00:00 Z').diff(moment(), 'days') + 1 | ||
} | ||
|
||
const message = | ||
[ | ||
suggestedIssueCount | ||
? `${metric(suggestedIssueCount)} ${maybePluralize( | ||
'open issue', | ||
suggestedIssueCount | ||
)}` | ||
: '', | ||
contributionCount | ||
? `${metric(contributionCount)} ${maybePluralize( | ||
'PR', | ||
contributionCount | ||
)}` | ||
: '', | ||
daysLeft > 0 | ||
? `${daysLeft} ${maybePluralize('day', daysLeft)} left` | ||
: '', | ||
] | ||
.filter(Boolean) | ||
.join(', ') || 'is done!' | ||
|
||
return { message } | ||
} | ||
|
||
async fetch({ user, repo, suggestionLabel = 'hacktoberfest' }) { | ||
const isValidOctoberPR = [ | ||
`repo:${user}/${repo}`, | ||
'is:pr', | ||
'created:2019-10-01..2019-10-31', | ||
`-label:invalid`, | ||
] | ||
.filter(Boolean) | ||
.join(' ') | ||
const { | ||
data: { | ||
repository: { | ||
issues: { totalCount: suggestedIssueCount }, | ||
}, | ||
search: { issueCount: contributionCount }, | ||
}, | ||
} = await this._requestGraphql({ | ||
query: gql` | ||
query( | ||
$user: String! | ||
$repo: String! | ||
$suggestionLabel: String! | ||
$isValidOctoberPR: String! | ||
) { | ||
repository(owner: $user, name: $repo) { | ||
issues(states: [OPEN], labels: [$suggestionLabel]) { | ||
totalCount | ||
} | ||
} | ||
search(query: $isValidOctoberPR, type: ISSUE, last: 0) { | ||
issueCount | ||
} | ||
} | ||
`, | ||
variables: { | ||
user, | ||
repo, | ||
suggestionLabel, | ||
isValidOctoberPR, | ||
}, | ||
schema, | ||
transformErrors, | ||
}) | ||
return { | ||
suggestedIssueCount, | ||
contributionCount, | ||
} | ||
} | ||
|
||
async handle({ user, repo }, { suggestion_label: suggestionLabel }) { | ||
const { suggestedIssueCount, contributionCount } = await this.fetch({ | ||
user, | ||
repo, | ||
suggestionLabel, | ||
}) | ||
return this.constructor.render({ | ||
suggestedIssueCount, | ||
contributionCount, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
'use strict' | ||
|
||
const Joi = require('@hapi/joi') | ||
const t = (module.exports = require('../tester').createServiceTester()) | ||
|
||
const isHactoberfestCombinedStatus = Joi.string().regex( | ||
/^[0-9]+ open issues?(, [0-9]+ PRs?)?(, [0-9]+ days? left)?$/ | ||
) | ||
|
||
t.create('GitHub Hacktoberfest combined status') | ||
.get('/badges/shields.json') | ||
.expectBadge({ | ||
label: 'hacktoberfest', | ||
message: isHactoberfestCombinedStatus, | ||
}) | ||
|
||
t.create('GitHub Hacktoberfest combined status (suggestion label override)') | ||
.get( | ||
`/badges/shields.json?suggestion_label=${encodeURIComponent( | ||
'good first issue' | ||
)}` | ||
) | ||
.expectBadge({ | ||
label: 'hacktoberfest', | ||
message: isHactoberfestCombinedStatus, | ||
}) |