/usr/share/drush/commands/drush_make/contrib/drush_make_d_o.drush.inc is in drush-make 2.3-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 | <?php
/**
* @file
* drush_make extension for integrating with the drupal.org packaging system.
*
* This extension serves the following purposes:
* - Adds the --drupal-org and --build-run-key command line options,
* which activate the functionality in the rest of the code.
* - Validates .make files to ensure they comply with drupal.org packaging
* requirements.
* - Restricts download locations to those approved for drupal.org packages.
* - Implements a custom logging function for error reporting to the packaging
* script.
* - Builds a list of package contents (nids of package items), and stores
* for the packaging system to use.
*/
// URI of the 'drush_make on drupal.org' handbook page.
define('DRUSH_MAKE_DO_DOCUMENTATION_LINK', 'http://drupal.org/node/642116');
// Name of the package contents file used by the packaging script.
define('DRUSH_MAKE_DO_PACKAGE_CONTENTS_FILE', 'package_contents.txt');
// Name of the build errors file used by the packaging script.
define('DRUSH_MAKE_DO_BUILD_ERRORS_FILE', 'build_errors.txt');
/**
* Implement hook_drush_command().
*/
function drush_make_d_o_drush_command() {
$items = array();
$items['convert-makefile'] = array(
'description' => '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) {
if (!drush_make_ensure_version()) {
return;
}
// Force the --drupal-org option, it's required to trigger the drupal.org
// specific validation.
drush_set_option('drupal-org', TRUE);
$info = drush_make_validate_info_file(drush_make_parse_info_file($makefile));
}
/**
* 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_validate_info().
*/
function drush_make_d_o_drush_make_validate_info($info) {
if (drush_get_option('drupal-org')) {
$transformers = array(
'top' => array(
'DrushMakeDo_TopWhitelist',
'DrushMakeDo_CoreVersion',
),
'project' => array(
'DrushMakeDo_ProjectVersion',
'DrushMakeDo_ProjectWhitelist',
'DrushMakeDo_ProjectPatch',
'DrushMakeDo_ProjectUpdateXML',
),
);
$pass = TRUE;
foreach ($transformers['top'] as $transformer) {
$object = new $transformer('make');
$pass = $pass && $object->verify($info);
}
if (!empty($info['projects'])) {
foreach ($info['projects'] as $project => $project_data) {
foreach ($transformers['project'] as $transformer) {
$object = new $transformer('make');
$pass = $pass && $object->verify($project_data, $project, $info['core']);
}
$info['projects'][$project] = $project_data;
}
}
// Abort if any errors were found.
if (!$pass) {
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;
}
abstract class DrushMakeDo_Transformer {
protected function log($message) {
drush_make_error('BUILD_ERROR', $message);
}
protected function latestVersion($project_name, $version, $core) {
// Project types that are valid for the version checker.
$allowed_project_types = array(
'core',
'module',
'theme',
);
$project = array(
'name' => $project_name,
'core' => $core,
'version' => $version,
'location' => drush_get_option('drush-make-update-default-url'),
);
$project = drush_make_updatexml($project);
// Because the version data we get back from the XML may not be the same
// data we passed, and because we can fail even if version data is
// returned, track both the success/failure of the request, and the
// version data.
// Check for bad projects and branch releases.
if (empty($project) || preg_match('/.*-dev$/', $project['version'])) {
// Use the passed version if no version was returned.
if (empty($project)) {
$final_version = $version;
}
else {
$final_version = $project['version'];
}
if ($project_name == 'drupal') {
drush_make_error('BUILD_ERROR', dt("Drupal core does not have an official release for version '%version' yet.", array('%version' => $final_version)));
}
else {
drush_make_error('BUILD_ERROR', dt("Project '%project' does not have an official release for version '%version'.", array('%project' => $project_name, '%version' => $final_version)));
}
$result = FALSE;
}
// Make sure the project is an allowed project type.
elseif (!in_array($project['type'], $allowed_project_types)) {
drush_make_error('BUILD_ERROR', dt("Project %project of type '%type' is not permitted.", array('%project' => $project_name, '%type' => $project['type'])));
$final_version = $project['version'];
$result = FALSE;
}
else {
$final_version = $project['version'];
$result = TRUE;
}
return $result ? $final_version : FALSE;
}
/**
* 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.
*/
protected function validVersion($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;
}
}
class DrushMakeDo_TopWhitelist extends DrushMakeDo_Transformer {
// The list of currently allowed top-leval attributes.
protected $attribute_whitelist = array('core', 'api', 'core_release', 'projects');
protected $level = 'top';
function verify(&$makefile, $name = NULL) {
$pass = TRUE;
// Check for disallowed top-level attributes.
foreach ($makefile as $attribute => $attribute_data) {
if (!in_array($attribute, $this->attribute_whitelist)) {
if ($this->level == 'top') {
$this->log(dt("The top-level attribute '%attribute' is not allowed.", array('%attribute' => $attribute)));
}
else {
$this->log(dt("The project-level attribute '%attribute' on project '%project' is not allowed.", array('%attribute' => $attribute, '%project' => $name)));
}
$pass = FALSE;
}
}
return $pass;
}
function convert($makefile) {
// Check for disallowed top-level attributes.
foreach ($makefile as $attribute => $attribute_data) {
if (!in_array($attribute, $this->attribute_whitelist)) {
unset($makefile[$attribute]);
}
}
return $makefile;
}
}
class DrushMakeDo_CoreVersion extends DrushMakeDo_Transformer {
function verify(&$makefile) {
return $this->validVersion(dt('Drupal core'), $makefile['core_release']);
}
function convert($makefile) {
// First order of business: convert core version.
$parts = explode('.', $makefile['core']);
$core_major = $parts[0];
$core = $core_major . '.x';
// Check for a stable release on the branch.
$final_version = $this->latestVersion('drupal', $core_major, $core);
if ($final_version) {
$makefile['core'] = $final_version;
return $makefile;
}
return FALSE;
}
}
class DrushMakeDo_ProjectVersion extends DrushMakeDo_Transformer {
function verify(&$project_data, $project) {
$pass = TRUE;
if (!is_array($project_data)) {
$project_data = array('version' => $project_data);
}
// 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)));
$pass = FALSE;
}
// Check for bad project version.
if (!$this->validVersion($project, $project_data['version'])) {
$pass = FALSE;
}
return $pass;
}
function convert($makefile) {
}
}
class DrushMakeDo_ProjectWhitelist extends DrushMakeDo_TopWhitelist {
protected $attribute_whitelist = array('version', 'subdir', 'patch');
protected $level = 'project';
}
class DrushMakeDo_ProjectPatch extends DrushMakeDo_Transformer {
function verify(&$project_data, $project) {
// Check that patches do in fact come from drupal.org.
if (isset($project_data['patch'])) {
foreach ($project_data['patch'] as $patch) {
if (strpos($patch, 'http://drupal.org/files/issues') !== 0) {
drush_make_error('BUILD_ERROR', dt("The patch '%patch' is not hosted on drupal.org", array('%patch' => $patch)));
$errors = TRUE;
}
}
}
return TRUE;
}
function convert($makefile) {
}
}
class DrushMakeDo_ProjectUpdateXML extends DrushMakeDo_Transformer {
function verify(&$project_data, $project, $core) {
$project_data += array(
'name' => $project,
'core' => $core,
);
$project_data = drush_make_d_o_updatexml($project_data);
return $project_data;
}
function convert($makefile) {
}
}
/**
* 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) {
if ($filename = _drush_make_download_file(drush_get_option('drush-make-update-default-url') . '/' . $project['name'] . '/' . $project['core'])) {
$release_history = simplexml_load_string(file_get_contents($filename));
drush_op('unlink', $filename);
}
// First, get the release history.
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;
}
$project['release_history'] = $release_history;
if (!isset($project['type'])) {
// Determine the project type.
$term_map = array(
'Modules' => 'module',
'Themes' => 'theme',
'Drupal core' => '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);
}
}
/**
* Custom logging function for conversions going to STDOUT.
*
* Drush doesn't output drush_set_error() messages to STDERR, lame-o... :(
*
* This helper function overcomes that deficiency by manually writing error
* messages to STDERR in the case where output is going to STDOUT.
*/
function drush_make_d_o_convert_log_stdout_handler($entry) {
if ($entry['type'] == 'error' || $entry['type'] == 'failed') {
fwrite(STDERR, dt("ERROR") . ": " . $entry['message'] . "\n");
}
}
|