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
12 changes: 12 additions & 0 deletions .env-dummy
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# Go to smee.io to generate a URL here
SMEE_URL=https://smee.io/CHANGEME

# Optionally customize redis host (local test server defined in docker-compose.yml)
REDIS_HOST=rq-server

# Optionally customize redis port
REDIS_PORT=6379

# Optionally configure time before jobs are killed and marked failed (in seconds, default 180s)
WORKER_JOB_TIMEOUT=21600

# Debug level (one of: "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
SPACKBOT_LOG_LEVEL=WARNING

# You don't need to change this unless you change the docker-compose volumes
GITHUB_PRIVATE_KEY=/app/spackbot/spack-bot-develop.private-key.pem

Expand Down
15 changes: 10 additions & 5 deletions .github/workflows/build-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,24 @@ on:
jobs:
deploy-test-containers:
runs-on: ubuntu-latest
name: Build Spackbot Container
strategy:
fail-fast: false
# matrix: [tag, path to Dockerfile, label]
matrix:
dockerfile: [[spack-bot, ./Dockerfile, Spackbot],
[spackbot-workers, ./workers/Dockerfile, "Spackbot Workers"]]
name: Build ${{matrix.dockerfile[2]}} Container
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Build and Run Test Container
run: |
docker build -t ghcr.io/spack/spack-bot:latest .
docker tag ghcr.io/spack/spack-bot:latest ghcr.io/spack/spack-bot:${GITHUB_SHA::8}

docker build -f ${{matrix.dockerfile[1]}} -t ghcr.io/spack/${{matrix.dockerfile[0]}}:latest .
docker tag ghcr.io/spack/${{matrix.dockerfile[0]}}:latest ghcr.io/spack/${{matrix.dockerfile[0]}}:${GITHUB_SHA::8}
- name: Login and Deploy Test Container
if: (github.event_name != 'pull_request')
run: |
docker images
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ secrets.GHCR_USERNAME }} --password-stdin
docker push --all-tags ghcr.io/spack/spack-bot
docker push --all-tags ghcr.io/spack/${{matrix.dockerfile[0]}}
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ FROM python:3.7

EXPOSE 8080

# dependencies first since they're the slowest
COPY requirements.txt .
COPY spackbot /app/spackbot
COPY entrypoint.sh /entrypoint.sh

RUN pip3 install -r requirements.txt

# copy app in last so that everything above can be cached
COPY spackbot /app/spackbot
COPY entrypoint.sh /entrypoint.sh

ENV PYTHONPATH "${PYTHONPATH}:/app"
CMD ["/bin/bash", "/entrypoint.sh"]
23 changes: 23 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,25 @@ services:
context: .
dockerfile: smee/Dockerfile

rq-worker:
build:
context: .
dockerfile: workers/Dockerfile
env_file:
- ./.env
deploy:
replicas: 1

rq-server:
env_file:
- ./.env
image: redis:alpine
expose:
- ${REDIS_PORT}
volumes:
- redis-data:/data
- redis-conf:/usr/local/etc/redis/redis.conf

spackbot:
build:
context: .
Expand All @@ -29,3 +48,7 @@ services:
- ./.env
links:
- smee

volumes:
redis-data:
redis-conf:
10 changes: 0 additions & 10 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
#!/bin/bash

# If we have an ssh key bound, add it
if [[ -f "/root/.ssh/id_rsa" ]]; then
printf "Found id_spackbot to authenticate write...\n"
eval "$(ssh-agent -s)"
ssh-add /root/.ssh/id_rsa
else
printf "No id_spackbot found, will not have full permissions\n"
fi

ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
exec python3 -m spackbot
6 changes: 1 addition & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
aiohttp
gidgethub
python_dotenv
rq
sh

# Add these so we don't wait for install
mypy
flake8
isort
15 changes: 10 additions & 5 deletions spackbot/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import logging
import os

import aiohttp
Expand All @@ -13,12 +12,12 @@
from gidgethub import aiohttp as gh_aiohttp
from .routes import router
from .auth import authenticate_installation
from .helpers import get_logger

# take environment variables from .env file (if present)
load_dotenv()

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("spackbot")
logger = get_logger(__name__)

#: Location for authenticatd app to get a token for one of its installations
INSTALLATION_TOKEN_URL = "app/installations/{installation_id}/access_tokens"
Expand All @@ -41,13 +40,19 @@ async def main(request):
logger.info(f"Received event {event}")

# get an installation token to make a GitHubAPI for API calls
token = await authenticate_installation(event.data)
installation_id = event.data["installation"]["id"]
token = await authenticate_installation(installation_id)

dispatch_kwargs = {
"installation_id": installation_id,
"token": token,
}

async with aiohttp.ClientSession() as session:
gh = gh_aiohttp.GitHubAPI(session, REQUESTER, oauth_token=token)

# call the appropriate callback for the event
await router.dispatch(event, gh, session=session)
await router.dispatch(event, gh, session=session, **dispatch_kwargs)

# return a "Success"
return web.Response(status=200)
Expand Down
7 changes: 1 addition & 6 deletions spackbot/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import logging
import os
import re
import time
Expand All @@ -16,9 +15,6 @@

load_dotenv()

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("spackbot")

#: Location for authenticatd app to get a token for one of its installations
INSTALLATION_TOKEN_URL = "app/installations/{installation_id}/access_tokens"

Expand Down Expand Up @@ -79,14 +75,13 @@ async def renew_jwt():
return await _tokens.get_token("JWT", renew_jwt)


async def authenticate_installation(payload):
async def authenticate_installation(installation_id):
"""Get an installation access token for the application.

Renew the JWT if necessary, then use it to get an installation access
token from github, if necessary.

"""
installation_id = payload["installation"]["id"]

async def renew_installation_token():
async with aiohttp.ClientSession() as session:
Expand Down
47 changes: 42 additions & 5 deletions spackbot/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import io
import random
import traceback
import spackbot.helpers as helpers


async def tell_joke(gh):
"""
Tell a joke to ease the PR tension!
"""
joke = await gh.getitem(
"https://official-joke-api.appspot.com/jokes/programming/random"
)
try:
joke = await gh.getitem(
"https://official-joke-api.appspot.com/jokes/programming/random"
)
except Exception:
return "To be honest, I haven't heard any good jokes lately."
Copy link
Member

Choose a reason for hiding this comment

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

I like it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I like the idea too but every time I try the site I get a Server Error.

Copy link
Member

Choose a reason for hiding this comment

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

The jokes were already there (thanks to Vanessa). I just meant the exception response.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think both are great. I was commenting on the overall feature and the exception.


joke = joke[0]
return f"> {joke['setup']}\n *{joke['punchline']}*\n😄️"

Expand Down Expand Up @@ -57,6 +63,31 @@ def get_style_message(output):
"""


def get_style_error_message(e_type, e_value, tb):
"""
Given job failure details, format an error message to post. The
parameters e_type, e_value, and tb (for traceback) should be the same as
returned by sys.exc_info().
"""
buffer = io.StringIO()
traceback.print_tb(tb, file=buffer)
tb_contents = buffer.getvalue()
buffer.close()

return f"""
I encountered an error attempting to format style.
<details>
<summary><b>Details</b></summary>

```bash
Error: {e_type}, {e_value}
Stack trace:
{tb_contents}
```
</details>
"""


commands_message = f"""
You can interact with me in many ways!

Expand All @@ -70,8 +101,14 @@ def get_style_message(output):
If you need help or see there might be an issue with me, open an issue [here](https://github.com/spack/spack-bot/issues)
"""

style_message = """
It looks like you had an issue with style checks! To fix this, you can run:
style_message = f"""
It looks like you had an issue with style checks! I can help with that if you ask me! Just say:

`{helpers.botname} fix style`

... and I'll try to fix style and push a commit to your fork with the fix.

Alternatively, you can run:

```bash
$ spack style --fix
Expand Down
19 changes: 15 additions & 4 deletions spackbot/handlers/labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import re
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("spackbot")
import spackbot.helpers as helpers

logger = helpers.get_logger(__name__)


#: ``label_patterns`` maps labels to patterns that tell us to apply the labels.
Expand Down Expand Up @@ -163,7 +163,18 @@ async def add_labels(event, gh):
attr_matches = []
# Pattern matches for for each attribute are or'd together
for attr, patterns in pattern_dict.items():
attr_matches.append(any(p.search(file[attr]) for p in patterns))
# 'patch' is an example of an attribute that is not required to
# appear in response when listing pull request files. See here:
#
# https://docs.github.com/en/rest/pulls/pulls#list-pull-requests-files
#
# If we don't get some attribute in the response, no labels that
# depend on finding a match in that attribute should be added.
attr_matches.append(
any(p.search(file[attr]) for p in patterns)
if attr in file
else False
)
# If all attributes have at least one pattern match, we add the label
if all(attr_matches):
labels.append(label)
Expand Down
5 changes: 2 additions & 3 deletions spackbot/handlers/pipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import logging
import os
import urllib.parse
import spackbot.helpers as helpers

import aiohttp

logger = logging.getLogger(__name__)
logger = helpers.get_logger(__name__)

# We can only make the request with a GITLAB TOKEN
GITLAB_TOKEN = os.environ.get("GITLAB_TOKEN")
Expand Down Expand Up @@ -50,7 +49,7 @@ async def run_pipeline(event, gh):

# We need the branch name plus number to assemble the GitLab CI
branch = pr["head"]["ref"]
branch = f"github/pr{number}_{branch}"
branch = f"pr{number}_{branch}"
branch = urllib.parse.quote_plus(branch)

url = f"{helpers.gitlab_spack_project_url}/pipeline?ref={branch}"
Expand Down
3 changes: 1 addition & 2 deletions spackbot/handlers/reviewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import logging
import os
import re

Expand All @@ -13,7 +12,7 @@
import spackbot.comments as comments
from gidgethub import BadRequest

logger = logging.getLogger(__name__)
logger = helpers.get_logger(__name__)


async def parse_maintainers_from_patch(gh, pull_request):
Expand Down
Loading