Progress towards more reliable build changesets detection

This commit is contained in:
Latif Khalifa
2013-01-22 03:00:46 +01:00
parent 36882eb5e8
commit 7d31e3af5a
20 changed files with 1460 additions and 86 deletions

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
*.exe
*.bz2
*.dmg
*.zip
*.bak
*~
*.log
lib/source/*
lib/singularity_revisions*

View File

@@ -1,7 +0,0 @@
syntax: glob
*.exe
*.bak
*~
*.log
lib/source/*
lib/signularity_revisions*

92
buildsite.css Normal file
View File

@@ -0,0 +1,92 @@
html {
padding: 0;
margin: 0;
background-color: #1e1e1e;
}
image {
border: none;
}
body, select {
padding: 0;
margin: 0;
background-color: #1e1e1e;
font: normal 10pt "Lucida Grande","Lucida Sans Unicode",sans-serif;
color: #a0a0a0;
}
div {
display: block;
padding: 0;
margin: 0;
}
#everything {
background: url(images/body-bg.png) top repeat-x;
min-height: 100%;
}
#page-wrapper {
margin: 0 auto;
width: 960px;
}
#header {
margin-bottom: 10px;
width: 100%;
height: 144px;
background: url(images/singularity_logo.gif) left top no-repeat;
border-bottom: solid 2px black;
}
.container {
padding: 5px;
margin-bottom: 10px;
background: url(images/container-bg.gif) top repeat-x;
border-bottom: solid 2px black;
background-color: #1b1b1b;
}
a, a:link, a:hover, a:visited, a:active {
text-decoration: none;
color: #e0e0e0;
}
a.dimmer {
color: #a0a0a0;
}
.bottom-links {
text-align: center;
}
td, th {
text-align: left;
margin: 0;
padding: 3px 3px 3px 3px;
vertical-align: top;
display: table-cell;
border: none;
}
th {
border-bottom: solid 1px #404040;
background: url(images/container-bg.gif) top repeat-x;
padding: 5px 3px 10px 3px;
}
.build-list {
padding: 0;
margin: 0;
border-spacing: 0;
width: 100%;
border: solid 1px #404040;
}
pre {
font-family: monospace;
font-size: 8pt;
white-space: pre-wrap;
word-wrap: break-word;
}

126
feed.php Normal file
View File

@@ -0,0 +1,126 @@
<?php
define("SITE_ROOT", realpath(dirname(__file__)));
require_once SITE_ROOT . "/lib/init.php";
function parse_email($email)
{
$ret = array("name" => $email, "email" => "");
if (preg_match("|([^\<]*)<([^>]*)>|", $email, $m)) {
$ret["name"] = trim($m[1]);
$ret["email"] = trim($m[2]);
}
return $ret;
}
function sort_by_date($a, $b)
{
if ($a["time"] < $b["time"]) {
return 1;
} else if ($a["time"] > $b["time"]) {
return -1;
}
return 0;
}
function print_changeset($row)
{
$author = parse_email($row["author"]);
$gid = md5($author["email"]);
$avatar = (USE_SSL ? "https://secure.gravatar.com" : "http://www.gravatar.com") .
"/avatar/$gid?r=x&amp;d=mm&amp;s=48";
return '<tr><td valign="top" align="center"><img src="' . $avatar . '" alt="Avatar"/><br/>'
. htmlspecialchars($author["name"]) . '</td>'
. '<td valign="top"><a href="https://github.com/siana/SingularityViewer/commit/' . htmlspecialchars($row["hash"]) . '">' . htmlspecialchars($row["hash"]) . '</a><br/>'
. htmlspecialchars($row["time"]). "<br/><br/>\n\n<pre>"
. htmlspecialchars($row["message"]) . '</pre></td>';
}
function print_changes($current, $next)
{
global $DB;
$ret = "";
$revs = array();
if (!($res = $DB->query(kl_str_sql("select revisions from changes where build<=!i and build>!i order by build desc", $current->nr, $next->nr)))) {
return $ret;
} else {
while ($row = $DB->fetchRow($res)) {
$revs = array_merge($revs, explode(",", $row["revisions"]));
}
}
if ($res = $DB->query("select * from revs where hash in ('" . implode("','", $revs) . "')")) {
$ret .= '<table width="100%">';
$changesets = array();
while ($row = $DB->fetchRow($res)) {
$changesets[] = $row;
}
usort($changesets, "sort_by_date");
foreach ($changesets as $change) {
$ret .= print_changeset($change);
}
$ret .= '</table>';
}
return $ret;
}
$buildFeed = new FeedWriter(ATOM);
$buildFeed->setTitle('Singularity Automatic Development Builds');
$buildFeed->setLink('http://files.streamgrid.net/singularity/');
$buildFeed->setDescription('Latest automated build of the Singularity Viewer project');
// $buildFeed->setImage('Testing the RSS writer class',
// 'http://www.ajaxray.com/projects/rss',
// 'http://www.rightbrainsolution.com/images/logo.gif');
$chan = "SingularityAlpha";
$pageSize = 20;
$builds = array();
if ($res = $DB->query(kl_str_sql("select * from builds where chan=!s order by nr desc limit !i", $chan, $pageSize + 1))) {
while ($row = $DB->fetchRow($res)) {
$build = new stdClass;
$DB->loadFromDbRow($build, $res, $row);
$builds[] = $build;
}
}
$nrBuilds = count($builds);
if ($nrBuilds) {
for ($i = 0; $i < $pageSize; $i++) {
if (!isset($builds[$i])) continue;
$newItem = $buildFeed->createNewItem();
$newItem->setTitle("Singularity Alpha build " . $builds[$i]->nr);
$newItem->setLink("http://files.streamgrid.net/singularity/?build_id=" . $builds[$i]->nr);
$newItem->setDate($builds[$i]->modified);
if (isset($builds[$i+1])) {
$newItem->setDescription(print_changes($builds[$i], $builds[$i + 1]));
}
$buildFeed->addItem($newItem);
}
$buildFeed->genarateFeed();
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

BIN
images/body-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

BIN
images/container-bg.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

BIN
images/dl.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

BIN
images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
images/singularity_logo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

219
index.php Normal file
View File

@@ -0,0 +1,219 @@
<?php
define("SITE_ROOT", realpath(dirname(__file__)));
require_once SITE_ROOT . "/lib/init.php";
function parse_email($email)
{
$ret = array("name" => $email, "email" => "");
if (preg_match("|([^\<]*)<([^>]*)>|", $email, $m)) {
$ret["name"] = trim($m[1]);
$ret["email"] = trim($m[2]);
}
return $ret;
}
function print_changeset($row)
{
$author = parse_email($row["author"]);
$gid = md5(strtolower($author["email"]));
$avatar = (USE_SSL ? "https://secure.gravatar.com" : "http://www.gravatar.com") .
"/avatar/$gid?r=x&amp;d=mm&amp;s=48";
print '
<tr>
<td rowspan="2" style="text-align: center;"><img src="' . $avatar . '" alt="Avatar"/><br />' .
htmlspecialchars($author["name"]) . '</td>
<td><a href="https://github.com/siana/SingularityViewer/commit/' . htmlspecialchars($row["hash"]) . '">' . htmlspecialchars($row["hash"]) . '</a></td>
<td>' . htmlspecialchars($row["time"]).
' (' . Layout::since(strtotime($row["time"])) . ' ago)</td>
</tr>
<tr>
<td colspan="2" width="99%"><pre>' . htmlspecialchars($row["message"]) . '</pre></td>
</tr>';
}
function sort_by_date($a, $b)
{
if ($a["time"] < $b["time"]) {
return 1;
} else if ($a["time"] > $b["time"]) {
return -1;
}
return 0;
}
function print_changes($current, $next, $chan)
{
global $DB;
$revs = array();
if (!($res = $DB->query(kl_str_sql("select revisions from changes where chan=!s and build<=!i and build>!i order by build desc", $chan, $current->nr, $next->nr)))) {
return;
} else {
while ($row = $DB->fetchRow($res)) {
$revs = array_merge($revs, explode(",", $row["revisions"]));
}
}
if ($res = $DB->query(kl_str_sql("select * from revs where chan=!s and hash in ('" . implode("','", $revs) . "')", $chan))) {
$changesets = array();
while ($row = $DB->fetchRow($res)) {
$changesets[] = $row;
}
if (count($changesets) == 0) return;
print '<table style="width: 99%;">';
usort($changesets, "sort_by_date");
foreach ($changesets as $change) {
print_changeset($change);
}
print '</table>';
}
}
Function print_build($current, $next, $buildNr, $chan)
{
print "
<tr style=\"background-color: #303030;\">
<th><a href=\"" . URL_ROOT ."?build_id={$current->nr}\">Build " . htmlspecialchars($current->nr). "</a><br/>";
if ($next) {
if (($current->linux_file && $current->osx_file && $current->linux64_file)) {
print "<br/><br/>";
}
elseif (($current->linux_file && $current->osx_file)) {
print "<br/>";
}
print '
<a class="dimmer" href="javascript:void(0)" id="toggle_link_'. $current->nr . '" onclick="javascript:toggleChanges('. $current->nr . ')">' .
($buildNr ? 'Hide changes &lt;&lt;' : 'Show changes &gt;&gt;') . '</a>';
}
print "</th><th>" . htmlspecialchars($current->modified). " (" . Layout::since(strtotime($current->modified)) . " ago)</th>
<th>" . htmlspecialchars($current->chan). "</th>
<th><a href='" . URL_ROOT . "/" . $current->file . "'><img src=\"" . IMG_ROOT . "/dl.gif\" alt=\"Download Windows Build\"/>&nbsp;Windows</a>&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a class='dimmer' href='" . URL_ROOT . "/" . $current->file . ".log'>Build Log</a>";
if ($current->linux_file) {
print "<br/><a href='" . URL_ROOT . "/" . $current->linux_file . "'><img src=\"" . IMG_ROOT . "/dl.gif\" alt=\"Download Linux Build (32 bit)\"/>&nbsp;Linux (32 bit)</a>";
if (file_exists($current->linux_file . ".log")) {
print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a class='dimmer' href='" . URL_ROOT . "/" . $current->linux_file . ".log'>Build Log</a>";
}
}
if ($current->linux64_file) {
print "<br/><a href='" . URL_ROOT . "/" . $current->linux64_file . "'><img src=\"" . IMG_ROOT . "/dl.gif\" alt=\"Download Linux Build (64 bit)\"/>&nbsp;Linux (64 bit)</a>";
if (file_exists($current->linux64_file . ".log")) {
print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a class='dimmer' href='" . URL_ROOT . "/" . $current->linux64_file . ".log'>Build Log</a>";
}
}
if ($current->osx_file) {
print "<br/><a href='" . URL_ROOT . "/" . $current->osx_file . "'><img src=\"" . IMG_ROOT . "/dl.gif\" alt=\"Download Mac OS X Build\"/>&nbsp;Mac OS X</a>";
if (file_exists($current->osx_file . ".log")) {
print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a class='dimmer' href='" . URL_ROOT . "/" . $current->osx_file . ".log'>Build Log</a>";
}
}
print "</th></tr>";
if ($next) {
print '<tr' . ($buildNr ? '' : ' style="display: none;"') . ' id="changes_' . $current->nr . '"><td colspan="4">';
print_changes($current, $next, $chan);
print "</td></tr>";
}
}
function chan_selector($current_chan)
{
// return;
global $CHANS;
print '<form method="get" action="index.php">';
print 'Select channel&nbsp;<select name="chan" onchange="this.form.submit()">';
foreach($CHANS as $chan => $ref) {
print "<option value=\"$chan\"" . ($current_chan == $chan ? " selected=\"selected\"" : "") . ">$chan</option>";
}
print '</select><noscript><input type="submit" value="Change"/></noscript></form><br />';
}
Layout::header();
if (isset($_GET["chan"]) && isset($CHANS[$_GET["chan"]])) {
$chan = $_GET["chan"];
} else {
// $chan = "SingularityMultiWearable";
$chan = "SingularityAlpha";
}
$pageSize = 20;
$builds = array();
$buildNr = 0;
$where = "";
if (isset($_GET["build_id"])) {
$buildNr = (int)$_GET["build_id"];
$pageSize = 1;
$where = kl_str_sql(" and nr <= !i ", $buildNr);
} else {
chan_selector($chan);
}
if ($res = $DB->query(kl_str_sql("select * from builds where chan=!s $where order by nr desc limit !i", $chan, $pageSize + 1))) {
while ($row = $DB->fetchRow($res)) {
$build = new stdClass;
$DB->loadFromDbRow($build, $res, $row);
$file = "{$chan}_" . str_replace(".", "-", $build->version) . "_Setup.exe";
$build->file = file_exists($file) ? $file : false;
$linux_file = "{$chan}-i686-{$build->version}.tar.bz2";
$build->linux_file = file_exists($linux_file) ? $linux_file : false;
$linux64_file = "{$chan}-x86_64-{$build->version}.tar.bz2";
$build->linux64_file = file_exists($linux64_file) ? $linux64_file : false;
$osx_file = "{$chan}_" . str_replace(".", "_", $build->version) . ".dmg";
$build->osx_file = file_exists($osx_file) ? $osx_file : false;
$builds[] = $build;
}
}
$nrBuilds = count($builds);
if ($nrBuilds) {
print '<table class="build-list">';
for ($i = 0; $i < $pageSize; $i++) {
if (!isset($builds[$i])) continue;
print_build($builds[$i], $builds[$i + 1], $buildNr, $chan);
}
print '</table>';
}
Layout::footer();
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View File

@@ -8,16 +8,16 @@ header("Content-Type: text/plain");
$chan = "SingularityAlpha";
if (isset($_GET["chan"])) {
$chan = preg_replace("%[^\\w_-]%i", "", $_GET["chan"]);
$chan = preg_replace("%[^\\w_-]%i", "", $_GET["chan"]);
}
$files = glob($chan . "_*.exe");
if (count($files) === 0) {
header("HTTP/1.0 404 Not Found");
header("Content-Type: text/plain");
print "Requested channel was not found";
die();
header("HTTP/1.0 404 Not Found");
header("Content-Type: text/plain");
print "Requested channel was not found";
die();
}
$files = array_reverse($files);
@@ -26,5 +26,14 @@ $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
$latest = urlencode($files[0]);
header("Location: http://${host}${uri}/${latest}");
?>
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View File

@@ -38,7 +38,7 @@ class DBH
$this->db_user = $db_user;
$this->db_host = $db_host;
$this->dbh = @sqlite_popen($db_name, 0666, $error_msg);
$this->dbh = @sqlite_open($db_name, 0666, $error_msg);
if (!$this->dbh) {
DBH::log("[error] connection to database failed: $error_msg");
@@ -54,7 +54,7 @@ class DBH
if (!$res) {
DBH::log("[error] ".$q);
DBH::log("[error_msg] " . $error_msg);
DBH::log("[error_msg] " . $error_msg . " " . @sqlite_error_string(@sqlite_last_error(($this->dbh))));
$this->last_error = $error_msg;
$e = debug_backtrace();
@@ -69,15 +69,6 @@ class DBH
return false;
} else {
if ($res !== TRUE) {
$result_id = (int)$res;
if (!isset($this->field_desc[$result_id])) {
$nf = sqlite_num_fields($res);
for ($i=0; $i<$nf; $i++) {
$this->field_desc[$result_id][sqlite_field_name($res, $i)] = sqlite_field_name($res, $i);
}
}
}
DBH::log("[success] ".$q);
return $res;
}
@@ -86,19 +77,14 @@ class DBH
function loadFromDbRow(&$obj, $res, $row)
{
foreach ($row as $symbolicName => $nativeName){
if ($nativeName && ($this->field_desc[(int)$res][$symbolicName] == "timestamp" ||
$this->field_desc[(int)$res][$symbolicName] == "date")) {
$obj->{$symbolicName} = strtotime($nativeName);
} else {
$obj->{$symbolicName} = $nativeName;
}
$obj->{$symbolicName} = $nativeName;
}
return true;
}
function fetchRow($res)
{
return @sqlite_fetch_array($res);
return @sqlite_fetch_array($res, SQLITE_ASSOC);
}
}

167
lib/FeedItem.php Normal file
View File

@@ -0,0 +1,167 @@
<?php
/**
* Univarsel Feed Writer
*
* FeedItem class - Used as feed element in FeedWriter class
*
* @package UnivarselFeedWriter
* @author Anis uddin Ahmad <anisniit@gmail.com>
* @link http://www.ajaxray.com/projects/rss
*/
class FeedItem
{
private $elements = array(); //Collection of feed elements
private $version;
/**
* Constructor
*
* @param contant (RSS1/RSS2/ATOM) RSS2 is default.
*/
function __construct($version = RSS2)
{
$this->version = $version;
}
/**
* Add an element to elements array
*
* @access public
* @param srting The tag name of an element
* @param srting The content of tag
* @param array Attributes(if any) in 'attrName' => 'attrValue' format
* @return void
*/
public function addElement($elementName, $content, $attributes = null)
{
$this->elements[$elementName]['name'] = $elementName;
$this->elements[$elementName]['content'] = $content;
$this->elements[$elementName]['attributes'] = $attributes;
}
/**
* Set multiple feed elements from an array.
* Elements which have attributes cannot be added by this method
*
* @access public
* @param array array of elements in 'tagName' => 'tagContent' format.
* @return void
*/
public function addElementArray($elementArray)
{
if(! is_array($elementArray)) return;
foreach ($elementArray as $elementName => $content)
{
$this->addElement($elementName, $content);
}
}
/**
* Return the collection of elements in this feed item
*
* @access public
* @return array
*/
public function getElements()
{
return $this->elements;
}
// Wrapper functions ------------------------------------------------------
/**
* Set the 'dscription' element of feed item
*
* @access public
* @param string The content of 'description' element
* @return void
*/
public function setDescription($description)
{
$tag = ($this->version == ATOM)? 'summary' : 'description';
$this->addElement($tag, $description);
}
/**
* @desc Set the 'title' element of feed item
* @access public
* @param string The content of 'title' element
* @return void
*/
public function setTitle($title)
{
$this->addElement('title', $title);
}
/**
* Set the 'date' element of feed item
*
* @access public
* @param string The content of 'date' element
* @return void
*/
public function setDate($date)
{
if(! is_numeric($date))
{
$date = strtotime($date);
}
if($this->version == ATOM)
{
$tag = 'updated';
$value = date(DATE_ATOM, $date);
}
elseif($this->version == RSS2)
{
$tag = 'pubDate';
$value = date(DATE_RSS, $date);
}
else
{
$tag = 'dc:date';
$value = date("Y-m-d", $date);
}
$this->addElement($tag, $value);
}
/**
* Set the 'link' element of feed item
*
* @access public
* @param string The content of 'link' element
* @return void
*/
public function setLink($link)
{
if($this->version == RSS2 || $this->version == RSS1)
{
$this->addElement('link', $link);
}
else
{
$this->addElement('link','',array('href'=>$link));
$this->addElement('id', FeedWriter::uuid($link,'urn:uuid:'));
}
}
/**
* Set the 'encloser' element of feed item
* For RSS 2.0 only
*
* @access public
* @param string The url attribute of encloser tag
* @param string The length attribute of encloser tag
* @param string The type attribute of encloser tag
* @return void
*/
public function setEncloser($url, $length, $type)
{
$attributes = array('url'=>$url, 'length'=>$length, 'type'=>$type);
$this->addElement('enclosure','',$attributes);
}
} // end of class FeedItem
?>

430
lib/FeedWriter.php Normal file
View File

@@ -0,0 +1,430 @@
<?php
// RSS 0.90 Officially obsoleted by 1.0
// RSS 0.91, 0.92, 0.93 and 0.94 Officially obsoleted by 2.0
// So, define constants for RSS 1.0, RSS 2.0 and ATOM
define('RSS1', 'RSS 1.0', true);
define('RSS2', 'RSS 2.0', true);
define('ATOM', 'ATOM', true);
/**
* Univarsel Feed Writer class
*
* Genarate RSS 1.0, RSS2.0 and ATOM Feed
*
* @package UnivarselFeedWriter
* @author Anis uddin Ahmad <anisniit@gmail.com>
* @link http://www.ajaxray.com/projects/rss
*/
class FeedWriter
{
private $channels = array(); // Collection of channel elements
private $items = array(); // Collection of items as object of FeedItem class.
private $data = array(); // Store some other version wise data
private $CDATAEncoding = array(); // The tag names which have to encoded as CDATA
private $version = null;
/**
* Constructor
*
* @param constant the version constant (RSS1/RSS2/ATOM).
*/
function __construct($version = RSS2)
{
$this->version = $version;
// Setting default value for assential channel elements
$this->channels['title'] = $version . ' Feed';
$this->channels['link'] = 'http://www.ajaxray.com/blog';
//Tag names to encode in CDATA
$this->CDATAEncoding = array('description', 'content:encoded', 'summary');
}
// Start # public functions ---------------------------------------------
/**
* Set a channel element
* @access public
* @param srting name of the channel tag
* @param string content of the channel tag
* @return void
*/
public function setChannelElement($elementName, $content)
{
$this->channels[$elementName] = $content ;
}
/**
* Set multiple channel elements from an array. Array elements
* should be 'channelName' => 'channelContent' format.
*
* @access public
* @param array array of channels
* @return void
*/
public function setChannelElementsFromArray($elementArray)
{
if(! is_array($elementArray)) return;
foreach ($elementArray as $elementName => $content)
{
$this->setChannelElement($elementName, $content);
}
}
/**
* Genarate the actual RSS/ATOM file
*
* @access public
* @return void
*/
public function genarateFeed()
{
header("Content-type: text/xml");
$this->printHead();
$this->printChannels();
$this->printItems();
$this->printTale();
}
/**
* Create a new FeedItem.
*
* @access public
* @return object instance of FeedItem class
*/
public function createNewItem()
{
$Item = new FeedItem($this->version);
return $Item;
}
/**
* Add a FeedItem to the main class
*
* @access public
* @param object instance of FeedItem class
* @return void
*/
public function addItem($feedItem)
{
$this->items[] = $feedItem;
}
// Wrapper functions -------------------------------------------------------------------
/**
* Set the 'title' channel element
*
* @access public
* @param srting value of 'title' channel tag
* @return void
*/
public function setTitle($title)
{
$this->setChannelElement('title', $title);
}
/**
* Set the 'description' channel element
*
* @access public
* @param srting value of 'description' channel tag
* @return void
*/
public function setDescription($desciption)
{
$this->setChannelElement('description', $desciption);
}
/**
* Set the 'link' channel element
*
* @access public
* @param srting value of 'link' channel tag
* @return void
*/
public function setLink($link)
{
$this->setChannelElement('link', $link);
}
/**
* Set the 'image' channel element
*
* @access public
* @param srting title of image
* @param srting link url of the imahe
* @param srting path url of the image
* @return void
*/
public function setImage($title, $link, $url)
{
$this->setChannelElement('image', array('title'=>$title, 'link'=>$link, 'url'=>$url));
}
/**
* Set the 'about' channel element. Only for RSS 1.0
*
* @access public
* @param srting value of 'about' channel tag
* @return void
*/
public function setChannelAbout($url)
{
$this->data['ChannelAbout'] = $url;
}
/**
* Genarates an UUID
* @author Anis uddin Ahmad <admin@ajaxray.com>
* @param string an optional prefix
* @return string the formated uuid
*/
public function uuid($key = null, $prefix = '')
{
$key = ($key == null)? uniqid(rand()) : $key;
$chars = md5($key);
$uuid = substr($chars,0,8) . '-';
$uuid .= substr($chars,8,4) . '-';
$uuid .= substr($chars,12,4) . '-';
$uuid .= substr($chars,16,4) . '-';
$uuid .= substr($chars,20,12);
return $prefix . $uuid;
}
// End # public functions ----------------------------------------------
// Start # private functions ----------------------------------------------
/**
* Prints the xml and rss namespace
*
* @access private
* @return void
*/
private function printHead()
{
$out = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
if($this->version == RSS2)
{
$out .= '<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
>' . PHP_EOL;
}
elseif($this->version == RSS1)
{
$out .= '<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
>' . PHP_EOL;;
}
else if($this->version == ATOM)
{
$out .= '<feed xmlns="http://www.w3.org/2005/Atom">' . PHP_EOL;;
}
echo $out;
}
/**
* Closes the open tags at the end of file
*
* @access private
* @return void
*/
private function printTale()
{
if($this->version == RSS2)
{
echo '</channel>' . PHP_EOL . '</rss>';
}
elseif($this->version == RSS1)
{
echo '</rdf:RDF>';
}
else if($this->version == ATOM)
{
echo '</feed>';
}
}
/**
* Creates a single node as xml format
*
* @access private
* @param srting name of the tag
* @param mixed tag value as string or array of nested tags in 'tagName' => 'tagValue' format
* @param array Attributes(if any) in 'attrName' => 'attrValue' format
* @return string formatted xml tag
*/
private function makeNode($tagName, $tagContent, $attributes = null)
{
$nodeText = '';
$attrText = '';
if(is_array($attributes))
{
foreach ($attributes as $key => $value)
{
$attrText .= " $key=\"$value\" ";
}
}
if(is_array($tagContent) && $this->version == RSS1)
{
$attrText = ' rdf:parseType="Resource"';
}
$attrText .= (in_array($tagName, $this->CDATAEncoding) && $this->version == ATOM)? ' type="html" ' : '';
$nodeText .= (in_array($tagName, $this->CDATAEncoding))? "<{$tagName}{$attrText}><![CDATA[" : "<{$tagName}{$attrText}>";
if(is_array($tagContent))
{
foreach ($tagContent as $key => $value)
{
$nodeText .= $this->makeNode($key, $value);
}
}
else
{
$nodeText .= (in_array($tagName, $this->CDATAEncoding))? $tagContent : htmlentities($tagContent);
}
$nodeText .= (in_array($tagName, $this->CDATAEncoding))? "]]></$tagName>" : "</$tagName>";
return $nodeText . PHP_EOL;
}
/**
* @desc Print channels
* @access private
* @return void
*/
private function printChannels()
{
//Start channel tag
switch ($this->version)
{
case RSS2:
echo '<channel>' . PHP_EOL;
break;
case RSS1:
echo (isset($this->data['ChannelAbout']))? "<channel rdf:about=\"{$this->data['ChannelAbout']}\">" : "<channel rdf:about=\"{$this->channels['link']}\">";
break;
}
//Print Items of channel
foreach ($this->channels as $key => $value)
{
if($this->version == ATOM && $key == 'link')
{
// ATOM prints link element as href attribute
echo $this->makeNode($key,'',array('href'=>$value));
//Add the id for ATOM
echo $this->makeNode('id',$this->uuid($value,'urn:uuid:'));
}
else
{
echo $this->makeNode($key, $value);
}
}
//RSS 1.0 have special tag <rdf:Seq> with channel
if($this->version == RSS1)
{
echo "<items>" . PHP_EOL . "<rdf:Seq>" . PHP_EOL;
foreach ($this->items as $item)
{
$thisItems = $item->getElements();
echo "<rdf:li resource=\"{$thisItems['link']['content']}\"/>" . PHP_EOL;
}
echo "</rdf:Seq>" . PHP_EOL . "</items>" . PHP_EOL . "</channel>" . PHP_EOL;
}
}
/**
* Prints formatted feed items
*
* @access private
* @return void
*/
private function printItems()
{
foreach ($this->items as $item)
{
$thisItems = $item->getElements();
//the argument is printed as rdf:about attribute of item in rss 1.0
echo $this->startItem($thisItems['link']['content']);
foreach ($thisItems as $feedItem )
{
echo $this->makeNode($feedItem['name'], $feedItem['content'], $feedItem['attributes']);
}
echo $this->endItem();
}
}
/**
* Make the starting tag of channels
*
* @access private
* @param srting The vale of about tag which is used for only RSS 1.0
* @return void
*/
private function startItem($about = false)
{
if($this->version == RSS2)
{
echo '<item>' . PHP_EOL;
}
elseif($this->version == RSS1)
{
if($about)
{
echo "<item rdf:about=\"$about\">" . PHP_EOL;
}
else
{
die('link element is not set .\n It\'s required for RSS 1.0 to be used as about attribute of item');
}
}
else if($this->version == ATOM)
{
echo "<entry>" . PHP_EOL;
}
}
/**
* Closes feed item tag
*
* @access private
* @return void
*/
private function endItem()
{
if($this->version == RSS2 || $this->version == RSS1)
{
echo '</item>' . PHP_EOL;
}
else if($this->version == ATOM)
{
echo "</entry>" . PHP_EOL;
}
}
// End # private functions ----------------------------------------------
} // end of class FeedWriter

98
lib/Layout.php Normal file
View File

@@ -0,0 +1,98 @@
<?php
class Layout
{
function since($since)
{
$since = time() - $since;
$chunks = array(
array(60 * 60 * 24 * 365 , 'year'),
array(60 * 60 * 24 * 30 , 'month'),
array(60 * 60 * 24 * 7, 'week'),
array(60 * 60 * 24 , 'day'),
array(60 * 60 , 'hour'),
array(60 , 'minute'),
array(1 , 'second')
);
for ($i = 0, $j = count($chunks); $i < $j; $i++) {
$seconds = $chunks[$i][0];
$name = $chunks[$i][1];
if (($count = floor($since / $seconds)) != 0) {
break;
}
}
$print = ($count == 1) ? '1 '.$name : "$count {$name}s";
return $print;
}
function header()
{ ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link rel="stylesheet" type="text/css" href="<?php print URL_ROOT ?>/buildsite.css"/>
<link href="<?php print URL_ROOT ?>/feed.php" rel="alternate" title="Singularity Automatic Development Builds" type="application/atom+xml" />
<link rel="shortcut icon" href="<?php print IMG_ROOT ?>/favicon.ico" type="image/x-icon" />
<title>Singularity Viewer Automated Build System</title>
<script type="text/javascript">
//<![CDATA[
function toggleChanges(id)
{
var change = document.getElementById("changes_" + id);
var link = document.getElementById("toggle_link_" + id);
if (change) {
if (change.style.display == "") {
change.style.display = "none";
link.innerHTML = "Show changes &gt;&gt;";
} else {
change.style.display = "";
link.innerHTML = "Hide changes &lt;&lt;";
}
}
return false;
}
//]]>
</script>
</head>
<body>
<div id="everything">
<div id="page-wrapper">
<div id="header"></div>
<div class="container"><a href="<?php print URL_ROOT ?>" style="font-size: 20px;">Automated Build System</a><br/><br/>
<?php
}
function footer()
{
{ ?>
</div><!-- container -->
<div class="container">
<table style="width: 100%; border: none; padding: 0;"><tr>
<td class="bottom-links"><a href="http://www.singularityviewer.org/">Singularity Main Site</a></td>
<td class="bottom-links"><a href="http://www.singularityviewer.org/about">About</a></td>
<td class="bottom-links"><a href="http://code.google.com/p/singularity-viewer/issues/">Issue Tracker</a></td>
<td class="bottom-links"><a href="https://github.com/singularity-viewer/SingularityViewer">Source Tracker</a></td>
<td width="50%" style="text-align: right;">&copy; 2012 Singularity Viewer Project</td>
</tr></table>
</div>
</div><!-- everything -->
</div><!-- page-wrapper -->
</body>
</html>
<?php
}
}
}

72
lib/cleanup.php Executable file
View File

@@ -0,0 +1,72 @@
#!/usr/bin/php
<?php
define("SITE_ROOT", realpath(dirname(__file__) . "/.."));
require_once SITE_ROOT . "/lib/init.php";
function get_old_builds($chan, $nrToKeep)
{
global $DB;
$builds = array();
if ($res = $DB->query(kl_str_sql("select * from builds where chan=!s and nr not in (select nr from builds where chan=!s order by nr desc limit !i)", $chan, $chan, $nrToKeep))) {
while ($row = $DB->fetchRow($res)) {
$build = new stdClass;
$DB->loadFromDbRow($build, $res, $row);
$file = "{$chan}_" . str_replace(".", "-", $build->version) . "_Setup.exe";
$build->file = file_exists($file) ? $file : false;
$linux_file = "{$chan}-i686-{$build->version}.tar.bz2";
$build->linux_file = file_exists($linux_file) ? $linux_file : false;
$linux64_file = "{$chan}-x86_64-{$build->version}.tar.bz2";
$build->linux64_file = file_exists($linux64_file) ? $linux64_file : false;
$osx_file = "{$chan}_" . str_replace(".", "_", $build->version) . ".dmg";
$build->osx_file = file_exists($osx_file) ? $osx_file : false;
$builds[] = $build;
}
}
return $builds;
}
chdir(SITE_ROOT);
$builds = get_old_builds("SingularityAlpha", 21);
$nrBuilds = count($builds);
for ($i=0; $i<$nrBuilds; $i++) {
$b = $builds[$i];
$f = array();
if ($b->file) $f[] = $b->file;
if ($b->linux_file) $f[] = $b->linux_file;
if ($b->linux64_file) $f[] = $b->linux64_file;
if ($b->osx_file) $f[] = $b->osx_file;
print "Cleaning build nr.: {$b->nr}\n";
for ($j=0; $j<count($f); $j++) {
print " Deleting {$f[$j]}\n";
@unlink(SITE_ROOT . "/" . $f[$j]);
@unlink(SITE_ROOT . "/" . $f[$j] . ".log");
}
$DB->query(kl_str_sql("delete from builds where nr=!i and chan=!s", $b->nr, $b->chan));
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

View File

@@ -104,6 +104,14 @@ function kl_str_sql()
return $res;
}
function pre_dump($var, $die = false)
{
print "<pre>";
var_dump($var);
print "</pre>";
if ($die) die();
}
/*
* Local variables:

38
lib/find_hash.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/bash
# set -x
ROOT=/var/www/singularity
SOURCE="$ROOT/lib/source"
DB="$ROOT/lib/singularity_revisions.db"
TMP_LIST="/tmp/find_hash_tmp.lst"
BUILD_LIST="/tmp/find_hash_tmp.bulds"
function update_source() {
cd $SOURCE
# git fetch --all
git reset --soft FETCH_HEAD
}
# main
chan="SingularityAlpha"
update_source
git rev-list HEAD > "$TMP_LIST"
sqlite $DB "select nr from builds where (hash = '' or hash is null) and chan='$chan' order by nr desc" > "$BUILD_LIST"
cat "$BUILD_LIST" | while read build; do
git reset --soft FETCH_HEAD
cat "$TMP_LIST" | while read rev; do
git reset --soft $rev
nr=$(git rev-list HEAD | wc -l)
if [ "x$nr" == "x$build" ]; then
echo "$build = $rev"
sqlite $DB "update builds set hash='$rev' where nr='$build' and chan='$chan'"
break
fi
done
done

View File

@@ -9,84 +9,206 @@ if (PHP_SAPI != "cli") {
// create table revs(id integer, hash varchar, author varchar, time timestamp, message text, diff text, primary key(id));
// create index hash_index on revs(hash);
define("SITE_ROOT", dirname(__file__) . "/..");
define("SITE_ROOT", realpath(dirname(__file__) . "/.."));
require_once SITE_ROOT . "/lib/init.php";
function import_rev($id, $hash)
function import_rev($raw, $chan)
{
global $DB;
print "Importing revision number $id with hash $hash\n";
$log = explode("\n", rtrim(`git log -n1 $hash`));
$log = explode("\n", rtrim($raw));
$hash = $log[0];
$author = "";
if (preg_match("|Author:\\s*(.*)|i", $log[1], $m)) {
$author = $m[1];
}
$date = "";
if (preg_match("|Date:\\s*(.*)|i", $log[2], $m)) {
$date = strtotime($m[1]);
}
$msg = "";
$inMsg = false;
$nrLog = count($log);
for ($i=4; $i<$nrLog; $i++) {
$msg .= substr($log[$i], 4);
if ($i<$nrLog-1) {
$msg .= "\n";
for ($i=0; $i<$nrLog; $i++) {
if ($inMsg) {
$msg .= substr($log[$i], 4);
if ($i<$nrLog-1) {
$msg .= "\n";
}
} else {
if (preg_match("|^author\\s*([^>]*>)\\s*([\\d]+)\\s*(.*)|i", $log[$i], $m)) {
$author = $m[1];
$date = (int)$m[2];
} else if (!trim($log[$i])) {
$inMsg = true;
}
}
}
$DB->query(
kl_str_sql(
"Insert into revs (id, hash, author, time, message) values (!i, !s, !s, !t, !s)",
$id, $hash, $author, $date, $msg));
"insert into revs (hash, chan, author, time, message) values (!s, !s, !s, !t, !s)",
$hash, $chan, $author, $date, $msg));
}
function update_source()
function save_build_changes($changes, $chan)
{
exec("git reset --hard", $out, $res);
if ($res) {
DBH::log("Command failed: ", implode("\n", $out));
return;
}
global $DB;
exec("git pull", $out, $res);
if ($res) {
DBH::log("Command failed: ", implode("\n", $out));
return;
}
print implode("\n", $out) . "\n";
$DB->query("begin transaction");
foreach ($changes as $buildNr => $revs) {
$DB->query(kl_str_sql("insert into changes (build, chan, revisions) values (!i, !s, !s)", $buildNr, $chan, implode(",", $revs)));
}
$DB->query("commit");
}
function update_revs()
{
global $DB, $CHANS;
$DB->query("begin transaction");
if (!($res = $DB->query("delete from revs"))) {
$DB->query("create table revs(hash varchar, chan varchar, author varchar, time timestamp, message text, diff text, primary key(hash))");
}
$DB->query("commit");
$DB->query("begin transaction");
if (!($res = $DB->query("delete from changes"))) {
$DB->query("create table changes (build integer, chan varchar, revisions text, primary key(build, chan))");
}
$DB->query("commit");
foreach ($CHANS as $chan => $branch) {
exec("git fetch --all 2>&1");
if ($branch == "HEAD") {
$branch = "FETCH_HEAD";
}
exec("git reset --soft $branch 2>&1");
$DB->query("begin transaction");
$revs = array_reverse(explode(chr(0), rtrim(`git rev-list HEAD --header`)));
$nrRevs = count($revs);
print "Importing $nrRevs revisions for $chan\n";
for ($i=0; $i<$nrRevs; $i++) {
import_rev($revs[$i], $chan);
}
$res = $DB->query("commit");
$revs = explode("\n", rtrim(`git rev-list HEAD`));
$res = 0;
$c =0;
$changesAt = array();
while (true) {
exec("git reset --soft HEAD^ 2>&1", $out, $res);
if ($res != 0) {
break;
} else {
$c++;
$newRevs = explode("\n", rtrim(`git rev-list HEAD`));
$changes = array_diff($revs, $newRevs);
$nrChanges = count($changes);
$build = count($revs);
$revs = $newRevs;
$changesAt[$build] = $changes;
print $nrChanges . " changes in build $build\n";
if ($build < 2883) break; // this is when we started building
}
}
save_build_changes($changesAt, $chan);
}
print "Number resets: $c\n";
exec("git fetch --all 2>&1");
exec("git reset --soft $branch 2>&1");
}
function update_builds()
{
global $DB;
$builds = glob(SITE_ROOT . "/*_*_Setup.exe");
$latest = 0;
// check if table exists
if (!($res = $DB->query("select count(*) as c from builds"))) {
$DB->query("create table builds(nr integer, chan varchar, version varchar, hash varchar, file varchar, modified timestamp, primary key(nr, chan))");
}
for ($i=0; $i<count($builds); $i++) {
$file = basename($builds[$i]);
if (preg_match("|^(\w+)_(\d+)-(\d+)-(\d+)-(\d+)_|", $file, $m)) {
$chan = $m[1];
$major = $m[2];
$minor = $m[3];
$maintenance = $m[4];
$build = $m[5];
$modified = filemtime(SITE_ROOT . "/" . $file);
$version = "$major.$minor.$maintenance.$build";
$res = $DB->query(kl_str_sql("select count(*) as c from builds where nr=!i and chan=!s", $build, $chan));
$row = $DB->fetchRow($res);
if ($row["c"] === "0") {
$DB->query(kl_str_sql("insert into builds (nr, chan, version, file, modified) ".
"values (!i, !s, !s, !s, !t)",
$build, $chan, $version, $file, $modified));
}
}
}
}
function add_build($build, $chan, $version, $hash)
{
global $DB;
// check if table exists
if (!($res = $DB->query("select count(*) as c from builds"))) {
$DB->query("create table builds(nr integer, chan varchar, version varchar, hash varchar, file varchar, modified timestamp, primary key(nr, chan))");
}
$res = $DB->query(kl_str_sql("select count(*) as c from builds where nr=!i and chan=!s", $build, $chan));
$row = $DB->fetchRow($res);
if ($row["c"] === "0") {
$DB->query(kl_str_sql("insert into builds (nr, chan, version, hash, modified) ".
"values (!i, !s, !s, !s, !t)",
$build, $chan, $version, $hash, time()));
}
}
/* main */
if ($_SERVER['argc'] < 4) {
print "Too few arguments.\nUsage: import_revs.php <channel> <version> <hash>\n";
exit(1);
}
$CHAN = $_SERVER['argv'][1];
$VERSION = $_SERVER['argv'][2];
$HASH = $_SERVER['argv'][3];
$build_parts = explode(".", $VERSION);
if (count($build_parts) != 4) {
print "Wrong version format, expected x.y.z.build\n";
die();
}
$BUILD = $build_parts[3];
print "$CHAN $VERSION $HASH $BUILD\n";
add_build($BUILD, $CHAN, $VERSION, $HASH);
$DB->query("PRAGMA synchronous = OFF");
chdir(SITE_ROOT . "/lib/source");
exec("git fetch --all");
update_revs();
# update_source();
$revsStr = rtrim(`git rev-list HEAD | tac`);
$revs = explode("\n", $revsStr);
$nrRevs = count($revs);
$latest = 0;
$res = $DB->query("select max(id) as id from revs");
if ($row = $DB->fetchRow($res)) {
if ($DB->loadFromDbRow($dbLatest, $res, $row)) {
$latest = (int)$dbLatest->id;
}
}
print "Found $nrRevs revisions\n";
print "Latest revision in the database: $latest\n";
if ($latest < $nrRevs) {
for ($rev = $latest + 1; $rev <= $nrRevs; $rev++) {
import_rev($rev, $revs[$rev - 1]);
}
}
chdir(SITE_ROOT);
update_builds();
/*
* Local variables:

View File

@@ -4,6 +4,7 @@ if (!defined('SITE_ROOT')) {
}
error_reporting(E_ALL ^ E_NOTICE);
ini_set("display_errors", "true");
if (!extension_loaded('kl')) {
require_once SITE_ROOT.'/lib/ext_kl.php';
@@ -19,11 +20,12 @@ function __autoload($class)
* Example: http://www.example.com/applications/app1/
*/
define('REL_DIR', '');
define('REL_DIR', 'singularity');
if (!defined('URL_ROOT')) {
$init_port = "";
$init_ssl = strlen($_SERVER["HTTPS"]) > 0 ? true:false;
define('USE_SSL', $init_ssl);
$init_url = $init_ssl ? "https://" : "http://";
@@ -53,9 +55,12 @@ if (!defined('IMG_ROOT')) {
}
// $CHANS = array("SingularityAlpha" => "HEAD", "SingularityMultiWearable" => "refs/remotes/shyotl/V2MultiWear");
$CHANS = array("SingularityAlpha" => "HEAD");
$DB = new DBH();
$DB_NAME = SITE_ROOT . '/lib/signularity_revisions.db';
$DB_NAME = SITE_ROOT . '/lib/singularity_revisions.db';
/* $DB_USER = 'gigaprims';
$DB_PASS = 'secrit';
$DB_HOST = 'localhost';