Variable Replacement with Ant

Technical

In the years I've been using Ant, there aren't many things I haven't been able to make it do for me to automate the end-to-end build and/or deployment process. There is at least one thing that's eluded me, though. Almost every application I write includes a config file. The application config file usually contains environmental information and maybe some authentication information that is likely to change from server to server during the deployment process.

My process for working with config files has always been to save to source control a file named config.cfm.sample (or config.php.sample or whatever). The sample file bakes in any static values, but includes dummy values where the live data will be sensitive (e.g. authentication credentials) or environment-specific. When I export (or checkout) the code, I make a copy of the sample file (dropping the .sample extension) and then supply "real" values. By adding the .sample extension to the committed file (and svn:ignore-ing the copied version without the extension), I can usually avoid committing any sensitive data.

The last step is one that I've thought should be automated, but I've never been able to find an adequate solution. I've thrown sed at simple config files, but that breaks down rapidly with more complex files for those of us who aren't wizards with sed or awk. I usually resort to documenting the changes that needed to be made to the file and then making those changes manually at each stage of deployment. Not anymore.

Today, a colleague of mine found a way to automate this with Ant and although I was somewhat annoyed to find that the solution is a core Ant task that has been right in front of my face all these years, I was jubilant (yeah, I said "jubilant", what of it?) to finally have the solution I've been chasing for so long.

The solution is in the replacetokens task. Here's how it works for a sample configuration file:

Sample Configuration File

The sample configuration file contains one or more substitution variables (tokens) that are denoted by the "@" sign before and after the text.

<?xml version="1.0" encoding="utf-8"?>
<config>
	<application>
		<name>My Application</name>
		<environment>@ENVIRONMENT@</environment>
		<fileroot>@FILEROOT@</fileroot>
		<classpath>
			<classroot type="application">@CLASSPATH@</classroot>
		</classpath>
	</application>
	<database>
		<type>MYSQL</type>
		<host>@DBHOST@</host>
		<name>databasename</name>

		<user>
			<username>@DBUSER@</username>
			<password>@DBPWD@</password>
		</user>
	</database>
</config>

The Build File (build.xml)

The build file will perform a simple copy of the sample file to the live file, but during the copy process will also perform the variable substitution.

<copy file="${local.buildroot}/${project.fsname}/project/config.php.sample"
      tofile="${local.buildroot}/${project.fsname}/project/config.php"
>
   <replacetokens>
      <token key="ENVIRONMENT" value="${config.env}" />
      <token key="FILEROOT" value="${config.fileroot}" />
      <token key="CLASSPATH" value="${config.classpath}" />
      <token key="DBHOST" value="${config.mysql.host}" />
      <token key="DBUSER" value="${config.mysql.dbuser}" />
      <token key="DBPWD" value="${config.mysql.dbpasswd}" />
   </replacetokens>
</copy>

Note that I haven't tried this yet, myself, but my colleague has done some testing and I'm so giddy to have an answer that I had to get it posted just to be sure I don't forget about it. I've also been doing a little reading and it appears that the filterset task would work also.

Thanks, Kyril. I've been looking for this particular answer for a very long time.

Update 1/15/2008

I've been doing some testing using the filterset task and it appears to work beautifully. Looks a little like this (using the same config file as above):

<project name="build-project" default="init" basedir=".">
   <description>
      Builds and deploys a sample project.
   </description>

   <target name="init">
      <echo>Testing copy</echo>

      <copy file="C:/path/to/my/config.php.sample"
            tofile="C:/path/to/my/config.php"
      >
         <filterset>
            <filter token="DBHOST" 
                    value="my_database"
            />
            <!-- 
               // SNIP //
               Additional filter tasks can be added
               for each variable to be replaced.
             -->
         </filterset>
      </copy>
   </target>
</project>

tags:
Ant, build
dshuck said:
 
Dang it Rob... this is good stuff. I was just saying that I needed to figure out how to do this. You bring the goods yet again.
 
posted 321 days ago
Add Comment Reply to: this comment OR this thread
 

Search

Rob  Wilkerson