After my last post about switching between these two types I actually started using it and found a major flaw in my solution. The problem is the declaration of asp.net and html control variables. Is WS these are done magically in the background and are in a partial class file generated during run-time. In WAP you put them in default.aspx.designer.cs files. So after using WS for a while there is no easy way to synchronize the designer.cs files with the controls on the aspx page. It would be nice if Visual Studio could do this and it doesn't at least not in my version which reports "Warning 24 Generation of designer file failed: Object reference not set to an instance of an object." a lot if not always. I also need to be Visual Studio independent.
So I did some more research and found out about VS 2005 Web Deployment Projects, which allow you do compile and merge all the assemblies in a WS project into a single assembly, better yet it is using the command line tool 'aspnet.merge.exe' so you could do this without Visual Studio.
So here's how I now build my deployment package for the staging and production servers.
For development in Visual Studio or Notepad2 I'm now always using WS, it is so much easier not having to recompile all the time. When I done with testing I run a batch file to create a new zip file with the files I need to copy to the servers.
As usualy I'm using some 4NT specific features so the batches wont work with cmd.exe but you get the idea.
I first compile the site with aspnet_compiler.exe into a new directory
I then use aspnet_merger.exe to merge all the assemblies into a single one for which I can specify a name.
I then run a second batch which compares the new directory with the previous version and adds all changed files into a zip file which I can then upload to the live server.
I ran into some small problems:
1. Apparently you can specify an assembly.cs file in aspnet_merger to specify the meta data for the single assembly. This didn't work but I needed to combine two different files anyway because I am using a common SolutionInfo.cs for all the different assemblies.
2. After I create the compiled site and try to run it, I get an error "Could not load type '__ASPNET_INHERITS'." in global.asax. The file has a single line "<%@ application inherits="__ASPNET_INHERITS" %>" what the heck is that? I found out that global.asax is not even suppose to be there anymore because the file bin/App_global.asax.compiled points the runtime to the correct class in the single assembly. So I just deleted global.asax and it works fine.
Here's the batch file for a specific project:
@ECHO OFF SETLOCAL set app=admin.twee.net set target=%tnHostsDir\Deployment\%app\compiled set iisSite=/LM/W3SVC/236937994/Root/Topas :: empty target rmdir /S /Q %target :: create new assemblyinfo.cs copy %tnHostsDir\Topas\SolutionInfo.cs %tnHostsDir\Topas\TweeNet.Topas.UI.Web.Info.cs %tnHostsDir\Topas\TweeNet.Topas.UI.Web\App_Code\AssemblyInfo.cs :: compile site %tnDnDir\aspnet_compiler.exe -m %iisSite %target -u -nologo :: merge assemblies %tnBinDir\aspnet_merge.exe %target -o TweeNet.Topas.WebUI -copyattrs :: clean bin directory PUSHD %target\bin del /Q /E SongTitles.* WinTopas.* topas.*.* *.xml nunit*.* TweeNet.Topas.Unittests.* *.pdb POPD :: and some files in the root PUSHD %target del /Q /E *.user *.sln *.csproj PrecompiledApp.config global.asax POPD :: now call the main script to find files to be updated CALL %tnHostsDir\scripts\DeploymentPackageCommon.cmd %app ENDLOCAL @ECHO ON
And here's a more generic one to find the updated files:
@ECHO OFF :: TweeNet Topas Deployment Synchronizer :: Description: :: ============= :: Batch to find all updated files to be copied to the live server. :: The is done by comparing a source and a master directory. :: 1. Find out which files are newer in the source :: 2. Create a batch file to list those files which is :: then used to copy them but also as a history log :: to see which files have been updated. :: 3. Copy the new files into a unique directory :: 4. Zip the content of that directory :: 5. Copy the new files into the master directory :: 6. Manually upload and deploy the files on the live server. :: Usage: :: ======= :: TopasDeploySync.cmd appName :: where appName is one of the applications we use. :: Requirements: :: ============== :: 4NT.exe 7.0 or higher is required for this :: Robocopy.exe Version XP010 or higher :: Windows NT 5.0 or higher :: Assumptions: :: ============= :: - All related files are in X:\hosts :: - The source files are in X:\hosts\Deployment\appname\compiled :: - The folders X:\hosts\Deployment\appname\wwwroot exists :: - in the source \bin\TweeNet.Topas.Business.dll exists :: History: :: ========= :: Version 1.0 / 4-Oct-2006 Singapore :: Version 1.1 / 12-Dec-2006 Singapore :: Credits: :: ========= :: written by Peter Hahndorf, contact me at www.twee.net/contact/ SETLOCAL SET scriptVersion=220.127.116.11 SET scriptName=TopasDeploySync.cmd :: we pass in the folder name for the application SET appName=%1 :: the second parameter is optional for the hosts directory :: if not specified we use \hosts on the current drive iff %# == 2 then SET hostDir=%2 else SET hostDir=%@LEFT[1,%_CWD]:\hosts endiff :: the third parameter is optional for the wwwroot directory :: if not specified we use wwwroot on the current drive iff %# == 3 then SET wwwrootDir=%3 else SET wwwrootDir=wwwroot endiff :: sourceDir is the folder with the latest files SET sourceDir=%hostDir\Deployment\%[appName]\compiled :: masterDir the folder with the last updated version SET masterDir=%hostDir\Deployment\%[appName]\wwwroot :: get the version from the main assembly in the source SET TopasVersion=%@VERINFO[%[sourceDir]\bin\TweeNet.Topas.Business.dll] iff "%@files[%[sourceDir]\bin\TweeNet.Topas.Business.dll]" == "0" then @ECHO No Version information found at ^n%[sourceDir]\bin\TweeNet.Topas.Business.dll EXIT /B 15 endiff :: if we have run this before, we need a new unique deployement directory iff "%@files[%hostDir\Deployment\%[appName]\%[TopasVersion]\]" == "0" then :: first time, deploy directory is simple the name and version SET uniqueToken= else :: Standard named directory is already there, use a different one :: winticks is pretty unique SET uniqueToken=_%_WINTICKS endiff :: use the unique token we got above to build the name SET DeployDir=%hostDir\Deployment\%[appName]\%[TopasVersion]%uniqueToken :: the zip file to use, u- stands for update, f- would be full :: we use the same unique token as above SET zipFile=u-%[appName]-vs%[TopasVersion]%[uniqueToken].zip :: the batch file we create and use to copy the files to be updated SET batchFile=%hostDir\Deployment\%[appName]\TopasSync_vs%[TopasVersion].cmd :: temp file to store list of files to be copied SET tempFile=%TEMP\%[appName]-vs%[TopasVersion].temp :: temp file to redirect robocopy output into SET tempFileSuppressOutput=%TEMP\%[appName]-vs%[TopasVersion]_output.temp :: display some info @ECHO ^n%scriptName Version %scriptVersion^n @ECHO Create Deployment package for %[appName] %TopasVersion @ECHO Source: %sourceDir @ECHO Target: %DeployDir @ECHO Master: %masterDir :: get all files that are relevant and different, we use the fabulous robocopy to do this: :: the /N* switches suppresses output. :: the /L switch means we don't actually copy anything, just get a list of the files. :: we only copy files that we really want on the live server, so loads of filters robocopy.exe %[sourceDir] %[masterDir] /S /R:3 /W:10 /XX /L /NC /NS /NJH /NJS /NP /NDL *.aspx *.asmx *.ascx *.asax *.gif *dll *.jpg *.xml *.xsl? *.js *.ico *.css *.master *.skin /XD old obj topas /XF nunit.framework.dll TweeNet.*.xml TweeNet.Topas.UnitTests.dll Microsoft.Web.Atlas.dll WebDev.WebHost.dll app.css get_aspx_ver.aspx /Log:%tempFile >%[tempFileSuppressOutput] :: we also want some *.exe files in the root :: robocopy.exe %[sourceDir] %masterDir /R:3 /W:10 /XX /L /NC /NS /NJH /NJS /NP /NDL *.exe /Log+:%tempFile >>%[tempFileSuppressOutput] :: count the number of lines, which represents the number of files. :: if we have files in both directory, we get one extra blank line SET numberOfNewFiles=%@LINES[%tempFile] :: we now have a file with the list of all files to be copied :: built content of the new batch file @ECHO :: Topas Deployment Batch File >%batchFile @ECHO :: created by %scriptName Version %scriptVersion >>%batchFile @ECHO :: created: %_ISODATE >>%batchFile @ECHO :: Topas Version: %TopasVersion >>%batchFile @ECHO :: Application: %[appName] >>%batchFile ^n @ECHO :: Files to be copied: ^n >>%batchFile :: go through every line of the file and just output the filename for /f %a in (@%tempFile) echo :: %@replace[%sourceDir,, %a] >>%batchFile :: the copy command expects existing directories, robocopy can create them :: but needs directory as input, not files, so we have to remove the file names :: which is a bit messy with 4NT commands. dummy.xyz is the file to copy, :: it never exist, so robocopy just creates directories for us. @ECHO ^n::Create directory structure with robocopy:>>%batchFile for /f %a in (@%tempFile) echo robocopy.exe %@replace[%@filename[%a],,%a] ^t %@replace[%@filename[%@replace[%sourceDir,%DeployDir, %a]],,%@replace[%sourceDir,%DeployDir, %a]] dummy.xyz /NC /NS /NJH /NJS /NP /NDL >>%batchFile :: this creates the actual copy commands @ECHO ^n::Copy the files to deployment >>%batchFile for /f %a in (@%tempFile) echo copy /Q %a ^t %@replace[%sourceDir,%DeployDir, %a] >>%batchFile :: we also want to copy the files to the master directory to bring it up to date. :: first directories, which may not be there @ECHO ^n::Create directory structure with robocopy:>>%batchFile for /f %a in (@%tempFile) echo robocopy.exe %@replace[%@filename[%a],,%a] ^t %@replace[%@filename[%@replace[%sourceDir,%masterDir, %a]],,%@replace[%sourceDir,%masterDir, %a]] dummy.xyz /NC /NS /NJH /NJS /NP /NDL >>%batchFile :: and the files themselves @ECHO ^n::Copy the files to master>>%batchFile for /f %a in (@%tempFile) echo copy /Q %a ^t %@replace[%sourceDir,%masterDir, %a] >>%batchFile :: delete the temp file DEL /Q %tempFile :: execute the sync batchfile we just created :: this does all the copying, we don't want any output CALL %batchFile >>%[tempFileSuppressOutput] :: delete the temp file for outpur suppression DEL /Q %[tempFileSuppressOutput] :: check whether there were some new files :: if not the Deploy directory has not been created iff "%@files[%DeployDir]" == "0" then :: delete the empty batch file DEL /Q %batchFile :: tell the user about this @ECHO ^nNo new files to be deployed else :: we have some new files :: now zip the target :: move into the directory above the content, :: this way we get a nice structure in the zip file. pushd %DeployDir :: create a new zip file zip -r -q %zipFile *.* :: copy the zip file to the news folder for easy upload copy /Q %zipFile %tnNewsDir :: move the batch file into the folder move /Q %batchFile %DeployDir :: back to our previous location popd :: tell the user we're done @ECHO ^N %numberOfNewFiles new files found @ECHO ^N Deployment package ready for upload at^N %tnNewsDir\%zipFile endiff ENDLOCAL