Reading more about automation stuff, I came across Phing which is like Apache Ant but uses php to write “tasks” classes. According the Phings website.
PHing Is Not GNU make; it’s a project build system based on Apache Ant. You can do anything with it that you could do with a traditional build system like GNU make, and its use of simple XML build files and extensible PHP “task” classes make it an easy-to-use and highly flexible build framework. Features include file transformations (e.g. token replacement, XSLT transformation, Smarty template transformations), file system operations, interactive build support, SQL execution, CVS operations, tools for creating PEAR packages, and much more.
So talking about synchronization, there is an interesting task called “FileSyncTask” for Unix systems which synchronizes files and directories from one location to another while minimizing data transfer. FileSyncTask can copy or display directory contents and copy files, optionally using compression and recursion.
FileSyncTask can be used to synchronize Website trees from staging to production servers and to backup key areas of the filesystems, which is exactly what I was after. So here’s additional information on how to go about it.
There are 4 different ways of using FileSyncTask:
- For copying local files.
- For copying from the local machine to a remote machine using a remote shell program as the transport (ssh).
- For copying from a remote machine to the local machine using a remote shell program.
- For listing files on a remote machine.
The SSH client called by FileSyncTask uses settings from the build.properties file:
[ini]
sync.source.projectdir=/home/development.com/public
sync.destination.projectdir=/home/staging.com
sync.remote.host=server.com
sync.remote.user=user
sync.destination.backupdir=/home/staging.com/backup
sync.exclude.file=/home/development.com/build/sync.exclude
[/ini]
Only listing files that have been modified
<taskdef name="sync" classname="phing.tasks.ext.FileSyncTask" />
<sync
sourcedir="${sync.source.projectdir}"
destinationdir="${sync.destination.projectdir}"
listonly="true"
verbose="true" />
Excluding files that dont need to be synchronized (config files etc)
*~ .svn public/images/uploads/* build/* log/* tmp
Copying files to a remote machine
<taskdef name="sync" classname="phing.tasks.ext.FileSyncTask" />
<sync
sourcedir="${sync.source.projectdir}"
destinationdir="${sync.remote.user}@${sync.remote.host}:${sync.destination.projectdir}"
excludefile="${sync.exclude.file}"
verbose="true" />
So here’s the directory structure of the configuration files
development.com
|-- build
| |-- build.properties
| |-- build.xml
| |-- sync.exclude
| `-- sync.properties
`-- public
`-- index.php
Finally the build file
Phing runs using xml build files for directions on what tasks to run and other configuration parameters, here’s a sample one.
<?xml version="1.0" ?>
<project name="example" basedir="." default="build">
<property name="version" value="1.0" />
<!-- Public targets -->
<target name="sync:list" description="List files">
<phingcall target="-sync-execute-task">
<property name="listonly" value="true" />
</phingcall>
</target>
<target name="sync" description="Copy files">
<phingcall target="-sync-execute-task">
<property name="listonly" value="false" />
</phingcall>
</target>
<!-- Private targets -->
<target name="-init" description="Load main settings">
<tstamp />
<property file="build.properties" />
</target>
<target name="-sync-execute-task" depends="-init">
<property file="sync.properties" />
<if>
<not>
<isset property="sync.verbose" />
</not>
<then>
<property name="sync.verbose" value="true" override="true" />
<echo message="The value of sync.verbose has been set to true" />
</then>
</if>
<property name="sync.remote.auth" value="${sync.remote.user}@${sync.remote.host}" />
<taskdef name="sync" classname="phing.tasks.ext.FileSyncTask" />
<sync
sourcedir="${sync.source.projectdir}"
destinationdir="${sync.remote.auth}:${sync.destination.projectdir}"
backupdir="${sync.remote.auth}:${sync.destination.backupdir}"
excludefile="${sync.exclude.file}"
listonly="${listonly}"
verbose="${sync.verbose}" />
</target>
</project>
Now executing the task
$ phing sync:list
Which outputs
Buildfile: /home/development.com/build/build.xml
example > sync:list:
[phingcall] Calling Buildfile '/home/development.com/build/build.xml'
with target '-sync-execute-task'
example > -init:
[property] Loading /home/development.com/build/build.properties
example > -sync-execute-task:
[property] Loading /home/development.com/build/sync.properties
[echo] The value of sync.verbose has been set to true
Execute Command
----------------------------------------
rsync -razv --list-only -b --backup-dir
Sync files to remote server
----------------------------------------
Source: /home/development.com/public
Destination: user@server.com:/home/staging.com
Backup: user@server.com:/home/staging.com/backup
Exclude patterns
----------------------------------------
*~
.svn
.htaccess
public/index.development.php
public/images/uploads/*
build/*
log/*
tmp/*
(list of files that have changed)
BUILD FINISHED
Total time: 1.9763 second
Excellent stuff, just the kind of sync script you would like per project, symfony does a great job at synchronizing local and remote code as part of the project:deploy task and that has really spoilt me to look for similar ways for other projects.
Additional Reading:
http://www.fedecarg.com/wiki/FileSyncTask
http://phing.info/docs/guide/current/
Writing your own phing task


[...] [...]