2023-05-22 22:58:13 +12:00
< ? php
2023-06-18 23:38:37 +12:00
use Appwrite\Auth\OAuth2\Github as OAuth2Github ;
2023-05-22 22:58:13 +12:00
use Utopia\App ;
use Appwrite\Event\Build ;
use Appwrite\Event\Delete ;
2023-08-06 20:51:53 +12:00
use Utopia\Validator\Host ;
2023-05-22 22:58:13 +12:00
use Utopia\Database\Database ;
use Utopia\Database\Document ;
use Appwrite\Utopia\Request ;
use Appwrite\Utopia\Response ;
use Utopia\Validator\Text ;
use Utopia\VCS\Adapter\Git\GitHub ;
use Appwrite\Extend\Exception ;
use Appwrite\Utopia\Database\Validator\Queries\Installations ;
2023-05-28 23:39:48 +12:00
use Appwrite\Vcs\Comment ;
2023-08-01 20:40:16 +12:00
use Utopia\CLI\Console ;
2023-07-30 20:30:47 +12:00
use Utopia\Config\Config ;
2023-06-18 23:38:37 +12:00
use Utopia\Database\DateTime ;
2023-08-06 20:51:53 +12:00
use Utopia\Database\Helpers\ID ;
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2023-05-22 22:58:13 +12:00
use Utopia\Database\Query ;
use Utopia\Database\Validator\Authorization ;
2023-09-14 07:08:49 +12:00
use Utopia\Detector\Adapter\Bun ;
2023-06-14 06:44:44 +12:00
use Utopia\Detector\Adapter\CPP ;
use Utopia\Detector\Adapter\Dart ;
use Utopia\Detector\Adapter\Deno ;
use Utopia\Detector\Adapter\Dotnet ;
use Utopia\Detector\Adapter\Java ;
use Utopia\Detector\Adapter\JavaScript ;
use Utopia\Detector\Adapter\PHP ;
use Utopia\Detector\Adapter\Python ;
use Utopia\Detector\Adapter\Ruby ;
use Utopia\Detector\Adapter\Swift ;
use Utopia\Detector\Detector ;
2023-06-18 23:38:37 +12:00
use Utopia\Validator\Boolean ;
2023-10-14 02:20:58 +13:00
use Utopia\VCS\Exception\RepositoryNotFound ;
2023-05-22 22:58:13 +12:00
2023-06-16 21:07:47 +12:00
use function Swoole\Coroutine\batch ;
2023-10-09 23:28:06 +13:00
$createGitDeployments = function ( GitHub $github , string $providerInstallationId , array $repositories , string $providerBranch , string $providerBranchUrl , string $providerRepositoryName , string $providerRepositoryUrl , string $providerRepositoryOwner , string $providerCommitHash , string $providerCommitAuthor , string $providerCommitAuthorUrl , string $providerCommitMessage , string $providerCommitUrl , string $providerPullRequestId , bool $external , Database $dbForConsole , Build $queueForBuilds , callable $getProjectDB , Request $request ) {
2023-07-30 21:51:13 +12:00
foreach ( $repositories as $resource ) {
2023-07-28 20:27:16 +12:00
$resourceType = $resource -> getAttribute ( 'resourceType' );
if ( $resourceType === " function " ) {
$projectId = $resource -> getAttribute ( 'projectId' );
$project = Authorization :: skip ( fn () => $dbForConsole -> getDocument ( 'projects' , $projectId ));
$dbForProject = $getProjectDB ( $project );
$functionId = $resource -> getAttribute ( 'resourceId' );
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2023-09-21 14:26:23 +12:00
$functionInternalId = $function -> getInternalId ();
2023-07-28 20:27:16 +12:00
$deploymentId = ID :: unique ();
2023-07-30 21:51:13 +12:00
$repositoryId = $resource -> getId ();
$repositoryInternalId = $resource -> getInternalId ();
$providerRepositoryId = $resource -> getAttribute ( 'providerRepositoryId' );
$installationId = $resource -> getAttribute ( 'installationId' );
2023-08-09 22:54:46 +12:00
$installationInternalId = $resource -> getAttribute ( 'installationInternalId' );
2023-07-30 21:51:13 +12:00
$productionBranch = $function -> getAttribute ( 'providerBranch' );
2023-07-28 20:27:16 +12:00
$activate = false ;
2023-07-30 21:51:13 +12:00
if ( $providerBranch == $productionBranch && $external === false ) {
2023-07-28 20:27:16 +12:00
$activate = true ;
}
2023-07-30 21:51:13 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId ) ? ? '' ;
2023-10-28 03:08:33 +13:00
try {
$repositoryName = $github -> getRepositoryName ( $providerRepositoryId ) ? ? '' ;
if ( empty ( $repositoryName )) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
} catch ( RepositoryNotFound $e ) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
2023-07-28 20:27:16 +12:00
if ( empty ( $repositoryName )) {
2023-07-31 18:47:47 +12:00
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
2023-07-28 20:27:16 +12:00
}
$isAuthorized = ! $external ;
2023-07-30 21:51:13 +12:00
if ( ! $isAuthorized && ! empty ( $providerPullRequestId )) {
if ( \in_array ( $providerPullRequestId , $resource -> getAttribute ( 'providerPullRequestIds' , []))) {
2023-07-28 20:27:16 +12:00
$isAuthorized = true ;
}
}
$commentStatus = $isAuthorized ? 'waiting' : 'failed' ;
2023-08-09 21:52:34 +12:00
$authorizeUrl = $request -> getProtocol () . '://' . $request -> getHostname () . " /git/authorize-contributor?projectId= { $projectId } &installationId= { $installationId } &repositoryId= { $repositoryId } &providerPullRequestId= { $providerPullRequestId } " ;
2023-08-10 03:53:58 +12:00
$action = $isAuthorized ? [ 'type' => 'logs' ] : [ 'type' => 'authorize' , 'url' => $authorizeUrl ];
2023-08-09 21:52:34 +12:00
2023-08-20 00:51:27 +12:00
$latestCommentId = '' ;
if ( ! empty ( $providerPullRequestId )) {
$latestComment = Authorization :: skip ( fn () => $dbForConsole -> findOne ( 'vcsComments' , [
Query :: equal ( 'providerRepositoryId' , [ $providerRepositoryId ]),
Query :: equal ( 'providerPullRequestId' , [ $providerPullRequestId ]),
Query :: orderDesc ( '$createdAt' ),
]));
2023-07-28 20:27:16 +12:00
2023-08-20 00:51:27 +12:00
if ( $latestComment !== false && ! $latestComment -> isEmpty ()) {
$latestCommentId = $latestComment -> getAttribute ( 'providerCommentId' , '' );
$comment = new Comment ();
$comment -> parseComment ( $github -> getComment ( $owner , $repositoryName , $latestCommentId ));
$comment -> addBuild ( $project , $function , $commentStatus , $deploymentId , $action );
$latestCommentId = \strval ( $github -> updateComment ( $owner , $repositoryName , $latestCommentId , $comment -> generateComment ()));
} else {
$comment = new Comment ();
$comment -> addBuild ( $project , $function , $commentStatus , $deploymentId , $action );
2023-07-30 21:51:13 +12:00
$latestCommentId = \strval ( $github -> createComment ( $owner , $repositoryName , $providerPullRequestId , $comment -> generateComment ()));
2023-08-20 00:51:27 +12:00
if ( ! empty ( $latestCommentId )) {
$teamId = $project -> getAttribute ( 'teamId' , '' );
$latestComment = Authorization :: skip ( fn () => $dbForConsole -> createDocument ( 'vcsComments' , new Document ([
'$id' => ID :: unique (),
'$permissions' => [
Permission :: read ( Role :: team ( ID :: custom ( $teamId ))),
Permission :: update ( Role :: team ( ID :: custom ( $teamId ), 'owner' )),
Permission :: update ( Role :: team ( ID :: custom ( $teamId ), 'developer' )),
Permission :: delete ( Role :: team ( ID :: custom ( $teamId ), 'owner' )),
Permission :: delete ( Role :: team ( ID :: custom ( $teamId ), 'developer' )),
],
'installationInternalId' => $installationInternalId ,
'installationId' => $installationId ,
'projectInternalId' => $project -> getInternalId (),
'projectId' => $project -> getId (),
'providerRepositoryId' => $providerRepositoryId ,
'providerBranch' => $providerBranch ,
'providerPullRequestId' => $providerPullRequestId ,
'providerCommentId' => $latestCommentId
])));
2023-07-28 20:27:16 +12:00
}
}
2023-08-20 00:51:27 +12:00
} elseif ( ! empty ( $providerBranch )) {
$latestComments = Authorization :: skip ( fn () => $dbForConsole -> find ( 'vcsComments' , [
Query :: equal ( 'providerRepositoryId' , [ $providerRepositoryId ]),
Query :: equal ( 'providerBranch' , [ $providerBranch ]),
Query :: orderDesc ( '$createdAt' ),
]));
2023-07-28 20:27:16 +12:00
2023-08-20 00:51:27 +12:00
foreach ( $latestComments as $comment ) {
$latestCommentId = $comment -> getAttribute ( 'providerCommentId' , '' );
$comment = new Comment ();
$comment -> parseComment ( $github -> getComment ( $owner , $repositoryName , $latestCommentId ));
$comment -> addBuild ( $project , $function , $commentStatus , $deploymentId , $action );
2023-07-28 20:27:16 +12:00
2023-08-20 00:51:27 +12:00
$latestCommentId = \strval ( $github -> updateComment ( $owner , $repositoryName , $latestCommentId , $comment -> generateComment ()));
}
2023-07-28 20:27:16 +12:00
}
if ( ! $isAuthorized ) {
$functionName = $function -> getAttribute ( 'name' );
$projectName = $project -> getAttribute ( 'name' );
$name = " { $functionName } ( { $projectName } ) " ;
$message = 'Authorization required for external contributor.' ;
2023-07-30 21:51:13 +12:00
$providerRepositoryId = $resource -> getAttribute ( 'providerRepositoryId' );
2023-10-28 03:08:33 +13:00
try {
$repositoryName = $github -> getRepositoryName ( $providerRepositoryId ) ? ? '' ;
if ( empty ( $repositoryName )) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
} catch ( RepositoryNotFound $e ) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
2023-07-30 21:51:13 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId );
2023-08-09 21:52:34 +12:00
$github -> updateCommitStatus ( $repositoryName , $providerCommitHash , $owner , 'failure' , $message , $authorizeUrl , $name );
2023-07-28 20:27:16 +12:00
continue ;
}
2023-08-12 04:52:13 +12:00
if ( $external ) {
$pullRequestResponse = $github -> getPullRequest ( $owner , $repositoryName , $providerPullRequestId );
$providerRepositoryName = $pullRequestResponse [ 'head' ][ 'repo' ][ 'owner' ][ 'login' ];
$providerRepositoryOwner = $pullRequestResponse [ 'head' ][ 'repo' ][ 'name' ];
}
2023-07-28 20:27:16 +12:00
$deployment = $dbForProject -> createDocument ( 'deployments' , new Document ([
'$id' => $deploymentId ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
'resourceId' => $functionId ,
2023-09-21 14:26:23 +12:00
'resourceInternalId' => $functionInternalId ,
2023-07-28 20:27:16 +12:00
'resourceType' => 'functions' ,
'entrypoint' => $function -> getAttribute ( 'entrypoint' ),
'commands' => $function -> getAttribute ( 'commands' ),
'type' => 'vcs' ,
2023-07-30 21:51:13 +12:00
'installationId' => $installationId ,
'installationInternalId' => $installationInternalId ,
'providerRepositoryId' => $providerRepositoryId ,
'repositoryId' => $repositoryId ,
'repositoryInternalId' => $repositoryInternalId ,
2023-08-10 05:35:23 +12:00
'providerBranchUrl' => $providerBranchUrl ,
2023-08-09 22:54:46 +12:00
'providerRepositoryName' => $providerRepositoryName ,
'providerRepositoryOwner' => $providerRepositoryOwner ,
'providerRepositoryUrl' => $providerRepositoryUrl ,
'providerCommitHash' => $providerCommitHash ,
2023-08-10 05:35:23 +12:00
'providerCommitAuthorUrl' => $providerCommitAuthorUrl ,
2023-08-09 22:54:46 +12:00
'providerCommitAuthor' => $providerCommitAuthor ,
'providerCommitMessage' => $providerCommitMessage ,
'providerCommitUrl' => $providerCommitUrl ,
2023-07-30 21:51:13 +12:00
'providerCommentId' => \strval ( $latestCommentId ),
'providerBranch' => $providerBranch ,
2023-07-28 20:27:16 +12:00
'search' => implode ( ' ' , [ $deploymentId , $function -> getAttribute ( 'entrypoint' )]),
'activate' => $activate ,
]));
2023-07-30 21:51:13 +12:00
if ( ! empty ( $providerCommitHash ) && $function -> getAttribute ( 'providerSilentMode' , false ) === false ) {
2023-07-28 20:27:16 +12:00
$functionName = $function -> getAttribute ( 'name' );
$projectName = $project -> getAttribute ( 'name' );
$name = " { $functionName } ( { $projectName } ) " ;
$message = 'Starting...' ;
2023-07-30 21:51:13 +12:00
$providerRepositoryId = $resource -> getAttribute ( 'providerRepositoryId' );
2023-10-28 03:08:33 +13:00
try {
$repositoryName = $github -> getRepositoryName ( $providerRepositoryId ) ? ? '' ;
if ( empty ( $repositoryName )) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
} catch ( RepositoryNotFound $e ) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
2023-07-30 21:51:13 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId );
2023-08-12 04:44:05 +12:00
$providerTargetUrl = $request -> getProtocol () . '://' . $request -> getHostname () . " /console/project- $projectId /functions/function- $functionId " ;
2023-07-30 21:51:13 +12:00
$github -> updateCommitStatus ( $repositoryName , $providerCommitHash , $owner , 'pending' , $message , $providerTargetUrl , $name );
2023-07-28 20:27:16 +12:00
}
2023-10-09 23:28:06 +13:00
$queueForBuilds
2023-07-28 20:27:16 +12:00
-> setType ( BUILD_TYPE_DEPLOYMENT )
-> setResource ( $function )
-> setDeployment ( $deployment )
-> setProject ( $project )
-> trigger ();
//TODO: Add event?
}
}
};
2023-07-31 18:47:47 +12:00
App :: get ( '/v1/vcs/github/authorize' )
2023-05-22 22:58:13 +12:00
-> desc ( 'Install GitHub App' )
-> groups ([ 'api' , 'vcs' ])
2023-08-31 06:44:33 +12:00
-> label ( 'scope' , 'vcs.read' )
2023-05-22 22:58:13 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-09-05 22:14:43 +12:00
-> label ( 'error' , __DIR__ . '/../../views/general/error.phtml' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-05-22 22:58:13 +12:00
-> label ( 'sdk.method' , 'createGitHubInstallation' )
-> label ( 'sdk.description' , '' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_MOVED_PERMANENTLY )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_HTML )
-> label ( 'sdk.methodType' , 'webAuth' )
2023-07-28 19:40:19 +12:00
-> label ( 'sdk.hide' , true )
2023-08-06 06:53:42 +12:00
-> param ( 'success' , '' , fn ( $clients ) => new Host ( $clients ), 'URL to redirect back to console after a successful installation attempt.' , true , [ 'clients' ])
-> param ( 'failure' , '' , fn ( $clients ) => new Host ( $clients ), 'URL to redirect back to console after a failed installation attempt.' , true , [ 'clients' ])
2023-08-08 03:37:36 +12:00
-> inject ( 'request' )
2023-05-22 22:58:13 +12:00
-> inject ( 'response' )
2023-08-31 06:44:33 +12:00
-> inject ( 'project' )
-> action ( function ( string $success , string $failure , Request $request , Response $response , Document $project ) {
2023-05-26 10:29:08 +12:00
$state = \json_encode ([
2023-08-31 06:44:33 +12:00
'projectId' => $project -> getId (),
2023-08-06 06:53:42 +12:00
'success' => $success ,
'failure' => $failure ,
2023-05-26 10:29:08 +12:00
]);
2023-07-28 20:53:07 +12:00
$appName = App :: getEnv ( '_APP_VCS_GITHUB_APP_NAME' );
2023-09-05 22:14:43 +12:00
2023-09-05 23:16:20 +12:00
if ( empty ( $appName )) {
2023-09-05 22:14:43 +12:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'GitHub App name is not configured. Please configure VCS (Version Control System) variables in .env file.' );
}
2023-07-30 04:03:18 +12:00
$url = " https://github.com/apps/ $appName /installations/new? " . \http_build_query ([
2023-08-08 03:37:36 +12:00
'state' => $state ,
'redirect_uri' => $request -> getProtocol () . '://' . $request -> getHostname () . " /v1/vcs/github/callback "
2023-07-30 04:03:18 +12:00
]);
2023-05-22 22:58:13 +12:00
$response
-> addHeader ( 'Cache-Control' , 'no-store, no-cache, must-revalidate, max-age=0' )
-> addHeader ( 'Pragma' , 'no-cache' )
2023-07-30 04:03:18 +12:00
-> redirect ( $url );
2023-05-22 22:58:13 +12:00
});
2023-07-30 04:03:18 +12:00
App :: get ( '/v1/vcs/github/callback' )
2023-06-18 23:38:37 +12:00
-> desc ( 'Capture installation and authorization from GitHub App' )
2023-05-22 22:58:13 +12:00
-> groups ([ 'api' , 'vcs' ])
2023-08-31 06:44:33 +12:00
-> label ( 'scope' , 'public' )
2023-06-22 22:59:41 +12:00
-> label ( 'error' , __DIR__ . '/../../views/general/error.phtml' )
2023-08-06 22:30:38 +12:00
-> param ( 'installation_id' , '' , new Text ( 256 , 0 ), 'GitHub installation ID' , true )
2023-10-13 09:15:27 +13:00
-> param ( 'setup_action' , '' , new Text ( 256 , 0 ), 'GitHub setup action type' , true )
2023-07-30 04:03:18 +12:00
-> param ( 'state' , '' , new Text ( 2048 ), 'GitHub state. Contains info sent when starting authorization flow.' , true )
2023-08-23 08:11:33 +12:00
-> param ( 'code' , '' , new Text ( 2048 , 0 ), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.' , true )
2023-05-26 20:44:08 +12:00
-> inject ( 'gitHub' )
2023-06-18 23:38:37 +12:00
-> inject ( 'user' )
2023-06-09 03:24:27 +12:00
-> inject ( 'project' )
2023-05-22 22:58:13 +12:00
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'dbForConsole' )
2023-07-30 21:51:13 +12:00
-> action ( function ( string $providerInstallationId , string $setupAction , string $state , string $code , GitHub $github , Document $user , Document $project , Request $request , Response $response , Database $dbForConsole ) {
2023-08-22 01:16:02 +12:00
if ( empty ( $state )) {
$error = 'Installation requests from organisation members for the Appwrite GitHub App are currently unsupported. To proceed with the installation, login to the Appwrite Console and install the GitHub App.' ;
throw new Exception ( Exception :: GENERAL_ARGUMENT_INVALID , $error );
}
2023-05-26 10:29:08 +12:00
$state = \json_decode ( $state , true );
2023-06-18 23:38:37 +12:00
$projectId = $state [ 'projectId' ] ? ? '' ;
2023-05-26 10:29:08 +12:00
2023-08-06 06:53:42 +12:00
$defaultState = [
'success' => $request -> getProtocol () . '://' . $request -> getHostname () . " /console/project- $projectId /settings/git-installations " ,
'failure' => $request -> getProtocol () . '://' . $request -> getHostname () . " /console/project- $projectId /settings/git-installations " ,
];
2023-06-09 03:24:27 +12:00
2023-08-22 01:16:02 +12:00
$state = \array_merge ( $defaultState , $state ? ? []);
2023-08-06 06:53:42 +12:00
$redirectSuccess = $state [ 'success' ] ? ? '' ;
$redirectFailure = $state [ 'failure' ] ? ? '' ;
$project = $dbForConsole -> getDocument ( 'projects' , $projectId );
2023-05-22 22:58:13 +12:00
if ( $project -> isEmpty ()) {
2023-08-06 06:53:42 +12:00
$error = 'Project with the ID from state could not be found.' ;
if ( ! empty ( $redirectFailure )) {
$separator = \str_contains ( $redirectFailure , '?' ) ? '&' : ':' ;
return $response
-> addHeader ( 'Cache-Control' , 'no-store, no-cache, must-revalidate, max-age=0' )
-> addHeader ( 'Pragma' , 'no-cache' )
-> redirect ( $redirectFailure . $separator . \http_build_query ([ 'error' => $error ]));
}
throw new Exception ( Exception :: PROJECT_NOT_FOUND , $error );
2023-05-22 22:58:13 +12:00
}
2023-06-18 23:38:37 +12:00
$personalSlug = '' ;
2023-05-26 10:29:08 +12:00
2023-06-18 23:38:37 +12:00
// OAuth Authroization
if ( ! empty ( $code )) {
2023-07-28 20:53:07 +12:00
$oauth2 = new OAuth2Github ( App :: getEnv ( '_APP_VCS_GITHUB_CLIENT_ID' , '' ), App :: getEnv ( '_APP_VCS_GITHUB_CLIENT_SECRET' , '' ), " " );
2023-07-28 19:40:19 +12:00
$accessToken = $oauth2 -> getAccessToken ( $code ) ? ? '' ;
$refreshToken = $oauth2 -> getRefreshToken ( $code ) ? ? '' ;
$accessTokenExpiry = $oauth2 -> getAccessTokenExpiry ( $code ) ? ? '' ;
$personalSlug = $oauth2 -> getUserSlug ( $accessToken ) ? ? '' ;
2023-08-09 21:52:34 +12:00
$email = $oauth2 -> getUserEmail ( $accessToken );
$oauth2ID = $oauth2 -> getUserID ( $accessToken );
// Makes sure this email is not already used in another identity
$identity = $dbForConsole -> findOne ( 'identities' , [
Query :: equal ( 'providerEmail' , [ $email ]),
]);
if ( $identity !== false && ! $identity -> isEmpty ()) {
if ( $identity -> getAttribute ( 'userInternalId' , '' ) !== $user -> getInternalId ()) {
throw new Exception ( Exception :: USER_EMAIL_ALREADY_EXISTS );
}
}
2023-05-22 22:58:13 +12:00
2023-08-09 21:52:34 +12:00
if ( $identity !== false && ! $identity -> isEmpty ()) {
$identity = $identity
-> setAttribute ( 'providerAccessToken' , $accessToken )
-> setAttribute ( 'providerRefreshToken' , $refreshToken )
-> setAttribute ( 'providerAccessTokenExpiry' , DateTime :: addSeconds ( new \DateTime (), ( int ) $accessTokenExpiry ));
2023-06-18 23:38:37 +12:00
2023-08-09 21:52:34 +12:00
$dbForConsole -> updateDocument ( 'identities' , $identity -> getId (), $identity );
} else {
$identity = $dbForConsole -> createDocument ( 'identities' , new Document ([
'$id' => ID :: unique (),
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: user ( $user -> getId ())),
Permission :: delete ( Role :: user ( $user -> getId ())),
],
'userInternalId' => $user -> getInternalId (),
'userId' => $user -> getId (),
'provider' => 'github' ,
'providerUid' => $oauth2ID ,
'providerEmail' => $email ,
'providerAccessToken' => $accessToken ,
'providerRefreshToken' => $refreshToken ,
'providerAccessTokenExpiry' => DateTime :: addSeconds ( new \DateTime (), ( int ) $accessTokenExpiry ),
]));
}
2023-06-18 23:38:37 +12:00
}
2023-05-22 22:58:13 +12:00
2023-06-18 23:38:37 +12:00
// Create / Update installation
2023-07-30 21:51:13 +12:00
if ( ! empty ( $providerInstallationId )) {
2023-07-28 20:53:07 +12:00
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-07-30 21:51:13 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId ) ? ? '' ;
2023-06-18 23:38:37 +12:00
$projectInternalId = $project -> getInternalId ();
2023-07-31 07:10:25 +12:00
$installation = $dbForConsole -> findOne ( 'installations' , [
2023-07-30 21:51:13 +12:00
Query :: equal ( 'providerInstallationId' , [ $providerInstallationId ]),
2023-06-18 23:38:37 +12:00
Query :: equal ( 'projectInternalId' , [ $projectInternalId ])
2023-05-22 22:58:13 +12:00
]);
2023-07-30 21:51:13 +12:00
if ( $installation === false || $installation -> isEmpty ()) {
2023-06-18 23:38:37 +12:00
$teamId = $project -> getAttribute ( 'teamId' , '' );
2023-07-30 21:51:13 +12:00
$installation = new Document ([
2023-06-18 23:38:37 +12:00
'$id' => ID :: unique (),
'$permissions' => [
Permission :: read ( Role :: team ( ID :: custom ( $teamId ))),
Permission :: update ( Role :: team ( ID :: custom ( $teamId ), 'owner' )),
Permission :: update ( Role :: team ( ID :: custom ( $teamId ), 'developer' )),
Permission :: delete ( Role :: team ( ID :: custom ( $teamId ), 'owner' )),
Permission :: delete ( Role :: team ( ID :: custom ( $teamId ), 'developer' )),
],
2023-07-30 21:51:13 +12:00
'providerInstallationId' => $providerInstallationId ,
2023-06-18 23:38:37 +12:00
'projectId' => $projectId ,
'projectInternalId' => $projectInternalId ,
'provider' => 'github' ,
'organization' => $owner ,
'personal' => $personalSlug === $owner
]);
2023-07-31 07:10:25 +12:00
$installation = $dbForConsole -> createDocument ( 'installations' , $installation );
2023-06-18 23:38:37 +12:00
} else {
2023-07-30 21:51:13 +12:00
$installation = $installation
-> setAttribute ( 'organization' , $owner )
-> setAttribute ( 'personal' , $personalSlug === $owner );
2023-07-31 07:10:25 +12:00
$installation = $dbForConsole -> updateDocument ( 'installations' , $installation -> getId (), $installation );
2023-06-18 23:38:37 +12:00
}
2023-06-22 22:59:41 +12:00
} else {
2023-08-06 06:53:42 +12:00
$error = 'Installation of the Appwrite GitHub App on organization accounts is restricted to organization owners. As a member of the organization, you do not have the necessary permissions to install this GitHub App. Please contact the organization owner to create the installation from the Appwrite console.' ;
if ( ! empty ( $redirectFailure )) {
$separator = \str_contains ( $redirectFailure , '?' ) ? '&' : ':' ;
return $response
-> addHeader ( 'Cache-Control' , 'no-store, no-cache, must-revalidate, max-age=0' )
-> addHeader ( 'Pragma' , 'no-cache' )
-> redirect ( $redirectFailure . $separator . \http_build_query ([ 'error' => $error ]));
}
throw new Exception ( Exception :: GENERAL_ARGUMENT_INVALID , $error );
2023-05-22 22:58:13 +12:00
}
$response
-> addHeader ( 'Cache-Control' , 'no-store, no-cache, must-revalidate, max-age=0' )
-> addHeader ( 'Pragma' , 'no-cache' )
2023-08-06 06:53:42 +12:00
-> redirect ( $redirectSuccess );
2023-05-22 22:58:13 +12:00
});
2023-07-30 21:51:13 +12:00
App :: post ( '/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection' )
2023-07-28 19:51:53 +12:00
-> desc ( 'Detect runtime settings from source code' )
-> groups ([ 'api' , 'vcs' ])
2023-08-31 06:44:33 +12:00
-> label ( 'scope' , 'vcs.write' )
2023-07-28 19:51:53 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-07-28 19:51:53 +12:00
-> label ( 'sdk.method' , 'createRepositoryDetection' )
-> label ( 'sdk.description' , '' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_DETECTION )
-> param ( 'installationId' , '' , new Text ( 256 ), 'Installation Id' )
2023-07-30 21:51:13 +12:00
-> param ( 'providerRepositoryId' , '' , new Text ( 256 ), 'Repository Id' )
2023-08-09 19:46:17 +12:00
-> param ( 'providerRootDirectory' , '' , new Text ( 256 , 0 ), 'Path to Root Directory' , true )
2023-07-28 19:51:53 +12:00
-> inject ( 'gitHub' )
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'dbForConsole' )
2023-07-30 21:51:13 +12:00
-> action ( function ( string $installationId , string $providerRepositoryId , string $providerRootDirectory , GitHub $github , Response $response , Document $project , Database $dbForConsole ) {
2023-08-12 01:34:57 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-07-28 19:51:53 +12:00
if ( $installation -> isEmpty ()) {
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
2023-07-30 21:51:13 +12:00
$providerInstallationId = $installation -> getAttribute ( 'providerInstallationId' );
2023-07-28 20:53:07 +12:00
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-07-28 19:51:53 +12:00
2023-07-30 21:51:13 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId );
2023-10-14 02:20:58 +13:00
try {
2023-10-28 03:08:33 +13:00
$repositoryName = $github -> getRepositoryName ( $providerRepositoryId ) ? ? '' ;
if ( empty ( $repositoryName )) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
2023-10-14 02:20:58 +13:00
} catch ( RepositoryNotFound $e ) {
2023-07-31 18:47:47 +12:00
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
2023-07-28 19:51:53 +12:00
}
2023-07-30 21:51:13 +12:00
$files = $github -> listRepositoryContents ( $owner , $repositoryName , $providerRootDirectory );
2023-08-10 05:35:23 +12:00
$languages = $github -> listRepositoryLanguages ( $owner , $repositoryName );
2023-07-28 19:51:53 +12:00
$detectorFactory = new Detector ( $files , $languages );
$detectorFactory
-> addDetector ( new JavaScript ())
2023-09-14 07:08:49 +12:00
-> addDetector ( new Bun ())
2023-07-28 19:51:53 +12:00
-> addDetector ( new PHP ())
-> addDetector ( new Python ())
-> addDetector ( new Dart ())
-> addDetector ( new Swift ())
-> addDetector ( new Ruby ())
-> addDetector ( new Java ())
-> addDetector ( new CPP ())
-> addDetector ( new Deno ())
-> addDetector ( new Dotnet ());
$runtime = $detectorFactory -> detect ();
2023-07-30 20:30:47 +12:00
$runtimes = Config :: getParam ( 'runtimes' );
$runtimeDetail = \array_reverse ( \array_filter ( \array_keys ( $runtimes ), function ( $key ) use ( $runtime , $runtimes ) {
return $runtimes [ $key ][ 'key' ] === $runtime ;
2023-07-30 21:51:13 +12:00
}))[ 0 ] ? ? '' ;
2023-07-30 20:30:47 +12:00
2023-07-28 19:51:53 +12:00
$detection = [];
2023-07-30 20:30:47 +12:00
$detection [ 'runtime' ] = $runtimeDetail ;
2023-07-28 19:51:53 +12:00
$response -> dynamic ( new Document ( $detection ), Response :: MODEL_DETECTION );
});
2023-07-30 21:51:13 +12:00
App :: get ( '/v1/vcs/github/installations/:installationId/providerRepositories' )
2023-07-24 08:39:16 +12:00
-> desc ( 'List Repositories' )
2023-05-22 22:58:13 +12:00
-> groups ([ 'api' , 'vcs' ])
2023-08-31 06:44:33 +12:00
-> label ( 'scope' , 'vcs.read' )
2023-05-22 22:58:13 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-05-22 22:58:13 +12:00
-> label ( 'sdk.method' , 'listRepositories' )
-> label ( 'sdk.description' , '' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2023-07-31 18:47:47 +12:00
-> label ( 'sdk.response.model' , Response :: MODEL_PROVIDER_REPOSITORY_LIST )
2023-05-22 22:58:13 +12:00
-> param ( 'installationId' , '' , new Text ( 256 ), 'Installation Id' )
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2023-05-26 20:44:08 +12:00
-> inject ( 'gitHub' )
2023-05-22 22:58:13 +12:00
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'dbForConsole' )
2023-07-30 21:51:13 +12:00
-> action ( function ( string $installationId , string $search , GitHub $github , Response $response , Document $project , Database $dbForConsole ) {
2023-05-22 22:58:13 +12:00
if ( empty ( $search )) {
$search = " " ;
}
2023-08-12 01:34:57 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-05-22 22:58:13 +12:00
if ( $installation -> isEmpty ()) {
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
2023-07-30 21:51:13 +12:00
$providerInstallationId = $installation -> getAttribute ( 'providerInstallationId' );
2023-07-28 20:53:07 +12:00
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-05-22 22:58:13 +12:00
$page = 1 ;
2023-08-30 05:41:40 +12:00
$perPage = 4 ;
2023-05-22 22:58:13 +12:00
2023-08-30 05:37:34 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId );
2023-08-30 05:41:40 +12:00
$repos = $github -> searchRepositories ( $owner , $page , $perPage , $search );
2023-05-22 22:58:13 +12:00
2023-07-31 01:24:10 +12:00
$repos = \array_map ( function ( $repo ) use ( $installation ) {
2023-07-24 22:11:30 +12:00
$repo [ 'id' ] = \strval ( $repo [ 'id' ] ? ? '' );
$repo [ 'pushedAt' ] = $repo [ 'pushed_at' ] ? ? null ;
$repo [ 'provider' ] = $installation -> getAttribute ( 'provider' , '' ) ? ? '' ;
$repo [ 'organization' ] = $installation -> getAttribute ( 'organization' , '' ) ? ? '' ;
2023-08-08 03:37:36 +12:00
return $repo ;
2023-05-27 23:55:34 +12:00
}, $repos );
2023-07-31 01:24:10 +12:00
$repos = batch ( \array_map ( function ( $repo ) use ( $github ) {
return function () use ( $repo , $github ) {
2023-08-01 20:40:16 +12:00
try {
$files = $github -> listRepositoryContents ( $repo [ 'organization' ], $repo [ 'name' ], '' );
2023-08-10 05:35:23 +12:00
$languages = $github -> listRepositoryLanguages ( $repo [ 'organization' ], $repo [ 'name' ]);
2023-08-01 20:40:16 +12:00
$detectorFactory = new Detector ( $files , $languages );
$detectorFactory
-> addDetector ( new JavaScript ())
2023-09-14 07:08:49 +12:00
-> addDetector ( new Bun ())
2023-08-01 20:40:16 +12:00
-> addDetector ( new PHP ())
-> addDetector ( new Python ())
-> addDetector ( new Dart ())
-> addDetector ( new Swift ())
-> addDetector ( new Ruby ())
-> addDetector ( new Java ())
-> addDetector ( new CPP ())
-> addDetector ( new Deno ())
-> addDetector ( new Dotnet ());
$runtime = $detectorFactory -> detect ();
$runtimes = Config :: getParam ( 'runtimes' );
$runtimeDetail = \array_reverse ( \array_filter ( \array_keys ( $runtimes ), function ( $key ) use ( $runtime , $runtimes ) {
return $runtimes [ $key ][ 'key' ] === $runtime ;
}))[ 0 ] ? ? '' ;
$repo [ 'runtime' ] = $runtimeDetail ;
} catch ( Throwable $error ) {
$repo [ 'runtime' ] = " " ;
Console :: warning ( " Runtime not detected for " . $repo [ 'organization' ] . " / " . $repo [ 'name' ]);
}
2023-07-31 01:24:10 +12:00
return $repo ;
};
}, $repos ));
2023-08-08 03:37:36 +12:00
$repos = \array_map ( function ( $repo ) {
return new Document ( $repo );
}, $repos );
2023-05-22 22:58:13 +12:00
$response -> dynamic ( new Document ([
2023-08-01 20:40:16 +12:00
'providerRepositories' => $repos ,
2023-05-22 22:58:13 +12:00
'total' => \count ( $repos ),
2023-07-31 18:47:47 +12:00
]), Response :: MODEL_PROVIDER_REPOSITORY_LIST );
2023-05-22 22:58:13 +12:00
});
2023-07-30 21:51:13 +12:00
App :: post ( '/v1/vcs/github/installations/:installationId/providerRepositories' )
2023-06-18 23:38:37 +12:00
-> desc ( 'Create repository' )
-> groups ([ 'api' , 'vcs' ])
2023-08-31 06:44:33 +12:00
-> label ( 'scope' , 'vcs.write' )
2023-06-18 23:38:37 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-06-18 23:38:37 +12:00
-> label ( 'sdk.method' , 'createRepository' )
-> label ( 'sdk.description' , '' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2023-07-31 18:47:47 +12:00
-> label ( 'sdk.response.model' , Response :: MODEL_PROVIDER_REPOSITORY )
2023-06-18 23:38:37 +12:00
-> param ( 'installationId' , '' , new Text ( 256 ), 'Installation Id' )
-> param ( 'name' , '' , new Text ( 256 ), 'Repository name (slug)' )
-> param ( 'private' , '' , new Boolean ( false ), 'Mark repository public or private' )
-> inject ( 'gitHub' )
-> inject ( 'user' )
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'dbForConsole' )
2023-07-30 21:51:13 +12:00
-> action ( function ( string $installationId , string $name , bool $private , GitHub $github , Document $user , Response $response , Document $project , Database $dbForConsole ) {
2023-08-12 01:34:57 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-06-18 23:38:37 +12:00
if ( $installation -> isEmpty ()) {
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
if ( $installation -> getAttribute ( 'personal' , false ) === true ) {
2023-07-28 20:53:07 +12:00
$oauth2 = new OAuth2Github ( App :: getEnv ( '_APP_VCS_GITHUB_CLIENT_ID' , '' ), App :: getEnv ( '_APP_VCS_GITHUB_CLIENT_SECRET' , '' ), " " );
2023-06-18 23:38:37 +12:00
2023-08-09 21:52:34 +12:00
$identity = $dbForConsole -> findOne ( 'identities' , [
Query :: equal ( 'provider' , [ 'github' ]),
Query :: equal ( 'userInternalId' , [ $user -> getInternalId ()]),
]);
if ( $identity === false || $identity -> isEmpty ()) {
throw new Exception ( Exception :: USER_IDENTITY_NOT_FOUND );
}
$accessToken = $identity -> getAttribute ( 'providerAccessToken' );
$refreshToken = $identity -> getAttribute ( 'providerRefreshToken' );
$accessTokenExpiry = $identity -> getAttribute ( 'providerAccessTokenExpiry' );
2023-06-18 23:38:37 +12:00
$isExpired = new \DateTime ( $accessTokenExpiry ) < new \DateTime ( 'now' );
if ( $isExpired ) {
$oauth2 -> refreshTokens ( $refreshToken );
$accessToken = $oauth2 -> getAccessToken ( '' );
$refreshToken = $oauth2 -> getRefreshToken ( '' );
$verificationId = $oauth2 -> getUserID ( $accessToken );
if ( empty ( $verificationId )) {
throw new Exception ( Exception :: GENERAL_RATE_LIMIT_EXCEEDED , " Another request is currently refreshing OAuth token. Please try again. " );
}
2023-08-09 21:52:34 +12:00
$identity = $identity
-> setAttribute ( 'providerAccessToken' , $accessToken )
-> setAttribute ( 'providerRefreshToken' , $refreshToken )
-> setAttribute ( 'providerAccessTokenExpiry' , DateTime :: addSeconds ( new \DateTime (), ( int ) $oauth2 -> getAccessTokenExpiry ( '' )));
2023-06-18 23:38:37 +12:00
2023-08-09 21:52:34 +12:00
$dbForConsole -> updateDocument ( 'identities' , $identity -> getId (), $identity );
2023-06-18 23:38:37 +12:00
}
2023-08-18 01:54:57 +12:00
try {
$repository = $oauth2 -> createRepository ( $accessToken , $name , $private );
} catch ( Exception $exception ) {
throw new Exception ( Exception :: GENERAL_PROVIDER_FAILURE , " GitHub failed to process the request: " . $exception -> getMessage ());
}
2023-06-18 23:38:37 +12:00
} else {
2023-07-30 21:51:13 +12:00
$providerInstallationId = $installation -> getAttribute ( 'providerInstallationId' );
2023-07-28 20:53:07 +12:00
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-07-30 21:51:13 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId );
2023-06-18 23:38:37 +12:00
2023-08-18 01:54:57 +12:00
try {
$repository = $github -> createRepository ( $owner , $name , $private );
} catch ( Exception $exception ) {
throw new Exception ( Exception :: GENERAL_PROVIDER_FAILURE , " GitHub failed to process the request: " . $exception -> getMessage ());
}
2023-06-18 23:38:37 +12:00
}
2023-08-08 21:46:43 +12:00
2023-08-04 20:56:46 +12:00
if ( isset ( $repository [ 'errors' ])) {
$message = $repository [ 'message' ] ? ? 'Unknown error.' ;
if ( isset ( $repository [ 'errors' ][ 0 ])) {
$message .= ' ' . $repository [ 'errors' ][ 0 ][ 'message' ];
}
throw new Exception ( Exception :: GENERAL_ARGUMENT_INVALID , 'Provider Error: ' . $message );
}
2023-08-20 00:51:27 +12:00
if ( isset ( $repository [ 'message' ])) {
throw new Exception ( Exception :: GENERAL_ARGUMENT_INVALID , 'Provider Error: ' . $repository [ 'message' ]);
}
2023-07-28 19:40:19 +12:00
$repository [ 'id' ] = \strval ( $repository [ 'id' ]) ? ? '' ;
$repository [ 'pushedAt' ] = $repository [ 'pushed_at' ] ? ? '' ;
2023-06-22 22:59:41 +12:00
$repository [ 'organization' ] = $installation -> getAttribute ( 'organization' , '' );
$repository [ 'provider' ] = $installation -> getAttribute ( 'provider' , '' );
2023-06-18 23:38:37 +12:00
2023-07-31 18:47:47 +12:00
$response -> dynamic ( new Document ( $repository ), Response :: MODEL_PROVIDER_REPOSITORY );
2023-06-18 23:38:37 +12:00
});
2023-07-30 21:51:13 +12:00
App :: get ( '/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId' )
2023-06-08 03:50:32 +12:00
-> desc ( 'Get repository' )
-> groups ([ 'api' , 'vcs' ])
2023-08-31 06:44:33 +12:00
-> label ( 'scope' , 'vcs.read' )
2023-06-08 03:50:32 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-06-08 03:50:32 +12:00
-> label ( 'sdk.method' , 'getRepository' )
-> label ( 'sdk.description' , '' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2023-07-31 18:47:47 +12:00
-> label ( 'sdk.response.model' , Response :: MODEL_PROVIDER_REPOSITORY )
2023-06-08 03:50:32 +12:00
-> param ( 'installationId' , '' , new Text ( 256 ), 'Installation Id' )
2023-07-30 21:51:13 +12:00
-> param ( 'providerRepositoryId' , '' , new Text ( 256 ), 'Repository Id' )
2023-06-08 03:50:32 +12:00
-> inject ( 'gitHub' )
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'dbForConsole' )
2023-07-30 21:51:13 +12:00
-> action ( function ( string $installationId , string $providerRepositoryId , GitHub $github , Response $response , Document $project , Database $dbForConsole ) {
2023-08-12 01:34:57 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-06-08 03:50:32 +12:00
if ( $installation -> isEmpty ()) {
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
2023-07-30 21:51:13 +12:00
$providerInstallationId = $installation -> getAttribute ( 'providerInstallationId' );
2023-07-28 20:53:07 +12:00
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-06-08 03:50:32 +12:00
2023-07-30 21:51:13 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId ) ? ? '' ;
2023-10-14 02:20:58 +13:00
try {
$repositoryName = $github -> getRepositoryName ( $providerRepositoryId ) ? ? '' ;
2023-10-28 03:08:33 +13:00
if ( empty ( $repositoryName )) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
2023-10-14 02:20:58 +13:00
} catch ( RepositoryNotFound $e ) {
2023-07-31 18:47:47 +12:00
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
2023-06-08 03:50:32 +12:00
}
$repository = $github -> getRepository ( $owner , $repositoryName );
2023-07-28 19:40:19 +12:00
$repository [ 'id' ] = \strval ( $repository [ 'id' ]) ? ? '' ;
$repository [ 'pushedAt' ] = $repository [ 'pushed_at' ] ? ? '' ;
2023-06-19 02:08:53 +12:00
$repository [ 'organization' ] = $installation -> getAttribute ( 'organization' , '' );
$repository [ 'provider' ] = $installation -> getAttribute ( 'provider' , '' );
2023-06-09 03:24:27 +12:00
2023-07-31 18:47:47 +12:00
$response -> dynamic ( new Document ( $repository ), Response :: MODEL_PROVIDER_REPOSITORY );
2023-06-08 03:50:32 +12:00
});
2023-07-30 21:51:13 +12:00
App :: get ( '/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches' )
2023-05-26 10:29:08 +12:00
-> desc ( 'List Repository Branches' )
-> groups ([ 'api' , 'vcs' ])
2023-08-31 06:44:33 +12:00
-> label ( 'scope' , 'vcs.read' )
2023-05-26 10:29:08 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-05-26 10:29:08 +12:00
-> label ( 'sdk.method' , 'listRepositoryBranches' )
-> label ( 'sdk.description' , '' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_BRANCH_LIST )
-> param ( 'installationId' , '' , new Text ( 256 ), 'Installation Id' )
2023-07-30 21:51:13 +12:00
-> param ( 'providerRepositoryId' , '' , new Text ( 256 ), 'Repository Id' )
2023-05-26 20:44:08 +12:00
-> inject ( 'gitHub' )
2023-05-26 10:29:08 +12:00
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'dbForConsole' )
2023-07-30 21:51:13 +12:00
-> action ( function ( string $installationId , string $providerRepositoryId , GitHub $github , Response $response , Document $project , Database $dbForConsole ) {
2023-08-12 01:34:57 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-05-26 10:29:08 +12:00
if ( $installation -> isEmpty ()) {
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
2023-07-30 21:51:13 +12:00
$providerInstallationId = $installation -> getAttribute ( 'providerInstallationId' );
2023-07-28 20:53:07 +12:00
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-05-26 10:29:08 +12:00
2023-07-30 21:51:13 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId ) ? ? '' ;
2023-10-14 02:20:58 +13:00
try {
$repositoryName = $github -> getRepositoryName ( $providerRepositoryId ) ? ? '' ;
2023-10-28 03:08:33 +13:00
if ( empty ( $repositoryName )) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
2023-10-14 02:20:58 +13:00
} catch ( RepositoryNotFound $e ) {
2023-07-31 18:47:47 +12:00
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
2023-07-28 19:40:19 +12:00
}
$branches = $github -> listBranches ( $owner , $repositoryName ) ? ? [];
2023-05-26 10:29:08 +12:00
$response -> dynamic ( new Document ([
'branches' => \array_map ( function ( $branch ) {
2023-08-24 23:04:49 +12:00
return new Document ([ 'name' => $branch ]);
2023-05-26 10:29:08 +12:00
}, $branches ),
'total' => \count ( $branches ),
]), Response :: MODEL_BRANCH_LIST );
});
2023-07-24 22:11:30 +12:00
App :: post ( '/v1/vcs/github/events' )
-> desc ( 'Create Event' )
2023-05-22 22:58:13 +12:00
-> groups ([ 'api' , 'vcs' ])
2023-08-31 06:44:33 +12:00
-> label ( 'scope' , 'public' )
2023-05-26 20:44:08 +12:00
-> inject ( 'gitHub' )
2023-05-22 22:58:13 +12:00
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'dbForConsole' )
2023-05-23 16:37:25 +12:00
-> inject ( 'getProjectDB' )
2023-10-09 23:28:06 +13:00
-> inject ( 'queueForBuilds' )
2023-05-22 22:58:13 +12:00
-> action (
2023-10-09 23:28:06 +13:00
function ( GitHub $github , Request $request , Response $response , Database $dbForConsole , callable $getProjectDB , Build $queueForBuilds ) use ( $createGitDeployments ) {
2023-05-22 22:58:13 +12:00
$payload = $request -> getRawPayload ();
2023-09-07 05:04:03 +12:00
$signatureRemote = $request -> getHeader ( 'x-hub-signature-256' , '' );
$signatureLocal = App :: getEnv ( '_APP_VCS_GITHUB_WEBHOOK_SECRET' , '' );
2023-06-15 22:37:28 +12:00
2023-09-07 05:04:03 +12:00
$valid = empty ( $signatureRemote ) ? true : $github -> validateWebhookEvent ( $payload , $signatureRemote , $signatureLocal );
2023-06-15 22:38:03 +12:00
if ( ! $valid ) {
2023-09-07 05:57:46 +12:00
throw new Exception ( Exception :: GENERAL_ACCESS_FORBIDDEN , " Invalid webhook payload signature. Please make sure the webhook secret has same value in your GitHub app and in the _APP_VCS_GITHUB_WEBHOOK_SECRET environment variable " );
2023-06-15 22:37:28 +12:00
}
$event = $request -> getHeader ( 'x-github-event' , '' );
2023-07-28 20:53:07 +12:00
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-07-24 22:11:30 +12:00
$parsedPayload = $github -> getEvent ( $event , $payload );
2023-05-22 22:58:13 +12:00
if ( $event == $github :: EVENT_PUSH ) {
2023-08-20 00:54:43 +12:00
$providerBranchCreated = $parsedPayload [ " branchCreated " ] ? ? false ;
2023-07-30 21:51:13 +12:00
$providerBranch = $parsedPayload [ " branch " ] ? ? '' ;
2023-08-10 05:35:23 +12:00
$providerBranchUrl = $parsedPayload [ " branchUrl " ] ? ? '' ;
2023-07-30 21:51:13 +12:00
$providerRepositoryId = $parsedPayload [ " repositoryId " ] ? ? '' ;
2023-08-09 22:54:46 +12:00
$providerRepositoryName = $parsedPayload [ " repositoryName " ] ? ? '' ;
2023-07-30 21:51:13 +12:00
$providerInstallationId = $parsedPayload [ " installationId " ] ? ? '' ;
2023-08-09 22:54:46 +12:00
$providerRepositoryUrl = $parsedPayload [ " repositoryUrl " ] ? ? '' ;
$providerCommitHash = $parsedPayload [ " commitHash " ] ? ? '' ;
$providerRepositoryOwner = $parsedPayload [ " owner " ] ? ? '' ;
$providerCommitAuthor = $parsedPayload [ " headCommitAuthor " ] ? ? '' ;
2023-08-10 05:35:23 +12:00
$providerCommitAuthorUrl = $parsedPayload [ " authorUrl " ] ? ? '' ;
2023-08-09 22:54:46 +12:00
$providerCommitMessage = $parsedPayload [ " headCommitMessage " ] ? ? '' ;
$providerCommitUrl = $parsedPayload [ " headCommitUrl " ] ? ? '' ;
2023-05-22 22:58:13 +12:00
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-05-29 23:51:03 +12:00
2023-05-22 22:58:13 +12:00
//find functionId from functions table
2023-11-15 04:50:59 +13:00
$repositories = Authorization :: skip ( fn () => $dbForConsole -> find ( 'repositories' , [
2023-07-30 21:51:13 +12:00
Query :: equal ( 'providerRepositoryId' , [ $providerRepositoryId ]),
2023-05-22 22:58:13 +12:00
Query :: limit ( 100 ),
2023-11-15 04:50:59 +13:00
]));
2023-05-22 22:58:13 +12:00
2023-08-10 03:53:58 +12:00
// create new deployment only on push and not when branch is created
if ( ! $providerBranchCreated ) {
2023-10-09 23:28:06 +13:00
$createGitDeployments ( $github , $providerInstallationId , $repositories , $providerBranch , $providerBranchUrl , $providerRepositoryName , $providerRepositoryUrl , $providerRepositoryOwner , $providerCommitHash , $providerCommitAuthor , $providerCommitAuthorUrl , $providerCommitMessage , $providerCommitUrl , '' , false , $dbForConsole , $queueForBuilds , $getProjectDB , $request );
2023-08-10 03:53:58 +12:00
}
2023-05-22 22:58:13 +12:00
} elseif ( $event == $github :: EVENT_INSTALLATION ) {
if ( $parsedPayload [ " action " ] == " deleted " ) {
2023-05-26 10:29:08 +12:00
// TODO: Use worker for this job instead (update function as well)
2023-07-30 21:51:13 +12:00
$providerInstallationId = $parsedPayload [ " installationId " ];
2023-05-22 22:58:13 +12:00
2023-07-31 07:10:25 +12:00
$installations = $dbForConsole -> find ( 'installations' , [
2023-07-30 21:51:13 +12:00
Query :: equal ( 'providerInstallationId' , [ $providerInstallationId ]),
2023-05-22 22:58:13 +12:00
Query :: limit ( 1000 )
]);
2023-07-30 21:51:13 +12:00
foreach ( $installations as $installation ) {
2023-11-15 04:50:59 +13:00
$repositories = Authorization :: skip ( fn () => $dbForConsole -> find ( 'repositories' , [
2023-07-30 21:51:13 +12:00
Query :: equal ( 'installationInternalId' , [ $installation -> getInternalId ()]),
2023-05-22 22:58:13 +12:00
Query :: limit ( 1000 )
2023-11-15 04:50:59 +13:00
]));
2023-05-22 22:58:13 +12:00
2023-07-30 21:51:13 +12:00
foreach ( $repositories as $repository ) {
2023-11-15 04:50:59 +13:00
Authorization :: skip ( fn () => $dbForConsole -> deleteDocument ( 'repositories' , $repository -> getId ()));
2023-05-22 22:58:13 +12:00
}
2023-07-31 07:10:25 +12:00
$dbForConsole -> deleteDocument ( 'installations' , $installation -> getId ());
2023-05-22 22:58:13 +12:00
}
}
} elseif ( $event == $github :: EVENT_PULL_REQUEST ) {
2023-06-28 20:48:10 +12:00
if ( $parsedPayload [ " action " ] == " opened " || $parsedPayload [ " action " ] == " reopened " || $parsedPayload [ " action " ] == " synchronize " ) {
2023-07-30 21:51:13 +12:00
$providerBranch = $parsedPayload [ " branch " ] ? ? '' ;
2023-08-10 05:35:23 +12:00
$providerBranchUrl = $parsedPayload [ " branchUrl " ] ? ? '' ;
2023-07-30 21:51:13 +12:00
$providerRepositoryId = $parsedPayload [ " repositoryId " ] ? ? '' ;
2023-08-09 22:54:46 +12:00
$providerRepositoryName = $parsedPayload [ " repositoryName " ] ? ? '' ;
2023-07-30 21:51:13 +12:00
$providerInstallationId = $parsedPayload [ " installationId " ] ? ? '' ;
2023-08-09 22:54:46 +12:00
$providerRepositoryUrl = $parsedPayload [ " repositoryUrl " ] ? ? '' ;
2023-07-30 21:51:13 +12:00
$providerPullRequestId = $parsedPayload [ " pullRequestNumber " ] ? ? '' ;
2023-08-09 22:54:46 +12:00
$providerCommitHash = $parsedPayload [ " commitHash " ] ? ? '' ;
$providerRepositoryOwner = $parsedPayload [ " owner " ] ? ? '' ;
2023-07-28 19:40:19 +12:00
$external = $parsedPayload [ " external " ] ? ? true ;
2023-08-09 22:54:46 +12:00
$providerCommitUrl = $parsedPayload [ " headCommitUrl " ] ? ? '' ;
2023-08-10 05:35:23 +12:00
$providerCommitAuthorUrl = $parsedPayload [ " authorUrl " ] ? ? '' ;
2023-05-29 23:51:03 +12:00
2023-06-28 23:31:35 +12:00
// Ignore sync for non-external. We handle it in push webhook
2023-07-01 18:46:21 +12:00
if ( ! $external && $parsedPayload [ " action " ] == " synchronize " ) {
2023-06-28 23:31:35 +12:00
return $response -> json ( $parsedPayload );
}
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-05-22 22:58:13 +12:00
2023-08-09 22:54:46 +12:00
$commitDetails = $github -> getCommit ( $providerRepositoryOwner , $providerRepositoryName , $providerCommitHash );
$providerCommitAuthor = $commitDetails [ " commitAuthor " ] ? ? '' ;
$providerCommitMessage = $commitDetails [ " commitMessage " ] ? ? '' ;
2023-11-15 04:50:59 +13:00
$repositories = Authorization :: skip ( fn () => $dbForConsole -> find ( 'repositories' , [
2023-07-30 21:51:13 +12:00
Query :: equal ( 'providerRepositoryId' , [ $providerRepositoryId ]),
2023-05-22 22:58:13 +12:00
Query :: orderDesc ( '$createdAt' )
2023-11-15 04:50:59 +13:00
]));
2023-05-22 22:58:13 +12:00
2023-10-09 23:28:06 +13:00
$createGitDeployments ( $github , $providerInstallationId , $repositories , $providerBranch , $providerBranchUrl , $providerRepositoryName , $providerRepositoryUrl , $providerRepositoryOwner , $providerCommitHash , $providerCommitAuthor , $providerCommitAuthorUrl , $providerCommitMessage , $providerCommitUrl , $providerPullRequestId , $external , $dbForConsole , $queueForBuilds , $getProjectDB , $request );
2023-06-28 20:48:10 +12:00
} elseif ( $parsedPayload [ " action " ] == " closed " ) {
// Allowed external contributions cleanup
2023-07-30 21:51:13 +12:00
$providerRepositoryId = $parsedPayload [ " repositoryId " ] ? ? '' ;
$providerPullRequestId = $parsedPayload [ " pullRequestNumber " ] ? ? '' ;
2023-07-28 19:40:19 +12:00
$external = $parsedPayload [ " external " ] ? ? true ;
2023-06-28 20:48:10 +12:00
if ( $external ) {
2023-11-15 04:50:59 +13:00
$repositories = Authorization :: skip ( fn () => $dbForConsole -> find ( 'repositories' , [
2023-07-30 21:51:13 +12:00
Query :: equal ( 'providerRepositoryId' , [ $providerRepositoryId ]),
2023-06-28 20:48:10 +12:00
Query :: orderDesc ( '$createdAt' )
2023-11-15 04:50:59 +13:00
]));
2023-06-28 20:48:10 +12:00
2023-07-30 21:51:13 +12:00
foreach ( $repositories as $repository ) {
$providerPullRequestIds = $repository -> getAttribute ( 'providerPullRequestIds' , []);
2023-06-28 20:48:10 +12:00
2023-07-30 21:51:13 +12:00
if ( \in_array ( $providerPullRequestId , $providerPullRequestIds )) {
$providerPullRequestIds = \array_diff ( $providerPullRequestIds , [ $providerPullRequestId ]);
$repository = $repository -> setAttribute ( 'providerPullRequestIds' , $providerPullRequestIds );
2023-07-31 07:10:25 +12:00
$repository = Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'repositories' , $repository -> getId (), $repository ));
2023-06-28 20:48:10 +12:00
}
}
}
2023-05-22 22:58:13 +12:00
}
}
$response -> json ( $parsedPayload );
}
);
App :: get ( '/v1/vcs/installations' )
-> desc ( 'List installations' )
2023-08-31 06:44:33 +12:00
-> groups ([ 'api' , 'vcs' ])
-> label ( 'scope' , 'vcs.read' )
2023-05-22 22:58:13 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-05-22 22:58:13 +12:00
-> label ( 'sdk.method' , 'listInstallations' )
-> label ( 'sdk.description' , '/docs/references/vcs/list-installations.md' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_INSTALLATION_LIST )
-> param ( 'queries' , [], new Installations (), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode ( ', ' , Installations :: ALLOWED_ATTRIBUTES ), true )
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
-> inject ( 'response' )
-> inject ( 'project' )
2023-06-06 19:50:52 +12:00
-> inject ( 'dbForProject' )
2023-05-22 22:58:13 +12:00
-> inject ( 'dbForConsole' )
2023-06-06 19:50:52 +12:00
-> action ( function ( array $queries , string $search , Response $response , Document $project , Database $dbForProject , Database $dbForConsole ) {
2023-05-22 22:58:13 +12:00
$queries = Query :: parseQueries ( $queries );
$queries [] = Query :: equal ( 'projectInternalId' , [ $project -> getInternalId ()]);
if ( ! empty ( $search )) {
$queries [] = Query :: search ( 'search' , $search );
}
// Get cursor document if there was a cursor query
2023-08-06 20:51:53 +12:00
$cursor = Query :: getByType ( $queries , [ Query :: TYPE_CURSORAFTER , Query :: TYPE_CURSORBEFORE ]);
2023-05-22 22:58:13 +12:00
$cursor = reset ( $cursor );
if ( $cursor ) {
/** @var Query $cursor */
2023-07-30 21:51:13 +12:00
$installationId = $cursor -> getValue ();
2023-07-31 07:10:25 +12:00
$cursorDocument = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-05-22 22:58:13 +12:00
if ( $cursorDocument -> isEmpty ()) {
2023-07-30 21:51:13 +12:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Installation ' { $installationId } ' for the 'cursor' value not found. " );
2023-05-22 22:58:13 +12:00
}
$cursor -> setValue ( $cursorDocument );
}
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
2023-07-31 07:10:25 +12:00
$results = $dbForConsole -> find ( 'installations' , $queries );
$total = $dbForConsole -> count ( 'installations' , $filterQueries , APP_LIMIT_COUNT );
2023-06-06 19:50:52 +12:00
2023-05-22 22:58:13 +12:00
$response -> dynamic ( new Document ([
2023-06-06 19:50:52 +12:00
'installations' => $results ,
'total' => $total ,
2023-05-22 22:58:13 +12:00
]), Response :: MODEL_INSTALLATION_LIST );
});
2023-06-08 03:50:32 +12:00
App :: get ( '/v1/vcs/installations/:installationId' )
2023-06-13 23:13:02 +12:00
-> desc ( 'Get installation' )
2023-08-31 06:44:33 +12:00
-> groups ([ 'api' , 'vcs' ])
-> label ( 'scope' , 'vcs.read' )
2023-06-08 03:50:32 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-06-08 03:50:32 +12:00
-> label ( 'sdk.method' , 'getInstallation' )
-> label ( 'sdk.description' , '/docs/references/vcs/get-installation.md' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_INSTALLATION )
-> param ( 'installationId' , '' , new Text ( 256 ), 'Installation Id' )
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'dbForConsole' )
2023-07-30 21:51:13 +12:00
-> action ( function ( string $installationId , Response $response , Document $project , Database $dbForConsole ) {
2023-07-31 07:10:25 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-06-08 03:50:32 +12:00
if ( $installation === false || $installation -> isEmpty ()) {
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
if ( $installation -> getAttribute ( 'projectInternalId' ) !== $project -> getInternalId ()) {
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
$response -> dynamic ( $installation , Response :: MODEL_INSTALLATION );
});
2023-05-22 22:58:13 +12:00
App :: delete ( '/v1/vcs/installations/:installationId' )
-> desc ( 'Delete Installation' )
2023-08-31 06:44:33 +12:00
-> groups ([ 'api' , 'vcs' ])
-> label ( 'scope' , 'vcs.write' )
2023-05-22 22:58:13 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-05-22 22:58:13 +12:00
-> label ( 'sdk.method' , 'deleteInstallation' )
-> label ( 'sdk.description' , '/docs/references/vcs/delete-installation.md' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
-> param ( 'installationId' , '' , new Text ( 256 ), 'Installation Id' )
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'dbForConsole' )
2023-11-15 19:11:42 +13:00
-> inject ( 'queueForDeletes' )
-> action ( function ( string $installationId , Response $response , Document $project , Database $dbForConsole , Delete $queueForDeletes ) {
2023-08-12 01:34:57 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-05-22 22:58:13 +12:00
if ( $installation -> isEmpty ()) {
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
2023-07-31 07:10:25 +12:00
if ( ! $dbForConsole -> deleteDocument ( 'installations' , $installation -> getId ())) {
2023-05-22 22:58:13 +12:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove installation from DB' );
}
2023-11-15 19:11:42 +13:00
$queueForDeletes
2023-05-22 22:58:13 +12:00
-> setType ( DELETE_TYPE_DOCUMENT )
-> setDocument ( $installation );
$response -> noContent ();
});
2023-06-14 06:44:44 +12:00
2023-07-30 21:51:13 +12:00
App :: patch ( '/v1/vcs/github/installations/:installationId/repositories/:repositoryId' )
2023-06-28 20:48:10 +12:00
-> desc ( 'Authorize external deployment' )
-> groups ([ 'api' , 'vcs' ])
2023-08-31 06:44:33 +12:00
-> label ( 'scope' , 'vcs.write' )
2023-06-28 20:48:10 +12:00
-> label ( 'sdk.namespace' , 'vcs' )
2023-08-30 21:19:55 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2023-06-28 20:48:10 +12:00
-> label ( 'sdk.method' , 'updateExternalDeployments' )
-> label ( 'sdk.description' , '' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
-> param ( 'installationId' , '' , new Text ( 256 ), 'Installation Id' )
2023-07-30 21:51:13 +12:00
-> param ( 'repositoryId' , '' , new Text ( 256 ), 'VCS Repository Id' )
-> param ( 'providerPullRequestId' , '' , new Text ( 256 ), 'GitHub Pull Request Id' )
2023-06-28 20:48:10 +12:00
-> inject ( 'gitHub' )
-> inject ( 'request' )
-> inject ( 'response' )
-> inject ( 'project' )
-> inject ( 'dbForConsole' )
-> inject ( 'getProjectDB' )
2023-10-09 23:28:06 +13:00
-> inject ( 'queueForBuilds' )
-> action ( function ( string $installationId , string $repositoryId , string $providerPullRequestId , GitHub $github , Request $request , Response $response , Document $project , Database $dbForConsole , callable $getProjectDB , Build $queueForBuilds ) use ( $createGitDeployments ) {
2023-08-12 01:34:57 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-06-28 20:48:10 +12:00
if ( $installation -> isEmpty ()) {
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
2023-11-15 04:50:59 +13:00
$repository = Authorization :: skip ( fn () => $dbForConsole -> getDocument ( 'repositories' , $repositoryId , [
2023-06-28 20:48:10 +12:00
Query :: equal ( 'projectInternalId' , [ $project -> getInternalId ()])
2023-11-15 04:50:59 +13:00
]));
2023-06-28 20:48:10 +12:00
2023-07-30 21:51:13 +12:00
if ( $repository -> isEmpty ()) {
2023-07-31 18:47:47 +12:00
throw new Exception ( Exception :: REPOSITORY_NOT_FOUND );
2023-06-28 20:48:10 +12:00
}
2023-07-30 21:51:13 +12:00
if ( \in_array ( $providerPullRequestId , $repository -> getAttribute ( 'providerPullRequestIds' , []))) {
2023-07-31 18:47:47 +12:00
throw new Exception ( Exception :: PROVIDER_CONTRIBUTION_CONFLICT );
2023-06-28 20:48:10 +12:00
}
2023-07-30 21:51:13 +12:00
$providerPullRequestIds = \array_unique ( \array_merge ( $repository -> getAttribute ( 'providerPullRequestIds' , []), [ $providerPullRequestId ]));
$repository = $repository -> setAttribute ( 'providerPullRequestIds' , $providerPullRequestIds );
2023-06-28 20:48:10 +12:00
// TODO: Delete from array when PR is closed
2023-11-15 04:50:59 +13:00
$repository = Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'repositories' , $repository -> getId (), $repository ));
2023-06-28 20:48:10 +12:00
2023-07-28 20:53:07 +12:00
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-07-30 21:51:13 +12:00
$providerInstallationId = $installation -> getAttribute ( 'providerInstallationId' );
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-06-28 20:48:10 +12:00
2023-07-30 21:51:13 +12:00
$repositories = [ $repository ];
$providerRepositoryId = $repository -> getAttribute ( 'providerRepositoryId' );
2023-06-28 20:48:10 +12:00
2023-07-30 21:51:13 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId );
2023-10-28 03:08:33 +13:00
try {
$repositoryName = $github -> getRepositoryName ( $providerRepositoryId ) ? ? '' ;
if ( empty ( $repositoryName )) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
} catch ( RepositoryNotFound $e ) {
throw new Exception ( Exception :: PROVIDER_REPOSITORY_NOT_FOUND );
}
2023-07-30 21:51:13 +12:00
$pullRequestResponse = $github -> getPullRequest ( $owner , $repositoryName , $providerPullRequestId );
2023-06-28 20:48:10 +12:00
2023-07-30 21:51:13 +12:00
$providerBranch = \explode ( ':' , $pullRequestResponse [ 'head' ][ 'label' ])[ 1 ] ? ? '' ;
$providerCommitHash = $pullRequestResponse [ 'head' ][ 'sha' ] ? ? '' ;
2023-06-28 20:48:10 +12:00
2023-10-09 23:28:06 +13:00
$createGitDeployments ( $github , $providerInstallationId , $repositories , $providerBranch , $providerCommitHash , $providerPullRequestId , true , $dbForConsole , $queueForBuilds , $getProjectDB , $request );
2023-06-28 20:48:10 +12:00
$response -> noContent ();
});