Skip to content

Commit 92db94a

Browse files
committed
Introduce create ticket script
Signed-off-by: Or Shoval <[email protected]>
1 parent cb03a1a commit 92db94a

12 files changed

+478
-130
lines changed

.gitignore

+2-128
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,3 @@
1-
# Byte-compiled / optimized / DLL files
2-
__pycache__/
3-
*.py[cod]
4-
*$py.class
5-
6-
# C extensions
7-
*.so
8-
9-
# Distribution / packaging
10-
.Python
11-
build/
12-
develop-eggs/
13-
dist/
14-
downloads/
15-
eggs/
16-
.eggs/
17-
lib/
18-
lib64/
19-
parts/
20-
sdist/
21-
var/
22-
wheels/
23-
pip-wheel-metadata/
24-
share/python-wheels/
25-
*.egg-info/
26-
.installed.cfg
27-
*.egg
28-
MANIFEST
29-
30-
# PyInstaller
31-
# Usually these files are written by a python script from a template
32-
# before PyInstaller builds the exe, so as to inject date/other infos into it.
33-
*.manifest
34-
*.spec
35-
36-
# Installer logs
37-
pip-log.txt
38-
pip-delete-this-directory.txt
39-
40-
# Unit test / coverage reports
41-
htmlcov/
1+
__pycache__
2+
secret.txt
423
.tox/
43-
.nox/
44-
.coverage
45-
.coverage.*
46-
.cache
47-
nosetests.xml
48-
coverage.xml
49-
*.cover
50-
*.py,cover
51-
.hypothesis/
52-
.pytest_cache/
53-
54-
# Translations
55-
*.mo
56-
*.pot
57-
58-
# Django stuff:
59-
*.log
60-
local_settings.py
61-
db.sqlite3
62-
db.sqlite3-journal
63-
64-
# Flask stuff:
65-
instance/
66-
.webassets-cache
67-
68-
# Scrapy stuff:
69-
.scrapy
70-
71-
# Sphinx documentation
72-
docs/_build/
73-
74-
# PyBuilder
75-
target/
76-
77-
# Jupyter Notebook
78-
.ipynb_checkpoints
79-
80-
# IPython
81-
profile_default/
82-
ipython_config.py
83-
84-
# pyenv
85-
.python-version
86-
87-
# pipenv
88-
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89-
# However, in case of collaboration, if having platform-specific dependencies or dependencies
90-
# having no cross-platform support, pipenv may install dependencies that don't work, or not
91-
# install all needed dependencies.
92-
#Pipfile.lock
93-
94-
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
95-
__pypackages__/
96-
97-
# Celery stuff
98-
celerybeat-schedule
99-
celerybeat.pid
100-
101-
# SageMath parsed files
102-
*.sage.py
103-
104-
# Environments
105-
.env
106-
.venv
107-
env/
108-
venv/
109-
ENV/
110-
env.bak/
111-
venv.bak/
112-
113-
# Spyder project settings
114-
.spyderproject
115-
.spyproject
116-
117-
# Rope project settings
118-
.ropeproject
119-
120-
# mkdocs documentation
121-
/site
122-
123-
# mypy
124-
.mypy_cache/
125-
.dmypy.json
126-
dmypy.json
127-
128-
# Pyre type checker
129-
.pyre/

Dockerfile

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM fedora:32
2+
3+
RUN dnf install -y python3 git pip \
4+
&& dnf clean all \
5+
&& rm -rf /var/cache/yum
6+
7+
RUN pip install requests jira
8+
9+
CMD [“echo”, “Hello World”]
10+

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright [yyyy] [name of copyright owner]
189+
Copyright [2021] [Red Hat, Inc]
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

README.md

+56-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,57 @@
11
# github2jira
2-
Scrap github issues and create Jira tickets
2+
github2jira automates mirroring of github issues to Jira tickets.
3+
4+
The tool scans github for issues that match the desired criteria,
5+
and for each one of them creates a Jira ticket (unless it already exists).
6+
7+
## One time configuration
8+
1. Create github token https://github.com/settings/tokens, refer it as `GITHUB_TOKEN`
9+
2. Make sure you have a Jira bot access (either a user:pass or user:token), refer as `JIRA_USERNAME`,`JIRA_TOKEN`
10+
3. Get your Jira project id, refer as `JIRA_PROJECT_ID`
11+
`curl -s -u JIRA_USERNAME:JIRA_TOKEN -X GET -H "Content-Type: application/json" <JIRA_SERVER>/rest/api/latest/project/<JIRA_PROJECT> | jq .id`
12+
13+
## Running manually
14+
15+
1. export the following envvars:
16+
```
17+
export JIRA_SERVER=<..> # for example https://nmstate.atlassian.net
18+
export JIRA_PROJECT=<..> # name of the Jira project (ticket names are JIRA_PROJECT-#)
19+
export JIRA_PROJECT_ID=<..> # see "One time configuration" section
20+
export JIRA_COMPONENT=<..> # which component to set in the created tickets
21+
export GITHUB_OWNER=<..> # the x of https://github.com/x/y
22+
export GITHUB_REPO=<..> # the y of https://github.com/x/y
23+
export GITHUB_LABEL=<..> # which label to filter
24+
25+
export JIRA_USERNAME=<..> # see "One time configuration" section
26+
export JIRA_TOKEN=<..> # see "One time configuration" section
27+
export GITHUB_TOKEN=<..> # see "One time configuration" section
28+
```
29+
30+
2. Run `./create_ticket.py` in order to fetch github issues and create a ticket for them
31+
32+
### Additional settings
33+
34+
`dryrun`: Use `./create_ticket.py --dryrun` in order to run the tool in dryrun mode.
35+
dryrun mode will fetch github issues, and report what Jira tickets it would create,
36+
but without creating them.
37+
38+
## Running as k8s payload
39+
40+
In order to have a fully automated mirroring process,
41+
it is suggested to run the tool as a cron jon.
42+
43+
One of the methods to achieve it, is to run it as k8s CronJob payload.
44+
45+
### One time configuration: Build docker image for the script
46+
47+
1. From the project folder, run `docker build -f Dockerfile -t <image> .`
48+
once its done, push it to your image repository, or rename and push to a local registry.
49+
50+
### Deploy as k8s payload
51+
52+
1. Create secret.txt with the exports from the section above (include the export command).
53+
54+
2. Create a configmap for the txt file
55+
`kubectl create configmap git-token --from-file=secret.txt`
56+
57+
3. Deploy either a pod or a CronJob (see manifests folder).

config.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env python3
2+
#
3+
# This file is part of the github2jira project
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
# Copyright 2021 Red Hat, Inc.
18+
#
19+
20+
import os
21+
22+
23+
class Config:
24+
def __init__(self, var_names):
25+
self.var_names = var_names
26+
self.vars = {}
27+
28+
def Load(self):
29+
for var_name in self.var_names:
30+
value = os.getenv(var_name)
31+
if value is None:
32+
print(f"Error: cant find {var_name}")
33+
return False
34+
self.vars[var_name] = value
35+
return True

githublib.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env python3
2+
#
3+
# This file is part of the github2jira project
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
# Copyright 2021 Red Hat, Inc.
18+
#
19+
20+
import time
21+
import requests
22+
23+
from datetime import datetime
24+
25+
from config import Config
26+
27+
SECONDS_PER_WEEK = 604800
28+
# max github pages to process
29+
GITHUB_MAX_PAGES = 20
30+
# process upto x weeks back
31+
MAX_DELTA_WEEKS = 4
32+
33+
34+
class GithubConfig(Config):
35+
def __init__(self):
36+
super().__init__(
37+
["GITHUB_TOKEN", "GITHUB_OWNER", "GITHUB_REPO", "GITHUB_LABEL"]
38+
)
39+
40+
41+
class Issue:
42+
@staticmethod
43+
def filter(issue, expected_label):
44+
if expected_label == "":
45+
return True
46+
for label in issue["labels"]:
47+
if label["name"] == expected_label:
48+
return True
49+
return False
50+
51+
@staticmethod
52+
def age_relevant(issue, max_delta):
53+
epoch_time_now = int(time.time())
54+
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
55+
timestamp = issue["created_at"]
56+
epoch = int(datetime.strptime(timestamp, TIME_FORMAT).timestamp())
57+
return (epoch_time_now - epoch) < (max_delta * SECONDS_PER_WEEK)
58+
59+
60+
class Github:
61+
def __init__(self, cfg):
62+
self.owner = cfg.vars["GITHUB_OWNER"]
63+
self.repo = cfg.vars["GITHUB_REPO"]
64+
self.expected_label = cfg.vars["GITHUB_LABEL"]
65+
self.query_url = f"https://api.github.com/repos/{self.owner}/{self.repo}/issues"
66+
self.headers = {"Authorization": f"token {cfg.vars['GITHUB_TOKEN']}"}
67+
68+
def issues(self):
69+
for page in range(1, GITHUB_MAX_PAGES):
70+
params = {"state": "open", "page": page, "per_page": "100"}
71+
r = requests.get(self.query_url, headers=self.headers, params=params)
72+
issues = r.json()
73+
74+
if len(issues) == 0:
75+
return
76+
77+
for issue in issues:
78+
if "pull" in issue["html_url"]:
79+
continue
80+
81+
if Issue.filter(issue, self.expected_label) and Issue.age_relevant(
82+
issue, MAX_DELTA_WEEKS
83+
):
84+
yield (issue)

0 commit comments

Comments
 (0)