| 1 |
<?php
|
| 2 |
// $Id: cvs_deploy.module,v 1.26 2009/09/26 06:57:34 dww Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
*
|
| 7 |
* This module improves the administrative UI for sites that are deployed
|
| 8 |
* directly via a CVS workspace (as opposed to sites built from packaged
|
| 9 |
* releases that are downloaded from drupal.org).
|
| 10 |
*
|
| 11 |
* See the README.txt file for more information.
|
| 12 |
*
|
| 13 |
* @author Derek Wright ("dww") http://drupal.org/user/46549
|
| 14 |
*/
|
| 15 |
|
| 16 |
/**
|
| 17 |
* Returns the human-readable version string from a given CVS tag.
|
| 18 |
*/
|
| 19 |
function cvs_deploy_version_from_tag($tag) {
|
| 20 |
// If there's nothing, it must be a HEAD checkout, and therefore,
|
| 21 |
// we have no idea what the version is.
|
| 22 |
$version = '';
|
| 23 |
$match = array();
|
| 24 |
if (empty($tag) || $tag == 'HEAD') {
|
| 25 |
$version = 'HEAD';
|
| 26 |
}
|
| 27 |
// See if it's a full, official release from a tag:
|
| 28 |
elseif (preg_match('@^DRUPAL-(\d+)--(\d+)-(\d+)(-.+)?@', $tag, $match)) {
|
| 29 |
$version = $match[1] .'.x-'. $match[2] .'.'. $match[3];
|
| 30 |
if (isset($match[4])) {
|
| 31 |
// This version's tag has 'extra', so clean that up.
|
| 32 |
$version .= '-'. preg_replace('/[_-]/', '', strtolower($match[4]));
|
| 33 |
}
|
| 34 |
}
|
| 35 |
// If not, see if it's from a branch (like a development snapshot).
|
| 36 |
elseif (preg_match('@^DRUPAL-(\d+)(--(\d+))?@', $tag, $match)) {
|
| 37 |
$version = $match[1] .'.x-'. (isset($match[3]) ? $match[3] : '1') .'.x-dev';
|
| 38 |
}
|
| 39 |
return $version;
|
| 40 |
}
|
| 41 |
|
| 42 |
/**
|
| 43 |
* Implementation of hook_system_info_alter().
|
| 44 |
*/
|
| 45 |
function cvs_deploy_system_info_alter(&$info, $file) {
|
| 46 |
// First, if we don't know the 'project' attribute, fill it in based on the
|
| 47 |
// CVS/Repository file.
|
| 48 |
if (empty($info['project'])) {
|
| 49 |
$info['project'] = cvs_deploy_get_project_name($file);
|
| 50 |
}
|
| 51 |
|
| 52 |
// Now, alter the version string based on the CVS sticky tag.
|
| 53 |
if (empty($info['version'])) {
|
| 54 |
$info['version'] = '';
|
| 55 |
}
|
| 56 |
_cvs_deploy_version_alter($info['version'], $file);
|
| 57 |
|
| 58 |
// Find the timestamp for the most recently modified CVS/Entries file.
|
| 59 |
$mtime = _cvs_deploy_find_latest_update(dirname($file->uri), 0);
|
| 60 |
|
| 61 |
// Only fill this in if the .info file does not define a 'datestamp'.
|
| 62 |
if (empty($info['datestamp'])) {
|
| 63 |
$info['datestamp'] = $mtime;
|
| 64 |
}
|
| 65 |
|
| 66 |
// However, the '_info_file_ctime' should always get the latest value.
|
| 67 |
if (empty($info['_info_file_ctime'])) {
|
| 68 |
$info['_info_file_ctime'] = $mtime;
|
| 69 |
}
|
| 70 |
else {
|
| 71 |
$info['_info_file_ctime'] = max($info['_info_file_ctime'], $mtime);
|
| 72 |
}
|
| 73 |
}
|
| 74 |
|
| 75 |
/**
|
| 76 |
* Private helper to alter the version of a module based on what we can figure
|
| 77 |
* out about the CVS tag in use.
|
| 78 |
*/
|
| 79 |
function _cvs_deploy_version_alter(&$version, $file) {
|
| 80 |
static $available = array();
|
| 81 |
$match = array();
|
| 82 |
if (empty($version)) {
|
| 83 |
// The .info file contains no version data. Find the version based
|
| 84 |
// on the sticky tag in the local workspace (the CVS/Tag file).
|
| 85 |
$cvs_dir = dirname($file->uri) .'/CVS';
|
| 86 |
if (is_dir($cvs_dir)) {
|
| 87 |
$tag = ''; // If there's no Tag file, there's no tag, a.k.a. HEAD.
|
| 88 |
if (file_exists($cvs_dir .'/Tag')) {
|
| 89 |
$tag_file = trim(file_get_contents($cvs_dir .'/Tag'));
|
| 90 |
if ($tag_file) {
|
| 91 |
// Get the sticky tag for this workspace: strip off the leading 'T'.
|
| 92 |
$tag = preg_replace('@^(T|N)@', '', $tag_file);
|
| 93 |
}
|
| 94 |
}
|
| 95 |
$version = cvs_deploy_version_from_tag($tag);
|
| 96 |
}
|
| 97 |
}
|
| 98 |
// The weird concatenation prevents CVS from 'expanding' this $Name.
|
| 99 |
elseif (preg_match('/\$'.'Name: (.*?)\$/', $version, $match)) {
|
| 100 |
$version = cvs_deploy_version_from_tag(trim($match[1]));
|
| 101 |
}
|
| 102 |
|
| 103 |
if (module_exists('update') && $version == 'HEAD') {
|
| 104 |
module_load_include('inc', 'update', 'update.compare');
|
| 105 |
// If there's available update_status data, we can use the version string
|
| 106 |
// the release node pointing to HEAD really has. However, we can only
|
| 107 |
// safely grab this data directly from the cache, since if we call
|
| 108 |
// update_get_available() here, we'd enter infinite recursion when that
|
| 109 |
// function invokes update_status_get_projects(), which in turn needs to
|
| 110 |
// process the .info files, which invokes the hook that leads here.
|
| 111 |
if (empty($available)) {
|
| 112 |
$cache = _update_cache_get('update_available_releases');
|
| 113 |
if (!empty($cache)) {
|
| 114 |
$available = $cache->data;
|
| 115 |
}
|
| 116 |
}
|
| 117 |
$project = update_get_project_name($file);
|
| 118 |
if (isset($available[$project]['releases'])) {
|
| 119 |
foreach ($available[$project]['releases'] as $release) {
|
| 120 |
if (isset($release['tag']) && $release['tag'] == 'HEAD') {
|
| 121 |
$version = $release['version'];
|
| 122 |
break;
|
| 123 |
}
|
| 124 |
}
|
| 125 |
}
|
| 126 |
}
|
| 127 |
}
|
| 128 |
|
| 129 |
/**
|
| 130 |
* Private helper to alter the 'project' of a module based on what directory
|
| 131 |
* in the CVS repository the module has been checked out from.
|
| 132 |
*/
|
| 133 |
function cvs_deploy_get_project_name($file) {
|
| 134 |
static $projects = array();
|
| 135 |
$name = $file->name;
|
| 136 |
if (empty($projects[$name])) {
|
| 137 |
// TODO: cache this in {cache}, too?
|
| 138 |
$cvs_dir = dirname($file->uri) .'/CVS';
|
| 139 |
if (is_dir($cvs_dir)) {
|
| 140 |
$repository = '';
|
| 141 |
if (file_exists($cvs_dir .'/Repository')) {
|
| 142 |
$repo_file = trim(file_get_contents($cvs_dir .'/Repository'));
|
| 143 |
if ($repo_file) {
|
| 144 |
$parts = explode('/', $repo_file);
|
| 145 |
if ($parts[0] == 'drupal') {
|
| 146 |
$projects[$name] = $parts[0];
|
| 147 |
}
|
| 148 |
else {
|
| 149 |
$projects[$name] = $parts[2];
|
| 150 |
}
|
| 151 |
}
|
| 152 |
}
|
| 153 |
}
|
| 154 |
}
|
| 155 |
return (isset($projects[$name]) ? $projects[$name] : '');
|
| 156 |
}
|
| 157 |
|
| 158 |
/**
|
| 159 |
* Recursive helper function to find the latest modification time on every
|
| 160 |
* CVS/Entires file in the current directory tree.
|
| 161 |
*
|
| 162 |
* @param $dir
|
| 163 |
* The directory to search.
|
| 164 |
* @param $timestamp
|
| 165 |
* The current latest modification timestamp on any CVS/Entries file.
|
| 166 |
* @return
|
| 167 |
* The latest mtime based on what we learned in the current directory.
|
| 168 |
*/
|
| 169 |
function _cvs_deploy_find_latest_update($dir, $timestamp) {
|
| 170 |
if (is_dir($dir)) {
|
| 171 |
$fp = opendir($dir);
|
| 172 |
while (FALSE !== ($file = readdir($fp))) {
|
| 173 |
if ($file == '.' || $file == '..') {
|
| 174 |
continue;
|
| 175 |
}
|
| 176 |
if ($file == 'CVS' && is_dir("$dir/CVS")) {
|
| 177 |
$entries_file = $dir .'/CVS/Entries';
|
| 178 |
if (file_exists($entries_file)) {
|
| 179 |
$mtime = filemtime($entries_file);
|
| 180 |
$timestamp = ($mtime > $timestamp) ? $mtime : $timestamp;
|
| 181 |
}
|
| 182 |
}
|
| 183 |
elseif (is_dir("$dir/$file")) {
|
| 184 |
$timestamp = _cvs_deploy_find_latest_update("$dir/$file", $timestamp);
|
| 185 |
}
|
| 186 |
}
|
| 187 |
closedir($fp);
|
| 188 |
}
|
| 189 |
return $timestamp;
|
| 190 |
}
|