Skip to content

Commit

Permalink
Add autopush and stable_days to an update.
Browse files Browse the repository at this point in the history
This commit adds an autopush boolean and stable_days integer
to an update. When this boolean is true the approve_testing
cronjob will push the update to stable if it has meet the
testing requirements (stable_days).

stable_days cannot be smaller than the release
mandatory_days_in_testing.

Signed-off-by: Clement Verna <cverna@tutanota.com>
  • Loading branch information
cverna committed Apr 1, 2019
1 parent 657e20a commit 3fd90bc
Show file tree
Hide file tree
Showing 12 changed files with 363 additions and 49 deletions.
3 changes: 3 additions & 0 deletions bodhi/client/__init__.py
Expand Up @@ -107,6 +107,9 @@ def _set_logging_debug(ctx, param, value):
click.option('--close-bugs', is_flag=True, help='Automatically close bugs'),
click.option('--request', help='Requested repository',
type=click.Choice(['testing', 'stable', 'unpush'])),
click.option('--autopush', is_flag=True, help='Enable stable push base on time in testing'),
click.option('--stable-days', type=click.INT,
help='Day in testing required to push to stable'),
click.option('--autokarma', is_flag=True, help='Enable karma automatism'),
click.option('--stable-karma', type=click.INT, help='Stable karma threshold'),
click.option('--unstable-karma', type=click.INT, help='Unstable karma threshold'),
Expand Down
5 changes: 5 additions & 0 deletions bodhi/client/bindings.py
Expand Up @@ -241,6 +241,11 @@ def save(self, **kwargs):
(``reboot``, ``logout``).
inheritance (bool): Follow koji build inheritance, which may result in
this update being pushed out to additional releases.
autopush (bool): Allow bodhi to automatically change the state of this
update based on the time spent in testing by this update. It
will push your update to ``stable`` once it reaches the ``stable_days``.
stable_days (int): The minimun amount of time an update has to spend in
``testing`` before being automatically pushed to ``stable``.
autokarma (bool): Allow bodhi to automatically change the state of this
update based on the ``karma`` from user feedback. It will
push your update to ``stable`` once it reaches the ``stable_karma``
Expand Down
@@ -0,0 +1,67 @@
# Copyright (c) 2019
#
# This file is part of Bodhi.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""
Add autopush boolean and stable_days to Update.
Revision ID: b1fd856efcf6
Revises: 58b7919b942c
Create Date: 2019-03-22 09:51:53.941289
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'b1fd856efcf6'
down_revision = '58b7919b942c'


def upgrade():
"""Add the autopush boolean and stable_days integer to the updates table."""
# autopush
op.add_column('updates', sa.Column('autopush', sa.Boolean()))
op.execute('UPDATE updates SET autopush=FALSE')
op.alter_column('updates', 'autopush', existing_type=sa.Boolean(), nullable=False)

# stable_days
op.add_column('updates', sa.Column('stable_days', sa.Integer()))
# set stable_days to 0 for old updates
op.execute(
"UPDATE updates SET stable_days=0 "
"WHERE updates.status IN ('stable', 'obsolete', 'unpushed')"
)
# set stable_days to 7 for non critpath FEDORA updates
op.execute(
"UPDATE updates SET stable_days=7 "
"WHERE updates.status NOT IN ('stable', 'obsolete', 'unpushed')"
"AND updates.critpath = FALSE AND updates.release_id NOT IN (8, 9, 10)" # EPEL releases
)
# set stable_days to 14 for critpath FEDORA updates or EPEL updates.
op.execute(
"UPDATE updates SET stable_days=14 "
"WHERE updates.status NOT IN ('stable', 'obsolete', 'unpushed')"
"AND updates.critpath = TRUE OR updates.release_id IN (8, 9, 10)" # EPEL releases
)

op.alter_column('updates', 'stable_days', existing_type=sa.Integer(), nullable=False)


def downgrade():
"""Drop the autopush boolean and the stable_days integer from the updates table."""
op.drop_column('updates', 'autopush')
op.drop_column('updates', 'stable_days')
32 changes: 29 additions & 3 deletions bodhi/server/models.py
Expand Up @@ -1559,8 +1559,12 @@ class Update(Base):
Attributes:
autokarma (bool): A boolean that indicates whether or not the update will
be automatically pushed when the stable_karma threshold is reached.
autopush (bool): A boolean that indicates whether or not the update will
be automatically pushed when the time threshold is reached.
stable_karma (int): A positive integer that indicates the amount of "good"
karma the update must receive before being automatically marked as stable.
stable_days (int): A positive integer that indicates the number of days an update
needs to spend in testing before being automatically marked as stable.
unstable_karma (int): A positive integer that indicates the amount of "bad"
karma the update must receive before being automatically marked as unstable.
requirements (unicode): A list of taskotron tests that must pass for this
Expand Down Expand Up @@ -1629,7 +1633,9 @@ class Update(Base):
__get_by__ = ('alias',)

autokarma = Column(Boolean, default=True, nullable=False)
autopush = Column(Boolean, default=False, nullable=False)
stable_karma = Column(Integer, nullable=False)
stable_days = Column(Integer, nullable=False)
unstable_karma = Column(Integer, nullable=False)
requirements = Column(UnicodeText)
require_bugs = Column(Boolean, default=False)
Expand Down Expand Up @@ -2005,6 +2011,16 @@ def new(cls, request, data):
release = data.pop('release', None)
up = Update(**data, release=release)

# stable_days can be set by the user we want to make sure that the value
# will not be lower than the mandatory_days_in_testing.
if up.mandatory_days_in_testing > up.stable_days:
up.stable_days = up.mandatory_days_in_testing
caveats.append({
'name': 'stable days',
'description': "The number of stable days required was set to the mandatory "
f"release value {up.mandatory_days_in_testing} days"
})

log.debug("Setting request for new update.")
up.set_request(db, req, request.user.name)

Expand Down Expand Up @@ -2043,6 +2059,16 @@ def edit(cls, request, data):
caveats = []
edited_builds = [build.nvr for build in up.builds]

# stable_days can be set by the user we want to make sure that the value
# will not be lower than the mandatory_days_in_testing.
if up.mandatory_days_in_testing > data.get('stable_days', up.stable_days):
data['stable_days'] = up.mandatory_days_in_testing
caveats.append({
'name': 'stable days',
'description': "The number of stable days required was set to the mandatory "
f"release value {up.mandatory_days_in_testing} days"
})

# Determine which builds have been added
new_builds = []
for build in data['builds']:
Expand Down Expand Up @@ -3330,7 +3356,7 @@ def meets_testing_requirements(self):
Returns:
bool: True if the update meets testing requirements, False otherwise.
"""
num_days = self.mandatory_days_in_testing
num_days = self.stable_days

if config.get('test_gating.required') and not self.test_gating_passed:
return False
Expand Down Expand Up @@ -3371,7 +3397,7 @@ def met_testing_requirements(self):
Returns:
bool: See description above for what the bool might mean.
"""
min_num_days = self.mandatory_days_in_testing
min_num_days = self.stable_days
if min_num_days:
if not self.meets_testing_requirements:
return False
Expand Down Expand Up @@ -3400,7 +3426,7 @@ def days_to_stable(self):
determined.
"""
if not self.meets_testing_requirements and self.date_testing:
num_days = (self.mandatory_days_in_testing - self.days_in_testing)
num_days = (self.stable_days - self.days_in_testing)
if num_days > 0:
return num_days
return 0
Expand Down
9 changes: 9 additions & 0 deletions bodhi/server/schemas.py
Expand Up @@ -239,6 +239,15 @@ class SaveUpdateSchema(CSRFProtectedSchema, colander.MappingSchema):
colander.Boolean(),
missing=True,
)
autopush = colander.SchemaNode(
colander.Boolean(),
missing=True,
)
stable_days = colander.SchemaNode(
colander.Integer(),
validator=colander.Range(min=1),
missing=0,
)


class Cosmetics(colander.MappingSchema):
Expand Down
50 changes: 38 additions & 12 deletions bodhi/server/scripts/approve_testing.py
Expand Up @@ -28,7 +28,7 @@

from pyramid.paster import get_appsettings

from ..models import Update, UpdateStatus
from ..models import Update, UpdateStatus, UpdateRequest
from ..config import config
from bodhi.server import Session, initialize_db, notifications

Expand All @@ -50,6 +50,23 @@ def usage(argv):
sys.exit(1)


def update_to_stable(update: Update, db: Session):
"""
Request an update to be pushed to stable.
Args:
update: The update object that will be pushed to stable.
db: A database session.
"""
print(f"Automatically marking {update.alias} as stable")
update.set_request(db=db, action=UpdateRequest.stable, username="bodhi")
update.date_pushed = None
notifications.publish(
topic='update.requirements_met.stable',
msg=dict(update=update, status='stable')
)


def main(argv=sys.argv):
"""
Comment on updates that are eligible to be pushed to stable.
Expand All @@ -75,10 +92,14 @@ def main(argv=sys.argv):
testing = db.query(Update).filter_by(status=UpdateStatus.testing,
request=None)
for update in testing:
# If this release does not have any testing requirements, skip it
if not update.release.mandatory_days_in_testing:
print('%s doesn\'t have mandatory days in testing' % update.release.name)
continue
if not update.autopush:
# If this release does not have any testing requirements and is not autopush,
# skip it
print('%s doesn\'t have mandatory days in testing' % update.release.name)
continue
# if the release is configured to autopush, push it to stable.
update_to_stable(update, db)

# If this has already met testing requirements, skip it
if update.met_testing_requirements:
Expand All @@ -100,14 +121,19 @@ def main(argv=sys.argv):
# not reached the karma threshold.
if update.meets_testing_requirements:
print(f'{update.alias} now meets testing requirements')
text = str(
config.get('testing_approval_msg') % update.mandatory_days_in_testing)
update.comment(db, text, author=u'bodhi')

notifications.publish(
topic='update.requirements_met.stable',
msg=dict(update=update))
db.commit()
if update.autopush:
update_to_stable(update, db)
else:
text = str(
config.get('testing_approval_msg') % update.stable_days
)
update.comment(db, text, author=u'bodhi')

notifications.publish(
topic='update.requirements_met.stable',
msg=dict(update=update)
)
db.commit()

except Exception as e:
print(str(e))
Expand Down
39 changes: 29 additions & 10 deletions bodhi/server/templates/new_update.html
Expand Up @@ -217,20 +217,29 @@ <h2 class="pull-left">Edit
checked
% endif
></dd>
<dt data-toggle="tooltip" title="If checked, this option allows bodhi to automatically move your update from testing to stable once enough positive karma has been given.">
Auto-request stable based on karma ?</dt>
<dd> <input type="checkbox" name="autokarma" data-singleton="true"
% if update and update.autokarma:
checked
% elif not update:
checked
% endif>
</dd>
<dt data-toggle="tooltip" title="If checked, this option allows bodhi to automatically move your update from testing to stable once enough time was spent in testing.">
Auto-request stable based on time ?</dt>
<dd> <input type="checkbox" name="autopush" data-singleton="true"
% if update and update.autopush:
checked
% elif not update:
checked
% endif>
</dd>
</dl>
</div>

<div class="col-md-5">
<dl id='radios'>
<dt data-toggle="tooltip" title="If checked, this option allows bodhi to automatically move your update from testing to stable once enough positive karma has been given.">
Auto-request stable?</dt>
<dd> <input type="checkbox" name="autokarma" data-singleton="true"
% if update and update.autokarma:
checked
% elif not update:
checked
% endif
></dd>
<dl>
<dt data-toggle="tooltip" title="This is the threshold of positive karma required to automatically push an update from testing to stable.">
Stable karma</dt>
<dd> <input type="number" name="stable_karma" min="1" required
Expand All @@ -251,6 +260,16 @@ <h2 class="pull-left">Edit
% endif
></dd>

<dt data-toggle="tooltip" title="This is required number of days an update needs to spend in testing to automatically upush an update to stable.">
Stable days</dt>
<dd> <input type="number" name="stable_days" min="1"
% if update:
value="${update.stable_days}"
% elif not update:
value=""
% endif
></dd>

<dt data-toggle="tooltip" title="If checked, this will require that positive feedback be given on all associated bugs before the update can pass to stable. If your update has no associated bugs, this option has no effect.">
Require bugs
</dt>
Expand Down

0 comments on commit 3fd90bc

Please sign in to comment.