You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ignite.apache.org by GitBox <gi...@apache.org> on 2018/09/14 18:00:03 UTC

[GitHub] asfgit closed pull request #10: IGNITE-9588 Separate page for JIRA, GitHub actions

asfgit closed pull request #10: IGNITE-9588 Separate page for JIRA, GitHub actions
URL: https://github.com/apache/ignite-teamcity-bot/pull/10
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcHelper.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcHelper.java
index 69df43d..1077a0e 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcHelper.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITcHelper.java
@@ -59,4 +59,14 @@
     Collection<String> getServerIds();
 
     List<String> getTrackedBranchesIds();
+
+    /**
+     * @param srvId Server id.
+     * @param prov Credentials.
+     * @param buildTypeId Suite name.
+     * @param branchForTc Branch for TeamCity.
+     * @param ticket JIRA ticket full name.
+     * @return {@code True} if JIRA was notified.
+     */
+    boolean notifyJira(String srvId, ICredentialsProv prov, String buildTypeId, String branchForTc, String ticket);
 }
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
index 8201f5c..6a475a4 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/ITeamcity.java
@@ -287,11 +287,11 @@ default SingleBuildRunCtx loadTestsAndProblems(@Nonnull Build build, @Deprecated
     PullRequest getPullRequest(String branch);
 
     /**
-     * @param ticket JIRA ticket name.
+     * @param ticket JIRA ticket full name.
      * @param comment Comment to be placed in the ticket conversation.
      * @return {@code True} if ticket was succesfully commented. Otherwise - {@code false}.
      */
-    boolean commentJiraTicket(String ticket, String comment);
+    boolean sendJiraComment(String ticket, String comment);
 
 
     default void setAuthData(String user, String password) {
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
index 1e10d78..7f17075 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgnitePersistentTeamcity.java
@@ -853,8 +853,8 @@ public void setExecutor(ExecutorService executor) {
     }
 
     /** {@inheritDoc} */
-    @Override public boolean commentJiraTicket(String ticket, String comment) {
-        return teamcity.commentJiraTicket(ticket, comment);
+    @Override public boolean sendJiraComment(String ticket, String comment) {
+        return teamcity.sendJiraComment(ticket, comment);
     }
 
     /** {@inheritDoc} */
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityHelper.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityHelper.java
index 249a72a..f55222e 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityHelper.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/IgniteTeamcityHelper.java
@@ -176,7 +176,7 @@ public IgniteTeamcityHelper(@Nullable String tcName) {
     }
 
     /** {@inheritDoc} */
-    @Override public boolean commentJiraTicket(String ticket, String comment) {
+    @Override public boolean sendJiraComment(String ticket, String comment) {
         try {
             String url = "https://issues.apache.org/jira/rest/api/2/issue/" + ticket + "/comment";
 
@@ -195,6 +195,7 @@ public IgniteTeamcityHelper(@Nullable String tcName) {
     @Override public PullRequest getPullRequest(String branchForTc) {
         String id = null;
 
+        // Get PR id from string "pull/XXXX/head"
         for (int i = 5; i < branchForTc.length(); i++) {
             char c = branchForTc.charAt(i);
 
diff --git a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/TcHelper.java b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/TcHelper.java
index a95483b..de0e7ea 100644
--- a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/TcHelper.java
+++ b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/TcHelper.java
@@ -20,16 +20,26 @@
 import com.google.common.base.Strings;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.ci.conf.BranchesTracked;
 import org.apache.ignite.ci.observer.BuildObserver;
 import org.apache.ignite.ci.issue.IssueDetector;
 import org.apache.ignite.ci.issue.IssuesStorage;
+import org.apache.ignite.ci.tcmodel.hist.BuildRef;
 import org.apache.ignite.ci.user.ICredentialsProv;
 import org.apache.ignite.ci.user.UserAndSessionsStorage;
 import org.apache.ignite.ci.util.ExceptionUtil;
 import org.apache.ignite.ci.web.TcUpdatePool;
+import org.apache.ignite.ci.web.model.current.ChainAtServerCurrentStatus;
+import org.apache.ignite.ci.web.model.current.SuiteCurrentStatus;
+import org.apache.ignite.ci.web.model.current.TestFailure;
+import org.apache.ignite.ci.web.model.current.TestFailuresSummary;
+import org.apache.ignite.ci.web.model.hist.FailureSummary;
+import org.apache.ignite.ci.web.rest.pr.GetPrTestFailures;
 import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
@@ -38,11 +48,19 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.ignite.ci.analysis.RunStat.MAX_LATEST_RUNS;
+import static org.apache.ignite.ci.util.XmlUtil.xmlEscapeText;
 
 /**
  * TC Bot implementation
  */
 public class TcHelper implements ITcHelper {
+    /** Logger. */
+    private static final Logger logger = LoggerFactory.getLogger(TcHelper.class);
+
     /** Stop guard. */
     private AtomicBoolean stop = new AtomicBoolean();
 
@@ -147,6 +165,167 @@ private BranchesTracked getTrackedBranches() {
         return getTrackedBranches().getIds();
     }
 
+    /** {@inheritDoc} */
+    @Override public boolean notifyJira(
+        String srvId,
+        ICredentialsProv prov,
+        String buildTypeId,
+        String branchForTc,
+        String ticket
+    ) {
+        try (IAnalyticsEnabledTeamcity teamcity = server(srvId, prov)) {
+            List<BuildRef> builds = teamcity.getFinishedBuilds(buildTypeId, branchForTc);
+            BuildRef build = builds.get(builds.size() - 1);
+            String comment;
+
+            try {
+                comment = generateJiraComment(buildTypeId, build.branchName, srvId, prov, build.webUrl);
+            }
+            catch (RuntimeException e) {
+                logger.error("Exception happened during generating comment for JIRA " +
+                    "[build=" + build.getId() + ", errMsg=" + e.getMessage() + ']');
+
+                return false;
+            }
+
+            if (build.state.equals("finished")) {
+                if (teamcity.sendJiraComment(ticket, comment))
+                    return true;
+            }
+
+            return false;
+        }
+    }
+
+    /**
+     * @param buildTypeId Suite name.
+     * @param branchForTc Branch for TeamCity.
+     * @param srvId Server id.
+     * @param prov Credentials.
+     * @param webUrl Build URL.
+     * @return Comment, which should be sent to the JIRA ticket.
+     */
+    private String generateJiraComment(
+        String buildTypeId,
+        String branchForTc,
+        String srvId,
+        ICredentialsProv prov,
+        String webUrl
+    ) {
+        StringBuilder res = new StringBuilder();
+        TestFailuresSummary summary = GetPrTestFailures.getTestFailuresSummary(
+            this, prov, srvId, buildTypeId, branchForTc,
+            "Latest", null, null);
+
+        if (summary != null) {
+            for (ChainAtServerCurrentStatus server : summary.servers) {
+                if (!server.serverName().equals("apache"))
+                    continue;
+
+                Map<String, List<SuiteCurrentStatus>> fails = findFailures(server);
+
+                for (List<SuiteCurrentStatus> suites : fails.values()) {
+                    for (SuiteCurrentStatus suite : suites) {
+                        res.append("{color:#d04437}").append(suite.name).append("{color}");
+                        res.append(" [[tests ").append(suite.failedTests);
+
+                        if (suite.result != null && !suite.result.equals(""))
+                            res.append(' ').append(suite.result);
+
+                        res.append('|').append(suite.webToBuild).append("]]\\n");
+
+                        for (TestFailure failure : suite.testFailures) {
+                            res.append("* ");
+
+                            if (failure.suiteName != null && failure.testName != null)
+                                res.append(failure.suiteName).append(": ").append(failure.testName);
+                            else
+                                res.append(failure.name);
+
+                            FailureSummary recent = failure.histBaseBranch.recent;
+
+                            if (recent != null) {
+                                if (recent.failureRate != null) {
+                                    res.append(" - ").append(recent.failureRate).append("% fails in last ")
+                                        .append(MAX_LATEST_RUNS).append(" master runs.");
+                                }
+                                else if (recent.failures != null && recent.runs != null) {
+                                    res.append(" - ").append(recent.failures).append(" fails / ")
+                                        .append(recent.runs).append(" runs.");
+                                }
+                            }
+
+                            res.append("\\n");
+                        }
+
+                        res.append("\\n");
+                    }
+                }
+
+                if (res.length() > 0) {
+                    res.insert(0, "{panel:title=Possible Blockers|" +
+                        "borderStyle=dashed|borderColor=#ccc|titleBGColor=#F7D6C1}\\n")
+                        .append("{panel}");
+                }
+                else {
+                    res.append("{panel:title=No blockers found!|" +
+                        "borderStyle=dashed|borderColor=#ccc|titleBGColor=#D6F7C1}{panel}");
+                }
+            }
+        }
+
+        res.append("\\n").append("[TeamCity Run All|").append(webUrl).append(']');
+
+        return xmlEscapeText(res.toString());
+    }
+
+    /**
+     * @param srv Server.
+     * @return Failures for given server.
+     */
+    private Map<String, List<SuiteCurrentStatus>> findFailures(ChainAtServerCurrentStatus srv) {
+        Map<String, List<SuiteCurrentStatus>> fails = new LinkedHashMap<>();
+
+        fails.put("compilation", new ArrayList<>());
+        fails.put("timeout", new ArrayList<>());
+        fails.put("exit code", new ArrayList<>());
+        fails.put("failed tests", new ArrayList<>());
+
+        for (SuiteCurrentStatus suite : srv.suites) {
+            String suiteRes = suite.result.toLowerCase();
+            String failType = null;
+
+            if (suiteRes.contains("compilation"))
+                failType = "compilation";
+
+            if (suiteRes.contains("timeout"))
+                failType = "timeout";
+
+            if (suiteRes.contains("exit code"))
+                failType = "exit code";
+
+            if (failType == null) {
+                List<TestFailure> failures = new ArrayList<>();
+
+                for (TestFailure testFailure : suite.testFailures) {
+                    if (testFailure.isNewFailedTest())
+                        failures.add(testFailure);
+                }
+
+                if (!failures.isEmpty()) {
+                    suite.testFailures = failures;
+
+                    failType = "failed tests";
+                }
+            }
+
+            if (failType != null)
+                fails.get(failType).add(suite);
+        }
+
+        return fails;
+    }
+
     public void close() {
         if (stop.compareAndSet(false, true)) {
             srvs.asMap().values().forEach(v -> {
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
index 445cd06..9de4dd3 100644
--- 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
@@ -23,18 +23,18 @@
 /**
  *
  */
-class BuildInfo {
+public class BuildInfo {
     /** Build. */
-    final Build build;
+    public final Build build;
 
     /** Server id. */
-    final String srvId;
+    public final String srvId;
 
     /** */
-    final ICredentialsProv prov;
+    public final ICredentialsProv prov;
 
-    /** JIRA ticket name. */
-    final String ticket;
+    /** JIRA ticket full name. */
+    public final String ticket;
 
     /**
      * @param build Build.
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 0c7a04b..aa3c2f5 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
@@ -17,35 +17,17 @@
 
 package org.apache.ignite.ci.observer;
 
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
 import java.util.Queue;
 import java.util.TimerTask;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import org.apache.ignite.ci.IAnalyticsEnabledTeamcity;
 import org.apache.ignite.ci.ITcHelper;
 import org.apache.ignite.ci.tcmodel.result.Build;
-import org.apache.ignite.ci.web.model.current.ChainAtServerCurrentStatus;
-import org.apache.ignite.ci.web.model.current.SuiteCurrentStatus;
-import org.apache.ignite.ci.web.model.current.TestFailure;
-import org.apache.ignite.ci.web.model.current.TestFailuresSummary;
-import org.apache.ignite.ci.web.model.hist.FailureSummary;
-import org.apache.ignite.ci.web.rest.pr.GetPrTestFailures;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.ignite.ci.analysis.RunStat.MAX_LATEST_RUNS;
-import static org.apache.ignite.ci.util.XmlUtil.xmlEscapeText;
 
 /**
  * Checks observed builds for finished status and comments JIRA ticket.
  */
 public class ObserverTask extends TimerTask {
-    /** Logger. */
-    private static final Logger logger = LoggerFactory.getLogger(ObserverTask.class);
-
     /** Helper. */
     private final ITcHelper helper;
 
@@ -63,143 +45,15 @@
     /** {@inheritDoc} */
     @Override public void run() {
         for (BuildInfo info : builds) {
-            IAnalyticsEnabledTeamcity teamcity = helper.server(info.srvId, info.prov);
-            Build build = teamcity.getBuild(info.build.getId());
-            String comment;
-
-            try {
-                comment = generateComment(build, info);
-            }
-            catch (RuntimeException e) {
-                logger.error("Exception happened during generating comment for JIRA " +
-                    "[build=" + build.getId() + ", errMsg=" + e.getMessage() + ']');
-
-                continue;
-            }
+            try (IAnalyticsEnabledTeamcity teamcity = helper.server(info.srvId, info.prov)) {
+                Build build = teamcity.getBuild(info.build.getId());
 
-            if (build.state.equals("finished")) {
-                if (teamcity.commentJiraTicket(info.ticket, comment))
-                    builds.remove(info);
-            }
-        }
-    }
-
-    /**
-     * @param build Build.
-     * @param info Info.
-     */
-    private String generateComment(Build build, BuildInfo info) {
-        StringBuilder res = new StringBuilder();
-        TestFailuresSummary summary = GetPrTestFailures.getTestFailuresSummary(
-            helper, info.prov, info.srvId, build.getBuildType().getId(), build.branchName,
-            "Latest", null, null);
-
-        if (summary != null) {
-            for (ChainAtServerCurrentStatus server : summary.servers) {
-                if (!server.serverName().equals("apache"))
+                if (!build.state.equals("finished"))
                     continue;
-
-                Map<String, List<SuiteCurrentStatus>> fails = findFailures(server);
-
-                for (List<SuiteCurrentStatus> suites : fails.values()) {
-                    for (SuiteCurrentStatus suite : suites) {
-                        res.append("{color:#d04437}").append(suite.name).append("{color}");
-                        res.append(" [[tests ").append(suite.failedTests);
-
-                        if (suite.result != null && !suite.result.equals(""))
-                            res.append(' ').append(suite.result);
-
-                        res.append('|').append(suite.webToBuild).append("]]\\n");
-
-                        for (TestFailure failure : suite.testFailures) {
-                            res.append("* ");
-
-                            if (failure.suiteName != null && failure.testName != null)
-                                res.append(failure.suiteName).append(": ").append(failure.testName);
-                            else
-                                res.append(failure.name);
-
-                            FailureSummary recent = failure.histBaseBranch.recent;
-
-                            if (recent != null) {
-                                if (recent.failureRate != null) {
-                                    res.append(" - ").append(recent.failureRate).append("% fails in last ")
-                                        .append(MAX_LATEST_RUNS).append(" master runs.");
-                                }
-                                else if (recent.failures != null && recent.runs != null) {
-                                    res.append(" - ").append(recent.failures).append(" fails / ")
-                                        .append(recent.runs).append(" runs.");
-                                }
-                            }
-
-                            res.append("\\n");
-                        }
-
-                        res.append("\\n");
-                    }
-                }
-
-                if (res.length() > 0) {
-                    res.insert(0, "{panel:title=Possible Blockers|" +
-                        "borderStyle=dashed|borderColor=#ccc|titleBGColor=#F7D6C1}\\n")
-                        .append("{panel}");
-                }
-                else {
-                    res.append("{panel:title=No blockers found!|" +
-                        "borderStyle=dashed|borderColor=#ccc|titleBGColor=#D6F7C1}{panel}");
-                }
             }
-        }
-
-        res.append("\\n").append("[TeamCity Run All|").append(build.webUrl).append(']');
-
-        return xmlEscapeText(res.toString());
-    }
-
-    /**
-     * @param srv Server.
-     * @return Failures for given server.
-     */
-    private Map<String, List<SuiteCurrentStatus>> findFailures(ChainAtServerCurrentStatus srv) {
-        Map<String, List<SuiteCurrentStatus>> fails = new LinkedHashMap<>();
-
-        fails.put("compilation", new ArrayList<>());
-        fails.put("timeout", new ArrayList<>());
-        fails.put("exit code", new ArrayList<>());
-        fails.put("failed tests", new ArrayList<>());
 
-        for (SuiteCurrentStatus suite : srv.suites) {
-            String suiteRes = suite.result.toLowerCase();
-            String failType = null;
-
-            if (suiteRes.contains("compilation"))
-                failType = "compilation";
-
-            if (suiteRes.contains("timeout"))
-                failType = "timeout";
-
-            if (suiteRes.contains("exit code"))
-                failType = "exit code";
-
-            if (failType == null) {
-                List<TestFailure> failures = new ArrayList<>();
-
-                for (TestFailure testFailure : suite.testFailures) {
-                    if (testFailure.isNewFailedTest())
-                        failures.add(testFailure);
-                }
-
-                if (!failures.isEmpty()) {
-                    suite.testFailures = failures;
-
-                    failType = "failed tests";
-                }
-            }
-
-            if (failType != null)
-                fails.get(failType).add(suite);
+            if (helper.notifyJira(info.srvId, info.prov, info.build.buildTypeId, info.build.branchName, info.ticket))
+                builds.remove(info);
         }
-
-        return fails;
     }
 }
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/TriggerBuild.java
index 9269cbe..c84e9c2 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/TriggerBuild.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.ci.web.rest;
 
+import com.google.common.base.Strings;
 import java.util.Arrays;
 import java.util.List;
 import javax.servlet.ServletContext;
@@ -32,7 +33,6 @@
 import org.apache.ignite.ci.github.PullRequest;
 import org.apache.ignite.ci.tcmodel.result.Build;
 import org.apache.ignite.ci.user.ICredentialsProv;
-import org.apache.ignite.ci.util.XmlUtil;
 import org.apache.ignite.ci.web.CtxListener;
 import org.apache.ignite.ci.web.rest.login.ServiceUnauthorizedException;
 import org.apache.ignite.ci.web.model.SimpleResult;
@@ -52,12 +52,13 @@
     @Path("trigger")
     public SimpleResult triggerBuild(
         @Nullable @QueryParam("serverId") String srvId,
-        @Nullable @QueryParam("branchName") String branchName,
+        @Nullable @QueryParam("branchName") String branchForTc,
         @Nullable @QueryParam("suiteId") String suiteId,
         @Nullable @QueryParam("top") Boolean top,
-        @Nullable @QueryParam("observe") Boolean observe
+        @Nullable @QueryParam("observe") Boolean observe,
+        @Nullable @QueryParam("ticketId") String ticketId
     ) {
-        String errors = "";
+        String jiraRes = "";
         final ICredentialsProv prov = ICredentialsProv.get(req);
 
         if (!prov.hasAccess(srvId))
@@ -66,37 +67,112 @@ public SimpleResult triggerBuild(
         ITcHelper helper = CtxListener.getTcHelper(context);
 
         try (final ITeamcity teamcity = helper.server(srvId, prov)) {
-            Build build = teamcity.triggerBuild(suiteId, branchName, false, top != null && top);
+            Build build = teamcity.triggerBuild(suiteId, branchForTc, false, top != null && top);
+
+            if (observe != null && observe)
+                jiraRes = observeJira(srvId, branchForTc, ticketId, helper, teamcity, build, prov);
+        }
+
+        return new SimpleResult("Tests started." + (!jiraRes.equals("") ? "<br>" + jiraRes : ""));
+    }
 
-            if (observe != null && observe) {
-                PullRequest pr = teamcity.getPullRequest(branchName);
+    @GET
+    @Path("commentJira")
+    public SimpleResult commentJira(
+        @Nullable @QueryParam("serverId") String srvId,
+        @Nullable @QueryParam("branchName") String branchForTc,
+        @Nullable @QueryParam("suiteId") String suiteId,
+        @Nullable @QueryParam("ticketId") String ticketId
+    ) {
+        System.out.println("commentJira ");
+        final ICredentialsProv prov = ICredentialsProv.get(req);
 
-                String ticketId = "";
+        if (!prov.hasAccess(srvId))
+            throw ServiceUnauthorizedException.noCreds(srvId);
 
-                if (pr.getTitle().startsWith("IGNITE-")) {
-                    int beginIdx = 7;
-                    int endIdx = 7;
+        ITcHelper helper = CtxListener.getTcHelper(context);
+        String jiraRes = "";
 
-                    while (endIdx < pr.getTitle().length() && Character.isDigit(pr.getTitle().charAt(endIdx)))
-                        endIdx++;
+        try (final ITeamcity teamcity = helper.server(srvId, prov)) {
+            if (Strings.isNullOrEmpty(ticketId)) {
+                PullRequest pr = teamcity.getPullRequest(branchForTc);
 
-                    ticketId = pr.getTitle().substring(beginIdx, endIdx);
-                }
+                ticketId = getTicketId(pr);
 
-                if (!ticketId.equals(""))
-                    helper.buildObserver().observe(build, srvId, prov, "ignite-" + ticketId);
-                else {
-                    errors += "<br>" +
-                        "JIRA ticket will not be notified after the tests are completed - " +
+                if (ticketId.equals("")) {
+                    jiraRes = "JIRA ticket can't be commented - " +
                         "PR title \"" + pr.getTitle() + "\" should starts with \"IGNITE-XXXX\"." +
                         " Please, rename PR according to the" +
                         " <a href='https://cwiki.apache.org/confluence/display/IGNITE/How+to+Contribute" +
-                        "#HowtoContribute-1.CreateGitHubpull-request'>contributing guide</a>.";
+                        "#HowtoContribute-1.CreateGitHubpull-request'>contributing guide</a>" +
+                        " or enter ticket id in the form.";
                 }
             }
         }
 
-        return new SimpleResult("Tests started." + errors);
+        if (helper.notifyJira(srvId, prov, suiteId, branchForTc, "ignite-" + ticketId))
+            return new SimpleResult("JIRA commented." + (!jiraRes.equals("") ? jiraRes : ""));
+        else
+            // TODO Write catched exceptions to the response.
+            return new SimpleResult("JIRA wasn't commented." + (!jiraRes.equals("") ? "<br>" + jiraRes : ""));
+    }
+
+    /**
+     * @param srvId Server id.
+     * @param branchForTc Branch for TeamCity.
+     * @param ticketId JIRA ticket number.
+     * @param helper Helper.
+     * @param teamcity TeamCity.
+     * @param build Build.
+     * @param prov Credentials.
+     * @return Message with result.
+     */
+    private String observeJira(
+        String srvId,
+        String branchForTc,
+        @Nullable String ticketId,
+        ITcHelper helper,
+        ITeamcity teamcity,
+        Build build,
+        ICredentialsProv prov
+    ) {
+        if (ticketId == null || ticketId.equals("")) {
+            PullRequest pr = teamcity.getPullRequest(branchForTc);
+
+            ticketId = getTicketId(pr);
+
+            if (ticketId.equals("")) {
+                return "JIRA ticket will not be notified after the tests are completed - " +
+                    "PR title \"" + pr.getTitle() + "\" should starts with \"IGNITE-XXXX\"." +
+                    " Please, rename PR according to the" +
+                    " <a href='https://cwiki.apache.org/confluence/display/IGNITE/How+to+Contribute" +
+                    "#HowtoContribute-1.CreateGitHubpull-request'>contributing guide</a>.";
+            }
+        }
+
+        helper.buildObserver().observe(build, srvId, prov, "ignite-" + ticketId);
+
+        return "JIRA ticket IGNITE-" + ticketId + " will be notified after the tests are completed.";
+    }
+
+    /**
+     * @param pr Pull Request.
+     * @return JIRA ticket number.
+     */
+    @NotNull private String getTicketId(PullRequest pr) {
+        String ticketId = "";
+
+        if (pr.getTitle().startsWith("IGNITE-")) {
+            int beginIdx = 7;
+            int endIdx = 7;
+
+            while (endIdx < pr.getTitle().length() && Character.isDigit(pr.getTitle().charAt(endIdx)))
+                endIdx++;
+
+            ticketId = pr.getTitle().substring(beginIdx, endIdx);
+        }
+
+        return ticketId;
     }
 
     @GET
diff --git a/ignite-tc-helper-web/src/main/webapp/index.html b/ignite-tc-helper-web/src/main/webapp/index.html
index 106ce02..839496e 100644
--- a/ignite-tc-helper-web/src/main/webapp/index.html
+++ b/ignite-tc-helper-web/src/main/webapp/index.html
@@ -52,7 +52,6 @@
         success: function(result) {
             $("#loadStatus").html("");
             showBuildsOnServers(result);
-            showRunAllForm(result);
         },
         error: showErrInLoadStatus
     });
@@ -112,56 +111,6 @@
     }
     $("#buildsCheck").html(res);
 }
-
-/**
- * This form allows user to start TeamCity Run All build.
- */
-function showRunAllForm(result) {
-    var res = "";
-
-    for (var i = 0; i < result.length; i++) {
-        var serverId = result[i];
-
-        res+="Server: <input type='text' name='serverId' value=" + serverId +" readonly>" ;
-        res+="Pull Request #<input type='text' name='prId' onkeypress='return trigBuild(event)'> ";
-        res+="<button onclick='trigBuild()'>Run All</button><br>";
-    }
-
-    //todo enabled this once feature is ready
-    // $("#runAll").html(res);
-}
-
-/**
- * Start Run All build on TeamCity and comment in JIRA ticket when build will be finished.
- */
-function trigBuild(event) {
-    if (event != null && event.key !== "Enter")
-        return;
-
-    var fields = document.getElementById("runAll").children;
-    var url = "rest/build/trigger?suiteId=IgniteTests24Java8_RunAll";
-    var prId = null;
-
-    for (let field of fields) {
-        if (field.name === "serverId")
-            url += "&serverId=" + field.value;
-
-        if (field.name === "prId") {
-            url += "&branchName=pull%2F" + field.value + "%2Fhead";
-            prId = field.value;
-        }
-    }
-
-    url += "&observe=true";
-
-    $.ajax({
-        url: url,
-        success: function(result) {
-            $("#runAll").html(result.result);
-        },
-        error: showErrInLoadStatus
-    });
-}
 </script>
 </head>
 <body>
diff --git a/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js b/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js
index efb6bef..dfa57a9 100644
--- a/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js
+++ b/ignite-tc-helper-web/src/main/webapp/js/common-1.6.js
@@ -118,6 +118,7 @@ function showMenu(menuData) {
         res += "<div class=\"navbar\">";
         res += "<a href=\"/\">Home</a>";
         res += "<a href=\"/compare.html\">Compare builds</a>";
+        res += "<a href=\"/services.html\">Services</a>";
 
 
         res += "<div class='topnav-right'>";
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 5ef3ac4..dd01b11 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
@@ -302,15 +302,18 @@ function notifyGit() {
     });
 }
 
-function triggerBuild(serverId, suiteId, branchName, top) {
+function triggerBuild(serverId, suiteId, branchName, top, observe, ticketId) {
     var queueAtTop = isDefinedAndFilled(top) && top;
+
     $.ajax({
         url: 'rest/build/trigger',
         data: {
             "serverId": serverId,
             "suiteId": suiteId,
             "branchName": branchName,
-            "top": queueAtTop
+            "top": queueAtTop,
+            "observe": observe,
+            "ticketId": ticketId
         },
         success: function(result) {
             var dialog = $("#triggerDialog");
@@ -388,6 +391,39 @@ function triggerBuilds(serverId, suiteIdList, branchName, top) {
     });
 }
 
+function commentJira(serverId, suiteId, branchName, ticketId) {
+    $("#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.");
+
+    $.ajax({
+        url: 'rest/build/commentJira',
+        data: {
+            "serverId": serverId,
+            "suiteId": suiteId,
+            "branchName": branchName,
+            "ticketId": ticketId
+        },
+        success: function(result) {$("#notifyJira").html("");
+            var dialog = $("#triggerDialog");
+
+            dialog.html("Trigger builds at server: " + serverId + "<br>" +
+                " Suite: " + suiteId + "<br>Branch:" + branchName + "<br>Top: " +
+                "<br><br> Result: " + result.result);
+            dialog.dialog({
+                modal: true,
+                buttons: {
+                    "Ok": function() {
+                        $(this).dialog("close");
+                    }
+                }
+            });
+
+            loadData(); // should be defined by page
+        },
+        error: showErrInLoadStatus
+    });
+}
+
 /**
  * Create html string with table rows, containing suite data.
  *
diff --git a/ignite-tc-helper-web/src/main/webapp/services.html b/ignite-tc-helper-web/src/main/webapp/services.html
new file mode 100644
index 0000000..443f2e3
--- /dev/null
+++ b/ignite-tc-helper-web/src/main/webapp/services.html
@@ -0,0 +1,152 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Services</title>
+    <link rel="icon" href="img/leaf-icon-png-7066.png">
+
+    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
+
+    <link rel="stylesheet" href="css/style-1.5.css">
+
+    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
+    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
+
+    <script src="js/common-1.6.js"></script>
+    <script src="js/testfails-2.1.js"></script>
+
+    <script>
+$(document).ready(function() {
+    $.getScript("js/common-1.6.js", function(data, textStatus, jqxhr){ });
+
+    $( document ).tooltip();
+    loadData();
+});
+
+function loadData() {
+    $("#loadStatus").html("<img src='https://www.wallies.com/filebin/images/loading_apple.gif' width=20px height=20px> Please wait");
+
+    $.ajax({
+        url: "rest/branches/version",
+        success: showVersionInfo,
+        error: showErrInLoadStatus
+    });
+
+    $.ajax({
+        url: "rest/branches/suites",
+        success: function(result) {
+            $("#loadStatus").html("");
+            showSuitesForTeamCityRunData(result);
+            showCommentJiraForm(result);
+        },
+        error: showErrInLoadStatus
+    });
+}
+
+function showSuitesForTeamCityRunData(result) {
+    var res = "";
+
+    for (var i = 0; i < result.length; i++) {
+        var chainAtServer = result[i];
+
+        // TODO multiple servers
+        if (chainAtServer.serverId != "apache")
+            continue;
+
+        res += "Server: <input type='text' name='serverId' value='" + chainAtServer.serverId + "' readonly>";
+        res += "Chain: <input type='text' name='suiteId' value='" + chainAtServer.suiteId + "' readonly>";
+        res += "Branch: <input type='text' name='branchForTc' required> ";
+        res += "Ticket#: <input type='text' name='ticketId'>";
+        res += "<button name='jira' type='button' onclick='trigBuild(\"tests\")'>Start tests</button>";
+        res += "<button name='jira' onclick='trigBuild(\"tests+jira\")'>Start tests and comment JIRA ticket on ready</button>";
+        //res += "<button name='git' onclick='trigBuild(\"tests+jira\")'>Start tests and comment GitHub PR on ready</button>";
+        /*
+        res+="Server: <input type='text' name='serverId' value=" + serverId +" readonly>" ;
+        res+="Pull Request #<input type='text' name='prId' onkeypress='return trigBuild(event)'> ";
+        res+="<button onclick='trigBuild()'>Run All</button><br>";
+        */
+    }
+
+    $("#suitesForRunAll").html(res);
+}
+
+/**
+ * This form allows user to start TeamCity Run All build.
+ */
+function showCommentJiraForm(result) {
+    var res = "";
+
+    for (var i = 0; i < result.length; i++) {
+        var chainAtServer = result[i];
+
+        // TODO multiple servers
+        if (chainAtServer.serverId != "apache")
+            continue;
+
+        res += "Server: <input type='text' name='serverId' value=" + chainAtServer.serverId +" readonly>" ;
+        res += "Chain: <input type='text' name='suiteId' value='" + chainAtServer.suiteId + "' readonly>";
+        res += "Branch: <input type='text' name='branchForTc' required> ";
+        res += "Ticket #<input type='text' name='ticketId'> ";
+        res += "<button name='action' onclick='notifyJira()'>Notify</button>";
+    }
+
+    //todo enabled this once feature is ready
+    $("#notifyJira").html(res);
+}
+
+/**
+ * Start Run All build on TeamCity and comment in JIRA ticket when build will be finished.
+ */
+function trigBuild(trigCase) {
+    var fields = document.getElementById("suitesForRunAll").children;
+
+    var srvId = fields.namedItem("serverId").value;
+    var suiteId = fields.namedItem("suiteId").value;
+    var branchName = fields.namedItem("branchForTc").value;
+    var ticketId = fields.namedItem("ticketId").value;
+
+    triggerBuild(srvId, suiteId, branchName, false, trigCase != "tests", ticketId)
+}
+
+/**
+ * Comment in JIRA ticket with results for the given PR.
+ */
+function notifyJira() {
+    var fields = document.getElementById("notifyJira").children;
+
+    var srvId = fields.namedItem("serverId").value;
+    var suiteId = fields.namedItem("suiteId").value;
+    var branchName = fields.namedItem("branchForTc").value;
+    var ticketId = fields.namedItem("ticketId").value;
+
+    commentJira(srvId, suiteId, branchName, ticketId)
+}
+    </script>
+</head>
+<body>
+
+TeamCity Run All:   <br>
+<div id="suitesForRunAll"></div>
+<br>
+
+Notify JIRA:   <br>
+<div id="notifyJira"></div>
+
+<!--Notify GitHub:   <br>
+<div id="notifyGitHub">
+    <form>
+        Server: <input type='text' name='serverId' value="apache" readonly>
+        Branch: <input type='text' name='branchForTc' required>
+        <input type='submit' name='action' value='Notify'>
+    </form>
+</div>
+<br>-->
+<div id="loadStatus"></div>
+<div><a href=".">Home</a><br></div>
+<div id="divFailures"></div>
+
+<div id="version"></div>
+
+<div style="visibility:hidden"><div id="triggerConfirm" title="Trigger Confirmation"></div><div id="triggerDialog" title="Trigger Result"></div></div>
+</body>
+</html>
\ No newline at end of file


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services