mirror of
https://github.com/silverstripe/silverstripe-installer
synced 2024-10-22 17:05:33 +02:00
API-CHANGE: new checkout and changelog tools using Phing
This commit is contained in:
parent
e4031aa344
commit
0ecaf0c45d
269
build.xml
Normal file
269
build.xml
Normal file
@ -0,0 +1,269 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Installation instructions:
|
||||
|
||||
sudo pear install phing
|
||||
sudo pear install VersionControl_Git-0.4.4
|
||||
phing tag -Dtagname=2.4.6
|
||||
|
||||
-->
|
||||
|
||||
<project name="silverstripe-installer" default="tag" phingVersion="2.4.5">
|
||||
|
||||
<!-- Load in the custom tasks -->
|
||||
<taskdef name="findRepos" classname="tools.FindRepositoriesTask" classpath="${project.basedir}" />
|
||||
<taskdef name="ssmodules" classname="tools.LoadModulesTask" classpath="${project.basedir}" />
|
||||
<taskdef name="sschanglog" classname="tools.CreateChangelog" classpath="${project.basedir}" />
|
||||
|
||||
<property name="basedir" value="." override="true" />
|
||||
<property name="dependent-modules-file" value="dependent-modules" override="true" />
|
||||
<property name="changelog-definitions-file" value="changelog-definitions" override="true" />
|
||||
<property name="ni_build" value="false" override="true"/> <!-- Prompt if local changes would be overwritten by update -->
|
||||
<property name="changelogFile" value="changelog.md" override="true"/>
|
||||
<property name="changelogSort" value="type" override="true"/>
|
||||
|
||||
<available file="${dependent-modules-file}" property="dependent-modules-file-exists" />
|
||||
<available file="${changelog-definition-file}" property="changelog-definition-file-exists" />
|
||||
|
||||
<target name="help">
|
||||
<echo>
|
||||
SilverStripe Project Build
|
||||
------------------------------------
|
||||
|
||||
This build file contains targets to assist in creating new SilverStripe builds and releases.
|
||||
|
||||
Important targets
|
||||
|
||||
* changelog - Create a changelog.md file with the changes specified in changelog.ini
|
||||
* tag - Creates a new git tag in all the nested working copies (optionally pushes the created tag)
|
||||
* update-package - Creates a package that excludes several diretories that can be used for extracting over the top of existing installs
|
||||
</echo>
|
||||
</target>
|
||||
|
||||
<target name="gitRepositories">
|
||||
<findRepos TargetDir="${basedir}" />
|
||||
</target>
|
||||
|
||||
<!-- find the git binary and set it -->
|
||||
<target name="gitBinary">
|
||||
<exec command="which git" outputProperty="gitPath1" />
|
||||
</target>
|
||||
|
||||
<!-- Tag a git repo with a specific tag (in $tagname) -->
|
||||
<target name="tagTask" if="tagname,reponame,gitPath1">
|
||||
<gittag
|
||||
repository="${reponame}"
|
||||
name="${tagname}"
|
||||
gitPath="${gitPath1}"
|
||||
force="true" /> <!-- allow overwrite of existing tags-->
|
||||
<echo msg="git tag '${tagname}' added to '${reponame}' git repository" />
|
||||
</target>
|
||||
|
||||
<!-- Push all local tags -->
|
||||
<target name="pushTask" if="reponame,gitPath1">
|
||||
<gitpush
|
||||
repository="${reponame}"
|
||||
tags="true"
|
||||
gitPath="${gitPath1}" />
|
||||
<echo msg="pushed all tags to '${reponame}' git repository" />
|
||||
</target>
|
||||
|
||||
<!-- Checkout the specified tag on all working copies -->
|
||||
<target name="checkoutTask" if="reponame,gitPath1,tagname">
|
||||
<gitcheckout
|
||||
repository="${reponame}"
|
||||
branchname="${tagname}"
|
||||
gitPath="${gitPath1}" />
|
||||
<echo msg="checked out ${tagname} tag/branch in '${reponame}' git repository" />
|
||||
</target>
|
||||
|
||||
<target name="archiveTask" if="archivetype,basedir,archivename">
|
||||
<!-- create tar archive -->
|
||||
<if>
|
||||
<equals arg1="${archivetype}" arg2="tar.gz" casesensitive="false" trim="true"/>
|
||||
<then>
|
||||
<tar destfile="${basedir}/${archivename}" compression="gzip">
|
||||
<fileset dir="${basedir}">
|
||||
<include name="**/**" />
|
||||
<exclude name="mysite/local.conf.php" />
|
||||
<exclude name="mysite/db.conf.php" />
|
||||
<exclude name="mysite/*.log" />
|
||||
<exclude name="**/.svn/**" />
|
||||
<exclude name="**/.git/**" />
|
||||
<exclude name="**/.project" /> <!-- remove eclipse configuration file -->
|
||||
<exclude name="**/.buildpath" />
|
||||
<exclude name="**/.settings" />
|
||||
<exclude name="**/.idea/**" /> <!-- remove phpstorm configuration file -->
|
||||
</fileset>
|
||||
</tar>
|
||||
</then>
|
||||
</if>
|
||||
|
||||
<!-- create zip archive -->
|
||||
<if>
|
||||
<equals arg1="${archivetype}" arg2="zip" casesensitive="false" trim="true"/>
|
||||
<then>
|
||||
<zip destfile="${basedir}/${archivename}">
|
||||
<fileset dir="${basedir}">
|
||||
<include name="**/**" />
|
||||
<exclude name="mysite/local.conf.php" />
|
||||
<exclude name="mysite/db.conf.php" />
|
||||
<exclude name="mysite/*.log" />
|
||||
<exclude name="**/.svn/**" />
|
||||
<exclude name="**/.git/**" />
|
||||
<exclude name="**/.project" />
|
||||
<exclude name="**/.buildpath" />
|
||||
<exclude name="**/.settings" />
|
||||
<exclude name="**/.idea/**" />
|
||||
</fileset>
|
||||
</zip>
|
||||
</then>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<target name="createDependentModulesFile" unless="dependent-modules-file-exists">
|
||||
<copy file="${dependent-modules-file}.default" tofile="${dependent-modules-file}" />
|
||||
</target>
|
||||
|
||||
<target name="createChangelogDefinitionsFile" unless="changelog-definitions-file-exists">
|
||||
<copy file="${changelog-definitions-file}.default" tofile="${changelog-definitions-file}" />
|
||||
</target>
|
||||
|
||||
|
||||
<!-- tags all git repositories in the current directory with a tag name -->
|
||||
<target name="tag" if="basedir" depends="gitRepositories,gitBinary">
|
||||
<if>
|
||||
<isset property="tagname"/>
|
||||
<then>
|
||||
<echo msg="Using '${tagname}' tag"/>
|
||||
</then>
|
||||
<else>
|
||||
<input propertyName="tagname" promptChar=":">Please enter the name of the tag</input>
|
||||
<echo msg="Using '${tagname}' tag"/>
|
||||
</else>
|
||||
</if>
|
||||
|
||||
<!-- find all git repos and run the tagTask on them -->
|
||||
<foreach list="${GitReposList}" param="reponame" target="tagTask" />
|
||||
|
||||
<input propertyName="pushToOrigin" defaultValue="no" validArgs="yes,no" promptChar=":">Push local tags to origin?</input>
|
||||
<if>
|
||||
<equals arg1="${pushToOrigin}" arg2="yes" casesensitive="false" trim="true"/>
|
||||
<then>
|
||||
<phingCall target="push" />
|
||||
</then>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<!-- Pushes all local tags to origin -->
|
||||
<target name="push" if="basedir" depends="gitRepositories,gitBinary">
|
||||
<foreach list="${GitReposList}" param="reponame" target="pushTask" />
|
||||
</target>
|
||||
|
||||
<!-- Switches all working copies to the specified tag or branch -->
|
||||
<target name="checkout" if="basedir" depends="gitRepositories,gitBinary">
|
||||
<if>
|
||||
<isset property="tagname"/>
|
||||
<then>
|
||||
<echo msg="Using '${tagname}' tag/branch"/>
|
||||
</then>
|
||||
<else>
|
||||
<input propertyName="tagname" defaultValue="HEAD" promptChar=":">Please enter the name of the tag or branch you wish to checkout</input>
|
||||
<echo msg="Using '${tagname}' tag/branch"/>
|
||||
</else>
|
||||
</if>
|
||||
|
||||
<!-- find all git repos and run the checkoutTask on them -->
|
||||
<foreach list="${GitReposList}" param="reponame" target="checkoutTask" />
|
||||
</target>
|
||||
|
||||
<!-- Creates a gzip archive from the current folder (removes any version control files) -->
|
||||
<target name="archive" if="basedir">
|
||||
<if>
|
||||
<isset property="archivetype"/>
|
||||
<then>
|
||||
<echo msg="Creating '${archivetype}' archive"/>
|
||||
</then>
|
||||
<else>
|
||||
<input propertyName="archivetype" defaultValue="tar.gz" validArgs="tar.gz,zip" promptChar=":">Please choose archive format</input>
|
||||
<echo msg="Creating '${archivetype}' archive"/>
|
||||
</else>
|
||||
</if>
|
||||
|
||||
<if>
|
||||
<isset property="archivename"/>
|
||||
<then>
|
||||
<echo msg="Creating '${archivename}' archive"/>
|
||||
</then>
|
||||
<else>
|
||||
<input propertyName="archivename" defaultValue="archive.${archivetype}" promptChar=":">Please enter a name for the archive</input>
|
||||
<echo msg="Creating '${archivename}' archive"/>
|
||||
</else>
|
||||
</if>
|
||||
|
||||
<!-- check if file exists -->
|
||||
<available file="${${basedir}/${archivename}}" property="afileexists" />
|
||||
<if>
|
||||
<istrue value="${afileexists}"/>
|
||||
<then>
|
||||
<echo msg="File ${basedir}/${archivename} already exists. Aborting."/>
|
||||
</then>
|
||||
<else><!-- File exists, we can continue building the archive -->
|
||||
<phingCall target="archiveTask" />
|
||||
|
||||
<echo msg="Created archive in: ${basedir}/${archivename}" />
|
||||
</else>
|
||||
</if>
|
||||
|
||||
</target>
|
||||
|
||||
<!-- Load modules where sensitive dependency exists -->
|
||||
<target name="update_modules" depends="createDependentModulesFile">
|
||||
<ssmodules file="${basedir}/${dependent-modules-file}" noninteractive="${ni_build}"/>
|
||||
</target>
|
||||
|
||||
<!-- Add a new module to the system. Run from the commandline, you can pass
|
||||
in the details of the module as phing add_module -Dmodule=blog -Dmodurl=http://path/to/svn -->
|
||||
<target name="add_module">
|
||||
<if>
|
||||
<isset property="modurl"/>
|
||||
<then>
|
||||
<echo msg="Downloading module from '${modurl}'"/>
|
||||
</then>
|
||||
<else>
|
||||
<input propertyName="modurl" promptChar=":">Please enter the module's git or svn URL</input>
|
||||
<echo msg="Downloading module from '${modurl}'"/>
|
||||
</else>
|
||||
</if>
|
||||
|
||||
<if>
|
||||
<isset property="module"/>
|
||||
<then>
|
||||
<echo msg="Creating new '${module}' module"/>
|
||||
</then>
|
||||
<else>
|
||||
<input propertyName="module" promptChar=":">Please enter the module's name (i.e. the folder to module should be created in)</input>
|
||||
<echo msg="Creating new '${module}' module"/>
|
||||
</else>
|
||||
</if>
|
||||
|
||||
<ssmodules name="${module}" url="${modurl}" />
|
||||
</target>
|
||||
|
||||
<!-- Create a changelog of git changes based on the -->
|
||||
<target name="changelog" depends="createChangelogDefinitionsFile" if="basedir,changelogFile,changelogSort">
|
||||
<sschanglog definitions="${changelog-definitions-file}" baseDir="${basedir}" sort="${changelogSort}"/>
|
||||
|
||||
<if>
|
||||
<isset property="changelogOutput"/>
|
||||
<then>
|
||||
<echo msg="${changelogOutput}" file="${changelogFile}" append="false" />
|
||||
<echo msg="Changelog written to '${changelogFile}' file" />
|
||||
</then>
|
||||
<else>
|
||||
<echo msg="There was a problem generating commits"/>
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
</project>
|
12
changelog-definitions.default
Normal file
12
changelog-definitions.default
Normal file
@ -0,0 +1,12 @@
|
||||
# Use this file to define which git repositories the "phing changlog" task will include when generating
|
||||
# a changelog.md file.
|
||||
#
|
||||
# Any paths not mentioned here will be excluded from the changelog. The script will ignore any paths that are not git
|
||||
# repositories, or are otherwise invalid.
|
||||
#
|
||||
# Leave the <from-commit> <to-commit> fields blank to include all commits.
|
||||
#
|
||||
# <path> <from-commit> <to-commit>
|
||||
. 2.4.4-rc1 2.4.5
|
||||
sapphire 2.4.4-rc1 2.4.5
|
||||
cms 2.4.4-rc1 2.4.5
|
34
dependent-modules.default
Normal file
34
dependent-modules.default
Normal file
@ -0,0 +1,34 @@
|
||||
# File format
|
||||
# -----------
|
||||
# Each line represents one dependent module.
|
||||
#
|
||||
# local_module_folder[:git_branch|:svn_revision_number] repository_url [run_dev_build=true] [local]
|
||||
#
|
||||
# Note that the local_module_folder can contain subfolders delimited via '/' characters
|
||||
# A specific git branch or SVN revision can be added in by specifying after the local
|
||||
# foldername, separated by a colon. By default, the 'master' branch of a git repository is used.
|
||||
#
|
||||
# It is recommended to have sqlite3 and cms first with [run_dev_build] set to "false".
|
||||
# Having this set to 'false' prevents the execution of the dev/build process, meaning it can be
|
||||
# deferred until all dependencies are in place, specifically the sapphire module. List
|
||||
# all additional modules after that.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# frontend-editing:development git://github.com/nyeholt/silverstripe-frontend-editing.git
|
||||
# themes/mytheme git://local.server/themes/mytheme.git false
|
||||
|
||||
|
||||
cms:master:2.4.5 git://github.com/silverstripe/silverstripe-cms.git
|
||||
sapphire:master:2.4.5 git://github.com/silverstripe/sapphire.git
|
||||
|
||||
|
||||
|
||||
# The following are the some other modules you might like to import
|
||||
|
||||
# sqlite3:master:1.1.0 git://github.com/smindel/silverstripe-sqlite3.git
|
||||
# userforms:master:0.3.0 git://github.com/silverstripe/silverstripe-userforms.git
|
||||
# securefiles http://svn.polemic.net.nz/silverstripe/modules/SecureFiles/tags/0.30/
|
||||
|
||||
|
||||
|
221
tools/CreateChangelog.php
Normal file
221
tools/CreateChangelog.php
Normal file
@ -0,0 +1,221 @@
|
||||
<?php
|
||||
include_once dirname(__FILE__) . '/SilverStripeBuildTask.php';
|
||||
|
||||
/**
|
||||
* Returns changelogs for the git (or svn) repositories specified in the changelog-definitions file
|
||||
*
|
||||
* @author jseide
|
||||
*
|
||||
*/
|
||||
class CreateChangelog extends SilverStripeBuildTask {
|
||||
|
||||
protected $definitions = null;
|
||||
protected $baseDir = null;
|
||||
protected $sort = 'type';
|
||||
protected $filter = null;
|
||||
|
||||
public function setDefinitions($definitions) {
|
||||
$this->definitions = $definitions;
|
||||
}
|
||||
|
||||
public function setBaseDir($base) {
|
||||
$this->baseDir = $base;
|
||||
}
|
||||
|
||||
public function setSort($sort) {
|
||||
$this->sort = $sort;
|
||||
}
|
||||
|
||||
public function setFilter($filter) {
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks is a folder is a version control repository
|
||||
*/
|
||||
protected function isRepository($dir, $filter) {
|
||||
$dir = realpath($dir);
|
||||
|
||||
// open this directory
|
||||
if ($handle = opendir($dir)) {
|
||||
|
||||
// get each file
|
||||
while (false !== ($file = readdir($handle))) {
|
||||
if ($file == $filter && is_dir($file)) {
|
||||
if ($filter == '.git') { //extra check for git repos
|
||||
if (file_exists($dir.'/'.$file.'/HEAD')) {
|
||||
return true; //$dir is a git repository
|
||||
}
|
||||
} else { //return true for .svn repos
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function isGitRepo($dir) {
|
||||
return $this->isRepository($dir, '.git');
|
||||
}
|
||||
|
||||
protected function isSvnRepo($dir) {
|
||||
return $this->isRepository($dir, '.svn');
|
||||
}
|
||||
|
||||
protected function gitLog($path, $from = null, $to = null) {
|
||||
//set the from -> to range, depending on which os these have been set
|
||||
if ($from && $to) $range = " $from..$to";
|
||||
elseif ($from) $range = " $from..HEAD";
|
||||
else $range = "";
|
||||
|
||||
$log = $this->exec("git log --pretty=tformat:\"%s (%aN) [%h]\"{$range} {$path}", true); //return output of command
|
||||
return $log;
|
||||
}
|
||||
|
||||
/** Sort by the first two letters of the commit string.
|
||||
* Put any commits without BUGFIX, ENHANCEMENT, etc. at the end of the list
|
||||
*/
|
||||
static function sortByType($a, $b) {
|
||||
if (strlen($a) >= 2) $a = substr($a,0,2);
|
||||
if (strlen($b) >= 2) $b = substr($b,0,2);
|
||||
|
||||
if (empty($b)) return -1; //put them at the end of the commit list
|
||||
if (is_numeric($b)) return -1;
|
||||
if (self::islower($b)) return -1;
|
||||
if (!self::isupper($b)) return -1;
|
||||
|
||||
if (empty($a)) return +1;
|
||||
if (self::islower($a)) return +1;
|
||||
if (!self::isupper($a)) return +1;
|
||||
|
||||
|
||||
if ($a == $b) {
|
||||
return 0;
|
||||
}
|
||||
return ($a > $b) ? +1 : -1;
|
||||
}
|
||||
|
||||
/** BETTER SORTING FUNCTION: Sort by the first two letters of the commit string.
|
||||
* Put any commits without BUGFIX, ENHANCEMENT, etc. at the end of the list
|
||||
*/
|
||||
static function sortByType2($array) {
|
||||
$bugfixes = array();
|
||||
$enhancements = array();
|
||||
$apichanges = array();
|
||||
$features = array();
|
||||
$minors = array();
|
||||
$others = array();
|
||||
|
||||
foreach($array as $ele) {
|
||||
if (strlen($ele) >= 2) $ele1 = substr($ele,0,2); else $ele1 = $ele;
|
||||
|
||||
if ($ele1 == "BU") $bugfixes[] = $ele;
|
||||
elseif ($ele1 == "EN") $enhancements[] = $ele;
|
||||
elseif ($ele1 == "AP") $apichanges[] = $ele;
|
||||
elseif ($ele1 == "FE") $features[] = $ele;
|
||||
elseif ($ele1 == "MI") $minors[] = $ele;
|
||||
elseif ($ele1 == "") ; //discard empty commit messages
|
||||
else $others[] = $ele;
|
||||
}
|
||||
|
||||
return array_merge(array("# API Changes"), $apichanges,
|
||||
array("","# Features & Enhancements"), $features, $enhancements,
|
||||
array("","# Bugfixes"),$bugfixes,
|
||||
array("","# Minors"), $minors,
|
||||
array("","# Others"), $others);
|
||||
}
|
||||
|
||||
static function isupper($i) {
|
||||
return (strtoupper($i) === $i);
|
||||
}
|
||||
static function islower($i) {
|
||||
return (strtolower($i) === $i);
|
||||
}
|
||||
|
||||
public function main() {
|
||||
chdir($this->baseDir); //change current working directory
|
||||
|
||||
//parse the definitions file
|
||||
$items = file($this->definitions);
|
||||
$repos = array(); //git (or svn) repos to scan
|
||||
foreach ($items as $item) {
|
||||
$item = trim($item);
|
||||
if (strpos($item, '#') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$bits = preg_split('/\s+/', $item);
|
||||
|
||||
if (count($bits) == 1) {
|
||||
$repos[$bits[0]] = "";
|
||||
} elseif (count($bits) == 2) {
|
||||
$repos[$bits[0]] = array($bits[1], null); //sapphire => array(from => HEAD)
|
||||
} elseif (count($bits) == 3) {
|
||||
$repos[$bits[0]] = array($bits[1],$bits[2]); //sapphire => array(from => to)
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//check all the paths are valid git repos
|
||||
$gitRepos = array();
|
||||
$svnRepos = array();
|
||||
foreach($repos as $path => $range) {
|
||||
if ($this->isGitRepo($path)) $gitRepos[$path] = $range; //add all git repos to a special array
|
||||
//TODO: for svn support use the isSvnRepo() method to add repos to the svnRepos array
|
||||
}
|
||||
|
||||
//run git log (with author information)
|
||||
$log = array();
|
||||
foreach($gitRepos as $path => $range) {
|
||||
if (!empty($range)) {
|
||||
$from = (isset($range[0])) ? $range[0] : null;
|
||||
$to = (isset($range[1])) ? $range[1] : null;
|
||||
$log[$path] = $this->gitLog($path, $from, $to);
|
||||
} else {
|
||||
$log[$path] = $this->gitLog($path);
|
||||
}
|
||||
}
|
||||
|
||||
//merge all the changelogs together
|
||||
$mergedLog = array();
|
||||
foreach($log as $path => $commitsString) {
|
||||
foreach(explode("\n",$commitsString) as $commit) { //array from newlines
|
||||
$mergedLog[] = $commit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//sort the output (based on params), grouping
|
||||
if ($this->sort == 'type') { //sort by type, i.e. first two letters
|
||||
$mergedLog = $this->sortByType2($mergedLog);
|
||||
} else {
|
||||
//leave as sorted by default order
|
||||
}
|
||||
|
||||
//filter out commits we don't want
|
||||
if ($this->filter) {
|
||||
foreach($mergedLog as $key => $item) {
|
||||
if (preg_match($this->filter, $item)) unset($mergedLog[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
//convert to string
|
||||
//and generate markdown (add list to beginning of each item)
|
||||
$output = "";
|
||||
foreach($mergedLog as $logMessage) {
|
||||
$firstCharacter = substr($logMessage,0,1);
|
||||
if ($firstCharacter != "#" && $firstCharacter != "") $output .= "- $logMessage\n";
|
||||
else $output .= "$logMessage\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$this->project->setProperty('changelogOutput',$output);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
62
tools/FindRepositoriesTask.php
Normal file
62
tools/FindRepositoriesTask.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Scans a folder and its subfolders and returns all the git repositories contained within
|
||||
* @author jseide
|
||||
*
|
||||
*/
|
||||
class FindRepositoriesTask extends Task {
|
||||
private $items = null;
|
||||
private $targetDir = null;
|
||||
private $copy = false;
|
||||
private $sourceDir = null;
|
||||
|
||||
|
||||
public function setTargetDir($targetDir) {
|
||||
$this->targetDir = $targetDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively lists a folder and includes only those directories that have the filter parameter as a sub-item
|
||||
*/
|
||||
protected function recursiveListDirFilter($dir, &$result, $filter = '.git') {
|
||||
$dir = realpath($dir);
|
||||
|
||||
// open this directory
|
||||
if ($handle = opendir($dir)) {
|
||||
|
||||
// get each git entry
|
||||
while (false !== ($file = readdir($handle))) {
|
||||
if ($file == "." || $file == "..") continue;
|
||||
//var_dump($file);
|
||||
if ($file == '.git' && is_dir($file)) {
|
||||
if (file_exists($dir.'/'.$file.'/HEAD')) {
|
||||
$result[] = $dir; //$dir is a git repository
|
||||
}
|
||||
} else {
|
||||
$path = $dir.'/'.$file;
|
||||
if (is_dir($path)) {
|
||||
$this->recursiveListDirFilter($path, $result, $filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// close directory
|
||||
closedir($handle);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function main() {
|
||||
if (!is_dir($this->targetDir)) {
|
||||
throw new BuildException("Invalid target directory: $this->targetDir");
|
||||
}
|
||||
|
||||
$gitDirs = array();
|
||||
$this->recursiveListDirFilter($this->targetDir, $gitDirs, '.git');
|
||||
$this->project->setProperty('GitReposList',implode(',',$gitDirs));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
312
tools/LoadModulesTask.php
Normal file
312
tools/LoadModulesTask.php
Normal file
@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
include_once dirname(__FILE__) . '/SilverStripeBuildTask.php';
|
||||
|
||||
/**
|
||||
* A phing task to load modules from a specific URL via SVN or git checkouts
|
||||
*
|
||||
* Passes commands directly to the commandline to actually perform the
|
||||
* svn checkout/updates, so you must have these on your path when this
|
||||
* runs.
|
||||
*
|
||||
* @author Marcus Nyeholt <marcus@silverstripe.com.au>
|
||||
*
|
||||
*/
|
||||
class LoadModulesTask extends SilverStripeBuildTask {
|
||||
/**
|
||||
* Character used to separate the module/revision name from the output path
|
||||
*/
|
||||
const MODULE_SEPARATOR = ':';
|
||||
|
||||
/**
|
||||
* The file that defines the dependency
|
||||
*
|
||||
* @var String
|
||||
*/
|
||||
private $file = '';
|
||||
/**
|
||||
* Optionally specify a module name
|
||||
*
|
||||
* @var String
|
||||
*/
|
||||
private $name = '';
|
||||
/**
|
||||
* And a module url
|
||||
* @var String
|
||||
*/
|
||||
private $url = '';
|
||||
|
||||
/**
|
||||
* Is this a non-interactive build session?
|
||||
* @var boolean
|
||||
*/
|
||||
private $nonInteractive = false;
|
||||
|
||||
public function setNoninteractive($v) {
|
||||
if (!strpos($v, '${') && $v == 'true' || $v == 1) {
|
||||
$this->nonInteractive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function setFile($v) {
|
||||
$this->file = $v;
|
||||
}
|
||||
|
||||
public function setName($v) {
|
||||
$this->name = $v;
|
||||
}
|
||||
|
||||
public function setUrl($v) {
|
||||
$this->url = $v;
|
||||
}
|
||||
|
||||
public function main() {
|
||||
$this->configureEnvFile();
|
||||
|
||||
if ($this->name) {
|
||||
$this->loadModule($this->name, $this->url);
|
||||
} else {
|
||||
// load the items from the dependencies file
|
||||
if (!file_exists($this->file)) {
|
||||
throw new BuildException("Modules file " . $this->modulesFile . " cannot be read");
|
||||
}
|
||||
|
||||
$items = file($this->file);
|
||||
foreach ($items as $item) {
|
||||
$item = trim($item);
|
||||
if (strpos($item, '#') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$bits = preg_split('/\s+/', $item);
|
||||
// skip malformed lines
|
||||
if (count($bits) < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$moduleName = trim($bits[0], '/');
|
||||
$svnUrl = trim($bits[1], '/');
|
||||
$storeLocally = false;
|
||||
|
||||
if (isset($bits[2])) {
|
||||
$devBuild = $bits[2] == 'true';
|
||||
$storeLocally = $bits[2] == 'local';
|
||||
if (isset($bits[3])) {
|
||||
$storeLocally = $bits[3] == 'local';
|
||||
}
|
||||
}
|
||||
|
||||
$this->loadModule($moduleName, $svnUrl, $devBuild, $storeLocally);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually load the module!
|
||||
*
|
||||
* @param String $moduleName
|
||||
* @param String $svnUrl
|
||||
* @param boolean $devBuild
|
||||
* Do we run a dev/build?
|
||||
* @param boolean $storeLocally
|
||||
* Should we store the module locally, for it to be included in
|
||||
* the local project's repository?
|
||||
*/
|
||||
protected function loadModule($moduleName, $svnUrl, $devBuild = false, $storeLocally=false) {
|
||||
$git = strrpos($svnUrl, '.git') == (strlen($svnUrl) - 4);
|
||||
$branch = 'master';
|
||||
$cmd = '';
|
||||
|
||||
$originalName = $moduleName;
|
||||
|
||||
if (strpos($moduleName, self::MODULE_SEPARATOR) > 0) {
|
||||
$branch = substr($moduleName, strpos($moduleName, self::MODULE_SEPARATOR) + 1);
|
||||
$moduleName = substr($moduleName, 0, strpos($moduleName, self::MODULE_SEPARATOR));
|
||||
}
|
||||
|
||||
$md = $this->loadMetadata();
|
||||
if (!isset($md['store'])) {
|
||||
// backwards compatibility
|
||||
$md['store'] = false;
|
||||
}
|
||||
|
||||
// check the module out if it doesn't exist
|
||||
$currentDir = trim(`pwd`," \n");
|
||||
if (!file_exists($moduleName)) {
|
||||
echo "Check out $moduleName from $svnUrl\n";
|
||||
// check whether it's git or svn
|
||||
if ($git) {
|
||||
$this->exec("git clone $svnUrl $moduleName");
|
||||
if ($branch != 'master') {
|
||||
// check if we're also hooking onto a revision
|
||||
$commitId = null;
|
||||
if (strpos($branch, self::MODULE_SEPARATOR) > 0) {
|
||||
$commitId = substr($branch, strpos($branch, self::MODULE_SEPARATOR) + 1);
|
||||
$branch = substr($branch, 0, strpos($branch, self::MODULE_SEPARATOR));
|
||||
}
|
||||
// need to make sure we've pulled from the correct branch also
|
||||
$currentDir = trim(`pwd`," \n");
|
||||
if ($branch != 'master') {
|
||||
$this->exec("cd $moduleName && git checkout -f -b $branch --track origin/$branch && cd \"$currentDir\"");
|
||||
}
|
||||
|
||||
if ($commitId) {
|
||||
$this->exec("cd $moduleName && git checkout $commitId && cd \"$currentDir\"");
|
||||
}
|
||||
}
|
||||
|
||||
if ($storeLocally) {
|
||||
rrmdir("$moduleName/.git");
|
||||
}
|
||||
} else {
|
||||
$revision = '';
|
||||
if ($branch != 'master') {
|
||||
$revision = " --revision $branch ";
|
||||
}
|
||||
|
||||
$cmd = 'co';
|
||||
if ($storeLocally) {
|
||||
$cmd = 'export';
|
||||
}
|
||||
|
||||
$this->exec("svn $cmd $revision $svnUrl $moduleName");
|
||||
}
|
||||
|
||||
// make sure to append it to the .gitignore file
|
||||
if (!$storeLocally && file_exists('.gitignore')) {
|
||||
$gitIgnore = file_get_contents('.gitignore');
|
||||
if (strpos($gitIgnore, $moduleName) === false) {
|
||||
$this->exec("echo $moduleName >> .gitignore");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "Updating $moduleName $branch from $svnUrl\n";
|
||||
|
||||
$overwrite = true;
|
||||
if (!$storeLocally) {
|
||||
$statCmd = $git ? "git diff --name-status" : "svn status";
|
||||
$mods = trim($this->exec("cd $moduleName && $statCmd && cd \"$currentDir\"", true));
|
||||
if (strlen($mods) && !$storeLocally) {
|
||||
$this->log("The following files are locally modified");
|
||||
echo "\n $mods\n\n";
|
||||
if (!$this->nonInteractive) {
|
||||
$overwrite = strtolower(trim($this->getInput("Overwrite local changes? [y/N]")));
|
||||
$overwrite = $overwrite == 'y';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the metadata and make sure it's not the same
|
||||
if ($md && isset($md[$moduleName]) && isset($md[$moduleName]['url'])) {
|
||||
if ($md[$moduleName]['url'] != $svnUrl || $md[$moduleName]['store'] != $storeLocally) {
|
||||
if ($overwrite) {
|
||||
// delete the directory and reload the module
|
||||
echo "Deleting $moduleName and reloading\n";
|
||||
unset($md[$moduleName]);
|
||||
$this->writeMetadata($md);
|
||||
rrmdir($moduleName, true);
|
||||
$this->loadModule($originalName, $svnUrl, $devBuild, $storeLocally);
|
||||
return;
|
||||
} else {
|
||||
throw new Exception("You have chosen not to overwrite changes, but also want to change your " .
|
||||
"SCM settings. Please resolve changes and try again");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$storeLocally) {
|
||||
if ($git) {
|
||||
$commitId = null;
|
||||
if (strpos($branch, self::MODULE_SEPARATOR) > 0) {
|
||||
$commitId = substr($branch, strpos($branch, self::MODULE_SEPARATOR) + 1);
|
||||
$branch = substr($branch, 0, strpos($branch, self::MODULE_SEPARATOR));
|
||||
}
|
||||
|
||||
$currentDir = trim(`pwd`," \n");
|
||||
|
||||
$currentBranch = trim($this->exec("cd $moduleName && git branch && cd \"$currentDir\"", true));
|
||||
|
||||
$overwriteOpt = $overwrite ? '-f' : '';
|
||||
|
||||
$this->exec("cd $moduleName && git checkout $overwriteOpt $branch && git pull origin $branch && cd \"$currentDir\"");
|
||||
|
||||
if ($commitId) {
|
||||
$this->exec("cd $moduleName && git pull && git checkout $commitId && cd \"$currentDir\"");
|
||||
}
|
||||
} else {
|
||||
$revision = '';
|
||||
if ($branch != 'master') {
|
||||
$revision = " --revision $branch ";
|
||||
}
|
||||
|
||||
echo $this->exec("svn up $revision $moduleName");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$metadata = array(
|
||||
'url' => $svnUrl,
|
||||
'store' => $storeLocally,
|
||||
'branch' => str_replace($moduleName, '', $originalName),
|
||||
);
|
||||
|
||||
$md[$moduleName] = $metadata;
|
||||
$this->writeMetadata($md);
|
||||
|
||||
|
||||
|
||||
// make sure to remove from the .gitignore file - don't need to do it EVERY
|
||||
// run, but it's better than munging code up above
|
||||
if ($storeLocally && file_exists('.gitignore')) {
|
||||
$gitIgnore = file('.gitignore');
|
||||
$newIgnore = array();
|
||||
foreach ($gitIgnore as $line) {
|
||||
$line = trim($line);
|
||||
if (!$line || $line == $moduleName || $line == "$moduleName/") {
|
||||
continue;
|
||||
}
|
||||
$newIgnore[] = $line;
|
||||
}
|
||||
|
||||
file_put_contents('.gitignore', implode("\n", $newIgnore));
|
||||
}
|
||||
|
||||
if ($devBuild) {
|
||||
$this->devBuild();
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadMetadata() {
|
||||
$metadataFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'phing-metadata';
|
||||
|
||||
$md = array();
|
||||
if (file_exists($metadataFile)) {
|
||||
$md = unserialize(file_get_contents($metadataFile));
|
||||
}
|
||||
|
||||
return $md;
|
||||
}
|
||||
|
||||
protected function writeMetadata($md) {
|
||||
$metadataFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'phing-metadata';
|
||||
file_put_contents($metadataFile, serialize($md));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!function_exists('rrmdir')) {
|
||||
function rrmdir($dir) {
|
||||
if (is_dir($dir)) {
|
||||
$objects = scandir($dir);
|
||||
foreach ($objects as $object) {
|
||||
if ($object != "." && $object != "..") {
|
||||
if (filetype($dir . "/" . $object) == "dir")
|
||||
rrmdir($dir . "/" . $object); else
|
||||
unlink($dir . "/" . $object);
|
||||
}
|
||||
}
|
||||
reset($objects);
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
}
|
80
tools/SilverStripeBuildTask.php
Normal file
80
tools/SilverStripeBuildTask.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/*
|
||||
*
|
||||
All code covered by the BSD license located at http://silverstripe.org/bsd-license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Build task that provides some commonly used functionality
|
||||
*
|
||||
* @author marcus
|
||||
*/
|
||||
abstract class SilverStripeBuildTask extends Task {
|
||||
protected $cleanupEnv = false;
|
||||
|
||||
protected function configureEnvFile() {
|
||||
// fake the _ss_environment.php file for the moment
|
||||
$ssEnv = <<<TEXT
|
||||
<?php
|
||||
// Set the \$_FILE_MAPPING for running the test cases, it's basically a fake but useful
|
||||
global \$_FILE_TO_URL_MAPPING;
|
||||
\$_FILE_TO_URL_MAPPING[dirname(__FILE__)] = 'http://localhost';
|
||||
TEXT;
|
||||
|
||||
$envFile = dirname(dirname(__FILE__)).'/_ss_environment.php';
|
||||
$this->cleanupEnv = false;
|
||||
if (!file_exists($envFile)) {
|
||||
file_put_contents($envFile, $ssEnv);
|
||||
$this->cleanupEnv = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function cleanEnv() {
|
||||
if ($this->cleanupEnv) {
|
||||
$envFile = dirname(dirname(__FILE__)).'/_ss_environment.php';
|
||||
if (file_exists($envFile)) {
|
||||
unlink($envFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function devBuild() {
|
||||
if (file_exists('sapphire/cli-script.php')) {
|
||||
$this->log("Running dev/build");
|
||||
$this->exec('php sapphire/cli-script.php dev/build');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get some input from the user
|
||||
*
|
||||
* @param string $prompt
|
||||
* @return string
|
||||
*/
|
||||
protected function getInput($prompt) {
|
||||
require_once 'phing/input/InputRequest.php';
|
||||
$request = new InputRequest($prompt);
|
||||
$request->setPromptChar(':');
|
||||
|
||||
$this->project->getInputHandler()->handleInput($request);
|
||||
$value = $request->getInput();
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function exec($cmd, $returnContent = false, $ignoreError = false) {
|
||||
$ret = null;
|
||||
$return = null;
|
||||
if ($returnContent) {
|
||||
$ret = shell_exec($cmd);
|
||||
} else {
|
||||
passthru($cmd, $return);
|
||||
}
|
||||
|
||||
if ($return != 0 && !$ignoreError) {
|
||||
throw new BuildException("Command '$cmd' failed");
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
0
tools/_manifest_exclude
Normal file
0
tools/_manifest_exclude
Normal file
Loading…
Reference in New Issue
Block a user