mirror of
https://github.com/silverstripe/silverstripe-installer
synced 2024-10-22 17:05:33 +02:00
MINOR Merged back /tools
This commit is contained in:
parent
54e8eefef2
commit
0bab1907d1
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
tools/phing-metadata
|
311
build.xml
Normal file
311
build.xml
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!-- Installation instructions:
|
||||||
|
|
||||||
|
sudo pear install phing
|
||||||
|
sudo pear install VersionControl_Git-0.4.4
|
||||||
|
phing help
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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}" />
|
||||||
|
<taskdef name="gitstash" classname="tools.GitStashTask" 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="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:
|
||||||
|
|
||||||
|
* archive - Creates a tar.gz or zip file from the current source, removing version control specific files
|
||||||
|
* checkout - Switches all working copies to the specified tag or branch
|
||||||
|
* tag - Creates a new git tag in all the nested working copies (optionally pushes the created tag)
|
||||||
|
* pushtags - Pushes all local tags to their respective origin repositories
|
||||||
|
* update_modules - Checks out repositories defined in the 'dependent-modules' file into the current directory
|
||||||
|
* add_module - Checks out a module at a specific repository URL
|
||||||
|
* changelog - Create a changelog.md file from the repositories specified in the 'changelog-definitions' file
|
||||||
|
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
-Dbasedir = . (the base directory to operate on)
|
||||||
|
-Ddependent-modules-file = dependent-modules (the file of dependent modules to use when updating modules)
|
||||||
|
-Dchangelog-definitions-file = changelog-definitions (the file of changelog-definitions to use when generating the changelog)
|
||||||
|
-DchangelogSort = type (sort the changelog file by commit type)
|
||||||
|
-Dni_build = false (non-interactive build, overwrite local changes without prompting)
|
||||||
|
|
||||||
|
-Dmodurl (the URL of a single module to load when using add_modules)
|
||||||
|
-Dmodule (the name of the module directory to create the module in when using add_modules)
|
||||||
|
|
||||||
|
-Darchivetype (the type of archive to use zip or tar.giz)
|
||||||
|
-Darchivename (the name of the created archive file)
|
||||||
|
|
||||||
|
-Dtagname (the name of the tag/branch to check out or to create as a new tag)
|
||||||
|
-DincludeBaseDir (whether or not to include the base dir in a git checkout operation)
|
||||||
|
</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">
|
||||||
|
<echo msg="checking out ${reponame}"/>
|
||||||
|
<gitstash repository="${reponame}" gitPath="${gitPath1}" />
|
||||||
|
|
||||||
|
<gitcheckout
|
||||||
|
repository="${reponame}"
|
||||||
|
branchname="${tagname}"
|
||||||
|
gitPath="${gitPath1}" />
|
||||||
|
|
||||||
|
<gitstash repository="${reponame}" gitPath="${gitPath1}" pop="true" />
|
||||||
|
|
||||||
|
<echo msg="checked out ${tagname} tag/branch in '${reponame}' git repository" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- Archives the base folder as a *.tar.gz or *.zip, without version information -->
|
||||||
|
<target name="archiveTask" if="archivetype,basedir,archivename,archivefilename">
|
||||||
|
<!-- Copy into a new folder, and tar the whole folder in order to avoid toplevel extracts -->
|
||||||
|
<php function="sys_get_temp_dir" returnProperty="systmp" />
|
||||||
|
<property name="tmp" value="${systmp}/archiveTask/" />
|
||||||
|
|
||||||
|
<copy todir="${tmp}/${archivename}">
|
||||||
|
<fileset dir="${basedir}">
|
||||||
|
<include name="**/**" />
|
||||||
|
<exclude name="assets/**" />
|
||||||
|
<exclude name="mysite/local.conf.php" />
|
||||||
|
<exclude name="mysite/db.conf.php" />
|
||||||
|
<exclude name="**/*.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 -->
|
||||||
|
<exclude name="tools/**" />
|
||||||
|
<exclude name="build.xml" />
|
||||||
|
<exclude name="dependent-modules*" />
|
||||||
|
<exclude name="changelog-definitions*" />
|
||||||
|
<exclude name="_ss_environment.php" />
|
||||||
|
</fileset>
|
||||||
|
<fileset dir="${basedir}">
|
||||||
|
<include name="assets/Uploads" />
|
||||||
|
<include name="assets/.htaccess" />
|
||||||
|
<include name="assets/web.config" />
|
||||||
|
</fileset>
|
||||||
|
</copy>
|
||||||
|
|
||||||
|
<!-- create tar archive -->
|
||||||
|
<if>
|
||||||
|
<equals arg1="${archivetype}" arg2="tar.gz" casesensitive="false" trim="true"/>
|
||||||
|
<then>
|
||||||
|
<tar destfile="${basedir}/${archivefilename}" compression="gzip">
|
||||||
|
<fileset dir="${tmp}">
|
||||||
|
<include name="**/**" />
|
||||||
|
</fileset>
|
||||||
|
</tar>
|
||||||
|
</then>
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<!-- create zip archive -->
|
||||||
|
<if>
|
||||||
|
<equals arg1="${archivetype}" arg2="zip" casesensitive="false" trim="true"/>
|
||||||
|
<then>
|
||||||
|
<zip destfile="${basedir}/${archivefilename}">
|
||||||
|
<fileset dir="${tmp}">
|
||||||
|
<include name="**/**" />
|
||||||
|
</fileset>
|
||||||
|
</zip>
|
||||||
|
</then>
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<delete dir="${tmp}" />
|
||||||
|
|
||||||
|
</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="pushtags" />
|
||||||
|
</then>
|
||||||
|
</if>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- Pushes all local tags to origin -->
|
||||||
|
<target name="pushtags" 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>
|
||||||
|
|
||||||
|
<if>
|
||||||
|
<isset property="includeBaseDir"/>
|
||||||
|
<then>
|
||||||
|
<echo msg="Including BaseDir in checkout: ${includeBaseDir}"/>
|
||||||
|
</then>
|
||||||
|
<else>
|
||||||
|
<input propertyName="includeBaseDir" validArgs="yes,no" promptChar=":">Include the base dir '${basedir}' in checkout?</input>
|
||||||
|
<echo msg="Including BaseDir in checkout: ${includeBaseDir}"/>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if>
|
||||||
|
<isfalse value="${includeBaseDir}"/>
|
||||||
|
<then><!-- get a list of git repos without the base dir -->
|
||||||
|
<findRepos TargetDir="${basedir}" includeTarget="${includeBaseDir}"/>
|
||||||
|
</then>
|
||||||
|
</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" promptChar=":">Please enter a name for the archive (without extension)</input>
|
||||||
|
<property name="archivefilename" value="${archivename}.${archivetype}" />
|
||||||
|
<echo msg="Creating '${archivename}' archive"/>
|
||||||
|
</else>
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<phingCall target="archiveTask" />
|
||||||
|
<echo msg="Created archive in: ${basedir}/${archivename}" />
|
||||||
|
|
||||||
|
</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,changelogSort">
|
||||||
|
<sschanglog definitions="${changelog-definitions-file}" baseDir="${basedir}" sort="${changelogSort}"/>
|
||||||
|
<echo msg="${changelogOutput}" />
|
||||||
|
</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
|
38
dependent-modules.default
Normal file
38
dependent-modules.default
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# Using the "piston" flag will strip versioning information, but keep metadata in the working copy
|
||||||
|
# to update from version control later on, and merge in potential local changes.
|
||||||
|
# See http://piston.rubyforge.org for details.
|
||||||
|
#
|
||||||
|
# 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/
|
||||||
|
|
||||||
|
|
||||||
|
|
300
tools/CreateChangelog.php
Normal file
300
tools/CreateChangelog.php
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order of the array keys determines order of the lists.
|
||||||
|
*/
|
||||||
|
public $types = array(
|
||||||
|
'API Changes' => array('/^API CHANGE:?/i','/^APICHANGE?:?/i'),
|
||||||
|
'Features and Enhancements' => array('/^(ENHANCEMENT|ENHNACEMENT):?/i', '/^FEATURE:?/i'),
|
||||||
|
'Bugfixes' => array('/^(BUGFIX|BUGFUX):?/i','/^BUG FIX:?/i'),
|
||||||
|
'Minor changes' => array('/^MINOR:?/i'),
|
||||||
|
'Other' => array('/^[^A-Z][^A-Z][^A-Z]/') // dirty trick: check for uppercase characters
|
||||||
|
);
|
||||||
|
|
||||||
|
public $commitUrls = array(
|
||||||
|
'.' => 'https://github.com/silverstripe/silverstripe-installer/commit/%s',
|
||||||
|
'sapphire' => 'https://github.com/silverstripe/sapphire/commit/%s',
|
||||||
|
'cms' => 'https://github.com/silverstripe/silverstripe-cms/commit/%s',
|
||||||
|
'themes/blackcandy' => 'https://github.com/silverstripe-themes/silverstripe-blackcandy/commit/%s',
|
||||||
|
);
|
||||||
|
|
||||||
|
public $ignoreRules = array(
|
||||||
|
'/^Merge/',
|
||||||
|
'/^Blocked revisions/',
|
||||||
|
'/^Initialized merge tracking /',
|
||||||
|
'/^Created (branches|tags)/',
|
||||||
|
'/^NOTFORMERGE/',
|
||||||
|
'/^\s*$/'
|
||||||
|
);
|
||||||
|
|
||||||
|
public function setDefinitions($definitions) {
|
||||||
|
$this->definitions = $definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBaseDir($base) {
|
||||||
|
$this->baseDir = realpath($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_path, $filter) {
|
||||||
|
$dir = $dir_path;
|
||||||
|
|
||||||
|
if (file_exists($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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Folder '$dir' is not a $filter repository\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "Folder '$dir' does not exist\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = "";
|
||||||
|
|
||||||
|
chdir("$this->baseDir/$path"); //switch to the module's path
|
||||||
|
|
||||||
|
// Internal serialization format, ideally this would be JSON but we can't escape characters in git logs.
|
||||||
|
$log = $this->exec("git log --pretty=tformat:\"message:%s|||author:%aN|||abbrevhash:%h|||hash:%H|||date:%ad|||timestamp:%at\" --date=short {$range}", true);
|
||||||
|
|
||||||
|
chdir($this->baseDir); //switch the working directory back
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
function sortByType($commits) {
|
||||||
|
$groupedByType = array();
|
||||||
|
|
||||||
|
// sort by timestamp
|
||||||
|
usort($commits, function($a, $b) {
|
||||||
|
if($a['timestamp'] == $b['timestamp']) return 0;
|
||||||
|
else return ($a['timestamp'] > $b['timestamp']) ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach($commits as $k => $commit) {
|
||||||
|
// TODO
|
||||||
|
// skip ignored revisions
|
||||||
|
// if(in_array($commit['changeset'], $this->ignorerevisions)) continue;
|
||||||
|
|
||||||
|
// Remove email addresses
|
||||||
|
$commit['message'] = preg_replace('/(<?[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}>?)/mi', '', $commit['message']);
|
||||||
|
|
||||||
|
// Condense git-style "From:" messages (remove preceding newline)
|
||||||
|
if(preg_match('/^From\:/mi', $commit['message'])) {
|
||||||
|
$commit['message'] = preg_replace('/\n\n^(From\:)/mi', ' $1', $commit['message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$matched = false;
|
||||||
|
foreach($this->types as $name => $rules) {
|
||||||
|
if(!isset($groupedByType[$name])) $groupedByType[$name] = array();
|
||||||
|
foreach($rules as $rule) {
|
||||||
|
if(!$matched && preg_match($rule, $commit['message'])) {
|
||||||
|
// @todo The fallback rule on other can't be replaced, as it doesn't match a full prefix
|
||||||
|
$commit['message'] = ($name != 'Other') ? trim(preg_replace($rule, '', $commit['message'])) : $commit['message'];
|
||||||
|
$groupedByType[$name][] = $commit;
|
||||||
|
$matched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!$matched) {
|
||||||
|
if(!isset($groupedByType['Other'])) $groupedByType['Other'] = array();
|
||||||
|
$groupedByType['Other'][] = $commit;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// // remove all categories which should be ignored
|
||||||
|
// if($this->categoryIgnore) foreach($this->categoryIgnore as $categoryIgnore) {
|
||||||
|
// if(isset($groupedByType[$categoryIgnore])) unset($groupedByType[$categoryIgnore]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return $groupedByType;
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitToArray($commit) {
|
||||||
|
$arr = array();
|
||||||
|
$parts = explode('|||', $commit);
|
||||||
|
foreach($parts as $part) {
|
||||||
|
preg_match('/([^:]*)\:(.*)/', $part, $matches);
|
||||||
|
$arr[$matches[1]] = $matches[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function isupper($i) {
|
||||||
|
return (strtoupper($i) === $i);
|
||||||
|
}
|
||||||
|
static function islower($i) {
|
||||||
|
return (strtolower($i) === $i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main() {
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
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
|
||||||
|
$log = array();
|
||||||
|
foreach($gitRepos as $path => $range) {
|
||||||
|
$logForPath = array();
|
||||||
|
if (!empty($range)) {
|
||||||
|
$from = (isset($range[0])) ? $range[0] : null;
|
||||||
|
$to = (isset($range[1])) ? $range[1] : null;
|
||||||
|
$logForPath = explode("\n", $this->gitLog($path, $from, $to));
|
||||||
|
} else {
|
||||||
|
$logForPath = explode("\n", $this->gitLog($path));
|
||||||
|
}
|
||||||
|
foreach($logForPath as $commit) {
|
||||||
|
if(!$commit) continue;
|
||||||
|
$commitArr = $this->commitToArray($commit);
|
||||||
|
$commitArr['path'] = $path;
|
||||||
|
// Avoid duplicates by keying on hash
|
||||||
|
$log[$commitArr['hash']] = $commitArr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove ignored commits
|
||||||
|
foreach($log as $k => $commit) {
|
||||||
|
$ignore = false;
|
||||||
|
foreach($this->ignoreRules as $ignoreRule) {
|
||||||
|
if(preg_match($ignoreRule, $commit['message'])) {
|
||||||
|
unset($log[$k]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort the output (based on params), grouping
|
||||||
|
if ($this->sort == 'type') {
|
||||||
|
$groupedLog = $this->sortByType($log);
|
||||||
|
} else {
|
||||||
|
//leave as sorted by default order
|
||||||
|
$groupedLog = array('All' => $log);
|
||||||
|
}
|
||||||
|
|
||||||
|
//filter out commits we don't want
|
||||||
|
// if ($this->filter) {
|
||||||
|
// foreach($groupedLog as $key => $item) {
|
||||||
|
// if (preg_match($this->filter, $item)) unset($groupedLog[$key]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//convert to string
|
||||||
|
//and generate markdown (add list to beginning of each item)
|
||||||
|
$output = "\n";
|
||||||
|
foreach($groupedLog as $groupName => $commits) {
|
||||||
|
if(!$commits) continue;
|
||||||
|
|
||||||
|
$output .= "\n### $groupName\n\n";
|
||||||
|
|
||||||
|
foreach($commits as $commit) {
|
||||||
|
if(isset($this->commitUrls[$commit['path']])) {
|
||||||
|
$hash = sprintf('[%s](%s)',
|
||||||
|
$commit['abbrevhash'],
|
||||||
|
sprintf($this->commitUrls[$commit['path']], $commit['abbrevhash'])
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$hash = sprintf('[%s]', $commit['abbrevhash']);
|
||||||
|
}
|
||||||
|
$commitStr = sprintf('%s %s %s (%s)',
|
||||||
|
$commit['date'],
|
||||||
|
$hash,
|
||||||
|
// Avoid rendering HTML in markdown
|
||||||
|
str_replace(array('<', '>'), array('<', '>'), $commit['message']),
|
||||||
|
$commit['author']
|
||||||
|
);
|
||||||
|
// $commitStr = sprintf($this->exec("git log --pretty=tformat:\"%s\" --date=short {$hash}^..{$hash}", true), $this->gitLogFormat);
|
||||||
|
$output .= " * $commitStr\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->project->setProperty('changelogOutput',$output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
77
tools/FindRepositoriesTask.php
Normal file
77
tools/FindRepositoriesTask.php
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans a folder and its subfolders and returns all the git repositories contained within
|
||||||
|
* @author jseide
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class FindRepositoriesTask extends Task {
|
||||||
|
|
||||||
|
private $targetDir = null;
|
||||||
|
private $includeTarget = true;
|
||||||
|
|
||||||
|
|
||||||
|
public function setTargetDir($targetDir) {
|
||||||
|
$this->targetDir = $targetDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIncludeTarget($includeTarget) {
|
||||||
|
$this->includeTarget = $includeTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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');
|
||||||
|
|
||||||
|
$gitDirsOutput = array();
|
||||||
|
if (!$this->includeTarget) { //don't include the target dir
|
||||||
|
foreach($gitDirs as $dir) {
|
||||||
|
if ($dir != $this->targetDir && $dir != realpath($this->targetDir)) {
|
||||||
|
$gitDirsOutput[] = $dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$gitDirsOutput = $gitDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->project->setProperty('GitReposList',implode(',',$gitDirsOutput));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
43
tools/GitStashTask.php
Normal file
43
tools/GitStashTask.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Git stashes a particular folder
|
||||||
|
* @author jseide
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class GitStashTask extends SilverStripeBuildTask {
|
||||||
|
private $repository = null;
|
||||||
|
private $gitPath = null;
|
||||||
|
private $pop = false;
|
||||||
|
|
||||||
|
public function setRepository($repo) {
|
||||||
|
$this->repository = $repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGitPath($gitPath) {
|
||||||
|
$this->gitPath = $gitPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPop($pop) {
|
||||||
|
$this->pop = $pop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main() {
|
||||||
|
if (!is_dir($this->repository)) {
|
||||||
|
throw new BuildException("Invalid target directory: $this->repository");
|
||||||
|
}
|
||||||
|
|
||||||
|
$cwd = realpath(getcwd());
|
||||||
|
|
||||||
|
chdir($this->repository);
|
||||||
|
|
||||||
|
if ($this->pop == true) $result = parent::exec("$this->gitPath stash pop",true);
|
||||||
|
else $result = parent::exec("$this->gitPath stash",true);
|
||||||
|
|
||||||
|
if ($result) echo $result;
|
||||||
|
|
||||||
|
chdir($cwd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
457
tools/LoadModulesTask.php
Normal file
457
tools/LoadModulesTask.php
Normal file
@ -0,0 +1,457 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
include_once dirname(__FILE__) . '/SilverStripeBuildTask.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A phing task to load modules from a specific URL via SVN, git checkout, or
|
||||||
|
* through the "piston" binary (http://piston.rubyforge.org).
|
||||||
|
*
|
||||||
|
* 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], '/');
|
||||||
|
$url = trim($bits[1], '/');
|
||||||
|
$storeLocally = false;
|
||||||
|
$usePiston = false;
|
||||||
|
if (isset($bits[2])) {
|
||||||
|
$devBuild = $bits[2] == 'true';
|
||||||
|
$storeLocally = $bits[2] == 'local';
|
||||||
|
$usePiston = $bits[2] == 'piston';
|
||||||
|
if (isset($bits[3])) {
|
||||||
|
$storeLocally = $bits[3] == 'local';
|
||||||
|
$usePiston = $bits[3] == 'piston';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->loadModule($moduleName, $url, $devBuild, $storeLocally, $usePiston);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually load the module!
|
||||||
|
*
|
||||||
|
* @param String $moduleName
|
||||||
|
* @param String $url
|
||||||
|
* @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?
|
||||||
|
* @param boolean $usePiston Same as $storeLocally, but retain versioning metadata in piston.
|
||||||
|
*/
|
||||||
|
protected function loadModule($moduleName, $url, $devBuild = false, $storeLocally=false, $usePiston=false) {
|
||||||
|
$git = strrpos($url, '.git') == (strlen($url) - 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create loader
|
||||||
|
if($usePiston) {
|
||||||
|
$loader = new LoadModulesTask_PistonLoader($this, $moduleName, $url, $branch);
|
||||||
|
} elseif($git) {
|
||||||
|
$loader = new LoadModulesTask_GitLoader($this, $moduleName, $url, $branch);
|
||||||
|
} else {
|
||||||
|
$loader = new LoadModulesTask_SubversionLoader($this, $moduleName, $url, $branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the module out if it doesn't exist
|
||||||
|
if (!file_exists($moduleName)) {
|
||||||
|
$this->log("Check out $moduleName from $url");
|
||||||
|
|
||||||
|
// Create new working copy
|
||||||
|
$loader->checkout($storeLocally);
|
||||||
|
|
||||||
|
// Ignore locally added modules from base working copy.
|
||||||
|
// Only applies when this base contains versioning information.
|
||||||
|
// Note: This is specific to the base working copy, not the module itself.
|
||||||
|
if (!$storeLocally && !$usePiston && file_exists('.gitignore')) {
|
||||||
|
$gitIgnore = file_get_contents('.gitignore');
|
||||||
|
if (strpos($gitIgnore, $moduleName) === false) {
|
||||||
|
$this->exec("echo $$moduleName >> .gitignore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->log("Updating $moduleName $branch from $url");
|
||||||
|
|
||||||
|
// Check for modifications
|
||||||
|
// TODO Shows all files as modified when switching repository types or branches'
|
||||||
|
$overwrite = true;
|
||||||
|
$mods = $loader->getModifiedFiles();
|
||||||
|
if (strlen($mods) && !$storeLocally) {
|
||||||
|
$this->log("The following files are locally modified");
|
||||||
|
$this->log($mods);
|
||||||
|
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
|
||||||
|
// TODO Doesn't handle switch from git to svn repositories
|
||||||
|
if ($md && isset($md[$moduleName]) && isset($md[$moduleName]['url'])) {
|
||||||
|
if (
|
||||||
|
$md[$moduleName]['url'] != $url
|
||||||
|
|| $md[$moduleName]['store'] != $storeLocally
|
||||||
|
|| $md[$moduleName]['piston'] != $usePiston
|
||||||
|
) {
|
||||||
|
if ($overwrite) {
|
||||||
|
// delete the directory and reload the module
|
||||||
|
$this->log("Deleting $moduleName and reloading");
|
||||||
|
unset($md[$moduleName]);
|
||||||
|
$this->writeMetadata($md);
|
||||||
|
rrmdir($moduleName, true);
|
||||||
|
// TODO Doesn't handle changes between svn/git/piston
|
||||||
|
$loader->checkout($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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update existing versioned copy
|
||||||
|
$loader->update($overwrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write new metadata
|
||||||
|
$metadata = array(
|
||||||
|
'url' => $url,
|
||||||
|
'store' => $storeLocally,
|
||||||
|
'piston' => $usePiston,
|
||||||
|
'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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadModulesTask_Loader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SilverStripeBuildTask
|
||||||
|
*/
|
||||||
|
protected $callingTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $branch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $nonInteractive = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SilverStripeBuildTask Phing crashes when extending the loader from SilverStripeBuildTask
|
||||||
|
* @param String
|
||||||
|
* @param String
|
||||||
|
* @param String
|
||||||
|
*/
|
||||||
|
function __construct($callingTask, $name, $url, $branch = null) {
|
||||||
|
$this->callingTask = $callingTask;
|
||||||
|
$this->name = $name;
|
||||||
|
$this->url = $url;
|
||||||
|
$this->branch = $branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check out a new working copy.
|
||||||
|
* Call {@link storeLocally()} afterwards to remove versioning information
|
||||||
|
* from the working copy.
|
||||||
|
*/
|
||||||
|
function checkout($storeLocally = false) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing working copy
|
||||||
|
*/
|
||||||
|
function update($overwrite = true) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function getModifiedFiles() {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadModulesTask_GitLoader extends LoadModulesTask_Loader {
|
||||||
|
|
||||||
|
function checkout($storeLocally = false) {
|
||||||
|
$branch = $this->branch;
|
||||||
|
$currentDir = getcwd();
|
||||||
|
$this->callingTask->exec("git clone $this->url $this->name");
|
||||||
|
|
||||||
|
if ($branch != 'master') {
|
||||||
|
// check if we're also hooking onto a revision
|
||||||
|
$commitId = null;
|
||||||
|
if (strpos($this->branch, LoadModulesTask::MODULE_SEPARATOR) > 0) {
|
||||||
|
$commitId = substr($branch, strpos($branch, LoadModulesTask::MODULE_SEPARATOR) + 1);
|
||||||
|
$branch = substr($branch, 0, strpos($branch, LoadModulesTask::MODULE_SEPARATOR));
|
||||||
|
}
|
||||||
|
// need to make sure we've pulled from the correct branch also
|
||||||
|
if ($branch != 'master') {
|
||||||
|
$this->callingTask->exec("cd $this->name && git checkout -f -b $branch --track origin/$branch && cd \"$currentDir\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($commitId) {
|
||||||
|
$this->callingTask->exec("cd $this->name && git checkout $commitId && cd \"$currentDir\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($storeLocally) rrmdir("$this->name/.git");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModifiedFiles() {
|
||||||
|
$currentDir = getcwd();
|
||||||
|
$statCmd = "git diff --name-status";
|
||||||
|
return trim($this->callingTask->exec("cd $this->name && $statCmd && cd \"$currentDir\"", true));
|
||||||
|
}
|
||||||
|
|
||||||
|
function update($overwrite = true) {
|
||||||
|
$branch = $this->branch;
|
||||||
|
$currentDir = getcwd();
|
||||||
|
|
||||||
|
$commitId = null;
|
||||||
|
if (strpos($branch, LoadModulesTask::MODULE_SEPARATOR) > 0) {
|
||||||
|
$commitId = substr($branch, strpos($branch, LoadModulesTask::MODULE_SEPARATOR) + 1);
|
||||||
|
$branch = substr($branch, 0, strpos($branch, LoadModulesTask::MODULE_SEPARATOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentBranch = trim($this->callingTask->exec("cd $moduleName && git branch && cd \"$currentDir\"", true));
|
||||||
|
|
||||||
|
$overwriteOpt = $overwrite ? '-f' : '';
|
||||||
|
|
||||||
|
$this->callingTask->exec("cd $this->name && git checkout $overwriteOpt $branch && git pull origin $branch && cd \"$currentDir\"");
|
||||||
|
|
||||||
|
if ($commitId) {
|
||||||
|
$this->callingTask->exec("cd $this->name && git pull && git checkout $commitId && cd \"$currentDir\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadModulesTask_SubversionLoader extends LoadModulesTask_Loader {
|
||||||
|
|
||||||
|
function checkout($storeLocally = false) {
|
||||||
|
$revision = '';
|
||||||
|
if ($this->branch != 'master') {
|
||||||
|
$revision = " --revision $this->branch ";
|
||||||
|
}
|
||||||
|
|
||||||
|
$cmd = ($storeLocally) ? 'export' : 'co';
|
||||||
|
$this->callingTask->exec("svn $cmd $revision $this->url $this->name");
|
||||||
|
}
|
||||||
|
|
||||||
|
function update($overwrite = true) {
|
||||||
|
$branch = $this->branch;
|
||||||
|
$currentDir = getcwd();
|
||||||
|
|
||||||
|
$revision = '';
|
||||||
|
if ($branch != 'master') {
|
||||||
|
$revision = " --revision $branch ";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $this->callingTask->exec("svn up $revision $this->name");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModifiedFiles() {
|
||||||
|
$currentDir = getcwd();
|
||||||
|
$statCmd = "svn stat";
|
||||||
|
return trim($this->callingTask->exec("cd $this->module && $statCmd && cd \"$currentDir\"", true));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadModulesTask_PistonLoader extends LoadModulesTask_Loader {
|
||||||
|
|
||||||
|
function __construct($callingTask, $name, $url, $branch = null) {
|
||||||
|
parent::__construct($callingTask, $name, $url, $branch);
|
||||||
|
|
||||||
|
if(strpos($branch, ':') !== FALSE) {
|
||||||
|
throw new BuildException(sprintf('Git tags not supported by piston'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update($overwrite = true) {
|
||||||
|
$currentDir = getcwd();
|
||||||
|
$revision = ($this->branch != 'master') ? " --commit $this->branch " : '';
|
||||||
|
$overwriteOpts = ($overwrite) ? '--force' : '';
|
||||||
|
echo $this->callingTask->exec("piston update $overwriteOpts $revision $this->name");
|
||||||
|
|
||||||
|
$this->callingTask->log(sprintf('Updated "$this->name" via piston, please don\'t forget to commit any changes'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkout($storeLocally = false) {
|
||||||
|
$git = strrpos($this->url, '.git') == (strlen($this->url) - 4);
|
||||||
|
$revision = ($this->branch != 'master') ? " --commit $this->branch " : '';
|
||||||
|
$type = ($git) ? 'git' : 'subversion';
|
||||||
|
$this->callingTask->exec("piston import --repository-type $type $revision $this->url $this->name");
|
||||||
|
|
||||||
|
$this->callingTask->log(sprintf('Created "$this->name" via piston, please don\'t forget to commit any changes'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo Check base working copy if not dealing with flattened directory.
|
||||||
|
*/
|
||||||
|
function getModifiedFiles() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
89
tools/SilverStripeBuildTask.php
Normal file
89
tools/SilverStripeBuildTask.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?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 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $verbose = false;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanEnv() {
|
||||||
|
if ($this->cleanupEnv) {
|
||||||
|
$envFile = dirname(dirname(__FILE__)).'/_ss_environment.php';
|
||||||
|
if (file_exists($envFile)) {
|
||||||
|
unlink($envFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exec($cmd, $returnContent = false, $ignoreError = false) {
|
||||||
|
$ret = null;
|
||||||
|
$return = null;
|
||||||
|
|
||||||
|
if($this->verbose) $this->log($cmd);
|
||||||
|
|
||||||
|
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
100
tools/lib/new-project.php
Normal file
100
tools/lib/new-project.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
Command line binary to pull core SilverStripe modules into a new project
|
||||||
|
|
||||||
|
This is an attempt to solve a problem introduced by the move to git, namely that new developers
|
||||||
|
who checkout the silverstripe-installer repo now don't get a "read to go" set of code, and they
|
||||||
|
shouldn't need to know how to use git or what modules need pulling in to get a base SilverStripe
|
||||||
|
install up and running.
|
||||||
|
|
||||||
|
Currently it doesn't attempt to solve the more general problem of updating or installing new modules
|
||||||
|
in an existing project - that's likely to be handled by some of this code + a re-architecture of sake
|
||||||
|
so that we can let developers add code to handle particulars of the process in an environment-aware manner
|
||||||
|
*/
|
||||||
|
|
||||||
|
$base = dirname(__FILE__);
|
||||||
|
|
||||||
|
require dirname($base).'/versions.php';
|
||||||
|
require 'sources.php';
|
||||||
|
require 'tools.php';
|
||||||
|
|
||||||
|
$opts = getopt('m:t:h',array('mode:', 'template:', 'help'));
|
||||||
|
|
||||||
|
$mode = isset($opts['m']) ? $opts['m'] : (isset($opts['mode']) ? $opts['mode'] : 'piston');
|
||||||
|
$templatefile = isset($opts['t']) ? $opts['t'] : (isset($opts['template']) ? $opts['template'] : 'template.php');
|
||||||
|
|
||||||
|
include $templatefile;
|
||||||
|
|
||||||
|
if (!isset($template)) {
|
||||||
|
echo "Template could not be found.\n\n";
|
||||||
|
$templatefile = null;
|
||||||
|
}
|
||||||
|
else if ($mode) {
|
||||||
|
$errors = array();
|
||||||
|
|
||||||
|
foreach ($template as $dest => $source) {
|
||||||
|
if ($mode == 'flat') $errors = array_merge($errors, (array)$source->canExport());
|
||||||
|
elseif ($mode == 'piston') $errors = array_merge($errors, (array)$source->canPiston());
|
||||||
|
elseif ($mode == 'contribute') $errors = array_merge($errors, (array)$source->canCheckout());
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = array_unique($errors);
|
||||||
|
if ($errors) {
|
||||||
|
echo "\nRequirements were not met for mode $mode:\n ";
|
||||||
|
echo implode("\n ", $errors);
|
||||||
|
echo "\n\nEither correct the requirements or try a different mode\n\n";
|
||||||
|
$mode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($mode != 'piston' && $mode != 'flat' && $mode != 'contribute') || !$templatefile || isset($opts['h']) || isset($opts['help'])) {
|
||||||
|
echo "Usage: new-project [-m | --mode piston | flat | contribute] [-t | --template template.php] [-h | --help]\n";
|
||||||
|
echo "\n";
|
||||||
|
echo " piston is the default mode, and uses the piston tool to add the core modules\n";
|
||||||
|
echo " It allows pulling down core module updates later while maintaining your changes.\n";
|
||||||
|
echo " It does not provide any tools for contributing your changes back upstream, though a third-party tool for git is available\n";
|
||||||
|
echo " It requires the external piston tool and all it's dependancies to be installed.\n";
|
||||||
|
echo " It only works on svn and git managed repositories.\n";
|
||||||
|
echo "\n";
|
||||||
|
echo " flat copies the core module code without using any tools or version control\n";
|
||||||
|
echo " It does not provide any tools for pulling down core modules updates later, or contributing changes back upstream\n";
|
||||||
|
echo " It requires only php with the zip and curl extensions\n";
|
||||||
|
echo " It works regardless of version control system\n";
|
||||||
|
echo "\n";
|
||||||
|
echo " contribute sets up the core as separate modules, allowing you to contribute any changes back upstream\n";
|
||||||
|
echo " It allows pulling down core module updates later while maintaining your changes.\n";
|
||||||
|
echo " It allows contributing your changes back upstream\n";
|
||||||
|
echo " It requires git and all it's dependancies to be installed.\n";
|
||||||
|
echo " It only works on git managed repositories.\n";
|
||||||
|
die;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check we're not being re-called before we do anything
|
||||||
|
$alreadyexists = false;
|
||||||
|
foreach ($template as $dest => $source) {
|
||||||
|
if (file_exists($dest)) {
|
||||||
|
echo "ERROR: Module $dest already exists. This script can not be called multiple times, or upgrade existing modules.\n";
|
||||||
|
$alreadyexists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($alreadyexists) die;
|
||||||
|
|
||||||
|
if ($mode == 'piston') {
|
||||||
|
echo "Now running piston to add modules. Piston is quite noisy, and can sometimes list errors as fatal that can be ignored.\n";
|
||||||
|
echo "If errors are shown, please check result before assuming failure\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($template as $dest => $source) {
|
||||||
|
if ($mode == 'contribute') {
|
||||||
|
GIT::ignore($dest);
|
||||||
|
$source->checkout($dest);
|
||||||
|
}
|
||||||
|
else if ($mode == 'piston') $source->piston($dest);
|
||||||
|
else $source->export($dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($mode == 'piston' && GIT::isGITRepo()) {
|
||||||
|
echo "\n\nNow commit the changes with something like \"git commit -m 'Import core SilverStripe modules'\"\n";
|
||||||
|
}
|
||||||
|
|
151
tools/lib/sources.php
Normal file
151
tools/lib/sources.php
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sources define a set of instructions for exporting, pistoning or checking out code from some sort of repository
|
||||||
|
*/
|
||||||
|
|
||||||
|
class GitRepo {
|
||||||
|
function __construct($data) {
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function repoURL() {
|
||||||
|
return $this->data['repo'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function export($out) {
|
||||||
|
throw new Exception('Dont know how to do this yet');
|
||||||
|
}
|
||||||
|
|
||||||
|
function canExport() {
|
||||||
|
return array('Cant currently use flat mode with git source repositories');
|
||||||
|
}
|
||||||
|
|
||||||
|
function piston($out) {
|
||||||
|
$data = $this->data;
|
||||||
|
Piston::import($this->repoURL(), $data['branch'], $out);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canPiston() {
|
||||||
|
$errors = array();
|
||||||
|
if (!GIT::available()) $errors = "Git is not available.";
|
||||||
|
if (!Piston::available()) $errors[] = "Piston is not available.";
|
||||||
|
if (!SVN::isSVNRepo() && !GIT::isGITRepo()) $errors[] = "Piston only works on svn working copies and git repositories.";
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkout($out) {
|
||||||
|
$data = $this->data;
|
||||||
|
GIT::checkout($this->repoURL(), $data['branch'], $out);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canCheckout() {
|
||||||
|
$errors = array();
|
||||||
|
if (!GIT::available()) $errors = "Git is not available.";
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Github extends GitRepo {
|
||||||
|
protected $data;
|
||||||
|
|
||||||
|
function __construct($data) {
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function repoURL() {
|
||||||
|
$data = $this->data;
|
||||||
|
return "git://github.com/{$data['user']}/{$data['project']}.git";
|
||||||
|
}
|
||||||
|
|
||||||
|
function export($out) {
|
||||||
|
$data = $this->data;
|
||||||
|
|
||||||
|
$tmp = tempnam(sys_get_temp_dir(), 'phpinstaller-') . '.zip';
|
||||||
|
|
||||||
|
HTTP::get("https://github.com/{$data['user']}/{$data['project']}/zipball/{$data['branch']}", $tmp);
|
||||||
|
Zip::import($tmp, $out, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canExport() {
|
||||||
|
$errors = array();
|
||||||
|
if (!HTTP::available()) $errors[] = "The curl module is not available";
|
||||||
|
if (!Zip::available()) $errors[] = "The zip module is not available";
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GithubSparse extends Github {
|
||||||
|
function piston($out) {
|
||||||
|
$this->export($out);
|
||||||
|
if (Git::isGITRepo()) Git::add($out);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canPiston() {
|
||||||
|
$data = $this->data;
|
||||||
|
echo "WARNING: Sparse import of directory {$data['subdir']} from {$this->repoURL()} will be flat, not pistoned\n";
|
||||||
|
return $this->canExport();
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkout($out) {
|
||||||
|
$this->export($out);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canCheckout() {
|
||||||
|
$data = $this->data;
|
||||||
|
echo "WARNING: Sparse import of directory {$data['subdir']} from {$this->repoURL()} will be flat, not checked out\n";
|
||||||
|
return $this->canExport();
|
||||||
|
}
|
||||||
|
|
||||||
|
function export($out) {
|
||||||
|
$data = $this->data;
|
||||||
|
|
||||||
|
$tmp = tempnam(sys_get_temp_dir(), 'phpinstaller-') . '.zip';
|
||||||
|
|
||||||
|
HTTP::get("https://github.com/{$data['user']}/{$data['project']}/zipball/{$data['branch']}", $tmp);
|
||||||
|
Zip::import($tmp, $out, 1, $data['subdir']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SvnRepo {
|
||||||
|
function __construct($data) {
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function repoURL() {
|
||||||
|
$data = $this->data;
|
||||||
|
return "{$data['repo']}/{$data['branch']}" . (isset($data['subdir']) ? "/{$data['subdir']}" : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function export($out) {
|
||||||
|
SVN::export($this->repoURL(), $out);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canExport() {
|
||||||
|
$errors = array();
|
||||||
|
if (!SVN::available()) $errors[] = "Subversion is not available.";
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function piston($out) {
|
||||||
|
Piston::import($this->repoURL(), null, $out);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canPiston() {
|
||||||
|
$errors = array();
|
||||||
|
if (!SVN::available()) $errors[] = "Subversion is not available.";
|
||||||
|
if (!Piston::available()) $errors[] = "Piston is not available.";
|
||||||
|
if (!SVN::isSVNRepo() && !GIT::isGITRepo()) $errors[] = "Piston only works on svn working copies and git repositories.";
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkout($out) {
|
||||||
|
SVN::checkout($this->repoURL(), $out);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canCheckout() {
|
||||||
|
$errors = array();
|
||||||
|
if (!SVN::available()) $errors[] = "Subversion is not available.";
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
}
|
32
tools/lib/template.php
Normal file
32
tools/lib/template.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
Base template used by new-project to know what modules to install where & where to get them from
|
||||||
|
Structure is likely to change at some point
|
||||||
|
*/
|
||||||
|
|
||||||
|
$template = array(
|
||||||
|
'sapphire' => new Github(array(
|
||||||
|
'user' => 'silverstripe',
|
||||||
|
'project' => 'sapphire',
|
||||||
|
'branch' => SAPPHIRE_CURRENT_BRANCH
|
||||||
|
)),
|
||||||
|
'cms' => new Github(array(
|
||||||
|
'user' => 'silverstripe',
|
||||||
|
'project' => 'silverstripe-cms',
|
||||||
|
'branch' => SAPPHIRE_CURRENT_BRANCH
|
||||||
|
)),
|
||||||
|
'themes/blackcandy' => new GithubSparse(array(
|
||||||
|
'user' => 'silverstripe-themes',
|
||||||
|
'project' => 'silverstripe-blackcandy',
|
||||||
|
'branch' => SAPPHIRE_CURRENT_BRANCH,
|
||||||
|
'subdir' => 'blackcandy'
|
||||||
|
)),
|
||||||
|
'themes/blackcandy_blog' => new GithubSparse(array(
|
||||||
|
'user' => 'silverstripe-themes',
|
||||||
|
'project' => 'silverstripe-blackcandy',
|
||||||
|
'branch' => SAPPHIRE_CURRENT_BRANCH,
|
||||||
|
'subdir' => 'blackcandy_blog'
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
150
tools/lib/tools.php
Normal file
150
tools/lib/tools.php
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
Interfaces to various external modules or binaries
|
||||||
|
*/
|
||||||
|
|
||||||
|
class HTTP {
|
||||||
|
static function available() {
|
||||||
|
return function_exists('curl_init');
|
||||||
|
}
|
||||||
|
|
||||||
|
static function get($url, $dst = null) {
|
||||||
|
$hndl = curl_init($url); $fhndl = null;
|
||||||
|
|
||||||
|
curl_setopt($hndl, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
// Unfortunately, ssl isn't set up right by default in php for windows
|
||||||
|
if (strpos(PHP_OS, "WIN") !== false) curl_setopt($hndl, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
|
||||||
|
if ($dst) {
|
||||||
|
$fhndl = fopen($dst, 'wb');
|
||||||
|
curl_setopt($hndl, CURLOPT_FILE, $fhndl);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
curl_setopt($hndl, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = curl_exec($hndl);
|
||||||
|
if (!$res) {
|
||||||
|
throw new Exception("Downloading ".$url." failed - curl says: ".curl_error($hndl));
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_close($hndl);
|
||||||
|
if ($fhndl) fclose($fhndl);
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SVN {
|
||||||
|
static function available() {
|
||||||
|
exec('svn --version', $out, $rv);
|
||||||
|
return $rv === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function isSVNRepo() {
|
||||||
|
return is_dir('.svn');
|
||||||
|
}
|
||||||
|
|
||||||
|
static function export($repo, $out) {
|
||||||
|
`svn export $repo $out`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function checkout($repo, $out) {
|
||||||
|
`svn checkout $repo $out`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GIT {
|
||||||
|
static function available() {
|
||||||
|
exec('git --version', $out, $rv);
|
||||||
|
return $rv === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function isGITRepo() {
|
||||||
|
return is_dir('.git');
|
||||||
|
}
|
||||||
|
|
||||||
|
static function add($dir) {
|
||||||
|
`git add $dir`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function ignore($dir) {
|
||||||
|
$hndl = fopen('.gitignore', 'a');
|
||||||
|
fwrite($hfnl, $dir."\n");
|
||||||
|
fclose($hndl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function checkout($repo, $branch, $out) {
|
||||||
|
if ($branch) `git clone -b $branch $repo $out`;
|
||||||
|
else `git clone -b $branch $repo $out`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Piston {
|
||||||
|
static function available() {
|
||||||
|
exec('piston --version', $out, $rv);
|
||||||
|
return $rv === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function import($src, $branch, $dest) {
|
||||||
|
if ($branch) `piston import --commit $branch $src $dest`;
|
||||||
|
else `piston import $src $dest`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Zip {
|
||||||
|
static function available() {
|
||||||
|
return class_exists('ZipArchive');
|
||||||
|
}
|
||||||
|
|
||||||
|
static function import($src, $dest, $skipdirs = 0, $subdir = null) {
|
||||||
|
$zip = new ZipArchive;
|
||||||
|
$res = $zip->open($src);
|
||||||
|
if ($res === TRUE) {
|
||||||
|
|
||||||
|
if ($skipdirs) {
|
||||||
|
$tmpdir = tempnam(sys_get_temp_dir(), 'phpinstaller-') . '.ext';
|
||||||
|
mkdir($tmpdir, 0700);
|
||||||
|
|
||||||
|
mkdir($dest);
|
||||||
|
|
||||||
|
$zip->extractTo($tmpdir);
|
||||||
|
|
||||||
|
for($i = 0; $i < $zip->numFiles; $i++){
|
||||||
|
$name = $srcname = $zip->getNameIndex($i);
|
||||||
|
$parts = array();
|
||||||
|
|
||||||
|
while ($name && $name != '.' && $name != '/') {
|
||||||
|
array_unshift($parts, basename($name));
|
||||||
|
$name = dirname($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($subdir) {
|
||||||
|
// We only need to move the level after the level after the skipdirs level, presuming that level after the skipdirs level == $subdir
|
||||||
|
if (count($parts) != $skipdirs+2) continue;
|
||||||
|
if ($parts[$skipdirs] != $subdir) continue;
|
||||||
|
|
||||||
|
$dstname = $parts[$skipdirs+1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// We only need to move the very next level after the skipdirs level
|
||||||
|
if (count($parts) != $skipdirs+1) continue;
|
||||||
|
|
||||||
|
$dstname = $parts[$skipdirs];
|
||||||
|
}
|
||||||
|
|
||||||
|
rename($tmpdir.'/'.$srcname, $dest.'/'.$dstname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$zip->extractTo($dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
$zip->close();
|
||||||
|
} else {
|
||||||
|
throw new Exception('Could not extract zip at '.$src.' to '.$dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
3
tools/new-project
Executable file
3
tools/new-project
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
base=`dirname $0`
|
||||||
|
php $base/lib/new-project.php "$@"
|
2
tools/new-project.bat
Normal file
2
tools/new-project.bat
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@echo off
|
||||||
|
php %~dp0\lib\new-project.php %*
|
3
tools/versions.php
Normal file
3
tools/versions.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
define('SAPPHIRE_CURRENT_BRANCH', 'master');
|
Loading…
Reference in New Issue
Block a user