'Convert the specified makefile to a drupal.org friendly format, and verify the converted file.', 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, ); $items['verify-makefile'] = array( 'description' => 'Verify the specified makefile is in a drupal.org-friendly format.', 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, ); return $items; } /** * Drush callback; verify a .make file is in a drupal.org friendly format. */ function drush_drush_make_d_o_verify_makefile($makefile) { include_once 'drush_make_d_o.convert.inc'; $verifier = new DrushMakeDrupalorgVerifier($makefile); $verifier->run(); } /** * Drush callback; convert the makefile to a drupal.org friendly format. */ function drush_drush_make_d_o_convert_makefile($from, $to = NULL) { include_once 'drush_make_d_o.convert.inc'; // If output is going to STDOUT, use a custom logger. if (empty($to)) { drush_set_context('DRUSH_LOG_CALLBACK', 'drush_make_d_o_convert_log_stdout_handler'); } $converter = new DrushMakeDrupalorgConverter($from, $to); $converter->run(); } /** * Implement EXTENSION_drush_make_init(). */ function drush_make_d_o_drush_init() { if (drush_get_option('drupal-org')) { // The --drupal-org switch implies these defaults: // Location to put our custom build files. drush_set_default('drupal-org-build-root', '.'); // The destination of the downloaded contrib projects. drush_set_default('contrib-destination', '.'); // Whether to allow a build without core. drush_set_default('no-core', TRUE); // Optionally set up a custom error logger. if (drush_get_option('drupal-org-log-errors-to-file')) { drush_set_context('DRUSH_LOG_CALLBACK', 'drush_make_d_o_log_errors_to_file'); } // Optionally set up the package contents file. The packaging script // expects it, so it's created as an empty file here to ensure it // exists. if (drush_get_option('drupal-org-log-package-items-to-file')) { $drupal_org_build_root = drush_get_option('drupal-org-build-root'); if (!touch($drupal_org_build_root . '/' . DRUSH_MAKE_DO_PACKAGE_CONTENTS_FILE)) { drush_make_error('FILE_ERROR', dt('Unable to write package contents file to %build_root', array('%build_root' => $drupal_org_build_root))); } } } } /** * Implement EXTENSION_drush_make_command(). */ function drush_make_d_o_drush_make_command(&$function, &$parameters, &$dependencies, $id, &$queue) { if (drush_get_option('drupal-org')) { switch ($function) { // No libraries on d.o. case 'drush_make_add_libraries': return FALSE; // Custom drupal.org validation for .make files. case 'drush_make_validate_info_file': $function = 'drush_make_d_o_validate_info_file'; break; // Custom drupal.org processing for loading update XML files. case 'drush_make_updatexml': $function = 'drush_make_d_o_updatexml'; break; } } } /** * Provide additional drupal.org specific validation for .make files. */ function drush_make_d_o_validate_info_file($info) { // Run the regular validation first. if (!($info = drush_make_validate_info_file($info))) { return FALSE; } // The list of currently allowed top-leval attributes. $top_level_attribute_whitelist = array('core', 'core_release', 'projects'); // The list of currently allowed project-level attributes. $project_attribute_whitelist = array('version', 'subdir'); // Assume no errors to start. $errors = FALSE; // Check for disallowed top-level attributes. foreach ($info as $attribute => $attribute_data) { if (!in_array($attribute, $top_level_attribute_whitelist)) { drush_make_error('BUILD_ERROR', dt("The top-level attribute '%attribute' is not allowed in drupal.org .make files", array('%attribute' => $attribute))); $errors = TRUE; } } // Check for a valid core value. // This is just a basic sanity check for a properly formatted value. if (!drush_make_d_o_check_valid_version(dt('Drupal core'), $info['core_release'])) { $errors = TRUE; } if (!empty($info['projects'])) { foreach ($info['projects'] as $project => $project_data) { // No specifying just project name. if (is_numeric($project)) { drush_make_error('BUILD_ERROR', dt('Automatic best release support for project %project is not supported -- you must specify a version.', array('%project' => $project_data))); $errors = TRUE; } else { // Version must be set. if (!isset($project_data['version'])) { drush_make_error('BUILD_ERROR', dt('No version specified for project %project -- version is required.', array('%project' => $project))); $errors = TRUE; } // Check for disallowed project-level attributes. foreach ($project_data as $attribute => $attribute_data) { if (!in_array($attribute, $project_attribute_whitelist)) { drush_make_error('BUILD_ERROR', dt("The project-level attribute '%attribute' is not allowed in drupal.org .make files", array('%attribute' => $attribute))); $errors = TRUE; } } // Check for bad project version. if (!drush_make_d_o_check_valid_version($project, $project_data['version'])) { $errors = TRUE; } } } } // Abort if any errors were found. if ($errors) { drush_make_error('BUILD_ERROR', dt('The drupal.org validation check failed -- see @doc_link for more information.', array('@doc_link' => DRUSH_MAKE_DO_DOCUMENTATION_LINK))); return FALSE; } return $info; } /** * Validate release versions. * * @param $name * The display name of the project. * @param $version_string * The version string to test. * @return * TRUE if the version string is bad, FALSE otherwise. */ function drush_make_d_o_check_valid_version($name, $version_string) {; // Development snapshots not allowed. if (preg_match('/.*-dev$/', $version_string)) { drush_make_error('BUILD_ERROR', dt('%project branch releases not allowed.', array('%project' => $name))); return FALSE; } // Basic sanity checking on release version strings. We need this because // drush_make supports projects[foo][version] = 1, which downloads the latest // stable release on the major version 1 branch. We require a specific // release. elseif (!preg_match('/^\d+\.\d+(-[a-z0-9]+)?$/', $version_string)) { drush_make_error('BUILD_ERROR', dt('%project version format incorrect -- specifying an official release version is required.', array('%project' => $name))); return FALSE; } return TRUE; } /** * Custom implementation of drush_make_updatexml(). * * Only allows loading update XML files from the default location, which for * our purposes is updates.drupal.org. This is also the place where package * items are collected for the package. */ function drush_make_d_o_updatexml($project) { $xml = drush_make_get_remote_file(drush_get_option('drush-make-update-default-url') . '/' . $project['name'] . '/' . $project['core']); // First, get the release history. $release_history = simplexml_load_string($xml); if (!is_object($release_history) || !$release_history->title) { drush_make_error('XML_ERROR', dt("Could not retrieve version information for %project.", array('%project' => $project['name']))); return FALSE; } drush_log(dt('Project information for %project retrieved.', array('%project' => $project['name'])), 'ok'); $project['release_history'] = $release_history; if (!isset($project['type'])) { // Determine the project type. $term_map = array( 'Modules' => 'module', 'Themes' => 'theme', 'Drupal project' => 'core', 'Installation profiles' => 'profile', 'Translations' => 'translation' ); // Iterate through all terms related to this project. foreach ($release_history->terms->term as $term) { // If we find a term from the term map, add it. if (in_array((string) $term->value, array_keys($term_map))) { $project['type'] = $term_map[(string)$term->value]; break; } } if (!isset($project['type'])) { drush_make_error('BUILD_ERROR', dt("Unable to determine project type for %project.", array('%project' => $project['name']))); return FALSE; } } // Process the file download data, and record the item_nid for the package. if ($project = drush_make_update_xml_download($project)) { // Optionally log package contents to a file. if (drush_get_option('drupal-org-log-package-items-to-file')) { $project = drush_make_d_o_write_package_item_nid($project); } return $project; } return FALSE; } /** * Store a package item in a package summary file. */ function drush_make_d_o_write_package_item_nid($project) { // Release/version logic ripped off from drush_make_update_xml_download(). // Make an array of releases. foreach ($project['release_history']->releases->release as $release) { $version = (string) $release->version_major; if ((string) $release->version_patch) { $version .= '.' . (string) $release->version_patch; } else { $version .= '.0'; } if ($extra_version = (string) $release->version_extra) { $version .= '-' . $extra_version; } // Grab the release nid from the release node link. $releases[$version] = basename($release->release_link); } // Write the item's nid to the package contents file. if ($project['version'] && ($item_nid = $releases[$project['version']])) { if (file_put_contents(drush_get_option('drupal-org-build-root') . '/' . DRUSH_MAKE_DO_PACKAGE_CONTENTS_FILE, "$item_nid\n", FILE_APPEND)) { return $project; } } return FALSE; } /** * Custom logging function for packaging errors. * * Logs all error messages to a build_errors.txt file in the root of the * package build. * * @see drush_log() for a description of the $entry array. */ function drush_make_d_o_log_errors_to_file($entry) { if ($entry['type'] == 'error' || $entry['type'] == 'failed') { file_put_contents(drush_get_option('drupal-org-build-root') . '/' . DRUSH_MAKE_DO_BUILD_ERRORS_FILE, $entry['message'] . "\n", FILE_APPEND); } }