Recently I attended the developer upgrade training course for Sitecore 7, which was an interesting (and busy!) day learning about what’s been changing in Sitecore in recent releases. But in amongst all that information, there was an interesting admission – Sitecore’s training has moved away from the “stick your Visual Studio Solution in the Sitecore website folder” model that had jarred with me when I first did their training.
I didn’t really like this model when it was taught at the original Sitecore training I attended a few years back. Before Sitecore I had done all my development work with my solution folders outside of the IIS sites. So the model I adopted for development when I started my first real project made use of MSBuild instead. (Rather than the Visual Studio Publishing wizard that the latest training discusses) So I thought I’d write down a few of the things I’d tried for setting up solutions, in case these of use to anyone else…
One of the things that’s important to think about right at the start of your project is the file system structure for your work. Whilst some of this will be determined by your source control choices, there’s one thing I think is important that seems to get missed by quite a few bits of code I find myself supporting. And that is keeping all the bits if your solution together when they’re deployed, to try and prevent clashes between different packages that get installed.
If you think that your Sitecore project will be installed alongside other packages and projects onto a single instance of Sitecore – and almost all projects will be – then it’s sensible to try and keep all your non-binary files in a single parent folder named for the project or package you’re working on. The key reason for this is to avoid the chance of collisions between files deployed by this project and anything else that might get deployed to the instance of Sitecore. It also helps future maintainers of your code find things easily too…
So the structure of a solution in Visual Studio might look something like:
Now some files (Global.asax or favicon.ico being good examples) don’t fit this model – but for packages like extensions or sets of templates this approach will make it much less likely that some other deployment to your server will get accidentally overwritten. Keeping things organised like this also simplifies the process of getting these files from the project folder and into the IIS Site.
With the structure determined, another thing to think about is how the files will get from your Visual Studio project folder into the IIS Website folders:
Every time you hit “build” in Visual Studio, you’re running the MSBuild scripts that are your project files. These are extensible in a variety of ways, but the easiest extension point for this purpose is the
AfterBuild event. Unsurprisingly, that’s what gets run after the build has succeeded. By default you’ll find an empty instance of this in any new project you create:
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> </Target> <Target Name="AfterBuild"> </Target> -->
To deploy our files via MSBuild we need the build script to know two things: Firstly where to send the files, and secondly what files to send. It’s not an uncommon scenario to want the results of your builds to go to different locations over the lifetime of a project, so it makes sense to have this configurable. A simple approach to allowing this is to have a text file in your solution that contains the target path. We can configure the AfterBuild event to run our deployment tasks if this destination file exists:
<target name="AfterBuild"> <calltarget targets="DeployToWeb" condition="Exists('.\BuildConfig\WebRootPath.txt')" /> </target> <target name="DeployToWeb"> </target>
To perfom the deployment we need to work our the set of files to include or exclude, get the target location from the config file and the perform the copy:
<target name="DeployToWeb"> <itemgroup> <itemstodeploy include="bin\*.dll" /> <itemstodeploy include="bin\*.pdb" /> <itemstodeploy include="App_Config\**\*.config" /> <itemstodeploy include="Client.Project\**\*.*" /> </itemgroup> <readlinesfromfile file=".\BuildConfig\WebRootPath.txt"> <output propertyname="DeployPath" taskparameter="Lines" /> </readlinesfromfile> <createitem include="@(ItemsToDeploy)" exclude="**\*.cs"> <output taskparameter="Include" itemname="DeployFiles" /> </createitem> <copy sourcefiles="@(DeployFiles)" destinationfiles="@(DeployFiles->'$(DeployPath)\%(RelativeDir)%(Filename)%(Extension)')" skipunchangedfiles="true" /> </target>
<ItemGroup/> element contains a description of the set of items you need to deploy. Here we’re including the binaries generated by the build, any Sitecore config patches required by the code, and the “stuff for this project” folder. the “**” symbol in the path here is a wildcard meaning “all child files and folders”. In your projects you can adjust these items to more closely match the needs of your project.
After this is declared, the
<readlinesfromfile/> reads the path from the configuration file and sticks the line of text into a property called “DeployPath”
<createitem/> element uses the data from the
<itemgroup/> above to work out the real set of files that need deploying. It includes everyting matching the patterns we specified, but excludes any
.cs files – we don’t need those as they’ve been compiled into the binaries.
And finally the
<copy/> element takes the list of files that need moving and transfers them to the same paths under the target directory our configuration defined.
So with that in place, whenever you build your solution, any changes to your Visual Studio project will be copied into your website root without you needing to click anything.