-
Notifications
You must be signed in to change notification settings - Fork 7
Update check-cla
to customize which CLA repo to use
#91
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
Changes from all commits
ee9d811
ec9f0b4
a3dded4
6c83ecc
2b2d732
5361102
528c212
83dab52
b3b9940
1542cb4
261fc09
1928626
00ae91f
8eee82e
4729d17
b2db323
9bdcc51
43ec963
d8454ec
8ee0ba0
5ab5369
ebb06e5
a4ac617
4f67ffb
d66d4eb
71c4f55
6146699
5616a98
3c9f741
ae8e19e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,55 @@ | ||
# Check CLA (Contributor License Agreement) | ||
|
||
This is a custom GitHub action to be used in the conda GitHub organization | ||
for checking the conda contributor license agreement. | ||
A custom GitHub action to be used in the conda GitHub organization for checking the | ||
conda contributor license agreement. | ||
|
||
## GitHub Action Usage | ||
|
||
In your GitHub repository include the action in your workflows: | ||
|
||
```yaml | ||
name: Contributor license agreement (CLA) | ||
name: Check CLA | ||
|
||
on: | ||
issue_comment: | ||
types: | ||
- created | ||
types: [created] | ||
pull_request_target: | ||
types: | ||
- reopened | ||
- opened | ||
- synchronize | ||
|
||
jobs: | ||
check: | ||
if: >- | ||
!github.event.repository.fork | ||
&& ( | ||
( | ||
github.event.comment.body == '@conda-bot check' | ||
&& github.event.issue.pull_request | ||
|| github.event_name == 'pull_request_target' | ||
) | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Check CLA | ||
uses: conda/actions/check-cla | ||
- uses: conda/actions/check-cla | ||
with: | ||
# [required] | ||
# label to add when actor has signed the CLA | ||
label: cla-signed | ||
# A token with ability to comment, label, and modify the commit status | ||
# (`pull_request: write` and `statuses: write` for fine-grained PAT; `repo` for classic PAT) | ||
# (default: secrets.GITHUB_TOKEN) | ||
token: | ||
# [required] | ||
# the GitHub Personal Access Token to comment and label with | ||
token: ${{ secrets.CLA_ACTION_TOKEN }} | ||
# Label to apply to contributor's PR once CLA is singed | ||
label: | ||
|
||
# Upstream repository in which to create PR | ||
# (default: conda/infrastructure) | ||
cla_repo: | ||
# Path to the CLA signees file within the provided `cla_repo` | ||
# (default: .clabot) | ||
cla_path: | ||
|
||
# Fork of cla_repo in which to create branch | ||
# (default: conda-bot/infrastructure) | ||
cla_fork: | ||
# [required] | ||
# Token for opening singee PR in the provided `cla_repo` | ||
# (`pull_request: write` for fine-grained PAT; `repo` and `workflow` for classic PAT) | ||
cla_token: | ||
# Git-format author/committer to use for pull request commits | ||
# (default: Conda Bot <[email protected]>) | ||
cla_author: | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,161 +3,196 @@ name: CLA check | |
description: Reacts to new PRs and check if the contributor has previously signed the conda contributor license agreement (CLA). | ||
inputs: | ||
token: | ||
description: Token for commenting and labeling on contributor's PR | ||
required: true | ||
infrastructure_token: | ||
description: Token for opening singee PR in conda/infrastructure | ||
description: >- | ||
A token with ability to comment, label, and modify the commit status | ||
(`pull_request: write` and `statuses: write` for fine-grained PAT; `repo` for classic PAT) | ||
default: ${{ github.token }} | ||
required: true | ||
label: | ||
description: Label to apply to contributor's PR once CLA is singed | ||
required: true | ||
cla_repo: | ||
description: Upstream repository in which to create PR | ||
default: conda/infrastructure | ||
cla_path: | ||
description: Path to the CLA signees file within the provided `cla_repo` | ||
default: .clabot | ||
cla_fork: | ||
description: Fork of `cla_repo` in which to create branch | ||
default: conda-bot/infrastructure | ||
cla_token: | ||
description: >- | ||
Token for opening singee PR in `cla_fork` | ||
(`pull_request: write` for fine-grained PAT; `repo` and `workflow` for classic PAT) | ||
required: true | ||
cla_author: | ||
description: Git-format author/committer to use for pull request commits | ||
default: Conda Bot <[email protected]> | ||
|
||
runs: | ||
using: composite | ||
steps: | ||
- name: Get PR metadata | ||
id: pr | ||
uses: actions/github-script@v6 | ||
# if triggered by a comment, leave a reaction | ||
- name: React to comment | ||
uses: peter-evans/[email protected] | ||
if: github.event_name == 'issue_comment' | ||
with: | ||
script: | | ||
const { owner, repo, number } = context.issue; | ||
const pullRequest = await github.rest.pulls.get({ | ||
owner, | ||
repo, | ||
pull_number: number, | ||
}); | ||
console.log(pullRequest); | ||
const sha = pullRequest.data.head.sha; | ||
console.log(sha); | ||
core.setOutput('sha', sha); | ||
|
||
const labels = pullRequest.data.labels.map(label => label.name) | ||
console.log(labels); | ||
core.setOutput('labels', labels); | ||
|
||
const hasLabel = labels.includes('${{ inputs.label }}') | ||
console.log(hasLabel); | ||
core.setOutput('hasLabel', hasLabel); | ||
token: ${{ inputs.token }} | ||
comment-id: ${{ github.event.comment.id }} | ||
reactions: eyes | ||
|
||
# commit status → pending | ||
- name: Set commit status with pending | ||
uses: dholth/github-status-action@runs-using-node16 | ||
uses: conda/actions/set-commit-status@customize-cla-repo | ||
with: | ||
authToken: ${{ inputs.token }} | ||
token: ${{ inputs.token }} | ||
context: CLA check | ||
description: Checking conda CLA... | ||
state: pending | ||
sha: ${{ steps.pr.outputs.sha || github.sha }} | ||
|
||
- name: Check if current actor has signed | ||
# has_label, number, contributor, url, has_signed | ||
- name: Collect PR metadata | ||
uses: actions/github-script@v6 | ||
id: contributors | ||
id: metadata | ||
with: | ||
github-token: ${{ inputs.token }} | ||
script: | | ||
console.log(context); | ||
const getContributors = async () => { | ||
try { | ||
const results = ( | ||
await github.rest.repos.getContent({ | ||
owner: 'conda', | ||
repo: 'infrastructure', | ||
path: '.clabot' | ||
}) | ||
); | ||
return JSON.parse(Buffer.from(results.data.content, results.data.encoding).toString('utf-8')).contributors; | ||
} catch (err) { | ||
core.error(`Could not retrieve contributors, returning undefined. Reason: ${err}`) | ||
return undefined; | ||
} | ||
} | ||
const contributors = await getContributors(); | ||
console.log(contributors); | ||
const pull_request = (context.payload.issue || context.payload.pull_request || context.payload); | ||
const creator = pull_request.user.login; | ||
console.log(creator); | ||
const hasSigned = contributors.includes(creator); | ||
console.log(hasSigned); | ||
core.setOutput('contributors', contributors); | ||
core.setOutput('hasSigned', hasSigned); | ||
|
||
# add [cla-signed] label if actor has already signed | ||
- name: Add label | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we add this to a separate javascript file? This is started to get long enough to warrant that, imo. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Specifying these JS blocks in separate files requires:
My problem with this is the checkout step which will make it harder to version our actions. There's an open request for something better: actions/github-script#326 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a common pattern for actions to include all JS inlined, even if it's not great, let's follow that for now |
||
const { owner, repo, number } = context.issue; | ||
core.debug(`owner: ${owner}`); | ||
core.debug(`repo: ${repo}`); | ||
core.setOutput('number', number); | ||
core.debug(`number: ${number}`); | ||
|
||
const raw = await github.rest.pulls.get({ | ||
owner: owner, | ||
repo: repo, | ||
pull_number: number | ||
}); | ||
const labels = raw.data.labels.map(label => label.name); | ||
core.debug(`labels: ${labels}`); | ||
|
||
const has_label = labels.includes('${{ inputs.label }}'); | ||
core.setOutput('has_label', has_label); | ||
core.debug(`has_label: ${has_label}`); | ||
|
||
const { content, encoding } = (await github.rest.repos.getContent({ | ||
owner: owner, | ||
repo: repo, | ||
path: '${{ inputs.cla_path }}' | ||
})).data; | ||
const contributors = JSON.parse( | ||
Buffer.from(content, encoding).toString('utf-8') | ||
).contributors; | ||
core.debug(`contributors: ${contributors}`); | ||
|
||
const payload = context.payload.issue || context.payload.pull_request || context.payload; | ||
const contributor = payload.user.login; | ||
core.setOutput('contributor', contributor); | ||
core.debug(`contributor: ${contributor}`); | ||
|
||
const url = payload.html_url; | ||
core.setOutput('url', url); | ||
core.debug(`url: ${url}`); | ||
|
||
const has_signed = contributors.includes(contributor); | ||
core.setOutput('has_signed', has_signed); | ||
core.debug(`has_signed: ${has_signed}`); | ||
Comment on lines
+53
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved Flattened logic since the error catching doesn't actually do anything special. Removes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good changes! |
||
|
||
# if contributor has already signed, add [cla-signed] label | ||
- name: Add label to PR | ||
uses: actions-ecosystem/[email protected] | ||
if: steps.contributors.outputs.hasSigned == 'true' && steps.pr.outputs.hasLabel == 'false' | ||
if: steps.metadata.outputs.has_signed == 'true' && steps.metadata.outputs.has_label == 'false' | ||
with: | ||
github_token: ${{ inputs.token }} | ||
labels: ${{ inputs.label }} | ||
|
||
# remove [cla-signed] label if actor has not signed yet | ||
- name: Remove label | ||
# if contributor has not signed yet, remove [cla-signed] label | ||
- name: Remove label to PR | ||
uses: actions-ecosystem/[email protected] | ||
if: steps.contributors.outputs.hasSigned == 'false' && steps.pr.outputs.hasLabel == 'true' | ||
if: steps.metadata.outputs.has_signed == 'false' && steps.metadata.outputs.has_label == 'true' | ||
with: | ||
github_token: ${{ inputs.token }} | ||
labels: ${{ inputs.label }} | ||
|
||
# checkout conda/infrastructure to update .clabot | ||
- uses: actions/checkout@v3 | ||
if: steps.contributors.outputs.hasSigned == 'false' | ||
# if unsigned, checkout cla_repo | ||
- name: Clone CLA singee repo | ||
uses: actions/checkout@v3 | ||
if: steps.metadata.outputs.has_signed == 'false' | ||
with: | ||
repository: conda/infrastructure | ||
repository: ${{ inputs.cla_repo }} | ||
|
||
# update .clabot | ||
- shell: python | ||
if: steps.contributors.outputs.hasSigned == 'false' | ||
# if unsigned, update cla_path | ||
- name: Add contributor as a CLA signee | ||
shell: python | ||
if: steps.metadata.outputs.has_signed == 'false' | ||
run: | | ||
import json | ||
from pathlib import Path | ||
|
||
path = Path(".clabot") | ||
clabot = json.loads(path.read_text()) | ||
clabot["contributors"].append("${{ github.actor }}") | ||
clabot["contributors"].sort() | ||
path.write_text(json.dumps(clabot)) | ||
|
||
# create PR | ||
- uses: peter-evans/create-pull-request@v4 | ||
id: cla-pr | ||
if: steps.contributors.outputs.hasSigned == 'false' | ||
path = Path("${{ inputs.cla_path }}") | ||
signees = json.loads(path.read_text()) | ||
signees["contributors"].append("${{ steps.metadata.outputs.contributor }}") | ||
signees["contributors"].sort() | ||
path.write_text(json.dumps(signees, indent=2)) | ||
|
||
# if unsigned, create PR | ||
- name: Create PR with new CLA signee | ||
uses: peter-evans/create-pull-request@v4 | ||
id: pull | ||
if: steps.metadata.outputs.has_signed == 'false' | ||
with: | ||
token: ${{ inputs.infrastructure_token }} | ||
branch: cla-${{ github.actor }} | ||
commit-message: Adding CLA singee ${{ github.actor }} | ||
title: Adding CLA singee ${{ github.actor }} | ||
push-to-fork: ${{ inputs.cla_fork }} | ||
token: ${{ inputs.cla_token }} | ||
branch: cla-${{ steps.metadata.outputs.contributor }} | ||
delete-branch: true | ||
commit-message: Adding CLA singee ${{ steps.metadata.outputs.contributor }} | ||
author: ${{ inputs.cla_author }} | ||
committer: ${{ inputs.cla_author }} | ||
title: Adding CLA singee ${{ steps.metadata.outputs.contributor }} | ||
body: | | ||
Adding CLA signee @${{ github.actor }} | ||
Adding CLA signee @${{ steps.metadata.outputs.contributor }} | ||
|
||
Xref ${{ github.event.pull_request.url }} | ||
Xref ${{ steps.metadata.outputs.url }} | ||
|
||
# create sticky comment if not signed | ||
- name: Create comment | ||
# if unsigned, create sticky comment | ||
- name: Create comment regarding missing CLA signature | ||
uses: marocchino/sticky-pull-request-comment@v2 | ||
if: steps.contributors.outputs.hasSigned == 'false' | ||
if: steps.metadata.outputs.has_signed == 'false' | ||
with: | ||
number: context.issue.number | ||
message: | | ||
We require contributors to sign our [Contributor License Agreement](https://conda.io/en/latest/contributing.html#conda-contributor-license-agreement) and we don't have one on file for @${{ github.event.pull_request.user.login }}. | ||
number: ${{ steps.metadata.outputs.number }} | ||
# GitHub flavored markdown reinvents how paragraphs work, adjoined lines of text are not | ||
# concatenated so instead we rely on YAML multi-line + extra newlines | ||
message: >- | ||
[cla]: https://conda.io/en/latest/contributing.html#conda-contributor-license-agreement | ||
|
||
|
||
We require contributors to sign our [Contributor License Agreement][cla] and we don't | ||
have one on file for @${{ steps.metadata.outputs.contributor }}. | ||
|
||
|
||
In order for us to review and merge your code, please e-sign the [Contributor License Agreement PDF](https://conda.io/en/latest/contributing.html#conda-contributor-license-agreement). We then need to manually verify your signature, merge the PR (${{ steps.cla-pr.outputs.pull-request-url }}), and ping the bot to refresh the PR. | ||
In order for us to review and merge your code, please e-sign the | ||
[Contributor License Agreement PDF][cla]. We then need to manually verify your | ||
signature, merge the PR (${{ steps.pull.outputs.pull-request-url }}), and ping the bot | ||
to refresh the PR. | ||
Comment on lines
+165
to
+176
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kathatherine this is the updated missing CLA message, is this good enough in light of conda/conda-docs#854 To see an example of this in action see conda-sandbox/test-check-cla#45 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hopefully the PR link will help with anyone who just reads to the CLA signing link and stops (like what happened with some of the PyCon sprinters I was working with). Maybe two links will encourage finishing the paragraph. :P This works for me. |
||
GITHUB_TOKEN: ${{ inputs.token }} | ||
|
||
# commit status → error | ||
- name: Set commit status to error | ||
if: steps.contributors.outputs.hasSigned == 'false' | ||
uses: dholth/github-status-action@runs-using-node16 | ||
if: steps.metadata.outputs.has_signed == 'false' | ||
uses: conda/actions/set-commit-status@customize-cla-repo | ||
with: | ||
authToken: ${{ inputs.token }} | ||
token: ${{ inputs.token }} | ||
context: CLA check | ||
description: Please follow the details link to sign the conda CLA. → | ||
state: error | ||
sha: ${{ steps.pr.outputs.sha || github.sha }} | ||
target_url: https://conda.io/en/latest/contributing.html#conda-contributor-license-agreement | ||
|
||
# commit status → success | ||
- name: Set commit status to success | ||
if: steps.contributors.outputs.hasSigned == 'true' | ||
uses: dholth/github-status-action@runs-using-node16 | ||
if: steps.metadata.outputs.has_signed == 'true' | ||
uses: conda/actions/set-commit-status@customize-cla-repo | ||
with: | ||
authToken: ${{ inputs.token }} | ||
token: ${{ inputs.token }} | ||
context: CLA check | ||
description: CLA signed, thank you! | ||
state: success | ||
sha: ${{ steps.pr.outputs.sha || github.sha }} |
Uh oh!
There was an error while loading. Please reload this page.