Add GitHub workflows - #2
byrichardpowell wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds GitHub automation to automatically close pull requests from external (non-Shopify) contributors, along with a pull request template that warns users before they create PRs.
- Adds a GitHub Actions workflow that checks PR author organization membership and automatically closes PRs from external contributors
- Adds a pull request template that warns users the repository doesn't accept external contributions
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| .github/workflows/close-external-prs.yml | Implements automated workflow to check Shopify organization membership and close external PRs with a helpful comment |
| .github/PULL_REQUEST_TEMPLATE.md | Adds template warning users that external PRs are not accepted and directing them to community forums |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| try { | ||
| // Check if the author is a member of the Shopify organization | ||
| await github.rest.orgs.checkMembershipForUser({ | ||
| org: 'Shopify', | ||
| username: author | ||
| }); | ||
|
|
||
| console.log(`${author} is a Shopify member`); | ||
| core.setOutput('is-shopify', 'true'); | ||
| } catch (error) { | ||
| console.log(`${author} is not a Shopify member or the check failed: ${error.message}`); |
There was a problem hiding this comment.
The checkMembershipForUser API call only succeeds if the organization membership is public or if the GitHub token has appropriate permissions to see private memberships. This could result in false negatives where actual Shopify members with private membership visibility have their PRs incorrectly closed. Consider either: (1) documenting that Shopify members must make their membership public, (2) using a different authentication method with org:read permissions, or (3) checking team membership instead if the repository has a specific team with access.
| try { | |
| // Check if the author is a member of the Shopify organization | |
| await github.rest.orgs.checkMembershipForUser({ | |
| org: 'Shopify', | |
| username: author | |
| }); | |
| console.log(`${author} is a Shopify member`); | |
| core.setOutput('is-shopify', 'true'); | |
| } catch (error) { | |
| console.log(`${author} is not a Shopify member or the check failed: ${error.message}`); | |
| const authorAssociation = context.payload.pull_request.author_association; | |
| // Treat PR authors with OWNER or MEMBER association as internal Shopify contributors. | |
| const internalAssociations = ['OWNER', 'MEMBER']; | |
| if (internalAssociations.includes(authorAssociation)) { | |
| console.log(`${author} is considered an internal Shopify member (association: ${authorAssociation})`); | |
| core.setOutput('is-shopify', 'true'); | |
| } else { | |
| console.log(`${author} is not considered an internal Shopify member (association: ${authorAssociation})`); |
| console.log(`${author} is not a Shopify member or the check failed: ${error.message}`); | ||
| core.setOutput('is-shopify', 'false'); |
There was a problem hiding this comment.
The error handling catches all exceptions and treats them as "not a Shopify member", which could include legitimate API failures, rate limiting errors, or permission issues. This means that during an API outage or rate limit scenario, all PRs (including those from Shopify members) would be closed. Consider checking the error type and only treating 404 (Not Found) as "not a member", while handling other errors (403, 500, rate limits) differently or failing the workflow.
| console.log(`${author} is not a Shopify member or the check failed: ${error.message}`); | |
| core.setOutput('is-shopify', 'false'); | |
| // Only treat 404 (Not Found) as "not a Shopify member". | |
| // For other errors (e.g., 403, 429, 500), fail the workflow to | |
| // avoid incorrectly closing PRs during API issues. | |
| if (error && error.status === 404) { | |
| console.log(`${author} is not a Shopify member (404): ${error.message}`); | |
| core.setOutput('is-shopify', 'false'); | |
| } else { | |
| core.setFailed(`Failed to check Shopify membership for ${author}: ${error && error.message ? error.message : error}`); | |
| } |
|
|
||
| We triage in the forums, not in this repo. PRs and issues here are closed without review. | ||
|
|
||
| For more details see CONTRIBUTING.md` |
There was a problem hiding this comment.
The reference to CONTRIBUTING.md should be formatted as a proper markdown link for consistency with the Pull Request Template, which uses a full URL. Consider changing this to a hyperlink format like: CONTRIBUTING.md or at minimum use a relative link format.
| For more details see CONTRIBUTING.md` | |
| For more details see [CONTRIBUTING.md](https://github.com/Shopify/shopify-app-python/blob/main/CONTRIBUTING.md)` |
| @@ -0,0 +1,64 @@ | |||
| name: Close External PRs | |||
|
|
|||
| on: | |||
There was a problem hiding this comment.
Using pull_request_target for external PRs is a known security risk as it runs with elevated permissions and access to secrets in the context of the base branch. While this workflow doesn't check out code, it's still important to ensure that no user-controlled input is used in potentially dangerous operations. The current implementation appears safe, but consider adding a comment documenting why pull_request_target is necessary here (to have write permissions for closing PRs) and confirming that no user input is used unsafely.
| on: | |
| on: | |
| # NOTE: We intentionally use pull_request_target here so this workflow has write | |
| # permissions to comment on and close PRs opened from forks/external contributors. | |
| # This workflow does NOT check out or execute code from the PR, and the only | |
| # user-controlled inputs used are the PR author login and PR number, which are | |
| # passed directly as identifiers to GitHub's REST API (no commands or secrets). |
No description provided.