You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by dp...@apache.org on 2018/10/10 11:50:53 UTC

[ignite-teamcity-bot] branch master updated: IGNITE-9770 Refactor notifyJira() - Fixes #27.

This is an automated email from the ASF dual-hosted git repository.

dpavlov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-teamcity-bot.git


The following commit(s) were added to refs/heads/master by this push:
     new bd96ed3  IGNITE-9770 Refactor notifyJira() - Fixes #27.
bd96ed3 is described below

commit bd96ed375306ad43c4713e88f7326cc48d800233
Author: Nikolai Kulagin <zz...@gmail.com>
AuthorDate: Wed Oct 10 14:50:42 2018 +0300

    IGNITE-9770 Refactor notifyJira() - Fixes #27.
    
    Signed-off-by: Dmitriy Pavlov <dp...@apache.org>
---
 .../org/apache/ignite/ci/observer/BuildInfo.java   |  81 --------
 .../apache/ignite/ci/observer/BuildObserver.java   |   5 +-
 .../org/apache/ignite/ci/observer/BuildsInfo.java  | 116 +++++++++++
 .../apache/ignite/ci/observer/ObserverTask.java    |  22 +-
 .../tcbot/visa/TcBotTriggerAndSignOffService.java  |  22 +-
 .../rest/{TriggerBuild.java => TriggerBuilds.java} |  45 +---
 .../src/main/webapp/js/testfails-2.1.js            | 231 +++++++++++++--------
 ignite-tc-helper-web/src/main/webapp/prs.html      |  37 +---
 8 files changed, 304 insertions(+), 255 deletions(-)

diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildInfo.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildInfo.java
deleted file mode 100644
index 9de4dd3..0000000
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildInfo.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.ci.observer;
-
-import org.apache.ignite.ci.tcmodel.result.Build;
-import org.apache.ignite.ci.user.ICredentialsProv;
-
-/**
- *
- */
-public class BuildInfo {
-    /** Build. */
-    public final Build build;
-
-    /** Server id. */
-    public final String srvId;
-
-    /** */
-    public final ICredentialsProv prov;
-
-    /** JIRA ticket full name. */
-    public final String ticket;
-
-    /**
-     * @param build Build.
-     * @param srvId Server id.
-     * @param prov Credentials.
-     * @param ticket JIRA ticket name.
-     */
-    BuildInfo(Build build, String srvId, ICredentialsProv prov, String ticket) {
-        this.build = build;
-        this.srvId = srvId;
-        this.prov = prov;
-        this.ticket = ticket;
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean equals(Object o) {
-        if (this == o)
-            return true;
-        if (o == null || getClass() != o.getClass())
-            return false;
-
-        BuildInfo info = (BuildInfo)o;
-
-        if (!build.equals(info.build))
-            return false;
-        if (!srvId.equals(info.srvId))
-            return false;
-        if (!prov.equals(info.prov))
-            return false;
-
-        return ticket.equals(info.ticket);
-    }
-
-    /** {@inheritDoc} */
-    @Override public int hashCode() {
-        int res = build.hashCode();
-
-        res = 31 * res + srvId.hashCode();
-        res = 31 * res + prov.hashCode();
-        res = 31 * res + ticket.hashCode();
-
-        return res;
-    }
-}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildObserver.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildObserver.java
index 8603d34..a6d302f 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildObserver.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildObserver.java
@@ -54,12 +54,11 @@ public class BuildObserver {
     }
 
     /**
-     * @param build Build id.
      * @param srvId Server id.
      * @param prov Credentials.
      * @param ticket JIRA ticket name.
      */
-    public void observe(Build build, String srvId, ICredentialsProv prov, String ticket) {
-        observerTask.builds.add(new BuildInfo(build, srvId, prov, ticket));
+    public void observe(String srvId, ICredentialsProv prov, String ticket, Build... builds) {
+        observerTask.builds.add(new BuildsInfo(srvId, prov, ticket, builds));
     }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildsInfo.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildsInfo.java
new file mode 100644
index 0000000..2dd3060
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/BuildsInfo.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.ci.observer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
+import org.apache.ignite.ci.tcmodel.result.Build;
+import org.apache.ignite.ci.user.ICredentialsProv;
+
+public class BuildsInfo {
+    /** Finished. */
+    public static final String FINISHED = "finished";
+
+    /** Server id. */
+    public final String srvId;
+
+    /** Build type id. */
+    public final String buildTypeId;
+
+    /** Branch name. */
+    public final String branchName;
+
+    /** Credentials. */
+    public final ICredentialsProv prov;
+
+    /** JIRA ticket full name. */
+    public final String ticket;
+
+    /** Finished builds. */
+    private final Map<Build, Boolean> finishedBuilds = new HashMap<>();
+
+    /**
+     * @param srvId Server id.
+     * @param prov Prov.
+     * @param ticket Ticket.
+     * @param builds Builds.
+     */
+    public BuildsInfo(String srvId, ICredentialsProv prov, String ticket, Build[] builds) {
+        this.srvId = srvId;
+        this.prov = prov;
+        this.ticket = ticket;
+        this.buildTypeId = builds.length > 1 ? "IgniteTests24Java8_RunAll" : builds[0].buildTypeId;
+        this.branchName = builds[0].branchName;
+
+        for (Build build : builds)
+            finishedBuilds.put(build, false);
+    }
+
+    /**
+     * @param teamcity Teamcity.
+     */
+    public boolean isFinished(IAnalyticsEnabledTeamcity teamcity) {
+        for (Map.Entry<Build, Boolean> entry : finishedBuilds.entrySet()) {
+            if (!entry.getValue()) {
+                Build build = teamcity.getBuild(entry.getKey().getId());
+                entry.setValue(build.state.equals(FINISHED));
+            }
+        }
+
+        return !finishedBuilds.containsValue(false);
+    }
+
+    /**
+     * Return builds count.
+     */
+    public int buildsCount(){
+        return finishedBuilds.size();
+    }
+
+    /**
+     * Return finished builds count.
+     */
+    public int finishedBuildsCount(){
+        return (int)finishedBuilds.values().stream().filter(v -> v).count();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (!(o instanceof BuildsInfo))
+            return false;
+
+        BuildsInfo info = (BuildsInfo)o;
+
+        return Objects.equals(srvId, info.srvId) &&
+            Objects.equals(buildTypeId, info.buildTypeId) &&
+            Objects.equals(branchName, info.branchName) &&
+            Objects.equals(prov, info.prov) &&
+            Objects.equals(ticket, info.ticket) &&
+            Objects.equals(finishedBuilds, info.finishedBuilds);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return Objects.hash(srvId, buildTypeId, branchName, prov, ticket, finishedBuilds);
+    }
+}
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java
index 374099b..a27fe03 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/observer/ObserverTask.java
@@ -28,7 +28,6 @@ import org.apache.ignite.ci.ITcServerProvider;
 import org.apache.ignite.ci.di.AutoProfiling;
 import org.apache.ignite.ci.di.MonitoredTask;
 import org.apache.ignite.ci.jira.IJiraIntegration;
-import org.apache.ignite.ci.tcmodel.result.Build;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -48,7 +47,7 @@ public class ObserverTask extends TimerTask {
     @Inject private IJiraIntegration jiraIntegration;
 
     /** Builds. */
-    final Queue<BuildInfo> builds;
+    final Queue<BuildsInfo> builds;
 
     /**
      */
@@ -73,23 +72,22 @@ public class ObserverTask extends TimerTask {
     @MonitoredTask(name = "Build Observer")
     protected String runObserverTask() {
         int checkedBuilds = 0;
-        int notFinihedBuilds = 0;
+        int notFinishedBuilds = 0;
         Set<String> ticketsNotified = new HashSet<>();
 
-        for (BuildInfo info : builds) {
-            checkedBuilds++;
-            IAnalyticsEnabledTeamcity teamcity = srvProvider.server(info.srvId, info.prov);
+        for (BuildsInfo info : builds) {
+            checkedBuilds += info.buildsCount();
 
-            Build build = teamcity.getBuild(info.build.getId());
+            IAnalyticsEnabledTeamcity teamcity = srvProvider.server(info.srvId, info.prov);
 
-            if (!"finished".equals(build.state)) {
-                notFinihedBuilds++;
+            if (!info.isFinished(teamcity)) {
+                notFinishedBuilds += info.buildsCount() - info.finishedBuildsCount();
 
                 continue;
             }
 
-            String jiraRes = jiraIntegration.notifyJira(info.srvId, info.prov, info.build.buildTypeId,
-                info.build.branchName, info.ticket);
+            String jiraRes = jiraIntegration.notifyJira(info.srvId, info.prov, info.buildTypeId,
+                info.branchName, info.ticket);
 
             if (JIRA_COMMENTED.equals(jiraRes)) {
                 ticketsNotified.add(info.ticket);
@@ -98,6 +96,6 @@ public class ObserverTask extends TimerTask {
             }
         }
 
-        return "Checked " + checkedBuilds + " not finished " + notFinihedBuilds + " notified: " + ticketsNotified;
+        return "Checked " + checkedBuilds + " not finished " + notFinishedBuilds + " notified: " + ticketsNotified;
     }
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java
index d3429e3..5fdd812 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/tcbot/visa/TcBotTriggerAndSignOffService.java
@@ -20,6 +20,7 @@ package org.apache.ignite.ci.tcbot.visa;
 import com.google.common.base.Strings;
 import com.google.inject.Provider;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.ws.rs.QueryParam;
@@ -74,10 +75,10 @@ public class TcBotTriggerAndSignOffService {
         return ticketId;
     }
 
-    @NotNull public String triggerBuildAndObserve(
+    @NotNull public String triggerBuildsAndObserve(
         @Nullable String srvId,
         @Nullable String branchForTc,
-        @Nullable String suiteId,
+        @Nullable String suiteIdList,
         @Nullable Boolean top,
         @Nullable Boolean observe,
         @Nullable String ticketId,
@@ -86,10 +87,15 @@ public class TcBotTriggerAndSignOffService {
 
         final ITeamcity teamcity = tcServerProvider.server(srvId, prov);
 
-        Build build = teamcity.triggerBuild(suiteId, branchForTc, false, top != null && top);
+        String[] suiteIds = Objects.requireNonNull(suiteIdList).split(",");
+
+        Build[] builds = new Build[suiteIds.length];
+
+        for (int i = 0; i < suiteIds.length; i++)
+            builds[i] = teamcity.triggerBuild(suiteIds[i], branchForTc, false, top != null && top);
 
         if (observe != null && observe)
-            jiraRes = observeJira(srvId, branchForTc, ticketId, teamcity, build, prov);
+            jiraRes = observeJira(srvId, branchForTc, ticketId, teamcity, prov, builds);
 
         return jiraRes;
     }
@@ -99,8 +105,8 @@ public class TcBotTriggerAndSignOffService {
      * @param branchForTc Branch for TeamCity.
      * @param ticketId JIRA ticket number.
      * @param teamcity TeamCity.
-     * @param build Build.
      * @param prov Credentials.
+     * @param builds Builds.
      * @return Message with result.
      */
     private String observeJira(
@@ -108,8 +114,8 @@ public class TcBotTriggerAndSignOffService {
         String branchForTc,
         @Nullable String ticketId,
         ITeamcity teamcity,
-        Build build,
-        ICredentialsProv prov
+        ICredentialsProv prov,
+        Build... builds
     ) {
         if (F.isEmpty(ticketId)) {
             try {
@@ -134,7 +140,7 @@ public class TcBotTriggerAndSignOffService {
             }
         }
 
-        buildObserverProvider.get().observe(build, srvId, prov, "ignite-" + ticketId);
+        buildObserverProvider.get().observe(srvId, prov, "ignite-" + ticketId, builds);
 
         return "JIRA ticket IGNITE-" + ticketId + " will be notified after the tests are completed.";
     }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuild.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuilds.java
similarity index 77%
rename from ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuild.java
rename to ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuilds.java
index 63539fb..e86886d 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuild.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/TriggerBuilds.java
@@ -19,7 +19,6 @@ package org.apache.ignite.ci.web.rest;
 
 import com.google.inject.Injector;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -44,9 +43,11 @@ import org.apache.ignite.ci.web.model.SimpleResult;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import static com.google.common.base.Strings.isNullOrEmpty;
+
 @Path("build")
 @Produces(MediaType.APPLICATION_JSON)
-public class TriggerBuild {
+public class TriggerBuilds {
     /** Servlet Context. */
     @Context
     private ServletContext ctx;
@@ -57,10 +58,10 @@ public class TriggerBuild {
 
     @GET
     @Path("trigger")
-    public SimpleResult triggerBuild(
+    public SimpleResult triggerBuilds(
         @Nullable @QueryParam("serverId") String srvId,
         @Nullable @QueryParam("branchName") String branchForTc,
-        @Nullable @QueryParam("suiteId") String suiteId,
+        @Nullable @QueryParam("suiteIdList") String suiteIdList,
         @Nullable @QueryParam("top") Boolean top,
         @Nullable @QueryParam("observe") Boolean observe,
         @Nullable @QueryParam("ticketId") String ticketId
@@ -71,14 +72,16 @@ public class TriggerBuild {
         if (!prov.hasAccess(srvId))
             throw ServiceUnauthorizedException.noCreds(srvId);
 
+        if (isNullOrEmpty(suiteIdList))
+            return new SimpleResult("Error: nothing to run.");
+
         String jiraRes = CtxListener.getInjector(ctx)
             .getInstance(TcBotTriggerAndSignOffService.class)
-            .triggerBuildAndObserve(srvId, branchForTc, suiteId, top, observe, ticketId, prov);
+            .triggerBuildsAndObserve(srvId, branchForTc, suiteIdList, top, observe, ticketId, prov);
 
         return new SimpleResult("Tests started." + (!jiraRes.isEmpty() ? "<br>" + jiraRes : ""));
     }
 
-
     @GET
     @Path("commentJira")
     public SimpleResult commentJira(
@@ -98,36 +101,6 @@ public class TriggerBuild {
     }
 
     @GET
-    @Path("triggerBuilds")
-    public SimpleResult triggerBuilds(
-        @Nullable @QueryParam("serverId") String serverId,
-        @Nullable @QueryParam("branchName") String branchName,
-        @NotNull @QueryParam("suiteIdList") String suiteIdList,
-        @Nullable @QueryParam("top") Boolean top) {
-
-        final ICredentialsProv prov = ICredentialsProv.get(req);
-
-        if (!prov.hasAccess(serverId))
-            throw ServiceUnauthorizedException.noCreds(serverId);
-
-        List<String> strings = Arrays.asList(suiteIdList.split(","));
-        if (strings.isEmpty())
-            return new SimpleResult("Error: nothing to run");
-
-        final ITeamcity helper = CtxListener.getTcHelper(ctx).server(serverId, prov);
-
-        boolean queueToTop = top != null && top;
-
-        for (String suiteId : strings) {
-            System.out.println("Triggering [ " + suiteId + "," + branchName + "," + "top=" + queueToTop + "]");
-
-            helper.triggerBuild(suiteId, branchName, false, queueToTop);
-        }
-
-        return new SimpleResult("OK");
-    }
-
-    @GET
     @Path("integrationUrls")
     public Set<ServerIntegrationLinks> getIntegrationUrls(@NotNull @QueryParam("serverIds") String srvIds) {
         final ICredentialsProv prov = ICredentialsProv.get(req);
diff --git a/ignite-tc-helper-web/src/main/webapp/js/testfails-2.1.js b/ignite-tc-helper-web/src/main/webapp/js/testfails-2.1.js
index 0cd70e9..958fe4c 100644
--- a/ignite-tc-helper-web/src/main/webapp/js/testfails-2.1.js
+++ b/ignite-tc-helper-web/src/main/webapp/js/testfails-2.1.js
@@ -65,7 +65,6 @@ function showChainResultsWithSettings(result, settings) {
         res += showChainCurrentStatusData(server, settings);
     }
 
-
     setTimeout(initMoreInfo, 100);
 
     return res;
@@ -125,11 +124,13 @@ function showChainCurrentStatusData(server, settings) {
     if (suitesFailedList.length !== 0 && isDefinedAndFilled(server.serverId) && isDefinedAndFilled(server.branchName)) {
         mInfo += "Trigger failed " + cntFailed + " builds";
         mInfo += " <a href='javascript:void(0);' ";
-        mInfo += " onClick='triggerBuilds(\"" + server.serverId + "\", \"" + suitesFailedList + "\", \"" + server.branchName + "\", false)' ";
+        mInfo += " onClick='triggerBuilds(\"" + server.serverId + "\", \"" + suitesFailedList + "\", \"" +
+            server.branchName + "\", false, false)' ";
         mInfo += " title='trigger builds'>in queue</a> ";
 
         mInfo += " <a href='javascript:void(0);' ";
-        mInfo += " onClick='triggerBuilds(\"" + server.serverId + "\", \"" + suitesFailedList + "\", \"" + server.branchName + "\", true)' ";
+        mInfo += " onClick='triggerBuilds(\"" + server.serverId + "\", \"" + suitesFailedList + "\", \"" +
+            server.branchName + "\", true, false)' ";
         mInfo += " title='trigger builds'>on top</a><br>";
     }
 
@@ -171,7 +172,28 @@ function showChainCurrentStatusData(server, settings) {
 
     if (settings.isJiraAvailable()) {
         res += "<button onclick='commentJira(\"" + server.serverId + "\", \"IgniteTests24Java8_RunAll\", \""
-            + server.branchName + "\")'>Comment JIRA</button>";
+            + server.branchName + "\")'>Comment JIRA</button>&nbsp;&nbsp;";
+
+        var blockersList = "";
+
+        for (var i = 0; i < server.suites.length; i++) {
+            var suite = server.suites[i];
+
+            suite = suiteWithCriticalFailuresOnly(suite);
+
+            if (suite != null) {
+                if (blockersList.length !== 0)
+                    blockersList += ",";
+
+                blockersList += suite.suiteId;
+            }
+        }
+
+        res += "<button onclick='triggerBuilds(\"" + server.serverId + "\", \"" + blockersList + "\", \"" +
+            server.branchName + "\", false, false)'> Re-run possible blockers</button><br>";
+
+        res += "<button onclick='triggerBuilds(\"" + server.serverId + "\", \"" + blockersList + "\", \"" +
+            server.branchName + "\", false, true)'> Re-run possible blockers & Comment JIRA</button><br>";
     }
 
     if (isDefinedAndFilled(server.baseBranchForTc)) {
@@ -321,96 +343,137 @@ function notifyGit() {
     });
 }
 
-function triggerBuild(serverId, suiteId, branchName, top, observe, ticketId) {
+function triggerBuilds(serverId, suiteIdList, branchName, top, observe, ticketId) {
     var queueAtTop = isDefinedAndFilled(top) && top;
+    var observeJira = isDefinedAndFilled(observe) && observe;
+    var suiteIdsNotExists = !isDefinedAndFilled(suiteIdList) || suiteIdList.length === 0;
+    var branchNotExists = !isDefinedAndFilled(branchName) || branchName.length === 0;
+    branchName = branchNotExists ? null : branchForTc(branchName);
+    ticketId = (isDefinedAndFilled(ticketId) && ticketId.length > 0) ? jiraTicketNumber(ticketId) : null;
 
-    $.ajax({
-        url: 'rest/build/trigger',
-        data: {
-            "serverId": serverId,
-            "suiteId": suiteId,
-            "branchName": branchName,
-            "top": queueAtTop,
-            "observe": observe,
-            "ticketId": ticketId
-        },
-        success: function(result) {
-            var dialog = $("#triggerDialog");
+    var triggerConfirm = $("#triggerConfirm");
 
-            dialog.html("Trigger builds at server: " + serverId + "<br>" +
-                " Suite: " + suiteId + "<br>Branch:" + branchName + "<br>Top: " + top +
-                "<br><br> Result: " + result.result);
-            dialog.dialog({
-                modal: true,
-                buttons: {
-                    "Ok": function() {
-                        $(this).dialog("close");
-                    }
-                }
-            });
+    if (suiteIdsNotExists || branchNotExists) {
+        triggerConfirm.html("No " + (suiteIdsNotExists ? "suites" +
+            (branchNotExists ? " and branch" : "") : "branch") + " to run!");
+        triggerConfirm.dialog({
+            modal: true,
+            buttons: {
+                "Ok" : closeDialog
+            }
+        });
 
-            loadData(); // should be defined by page
-        },
-        error: showErrInLoadStatus
-    });
-}
+        return;
+    }
 
-function triggerBuilds(serverId, suiteIdList, branchName, top) {
-    var res = "Trigger builds at server: " + serverId + "<br>" +
-        "Branch:" + branchName + "<br>Top: " + top + "<br>";
+    var suites = suiteIdList.split(',');
+    var fewSuites = suites.length > 1;
 
-    var partsOfStr = suiteIdList.split(',');
+    var message = "Trigger build" + (fewSuites ? "s" : "") + " at <b>server:</b> " + serverId + "<br>" +
+    "<b>Branch:</b> " + branchName + "<br><b>Top:</b> " + top + "<br>" +
+    "<b>Suite ID" + (fewSuites ? "s" : "") + ":</b> ";
 
-    for (var i = 0; i < partsOfStr.length; i++) {
-        var suite = partsOfStr[i];
-        res += "Suite ID: " + suite + "<br>";
-    }
-    var triggerConfirm = $("#triggerConfirm");
+    for (var i = 0; i < suites.length; i++)
+        message += suites[i] + "<br>";
 
-    triggerConfirm.html(res);
-
-    triggerConfirm.dialog({
-        modal: true,
-        buttons: {
-            "Run": function() {
-                $(this).dialog("close");
-
-                var queueAtTop = isDefinedAndFilled(top) && top
-                $.ajax({
-                    url: 'rest/build/triggerBuilds',
-                    data: {
-                        "serverId": serverId,
-                        "suiteIdList": suiteIdList,
-                        "branchName": branchName,
-                        "top": queueAtTop
-                    },
-                    success: function(result) {
-                        var dialog = $("#triggerDialog");
-
-                        dialog.html("Trigger builds at server: " + serverId + "<br>" +
-                            " Suites " + suiteIdList + "<br>Branch:" + branchName + "<br>Top: " + top +
-                            "<br><br> Result: " + result.result);
-                        dialog.dialog({
-                            modal: true,
-                            buttons: {
-                                "Ok": function() {
-                                    $(this).dialog("close");
-                                }
-                            }
-                        });
-                        loadData(); // should be defined by page
-                    },
-                    error: showErrInLoadStatus
-                });
+    if (fewSuites) {
+        triggerConfirm.html(message);
+        triggerConfirm.dialog({
+            modal: true,
+            buttons: {
+                "Run" : function () {
+                    $(this).dialog("close");
+                    sendGetRequest();
+                },
+                "Cancel": closeDialog
+            }
+        });
+    } else
+        sendGetRequest();
+
+    function sendGetRequest() {
+        $.ajax({
+            url: 'rest/build/trigger',
+            data: {
+                "serverId": serverId,
+                "suiteIdList": suiteIdList,
+                "branchName": branchName,
+                "top": queueAtTop,
+                "observe": observeJira,
+                "ticketId": ticketId
             },
-            Cancel: function() {
-                $(this).dialog("close");
+            success: successDialog,
+            error: showErrInLoadStatus
+        });
+    }
+
+    function successDialog(result) {
+        var triggerDialog = $("#triggerDialog");
+
+        triggerDialog.html(message + "<br><b>Result:</b> " + result.result);
+        triggerDialog.dialog({
+            modal: true,
+            buttons: {
+                "Ok": closeDialog
             }
-        }
-    });
+        });
+
+        loadData();
+    }
+
+    function closeDialog() {
+        $(this).dialog("close");
+    }
+}
+
+/**
+ * Converts PR number to branch for TeamCity.
+ *
+ * @param pr - Pull Request number.
+ * @returns {String} Branch for TeamCity.
+ */
+function branchForTc(pr) {
+    var regExpr = /(\d*)/i;
+
+    if (regExpr.exec(pr)[0] === pr)
+        return "pull/" + regExpr.exec(pr)[0] + "/head";
+
+    return pr;
+}
+
+/**
+ * Converts JIRA ticket full name to the tickets number.
+ *
+ * @param ticket - JIRA ticket.
+ * @returns {string} JIRA ticket number.
+ */
+function jiraTicketNumber(ticket) {
+    var regExpr = /(ignite-)?(\d*)/i;
+
+    return regExpr.exec(ticket)[2];
 }
 
 function commentJira(serverId, suiteId, branchName, ticketId) {
+    var branchNotExists = !isDefinedAndFilled(branchName) || branchName.length === 0;
+    branchName = branchNotExists ? null : branchForTc(branchName);
+    ticketId = (isDefinedAndFilled(ticketId) && ticketId.length > 0) ? jiraTicketNumber(ticketId) : null;
+
+    if (branchNotExists) {
+        var triggerConfirm = $("#triggerConfirm");
+
+        triggerConfirm.html("No branch to run!");
+        triggerConfirm.dialog({
+            modal: true,
+            buttons: {
+                "Ok" : function () {
+                    $(this).dialog("close");
+                }
+            }
+        });
+
+        return;
+    }
+
     $("#notifyJira").html("<img src='https://www.wallies.com/filebin/images/loading_apple.gif' width=20px height=20px>" +
         " Please wait. First action for PR run-all data may require significant time.");
 
@@ -540,11 +603,13 @@ function showSuiteData(suite, settings) {
     if (isDefinedAndFilled(suite.serverId) && isDefinedAndFilled(suite.suiteId) && isDefinedAndFilled(suite.branchName)) {
         mInfo += " Trigger build: ";
         mInfo += "<a href='javascript:void(0);' ";
-        mInfo += " onClick='triggerBuild(\"" + suite.serverId + "\", \"" + suite.suiteId + "\", \"" + suite.branchName + "\", false)' ";
+        mInfo += " onClick='triggerBuilds(\"" + suite.serverId + "\", \"" + suite.suiteId + "\", \"" + suite.branchName
+            + "\", false, false)' ";
         mInfo += " title='trigger build' >queue</a> ";
 
         mInfo += "<a href='javascript:void(0);' ";
-        mInfo += " onClick='triggerBuild(\"" + suite.serverId + "\", \"" + suite.suiteId + "\", \"" + suite.branchName + "\", true)' ";
+        mInfo += " onClick='triggerBuilds(\"" + suite.serverId + "\", \"" + suite.suiteId + "\", \"" + suite.branchName
+            + "\", true, false)' ";
         mInfo += " title='trigger build at top of queue'>top</a><br>";
     }
 
diff --git a/ignite-tc-helper-web/src/main/webapp/prs.html b/ignite-tc-helper-web/src/main/webapp/prs.html
index 0bf9b07..d6cad13 100644
--- a/ignite-tc-helper-web/src/main/webapp/prs.html
+++ b/ignite-tc-helper-web/src/main/webapp/prs.html
@@ -212,10 +212,10 @@
 
             var srvId = fields.namedItem("serverId").value;
             var suiteId = fields.namedItem("suiteId").value;
-            var branchName = branchForTc(fields.namedItem("branchForTc").value);
-            var ticketId = jiraTicketNumber(fields.namedItem("ticketId").value);
+            var branchName = fields.namedItem("branchForTc").value;
+            var ticketId = fields.namedItem("ticketId").value;
 
-            triggerBuild(srvId, suiteId, branchName, false, trigCase !== "tests", ticketId)
+            triggerBuilds(srvId, suiteId, branchName, false, trigCase !== "tests", ticketId)
         }
 
         /**
@@ -226,39 +226,12 @@
 
             var srvId = fields.namedItem("serverId").value;
             var suiteId = fields.namedItem("suiteId").value;
-            var branchName = branchForTc(fields.namedItem("branchForTc").value);
-            var ticketId = jiraTicketNumber(fields.namedItem("ticketId").value);
+            var branchName = fields.namedItem("branchForTc").value;
+            var ticketId = fields.namedItem("ticketId").value;
 
             commentJira(srvId, suiteId, branchName, ticketId)
         }
 
-        /**
-         * Converts PR number to branch for TeamCity.
-         *
-         * @param pr - Pull Request number.
-         * @returns {String} Branch for TeamCity.
-         */
-        function branchForTc(pr) {
-            var regExpr = /(\d*)/i;
-
-            if (regExpr.exec(pr)[0] === pr)
-                return "pull/" + regExpr.exec(pr)[0] + "/head";
-
-            return pr;
-        }
-
-        /**
-         * Converts JIRA ticket full name to the tickets number.
-         *
-         * @param ticket - JIRA ticket.
-         * @returns {string} JIRA ticket number.
-         */
-        function jiraTicketNumber(ticket) {
-            var regExpr = /(ignite-)?(\d*)/i;
-
-            return regExpr.exec(ticket)[2];
-        }
-
         function showFormAndSuitesForPrCheck(result) {
             var res = "";