I have a Windows.Forms client that is packaged as MSI by using a Visual Studio Deployment Project.
My app.config has some configuration properties whose values must be replaced by the correct ones depending on the environment I want to deploy the client into.
For instance, my client do connect to a set of COM+ services that are behind a WCF facade, so I have some URLs in the configuration file that vary depending on the environment (development, integration, etc etc).
In such way one can find in the application config line like this:
<add ServiceName="MyService" ServiceEndpoint="$(WS_URL)/MyService.svc" MaxMessageSize="xxxxxx"></add>
Well, the thing is that I need to execute a batch file (.bat) to load some variables and then replace the variables in the app.config, but, I need to do the replacements in the application configuration file that is being packaged in the MSI, and not perform the replacement in the "original" configuration file that is in the client project.
In fact what I would need is just to get the path, of the application configuration file, that VS is about to package, and pass the file-path to my script so that it does the replacements.
NOTE: In the above sample line $(WS_URL) is the variable that must be replaced by its correct value depending on the deployment environment configuration.
I would create 2 copies of your App.Config in source control and associate one with your project for F5 builds / dev debugging and another one that gets consumed by the installer for production use.
Finally I have the solution that fit our needs. My need was mainly to just have one unique configuration file for the Windows.Forms client and being able to replace some property values in it, just before the file is packaged in the MSI.
I thought the binaries that are packaged within the MSI were taken from the project(s) output dir., but they are not, so I couldn't use that location to execute our "variable replacer" script and get the final App.config file ready to be packaged.
We a have a .bat file, one for each kind of environment configuration, in which we centralize the configuration properties applying for Windows.Forms, Web-Applications, COM+ Serviced Components, so I was disgusted with the idea of replicating the configuration file of the client, because my aim is also to reduce the number of configuration files, to reduce the risk of making a mistake, while keeping the process of creating/maintaining build environments easy.
I couldn't find any doc to tell me how could I get the path, of the temporal folder or whatever, where the files are, just before the MSI is being packaged. If I would have access to it, then I would have executed our "variable replacer" script pointing to that directory and problem solved.
My workaround was to place a customized pre-build event, so that the I copy the "template" App.config to the project directory, and once it is there, I run the script to replace the property values.
Now, every time the deployment project is built, we are sure it contains a valid configuration file.
Just FYI, here is how the pre-build event looks like:
XCOPY /Y $(ProjectDir)config\*.* $(ProjectDir)
CALL ..\ScriptsCentral\do_apply_config.bat $(ProjectDir)App.config etc
EXIT 0
NOTE: I had to put the EXIT 0 explicitly because we build all the projects with msbuild and devenv.com and I got the build process stopped in the part where it executes the build event.
(thanks #ChristopherPainter for your time and comments. +1 for your proposal)
Related
I'm part of a large developmentteam with a big project that is built with TFS 2013. We have gotten the build to work with automatic tests and web transformations as well as deployment to correct folders. The last part we need is a way to copy additional files to the drop location with regards to different environment.
We have a folder in the solution that contains several deployment files for different environments. We build for several environment with each build.
The folder looks like the following:
A folder named contains several powershell scriptfiles
(Deploy.ps1, RunDeploy.ps1, StartService.ps1)
The first file should be copied to the root of the drop folder location for each configuration/environment.
The last two files should be copied to a new folder named Deploy under each configuration in the drop folder.
Additional to this we have several settings files in the same sourcefolder. One file for each environment named settings-.txt
These files should be copied to the Deploy folder for the correct configuration under the drop location.
We are using TFS 2013 so preferable using a custom workflow but we can use a target-file if needed.
Any idea how this can be created?
Where should I start?
I have been unable to locate a variable in a custom task in the build process that contains the location of the dropfolder for each configuration.
I managed to create a custom task in the build template after some searching for the variables I finally could create a custom task that created the correct folders and located the files that needed to be brought along in the build.
To find the variables I used the common task GetEnvironmentVariable with the specified variables. To see what each variable contained I added a print line just afterwards and tried the build and then when I had found the needed sources of information the task to create a custom build task was fairly easy.
I'm working in a solution which includes a windows service host project that uses a single app.config file which contains everything the service needs (logging configuration, WCF configuration, custom configuration, and, connection strings).
The way I actually work is when a user complains about something wrong in the production environnement, I edit the app.config file, modify the connection string so it points to the production database, recompile the service host project, then run the application in my dev environnment to see what's going on.
If I have to test things that would be too risky for the production environment, I edit again the app.config file, modify the app.config file so it points to the test database, recompile the service host project, ... you see where I'm going ...
To avoid the burden of editing the app.config file everytime I have to switch environments, I decided to create a "Production" build configuration and a "Test" build configuration. I added two additionnal config files, one for each environment, which are replicas of the main app.config file except that their connection string points to their respective database. I modified the pre-build event of the project to include code that copies the environment's app.config file depending of the selected build configuration.
I have two concerns about that method :
If I have anything else to modify in the app.config file (WCF, logging, etc.) I have to remember to replicate my modification in the other file. (Big problem for me as I have as much memory as a red fish)
I hate making the project more complex to work with because of the additional build events, additional files in the project directory, etc.
Each build configuration outputs in a different directory. I hate having duplicates of the same code just to overcome that data source issue.
Anybody can suggest a simpler way to work with multiple environments ?
Thanks in advance.
Our code uses different settings for development and production environments so we were looking at using VS2010's web.config transform capabilities. After hours of trial and error, nothing has worked. We found a Web.config transformation tester and found that what we had been trying was supposed to work (according to this tool.)
We tried testing the transformations using Build, not Publish. Does it only run on Publish or could something else be wrong?
Yes, it only runs on Publish. To test, publish to a local dir. You'll want to publish your application with the correct target environment set.
You can also integrates web config transformations with MS Build.
It seems you don't have to be in Publish mode to generate a transformed Web.config file. There's just a bit more work involved.
Open the Visual Studio Command Prompt and navigate to your working project directory that contains your .csproj file. Enter the following command:
MSBuild project.csproj /t:TransformWebConfig /p:Configuration=Debug
The example above would run the Debug transformation during the build of project.csproj. This will output a Web.config file into the obj\Debug\TransformWebConfig\transformed\ directory, where Debug is whatever Configuration you set in the command above.
Copy this file to replace your root Web.config file, and you're done. You could write a batch script to run both of those items automatically, but for larger projects with many configurations it could become unwieldy.
You might be able to add those command line arguments to the build process inside of Visual Studio, but I'm not sure how - as far as I know for this method to work you would have to build from the command line instead of inside Visual Studio. You can still use Clean inside of Visual Studio to clean out the obj folder but it will only clean the solution configuration mode selected in the IDE. Clean will not revert your edited web.config file, so you may want to back it up before proceeding if you need to.
(Command line arguments found from this MSDN article.)
You don't need run Publish/Build Package in order to test Web.config transformation. There is a cool trick to quickly know the transformation result here. Scroll down until you see a comment about creating TransformConfig project. It works like a charm, note that you can safely ignore 7th step (frankly I don't know how to do that step properly but fortunately we don't need it :)).
We have different dev environments between developers here. When I build, I want my compiled files to be copied to a bin folder located in C:\Web\bin\. Another developer may want those files dropped in C:\Web_2011\bin\.
Using Visual Studio 2010, the way we work this now is to run a BAT file with the directories defined as parameters that need to be changed if pulling from another developer's branch.
Is it possible to store a solution-wide parameter, (in a .user or .suo file maybe,) to define where a developer wants to drop his builds?
You could do it through the project file (.vcxproj for C++ project for example).
The simplest solution would be to add a Custom Build Step that runs some batch file. This batch file could check the current user name and copy the files based on that.
(An even simpler solution would be to run a user specific batch file from his local disk)
If you really want the fully fledged solution that will allow you to save this data to the user file, you can do it by editing the project file and adding a PropertyPageSchema element that extends VS property pages with another parameter (your destination directory). You can define the Persistence attribute of DataSource element as "UserFile" and the data will be saved on your .user file. You will need to add some target that actually uses this data (copies files to the directory specified).
For more information, read about msbuild and PropertyPageSchema.
Greetings,
I'm trying to figure out how to pull off the following scenario with MSBuild and Visual Studio 2010.
I have a set of three services that I would like to install. The default installation directory should vary with the build (qa, uat, and production).
To add another fun wrinkle to this whole thing, sometimes the uat environment can be pressed into service when we are at peak load, so each build of the service will need to have a different name. It doesn't happen frequently, but it is on the list. How can I configure the service installers to alter the service name dynamically?
I want to be able to create MSI installers for the services (for whatever the current build is). I have an existing and extensive MSBuild script for the various websites I'm working with already, but I'm a little unsure how to proceed with making the services work.
Obviously, the configuration files for each service build will be different.
I've added installer classes for each of the services.
I guess I'm a little confused with how to start this, so any help I can get would be awesome. I had considered simply hardcoding the different service names and using conditional compilation statements to set them, but I don't think doing so is a particularly clear way to go about it all. Any thoughts?
It might be simpler to just zip up the service bits during the build and deploy with MSBuild using or MSBuild Extension tasks. You would put your environment specific configuration data in an msbuild .properties file (mylocal.service.properites, qp.service.properties, uat.service.properties, etc.). This is the way I deploy services.
note: the property file would contain things like your db connection string, TargetDir, ServiceName, etc.
Service names are specified at install time see 'sc', 'installutil' or the WindowsService msbuild extensions pack task snippet below. This means you can copy the same service bits everal directories and install each with a unique name (e.g. QAService, UATService, PRODService).
note: I want to reinforce that the service name is a deploy-time consideration, not a build-time consideration.
<WindowsService TaskAction="Install"
ServiceName="$(ServiceName)"
MachineName="$(TargetServer)"
ServicePath="$(FullServicePath)"
User="$(User)" />
The approach is similar for MSI installers. I assume your installers prompt for all necessary environment specific configuration data... All [decent] installers have a way to provide answers from a file versus using the installer interactively. So, as above, you create one answer file per environment and feed it to the installer on the command-line.
You do not want to do this at build time... and thereby have a separate installer per platform. Was forced to do this with an ancient version of the wyse installer. Made me sad. You want a single MSI installer that can be run in every environment (given the environment specific answer file).
Details of the MSI command-line and answer file format will vary by product. What installer package are you using?
Cheers,
/jhd