From 37b79940914b1c11299d418ab1db9806721a80b0 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Tue, 14 Jan 2025 17:11:07 +0100 Subject: [PATCH] MT#61875 build: support failed BuildRelease In order to allow to keep information of failed builds. Before we had to remove the info from a failed build in order to trigger another full build. Change-Id: I7cfcbedecdfed81951391fcdd84bb5be3c1b9e24 --- build/migrations/0007_buildrelease_failed.py | 17 +++++++ build/models.py | 3 ++ build/views.py | 5 ++ panel/static/panel/js/panel_release.js | 40 +++++++++++++++ panel/templates/panel/release_info.html | 4 ++ .../release_dashboard/build_release.html | 50 ++++++++++++++++++- 6 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 build/migrations/0007_buildrelease_failed.py diff --git a/build/migrations/0007_buildrelease_failed.py b/build/migrations/0007_buildrelease_failed.py new file mode 100644 index 0000000..f183c66 --- /dev/null +++ b/build/migrations/0007_buildrelease_failed.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.18 on 2025-01-14 16:05 +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + dependencies = [ + ("build", "0006_alter_buildrelease_options"), + ] + + operations = [ + migrations.AddField( + model_name="buildrelease", + name="failed", + field=models.BooleanField(default=False), + ), + ] diff --git a/build/models.py b/build/models.py index e1cc114..4e159b1 100644 --- a/build/models.py +++ b/build/models.py @@ -151,6 +151,7 @@ class BuildRelease(models.Model): failed_projects = models.TextField(null=True, editable=False) pool_size = models.SmallIntegerField(default=0, editable=False) triggered_jobs = models.TextField(null=True, editable=False) + failed = models.BooleanField(default=False, editable=True) objects = BuildReleaseManager() class Meta: @@ -197,6 +198,8 @@ class BuildRelease(models.Model): @property def done(self): + if self.failed: + return True if self.built_projects is None: return False built_len = len(self.built_projects) diff --git a/build/views.py b/build/views.py index cf3bbc7..7c5219f 100644 --- a/build/views.py +++ b/build/views.py @@ -88,6 +88,11 @@ class BuildReleaseDetail(generics.RetrieveDestroyAPIView): instance.resume() serializer = self.get_serializer(instance) return Response(serializer.data) + elif action == "failed": + instance.failed = True + instance.save() + serializer = self.get_serializer(instance) + return Response(serializer.data) return JsonResponse({"error": "Action unknown"}, status=400) diff --git a/panel/static/panel/js/panel_release.js b/panel/static/panel/js/panel_release.js index 6f47670..63de6cb 100644 --- a/panel/static/panel/js/panel_release.js +++ b/panel/static/panel/js/panel_release.js @@ -58,6 +58,46 @@ function resume_build( id ) { } ); } +/* eslint-disable-next-line no-unused-vars*/ // used at onClick +function click_failed( e, id ) { + mark_build_failed( id ); + e.preventDefault(); +} + +function mark_build_failed( id ) { + + function successFunc( _data, _textStatus, _jqXHR ) { + $( "#failed" ).prop( "disabled", true ); + $( "#resume" ).prop( "disabled", true ); + } + + function errorFunc( _jqXHR, _status, error ) { + $( "#release_error" ).html( error ); + } + var csrftoken = jQuery( "[name=csrfmiddlewaretoken]" ).val(); + function csrfSafeMethod( method ) { + + // these HTTP methods do not require CSRF protection + return ( /^(GET|HEAD|OPTIONS|TRACE)$/.test( method ) ); + } + $.ajaxSetup( { + beforeSend: function( xhr, settings ) { + if ( !csrfSafeMethod( settings.type ) && !this.crossDomain ) { + xhr.setRequestHeader( "X-CSRFToken", csrftoken ); + } + } + } ); + $.ajax( { + url: "/build/" + id + "/?format=json", + data: JSON.stringify( { action: "failed" } ), + method: "PATCH", + contentType: "application/json; charset=utf-8", + dataType: "json", + success: successFunc, + error: errorFunc + } ); +} + function clean_all_uuids( project ) { for ( var uuid of $.release[ project ].uuids ) { $( "#" + project + "-" + uuid ).remove(); diff --git a/panel/templates/panel/release_info.html b/panel/templates/panel/release_info.html index 3cbb793..a64b5ed 100644 --- a/panel/templates/panel/release_info.html +++ b/panel/templates/panel/release_info.html @@ -23,6 +23,10 @@ disabled="disabled" onclick="click_resume(event, '{{ build_release.id }}')" class="btn btn-primary">Resume + {% endif %} diff --git a/release_dashboard/templates/release_dashboard/build_release.html b/release_dashboard/templates/release_dashboard/build_release.html index 3cd3fd5..24a52fa 100644 --- a/release_dashboard/templates/release_dashboard/build_release.html +++ b/release_dashboard/templates/release_dashboard/build_release.html @@ -41,14 +41,17 @@ Action {% for br in build_releases %} - + {{ br.uuid }} {{ br.start_date }} {{ br.last_update }} + class="btn btn-primary" {% if not perms.build.can_trigger or br.done %}disabled="disabled"{% endif %}>Refresh projects + @@ -172,5 +175,48 @@ function refresh_release_projects( id ) { error: errorFunc } ); } + +/* eslint-disable-next-line no-unused-vars*/ // used at onClick +function click_failed( e, id ) { + mark_build_failed( id ); + e.preventDefault(); +} + +function mark_build_failed( id ) { + + function successFunc( _data, _textStatus, _jqXHR ) { + $("#br_" + id).removeClass("success"); + $("#br_" + id).addClass("warning"); + $("#build_button" ).removeAttr("disabled"); + $("#failed_" + id ).attr("disabled", "disabled"); + $("#refresh_" + id).attr("disabled", "disabled"); + } + + function errorFunc( _jqXHR, _status, error ) { + $( "#error" ).html( error ); + } + var csrftoken = jQuery( "[name=csrfmiddlewaretoken]" ).val(); + function csrfSafeMethod( method ) { + // these HTTP methods do not require CSRF protection + return ( /^(GET|HEAD|OPTIONS|TRACE)$/.test( method ) ); + } + $.ajaxSetup( { + beforeSend: function( xhr, settings ) { + if ( !csrfSafeMethod( settings.type ) && !this.crossDomain ) { + xhr.setRequestHeader( "X-CSRFToken", csrftoken ); + } + } + } ); + $.ajax( { + url: "/build/" + id + "/?format=json", + data: JSON.stringify( { action: "failed" } ), + method: "PATCH", + contentType: "application/json; charset=utf-8", + dataType: "json", + success: successFunc, + error: errorFunc + } ); +} + {% endblock %} \ No newline at end of file