Variable Substitution in OctopusDeploy - octopus-deploy

I'm trying to substitute a #{port} variable during by deployment using OctopusDeploy. I am able to do it if I directly add it in my App.Config as follows:
<services>
<service name="SampleService" behaviorConfiguration="ServiceBehaviour">
<host>
<baseAddresses>
<add baseAddress="http://localhost:#{port}/SampleService/" />
</baseAddresses>
</host>
..
</service>
Is there a way I can add a default port here (say 25555, so that developers can use it without replacing #{port} always), but still specify to OctopusDeploy that this particular port has to be modified?
(I'm using multi-tenant deployments)

You can use the XML transform feature of config files in conjunction with Octopus deploy.
Octopus itself can run these transforms. When you compile your code, the app.config is renamed [applicationName].exe.config If you create a file called [applicationName].exe.release.config and add this to your package then you can use the same style of transform, that you would normally use in web.config transforms, in that file. If Octopus sees a file called *.release.config it will run any transforms during the deployment process.
Alternatively This blog contains instructions of extending your project so you can use the transform ability of web.config files in app.config files.
So you could do this several ways. Have the hard coded value in you "base" app.config then have the Octopus Variable as a transform in the app.release.config. When you build and package your code, so long as you use the "release" configuration the transform will fire and the hard coded value will be replaced by your build and added to the package.
Or you could do it the other way round, have the hard coded value in the app.debug.config and the Octopus Variable in the app.config. When a developer runs a debug build the Variable would be replaced by the value in the debug.config.

Octopus supports if and unless in variable substitutions (docs: http://docs.octopusdeploy.com/display/OD/Variable+Substitution+Syntax)
That means you could apply a default with relative ease.
Firstly, if the variable exists, use it:
#{if port}#{port}#{/if}
Secondly, if the variable does not exists, use your default:
#{unless port}25555#{/unless}
So just combine them together:
#{if port}#{port}#{/if}#{unless port}25555#{/unless}

While you create a variable using Project variable template , you have the option to provide a default value.
Check the image in the link - Shows Variable template with default value
Unless overridden the default value is substituted while deployed.
So in this case you could create a #{port} variable add the reference variable to project variable template with default value of 25555, which unless overridden will always substitute to 25555 while deployments .

Related

How to get the same file path in the different build version

I have a file in different path between develop and production version, how to keep the same when i want to test them?
// In develop version, file in
~/project/assets/file
// In production version, file in
/service/assets/file
I like using a flag library like alecthomas/kingpin, which allows you to set a parameter like:
env := ""
appk.Flag("env", "Environnement (dev, qual, pprd or prod)").Envar("HOST_ENV").Short('e').Required().EnumVar(&env, "dev", "rct", "pprd", "prod")
Not only will you pass an environment name which is always correct (one of the four values "dev", "rct", "pprd", "prod"), but you can also not pass it directly, and it would still be detected through the system environment variable name "HOST_ENV"
You could also pass/set directly a file path/name.
But the idea remains: you can chose, with this library, between:
a config file
a parameter
an environment variable

Setting environment variable with WiX, losing filename at end of path

I am utilizing the WiX "Environment" element to set an environment variable.
<Environment Action="create" Name="My_Certificates" System="yes" Id="certificate_env" Value="[CERT_XML_PATH]" Permanent="yes" />
CERT_XML_PATH gets set to the following: c:\this\is\my\path\myfile.xml
If that file actually exists, the environment variable is set properly and points to the file.
If that particular file doesn't exist at the time of the install, the environment variable is created and set, but the path is truncated to remove the myfile.xml portion.
This installer is part of a larger suite. It has its own standalone MSI. Installing one of the packages creates this xml file, but not the particular one I'm working on. If the xml file has been created, everything works fine. But if this installer is ran first, it will set the environment variable to
The environment variable "My_Certificates" gets created, but set to:
c:\this\is\my\path\
Instead of:
c:\this\is\my\path\myfile.xml
Troubleshooting:
The install log shows the property being set to the proper location.
MSI (s) (70:80) [15:27:44:988]: PROPERTY CHANGE: Adding CERT_XML_PATH property. Its value is 'c:\this\is\my\path\myfile.xml'.
Then we see the installer doing a WriteEnvironmentStrings a utilizing the proper path, including the xml file.
MSI (s) (70:80) [15:27:50:644]: Executing op: UpdateEnvironmentStrings(Name=My_Certificates,Value=c:\this\is\my\path\myfile.xml,Delimiter=[~],Action=536870914,)
WriteEnvironmentStrings: Name: My_Certificates, Value: c:\this\is\my\path\myfile.xml, Action 536870914
screen shot of system variables showing it not set properly
Any insight into why the file name is being truncated would be greatly appreciated.
Found the issue. There is a custom action in the installer that is also trying to set the Environment Variable. When disabling that custom action, everything works as intended.

TeamCity AutoIncrementer Plugin Modify Build Number

I'm using the TeamCity AutoIncrementer Plugin: https://confluence.jetbrains.com/display/TW/Autoincrementer
I'm attempting to modify the config file at: [TeamCity Data Directory]/config/autoincrementer.properties
Within the config file it says:
# Autoincrementer plugin (pre 6.5 EAP).
#
# This file contains values for auto-incremented properties.
# Each auto-incremented property must start with 'autoinc_Test.' prefix.
# Value of the property must be a positive integer.
# To use auto-incremented property in a build you can add a reference to the property
# in build configuration settings, e.g: %autoinc_Test.build.number%. When plugin discovers such reference
# initial value for the referenced property will be written in this file.
# You can change properties values in this file manually at any time.
# Note that if you want to set a property to some value you need to put ! character
# before the value (after the equals sign).
My values look like:
autoinc.foo=1683367\:25
autoinc.bar=-1\:10
I believe the first number is the changelist from Perforce.
I want to change the last number for foo from 25 to 200 (this is the build number).
I've tried putting the exclamation character at autoinc.foo=!1683367:200 and at autoinc.foo=1683367:!200. In both cases TeamCity will overwrite this file and change it back to 26 on the next build.
1) How do I change the build number? Does this require a TeamCity restart?
2) How is this working at all if each autoinc requires 'autoinc_Test.' prefix?
I am using TeamCity 8.0.6.
The version we are using works by adding a ! after the = sign as per the comments in the file (e.g. autoinc.globalbuildnum=!1).
I noticed that the comments in the version we are using are different to those you have posted. We are using the plugin that is compatible with TeamCity 8.x+ which is currently at the link you posted. It was updated on 7th April 2015

Conditional compilation in database project [duplicate]

I am using a SQL 2008 database project (in visual studio) to manage the schema and initial test data for my project. The atabase project uses a post deployment which includes a number of other scripts using SQLCMD's ":r " syntax.
I would like to be able to conditionally include certain files based on a SQLCMD variable. This will allow me to run the project several times with our nightly build to setup various version of the database with different configurations of the data (for a multi-tenant system).
I have tried the following:
IF ('$(ConfigSetting)' = 'Configuration1')
BEGIN
print 'inserting specific configuration'
:r .\Configuration1\Data.sql
END
ELSE
BEGIN
print 'inserting generic data'
:r .\GenericConfiguration\Data.sql
END
But I get a compilation error:
SQL01260: A fatal parser error occurred: Script.PostDeployment.sql
Has anyone seen this error or managed to configure their postdeployment script to be flexible in this way? Or am I going about this in the wrong way completely?
Thanks,
Rob
P.S. I've also tried changing this around so that the path to the file is a variable, similar to this post. But this gives me an error saying that the path is incorrect.
UPDATE
I've now discovered that the if/else syntax above doesn't work for me because some of my linked scripts require a GO statement. Essentially the :r just imports the scripts inline, so this becomes invalid sytax.
If you need a GO statement in the linked scripts (as I do) then there isn't any easy way around this, I ended up creating several post deployment scripts and then changing my project to overwrite the main post depeployment script at build time depending on the build configuration. This is now doing what I need, but it seems like there should be an easier way!
For anyone needing the same thing - I found this post useful
So in my project I have the following post deployment files:
Script.PostDeployment.sql (empty file which will be replaced)
Default.Script.PostDeployment.sql (links to scripts needed for standard data config)
Configuration1.Script.PostDeployment.sql (links to scripts needed for a specific data config)
I then added the following to the end of the project file (right click to unload and then right click edit):
<Target Name="BeforeBuild">
<Message Text="Copy files task running for configuration: $(Configuration)" Importance="high" />
<Copy Condition=" '$(Configuration)' == 'Release' " SourceFiles="Scripts\Post-Deployment\Default.Script.PostDeployment.sql" DestinationFiles="Scripts\Post-Deployment\Script.PostDeployment.sql" OverwriteReadOnlyFiles="true" />
<Copy Condition=" '$(Configuration)' == 'Debug' " SourceFiles="Scripts\Post-Deployment\Default.Script.PostDeployment.sql" DestinationFiles="Scripts\Post-Deployment\Script.PostDeployment.sql" OverwriteReadOnlyFiles="true" />
<Copy Condition=" '$(Configuration)' == 'Configuration1' " SourceFiles="Scripts\Post-Deployment\Configuration1.Script.PostDeployment.sql" DestinationFiles="Scripts\Post-Deployment\Script.PostDeployment.sql" OverwriteReadOnlyFiles="true" />
</Target>
Finally, you will need to setup matching build configurations in the solution.
Also, for anyone trying other work arounds, I also tried the following without any luck:
Creating a post build event to copy the files instead of having to hack the project file XML. i couldn't get this to work because I couldn't form the correct path to the post deployment script file. This connect issue describes the problem
Using variables for the script path to pass to the :r command. But I came across several errors with this approach.
I managed to work around the problem using the noexec method.
So, instead of this:
IF ('$(ConfigSetting)' = 'Configuration1')
BEGIN
print 'inserting specific configuration'
:r .\Configuration1\Data.sql
END
I reversed the conditional and set NOEXEC ON to skip over the imported statement(s) thusly:
IF ('$(ConfigSetting)' <> 'Configuration1')
SET NOEXEC ON
:r .\Configuration1\Data.sql
SET NOEXEC OFF
Make sure you turn it back off if you want to execute any subsequent statements.
Here's how I am handling conditional deployment within the post deployment process to deploy test data for the Debug but not Release configuration.
First, in solution explorer, open the project properties folder, and right-click to add a new SqlCmd.variables file.
Name the file Debug.sqlcmdvars.
Within the file, add your custom variables, and then add a final variable called $(BuildConfiguration), and set the value to Debug.
Repeat the process to create a Release.sqlcmdvars, setting the $(BuildConfiguration) to Release.
Now, configure your configurations:
Open up the project properties page to the Deploy tab.
On the top dropdown, set the configuration to be Debug.
On the bottom dropdown, (Sql command variables), set the file to Properties\Debug.sqlcmdvars.
Repeat for Release as:
On the top dropdown, set the configuration to be Release.
On the bottom dropdown, (Sql command variables), set the file to Properties\Release.sqlcmdvars.
Now, within your Script.PostDeployment.sql file, you can specify conditional logic such as:
IF 'Debug' = '$(BuildConfiguration)'
BEGIN
PRINT '***** Creating Test Data for Debug configuration *****';
:r .\TestData\TestData.sql
END
In solution explorer, right click on the top level solution and open Configuration Manager. You can specify which configuration is active for your build.
You can also specify the configuration on the MSBUILD.EXE command line.
There you go- now your developer builds have test data, but not your release build!
As Rob worked out, GO statements aren't allowed in the linked SQL scripts as this would nest it within the BEGIN/END statements.
However, I have a different solution to his - if possible, remove any GO statements from the referenced scripts, and put a single one after the END statement:
IF '$(DeployTestData)' = 'True'
BEGIN
:r .\TestData\Data.sql
END
GO -- moved from Data.sql
Note that I've also created a new variable in my sqlcmdvars file called $(DeployTestData) which allows me to turn on/off test script deployment.
I found a hack from an MSDN blog which worked fairly well. The trick is to write the commands to a temp script file and then execute that script instead. Basically the equivalent of dynamic SQL for SQLCMD.
-- Helper newline variable
:setvar CRLF "CHAR(13) + CHAR(10)"
GO
-- Redirect output to the TempScript.sql file
:OUT $(TEMP)\TempScript.sql
IF ('$(ConfigSetting)' = 'Configuration1')
BEGIN
PRINT 'print ''inserting specific configuration'';' + $(CRLF)
PRINT ':r .\Configuration1\Data.sql' + $(CRLF)
END
ELSE
BEGIN
PRINT 'print ''inserting generic data'';' + $(CRLF)
PRINT ':r .\GenericConfiguration\Data.sql' + $(CRLF)
END
GO
-- Change output to stdout
:OUT stdout
-- Now execute the generated script
:r $(TEMP)\TempScript.sql
GO
The TempScript.sql file will then contain either:
print 'inserting specific configuration';
:r .\Configuration1\Data.sql
or
print 'inserting generic data';
:r .\GenericConfiguration\Data.sql
depending on the value of $(ConfigSetting) and there will be no problems with GO statements etc. when it is executed.
I was inspired by Rob Bird's solution. However, I am simply using the Build Events to replace the post deployment scripts based on the selected build configuration.
I have one empty "dummy" post deployment script.
I set up a pre-build event to replace this "dummy" file based on the selected build configuration (see attached picture).
I set up a post-build event to place the "dummy" file back after the build has finished (see attached picture). The reason is that I do not want to generate changes in the change control after the build.

How to pass an integration property to a batch file with CruiseControlNet?

In the build log of my project, i can see these properties:
<integrationProperties>
<CCNetProject>Gdet_T</CCNetProject>
...
<LastModificationDate>4/6/2010 1:29:04 PM</LastModificationDate>
<LastChangeNumber>10841</LastChangeNumber>
</integrationProperties>
I want to pass the property CCNetProject and LastChangeNumber to a batch file. it works well with CCNetProject, as it can be used in the batch as an environment variable %CCNetProject%.
But it doesn't work with other properties (those are not starting with the CCnet prefix) as LastChangeNumber or LastModificationDate.
I tried to pass it as argument, but it fails !
<exec>
<executable>$(WorkingFolderBase)\MyBatch.bat</executable>
<baseDirectory>$(WorkingFolderBase)\</baseDirectory>
<buildArgs>$(LastModificationDate)</buildArgs>
</exec>
I tried to pass it as environment variable, but it fails:
<exec>
<executable>$(WorkingFolderBase)\MyBatch.bat</executable>
<baseDirectory>$(WorkingFolderBase)\</baseDirectory>
<environment>
<variable>
<name>svn_label</name>
<value>"${LastModificationDate}"</value>
</variable>
</environment>
</exec>
The results is always the same when I display the parameter or variable : empty string or the variable name $(svn_label)
I'm sure it is simple, but ... I can't find ! Any idea ?
CCNET passes the following parameters to external programs:
CCNetArtifactDirectory
CCNetBuildCondition
CCNetBuildDate
CCNetBuildTime
CCNetFailureUsers
CCNetIntegrationStatus
CCNetLabel
CCNetLastIntegrationStatus
CCNetListenerFile
CCNetModifyingUsers
CCNetNumericLabel
CCNetProject
CCNetProjectUrl
CCNetRequestSource
CCNetUser
CCNetWorkingDirectory
As you can see LastIntegrationStatus e.g. is available through CCNetLastIntegrationStatus but LastModificationDate e.g. has no equivalent.
You can pass additional arguments via <buildArgs> or <environment> but inside CCNET configuration you have no access on the integration properties mentioned above. Most people starting with CCNET (including myself) try something like <buildArgs>$(CCNetProject)</buildArgs> and fail.
Have a look on my answer to a similar question.
Sorry I can't provide a better solution.
Update (regarding Thinker's suggestion):
Using $[$CCNetLabel] inside CCNET configuration does not seem to work.
Frankly spoken, I would have been rather surprised, if it had. The configuration is something static whereas CCNetLabel is something dynamic, that potentially changes with every integration build. Assuming you have access to these dynamic properties inside the configuration, the configuration might change with every build. Since changing the configuration means restarting the CCNET server automatically, you would cause a server restart with every build. Not actually a desirable behavior, is it?
ok, found the solution.
Need to use a specific label called SvnRevisionLabeller to retrieve the svn revision.
it is then available via the CCNetLabel environement variable.
http://code.google.com/p/svnrevisionlabeller/
<labeller type="svnRevisionLabeller">
<url>http://mysvnrootproject/trunk</url>
</labeller>

Resources