/** * Invoke `wp` commands on a Pantheon development site * * <commands>... * : The WP-CLI commands you intend to run. * * [--<flag>=<value>] * : Additional WP-CLI flag(s) to pass in to the command. * * [--site=<site>] * : The name (DNS shortname) of your site on Pantheon. * * [--env=<environment>] * : Your Pantheon environment. Default: dev * */ function __invoke($args, $assoc_args) { $command = implode($args, ' '); $this->checkCommand($command); $sites = new Sites(); $site = $sites->get(Input::sitename($assoc_args)); $environment = Input::env(array('args' => $assoc_args, 'site' => $site)); if (!$site) { $this->failure('Command could not be completed. Unknown site specified.'); } /** * See https://github.com/pantheon-systems/titan-mt/blob/master/.. * ..dashboardng/app/workshops/site/models/environment.coffee */ $server = $this->getAppserverInfo(array('site' => $site->get('id'), 'environment' => $environment)); // Sanitize assoc args so we don't try to pass our own flags. unset($assoc_args['site']); if (isset($assoc_args['env'])) { unset($assoc_args['env']); } // Create user-friendly output $flags = ''; foreach ($assoc_args as $k => $v) { if (isset($v) && (string) $v != '') { $flags .= "--{$k}=" . escapeshellarg($v) . ' '; } else { $flags .= "--{$k} "; } } $this->log()->info('Running wp {cmd} {flags} on {site}-{env}', array('cmd' => $command, 'flags' => $flags, 'site' => $site->get('name'), 'env' => $environment)); $result = $this->sendCommand($server, 'wp', $args, $assoc_args); if (Terminus::getConfig('format') != 'normal') { $this->output()->outputRecordList($result); } }
/** * List an organization's sites * * ## OPTIONS * * [--org=<id>] * : Organization id * * [--tag=<tag>] * : Tag name to filter sites list by * * [--add=<site>] * : Site to add to organization * * [--remove=<site>] * : Site to remove from organization * * @subcommand sites * */ public function sites($args, $assoc_args) { $org_id = Input::orgid($assoc_args, 'org', null, array('allow_none' => false)); $org = new Organization($org_id); if (isset($assoc_args['add'])) { $add = SiteFactory::instance(Input::sitename($assoc_args, 'add')); Terminus::confirm("Are you sure you want to add %s to %s ?", $assoc_args, array($add->getName(), $org->name)); $org->addSite($add); Terminus::success("Added site!"); return true; } if (isset($assoc_args['remove'])) { $remove = SiteFactory::instance(Input::sitename($assoc_args, 'remove')); Terminus::confirm("Are you sure you want to remove %s to %s ?", $assoc_args, array($remove->getName(), $org->name)); $org->removeSite($remove); Terminus::success("Removed site!"); return true; } $org->siteMemberships->fetch(); $memberships = $org->siteMemberships->all(); foreach ($memberships as $membership) { if (isset($assoc_args['tag']) && !in_array($assoc_args['tag'], $membership->get('tags'))) { continue; } $site = $membership->get('site'); $data[] = array('name' => $site->name, 'id' => $site->id, 'service_level' => $site->service_level, 'framework' => $site->framework, 'created' => date('Y-m-d H:i:s', $site->created), 'tags' => $membership->get('tags')); } $this->handleDisplay($data); }
/** * Log in as a user * * ## OPTIONS * [<email>] * : Email address to log in as. * * [--password=<value>] * : Log in non-interactively with this password. Useful for automation. * * [--machine-token=<value>] * : Authenticate using an Auth0 token * * [--session=<value>] * : Authenticate using an existing session token * [--debug] * : dump call information when logging in. */ public function login($args, $assoc_args) { // Try to login using a machine token, if provided. if (isset($assoc_args['machine-token']) || empty($args) && isset($_SERVER['TERMINUS_MACHINE_TOKEN'])) { if (isset($assoc_args['machine-token'])) { $token = $assoc_args['machine-token']; } elseif (isset($_SERVER['TERMINUS_MACHINE_TOKEN'])) { $token = $_SERVER['TERMINUS_MACHINE_TOKEN']; } $this->auth->logInViaMachineToken($token); } elseif (isset($assoc_args['session'])) { $this->auth->logInViaSessionToken($assoc_args['session']); } else { // Otherwise, do a normal email/password-based login. if (empty($args)) { if (isset($_SERVER['TERMINUS_USER'])) { $email = $_SERVER['TERMINUS_USER']; } else { $email = Input::prompt(array('message' => 'Your email address?')); } } else { $email = $args[0]; } if (isset($assoc_args['password'])) { $password = $assoc_args['password']; } else { $password = Input::promptSecret(array('message' => 'Your dashboard password (input will not be shown)')); } $this->auth->logInViaUsernameAndPassword($email, $password); } $this->log()->debug(get_defined_vars()); Terminus::launchSelf('art', array('fist')); }
/** * List an organization's sites * * ## OPTIONS * * <add|remove|list> * : subfunction to run * * [--org=<id|name>] * : Organization UUID or name * * [--tag=<tag>] * : Tag name to filter sites list by * * [--site=<site>] * : Site to add to or remove from organization * * @subcommand sites * */ public function sites($args, $assoc_args) { $action = array_shift($args); $org_id = Input::orgid($assoc_args, 'org', null, array('allow_none' => false)); $orgs = new UserOrganizationMemberships(); $org = $orgs->get($org_id); $org_info = $org->get('organization'); $memberships = $org->site_memberships->all(); switch ($action) { case 'add': if (isset($assoc_args['site'])) { if ($this->siteIsMember($memberships, $assoc_args['site'])) { $this->failure('{site} is already a member of {org}', array('site' => $assoc_args['site'], 'org' => $org_info->profile->name)); } else { $site = $this->sites->get($assoc_args['site']); } } else { $site = $this->sites->get(Input::menu($this->getNonmemberSiteList($memberships), null, 'Choose site')); } Terminus::confirm('Are you sure you want to add %s to %s ?', array($site->get('name'), $org_info->profile->name)); $workflow = $org->site_memberships->addMember($site); $workflow->wait(); $this->workflowOutput($workflow); break; case 'remove': if (isset($assoc_args['site'])) { if (!$this->siteIsMember($memberships, $assoc_args['site'])) { $this->failure('{site} is not a member of {org}', array('site' => $assoc_args['site'], 'org' => $org_info->profile->name)); } else { $site = $this->sites->get($assoc_args['site']); } } else { $site = $this->sites->get(Input::menu($this->getMemberSiteList($memberships), null, 'Choose site')); } $member = $org->site_memberships->get($site->get('id')); Terminus::confirm('Are you sure you want to remove %s from %s ?', array($site->get('name'), $org_info->profile->name)); $workflow = $member->removeMember(); $workflow->wait(); $this->workflowOutput($workflow); break; case 'list': default: foreach ($memberships as $membership) { if (isset($assoc_args['tag']) && !in_array($assoc_args['tag'], $membership->get('tags'))) { continue; } $site = $membership->get('site'); $data_array = array('name' => null, 'id' => null, 'service_level' => null, 'framework' => null, 'created' => null, 'tags' => $membership->get('tags')); foreach ($data_array as $key => $value) { if ($value == null && isset($site->{$key})) { $data_array[$key] = $site->{$key}; } } $data_array['created'] = date('Y-m-dTH:i:s', $data_array['created']); $data[] = $data_array; } $this->output()->outputRecordList($data); break; } }
/** * List an organizations sites * * ## OPTIONS * * [--org=<org>] * : Organization name or Id * * [--add=<site>] * : Site to add to organization * * [--remove=<site>] * : Site to remove from organization * * @subcommand sites * */ public function sites($args, $assoc_args) { $orgs = array(); $user = new User(); foreach ($user->organizations() as $id => $org) { $orgs[$id] = $org->name; } if (!isset($assoc_args['org']) or empty($assoc_args['org'])) { $selected_org = Terminus::menu($orgs, false, "Choose an organization"); } else { $selected_org = $assoc_args['org']; } $org = new Organization($selected_org); if (isset($assoc_args['add'])) { $add = SiteFactory::instance(Input::site($assoc_args, 'add')); Terminus::confirm("Are you sure you want to add %s to %s ?", $assoc_args, array($add->getName(), $org->name)); $org->addSite($add); Terminus::success("Added site!"); return true; } if (isset($assoc_args['remove'])) { $remove = SiteFactory::instance(Input::site($assoc_args, 'remove')); Terminus::confirm("Are you sure you want to remove %s to %s ?", $assoc_args, array($remove->getName(), $org->name)); $org->removeSite($remove); Terminus::success("Removed site!"); return true; } $sites = $org->getSites(); $data = array(); foreach ($sites as $site) { $data[] = array('name' => $site->site->name, 'service level' => isset($site->site->service_level) ? $site->site->service_level : '', 'framework' => isset($site->site->framework) ? $site->site->framework : '', 'created' => date('Y-m-d H:i:s', $site->site->created)); } $this->handleDisplay($data); }
/** * Invoke `drush` commands on a Pantheon development site * * <commands>... * : The Drush commands you intend to run. * * [--<flag>=<value>] * : Additional Drush flag(s) to pass in to the command. * * [--site=<site>] * : The name (DNS shortname) of your site on Pantheon. * * [--env=<environment>] * : Your Pantheon environment. Default: dev * */ function __invoke($args, $assoc_args) { $environment = Input::env($assoc_args); $sites = new Sites(); $site = $sites->get(Input::sitename($assoc_args)); if (!$site) { throw new TerminusException("Command could not be completed. Unknown site specified."); } $server = array('user' => "{$environment}.{$site->get('id')}", 'host' => "appserver.{$environment}.{$site->get('id')}.drush.in", 'port' => '2222'); if (strpos(TERMINUS_HOST, 'onebox') !== FALSE) { $server['user'] = "******"; $server['host'] = TERMINUS_HOST; } # Sanitize assoc args so we don't try to pass our own flags. # TODO: DRY this out? unset($assoc_args['site']); if (isset($assoc_args['env'])) { unset($assoc_args['env']); } # Create user-friendly output $command = implode($args, ' '); $flags = ''; foreach ($assoc_args as $k => $v) { if (isset($v) && (string) $v != '') { $flags .= "--{$k}={$v} "; } else { $flags .= "--{$k} "; } } $this->log()->info("Running drush {cmd} {flags} on {site}-{env}", array('cmd' => $command, 'flags' => $flags, 'site' => $site->get('name'), 'env' => $environment)); $this->send_command($server, 'drush', $args, $assoc_args); }
/** * Invoke `wp` commands on a Pantheon development site * * <commands>... * : The WP-CLI commands you intend to run. * * [--<flag>=<value>] * : Additional WP-CLI flag(s) to pass in to the command. * * [--site=<site>] * : The name (DNS shortname) of your site on Pantheon. * * [--env=<environment>] * : Your Pantheon environment. Default: dev * */ function __invoke($args, $assoc_args) { $environment = Input::env($assoc_args); $sites = new Sites(); $site = $sites->get(Input::sitename($assoc_args)); if (!$site) { throw new TerminusException("Command could not be completed. Unknown site specified."); } # see https://github.com/pantheon-systems/titan-mt/blob/master/dashboardng/app/workshops/site/models/environment.coffee $server = array('user' => "{$environment}.{$site->get('id')}", 'host' => "appserver.{$environment}.{$site->get('id')}.drush.in", 'port' => '2222'); if (strpos(TERMINUS_HOST, 'onebox') !== FALSE) { $server['user'] = "******"; $server['host'] = TERMINUS_HOST; } # Sanitize assoc args so we don't try to pass our own flags. # TODO: DRY this out? unset($assoc_args['site']); if (isset($assoc_args['env'])) { unset($assoc_args['env']); } # Create user-friendly output $command = implode($args, ' '); $flags = ''; foreach ($assoc_args as $k => $v) { if (isset($v) && (string) $v != '') { $flags .= "--{$k}=" . escapeshellarg($v) . ' '; } else { $flags .= "--{$k} "; } } $this->log()->info("Running wp {cmd} {flags} on {site}-{env}", array('cmd' => $command, 'flags' => $flags, 'site' => $site->get('name'), 'env' => $environment)); $this->send_command($server, 'wp', $args, $assoc_args); }
/** * Delete a machine token from your account * * ## OPTIONS * [--machine-token-id=<id>] * : UUID or name of the site you want to delete * * [--force] * : to skip the confirmations */ public function delete($args, $assoc_args) { $user = Session::getUser(); $id = $assoc_args['machine-token-id']; if (empty($id)) { $this->failure('You must specify a machine token id to delete.'); } // Find the token $machine_token = $user->machine_tokens->get($assoc_args['machine-token-id']); if (empty($machine_token)) { $this->failure('There are no machine tokens with the id {id}.', array('id' => $id)); } $name = $machine_token->get('device_name'); if (!isset($assoc_args['force']) && !Terminus::getConfig('yes')) { //If the force option isn't used, we'll ask you some annoying questions Input::confirm(array('message' => 'Are you sure you want to delete %s?', 'context' => $name)); } $this->log()->info('Deleting {name} ...', array('name' => $name)); $response = $machine_token->delete(); if ($response['status_code'] == 200) { $this->log()->info('Deleted {name}!', array('name' => $name)); } else { $this->failure('There was an problem deleting the machine token.'); } }
/** * Get help on a certain command. * * [<commands>...] * : The command you want information on * * [--recursive] * : Display full information on all subcommands and their subcommands * * ## EXAMPLES * * # get help for `sites` command * terminus help sites * * # get help for `sites` command and all its subcommands * terminus help sites --recursive * * # get help for `sites list` subcommand * terminus help sites list */ public function __invoke($args, $assoc_args) { $this->recursive = Input::optional(array('key' => 'recursive', 'choices' => $assoc_args, 'default' => false)); $command = $this->findSubcommand($args); if ($command) { $status = $this->showHelp($command); exit($status); } $this->failure('"{cmd}" is not a registered command.', array('cmd' => $args[0])); }
/** * Retrieves the model of the given ID * * @param [string] $id ID or name of desired organization * @return [UserOrganizationMembership] $model */ public function get($id) { $orgs = $this->getMembers(); $orglist = \Terminus\Helpers\Input::orglist(); $model = null; if (isset($orgs[$id])) { $model = $this->models[$id]; } elseif (($location = array_search($id, $orglist)) !== false) { $model = $this->models[$location]; } return $model; }
public static function orgid($args, $key, $default = null) { $orglist = Input::orglist(); $flip = array_flip($orglist); if (isset($args[$key]) and array_key_exists($args[$key], $flip)) { // if we have a valid name provided and we need the id return $flip[$args[$key]]; } elseif (isset($args[$key]) and array_key_exists($args[$key], $orglist)) { return $args[$key]; } $orglist = Input::orglist(); $org = \Terminus::menu($orglist, false, "Choose organization"); if ('-' === $org) { return $default; } return $org; }
/** * Show operation details for a workflow * * ## OPTIONS * [--workflow_id] * : Uuid of workflow to show * [--site=<site>] * : Site from which to list workflows * * @subcommand show */ public function show($args, $assoc_args) { $site = $this->sites->get(Input::sitename($assoc_args)); $workflow = Input::workflow($site, $assoc_args, 'workflow_id'); $workflow_data = $workflow->serialize(); if (Terminus::getConfig('format') == 'normal') { $operations_data = $workflow_data['operations']; unset($workflow_data['operations']); $this->output()->outputRecord($workflow_data); if (count($operations_data)) { $this->log()->info('Workflow operations:'); $this->output()->outputRecordList($operations_data); } else { $this->log()->info('Workflow has no operations'); } } else { $this->output()->outputRecordList($workflow_data); } }
/** * Invoke `wp` commands on a Pantheon development site * * <commands>... * : The WP-CLI command you intend to run with its arguments, in quotes * * [--site=<site>] * : The name (DNS shortname) of your site on Pantheon * * [--env=<environment>] * : Your Pantheon environment. Default: dev * */ public function __invoke($args, $assoc_args) { $command = array_pop($args); $this->checkCommand($command); $sites = new Sites(); $site = $sites->get(Input::sitename($assoc_args)); $environment = Input::env(array('args' => $assoc_args, 'site' => $site)); if (!$site) { $this->failure('Command could not be completed. Unknown site specified.'); } /** * See https://github.com/pantheon-systems/titan-mt/blob/master/.. * ..dashboardng/app/workshops/site/models/environment.coffee */ $server = $this->getAppserverInfo(array('site' => $site->get('id'), 'environment' => $environment)); $this->log()->info('Running wp {cmd} on {site}-{env}', array('cmd' => $command, 'site' => $site->get('name'), 'env' => $environment)); $result = $this->sendCommand(array('server' => $server, 'remote_exec' => 'wp', 'command' => $command)); if (Terminus::getConfig('format') != 'normal') { $this->output()->outputRecordList($result); } }
/** * Invoke `drush` commands on a Pantheon development site * * <commands>... * : The WP-CLI command you intend to run, with its arguments * * [--site=<site>] * : The name (DNS shortname) of your site on Pantheon * * [--env=<environment>] * : Your Pantheon environment. Default: dev * */ public function __invoke($args, $assoc_args) { $command = array_pop($args); $this->checkCommand($command); $sites = new Sites(); $assoc_args['site'] = Input::sitename($assoc_args); $site = $sites->get($assoc_args['site']); if (!$site) { $this->failure('Command could not be completed. Unknown site specified.'); } $assoc_args['env'] = $environment = Input::env(array('args' => $assoc_args, 'site' => $site)); $server = $this->getAppserverInfo(array('site' => $site->get('id'), 'environment' => $environment)); if (in_array(Terminus::getConfig('format'), array('bash', 'json', 'silent'))) { $assoc_args['pipe'] = 1; } $this->log()->info("Running drush {cmd} on {site}-{env}", array('cmd' => $command, 'site' => $site->get('name'), 'env' => $environment)); $result = $this->sendCommand(array('server' => $server, 'remote_exec' => 'drush', 'command' => $command)); if (Terminus::getConfig('format') != 'normal') { $this->output()->outputRecordList($result); } }
/** * List Worflows for a Site * * ## OPTIONS * [--site=<site>] * : Site from which to list workflows * * @subcommand list */ public function index($args, $assoc_args) { $site = $this->sites->get(Input::sitename($assoc_args)); $workflows = $site->workflows->all(); $data = array(); foreach ($workflows as $workflow) { $user = '******'; if (isset($workflow->get('user')->email)) { $user = $workflow->get('user')->email; } if ($workflow->get('total_time')) { $elapsed_time = $workflow->get('total_time'); } else { $elapsed_time = time() - $workflow->get('created_at'); } $data[] = array('id' => $workflow->id, 'env' => $workflow->get('environment'), 'workflow' => $workflow->get('description'), 'user' => $user, 'status' => $workflow->get('phase'), 'time' => sprintf("%ds", $elapsed_time)); } if (count($data) == 0) { $this->log()->warning('No workflows have been run on {site}', array('site' => $site->get('name'))); } $this->output()->outputRecordList($data, array('update' => 'Last update')); }
/** * List an organization's sites * * ## OPTIONS * * [--org=<id|name>] * : Organization UUID or name * * [--tag=<tag>] * : Tag name to filter sites list by * * [--add=<site>] * : Site to add to organization * * [--remove=<site>] * : Site to remove from organization * * @subcommand sites * */ public function sites($args, $assoc_args) { $org_id = Input::orgid($assoc_args, 'org', null, array('allow_none' => false)); $orgs = new UserOrganizationMemberships(); $org = $orgs->get($org_id); $memberships = $org->site_memberships; if (isset($assoc_args['add'])) { $site = $this->sites->get(Input::sitename($assoc_args, 'add')); Terminus::confirm('Are you sure you want to add %s to %s ?', $assoc_args, array($site->get('name'), $org->get('name'))); $memberships->addMember($site); Terminus::success('Added site!'); return true; } if (isset($assoc_args['remove'])) { $site_id = $assoc_args['remove']; $member = $memberships->get($assoc_args['remove']); $site = $member->get('site'); Terminus::confirm('Are you sure you want to remove %s from %s ?', $assoc_args, array($site->name, $org->get('name'))); $member->removeMember(); Terminus::success('Removed site!'); return true; } $memberships = $org->getSites(); foreach ($memberships as $membership) { if (isset($assoc_args['tag']) && !in_array($assoc_args['tag'], $membership->get('tags'))) { continue; } $site = $membership->get('site'); $data_array = array('name' => null, 'id' => null, 'service_level' => null, 'framework' => null, 'created' => null, 'tags' => $membership->get('tags')); foreach ($data_array as $key => $value) { if ($value == null && isset($site->{$key})) { $data_array[$key] = $site->{$key}; } } $data_array['created'] = date('Y-m-dTH:i:s', $data_array['created']); $data[] = $data_array; } $this->outputter->outputRecordList($data); }
/** * @vcr input_helper_org_helpers */ function testOrgHelpers() { $orglist = Input::orglist(); $this->assertInternalType('array', $orglist); $this->assertArrayHasKey('-', $orglist); $this->assertArrayHasKey('d59379eb-0c23-429c-a7bc-ff51e0a960c2', $orglist); // test normal usage $args = array('org' => 'Terminus Testing'); $org = Input::orgname($args, 'org'); $this->assertEquals('Terminus Testing', $org); // test case where an orgid is sent and a name should be returned $args = array('org' => 'd59379eb-0c23-429c-a7bc-ff51e0a960c2'); $org = Input::orgname($args, 'org'); $this->assertEquals('Terminus Testing', $org); // test case where an orgid is sent and a name should be returned $args = array('org' => 'd59379eb-0c23-429c-a7bc-ff51e0a960c2'); $org = Input::orgid($args, 'org'); $this->assertEquals('d59379eb-0c23-429c-a7bc-ff51e0a960c2', $org); $args = array('org' => 'Terminus Testing'); $org = Input::orgid($args, 'org'); $this->assertEquals('d59379eb-0c23-429c-a7bc-ff51e0a960c2', $org); }
/** * Invoke `drush` commands on a Pantheon development site * * <commands>... * : The Drush commands you intend to run. * * [--<flag>=<value>] * : Additional Drush flag(s) to pass in to the command. * * [--site=<site>] * : The name (DNS shortname) of your site on Pantheon. * * [--env=<environment>] * : Your Pantheon environment. Default: dev * */ public function __invoke($args, $assoc_args) { $command = implode($args, ' '); $this->checkCommand($command); $sites = new Sites(); $assoc_args['site'] = Input::sitename($assoc_args); $site = $sites->get($assoc_args['site']); if (!$site) { $this->failure('Command could not be completed. Unknown site specified.'); } $assoc_args['env'] = $environment = Input::env($assoc_args); $server = $this->getAppserverInfo(array('site' => $site->get('id'), 'environment' => $environment)); # Sanitize assoc args so we don't try to pass our own flags. # TODO: DRY this out? if (isset($assoc_args['site'])) { unset($assoc_args['site']); } if (isset($assoc_args['env'])) { unset($assoc_args['env']); } # Create user-friendly output $flags = ''; foreach ($assoc_args as $k => $v) { if (isset($v) && (string) $v != '') { $flags .= "--{$k}={$v} "; } else { $flags .= "--{$k} "; } } if (in_array(Terminus::getConfig('format'), array('bash', 'json', 'silent'))) { $assoc_args['pipe'] = 1; } $this->log()->info("Running drush {cmd} {flags} on {site}-{env}", array('cmd' => $command, 'flags' => $flags, 'site' => $site->get('name'), 'env' => $environment)); $result = $this->sendCommand($server, 'drush', $args, $assoc_args); if (Terminus::getConfig('format') != 'normal') { $this->output()->outputRecordList($result); } }
/** * Displays an environment's regular backup schedule * * @params [array] $assoc_args Parameters and flags from the command line * @return [void] */ private function showBackupSchedule($assoc_args) { $site = $this->sites->get(Input::sitename($assoc_args)); $env = $site->environments->get(Input::env(array('args' => $assoc_args, 'choices' => array('dev', 'live')))); $schedule = $env->backups->getBackupSchedule(); if (is_null($schedule['daily_backup_hour'])) { $this->log()->info('Backups are not currently scheduled to be run.'); } else { $this->output()->outputRecord($schedule); } }
/** * Update alls dev sites with an available upstream update. * * ## OPTIONS * * [--report] * : If set output will contain list of sites and whether they are up-to-date * * [--upstream=<upstream>] * : Specify a specific upstream to check for updating. * * [--no-updatedb] * : Use flag to skip running update.php after the update has applied * * [--xoption=<theirs|ours>] * : Corresponds to git's -X option, set to 'theirs' by default -- https://www.kernel.org/pub/software/scm/git/docs/git-merge.html * * @subcommand mass-update */ public function mass_update($args, $assoc_args) { $sites = SiteFactory::instance(); $env = 'dev'; $upstream = Input::optional('upstream', $assoc_args, false); $data = array(); $report = Input::optional('report', $assoc_args, false); $confirm = Input::optional('confirm', $assoc_args, false); // Start status messages. if ($upstream) { Terminus::line('Looking for sites using ' . $upstream . '.'); } foreach ($sites as $site) { $updates = $site->getUpstreamUpdates(); if (!isset($updates->behind)) { // No updates, go back to start. continue; } // Check for upstream argument and site upstream URL match. $siteUpstream = $site->info('upstream'); if ($upstream and isset($siteUpstream->url)) { if ($siteUpstream->url != $upstream) { // Uptream doesn't match, go back to start. continue; } } if ($updates->behind > 0) { $data[$site->getName()] = array('site' => $site->getName(), 'status' => "Needs update"); $noupdatedb = Input::optional($assoc_args, 'updatedb', false); $update = $noupdatedb ? false : true; $xoption = Input::optional($assoc_args, 'xoption', 'theirs'); if (!$report) { $confirmed = Input::yesno("Apply upstream updatefs to %s ( run update.php:%s, xoption:%s ) ", array($site->getName(), var_export($update, 1), var_export($xoption, 1))); if (!$confirmed) { continue; } // Suer says No, go back to start. // Backup the DB so the client can restore if something goes wrong. Terminus::line('Backing up ' . $site->getName() . '.'); $backup = $site->environment('dev')->createBackup(array('element' => 'all')); // Only continue if the backup was successful. if ($backup) { Terminus::success("Backup of " . $site->getName() . " created."); Terminus::line('Updating ' . $site->getName() . '.'); // Apply the update, failure here would trigger a guzzle exception so no need to validate success. $response = $site->applyUpstreamUpdates($env, $update, $xoption); $data[$site->getName()]['status'] = 'Updated'; Terminus::success($site->getName() . ' is updated.'); } else { $data[$site->getName()]['status'] = 'Backup failed'; Terminus::error('There was a problem backing up ' . $site->getName() . '. Update aborted.'); } } } else { if (isset($assoc_args['report'])) { $data[$site->getName()] = array('site' => $site->getName(), 'status' => "Up to date"); } } } if (!empty($data)) { sort($data); $this->handleDisplay($data); } else { Terminus::line('No sites in need up updating.'); } }
/** * Complete wipe and reset a site * * ## OPTIONS * * [--site=<site>] * : Site to use * * [--env=<env>] * : Specify environment, default = dev */ public function wipe($args, $assoc_args) { try { $env = @$assoc_args['env'] ?: 'dev'; $site = SiteFactory::instance(Input::site($assoc_args)); $site_id = $site->getId(); $env = Input::env($assoc_args, 'env'); Terminus::line("Wiping %s %s", array($site_id, $env)); $resp = $site->environment($env)->wipe(); if ($resp) { $this->waitOnWorkflow('sites', $site_id, $resp['data']->id); Terminus::success("Successfully wiped %s -- %s", array($site->getName(), $env)); } } catch (Exception $e) { Terminus::error("%s", array($e->getMessage())); } }
/** * Complete wipe and reset a site * * ## OPTIONS * * [--site=<site>] * : Site to use * * [--env=<env>] * : Environment to be wiped */ public function wipe($args, $assoc_args) { $site = $this->sites->get(Input::sitename($assoc_args)); $env = $site->environments->get(Input::env($assoc_args, 'env')); Terminus::confirm('Are you sure you want to wipe {site}-{env}?', array('site' => $site->get('name'), 'env' => $env->get('id'))); $workflow = $env->wipe(); $workflow->wait(); $this->log()->info('Successfully wiped {site}-{env}', array('site' => $site->get('name'), 'env' => $env->get('id'))); }
/** * Streams new and finished workflows to the console * * ## OPTIONS * [--site=<site>] * : Site from which to list workflows * * @subcommand watch */ public function watch($args, $assoc_args) { $site = $this->sites->get(Input::siteName(array('args' => $assoc_args))); // Keep track of workflows that have been printed. // This is necessary because the local clock may drift from // the server's clock, causing events to be printed twice. $started = array(); $finished = array(); $this->logger->info('Watching workflows...'); $site->workflows->fetchWithOperations(); while (true) { $last_created_at = $site->workflows->lastCreatedAt(); $last_finished_at = $site->workflows->lastFinishedAt(); sleep(WORKFLOWS_WATCH_INTERVAL); $site->workflows->fetchWithOperations(); $workflows = $site->workflows->all(); foreach ($workflows as $workflow) { if ($workflow->get('created_at') > $last_created_at && !in_array($workflow->id, $started)) { array_push($started, $workflow->id); $started_message = sprintf("Started %s %s (%s)", $workflow->id, $workflow->get('description'), $workflow->get('environment')); $this->logger->info($started_message); } if ($workflow->get('finished_at') > $last_finished_at && !in_array($workflow->id, $finished)) { array_push($finished, $workflow->id); $finished_message = sprintf("Finished Workflow %s %s (%s)", $workflow->id, $workflow->get('description'), $workflow->get('environment')); $this->logger->info($finished_message); if ($workflow->get('has_operation_log_output')) { $workflow->fetchWithLogs(); $operations = $workflow->operations(); foreach ($operations as $operation) { if ($operation->has('log_output')) { $log_msg = sprintf("\n------ %s (%s) ------\n%s", $operation->description(), $operation->get('environment'), $operation->get('log_output')); $this->log()->info($log_msg); } } } } } } }
/** * Update alls dev sites with an available upstream update. * * ## OPTIONS * * [--report] * : If set output will contain list of sites and whether they are up-to-date * * [--upstream=<upstream>] * : Specify a specific upstream to check for updating. * * [--no-updatedb] * : Use flag to skip running update.php after the update has applied * * [--xoption=<theirs|ours>] * : Corresponds to git's -X option, set to 'theirs' by default -- https://www.kernel.org/pub/software/scm/git/docs/git-merge.html * * [--tag=<tag>] * : Tag to filter by * * [--org=<id>] * : Only necessary if using --tag. Organization which has tagged the site thusly * * [--cached] * : Set to prevent rebuilding of sites cache * * @subcommand mass-update */ public function mass_update($args, $assoc_args) { // Ensure the sitesCache is up to date if (!isset($assoc_args['cached'])) { $this->sites->rebuildCache(); } $upstream = Input::optional('upstream', $assoc_args, false); $data = array(); $report = Input::optional('report', $assoc_args, false); $confirm = Input::optional('confirm', $assoc_args, false); $tag = Input::optional('tag', $assoc_args, false); $org = ''; if ($tag) { $org = Input::orgid($assoc_args, 'org'); } $sites = $this->sites->filterAllByTag($tag, $org); // Start status messages. if ($upstream) { $this->log()->info('Looking for sites using ' . $upstream . '.'); } foreach ($sites as $site) { $site->fetch(); $updates = $site->getUpstreamUpdates(); if (!isset($updates->behind)) { // No updates, go back to start. continue; } // Check for upstream argument and site upstream URL match. $siteUpstream = $site->info('upstream'); if ($upstream and isset($siteUpstream->url)) { if ($siteUpstream->url != $upstream) { // Uptream doesn't match, go back to start. continue; } } if ($updates->behind > 0) { $data[$site->get('name')] = array('site' => $site->get('name'), 'status' => "Needs update"); $env = $site->environments->get('dev'); if ($env->getConnectionMode() == 'sftp') { $this->log()->warning('{site} has available updates, but is in SFTP mode. Switch to Git mode to apply updates.', array('site' => $site->get('name'))); $data[$site->get('name')] = array('site' => $site->get('name'), 'status' => "Needs update - switch to Git mode"); continue; } $updatedb = !Input::optional($assoc_args, 'updatedb', false); $xoption = Input::optional($assoc_args, 'xoption', 'theirs'); if (!$report) { $confirmed = Input::yesno("Apply upstream updates to %s ( run update.php:%s, xoption:%s ) ", array($site->get('name'), var_export($updatedb, 1), var_export($xoption, 1))); if (!$confirmed) { continue; } // User says No, go back to start. // Backup the DB so the client can restore if something goes wrong. $this->log()->info('Backing up ' . $site->get('name') . '.'); $backup = $env->createBackup(array('element' => 'all')); // Only continue if the backup was successful. if ($backup) { $this->log()->info("Backup of " . $site->get('name') . " created."); $this->log()->info('Updating ' . $site->get('name') . '.'); // Apply the update, failure here would trigger a guzzle exception so no need to validate success. $response = $site->applyUpstreamUpdates($env->get('id'), $updatedb, $xoption); $data[$site->get('name')]['status'] = 'Updated'; $this->log()->info($site->get('name') . ' is updated.'); } else { $data[$site->get('name')]['status'] = 'Backup failed'; $this->log()->error('There was a problem backing up ' . $site->get('name') . '. Update aborted.'); } } } else { if (isset($assoc_args['report'])) { $data[$site->get('name')] = array('site' => $site->get('name'), 'status' => "Up to date"); } } } if (!empty($data)) { sort($data); $this->output()->outputRecordList($data); } else { $this->log()->info('No sites in need of updating.'); } }
function testStringReturnsString() { $args = array('args' => array('key' => 'value'), 'key' => 'key', 'default' => false); $string = Input::string($args); $this->assertInternalType('string', $string); $this->assertEquals('value', $string); }
/** * List a site's workflows * * ## OPTIONS * [--site=<site>] * : Site to check * * @subcommand workflows */ public function workflows($args, $assoc_args) { $site = SiteFactory::instance(Input::sitename($assoc_args)); $workflows = $site->getWorkflows(); $data = array(); foreach ($workflows as $workflow) { $user = '******'; if (isset($workflow->get('user')->email)) { $user = $workflow->get('user')->email; } $data[] = array('workflow' => $workflow->get('description'), 'user' => $user, 'status' => $workflow->get('phase'), 'last_update' => date('Y-m-dTH:i:s', $workflow->get('created_at') + $workflow->get('total_time')), 'tasks/complete' => $workflow->get('step') . '/' . $workflow->get('number_of_tasks')); } if (count($data) > 0) { $this->constructTableForResponse($data); } else { Terminus::error('No workflows have been run on ' . $site->getName()); } }
/** * List a site's workflows * * ## OPTIONS * [--site=<site>] * : Site to check * * @subcommand workflows */ public function workflows($args, $assoc_args) { $site = $this->sites->get(Input::sitename($assoc_args)); $workflows = $site->workflows->all(); $data = array(); foreach ($workflows as $workflow) { $user = '******'; if (isset($workflow->get('user')->email)) { $user = $workflow->get('user')->email; } $data[] = array('workflow' => $workflow->get('description'), 'user' => $user, 'status' => $workflow->get('phase'), 'update' => date('Y-m-dTH:i:s', $workflow->get('created_at') + $workflow->get('total_time')), 'tasks' => $workflow->get('number_of_tasks'), 'complete' => $workflow->get('step')); } if (count($data) == 0) { $this->log()->warning('No workflows have been run on {site}', array('site' => $site->getName())); } $this->output()->outputRecordList($data, array('update' => 'Last update')); }
/** * Ensures that the given destination is valid * * @param string $destination Location of directory to ensure viability of * @param bool $make True to create destination if it does not exist * @return string Same as the parameter * @throws TerminusException */ function destinationIsValid($destination, $make = true) { if (file_exists($destination) and !is_dir($destination)) { throw new TerminusException('Destination given is a file. It must be a directory.'); } if (!is_dir($destination)) { if (!$make) { $make = Input::confirm(array('message' => 'Directory does not exists. Create it now?')); } if ($make) { mkdir($destination, 0755); } } return $destination; }
/** * Helper function to get role * * @param [array] $assoc_args Argument array passed from commands * @param [string] $message Prompt to STDOUT * @return [string] $role Name of role */ public static function role($assoc_args, $message = 'Select a role for this member') { $roles = array('developer', 'team_member', 'admin'); if (!isset($assoc_args['role']) || !in_array(strtolower($assoc_args['role']), $roles)) { $role = strtolower($roles[Input::menu($roles, null, 'Select a role for the new member')]); } else { $role = $assoc_args['role']; } return $role; }
/** * List an organization's team members * * ## OPTIONS * * [--org=<id|name>] * : Organization UUID or name * * @subcommand team */ public function team($args, $assoc_args) { $org_id = Input::orgId(array('args' => $assoc_args, 'allow_none' => false)); $orgs = new UserOrganizationMemberships(); $org = $orgs->get($org_id); if (is_null($org)) { $message = 'The organization {org} is either invalid or you haven\'t'; $message .= ' permission sufficient to access its data.'; $this->failure($message, array('org' => $assoc_args['org'])); } $org_info = $org->get('organization'); $org_model = new Organization($org_info); $memberships = $org->user_memberships->all(); $data = array(); foreach ($memberships as $membership) { $member = $membership->get('user'); $first_name = $last_name = null; if (isset($member->profile->firstname)) { $first_name = $member->profile->firstname; } if (isset($member->profile->lastname)) { $last_name = $member->profile->lastname; } $data[$member->id] = array('first' => $first_name, 'last' => $last_name, 'email' => $member->email, 'uuid' => $member->id); } $this->output()->outputRecordList($data); return $data; }