#276 requests: add "Releng commands" section
Merged a year ago by kparal. Opened 2 years ago by kparal.

@@ -380,7 +380,7 @@ 

  

      response = make_response(render_template(

          'requests.txt', blocker_updates=blocker_updates, fe_updates=fe_updates,

-         milestone=milestone.id, bugs_with_deps=bugs_with_deps))

+         milestone=milestone.id, bugs_with_deps=bugs_with_deps, release_num=num))

      response.mimetype = 'text/plain'

      return response

  

file modified
+4 -2
@@ -39,7 +39,7 @@ 

  

      id: db.Mapped[int] = db.mapped_column(primary_key=True)

      updateid: db.Mapped[str] = db.mapped_column(db.Text, nullable=False, unique=True)

-     """E.g. FEDORA-2021-5282b5cafd"""

+     """E.g. FEDORA-2021-5282b5cafd. This is the same as the 'alias' field in a Bodhi object."""

      release_id: db.Mapped[int] = db.mapped_column(db.ForeignKey('release.id'))

      release: db.Mapped['Release'] = db.relationship(  # noqa: F821

          back_populates='updates', cascade_backrefs=False)
@@ -61,8 +61,10 @@ 

      """One of: 'revoke', 'unpush', 'obsolete', 'stable', 'testing', None. Because this is an enum,

      Updates can be sorted by this in the specified order (updates in a need of testing have higher

      value)."""

+     # FIXME: Make title non-optional, it's always present

      title: db.Mapped[Optional[str]] = db.mapped_column(db.Text)

-     """E.g. firewalld-0.9.4-1.fc34"""

+     """E.g. firewalld-0.9.4-1.fc34. Bodhi currently defaults to a space-delimited list of NVRs for

+     this, but it might change in the future."""

      stable_karma: db.Mapped[Optional[int]]

      """Target karma value for allowing the update to go stable (via auto or manual push)."""

      bugs: db.Mapped[List['Bug']] = db.relationship(  # noqa: F821

@@ -1,37 +1,90 @@ 

  {% autoescape false -%}

- ### CANDIDATE COMPOSE ###

  

- == Blockers ==

+ {#- Try to prevent a possible command injection in the "Releng commands" section -#}

+ {%- set ns = namespace(safe_cmd=true) -%}

+ {%- for update in (blocker_updates + fe_updates) -%}

+ {% if "'" in update.title or "'" in update.updateid %}

+ {%- set ns.safe_cmd = false -%}

+ An unsafe character present in an update details! A possible command injection?! Update details: {{ update.updateid}} {{ update.title }}

+ {% endif %}

+ {%- endfor -%}

+ 

+ {#- Note: Bugzilla ticket numbers use ⧣ instead of # as a prefix, to prevent Pagure from interpreting them as Pagure ticket numbers. -#}

+ 

+ ## CANDIDATE COMPOSE ##

+ 

+ #### Blockers ####

  {% for update in blocker_updates %}

- * [{{ update.title or update.updateid }}]({{ update.url }}) for {%- for bug in update.bugs if bug.milestone_id == milestone and (bug.accepted_blocker or bug.accepted_0day or bug.accepted_fe) %} [#{{ bug.bugid }}]({{ bug.url }}){% if bug.accepted_fe and not bug.accepted_blocker and not bug.accepted_prevrel and not bug.accepted_0day %} (FE){% endif %}{% endfor %}

+ * [{{ update.updateid }}: {{ update.title }}]({{ update.url }}) for {% for bug in update.bugs if bug.milestone_id == milestone and (bug.accepted_blocker or bug.accepted_0day or bug.accepted_fe) %}[⧣{{ bug.bugid }}{% if bug.accepted_fe and not bug.accepted_blocker and not bug.accepted_prevrel and not bug.accepted_0day %} (FE){% endif %}]({{ bug.url }}){% if not loop.last %}, {% endif %}{% endfor %}

  {%- endfor %}

  

- == Freeze exceptions ==

+ #### Freeze exceptions ####

  {% for update in fe_updates %}

- * [{{ update.title or update.updateid }}]({{ update.url }}) for {%- for bug in update.bugs if bug.milestone_id == milestone and (bug.accepted_blocker or bug.accepted_0day or bug.accepted_fe) %} [#{{ bug.bugid }}]({{ bug.url }}){% endfor %}

+ * [{{ update.updateid }}: {{ update.title }}]({{ update.url }}) for {% for bug in update.bugs if bug.milestone_id == milestone and (bug.accepted_blocker or bug.accepted_0day or bug.accepted_fe) %}[⧣{{ bug.bugid }}]({{ bug.url }}){% if not loop.last %}, {% endif %}{% endfor %}

  {%- endfor %}

  

+ #### Releng commands ####

+ {% if (blocker_updates or fe_updates) and ns.safe_cmd %}

+ Untag all previous builds:

+ ```

+ koji list-tagged f{{ release_num }}-compose --quiet | awk '{print $1}' | xargs -rt koji untag-pkg f{{ release_num }}-compose

+ ```

+ and then tag the new builds:

+ ```

+ blockers=(

+ {% for update in blocker_updates -%}

+ {{ update.title }}

+ {% endfor -%}

+ )

+ fes=(

+ {% for update in fe_updates -%}

+ {{ update.title }}

+ {% endfor -%}

+ )

+ koji tag-build f{{ release_num }}-compose ${blockers[@]} ${fes[@]}

+ ```

+ {%- endif %}

  

- ### FREEZE PUSH ###

+ ## FREEZE PUSH ##

+ {%- set freeze_blocker_updates = blocker_updates|selectattr('request', 'equalto', 'stable')|list -%}

+ {%- set freeze_fe_updates = fe_updates|selectattr('request', 'equalto', 'stable')|list %}

  

- == Blockers ==

- {% for update in blocker_updates if update.request == 'stable' %}

- * [{{ update.title or update.updateid }}]({{ update.url }}) for {%- for bug in update.bugs if bug.milestone_id == milestone and (bug.accepted_blocker or bug.accepted_0day or bug.accepted_fe) %} [#{{ bug.bugid }}]({{ bug.url }}){% if bug.accepted_fe and not bug.accepted_blocker and not bug.accepted_prevrel and not bug.accepted_0day %} (FE){% endif %}{% endfor %}

+ #### Blockers ####

+ {% for update in freeze_blocker_updates %}

+ * [{{ update.updateid }}: {{ update.title }}]({{ update.url }}) for {% for bug in update.bugs if bug.milestone_id == milestone and (bug.accepted_blocker or bug.accepted_0day or bug.accepted_fe) %}[⧣{{ bug.bugid }}{% if bug.accepted_fe and not bug.accepted_blocker and not bug.accepted_prevrel and not bug.accepted_0day %} (FE){% endif %}]({{ bug.url }}){% if not loop.last %}, {% endif %}{% endfor %}

  {%- endfor %}

  

- == Freeze exceptions ==

- {% for update in fe_updates if update.request == 'stable' %}

- * [{{ update.title or update.updateid }}]({{ update.url }}) for {%- for bug in update.bugs if bug.milestone_id == milestone and (bug.accepted_blocker or bug.accepted_0day or bug.accepted_fe) %} [#{{ bug.bugid }}]({{ bug.url }}){% endfor %}

+ #### Freeze exceptions ####

+ {% for update in freeze_fe_updates %}

+ * [{{ update.updateid }}: {{ update.title }}]({{ update.url }}) for {% for bug in update.bugs if bug.milestone_id == milestone and (bug.accepted_blocker or bug.accepted_0day or bug.accepted_fe) %}[⧣{{ bug.bugid }}]({{ bug.url }}){% if not loop.last %}, {% endif %}{% endfor %}

  {%- endfor %}

- {% endautoescape %}

  

+ #### Releng commands ####

+ {% if (freeze_blocker_updates + freeze_fe_updates) and ns.safe_cmd %}

+ Make sure all builds are pending stable:

+ ```

+ blockers=(

+ {% for update in freeze_blocker_updates -%}

+ {{ update.updateid }}

+ {% endfor -%}

+ )

+ fes=(

+ {% for update in freeze_fe_updates -%}

+ {{ update.updateid }}

+ {% endfor -%}

+ )

+ updates=$(echo "${blockers[@]} ${fes[@]}" | xargs echo -n)  # trim whitespace

+ sudo -u apache bodhi-push --updates "${updates// /,}"

+ ```

+ {%- endif %}

  

  {% if bugs_with_deps -%}

- ### THE ABOVE MIGHT NOT BE COMPLETE - CHECK BUG DEPENDENCIES ###

+ ## THE ABOVE MIGHT NOT BE COMPLETE - CHECK BUG DEPENDENCIES ##

  

  The following accepted bugs depend on other bugs, and those dependant bugs should be checked manually for pending updates:

  {% for bug in bugs_with_deps %}

- ({{ bug.bugid }}) {{ bug.summary }}

+ (⧣{{ bug.bugid }}) {{ bug.summary }}

  {{ bug.url }}

  {% endfor %}

  {% endif %}

+ {% endautoescape %}

file modified
+40 -17
@@ -62,6 +62,8 @@ 

  '''The next zen line to use'''

  virtues_counter = 0

  '''The next virtues line to use'''

+ pkg_counter = 0

+ ''' This makes generated package names different '''

  

  current_date = datetime.datetime.utcnow()

  month_old_date = current_date - datetime.timedelta(days=30)
@@ -93,13 +95,14 @@ 

  

  

  def add_bug(bugid, milestone, summary=None, status='NEW', active=True, needinfo=False,

-             needinfo_requestee=None, depends_on=[], last_whiteboard_change=month_old_date,

+             needinfo_requestee=None, depends_on=None, last_whiteboard_change=month_old_date,

              last_bug_sync=month_old_date, **kwargs):

      """Create a new Bug and return it. Use `**kwargs` for specifying additional attributes not

      exposed in the Bug constructor.

      """

+     depends_on = depends_on or []

      bug = Bug(bugid=bugid,

-               url=f'http://localhost/testbug_{bugid}',

+               url=f'http://localhost/bug/{bugid}',

                summary=summary or get_zen(),

                status=status,

                component=get_virtue(),
@@ -115,16 +118,26 @@ 

      return bug

  

  

- def add_update(updateid_num, release, bugs, status='testing', karma=0,

-                date_submitted=month_old_date, request=None, title=None, stable_karma=3):

+ def add_update(updateid_hash: str, release: Release, bugs: list[Bug], status: str = 'testing',

+                karma: int = 0, date_submitted: datetime.datetime = month_old_date,

+                request: str | None = None, stable_karma: int | None = 3):

      """Create a new Update and return it.

      """

-     updateid = f'FEDORA-101-{updateid_num}'

+     updateid = f'FEDORA-{release.number}-{updateid_hash}'

+ 

+     # the update title consists of space-delimited NVRs

+     global pkg_counter

+     nvrs = []

+     for _ in bugs:

+         pkg_counter += 1

+         nvrs.append(f'pkg{pkg_counter}-1-1.fc{release.number}')

+     title = ' '.join(nvrs) or updateid

+ 

      update = Update(updateid=updateid,

                      release=release,

                      status=status,

                      karma=karma,

-                     url=f'http://localhost/testupdate_{updateid}',

+                     url=f'http://localhost/updates/{updateid}',

                      date_submitted=date_submitted,

                      request=request,

                      title=title,
@@ -210,6 +223,10 @@ 

      {"betablocker": {"-1": ["person1"], "0": [], "+1": ["person2", "person3"]}}

      '''

      db.session.add(bug108)

+     bug109 = add_bug(109, beta_milestone, accepted_blocker=True)

+     db.session.add(bug109)

+     bug110 = add_bug(110, beta_milestone, accepted_fe=True)

+     db.session.add(bug110)

      # -- final bugs

      bug200 = add_bug(200, final_milestone, proposed_blocker=True)

      db.session.add(bug200)
@@ -222,28 +239,34 @@ 

  

      # create updates

      # -- updates for beta bugs

-     update1 = add_update(1, release, [bug101])

+     update1 = add_update('1', release, [bug101])

      db.session.add(update1)

-     update2 = add_update(2, release, [bug108], request='stable', karma=5)

+     update2 = add_update('2', release, [bug108], request='stable', karma=5)

      db.session.add(update2)

-     update3 = add_update(3, release, [bug104], status='stable', stable_karma=0)

+     update3 = add_update('3', release, [bug104], status='stable', stable_karma=0)

      db.session.add(update3)

-     update4 = add_update(4, release, [bug102], status='pending', request='testing',

-                          title='Sunshine tweak update')

+     update4 = add_update('4', release, [bug102], status='pending', request='testing')

      db.session.add(update4)

-     update5 = add_update(5, release, [bug101], status='pending', date_submitted=month_old_date,

+     update5 = add_update('5', release, [bug101], status='pending', date_submitted=month_old_date,

                           stable_karma=0, karma=-2)

      db.session.add(update5)

-     update6 = add_update(6, release, [bug106], status='pending')

+     update6 = add_update('6', release, [bug106], status='pending')

      db.session.add(update6)

-     update7 = add_update(7, release, [bug107])

+     update7 = add_update('7', release, [bug107])

      db.session.add(update7)

+     # Fix both a blocker and an FE

+     update8 = add_update('8', release, [bug109, bug106], status='pending')

+     db.session.add(update8)

+     update9 = add_update('9', release, [bug110], request='stable')

+     update9.title += f' otherpkg1-1.1.fc{release_num}'

+     db.session.add(update9)

      # -- updates for final bugs

-     update20 = add_update(20, release, [bug201])

+     update20 = add_update('20', release, [bug201])

      db.session.add(update20)

      # -- special updates

-     update90 = add_update(90, release, [bug101, bug102, bug201], status='pending', request='stable',

-                           title='Update for several bugs and milestones')

+     # Update for several bugs and milestones

+     update90 = add_update('90', release, [bug101, bug102, bug201], status='pending',

+                           request='stable')

      db.session.add(update90)

      # save

      db.session.commit()

file modified
+1 -1
@@ -132,7 +132,7 @@ 

          update = data[-1]

          assert update['updateid'] == u'test-pending-stable.fc99'

          assert update['title'] == 'mega fixer'

-         assert update['url'] == u'http://localhost/update'

+         assert update['url'] == f'https://bodhi.fedoraproject.org/updates/{update["updateid"]}'

          assert update['karma'] == 1

          assert update['stable_karma'] == 3

          assert update['status'] == 'testing'

file modified
+148 -77
@@ -10,6 +10,9 @@ 

  from blockerbugs import app, db

  from blockerbugs.controllers import main

  

+ pkg_counter = 0

+ ''' This makes generated package names different '''

+ 

  

  def add_release(number=99):

      test_release = Release(number)
@@ -29,7 +32,7 @@ 

  

  def add_bug(bugid, summary, milestone):

      test_bug = Bug(bugid=bugid,

-                    url='https://bugzilla.redhat.com/show_bug.cgi?id=%d' % bugid,

+                    url=f'https://bugzilla.redhat.com/show_bug.cgi?id={bugid}',

                     summary=summary,

                     status='NEW',

                     component='testcomponent',
@@ -43,15 +46,22 @@ 

      return test_bug

  

  

- def add_update(updateid, release, status, bugs=[]):

+ def add_update(updateid, release, status, bugs=None):

+     bugs = bugs or []

+     nvrs = []

+     global pkg_counter

+     for i, bug in enumerate(bugs):

+         pkg_counter += 1

+         nvrs.append(f'pkg{pkg_counter}-{i+1}-1.fc{release.number}')

+     title = ' '.join(nvrs) or updateid

      test_update = Update(updateid=updateid,

                           release=release,

                           status=status,

                           karma=1,

-                          url='http://localhost/update',

+                          url=f'https://bodhi.fedoraproject.org/updates/{updateid}',

                           date_submitted=datetime.datetime.utcnow(),

                           request=None,

-                          title=None,

+                          title=title,

                           stable_karma=3,

                           bugs=bugs)

      db.session.add(test_update)
@@ -74,35 +84,45 @@ 

      return True

  

  

- def split_requests(rendered_requests: str) -> tuple[str, str, str, str, str]:

+ def split_requests(rendered_requests: str) -> tuple[str, str, str, str, str, str, str]:

      """Take a rendered 'requests.txt' template, split it into parts and return them.

  

-     :returns: (compose_blockers, compose_fes, push_blockers, push_fes, deps)

+     :returns: (compose_blockers, compose_fes, compose_cmds, push_blockers, push_fes, push_cmds,

+                deps)

      """

-     index_fp = rendered_requests.index('### FREEZE PUSH ###')

+     index_push = rendered_requests.index('## FREEZE PUSH ##')

      try:

-         index_deps = rendered_requests.index('### THE ABOVE MIGHT NOT BE COMPLETE')

+         index_deps = rendered_requests.index('## THE ABOVE MIGHT NOT BE COMPLETE')

      except ValueError:

          # deps section might not be present

          index_deps = len(rendered_requests)

-     compose = rendered_requests[:index_fp]

-     assert '### CANDIDATE COMPOSE ###' in compose

-     push = rendered_requests[index_fp:index_deps]

-     assert '### FREEZE PUSH ###' in push

+     compose = rendered_requests[:index_push]

+     assert '## CANDIDATE COMPOSE ##' in compose

+     push = rendered_requests[index_push:index_deps]

+     assert '## FREEZE PUSH ##' in push

      deps = rendered_requests[index_deps:]

-     assert '### THE ABOVE MIGHT NOT BE COMPLETE' in deps or not deps

-     index_fe = compose.index('== Freeze exceptions ==')

+     assert '## THE ABOVE MIGHT NOT BE COMPLETE' in deps or not deps

+ 

+     index_fe = compose.index('#### Freeze exceptions ####')

+     index_cmd = compose.index('#### Releng commands ####')

      compose_blockers = compose[:index_fe]

-     assert '== Blockers ==' in compose_blockers

-     compose_fes = compose[index_fe:]

-     assert '== Freeze exceptions ==' in compose_fes

-     index_fe = push.index('== Freeze exceptions ==')

+     assert '#### Blockers ####' in compose_blockers

+     compose_fes = compose[index_fe:index_cmd]

+     assert '#### Freeze exceptions ####' in compose_fes

+     compose_commands = compose[index_cmd:]

+     assert '#### Releng commands ####' in compose_commands

+ 

+     index_fe = push.index('#### Freeze exceptions ####')

+     index_cmd = push.index('#### Releng commands ####')

      push_blockers = push[:index_fe]

-     assert '== Blockers ==' in push_blockers

-     push_fes = push[index_fe:]

-     assert '== Freeze exceptions ==' in push_fes

- 

-     all_sections = (compose_blockers, compose_fes, push_blockers, push_fes, deps)

+     assert '#### Blockers ####' in push_blockers

+     push_fes = push[index_fe:index_cmd]

+     assert '#### Freeze exceptions ####' in push_fes

+     push_commands = push[index_cmd:]

+     assert '#### Releng commands ####' in push_commands

+ 

+     all_sections = (compose_blockers, compose_fes, compose_commands, push_blockers, push_fes,

+                     push_commands, deps)

      assert len(rendered_requests) == len(''.join(all_sections))

      return all_sections

  
@@ -297,20 +317,14 @@ 

  

      def test_get_milestone_updates(self):

          updates = main.get_milestone_updates(self.milestone)

-         assert set(updates) == set([self.update_pending_stable,

-                                     self.update_stable1,

-                                     self.update_pending_testing,

-                                     self.update_testing1,

-                                     self.update_complex,

-                                     self.update_complexfe

-                                     ])

+         assert set(updates) == {self.update_pending_stable, self.update_stable1,

+                                 self.update_pending_testing, self.update_testing1,

+                                 self.update_complex, self.update_complexfe}

  

      def test_get_updates_nonstable_blockers(self):

          updates = main.get_updates_nonstable_blockers(self.milestone)

-         assert set(updates) == set([self.update_pending_stable,

-                                     self.update_pending_testing,

-                                     self.update_testing1

-                                     ])

+         assert set(updates) == {self.update_pending_stable, self.update_pending_testing,

+                                 self.update_testing1}

  

      def test_get_updates_nonstable_FEs(self):

          updates = main.get_updates_nonstable_FEs(self.milestone)
@@ -322,7 +336,7 @@ 

  

      def test_requests(self):

          # we also test the requests template generation here, as it's

-         # what the get_* queries provide and it makes sense to do it

+         # what the get_* queries provide, and it makes sense to do it

          # with all these bits in place

          with app.app_context():

              beta_response = self.client.get(
@@ -337,58 +351,115 @@ 

          print(beta_requests)

          # split up the text

          all_sections = split_requests(beta_requests)

-         compose_blockers, compose_fes, push_blockers, push_fes, deps = all_sections

+         compose_blockers, compose_fes, compose_cmds, push_blockers, push_fes, push_cmds, \

+             deps = all_sections

  

          # check all the right updates and bugs are and are not in the correct sections

-         assert item_just_in("test-pending-stable", [compose_blockers, push_blockers], all_sections)

-         assert item_just_in("test-stable1", [], all_sections)

-         assert item_just_in("test-stable2", [], all_sections)

-         assert item_just_in("test-pending-testing", [compose_blockers], all_sections)

-         assert item_just_in("test-testing1", [compose_blockers], all_sections)

-         assert item_just_in("test-testing2", [], all_sections)

-         assert item_just_in("test-complex1", [compose_fes], all_sections)

-         assert item_just_in("test-complexfe", [], all_sections)

-         # bug 1 id

-         assert item_just_in("9000", [compose_blockers, push_blockers, deps], all_sections)

-         # bug 2 id

-         assert item_just_in("9001", [], all_sections)

-         # finalbug id

-         assert item_just_in("9002", [], all_sections)

-         # betafebug id

-         assert item_just_in("9003", [compose_fes], all_sections)

-         # propbetafebug id

-         assert item_just_in("9004", [], all_sections)

-         # finalfebug id

-         assert item_just_in("9005", [], all_sections)

+         # self.update_pending_stable

+         assert item_just_in(self.update_pending_stable.updateid, [compose_blockers, push_blockers,

+                                                                   push_cmds], all_sections)

+         for build in self.update_pending_stable.title.split(' '):

+             assert item_just_in(build, [compose_blockers, compose_cmds, push_blockers],

+                                 all_sections)

+         # self.update_stable1

+         assert item_just_in(self.update_stable1.updateid, [], all_sections)

+         for build in self.update_stable1.title.split(' '):

+             assert item_just_in(build, [], all_sections)

+         # self.update_stable2

+         assert item_just_in(self.update_stable2.updateid, [], all_sections)

+         for build in self.update_stable2.title.split(' '):

+             assert item_just_in(build, [], all_sections)

+         # self.update_pending_testing

+         assert item_just_in(self.update_pending_testing.updateid, [compose_blockers],

+                             all_sections)

+         for build in self.update_pending_testing.title.split(' '):

+             assert item_just_in(build, [compose_blockers, compose_cmds], all_sections)

+         # self.update_testing1

+         assert item_just_in(self.update_testing1.updateid, [compose_blockers], all_sections)

+         for build in self.update_testing1.title.split(' '):

+             assert item_just_in(build, [compose_blockers, compose_cmds], all_sections)

+         # self.update_testing2

+         assert item_just_in(self.update_testing2.updateid, [], all_sections)

+         for build in self.update_testing2.title.split(' '):

+             assert item_just_in(build, [], all_sections)

+         # self.update_complex

+         assert item_just_in(self.update_complex.updateid, [compose_fes], all_sections)

+         for build in self.update_complex.title.split(' '):

+             assert item_just_in(build, [compose_fes, compose_cmds], all_sections)

+         # self.update_complexfe

+         assert item_just_in(self.update_complexfe.updateid, [], all_sections)

+         for build in self.update_complexfe.title.split(' '):

+             assert item_just_in(build, [], all_sections)

+         # self.bug1

+         assert item_just_in(str(self.bug1.bugid), [compose_blockers, push_blockers, deps],

+                             all_sections)

+         # self.bug2

+         assert item_just_in(str(self.bug2.bugid), [], all_sections)

+         # self.finalbug

+         assert item_just_in(str(self.finalbug.bugid), [], all_sections)

+         # self.betafebug

+         assert item_just_in(str(self.betafebug.bugid), [compose_fes], all_sections)

+         # self.propbetafebug

+         assert item_just_in(str(self.propbetafebug.bugid), [], all_sections)

+         # self.finalfebug

+         assert item_just_in(str(self.finalfebug.bugid), [], all_sections)

  

          # === final milestone ===

          final_requests = final_response.get_data(as_text=True)

          print(final_requests)

          # split up the text

          all_sections = split_requests(final_requests)

-         compose_blockers, compose_fes, push_blockers, push_fes, deps = all_sections

+         compose_blockers, compose_fes, compose_cmds, push_blockers, push_fes, push_cmds, \

+             deps = all_sections

  

          # check all the right updates and bugs are and are not in the correct sections

-         assert item_just_in("test-pending-stable", [compose_blockers, push_blockers], all_sections)

-         assert item_just_in("test-stable1", [], all_sections)

-         assert item_just_in("test-stable2", [], all_sections)

-         assert item_just_in("test-pending-testing", [], all_sections)

-         assert item_just_in("test-testing1", [], all_sections)

-         assert item_just_in("test-testing2", [], all_sections)

-         assert item_just_in("test-complex1", [compose_blockers], all_sections)

-         assert item_just_in("test-complexfe", [compose_fes], all_sections)

-         # bug 1 id

-         assert item_just_in("9000", [], all_sections)

-         # bug 2 id

-         assert item_just_in("9001", [], all_sections)

-         # finalbug id

-         assert item_just_in("9002", [compose_blockers, push_blockers], all_sections)

-         # betafebug id

-         assert item_just_in("9003", [], all_sections)

-         # propbetafebug id

-         assert item_just_in("9004", [], all_sections)

-         # finalfebug id

-         assert item_just_in("9005", [compose_fes], all_sections)

+         # self.update_pending_stable

+         assert item_just_in(self.update_pending_stable.updateid, [compose_blockers, push_blockers,

+                                                                   push_cmds], all_sections)

+         for build in self.update_pending_stable.title.split(' '):

+             assert item_just_in(build, [compose_blockers, compose_cmds, push_blockers],

+                                 all_sections)

+         # self.update_stable1

+         assert item_just_in(self.update_stable1.updateid, [], all_sections)

+         for build in self.update_stable1.title.split(' '):

+             assert item_just_in(build, [], all_sections)

+         # self.update_stable2

+         assert item_just_in(self.update_stable2.updateid, [], all_sections)

+         for build in self.update_stable2.title.split(' '):

+             assert item_just_in(build, [], all_sections)

+         # self.update_pending_testing

+         assert item_just_in(self.update_pending_testing.updateid, [], all_sections)

+         for build in self.update_pending_testing.title.split(' '):

+             assert item_just_in(build, [], all_sections)

+         # self.update_testing1

+         assert item_just_in(self.update_testing1.updateid, [], all_sections)

+         for build in self.update_testing1.title.split(' '):

+             assert item_just_in(build, [], all_sections)

+         # self.update_testing2

+         assert item_just_in(self.update_testing2.updateid, [], all_sections)

+         for build in self.update_testing2.title.split(' '):

+             assert item_just_in(build, [], all_sections)

+         # self.update_complex

+         assert item_just_in(self.update_complex.updateid, [compose_blockers], all_sections)

+         for build in self.update_complex.title.split(' '):

+             assert item_just_in(build, [compose_blockers, compose_cmds], all_sections)

+         # self.update_complexfe

+         assert item_just_in(self.update_complexfe.updateid, [compose_fes], all_sections)

+         for build in self.update_complexfe.title.split(' '):

+             assert item_just_in(build, [compose_fes, compose_cmds], all_sections)

+         # self.bug1

+         assert item_just_in(str(self.bug1.bugid), [], all_sections)

+         # self.bug2

+         assert item_just_in(str(self.bug2.bugid), [], all_sections)

+         # self.finalbug

+         assert item_just_in(str(self.finalbug.bugid), [compose_blockers, push_blockers],

+                             all_sections)

+         # self.betafebug

+         assert item_just_in(str(self.betafebug.bugid), [], all_sections)

+         # self.propbetafebug

+         assert item_just_in(str(self.propbetafebug.bugid), [], all_sections)

+         # self.finalfebug

+         assert item_just_in(str(self.finalfebug.bugid), [compose_fes], all_sections)

  

      def test_display_bug_updates(self):

          with app.app_context():

Based on releng request, this adds a section to Requests showing the exact
commands releng should run. When creating a releng ticket, QA will have to make
sure the FE and Releng sections match, if any of the FEs are filtered out by QA.

This will only work as long as Bodhi defaults to populate each Bodhi update
title with a space-delimited list of NVRs. If they stop doing that, we'll have
to switch to a more proper approach.

Fixes: https://pagure.io/fedora-qa/blockerbugs/issue/275
Related: https://pagure.io/fedora-qa/blockerbugs/pull-request/271

Co-authored-by: Kevin Fenzi kevin@scrye.com
Co-authored-by: František Zatloukal fzatlouk@redhat.com


Here's an example Requests output with my test data:
https://pastebin.com/raw/R7pi5T27

@kevin @adamwill I'll need you guys to make some choices. In the Releng Commands sections, there are a few ALTERNATIVE blocks. I need you to tell me which work best for you. The problem is, I believe Adam doesn't just copy&paste all the available FEs into a compose/freeze push ticket, but handpicks those which look reasonable for that moment. So if we're going to have a Releng Commands section, he would also need to make the same changes there. However, it's not that easy to reliably edit a long bash command with possibly dozens of arguments. So in those ALTERNATIVE sections, I made sure each FE gets its own line. Is that better?

Also, in the Freeze Push section, we have an option to work with update aliases or list of builds. The first feels to be more reliable, but the second makes it probably easier to figure out which FE the current line refers to and whether to keep it or erase it. So which style do we want to have in there?

@kevin Please verify whether the bash commands syntax looks OK. I made some changes from your original PR.

rebased onto af3938f

2 years ago

Looks good. From the releng side it's better is it's just one bodhi push command / compose, so ideally all the blockers and FE's would be in one.
But if that doesn't work for Qe workflow we can still do two composes (blockers, then fe's).

It's not clear from the alternative example, but it would be one bodhi-push for blockers, and then one bodhi-push for each FE. I assumed running a multiple commands shouldn't be a problem. But I don't know exactly what it does. Is it too heavyweight?

Well, there's no actual compose in this case... bodhi just tags packages in and updates updates, the composing is done in the nightly (or rc) after that. So, yeah, it only takes a few minutes. However, it has to be done with no other updates running and so it's nicer if the window is smaller. Either way is probibly ok, but fewer/shorter window is better.

Ok, in that case we'll wait for @adamwill 's opinion. If we decided to have all FEs on a single command line, I think we should go with the bodhi-push --updates version. It's a little more difficult to search for the update alias in the Freeze Exceptions section, but it's much easier to then edit it on the command line, as opposed to editing the bodhi-push --builds one (some updates might have 10+ builds).

I have updated the pastebin link in the original description to include a better example with several FEs.

Another option is to generate a shell script that's easy to edit, and executes everything as a single command. Something roughly like this:

blockers=(
FEDORA-100
FEDORA-101
FEDORA-102
)
fes=(
FEDORA-103
FEDORA-104
FEDORA-105
)
# do some magic to convert spaces to commas
bodhi-push --updates ${blockers[@]},${fes[@]}

This is both reasonably easy to edit and executes as a single command. Is it better?

rebased onto 2efc242

2 years ago

Yes, that would work.

what...might actually work best, honestly...is if there was some magic metadata somewhere to mark an update as 'ready' or 'not ready'. then the commands could just be...the right commands. I'm not sure how exactly we could work that, though. magic text in a bodhi comment? any better ideas?

or...well, maybe we make the requests tab a bit more web UI-ish, and let it have an actual, you know, UI, with a list of updates with checkboxes. and you could uncheck one to drop it from the generated commands.

if there was some magic metadata somewhere to mark an update as 'ready' or 'not ready'

This would include some extra changes (DB schema, pulling the data), but most importantly, it would be subject to the sync delay. And I had the impression that you want to decide which FE is OK to pull in at that very moment when you submit the request. Sometimes an FE is OK one day and not OK the next day, because it's then too late. Also, adding comments to Bodhi sounds ugly (don't forget e.g. about checking permissions), and I don't have a better idea where to place it. Not a fan.

let it have an actual, you know, UI, with a list of updates with checkboxes

This is a good solution, I like it. But it's also a good chunk of work, at least for me, with my poor html and non-existent javascript skills. This was supposed to be a quick and simple change (already wasn't). If we're talking about completely redesigning the Requests page, I don't think it will be ready for the F39 cycle. I have higher priority items on the todo list.

So what if, for the moment, I used the approach displayed here, and you might or might not use it when updating the releng ticket? Depending on the situation. If it's used, it makes life for releng easier, if it's not used, they'll have to manage themselves as they have done in the past. And I'll file a new ticket describing the requested web UI-style improvements, as a separate task. Is it OK, or would you rather not have the releng section included at all (until the usability changes are implemented)?

1 new commit added

  • create Releng commands section as a bash script
a year ago

I'd like to finish this asap, because the F39 cycle started, and our current version deployed to production generates erroneous releng commands in certain cases.

So, I've reworked the releng commands sections into a bash script, as proposed above. This is easy to edit by QA (when creating the ticket), and fast to execute by releng (it's a single command). I've also adjusted the overall structure of the blocker and FE sections and their markup text, so that it's easier to search for important parts (like update aliases).

The new example output is here:
https://pastebin.com/raw/E5Fi6kSn

And you can see it rendered here:
https://stg.pagure.io/fedoraqa-test/issue/382

@kevin @adamwill Please tell me whether this looks acceptable, thanks.

I believe it's definitely an improvement over the existing state. I'll file Adam's RFE (the interactive command generation through checkboxes) as a separate ticket, as a future improvement.

I think that looks ok, but I don't have any way to test it really. ;)

We can give it a try and adjust if needed, but it looks reasonable to me.

I'll file Adam's RFE (the interactive command generation through checkboxes) as a separate ticket, as a future improvement.

Filed as #277.

Commit aceaddd fixes this pull-request

Pull-Request has been merged by kparal

a year ago

I'll try to have this deployed on Monday.

So, because Frantisek is being Frantisek, it has been deployed already. If something breaks, he has the oncall duty over the weekend ;-)

fwiw, I used it throughout Beta and it seems fine. thanks!