getDomain(); $domain = phutil_utf8_strtolower($domain); switch ($domain) { case 'github.com': case 'www.github.com': return $uri_object->getPath(); default: return null; } } public function execute( HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $viewer = PhabricatorUser::getOmnipotentUser(); if (PhabricatorEnv::getEnvConfig('phabricator.silent')) { $this->logSilencedCall($build, $build_target, pht('CircleCI')); throw new HarbormasterBuildFailureException(); } $buildable = $build->getBuildable(); $object = $buildable->getBuildableObject(); $object_phid = $object->getPHID(); if (!($object instanceof HarbormasterCircleCIBuildableInterface)) { throw new Exception( pht( 'Object ("%s") does not implement interface "%s". Only objects '. 'which implement this interface can be built with CircleCI.', $object_phid, 'HarbormasterCircleCIBuildableInterface')); } $github_uri = $object->getCircleCIGitHubRepositoryURI(); $build_type = $object->getCircleCIBuildIdentifierType(); $build_identifier = $object->getCircleCIBuildIdentifier(); $path = self::getGitHubPath($github_uri); if ($path === null) { throw new Exception( pht( 'Object ("%s") claims "%s" is a GitHub repository URI, but the '. 'domain does not appear to be GitHub.', $object_phid, $github_uri)); } $path_parts = trim($path, '/'); $path_parts = explode('/', $path_parts); if (count($path_parts) < 2) { throw new Exception( pht( 'Object ("%s") claims "%s" is a GitHub repository URI, but the '. 'path ("%s") does not have enough components (expected at least '. 'two).', $object_phid, $github_uri, $path)); } list($github_namespace, $github_name) = $path_parts; $github_name = preg_replace('(\\.git$)', '', $github_name); $credential_phid = $this->getSetting('token'); $api_token = id(new PassphraseCredentialQuery()) ->setViewer($viewer) ->withPHIDs(array($credential_phid)) ->needSecrets(true) ->executeOne(); if (!$api_token) { throw new Exception( pht( 'Unable to load API token ("%s")!', $credential_phid)); } // When we pass "revision", the branch is ignored (and does not even need // to exist), and only shows up in the UI. Use a cute string which will // certainly never break anything or cause any kind of problem. $ship = "\xF0\x9F\x9A\xA2"; $branch = "{$ship}Harbormaster"; $token = $api_token->getSecret()->openEnvelope(); $parts = array( 'https://circleci.com/api/v1/project', phutil_escape_uri($github_namespace), phutil_escape_uri($github_name)."?circle-token={$token}", ); $uri = implode('/', $parts); $data_structure = array(); switch ($build_type) { case 'tag': $data_structure['tag'] = $build_identifier; break; case 'revision': $data_structure['revision'] = $build_identifier; break; default: throw new Exception( pht( 'Unknown CircleCI build type "%s". Expected "%s" or "%s".', $build_type, 'tag', 'revision')); } $data_structure['build_parameters'] = array( 'HARBORMASTER_BUILD_TARGET_PHID' => $build_target->getPHID(), ); $json_data = phutil_json_encode($data_structure); $future = id(new HTTPSFuture($uri, $json_data)) ->setMethod('POST') ->addHeader('Content-Type', 'application/json') ->addHeader('Accept', 'application/json') ->setTimeout(60); $this->resolveFutures( $build, $build_target, array($future)); $this->logHTTPResponse($build, $build_target, $future, pht('CircleCI')); list($status, $body) = $future->resolve(); if ($status->isError()) { throw new HarbormasterBuildFailureException(); } $response = phutil_json_decode($body); $build_uri = idx($response, 'build_url'); if (!$build_uri) { throw new Exception( pht( 'CircleCI did not return a "%s"!', 'build_url')); } $target_phid = $build_target->getPHID(); // Write an artifact to create a link to the external build in CircleCI. $api_method = 'harbormaster.createartifact'; $api_params = array( 'buildTargetPHID' => $target_phid, 'artifactType' => HarbormasterURIArtifact::ARTIFACTCONST, 'artifactKey' => 'circleci.uri', 'artifactData' => array( 'uri' => $build_uri, 'name' => pht('View in CircleCI'), 'ui.external' => true, ), ); id(new ConduitCall($api_method, $api_params)) ->setUser($viewer) ->execute(); } public function getFieldSpecifications() { return array( 'token' => array( 'name' => pht('API Token'), 'type' => 'credential', 'credential.type' => PassphraseTokenCredentialType::CREDENTIAL_TYPE, 'credential.provides' => PassphraseTokenCredentialType::PROVIDES_TYPE, 'required' => true, ), ); } public function supportsWaitForMessage() { // NOTE: We always wait for a message, but don't need to show the UI // control since "Wait" is the only valid choice. return false; } public function shouldWaitForMessage(HarbormasterBuildTarget $target) { return true; } }