Add Harbormaster TeamCity Plugin
The complete plugin required to trigger a teamcity build from a harbormaster build plan.
This commit is contained in:
parent
8f54f6f69b
commit
11558ddf54
4 changed files with 258 additions and 3 deletions
|
@ -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;
|
||||
}
|
||||
}
|
17
Harbormaster-Teamcity-Plugin/PassphraseTokenKey.php
Normal file
17
Harbormaster-Teamcity-Plugin/PassphraseTokenKey.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
80
Harbormaster-Teamcity-Plugin/TeamCityXmlBuildBuilder.php
Normal file
80
Harbormaster-Teamcity-Plugin/TeamCityXmlBuildBuilder.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
17
README.md
17
README.md
|
@ -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.
|
||||
|
||||
Below are the plugins:
|
||||
* **Harbomaster-Teamcity-Plugin**
|
||||
* **Teamcity-Phabricator-Plugin**
|
||||
* **SonarQube-Phabricator-Plugin**
|
||||
* Harbormaster-Teamcity-Plugin
|
||||
* Teamcity-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
|
||||
|
||||
|
|
Loading…
Reference in a new issue