Source of file DNProject.php
Size: 14,812 Bytes - Last Modified: 2021-12-23T10:29:15+00:00
/var/www/docs.ssmods.com/process/src/code/model/DNProject.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 | <?php /** * DNProject represents a project that relates to a group of target * environments. * */ class DNProject extends DataObject { /** * * @var array */ public static $db = array( "Name" => "Varchar", "CVSPath" => "Varchar(255)", "DiskQuotaMB" => "Int" ); /** * * @var array */ public static $has_many = array( "Environments" => "DNEnvironment", ); /** * * @var array */ public static $many_many = array( "Viewers" => "Group", ); /** * * @var array */ public static $summary_fields = array( "Name", "ViewersList", ); /** * * @var array */ public static $searchable_fields = array( "Name", ); private static $singular_name = 'Project'; private static $plural_name = 'Projects'; /** * * @var string */ private static $default_sort = 'Name'; /** * Display the repository URL on the project page. * * @var bool */ private static $show_repository_url = false; /** * In-memory cache for currentBuilds per environment since fetching them from * disk is pretty resource hungry. * * @var array */ protected static $relation_cache = array(); /** * Used by the sync task * * @param string $path * @return \DNProject */ public static function create_from_path($path) { $project = DNProject::create(); $project->Name = $path; $project->write(); // add the administrators group as the viewers of the new project $adminGroup = Group::get()->filter('Code', 'administrators')->first(); if($adminGroup && $adminGroup->exists()) { $project->Viewers()->add($adminGroup); } return $project; } /** * Return the used quota in MB. * * @param mixed $round Number of decimal places to round to * @return string|int The used quota size in MB */ public function getUsedQuotaMB($round = 2) { $size = 0; foreach($this->Environments() as $environment) { foreach($environment->DataArchives()->filter('IsBackup', 0) as $archive) { $size += $archive->ArchiveFile()->getAbsoluteSize(); } } // convert bytes to megabytes and round return round(($size / 1024) / 1024, $round); } /** * Getter for DiskQuotaMB field to provide a default for existing * records that have no quota field set, as it will need to default * to a globally set size. * * @return string|int The quota size in MB */ public function getDiskQuotaMB() { $size = $this->getField('DiskQuotaMB'); if(empty($size)) { $defaults = $this->config()->get('defaults'); $size = (isset($defaults['DiskQuotaMB'])) ? $defaults['DiskQuotaMB'] : 0; } return $size; } /** * Has the disk quota been exceeded? * @return boolean */ public function HasExceededDiskQuota() { return $this->getUsedQuotaMB(0) >= $this->getDiskQuotaMB(); } /** * Is there a disk quota set for this project? * @return boolean */ public function HasDiskQuota() { return $this->getDiskQuotaMB() > 0; } /** * Get the menu to be shown on projects * * @return ArrayList */ public function Menu() { $list = new ArrayList(); $list->push(new ArrayData(array( 'Link' => sprintf('naut/project/%s', $this->Name), 'Title' => 'Deploy', 'IsActive' => Controller::curr()->getAction() == 'project' ))); if(DNRoot::FlagSnapshotsEnabled()) { $list->push(new ArrayData(array( 'Link' => sprintf('naut/project/%s/snapshots', $this->Name), 'Title' => 'Snapshots', 'IsActive' => Controller::curr()->getAction() == 'snapshots' ))); } $this->extend('updateMenu', $list); return $list; } /** * Restrict access to viewing this project * * @param Member $member * @return boolean */ public function canView($member = null) { if(!$member) $member = Member::currentUser(); if(Permission::checkMember($member, 'ADMIN')) return true; return $member->inGroups($this->Viewers()); } public function canRestore($member = null) { return (bool)$this->Environments()->filterByCallback(function($env) use($member) { return $env->canRestore($member); })->Count(); } public function canBackup($member = null) { return (bool)$this->Environments()->filterByCallback(function($env) use($member) { return $env->canBackup($member); })->Count(); } public function canUploadArchive($member = null) { return (bool)$this->Environments()->filterByCallback(function($env) use($member) { return $env->canUploadArchive($member); })->Count(); } public function canDownloadArchive($member = null) { return (bool)$this->Environments()->filterByCallback(function($env) use($member) { return $env->canDownloadArchive($member); })->Count(); } public function DataArchives() { $envIds = $this->Environments()->column('ID'); return DNDataArchive::get()->filter('EnvironmentID', $envIds); } /** * Return all archives which are "manual upload requests", * meaning they don't have a file attached to them (yet). * * @return ArrayList */ public function PendingManualUploadDataArchives() { return $this->DataArchives()->filter('ArchiveFileID', null); } /** * Build an environment variable array to be used with this project. * * This is relevant if every project needs to use an individual SSH pubkey. * * Include this with all Gitonomy\Git\Repository, and * \Symfony\Component\Process\Processes. * * @return array */ public function getProcessEnv() { if (file_exists($this->getPrivateKeyPath())) { // Key-pair is available, use it. $processEnv = array( 'IDENT_KEY' => $this->getPrivateKeyPath(), 'GIT_SSH' => BASE_PATH . "/deploynaut/git-deploy.sh" ); } else { $processEnv = array(); } $this->extend('updateProcessEnv', $processEnv); return $processEnv; } /** * Get a string of people allowed to view this project * * @return string */ public function getViewersList() { return implode(", ", $this->Viewers()->column("Title")); } /** * * @return DNData */ public function DNData() { return DNData::inst(); } /** * Provides a DNBuildList of builds found in this project. */ public function DNBuildList() { return DNReferenceList::create($this, $this->DNData()); } /** * Provides a list of the branches in this project. */ public function DNBranchList() { if($this->CVSPath && !$this->repoExists()) { $this->cloneRepo(); } return DNBranchList::create($this, $this->DNData()); } /** * Provides a list of the tags in this project. */ public function DNTagList() { if($this->CVSPath && !$this->repoExists()) { $this->cloneRepo(); } return DNReferenceList::create($this, $this->DNData(), null, null, true); } /** * * @return Gitonomy\Git\Repository */ public function getRepository() { if(!$this->repoExists()) { return false; } return new Gitonomy\Git\Repository($this->LocalCVSPath); } /** * Provides a list of environments found in this project. * CAUTION: filterByCallback will change this into an ArrayList! * * @return ArrayList */ public function DNEnvironmentList() { return $this->Environments() ->filterByCallBack(function($item) { return $item->canView(); }); } /** * Returns a map of envrionment name to build name */ public function currentBuilds() { if(!isset(self::$relation_cache['currentBuilds.'.$this->ID])) { $currentBuilds = array(); foreach($this->Environments() as $env) { $currentBuilds[$env->Name] = $env->CurrentBuild(); } self::$relation_cache['currentBuilds.'.$this->ID] = $currentBuilds; } return self::$relation_cache['currentBuilds.'.$this->ID]; } /** * * @return string */ public function Link($action='') { return Controller::join_links("naut", "project", $this->Name, $action); } /** * * @param string $action * @return string */ public function APILink($action) { return Controller::join_links("naut", "api", $this->Name, $action); } /** * * @return FieldList */ public function getCMSFields() { $fields = parent::getCMSFields(); $environments = $fields->dataFieldByName("Environments"); $fields->fieldByName("Root")->removeByName("Viewers"); $fields->fieldByName("Root")->removeByName("Environments"); $fields->fieldByName("Root")->removeByName("LocalCVSPath"); $fields->dataFieldByName('DiskQuotaMB')->setDescription('This is the maximum amount of disk space (in megabytes) that all environments within this project can use for stored snapshots'); $fields->fieldByName('Root.Main.Name') ->setTitle('Project name') ->setDescription('Changing the name will <strong>reset</strong> the deploy configuration and avoid using non alphanumeric characters'); $fields->fieldByName('Root.Main.CVSPath') ->setTitle('Git repository') ->setDescription('E.g. git@github.com:silverstripe/silverstripe-installer.git'); $workspaceField = new ReadonlyField('LocalWorkspace', 'Git workspace', $this->getLocalCVSPath()); $workspaceField->setDescription('This is where the GIT repository are located on this server'); $fields->insertAfter($workspaceField, 'CVSPath'); $readAccessGroups = ListboxField::create('Viewers', 'Project viewers', Group::get()->map()->toArray()) ->setMultiple(true) ->setDescription('These groups can view the project in the front-end.'); $fields->addFieldToTab("Root.Main", $readAccessGroups); $this->setCreateProjectFolderField($fields); $this->setEnvironmentFields($fields, $environments); return $fields; } /** * If there isn't a capistrano env project folder, show options to create one * * @param FieldList $fields */ public function setCreateProjectFolderField(&$fields) { // Check if the capistrano project folder exists if(!$this->Name) { return; } if($this->projectFolderExists()) { return; } $createFolderNotice = new LabelField('CreateEnvFolderNotice', 'Warning: No Capistrano project folder exists'); $createFolderNotice->addExtraClass('message warning'); $fields->insertBefore($createFolderNotice, 'Name'); $createFolderField = new CheckboxField('CreateEnvFolder', 'Create folder'); $createFolderField->setDescription('Would you like to create the capistrano project folder?'); $fields->insertAfter($createFolderField, 'CreateEnvFolderNotice'); } /** * * @return boolean */ public function projectFolderExists() { if(file_exists($this->DNData()->getEnvironmentDir().'/'.$this->Name)) { return true; } return false; } /** * * @return bool */ public function repoExists() { return file_exists(DEPLOYNAUT_LOCAL_VCS_PATH . '/' . $this->Name.'/HEAD'); } /** * Setup a asyncronous resque job to clone a git repository * */ public function cloneRepo() { Resque::enqueue('git', 'CloneGitRepo', array( 'repo' => $this->CVSPath, 'path' => $this->getLocalCVSPath(), 'env' => $this->getProcessEnv() )); } /** * * @return string */ public function getLocalCVSPath() { return DEPLOYNAUT_LOCAL_VCS_PATH . '/' . $this->Name; } /** * Checks for missing folders folder and schedules a git clone if the necessary * */ public function onBeforeWrite() { parent::onBeforeWrite(); $this->checkProjectPath(); $this->checkCVSPath(); } /** * Ensure the path for this project has been created */ protected function checkProjectPath() { // Create the project capistrano folder if($this->CreateEnvFolder && !file_exists($this->getProjectFolderPath())) { mkdir($this->DNData()->getEnvironmentDir().'/'.$this->Name); } } /** * Check if the CVSPath has been changed, and if so, ensure the repository has been updated */ protected function checkCVSPath() { $changedFields = $this->getChangedFields(true, 2); if (!$this->CVSPath) { return; } if (isset($changedFields['CVSPath']) || isset($changedFields['Name'])) { $this->cloneRepo(); } } /** * Delete related environments and folders */ public function onAfterDelete() { parent::onAfterDelete(); // Delete related environments foreach($this->Environments() as $env) { $env->delete(); } if(!file_exists($this->getProjectFolderPath())) { return; } // Create a basic new environment config from a template if(Config::inst()->get('DNEnvironment', 'allow_web_editing')) { FileSystem::removeFolder($this->getProjectFolderPath()); } } /** * Fetch the public key for this project. */ public function getPublicKey() { $key = $this->getPublicKeyPath(); if (file_exists($key)) { return file_get_contents($key); } } /** * This returns that path of the public key if a key directory is set. It doesn't check whether the file exists. * * @return string|null */ public function getPublicKeyPath() { if($privateKey = $this->getPrivateKeyPath()) { return $privateKey . '.pub'; } return null; } /** * This returns that path of the private key if a key directory is set. It doesn't check whether the file exists. * * @return string|null */ public function getPrivateKeyPath() { if($keyDir = $this->getKeyDir()) { $filter = FileNameFilter::create(); $name = $filter->filter($this->Name); return $this->getKeyDir() . '/' . $name; } return null; } /** * Returns the location of the projects key dir if one exists. * * @return string|null */ public function getKeyDir() { $keyDir = $this->DNData()->getKeyDir(); if(!$keyDir) return null; $filter = FileNameFilter::create(); $name = $filter->filter($this->Name); return $this->DNData()->getKeyDir() . '/' . $name; } /** * Setup a gridfield for the environment configs * * @param FieldList $fields * @param $environments * @return void */ protected function setEnvironmentFields(&$fields, $environments) { if(!$environments) { return; } $environments->getConfig()->addComponent(new GridFieldAddNewMultiClass()); $environments->getConfig()->removeComponentsByType('GridFieldAddNewButton'); $environments->getConfig()->removeComponentsByType('GridFieldAddExistingAutocompleter'); $environments->getConfig()->removeComponentsByType('GridFieldDeleteAction'); $environments->getConfig()->removeComponentsByType('GridFieldPageCount'); if(Config::inst()->get('DNEnvironment', 'allow_web_editing')) { $addNewRelease = new GridFieldAddNewButton('toolbar-header-right'); $addNewRelease->setButtonName('Add'); $environments->getConfig()->addComponent($addNewRelease); } $fields->addFieldToTab("Root.Main", $environments); } /** * Provide current repository URL to the users. */ public function getRepositoryURL() { $showUrl = Config::inst()->get($this->class, 'show_repository_url'); if ($showUrl) { return $this->CVSPath; } } /** * * @return string */ protected function getProjectFolderPath() { return $this->DNData()->getEnvironmentDir().'/'.$this->Name; } } |