Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/comment_pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Comment on pull request post pull request auto review
# depends on and only executes after pull_request_processor.yml is complete

on:
workflow_run:
workflows: ["Checking CLA Signature"]
types:
- completed

jobs:
one:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8]
if: >
${{ github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Print github contexts
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: |
ls
echo "GH context:"
echo "$GITHUB_CONTEXT"
- name: 'Download artifact'
uses: actions/github-script@v3.1.0
with:
script: |
var artifacts = await github.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{github.event.workflow_run.id }},
});
console.log(artifacts);
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "prcontext"
})[0];
var download = await github.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
var fs = require('fs');
fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data));
- run: |
ls
unzip pr.zip
cat ./comment

- name: 'Comment on Current Pull Request'
uses: actions/github-script@v3
with:
github-token: ${{ secrets.COMMENT_BOT_TOKEN }}
script: |
var fs = require('fs');
var issue_number = Number(fs.readFileSync('./PRNumber'));
var comment = String(fs.readFileSync('./comment'));
await github.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: comment
});
177 changes: 177 additions & 0 deletions .github/workflows/python/verify_cla_signature_pr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import os
import sys
import requests
import json
import subprocess
import re


print("current working directory is: ", os.getcwd())
STATUS_FAILED = 'FAILED'
SUCCESS_MESSAGE = 'ok'


def get_github_details():
github_info_file = open('./.tmp/github.json', 'r')
return json.load(github_info_file)


def get_commit_details():
commit_info_file = open('./.tmp/commitDetails.json', 'r')
return json.load(commit_info_file)


def process_git_local_details():
# Check if current dir is git dir
is_git_dir = subprocess.check_output(
['/usr/bin/git', 'rev-parse', '--is-inside-work-tree']).decode('utf-8')
print("Is git dir: ", is_git_dir)

# git status
git_status = subprocess.check_output(
['/usr/bin/git', 'status']).decode('utf-8')
print("Git status: ", git_status)

# last n commits
last_10_commit_list = subprocess.check_output(
['/usr/bin/git', 'rev-list', '--max-count=10', 'HEAD']).decode('utf-8')
print("last 10 commit ids are: ", last_10_commit_list)

return {
'is_git_dir': is_git_dir,
'last_10_commit_list': last_10_commit_list
}


def collect_pr_details():
github = get_github_details()
commits = get_commit_details()
git_local = process_git_local_details()
return {
'github': github,
'commits': commits,
'num_commits_in_pr': len(commits),
'event_name': github["event_name"],
'pr_submitter_github_login': github['event']['pull_request']['user']['login'],
'github_repo': github['repository'],
'pr_number': github['event']['number'],
'is_git_dir': git_local['is_git_dir'],
'last_10_commit_list': git_local['last_10_commit_list'],
}


def write_comment(comment):
print(comment)
f = open("./.tmp/comment", "a")
f.write(comment)
f.write("\n")
f.close()


def task_failed(comment):
f = open("./.tmp/failed", "a")
f.write(comment)
f.write("\n")
f.close()
write_comment(comment)
return STATUS_FAILED


def extract_personal_contributer_details():
personal_cla_link = sys.argv[1]
f = requests.get(personal_cla_link)
personal_cla_contents = f.text

personal_contributers_regex = re.compile('\| *\[([^\s]+)\]\([^\s]+\) *\|')
personal_contributers = personal_contributers_regex.findall(
personal_cla_contents)

return personal_contributers


def extract_employer_contributer_details():
employer_cla_link = sys.argv[2]
f = requests.get(employer_cla_link)
employer_cla_contents = f.text

employer_contributers_regex = re.compile('\| *\[([^\s]+)\]\([^\s]+\) *\|')
employer_contributers = employer_contributers_regex.findall(
employer_cla_contents)

return employer_contributers


def validate_is_pull_request(pr_details):
print('Validate pull request called')
github_details = pr_details['github']
if github_details["event_name"] != "pull_request":
print("Error! This operation is valid on github pull requests. Exiting. Event received: ",
github_details["event_name"])
sys.exit(1)


def validate_pr_raiser_cla(pr_raiser_login, employer_contributors, personal_contributors):
print('PR raiser login: ' + pr_raiser_login)
if pr_raiser_login not in employer_contributors and pr_raiser_login not in personal_contributors:
return task_failed('### Error: Contributor Licence Agreement Signature Missing\n' +
'Please sign the Contributor Licence Agreement by clicking the following link.\n' +
'<p align="center"> <a href="https://phcode-dev.github.io/contributor-license-agreement/">Click here to sign the CLA</a></p>'
)
print('Pass: Pull request raiser has signed the Contributor Licence Agreement')
return SUCCESS_MESSAGE


def validate_commiters_cla(commits, employer_contributors, personal_contributors):
# github logins of all committers
commit_logins = []
for commit in commits:
commiter_github_login = commit['author']['login']
if commiter_github_login not in commit_logins:
commit_logins.append(commiter_github_login)
print("All github users who made changes to the pull request: ", commit_logins)

unauthorized_commiters = []
for user in commit_logins:
if user not in personal_contributors and user not in employer_contributors:
unauthorized_commiters.append(user)
if len(unauthorized_commiters) != 0:
return task_failed('### Error: Contributor Licence Agreement Signature Missing\n' +
'The following commiter(s) has not signed the Contributor Licence Agreement:\n' +
', '.join(unauthorized_commiters) + '\n' +
'Please sign the Contributor Licence Agreement by clicking the following link. \n' +
'<p align="center"> <a href="https://phcode-dev.github.io/contributor-license-agreement/">Click here to sign the CLA</a></p>'
)

print('Pass: All the commiters have signed the Contributor Licence Agreement')
return SUCCESS_MESSAGE


def validate_cla_signature(pr_raiser_login, commits):
employer_contributors = extract_employer_contributer_details()
personal_contributors = extract_personal_contributer_details()

PR_RAISER_CLA_VALIDATION = validate_pr_raiser_cla(pr_raiser_login, employer_contributors, personal_contributors)
COMMITERS_CLA_VALIDATION = validate_commiters_cla(commits, employer_contributors, personal_contributors)

if PR_RAISER_CLA_VALIDATION == STATUS_FAILED or COMMITERS_CLA_VALIDATION == STATUS_FAILED:
return STATUS_FAILED

return SUCCESS_MESSAGE


def review_pr():
print('Reviewing PR')
pr_details = collect_pr_details()
validate_is_pull_request(pr_details)
CLA_SIGNATURE_VALIDATION = validate_cla_signature(pr_details['pr_submitter_github_login'], pr_details['commits'])

if CLA_SIGNATURE_VALIDATION == STATUS_FAILED:
print('Validations failed. Exiting!')
return

write_comment('\n## Thank You for making this pull request.')


review_pr()

# assert validate_cla_signature('psdhanesh7') == SUCCESS_MESSAGE
74 changes: 74 additions & 0 deletions .github/workflows/verify_cla_signature_pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Checking CLA Signature
# https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context
# The pull request target event provides RW token to github
# https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/
# But `on: pull_request_target` should be avoided due to security
# reasons. Read more: [SEC_ADV_1] https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
#
# We will use a mix of github pull_request that does not provide any write access to pull requests on forks
# and workflow event, as discussed in [SEC_ADV_1]

on:
pull_request:
branches:
- main

jobs:
one:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.8]

steps:
- uses: actions/checkout@v2
with:
fetch-depth: 100
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Setting up prerequisites
run: |
mkdir ./.tmp
pip3 install requests
- name: Getting commit details
uses: wei/wget@v1
with:
args: -O ./.tmp/commitDetails.json ${{ toJSON(github.event.pull_request._links.commits.href) }}
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: |
echo "$GITHUB_CONTEXT" > ./.tmp/github.json
echo ${{ github.event.number }} > ./.tmp/PRNumber
cat ./.tmp/github.json
echo "commit details: "
cat ./.tmp/commitDetails.json

- name: Review pull request
run: |
which git
if ! python ./.github/workflows/python/verify_cla_signature_pr.py $PERSONAL_CLA_LINK $EMPLOYER_CLA_LINK; then
echo "Pull request details could not be extracted"
exit 1
else
echo "all good"
fi
env:
EMPLOYER_CLA_LINK: https://raw.githubusercontent.com/phcode-dev/contributor-license-agreement/main/employer_contributor_license_agreement.md
PERSONAL_CLA_LINK: https://raw.githubusercontent.com/phcode-dev/contributor-license-agreement/main/personal_contributor_licence_agreement.md

- uses: actions/upload-artifact@v2
with:
name: prcontext
path: .tmp/

- name: Fail on validation errors
run: |
FILE=./.tmp/failed
if test -f "$FILE"; then
echo "Validation failed. Reason:"
cat ./.tmp/failed
exit 1
fi