Skip to content
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

Bedevere as GH Actions #568

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
383afcf
Bedevere as GitHub Actions
Mariatta Jun 12, 2023
6f5ecee
Set the python-version
Mariatta Jun 12, 2023
3c8d9a9
Create Dockerfile
Mariatta Jun 12, 2023
6c0af43
use setup actions v3 and Python 3.11
Mariatta Jun 12, 2023
dc5bd1d
Update Dockerfile and copy the bedevere directory
Mariatta Jun 13, 2023
1ad09f8
File formatting. Empty line at the end of the file.
Mariatta Jun 13, 2023
c8afadd
Typo in Dockerfile
Mariatta Jun 13, 2023
3250100
RUN dir -s
Mariatta Jun 13, 2023
d562d69
Copy to GitHub workspace directory
Mariatta Jun 13, 2023
e6d77d6
Create entrypoint.sh
Mariatta Jun 13, 2023
e9c572e
Entrypoint was in wrong dir
Mariatta Jun 13, 2023
7d0c3a7
Typo in entrypoint filename
Mariatta Jun 13, 2023
ae8aaab
Make entrypoint executable
Mariatta Jun 13, 2023
494aba6
Copy bedevere to GitHub workspace
Mariatta Jun 13, 2023
5cf8707
Copy bedevere folder
Mariatta Jun 13, 2023
17e9c42
Copy bedevere folder
Mariatta Jun 13, 2023
cde6ac1
debug 400 error
Mariatta Jun 13, 2023
4e267ef
debug gh token
Mariatta Jun 13, 2023
4b385c8
Set the GH Token env var
Mariatta Jun 13, 2023
d9d8c6c
Revert changes
Mariatta Jun 13, 2023
484ece3
Remove debug prints
Mariatta Jun 14, 2023
db8e6cb
Remove Procfile
Mariatta Jun 14, 2023
3f9ea88
Remove runtime.txt. It was for Heroku.
Mariatta Jun 14, 2023
784a19c
Set exit code if bedevere errors out.
Mariatta Jun 14, 2023
9dac39a
Debugging
Mariatta Jun 14, 2023
424509d
Debugging
Mariatta Jun 14, 2023
6fa714a
Debugging
Mariatta Jun 14, 2023
9d1a990
Use repo_full_name when retrieving Git Hash
Mariatta Jun 14, 2023
f59a006
Fix tests
Mariatta Jun 14, 2023
05b6aa1
Remove prints
Mariatta Jun 14, 2023
4834a6f
Debug post status
Mariatta Jun 14, 2023
8ec31af
Debug post status
Mariatta Jun 14, 2023
67849cf
Debug post status
Mariatta Jun 14, 2023
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
49 changes: 49 additions & 0 deletions .github/workflows/bedevere.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Bedevere

on:
pull_request:
types:
- opened
- synchronize
- reopened
- edited
Mariatta marked this conversation as resolved.
Show resolved Hide resolved
- labeled
- unlabeled
- review_requested
- ready_for_review
- converted_to_draft
- closed
create:
types:
- branch
pull_request_review:
types:
- submitted
- dismissed
issue_comment:
types:
- created
push:

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ "3.11" ]
steps:
- name: Check out repo
uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
- name: execute bedevere
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python -m bedevere
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3.11-slim

COPY requirements.txt requirements.txt
COPY dev-requirements.txt dev-requirements.txt
Mariatta marked this conversation as resolved.
Show resolved Hide resolved


COPY entrypoint.sh /entrypoint.sh
COPY bedevere/ /bedevere/

RUN pip install --no-cache-dir -U pip
RUN pip install --no-cache-dir -r requirements.txt

ENTRYPOINT ["/entrypoint.sh"]
1 change: 0 additions & 1 deletion Procfile

This file was deleted.

10 changes: 10 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: bedevere-action
description: Bedevere GitHub Actions

branding:
color: blue
icon: activity
runs:
using: docker
image: Dockerfile
39 changes: 20 additions & 19 deletions bedevere/__main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import asyncio
import importlib

import os
import sys
import traceback
Expand All @@ -10,27 +10,30 @@
from gidgethub import aiohttp as gh_aiohttp
from gidgethub import routing
from gidgethub import sansio
from gidgethub import actions

from . import backport, gh_issue, close_pr, filepaths, news, stage

import sentry_sdk
# import sentry_sdk
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we still use sentry in GitHub Actions? Is it needed? If not we can remove it from requirements.txt


router = routing.Router(backport.router, gh_issue.router, close_pr.router,
filepaths.router, news.router,
stage.router)
cache = cachetools.LRUCache(maxsize=500)

sentry_sdk.init(os.environ.get("SENTRY_DSN"))
# sentry_sdk.init(os.environ.get("SENTRY_DSN"))

async def main(request):
async def main(event_payload):
try:
body = await request.read()
secret = os.environ.get("GH_SECRET")
event = sansio.Event.from_http(request.headers, body, secret=secret)
event_name = os.environ["GITHUB_EVENT_NAME"]
job_id = os.environ["GITHUB_JOB"]
event = sansio.Event(event_payload, event=event_name, delivery_id=job_id)
print(f"{event.data=}")
print(f"{event_payload}")
print(f"{event_name=}")
print('GH delivery ID', event.delivery_id, file=sys.stderr)
if event.event == "ping":
return web.Response(status=200)
oauth_token = os.environ.get("GH_AUTH")

oauth_token = os.environ.get("GITHUB_TOKEN")
async with aiohttp.ClientSession() as session:
gh = gh_aiohttp.GitHubAPI(session, "python/bedevere",
oauth_token=oauth_token,
Expand All @@ -42,16 +45,14 @@ async def main(request):
print('GH requests remaining:', gh.rate_limit.remaining)
except AttributeError:
pass
return web.Response(status=200)

except Exception as exc:
traceback.print_exc(file=sys.stderr)
return web.Response(status=500)

sys.exit(1)

if __name__ == "__main__": # pragma: no cover
app = web.Application()
app.router.add_post("/", main)
port = os.environ.get("PORT")
if port is not None:
port = int(port)
web.run_app(app, port=port)
if os.environ.get("GITHUB_EVENT_PATH"):
event_from = actions.event()
asyncio.run(main(event_from))
else:
print(f"Environment Variable 'GITHUB_EVENT_PATH' not found.")
1 change: 1 addition & 0 deletions bedevere/news.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ async def check_news(gh, pull_request, files=None):

The routing is handled through the filepaths module.
"""
print(f"Check news {pull_request=}")
if not files:
files = await util.files_for_PR(gh, pull_request)
in_next_dir = file_found = False
Expand Down
3 changes: 2 additions & 1 deletion bedevere/stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ async def new_commit_pushed(event, gh, *arg, **kwargs):
if len(commits) > 0:
# get the latest commit hash
commit_hash = commits[-1]["id"]
pr = await util.get_pr_for_commit(gh, commit_hash)
repo_full_name = event.data["repository"]["full_name"]
pr = await util.get_pr_for_commit(gh, commit_hash, repo_full_name)
for label in util.labels(pr):
if label == "awaiting merge":
issue = await util.issue_for_PR(gh, pr)
Expand Down
7 changes: 5 additions & 2 deletions bedevere/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def create_status(context, state, *, description=None, target_url=None):

async def post_status(gh, event, status):
"""Post a status in reaction to an event."""
print(f"Post status {event.data=}")
await gh.post(event.data["pull_request"]["statuses_url"], data=status)


Expand Down Expand Up @@ -227,10 +228,12 @@ def no_labels(event_data):
return False


async def get_pr_for_commit(gh, sha):
async def get_pr_for_commit(gh, sha, repo_full_name=None):
"""Find the PR containing the specific commit hash."""
if not repo_full_name:
repo_full_name = "python/cpython"
prs_for_commit = await gh.getitem(
f"/search/issues?q=type:pr+repo:python/cpython+sha:{sha}"
f"/search/issues?q=type:pr+repo:{repo_full_name}+sha:{sha}"
)
if prs_for_commit["total_count"] > 0: # there should only be one
return prs_for_commit["items"][0]
Expand Down
5 changes: 5 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh -l

cp -r /bedevere ./bedevere

python -m bedevere
1 change: 0 additions & 1 deletion runtime.txt

This file was deleted.

18 changes: 18 additions & 0 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import os
import pytest


@pytest.fixture
def tmp_event_name(request, monkeypatch):
monkeypatch.setenv("GITHUB_EVENT_NAME", request.param)

@pytest.fixture
def tmp_job_id(monkeypatch):
monkeypatch.setenv("GITHUB_JOB", "12345")

@pytest.fixture
def tmp_webhook(tmp_path, monkeypatch):
"""Create a temporary file for an actions webhook event."""
tmp_file_path = tmp_path / "event.json"
monkeypatch.setenv("GITHUB_EVENT_PATH", os.fspath(tmp_file_path))
return tmp_file_path
47 changes: 17 additions & 30 deletions tests/test___main__.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,25 @@
from aiohttp import web
import pytest

from bedevere import __main__ as main

from tests.fixtures import tmp_webhook, tmp_event_name, tmp_job_id

async def test_ping(aiohttp_client):
app = web.Application()
app.router.add_post("/", main.main)
client = await aiohttp_client(app)
headers = {"x-github-event": "ping",
"x-github-delivery": "1234"}
data = {"zen": "testing is good"}
response = await client.post("/", headers=headers, json=data)
assert response.status == 200

@pytest.mark.parametrize('tmp_event_name', ["created"], indirect=True)
async def test_success(tmp_webhook, tmp_event_name, tmp_job_id, monkeypatch):
from bedevere import __main__ as main

async def test_success(aiohttp_client):
app = web.Application()
app.router.add_post("/", main.main)
client = await aiohttp_client(app)
headers = {"x-github-event": "project",
"x-github-delivery": "1234"}
# Sending a payload that shouldn't trigger any networking, but no errors
# either.
data = {"action": "created"}
response = await client.post("/", headers=headers, json=data)
assert response.status == 200


async def test_failure(aiohttp_client):
"""Even in the face of an exception, the server should not crash."""
app = web.Application()
app.router.add_post("/", main.main)
client = await aiohttp_client(app)
# Missing key headers.
response = await client.post("/", headers={})
assert response.status == 500
event_payload = {"action": "created"}
await main.main(event_payload)


async def test_failure(tmp_webhook):
from bedevere import __main__ as main
"""Actions will have exit code in case of errors."""

# Missing GitHub environment variables
event_payload = {}
with pytest.raises(SystemExit) as exc:
await main.main(event_payload)
assert exc.value.code == 1
17 changes: 11 additions & 6 deletions tests/test_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -1097,16 +1097,19 @@ async def test_awaiting_label_not_removed_when_pr_not_merged(label):
assert gh.delete_url is None


async def test_new_commit_pushed_to_approved_pr():
@pytest.mark.parametrize("repo_full_name", ["python/cpython", "mariatta/cpython"])
async def test_new_commit_pushed_to_approved_pr(repo_full_name):
# There is new commit on approved PR
username = "brettcannon"
sha = "f2393593c99dd2d3ab8bfab6fcc5ddee540518a9"
data = {"commits": [{"id": sha}]}
data = {"commits": [{"id": sha}],
"repository": {"full_name": repo_full_name},
}
event = sansio.Event(data, event="push", delivery_id="12345")
teams = [{"name": "python core", "id": 6}]
items = {
f"https://api.github.com/teams/6/memberships/{username}": "OK",
f"https://api.github.com/search/issues?q=type:pr+repo:python/cpython+sha:{sha}": {
f"https://api.github.com/search/issues?q=type:pr+repo:{repo_full_name}+sha:{sha}": {
"total_count": 1,
"items": [
{
Expand Down Expand Up @@ -1169,13 +1172,15 @@ async def test_new_commit_pushed_to_approved_pr():
}


async def test_new_commit_pushed_to_not_approved_pr():
@pytest.mark.parametrize("repo_full_name", ["python/cpython", "mariatta/cpython"])
async def test_new_commit_pushed_to_not_approved_pr(repo_full_name):
# There is new commit on approved PR
sha = "f2393593c99dd2d3ab8bfab6fcc5ddee540518a9"
data = {"commits": [{"id": sha}]}
data = {"commits": [{"id": sha}],
"repository": {"full_name": repo_full_name},}
event = sansio.Event(data, event="push", delivery_id="12345")
items = {
f"https://api.github.com/search/issues?q=type:pr+repo:python/cpython+sha:{sha}": {
f"https://api.github.com/search/issues?q=type:pr+repo:{repo_full_name}+sha:{sha}": {
"total_count": 1,
"items": [
{
Expand Down