diff --git a/.travis.yml b/.travis.yml index 5a631ab7..d8dad6d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,9 +8,9 @@ env: - LATEST_VERSION=7.3 - DOCKSAL_VERSION=develop matrix: - - VERSION=7.1 - VERSION=7.2 - VERSION=7.3 + - VERSION=7.4 # Skip building master. # Stable image tags are pushed during release tag builds so that stable and release tags match on Docker Hub. @@ -30,7 +30,7 @@ install: script: # Pull the latest edge image to speed up builds (hoping for image layer cache hits) - - docker pull ${REPO}:edge-php${VERSION} + - docker pull ${REPO}:edge-php${VERSION} || true # Build the base image - cd ${TRAVIS_BUILD_DIR}/${VERSION} - travis_retry make # Retry builds, as pecl.php.net tends to time out often diff --git a/7.1/config/.drush/acapi.drush.inc b/7.1/config/.drush/acapi.drush.inc deleted file mode 100644 index b04b8a3c..00000000 --- a/7.1/config/.drush/acapi.drush.inc +++ /dev/null @@ -1,1227 +0,0 @@ - 'Retrieve Drush aliases for all accessible Acquia Cloud sites.', - 'arguments' => array( - ), - 'options' => $options, - ); - - // Most of the commands below use drush_acapi_ac_generic_callback, which - // translates their 'method', 'resource', and 'arguments' properties into - // the appropriate API call, executes it, and displays the result. - - ////////////////////////////////////////////////////////////////////// - // Login - ////////////////////////////////////////////////////////////////////// - $items['ac-api-login'] = array( - 'description' => 'Store Acquia Cloud API credentials and configuration information.', - 'arguments' => array( - ), - 'options' => $options + array( - 'reset' => array( - 'description' => 'Discard any existing stored values from a previous call. Without this option, new values will be merged with existing values.' - ), - ), - ); - - ////////////////////////////////////////////////////////////////////// - // Sites and environments - ////////////////////////////////////////////////////////////////////// - $items['ac-site-list'] = array( - 'description' => 'List all sites available to the current user.', - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-site-info'] = array( - 'description' => 'Show information about a site.', - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-list'] = array( - 'description' => "List a site's environments.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-info'] = array( - 'description' => "Show information about a site environment.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-install'] = array( - 'description' => "Install a Drupal distribution from a pre-selected list, URL, or Drush Makefile.", - 'arguments' => array( - 'type' => 'Type of distro source: distro_url or make_url.', - 'source' => 'A URL to a distro or URL to a Drush make file.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/install/:type', - 'params' => array('source'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-livedev'] = array( - 'description' => "Configure Live Development on a site environment.", - 'arguments' => array( - 'action' => 'Action to take. \'enable\' or \'disable\' live development.', - 'discard' => 'When action is \'disable\', set to 1 to discard uncommitted changes.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/livedev/:action', - 'params' => array('discard'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Servers - ////////////////////////////////////////////////////////////////////// - $items['ac-server-list'] = array( - 'description' => "List servers for a site and environment.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/servers', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-server-info'] = array( - 'description' => "Show information about a server.", - 'arguments' => array( - 'server' => 'Server name.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/servers/:server', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-server-php-procs'] = array( - 'description' => "Calculate the php max procs value based on possible memory limits and apc shm settings.", - 'arguments' => array( - 'server' => 'Server name.', - 'memory_limits' => 'Memory limits.', - 'apc_shm' => 'APC shm settings.' - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/servers/:server/php-procs', - 'params_array' => array( - 'memory_limits', - 'apc_shm', - ), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Databases - ////////////////////////////////////////////////////////////////////// - $items['ac-database-list'] = array( - 'description' => "List a site's databases.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/dbs', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-info'] = array( - 'description' => "Show information about a site database.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/dbs/:db', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-add'] = array( - 'description' => 'Add a database.', - 'arguments' => array( - 'db' => 'The database.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/dbs', - 'body_fields' => array('db'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-delete'] = array( - 'description' => 'Delete a database.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/dbs/:db', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-list'] = array( - 'description' => "List a site environment's database instances.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-info'] = array( - 'description' => "Show information about a site environment's database instance.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-list'] = array( - 'description' => "List a site environment's database instance backups.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-info'] = array( - 'description' => "Show information about a site environment's database instance backup.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backup' => 'Backup id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backup', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-download'] = array( - 'description' => "Download a site environment database instance backup.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backup' => 'Backup id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backup/download', - 'callback' => 'drush_acapi_ac_database_backup_download', - 'options' => array('result-file' => - array( - 'description' => 'Save to a file; specify the full path in which to store the backup. If not provided, the backup is sent the standard output.', - 'example-value' => '/path/to/file', - )) + $options, - ); - $items['ac-database-instance-backup'] = array( - 'description' => 'Create a database instance backup.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-restore'] = array( - 'description' => 'Restore a database instance backup.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backupid' => 'The backup id.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backupid/restore', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-delete'] = array( - 'description' => 'Delete a database instance backup.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backupid' => 'The backup id.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backupid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Tasks - ////////////////////////////////////////////////////////////////////// - // API resource /sites/:realm::site/tasks returns an array of task records, - // unlike other list resources which return a list of ids, so we need a - // non-standard callback. We'd have to make N API calls to implement this the - // way other data types work, which I think shows that the returning a list of - // records is better. - $items['ac-task-list'] = array( - 'description' => "List a site's tasks.", - 'arguments' => array( - ), - 'options' => array( - 'state' => array( - 'description' => 'The task state to retrieve. If not specified, retrieve all tasks for the site.', - 'example-value' => 'done', - ), - 'days' => array( - 'description' => 'The number of days worth of tasks to retrieve. If not specified, retrieve, at a maximum, 7 days worth of tasks.', - 'example-value' => '5', - ), - 'limit' => array( - 'description' => 'The maximum number of tasks to retrieve. If not specified, retrieve a maximum of 50 tasks. The maximum value allowed is 1000.', - 'example-value' => '500' - ), - ) + $options, - ); - - $items['ac-task-info'] = array( - 'description' => "Show information about a site task.", - 'arguments' => array( - 'task' => 'The task id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/tasks/:task', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Workflow - ////////////////////////////////////////////////////////////////////// - $items['ac-code-deploy'] = array( - 'description' => 'Deploy code from one site environment to another.', - 'arguments' => array( - 'target' => 'The target environment.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/code-deploy/:env/:target', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-code-path-deploy'] = array( - 'description' => 'Deploy a specific branch or tag in an environment.', - 'arguments' => array( - 'path' => 'The branch or tag to deploy.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/code-deploy', - 'params' => array('path'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-copy'] = array( - 'description' => 'Copy a database from one site environment to another.', - 'arguments' => array( - 'db' => 'The database.', - 'target' => 'The target environment.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/dbs/:db/db-copy/:env/:target', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-files-copy'] = array( - 'description' => 'Copy user-uploaded files from one site environment to another.', - 'arguments' => array( - 'target' => 'The target environment.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/files-copy/:env/:target', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-move'] = array( - 'description' => 'Move a domain from one site environment to another.', - 'arguments' => array( - 'target' => 'The target environment.', - 'domains' => 'Comma separated list of domains, or * for all.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/domain-move/:env/:target', - 'body_fields_array' => array('domains'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // SSH keys - ////////////////////////////////////////////////////////////////////// - $items['ac-sshkey-list'] = array( - 'description' => "List a site's SSH keys.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/sshkeys', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-sshkey-info'] = array( - 'description' => "Show information about a site SSH key.", - 'arguments' => array( - 'sshkeyid' => 'SSH key id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/sshkeys/:sshkeyid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-sshkey-add'] = array( - 'description' => 'Add an SSH key to a site.', - 'arguments' => array( - 'ssh_pub_key' => 'File containing the SSH public key.', - 'nickname' => 'The SSH key nickname.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/sshkeys', - 'params' => array('nickname'), - 'body_fields_path' => array('ssh_pub_key'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options + array( - 'shell_access' => - array('description' => 'Set shell access for this key. (true or false)', - 'param' => 'shell_access',), - 'vcs_access' => - array('description' => 'Set git access for this key. (true or false)', - 'param' => 'vcs_access',), - 'blacklist' => - array('description' => 'Array containing a list of environments to disallow access for this key', - 'param' => 'blacklist',), - ), - ); - $items['ac-sshkey-delete'] = array( - 'description' => 'Delete an SSH key from a site.', - 'arguments' => array( - 'sshkeyid' => 'The SSH key id to delete.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/sshkeys/:sshkeyid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // SVN users - ////////////////////////////////////////////////////////////////////// - $items['ac-svnuser-list'] = array( - 'description' => "List a site's SVN users.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/svnusers', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-svnuser-info'] = array( - 'description' => "Show information about a site SVN user.", - 'arguments' => array( - 'svnuserid' => 'SVN user id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/svnusers/:svnuserid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-svnuser-add'] = array( - 'description' => 'Add an SVN user to a site.', - 'arguments' => array( - 'username' => 'SVN username.', - 'password' => 'SVN password.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/svnusers/:username', - 'body_fields' => array('password'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-svnuser-delete'] = array( - 'description' => 'Delete an SVN user from a site.', - 'arguments' => array( - 'svnuserid' => 'The SVN user id to delete.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/svnusers/:svnuserid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Domains - ////////////////////////////////////////////////////////////////////// - $items['ac-domain-list'] = array( - 'description' => "List a site's domains.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/domains', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-info'] = array( - 'description' => "Show information about a site domain.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-add'] = array( - 'description' => "Add a domain name to an environment.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-delete'] = array( - 'description' => "Delete a domain name from an environment.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-purge'] = array( - 'description' => "Purge a domain from the Varnish cache.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain/cache', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - foreach ($items as $command => $item) { - $items[$command]['orig_options'] = $item['options']; - if (DRUSH_VERSION < 5) { - // Make our command structure work with previous versions of drush while - // still supporting the way we handle arguments, options, and defaults. - $items[$command]['argument-description'] = $item['arguments']; - foreach ($item['options'] as $option => $info) { - $items[$command]['options'][$option] = $info['description']; - } - } - else { - $items[$command]['handle-remote-commands'] = TRUE; - } - - $items[$command]['bootstrap'] = DRUSH_BOOTSTRAP_DRUSH; - $items[$command]['required-arguments'] = TRUE; - } - - return $items; -} - -function acapi_drush_help($section) { - switch ($section) { - case 'meta:acapi:title': - return dt('Acquia commands'); - case 'meta:acapi:summary': - return dt('Acquia Cloud commands.'); - case 'drush:ac-api-login': - $file = '$HOME/.acquia/cloudapi.conf'; - return dt("Store Acquia Cloud API credentials and endpoint information. - -This command stores default email, key, and optionally endpoint values for future Acquia Cloud API commands in @file. File location can be altered with the ac-config option on all Acquia Cloud commands.", array('@file' => $file)); - } -} - -/** - * Write content to a file if the file does not already have that content. - * Log a message when the file is updated. - * - * @param $path - * The full path to update. - * @param $content - * The content to write. - * @return - * TRUE if the file is updated, FALSE otherwise. - */ -function drush_acapi_update_file($path, $content) { - $current = @file_get_contents($path); - if ($current != $content) { - file_put_contents($path, $content); - drush_log(dt('Updated %path.', array('%path' => $path)), 'ok'); - return TRUE; - } - return FALSE; -} - -/** - * Update the Drush aliases for all accessible Cloud sites. - */ -function drush_acapi_acquia_update() { - list($status, $all_aliases) = acapi_call('GET', '/me/drushrc', array(), array(), array(), array('display' => FALSE)); - if ($status == 200) { - foreach ($all_aliases as $realm_site => $aliases) { - list($realm, $site) = explode(':', $realm_site); - $file = _drush_config_file('home.drush', "$site.aliases"); - $content = "'; - - // Preserve existing defaults by loading them unless the user said not to. - $acapi_options = array(); - if (! drush_get_option('reset', FALSE)) { - $acapi_options = acapi_load_options(); - } - - // Collect specified values for each option. - foreach ($defaults as $k => $info) { - // Get the value from the command line, if provided. - $option = drush_get_option($k, NULL); - - if (!isset($option)) { - // No value on cli. The default is the previous value, if any, or what - // the option declaration specifies. - $default = isset($acapi_options[$k]) ? $acapi_options[$k] : $info['default_value']; - - // Prompt for some options, and just use the default for the others. - if (!empty($info['prompt'])) { - // Hide the existing key, if there is one. - if ($info['prompt'][3] && !empty($default)) { - $info['prompt'][1] = $hide_default; - } - else { - $info['prompt'][1] = $default; - } - $option = call_user_func_array('drush_prompt', $info['prompt']); - if ($option == $hide_default) { - $option = $default; - } - } - else { - $option = $default; - } - } - - // Only save non-default values so we can update the defaults without pain. - if ($option == $info['default_value']) { - unset($acapi_options[$k]); - } - else { - $acapi_options[$k] = $option; - } - } - - // We can't store the default path to the config file in the config file. - unset($acapi_options['ac-config']); - - acapi_save_options($acapi_options); -} - -/** - * Save acapi options to the config file. - * - * @param $options - * Hash of options to save. - */ -function acapi_save_options($options) { - $file = acapi_get_option_file(); - $dir = dirname($file); - $dt_args = array('@file' => $file, '@dir' => $dir); - $verbose = drush_get_option('verbose', FALSE); - $simulate = drush_get_option('simulate', FALSE); - if ($verbose || $simulate) { - drush_log(dt('Storing Acquia Cloud API defaults in @file.', $dt_args), 'ok'); - } - if ($verbose) { - drush_print($output); - } - if (! $simulate) { - $output = json_encode($options) . "\n"; - if (!is_dir($dir)) { - if (@mkdir($dir) === FALSE) { - return drush_set_error('ACAPI_CANNOT_WRITE_LOGIN', dt('Cannot create Acquia Cloud API defaults directory @dir.', $dt_args)); - } - } - if (file_put_contents($file, $output) === FALSE) { - return drush_set_error('ACAPI_CANNOT_WRITE_LOGIN', dt('Cannot write to Acquia Cloud API defaults file @file.', $dt_args)); - } - } -} - -/** - * Get saved values for acapi common options from the ac-config file. - * - * @returns - * A hash of saved acapi option names and values. - */ -function acapi_load_options() { - $ret = array(); - $file = acapi_get_option_file(); - $contents = @file_get_contents($file); - if ($contents !== FALSE) { - // Parse old-style Drush PHP config files, and save them in the new format. - if (preg_match('@^<\?php@', $contents) && preg_match_all('@^\$options\[\'acapi\'\]\[\'(\w+)\'\]\s*=\s*\'(.*)\';$@m', $contents, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $ret[$match[1]] = $match[2]; - } - acapi_save_options($ret); - } - else { - $data = @json_decode($contents, TRUE); - if (is_array($data)) { - $ret = $data; - } - else { - drush_set_error('ACAPI_INVALID_CONF', dt('Acquia Cloud API config file @file is invalid. Run drush ac-api-login.', array('@file' => $file))); - } - } - } - return $ret; -} - -/** - * Return the path for the acapi option file, either --ac-config or - * $HOME/.acquia/cloudapi.conf. - */ -function acapi_get_option_file() { - $defaults = acapi_common_options(); - return drush_get_option('ac-config', $defaults['ac-config']['default_value']); -} - -////////////////////////////////////////////////////////////////////// -// Custom callbacks -////////////////////////////////////////////////////////////////////// - -/** - * List a site's tasks. See the command definition for why this function is - * different. - */ -function drush_acapi_ac_database_backup_download($db, $backupid) { - $command = drush_get_context('command'); - list($site, $env) = acapi_get_site(); - $simulate = drush_get_option('simulate', FALSE); - $result_file = drush_get_option('result-file', ''); - // Similar to drush_sql_build_dump_command(). If the user has set - // $options['result-file'] = TRUE, then we will generate an SQL dump file in - // an automatically-generated backup directory based on site and env values. - if ($result_file === TRUE) { - // User did not pass a specific value for --result-file. Make one. - $backup = drush_include_engine('version_control', 'backup'); - $backup_dir = $backup->prepare_backup_dir($site . '.' . $env); - if (empty($backup_dir)) { - $backup_dir = "/tmp"; - } - $result_file = $backup_dir . '/' . $db . '-' . $backupid .'.sql.gz'; - } - if ($result_file == '') { - $fp = STDOUT; - } - else { - $fp = fopen($result_file, 'w'); - if ($fp == NULL) { - return drush_set_error('ACAPI_ENOENT', dt('Cannot write to result file @result_file.', array('@name' => $result_file))); - } - } - - $api_args = acapi_get_site_args() + array( - ':db' => $db, - ':backup' => $backupid, - ); - list($status, $result) = acapi_call( - $command['method'], - $command['resource'], - $api_args, - array(), - array(), - array('result_stream' => $fp, 'redirect' => 1, 'display' => FALSE) - ); -} - -/** - * List a site's tasks. See the command definition for why this function is - * different. - */ -function drush_acapi_ac_task_list() { - $api_args = acapi_get_site_args(); - $format = acapi_get_option('format'); - $state = drush_get_option('state', NULL); - $days = drush_get_option('days', NULL); - $limit = drush_get_option('limit', NULL); - $params = array(); - if (isset($state)) { - $params['state'] = $state; - } - if (isset($days)) { - $params['days'] = $days; - } - if (isset($limit)) { - $params['limit'] = $limit; - } - - list($status, $result) = acapi_call( - 'GET', - '/sites/:realm::site/tasks', - $api_args, - $params, - array(), - array('display' => !empty($format)) - ); - - $simulate = drush_get_option('simulate', FALSE); - if ($simulate) { - return; - } - - if (empty($format)) { - $display = array(); - foreach ($result as $id => $task) { - $display[$task->id] = $task->description; - } - drush_print_table(drush_key_value_to_array_table($display)); - } -} - -////////////////////////////////////////////////////////////////////// -// Utility functions -////////////////////////////////////////////////////////////////////// - -/** - * Define common options for Acquia Cloud API commands, and their defaults. - */ -function acapi_common_options() { - $options = array( - 'email' => array( - 'description' => 'Email address for your Acquia Network user account', - 'default_value' => '', - 'prompt' => array('Email', NULL, TRUE, FALSE), - 'example-value' => 'example@acquia.com', - ), - 'key' => array( - 'description' => 'Private Cloud API key for your Acquia Network user account', - 'default_value' => '', - 'prompt' => array('Key', NULL, TRUE, TRUE), - 'example-value' => 'apikey', - ), - 'acapi-conf-path' => array( - 'description' => 'Acquia Cloud API config files location. If not specified config will be loaded from $HOME/.drush', - 'default_value' => '', - 'example-value' => '/home/user/acapi-site-configs', - ), - 'ac-config' => array( - 'description' => 'Acquia Cloud API user config file location. If not specified config will be loaded from $HOME', - 'default_value' => drush_server_home() . '/.acquia/cloudapi.conf', - 'example-value' => drush_server_home() . '/.acquia/cloudapi-site-specific.conf', - ), - 'endpoint' => array( - 'description' => 'Acquia Cloud API endpoint URL.', - 'default_value' => 'https://cloudapi.acquia.com/v1', - 'prompt' => array('Endpoint URL', NULL, TRUE, FALSE), - 'example-value' => 'https://cloudapi.acquia.com/v1', - ), - 'cainfo' => array( - 'description' => 'Path to a file containing the SSL certificates needed to verify the ac-api-endpoint.', - 'default_value' => dirname(__FILE__) . '/cloudapi.acquia.com.pem', - 'example-value' => 'cloudapi.acquia.com.pem', - ), - 'format' => array( - 'description' => 'Format to output the object. Use "print_r" for print_r, "export" for var_export, and "json" for JSON. If not provided, the output is printed in a human-readable format.', - 'default_value' => '', - 'example-value' => 'json', - ), - ); - return $options; -} - -/** - * Retrieve an Acquia Cloud API option, in priority order: - * - * - command line - * - ac-config file ($HOME/.acquia/cloudapi.conf by default) - * - per-site acapi file ($HOME/.drush/.acapi.drushrc.php) - * - default from acapi_common_options() - * - * @param $name - * An ac-api option name. - * @return - * The option value, or NULL. - */ -function acapi_get_option($name) { - // Make sure $name is an acapi option. - $options = acapi_common_options(); - if (!isset($options[$name])) { - return drush_set_error('ACAPI_UNKNOWN_OPTION', dt('Unknown ac-api option @name.', array('@name' => $name))); - } - - // If the user specified --$name= on the command line, return . - $value = drush_get_option($name, NULL); - if (isset($value)) { - return $value; - } - - // If the ac-config file sets $name, return the value. - $values = acapi_load_options($name); - if (isset($values[$name])) { - return $values[$name]; - } - - // If $name has a default value, return it. - if (!empty($options[$name]['default_value'])) { - return $options[$name]['default_value']; - } - - // No specified value, no default, return NULL. - return; -} - -/** - * A generic callback for API commands. The command must have: - * - * 'method': $method for acapi_call(). - * 'resource': $resource for acapi_call(). API resource argument names can - * include any argument name from the command's arguments in addition to :site - * and :env which are taken from the site alias. - * - * The command calls acapi_call() with arguments for the specified method, - * resource, and arguments, calling the API and displaying the results. - * - * @return NULL - * This function always returns NULL to avoid invalid JSON. - */ -function drush_acapi_ac_generic_callback() { - $command = drush_get_context('command'); - $api_args = preg_match('@:site@', $command['resource']) ? acapi_get_site_args() : array(); - $params = array(); - $body = array(); - - if (isset($command['default_params'])) { - $params += $command['default_params']; - } - - foreach ($command['argument-description'] as $k => $desc) { - if (isset($command['params']) && array_search($k, $command['params']) !== FALSE) { - $params[$k] = array_shift($command['arguments']); - } - elseif (isset($command['params_array']) && array_search($k, $command['params_array']) !== FALSE) { - $params[$k] = explode(',', array_shift($command['arguments'])); - } - elseif (isset($command['body_fields']) && array_search($k, $command['body_fields']) !== FALSE) { - $body[$k] = array_shift($command['arguments']); - } - elseif (isset($command['body_fields_array']) && array_search($k, $command['body_fields_array']) !== FALSE) { - $body[$k] = explode(',', array_shift($command['arguments'])); - } - elseif (isset($command['body_fields_path']) && array_search($k, $command['body_fields_path']) !== FALSE) { - $path = array_shift($command['arguments']); - $body[$k] = file_get_contents($path); - if ($body[$k] === FALSE) { - drush_set_error('ACAPI_ENOENT', dt('Cannot read @arg path @path.', array('@arg' => $k, '@path' => $path))); - return; - } - } - else { - $api_args[":$k"] = array_shift($command['arguments']); - } - } - - foreach ($command['orig_options'] as $option => $info) { - if (!empty($info['param'])) { - if (drush_get_option($option, FALSE)) { - $params[$info['param']] = $info['value']; - } - } - } - - // acapi_call() will print the results, so returning here would result in - // invalid JSON. - acapi_call($command['method'], $command['resource'], $api_args, $params, $body); -} - -/** - * Return the Acquia Cloud site information specified via the site - * alias. - * - * @param $site_required (TRUE) - * Set an error if site alias options are not found. - * @return - * An array of three elements, site name, environment and realm unless the - * alias file pre-dates the addition of realm. - */ -function acapi_get_site($site_required = TRUE) { - return array_values(acapi_get_site_args($site_required)); -} - -/** - * Get arguments aboud the Acquia Cloud site ready to be used for replacement - * in a URI. - * - * @param $site_required (TRUE) - * Set an error if site alias options are not found. - * @return array - * An associative array containing :site, :env and :realm or an - * empty array if site alias option not found. - */ -function acapi_get_site_args($site_required = TRUE) { - $params = array( - ':site' => drush_get_option('ac-site'), - ':env' => drush_get_option('ac-env'), - ':realm' => drush_get_option('ac-realm'), - ); - - $missing = array_intersect($params, array(NULL)); - if ($missing) { - if ($site_required) { - $missing = str_replace(':', 'ac-', implode(', ', array_keys($missing))); - $error = dt( - 'Alias file is missing Acquia Cloud information: !missing. Be sure to specify a complete Acquia Cloud alias name, such as @mysite.dev.', - array('!missing' => $missing) - ); - drush_set_error('ACAPI_SITE_REQUIRED', $error); - } - return array(); - } - - return $params; -} - -/** - * Call an Acquia Cloud API resource. - * - * @param $method - * The HTTP method; e.g. GET. - * @param $resource - * The API function to call; e.g. /sites/:realm::site. - * @param $args = array() - * An array of argument values for the resource; e.g: array(':site' => - * 'mysite'). - * @params $params = array() - * An array of query parameters to append to the URL. - * @params $body = array() - * An array of parameters to include in the POST body in JSON format. - * @params $options = array() - * An array of options: - * - display (TRUE): whether to output the result to stdout - * - result_stream: open stream to which to write the response body - * - redirect: the maximum number of redirects to allow - */ -function acapi_call($method, $resource, $args, $params = array(), $body = array(), $options = array()) { - $default_options = array( - 'display' => TRUE, - ); - $options = array_merge($default_options, $options); - - $debug = drush_get_option('debug', FALSE); - $verbose = drush_get_option('verbose', FALSE); - $simulate = drush_get_option('simulate', FALSE); - $format = acapi_get_option('format'); - - // Build the API call URL. - $url = acapi_get_option('endpoint'); - $url .= acapi_dt($resource, $args); - $url .= '.json'; - - foreach ($params as $k => $v) { - if (is_array($v)) { - unset($params[$k]); - foreach ($v as $key => $val) { - $params["$k-$key"] = "$k%5B%5D=" . urlencode($val); - } - } - else { - $params[$k] = "$k=" . urlencode($v); - } - } - - $url .= '?' . implode('&', $params); - - $creds = acapi_get_creds(); - if (!$creds) { - return FALSE; - } - - // Build the body. - $json_body = json_encode($body); - - $display = "curl -X $method '$url'"; - if ($debug) { - $display .= " ($creds)"; - } - if ($debug || $verbose || $simulate) { - drush_print($display, 0, STDERR); - if (!empty($body)) { - drush_print(" $json_body", 0, STDERR); - } - } - - if ($simulate) { - return; - } - - $headers = array(); - $ch = curl_init($url); - // Basic request settings - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); - curl_setopt($ch, CURLOPT_USERAGENT, basename(__FILE__)); - if (!empty($options['result_stream'])) { - curl_setopt($ch, CURLOPT_FILE, $options['result_stream']); - } - else { - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); - } - // User authentication - curl_setopt($ch, CURLOPT_HTTPAUTH, TRUE); - curl_setopt($ch, CURLOPT_USERPWD, $creds); - // SSL - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, preg_match('@^https:@', acapi_get_option('endpoint'))); - curl_setopt($ch, CURLOPT_CAINFO, acapi_get_option('cainfo')); - // Redirects - if (!empty($options['redirect'])) { - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); - curl_setopt($ch, CURLOPT_MAXREDIRS, $options['redirect']+1); - } - /* Body - We need to set a Content-Length header even on empty POST requests, or the webserver - will throw a 411 Length Required. - */ - - curl_setopt($ch, CURLOPT_POSTFIELDS, $json_body); - $headers[] = 'Content-Type: application/json;charset=utf-8'; - $headers[] = 'Content-Length: ' . strlen($json_body); - // Headers - if (!empty($headers)) { - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - } - // Debugging - curl_setopt($ch, CURLOPT_VERBOSE, $debug); - // Go - $content = curl_exec($ch); - if (curl_errno($ch) > 0) { - return drush_set_error('ACAPI_CURL_ERROR', dt('Error accessing @url: @err', array('@url' => $url, '@err' => curl_error($ch)))); - } - - $result = json_decode($content); - $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if (!empty($format)) { - drush_print(drush_format($result, NULL, $format)); - } - else if ($options['display']) { - if (is_array($result)) { - foreach ($result as $item) { - if (! is_scalar($item)) { - drush_print_table(drush_key_value_to_array_table(acapi_convert_values($item))); - } - else { - drush_print($item); - } - } - } - else { - if ($method == 'POST') { - // All POST actions return a task. Display something helpful. - drush_log(dt('Task @taskid started.', array('@taskid' => $result->id)), 'ok'); - } - else { - drush_print_table(drush_key_value_to_array_table(acapi_convert_values($result))); - } - } - } - - if ($status != 200) { - return drush_set_error('ACAPI_HTTP_STATUS_' . $status, dt('API status code @status', array('@status' => $status))); - } - - return array($status, $result); -} - -/** - * Return Acquia Cloud API credentials as username:password, or log an error - * if they are unavailable. - */ -function acapi_get_creds() { - $user = acapi_get_option('email'); - $pass = acapi_get_option('key'); - if (empty($user) || empty($pass)) { - return drush_set_error('ACAPI_CREDS_MISSING', dt('Email and api key required; specify --email/--key or run drush ac-api-login')); - } - return "$user:$pass"; -} - -/** - * Convert NULL, array and object values to appropriate string representations - * so they are printed correctly. - */ -function acapi_convert_values($arr) { - foreach ($arr as $k => $v) { - if (!isset($v)) { - $arr->{$k} = ''; - } - elseif (is_array($v) || is_object($v)) { - $arr->{$k} = '...'; - } - } - return (array) $arr; -} - -/** - * dt() wrapper that URL-encodes all substituted parameters that begin with - * a colon (':'). - */ -function acapi_dt($string, $args = array()) { - foreach ($args as $k => $v) { - if ($k[0] == ':') { - $args[$k] = urlencode($v); - } - } - return dt($string, $args); -} diff --git a/7.1/config/.drush/cloudapi.acquia.com.pem b/7.1/config/.drush/cloudapi.acquia.com.pem deleted file mode 100644 index a5da6cb6..00000000 --- a/7.1/config/.drush/cloudapi.acquia.com.pem +++ /dev/null @@ -1,98 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGvzCCBaegAwIBAgIQD1pdYvA5l5l2ezeWEsVwNTANBgkqhkiG9w0BAQUFADBm -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBDQS0zMB4XDTEyMDgyNzAwMDAwMFoXDTE1MDkwOTEyMDAwMFowfzELMAkGA1UE -BhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEzARBgNVBAcTCkJ1cmxpbmd0 -b24xEzARBgNVBAoTCkFjcXVpYSBJbmMxFzAVBgNVBAsTDkFjcXVpYSBIb3N0aW5n -MRUwEwYDVQQDDAwqLmFjcXVpYS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCVmcKz54qAbNO9NyP3hA96gzGRORSB9Bz4EOuFM1kfD/gvZoXoqk87 -NC/jPrvPAxNZDJ33IrO08WDWxBVi6UQ7Q9YYFgU1mm0se4qjwld7dtziDnaq2zXc -x4Q+AVfBj92w+RsVDWA2mNtMSaDePqXGzLOz4muUCA5oCtMg2QD+XIEp1yt13nQb -5nW6PbY6kHHviepNX3wj7TdqTLNPdCVcK9BJz2YTjfHtBBxnGxR/m804RL9fYWKz -r6XUbbf+gcKFwMqRI54Qs5cD20jmYnYgXFYwAx6HvOH9Lr969xMq5ePIQkKc66kG -Opie8unjAQRgd6T7lL+zvpaen1UhJruhAgMBAAGjggNOMIIDSjAfBgNVHSMEGDAW -gBRQ6nOJ2yn7EI+e5QEg1N55mUiD9zAdBgNVHQ4EFgQUJuo64qCLWo0Fvi6AsOYG -IiNj+s8wIwYDVR0RBBwwGoIMKi5hY3F1aWEuY29tggphY3F1aWEuY29tMA4GA1Ud -DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwYQYDVR0f -BFowWDAqoCigJoYkaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL2NhMy1nMjcuY3Js -MCqgKKAmhiRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vY2EzLWcyNy5jcmwwggHE -BgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwBATCCAaQwOgYIKwYBBQUHAgEWLmh0 -dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFk -BggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBz -ACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBz -ACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBD -AGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBp -AG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBo -ACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBl -ACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAg -AHIAZQBmAGUAcgBlAG4AYwBlAC4wewYIKwYBBQUHAQEEbzBtMCQGCCsGAQUFBzAB -hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRQYIKwYBBQUHMAKGOWh0dHA6Ly9j -YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VDQS0zLmNy -dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQCwbrUX+rSNdiS1ivce -pI3gzzlOG9FjcPPTfoLD/+eiysiTKe7d3Hb9urHZWGuEWWxXRl+6tND3TAt8ONpM -ZCs+nls+qvspG8ApQMZLgak4kb4+CaLMi6ZpQ2JTI89iChonOjI0OP6dVDbGbTvV -a7LKkiZw1xJNYUnhIiqOE1B9Ww3SUUpe1TLmiAYiiYiiuiyBxMz48sARHXnlEbWw -0dHOnU51tvG1zkv9sATYwv3LtIjf9pmXZnS1W0j5Ap0JeFbgFywVi0zSpbix0fzr -pyArWETFKxirR2bCVFH0sVZ2q3/sznguUx1QgH+tese0hqHm2epRORcOi69mcDYi -Cf5v ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR -CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv -KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 -BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf -1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs -zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d -32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w -ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm -MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB -hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln -aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl -cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME -GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB -INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a -vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j -CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X -dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE -JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY -Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K ------END CERTIFICATE----- diff --git a/7.2/Dockerfile b/7.2/Dockerfile index f00066c8..92319d64 100644 --- a/7.2/Dockerfile +++ b/7.2/Dockerfile @@ -147,7 +147,7 @@ RUN set -xe; \ libicu63 \ libjpeg62-turbo \ libldap-2.4-2 \ - libmagickcore-6.q16-6 \ + libmagickcore-6.q16-*-extra \ libmagickwand-6.q16-6 \ libmemcached11 \ libmemcachedutil2 \ @@ -197,6 +197,7 @@ RUN set -xe; \ ssh2 \ xsl \ zip \ + sysvsem \ ;\ pecl update-channels; \ pecl install >/dev/null /dev/null; \ - mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/drush/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ + mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/backdrop-drush-extension/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ drush cc drush # Node.js (installed as user) ENV \ - NVM_VERSION=0.35.0 \ - NODE_VERSION=12.13.0 \ - YARN_VERSION=1.19.1 + NVM_VERSION=0.35.3 \ + NODE_VERSION=12.18.1 \ + YARN_VERSION=1.22.4 # Don't use -x here, as the output may be excessive RUN set -e; \ # NVM and a defaut Node.js version @@ -316,8 +322,8 @@ RUN set -e; \ # Ruby (installed as user) ENV \ - RVM_VERSION_INSTALL=1.29.9 \ - RUBY_VERSION_INSTALL=2.6.5 + RVM_VERSION_INSTALL=1.29.10 \ + RUBY_VERSION_INSTALL=2.7.1 # Don't use -x here, as the output may be excessive RUN set -e; \ # Public GPG servers are not realiable, so downloading keys from rvm.io instead. @@ -356,8 +362,8 @@ RUN set -e; \ # Python (installed as user) ENV \ - PYENV_VERSION_INSTALL=1.2.14 -# PYTHON_VERSION_INSTALL=3.7.0 + PYENV_VERSION_INSTALL=1.2.18 +# PYTHON_VERSION_INSTALL=3.8.3 RUN set -xe; \ git clone --depth 1 -b v${PYENV_VERSION_INSTALL} https://github.com/pyenv/pyenv.git $HOME/.pyenv; \ rm -rf $HOME/.pyenv/.git; \ @@ -374,22 +380,26 @@ RUN set -xe; \ # pyenv install ${PYTHON_VERSION_INSTALL}; \ # pyenv global ${PYTHON_VERSION_INSTALL} +# Notify web container about started fin exec +RUN echo '(/opt/ping-web.sh &)' >> $HOME/.profile + USER root SHELL ["/bin/sh", "-c"] # Copy configs and scripts -COPY --chown=docker:docker config/.drush /home/docker/.drush COPY --chown=docker:docker config/.terminus /home/docker/.terminus COPY --chown=docker:docker config/.ssh /home/docker/.ssh COPY config/supervisor /etc/supervisor/conf.d COPY startup.sh /opt/startup.sh COPY healthcheck.sh /opt/healthcheck.sh +COPY ping-web.sh /opt/ping-web.sh # PHP default settings, global overrides and fpm overrides # PHP_VERSION is set upstream. The source code for the exact PHP version may not be available in Github # TODO: For now, will just hardcoded a specific version available on https://github.com/php/php-src/ -ADD https://raw.githubusercontent.com/php/php-src/php-7.2.22/php.ini-development /usr/local/etc/php/php.ini +ADD https://raw.githubusercontent.com/php/php-src/php-7.2.31/php.ini-development /usr/local/etc/php/php.ini COPY config/php/zz-php.ini /usr/local/etc/php/conf.d/zz-php.ini COPY config/php/xdebug.ini /opt/docker-php-ext-xdebug.ini +COPY config/php/xhprof.ini /opt/docker-php-ext-xhprof.ini COPY config/php/zz-php-fpm.conf /usr/local/etc/php-fpm.d/zz-php-fpm.conf ENV \ @@ -402,8 +412,12 @@ ENV \ # Default values for HOST_UID and HOST_GUI to match the default Ubuntu user. These are used in startup.sh HOST_UID=1000 \ HOST_GID=1000 \ + # Delay in seconds between pings web from cli, while running fin exec. 0 - disabled + WEB_KEEPALIVE=0 \ # xdebug disabled by default - XDEBUG_ENABLED=0 + XDEBUG_ENABLED=0 \ + XHPROF_ENABLED=0 \ + XHPROF_OUTPUT_DIR=/tmp/xhprof # TODO: [v3] remove and set these via docker-compose EXPOSE 9000 @@ -431,22 +445,21 @@ USER docker ARG HOME=/home/docker ENV \ - # TODO: 2.x versions do not yet work with xdebug - CODE_SERVER_VERSION=1.1156-vsc1.33.1 \ + CODE_SERVER_VERSION=3.4.1 \ VSCODE_HOME="${HOME}/code-server" \ VSCODE_EXT_DIRECTORY="${HOME}/code-server/extensions" \ VSCODE_XDEBUG_VERSION=1.13.0 \ - VSCODE_GITLENS_VERSION=9.9.3 + VSCODE_GITLENS_VERSION=10.2.2 # Install code-server RUN \ set -xe; \ - curl -fsSL "https://github.com/cdr/code-server/releases/download/${CODE_SERVER_VERSION}/code-server${CODE_SERVER_VERSION}-linux-x64.tar.gz" -o /tmp/code-server.tar.gz; \ - tar -zxv --file=/tmp/code-server.tar.gz --directory=/tmp --strip-components=1; \ - sudo mv /tmp/code-server /usr/local/bin/; rm -rf /tmp/*.* + curl -fsSL "https://github.com/cdr/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server_${CODE_SERVER_VERSION}_amd64.deb" -o /tmp/code-server_amd64.deb; \ + sudo dpkg -i /tmp/code-server_amd64.deb; \ + rm -rf /tmp/*.* # Settings and scripts -COPY opt/code-server /opt/code-server +COPY --chown=docker:docker opt/code-server /opt/code-server # Install extensions RUN \ diff --git a/7.2/Makefile b/7.2/Makefile index e127bccc..541614a0 100644 --- a/7.2/Makefile +++ b/7.2/Makefile @@ -2,8 +2,9 @@ -include env_make VERSION ?= 7.2 -TAG_APPENDIX ?= -php$(VERSION) -BUILD_TAG = build-$(TAG_APPENDIX) + +SOFTWARE_VERSION ?= php$(VERSION) +BUILD_TAG = build-$(SOFTWARE_VERSION) REPO = docksal/cli NAME = docksal-cli-$(VERSION) CWD = $(shell pwd) @@ -16,7 +17,7 @@ VOLUMES += -v /home/docker .PHONY: build test push shell run start stop logs clean release build: - docker build --cache-from=$(REPO):edge$(TAG_APPENDIX) -t $(REPO):$(BUILD_TAG) . + docker build --cache-from=$(REPO):edge-$(SOFTWARE_VERSION) -t $(REPO):$(BUILD_TAG) . test: IMAGE=$(REPO):$(BUILD_TAG) NAME=$(NAME) VERSION=$(VERSION) ../tests/test.bats diff --git a/7.2/config/.drush/acapi.drush.inc b/7.2/config/.drush/acapi.drush.inc deleted file mode 100644 index b04b8a3c..00000000 --- a/7.2/config/.drush/acapi.drush.inc +++ /dev/null @@ -1,1227 +0,0 @@ - 'Retrieve Drush aliases for all accessible Acquia Cloud sites.', - 'arguments' => array( - ), - 'options' => $options, - ); - - // Most of the commands below use drush_acapi_ac_generic_callback, which - // translates their 'method', 'resource', and 'arguments' properties into - // the appropriate API call, executes it, and displays the result. - - ////////////////////////////////////////////////////////////////////// - // Login - ////////////////////////////////////////////////////////////////////// - $items['ac-api-login'] = array( - 'description' => 'Store Acquia Cloud API credentials and configuration information.', - 'arguments' => array( - ), - 'options' => $options + array( - 'reset' => array( - 'description' => 'Discard any existing stored values from a previous call. Without this option, new values will be merged with existing values.' - ), - ), - ); - - ////////////////////////////////////////////////////////////////////// - // Sites and environments - ////////////////////////////////////////////////////////////////////// - $items['ac-site-list'] = array( - 'description' => 'List all sites available to the current user.', - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-site-info'] = array( - 'description' => 'Show information about a site.', - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-list'] = array( - 'description' => "List a site's environments.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-info'] = array( - 'description' => "Show information about a site environment.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-install'] = array( - 'description' => "Install a Drupal distribution from a pre-selected list, URL, or Drush Makefile.", - 'arguments' => array( - 'type' => 'Type of distro source: distro_url or make_url.', - 'source' => 'A URL to a distro or URL to a Drush make file.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/install/:type', - 'params' => array('source'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-livedev'] = array( - 'description' => "Configure Live Development on a site environment.", - 'arguments' => array( - 'action' => 'Action to take. \'enable\' or \'disable\' live development.', - 'discard' => 'When action is \'disable\', set to 1 to discard uncommitted changes.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/livedev/:action', - 'params' => array('discard'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Servers - ////////////////////////////////////////////////////////////////////// - $items['ac-server-list'] = array( - 'description' => "List servers for a site and environment.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/servers', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-server-info'] = array( - 'description' => "Show information about a server.", - 'arguments' => array( - 'server' => 'Server name.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/servers/:server', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-server-php-procs'] = array( - 'description' => "Calculate the php max procs value based on possible memory limits and apc shm settings.", - 'arguments' => array( - 'server' => 'Server name.', - 'memory_limits' => 'Memory limits.', - 'apc_shm' => 'APC shm settings.' - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/servers/:server/php-procs', - 'params_array' => array( - 'memory_limits', - 'apc_shm', - ), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Databases - ////////////////////////////////////////////////////////////////////// - $items['ac-database-list'] = array( - 'description' => "List a site's databases.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/dbs', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-info'] = array( - 'description' => "Show information about a site database.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/dbs/:db', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-add'] = array( - 'description' => 'Add a database.', - 'arguments' => array( - 'db' => 'The database.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/dbs', - 'body_fields' => array('db'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-delete'] = array( - 'description' => 'Delete a database.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/dbs/:db', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-list'] = array( - 'description' => "List a site environment's database instances.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-info'] = array( - 'description' => "Show information about a site environment's database instance.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-list'] = array( - 'description' => "List a site environment's database instance backups.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-info'] = array( - 'description' => "Show information about a site environment's database instance backup.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backup' => 'Backup id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backup', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-download'] = array( - 'description' => "Download a site environment database instance backup.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backup' => 'Backup id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backup/download', - 'callback' => 'drush_acapi_ac_database_backup_download', - 'options' => array('result-file' => - array( - 'description' => 'Save to a file; specify the full path in which to store the backup. If not provided, the backup is sent the standard output.', - 'example-value' => '/path/to/file', - )) + $options, - ); - $items['ac-database-instance-backup'] = array( - 'description' => 'Create a database instance backup.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-restore'] = array( - 'description' => 'Restore a database instance backup.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backupid' => 'The backup id.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backupid/restore', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-delete'] = array( - 'description' => 'Delete a database instance backup.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backupid' => 'The backup id.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backupid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Tasks - ////////////////////////////////////////////////////////////////////// - // API resource /sites/:realm::site/tasks returns an array of task records, - // unlike other list resources which return a list of ids, so we need a - // non-standard callback. We'd have to make N API calls to implement this the - // way other data types work, which I think shows that the returning a list of - // records is better. - $items['ac-task-list'] = array( - 'description' => "List a site's tasks.", - 'arguments' => array( - ), - 'options' => array( - 'state' => array( - 'description' => 'The task state to retrieve. If not specified, retrieve all tasks for the site.', - 'example-value' => 'done', - ), - 'days' => array( - 'description' => 'The number of days worth of tasks to retrieve. If not specified, retrieve, at a maximum, 7 days worth of tasks.', - 'example-value' => '5', - ), - 'limit' => array( - 'description' => 'The maximum number of tasks to retrieve. If not specified, retrieve a maximum of 50 tasks. The maximum value allowed is 1000.', - 'example-value' => '500' - ), - ) + $options, - ); - - $items['ac-task-info'] = array( - 'description' => "Show information about a site task.", - 'arguments' => array( - 'task' => 'The task id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/tasks/:task', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Workflow - ////////////////////////////////////////////////////////////////////// - $items['ac-code-deploy'] = array( - 'description' => 'Deploy code from one site environment to another.', - 'arguments' => array( - 'target' => 'The target environment.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/code-deploy/:env/:target', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-code-path-deploy'] = array( - 'description' => 'Deploy a specific branch or tag in an environment.', - 'arguments' => array( - 'path' => 'The branch or tag to deploy.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/code-deploy', - 'params' => array('path'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-copy'] = array( - 'description' => 'Copy a database from one site environment to another.', - 'arguments' => array( - 'db' => 'The database.', - 'target' => 'The target environment.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/dbs/:db/db-copy/:env/:target', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-files-copy'] = array( - 'description' => 'Copy user-uploaded files from one site environment to another.', - 'arguments' => array( - 'target' => 'The target environment.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/files-copy/:env/:target', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-move'] = array( - 'description' => 'Move a domain from one site environment to another.', - 'arguments' => array( - 'target' => 'The target environment.', - 'domains' => 'Comma separated list of domains, or * for all.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/domain-move/:env/:target', - 'body_fields_array' => array('domains'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // SSH keys - ////////////////////////////////////////////////////////////////////// - $items['ac-sshkey-list'] = array( - 'description' => "List a site's SSH keys.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/sshkeys', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-sshkey-info'] = array( - 'description' => "Show information about a site SSH key.", - 'arguments' => array( - 'sshkeyid' => 'SSH key id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/sshkeys/:sshkeyid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-sshkey-add'] = array( - 'description' => 'Add an SSH key to a site.', - 'arguments' => array( - 'ssh_pub_key' => 'File containing the SSH public key.', - 'nickname' => 'The SSH key nickname.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/sshkeys', - 'params' => array('nickname'), - 'body_fields_path' => array('ssh_pub_key'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options + array( - 'shell_access' => - array('description' => 'Set shell access for this key. (true or false)', - 'param' => 'shell_access',), - 'vcs_access' => - array('description' => 'Set git access for this key. (true or false)', - 'param' => 'vcs_access',), - 'blacklist' => - array('description' => 'Array containing a list of environments to disallow access for this key', - 'param' => 'blacklist',), - ), - ); - $items['ac-sshkey-delete'] = array( - 'description' => 'Delete an SSH key from a site.', - 'arguments' => array( - 'sshkeyid' => 'The SSH key id to delete.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/sshkeys/:sshkeyid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // SVN users - ////////////////////////////////////////////////////////////////////// - $items['ac-svnuser-list'] = array( - 'description' => "List a site's SVN users.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/svnusers', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-svnuser-info'] = array( - 'description' => "Show information about a site SVN user.", - 'arguments' => array( - 'svnuserid' => 'SVN user id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/svnusers/:svnuserid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-svnuser-add'] = array( - 'description' => 'Add an SVN user to a site.', - 'arguments' => array( - 'username' => 'SVN username.', - 'password' => 'SVN password.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/svnusers/:username', - 'body_fields' => array('password'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-svnuser-delete'] = array( - 'description' => 'Delete an SVN user from a site.', - 'arguments' => array( - 'svnuserid' => 'The SVN user id to delete.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/svnusers/:svnuserid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Domains - ////////////////////////////////////////////////////////////////////// - $items['ac-domain-list'] = array( - 'description' => "List a site's domains.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/domains', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-info'] = array( - 'description' => "Show information about a site domain.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-add'] = array( - 'description' => "Add a domain name to an environment.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-delete'] = array( - 'description' => "Delete a domain name from an environment.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-purge'] = array( - 'description' => "Purge a domain from the Varnish cache.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain/cache', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - foreach ($items as $command => $item) { - $items[$command]['orig_options'] = $item['options']; - if (DRUSH_VERSION < 5) { - // Make our command structure work with previous versions of drush while - // still supporting the way we handle arguments, options, and defaults. - $items[$command]['argument-description'] = $item['arguments']; - foreach ($item['options'] as $option => $info) { - $items[$command]['options'][$option] = $info['description']; - } - } - else { - $items[$command]['handle-remote-commands'] = TRUE; - } - - $items[$command]['bootstrap'] = DRUSH_BOOTSTRAP_DRUSH; - $items[$command]['required-arguments'] = TRUE; - } - - return $items; -} - -function acapi_drush_help($section) { - switch ($section) { - case 'meta:acapi:title': - return dt('Acquia commands'); - case 'meta:acapi:summary': - return dt('Acquia Cloud commands.'); - case 'drush:ac-api-login': - $file = '$HOME/.acquia/cloudapi.conf'; - return dt("Store Acquia Cloud API credentials and endpoint information. - -This command stores default email, key, and optionally endpoint values for future Acquia Cloud API commands in @file. File location can be altered with the ac-config option on all Acquia Cloud commands.", array('@file' => $file)); - } -} - -/** - * Write content to a file if the file does not already have that content. - * Log a message when the file is updated. - * - * @param $path - * The full path to update. - * @param $content - * The content to write. - * @return - * TRUE if the file is updated, FALSE otherwise. - */ -function drush_acapi_update_file($path, $content) { - $current = @file_get_contents($path); - if ($current != $content) { - file_put_contents($path, $content); - drush_log(dt('Updated %path.', array('%path' => $path)), 'ok'); - return TRUE; - } - return FALSE; -} - -/** - * Update the Drush aliases for all accessible Cloud sites. - */ -function drush_acapi_acquia_update() { - list($status, $all_aliases) = acapi_call('GET', '/me/drushrc', array(), array(), array(), array('display' => FALSE)); - if ($status == 200) { - foreach ($all_aliases as $realm_site => $aliases) { - list($realm, $site) = explode(':', $realm_site); - $file = _drush_config_file('home.drush', "$site.aliases"); - $content = "'; - - // Preserve existing defaults by loading them unless the user said not to. - $acapi_options = array(); - if (! drush_get_option('reset', FALSE)) { - $acapi_options = acapi_load_options(); - } - - // Collect specified values for each option. - foreach ($defaults as $k => $info) { - // Get the value from the command line, if provided. - $option = drush_get_option($k, NULL); - - if (!isset($option)) { - // No value on cli. The default is the previous value, if any, or what - // the option declaration specifies. - $default = isset($acapi_options[$k]) ? $acapi_options[$k] : $info['default_value']; - - // Prompt for some options, and just use the default for the others. - if (!empty($info['prompt'])) { - // Hide the existing key, if there is one. - if ($info['prompt'][3] && !empty($default)) { - $info['prompt'][1] = $hide_default; - } - else { - $info['prompt'][1] = $default; - } - $option = call_user_func_array('drush_prompt', $info['prompt']); - if ($option == $hide_default) { - $option = $default; - } - } - else { - $option = $default; - } - } - - // Only save non-default values so we can update the defaults without pain. - if ($option == $info['default_value']) { - unset($acapi_options[$k]); - } - else { - $acapi_options[$k] = $option; - } - } - - // We can't store the default path to the config file in the config file. - unset($acapi_options['ac-config']); - - acapi_save_options($acapi_options); -} - -/** - * Save acapi options to the config file. - * - * @param $options - * Hash of options to save. - */ -function acapi_save_options($options) { - $file = acapi_get_option_file(); - $dir = dirname($file); - $dt_args = array('@file' => $file, '@dir' => $dir); - $verbose = drush_get_option('verbose', FALSE); - $simulate = drush_get_option('simulate', FALSE); - if ($verbose || $simulate) { - drush_log(dt('Storing Acquia Cloud API defaults in @file.', $dt_args), 'ok'); - } - if ($verbose) { - drush_print($output); - } - if (! $simulate) { - $output = json_encode($options) . "\n"; - if (!is_dir($dir)) { - if (@mkdir($dir) === FALSE) { - return drush_set_error('ACAPI_CANNOT_WRITE_LOGIN', dt('Cannot create Acquia Cloud API defaults directory @dir.', $dt_args)); - } - } - if (file_put_contents($file, $output) === FALSE) { - return drush_set_error('ACAPI_CANNOT_WRITE_LOGIN', dt('Cannot write to Acquia Cloud API defaults file @file.', $dt_args)); - } - } -} - -/** - * Get saved values for acapi common options from the ac-config file. - * - * @returns - * A hash of saved acapi option names and values. - */ -function acapi_load_options() { - $ret = array(); - $file = acapi_get_option_file(); - $contents = @file_get_contents($file); - if ($contents !== FALSE) { - // Parse old-style Drush PHP config files, and save them in the new format. - if (preg_match('@^<\?php@', $contents) && preg_match_all('@^\$options\[\'acapi\'\]\[\'(\w+)\'\]\s*=\s*\'(.*)\';$@m', $contents, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $ret[$match[1]] = $match[2]; - } - acapi_save_options($ret); - } - else { - $data = @json_decode($contents, TRUE); - if (is_array($data)) { - $ret = $data; - } - else { - drush_set_error('ACAPI_INVALID_CONF', dt('Acquia Cloud API config file @file is invalid. Run drush ac-api-login.', array('@file' => $file))); - } - } - } - return $ret; -} - -/** - * Return the path for the acapi option file, either --ac-config or - * $HOME/.acquia/cloudapi.conf. - */ -function acapi_get_option_file() { - $defaults = acapi_common_options(); - return drush_get_option('ac-config', $defaults['ac-config']['default_value']); -} - -////////////////////////////////////////////////////////////////////// -// Custom callbacks -////////////////////////////////////////////////////////////////////// - -/** - * List a site's tasks. See the command definition for why this function is - * different. - */ -function drush_acapi_ac_database_backup_download($db, $backupid) { - $command = drush_get_context('command'); - list($site, $env) = acapi_get_site(); - $simulate = drush_get_option('simulate', FALSE); - $result_file = drush_get_option('result-file', ''); - // Similar to drush_sql_build_dump_command(). If the user has set - // $options['result-file'] = TRUE, then we will generate an SQL dump file in - // an automatically-generated backup directory based on site and env values. - if ($result_file === TRUE) { - // User did not pass a specific value for --result-file. Make one. - $backup = drush_include_engine('version_control', 'backup'); - $backup_dir = $backup->prepare_backup_dir($site . '.' . $env); - if (empty($backup_dir)) { - $backup_dir = "/tmp"; - } - $result_file = $backup_dir . '/' . $db . '-' . $backupid .'.sql.gz'; - } - if ($result_file == '') { - $fp = STDOUT; - } - else { - $fp = fopen($result_file, 'w'); - if ($fp == NULL) { - return drush_set_error('ACAPI_ENOENT', dt('Cannot write to result file @result_file.', array('@name' => $result_file))); - } - } - - $api_args = acapi_get_site_args() + array( - ':db' => $db, - ':backup' => $backupid, - ); - list($status, $result) = acapi_call( - $command['method'], - $command['resource'], - $api_args, - array(), - array(), - array('result_stream' => $fp, 'redirect' => 1, 'display' => FALSE) - ); -} - -/** - * List a site's tasks. See the command definition for why this function is - * different. - */ -function drush_acapi_ac_task_list() { - $api_args = acapi_get_site_args(); - $format = acapi_get_option('format'); - $state = drush_get_option('state', NULL); - $days = drush_get_option('days', NULL); - $limit = drush_get_option('limit', NULL); - $params = array(); - if (isset($state)) { - $params['state'] = $state; - } - if (isset($days)) { - $params['days'] = $days; - } - if (isset($limit)) { - $params['limit'] = $limit; - } - - list($status, $result) = acapi_call( - 'GET', - '/sites/:realm::site/tasks', - $api_args, - $params, - array(), - array('display' => !empty($format)) - ); - - $simulate = drush_get_option('simulate', FALSE); - if ($simulate) { - return; - } - - if (empty($format)) { - $display = array(); - foreach ($result as $id => $task) { - $display[$task->id] = $task->description; - } - drush_print_table(drush_key_value_to_array_table($display)); - } -} - -////////////////////////////////////////////////////////////////////// -// Utility functions -////////////////////////////////////////////////////////////////////// - -/** - * Define common options for Acquia Cloud API commands, and their defaults. - */ -function acapi_common_options() { - $options = array( - 'email' => array( - 'description' => 'Email address for your Acquia Network user account', - 'default_value' => '', - 'prompt' => array('Email', NULL, TRUE, FALSE), - 'example-value' => 'example@acquia.com', - ), - 'key' => array( - 'description' => 'Private Cloud API key for your Acquia Network user account', - 'default_value' => '', - 'prompt' => array('Key', NULL, TRUE, TRUE), - 'example-value' => 'apikey', - ), - 'acapi-conf-path' => array( - 'description' => 'Acquia Cloud API config files location. If not specified config will be loaded from $HOME/.drush', - 'default_value' => '', - 'example-value' => '/home/user/acapi-site-configs', - ), - 'ac-config' => array( - 'description' => 'Acquia Cloud API user config file location. If not specified config will be loaded from $HOME', - 'default_value' => drush_server_home() . '/.acquia/cloudapi.conf', - 'example-value' => drush_server_home() . '/.acquia/cloudapi-site-specific.conf', - ), - 'endpoint' => array( - 'description' => 'Acquia Cloud API endpoint URL.', - 'default_value' => 'https://cloudapi.acquia.com/v1', - 'prompt' => array('Endpoint URL', NULL, TRUE, FALSE), - 'example-value' => 'https://cloudapi.acquia.com/v1', - ), - 'cainfo' => array( - 'description' => 'Path to a file containing the SSL certificates needed to verify the ac-api-endpoint.', - 'default_value' => dirname(__FILE__) . '/cloudapi.acquia.com.pem', - 'example-value' => 'cloudapi.acquia.com.pem', - ), - 'format' => array( - 'description' => 'Format to output the object. Use "print_r" for print_r, "export" for var_export, and "json" for JSON. If not provided, the output is printed in a human-readable format.', - 'default_value' => '', - 'example-value' => 'json', - ), - ); - return $options; -} - -/** - * Retrieve an Acquia Cloud API option, in priority order: - * - * - command line - * - ac-config file ($HOME/.acquia/cloudapi.conf by default) - * - per-site acapi file ($HOME/.drush/.acapi.drushrc.php) - * - default from acapi_common_options() - * - * @param $name - * An ac-api option name. - * @return - * The option value, or NULL. - */ -function acapi_get_option($name) { - // Make sure $name is an acapi option. - $options = acapi_common_options(); - if (!isset($options[$name])) { - return drush_set_error('ACAPI_UNKNOWN_OPTION', dt('Unknown ac-api option @name.', array('@name' => $name))); - } - - // If the user specified --$name= on the command line, return . - $value = drush_get_option($name, NULL); - if (isset($value)) { - return $value; - } - - // If the ac-config file sets $name, return the value. - $values = acapi_load_options($name); - if (isset($values[$name])) { - return $values[$name]; - } - - // If $name has a default value, return it. - if (!empty($options[$name]['default_value'])) { - return $options[$name]['default_value']; - } - - // No specified value, no default, return NULL. - return; -} - -/** - * A generic callback for API commands. The command must have: - * - * 'method': $method for acapi_call(). - * 'resource': $resource for acapi_call(). API resource argument names can - * include any argument name from the command's arguments in addition to :site - * and :env which are taken from the site alias. - * - * The command calls acapi_call() with arguments for the specified method, - * resource, and arguments, calling the API and displaying the results. - * - * @return NULL - * This function always returns NULL to avoid invalid JSON. - */ -function drush_acapi_ac_generic_callback() { - $command = drush_get_context('command'); - $api_args = preg_match('@:site@', $command['resource']) ? acapi_get_site_args() : array(); - $params = array(); - $body = array(); - - if (isset($command['default_params'])) { - $params += $command['default_params']; - } - - foreach ($command['argument-description'] as $k => $desc) { - if (isset($command['params']) && array_search($k, $command['params']) !== FALSE) { - $params[$k] = array_shift($command['arguments']); - } - elseif (isset($command['params_array']) && array_search($k, $command['params_array']) !== FALSE) { - $params[$k] = explode(',', array_shift($command['arguments'])); - } - elseif (isset($command['body_fields']) && array_search($k, $command['body_fields']) !== FALSE) { - $body[$k] = array_shift($command['arguments']); - } - elseif (isset($command['body_fields_array']) && array_search($k, $command['body_fields_array']) !== FALSE) { - $body[$k] = explode(',', array_shift($command['arguments'])); - } - elseif (isset($command['body_fields_path']) && array_search($k, $command['body_fields_path']) !== FALSE) { - $path = array_shift($command['arguments']); - $body[$k] = file_get_contents($path); - if ($body[$k] === FALSE) { - drush_set_error('ACAPI_ENOENT', dt('Cannot read @arg path @path.', array('@arg' => $k, '@path' => $path))); - return; - } - } - else { - $api_args[":$k"] = array_shift($command['arguments']); - } - } - - foreach ($command['orig_options'] as $option => $info) { - if (!empty($info['param'])) { - if (drush_get_option($option, FALSE)) { - $params[$info['param']] = $info['value']; - } - } - } - - // acapi_call() will print the results, so returning here would result in - // invalid JSON. - acapi_call($command['method'], $command['resource'], $api_args, $params, $body); -} - -/** - * Return the Acquia Cloud site information specified via the site - * alias. - * - * @param $site_required (TRUE) - * Set an error if site alias options are not found. - * @return - * An array of three elements, site name, environment and realm unless the - * alias file pre-dates the addition of realm. - */ -function acapi_get_site($site_required = TRUE) { - return array_values(acapi_get_site_args($site_required)); -} - -/** - * Get arguments aboud the Acquia Cloud site ready to be used for replacement - * in a URI. - * - * @param $site_required (TRUE) - * Set an error if site alias options are not found. - * @return array - * An associative array containing :site, :env and :realm or an - * empty array if site alias option not found. - */ -function acapi_get_site_args($site_required = TRUE) { - $params = array( - ':site' => drush_get_option('ac-site'), - ':env' => drush_get_option('ac-env'), - ':realm' => drush_get_option('ac-realm'), - ); - - $missing = array_intersect($params, array(NULL)); - if ($missing) { - if ($site_required) { - $missing = str_replace(':', 'ac-', implode(', ', array_keys($missing))); - $error = dt( - 'Alias file is missing Acquia Cloud information: !missing. Be sure to specify a complete Acquia Cloud alias name, such as @mysite.dev.', - array('!missing' => $missing) - ); - drush_set_error('ACAPI_SITE_REQUIRED', $error); - } - return array(); - } - - return $params; -} - -/** - * Call an Acquia Cloud API resource. - * - * @param $method - * The HTTP method; e.g. GET. - * @param $resource - * The API function to call; e.g. /sites/:realm::site. - * @param $args = array() - * An array of argument values for the resource; e.g: array(':site' => - * 'mysite'). - * @params $params = array() - * An array of query parameters to append to the URL. - * @params $body = array() - * An array of parameters to include in the POST body in JSON format. - * @params $options = array() - * An array of options: - * - display (TRUE): whether to output the result to stdout - * - result_stream: open stream to which to write the response body - * - redirect: the maximum number of redirects to allow - */ -function acapi_call($method, $resource, $args, $params = array(), $body = array(), $options = array()) { - $default_options = array( - 'display' => TRUE, - ); - $options = array_merge($default_options, $options); - - $debug = drush_get_option('debug', FALSE); - $verbose = drush_get_option('verbose', FALSE); - $simulate = drush_get_option('simulate', FALSE); - $format = acapi_get_option('format'); - - // Build the API call URL. - $url = acapi_get_option('endpoint'); - $url .= acapi_dt($resource, $args); - $url .= '.json'; - - foreach ($params as $k => $v) { - if (is_array($v)) { - unset($params[$k]); - foreach ($v as $key => $val) { - $params["$k-$key"] = "$k%5B%5D=" . urlencode($val); - } - } - else { - $params[$k] = "$k=" . urlencode($v); - } - } - - $url .= '?' . implode('&', $params); - - $creds = acapi_get_creds(); - if (!$creds) { - return FALSE; - } - - // Build the body. - $json_body = json_encode($body); - - $display = "curl -X $method '$url'"; - if ($debug) { - $display .= " ($creds)"; - } - if ($debug || $verbose || $simulate) { - drush_print($display, 0, STDERR); - if (!empty($body)) { - drush_print(" $json_body", 0, STDERR); - } - } - - if ($simulate) { - return; - } - - $headers = array(); - $ch = curl_init($url); - // Basic request settings - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); - curl_setopt($ch, CURLOPT_USERAGENT, basename(__FILE__)); - if (!empty($options['result_stream'])) { - curl_setopt($ch, CURLOPT_FILE, $options['result_stream']); - } - else { - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); - } - // User authentication - curl_setopt($ch, CURLOPT_HTTPAUTH, TRUE); - curl_setopt($ch, CURLOPT_USERPWD, $creds); - // SSL - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, preg_match('@^https:@', acapi_get_option('endpoint'))); - curl_setopt($ch, CURLOPT_CAINFO, acapi_get_option('cainfo')); - // Redirects - if (!empty($options['redirect'])) { - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); - curl_setopt($ch, CURLOPT_MAXREDIRS, $options['redirect']+1); - } - /* Body - We need to set a Content-Length header even on empty POST requests, or the webserver - will throw a 411 Length Required. - */ - - curl_setopt($ch, CURLOPT_POSTFIELDS, $json_body); - $headers[] = 'Content-Type: application/json;charset=utf-8'; - $headers[] = 'Content-Length: ' . strlen($json_body); - // Headers - if (!empty($headers)) { - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - } - // Debugging - curl_setopt($ch, CURLOPT_VERBOSE, $debug); - // Go - $content = curl_exec($ch); - if (curl_errno($ch) > 0) { - return drush_set_error('ACAPI_CURL_ERROR', dt('Error accessing @url: @err', array('@url' => $url, '@err' => curl_error($ch)))); - } - - $result = json_decode($content); - $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if (!empty($format)) { - drush_print(drush_format($result, NULL, $format)); - } - else if ($options['display']) { - if (is_array($result)) { - foreach ($result as $item) { - if (! is_scalar($item)) { - drush_print_table(drush_key_value_to_array_table(acapi_convert_values($item))); - } - else { - drush_print($item); - } - } - } - else { - if ($method == 'POST') { - // All POST actions return a task. Display something helpful. - drush_log(dt('Task @taskid started.', array('@taskid' => $result->id)), 'ok'); - } - else { - drush_print_table(drush_key_value_to_array_table(acapi_convert_values($result))); - } - } - } - - if ($status != 200) { - return drush_set_error('ACAPI_HTTP_STATUS_' . $status, dt('API status code @status', array('@status' => $status))); - } - - return array($status, $result); -} - -/** - * Return Acquia Cloud API credentials as username:password, or log an error - * if they are unavailable. - */ -function acapi_get_creds() { - $user = acapi_get_option('email'); - $pass = acapi_get_option('key'); - if (empty($user) || empty($pass)) { - return drush_set_error('ACAPI_CREDS_MISSING', dt('Email and api key required; specify --email/--key or run drush ac-api-login')); - } - return "$user:$pass"; -} - -/** - * Convert NULL, array and object values to appropriate string representations - * so they are printed correctly. - */ -function acapi_convert_values($arr) { - foreach ($arr as $k => $v) { - if (!isset($v)) { - $arr->{$k} = ''; - } - elseif (is_array($v) || is_object($v)) { - $arr->{$k} = '...'; - } - } - return (array) $arr; -} - -/** - * dt() wrapper that URL-encodes all substituted parameters that begin with - * a colon (':'). - */ -function acapi_dt($string, $args = array()) { - foreach ($args as $k => $v) { - if ($k[0] == ':') { - $args[$k] = urlencode($v); - } - } - return dt($string, $args); -} diff --git a/7.2/config/.drush/cloudapi.acquia.com.pem b/7.2/config/.drush/cloudapi.acquia.com.pem deleted file mode 100644 index a5da6cb6..00000000 --- a/7.2/config/.drush/cloudapi.acquia.com.pem +++ /dev/null @@ -1,98 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGvzCCBaegAwIBAgIQD1pdYvA5l5l2ezeWEsVwNTANBgkqhkiG9w0BAQUFADBm -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBDQS0zMB4XDTEyMDgyNzAwMDAwMFoXDTE1MDkwOTEyMDAwMFowfzELMAkGA1UE -BhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEzARBgNVBAcTCkJ1cmxpbmd0 -b24xEzARBgNVBAoTCkFjcXVpYSBJbmMxFzAVBgNVBAsTDkFjcXVpYSBIb3N0aW5n -MRUwEwYDVQQDDAwqLmFjcXVpYS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCVmcKz54qAbNO9NyP3hA96gzGRORSB9Bz4EOuFM1kfD/gvZoXoqk87 -NC/jPrvPAxNZDJ33IrO08WDWxBVi6UQ7Q9YYFgU1mm0se4qjwld7dtziDnaq2zXc -x4Q+AVfBj92w+RsVDWA2mNtMSaDePqXGzLOz4muUCA5oCtMg2QD+XIEp1yt13nQb -5nW6PbY6kHHviepNX3wj7TdqTLNPdCVcK9BJz2YTjfHtBBxnGxR/m804RL9fYWKz -r6XUbbf+gcKFwMqRI54Qs5cD20jmYnYgXFYwAx6HvOH9Lr969xMq5ePIQkKc66kG -Opie8unjAQRgd6T7lL+zvpaen1UhJruhAgMBAAGjggNOMIIDSjAfBgNVHSMEGDAW -gBRQ6nOJ2yn7EI+e5QEg1N55mUiD9zAdBgNVHQ4EFgQUJuo64qCLWo0Fvi6AsOYG -IiNj+s8wIwYDVR0RBBwwGoIMKi5hY3F1aWEuY29tggphY3F1aWEuY29tMA4GA1Ud -DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwYQYDVR0f -BFowWDAqoCigJoYkaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL2NhMy1nMjcuY3Js -MCqgKKAmhiRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vY2EzLWcyNy5jcmwwggHE -BgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwBATCCAaQwOgYIKwYBBQUHAgEWLmh0 -dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFk -BggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBz -ACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBz -ACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBD -AGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBp -AG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBo -ACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBl -ACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAg -AHIAZQBmAGUAcgBlAG4AYwBlAC4wewYIKwYBBQUHAQEEbzBtMCQGCCsGAQUFBzAB -hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRQYIKwYBBQUHMAKGOWh0dHA6Ly9j -YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VDQS0zLmNy -dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQCwbrUX+rSNdiS1ivce -pI3gzzlOG9FjcPPTfoLD/+eiysiTKe7d3Hb9urHZWGuEWWxXRl+6tND3TAt8ONpM -ZCs+nls+qvspG8ApQMZLgak4kb4+CaLMi6ZpQ2JTI89iChonOjI0OP6dVDbGbTvV -a7LKkiZw1xJNYUnhIiqOE1B9Ww3SUUpe1TLmiAYiiYiiuiyBxMz48sARHXnlEbWw -0dHOnU51tvG1zkv9sATYwv3LtIjf9pmXZnS1W0j5Ap0JeFbgFywVi0zSpbix0fzr -pyArWETFKxirR2bCVFH0sVZ2q3/sznguUx1QgH+tese0hqHm2epRORcOi69mcDYi -Cf5v ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR -CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv -KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 -BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf -1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs -zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d -32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w -ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm -MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB -hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln -aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl -cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME -GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB -INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a -vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j -CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X -dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE -JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY -Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K ------END CERTIFICATE----- diff --git a/7.2/config/php/xhprof.ini b/7.2/config/php/xhprof.ini new file mode 100644 index 00000000..6c2e4dc9 --- /dev/null +++ b/7.2/config/php/xhprof.ini @@ -0,0 +1,2 @@ +[xhprof] +extension="xhprof.so" diff --git a/7.2/config/supervisor/supervisord-code-server.conf.tmpl b/7.2/config/supervisor/supervisord-code-server.conf.tmpl index 14da7eee..30f5cc9a 100644 --- a/7.2/config/supervisor/supervisord-code-server.conf.tmpl +++ b/7.2/config/supervisor/supervisord-code-server.conf.tmpl @@ -1,6 +1,6 @@ # VS Code Server web IDE [program:code-server] # Using bash -lc here to load docker user profile (necessary for nvn/node to initialize) -command = gosu docker bash -lc '/usr/local/bin/code-server --user-data-dir=/home/docker/code-server -p 8080 /var/www --allow-http {{ if not (getenv "PASSWORD") }}--no-auth{{ end }}' +command = gosu docker bash -lc '/usr/bin/code-server --user-data-dir=/home/docker/code-server --disable-telemetry --bind-addr 0.0.0.0:8080 {{ if not (getenv "PASSWORD") }}--auth none{{ end }} /var/www' stdout_logfile = /var/log/supervisor/code-server-stdout stderr_logfile = /var/log/supervisor/code-server-stderr diff --git a/7.2/opt/code-server/settings.json b/7.2/opt/code-server/settings.json index 4dc66cab..a1b14cbd 100644 --- a/7.2/opt/code-server/settings.json +++ b/7.2/opt/code-server/settings.json @@ -22,5 +22,15 @@ "port": 9000 } ] + }, + // File associations + "files.associations": { + "*.inc": "php", + "*.module": "php", + "*.install": "php", + "*.theme": "php", + "*.tpl.php": "php", + "*.test": "php", + "*.php": "php" } } diff --git a/7.2/php-modules.txt b/7.2/php-modules.txt index 61d7753c..d2df798c 100644 --- a/7.2/php-modules.txt +++ b/7.2/php-modules.txt @@ -52,6 +52,7 @@ sqlite3 sqlsrv ssh2 standard +sysvsem tokenizer xml xmlreader diff --git a/7.2/ping-web.sh b/7.2/ping-web.sh new file mode 100755 index 00000000..6a5ba7b8 --- /dev/null +++ b/7.2/ping-web.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Notify web container about started fin exec +if [[ "${WEB_KEEPALIVE}" != "0" ]] && [[ "${VIRTUAL_HOST}" != "" ]] +then + while true + do + curl -s -m 1 ${VIRTUAL_HOST}/exec_in_progress_inside_cli >/dev/null 2>&1 + sleep ${WEB_KEEPALIVE} + done +fi diff --git a/7.2/startup.sh b/7.2/startup.sh index 58c0394a..bd4d3a7e 100755 --- a/7.2/startup.sh +++ b/7.2/startup.sh @@ -28,6 +28,18 @@ xdebug_enable () ln -s /opt/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/ } +xhprof_enable () +{ + echo-debug "Enabling xhprof..." + cp /opt/docker-php-ext-xhprof.ini /usr/local/etc/php/conf.d/ + # Output directory to the ini file + echo "xhprof.output_dir = ${XHPROF_OUTPUT_DIR}" >> /usr/local/etc/php/conf.d/docker-php-ext-xhprof.ini + # Try to create directory if it doesn't exist + mkdir ${XHPROF_OUTPUT_DIR} || true + # Change owner of directory + chown docker:docker ${XHPROF_OUTPUT_DIR} +} + ide_mode_enable () { echo-debug "Enabling web IDE..." @@ -101,22 +113,6 @@ convert_secrets () done } -# Acquia Cloud API login -acquia_login () -{ - echo-debug "Authenticating with Acquia..." - # This has to be done using the docker user via su to load the user environment - # Note: Using 'su -l' to initiate a login session and have .profile sourced for the docker user - local command="drush ac-api-login --email='${ACAPI_EMAIL}' --key='${ACAPI_KEY}' --endpoint='https://cloudapi.acquia.com/v1' && drush ac-site-list" - local output=$(su -l docker -c "${command}" 2>&1) - if [[ $? != 0 ]]; then - echo-debug "ERROR: Acquia authentication failed." - echo - echo "$output" - echo - fi -} - # Pantheon (terminus) login terminus_login () { @@ -156,6 +152,9 @@ convert_secrets # Enable xdebug [[ "$XDEBUG_ENABLED" != "" ]] && [[ "$XDEBUG_ENABLED" != "0" ]] && xdebug_enable +# Enable xdebug +[[ "$XHPROF_ENABLED" != "" ]] && [[ "$XHPROF_ENABLED" != "0" ]] && xhprof_enable + # Enable web IDE [[ "$IDE_ENABLED" != "" ]] && [[ "$IDE_ENABLED" != "0" ]] && ide_mode_enable @@ -172,8 +171,6 @@ chown "${HOST_UID:-1000}:${HOST_GID:-1000}" /var/www # These have to happen after the home directory permissions are reset, # otherwise the docker user may not have write access to /home/docker, where the auth session data is stored. -# Acquia Cloud API config -[[ "$ACAPI_EMAIL" != "" ]] && [[ "$ACAPI_KEY" != "" ]] && acquia_login # Automatically authenticate with Pantheon if Terminus token is present [[ "$TERMINUS_TOKEN" != "" ]] && terminus_login diff --git a/7.3/Dockerfile b/7.3/Dockerfile index 42bfdd3c..c19b23ef 100644 --- a/7.3/Dockerfile +++ b/7.3/Dockerfile @@ -147,7 +147,7 @@ RUN set -xe; \ libicu63 \ libjpeg62-turbo \ libldap-2.4-2 \ - libmagickcore-6.q16-6 \ + libmagickcore-6.q16-*-extra \ libmagickwand-6.q16-6 \ libmemcached11 \ libmemcachedutil2 \ @@ -197,6 +197,7 @@ RUN set -xe; \ ssh2 \ xsl \ zip \ + sysvsem \ ;\ pecl update-channels; \ pecl install >/dev/null /dev/null; \ - mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/drush/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ + mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/backdrop-drush-extension/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ drush cc drush # Node.js (installed as user) ENV \ - NVM_VERSION=0.35.0 \ - NODE_VERSION=12.13.0 \ - YARN_VERSION=1.19.1 + NVM_VERSION=0.35.3 \ + NODE_VERSION=12.18.1 \ + YARN_VERSION=1.22.4 # Don't use -x here, as the output may be excessive RUN set -e; \ # NVM and a defaut Node.js version @@ -316,8 +322,8 @@ RUN set -e; \ # Ruby (installed as user) ENV \ - RVM_VERSION_INSTALL=1.29.9 \ - RUBY_VERSION_INSTALL=2.6.5 + RVM_VERSION_INSTALL=1.29.10 \ + RUBY_VERSION_INSTALL=2.7.1 # Don't use -x here, as the output may be excessive RUN set -e; \ # Public GPG servers are not realiable, so downloading keys from rvm.io instead. @@ -356,8 +362,8 @@ RUN set -e; \ # Python (installed as user) ENV \ - PYENV_VERSION_INSTALL=1.2.14 -# PYTHON_VERSION_INSTALL=3.7.0 + PYENV_VERSION_INSTALL=1.2.18 +# PYTHON_VERSION_INSTALL=3.8.3 RUN set -xe; \ git clone --depth 1 -b v${PYENV_VERSION_INSTALL} https://github.com/pyenv/pyenv.git $HOME/.pyenv; \ rm -rf $HOME/.pyenv/.git; \ @@ -374,22 +380,26 @@ RUN set -xe; \ # pyenv install ${PYTHON_VERSION_INSTALL}; \ # pyenv global ${PYTHON_VERSION_INSTALL} +# Notify web container about started fin exec +RUN echo '(/opt/ping-web.sh &)' >> $HOME/.profile + USER root SHELL ["/bin/sh", "-c"] # Copy configs and scripts -COPY --chown=docker:docker config/.drush /home/docker/.drush COPY --chown=docker:docker config/.terminus /home/docker/.terminus COPY --chown=docker:docker config/.ssh /home/docker/.ssh COPY config/supervisor /etc/supervisor/conf.d COPY startup.sh /opt/startup.sh COPY healthcheck.sh /opt/healthcheck.sh +COPY ping-web.sh /opt/ping-web.sh # PHP default settings, global overrides and fpm overrides # PHP_VERSION is set upstream. The source code for the exact PHP version may not be available in Github # TODO: For now, will just hardcoded a specific version available on https://github.com/php/php-src/ -ADD https://raw.githubusercontent.com/php/php-src/php-7.3.9/php.ini-development /usr/local/etc/php/php.ini +ADD https://raw.githubusercontent.com/php/php-src/php-7.3.19/php.ini-development /usr/local/etc/php/php.ini COPY config/php/zz-php.ini /usr/local/etc/php/conf.d/zz-php.ini COPY config/php/xdebug.ini /opt/docker-php-ext-xdebug.ini +COPY config/php/xhprof.ini /opt/docker-php-ext-xhprof.ini COPY config/php/zz-php-fpm.conf /usr/local/etc/php-fpm.d/zz-php-fpm.conf ENV \ @@ -402,8 +412,12 @@ ENV \ # Default values for HOST_UID and HOST_GUI to match the default Ubuntu user. These are used in startup.sh HOST_UID=1000 \ HOST_GID=1000 \ + # Delay in seconds between pings web from cli, while running fin exec. 0 - disabled + WEB_KEEPALIVE=0 \ # xdebug disabled by default - XDEBUG_ENABLED=0 + XDEBUG_ENABLED=0 \ + XHPROF_ENABLED=0 \ + XHPROF_OUTPUT_DIR=/tmp/xhprof # TODO: [v3] remove and set these via docker-compose EXPOSE 9000 @@ -431,22 +445,21 @@ USER docker ARG HOME=/home/docker ENV \ - # TODO: 2.x versions do not yet work with xdebug - CODE_SERVER_VERSION=1.1156-vsc1.33.1 \ + CODE_SERVER_VERSION=3.4.1 \ VSCODE_HOME="${HOME}/code-server" \ VSCODE_EXT_DIRECTORY="${HOME}/code-server/extensions" \ VSCODE_XDEBUG_VERSION=1.13.0 \ - VSCODE_GITLENS_VERSION=9.9.3 + VSCODE_GITLENS_VERSION=10.2.2 # Install code-server RUN \ set -xe; \ - curl -fsSL "https://github.com/cdr/code-server/releases/download/${CODE_SERVER_VERSION}/code-server${CODE_SERVER_VERSION}-linux-x64.tar.gz" -o /tmp/code-server.tar.gz; \ - tar -zxv --file=/tmp/code-server.tar.gz --directory=/tmp --strip-components=1; \ - sudo mv /tmp/code-server /usr/local/bin/; rm -rf /tmp/*.* + curl -fsSL "https://github.com/cdr/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server_${CODE_SERVER_VERSION}_amd64.deb" -o /tmp/code-server_amd64.deb; \ + sudo dpkg -i /tmp/code-server_amd64.deb; \ + rm -rf /tmp/*.* # Settings and scripts -COPY opt/code-server /opt/code-server +COPY --chown=docker:docker opt/code-server /opt/code-server # Install extensions RUN \ diff --git a/7.3/Makefile b/7.3/Makefile index 0d425226..cbe3787c 100644 --- a/7.3/Makefile +++ b/7.3/Makefile @@ -2,8 +2,9 @@ -include env_make VERSION ?= 7.3 -TAG_APPENDIX ?= -php$(VERSION) -BUILD_TAG = build-$(TAG_APPENDIX) + +SOFTWARE_VERSION ?= php$(VERSION) +BUILD_TAG = build-$(SOFTWARE_VERSION) REPO = docksal/cli NAME = docksal-cli-$(VERSION) CWD = $(shell pwd) @@ -16,7 +17,7 @@ VOLUMES += -v /home/docker .PHONY: build test push shell run start stop logs clean release build: - docker build --cache-from=$(REPO):edge$(TAG_APPENDIX) -t $(REPO):$(BUILD_TAG) . + docker build --cache-from=$(REPO):edge-$(SOFTWARE_VERSION) -t $(REPO):$(BUILD_TAG) . test: IMAGE=$(REPO):$(BUILD_TAG) NAME=$(NAME) VERSION=$(VERSION) ../tests/test.bats diff --git a/7.3/config/.drush/acapi.drush.inc b/7.3/config/.drush/acapi.drush.inc deleted file mode 100644 index b04b8a3c..00000000 --- a/7.3/config/.drush/acapi.drush.inc +++ /dev/null @@ -1,1227 +0,0 @@ - 'Retrieve Drush aliases for all accessible Acquia Cloud sites.', - 'arguments' => array( - ), - 'options' => $options, - ); - - // Most of the commands below use drush_acapi_ac_generic_callback, which - // translates their 'method', 'resource', and 'arguments' properties into - // the appropriate API call, executes it, and displays the result. - - ////////////////////////////////////////////////////////////////////// - // Login - ////////////////////////////////////////////////////////////////////// - $items['ac-api-login'] = array( - 'description' => 'Store Acquia Cloud API credentials and configuration information.', - 'arguments' => array( - ), - 'options' => $options + array( - 'reset' => array( - 'description' => 'Discard any existing stored values from a previous call. Without this option, new values will be merged with existing values.' - ), - ), - ); - - ////////////////////////////////////////////////////////////////////// - // Sites and environments - ////////////////////////////////////////////////////////////////////// - $items['ac-site-list'] = array( - 'description' => 'List all sites available to the current user.', - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-site-info'] = array( - 'description' => 'Show information about a site.', - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-list'] = array( - 'description' => "List a site's environments.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-info'] = array( - 'description' => "Show information about a site environment.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-install'] = array( - 'description' => "Install a Drupal distribution from a pre-selected list, URL, or Drush Makefile.", - 'arguments' => array( - 'type' => 'Type of distro source: distro_url or make_url.', - 'source' => 'A URL to a distro or URL to a Drush make file.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/install/:type', - 'params' => array('source'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-environment-livedev'] = array( - 'description' => "Configure Live Development on a site environment.", - 'arguments' => array( - 'action' => 'Action to take. \'enable\' or \'disable\' live development.', - 'discard' => 'When action is \'disable\', set to 1 to discard uncommitted changes.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/livedev/:action', - 'params' => array('discard'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Servers - ////////////////////////////////////////////////////////////////////// - $items['ac-server-list'] = array( - 'description' => "List servers for a site and environment.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/servers', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-server-info'] = array( - 'description' => "Show information about a server.", - 'arguments' => array( - 'server' => 'Server name.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/servers/:server', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-server-php-procs'] = array( - 'description' => "Calculate the php max procs value based on possible memory limits and apc shm settings.", - 'arguments' => array( - 'server' => 'Server name.', - 'memory_limits' => 'Memory limits.', - 'apc_shm' => 'APC shm settings.' - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/servers/:server/php-procs', - 'params_array' => array( - 'memory_limits', - 'apc_shm', - ), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Databases - ////////////////////////////////////////////////////////////////////// - $items['ac-database-list'] = array( - 'description' => "List a site's databases.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/dbs', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-info'] = array( - 'description' => "Show information about a site database.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/dbs/:db', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-add'] = array( - 'description' => 'Add a database.', - 'arguments' => array( - 'db' => 'The database.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/dbs', - 'body_fields' => array('db'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-delete'] = array( - 'description' => 'Delete a database.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/dbs/:db', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-list'] = array( - 'description' => "List a site environment's database instances.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-info'] = array( - 'description' => "Show information about a site environment's database instance.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-list'] = array( - 'description' => "List a site environment's database instance backups.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-info'] = array( - 'description' => "Show information about a site environment's database instance backup.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backup' => 'Backup id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backup', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-download'] = array( - 'description' => "Download a site environment database instance backup.", - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backup' => 'Backup id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backup/download', - 'callback' => 'drush_acapi_ac_database_backup_download', - 'options' => array('result-file' => - array( - 'description' => 'Save to a file; specify the full path in which to store the backup. If not provided, the backup is sent the standard output.', - 'example-value' => '/path/to/file', - )) + $options, - ); - $items['ac-database-instance-backup'] = array( - 'description' => 'Create a database instance backup.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-restore'] = array( - 'description' => 'Restore a database instance backup.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backupid' => 'The backup id.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backupid/restore', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-instance-backup-delete'] = array( - 'description' => 'Delete a database instance backup.', - 'arguments' => array( - 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', - 'backupid' => 'The backup id.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backupid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Tasks - ////////////////////////////////////////////////////////////////////// - // API resource /sites/:realm::site/tasks returns an array of task records, - // unlike other list resources which return a list of ids, so we need a - // non-standard callback. We'd have to make N API calls to implement this the - // way other data types work, which I think shows that the returning a list of - // records is better. - $items['ac-task-list'] = array( - 'description' => "List a site's tasks.", - 'arguments' => array( - ), - 'options' => array( - 'state' => array( - 'description' => 'The task state to retrieve. If not specified, retrieve all tasks for the site.', - 'example-value' => 'done', - ), - 'days' => array( - 'description' => 'The number of days worth of tasks to retrieve. If not specified, retrieve, at a maximum, 7 days worth of tasks.', - 'example-value' => '5', - ), - 'limit' => array( - 'description' => 'The maximum number of tasks to retrieve. If not specified, retrieve a maximum of 50 tasks. The maximum value allowed is 1000.', - 'example-value' => '500' - ), - ) + $options, - ); - - $items['ac-task-info'] = array( - 'description' => "Show information about a site task.", - 'arguments' => array( - 'task' => 'The task id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/tasks/:task', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Workflow - ////////////////////////////////////////////////////////////////////// - $items['ac-code-deploy'] = array( - 'description' => 'Deploy code from one site environment to another.', - 'arguments' => array( - 'target' => 'The target environment.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/code-deploy/:env/:target', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-code-path-deploy'] = array( - 'description' => 'Deploy a specific branch or tag in an environment.', - 'arguments' => array( - 'path' => 'The branch or tag to deploy.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/code-deploy', - 'params' => array('path'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-database-copy'] = array( - 'description' => 'Copy a database from one site environment to another.', - 'arguments' => array( - 'db' => 'The database.', - 'target' => 'The target environment.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/dbs/:db/db-copy/:env/:target', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-files-copy'] = array( - 'description' => 'Copy user-uploaded files from one site environment to another.', - 'arguments' => array( - 'target' => 'The target environment.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/files-copy/:env/:target', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-move'] = array( - 'description' => 'Move a domain from one site environment to another.', - 'arguments' => array( - 'target' => 'The target environment.', - 'domains' => 'Comma separated list of domains, or * for all.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/domain-move/:env/:target', - 'body_fields_array' => array('domains'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // SSH keys - ////////////////////////////////////////////////////////////////////// - $items['ac-sshkey-list'] = array( - 'description' => "List a site's SSH keys.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/sshkeys', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-sshkey-info'] = array( - 'description' => "Show information about a site SSH key.", - 'arguments' => array( - 'sshkeyid' => 'SSH key id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/sshkeys/:sshkeyid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-sshkey-add'] = array( - 'description' => 'Add an SSH key to a site.', - 'arguments' => array( - 'ssh_pub_key' => 'File containing the SSH public key.', - 'nickname' => 'The SSH key nickname.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/sshkeys', - 'params' => array('nickname'), - 'body_fields_path' => array('ssh_pub_key'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options + array( - 'shell_access' => - array('description' => 'Set shell access for this key. (true or false)', - 'param' => 'shell_access',), - 'vcs_access' => - array('description' => 'Set git access for this key. (true or false)', - 'param' => 'vcs_access',), - 'blacklist' => - array('description' => 'Array containing a list of environments to disallow access for this key', - 'param' => 'blacklist',), - ), - ); - $items['ac-sshkey-delete'] = array( - 'description' => 'Delete an SSH key from a site.', - 'arguments' => array( - 'sshkeyid' => 'The SSH key id to delete.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/sshkeys/:sshkeyid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // SVN users - ////////////////////////////////////////////////////////////////////// - $items['ac-svnuser-list'] = array( - 'description' => "List a site's SVN users.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/svnusers', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-svnuser-info'] = array( - 'description' => "Show information about a site SVN user.", - 'arguments' => array( - 'svnuserid' => 'SVN user id.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/svnusers/:svnuserid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-svnuser-add'] = array( - 'description' => 'Add an SVN user to a site.', - 'arguments' => array( - 'username' => 'SVN username.', - 'password' => 'SVN password.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/svnusers/:username', - 'body_fields' => array('password'), - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-svnuser-delete'] = array( - 'description' => 'Delete an SVN user from a site.', - 'arguments' => array( - 'svnuserid' => 'The SVN user id to delete.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/svnusers/:svnuserid', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - ////////////////////////////////////////////////////////////////////// - // Domains - ////////////////////////////////////////////////////////////////////// - $items['ac-domain-list'] = array( - 'description' => "List a site's domains.", - 'arguments' => array( - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/domains', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-info'] = array( - 'description' => "Show information about a site domain.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'GET', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-add'] = array( - 'description' => "Add a domain name to an environment.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'POST', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-delete'] = array( - 'description' => "Delete a domain name from an environment.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - $items['ac-domain-purge'] = array( - 'description' => "Purge a domain from the Varnish cache.", - 'arguments' => array( - 'domain' => 'Domain name.', - ), - 'method' => 'DELETE', - 'resource' => '/sites/:realm::site/envs/:env/domains/:domain/cache', - 'callback' => 'drush_acapi_ac_generic_callback', - 'options' => $options, - ); - - foreach ($items as $command => $item) { - $items[$command]['orig_options'] = $item['options']; - if (DRUSH_VERSION < 5) { - // Make our command structure work with previous versions of drush while - // still supporting the way we handle arguments, options, and defaults. - $items[$command]['argument-description'] = $item['arguments']; - foreach ($item['options'] as $option => $info) { - $items[$command]['options'][$option] = $info['description']; - } - } - else { - $items[$command]['handle-remote-commands'] = TRUE; - } - - $items[$command]['bootstrap'] = DRUSH_BOOTSTRAP_DRUSH; - $items[$command]['required-arguments'] = TRUE; - } - - return $items; -} - -function acapi_drush_help($section) { - switch ($section) { - case 'meta:acapi:title': - return dt('Acquia commands'); - case 'meta:acapi:summary': - return dt('Acquia Cloud commands.'); - case 'drush:ac-api-login': - $file = '$HOME/.acquia/cloudapi.conf'; - return dt("Store Acquia Cloud API credentials and endpoint information. - -This command stores default email, key, and optionally endpoint values for future Acquia Cloud API commands in @file. File location can be altered with the ac-config option on all Acquia Cloud commands.", array('@file' => $file)); - } -} - -/** - * Write content to a file if the file does not already have that content. - * Log a message when the file is updated. - * - * @param $path - * The full path to update. - * @param $content - * The content to write. - * @return - * TRUE if the file is updated, FALSE otherwise. - */ -function drush_acapi_update_file($path, $content) { - $current = @file_get_contents($path); - if ($current != $content) { - file_put_contents($path, $content); - drush_log(dt('Updated %path.', array('%path' => $path)), 'ok'); - return TRUE; - } - return FALSE; -} - -/** - * Update the Drush aliases for all accessible Cloud sites. - */ -function drush_acapi_acquia_update() { - list($status, $all_aliases) = acapi_call('GET', '/me/drushrc', array(), array(), array(), array('display' => FALSE)); - if ($status == 200) { - foreach ($all_aliases as $realm_site => $aliases) { - list($realm, $site) = explode(':', $realm_site); - $file = _drush_config_file('home.drush', "$site.aliases"); - $content = "'; - - // Preserve existing defaults by loading them unless the user said not to. - $acapi_options = array(); - if (! drush_get_option('reset', FALSE)) { - $acapi_options = acapi_load_options(); - } - - // Collect specified values for each option. - foreach ($defaults as $k => $info) { - // Get the value from the command line, if provided. - $option = drush_get_option($k, NULL); - - if (!isset($option)) { - // No value on cli. The default is the previous value, if any, or what - // the option declaration specifies. - $default = isset($acapi_options[$k]) ? $acapi_options[$k] : $info['default_value']; - - // Prompt for some options, and just use the default for the others. - if (!empty($info['prompt'])) { - // Hide the existing key, if there is one. - if ($info['prompt'][3] && !empty($default)) { - $info['prompt'][1] = $hide_default; - } - else { - $info['prompt'][1] = $default; - } - $option = call_user_func_array('drush_prompt', $info['prompt']); - if ($option == $hide_default) { - $option = $default; - } - } - else { - $option = $default; - } - } - - // Only save non-default values so we can update the defaults without pain. - if ($option == $info['default_value']) { - unset($acapi_options[$k]); - } - else { - $acapi_options[$k] = $option; - } - } - - // We can't store the default path to the config file in the config file. - unset($acapi_options['ac-config']); - - acapi_save_options($acapi_options); -} - -/** - * Save acapi options to the config file. - * - * @param $options - * Hash of options to save. - */ -function acapi_save_options($options) { - $file = acapi_get_option_file(); - $dir = dirname($file); - $dt_args = array('@file' => $file, '@dir' => $dir); - $verbose = drush_get_option('verbose', FALSE); - $simulate = drush_get_option('simulate', FALSE); - if ($verbose || $simulate) { - drush_log(dt('Storing Acquia Cloud API defaults in @file.', $dt_args), 'ok'); - } - if ($verbose) { - drush_print($output); - } - if (! $simulate) { - $output = json_encode($options) . "\n"; - if (!is_dir($dir)) { - if (@mkdir($dir) === FALSE) { - return drush_set_error('ACAPI_CANNOT_WRITE_LOGIN', dt('Cannot create Acquia Cloud API defaults directory @dir.', $dt_args)); - } - } - if (file_put_contents($file, $output) === FALSE) { - return drush_set_error('ACAPI_CANNOT_WRITE_LOGIN', dt('Cannot write to Acquia Cloud API defaults file @file.', $dt_args)); - } - } -} - -/** - * Get saved values for acapi common options from the ac-config file. - * - * @returns - * A hash of saved acapi option names and values. - */ -function acapi_load_options() { - $ret = array(); - $file = acapi_get_option_file(); - $contents = @file_get_contents($file); - if ($contents !== FALSE) { - // Parse old-style Drush PHP config files, and save them in the new format. - if (preg_match('@^<\?php@', $contents) && preg_match_all('@^\$options\[\'acapi\'\]\[\'(\w+)\'\]\s*=\s*\'(.*)\';$@m', $contents, $matches, PREG_SET_ORDER)) { - foreach ($matches as $match) { - $ret[$match[1]] = $match[2]; - } - acapi_save_options($ret); - } - else { - $data = @json_decode($contents, TRUE); - if (is_array($data)) { - $ret = $data; - } - else { - drush_set_error('ACAPI_INVALID_CONF', dt('Acquia Cloud API config file @file is invalid. Run drush ac-api-login.', array('@file' => $file))); - } - } - } - return $ret; -} - -/** - * Return the path for the acapi option file, either --ac-config or - * $HOME/.acquia/cloudapi.conf. - */ -function acapi_get_option_file() { - $defaults = acapi_common_options(); - return drush_get_option('ac-config', $defaults['ac-config']['default_value']); -} - -////////////////////////////////////////////////////////////////////// -// Custom callbacks -////////////////////////////////////////////////////////////////////// - -/** - * List a site's tasks. See the command definition for why this function is - * different. - */ -function drush_acapi_ac_database_backup_download($db, $backupid) { - $command = drush_get_context('command'); - list($site, $env) = acapi_get_site(); - $simulate = drush_get_option('simulate', FALSE); - $result_file = drush_get_option('result-file', ''); - // Similar to drush_sql_build_dump_command(). If the user has set - // $options['result-file'] = TRUE, then we will generate an SQL dump file in - // an automatically-generated backup directory based on site and env values. - if ($result_file === TRUE) { - // User did not pass a specific value for --result-file. Make one. - $backup = drush_include_engine('version_control', 'backup'); - $backup_dir = $backup->prepare_backup_dir($site . '.' . $env); - if (empty($backup_dir)) { - $backup_dir = "/tmp"; - } - $result_file = $backup_dir . '/' . $db . '-' . $backupid .'.sql.gz'; - } - if ($result_file == '') { - $fp = STDOUT; - } - else { - $fp = fopen($result_file, 'w'); - if ($fp == NULL) { - return drush_set_error('ACAPI_ENOENT', dt('Cannot write to result file @result_file.', array('@name' => $result_file))); - } - } - - $api_args = acapi_get_site_args() + array( - ':db' => $db, - ':backup' => $backupid, - ); - list($status, $result) = acapi_call( - $command['method'], - $command['resource'], - $api_args, - array(), - array(), - array('result_stream' => $fp, 'redirect' => 1, 'display' => FALSE) - ); -} - -/** - * List a site's tasks. See the command definition for why this function is - * different. - */ -function drush_acapi_ac_task_list() { - $api_args = acapi_get_site_args(); - $format = acapi_get_option('format'); - $state = drush_get_option('state', NULL); - $days = drush_get_option('days', NULL); - $limit = drush_get_option('limit', NULL); - $params = array(); - if (isset($state)) { - $params['state'] = $state; - } - if (isset($days)) { - $params['days'] = $days; - } - if (isset($limit)) { - $params['limit'] = $limit; - } - - list($status, $result) = acapi_call( - 'GET', - '/sites/:realm::site/tasks', - $api_args, - $params, - array(), - array('display' => !empty($format)) - ); - - $simulate = drush_get_option('simulate', FALSE); - if ($simulate) { - return; - } - - if (empty($format)) { - $display = array(); - foreach ($result as $id => $task) { - $display[$task->id] = $task->description; - } - drush_print_table(drush_key_value_to_array_table($display)); - } -} - -////////////////////////////////////////////////////////////////////// -// Utility functions -////////////////////////////////////////////////////////////////////// - -/** - * Define common options for Acquia Cloud API commands, and their defaults. - */ -function acapi_common_options() { - $options = array( - 'email' => array( - 'description' => 'Email address for your Acquia Network user account', - 'default_value' => '', - 'prompt' => array('Email', NULL, TRUE, FALSE), - 'example-value' => 'example@acquia.com', - ), - 'key' => array( - 'description' => 'Private Cloud API key for your Acquia Network user account', - 'default_value' => '', - 'prompt' => array('Key', NULL, TRUE, TRUE), - 'example-value' => 'apikey', - ), - 'acapi-conf-path' => array( - 'description' => 'Acquia Cloud API config files location. If not specified config will be loaded from $HOME/.drush', - 'default_value' => '', - 'example-value' => '/home/user/acapi-site-configs', - ), - 'ac-config' => array( - 'description' => 'Acquia Cloud API user config file location. If not specified config will be loaded from $HOME', - 'default_value' => drush_server_home() . '/.acquia/cloudapi.conf', - 'example-value' => drush_server_home() . '/.acquia/cloudapi-site-specific.conf', - ), - 'endpoint' => array( - 'description' => 'Acquia Cloud API endpoint URL.', - 'default_value' => 'https://cloudapi.acquia.com/v1', - 'prompt' => array('Endpoint URL', NULL, TRUE, FALSE), - 'example-value' => 'https://cloudapi.acquia.com/v1', - ), - 'cainfo' => array( - 'description' => 'Path to a file containing the SSL certificates needed to verify the ac-api-endpoint.', - 'default_value' => dirname(__FILE__) . '/cloudapi.acquia.com.pem', - 'example-value' => 'cloudapi.acquia.com.pem', - ), - 'format' => array( - 'description' => 'Format to output the object. Use "print_r" for print_r, "export" for var_export, and "json" for JSON. If not provided, the output is printed in a human-readable format.', - 'default_value' => '', - 'example-value' => 'json', - ), - ); - return $options; -} - -/** - * Retrieve an Acquia Cloud API option, in priority order: - * - * - command line - * - ac-config file ($HOME/.acquia/cloudapi.conf by default) - * - per-site acapi file ($HOME/.drush/.acapi.drushrc.php) - * - default from acapi_common_options() - * - * @param $name - * An ac-api option name. - * @return - * The option value, or NULL. - */ -function acapi_get_option($name) { - // Make sure $name is an acapi option. - $options = acapi_common_options(); - if (!isset($options[$name])) { - return drush_set_error('ACAPI_UNKNOWN_OPTION', dt('Unknown ac-api option @name.', array('@name' => $name))); - } - - // If the user specified --$name= on the command line, return . - $value = drush_get_option($name, NULL); - if (isset($value)) { - return $value; - } - - // If the ac-config file sets $name, return the value. - $values = acapi_load_options($name); - if (isset($values[$name])) { - return $values[$name]; - } - - // If $name has a default value, return it. - if (!empty($options[$name]['default_value'])) { - return $options[$name]['default_value']; - } - - // No specified value, no default, return NULL. - return; -} - -/** - * A generic callback for API commands. The command must have: - * - * 'method': $method for acapi_call(). - * 'resource': $resource for acapi_call(). API resource argument names can - * include any argument name from the command's arguments in addition to :site - * and :env which are taken from the site alias. - * - * The command calls acapi_call() with arguments for the specified method, - * resource, and arguments, calling the API and displaying the results. - * - * @return NULL - * This function always returns NULL to avoid invalid JSON. - */ -function drush_acapi_ac_generic_callback() { - $command = drush_get_context('command'); - $api_args = preg_match('@:site@', $command['resource']) ? acapi_get_site_args() : array(); - $params = array(); - $body = array(); - - if (isset($command['default_params'])) { - $params += $command['default_params']; - } - - foreach ($command['argument-description'] as $k => $desc) { - if (isset($command['params']) && array_search($k, $command['params']) !== FALSE) { - $params[$k] = array_shift($command['arguments']); - } - elseif (isset($command['params_array']) && array_search($k, $command['params_array']) !== FALSE) { - $params[$k] = explode(',', array_shift($command['arguments'])); - } - elseif (isset($command['body_fields']) && array_search($k, $command['body_fields']) !== FALSE) { - $body[$k] = array_shift($command['arguments']); - } - elseif (isset($command['body_fields_array']) && array_search($k, $command['body_fields_array']) !== FALSE) { - $body[$k] = explode(',', array_shift($command['arguments'])); - } - elseif (isset($command['body_fields_path']) && array_search($k, $command['body_fields_path']) !== FALSE) { - $path = array_shift($command['arguments']); - $body[$k] = file_get_contents($path); - if ($body[$k] === FALSE) { - drush_set_error('ACAPI_ENOENT', dt('Cannot read @arg path @path.', array('@arg' => $k, '@path' => $path))); - return; - } - } - else { - $api_args[":$k"] = array_shift($command['arguments']); - } - } - - foreach ($command['orig_options'] as $option => $info) { - if (!empty($info['param'])) { - if (drush_get_option($option, FALSE)) { - $params[$info['param']] = $info['value']; - } - } - } - - // acapi_call() will print the results, so returning here would result in - // invalid JSON. - acapi_call($command['method'], $command['resource'], $api_args, $params, $body); -} - -/** - * Return the Acquia Cloud site information specified via the site - * alias. - * - * @param $site_required (TRUE) - * Set an error if site alias options are not found. - * @return - * An array of three elements, site name, environment and realm unless the - * alias file pre-dates the addition of realm. - */ -function acapi_get_site($site_required = TRUE) { - return array_values(acapi_get_site_args($site_required)); -} - -/** - * Get arguments aboud the Acquia Cloud site ready to be used for replacement - * in a URI. - * - * @param $site_required (TRUE) - * Set an error if site alias options are not found. - * @return array - * An associative array containing :site, :env and :realm or an - * empty array if site alias option not found. - */ -function acapi_get_site_args($site_required = TRUE) { - $params = array( - ':site' => drush_get_option('ac-site'), - ':env' => drush_get_option('ac-env'), - ':realm' => drush_get_option('ac-realm'), - ); - - $missing = array_intersect($params, array(NULL)); - if ($missing) { - if ($site_required) { - $missing = str_replace(':', 'ac-', implode(', ', array_keys($missing))); - $error = dt( - 'Alias file is missing Acquia Cloud information: !missing. Be sure to specify a complete Acquia Cloud alias name, such as @mysite.dev.', - array('!missing' => $missing) - ); - drush_set_error('ACAPI_SITE_REQUIRED', $error); - } - return array(); - } - - return $params; -} - -/** - * Call an Acquia Cloud API resource. - * - * @param $method - * The HTTP method; e.g. GET. - * @param $resource - * The API function to call; e.g. /sites/:realm::site. - * @param $args = array() - * An array of argument values for the resource; e.g: array(':site' => - * 'mysite'). - * @params $params = array() - * An array of query parameters to append to the URL. - * @params $body = array() - * An array of parameters to include in the POST body in JSON format. - * @params $options = array() - * An array of options: - * - display (TRUE): whether to output the result to stdout - * - result_stream: open stream to which to write the response body - * - redirect: the maximum number of redirects to allow - */ -function acapi_call($method, $resource, $args, $params = array(), $body = array(), $options = array()) { - $default_options = array( - 'display' => TRUE, - ); - $options = array_merge($default_options, $options); - - $debug = drush_get_option('debug', FALSE); - $verbose = drush_get_option('verbose', FALSE); - $simulate = drush_get_option('simulate', FALSE); - $format = acapi_get_option('format'); - - // Build the API call URL. - $url = acapi_get_option('endpoint'); - $url .= acapi_dt($resource, $args); - $url .= '.json'; - - foreach ($params as $k => $v) { - if (is_array($v)) { - unset($params[$k]); - foreach ($v as $key => $val) { - $params["$k-$key"] = "$k%5B%5D=" . urlencode($val); - } - } - else { - $params[$k] = "$k=" . urlencode($v); - } - } - - $url .= '?' . implode('&', $params); - - $creds = acapi_get_creds(); - if (!$creds) { - return FALSE; - } - - // Build the body. - $json_body = json_encode($body); - - $display = "curl -X $method '$url'"; - if ($debug) { - $display .= " ($creds)"; - } - if ($debug || $verbose || $simulate) { - drush_print($display, 0, STDERR); - if (!empty($body)) { - drush_print(" $json_body", 0, STDERR); - } - } - - if ($simulate) { - return; - } - - $headers = array(); - $ch = curl_init($url); - // Basic request settings - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); - curl_setopt($ch, CURLOPT_USERAGENT, basename(__FILE__)); - if (!empty($options['result_stream'])) { - curl_setopt($ch, CURLOPT_FILE, $options['result_stream']); - } - else { - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); - } - // User authentication - curl_setopt($ch, CURLOPT_HTTPAUTH, TRUE); - curl_setopt($ch, CURLOPT_USERPWD, $creds); - // SSL - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, preg_match('@^https:@', acapi_get_option('endpoint'))); - curl_setopt($ch, CURLOPT_CAINFO, acapi_get_option('cainfo')); - // Redirects - if (!empty($options['redirect'])) { - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); - curl_setopt($ch, CURLOPT_MAXREDIRS, $options['redirect']+1); - } - /* Body - We need to set a Content-Length header even on empty POST requests, or the webserver - will throw a 411 Length Required. - */ - - curl_setopt($ch, CURLOPT_POSTFIELDS, $json_body); - $headers[] = 'Content-Type: application/json;charset=utf-8'; - $headers[] = 'Content-Length: ' . strlen($json_body); - // Headers - if (!empty($headers)) { - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - } - // Debugging - curl_setopt($ch, CURLOPT_VERBOSE, $debug); - // Go - $content = curl_exec($ch); - if (curl_errno($ch) > 0) { - return drush_set_error('ACAPI_CURL_ERROR', dt('Error accessing @url: @err', array('@url' => $url, '@err' => curl_error($ch)))); - } - - $result = json_decode($content); - $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if (!empty($format)) { - drush_print(drush_format($result, NULL, $format)); - } - else if ($options['display']) { - if (is_array($result)) { - foreach ($result as $item) { - if (! is_scalar($item)) { - drush_print_table(drush_key_value_to_array_table(acapi_convert_values($item))); - } - else { - drush_print($item); - } - } - } - else { - if ($method == 'POST') { - // All POST actions return a task. Display something helpful. - drush_log(dt('Task @taskid started.', array('@taskid' => $result->id)), 'ok'); - } - else { - drush_print_table(drush_key_value_to_array_table(acapi_convert_values($result))); - } - } - } - - if ($status != 200) { - return drush_set_error('ACAPI_HTTP_STATUS_' . $status, dt('API status code @status', array('@status' => $status))); - } - - return array($status, $result); -} - -/** - * Return Acquia Cloud API credentials as username:password, or log an error - * if they are unavailable. - */ -function acapi_get_creds() { - $user = acapi_get_option('email'); - $pass = acapi_get_option('key'); - if (empty($user) || empty($pass)) { - return drush_set_error('ACAPI_CREDS_MISSING', dt('Email and api key required; specify --email/--key or run drush ac-api-login')); - } - return "$user:$pass"; -} - -/** - * Convert NULL, array and object values to appropriate string representations - * so they are printed correctly. - */ -function acapi_convert_values($arr) { - foreach ($arr as $k => $v) { - if (!isset($v)) { - $arr->{$k} = ''; - } - elseif (is_array($v) || is_object($v)) { - $arr->{$k} = '...'; - } - } - return (array) $arr; -} - -/** - * dt() wrapper that URL-encodes all substituted parameters that begin with - * a colon (':'). - */ -function acapi_dt($string, $args = array()) { - foreach ($args as $k => $v) { - if ($k[0] == ':') { - $args[$k] = urlencode($v); - } - } - return dt($string, $args); -} diff --git a/7.3/config/.drush/cloudapi.acquia.com.pem b/7.3/config/.drush/cloudapi.acquia.com.pem deleted file mode 100644 index a5da6cb6..00000000 --- a/7.3/config/.drush/cloudapi.acquia.com.pem +++ /dev/null @@ -1,98 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGvzCCBaegAwIBAgIQD1pdYvA5l5l2ezeWEsVwNTANBgkqhkiG9w0BAQUFADBm -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBDQS0zMB4XDTEyMDgyNzAwMDAwMFoXDTE1MDkwOTEyMDAwMFowfzELMAkGA1UE -BhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEzARBgNVBAcTCkJ1cmxpbmd0 -b24xEzARBgNVBAoTCkFjcXVpYSBJbmMxFzAVBgNVBAsTDkFjcXVpYSBIb3N0aW5n -MRUwEwYDVQQDDAwqLmFjcXVpYS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCVmcKz54qAbNO9NyP3hA96gzGRORSB9Bz4EOuFM1kfD/gvZoXoqk87 -NC/jPrvPAxNZDJ33IrO08WDWxBVi6UQ7Q9YYFgU1mm0se4qjwld7dtziDnaq2zXc -x4Q+AVfBj92w+RsVDWA2mNtMSaDePqXGzLOz4muUCA5oCtMg2QD+XIEp1yt13nQb -5nW6PbY6kHHviepNX3wj7TdqTLNPdCVcK9BJz2YTjfHtBBxnGxR/m804RL9fYWKz -r6XUbbf+gcKFwMqRI54Qs5cD20jmYnYgXFYwAx6HvOH9Lr969xMq5ePIQkKc66kG -Opie8unjAQRgd6T7lL+zvpaen1UhJruhAgMBAAGjggNOMIIDSjAfBgNVHSMEGDAW -gBRQ6nOJ2yn7EI+e5QEg1N55mUiD9zAdBgNVHQ4EFgQUJuo64qCLWo0Fvi6AsOYG -IiNj+s8wIwYDVR0RBBwwGoIMKi5hY3F1aWEuY29tggphY3F1aWEuY29tMA4GA1Ud -DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwYQYDVR0f -BFowWDAqoCigJoYkaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL2NhMy1nMjcuY3Js -MCqgKKAmhiRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vY2EzLWcyNy5jcmwwggHE -BgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwBATCCAaQwOgYIKwYBBQUHAgEWLmh0 -dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFk -BggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBz -ACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBz -ACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBD -AGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBp -AG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBo -ACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBl -ACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAg -AHIAZQBmAGUAcgBlAG4AYwBlAC4wewYIKwYBBQUHAQEEbzBtMCQGCCsGAQUFBzAB -hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRQYIKwYBBQUHMAKGOWh0dHA6Ly9j -YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VDQS0zLmNy -dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQCwbrUX+rSNdiS1ivce -pI3gzzlOG9FjcPPTfoLD/+eiysiTKe7d3Hb9urHZWGuEWWxXRl+6tND3TAt8ONpM -ZCs+nls+qvspG8ApQMZLgak4kb4+CaLMi6ZpQ2JTI89iChonOjI0OP6dVDbGbTvV -a7LKkiZw1xJNYUnhIiqOE1B9Ww3SUUpe1TLmiAYiiYiiuiyBxMz48sARHXnlEbWw -0dHOnU51tvG1zkv9sATYwv3LtIjf9pmXZnS1W0j5Ap0JeFbgFywVi0zSpbix0fzr -pyArWETFKxirR2bCVFH0sVZ2q3/sznguUx1QgH+tese0hqHm2epRORcOi69mcDYi -Cf5v ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR -CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv -KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 -BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf -1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs -zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d -32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w -ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm -MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB -hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln -aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl -cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME -GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB -INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a -vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j -CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X -dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE -JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY -Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K ------END CERTIFICATE----- diff --git a/7.3/config/php/xhprof.ini b/7.3/config/php/xhprof.ini new file mode 100644 index 00000000..6c2e4dc9 --- /dev/null +++ b/7.3/config/php/xhprof.ini @@ -0,0 +1,2 @@ +[xhprof] +extension="xhprof.so" diff --git a/7.3/config/supervisor/supervisord-code-server.conf.tmpl b/7.3/config/supervisor/supervisord-code-server.conf.tmpl index 14da7eee..30f5cc9a 100644 --- a/7.3/config/supervisor/supervisord-code-server.conf.tmpl +++ b/7.3/config/supervisor/supervisord-code-server.conf.tmpl @@ -1,6 +1,6 @@ # VS Code Server web IDE [program:code-server] # Using bash -lc here to load docker user profile (necessary for nvn/node to initialize) -command = gosu docker bash -lc '/usr/local/bin/code-server --user-data-dir=/home/docker/code-server -p 8080 /var/www --allow-http {{ if not (getenv "PASSWORD") }}--no-auth{{ end }}' +command = gosu docker bash -lc '/usr/bin/code-server --user-data-dir=/home/docker/code-server --disable-telemetry --bind-addr 0.0.0.0:8080 {{ if not (getenv "PASSWORD") }}--auth none{{ end }} /var/www' stdout_logfile = /var/log/supervisor/code-server-stdout stderr_logfile = /var/log/supervisor/code-server-stderr diff --git a/7.3/opt/code-server/settings.json b/7.3/opt/code-server/settings.json index 4dc66cab..a1b14cbd 100644 --- a/7.3/opt/code-server/settings.json +++ b/7.3/opt/code-server/settings.json @@ -22,5 +22,15 @@ "port": 9000 } ] + }, + // File associations + "files.associations": { + "*.inc": "php", + "*.module": "php", + "*.install": "php", + "*.theme": "php", + "*.tpl.php": "php", + "*.test": "php", + "*.php": "php" } } diff --git a/7.3/php-modules.txt b/7.3/php-modules.txt index 61d7753c..d2df798c 100644 --- a/7.3/php-modules.txt +++ b/7.3/php-modules.txt @@ -52,6 +52,7 @@ sqlite3 sqlsrv ssh2 standard +sysvsem tokenizer xml xmlreader diff --git a/7.3/ping-web.sh b/7.3/ping-web.sh new file mode 100755 index 00000000..6a5ba7b8 --- /dev/null +++ b/7.3/ping-web.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Notify web container about started fin exec +if [[ "${WEB_KEEPALIVE}" != "0" ]] && [[ "${VIRTUAL_HOST}" != "" ]] +then + while true + do + curl -s -m 1 ${VIRTUAL_HOST}/exec_in_progress_inside_cli >/dev/null 2>&1 + sleep ${WEB_KEEPALIVE} + done +fi diff --git a/7.3/startup.sh b/7.3/startup.sh index 58c0394a..bd4d3a7e 100755 --- a/7.3/startup.sh +++ b/7.3/startup.sh @@ -28,6 +28,18 @@ xdebug_enable () ln -s /opt/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/ } +xhprof_enable () +{ + echo-debug "Enabling xhprof..." + cp /opt/docker-php-ext-xhprof.ini /usr/local/etc/php/conf.d/ + # Output directory to the ini file + echo "xhprof.output_dir = ${XHPROF_OUTPUT_DIR}" >> /usr/local/etc/php/conf.d/docker-php-ext-xhprof.ini + # Try to create directory if it doesn't exist + mkdir ${XHPROF_OUTPUT_DIR} || true + # Change owner of directory + chown docker:docker ${XHPROF_OUTPUT_DIR} +} + ide_mode_enable () { echo-debug "Enabling web IDE..." @@ -101,22 +113,6 @@ convert_secrets () done } -# Acquia Cloud API login -acquia_login () -{ - echo-debug "Authenticating with Acquia..." - # This has to be done using the docker user via su to load the user environment - # Note: Using 'su -l' to initiate a login session and have .profile sourced for the docker user - local command="drush ac-api-login --email='${ACAPI_EMAIL}' --key='${ACAPI_KEY}' --endpoint='https://cloudapi.acquia.com/v1' && drush ac-site-list" - local output=$(su -l docker -c "${command}" 2>&1) - if [[ $? != 0 ]]; then - echo-debug "ERROR: Acquia authentication failed." - echo - echo "$output" - echo - fi -} - # Pantheon (terminus) login terminus_login () { @@ -156,6 +152,9 @@ convert_secrets # Enable xdebug [[ "$XDEBUG_ENABLED" != "" ]] && [[ "$XDEBUG_ENABLED" != "0" ]] && xdebug_enable +# Enable xdebug +[[ "$XHPROF_ENABLED" != "" ]] && [[ "$XHPROF_ENABLED" != "0" ]] && xhprof_enable + # Enable web IDE [[ "$IDE_ENABLED" != "" ]] && [[ "$IDE_ENABLED" != "0" ]] && ide_mode_enable @@ -172,8 +171,6 @@ chown "${HOST_UID:-1000}:${HOST_GID:-1000}" /var/www # These have to happen after the home directory permissions are reset, # otherwise the docker user may not have write access to /home/docker, where the auth session data is stored. -# Acquia Cloud API config -[[ "$ACAPI_EMAIL" != "" ]] && [[ "$ACAPI_KEY" != "" ]] && acquia_login # Automatically authenticate with Pantheon if Terminus token is present [[ "$TERMINUS_TOKEN" != "" ]] && terminus_login diff --git a/7.1/Dockerfile b/7.4/Dockerfile similarity index 87% rename from 7.1/Dockerfile rename to 7.4/Dockerfile index b8f63c97..4722f162 100644 --- a/7.1/Dockerfile +++ b/7.4/Dockerfile @@ -1,4 +1,4 @@ -FROM php:7.1-fpm-buster as cli +FROM php:7.4-fpm-buster as cli ARG DEBIAN_FRONTEND=noninteractive ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 @@ -124,7 +124,6 @@ RUN set -xe; \ libldap2-dev \ libmagickcore-dev \ libmagickwand-dev \ - libmcrypt-dev \ libmemcached-dev \ libmhash-dev \ libpng-dev \ @@ -148,9 +147,8 @@ RUN set -xe; \ libicu63 \ libjpeg62-turbo \ libldap-2.4-2 \ - libmagickcore-6.q16-6 \ + libmagickcore-6.q16-*-extra \ libmagickwand-6.q16-6 \ - libmcrypt4 \ libmemcached11 \ libmemcachedutil2 \ libmhash2 \ @@ -165,16 +163,16 @@ RUN set -xe; \ # SSH2 must be installed from source for PHP 7.x git clone https://github.com/php/pecl-networking-ssh2.git /usr/src/php/ext/ssh2 && rm -rf /usr/src/php/ext/ssh2/.git; \ \ + # --with-png-dir has been removed in PHP 7.4. libpng is required docker-php-ext-configure >/dev/null gd \ - --with-freetype-dir=/usr/include/ \ - --with-jpeg-dir=/usr/include/ \ - --with-webp-dir=/usr/include/ \ - --with-png-dir=/usr/include/ \ - --with-xpm-dir=/usr/include/; \ - docker-php-ext-configure >/dev/null imap --with-kerberos --with-imap-ssl; \ + --with-freetype \ + --with-jpeg \ + --with-webp \ + --with-xpm; \ + PHP_OPENSSL=yes docker-php-ext-configure >/dev/null imap --with-kerberos --with-imap-ssl; \ docker-php-ext-configure >/dev/null ldap --with-libdir=lib/x86_64-linux-gnu/; \ docker-php-ext-configure >/dev/null pgsql --with-pgsql=/usr/local/pgsql/; \ - docker-php-ext-configure >/dev/null zip --with-libzip; \ + docker-php-ext-configure >/dev/null zip; \ \ docker-php-ext-install >/dev/null -j$(nproc) \ bcmath \ @@ -186,8 +184,6 @@ RUN set -xe; \ imap \ intl \ ldap \ - # mcrypt is deprecated in 7.1 and removed in 7.2. See Deprecated features. - mcrypt \ mysqli \ opcache \ pcntl \ @@ -199,6 +195,7 @@ RUN set -xe; \ ssh2 \ xsl \ zip \ + sysvsem \ ;\ pecl update-channels; \ pecl install >/dev/null /dev/null; \ - mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/drush/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ + mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/backdrop-drush-extension/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ drush cc drush # Node.js (installed as user) ENV \ - NVM_VERSION=0.35.0 \ - NODE_VERSION=12.13.0 \ - YARN_VERSION=1.19.1 + NVM_VERSION=0.35.3 \ + NODE_VERSION=12.18.1 \ + YARN_VERSION=1.22.4 # Don't use -x here, as the output may be excessive RUN set -e; \ # NVM and a defaut Node.js version @@ -318,8 +320,8 @@ RUN set -e; \ # Ruby (installed as user) ENV \ - RVM_VERSION_INSTALL=1.29.9 \ - RUBY_VERSION_INSTALL=2.6.5 + RVM_VERSION_INSTALL=1.29.10 \ + RUBY_VERSION_INSTALL=2.7.1 # Don't use -x here, as the output may be excessive RUN set -e; \ # Public GPG servers are not realiable, so downloading keys from rvm.io instead. @@ -358,8 +360,8 @@ RUN set -e; \ # Python (installed as user) ENV \ - PYENV_VERSION_INSTALL=1.2.14 -# PYTHON_VERSION_INSTALL=3.7.0 + PYENV_VERSION_INSTALL=1.2.18 +# PYTHON_VERSION_INSTALL=3.8.3 RUN set -xe; \ git clone --depth 1 -b v${PYENV_VERSION_INSTALL} https://github.com/pyenv/pyenv.git $HOME/.pyenv; \ rm -rf $HOME/.pyenv/.git; \ @@ -376,22 +378,27 @@ RUN set -xe; \ # pyenv install ${PYTHON_VERSION_INSTALL}; \ # pyenv global ${PYTHON_VERSION_INSTALL} +# Notify web container about started fin exec +RUN echo '(/opt/ping-web.sh &)' >> $HOME/.profile + USER root SHELL ["/bin/sh", "-c"] # Copy configs and scripts -COPY --chown=docker:docker config/.drush /home/docker/.drush COPY --chown=docker:docker config/.terminus /home/docker/.terminus COPY --chown=docker:docker config/.ssh /home/docker/.ssh COPY config/supervisor /etc/supervisor/conf.d COPY startup.sh /opt/startup.sh COPY healthcheck.sh /opt/healthcheck.sh +COPY ping-web.sh /opt/ping-web.sh # PHP default settings, global overrides and fpm overrides # PHP_VERSION is set upstream. The source code for the exact PHP version may not be available in Github # TODO: For now, will just hardcoded a specific version available on https://github.com/php/php-src/ -ADD https://raw.githubusercontent.com/php/php-src/php-7.1.32/php.ini-development /usr/local/etc/php/php.ini +ADD https://raw.githubusercontent.com/php/php-src/php-7.4.7/php.ini-development /usr/local/etc/php/php.ini COPY config/php/zz-php.ini /usr/local/etc/php/conf.d/zz-php.ini COPY config/php/xdebug.ini /opt/docker-php-ext-xdebug.ini +COPY config/php/xhprof.ini /opt/docker-php-ext-xhprof.ini +COPY config/php/opcache.ini /opt/docker-php-ext-opcache.ini COPY config/php/zz-php-fpm.conf /usr/local/etc/php-fpm.d/zz-php-fpm.conf ENV \ @@ -404,8 +411,12 @@ ENV \ # Default values for HOST_UID and HOST_GUI to match the default Ubuntu user. These are used in startup.sh HOST_UID=1000 \ HOST_GID=1000 \ + # Delay in seconds between pings web from cli, while running fin exec. 0 - disabled + WEB_KEEPALIVE=0 \ # xdebug disabled by default - XDEBUG_ENABLED=0 + XDEBUG_ENABLED=0 \ + XHPROF_ENABLED=0 \ + XHPROF_OUTPUT_DIR=/tmp/xhprof # TODO: [v3] remove and set these via docker-compose EXPOSE 9000 @@ -433,22 +444,21 @@ USER docker ARG HOME=/home/docker ENV \ - # TODO: 2.x versions do not yet work with xdebug - CODE_SERVER_VERSION=1.1156-vsc1.33.1 \ + CODE_SERVER_VERSION=3.4.1 \ VSCODE_HOME="${HOME}/code-server" \ VSCODE_EXT_DIRECTORY="${HOME}/code-server/extensions" \ VSCODE_XDEBUG_VERSION=1.13.0 \ - VSCODE_GITLENS_VERSION=9.9.3 + VSCODE_GITLENS_VERSION=10.2.2 # Install code-server RUN \ set -xe; \ - curl -fsSL "https://github.com/cdr/code-server/releases/download/${CODE_SERVER_VERSION}/code-server${CODE_SERVER_VERSION}-linux-x64.tar.gz" -o /tmp/code-server.tar.gz; \ - tar -zxv --file=/tmp/code-server.tar.gz --directory=/tmp --strip-components=1; \ - sudo mv /tmp/code-server /usr/local/bin/; rm -rf /tmp/*.* + curl -fsSL "https://github.com/cdr/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server_${CODE_SERVER_VERSION}_amd64.deb" -o /tmp/code-server_amd64.deb; \ + sudo dpkg -i /tmp/code-server_amd64.deb; \ + rm -rf /tmp/*.* # Settings and scripts -COPY opt/code-server /opt/code-server +COPY --chown=docker:docker opt/code-server /opt/code-server # Install extensions RUN \ diff --git a/7.1/Makefile b/7.4/Makefile similarity index 86% rename from 7.1/Makefile rename to 7.4/Makefile index 72d0ee68..0429420a 100644 --- a/7.1/Makefile +++ b/7.4/Makefile @@ -1,9 +1,10 @@ -include ../tests/env_make -include env_make -VERSION ?= 7.1 -TAG_APPENDIX ?= -php$(VERSION) -BUILD_TAG = build-$(TAG_APPENDIX) +VERSION ?= 7.4 + +SOFTWARE_VERSION ?= php$(VERSION) +BUILD_TAG = build-$(SOFTWARE_VERSION) REPO = docksal/cli NAME = docksal-cli-$(VERSION) CWD = $(shell pwd) @@ -16,7 +17,7 @@ VOLUMES += -v /home/docker .PHONY: build test push shell run start stop logs clean release build: - docker build --cache-from=$(REPO):edge$(TAG_APPENDIX) -t $(REPO):$(BUILD_TAG) . + docker build --cache-from=$(REPO):edge-$(SOFTWARE_VERSION) -t $(REPO):$(BUILD_TAG) . test: IMAGE=$(REPO):$(BUILD_TAG) NAME=$(NAME) VERSION=$(VERSION) ../tests/test.bats diff --git a/7.1/config/.ssh/config b/7.4/config/.ssh/config similarity index 100% rename from 7.1/config/.ssh/config rename to 7.4/config/.ssh/config diff --git a/7.1/config/.ssh/id_rsa.tmpl b/7.4/config/.ssh/id_rsa.tmpl similarity index 100% rename from 7.1/config/.ssh/id_rsa.tmpl rename to 7.4/config/.ssh/id_rsa.tmpl diff --git a/7.1/config/.terminus/config.yml b/7.4/config/.terminus/config.yml similarity index 100% rename from 7.1/config/.terminus/config.yml rename to 7.4/config/.terminus/config.yml diff --git a/7.4/config/php/opcache.ini b/7.4/config/php/opcache.ini new file mode 100644 index 00000000..bbc72325 --- /dev/null +++ b/7.4/config/php/opcache.ini @@ -0,0 +1,3 @@ +; Extention settings +[opcache] +opcache.preload=/var/www/.docksal/etc/php/preload.php diff --git a/7.1/config/php/xdebug.ini b/7.4/config/php/xdebug.ini similarity index 100% rename from 7.1/config/php/xdebug.ini rename to 7.4/config/php/xdebug.ini diff --git a/7.4/config/php/xhprof.ini b/7.4/config/php/xhprof.ini new file mode 100644 index 00000000..6c2e4dc9 --- /dev/null +++ b/7.4/config/php/xhprof.ini @@ -0,0 +1,2 @@ +[xhprof] +extension="xhprof.so" diff --git a/7.1/config/php/zz-php-fpm.conf b/7.4/config/php/zz-php-fpm.conf similarity index 100% rename from 7.1/config/php/zz-php-fpm.conf rename to 7.4/config/php/zz-php-fpm.conf diff --git a/7.1/config/php/zz-php.ini b/7.4/config/php/zz-php.ini similarity index 100% rename from 7.1/config/php/zz-php.ini rename to 7.4/config/php/zz-php.ini diff --git a/7.1/config/supervisor/supervisord-code-server.conf.tmpl b/7.4/config/supervisor/supervisord-code-server.conf.tmpl similarity index 55% rename from 7.1/config/supervisor/supervisord-code-server.conf.tmpl rename to 7.4/config/supervisor/supervisord-code-server.conf.tmpl index 14da7eee..30f5cc9a 100644 --- a/7.1/config/supervisor/supervisord-code-server.conf.tmpl +++ b/7.4/config/supervisor/supervisord-code-server.conf.tmpl @@ -1,6 +1,6 @@ # VS Code Server web IDE [program:code-server] # Using bash -lc here to load docker user profile (necessary for nvn/node to initialize) -command = gosu docker bash -lc '/usr/local/bin/code-server --user-data-dir=/home/docker/code-server -p 8080 /var/www --allow-http {{ if not (getenv "PASSWORD") }}--no-auth{{ end }}' +command = gosu docker bash -lc '/usr/bin/code-server --user-data-dir=/home/docker/code-server --disable-telemetry --bind-addr 0.0.0.0:8080 {{ if not (getenv "PASSWORD") }}--auth none{{ end }} /var/www' stdout_logfile = /var/log/supervisor/code-server-stdout stderr_logfile = /var/log/supervisor/code-server-stderr diff --git a/7.1/config/supervisor/supervisord-crond.conf b/7.4/config/supervisor/supervisord-crond.conf similarity index 100% rename from 7.1/config/supervisor/supervisord-crond.conf rename to 7.4/config/supervisor/supervisord-crond.conf diff --git a/7.1/config/supervisor/supervisord-php-fpm.conf b/7.4/config/supervisor/supervisord-php-fpm.conf similarity index 100% rename from 7.1/config/supervisor/supervisord-php-fpm.conf rename to 7.4/config/supervisor/supervisord-php-fpm.conf diff --git a/7.1/config/supervisor/supervisord-sshd.conf b/7.4/config/supervisor/supervisord-sshd.conf similarity index 100% rename from 7.1/config/supervisor/supervisord-sshd.conf rename to 7.4/config/supervisor/supervisord-sshd.conf diff --git a/7.1/config/supervisor/supervisord.conf b/7.4/config/supervisor/supervisord.conf similarity index 100% rename from 7.1/config/supervisor/supervisord.conf rename to 7.4/config/supervisor/supervisord.conf diff --git a/7.1/healthcheck.sh b/7.4/healthcheck.sh similarity index 100% rename from 7.1/healthcheck.sh rename to 7.4/healthcheck.sh diff --git a/7.1/opt/code-server/install-vscode-extension b/7.4/opt/code-server/install-vscode-extension similarity index 100% rename from 7.1/opt/code-server/install-vscode-extension rename to 7.4/opt/code-server/install-vscode-extension diff --git a/7.1/opt/code-server/settings.json b/7.4/opt/code-server/settings.json similarity index 76% rename from 7.1/opt/code-server/settings.json rename to 7.4/opt/code-server/settings.json index 4dc66cab..a1b14cbd 100644 --- a/7.1/opt/code-server/settings.json +++ b/7.4/opt/code-server/settings.json @@ -22,5 +22,15 @@ "port": 9000 } ] + }, + // File associations + "files.associations": { + "*.inc": "php", + "*.module": "php", + "*.install": "php", + "*.theme": "php", + "*.tpl.php": "php", + "*.test": "php", + "*.php": "php" } } diff --git a/7.1/php-modules.txt b/7.4/php-modules.txt similarity index 96% rename from 7.1/php-modules.txt rename to 7.4/php-modules.txt index ca82761c..d2df798c 100644 --- a/7.1/php-modules.txt +++ b/7.4/php-modules.txt @@ -25,7 +25,6 @@ json ldap libxml mbstring -mcrypt memcached mysqli mysqlnd @@ -47,11 +46,13 @@ session SimpleXML soap sockets +sodium SPL sqlite3 sqlsrv ssh2 standard +sysvsem tokenizer xml xmlreader diff --git a/7.4/ping-web.sh b/7.4/ping-web.sh new file mode 100755 index 00000000..6a5ba7b8 --- /dev/null +++ b/7.4/ping-web.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Notify web container about started fin exec +if [[ "${WEB_KEEPALIVE}" != "0" ]] && [[ "${VIRTUAL_HOST}" != "" ]] +then + while true + do + curl -s -m 1 ${VIRTUAL_HOST}/exec_in_progress_inside_cli >/dev/null 2>&1 + sleep ${WEB_KEEPALIVE} + done +fi diff --git a/7.1/startup.sh b/7.4/startup.sh similarity index 89% rename from 7.1/startup.sh rename to 7.4/startup.sh index 58c0394a..c3ad17f5 100755 --- a/7.1/startup.sh +++ b/7.4/startup.sh @@ -28,6 +28,24 @@ xdebug_enable () ln -s /opt/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/ } +xhprof_enable () +{ + echo-debug "Enabling xhprof..." + cp /opt/docker-php-ext-xhprof.ini /usr/local/etc/php/conf.d/ + # Output directory to the ini file + echo "xhprof.output_dir = ${XHPROF_OUTPUT_DIR}" >> /usr/local/etc/php/conf.d/docker-php-ext-xhprof.ini + # Try to create directory if it doesn't exist + mkdir ${XHPROF_OUTPUT_DIR} || true + # Change owner of directory + chown docker:docker ${XHPROF_OUTPUT_DIR} +} + +opcache_preload_enable() +{ + echo-debug "Enabling opcache preload..." + ln -s /opt/docker-php-ext-opcache.ini /usr/local/etc/php/conf.d/ +} + ide_mode_enable () { echo-debug "Enabling web IDE..." @@ -101,22 +119,6 @@ convert_secrets () done } -# Acquia Cloud API login -acquia_login () -{ - echo-debug "Authenticating with Acquia..." - # This has to be done using the docker user via su to load the user environment - # Note: Using 'su -l' to initiate a login session and have .profile sourced for the docker user - local command="drush ac-api-login --email='${ACAPI_EMAIL}' --key='${ACAPI_KEY}' --endpoint='https://cloudapi.acquia.com/v1' && drush ac-site-list" - local output=$(su -l docker -c "${command}" 2>&1) - if [[ $? != 0 ]]; then - echo-debug "ERROR: Acquia authentication failed." - echo - echo "$output" - echo - fi -} - # Pantheon (terminus) login terminus_login () { @@ -156,6 +158,12 @@ convert_secrets # Enable xdebug [[ "$XDEBUG_ENABLED" != "" ]] && [[ "$XDEBUG_ENABLED" != "0" ]] && xdebug_enable +# Enable xdebug +[[ "$XHPROF_ENABLED" != "" ]] && [[ "$XHPROF_ENABLED" != "0" ]] && xhprof_enable + +# Enable opcache preload +[[ -f "/var/www/.docksal/etc/php/preload.php" ]] && opcache_preload_enable + # Enable web IDE [[ "$IDE_ENABLED" != "" ]] && [[ "$IDE_ENABLED" != "0" ]] && ide_mode_enable @@ -172,8 +180,6 @@ chown "${HOST_UID:-1000}:${HOST_GID:-1000}" /var/www # These have to happen after the home directory permissions are reset, # otherwise the docker user may not have write access to /home/docker, where the auth session data is stored. -# Acquia Cloud API config -[[ "$ACAPI_EMAIL" != "" ]] && [[ "$ACAPI_KEY" != "" ]] && acquia_login # Automatically authenticate with Pantheon if Terminus token is present [[ "$TERMINUS_TOKEN" != "" ]] && terminus_login diff --git a/Makefile b/Makefile index 963735de..72fd1f60 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Makefile # -DIRS = 7.1 7.2 7.3 +DIRS = 7.2 7.3 7.4 # the sets of directories to do various things in BUILDDIRS = $(DIRS:%=build-%) INSTALLDIRS = $(DIRS:%=install-%) diff --git a/README.md b/README.md index 2f5dc884..566f7bb9 100644 --- a/README.md +++ b/README.md @@ -2,30 +2,30 @@ This image is focused on console tools necessary to develop LAMP stack (and other web) applications. -This image(s) is part of the [Docksal](http://docksal.io) image library. +This image(s) is part of the [Docksal](https://docksal.io) image library. ## Features -- php/php-fpm (w/ xdebug), nodejs (via nvm), phyton (via pyenv), ruby (via rvm) +- php/php-fpm (w/ xdebug), nodejs (via nvm), python (via pyenv), ruby (via rvm) - Framework specific tools for Drupal, Wordpress, Magento - Miscellaneous cli tools for day to day web development - Hosting provider cli tools (Acquia, Pantheon, Platform.sh) - Cron job scheduling - Custom startup script support -- Coder (Visual Studio Code web IDE) +- [VS Code Server](https://github.com/cdr/code-server) (VS Code in the browser) ## Versions and image tag naming convention - Stable versions - - `2.6-php7.1`, `php7.1` - PHP 7.1 - - `2.6-php7.2`, `php7.2`, `latest` - PHP 7.2 - - `2.6-php7.3`, `php7.3` - PHP 7.3 + - `2.11-php7.2`, `php7.2` - PHP 7.2 + - `2.11-php7.3`, `php7.3`, `latest` - PHP 7.3 + - `2.11-php7.4`, `php7.4` - PHP 7.4 - Development versions - - `edge-php7.1` - PHP 7.1 - `edge-php7.2` - PHP 7.2 - `edge-php7.3` - PHP 7.3 + - `edge-php7.4` - PHP 7.4 ## PHP @@ -68,26 +68,24 @@ cli ... ``` -[See docs](https://docs.docksal.io/en/master/tools/xdebug) on using Xdebug for web and cli PHP debugging. +[See docs](https://docs.docksal.io/tools/xdebug/) on using Xdebug for web and cli PHP debugging. ## NodeJS - nvm -- node v10.15.0 w/ npm v6.4.1 (following NodeJS LTS release cycle) +- node v12.18.1 (following NodeJS LTS release cycle) - yarn NodeJS is installed via `nvm` in the `docker` user's profile inside the image (`/home/docker/.nvm`). -This image comes with NodeJS v10.15.0 by default . - If you need a different version of node, use `nvm` to install it, e.g, `nvm install 11.6.0`. Then, use `nvm use 11.6.0` to use it in the current session or `nvm alias default 11.6.0` to use it by default. ## Python - pyenv -- python 2.7.13 +- python 3.8.3 This image comes with a system level installed Python version from upstream (Debian 9). @@ -99,7 +97,7 @@ Note: additional versions will be installed in the `docker` user's profile insid ## Ruby - rvm -- ruby v2.6.0 +- ruby v2.7.1 - gem - bundler @@ -121,9 +119,9 @@ Then, `rvm use 2.5.1` to use it in the current session or `rvm --default use 2.5 ## Hosting provider tools -- Acquia Cloud API drush commands ([Acquia](https://www.acquia.com/)) -- terminus ([Pantheon](https://pantheon.io/)) -- platform ([Platform.sh](https://platform.sh/)) +- `acquia_cli` for Acquia Cloud APIv2 ([Acquia](https://www.acquia.com/)) +- `terminus` ([Pantheon](https://pantheon.io/)) +- `platform` ([Platform.sh](https://platform.sh/)) Also, see the [Secrets](#secrets) section below for more information on managing and using your hosting provider keys. @@ -166,12 +164,12 @@ The value must be base64 encoded, i.e: cat /path/to/some_key_rsa | base64 ``` -`SECRET_ACAPI_EMAIL` and `SECRET_ACAPI_KEY` +`SECRET_ACQUIACLI_KEY` and `SECRET_ACQUIACLI_SECRET` -Credentials used to authenticate with [Acquia Cloud API](https://docs.acquia.com/acquia-cloud/api). -Stored in `/home/docker/.acquia/cloudapi.conf` inside `cli`. +Credentials used to authenticate [Acquia CLI](https://github.com/typhonius/acquia_cli) with Acquia Cloud APIv2. +Stored as `ACQUIACLI_KEY` and `ACQUIACLI_SECRET` environment variables inside `cli`. -Acquia Cloud API can be used via `ac-` group of commands in Drush. +Acquia CLI is installed and available globally in `cli`. `SECRET_TERMINUS_TOKEN` @@ -187,6 +185,9 @@ Stored in `/home/docker/.platform` inside `cli`. Platform CLI is installed and available globally in `cli`. +`WEB_KEEPALIVE` + +Sets the delay in seconds between pings of the web container during execution `fin exec`. Setting this variable to non zero value prevents the project from stopping in cases of long `fin exec` and web container inactivity. Disabled by default (set to 0). ## Git configuration @@ -216,4 +217,4 @@ Starting with version 2.8, there is the `ide` flavor of the images, which comes Store your preferred password in this variable if you need to password protect the IDE environment. -[See docs](https://docs.docksal.io/en/master/tools/ide/) for instructions on using Coder in Docksal. +[See docs](https://docs.docksal.io/tools/ide/) for instructions on using Coder in Docksal. diff --git a/scripts/docker-push.sh b/scripts/docker-push.sh index de955b29..a681498b 100755 --- a/scripts/docker-push.sh +++ b/scripts/docker-push.sh @@ -49,12 +49,15 @@ IFS='.' read -a ver_arr <<< "$TRAVIS_TAG" VERSION_MAJOR=${ver_arr[0]#v*} # 2.7.0 => "2" VERSION_MINOR=${ver_arr[1]} # "2.7.0" => "7" +# Set tags if exists +SOFTWARE_VERSION="${SOFTWARE_VERSION:+-${SOFTWARE_VERSION}}" + # Possible docker image tags # "image:tag" pattern: :[-][-] -IMAGE_TAG_EDGE="edge${TAG_APPENDIX}" # e.g., edge[APPENDIX] -IMAGE_TAG_STABLE="stable${TAG_APPENDIX}" # e.g., stable[APPENDIX] -IMAGE_TAG_RELEASE_MAJOR="${VERSION_MAJOR}${TAG_APPENDIX}" # e.g., 2[APPENDIX] -IMAGE_TAG_RELEASE_MAJOR_MINOR="${VERSION_MAJOR}.${VERSION_MINOR}${TAG_APPENDIX}" # e.g., 2.7[APPENDIX] +IMAGE_TAG_EDGE="edge${SOFTWARE_VERSION}" # e.g., edge[-SOFTWARE_VERSION] +IMAGE_TAG_STABLE="stable${SOFTWARE_VERSION}" # e.g., stable[-SOFTWARE_VERSION] +IMAGE_TAG_RELEASE_MAJOR="${VERSION_MAJOR}${SOFTWARE_VERSION}" # e.g., 2[-SOFTWARE_VERSION] +IMAGE_TAG_RELEASE_MAJOR_MINOR="${VERSION_MAJOR}.${VERSION_MINOR}${SOFTWARE_VERSION}" # e.g., 2.7[-SOFTWARE_VERSION] IMAGE_TAG_LATEST="latest" # Skip pull request builds diff --git a/tests/.docksal/etc/php/preload.php b/tests/.docksal/etc/php/preload.php new file mode 100644 index 00000000..a4abe2da --- /dev/null +++ b/tests/.docksal/etc/php/preload.php @@ -0,0 +1,2 @@ + 128M => 128M" unset output + # Check Opcache Preload Enabled for 7.4 + run make exec -e CMD='php -i' + if [[ "${VERSION}" == "7.4" ]]; then echo "$output" | grep "opcache.preload"; fi + unset output + ### Cleanup ### make clean } @@ -419,47 +429,6 @@ _healthcheck_wait () make clean } -@test "Check Acquia integration" { - [[ $SKIP == 1 ]] && skip - - # Confirm secret is not empty - [[ "${SECRET_ACAPI_EMAIL}" != "" ]] - [[ "${SECRET_ACAPI_KEY}" != "" ]] - - ### Setup ### - make start -e ENV='-e SECRET_ACAPI_EMAIL -e SECRET_ACAPI_KEY' - - run _healthcheck_wait - unset output - - ### Tests ### - - # Confirm secrets were passed to the container - run docker exec -u docker "${NAME}" bash -lc 'echo SECRET_ACAPI_EMAIL: ${SECRET_ACAPI_EMAIL}' - [[ "${output}" == "SECRET_ACAPI_EMAIL: ${SECRET_ACAPI_EMAIL}" ]] - unset output - run docker exec -u docker "${NAME}" bash -lc 'echo SECRET_ACAPI_KEY: ${SECRET_ACAPI_KEY}' - [[ "${output}" == "SECRET_ACAPI_KEY: ${SECRET_ACAPI_KEY}" ]] - unset output - - # Confirm the SECRET_ prefix was stripped - run docker exec -u docker "${NAME}" bash -lc 'echo ACAPI_EMAIL: ${SECRET_ACAPI_EMAIL}' - [[ "${output}" == "ACAPI_EMAIL: ${SECRET_ACAPI_EMAIL}" ]] - unset output - run docker exec -u docker "${NAME}" bash -lc 'echo ACAPI_KEY: ${SECRET_ACAPI_KEY}' - [[ "${output}" == "ACAPI_KEY: ${SECRET_ACAPI_KEY}" ]] - unset output - - # Confirm authentication works - run docker exec -u docker "${NAME}" bash -lc 'drush ac-site-list' - [[ ${status} == 0 ]] - [[ ! "${output}" =~ "Not authorized" ]] - unset output - - ### Cleanup ### - make clean -} - @test "Check Platform.sh integration" { [[ $SKIP == 1 ]] && skip @@ -606,6 +575,8 @@ _healthcheck_wait () @test "Check Drush Backdrop Commands" { [[ $SKIP == 1 ]] && skip + # Skip until Drush Backdrop is compatible with PHP 7.4 + [[ "$VERSION" == "7.4" ]] && skip ### Setup ### make start @@ -636,7 +607,7 @@ _healthcheck_wait () ### Tests ### run make logs - echo "$output" | grep "Documentation on securing your setup" + echo "$output" | grep 'HTTP server listening on http://0\.0\.0\.0:8080' unset output ### Cleanup ###