7

We are trying to automate the build process to our staging servers but have run into a snag, albeit fairly minor. We are using the Publish functionality built into VS2010, committing to Subversion, and then a 3rd party app (Beanstalk) automatically pulls the updated files and FTPs them to the Staging server.

The problem we've run into is that we only appear to have the following choices:

  • (Lesser of 2 evils) If we choose to use "Replace matching files with local copies", this works great, with one exception: this option does not delete any files that were deleted from the project. This will lead to junk and/or security issues for unkempt files from the days of old.
  • If we choose to use "Delete all existing files prior to publish", this deletes the entire folder structure, including the .SVN hidden folders that Subversion uses for Update tracking, etc. This seems like the best solution from an accuracy standpoint, but it really destroys the local SVN environment, which is the middle-man for this automation.

My question: Is there an easy work around for this, or a totally different deployment option we're overlooking (we do not want to publish directly to the server from VS, as we want to track who/what/when a deployment takes place)? The only thing I've come across is to delete the file contents manually prior to publishing, while leaving the folder structure intact, then deploying with "Replace matching files with local copies". Unfortunately, this brings on a whole new meaning of the word "automation".

Any ideas on how best to accomplish this?

Keith
  • 5,311
  • 3
  • 34
  • 50
  • 1
    Perfect timing. I'm also looking for a solution. Have you tried messing around with Solution Configurations and Post-Built events? – Sergey Akopov Dec 29 '10 at 06:26
  • Not at all - up til now all deployments have been manual, which can be quite labor intensive with a large change set. I just can't believe there's not a better option built in that gives you an accurate build without destroying the folders. – Keith Dec 29 '10 at 14:33

4 Answers4

4

You may want to consider using NAnt or something similar for tasks you wish to automate, like building and publishing to Subversion. This is most of my build file for a WebApplication Project. It might be different for MVC. If so, I'm sure you can use this as a starting point. I am by no means an NAnt expert so there may be some flaws, but this is definitely working for me.

I had to add a PublishToFileSystem target to each .csproj file I wanted to publish. The source for that can be found here.

Build file also available on Pastebin

<?xml version="1.0"?>
<project name="deploy" default="all">
    <property name="nant.settings.currentframework" value="net-4.0" />  
    <!-- Any of these can be passed through the command line -->
    <property name="sourceDirectory" value="${project::get-base-directory()}" />
    <property name="publishDirectory" value="${sourceDirectory}\build" />
    <property name="MSBuildPath" value="${framework::get-assembly-directory(framework::get-target-framework())}\msbuild.exe" />
    <!-- The build configuration to use when publishing and transforming the web.config file. This is useful when you have multiple environments for which you create builds -->
    <property name="buildConfiguration" value="Release" /> 
    <!-- Set these as needed -->
    <property name="svn.username" value="" />
    <property name="svn.password" value="" />

    <target name="SvnPrep">
        <property name="svn.dir" value="${publishDirectory}\.svn" />
        <property name="svn.update" value="true" readonly="false" />
        <echo>env.svn.path = svn</echo>
        <echo>svn.dir = ${svn.dir}</echo>
        <mkdir dir="${publishDirectory}" unless="${directory::exists(publishDirectory)}" />
        <!-- Check if there's a .svn dir already. If not: checkout, else: update. -->
        <if test="${not directory::exists(svn.dir)}">
            <exec program='svn.exe' workingdir="${publishDirectory}" verbose="true">
                <arg line='co ${svn.builduri} --username ${svn.username} --password ${svn.password} --non-interactive ./' />
            </exec>
            <property name="svn.update" value="false" readonly="false" />
        </if>
        <if test="${svn.update}">
            <exec program='svn.exe' workingdir="${publishDirectory}\" verbose="true">
                <arg line='up --username ${svn.username} --password ${svn.password} --non-interactive --force ./' />
            </exec>
        </if>
        <!-- Force any conflicts to be resolved with the most recent code -->
        <exec program='svn.exe' workingdir="${publishDirectory}\" verbose="true">
            <arg line='resolve --accept theirs-full -R ./' />
        </exec>
    </target>   

    <target name="DeleteFiles">
        <!-- Delete only the files (retain directory structure) in the directory to which you are going to publish/build. NAnt excludes svn directories by default. -->
        <delete includeemptydirs="false">
            <fileset basedir="${publishDirectory}">
                <include name="**/*.*" /> 
            </fileset>
        </delete>
    </target>
    <target name="Publish">
        <!-- I know there's an MSBuild task, I don't know why I didn't use it, but this works. -->
        <!-- Build and publish frontend -->
        <exec program="${MSBuildPath}">
            <arg line='"${sourceDirectory}\YourProject.csproj"' />
            <arg value='"/p:Platform=AnyCPU;Configuration=${buildConfiguration};PublishDestination=${publishDirectory}"' />
            <arg value="/target:PublishToFileSystem" />
        </exec>
        <!-- Transform the correct web.config and copy it to the build folder. PublishToFileSystem doesn't transform the web.config, unfortunately. -->
        <exec program="${MSBuildPath}">
            <arg line='"${sourceDirectory}\YourProject.csproj"' />
            <arg value='"/p:Platform=AnyCPU;Configuration=${buildConfiguration};PublishDestination=${publishDirectory}"' />
            <arg value="/target:TransformWebConfig" />
        </exec>
        <copy file="${sourceDirectory}\YourProject\obj\${buildConfiguration}\TransformWebConfig\transformed\Web.config" tofile="${publishDirectory}\YourProject\web.config" overwrite="true" />     
    </target>

    <target name="SvnCommit">       
        <!-- add any new files -->
        <exec program='svn.exe' workingdir="${publishDirectory}" verbose="true">
            <arg line='add --force .' />
        </exec>
        <!-- delete any missing files, a modification of this http://stackoverflow.com/questions/1071857/how-do-i-svn-add-all-unversioned-files-to-svn -->
        <!-- When there's nothing to delete it looks like this fails (to NAnt) but it is actually fine, that's why failonerror is false -->     
        <exec program='cmd.exe' workingdir="${publishDirectory}\" verbose="true" failonerror="false" 
            commandline='/C for /f "usebackq tokens=2*" %i in (`svn status ^| findstr /r "^\!"`) do svn del "%i %j"' >
        </exec>
        <exec program='svn.exe' workingdir="${publishDirectory}" verbose="true">
            <arg line='commit -m "Automated commit from build runner"' />
        </exec>
    </target>

    <target name="ShowProperties">
        <script language="C#" prefix="util" >
            <code>
                <![CDATA[
                public static void ScriptMain(Project project) 
                {
                    foreach (DictionaryEntry entry in project.Properties)
                    {
                        Console.WriteLine("{0}={1}", entry.Key, entry.Value);
                    }
                }
                ]]>
            </code>
        </script>
    </target>

    <target name="all">
        <call target="ShowProperties" />
        <call target="SvnPrep" />
        <call target="DeleteFiles" />
        <call target="Publish" />
        <call target="SvnCommit" />
    </target>
</project>
  • Looks promising, I'll have to look into this when I have a chance. Thank you. – Keith Jan 11 '11 at 17:05
  • Getting started with NAnt and other automation tools can be time-consuming but in the end it is well worth it. Let me know how it works out for you or if you have any other questions. – Alexander Pendleton Jan 11 '11 at 17:15
  • +1 nice answer @Alex why have it inside the source folder instead of a separate location in svn? it'd download it when someone does a svn update and additionally you have the config shared to anyone with access to the source @Keith did you use it, how has it gone, any difference you ended up with? – eglasius May 15 '11 at 05:11
0

We also deploy out of SVN and ran into the same problem. Our solution is to essentially branch the project for "significant" upgrades -- situations where we were adding and deleting files, not just fixing small bugs and making tweaks which you can usually handle by xcopy. Svn layout looks like:

--project
---production
----20100101
----20100213
[etc, etc]

Procedure-wise, it is pretty simple -- if there are big enough changes, check in build artifacts as appropriate.

Another thing you might want to try, especially if you cannot get your production bits to "switch" branches easily, would be to use something fancier such as powershell to execute the delete files command, that could filter out the *.svn folders.

Wyatt Barnett
  • 15,573
  • 3
  • 34
  • 53
  • 1
    The problem with a branch is it adds a manual step to a process we would like to make fully automatic. Thanks for the input but this is definitely not the direction we're looking for - we would rather manually delete the files for now until we can find something nicer. – Keith Jan 02 '11 at 18:20
  • Hrm, given that and your other answers you might want to try something that automatically tracks changes rather than relying upon something that drops tracks everywhere. Mercurial comes to mind. – Wyatt Barnett Jan 11 '11 at 04:05
0

I would say "fortunately" this brings a whole new meaning to the word automation :) What you're describing is knows as Application Release Automation, also sometimes called Deployment Automation. If you really want to know who did what and where, what was the outcome, etc. then you are looking for a product like Nolio ASAP (http://www.noliosoft.com). Please let me know if this helps, since from what you're describing, it seems like a perfect match.

+Daniel

Daniel Kushner
  • 191
  • 1
  • 4
  • 1
    Not exactly what we're looking for, as it doesn't appear to integrate with Subversion (at least it doesn't mention it outright). It does look like a very nice product however, but we already have a full deployment solution, with the minor problem outlined above. – Keith Jan 04 '11 at 14:36
0

Why are you publishing the site to a folder that is handled by Subversion?

The way I would do it is work directly with the files in the SVN handled folders. As soon as I commit anything, it gets pulled by beanstalk to the staging area. This way, files deleted are always deleted from the repo and you don't have to worry about that. Everything is always in sync.

If you feel that this is putting too many files to the staging area, you can still use scripts and Visual Studio commands to publish the site. But I'm not sure how well Beanstalk integrates with this scenario. I know CC.net and many other alternatives do.

Peter Evjan
  • 2,423
  • 3
  • 32
  • 50
  • 1
    First off, we use our staging server to "stage" what is actually going to be deployed into production, which is PRECOMPILED code... Second, we use config transforms to manage connection strings and app settings that vary between the different environments. Having Beanstalk pull the current version of web.config directly from SVN is not going to work. – Keith Jan 05 '11 at 14:46