2022-01-24 11:25:46 +13:00
< ? php
2023-06-13 18:26:36 +12:00
use Swoole\Coroutine as Co ;
2022-04-19 04:21:45 +12:00
use Appwrite\Event\Event ;
2022-11-16 07:13:17 +13:00
use Appwrite\Event\Func ;
2022-03-01 00:50:27 +13:00
use Appwrite\Messaging\Adapter\Realtime ;
2022-01-24 11:25:46 +13:00
use Appwrite\Resque\Worker ;
2022-04-19 04:21:45 +12:00
use Appwrite\Utopia\Response\Model\Deployment ;
2022-02-04 14:29:40 +13:00
use Executor\Executor ;
2022-08-09 18:28:38 +12:00
use Appwrite\Usage\Stats ;
2023-05-28 23:39:48 +12:00
use Appwrite\Vcs\Comment ;
2022-07-13 01:32:39 +12:00
use Utopia\Database\DateTime ;
2022-01-24 11:25:46 +13:00
use Utopia\App ;
use Utopia\CLI\Console ;
2023-02-06 09:07:46 +13:00
use Utopia\Database\Helpers\ID ;
2022-11-18 01:50:17 +13:00
use Utopia\DSN\DSN ;
2022-01-24 11:25:46 +13:00
use Utopia\Database\Document ;
use Utopia\Config\Config ;
2023-05-29 23:51:03 +12:00
use Utopia\Database\Database ;
2023-06-22 22:59:41 +12:00
use Utopia\Database\Query ;
2022-11-18 06:43:59 +13:00
use Utopia\Storage\Storage ;
2023-05-22 22:58:13 +12:00
use Utopia\Database\Validator\Authorization ;
use Utopia\VCS\Adapter\Git\GitHub ;
2022-01-24 11:25:46 +13:00
2022-04-20 01:13:55 +12:00
require_once __DIR__ . '/../init.php' ;
2022-01-24 11:25:46 +13:00
Console :: title ( 'Builds V1 Worker' );
2022-04-20 01:13:55 +12:00
Console :: success ( APP_NAME . ' build worker v1 has started' );
2022-01-24 11:25:46 +13:00
2022-01-25 09:06:29 +13:00
// TODO: Executor should return appropriate response codes.
2022-01-24 11:25:46 +13:00
class BuildsV1 extends Worker
2022-04-14 00:39:31 +12:00
{
2022-04-20 01:13:55 +12:00
private ? Executor $executor = null ;
2022-01-24 11:25:46 +13:00
2022-04-20 01:13:55 +12:00
public function getName () : string
2022-01-26 12:45:41 +13:00
{
2022-01-24 11:25:46 +13:00
return " builds " ;
}
2022-05-10 00:36:29 +12:00
public function init () : void
{
2022-04-14 04:15:25 +12:00
$this -> executor = new Executor ( App :: getEnv ( '_APP_EXECUTOR_HOST' ));
2022-02-04 14:29:40 +13:00
}
2022-01-24 11:25:46 +13:00
public function run () : void
{
$type = $this -> args [ 'type' ] ? ? '' ;
2022-04-20 01:13:55 +12:00
$project = new Document ( $this -> args [ 'project' ] ? ? []);
$resource = new Document ( $this -> args [ 'resource' ] ? ? []);
$deployment = new Document ( $this -> args [ 'deployment' ] ? ? []);
2023-07-30 21:51:13 +12:00
$template = new Document ( $this -> args [ 'template' ] ? ? []);
2022-04-14 00:39:31 +12:00
2022-01-24 11:25:46 +13:00
switch ( $type ) {
2022-01-27 02:09:32 +13:00
case BUILD_TYPE_DEPLOYMENT :
2022-02-17 09:06:22 +13:00
case BUILD_TYPE_RETRY :
2022-04-20 01:13:55 +12:00
Console :: info ( 'Creating build for deployment: ' . $deployment -> getId ());
2023-05-26 20:44:08 +12:00
$github = new GitHub ( $this -> getCache ());
2023-08-12 04:52:13 +12:00
$this -> buildDeployment ( $github , $project , $resource , $deployment , $template );
2022-01-24 11:25:46 +13:00
break ;
2022-01-24 12:01:42 +13:00
2022-01-24 11:25:46 +13:00
default :
2022-01-24 12:01:42 +13:00
throw new \Exception ( 'Invalid build type' );
2022-01-24 11:25:46 +13:00
break ;
}
}
2023-02-07 01:54:37 +13:00
/**
* @ throws \Utopia\Database\Exception\Authorization
* @ throws \Utopia\Database\Exception\Structure
* @ throws Throwable
*/
2023-08-12 04:52:13 +12:00
protected function buildDeployment ( GitHub $github , Document $project , Document $function , Document $deployment , Document $template )
2022-01-24 11:25:46 +13:00
{
2022-11-17 01:19:29 +13:00
global $register ;
2022-08-13 19:57:04 +12:00
$dbForProject = $this -> getProjectDB ( $project );
2023-05-22 22:58:13 +12:00
$dbForConsole = $this -> getConsoleDB ();
2022-04-14 00:39:31 +12:00
2022-04-20 01:13:55 +12:00
$function = $dbForProject -> getDocument ( 'functions' , $function -> getId ());
2022-01-24 11:25:46 +13:00
if ( $function -> isEmpty ()) {
throw new Exception ( 'Function not found' , 404 );
}
2022-04-20 01:13:55 +12:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deployment -> getId ());
2022-01-27 02:09:32 +13:00
if ( $deployment -> isEmpty ()) {
throw new Exception ( 'Deployment not found' , 404 );
2022-01-24 11:25:46 +13:00
}
2023-08-23 01:16:07 +12:00
if ( empty ( $deployment -> getAttribute ( 'entrypoint' , '' ))) {
2023-08-23 20:16:52 +12:00
throw new Exception ( 'Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".' , 500 );
2023-08-23 01:16:07 +12:00
}
2023-09-05 05:53:25 +12:00
$version = $function -> getAttribute ( 'version' , 'v2' );
$runtimes = Config :: getParam ( $version === 'v2' ? 'runtimes-v2' : 'runtimes' , []);
2022-01-24 11:25:46 +13:00
$key = $function -> getAttribute ( 'runtime' );
$runtime = isset ( $runtimes [ $key ]) ? $runtimes [ $key ] : null ;
if ( \is_null ( $runtime )) {
throw new Exception ( 'Runtime "' . $function -> getAttribute ( 'runtime' , '' ) . '" is not supported' );
}
2023-06-22 22:59:41 +12:00
// Realtime preparation
$allEvents = Event :: generateEvents ( 'functions.[functionId].deployments.[deploymentId].update' , [
'functionId' => $function -> getId (),
'deploymentId' => $deployment -> getId ()
]);
2022-07-14 02:02:49 +12:00
$startTime = DateTime :: now ();
2023-03-29 02:21:42 +13:00
$durationStart = \microtime ( true );
2023-06-22 22:59:41 +12:00
$buildId = $deployment -> getAttribute ( 'buildId' , '' );
$isNewBuild = empty ( $buildId );
2023-08-19 18:15:47 +12:00
if ( $isNewBuild ) {
2022-08-15 02:22:38 +12:00
$buildId = ID :: unique ();
2023-06-22 22:59:41 +12:00
$build = $dbForProject -> createDocument ( 'builds' , new Document ([
'$id' => $buildId ,
'$permissions' => [],
'startTime' => $startTime ,
'deploymentInternalId' => $deployment -> getInternalId (),
'deploymentId' => $deployment -> getId (),
'status' => 'processing' ,
'path' => '' ,
'runtime' => $function -> getAttribute ( 'runtime' ),
'source' => $deployment -> getAttribute ( 'path' , '' ),
'sourceType' => strtolower ( App :: getEnv ( '_APP_STORAGE_DEVICE' , Storage :: DEVICE_LOCAL )),
2023-08-06 02:50:28 +12:00
'logs' => '' ,
2023-06-22 22:59:41 +12:00
'endTime' => null ,
'duration' => 0 ,
'size' => 0
]));
2023-05-22 22:58:13 +12:00
2023-06-22 22:59:41 +12:00
$deployment -> setAttribute ( 'buildId' , $build -> getId ());
$deployment -> setAttribute ( 'buildInternalId' , $build -> getInternalId ());
$deployment = $dbForProject -> updateDocument ( 'deployments' , $deployment -> getId (), $deployment );
} else {
$build = $dbForProject -> getDocument ( 'builds' , $buildId );
}
2023-05-22 22:58:13 +12:00
2023-06-22 22:59:41 +12:00
$source = $deployment -> getAttribute ( 'path' , '' );
2023-07-30 21:51:13 +12:00
$installationId = $deployment -> getAttribute ( 'installationId' , '' );
$providerRepositoryId = $deployment -> getAttribute ( 'providerRepositoryId' , '' );
2023-08-12 04:44:05 +12:00
$providerCommitHash = $deployment -> getAttribute ( 'providerCommitHash' , '' );
2023-07-30 21:51:13 +12:00
$isVcsEnabled = $providerRepositoryId ? true : false ;
2023-06-22 22:59:41 +12:00
$owner = '' ;
$repositoryName = '' ;
2023-06-20 19:12:33 +12:00
2023-08-19 18:15:47 +12:00
if ( $isVcsEnabled ) {
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
$providerInstallationId = $installation -> getAttribute ( 'providerInstallationId' );
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-05-22 22:58:13 +12:00
2023-08-19 18:15:47 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
}
2023-05-22 22:58:13 +12:00
2023-08-19 18:15:47 +12:00
try {
if ( $isNewBuild && $isVcsEnabled ) {
$tmpDirectory = '/tmp/builds/' . $buildId . '/code' ;
$rootDirectory = $function -> getAttribute ( 'providerRootDirectory' , '' );
$rootDirectory = \rtrim ( $rootDirectory , '/' );
$rootDirectory = \ltrim ( $rootDirectory , '.' );
$rootDirectory = \ltrim ( $rootDirectory , '/' );
$owner = $github -> getOwnerName ( $providerInstallationId );
$repositoryName = $github -> getRepositoryName ( $providerRepositoryId );
$cloneOwner = $deployment -> getAttribute ( 'providerRepositoryOwner' , $owner );
$cloneRepository = $deployment -> getAttribute ( 'providerRepositoryName' , $repositoryName );
$branchName = $deployment -> getAttribute ( 'providerBranch' );
2023-08-21 23:33:07 +12:00
$commitHash = $deployment -> getAttribute ( 'providerCommitHash' , '' );
$gitCloneCommand = $github -> generateCloneCommand ( $cloneOwner , $cloneRepository , $branchName , $tmpDirectory , $rootDirectory , $commitHash );
2023-08-19 18:15:47 +12:00
$stdout = '' ;
$stderr = '' ;
2023-08-21 01:27:34 +12:00
Console :: execute ( 'mkdir -p /tmp/builds/' . \escapeshellcmd ( $buildId ), '' , $stdout , $stderr );
2023-08-19 18:15:47 +12:00
$exit = Console :: execute ( $gitCloneCommand , '' , $stdout , $stderr );
if ( $exit !== 0 ) {
throw new \Exception ( 'Unable to clone code repository: ' . $stderr );
}
2023-06-28 23:31:35 +12:00
2023-08-19 18:15:47 +12:00
// Build from template
$templateRepositoryName = $template -> getAttribute ( 'repositoryName' , '' );
$templateOwnerName = $template -> getAttribute ( 'ownerName' , '' );
$templateBranch = $template -> getAttribute ( 'branch' , '' );
2023-06-28 23:31:35 +12:00
2023-08-19 18:15:47 +12:00
$templateRootDirectory = $template -> getAttribute ( 'rootDirectory' , '' );
$templateRootDirectory = \rtrim ( $templateRootDirectory , '/' );
$templateRootDirectory = \ltrim ( $templateRootDirectory , '.' );
$templateRootDirectory = \ltrim ( $templateRootDirectory , '/' );
2023-06-28 23:31:35 +12:00
2023-08-19 18:15:47 +12:00
if ( ! empty ( $templateRepositoryName ) && ! empty ( $templateOwnerName ) && ! empty ( $templateBranch )) {
// Clone template repo
2023-08-21 01:27:34 +12:00
$tmpTemplateDirectory = '/tmp/builds/' . \escapeshellcmd ( $buildId ) . '/template' ;
2023-08-19 18:15:47 +12:00
$gitCloneCommandForTemplate = $github -> generateCloneCommand ( $templateOwnerName , $templateRepositoryName , $templateBranch , $tmpTemplateDirectory , $templateRootDirectory );
$exit = Console :: execute ( $gitCloneCommandForTemplate , '' , $stdout , $stderr );
2023-05-22 22:58:13 +12:00
2023-06-22 22:59:41 +12:00
if ( $exit !== 0 ) {
throw new \Exception ( 'Unable to clone code repository: ' . $stderr );
}
2023-05-22 22:58:13 +12:00
2023-08-19 18:15:47 +12:00
// Ensure directories
Console :: execute ( 'mkdir -p ' . $tmpTemplateDirectory . '/' . $templateRootDirectory , '' , $stdout , $stderr );
Console :: execute ( 'mkdir -p ' . $tmpDirectory . '/' . $rootDirectory , '' , $stdout , $stderr );
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
// Merge template into user repo
Console :: execute ( 'cp -rfn ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/* ' . $tmpDirectory . '/' . $rootDirectory , '' , $stdout , $stderr );
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
// Commit and push
2023-08-22 19:13:00 +12:00
$exit = Console :: execute ( 'git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . $tmpDirectory . ' && git add . && git commit -m "Create \'' . \escapeshellcmd ( $function -> getAttribute ( 'name' , '' )) . '\' function" && git push origin ' . \escapeshellcmd ( $branchName ), '' , $stdout , $stderr );
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
if ( $exit !== 0 ) {
throw new \Exception ( 'Unable to push code repository: ' . $stderr );
}
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
$exit = Console :: execute ( 'cd ' . $tmpDirectory . ' && git rev-parse HEAD' , '' , $stdout , $stderr );
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
if ( $exit !== 0 ) {
throw new \Exception ( 'Unable to get vcs commit SHA: ' . $stderr );
2023-06-22 22:59:41 +12:00
}
2023-08-19 18:15:47 +12:00
$providerCommitHash = \trim ( $stdout );
$authorUrl = " https://github.com/ $cloneOwner " ;
$deployment -> setAttribute ( 'providerCommitHash' , $providerCommitHash ? ? '' );
$deployment -> setAttribute ( 'providerCommitAuthorUrl' , $authorUrl );
$deployment -> setAttribute ( 'providerCommitAuthor' , 'Appwrite' );
$deployment -> setAttribute ( 'providerCommitMessage' , " Create ' " . $function -> getAttribute ( 'name' , '' ) . " ' function " );
$deployment -> setAttribute ( 'providerCommitUrl' , " https://github.com/ $cloneOwner / $cloneRepository /commit/ $providerCommitHash " );
$deployment = $dbForProject -> updateDocument ( 'deployments' , $deployment -> getId (), $deployment );
/**
* Send realtime Event
*/
$target = Realtime :: fromPayload (
// Pass first, most verbose event pattern
event : $allEvents [ 0 ],
payload : $build ,
project : $project
);
Realtime :: send (
projectId : 'console' ,
payload : $build -> getArrayCopy (),
events : $allEvents ,
channels : $target [ 'channels' ],
roles : $target [ 'roles' ]
);
}
2023-06-22 22:59:41 +12:00
2023-08-21 01:27:34 +12:00
Console :: execute ( 'tar --exclude code.tar.gz -czf /tmp/builds/' . \escapeshellcmd ( $buildId ) . '/code.tar.gz -C /tmp/builds/' . \escapeshellcmd ( $buildId ) . '/code' . ( empty ( $rootDirectory ) ? '' : '/' . $rootDirectory ) . ' .' , '' , $stdout , $stderr );
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
$deviceFunctions = $this -> getFunctionsDevice ( $project -> getId ());
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
$fileName = 'code.tar.gz' ;
$fileTmpName = '/tmp/builds/' . $buildId . '/code.tar.gz' ;
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
$path = $deviceFunctions -> getPath ( $deployment -> getId () . '.' . \pathinfo ( $fileName , PATHINFO_EXTENSION ));
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
$result = $deviceFunctions -> move ( $fileTmpName , $path );
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
if ( ! $result ) {
throw new \Exception ( " Unable to move file " );
}
2023-06-22 22:59:41 +12:00
2023-08-21 01:27:34 +12:00
Console :: execute ( 'rm -rf /tmp/builds/' . \escapeshellcmd ( $buildId ), '' , $stdout , $stderr );
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
$source = $path ;
2023-06-22 22:59:41 +12:00
2023-08-19 18:15:47 +12:00
$build = $dbForProject -> updateDocument ( 'builds' , $build -> getId (), $build -> setAttribute ( 'source' , $source ));
$this -> runGitAction ( 'processing' , $github , $providerCommitHash , $owner , $repositoryName , $project , $function , $deployment -> getId (), $dbForProject , $dbForConsole );
2023-05-22 22:58:13 +12:00
}
2023-06-22 22:59:41 +12:00
/** Request the executor to build the code... */
$build -> setAttribute ( 'status' , 'building' );
$build = $dbForProject -> updateDocument ( 'builds' , $buildId , $build );
2022-02-03 07:58:03 +13:00
2023-06-22 22:59:41 +12:00
if ( $isVcsEnabled ) {
2023-08-12 04:44:05 +12:00
$this -> runGitAction ( 'building' , $github , $providerCommitHash , $owner , $repositoryName , $project , $function , $deployment -> getId (), $dbForProject , $dbForConsole );
2023-06-22 22:59:41 +12:00
}
2022-02-03 07:58:03 +13:00
2023-06-22 22:59:41 +12:00
/** Trigger Webhook */
$deploymentModel = new Deployment ();
2023-05-22 22:58:13 +12:00
2023-06-22 22:59:41 +12:00
$deploymentUpdate = new Event ( Event :: WEBHOOK_QUEUE_NAME , Event :: WEBHOOK_CLASS_NAME );
$deploymentUpdate
-> setProject ( $project )
-> setEvent ( 'functions.[functionId].deployments.[deploymentId].update' )
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ())
-> setPayload ( $deployment -> getArrayCopy ( array_keys ( $deploymentModel -> getRules ())))
-> trigger ();
2022-09-16 21:54:59 +12:00
2023-06-22 22:59:41 +12:00
/** Trigger Functions */
$pools = $register -> get ( 'pools' );
$connection = $pools -> get ( 'queue' ) -> pop ();
2022-04-19 04:21:45 +12:00
2023-06-22 22:59:41 +12:00
$functions = new Func ( $connection -> getResource ());
$functions
-> from ( $deploymentUpdate )
-> trigger ();
2022-11-17 06:55:26 +13:00
2023-06-22 22:59:41 +12:00
$connection -> reclaim ();
2022-04-19 04:21:45 +12:00
2023-06-22 22:59:41 +12:00
/** Trigger Realtime */
$target = Realtime :: fromPayload (
// Pass first, most verbose event pattern
event : $allEvents [ 0 ],
payload : $build ,
project : $project
);
2022-04-19 04:21:45 +12:00
2023-06-22 22:59:41 +12:00
Realtime :: send (
projectId : 'console' ,
payload : $build -> getArrayCopy (),
events : $allEvents ,
channels : $target [ 'channels' ],
roles : $target [ 'roles' ]
);
2023-05-22 22:58:13 +12:00
2023-06-22 22:59:41 +12:00
$vars = [];
2023-09-05 20:21:36 +12:00
// Shared vars
foreach ( $function -> getAttribute ( 'varsProject' , []) as $var ) {
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
2023-08-18 18:55:44 +12:00
}
2023-06-22 22:59:41 +12:00
// Function vars
2023-09-05 20:21:36 +12:00
foreach ( $function -> getAttribute ( 'vars' , []) as $var ) {
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
}
2023-06-22 22:59:41 +12:00
// Appwrite vars
$vars = \array_merge ( $vars , [
'APPWRITE_FUNCTION_ID' => $function -> getId (),
'APPWRITE_FUNCTION_NAME' => $function -> getAttribute ( 'name' ),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment -> getId (),
'APPWRITE_FUNCTION_PROJECT_ID' => $project -> getId (),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime [ 'name' ] ? ? '' ,
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime [ 'version' ] ? ? '' ,
]);
2022-08-10 21:32:48 +12:00
2023-07-21 23:52:14 +12:00
$command = $deployment -> getAttribute ( 'commands' , '' );
2023-03-29 02:21:42 +13:00
$command = \str_replace ( '"' , '\\"' , $command );
2023-06-13 18:26:36 +12:00
$response = null ;
2023-06-22 22:59:41 +12:00
$err = null ;
2023-06-17 22:07:30 +12:00
// TODO: Remove run() wrapper when switching to new utopia queue. That should be done on Swoole adapter in the libary
2023-06-22 22:59:41 +12:00
Co\run ( function () use ( $project , $deployment , & $response , $source , $function , $runtime , $vars , $command , & $build , $dbForProject , $allEvents , & $err ) {
2023-06-13 18:26:36 +12:00
Co :: join ([
2023-06-22 22:59:41 +12:00
Co\go ( function () use ( & $response , $project , $deployment , $source , $function , $runtime , $vars , $command , & $err ) {
try {
2023-09-05 05:53:25 +12:00
$version = $function -> getAttribute ( 'version' , 'v2' );
$command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $command . '"' ;
2023-06-22 22:59:41 +12:00
$response = $this -> executor -> createRuntime (
deploymentId : $deployment -> getId (),
2023-08-19 18:15:47 +12:00
projectId : $project -> getId (),
2023-06-22 22:59:41 +12:00
source : $source ,
image : $runtime [ 'image' ],
2023-09-05 05:53:25 +12:00
version : $version ,
2023-06-22 22:59:41 +12:00
remove : true ,
entrypoint : $deployment -> getAttribute ( 'entrypoint' ),
destination : APP_STORAGE_BUILDS . " /app- { $project -> getId () } " ,
variables : $vars ,
2023-09-05 05:53:25 +12:00
command : $command
2023-06-22 22:59:41 +12:00
);
} catch ( Exception $error ) {
$err = $error ;
}
}),
Co\go ( function () use ( $project , $deployment , & $response , & $build , $dbForProject , $allEvents , & $err ) {
try {
$this -> executor -> getLogs (
deploymentId : $deployment -> getId (),
2023-08-19 18:15:47 +12:00
projectId : $project -> getId (),
2023-06-22 22:59:41 +12:00
callback : function ( $logs ) use ( & $response , & $build , $dbForProject , $allEvents , $project ) {
if ( $response === null ) {
2023-08-23 16:43:23 +12:00
$build = $dbForProject -> getDocument ( 'builds' , $build -> getId ());
if ( $build -> isEmpty ()) {
throw new Exception ( 'Build not found' , 404 );
}
2023-08-06 02:50:28 +12:00
$build = $build -> setAttribute ( 'logs' , $build -> getAttribute ( 'logs' , '' ) . $logs );
2023-06-22 22:59:41 +12:00
$build = $dbForProject -> updateDocument ( 'builds' , $build -> getId (), $build );
/**
* Send realtime Event
*/
$target = Realtime :: fromPayload (
// Pass first, most verbose event pattern
event : $allEvents [ 0 ],
payload : $build ,
project : $project
);
Realtime :: send (
projectId : 'console' ,
payload : $build -> getArrayCopy (),
events : $allEvents ,
channels : $target [ 'channels' ],
roles : $target [ 'roles' ]
);
}
2023-06-13 18:26:36 +12:00
}
2023-06-22 22:59:41 +12:00
);
} catch ( Exception $error ) {
if ( empty ( $err )) {
$err = $error ;
2023-06-13 18:26:36 +12:00
}
2023-06-22 22:59:41 +12:00
}
2023-06-13 23:13:02 +12:00
}),
2023-06-13 18:26:36 +12:00
]);
});
2023-06-22 22:59:41 +12:00
if ( $err ) {
throw $err ;
}
2023-03-15 21:20:25 +13:00
$endTime = DateTime :: now ();
2023-06-22 22:59:41 +12:00
$durationEnd = \microtime ( true );
2022-12-21 01:56:56 +13:00
2022-02-14 10:26:36 +13:00
/** Update the build document */
2022-12-18 20:35:16 +13:00
$build -> setAttribute ( 'startTime' , DateTime :: format (( new \DateTime ()) -> setTimestamp ( $response [ 'startTime' ])));
2023-03-15 21:20:25 +13:00
$build -> setAttribute ( 'endTime' , $endTime );
2023-06-22 22:59:41 +12:00
$build -> setAttribute ( 'duration' , \intval ( \ceil ( $durationEnd - $durationStart )));
2023-02-03 08:21:00 +13:00
$build -> setAttribute ( 'status' , 'ready' );
2023-03-15 21:20:25 +13:00
$build -> setAttribute ( 'path' , $response [ 'path' ]);
2022-12-18 20:20:50 +13:00
$build -> setAttribute ( 'size' , $response [ 'size' ]);
2023-08-06 02:50:28 +12:00
$build -> setAttribute ( 'logs' , $response [ 'output' ]);
2022-02-17 00:43:21 +13:00
2023-05-22 22:58:13 +12:00
if ( $isVcsEnabled ) {
2023-08-12 04:44:05 +12:00
$this -> runGitAction ( 'ready' , $github , $providerCommitHash , $owner , $repositoryName , $project , $function , $deployment -> getId (), $dbForProject , $dbForConsole );
2023-05-22 22:58:13 +12:00
}
2022-02-17 00:43:21 +13:00
Console :: success ( " Build id: $buildId created " );
/** Set auto deploy */
if ( $deployment -> getAttribute ( 'activate' ) === true ) {
2022-12-26 01:07:54 +13:00
$function -> setAttribute ( 'deploymentInternalId' , $deployment -> getInternalId ());
2022-02-17 00:43:21 +13:00
$function -> setAttribute ( 'deployment' , $deployment -> getId ());
2023-06-22 22:59:41 +12:00
$function -> setAttribute ( 'live' , true );
2022-08-19 22:26:09 +12:00
$function = $dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function );
2022-02-17 00:43:21 +13:00
}
/** Update function schedule */
2022-11-17 01:19:29 +13:00
$dbForConsole = $this -> getConsoleDB ();
2023-07-28 19:56:07 +12:00
// Inform scheduler if function is still active
2023-08-20 01:00:22 +12:00
$schedule = $dbForConsole -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-02-14 10:26:36 +13:00
} catch ( \Throwable $th ) {
2022-09-03 02:19:36 +12:00
$endTime = DateTime :: now ();
2023-03-29 02:21:42 +13:00
$durationEnd = \microtime ( true );
2022-12-21 01:56:56 +13:00
$build -> setAttribute ( 'endTime' , $endTime );
2023-03-29 02:21:42 +13:00
$build -> setAttribute ( 'duration' , \intval ( \ceil ( $durationEnd - $durationStart )));
2022-02-14 10:26:36 +13:00
$build -> setAttribute ( 'status' , 'failed' );
2023-08-06 02:50:28 +12:00
$build -> setAttribute ( 'logs' , $th -> getMessage ());
2022-02-14 13:20:12 +13:00
Console :: error ( $th -> getMessage ());
2023-08-19 18:15:47 +12:00
Console :: error ( $th -> getFile () . ':' . $th -> getLine ());
Console :: error ( $th -> getTraceAsString ());
2023-05-23 16:37:25 +12:00
if ( $isVcsEnabled ) {
2023-08-12 04:44:05 +12:00
$this -> runGitAction ( 'failed' , $github , $providerCommitHash , $owner , $repositoryName , $project , $function , $deployment -> getId (), $dbForProject , $dbForConsole );
2023-05-23 16:37:25 +12:00
}
2022-02-17 00:43:21 +13:00
} finally {
$build = $dbForProject -> updateDocument ( 'builds' , $buildId , $build );
2022-03-01 00:50:27 +13:00
2022-05-24 02:54:50 +12:00
/**
2022-04-14 00:39:31 +12:00
* Send realtime Event
2022-03-01 00:50:27 +13:00
*/
2022-05-10 00:36:29 +12:00
$target = Realtime :: fromPayload (
2023-08-21 00:29:43 +12:00
// Pass first, most verbose event pattern
2022-05-10 00:36:29 +12:00
event : $allEvents [ 0 ],
payload : $build ,
project : $project
);
2022-03-01 00:50:27 +13:00
Realtime :: send (
projectId : 'console' ,
payload : $build -> getArrayCopy (),
2022-04-19 04:21:45 +12:00
events : $allEvents ,
2022-03-01 00:50:27 +13:00
channels : $target [ 'channels' ],
roles : $target [ 'roles' ]
);
2022-12-13 20:35:05 +13:00
2023-08-21 00:29:43 +12:00
/** Update usage stats */
if ( App :: getEnv ( '_APP_USAGE_STATS' , 'enabled' ) === 'enabled' ) {
$statsd = $register -> get ( 'statsd' );
$usage = new Stats ( $statsd );
$usage
-> setParam ( 'projectInternalId' , $project -> getInternalId ())
-> setParam ( 'projectId' , $project -> getId ())
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'builds.{scope}.compute' , 1 )
-> setParam ( 'buildStatus' , $build -> getAttribute ( 'status' , '' ))
-> setParam ( 'buildTime' , $build -> getAttribute ( 'duration' ))
-> setParam ( 'networkRequestSize' , 0 )
-> setParam ( 'networkResponseSize' , 0 )
-> submit ();
}
}
2022-01-24 11:25:46 +13:00
}
2023-08-19 18:15:47 +12:00
protected function runGitAction ( string $status , GitHub $github , string $providerCommitHash , string $owner , string $repositoryName , Document $project , Document $function , string $deploymentId , Database $dbForProject , Database $dbForConsole ) : void
2023-05-29 23:51:03 +12:00
{
2023-07-30 21:51:13 +12:00
if ( $function -> getAttribute ( 'providerSilentMode' , false ) === true ) {
2023-06-08 03:50:32 +12:00
return ;
}
2023-05-29 23:51:03 +12:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2023-07-30 21:51:13 +12:00
$commentId = $deployment -> getAttribute ( 'providerCommentId' , '' );
2023-05-29 23:51:03 +12:00
2023-07-30 21:51:13 +12:00
if ( ! empty ( $providerCommitHash )) {
2023-05-29 23:51:03 +12:00
$message = match ( $status ) {
'ready' => 'Build succeeded.' ,
'failed' => 'Build failed.' ,
'processing' => 'Building...' ,
default => $status
};
$state = match ( $status ) {
'ready' => 'success' ,
'failed' => 'failure' ,
'processing' => 'pending' ,
default => $status
};
$functionName = $function -> getAttribute ( 'name' );
$projectName = $project -> getAttribute ( 'name' );
$name = " { $functionName } ( { $projectName } ) " ;
2023-08-12 04:44:05 +12:00
$protocol = App :: getEnv ( '_APP_OPTIONS_FORCE_HTTPS' ) == 'disabled' ? 'http' : 'https' ;
$hostname = App :: getEnv ( '_APP_DOMAIN' );
$functionId = $function -> getId ();
$projectId = $project -> getId ();
$providerTargetUrl = $protocol . '://' . $hostname . " /console/project- $projectId /functions/function- $functionId " ;
2023-07-30 21:51:13 +12:00
$github -> updateCommitStatus ( $repositoryName , $providerCommitHash , $owner , $state , $message , $providerTargetUrl , $name );
2023-05-29 23:51:03 +12:00
}
2023-06-13 18:26:36 +12:00
if ( ! empty ( $commentId )) {
2023-06-22 22:59:41 +12:00
$retries = 0 ;
2023-08-19 18:15:47 +12:00
while ( true ) {
2023-06-22 22:59:41 +12:00
$retries ++ ;
try {
$dbForConsole -> createDocument ( 'vcsCommentLocks' , new Document ([
'$id' => $commentId
]));
break ;
} catch ( Exception $err ) {
if ( $retries >= 9 ) {
throw $err ;
}
2023-08-19 18:15:47 +12:00
\sleep ( 1 );
}
2023-06-22 22:59:41 +12:00
}
2023-08-19 18:15:47 +12:00
// Wrap in try/finally to ensure lock file gets deleted
2023-06-28 23:31:35 +12:00
try {
$comment = new Comment ();
$comment -> parseComment ( $github -> getComment ( $owner , $repositoryName , $commentId ));
2023-08-10 12:31:49 +12:00
$comment -> addBuild ( $project , $function , $status , $deployment -> getId (), [ 'type' => 'logs' ]);
2023-06-28 23:31:35 +12:00
$github -> updateComment ( $owner , $repositoryName , $commentId , $comment -> generateComment ());
} finally {
$dbForConsole -> deleteDocument ( 'vcsCommentLocks' , $commentId );
}
2023-06-13 18:26:36 +12:00
}
2023-05-29 23:51:03 +12:00
}
2022-04-20 01:13:55 +12:00
public function shutdown () : void
{
}
2022-01-24 11:25:46 +13:00
}