forked from woocommerce/woocommerce
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add workflow to label community PRs and Issues. (woocommerce#34309)
* Add workflow to label community PRs and Issues. * Move most of the logic for the script to check for community contributors into a separate module
- Loading branch information
1 parent
852dc0c
commit dc2c7a4
Showing
2 changed files
with
152 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,60 @@ | ||
name: Add Community Label | ||
|
||
on: | ||
issues: | ||
types: [opened] | ||
pull_request_target: | ||
types: [opened] | ||
|
||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.ref }} | ||
cancel-in-progress: true | ||
|
||
jobs: | ||
verify: | ||
name: Verify | ||
runs-on: ubuntu-20.04 | ||
outputs: | ||
issueId: ${{ steps.check.outputs.issueId }} | ||
run: ${{ steps.check.outputs.run }} | ||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Check if user is a community contributor | ||
id: check | ||
uses: actions/github-script@v6 | ||
with: | ||
script: | | ||
const isCommunityContributor = require( './.github/workflows/scripts/is-community-contributor.js' ); | ||
const config = { | ||
types: [ 'pull_request', 'issue' ], | ||
orgs: [ 'woocommerce', 'automattic' ], | ||
}; | ||
( async ( { github, context, config } ) => { | ||
try { | ||
const isCommunity = await isCommunityContributor( { github, context, config } ); | ||
console.log( '::set-output name=run::%s', isCommunity ? 'true' : 'false' ); | ||
} catch ( e ) { | ||
console.log( '::set-output name=run::false' ); | ||
} | ||
} )( { github, context, config } ); | ||
label_community: | ||
name: Label Community Issues and PRs | ||
runs-on: ubuntu-20.04 | ||
needs: verify | ||
if: needs.verify.outputs.run == 'true' | ||
steps: | ||
- name: label | ||
uses: actions/github-script@v6 | ||
with: | ||
script: | | ||
github.rest.issues.addLabels( { | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
issue_number: context.issue.number, | ||
labels: [ 'type: community contribution' ] | ||
} ); |
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,92 @@ | ||
const getUserFromContext = ( types, context ) => { | ||
for ( const type of types ) { | ||
if ( context.payload[ type ] && context.payload[ type ].user && context.payload[ type ].user.login ) { | ||
return context.payload[ type ].user; | ||
} | ||
} | ||
return false; | ||
}; | ||
|
||
const userIsBot = user => user.type && user.type === 'Bot'; | ||
|
||
const userInOrg = async ( github, username, org ) => { | ||
console.log( 'Checking for user %s in org %s', username, org ); | ||
try { | ||
// First attempt to check memberships. | ||
const result = await github.request( 'GET /orgs/{org}/memberships/{username}', { | ||
org, | ||
username, | ||
} ); | ||
|
||
if ( result && Math.floor( result.status / 100 ) === 2 ) { | ||
console.log( 'User %s found in %s via membership check', username, org ); | ||
return true; | ||
} | ||
} catch ( error ) { | ||
// A 404 status means the user is definitively not in the organization. | ||
if ( error.status === 404 ) { | ||
console.log( 'User %s NOT found in %s via membership check', username, org ); | ||
return false; | ||
} | ||
|
||
try { | ||
// For anything else, we should also check public memberships. | ||
const result = await github.request( 'GET /orgs/{org}/public_members/{username}', { | ||
org, | ||
username, | ||
} ); | ||
|
||
if ( result && Math.floor( result.status / 100 ) === 2 ) { | ||
console.log( 'User %s found in %s via public membership check', username, org ); | ||
return true; | ||
} | ||
} catch ( error ) { | ||
if ( error.status === 404 ) { | ||
console.log( 'User %s NOT found in %s via public membership check', username, org ); | ||
return false; | ||
} | ||
} | ||
} | ||
// If we've gotten to this point, status is unknown. | ||
throw new Error( "Unable to determine user's membership in org" ); | ||
}; | ||
|
||
const userInNonCommunityOrgs = async ( orgs, github, username ) => { | ||
let checked = 0; | ||
for ( const org of orgs ) { | ||
try { | ||
const isUserInOrg = await userInOrg( github, username, org ); | ||
if ( isUserInOrg ) { | ||
return true; | ||
} | ||
// If no error was thrown, increment checked. | ||
checked++; | ||
} catch( e ) {} // Do nothing. | ||
} | ||
|
||
if ( checked !== orgs.length ) { | ||
throw new Error( "Unable to check user's membership in all orgs." ); | ||
} | ||
|
||
return false; | ||
}; | ||
|
||
const isCommunityContributor = async ( { github, context, config } ) => { | ||
const user = getUserFromContext( config.types, context ); | ||
if ( ! user ) { | ||
throw new Error( 'Unable to get user from context' ); | ||
} | ||
console.log( 'Checking user %s', user.login ); | ||
|
||
if ( userIsBot( user ) ) { | ||
// Bots are not community contributors. | ||
console.log( 'User detected as bot, skipping' ); | ||
return false; | ||
} | ||
|
||
const isInNonCommunityOrgs = await userInNonCommunityOrgs( config.orgs, github, user.login ); | ||
|
||
return ! isInNonCommunityOrgs; | ||
}; | ||
|
||
module.exports = isCommunityContributor; |