TT#15305 release_dashboard: hotfix from release directly

* don't use any info from Projects, just from ReleaseConfig

Change-Id: I703f14d9ae0e3781adf1fb8f47147ee24c3c01a0
pull/7/head
Victor Seva 3 years ago
parent b1d13db756
commit 24dd8bcfbd

@ -0,0 +1,70 @@
/**
*
*
*/
$( "button.hotfix" ).click( function( e ) {
// don't send the form
e.preventDefault();
var button = $( this );
var id = button.attr( "id" ).replace( "hotfix_", "" );
var repo = id;
var span = $( "span#hotfix_error_" + id );
var links = $( "#links_" + id );
var push = $( "select#push_" + id + " option:selected" ).val();
var empty = $( "input#empty_" + id ).prop( "checked" );
$.ajax( {
url: repo + "/",
type: "POST",
data: JSON.stringify( { push: push, empty: empty } ),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: successFunc,
error: errorFunc,
/* eslint-disable-next-line no-undef */ // at csrf.js
beforeSend: csrftokenFunc
} );
button.attr( "disabled", "disabled" );
span.html( "processing" );
span.show();
links.addClass( "hidden" );
function successFunc( data, _status ) {
span.html( "" );
$( "#link_done_" + id ).attr( "href", data.urls[ 0 ] );
$( "#link_latest_" + id ).attr( "href", data.urls[ 1 ] );
links.removeClass( "hidden" );
button.removeAttr( "disabled" );
}
function errorFunc( _jqXHR, _status, error ) {
span.html( error );
button.removeAttr( "disabled" );
}
} );
$( "td.version > select" ).change( function() {
var id = $( this ).attr( "id" ).replace( "version_", "" );
var version = $( this ).val();
var button = $( "button#hotfix_" + id );
var span = $( "span#hotfix_error_" + id );
var links = $( "#links_" + id );
if ( version.match( /^branch\/mr[0-9]+\.[0-9]+\.[0-9]+$/ ) ) {
button.html( "Release hotfix" );
button.removeAttr( "disabled" );
} else {
button.html( "Select branch to hotfix" );
button.attr( "disabled", "disabled" );
}
span.html( "" );
links.addClass( "hidden" );
} );
$( document ).ready( function() {
$( "td.version > select option[value^=\"branch/mr\"]" ).each( function() {
$( this ).change();
} );
} );

@ -61,13 +61,19 @@
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Projects to build</h3>
<div class="row">
<h3 class="col-xs-10 panel-title">Projects to build</h3>
<div class="col-xs-1 hidden" id="hotfix">
<a href="{% url 'release_dashboard:hotfix_release' config.release %}" class="btn btn-primary right">Hotfixes</a>
</div>
</div>
</div>
<div class="panel-body">
<ul class="list-group" style="column-count: 4;">
{% for prj in config.projects %}
<li class="list-group-item {% if prj in build_deps %}list-group-item-warning{% endif %}">
<a href="{{ gerrit_url }}{{ prj }}.git;a=shortlog;h=refs/heads/{{ config.branch }}">{{ prj }}</a>
{% if prj in build_deps %}<div class="hidden-xs pull-right label label-warning">dependency</div>{% endif %}
</li>
{% endfor %}
</ul>
@ -84,6 +90,7 @@ $( document ).ready(function() {
if (builds > 0 && release.match(/^release-mr[0-9]+\.[0-9]+\.[0-9]+$/)) {
$( "#build_button" ).attr("disabled", "disabled");
console.debug("mrX.X.X release, can't be built twice");
$( "#hotfix" ).toggleClass("hidden");
}
});
@ -98,6 +105,7 @@ function delete_build_release( id ) {
$("#br_" + id).remove();
if ( $(".build_release").length === 0 ) {
$( "#build_button" ).removeAttr("disabled");
$( "#hotfix" ).toggleClass("hidden");
}
}

@ -0,0 +1,66 @@
{% extends "release_dashboard/base.html" %}
{% load static %}
{% block title %}Release Dashboard{% endblock %}
{% block navlist %}
<li><a href="{% url 'release_dashboard:index'%}">Release Dashboard</a></li>
<li><a href="{% url 'release_dashboard:build_index'%}">Build Release</a></li>
<li><a href="{% url 'release_dashboard:build_release' config.release %}">{{ config.release }}</a></li>
{% endblock %}
{% block content %}
<div class="container">
<table class="table table-condensed">
<thead>
<tr>
<th>Project</th>
<th>Version</th>
<th>Push</th>
<th>Empty</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<form class="form-inline">{% csrf_token %}</form>
{% for p in config.projects %}
<tr class="repo">
<th><label for="version_{{ p }}">{{ p }}</label></th>
<td class="version">
<select class="form-control" id="version_{{ p }}" name="version_{{ p }}">
<option value="ignore">ignore</option>
<option value="branch/{{ config.branch }}">branch/{{ config.branch }}</option>
</select>
</td>
<td>
<select class="form-control" id="push_{{ p }}">
<option value="yes" selected="selected">yes</option>
<option value="no">no</option>
</select>
</td>
<td>
<input id="empty_{{ p }}" type="checkbox">
</td>
<td>
<button type="button" id="hotfix_{{ p }}" class="btn btn-warning hotfix" disabled="disabled">Select branch to hotfix</button>
</td>
<td>
<span class="text-danger" id="hotfix_error_{{ p }}"></span>
<div id="links_{{ p }}" class="hidden">
<div class="btn-group btn-group-sm" role="group" aria-label="grp1">
<a class="btn btn-default btn-success" type="button" role="group" id="link_done_{{ p }}">Done</a>
</div>
<div class="btn-group btn-group-sm" role="group" aria-label="grp2">
<a class="btn btn-default btn-info" type="button" role="group" id="link_latest_{{ p }}">latest Build</a>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
{% block extrajs %}
<script src="{% static 'release_dashboard/js/csrf.js' %}"></script>
<script src="{% static 'release_dashboard/js/hotfix_release.js' %}"></script>
{% endblock %}

@ -1,4 +1,4 @@
# Copyright (C) 2020 The Sipwise Team - http://sipwise.com
# Copyright (C) 2020-2022 The Sipwise Team - http://sipwise.com
#
# 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
@ -68,6 +68,68 @@ class TestHotfix(TestCase):
self.assertEqual(res.context["projects"], expected)
class TestHotfixRelease(TestCase):
def test_no_login(self):
res = self.client.get(
reverse(
"release_dashboard:hotfix_release", args=["release-mr7.5.2"]
)
)
self.assertNotEqual(res.status_code, 200)
def test_login_ok(self):
user = User.objects.create_user(username="test")
self.client.force_login(user)
res = self.client.get(
reverse(
"release_dashboard:hotfix_release", args=["release-mr7.5.2"]
)
)
self.assertEqual(res.status_code, 200)
def test_no_mrXXX(self):
user = User.objects.create_user(username="test")
self.client.force_login(user)
res = self.client.get(
reverse("release_dashboard:hotfix_release", args=["release-mr7.5"])
)
self.assertNotEqual(res.status_code, 200)
res = self.client.get(
reverse(
"release_dashboard:hotfix_release",
args=["release-trunk-buster"],
)
)
self.assertNotEqual(res.status_code, 200)
def test_project_ok(self):
user = User.objects.create_user(username="test")
self.client.force_login(user)
res = self.client.post(
reverse(
"release_dashboard:hotfix_release_build",
args=["release-mr7.5.2", "data-hal"],
),
{"push": "no"},
content_type="application/json",
)
self.assertEqual(res.status_code, 200)
def test_project_wrong(self):
user = User.objects.create_user(username="test")
self.client.force_login(user)
res = self.client.post(
reverse(
"release_dashboard:hotfix_release_build",
args=["release-mr7.5.2", "fake-project"],
),
{"push": "no"},
content_type="application/json",
)
self.assertNotEqual(res.status_code, 200)
class TestDocker(TestCase):
def test_no_login(self):
res = self.client.get(reverse("release_dashboard:docker_images"))
@ -81,17 +143,17 @@ class TestDocker(TestCase):
class TestBuildRelease(BaseTest):
fixtures = ['test_build_release']
fixtures = ["test_build_release"]
def test_no_login(self):
url = reverse("release_dashboard:build_release", args=['trunk'])
url = reverse("release_dashboard:build_release", args=["trunk"])
res = self.client.get(url)
self.assertNotEqual(res.status_code, 200)
def test_login_ok(self):
user = User.objects.create_user(username="test")
self.client.force_login(user)
url = reverse("release_dashboard:build_release", args=['trunk'])
url = reverse("release_dashboard:build_release", args=["trunk"])
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
@ -99,10 +161,10 @@ class TestBuildRelease(BaseTest):
user = User.objects.create_user(username="test")
self.client.force_login(user)
# no build yet
url = reverse("release_dashboard:build_release", args=['mr8.1'])
url = reverse("release_dashboard:build_release", args=["mr8.1"])
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
self.assertTrue(res.context['done'])
self.assertTrue(res.context["done"])
def test_context_not_done(self):
user = User.objects.create_user(username="test")
@ -112,7 +174,7 @@ class TestBuildRelease(BaseTest):
)
br.built_projects = None
br.save()
url = reverse("release_dashboard:build_release", args=['trunk'])
url = reverse("release_dashboard:build_release", args=["trunk"])
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
self.assertFalse(res.context['done'])
self.assertFalse(res.context["done"])

@ -27,6 +27,16 @@ urlpatterns = [
build.build_release,
name="build_release",
),
re_path(
r"^hotfix_release/(?P<release>[^/]+)/$",
build.hotfix_release,
name="hotfix_release",
),
re_path(
r"^hotfix_release/(?P<release>[^/]+)/(?P<project>[^/]+)/$",
build.hotfix_release_build,
name="hotfix_release_build",
),
re_path(r"^hotfix/$", build.hotfix, name="hotfix"),
re_path(
r"^hotfix/(?P<branch>[^/]+)/(?P<project>[^/]+)/$", build.hotfix_build

@ -17,10 +17,11 @@ import re
from django.views.generic.base import TemplateView
from natsort import humansorted
from ..conf import settings
from ..utils import get_branches
from ..utils import get_tags
regex_hotfix = re.compile(r"^mr[0-9]+\.[0-9]+\.[0-9]+$")
regex_hotfix = re.compile(settings.RELEASE_DASHBOARD_FILTER_MRXXX)
regex_mr = re.compile(r"^mr.+$")

@ -112,6 +112,44 @@ def hotfix(request):
return render(request, "release_dashboard/hotfix.html", context)
@login_required
@require_http_methods(["POST"])
def hotfix_release_build(request, release, project):
release_config = ReleaseConfig(release)
if project not in release_config.projects:
error = f"project:{project} not in {release_config.release}"
logger.error(error)
return HttpResponseNotFound(error)
if not regex_hotfix.match(release_config.branch):
error = f"branch:{release_config.branch} not valid. Not mrX.X.X format"
logger.error(error)
return HttpResponseNotFound(error)
logger.debug(body=request.body)
json_data = json.loads(request.body.decode("utf-8"))
push = json_data.get("push", "no")
empty = json_data.get("empty", False)
if push == "no":
logger.warn(f"dry-run for {project}:{release_config.branch}")
urls = build.trigger_hotfix(
project, release_config.branch, request.user, push, empty
)
return JsonResponse({"urls": urls})
@login_required
def hotfix_release(request, release):
release_config = ReleaseConfig(release)
if not regex_hotfix.match(release_config.branch):
error = (
"branch:%s not valid. Not mrX.X.X format" % release_config.branch
)
logger.error(error)
return HttpResponseNotFound(error)
context = {"config": release_config}
return render(request, "release_dashboard/hotfix_release.html", context)
@login_required
def refresh_all(request):
if request.method == "POST":

Loading…
Cancel
Save