Commit 2c142a43 authored by Arkadiusz Hiler's avatar Arkadiusz Hiler

series/revision: Add skip testing button

Add a button that marks the latest revision of a series as "skip
testing". Only the author of the series and maintainers of the project
can perform this action.

The `skip_testing` state is exposed through the JSON API:
/api/1.0/series/{int}/revisions/{int}/

The attached CI/testing infrastructure is responsible for checking this
flag.

The UI shows which revisions were skipped and who has triggered it.

Closes #27Signed-off-by: default avatarArkadiusz Hiler <arkadiusz.hiler@intel.com>
parent db413dd4
Pipeline #109457 passed with stage
in 1 minute and 47 seconds
# Generated by Django 2.2.10 on 2020-02-17 22:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('patchwork', '0032_seriesrevision_completed'),
]
operations = [
migrations.AddField(
model_name='seriesrevision',
name='skip_testing',
field=models.BooleanField(default=False),
),
]
# Generated by Django 2.2.10 on 2020-02-17 23:09
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('patchwork', '0033_seriesrevision_skip_testing'),
]
operations = [
migrations.AddField(
model_name='seriesrevision',
name='skip_requester',
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
]
......@@ -631,6 +631,11 @@ class SeriesRevision(models.Model):
state_summary = jsonfield.JSONField(null=True)
test_state = models.SmallIntegerField(choices=TestState.STATE_CHOICES,
null=True, blank=True)
skip_testing = models.BooleanField(default=False)
skip_requester = models.ForeignKey(User, null=True, default=None,
on_delete=models.SET_NULL)
is_rerun = models.BooleanField(default=False)
completed = models.DateTimeField(null=True, default=None)
......@@ -693,6 +698,8 @@ class SeriesRevision(models.Model):
new.raw_cover_letter_headers = self.raw_cover_letter_headers
new.version = self.version + 1
new.test_state = None
new.skip_testing = False
new.skip_requester = None
new.save()
return new
......
......@@ -50,3 +50,6 @@ class Can:
series.submitter.user == self.user)
return False
def skip_testing(self, obj):
return self.retest(obj)
......@@ -191,7 +191,8 @@ class RevisionSerializer(PatchworkModelSerializer):
class Meta:
model = SeriesRevision
fields = ('version', 'cover_letter', 'raw_cover_letter', 'patches')
fields = ('version', 'cover_letter', 'raw_cover_letter', 'patches',
'skip_testing')
read_only_fields = ('version', 'cover_letter')
expand_serializers = {
'patches': PatchSerializer,
......
......@@ -60,6 +60,37 @@ $(document).ready(function() {
pw.post_data(url, undefined, on_post_success, on_post_failure);
});
$("#skip-latest").click(function(e) {
e.preventDefault();
var button = $(this);
button.prop('disabled', true);
button.removeClass("btn-info");
button.addClass("btn-warning");
button.text("requesting retest");
var on_post_success = function() {
button.removeClass("btn-warning");
button.addClass("btn-success");
button.text("revision skipped");
};
var on_post_failure = function() {
button.removeClass("btn-warning");
button.addClass("btn-failure");
button.text("failed!");
};
var series = button.data('series');
var revision = button.data('revision');
var url = '/series/' + series + '/revisions/' + revision + '/skiptesting/';
pw.post_data(url, undefined, on_post_success, on_post_failure);
});
{% else %}
var retest_container = $("#retest-latest-container");
retest_container.fadeTo(0, 0.2); // immediate "transition"
......@@ -135,6 +166,16 @@ $(document).ready(function() {
data-series="{{ series.id }}" data-revision="{{ series.last_revision.version }}">
test revision {{ series.last_revision.version }} again
</button>
{% if series.last_revision.skip_testing %}
<button class="btn btn-danger disabled">
testing of revision {{ series.last_revision.version }} skipped!
</button>
{% else %}
<button class="btn btn-warning" id="skip-latest"
data-series="{{ series.id }}" data-revision="{{ series.last_revision.version }}">
skip revision {{ series.last_revision.version }} testing
</button>
{% endif %}
{% else %}
<button class="btn btn-danger disabled" id="retest-latest"
data-series="{{ series.id }}" data-revision="{{ series.last_revision.version }}">
......@@ -156,6 +197,12 @@ $(document).ready(function() {
<div role="tabpanel" id="rev{{ revision.version }}"
class="tab-pane fade{% if forloop.last %} in active{% endif %}">
{% if revision.skip_testing %}
<div class="alert alert-danger">
<strong>TESTING OF THIS REVISION WAS SKIPPED.</strong> Requested by: {{ revision.skip_requester }}
</div>
{% endif %}
{% if revision.is_rerun %}
<div class="alert alert-info">
<strong>THIS SERIES REVISION IS A RERUN.</strong> Please don't overuse the "test again" button.
......
......@@ -349,6 +349,19 @@ class RevisionViewSet(mixins.ListModelMixin, ListMixin,
new_rev.save()
return HttpResponse()
@action(detail=True, methods=['post'])
def skiptesting(self, request, series_pk=None, pk=None):
rev = get_object_or_404(SeriesRevision, series=series_pk, version=pk)
if not Can(request.user).skip_testing(rev.series):
raise PermissionDenied
rev.skip_testing = True
rev.skip_requester = request.user
rev.save()
return HttpResponse()
class ResultMixin(object):
......
......@@ -52,6 +52,9 @@ class SeriesView(View):
cover_letters = [(r.version, r.cover_letter)
for r in revisions if r.cover_letter]
for rev in revisions:
print(rev.skip_requester)
return render(request, 'patchwork/series.html', {
'series': series,
'project': project,
......
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