1
0
Fork 0

Add Harbormaster TeamCity Plugin

The complete plugin required to trigger a teamcity build from a harbormaster build plan.
This commit is contained in:
Steven Cooney 2019-05-30 13:16:13 +01:00
parent 8f54f6f69b
commit 11558ddf54
4 changed files with 258 additions and 3 deletions

View file

@ -0,0 +1,147 @@
<?php
final class HarbormasterTeamCityBuildStepImplementation
extends HarbormasterBuildStepImplementation {
public function getName() {
return pht('Build with TeamCity');
}
public function getGenericDescription() {
return pht('Trigger TeamCity Builds with Harbormaster');
}
public function getBuildStepGroupKey() {
return HarbormasterExternalBuildStepGroup::GROUPKEY;
}
public function getDescription() {
$domain = null;
$uri = $this->getSetting('uri');
if ($uri) {
$domain = id(new PhutilURI($uri))->getDomain();
}
$method = $this->formatSettingForDescription('method', 'POST');
$domain = $this->formatValueForDescription($domain);
if ($this->getSetting('credential')) {
return pht(
'Make an authenticated HTTP %s request to %s.',
$method,
$domain);
} else {
return pht(
'Make an HTTP %s request to %s.',
$method,
$domain);
}
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$viewer = PhabricatorUser::getOmnipotentUser();
// Settings includes the custom fields set in getFieldSpecifications()
$settings = $this->getSettings();
$variables = $build_target->getVariables();
// Combined TeamCity URI
$uri = $settings['uri'] . '/app/rest/buildQueue';
$method = 'POST';
$contentType = 'application/xml';
// Using TeamCityXmlBuildBuilder create the payload to send to
// TeamCity server
$xmlBuilder = new TeamCityXmlBuildBuilder();
$payload = $xmlBuilder
->addBuildId($settings['buildId'])
->addBranchName(implode(array("D", $variables['buildable.revision'], "-", $variables['build.id'])))
->addPhabBuildId($variables['build.id'])
->addDiffId($variables['buildable.diff'])
->addHarbormasterPHID($variables['target.phid'])
->addRevisionId(implode(array("D", $variables['buildable.revision'])))
->build();
$future = id(new HTTPSFuture($uri, $payload))
->setMethod($method)
->addHeader('Content-Type', $contentType)
->addHeader('Origin', $settings['uri'])
->setTimeout(60);
// Add credentials to HTTP request if they have been set
$credential_phid = $this->getSetting('credential');
if ($credential_phid) {
$key = PassphraseTokenKey::loadFromPHID(
$credential_phid,
$viewer);
$future->addHeader(
'Authorization',
implode(array("Bearer ", $key->getPasswordEnvelope()->openEnvelope()))
);
}
$this->resolveFutures(
$build,
$build_target,
array($future));
list($status, $body, $headers) = $future->resolve();
$header_lines = array();
// TODO: We don't currently preserve the entire "HTTP" response header, but
// should. Once we do, reproduce it here faithfully.
$status_code = $status->getStatusCode();
$header_lines[] = "HTTP {$status_code}";
foreach ($headers as $header) {
list($head, $tail) = $header;
$header_lines[] = "{$head}: {$tail}";
}
$header_lines = implode("\n", $header_lines);
$build_target
->newLog($uri, 'http.head')
->append($header_lines);
$build_target
->newLog($uri, 'http.body')
->append($body);
if ($status->isError()) {
throw new HarbormasterBuildFailureException();
}
}
public function getFieldSpecifications() {
return array(
'uri' => array(
'name' => pht('URI'),
'type' => 'text',
'required' => true,
),
'buildId' => array(
'name' => pht('TeamCity Build Configuration ID'),
'type' => 'text',
'required' => true,
),
'credential' => array(
'name' => pht('TeamCity Credentials'),
'type' => 'credential',
'required' => true,
'credential.type'
=> PassphraseTokenCredentialType::CREDENTIAL_TYPE,
'credential.provides'
=> PassphraseTokenCredentialType::PROVIDES_TYPE,
),
);
}
public function supportsWaitForMessage() {
return true;
}
}

View file

@ -0,0 +1,17 @@
<?php
final class PassphraseTokenKey extends PassphraseAbstractKey {
public static function loadFromPHID($phid, PhabricatorUser $viewer) {
$key = new PassphraseTokenKey();
return $key->loadAndValidateFromPHID(
$phid,
$viewer,
PassphraseTokenCredentialType::PROVIDES_TYPE);
}
public function getPasswordEnvelope() {
return $this->requireCredential()->getSecret();
}
}

View file

@ -0,0 +1,80 @@
<?php
final class TeamCityXmlBuildBuilder {
private $xml;
private $root;
function __construct(){
$this->xml = new DOMDocument('1.0', 'UTF-8');
$this->root = $this->xml->createElement('build');
}
function addBuildId($buildId){
$buildIdElement =
$this->
xml->
createElement('buildType');
$buildIdElement->setAttribute('id', $buildId);
$this->root->appendChild($buildIdElement);
return $this;
}
function addPhabBuildId($buildId){
$this->addProperty("env.buildId", $buildId);
return $this;
}
function addRevisionId($revisionId){
$this->addProperty("env.revisionId", $revisionId);
return $this;
}
function addBranchName($branchName){
// $this->
// root->
// setAttribute('branchName', $branchName);
$this->addProperty("env.branchName", $branchName);
return $this;
}
function addHarbormasterPHID($phid){
$this->addProperty('env.harbormasterTargetPHID', $phid);
return $this;
}
function addDiffId($diffId){
$this->addProperty('env.diffId', $diffId);
return $this;
}
function build(){
$this->xml->appendChild($this->root);
return $this->xml->saveXML();
}
private function addProperty($name, $value){
$this->verifyPropertiesExist();
$property = $this->xml->createElement('property');
$property->setAttribute('name', $name);
$property->setAttribute('value', $value);
$this->
root->
getElementsByTagName('properties')->
item(0)->
appendChild($property);
}
private function verifyPropertiesExist(){
if($this->root->getElementsByTagName('properties')->length == 0){
$propertiesElement = $this->xml->createElement('properties');
$this->root->appendChild($propertiesElement);
}
}
}

View file

@ -5,9 +5,20 @@ _X-Lab's Linting Spine_
This repository holds the plugins created to link together our internal systems. The original premise to link Phabricator, TeamCity and SonarQube to enable linting on differential reviews. This repository holds the plugins created to link together our internal systems. The original premise to link Phabricator, TeamCity and SonarQube to enable linting on differential reviews.
Below are the plugins: Below are the plugins:
* **Harbomaster-Teamcity-Plugin** * Harbormaster-Teamcity-Plugin
* **Teamcity-Phabricator-Plugin** * Teamcity-Phabricator-Plugin
* **SonarQube-Phabricator-Plugin** * SonarQube-Phabricator-Plugin
### Harbomaster-Teamcity-Plugin
The harbormaster plugin allows us to trigger a build configuration within TeamCity as part of a harbormaster build plan.
The plugin requires:
1. TeamCity URI
2. Build Configuration to trigger a build for
3. TeamCity access token to authenticate with the server
To deploy simply drag the contents of the folder to `src/extensions/` on the Phabricator instance and then restart the application.
## Useful Links ## Useful Links