Automated Deployment for SharePoint 2007 with Team Server and VSeWSS
So you’ve built a custom Web Part. VSeWSS has been your tool of choice, especially since you lassoed that pkg folder into source control. It deftly dispatches the drudgery of creating WSP and XML files from your content and code. It even writes you an adorable little batch file to provide a smooth install with just a double-click. But you and I both know that’s not enough. It’s the twenty-first flippin’ century, and we want Continuous Integration.
I’m going to go ahead and assume that you have checked your VSeWSS project into Team Foundation Server, and created a basic Team Build that is kicked off on a check-in. If you haven’t, by all means, go set that up. The rest of the class and I are waiting.
Ps I Love You
The SharePoint administration command-line tool stsadm is designed in such a way that it only operates on the SharePoint server instance on the local machine. This is all fine and dandy for the small shop whose Team and SharePoint Servers (as well as their Exchange, SQL, and warez servers) reside on the same put-upon little machine. For the rest of us, however, this means that we have to bribe a lonely sysadmin to log on to the SharePoint box and copy/execute the aforementioned batch file. Or, we could whisper a prayer and have it answered by a miraculous man known only as Mark Russinovich.
Several years back, Mr. Russinovich decided that it was about time we had a decent tool to run commands on a remote Windows machine. He was right, and today we have PsExec. Step one, for us, is to download and install PsTools on our Team Server host.
If You MSBuild It…
Step two is slightly more developer-intensive. And by developer-intensive, I of course mean that you will have to copy and paste the MSBuild code from this post into your TFSBuild.proj file, pausing only briefly to modify some configuratory values on your way to SharePointy continuously integrational bliss. You probably will not need to alter the Executables values from the listing below except for the psexec credentials, but surely you must have a better name for your SharePoint solution.
<!--Figure the First, in which our constants are defined --> <PropertyGroup> <!--Executables--> <Devenv>"%ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE\devenv"</Devenv> <Psexec>psexec /i /accepteula \\$(TargetServer) /u spAdminUsername /p s01337itHz</Psexec> <!--Paths--> <SolutionDir>$(SourceDir)\BestPortalEver</SolutionDir> <ProjectDir>$(SolutionDir)\MahWebParts</ProjectDir> <TempDropFolder>%TEMP%\$(BuildNumber)</TempDropFolder> <!--Etc.--> <Configuration>Release</Configuration> <SolutionFileName>BestPortalEver.sln</SolutionFileName> <WSPName>Aptera.Blog.BestPortalEver.MahWebParts.wsp</WSPName> <TargetServer>development.sharepoint.apterasoftware.com</TargetServer> </PropertyGroup>
The third step is to add an AfterCompile Target, in which we will spin up a second instance of Visual Studio to build the solution with the /Package switch. This will (oddly enough) fill out the pkg folder with the WSP and setup.bat files needed to deploy our solution.
<!--Figure the Second, in which we do it again the right way.--> <Target Name="AfterCompile"> <!-- Build using /package switch --> <Exec Command="$(Devenv) "$(SolutionDir)\$(SolutionFileName)" /Deploy $(Configuration) /Package" /> <!--Copy Package to the Drop Location--> <Exec Command="xcopy /y /e /i "$(ProjectDir)\bin\$(Configuration)\*.*" "$(DropLocation)\$(BuildNumber)\$(Configuration)\"" /> </Target>
This seems simple and straightforward enough; but keep your tin foil hat on, this is pre-2010 SharePoint development. The oddity here is that the pkg folder (being, from Team Server’s point of view, more source code than resultant binary files) defaults to Read-Only in the workspace. If you would be so bold as to launch your build at this point, it would trip on this step and fail with the following edifying pronouncement:
------ Validate solution and resolve deployment conflict(s) ------ Validating solution ... EXEC : error : System.UnauthorizedAccessException Access to the path 'C:\Documents and Settings\tfsBuildUser\Local Settings\Temp\Aptera.Blog.BestPortalEver\Check-in Build\Sources\Aptera.Blog.BestPortalEver\MahWebParts\pkg\solution.xml' is denied.
The fix for this flounder is to remove the Read-Only flag from our favorite little output directory, just before the call to devenv:
<!-- Make the pkg folder and its contents writable --> <Exec Command="attrib -R "$(ProjectDir)\pkg\*.*" /S /D" />
What Can PsExec Do For You
Now that we have a viable pkg folder full of deliverable SharePoint goodness, it is time to deliver the goods. To do this, we must call upon PsExec. Remember PsExec?
Back in step two, to simplify things, we added a property that sets up a call to PsExec with all of the necessary parameters, such as remote host name, user credentials, etc. Let’s see those again in slow-motion, for the people in the back:
<Psexec>psexec /i /accepteula \\$(TargetServer) /u mahSPAdminUser /p s01337itHz</Psexec>
Our fourth and final step is to create another Target named AfterDropBuild. In it we will use PsExec to copy the build’s outputs over to our SharePoint host, and execute the setup script:
<!--Figure the Third, in which the magic happens.--> <Target Name="AfterDropBuild"> <!--Copy Files to Target Server--> <Exec Command="$(Psexec) xcopy /y /e "$(DropLocation)\$(BuildNumber)\$(Configuration)\" "$(TempDropFolder)\"" /> <!--Deactivate and Uninstall previous version of the Solution--> <Exec Command="$(Psexec) echo . | "$(TempDropFolder)\setup.bat" /uninstall " /> <!--Install and Activate the Solution--> <Exec Command="$(Psexec) echo . | "$(TempDropFolder)\setup.bat" /install " /> </Target>
P.S. – A quick sidebar on calling VSeWSS’s setup.bat
Presumably under great duress and/or substance abuse, the decision was made on the VSeWSS team that when the generated setup script had finished its business, it would say so and wait more patiently than you for someone to hit the Enter key before it exited completely. This is highly convenient and even polite in the context of our lonely, lonely sysadmin running said batch file on our behalf, but it gets old pretty quickly when your Team Build is waiting just as patiently for the script to return control. Echoing a period and piping it into a script is a common technique for simulating the required keystroke at the script’s prompt.
In case that was keeping you up at night.