Page MenuHomePhabricator

D7555.diff

diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -857,7 +857,7 @@
),
'aphront-dialog-view-css' =>
array(
- 'uri' => '/res/6b6a41c6/rsrc/css/aphront/dialog-view.css',
+ 'uri' => '/res/8f151d2a/rsrc/css/aphront/dialog-view.css',
'type' => 'css',
'requires' =>
array(
@@ -4328,7 +4328,7 @@
), array(
'packages' =>
array(
- 'd831cac3' =>
+ '1a71c1b4' =>
array(
'name' => 'core.pkg.css',
'symbols' =>
@@ -4377,7 +4377,7 @@
41 => 'phabricator-tag-view-css',
42 => 'phui-list-view-css',
),
- 'uri' => '/res/pkg/d831cac3/core.pkg.css',
+ 'uri' => '/res/pkg/1a71c1b4/core.pkg.css',
'type' => 'css',
),
'2c1dba03' =>
@@ -4569,15 +4569,15 @@
),
'reverse' =>
array(
- 'aphront-dialog-view-css' => 'd831cac3',
- 'aphront-error-view-css' => 'd831cac3',
- 'aphront-list-filter-view-css' => 'd831cac3',
- 'aphront-pager-view-css' => 'd831cac3',
- 'aphront-panel-view-css' => 'd831cac3',
- 'aphront-table-view-css' => 'd831cac3',
- 'aphront-tokenizer-control-css' => 'd831cac3',
- 'aphront-tooltip-css' => 'd831cac3',
- 'aphront-typeahead-control-css' => 'd831cac3',
+ 'aphront-dialog-view-css' => '1a71c1b4',
+ 'aphront-error-view-css' => '1a71c1b4',
+ 'aphront-list-filter-view-css' => '1a71c1b4',
+ 'aphront-pager-view-css' => '1a71c1b4',
+ 'aphront-panel-view-css' => '1a71c1b4',
+ 'aphront-table-view-css' => '1a71c1b4',
+ 'aphront-tokenizer-control-css' => '1a71c1b4',
+ 'aphront-tooltip-css' => '1a71c1b4',
+ 'aphront-typeahead-control-css' => '1a71c1b4',
'differential-changeset-view-css' => '1084b12b',
'differential-core-view-css' => '1084b12b',
'differential-inline-comment-editor' => '5e9e5c4e',
@@ -4591,7 +4591,7 @@
'differential-table-of-contents-css' => '1084b12b',
'diffusion-commit-view-css' => '7aa115b4',
'diffusion-icons-css' => '7aa115b4',
- 'global-drag-and-drop-css' => 'd831cac3',
+ 'global-drag-and-drop-css' => '1a71c1b4',
'inline-comment-summary-css' => '1084b12b',
'javelin-aphlict' => '2c1dba03',
'javelin-behavior' => '3e3be199',
@@ -4666,56 +4666,56 @@
'javelin-util' => '3e3be199',
'javelin-vector' => '3e3be199',
'javelin-workflow' => '3e3be199',
- 'lightbox-attachment-css' => 'd831cac3',
+ 'lightbox-attachment-css' => '1a71c1b4',
'maniphest-task-summary-css' => '49898640',
- 'phabricator-action-list-view-css' => 'd831cac3',
- 'phabricator-application-launch-view-css' => 'd831cac3',
+ 'phabricator-action-list-view-css' => '1a71c1b4',
+ 'phabricator-application-launch-view-css' => '1a71c1b4',
'phabricator-busy' => '2c1dba03',
'phabricator-content-source-view-css' => '1084b12b',
- 'phabricator-core-css' => 'd831cac3',
- 'phabricator-crumbs-view-css' => 'd831cac3',
+ 'phabricator-core-css' => '1a71c1b4',
+ 'phabricator-crumbs-view-css' => '1a71c1b4',
'phabricator-drag-and-drop-file-upload' => '5e9e5c4e',
'phabricator-dropdown-menu' => '2c1dba03',
'phabricator-file-upload' => '2c1dba03',
- 'phabricator-filetree-view-css' => 'd831cac3',
- 'phabricator-flag-css' => 'd831cac3',
+ 'phabricator-filetree-view-css' => '1a71c1b4',
+ 'phabricator-flag-css' => '1a71c1b4',
'phabricator-hovercard' => '2c1dba03',
- 'phabricator-jump-nav' => 'd831cac3',
+ 'phabricator-jump-nav' => '1a71c1b4',
'phabricator-keyboard-shortcut' => '2c1dba03',
'phabricator-keyboard-shortcut-manager' => '2c1dba03',
- 'phabricator-main-menu-view' => 'd831cac3',
+ 'phabricator-main-menu-view' => '1a71c1b4',
'phabricator-menu-item' => '2c1dba03',
- 'phabricator-nav-view-css' => 'd831cac3',
+ 'phabricator-nav-view-css' => '1a71c1b4',
'phabricator-notification' => '2c1dba03',
- 'phabricator-notification-css' => 'd831cac3',
- 'phabricator-notification-menu-css' => 'd831cac3',
+ 'phabricator-notification-css' => '1a71c1b4',
+ 'phabricator-notification-menu-css' => '1a71c1b4',
'phabricator-object-selector-css' => '1084b12b',
'phabricator-phtize' => '2c1dba03',
'phabricator-prefab' => '2c1dba03',
'phabricator-project-tag-css' => '49898640',
- 'phabricator-remarkup-css' => 'd831cac3',
+ 'phabricator-remarkup-css' => '1a71c1b4',
'phabricator-shaped-request' => '5e9e5c4e',
- 'phabricator-side-menu-view-css' => 'd831cac3',
- 'phabricator-standard-page-view' => 'd831cac3',
- 'phabricator-tag-view-css' => 'd831cac3',
+ 'phabricator-side-menu-view-css' => '1a71c1b4',
+ 'phabricator-standard-page-view' => '1a71c1b4',
+ 'phabricator-tag-view-css' => '1a71c1b4',
'phabricator-textareautils' => '2c1dba03',
'phabricator-tooltip' => '2c1dba03',
- 'phabricator-transaction-view-css' => 'd831cac3',
- 'phabricator-zindex-css' => 'd831cac3',
- 'phui-button-css' => 'd831cac3',
- 'phui-form-css' => 'd831cac3',
- 'phui-form-view-css' => 'd831cac3',
- 'phui-header-view-css' => 'd831cac3',
- 'phui-icon-view-css' => 'd831cac3',
- 'phui-list-view-css' => 'd831cac3',
- 'phui-object-item-list-view-css' => 'd831cac3',
- 'phui-property-list-view-css' => 'd831cac3',
- 'phui-spacing-css' => 'd831cac3',
- 'sprite-apps-large-css' => 'd831cac3',
- 'sprite-gradient-css' => 'd831cac3',
- 'sprite-icons-css' => 'd831cac3',
- 'sprite-menu-css' => 'd831cac3',
- 'sprite-status-css' => 'd831cac3',
- 'syntax-highlighting-css' => 'd831cac3',
+ 'phabricator-transaction-view-css' => '1a71c1b4',
+ 'phabricator-zindex-css' => '1a71c1b4',
+ 'phui-button-css' => '1a71c1b4',
+ 'phui-form-css' => '1a71c1b4',
+ 'phui-form-view-css' => '1a71c1b4',
+ 'phui-header-view-css' => '1a71c1b4',
+ 'phui-icon-view-css' => '1a71c1b4',
+ 'phui-list-view-css' => '1a71c1b4',
+ 'phui-object-item-list-view-css' => '1a71c1b4',
+ 'phui-property-list-view-css' => '1a71c1b4',
+ 'phui-spacing-css' => '1a71c1b4',
+ 'sprite-apps-large-css' => '1a71c1b4',
+ 'sprite-gradient-css' => '1a71c1b4',
+ 'sprite-icons-css' => '1a71c1b4',
+ 'sprite-menu-css' => '1a71c1b4',
+ 'sprite-status-css' => '1a71c1b4',
+ 'syntax-highlighting-css' => '1a71c1b4',
),
));
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -387,6 +387,7 @@
'DifferentialJIRAIssuesFieldSpecification' => 'applications/differential/field/specification/DifferentialJIRAIssuesFieldSpecification.php',
'DifferentialLandingActionMenuEventListener' => 'applications/differential/landing/DifferentialLandingActionMenuEventListener.php',
'DifferentialLandingStrategy' => 'applications/differential/landing/DifferentialLandingStrategy.php',
+ 'DifferentialLandingToGitHub' => 'applications/differential/landing/DifferentialLandingToGitHub.php',
'DifferentialLandingToHostedGit' => 'applications/differential/landing/DifferentialLandingToHostedGit.php',
'DifferentialLandingToHostedMercurial' => 'applications/differential/landing/DifferentialLandingToHostedMercurial.php',
'DifferentialLinesFieldSpecification' => 'applications/differential/field/specification/DifferentialLinesFieldSpecification.php',
@@ -2675,6 +2676,7 @@
'DifferentialInlineCommentView' => 'AphrontView',
'DifferentialJIRAIssuesFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialLandingActionMenuEventListener' => 'PhabricatorEventListener',
+ 'DifferentialLandingToGitHub' => 'DifferentialLandingStrategy',
'DifferentialLandingToHostedGit' => 'DifferentialLandingStrategy',
'DifferentialLandingToHostedMercurial' => 'DifferentialLandingStrategy',
'DifferentialLinesFieldSpecification' => 'DifferentialFieldSpecification',
diff --git a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
--- a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
+++ b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php
@@ -37,6 +37,11 @@
$adapter = $this->getAdapter();
$adapter->setState(PhabricatorHash::digest($request->getCookie('phcid')));
+ $scope = $request->getStr("scope");
+ if ($scope) {
+ $adapter->setScope($scope);
+ }
+
$attributes = array(
'method' => 'GET',
'uri' => $adapter->getAuthenticateURI(),
diff --git a/src/applications/differential/controller/DifferentialRevisionLandController.php b/src/applications/differential/controller/DifferentialRevisionLandController.php
--- a/src/applications/differential/controller/DifferentialRevisionLandController.php
+++ b/src/applications/differential/controller/DifferentialRevisionLandController.php
@@ -36,13 +36,14 @@
}
if ($request->isDialogFormPost()) {
+ $response = null;
+ $text = '';
try {
- $this->attemptLand($revision, $request);
+ $response = $this->attemptLand($revision, $request);
$title = pht("Success!");
$text = pht("Revision was successfully landed.");
} catch (Exception $ex) {
$title = pht("Failed to land revision");
- $text = 'moo';
if ($ex instanceof PhutilProxyException) {
$text = hsprintf(
'%s:<br><pre>%s</pre>',
@@ -55,13 +56,15 @@
->appendChild($text);
}
- $dialog = id(new AphrontDialogView())
- ->setUser($viewer)
- ->setTitle($title)
- ->appendChild(phutil_tag('p', array(), $text))
- ->setSubmitURI('/D'.$revision_id)
- ->addSubmitButton(pht('Done'));
-
+ if ($response instanceof AphrontDialogView) {
+ $dialog = $response;
+ } else {
+ $dialog = id(new AphrontDialogView())
+ ->setUser($viewer)
+ ->setTitle($title)
+ ->appendChild(phutil_tag('p', array(), $text))
+ ->addCancelButton('/D'.$revision_id, pht('Done'));
+ }
return id(new AphrontDialogResponse())->setDialog($dialog);
}
@@ -108,7 +111,7 @@
$lock = $this->lockRepository($repository);
try {
- $this->pushStrategy->processLandRequest(
+ $response = $this->pushStrategy->processLandRequest(
$request,
$revision,
$repository);
@@ -118,6 +121,7 @@
}
$lock->unlock();
+ return $response;
}
private function lockRepository($repository) {
diff --git a/src/applications/differential/landing/DifferentialLandingToGitHub.php b/src/applications/differential/landing/DifferentialLandingToGitHub.php
new file mode 100644
--- /dev/null
+++ b/src/applications/differential/landing/DifferentialLandingToGitHub.php
@@ -0,0 +1,179 @@
+<?php
+
+final class DifferentialLandingToGitHub
+ extends DifferentialLandingStrategy {
+
+ private $account;
+ private $provider;
+
+ public function processLandRequest(
+ AphrontRequest $request,
+ DifferentialRevision $revision,
+ PhabricatorRepository $repository) {
+
+ $viewer = $request->getUser();
+ $this->init($viewer, $repository);
+
+ $workspace = $this->getGitWorkspace($repository);
+
+ try {
+ id(new DifferentialLandingToHostedGit())
+ ->commitRevisionToWorkspace(
+ $revision,
+ $workspace,
+ $viewer);
+ } catch (Exception $e) {
+ throw new PhutilProxyException(
+ 'Failed to commit patch',
+ $e);
+ }
+
+ try {
+ $this->pushWorkspaceRepository($repository, $workspace);
+ } catch (Exception $e) {
+ // If it's a permission problem, we know more than git.
+ $dialog = $this->verifyRemotePermissions($viewer, $revision, $repository);
+ if ($dialog) {
+ return $dialog;
+ }
+
+ // Else, throw what git said.
+ throw new PhutilProxyException(
+ 'Failed to push changes upstream',
+ $e);
+ }
+ }
+
+ /**
+ * returns PhabricatorActionView or an array of PhabricatorActionView or null.
+ */
+ public function createMenuItems(
+ PhabricatorUser $viewer,
+ DifferentialRevision $revision,
+ PhabricatorRepository $repository) {
+
+ $vcs = $repository->getVersionControlSystem();
+ if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) {
+ return;
+ }
+
+ if ($repository->isHosted()) {
+ return;
+ }
+
+ try {
+ // These throw when failing.
+ $this->init($viewer, $repository);
+ $this->findGitHubRepo($repository);
+ } catch (Exception $e) {
+ return;
+ }
+
+ return $this->createActionView(
+ $revision,
+ pht('Land to GitHub'));
+ }
+
+ public function pushWorkspaceRepository(
+ PhabricatorRepository $repository,
+ ArcanistRepositoryAPI $workspace) {
+
+ $token = $this->getAccessToken();
+
+ $github_repo = $this->findGitHubRepo($repository);
+
+ $remote = urisprintf(
+ 'https://%s:x-oauth-basic@%s/%s.git',
+ $token,
+ $this->provider->getProviderDomain(),
+ $github_repo);
+
+ $workspace->execxLocal(
+ "push %P HEAD:master",
+ new PhutilOpaqueEnvelope($remote));
+ }
+
+ private function init($viewer, $repository) {
+ $repo_uri = $repository->getRemoteURIObject();
+ $repo_domain = $repo_uri->getDomain();
+
+ $this->account = id(new PhabricatorExternalAccountQuery())
+ ->setViewer($viewer)
+ ->withUserPHIDs(array($viewer->getPHID()))
+ ->withAccountTypes(array("github"))
+ ->withAccountDomains(array($repo_domain))
+ ->executeOne();
+
+ if (!$this->account) {
+ throw new Exception(
+ "No matching GitHub account found for {$repo_domain}.");
+ }
+
+ $this->provider = PhabricatorAuthProvider::getEnabledProviderByKey(
+ $this->account->getProviderKey());
+ if (!$this->provider) {
+ throw new Exception("GitHub provider for {$repo_domain} is not enabled.");
+ }
+ }
+
+ private function findGitHubRepo(PhabricatorRepository $repository) {
+ $repo_uri = $repository->getRemoteURIObject();
+
+ $repo_path = $repo_uri->getPath();
+
+ if (substr($repo_path, -4) == '.git') {
+ $repo_path = substr($repo_path, 0, -4);
+ }
+ $repo_path = ltrim($repo_path, '/');
+
+ return $repo_path;
+ }
+
+ private function getAccessToken() {
+ return $this->provider->getOAuthAccessToken($this->account);
+ }
+
+ private function verifyRemotePermissions($viewer, $revision, $repository) {
+ $github_user = $this->account->getUsername();
+ $github_repo = $this->findGitHubRepo($repository);
+
+ $uri = urisprintf(
+ 'https://api.github.com/repos/%s/collaborators/%s',
+ $github_repo,
+ $github_user);
+
+ $uri = new PhutilURI($uri);
+ $uri->setQueryParam('access_token', $this->getAccessToken());
+ list($status, $body, $headers) = id(new HTTPSFuture($uri))->resolve();
+
+ // Likely status codes:
+ // 204 No Content: Has permissions. Token might be too weak.
+ // 404 Not Found: Not a collaborator.
+ // 401 Unauthorized: Token is bad/revoked.
+
+ $no_permission = ($status->getStatusCode() == 404);
+
+ if ($no_permission) {
+ throw new Exception(
+ "You don't have permission to push to this repository. \n".
+ "Push permissions for this repository are managed on GitHub.");
+ }
+
+ $scopes = BaseHTTPFuture::getHeader($headers, 'X-OAuth-Scopes');
+ if (strpos($scopes, 'public_repo') === false) {
+ $provider_key = $this->provider->getProviderKey();
+ $refresh_token_uri = new PhutilURI("/auth/refresh/{$provider_key}/");
+ $refresh_token_uri->setQueryParam('scope', 'public_repo');
+
+ return id(new AphrontDialogView())
+ ->setUser($viewer)
+ ->setTitle(pht('Stronger token needed'))
+ ->appendChild(pht(
+ 'In order to complete this action, you need a '.
+ 'stronger GitHub token.'))
+ ->setSubmitURI($refresh_token_uri)
+ ->addCancelButton('/D'.$revision->getId())
+ ->addSubmitButton(pht('Refresh Account Link'));
+ }
+ }
+}
diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php
--- a/src/applications/repository/storage/PhabricatorRepository.php
+++ b/src/applications/repository/storage/PhabricatorRepository.php
@@ -577,7 +577,7 @@
* @{class@libphutil:PhutilGitURI}.
* @task uri
*/
- private function getRemoteURIObject() {
+ public function getRemoteURIObject() {
$raw_uri = $this->getDetail('remote-uri');
if (!$raw_uri) {
return new PhutilURI('');

File Metadata

Mime Type
text/x-diff
Storage Engine
amazon-s3
Storage Format
Raw Data
Storage Handle
phabricator/g7/gi/reme3huf5gkdnuia
Default Alt Text
D7555.diff (16 KB)

Event Timeline