#!/usr/bin/env php null, 'vcs_type' => null, 'repo' => null, 'id' => null, 'username' => null, 'committed' => null, 'submitted_username' => null, 'lines_changed' => null, 'log' => null, ); // end log_info /** MAIN **/ $short_options = 'h?dc:i:r:t:u:'; $long_options = array( 'help', 'debug', 'config', 'id', 'repo', 'user', 'type', ); $options = Console_Getopt::getopt($_SERVER['argv'], $short_options, $long_options); if (PEAR::isError($options)) { Usage("Error in command line: " . $options->getMessage()); } if (0 < count($options[0])) { foreach ($options[0] as $option) { $option_switch = $option[0]; $option_value = $option[1]; switch ($option_switch) { // config file case 'c': case '--config': $GLOBALS['vcsl']['config_file'] = $option_value; break; // debug case 'd': case '--debug': $GLOBALS['vcsl']['debug'] = true; break; // id case 'i': case '--id': $GLOBALS['vcsl']['log_info']['id'] = $option_value; break; // repository case 'r': case '--repo': $GLOBALS['vcsl']['log_info']['repo_url'] = $option_value; break; // VCS type case 't': case '--type': $GLOBALS['vcsl']['vcs_type'] = $option_value; $GLOBALS['vcsl']['log_info']['vcs_type'] = $GLOBALS['vcsl']['vcs_type']; if (!in_array($GLOBALS['vcsl']['vcs_type'], $GLOBALS['vcsl']['valid_vcs_type'])) { Usage('Invalid VCS type; must be one of: ' . implode(',', $GLOBALS['vcsl']['valid_vcs_type']), EXIT_STATUS_INVALID_VCS); } // end if invalid vcs type break; // user case 'u': case '--user': $GLOBALS['vcsl']['log_info']['username'] = $option_value; break; // help default: case 'h': case '?': case '--help': // no action - default break; } // end switch option } // end foreach option } // end if options // parse config file $GLOBALS['vcsl']['config'] = parse_config_file($GLOBALS['vcsl']['config_file']); // check vcs parameters parse_vcs_parameters($GLOBALS['vcsl']['vcs_type'], $GLOBALS['vcsl']['log_info']); // get raw log output from vcs $vcslog = gather_vcs_log($GLOBALS['vcsl']['vcs_type'], $GLOBALS['vcsl']['log_info']); // parse vcs log parse_vcs_log($GLOBALS['vcsl']['vcs_type'], $vcslog, $GLOBALS['vcsl']['log_info']); // store log info in database save_vcs_log_info($GLOBALS['vcsl']['log_info']); /***** FUNCTIONS *****/ function Usage($message = '', $exit_code = EXIT_STATUS_USAGE) { $error_message = null; if (!empty($message)) { $error_message = rtrim($message)."\n\n"; } // end if message $usage_message =<<< END == {$GLOBALS['vcsl']['progname']} v{$GLOBALS['vcsl']['version']} == {$_SERVER['argv'][0]} [-h|-?|--help] [-c|--config ] [-t|--type] [-r|--repo ] [-u|--user ] [-i|--id ] ex: {$_SERVER['argv'][0]} -t hg -u remote_user ex: {$_SERVER['argv'][0]} -t svn -r file:///path/to/repo -i 1234 END; fwrite(STDOUT, $error_message); fwrite(STDOUT, $usage_message); exit($exit_code); } // end function Usage() /** * Gathers the log for the given revision * * @param string $vcs_type * @param string $log_info * @return string */ function gather_vcs_log($vcs_type, $log_info) { $return_val = null; $cmd = null; switch ($vcs_type) { case 'hg': $cmd = $GLOBALS['vcsl']['config']['hgcmd'] . ' log -v -r ' . escapeshellarg($log_info['id']) . ' ' . escapeshellarg($log_info['repo_url']); break; case 'svn': $cmd = $GLOBALS['vcsl']['config']['svncmd'] . ' log -v -r ' . escapeshellarg($log_info['id']) . ' ' . escapeshellarg($log_info['repo_url']); break; } // end switch $vcs_type; if (!empty($cmd)) { $cmd .= ' 2> /dev/null'; vcs_debug('Executing VCS log command: ' . $cmd); exec($cmd, $cmd_output, $cmd_return); if (0 != $cmd_return) { Usage('Unable to retreive VCS log', EXIT_STATUS_ERROR_RETREIVING_VCS_LOG); } // end if bad return value $return_val = implode("\n", $cmd_output); } // end if cmd vcs_debug('Log: ' . $return_val); return $return_val; } // end function gather_vcs_log() /** * Parses config file * * @param string $config_file path to config file * @return array */ function parse_config_file($config_file) { if (!file_exists($config_file)) { Usage("Config File '$config_file' doesn't exist", EXIT_STATUS_CONFIG_ERROR); } // end if no file $config = parse_ini_file($config_file); vcs_debug('Parsed config file: ' . print_r($config, true)); return $config; } // end function parse_config_file() /** * Parses the VCS log * * @param string $vcs_type * @param string $log * @param array $log_info */ function parse_vcs_log($vcs_type, $log, &$log_info) { $parse_vcs_log_func = 'parse_vcs_log_'.$vcs_type; $parsed_log = $parse_vcs_log_func($log); if (empty($parsed_log)) { Usage('Error parsing VCS log', EXIT_STATUS_ERROR_PARSING_LOG); } // end if no $parsed_log $log_info['committed'] = $parsed_log['committed']; $log_info['log'] = $parsed_log['log']; if (!empty($parsed_log['lines_changed'])) { $log_info['lines_changed'] = $parsed_log['lines_changed']; } // end if lines_changed if (empty($log_info['username'])) { $log_info['username'] = $parsed_log['username']; } // end if no existing username else { $log_info['submitted_username'] = $parsed_log['username']; } // end if existing username vcs_debug('Parsed VCS Log: ' . print_r($log_info, true)); } // end parse_vcs_log() /** * Parses hg log into an array * * @param string $log * @return array */ function parse_vcs_log_hg($log) { $return_arr = array(); /* changeset: 2:aca97cbe35ef user: Douglas E. Warner date: Thu May 08 14:42:39 2008 -0400 files: stuff.txt description: updating stuff */ /* changeset: 6:f10158047613 tag: tip user: Douglas E. Warner date: Thu May 08 17:05:21 2008 -0400 files: stuff/things.txt description: adding things */ $matches = array(); if (preg_match('/user:\s+(.*)/', $log, $matches)) { $return_arr['username'] = $matches[1]; } // end if matches if (preg_match('/date:\s+(.*)/', $log, $matches)) { $return_arr['committed'] = strtotime($matches[1]); } // end if matches if (preg_match('/' . "(files:\s+.*?\n" . "description:\n.*)" . '/ms', $log, $matches)) { $return_arr['log'] = trim($matches[1]); } // end if matches return $return_arr; } // end function parse_vcs_log_hg() /** * parses text in $log into an array * * @param string $log * @return array */ function parse_vcs_log_svn($log) { $return_arr = array(); /* ------------------------------------------------------------------------ r1138 | silfreed | 2005-03-02 13:19:02 -0500 (Wed, 02 Mar 2005) | 2 lines bug#0000366: testing getting svn comments into mantis [x4] ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ r1162 | balleman | 2005-07-06 20:24:35 -0400 (Wed, 06 Jul 2005) | 3 lines Changed paths: M /trunk/etc/netmrg.xml.in M /trunk/share/doc/ChangeLog M /trunk/src/settings.cpp M /trunk/www/lib/format.php M /trunk/www/lib/graphing.php M /trunk/www/lib/misc.php M /trunk/www/lib/processing.php - bug#0000385: Graph titles, axis labels, legend labels not properly escaped - bug#0000406: verify against rrdtool-1.2 (added config option for rrd version, fixed escaping) ------------------------------------------------------------------------ ` */ $matches = array(); if (preg_match('/[-]{70,}' . '\s+r(\d+)\s+\|' . '\s+(.*?)\s+\|' . '\s+(\d+-\d+-\d+\s+\d+:\d+:\d+).*?\s+\|' . '\s+(\d+)\s+\S+\s+' . '(.*?)' . '[-]{70,}/ms', $log, $matches)) { $return_arr['username'] = $matches[2]; $return_arr['committed'] = strtotime($matches[3]); $return_arr['lines_changed'] = $matches[4]; $return_arr['log'] = trim($matches[5]); } // end if matches return $return_arr; } // end function parse_vcs_log_svn() /** * parses VCS parameters according to VCS type * * @param string $vcs_type * @param array $log_info */ function parse_vcs_parameters($vcs_type, &$log_info) { switch ($vcs_type) { case 'hg': $log_info['repo_url'] = getcwd(); $log_info['id'] = $_SERVER['HG_NODE']; break; case 'svn': break; default: Usage('Unknown VCS type ('.$vcs_type.')', EXIT_STATUS_HANDLING_VCS); break; } // end switch $vcs_type if (empty($log_info['repo_url']) || empty($log_info['id'])) { Usage('No repo URL or revision id found', EXIT_STATUS_HANDLING_VCS); } // end if no repo_url or id $log_info['repo'] = basename($log_info['repo_url']); /* If the user has authenticated through HTTP, we want to store that * user as the real user who submitted the change. * Any usernames saved in the log later will be stored in the * submitted_username field. */ if (!empty($_SERVER['HTTP_AUTH_USER'])) { $log_info['username'] = $_SERVER['HTTP_AUTH_USER']; } // end if HTTP_AUTH_USER else if (!empty($_SERVER['REMOTE_USER'])) { $log_info['username'] = $_SERVER['REMOTE_USER']; } // end if REMOTE_USER } // end function parse_vcs_parameters() /** * Saves VCS log info into database * * @param array $log_info */ function save_vcs_log_info($log_info) { $db = MDB2::connect($GLOBALS['vcsl']['config']['dsn']); if (PEAR::isError($db)) { Usage('Error connecting to database: ' . $db->getMessage(), EXIT_STATUS_ERROR_DB_CONNECT); } // end if error $sql = 'REPLACE INTO `vcslog` SET ' . '`repo` = ' . $db->quote($log_info['repo']) . ', ' . '`vcs_type` = ' . $db->quote($log_info['vcs_type']) . ', ' . '`id` = ' . $db->quote($log_info['id']) . ', ' . '`username` = ' . $db->quote($log_info['username']) . ', ' . '`committed` = FROM_UNIXTIME(' . $log_info['committed'] . '), ' . '`submitted_username` = ' . $db->quote($log_info['submitted_username']) . ', ' . '`lines_changed` = ' . $db->quote($log_info['lines_changed']) . ', ' . '`log` = ' . $db->quote($log_info['log']) . ' '; vcs_debug('Running SQL: ' . $sql); $res = $db->exec($sql); if (PEAR::isError($res)) { Usage('Error saving VCS log: ' . $res->getMessage(), EXIT_STATUS_ERROR_DB_QUERY); } // end if error } // end function save_vcs_log_info() /** * prints debug messages to stderr if debug is turned on * * @param string $message */ function vcs_debug($message) { if ($GLOBALS['vcsl']['debug']) { fwrite(STDERR, trim($message)."\n"); } // end if debug } // end function vcs_debug() // vim:fenc=utf-8:ft=php:ai:si:ts=4:sw=4: