1
0
Fork 0

Send Build Result to Harbormaster

Once a build with the phabricator build feature configured is completed then the build status will be sent back to harbormaster.
This commit is contained in:
Steven Cooney 2019-06-03 11:21:53 +01:00
parent 13e6efd89e
commit 57ff16d304
7 changed files with 107 additions and 12 deletions

View file

@ -27,6 +27,21 @@ The teamcity plugin comprises of server and agent plugins for teamcity.
#### Server #### Server
The server plugin monitors builds with the phabricator feature enabled waiting for builds to finish and notify phabricator. The server plugin monitors builds with the phabricator feature enabled waiting for builds to finish and notify phabricator.
Once build is triggered from phabricator using **Harbomaster-Teamcity-Plugin** and the phabricator build feature is configured on the build configuration then the server plugin will wait and listen for the build to finish before reporting the result back to harbormaster to be displayed in phabricator.
## Getting Started
**Harbormaster-Teamcity-Plugin**
No building is needed for for the harbormaster plugin just dev away and copy the files into the `src/extensions` folder with your phabricator instance
**Teamcity-Phabricator-Plugin**
You will need to move in to the `Teamcity-Phabricator-Plugin/` folder:
* To build the plugins (server and agent) just run: `mvn package`
By default the teamcity plugin is equipped with teamcity-sdk-maven-plugin which allows for a local instance of teamcity with debugging capabilities. See their documentation for how to use: https://github.com/JetBrains/teamcity-sdk-maven-plugin
## Useful Links ## Useful Links
* https://confluence.jetbrains.com/display/TCD10/Web+UI+Extensions * https://confluence.jetbrains.com/display/TCD10/Web+UI+Extensions

View file

@ -16,6 +16,12 @@
<version>${teamcity-version}</version> <version>${teamcity-version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View file

@ -5,9 +5,10 @@ public class Constants {
public static final String PLUGIN_NAME = "phabricator"; public static final String PLUGIN_NAME = "phabricator";
public static final String PLUGIN_DISPLAY_NAME = "Phabricator Plugin"; public static final String PLUGIN_DISPLAY_NAME = "Phabricator Plugin";
// Build Feature // Build Feature Settings
public static final String BUILD_FEATURE_TYPE = "phabricator-build-feature"; public static final String BUILD_FEATURE_TYPE = "phabricator-build-feature";
public static final String PHABRICATOR_URL_SETTING = "phabricator_url_setting"; public static final String PHABRICATOR_URL_SETTING = "plugin.phabricatorUrl";
public static final String PHABRICATOR_CONDUIT_TOKEN_SETTING = "plugin.conduitToken";
// Build Config // Build Config
public static final String BRANCH_NAME = "env.PHAB_BRANCH_NAME"; public static final String BRANCH_NAME = "env.PHAB_BRANCH_NAME";

View file

@ -20,6 +20,7 @@ public class PhabricatorPluginConfig {
// Build Feature Variables // Build Feature Variables
private URL phabricatorUrl; private URL phabricatorUrl;
private String conduitToken;
// Harbormaster Variables // Harbormaster Variables
private String branchName; private String branchName;
@ -61,6 +62,10 @@ public class PhabricatorPluginConfig {
logger.warn(String.format("Failed to parse phabricator URL: %s", logger.warn(String.format("Failed to parse phabricator URL: %s",
params.get(Constants.PHABRICATOR_URL_SETTING)), e); params.get(Constants.PHABRICATOR_URL_SETTING)), e);
} }
case Constants.PHABRICATOR_CONDUIT_TOKEN_SETTING:
logger.info("Found Phabricator Conduit Token");
conduitToken = params.get(Constants.PHABRICATOR_CONDUIT_TOKEN_SETTING);
case Constants.BRANCH_NAME: case Constants.BRANCH_NAME:
logger.info(String.format("Found branch name: %s", params.get(Constants.BRANCH_NAME))); logger.info(String.format("Found branch name: %s", params.get(Constants.BRANCH_NAME)));
branchName = params.get(Constants.BRANCH_NAME); branchName = params.get(Constants.BRANCH_NAME);
@ -91,6 +96,18 @@ public class PhabricatorPluginConfig {
return false; return false;
} }
public URL getPhabricatorURL() {
return phabricatorUrl;
}
public String getConduitToken() {
return conduitToken;
}
public String getHarbormasterPHID() {
return harbormasterPHID;
}
private URL parsePhabricatorURL(String input) throws MalformedURLException { private URL parsePhabricatorURL(String input) throws MalformedURLException {
URL inputURL = new URL(input); URL inputURL = new URL(input);
String scheme = inputURL.getProtocol(); String scheme = inputURL.getProtocol();

View file

@ -8,8 +8,8 @@
</parent> </parent>
<artifactId>phabricator-plugin-server</artifactId> <artifactId>phabricator-plugin-server</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<dependencies>
<dependencies>
<dependency> <dependency>
<groupId>uk.xlab.teamcity.phabricator</groupId> <groupId>uk.xlab.teamcity.phabricator</groupId>
<artifactId>phabricator-plugin-common</artifactId> <artifactId>phabricator-plugin-common</artifactId>
@ -39,6 +39,12 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View file

@ -1,12 +1,25 @@
package uk.xlab.teamcity.phabricator; package uk.xlab.teamcity.phabricator;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import jetbrains.buildServer.messages.Status;
import jetbrains.buildServer.serverSide.SBuildFeatureDescriptor; import jetbrains.buildServer.serverSide.SBuildFeatureDescriptor;
import jetbrains.buildServer.serverSide.SRunningBuild; import jetbrains.buildServer.serverSide.SRunningBuild;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
/** /**
* Class that is run in a thread once a build has started. If the build does not * Class that is run in a thread once a build has started. If the build does not
* have the phabricator build feature then the tread should come to an end * have the phabricator build feature then the tread should come to an end
@ -52,8 +65,7 @@ public class BuildTracker implements Runnable {
// Now we have set all the parameters we need to check if // Now we have set all the parameters we need to check if
// everything is present and correct for us to continue // everything is present and correct for us to continue
if (!phabricatorConfig.isPluginSetup()) if (!phabricatorConfig.isPluginSetup()) {
{
logger.info("Plugin incorrectly configured"); logger.info("Plugin incorrectly configured");
return; return;
} }
@ -63,10 +75,46 @@ public class BuildTracker implements Runnable {
} }
logger.info(String.format("Build %s finished: %s", build.getBuildNumber(), build.getBuildStatus())); logger.info(String.format("Build %s finished: %s", build.getBuildNumber(), build.getBuildStatus()));
String harbormasterBuildStatus = parseTeamCityBuildStatus(build.getBuildStatus());
if (build.getStatusDescriptor().isSuccessful()) { NotifyHarbormaster(harbormasterBuildStatus);
logger.info("Successful Build");
}
} }
} }
/**
* Compose and dispatch an API call to harbormaster to notify of the build
* status
*/
private void NotifyHarbormaster(String buildStatus) {
URL phabricatorURL = phabricatorConfig.getPhabricatorURL();
try (CloseableHttpClient client = HttpClients.createDefault()) {
String requestEndpoint = String.format("%s/api/harbormaster.sendmessage", phabricatorURL.toString());
logger.info(String.format("Sending build status to: %s", requestEndpoint));
HttpPost httpPost = new HttpPost(requestEndpoint);
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("api.token", phabricatorConfig.getConduitToken()));
params.add(new BasicNameValuePair("buildTargetPHID", phabricatorConfig.getHarbormasterPHID()));
params.add(new BasicNameValuePair("type", buildStatus));
httpPost.setEntity(new UrlEncodedFormEntity(params));
try (CloseableHttpResponse response = client.execute(httpPost)) {
String responseText = EntityUtils.toString(response.getEntity());
logger.info(String.format("Phabricator response: %s", responseText));
}
} catch (Exception exception) {
// Just catching any Exception because the request to Phabricator may have
// failed and we should investigate
logger.error("Request to harbormaster failed", exception);
}
}
private String parseTeamCityBuildStatus(Status buildFinishedStatus){
if (buildFinishedStatus.isSuccessful()) {
return "pass";
}
return "fail";
}
} }

View file

@ -3,7 +3,9 @@
<jsp:useBean id="propertiesBean" scope="request" type="jetbrains.buildServer.controllers.BasePropertiesBean"/> <jsp:useBean id="propertiesBean" scope="request" type="jetbrains.buildServer.controllers.BasePropertiesBean"/>
<c:set var="phabricatorUrl" value="${propertiesBean.properties['phabricator_url_setting']}" /> <c:set var="phabricatorUrl" value="${propertiesBean.properties['plugin.phabricatorUrl']}" />
<c:set var="conduitToken" value="${propertiesBean.properties['plugin.conduitToken']}" />
<tr><td colspan="2">Report build status in real-time to your Phabricator instance.</td></tr> <tr><td colspan="2">Report build status in real-time to your Phabricator instance.</td></tr>
<tr><th>Phabricator URL:</th><td><props:textProperty name="phabricator_url_setting"/></td></tr> <tr><th>Phabricator URL:</th><td><props:textProperty name="plugin.phabricatorUrl"/></td></tr>
<tr><th>Conduit Token:</th><td><props:passwordProperty name="plugin.conduitToken" size="54"/></td></tr>