@@ -229,6 +229,9 @@ class KernelSourceEntry:
def __ne__(self, other):
return not self.__eq__(other)
+ def __hash__(self):
+ return hash((self._name, self._series))
+
@property
def name(self):
return self._name
@@ -428,6 +431,9 @@ class KernelSeriesEntry:
def __ne__(self, other):
return not self.__eq__(other)
+ def __hash__(self):
+ return hash((self._name))
+
@property
def name(self):
return self._name
@@ -6,7 +6,7 @@ import re
from datetime import datetime, timedelta
# the so-trello root dir needs to be on PYTHONPATH
-from trellotool.trellotool import TrelloTool
+from libtrello import TrelloBoard
from ktl.kernel_series import KernelSeries
from ktl.utils import run_command
@@ -14,88 +14,6 @@ class TrelloError(Exception):
pass
-class SRUBoard:
- def __init__(self, tt, name):
- """
- :param tt: TrelloTool object
- :param name: name of the board
- """
- self.tt = tt
- self.name = name
- self.id = None
- self.url = None
- self.default_list_name = None
- self.default_list_id = None
-
- def create(self, org):
- """
- Create the Trello board
-
- :param org: Trello organization (team)
- :return None
- """
- params = {
- 'name': self.name,
- 'idOrganization': org,
- 'prefs_permissionLevel': 'org',
- 'defaultLists': 'false'
- }
-
- self.tt.trello.board_create(**params)
- self.lookup_board_id()
-
- def lookup_board_id(self):
- for board in self.tt.trello.member_boards('me'):
- if board['name'] == self.name:
- self.id = board['id']
- self.url = board['url']
- return
- raise TrelloError("Could not find id for board '%s'" % self.name)
-
- def add_lists(self, list_names, default_list):
- """
- Add the lists to the board and save the default list
- to be used to later add the cards to
-
- :param list_names: list with the list names
- :param default_list: default list to add the cards to
- :return: None
- """
- for list_name in list_names:
- params = {
- 'name': list_name,
- 'pos': 'bottom'
- }
- self.tt.trello.board_addlist(self.id, **params)
-
- self.default_list_name = default_list
- self.lookup_list_id(default_list)
-
- def lookup_list_id(self, list_name):
- for board_list in self.tt.trello.board_lists(self.id):
- if board_list['name'] == list_name:
- self.default_list_id = board_list['id']
- return
- raise TrelloError("Could not find id for list '%s'" % list_name)
-
- def add_card(self, name, desc=None):
- """
- Add the given card to the default list board
-
- :param name: card name
- :param desc: card description
- :return: None
- """
- params = {
- 'name': name,
- 'pos': 'bottom',
- }
- if desc:
- params['desc'] = desc
-
- self.tt.trello.list_addcard(self.default_list_id, **params)
-
-
class SRUCardsCreator:
def __init__(self, args):
"""
@@ -108,9 +26,6 @@ class SRUCardsCreator:
self.config = {}
self.config_load()
- self.tt = TrelloTool()
- self.tt.assert_authenticated()
-
def config_load(self):
with open(self.config_file, 'r') as cfd:
self.config = yaml.safe_load(cfd)
@@ -144,10 +59,10 @@ class SRUCardsCreator:
:return: None
"""
# create the board with the lists on the organization
- board = SRUBoard(self.tt, self.config['board']['prefix_name'] + self.cycle)
- print('Create board: %s' % (self.config['board']['prefix_name'] + self.cycle))
+ board = TrelloBoard()
if not self.args.dry_run:
- board.create(self.config['board']['trello_organization'])
+ print('Create board: %s' % (self.config['board']['prefix_name'] + self.cycle))
+ board.create(self.config['board']['trello_organization'], self.config['board']['prefix_name'] + self.cycle)
board.add_lists(self.config['board']['lists'], self.config['board']['default_list'])
# Cache the tuples (series, source) of supported sources
@@ -284,8 +199,8 @@ def update_cycle_info(args):
if __name__ == '__main__':
retval = 0
- default_config = '%s/create-sru-cards.yaml' % os.path.dirname(__file__)
- default_cycle_info = '%s/../info/sru-cycle.yaml' % os.path.dirname(__file__)
+ default_config = TrelloBoard.default_config
+ default_cycle_info = TrelloBoard.default_cycle_info
description = 'Create a Trello board with cards for SRU cycles'
epilog = '''
The script reads the configuration from a yaml file, updates the sru-cycle info
new file mode 100644
@@ -0,0 +1,114 @@
+import os
+import yaml
+from trellotool.trellotool import TrelloTool
+
+class TrelloError(Exception):
+ pass
+
+class TrelloBoard:
+ default_config = '%s/create-sru-cards.yaml' % os.path.dirname(__file__)
+ default_cycle_info = '%s/../info/sru-cycle.yaml' % os.path.dirname(__file__)
+
+ def __init__(self):
+ """
+ :param tt: TrelloTool object
+ :param name: name of the board
+ """
+ self.tt = TrelloTool()
+ self.id = None
+ self.url = None
+ self.default_list_name = None
+ self.default_list_id = None
+ self.load_config()
+
+ def load_cycle_info(self):
+ with open(self.default_cycle_info, 'r') as cfd:
+ self.cycle_info = yaml.safe_load(cfd)
+
+ def load_config(self):
+ with open(self.default_config, 'r') as cfd2:
+ self.config = yaml.safe_load(cfd2)
+
+ def create(self, org, name):
+ """
+ Create the Trello board
+
+ :param org: Trello organization (team)
+ :return None
+ """
+ params = {
+ 'name': name,
+ 'idOrganization': org,
+ 'prefs_permissionLevel': 'org',
+ 'defaultLists': 'false'
+ }
+
+ self.tt.trello.board_create(**params)
+ self.lookup_board_id(name)
+
+ def lookup_board_id(self, name):
+ for board in self.tt.trello.member_boards('me'):
+ if board['name'] == name:
+ self.id = board['id']
+ self.url = board['url']
+ self.name = board['name']
+ return
+ raise TrelloError("Could not find id for board '%s'" % name)
+
+ def get_current_cycle(self):
+ with open(self.default_cycle_info, "r") as f:
+ for line in f:
+ # print ('length of line is %s' %(len(line)))
+ if line[0] != "#" and len(line) != 1:
+ # this should be the first non commented, non-empty line of the file
+ return (line[1:-3])
+
+ def add_lists(self, list_names, default_list):
+ """
+ Add the lists to the board and save the default list
+ to be used to later add the cards to
+
+ :param list_names: list with the list names
+ :param default_list: default list to add the cards to
+ :return: None
+ """
+ for list_name in list_names:
+ params = {
+ 'name': list_name,
+ 'pos': 'bottom'
+ }
+ self.tt.trello.board_addlist(self.id, **params)
+
+ self.default_list_name = default_list
+ self.lookup_list_id(default_list)
+
+ def lookup_list_id(self, list_name):
+ for board_list in self.tt.trello.board_lists(self.id):
+ if board_list['name'] == list_name:
+ self.default_list_id = board_list['id']
+ return
+ raise TrelloError("Could not find id for list '%s'" % list_name)
+
+ def set_default_list(self, list_name):
+ for board_list in self.tt.trello.board_lists(self.id):
+ if board_list['name'] == list_name:
+ self.default_list_id = board_list['id']
+ return
+ raise TrelloError("Could not find id for list '%s'" % list_name)
+
+ def add_card(self, name, desc=None):
+ """
+ Add the given card to the default list board
+
+ :param name: card name
+ :param desc: card description
+ :return: None
+ """
+ params = {
+ 'name': name,
+ 'pos': 'bottom',
+ }
+ if desc:
+ params['desc'] = desc
+
+ self.tt.trello.list_addcard(self.default_list_id, **params)
@@ -6,6 +6,7 @@ import os
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'py3')))
+from libtrello import TrelloBoard
from argparse import ArgumentParser, RawDescriptionHelpFormatter
from ktl.kernel_series import KernelSeries
from ktl.tracking_bug import TrackingBug
@@ -77,10 +78,10 @@ class Crankers():
origin = s.source_get_origin(source)
if not origin:
if s.series_wanted(series):
- retval.setdefault((series.codename, source.name), [])
+ retval.setdefault((series, source), [])
else:
if s.series_wanted(origin.series):
- retval_series = retval.setdefault((origin.series.codename, origin.name), [])
+ retval_series = retval.setdefault((origin.series, origin), [])
retval_series.append((series.codename, source.name))
return retval
@@ -122,19 +123,42 @@ class Crankers():
retval = 1
try:
data = s.tracked_packages()
- cycle_tag = 'kernel-sru-cycle-' + s.args.cycle
+ board = TrelloBoard()
- for (series, master_package) in sorted(data):
- print('%s' % series)
- print(' ' + master_package)
+ if s.args.cycle:
+ cycle_string = s.args.cycle
+ else:
+ cycle_string = board.get_current_cycle()
+
+ print('Using SRU cycle: %s'%(cycle_string))
+ cycle_tag = 'kernel-sru-cycle-' + cycle_string + '-' + str(s.args.spin)
+ board.lookup_board_id(board.config['board']['prefix_name'] + cycle_string)
+
+ board.set_default_list('Backlog')
+
+ for (series, master_package) in data:
+
+ print(' %s:%s' % (series.codename, master_package.name))
- print(' %s:%s' % (series, master_package))
if not s.args.dryrun:
if s.args.usemasterbug:
master_bug = s.tb.get_bug(s.args.usemasterbug[0])
print("Using pre-existing master-bug: %s" %(master_bug.id))
else:
- master_bug = s.tb.open(master_package, '<version to be filled>', True, None, series)
+ master_bug = s.tb.open(master_package.name, '<version to be filled>', True, None, series.codename)
+ if s.args.spin > 1:
+ card_name = 'Respin #' + str(s.args.spin) + ': '
+ else:
+ card_name = ''
+ card_name += 'Crank %s/%s' % (series.codename, master_package.name)
+
+ card_desc = 'https://bugs.launchpad.net/bugs/' + str(master_bug.id)
+ if series.esm:
+ card_desc += '\nESM mode: Note different git location and build PPA'
+
+ print(' Adding trello card: %s' % (card_name))
+ board.add_card(card_name, card_desc)
+ print(' ----\n')
for task in master_bug.tasks:
# Move the primary package to Confirmed, link-to-tracker and
@@ -154,19 +178,37 @@ class Crankers():
print(' -> EE: Bug creation failed (does the package exist in the archive?)!')
continue
bug.tags.append(cycle_tag)
- if series == derivative_series:
+ if series.codename == derivative_series:
derivatives.append('bug {} ({})'.format(str(bug.id), derivative_package))
bug.tags.append('kernel-sru-derivative-of-%s' % master_bug.id)
else:
backports.append('bug {} ({})'.format(str(bug.id), derivative_package))
bug.tags.append('kernel-sru-backport-of-%s' % master_bug.id)
- if not s.args.dryrun:
- description = master_bug.description
- description += '\n'
- description += 'backports: %s\n' % ', '.join(backports)
- description += 'derivatives: %s\n' % ', '.join(derivatives)
- master_bug.description = description
+ if s.args.spin > 1:
+ card_name = 'Respin #' + str(s.args.spin) + ': '
+ else:
+ card_name = ''
+ card_name += 'Crank ' + derivative_series + '/' + derivative_package
+
+ card_desc = 'https://bugs.launchpad.net/bugs/' + str(bug.id)
+ if series.esm:
+ card_desc += '\nESM mode: Note different git location and build PPA'
+ if derivative_package == 'linux-euclid':
+ card_desc += '\nNo rebase to be done. Only needed if there are high and critical CVEs to be fixed.'
+
+ print(' Adding trello card: %s' % (card_name))
+ board.add_card(card_name, card_desc)
+ print(' ----\n')
+
+
+
+ if not s.args.dryrun:
+ description = master_bug.description
+ description += '\n'
+ description += 'backports: %s\n' % ', '.join(backports)
+ description += 'derivatives: %s\n' % ', '.join(derivatives)
+ master_bug.description = description
retval = 0
@@ -191,17 +233,18 @@ is being re-spun.
app_epilog = '''
Examples:
- start-sru-cycle 2018.01.01-1
- start-sru-cycle 2018.02.20-3 --series artful
+ start-sru-cycle 2018.01.01
+ start-sru-cycle 2018.02.20 --series artful --spin 3
'''
parser = ArgumentParser(description=app_description, epilog=app_epilog, formatter_class=RawDescriptionHelpFormatter)
- parser.add_argument('cycle', metavar='cycle', help='The sru cycle tag to be applied to the bugs that are created. The format is YYYY.MM.DD-# where the final "-#" is the spin number. If this is the initial start of a new SRU cycle the spin number will be 1 and is incremented for every re-spin. (e.g. \'2017.04.04-1\').')
+ parser.add_argument('cycle', nargs='?', help='The sru cycle tag to be applied to the bugs that are created. The format is YYYY.MM.DD (e.g. \'2017.04.04\'). This must match the title of a Trello SRU board for the Trello cards to be created on that board. If this option isn\'t specified, the most recent cycle from info/sru-cycle.yaml will be used.')
parser.add_argument('--series', action='append', default=[], metavar='series', help='Only creates tracking bugs for the specified series. This includes the main kernel and all of it\'s derivatives. Can be used multiple times.')
parser.add_argument('--source', action='append', default=[], metavar='source', help='Only create tracking bugs for the specified source package(s). Requiers exactly one series argument to be given. This option can be used multiple times.')
parser.add_argument('--staging', action='store_true', default=False, help='Use the staging LP server to create the bug. This is just for testing and will go away when the staging database is reset.')
parser.add_argument('--dry-run', '--dryrun', action='store_true', default=False, help='Make no permanent changes.', dest='dryrun')
parser.add_argument('--usemasterbug', type=int, nargs=1, help='Use pre-existing master-bug instead of creating a new one')
+ parser.add_argument('--spin', type=int, help='Specify the respin number. Default is 1', default=1)
args = parser.parse_args()
Move the Trello-related code from create-sru-cards.py to its own libtrello.py file. Modify start-sru-cycle to re-use some of that code to create Trello cards that correspond to the tracking bugs that it already currently creates. The 'cycle' option that start-sru-cycle accepts is now optional instead of mandatory. When it's omitted, the most recent cycle from info/sru-cycle.yaml is used. start-sru-cycle also accepts a new option now: '--spin X' . This can be used to specify the respin number (default is '1' so this option can be omitted for the first spin). Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com> --- ktl/kernel_series.py | 6 ++ stable/create-sru-cards.py | 97 ++----------------------------- stable/libtrello.py | 114 +++++++++++++++++++++++++++++++++++++ stable/start-sru-cycle | 79 +++++++++++++++++++------ 4 files changed, 187 insertions(+), 109 deletions(-) create mode 100644 stable/libtrello.py