Land to GitHub + support stuff
Summary:
A usable, Land to GitHub flow.
Still to do:
- Refactor all git/hg stratagies to a sane structure.
- Make the dialogs Workflow + explain why it's disabled.
- Show button and request Link Account if GH is enabled, but user is not linked.
- After refreshing token, user ends up in the settings stage.
Hacked something in LandController to be able to show an arbitrary dialog from a strategy.
It's not very nice, but I want to make some more refactoring to the controller/strategy/ies anyway.
Also made PhabricatorRepository::getRemoteURIObject() public, because it was very useful in getting
the domain and path for the repo.
Test Plan:
Went through these flows:
- load revision in hosted, github-backed, non-github backed repos to see button as needed.
- hit land with weak token - sent to refresh it with the extra scope.
- Land to repo I'm not allowed - got proper error message.
- Successfully landed; Failed to apply patch.
Reviewers: epriestley, #blessed_reviewers
Reviewed By: epriestley
CC: Korvin, epriestley, aran
Maniphest Tasks: T182
Differential Revision: https://secure.phabricator.com/D7555
2013-11-13 17:25:14 -08:00
|
|
|
<?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.
|
|
|
|
*/
|
2014-01-30 09:07:50 -08:00
|
|
|
public function createMenuItem(
|
Land to GitHub + support stuff
Summary:
A usable, Land to GitHub flow.
Still to do:
- Refactor all git/hg stratagies to a sane structure.
- Make the dialogs Workflow + explain why it's disabled.
- Show button and request Link Account if GH is enabled, but user is not linked.
- After refreshing token, user ends up in the settings stage.
Hacked something in LandController to be able to show an arbitrary dialog from a strategy.
It's not very nice, but I want to make some more refactoring to the controller/strategy/ies anyway.
Also made PhabricatorRepository::getRemoteURIObject() public, because it was very useful in getting
the domain and path for the repo.
Test Plan:
Went through these flows:
- load revision in hosted, github-backed, non-github backed repos to see button as needed.
- hit land with weak token - sent to refresh it with the extra scope.
- Land to repo I'm not allowed - got proper error message.
- Successfully landed; Failed to apply patch.
Reviewers: epriestley, #blessed_reviewers
Reviewed By: epriestley
CC: Korvin, epriestley, aran
Maniphest Tasks: T182
Differential Revision: https://secure.phabricator.com/D7555
2013-11-13 17:25:14 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-12-09 13:29:12 -08:00
|
|
|
return $this->createActionView($revision, pht('Land to GitHub'))
|
2014-05-13 07:45:39 -07:00
|
|
|
->setIcon('fa-cloud-upload');
|
Land to GitHub + support stuff
Summary:
A usable, Land to GitHub flow.
Still to do:
- Refactor all git/hg stratagies to a sane structure.
- Make the dialogs Workflow + explain why it's disabled.
- Show button and request Link Account if GH is enabled, but user is not linked.
- After refreshing token, user ends up in the settings stage.
Hacked something in LandController to be able to show an arbitrary dialog from a strategy.
It's not very nice, but I want to make some more refactoring to the controller/strategy/ies anyway.
Also made PhabricatorRepository::getRemoteURIObject() public, because it was very useful in getting
the domain and path for the repo.
Test Plan:
Went through these flows:
- load revision in hosted, github-backed, non-github backed repos to see button as needed.
- hit land with weak token - sent to refresh it with the extra scope.
- Land to repo I'm not allowed - got proper error message.
- Successfully landed; Failed to apply patch.
Reviewers: epriestley, #blessed_reviewers
Reviewed By: epriestley
CC: Korvin, epriestley, aran
Maniphest Tasks: T182
Differential Revision: https://secure.phabricator.com/D7555
2013-11-13 17:25:14 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
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(
|
2014-06-09 11:36:49 -07:00
|
|
|
'push %P HEAD:master',
|
Land to GitHub + support stuff
Summary:
A usable, Land to GitHub flow.
Still to do:
- Refactor all git/hg stratagies to a sane structure.
- Make the dialogs Workflow + explain why it's disabled.
- Show button and request Link Account if GH is enabled, but user is not linked.
- After refreshing token, user ends up in the settings stage.
Hacked something in LandController to be able to show an arbitrary dialog from a strategy.
It's not very nice, but I want to make some more refactoring to the controller/strategy/ies anyway.
Also made PhabricatorRepository::getRemoteURIObject() public, because it was very useful in getting
the domain and path for the repo.
Test Plan:
Went through these flows:
- load revision in hosted, github-backed, non-github backed repos to see button as needed.
- hit land with weak token - sent to refresh it with the extra scope.
- Land to repo I'm not allowed - got proper error message.
- Successfully landed; Failed to apply patch.
Reviewers: epriestley, #blessed_reviewers
Reviewed By: epriestley
CC: Korvin, epriestley, aran
Maniphest Tasks: T182
Differential Revision: https://secure.phabricator.com/D7555
2013-11-13 17:25:14 -08:00
|
|
|
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()))
|
2014-06-09 11:36:49 -07:00
|
|
|
->withAccountTypes(array('github'))
|
Land to GitHub + support stuff
Summary:
A usable, Land to GitHub flow.
Still to do:
- Refactor all git/hg stratagies to a sane structure.
- Make the dialogs Workflow + explain why it's disabled.
- Show button and request Link Account if GH is enabled, but user is not linked.
- After refreshing token, user ends up in the settings stage.
Hacked something in LandController to be able to show an arbitrary dialog from a strategy.
It's not very nice, but I want to make some more refactoring to the controller/strategy/ies anyway.
Also made PhabricatorRepository::getRemoteURIObject() public, because it was very useful in getting
the domain and path for the repo.
Test Plan:
Went through these flows:
- load revision in hosted, github-backed, non-github backed repos to see button as needed.
- hit land with weak token - sent to refresh it with the extra scope.
- Land to repo I'm not allowed - got proper error message.
- Successfully landed; Failed to apply patch.
Reviewers: epriestley, #blessed_reviewers
Reviewed By: epriestley
CC: Korvin, epriestley, aran
Maniphest Tasks: T182
Differential Revision: https://secure.phabricator.com/D7555
2013-11-13 17:25:14 -08:00
|
|
|
->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())
|
2014-06-07 12:05:30 -07:00
|
|
|
->setDisableWorkflowOnSubmit(true)
|
Land to GitHub + support stuff
Summary:
A usable, Land to GitHub flow.
Still to do:
- Refactor all git/hg stratagies to a sane structure.
- Make the dialogs Workflow + explain why it's disabled.
- Show button and request Link Account if GH is enabled, but user is not linked.
- After refreshing token, user ends up in the settings stage.
Hacked something in LandController to be able to show an arbitrary dialog from a strategy.
It's not very nice, but I want to make some more refactoring to the controller/strategy/ies anyway.
Also made PhabricatorRepository::getRemoteURIObject() public, because it was very useful in getting
the domain and path for the repo.
Test Plan:
Went through these flows:
- load revision in hosted, github-backed, non-github backed repos to see button as needed.
- hit land with weak token - sent to refresh it with the extra scope.
- Land to repo I'm not allowed - got proper error message.
- Successfully landed; Failed to apply patch.
Reviewers: epriestley, #blessed_reviewers
Reviewed By: epriestley
CC: Korvin, epriestley, aran
Maniphest Tasks: T182
Differential Revision: https://secure.phabricator.com/D7555
2013-11-13 17:25:14 -08:00
|
|
|
->addSubmitButton(pht('Refresh Account Link'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|