-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The script automates github issue to Jita ticket mirroring. Signed-off-by: Or Shoval <oshoval@redhat.com>
Showing
21 changed files
with
875 additions
and
146 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 |
---|---|---|
@@ -1,42 +1,29 @@ | ||
# This is a basic workflow to help you get started with Actions | ||
|
||
name: CI | ||
|
||
# Controls when the workflow will run | ||
on: | ||
# Triggers the workflow on push or pull request events but only for the main branch | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
# Allows you to run this workflow manually from the Actions tab | ||
workflow_dispatch: | ||
|
||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel | ||
jobs: | ||
# This workflow contains a single job called "build" | ||
build: | ||
# The type of runner that the job will run on | ||
runs-on: ubuntu-latest | ||
|
||
# Steps represent a sequence of tasks that will be executed as part of the job | ||
steps: | ||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it | ||
- uses: actions/checkout@v2 | ||
|
||
- name: Setup Python | ||
uses: actions/setup-python@v2.2.2 | ||
with: | ||
# Version range or exact version of a Python version to use, using SemVer's version range syntax. | ||
python-version: "3.6" | ||
|
||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install flake8 pytest tox pylint | ||
pip install -r requirements.txt | ||
pip install tox | ||
- name: Test with tox | ||
run: | | ||
tox | ||
tox |
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,129 +1,3 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
.Python | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
pip-wheel-metadata/ | ||
share/python-wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
MANIFEST | ||
|
||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
__pycache__ | ||
secret*.txt | ||
.tox/ | ||
.nox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*.cover | ||
*.py,cover | ||
.hypothesis/ | ||
.pytest_cache/ | ||
|
||
# Translations | ||
*.mo | ||
*.pot | ||
|
||
# Django stuff: | ||
*.log | ||
local_settings.py | ||
db.sqlite3 | ||
db.sqlite3-journal | ||
|
||
# Flask stuff: | ||
instance/ | ||
.webassets-cache | ||
|
||
# Scrapy stuff: | ||
.scrapy | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
target/ | ||
|
||
# Jupyter Notebook | ||
.ipynb_checkpoints | ||
|
||
# IPython | ||
profile_default/ | ||
ipython_config.py | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# pipenv | ||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||
# However, in case of collaboration, if having platform-specific dependencies or dependencies | ||
# having no cross-platform support, pipenv may install dependencies that don't work, or not | ||
# install all needed dependencies. | ||
#Pipfile.lock | ||
|
||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow | ||
__pypackages__/ | ||
|
||
# Celery stuff | ||
celerybeat-schedule | ||
celerybeat.pid | ||
|
||
# SageMath parsed files | ||
*.sage.py | ||
|
||
# Environments | ||
.env | ||
.venv | ||
env/ | ||
venv/ | ||
ENV/ | ||
env.bak/ | ||
venv.bak/ | ||
|
||
# Spyder project settings | ||
.spyderproject | ||
.spyproject | ||
|
||
# Rope project settings | ||
.ropeproject | ||
|
||
# mkdocs documentation | ||
/site | ||
|
||
# mypy | ||
.mypy_cache/ | ||
.dmypy.json | ||
dmypy.json | ||
|
||
# Pyre type checker | ||
.pyre/ |
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,7 @@ | ||
FROM fedora:32 | ||
|
||
RUN dnf install -y python3 git pip \ | ||
&& dnf clean all \ | ||
&& rm -rf /var/cache/yum | ||
|
||
RUN pip install requests jira |
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
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,2 +1,61 @@ | ||
# github2jira | ||
Scrap github issues and create Jira tickets | ||
github2jira automates mirroring of github issues to Jira tickets. | ||
|
||
The tool scans github for issues that match the desired criteria, | ||
and for each one of them creates a Jira ticket (unless it already exists). | ||
|
||
## One time configuration | ||
1. Create github token https://github.com/settings/tokens, refer it as `GITHUB_TOKEN` | ||
2. Make sure you have a Jira bot access (either a user:pass or user:token), refer as `JIRA_USERNAME`,`JIRA_TOKEN` | ||
3. Get your Jira project id, refer as `JIRA_PROJECT_ID` | ||
`curl -s -u JIRA_USERNAME:JIRA_TOKEN -X GET -H "Content-Type: application/json" <JIRA_SERVER>/rest/api/latest/project/<JIRA_PROJECT> | jq .id` | ||
|
||
## Running manually | ||
|
||
1. export the following envvars: | ||
``` | ||
export JIRA_SERVER=<..> # for example https://nmstate.atlassian.net | ||
export JIRA_PROJECT=<..> # name of the Jira project (ticket names are JIRA_PROJECT-#) | ||
export JIRA_PROJECT_ID=<..> # see "One time configuration" section | ||
export JIRA_COMPONENT=<..> # which component to set in the created tickets | ||
export GITHUB_OWNER=<..> # the x of https://github.com/x/y | ||
export GITHUB_REPO=<..> # the y of https://github.com/x/y | ||
export GITHUB_LABEL=<..> # which label to filter | ||
export JIRA_USERNAME=<..> # see "One time configuration" section | ||
export JIRA_TOKEN=<..> # see "One time configuration" section | ||
export GITHUB_TOKEN=<..> # see "One time configuration" section | ||
``` | ||
|
||
2. Run `./main.py` in order to fetch github issues and create a ticket for them | ||
|
||
### Additional settings | ||
|
||
`dryrun`: Use `./main.py --dryrun` in order to run the tool in dryrun mode. | ||
dryrun mode will fetch github issues, and report what Jira tickets it would create, | ||
but without creating them. | ||
|
||
`--issue`: Use `./main.py --issue=<issue_id>` in order to create an issue for | ||
a specified issue id. | ||
No additional checks are performed in this case. | ||
|
||
## Running as k8s payload | ||
|
||
In order to have a fully automated mirroring process, | ||
it is suggested to run the tool as a cron jon. | ||
|
||
One of the methods to achieve it, is to run it as k8s CronJob payload. | ||
|
||
### One time configuration: Build docker image for the script | ||
|
||
1. From the project folder, run `docker build -f Dockerfile -t <image> .` | ||
once its done, push it to your image repository, or rename and push to a local registry. | ||
|
||
### Deploy as k8s payload | ||
|
||
1. Create secret.txt with the exports from the section above (include the export command). | ||
|
||
2. Create a configmap for the txt file | ||
`kubectl create configmap git-token --from-file=secret.txt` | ||
|
||
3. Deploy either a pod or a CronJob (see manifests folder). |
Empty file.
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,34 @@ | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
import os | ||
|
||
|
||
class Config: | ||
def __init__(self, var_names): | ||
self._vars = {name: None for name in var_names} | ||
|
||
@property | ||
def vars(self): | ||
return self._vars | ||
|
||
def Load(self): | ||
for var_name in self._vars.keys(): | ||
value = os.getenv(var_name) | ||
if value is None: | ||
raise NameError(f"can't find {var_name}") | ||
self._vars[var_name] = value |
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,123 @@ | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
import time | ||
import requests | ||
|
||
from datetime import datetime | ||
|
||
from github2jira.config import Config | ||
|
||
SECONDS_PER_WEEK = 7 * 24 * 60 * 60 | ||
# max github pages to process | ||
GITHUB_MAX_PAGES = 20 | ||
# process upto x weeks back | ||
MAX_DELTA_WEEKS = 4 | ||
|
||
|
||
class GithubEnv: | ||
TOKEN = "GITHUB_TOKEN" | ||
OWNER = "GITHUB_OWNER" | ||
REPO = "GITHUB_REPO" | ||
LABEL = "GITHUB_LABEL" | ||
|
||
|
||
_ENV_VAR_NAMES = [GithubEnv.TOKEN, GithubEnv.OWNER, GithubEnv.REPO, GithubEnv.LABEL] | ||
|
||
|
||
def config(): | ||
c = Config(_ENV_VAR_NAMES) | ||
c.Load() | ||
return c | ||
|
||
|
||
class Issue: | ||
def __init__(self, issue): | ||
self.issue = issue | ||
|
||
@property | ||
def repo(self): | ||
return self.issue["html_url"].split("/")[4] | ||
|
||
@property | ||
def id(self): | ||
return self.issue["number"] | ||
|
||
@property | ||
def url(self): | ||
return self.issue["html_url"] | ||
|
||
@property | ||
def title(self): | ||
return self.issue["title"] | ||
|
||
@property | ||
def epoch(self): | ||
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" | ||
timestamp = self.issue["created_at"] | ||
return int(datetime.strptime(timestamp, TIME_FORMAT).timestamp()) | ||
|
||
@property | ||
def labels(self): | ||
return [l["name"] for l in self.issue["labels"]] | ||
|
||
|
||
class Github: | ||
def __init__(self, cfg): | ||
owner = cfg.vars[GithubEnv.OWNER] | ||
repo = cfg.vars[GithubEnv.REPO] | ||
self.query_url = f"https://api.github.com/repos/{owner}/{repo}/issues" | ||
self.headers = {"Authorization": f"token {cfg.vars[GithubEnv.TOKEN]}"} | ||
self.expected_label = cfg.vars[GithubEnv.LABEL] | ||
|
||
def issue_by_id(self, issue_id): | ||
r = requests.get(f"{self.query_url}/{issue_id}", headers=self.headers) | ||
issue = r.json() | ||
if issue.get("url", None) is None: | ||
return None | ||
return Issue(issue) | ||
|
||
def issues(self): | ||
return self._filter(self._open_issues()) | ||
|
||
def _filter(self, issues): | ||
for issue in issues: | ||
if "pull" in issue.url: | ||
continue | ||
|
||
if self.expected_label in issue.labels and issue_in_window( | ||
issue, MAX_DELTA_WEEKS | ||
): | ||
yield issue | ||
|
||
def _open_issues(self): | ||
for page in range(1, GITHUB_MAX_PAGES): | ||
params = {"state": "open", "page": page, "per_page": "100"} | ||
r = requests.get(self.query_url, headers=self.headers, params=params) | ||
issues = r.json() | ||
|
||
if len(issues) == 0: | ||
return | ||
|
||
for issue in issues: | ||
yield Issue(issue) | ||
|
||
|
||
def issue_in_window(issue, max_delta_weeks): | ||
epoch = issue.epoch | ||
epoch_time_now = int(time.time()) | ||
return (epoch_time_now - epoch) < (max_delta_weeks * SECONDS_PER_WEEK) |
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,102 @@ | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
import json | ||
|
||
from jira import JIRA | ||
|
||
from github2jira.config import Config | ||
|
||
|
||
class JiraEnv: | ||
SERVER = "JIRA_SERVER" | ||
USERNAME = "JIRA_USERNAME" | ||
TOKEN = "JIRA_TOKEN" | ||
PROJECT = "JIRA_PROJECT" | ||
PROJECT_ID = "JIRA_PROJECT_ID" | ||
COMPONENT = "JIRA_COMPONENT" | ||
|
||
|
||
_ENV_VAR_NAMES = [ | ||
JiraEnv.SERVER, | ||
JiraEnv.USERNAME, | ||
JiraEnv.TOKEN, | ||
JiraEnv.PROJECT, | ||
JiraEnv.PROJECT_ID, | ||
JiraEnv.COMPONENT, | ||
] | ||
|
||
|
||
def config(): | ||
c = Config(_ENV_VAR_NAMES) | ||
c.Load() | ||
return c | ||
|
||
|
||
class Jira: | ||
def __init__(self, cfg): | ||
self.project = cfg.vars[JiraEnv.PROJECT] | ||
self.project_id = cfg.vars[JiraEnv.PROJECT_ID] | ||
self.server = cfg.vars[JiraEnv.SERVER] | ||
self.component = cfg.vars[JiraEnv.COMPONENT] | ||
|
||
jiraOptions = {"server": self.server} | ||
self.jira = JIRA( | ||
options=jiraOptions, | ||
basic_auth=(cfg.vars[JiraEnv.USERNAME], cfg.vars[JiraEnv.TOKEN]), | ||
) | ||
|
||
def issue_exists(self, git_issue): | ||
repo = git_issue.repo | ||
id = git_issue.id | ||
query = f'project={self.project} AND text ~ "GITHUB:{repo}-{id}"' | ||
issues = self.jira.search_issues(query) | ||
return len(issues) != 0 | ||
|
||
def create_issue(self, git_issue): | ||
issue_data = self._create_issue_data(git_issue) | ||
created_issue = self.jira.create_issue(issue_data) | ||
|
||
issue_url = f"{self.server}/browse/{created_issue}" | ||
print(f"Created issue {issue_url} for {git_issue.url}") | ||
|
||
def _create_issue_data(self, git_issue): | ||
issue_data = { | ||
"project": {"id": self.project_id}, | ||
"summary": f"[GITHUB:{git_issue.repo}-{git_issue.id}] {git_issue.title}", | ||
"description": git_issue.url, | ||
"issuetype": {"name": "Task"}, | ||
} | ||
|
||
if self.component != "": | ||
issue_data["components"] = [{"name": self.component}] | ||
|
||
return issue_data | ||
|
||
|
||
class DryRunJira: | ||
def __init__(self, jira): | ||
self.jira = jira | ||
|
||
def issue_exists(self, git_issue): | ||
return self.jira.issue_exists(git_issue) | ||
|
||
def create_issue(self, git_issue): | ||
json_data = json.dumps( | ||
self.jira._create_issue_data(git_issue), sort_keys=True, indent=4 | ||
) | ||
print(f"Dryrun would create the following for {git_issue.url}\n{json_data}") |
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,42 @@ | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
# how many tickets can be opened on each cycle | ||
FLOOD_PROTECTION_LIMIT = 3 | ||
|
||
|
||
class TicketManager: | ||
def __init__(self, jira): | ||
self.issues_created = 0 | ||
self.jira = jira | ||
|
||
def create(self, issue): | ||
if not self.jira.issue_exists(issue): | ||
if self._flood_protection_reached(): | ||
print("Flood protection reached, skipping creation of", issue.url) | ||
return False | ||
|
||
self.jira.create_issue(issue) | ||
self.issues_created += 1 | ||
return True | ||
else: | ||
print("Issue for", issue.url, "already exists") | ||
|
||
return False | ||
|
||
def _flood_protection_reached(self): | ||
return self.issues_created == FLOOD_PROTECTION_LIMIT |
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,62 @@ | ||
#!/usr/bin/env python3 | ||
# | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
import argparse | ||
|
||
from github2jira.ticketmanager import TicketManager | ||
import github2jira.jiralib as jiralib | ||
import github2jira.githublib as githublib | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--dryrun", default=False, action="store_true") | ||
parser.add_argument("--issue") | ||
args = parser.parse_args() | ||
|
||
jira = jiralib.Jira(jiralib.config()) | ||
|
||
if args.dryrun: | ||
print("INFO: dryrun mode enabled") | ||
jira = jiralib.DryRunJira(jira) | ||
|
||
github = githublib.Github(githublib.config()) | ||
ticket_manager = TicketManager(jira) | ||
|
||
if args.issue is None: | ||
process_issues(github, ticket_manager) | ||
else: | ||
process_issue(args.issue, github, ticket_manager) | ||
|
||
|
||
def process_issues(github, ticket_manager): | ||
for issue in github.issues(): | ||
ticket_manager.create(issue) | ||
|
||
|
||
def process_issue(issue, github, ticket_manager): | ||
issue = github.issue_by_id(issue) | ||
if issue is not None: | ||
ticket_manager.create(issue) | ||
else: | ||
print("Issue not found") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
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,32 @@ | ||
--- | ||
apiVersion: batch/v1 | ||
kind: CronJob | ||
metadata: | ||
name: cron-github | ||
spec: | ||
schedule: "0 */1 * * *" | ||
successfulJobsHistoryLimit: 1 | ||
failedJobsHistoryLimit: 1 | ||
jobTemplate: | ||
spec: | ||
template: | ||
spec: | ||
containers: | ||
- name: github | ||
image: quay.io/oshoval/github:latest | ||
command: | ||
- /bin/sh | ||
- -ce | ||
- | | ||
source /app/secret.txt | ||
git clone https://github.com/oshoval/github2jira.git | ||
./github2jira/main.py | ||
volumeMounts: | ||
- name: configs | ||
mountPath: /app/secret.txt | ||
subPath: secret.txt | ||
restartPolicy: Never | ||
volumes: | ||
- name: configs | ||
configMap: | ||
name: git-token |
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,22 @@ | ||
--- | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: github | ||
namespace: default | ||
spec: | ||
containers: | ||
- image: quay.io/oshoval/github:latest | ||
name: github | ||
command: | ||
- /bin/bash | ||
- -c | ||
- sleep infinity | ||
volumeMounts: | ||
- name: configs | ||
mountPath: /app/secret.txt | ||
subPath: secret.txt | ||
volumes: | ||
- name: configs | ||
configMap: | ||
name: git-token |
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,4 @@ | ||
requests | ||
jira | ||
pytest | ||
responses |
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,47 @@ | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
from github2jira.ticketmanager import TicketManager, FLOOD_PROTECTION_LIMIT | ||
from github2jira.githublib import Issue | ||
|
||
|
||
class MockJira: | ||
def __init__(self): | ||
self.counter = 0 | ||
|
||
def issue_exists(self, git_issue): | ||
self.counter += 1 | ||
return self.counter % 2 != 0 | ||
|
||
def create_issue(self, git_issue): | ||
return | ||
|
||
|
||
def test_ticketmanager_create(): | ||
jira = MockJira() | ||
ticket_manager = TicketManager(jira) | ||
|
||
raw_issue = {} | ||
raw_issue["html_url"] = "dummy" | ||
git_issue = Issue(raw_issue) | ||
|
||
issues_created = 0 | ||
for i in range(FLOOD_PROTECTION_LIMIT * 3): | ||
if ticket_manager.create(git_issue): | ||
issues_created += 1 | ||
|
||
assert issues_created == FLOOD_PROTECTION_LIMIT |
Empty file.
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,43 @@ | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
import time | ||
from datetime import datetime | ||
|
||
import github2jira.githublib as githublib | ||
|
||
PROJECT_ID = "100" | ||
REPO = "repo" | ||
ISSUE_ID = "10" | ||
TITLE = "title" | ||
COMPONENT = "dummy" | ||
|
||
|
||
def get_raw_issue(): | ||
raw_issue = {} | ||
raw_issue["html_url"] = f"https://github.com/owner/{REPO}/issues/{ISSUE_ID}" | ||
raw_issue["number"] = ISSUE_ID | ||
raw_issue["labels"] = [{"name": "sig/network"}, {"name": "sig/compute"}] | ||
raw_issue["title"] = TITLE | ||
raw_issue["url"] = f"https://api.github.com/repos/owner/{REPO}/issues/10" | ||
|
||
ts_epoch = int(time.time()) - githublib.SECONDS_PER_WEEK * 0.5 | ||
raw_issue["created_at"] = datetime.fromtimestamp(ts_epoch).strftime( | ||
"%Y-%m-%dT%H:%M:%SZ" | ||
) | ||
|
||
return raw_issue |
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,42 @@ | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
import os | ||
|
||
from unittest import mock | ||
import pytest | ||
|
||
import github2jira.githublib as githublib | ||
|
||
|
||
def mockenv(**envvars): | ||
return mock.patch.dict(os.environ, envvars) | ||
|
||
|
||
@mockenv( | ||
GITHUB_TOKEN="dummy", | ||
GITHUB_OWNER="owner", | ||
GITHUB_REPO="repo", | ||
GITHUB_LABEL="sig/network", | ||
) | ||
def test_config_load(): | ||
githublib.config() | ||
|
||
|
||
def test_negative_config_load(): | ||
with pytest.raises(Exception): | ||
githublib.config() |
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,101 @@ | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
import os | ||
|
||
from unittest import mock | ||
import responses | ||
|
||
import github2jira.githublib as githublib | ||
from tests.common import get_raw_issue | ||
|
||
|
||
def mockenv(**envvars): | ||
return mock.patch.dict(os.environ, envvars) | ||
|
||
|
||
def test_issue_properties(): | ||
raw_issue = get_raw_issue() | ||
issue = githublib.Issue(raw_issue) | ||
|
||
assert issue.repo == "repo" | ||
assert issue.id == raw_issue["number"] | ||
assert issue.url == raw_issue["html_url"] | ||
assert issue.title == "title" | ||
assert issue.labels == ["sig/network", "sig/compute"] | ||
|
||
assert githublib.issue_in_window(issue, 1) | ||
assert not githublib.issue_in_window(issue, 0.2) | ||
|
||
|
||
@mockenv( | ||
GITHUB_TOKEN="dummy", | ||
GITHUB_OWNER="owner", | ||
GITHUB_REPO="repo", | ||
GITHUB_LABEL="sig/network", | ||
) | ||
@responses.activate | ||
def test_githublib_issues(): | ||
github = githublib.Github(githublib.config()) | ||
|
||
entry1 = get_raw_issue() | ||
entry2 = get_raw_issue() | ||
entry2["html_url"] = "https://github.com/kubevirt/kubevirt/issues/11" | ||
entry3 = get_raw_issue() | ||
entry3["labels"][0]["name"] = "sig/na" | ||
entry3["html_url"] = "https://github.com/kubevirt/kubevirt/issues/12" | ||
entry4 = get_raw_issue() | ||
entry4["html_url"] = "https://github.com/kubevirt/kubevirt/issues/13" | ||
|
||
responses.add(responses.GET, github.query_url, json=[entry1, entry2], status=200) | ||
responses.add(responses.GET, github.query_url, json=[entry3], status=200) | ||
responses.add(responses.GET, github.query_url, json=[entry4], status=200) | ||
responses.add(responses.GET, github.query_url, json=[], status=200) | ||
|
||
issues = list(github.issues()) | ||
|
||
assert len(issues) == 3 | ||
assert issues[0].url == "https://github.com/owner/repo/issues/10" | ||
assert issues[1].url == "https://github.com/kubevirt/kubevirt/issues/11" | ||
assert issues[2].url == "https://github.com/kubevirt/kubevirt/issues/13" | ||
|
||
|
||
@mockenv( | ||
GITHUB_TOKEN="dummy", | ||
GITHUB_OWNER="owner", | ||
GITHUB_REPO="repo", | ||
GITHUB_LABEL="sig/network", | ||
) | ||
@responses.activate | ||
def test_githublib_issue_by_id(): | ||
github = githublib.Github(githublib.config()) | ||
|
||
entry1 = get_raw_issue() | ||
issue_id = 777 | ||
issue_id_na = 1 | ||
responses.add( | ||
responses.GET, f"{github.query_url}/{issue_id}", json=entry1, status=200 | ||
) | ||
responses.add( | ||
responses.GET, | ||
f"{github.query_url}/{issue_id_na}", | ||
json={"message": "Not Found"}, | ||
status=200, | ||
) | ||
|
||
assert github.issue_by_id(issue_id) is not None | ||
assert github.issue_by_id(issue_id_na) is None |
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,65 @@ | ||
# This file is part of the github2jira project | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
# Copyright 2021 Red Hat, Inc. | ||
# | ||
|
||
import os | ||
|
||
from unittest import mock | ||
|
||
from unittest.mock import patch | ||
import pytest | ||
|
||
from github2jira.githublib import Issue | ||
import tests.common as common | ||
from tests.common import get_raw_issue | ||
|
||
|
||
def mockenv(**envvars): | ||
return mock.patch.dict(os.environ, envvars) | ||
|
||
|
||
class MockedJIRA: | ||
def __init__(self, options=None, basic_auth=None): | ||
return | ||
|
||
|
||
@pytest.fixture | ||
def mocked_jira(): | ||
return MockedJIRA() | ||
|
||
|
||
@mockenv( | ||
JIRA_SERVER="dummy", | ||
JIRA_USERNAME="dummy", | ||
JIRA_TOKEN="dummy", | ||
JIRA_PROJECT="dummy", | ||
JIRA_PROJECT_ID=common.PROJECT_ID, | ||
JIRA_COMPONENT=common.COMPONENT, | ||
) | ||
@patch("jira.JIRA") | ||
def test_initialization(self, mocked_jira): | ||
from github2jira.jiralib import Jira, config | ||
|
||
jira = Jira(config()) | ||
issue = Issue(get_raw_issue()) | ||
issue_data = jira._create_issue_data(issue) | ||
assert issue_data == { | ||
"project": {"id": common.PROJECT_ID}, | ||
"summary": f"[GITHUB:{common.REPO}-{common.ISSUE_ID}] {common.TITLE}", | ||
"description": f"https://github.com/owner/{common.REPO}/issues/{common.ISSUE_ID}", | ||
"issuetype": {"name": "Task"}, | ||
"components": [{"name": common.COMPONENT}], | ||
} |
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 @@ | ||
[tox] | ||
envlist = pytest, black, flake8, pylint, yamllint, py36, py37, py38 | ||
skip_missing_interpreters = True | ||
skipsdist=True | ||
|
||
[testenv:pytest] | ||
deps = | ||
-rrequirements.txt | ||
pytest-cov==2.8.1 | ||
pytest==5.3.1 | ||
|
||
commands = | ||
pytest \ | ||
--log-level=DEBUG \ | ||
--durations=5 \ | ||
--cov-report=term \ | ||
--cov-report=xml \ | ||
--cov-report=html:htmlcov-{envname} \ | ||
{posargs} | ||
|
||
[testenv:black] | ||
skip_install = true | ||
py36: basepython=python3.6 | ||
py37: basepython=python3.7 | ||
py38: basepython=python3.8 | ||
changedir = {toxinidir} | ||
deps = | ||
black==21.6b0 | ||
# style configured via pyproject.toml | ||
commands = | ||
black \ | ||
--check \ | ||
--diff \ | ||
{posargs} \ | ||
./ | ||
|
||
[testenv:flake8] | ||
py36: basepython=python3.6 | ||
py37: basepython=python3.7 | ||
py38: basepython=python3.8 | ||
skip_install = true | ||
changedir = {toxinidir} | ||
deps = | ||
flake8==3.7.9 | ||
commands = | ||
flake8 \ | ||
--statistics {posargs} \ | ||
main.py \ | ||
github2jira/ \ | ||
tests/ | ||
|
||
[testenv:pylint] | ||
py36: basepython=python3.6 | ||
py37: basepython=python3.7 | ||
py38: basepython=python3.8 | ||
sitepackages = true | ||
skip_install = true | ||
changedir = {toxinidir} | ||
deps = | ||
-rrequirements.txt | ||
pylint==2.4.4 | ||
commands = | ||
pylint \ | ||
--errors-only \ | ||
{posargs} \ | ||
main.py \ | ||
github2jira/ \ | ||
tests/ | ||
|
||
[testenv:yamllint] | ||
py36: basepython=python3.6 | ||
py37: basepython=python3.7 | ||
py38: basepython=python3.8 | ||
skip_install = true | ||
changedir = {toxinidir} | ||
deps = | ||
yamllint==1.23.0 | ||
commands = | ||
yamllint manifests/ | ||
|
||
[flake8] | ||
show_source = True | ||
max-line-length=90 |