<?xml version="1.0"?>
<project name="myCDN" default="picktarget" basedir=".">

	<!-- report props -->
	<property name="email.to" value="email@domain.com" />
	<property name="email.from" value="email@domain.com" />
	
	<!-- init the DSTAMP, TSTAMP, and TODAY properties -->
	<tstamp>
		<format property="svn.builddate" pattern="yyMMddhhmm"/>		
	</tstamp>

	<!-- Define log file -->
	<record name="build.logfile" />
	
	<!-- pick up env.COMPUTERNAME -->
	<property environment="env"/>

	<!-- TARGETS -->
	<target name="picktarget">
		<input message="Do you want to deploy?" validargs="production,staging,development" addproperty="target" />

		<echo>Deploy target: ${target}</echo>
		
		<!-- if the file exists, target.props.available will exist, otherwise it won't! -->
		<available file="${target}.properties" property="target.props.available" />

		<!-- if target.props.available DOESNT exist, we fail with a message -->
		<fail message="The properties file, ${target}.properties, could not be found!" unless="target.props.available" />

		<!-- read in props including AWS credentials -->
		<property file="${target}.properties" />

		<taskdef name="svn" classname="org.tigris.subversion.svnant.SvnTask"> 
			<classpath> 
				<fileset dir="${jar.path}"> 
					<include name="**/svn*.jar"/> 
				</fileset> 
			</classpath> 
		</taskdef>		
		

		<!-- yui-compressor task definition -->
		<path id="yui.classpath">
			<pathelement location="${jar.path}/yuicompressor-2.4.2.jar" />
			<pathelement location="${jar.path}/yui-compressor-ant-task-0.4.jar" />
		</path>		

		<taskdef name="yui-compressor" classname="net.noha.tools.ant.yuicompressor.tasks.YuiCompressorTask">
			<classpath refid="yui.classpath" />
		</taskdef>

		
		<!-- bring antcontrib for/foreach support -->
		<taskdef resource="net/sf/antcontrib/antlib.xml">
			<classpath>
				<pathelement location="${jar.path}/ant-contrib-1.0b3.jar" />
			</classpath>
		</taskdef>

		
		<!-- redirect to desired target -->
		<antcall target="${project.defaultTarget}" />
		
	</target>	


	<target name="init" description="Create temp local directories for build">

		<!-- does nothing if dir already exists -->
		<mkdir dir="${project.build.root}" />
		
		<!-- leave deleted because svn export will create it -->
		<delete dir="${project.clean.root}" />
		
		<delete dir="${project.compile.root}" />
		<mkdir dir="${project.compile.root}" />
		
		<echo message="Temporary build directories created successfully!" />

		<!-- if we have a working copy, we'll just update to save time/bandwidth, /images is a directory that my static assets repo contains so I check for it  -->
		<available file="${project.build.root}/images" property="target.exists" />

		<!-- either update or checkout the repo -->
		<antcall target="-checkout" />
		<antcall target="-update" />

		<!-- now export to the clean dir -->
		<svn username="${svn.username}" password="${svn.password}" javahl="false">
			<export srcUrl="${svn.projecturl}" destPath="${project.clean.root}" />
		</svn>
		
	</target>


	<target name="-checkout" description="Pulls code from Subversion into the build directory" unless="target.exists">

		<!-- get the repo info -->
		<echo message="Checking out files from svn repository:" />
		<input message="Please enter svn repo username:" addproperty="svn.username" />
		<input message="Please enter svn repo password:" addproperty="svn.password" />
		<input message="Enter version to deploy (default: HEAD):" addproperty="svn.revision" defaultvalue="HEAD" />
				
		<!-- checkout and get version -->
		<svn username="${svn.username}" password="${svn.password}" javahl="false">
			<checkout url="${svn.projecturl}" destPath="${project.build.root}" revision="${svn.revision}" recurse="true" />
			<status path="${project.build.root}" revisionProperty="revision" />
		</svn>
		
		<echo>Release version is ${revision}</echo> 
		
	</target>

	
	<target name="-update" description="svn update a working copy instead" if="target.exists">

		<echo message="Updating existing working copy:" />
		<input message="Enter version to deploy (default: HEAD):" addproperty="svn.revision" defaultvalue="HEAD" />
		
		<svn javahl="false">
			<update dir="${project.build.root}" revision="${svn.revision}" recurse="true" />
			<status path="${project.build.root}" revisionProperty="revision" />
		</svn>

		<echo>Release version is ${revision}</echo> 
		
	</target>

	
	<target name="precompile" description="set up compilation environment">
		
		<mkdir dir="${project.compile.root}" />
		<mkdir dir="${project.compile.root}/js" />
		<mkdir dir="${project.compile.root}/css" />
		
	</target>


	<target name="-precombine">
		
		<!-- must first clean up or else this won't regenerate new files -->
		<delete>
			<fileset dir="${project.compile.root}/js" includes="*.js" />
			<fileset dir="${project.compile.root}/css" includes="*.css" />
		</delete>
		<delete dir="${project.clean.root}/global" />
		
	</target>
	

	<target name="fetch" description="Retrieve external resources for inclusion">
		
		<!-- get remote files -->
		<get src="http://www.google-analytics.com/ga.js" dest="${project.compile.root}/js/ga.js" verbose="true" />
		<get src="https://secure.addthis.com/js/200/addthis_widget.js" dest="${project.compile.root}/js/sharethis.js" verbose="true" />

	</target>

	
	<target name="combine" description="Combine permitted files together for deployment" depends="-precombine,fetch">
		
		<echo message="Building global javascript and style sheets..." />

		<concat destfile="${project.compile.root}/js/core.js" encoding="UTF8" eol="unix" force="no">
			<!-- explicitly order concat because ordering matters here -->
			<fileset dir="${project.clean.root}" includes="js/library/jquery/1.3.2/jquery.js" />
			<fileset dir="${project.clean.root}" includes="js/qforms/qforms-combined.js" />
			<fileset dir="${project.compile.root}" includes="js/ga.js" />
			<fileset dir="${project.compile.root}" includes="js/sharethis.js" />
		</concat>

		<concat destfile="${project.compile.root}/js/forms.js" encoding="UTF8" eol="unix" force="no">
			<!-- explicitly order concat because ordering matters here -->
			<fileset dir="${project.clean.root}" includes="js/library/jquery/1.2.3/jquery.color.js" />
			<fileset dir="${project.clean.root}" includes="js/library/jquery/1.2.6/jquery.autocomplete.js" />
			<fileset dir="${project.clean.root}" includes="js/library/jquery/1.3.1/jquery.checkboxes.min.js" />
			<fileset dir="${project.clean.root}" includes="js/library/jquery/1.3.1/jquery.selectboxes.min.js" />
			<fileset dir="${project.clean.root}" includes="js/library/jquery/1.3.1/jquery.field.min.js" />
			<fileset dir="${project.clean.root}" includes="js/library/jquery/1.3.1/jquery.colorbox.min.js" />
		</concat>



		<concat destfile="${project.compile.root}/css/pap.css" encoding="UTF8" eol="unix" force="no">
			<!-- explicitly order concat because ordering matters here -->
			<fileset dir="${project.clean.root}" includes="css/pap.css" />
			<fileset dir="${project.clean.root}" includes="css/forums.css" />
			<fileset dir="${project.clean.root}" includes="css/pap_print.css" />
		</concat>

		<concat destfile="${project.compile.root}/css/pmp.css" encoding="UTF8" eol="unix" force="no">
			<!-- explicitly order concat because ordering matters here -->
			<fileset dir="${project.clean.root}" includes="css/pmp.css" />
			<fileset dir="${project.clean.root}" includes="css/forums.css" />
			<fileset dir="${project.clean.root}" includes="css/pmp_print.css" />
		</concat>

		<concat destfile="${project.compile.root}/css/regform.css" encoding="UTF8" eol="unix" force="no">
			<!-- explicitly order concat because ordering matters here -->
			<fileset dir="${project.clean.root}" includes="css/jquery.colorbox.css" />
			<fileset dir="${project.clean.root}" includes="css/regform.css" />
		</concat>


		<!-- invoke compressor -->
		<yui-compressor warn="false" munge="true" charset="UTF-8" fromdir="${project.compile.root}" todir="${project.compile.root}">
			<include name="js/core.js" /><!-- creates version named js/core-min.js -->
			<include name="js/forms.js" />
			<include name="css/pap.css" />
			<include name="css/pmp.css" />
			<include name="css/regform.css" />
		</yui-compressor>

		 		
		<!-- push these updated files back into the clean repo in anticipation of being pushed to s3 -->
		<mkdir dir="${project.clean.root}/global" />

		<!-- special treatment of core -->		
		<copy file="${project.compile.root}/js/core-min.js" tofile="${project.clean.root}/global/core.js" />
		<copy file="${project.compile.root}/js/forms-min.js" tofile="${project.clean.root}/global/forms.js" />
		<copy file="${project.compile.root}/css/pap-min.css" tofile="${project.clean.root}/global/pap.css" />
		<copy file="${project.compile.root}/css/pmp-min.css" tofile="${project.clean.root}/global/pmp.css" />
		<copy file="${project.compile.root}/css/regform-min.css" tofile="${project.clean.root}/global/regform.css" />
		
	</target>
	

	<target name="versionize" description="Rename files based upon their checksums as a versioning system for the CDN">

		<!-- md5 hash in name means that anytime files are modified, the rolled up version name will change.
		     since you'll need to change this in your HTML, the client will be forced to download the updated version 
		     which eliminates having to worry about long lived Expires headers -->
		<checksum file="${project.clean.root}/global/core.js" property="chksum.core" />
		<move file="${project.clean.root}/global/core.js" tofile="${project.clean.root}/global/core_${chksum.core}.js" />

		<checksum file="${project.clean.root}/global/forms.js" property="chksum.forms" />
		<move file="${project.clean.root}/global/forms.js" tofile="${project.clean.root}/global/forms_${chksum.forms}.js" />

		<checksum file="${project.clean.root}/global/pap.css" property="chksum.pap" />
		<move file="${project.clean.root}/global/pap.css" tofile="${project.clean.root}/global/pap_${chksum.pap}.css" />

		<checksum file="${project.clean.root}/global/pmp.css" property="chksum.pmp" />
		<move file="${project.clean.root}/global/pmp.css" tofile="${project.clean.root}/global/pmp_${chksum.pmp}.css" />

		<checksum file="${project.clean.root}/global/regform.css" property="chksum.regform" />
		<move file="${project.clean.root}/global/regform.css" tofile="${project.clean.root}/global/regform_${chksum.regform}.css" />


		<echo>Most recent globals, compare with previous:</echo>
		<for param="file">
			<path>
				<fileset dir="${project.clean.root}/global/" />
			</path>
			<sequential>
				<echo message="@{file}" />
			</sequential>
		</for>
		
	</target>


	<target name="localdeploy" description="Finalize build and stop before deploying to CDN">
		
		<echo message="Exporting file transfer to ${project.deploy.root}..." />

		<!-- clean up permissions and ownership -->
		<chmod dir="${project.clean.root}" type="file" perm="0644" />
		<chmod dir="${project.clean.root}" type="dir" perm="0755" />
		
		<!-- copy to local destination dir -->
		<mkdir dir="${project.deploy.root}" />
		<sync todir="${project.deploy.root}" includeEmptyDirs="true" overwrite="true">
			<fileset dir="${project.clean.root}" />
		</sync>

	</target>
			
	
	<target name="push" description="take a finished build and move it to Amazon S3">
		
		<!-- hardcoded for now, until we see if changing the headers will force a re-upload -->
		<property name="http.expires" value="Fri, 31 Dec 2010 12:00:00 GMT" /><!-- technically should not be > 1 year: http://code.google.com/speed/page-speed/docs/caching.html -->

		<!-- push all files with long expires/cache headers -->
		<exec executable="${exec.python}" failonerror="true">
			<arg value="${exec.s3cmd}" />
			<arg value="--guess-mime-type" />
			<arg value="--add-header=Cache-Control:public, max-age=630657344" /><!-- equivalent to 7229 days or so -->
			<arg value="--add-header=Expires:${http.expires}" />
			<arg value="--encoding=UTF-8" />
			<arg value="--skip-existing" /><!-- we have to rename to version, so reuploading existing doesn't make sense -->
			<arg value="--recursive" />
			<arg value="--acl-public" /><!-- public read -->
			<arg value="sync" />
			<arg value="${project.clean.root}/" />
			<arg value="s3://${aws.bucket.uncompressed}/" />
		</exec>		

	</target>
	

	<target name="-compress" description="gzip text files for additional speed">

		<delete dir="${project.compressed.root}" />
		<mkdir dir="${project.compressed.root}" />

		<!-- make copy so we don't double-compress files -->
		<sync todir="${project.compressed.root}" includeEmptyDirs="true" overwrite="true">
			<fileset dir="${project.clean.root}" />
		</sync>
		
		<for param="file">
			<path>
				<fileset dir="${project.compressed.root}" includes="**/*.js" />
				<fileset dir="${project.compressed.root}" includes="**/*.css" />
				<fileset dir="${project.compressed.root}" includes="**/*.xml" /><!-- fckeditor files -->
				<fileset dir="${project.compressed.root}" includes="**/*.html" /><!-- fckeditor files -->
			</path>
			<sequential>
				<!-- we keep the filenames the same and transparently serve gzipped content via http headers -->
				<gzip src="@{file}" destfile="@{file}.gz" />
				<move file="@{file}.gz" tofile="@{file}" overwrite="true" />
			</sequential>
		</for>

	</target>
	

	<target name="push-compressed" description="take a finished build and move it to Amazon S3" depends="-compress">
		
		<!-- hardcoded for now, until we see if changing the headers will force a re-upload -->
		<property name="http.expires" value="Fri, 31 Dec 2010 12:00:00 GMT" />

		<!-- push all non-compressed files first, then just compressed second -->
		<exec executable="${exec.python}" failonerror="true">
			<arg value="${exec.s3cmd}" />
			<arg value="--skip-existing" /><!-- we have to rename to version, so reuploading existing doesn't make sense -->
			<arg value="--guess-mime-type" />
			<arg value="--exclude=*.js" /><!-- these are the four types we compress, so exclude -->
			<arg value="--exclude=*.css" />
			<arg value="--exclude=*.xml" />
			<arg value="--exclude=*.html" />
			<arg value="--add-header=Cache-Control:public, max-age=630657344" /><!-- equivalent to 7229 days or so -->
			<arg value="--add-header=Expires:${http.expires}" />
			<arg value="--encoding=UTF-8" />
			<arg value="--recursive" />
			<arg value="--acl-public" /><!-- public read -->
			<arg value="sync" />
			<arg value="${project.compressed.root}/" />
			<arg value="s3://${aws.bucket.compressed}/" />
		</exec>

		
		<exec executable="${exec.python}" failonerror="true">
			<arg value="${exec.s3cmd}" />
			<arg value="--skip-existing" /><!-- we have to rename to version, so reuploading existing doesn't make sense -->
			<arg value="--guess-mime-type" />
			<arg value="--exclude=*" /><!-- now ONLY upload those files which are compressed, with gzip header -->
			<arg value="--include=*.js" />
			<arg value="--include=*.css" />
			<arg value="--include=*.xml" />
			<arg value="--include=*.html" />
			<arg value="--add-header=Cache-Control:public, max-age=630657344" /><!-- equivalent to 7229 days or so -->
			<arg value="--add-header=Expires:${http.expires}" />
			<arg value="--add-header=Vary:Accept-Encoding" /><!-- tell proxies to only serve this compressed content to a client who has the accept-encoding header: http://blog.port80software.com/2005/01/21/ -->
			<arg value="--add-header=Content-Encoding:gzip" />
			<arg value="--encoding=UTF-8" />
			<arg value="--recursive" />
			<arg value="--acl-public" /><!-- public read -->
			<arg value="sync" />
			<arg value="${project.compressed.root}/" />
			<arg value="s3://${aws.bucket.compressed}/" />
		</exec>		
		
	</target>	
	
	
	<!-- optional email -->
	<target name="sendMail" description="Send email notification">	
		<!-- clean up logfile to not have bare linefeeds (prohibited by qmail!) -->
		<fixcrlf srcdir="." includes="**/*.logfile" eol="crlf" eof="remove" />
		
		<!-- send mail -->
		<mail mailhost="yourmailserver.com" mailport="25" 
				subject="'${target}' build at revision ${revision} successful" 
				messagefile="build.logfile"
				encoding="plain">
		  <from address="${email.to}"/>
		  <to address="${email.from}"/>
		</mail>
	    <echo message="Mail sent!"/>
	</target>		


	<!-- roll up target, depends order is descending: 1 depends on 2 depends on 3 depends on 4... -->
	<target name="deploy" depends="init,precompile,combine,versionize,localdeploy,push,push-compressed,sendMail"></target>

	<target name="predeploy" depends="init,precompile,combine,versionize,-compress"></target>

	<target name="redeploy" depends="combine,versionize,localdeploy"></target>

	<target name="localpush" depends="init,precompile,combine,versionize,localdeploy"></target>

	<target name="repush" depends="push,push-compressed"></target>
			
</project>