Read about our upcoming Code of Conduct on this issue

Commit 20fe7c8b authored by Georges Racinet's avatar Georges Racinet 🦑
Browse files

fetch-watch-tags: issue creator bot for new upstream releases

Initial import, as-is.
parent a16256772d0c
\ No newline at end of file
#!/usr/bin/env python3
import argparse
import os
import re
import requests
import sys
import time
from subprocess import Popen, PIPE, check_call
# Regular expression for a new version tag on the remote.
# The required whitespace after the last number excludes rc releases
# and similar on purpose
NEW_TAG_RX = re.compile(r'\[new tag\]\s+v(\d+)[.](\d+)[.](\d+)\s+')
def level2_version(s):
split = s.strip().split('.')
if len(split) != 2:
raise TypeError("Invalid level 2 version string")
return int(split[0]), int(split[1])
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--simulate", action='store_true')
parser.add_argument("--gitlab-repo", default='gitlab-ce',
help="Path to GitLab CE Git repo, relative "
"to this script location")
parser.add_argument("--create-issue", action='store_true')
parser.add_argument("--heptapod-server", default='')
parser.add_argument("--heptapod-project", default='heptapod/heptapod')
parser.add_argument("--version-series", type=level2_version, nargs='*',
help="Level two version series to watch, ex: 13.7. "
"If specified, only the maximal version in the series "
"is considered, e.g, 13.7.6 if 13.7.5 and 13.7.6 are new."
cl_args = parser.parse_args()
simulate = cl_args.simulate
parent_dir = os.path.dirname(__file__)
cmd = ['git', 'fetch', '--tags']
if simulate:
git = Popen(cmd, stderr=PIPE, stdout=PIPE)
out, err = git.communicate()
if git.returncode != 0:
raise RuntimeError("Git returned code %d. stdout: %r, stderr: %r" % (
git.returncode, out, err))
new_versions = []
for line in err.splitlines(): # all Git useful output is on stderr
line = line.strip().decode('ascii', 'ignore')
m =
if m is not None:
if new_versions:
print("Releases to consider in new tags: %r" % new_versions)
print("No release found in new tags")
series = cl_args.version_series
if series:
print("Filtering according to specified version series: " + repr(series))
new_versions = [max(v for v in new_versions if v[:2] in series)]
except ValueError: # max() on empty sequence
print("No release matches the given series")
if cl_args.create_issue:
for version in new_versions:
version_str = '.'.join(str(i) for i in version)
print("Creating issue for GitLab " + repr(version))
url = '/'.join((cl_args.heptapod_server, 'api', 'v4',
cl_args.heptapod_project.replace('/', '%2F'),
with open(os.path.expanduser(cl_args.heptapod_token)) as tokenf:
token =
post_data = dict(
description="A new release tag v%s appeared in upstream GitLab."
"It should be considered for a stable Heptapod "
"release.\n" % version_str,
title="GitLab " + version_str
if simulate:
print("Simulate mode: would POST %r, data=%r" % (url, post_data))
resp =,
headers={'Private-Token': token},
as_json = resp.json()
if resp.status_code < 400:
print("Issue %d created. Details: %r" % (as_json['iid'], as_json))
print("Error %d creating issue. Details: %r" % (
resp.status_code, as_json))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment