-
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from jupyterlab/pr-script
Add a PR Script Workflow
- Loading branch information
Showing
3 changed files
with
136 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,39 @@ | ||
name: Run Script on PR | ||
on: | ||
workflow_dispatch: | ||
inputs: | ||
target: | ||
description: Target Pull Request Link | ||
required: true | ||
script: | ||
description: Command(s) to run | ||
required: false | ||
pre_commit: | ||
description: Whether to run the pre-commit script | ||
required: false | ||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v1 | ||
- name: Install Python | ||
uses: actions/setup-python@v1 | ||
with: | ||
python-version: "3.9" | ||
architecture: "x64" | ||
- name: Upgrade packaging dependencies | ||
run: | | ||
pip install --upgrade pip setuptools wheel --user | ||
- name: Install dependencies | ||
run: | | ||
pip install ghapi pre-commit | ||
- name: Run the script | ||
env: | ||
GITHUB_ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} | ||
MAINTAINER: ${{ github.actor }} | ||
TARGET: ${{ github.event.inputs.target }} | ||
SCRIPT: ${{ github.event.inputs.script }} | ||
PRE_COMMIT: ${{ github.event.inputs.pre_commit }} | ||
run: | | ||
python pr_script.py |
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 |
---|---|---|
@@ -1 +1,15 @@ | ||
# maintainer-tools | ||
|
||
## Workflows | ||
|
||
Workflows for use by maintainers. These should be run from your fork of this repository, with | ||
an [encrypted secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets) called | ||
`ACCESS_TOKEN` that is a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) with `repo` and `workflow` | ||
scopes. | ||
|
||
### PR Script | ||
|
||
The PR Script Workflow allows you to make a commit against a PR as a maintainer without having | ||
to check out the PR locally and push the change. The manual workflow takes as its inputs a link to the PR | ||
and a comma-separated list of quoted commands to run. As a convenience, you can also type "True" for the | ||
option to run pre-commit against the PR to fix up any pre-commit errors. |
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,83 @@ | ||
import json | ||
import os | ||
from pathlib import Path | ||
import re | ||
import shlex | ||
import shutil | ||
from subprocess import check_output, CalledProcessError, PIPE | ||
import sys | ||
|
||
from ghapi.core import GhApi | ||
|
||
|
||
def run(cmd, **kwargs): | ||
"""Run a command as a subprocess and get the output as a string""" | ||
if not kwargs.pop("quiet", False): | ||
print(f"+ {cmd}") | ||
else: | ||
kwargs.setdefault("stderr", PIPE) | ||
|
||
parts = shlex.split(cmd) | ||
if "/" not in parts[0]: | ||
executable = shutil.which(parts[0]) | ||
if not executable: | ||
raise CalledProcessError(1, f'Could not find executable "{parts[0]}"') | ||
parts[0] = executable | ||
|
||
try: | ||
return check_output(parts, **kwargs).decode("utf-8").strip() | ||
except CalledProcessError as e: | ||
print("output:", e.output.decode("utf-8").strip()) | ||
if e.stderr: | ||
print("stderr:", e.stderr.decode("utf-8").strip()) | ||
raise e | ||
|
||
|
||
def run_script(target, script): | ||
"""Run a script on the target pull request URL""" | ||
# e.g. https://github.com/foo/bar/pull/81 | ||
owner, repo = target.replace("https://github.com/", "").split('/')[:2] | ||
number = target.split("/")[-1] | ||
auth = os.environ['GITHUB_ACCESS_TOKEN'] | ||
gh = GhApi(owner=owner, repo=repo, token=auth) | ||
# here we get the target owner and branch so we can check it out below | ||
pull = gh.pulls.get(number) | ||
user_name = pull.head.repo.owner.login | ||
branch = pull.head.ref | ||
|
||
if Path("./test").exists(): | ||
shutil.rmtree("./test") | ||
run(f"git clone https://{maintainer}:{auth}@github.com/{user_name}/{repo} -b {branch} test") | ||
os.chdir("test") | ||
run("pip install -e '.[test]'") | ||
for cmd in script: | ||
try: | ||
run(cmd) | ||
except Exception: | ||
continue | ||
|
||
# Use email address for the GitHub Actions bot | ||
# https://github.community/t/github-actions-bot-email-address/17204/6 | ||
run( | ||
'git config user.email "41898282+github-actions[bot]@users.noreply.github.com"' | ||
) | ||
run('git config user.name "GitHub Action"') | ||
run(f"git commit -a -m 'Run maintainer script' -m 'by {maintainer}' -m '{json.dumps(script)}'") | ||
run(f"git push origin {branch}") | ||
|
||
|
||
if __name__ == '__main__': | ||
# https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#inputs | ||
target = os.environ.get('TARGET') | ||
maintainer = os.environ['MAINTAINER'] | ||
try: | ||
script = json.loads(os.environ.get('SCRIPT', '[]')) | ||
except Exception: | ||
script = os.environ.get('SCRIPT', []) | ||
if not isinstance(script, list): | ||
script = [script] | ||
if os.environ.get('PRE_COMMIT') == 'true': | ||
script += ['pre-commit run --all-files'] | ||
print(f'Running script on {target}:') | ||
print(f' {script}') | ||
run_script(target, script) |