Foliotek Developer Blog

Visual Studio Custom Start Page

Feature Driven Development

More than a year ago, the Foliotek development team moved to a Feature Driven Development Cycle (FDD). If you’ve never researched into this form of agile development I would recommend looking into it, because I won’t go into too much detail about it. Here’s a quick overview of how we use this development process here at Foliotek (assuming you have at least a basic understanding of SVN):

In each project repository, trunk is our production copy. This means that trunk needs to build without any errors, and is under a strict testing cycle. When we start work on a feature we will create a branch off of trunk. All the work for that feature goes into that branch, therefore we’re never checking in incomplete code into the production copy. This means that several developers can work on multiple features at one time, without interfering with each other. Testing can be done separately in these feature branches as well. After a feature has been approved, we reintegrate the branch into trunk, and it is ready for production.

The Problem

The problem we ran into with this cycle was the amount of time and effort that went into setting up a feature for development. We’d have to

  1. Create the branch with an ID based on the case in our project managment software.
  2. Check it out onto our computer (using TortoiseSVN)
  3. Set up an IIS entry (including virtual directories) so we could run the feature branch on our machine.
    This allows testers and developers to access the work at http://computername/task_12345

After all is said and done it takes 10-15 minutes just to set each branch up (not to mention maintain it), and some of us can have up to 5 or 6 features open at a time. This also gets really hard to manage when you’re working on multiple features for separate projects. Meanwhile other people are reintegrating their features into trunk, and in order to avoid conflicts in SVN you need to be constantly merging the latest changes in trunk into your feature branches. As anyone with merging experience in SVN can tell you, if you don’t do this reintegrating your branch can result in a ton of conflicts. It didn’t take long to realize that we needed a better way to streamline this process.

The Solution – The Foliotek Start Page!

We settled on developing a Custom Start Page for Visual Studio, that would help us keep track of all of our branches. It would also help us keep them updated, reintegrate them, and allow easier access to them. Since we’ve had so much success with this tool, we wanted to share it, in case any other teams were looking for a similar solution. This will be a hybrid introduction and setup guide. If you’re only interested in what the final product is, you can scroll to the bottom.
Note: We’ve not spent much time on the UI of this tool – you’ve been warned…

Prerequisites

  • Visual Studio 2010 (not Express Versions)
  • IIS 7
  • Tortoise SVN (1.6 or 1.7)

Before even downloading the start page, you’ll need to set up a settings xml document. Copy the contents of the following xml sample (or download the settings file) to the following location and replace all the values with your project’s details.

C:\Users\[USERNAME]\Documents\Visual Studio 2010\Settings\StartPageSettings.xml
(or whatever the path is to your Visual Studio 2010 Settings folder)

I recommend setting each Project element’s Path to an empty directory to allow the start page to start from a clean slate.

<?xml version="1.0" encoding="utf-8" ??>  
<settings>  
    <projects>
        <project>
            <name>Example Project</name>
            <shortname>example</shortname>
            <svnurl>https://svn.example.com/svn/example</svnurl>
            <path>C:\Users\USERNAME\Documents\Visual Studio 2010\Projects\Example</path>
            <webfolderpath>Web</webfolderpath>
            <solutionfolder></solutionfolder>
            <iisuser></iisuser>
            <iispassword></iispassword>
            <iisapppool>ExampleAppPool</iisapppool>
            <iisapppoolversion>4.0</iisapppoolversion>
            <virtualdirectories>
            <virtualdirectory>
            <name>Resources</name>
            <path>\\shareddirectory\resources</path>
            </virtualdirectory>
            <virtualdirectory>
            <name>Resources2</name>
            <path>\\shareddirectory\resources2</path>
            </virtualdirectory>
            </virtualdirectories>
        </project>
    </projects>
    <svnversion>1.7</svnversion>
    <username></username>
    <email></email>
    <fogbugzurl></fogbugzurl>
    <fogbugzusername></fogbugzusername>
    <fogbugzpassword></fogbugzpassword>
    <websvnurl></websvnurl>
</settings>  

Installation

After you’re finished setting up the settings document, we can download the start page and get started. Once you’ve downloaded and installed the Foliotek Start Page Extension, It will ask you to restart Visual Studio, and you’ll notice that your start page isn’t set yet. You’ll have to go to (in visual studio)

Tools -> Options -> Environment -> Startup -> Customize Start Page -> Select the Foliotek Start Page.
Setting the start page in Visual Studio 2010

After you click Ok Visual Studio will open the start page. It might give you a dialog that mentions that the directory doesn’t exist so it needs to create it, which is fine. Once the start page is loaded, if you chose a fresh directory, you’ll see this…



In order to do Feature Driven Development you’re going to need a local copy of trunk, so you click Get Trunk and the start page will open a Tortoise dialog asking you to download the trunk of this project’s repository. After you download the copy of trunk you’ll have the following options on your start page…




Ignoring the buttons on the trunk control (we’ll explain those later), let’s say we’re ready to start on one of our features. We could click on “Create Task”, and type in our branch name into the Name textbox and click add.


After we click add, the start page will create the branch (if it doesn’t already exist), then we’ll get the TortoiseSVN dialog to download the branch.


After we click Ok on the Tortoise dialog, the start page will check out the branch and create an IIS entry for the branch. You can see in the image below we have an entry for /foliotek (trunk) and /foliotektaskDemo.


Now you’re ready to start developing!! The above process is the base functionality of the start page. Before the start page, this Demo branch would have taken around 10-15 minutes to set up before I could even develop. Now I have it ready in under a minute. In addition to improving the set up process, we realized that we could improve the maintenance and management of the project with a few extra buttons.
Foliotek Start Page - Individual Task

Some of the buttons are self explanatory, some of them aren’t, so I’ll just go from left to right and explain each one.

  1. The Visual Studio button that opens the solution.
  2. The windows explorer button that opens that individual task’s folder, then the browser button which actually just opens the project in your default browser, not IE.
  3. Open the SVN Repository Url in the browser
  4. Open the WebSVN Url (if one exists). We use WebSVN because it is a nicer UI for browsing source, but the only one that’s mandatory is an SVN url
  5. Next we have a FogBugz (our bug/feature tracking software) button that opens the feature’s description.
  6. Update button grabs the latest changes from the SVN repo
  7. The local build button builds the solution without opening it in Visual Studio.
  8. The Merge from Trunk button opens a TortoiseSVN merge dialog, with the fields auto-populated, and all you have to do is click next.
  9. The Reintegrate button opens a TortoiseSVN dialog, but unfortunately it doesn’t auto populate the fields for you. In the future we hope to get that working, but for right now you’ll have to enter the reintegration fields yourself.
  10. The big red “X” will delete the folder from your computers, and also remove the IIS entry associated with the branch.

Conclusion

This start page has improved our development process immensely, and we thought if anyone else is using this development cycle, they might benefit from it as well. Even if your team isn’t using Feature Driven Development, the start page can still be useful to teams using SVN and IIS. If anyone is interested in trying it, you can get it from its extension gallery page, or you can find it in the extension manager in visual studio.

Visual Studio -> Tools -> Extension Manager-> Online Gallery -> Search for “Foliotek Start Page
Our plans for the unforeseeable future include open sourcing this extension, and also implementing an architecture that would allow people to write their own actions to perform on branches, and adding some screens to add projects and edit settings.

Let us know if you have any feedback or problems setting it up, I’d be more than happy to help.


Windows Batch Files: '@echo is not recognized'

Today, after loading up a publish script and running it, I got a strange error: ?'*** @echo' is not recognized as an internal or external command, operable program or batch file. It then continued to run each line individually, throwing errors on basically every line.

After playing around with creating a brand new empty file, and getting the same error – I realized the problem. The encoding was set to UTF-8, which the Windows command line failing on.

To fix it, just set your encoding to ANSI. Here is a screenshot of how to do that with Notepad++:

And now, everything works wonderfully and I can get back to work:


Rename Applications and Virtual Directories in IIS7

Have you ever wondered why the box to change the name or “Alias” on an application or virtual directory is greyed out (see screenshot below)? I found a way to change the name without recreating all your settings. It uses the built in administration commands in IIS7, called appcmd.

Renaming Applications In IIS7

  1. Open a command prompt to see all of your applications. C:> %systemroot%\system32\inetsrv\appcmd list app APP "Default Web Site/OldApplicationName" APP "Default Web Site/AnotherApplication"
  2. Run a command like this to change your “OldApplicationName” path to “NewApplicationName”. Now you can use http://localhost/newapplicationname C:> %systemroot\%system32\inetsrv\appcmd set app "Default Web Site/OldApplicationName" -path:/NewApplicationName; APP object "Default Web Site/OldApplicationName" changed

Renaming Virtual Directories In IIS7

  1. Open a command prompt to see all of your virtual directories. C:> %systemroot%\system32\inetsrv\appcmd list vdir VDIR "Default Web Site/OldApplicationName/Images" (physicalPath:\serverimages) VDIR "Default Web Site/OldApplicationName/Data/Config" (physicalPath:\serverconfig)

We want to rename /Images to /Images2 and /Data/Config to /Data/Config2. Here are the example commands:

C:> %systemroot%\system32\inetsrv\appcmd set vdir "Default Web Site/OldApplicationName/Images" -path:/Images2 VDIR object "Default Web Site/OldApplicationName/Images" changed C:> %systemroot%\system32\inetsrv\appcmd set vdir "Default Web Site/OldApplicationName/Data/Config" -path:/Data/Config2 VDIR object "Default Web Site/OldApplicationName/Data/Config" changed


Changing .Net Framework version on a website in IIS without restarting the W3SVC process

If you are changing the .Net Framework version of a website hosted in IIS, you will most likely receive a message that looks like the following

The problem is that if you restart the W3SVC, all the application pools will be recycled. In my case we are hosting multiple versions of the site in IIS (Live, Staging, etc.). I didn’t want to recycle every application pool if I didn’t need to. Plus, if you are storing state information in the worker process it will be lost when the application pool gets recycled.

The dialog points to a command you can run in order to keep the W3SVC process from restarting. The command is pretty straight forward except for the "IIS-Virtual-Path" part. The "IIS-Virtual-Path" is the path IIS uses in order to differentiate between sites. This path is in the form "W3SVC/Site ID/root". To find the Site ID, just click on the "Web Sites" folder in IIS and you should see a table in the right column that looks similar to this:

IIS 6

IIS 7

Once you find the Site ID, open up a command prompt window and navigate to the folder of the framework version you are wanting to change to. The framework version folders are usually found in c:/Windows/Microsoft.Net/Framework/. Once you are inside the folder, you can just run the following command for a site with the ID 1957079098:

aspnet_regiis.exe -norestart -s W3SVC/1957079098/root/  

After you update the framework version, you will still need to restart the application pool for the website that you are changing though.


Controlling IIS7 With Custom Build Script

Handling IIS after doing a publish was always a bit of a pain.

We use Web Deployment Projects which automates much of the build process, such as minifying CSS and JavaScript and copying new files to the web servers. But we still had to deal with the pain of manually controlling IIS after a publish. This actually required remoting into the servers, and changing the home directories of the site in IIS.

This was always a small headache, but if we only published once a month or so, it wasn’t a big deal. A few months ago, we started doing weekly releases in Foliotek, our assessment portfolio tool to take care of any problems that customers were having on the live site.

Here is what we used to do:

  1. Build the WebDeploy project in ‘release’ mode. This will copy files to a StagingWeb folder on both servers.
  2. Load up the staging website (which is hosted on Server1 and pointing directly at the StagingWeb folder). Verify any fixes.
  3. (Here is the annoying part): Remote into both servers, and open IIS Manager. At approximately the same time, switch the home folder over to StagingWeb and make sure the site is still up. Back up the Web folder, delete it and copy StagingWeb then rename the copy to Web. Point IIS back to the Web folder.

We have extra steps in part 3 to prevent any downtime, but it is tedius and prone to error, timing problems,etc. And it is not something to look forward to at the start of the week. Why should we do something manually that should be easy to automate?

Here is what we do now:

  1. Build the WebDeploy project in ‘release’ mode. This will copy files to a StagingWeb folder on both servers.
  2. Load up the staging website (which is hosted on Server1 and pointing directly at the StagingWeb folder). Verify any fixes.
  3. Run a batch script to handle backups and switching IIS

The solution uses the PsExec utility which allows remote execution of batch scripts,and the AppCmd.exe command line tool for managing IIS7.

It is just a couple of batch scripts. You can call them on the command line, or through MSBuild. We actually have a Windows Forms project that gets run on the AfterBuild event of our Web Deployment project. This executable has links to all of the sites, shortcuts to remote desktop, status of the servers, and a link to the publish script.

Run it using this command:

[sourcecode lang="bash"]
publishserverconnector.bat \Server1
publishserverconnector.bat \Server2
[/sourcecode]

The source code is below. It would probably need to be modified for different environments, and I’m sure there are better ways to handle this problem, at least this is simple to read and modify.

publishserverconnection.bat

This file basically just calls PSExec on the given server passing up the myproject_publish.bat script.
[sourcecode lang="bash"]
@ECHO off
SETLOCAL

REM Publish Connection Script
REM This will call the publish.bat script on the server specified in the first argument
REM Usage: publishserverconnection.bat [servername]

SET server=”%1″
SET serverdir=”%1c$inetpubwwwrootMyProject”
SET psexec=”%~dp0psexec.exe”
SET publish=”%~dp0myproject
publish.bat”
SET usage=USAGE: publishserverconnection.bat [servername]

if not exist %serverdir% (
ECHO ERROR: The path %server
dir% does not exist! Exiting..
ECHO %usage%
GOTO End
)
if not exist %psexec% (
echo ERROR: Could Not Find PSEXEC at path: %psexec%
GOTO End
)
if not exist %publish% (
echo ERROR: Could Not Find PUBLISH at path: %publish%
GOTO End
)

ECHO Starting publish on %server%.
%psexec% %server% -c -v %publish%

if ErrorLevel 1 (
ECHO.
ECHO ERROR: Having problems starting PSExec. Please verify access to the server, and retry.
ECHO If the problem persists, then you will need to manually publish.
ECHO.
)

:End

ENDLOCAL

[/sourcecode]

myproject_publish.bat

This file will be copied to the servers using PSExec, and will be executed locally in the server environment.
[sourcecode lang="bash"]
@ECHO off
SETLOCAL

REM Publish Script
REM This is called from development machines, using the PSExec Command.

CLS
ECHO.
ECHO =======================================================
ECHO Build Script.
ECHO %COMPUTERNAME%
ECHO This script will:
ECHO 1. Point the IIS entry for the LIVE website to StagingWeb
ECHO 2. Backup the original Web folder
ECHO 3. Copy StagingWeb over the original Web folder
ECHO 4. Point the IIS entry to the new Web folder
ECHO =======================================================
Echo.
ECHO Make sure you go to the staging site and confirm it is ready to go live.

For /f “tokens=2-4 delims=/ ” %%a in (‘date /t’) do (SET startdate=%%c-%%a-%%b)
For /f “tokens=1-2 delims=/:” %%a in (“%TIME%”) do (SET starttime=%%a%%b)

SET livedir=”C:inetpubwwwrootMyProjectWeb”
SET stagingdir=”C:inetpubwwwrootMyProjectStagingWeb”
SET livedirrevert=”C:inetpubwwwrootMyProjectWebRevert”
SET backupdir=”C:inetpubwwwrootMyProjectbackups%startdate%
%starttime%”
SET appcmd=”C:WindowsSystem32inetsrvappcmd.exe”
SET appcmdchange=%appcmd% set vdir “MyProject/” -physicalPath:%stagingdir%
SET appcmd
revert=%appcmd% set vdir “MyProject/” -physicalPath:%livedir%

IF NOT EXIST %livedir% (
ECHO Could not find path %livedir% on %COMPUTERNAME%
GOTO End
)
IF NOT EXIST %stagingdir% (
ECHO Could not find path %stagingdir% on %COMPUTERNAME%
GOTO End
)

Choice /M “Are you ready to start?”
If Errorlevel 2 GOTO End REM Only proceed if ready, exit if user types “N”

ECHO.
ECHO Pointing website at the StagingWeb folder…

CALL %appcmd_change%
If Errorlevel 1 GOTO IISError

ECHO New site is live
ECHO.

Choice /M “Does the site still work? If NO, IIS will be reverted.”
If Errorlevel 2 GOTO Revert

GOTO Backup

:Backup
ECHO Starting Web Backup to archives folder, and WebRevert in case you need to revert changes.
if exist %livedirrevert% (
rmdir %livedir
revert% /s /q
)
xcopy %livedir% %backupdir% /E /Y /q
xcopy %livedir% %livedir_revert% /E /Y /q

ECHO.
ECHO Removing old Web folder and copying StagingWeb to Web.
rmdir %livedir% /s /q
xcopy %stagingdir% %livedir% /E /Y /q

If Errorlevel 1 GOTO BackupError

ECHO.
ECHO Backup path is: %backupdir%
ECHO Backup Success! Resetting IIS to the Web/ folder…

REM Reset IIS to Web/ (which is copied from StagingWeb/)
CALL %appcmd_revert%
If Errorlevel 1 GOTO IISError

ECHO.
ECHO Great job! Now published on %COMPUTERNAME%. Don’t forget to set the other live web servers!
GOTO End

:BackupError
ECHO IMPORTANT: There was an error backing up the files.
ECHO This could be caused by lack of permissions when trying to remove the old Web directory, if IIS has a lock on the folder.
ECHO Don’t worry, the live site should still be ok, but you will need to manually remote into the server to sort out the backups.
GOTO End

:IISError
ECHO IMPORTANT: There was an error switching IIS over (error code = %ErrorLevel%)
ECHO Please manually remote into the server to sort things out.
GOTO End

:Revert
echo Resetting to the original Web folder…
CALL %appcmd_revert%
If Errorlevel 1 GOTO IISError
GOTO End

:End

ENDLOCAL
[/sourcecode]