Path: blob/master/src/applications/harbormaster/step/HarbormasterBuildkiteBuildStepImplementation.php
12256 views
<?php12final class HarbormasterBuildkiteBuildStepImplementation3extends HarbormasterBuildStepImplementation {45public function getName() {6return pht('Build with Buildkite');7}89public function getGenericDescription() {10return pht('Trigger a build in Buildkite.');11}1213public function getBuildStepGroupKey() {14return HarbormasterExternalBuildStepGroup::GROUPKEY;15}1617public function getDescription() {18return pht('Run a build in Buildkite.');19}2021public function getEditInstructions() {22$hook_uri = '/harbormaster/hook/buildkite/';23$hook_uri = PhabricatorEnv::getProductionURI($hook_uri);2425return pht(<<<EOTEXT26WARNING: This build step is new and experimental!2728To build **revisions** with Buildkite, they must:2930- belong to a tracked repository;31- the repository must have a Staging Area configured;32- you must configure a Buildkite pipeline for that Staging Area; and33- you must configure the webhook described below.3435To build **commits** with Buildkite, they must:3637- belong to a tracked repository;38- you must configure a Buildkite pipeline for that repository; and39- you must configure the webhook described below.4041Webhook Configuration42=====================4344In {nav Settings} for your Organization in Buildkite, under45{nav Notification Services}, add a new **Webook Notification**.4647Use these settings:4849- **Webhook URL**: %s50- **Token**: The "Webhook Token" field below and the "Token" field in51Buildkite should both be set to the same nonempty value (any random52secret). You can use copy/paste the value Buildkite generates into53this form.54- **Events**: Only **build.finish** needs to be active.5556Environment57===========5859These variables will be available in the build environment:6061| Variable | Description |62|----------|-------------|63| `HARBORMASTER_BUILD_TARGET_PHID` | PHID of the Build Target.64EOTEXT65,66$hook_uri);67}6869public function execute(70HarbormasterBuild $build,71HarbormasterBuildTarget $build_target) {72$viewer = PhabricatorUser::getOmnipotentUser();7374if (PhabricatorEnv::getEnvConfig('phabricator.silent')) {75$this->logSilencedCall($build, $build_target, pht('Buildkite'));76throw new HarbormasterBuildFailureException();77}7879$buildable = $build->getBuildable();8081$object = $buildable->getBuildableObject();82if (!($object instanceof HarbormasterBuildkiteBuildableInterface)) {83throw new Exception(84pht('This object does not support builds with Buildkite.'));85}8687$organization = $this->getSetting('organization');88$pipeline = $this->getSetting('pipeline');8990$uri = urisprintf(91'https://api.buildkite.com/v2/organizations/%s/pipelines/%s/builds',92$organization,93$pipeline);9495$data_structure = array(96'commit' => $object->getBuildkiteCommit(),97'branch' => $object->getBuildkiteBranch(),98'message' => pht(99'Harbormaster Build %s ("%s") for %s',100$build->getID(),101$build->getName(),102$buildable->getMonogram()),103'env' => array(104'HARBORMASTER_BUILD_TARGET_PHID' => $build_target->getPHID(),105),106'meta_data' => array(107'buildTargetPHID' => $build_target->getPHID(),108109// See PHI611. These are undocumented secret magic.110'phabricator:build:id' => (int)$build->getID(),111'phabricator:build:url' =>112PhabricatorEnv::getProductionURI($build->getURI()),113'phabricator:buildable:id' => (int)$buildable->getID(),114'phabricator:buildable:url' =>115PhabricatorEnv::getProductionURI($buildable->getURI()),116),117);118119$engine = HarbormasterBuildableEngine::newForObject(120$object,121$viewer);122123$author_identity = $engine->getAuthorIdentity();124if ($author_identity) {125$data_structure += array(126'author' => array(127'name' => $author_identity->getIdentityDisplayName(),128'email' => $author_identity->getIdentityEmailAddress(),129),130);131}132133$json_data = phutil_json_encode($data_structure);134135$credential_phid = $this->getSetting('token');136$api_token = id(new PassphraseCredentialQuery())137->setViewer($viewer)138->withPHIDs(array($credential_phid))139->needSecrets(true)140->executeOne();141if (!$api_token) {142throw new Exception(143pht(144'Unable to load API token ("%s")!',145$credential_phid));146}147148$token = $api_token->getSecret()->openEnvelope();149150$future = id(new HTTPSFuture($uri, $json_data))151->setMethod('POST')152->addHeader('Content-Type', 'application/json')153->addHeader('Accept', 'application/json')154->addHeader('Authorization', "Bearer {$token}")155->setTimeout(60);156157$this->resolveFutures(158$build,159$build_target,160array($future));161162$this->logHTTPResponse($build, $build_target, $future, pht('Buildkite'));163164list($status, $body) = $future->resolve();165if ($status->isError()) {166throw new HarbormasterBuildFailureException();167}168169$response = phutil_json_decode($body);170171$uri_key = 'web_url';172$build_uri = idx($response, $uri_key);173if (!$build_uri) {174throw new Exception(175pht(176'Buildkite did not return a "%s"!',177$uri_key));178}179180$target_phid = $build_target->getPHID();181182$api_method = 'harbormaster.createartifact';183$api_params = array(184'buildTargetPHID' => $target_phid,185'artifactType' => HarbormasterURIArtifact::ARTIFACTCONST,186'artifactKey' => 'buildkite.uri',187'artifactData' => array(188'uri' => $build_uri,189'name' => pht('View in Buildkite'),190'ui.external' => true,191),192);193194id(new ConduitCall($api_method, $api_params))195->setUser($viewer)196->execute();197}198199public function getFieldSpecifications() {200return array(201'token' => array(202'name' => pht('API Token'),203'type' => 'credential',204'credential.type'205=> PassphraseTokenCredentialType::CREDENTIAL_TYPE,206'credential.provides'207=> PassphraseTokenCredentialType::PROVIDES_TYPE,208'required' => true,209),210'organization' => array(211'name' => pht('Organization Name'),212'type' => 'text',213'required' => true,214),215'pipeline' => array(216'name' => pht('Pipeline Name'),217'type' => 'text',218'required' => true,219),220'webhook.token' => array(221'name' => pht('Webhook Token'),222'type' => 'text',223'required' => true,224),225);226}227228public function supportsWaitForMessage() {229return false;230}231232public function shouldWaitForMessage(HarbormasterBuildTarget $target) {233return true;234}235236}237238239