diff --git a/RAP4/customizations/bootstrap/files/DeploymentInterface.php b/RAP4/customizations/bootstrap/files/DeploymentInterface.php new file mode 100644 index 00000000..0d3d9225 --- /dev/null +++ b/RAP4/customizations/bootstrap/files/DeploymentInterface.php @@ -0,0 +1,8 @@ +userName = $userName; + $this->ee = $ee; + } + + public function deploy() { + // Your Docker deployment code here + /** Deployed with Docker Compose */ + + // Stop any existing prototype container for this user + $remove = new Command( + "docker rm", + [ "-f", + "\"{$this->$userName}\"" + ], + $ee->getLogger() + ); + $remove->execute(); + + // Run student prototype with Docker + $this->$command = new Command( + "echo \"{$this->$zipContentForCommandline} {$this->$mainAldForCommandLine}\" | docker run", + [ "--name \"{$this->$userName}\"", + "--rm", # deletes the container when it is stopped. Useful to prevent container disk space usage to explode. + "-i", + "-p 8000:80", + "-a stdin", // stdin ensures that the content of the script is available in the container. + "--network proxy", // the reverse proxy Traefik is in the proxy network + "--label traefik.enable=true", // label for Traefik to route trafic + "--label traefik.docker.network=proxy", // solving RAP issue #92 + "--label traefik.http.routers.{$this->$userName}-insecure.rule=\"Host(\\`{$this->$hostName}\\`)\"", // e.g. student123.rap.cs.ou.nl + "--label student-prototype", // label used by cleanup process to remove all (expired) student prototypes + "-e AMPERSAND_DBHOST=" . getenv('AMPERSAND_DBHOST'), // use same database host as the RAP4 application itself + "-e AMPERSAND_DBNAME=\"student_{$this->$userName}\"", + "-e AMPERSAND_DBUSER=" . getenv('AMPERSAND_DBUSER'), // TODO change db user to a student prototype specific user with less privileges and limited to databases with prefix 'student_' + "-e AMPERSAND_DBPASS=" . getenv('AMPERSAND_DBPASS'), + "-e AMPERSAND_PRODUCTION_MODE=\"false\"", // student must be able to reset his/her application + "-e AMPERSAND_DEBUG_MODE=\"true\"", // show student detailed log information, is needed otherwise user is e.g. not redirected to reinstall page + "-e AMPERSAND_SERVER_URL=\"https://{$this->$hostName}\"", + "-e AMPERSAND_LOG_CONFIG={$this->$studentProtoLogConfig}", // use high level logging + $this->$studentProtoImage // image name to run + ], + $ee->getLogger() + ); + $this->$command->execute(); + + // Add docker container also to rap_db network + $command2 = new Command( + "docker network connect rap_db {$this->$userName}", + [], + $ee->getLogger() + ); + $command2->execute(); + } +} \ No newline at end of file diff --git a/RAP4/customizations/bootstrap/files/ExecEngineFunctions.php b/RAP4/customizations/bootstrap/files/ExecEngineFunctions.php index 2fee6f68..3a6bede8 100644 --- a/RAP4/customizations/bootstrap/files/ExecEngineFunctions.php +++ b/RAP4/customizations/bootstrap/files/ExecEngineFunctions.php @@ -9,6 +9,7 @@ use Ampersand\Rule\ExecEngine; use Ampersand\Core\Link; use Ampersand\Extension\RAP4\Command; +use RAP4\Prototype; /* Ampersand commands must not be changed in this file, but in a configuration yaml file. * @@ -290,179 +291,193 @@ * Phan analyzes the inner body of this closure as if it were a closure declared in ExecEngine. */ ExecEngine::registerFunction('Prototype', function (string $path, Atom $scriptAtom, Atom $scriptVersionAtom, string $userName) { - /** @var \Ampersand\Rule\ExecEngine $ee */ $ee = $this; // because autocomplete does not work on $this + $prototype = new Prototype($path, $scriptAtom, $scriptVersionAtom, $userName, $ee); + $prototype->execute(); +}); +//ExecEngine::registerFunction('Prototype', function (string $path, Atom $scriptAtom, Atom $scriptVersionAtom, string $userName) { + ///** @var \Ampersand\Rule\ExecEngine $ee */ + //$ee = $this; // because autocomplete does not work on $this - $scriptContentPairs = $scriptVersionAtom->getLinks('content[ScriptVersion*ScriptContent]'); + // $scriptContentPairs = $scriptVersionAtom->getLinks('content[ScriptVersion*ScriptContent]'); - $serverName = getenv('RAP_HOST_NAME'); - $studentProtoImage = getenv('RAP_STUDENT_PROTO_IMAGE'); - $studentProtoLogConfig = getenv('RAP_STUDENT_PROTO_LOG_CONFIG'); - if ($studentProtoLogConfig === false) { - $studentProtoLogConfig = 'logging.yaml'; - } + // $serverName = getenv('RAP_HOST_NAME'); + // $studentProtoImage = getenv('RAP_STUDENT_PROTO_IMAGE'); + // $studentProtoLogConfig = getenv('RAP_STUDENT_PROTO_LOG_CONFIG'); + // if ($studentProtoLogConfig === false) { + // $studentProtoLogConfig = 'logging.yaml'; + // } - if (count($scriptContentPairs) != 1) { - throw new Exception("No (or multiple) script content found for '{$scriptVersionAtom}'", 500); - } + // if (count($scriptContentPairs) != 1) { + // throw new Exception("No (or multiple) script content found for '{$scriptVersionAtom}'", 500); + // } - $scriptContent = $scriptContentPairs[0]->tgt()->getId(); - $scriptContentForCommandline = base64_encode($scriptContent); + // $scriptContent = $scriptContentPairs[0]->tgt()->getId(); //paths - $relDir = pathinfo($path, PATHINFO_DIRNAME); - $workDir = realpath($ee->getApp()->getSettings()->get('global.absolutePath')) . "/data/" . $relDir; + // $relDir = pathinfo($path, PATHINFO_DIRNAME); + // $workDir = realpath($ee->getApp()->getSettings()->get('global.absolutePath')) . "/data/" . $relDir; //zip - $projectFolder = "{$workDir}/project"; - $mainAdl = "{$projectFolder}/main.adl"; + // $projectFolder = "{$workDir}/project"; + // $mainAdl = "{$projectFolder}/main.adl"; - mkdir($projectFolder); - file_put_contents($mainAdl, $scriptContent); - - $zipFile = "{$workDir}/project.zip"; - $zip = new \ZipArchive; - $zip->open($zipFile, \ZipArchive::CREATE); - $files = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($projectFolder), - \RecursiveIteratorIterator::LEAVES_ONLY - ); + // mkdir($projectFolder); + // file_put_contents($mainAdl, $scriptContent); - foreach ($files as $name => $file) { - if (!$file->isDir()) { - $filePath = $file->getRealPath(); - $relativePath = substr($filePath, strlen($projectFolder) + 1); + // $zipFile = "{$workDir}/project.zip"; + // $zip = new \ZipArchive; + // $zip->open($zipFile, \ZipArchive::CREATE); + // $files = new \RecursiveIteratorIterator( + // new \RecursiveDirectoryIterator($projectFolder), + // \RecursiveIteratorIterator::LEAVES_ONLY + // ); - $zip->addFile($filePath, $relativePath); - } - } + // foreach ($files as $name => $file) { + // if (!$file->isDir()) { + // $filePath = $file->getRealPath(); + // $relativePath = substr($filePath, strlen($projectFolder) + 1); - $zip->close(); + // $zip->addFile($filePath, $relativePath); + // } + // } - $zipContent = file_get_contents($zipFile); - $zipContentForCommandline = base64_encode($zipContent); - $mainAldForCommandLine = base64_encode("main.adl"); + // $zip->close(); - $pattern = '/[\W+]/'; + // $zipContent = file_get_contents($zipFile); + // $zipContentForCommandline = base64_encode($zipContent); + // $mainAldForCommandLine = base64_encode("main.adl"); - $userName=strtolower($userName); - $userName = preg_replace($pattern, '-', $userName); + // $pattern = '/[\W+]/'; - $deployment = getenv('RAP_DEPLOYMENT'); - if ($deployment == 'Kubernetes') { - /** Deployed on Kubernetes Cluster - * Save student-manifest-template.yaml at a logical location - * - Copy student-manifest-template.yaml as student-manifest-{{student}}.yaml to /data/ - * - replace {{student}} and {{namespace}} - * - replace {{adl-base64}} with base64 compiled adl file - * - save - * - run kubectl apply -f "student-manifest-{{student}}.yaml" - */ + // $userName=strtolower($userName); + // $userName = preg_replace($pattern, '-', $userName); - $namespace=getenv('RAP_KUBERNETES_NAMESPACE'); - $containerImage=getenv('RAP_STUDENT_PROTO_IMAGE'); + // $deployment = getenv('RAP_DEPLOYMENT'); + // if ($deployment == 'Kubernetes') { + // /** Deployed on Kubernetes Cluster + // * Save student-manifest-template.yaml at a logical location + // * - Copy student-manifest-template.yaml as student-manifest-{{student}}.yaml to /data/ + // * - replace {{student}} and {{namespace}} + // * - replace {{adl-base64}} with base64 compiled adl file + // * - save + // * - run kubectl apply -f "student-manifest-{{student}}.yaml" + // */ - $hostname=getenv('RAP_HOST_NAME'); - $hostname="{$userName}.{$hostname}"; + // $namespace=getenv('RAP_KUBERNETES_NAMESPACE'); - $suffix=substr($namespace, 3); + // $getImageCommand = new Command( + // "kubectl get deployment/student-prototype{$suffix} -n {$namespace}", + // [ "-o=jsonpath='{$.spec.template.spec.containers[0].image}'" + // ], + // $ee->getLogger() + // ); - $dbName="rap-db{$suffix}"; - - $dbSecret="db-secrets{$suffix}"; + // $getImageCommand->execute(); - $tlsSecret="{$userName}-tls{$suffix}"; + // $containerImage=$getImageCommand->getResponse(); - // Location to save files - $manifestFile = $ee->getApp()->getSettings()->get('global.absolutePath') . '/bootstrap/files/student-manifest-template.yaml'; + // $hostname=getenv('RAP_HOST_NAME'); + // $hostname="{$userName}.{$hostname}"; - // Open student-manifest-template.yaml - $manifest=file_get_contents($manifestFile); - if ($manifest === false) { - throw new Exception("Student manifest template not found for '{$scriptVersionAtom}', workDir: {$workDir}, manifestFile: {$manifestFile}", 500); - } - // replace {{student}}, {{namespace}} and {{scriptContent}} - $manifest=str_replace("{{student}}", $userName, $manifest); - $manifest=str_replace("{{namespace}}", $namespace, $manifest); - $manifest=str_replace("{{containerImage}}", $containerImage, $manifest); - $manifest=str_replace("{{dbName}}", $dbName, $manifest); - $manifest=str_replace("{{dbSecrets}}", $dbSecret, $manifest); - $manifest=str_replace("{{hostName}}", $hostname, $manifest); - $manifest=str_replace("{{tlsSecret}}", $tlsSecret, $manifest); - $manifest=str_replace("{{zipContent}}", $zipContentForCommandline, $manifest); - $manifest=str_replace("{{mainAdl}}", $mainAldForCommandLine, $manifest); + // $suffix=substr($namespace, 3); + + // $dbName="rap-db{$suffix}"; - // Save manifest file - $studentManifestFile="{$workDir}/student-manifest-{$userName}.yaml"; - file_put_contents($studentManifestFile, $manifest); + // $dbSecret="db-secrets{$suffix}"; + + // $tlsSecret="{$userName}-tls{$suffix}"; + + // // Location to save files + // $manifestFile = $ee->getApp()->getSettings()->get('global.absolutePath') . '/bootstrap/files/student-manifest-template.yaml'; + + // // Open student-manifest-template.yaml + // $manifest=file_get_contents($manifestFile); + // if ($manifest === false) { + // throw new Exception("Student manifest template not found for '{$scriptVersionAtom}', workDir: {$workDir}, manifestFile: {$manifestFile}", 500); + // } + // // replace {{student}}, {{namespace}} and {{scriptContent}} + // $manifest=str_replace("{{student}}", $userName, $manifest); + // $manifest=str_replace("{{namespace}}", $namespace, $manifest); + // $manifest=str_replace("{{containerImage}}", $containerImage, $manifest); + // $manifest=str_replace("{{dbName}}", $dbName, $manifest); + // $manifest=str_replace("{{dbSecrets}}", $dbSecret, $manifest); + // $manifest=str_replace("{{hostName}}", $hostname, $manifest); + // $manifest=str_replace("{{tlsSecret}}", $tlsSecret, $manifest); + // $manifest=str_replace("{{zipContent}}", $zipContentForCommandline, $manifest); + // $manifest=str_replace("{{mainAdl}}", $mainAldForCommandLine, $manifest); - // Call Kubernetes API to add script - $command = new Command( - "kubectl apply", - [ "-f", - "\"{$studentManifestFile}\"" - ], - $ee->getLogger() - ); - $command->execute(); - } - else { - /** Deployed with Docker Compose */ - - // Stop any existing prototype container for this user - $remove = new Command( - "docker rm", - [ "-f", - "\"{$userName}\"" - ], - $ee->getLogger() - ); - $remove->execute(); + // // Save manifest file + // $studentManifestFile="{$workDir}/student-manifest-{$userName}.yaml"; + // file_put_contents($studentManifestFile, $manifest); - // Run student prototype with Docker - $command = new Command( - "echo \"{$zipContentForCommandline} {$mainAldForCommandLine}\" | docker run", - [ "--name \"{$userName}\"", - "--rm", # deletes the container when it is stopped. Useful to prevent container disk space usage to explode. - "-i", - "-p 8000:80", - "-a stdin", // stdin ensures that the content of the script is available in the container. - "--network proxy", // the reverse proxy Traefik is in the proxy network - "--label traefik.enable=true", // label for Traefik to route trafic - "--label traefik.docker.network=proxy", // solving RAP issue #92 - "--label traefik.http.routers.{$userName}-insecure.rule=\"Host(\\`{$userName}.{$serverName}\\`)\"", // e.g. student123.rap.cs.ou.nl - "--label student-prototype", // label used by cleanup process to remove all (expired) student prototypes - "-e AMPERSAND_DBHOST=" . getenv('AMPERSAND_DBHOST'), // use same database host as the RAP4 application itself - "-e AMPERSAND_DBNAME=\"student_{$userName}\"", - "-e AMPERSAND_DBUSER=" . getenv('AMPERSAND_DBUSER'), // TODO change db user to a student prototype specific user with less privileges and limited to databases with prefix 'student_' - "-e AMPERSAND_DBPASS=" . getenv('AMPERSAND_DBPASS'), - "-e AMPERSAND_PRODUCTION_MODE=\"false\"", // student must be able to reset his/her application - "-e AMPERSAND_DEBUG_MODE=\"true\"", // show student detailed log information, is needed otherwise user is e.g. not redirected to reinstall page - "-e AMPERSAND_SERVER_URL=\"https://{$userName}.{$serverName}\"", - "-e AMPERSAND_LOG_CONFIG={$studentProtoLogConfig}", // use high level logging - $studentProtoImage // image name to run - ], - $ee->getLogger() - ); - $command->execute(); + // // Call Kubernetes API to add script + // $command = new Command( + // "kubectl apply", + // [ "-f", + // "\"{$studentManifestFile}\"" + // ], + // $ee->getLogger() + // ); + // $command->execute(); + // } + // else { + // /** Deployed with Docker Compose */ + + // // Stop any existing prototype container for this user + // $remove = new Command( + // "docker rm", + // [ "-f", + // "\"{$userName}\"" + // ], + // $ee->getLogger() + // ); + // $remove->execute(); - // Add docker container also to rap_db network - $command2 = new Command( - "docker network connect rap_db {$userName}", - [], - $ee->getLogger() - ); - $command2->execute(); - } + // // Run student prototype with Docker + // $command = new Command( + // "echo \"{$zipContentForCommandline} {$mainAldForCommandLine}\" | docker run", + // [ "--name \"{$userName}\"", + // "--rm", # deletes the container when it is stopped. Useful to prevent container disk space usage to explode. + // "-i", + // "-p 8000:80", + // "-a stdin", // stdin ensures that the content of the script is available in the container. + // "--network proxy", // the reverse proxy Traefik is in the proxy network + // "--label traefik.enable=true", // label for Traefik to route trafic + // "--label traefik.docker.network=proxy", // solving RAP issue #92 + // "--label traefik.http.routers.{$userName}-insecure.rule=\"Host(\\`{$userName}.{$serverName}\\`)\"", // e.g. student123.rap.cs.ou.nl + // "--label student-prototype", // label used by cleanup process to remove all (expired) student prototypes + // "-e AMPERSAND_DBHOST=" . getenv('AMPERSAND_DBHOST'), // use same database host as the RAP4 application itself + // "-e AMPERSAND_DBNAME=\"student_{$userName}\"", + // "-e AMPERSAND_DBUSER=" . getenv('AMPERSAND_DBUSER'), // TODO change db user to a student prototype specific user with less privileges and limited to databases with prefix 'student_' + // "-e AMPERSAND_DBPASS=" . getenv('AMPERSAND_DBPASS'), + // "-e AMPERSAND_PRODUCTION_MODE=\"false\"", // student must be able to reset his/her application + // "-e AMPERSAND_DEBUG_MODE=\"true\"", // show student detailed log information, is needed otherwise user is e.g. not redirected to reinstall page + // "-e AMPERSAND_SERVER_URL=\"https://{$userName}.{$serverName}\"", + // "-e AMPERSAND_LOG_CONFIG={$studentProtoLogConfig}", // use high level logging + // $studentProtoImage // image name to run + // ], + // $ee->getLogger() + // ); + // $command->execute(); + + // // Add docker container also to rap_db network + // $command2 = new Command( + // "docker network connect rap_db {$userName}", + // [], + // $ee->getLogger() + // ); + // $command2->execute(); + // } - sleep(5); // helps to reduce "bad gateway" and "404 page not found" errors. + // sleep(5); // helps to reduce "bad gateway" and "404 page not found" errors. // Populate 'protoOk' upon success - setProp('protoOk[ScriptVersion*ScriptVersion]', $scriptVersionAtom, $command->getExitcode() == 0); + // setProp('protoOk[ScriptVersion*ScriptVersion]', $scriptVersionAtom, $command->getExitcode() == 0); - $message = $command->getExitcode() === 0 ? "Open prototype" : $command->getResponse(); - $scriptVersionAtom->link($message, 'compileresponse[ScriptVersion*CompileResponse]')->add(); -}); + // $message = $command->getExitcode() === 0 ? "Open prototype" : $command->getResponse(); + // $scriptVersionAtom->link($message, 'compileresponse[ScriptVersion*CompileResponse]')->add(); +//}); /** * @phan-closure-scope \Ampersand\Rule\ExecEngine diff --git a/RAP4/customizations/bootstrap/files/KubernetesDeployment.php b/RAP4/customizations/bootstrap/files/KubernetesDeployment.php new file mode 100644 index 00000000..54f84cb1 --- /dev/null +++ b/RAP4/customizations/bootstrap/files/KubernetesDeployment.php @@ -0,0 +1,73 @@ +userName = $userName; + $this->ee = $ee; + } + + public function deploy() { + // Your Kubernetes deployment code here + /** Deployed on Kubernetes Cluster + * Save student-manifest-template.yaml at a logical location + * - Copy student-manifest-template.yaml as student-manifest-{{student}}.yaml to /data/ + * - replace {{student}} and {{namespace}} + * - replace {{adl-base64}} with base64 compiled adl file + * - save + * - run kubectl apply -f "student-manifest-{{student}}.yaml" + */ + $namespace=getenv('RAP_KUBERNETES_NAMESPACE'); + $suffix=substr($namespace, 3); + + $getImageCommand = new Command( + "kubectl get deployment/student-prototype{$suffix} -n {$namespace}", + [ "-o=jsonpath='{\$.spec.template.spec.containers[0].image}'" + ], + $ee->getLogger() + ); + $getImageCommand->execute(); + $containerImage=$getImageCommand->getResponse(); + + $dbName="rap-db{$suffix}"; + $dbSecret="db-secrets{$suffix}"; + $tlsSecret="{$this->$userName}-tls{$suffix}"; + // Location to save files + $manifestFile = $ee->getApp()->getSettings()->get('global.absolutePath') . '/bootstrap/files/student-manifest-template.yaml'; + // Open student-manifest-template.yaml + $manifest=file_get_contents($manifestFile); + if ($manifest === false) { + throw new Exception("Student manifest template not found for '{$this->$scriptVersionAtom}', workDir: {$this->$workDir}, manifestFile: {$manifestFile}", 500); + } + // replace {{student}}, {{namespace}} and {{scriptContent}} + $manifest=str_replace("{{student}}", $this->$userName, $manifest); + $manifest=str_replace("{{namespace}}", $namespace, $manifest); + $manifest=str_replace("{{containerImage}}", $containerImage, $manifest); + $manifest=str_replace("{{dbName}}", $dbName, $manifest); + $manifest=str_replace("{{dbSecrets}}", $dbSecret, $manifest); + $manifest=str_replace("{{hostName}}", $this->$hostName, $manifest); + $manifest=str_replace("{{tlsSecret}}", $tlsSecret, $manifest); + $manifest=str_replace("{{zipContent}}", $this->$zipContentForCommandline, $manifest); + $manifest=str_replace("{{mainAdl}}", $this->$mainAldForCommandLine, $manifest); + // Save manifest file + $studentManifestFile="{$this->$workDir}/student-manifest-{$this->$userName}.yaml"; + file_put_contents($studentManifestFile, $manifest); + // Call Kubernetes API to add script + $this->$command = new Command( + "kubectl apply", + [ "-f", + "\"{$studentManifestFile}\"" + ], + $ee->getLogger() + ); + $this->$command->execute(); + } +} \ No newline at end of file diff --git a/RAP4/customizations/bootstrap/files/Prototype.php b/RAP4/customizations/bootstrap/files/Prototype.php new file mode 100644 index 00000000..8d1a1593 --- /dev/null +++ b/RAP4/customizations/bootstrap/files/Prototype.php @@ -0,0 +1,128 @@ +scriptAtom = $scriptAtom; + $this->scriptVersionAtom = $scriptVersionAtom; + $this->ee = $ee; + + $this->getEnvironmentVariables(); + + $relDir = pathinfo($path, PATHINFO_DIRNAME); + $this->$workDir = realpath($ee->getApp()->getSettings()->get('global.absolutePath')) . "/data/" . $relDir; + + $pattern = '/[\W+]/'; + + $user = strtolower($userName); + $this->$userName = preg_replace($pattern, '-', $user); + + if ($deployment == 'Kubernetes') { + $this->deploymentHandler = new KubernetesDeployment($userName, $ee); + } else { + $this->deploymentHandler = new DockerDeployment($userName, $ee); + } + } + + public function execute() { + $this->prepareScriptContent(); + $this->createZipFile(); + $this->handleDeployment(); + + sleep(5); // helps to reduce "bad gateway" and "404 page not found" errors. + + $this->populateProtoOk(); + } + + private function getEnvironmentVariables(){ + $serverName=getenv('RAP_HOST_NAME'); + $this->$hostName="{$this->$userName}.{$serverName}"; + $this->$studentProtoImage = getenv('RAP_STUDENT_PROTO_IMAGE'); + $this->$studentProtoLogConfig = getenv('RAP_STUDENT_PROTO_LOG_CONFIG'); + if ($this->$studentProtoLogConfig === false) { + $this->$studentProtoLogConfig = 'logging.yaml'; + } + $this->$deployment = getenv('RAP_DEPLOYMENT'); + } + + private function prepareScriptContent() : void { + // Code for preparing script content + $scriptContentPairs = $this->$scriptVersionAtom->getLinks('content[ScriptVersion*ScriptContent]'); + if (count($scriptContentPairs) != 1) { + throw new Exception("No (or multiple) script content found for '{$this->$scriptVersionAtom}'", 500); + } + $this->$scriptContent = $scriptContentPairs[0]->tgt()->getId(); + } + + private function createZipFile() { + // Code for creating zip file + $projectFolder = "{$this->$workDir}/project"; + $mainAdl = "{$projectFolder}/main.adl"; + + mkdir($projectFolder); + file_put_contents($mainAdl, $this->$scriptContent); + + $zipFile = "{$this->$workDir}/project.zip"; + $zip = new \ZipArchive; + $zip->open($zipFile, \ZipArchive::CREATE); + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($projectFolder), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($files as $name => $file) { + if (!$file->isDir()) { + $filePath = $file->getRealPath(); + $relativePath = substr($filePath, strlen($projectFolder) + 1); + + $zip->addFile($filePath, $relativePath); + } + } + + $zip->close(); + + $zipContent = file_get_contents($zipFile); + $this->$zipContentForCommandline = base64_encode($zipContent); + $this->$mainAldForCommandLine = base64_encode("main.adl"); + } + + private function handleDeployment() { + // Code for handling deployment + $this->$deploymentHandler->deploy(); + } + + private function populateProtoOk() { + // Code for populating 'protoOk' + setProp('protoOk[ScriptVersion*ScriptVersion]', $this->$scriptVersionAtom, $this->$command->getExitcode() == 0); + + $message = $this->$command->getExitcode() === 0 ? "$userName}.{$this->$serverName}\" target=\"_blank\">Open prototype" : $this->$command->getResponse(); + $this->$scriptVersionAtom->link($message, 'compileresponse[ScriptVersion*CompileResponse]')->add(); + } +}