Override a duplicate Task in NAnt? - nant

For better or worse, I've built my own msbuild task. I've given it the name 'msbuild' and it's colliding with one from NAntContrib.
Assuming, I can't remove the one from NAntContrib, is there a way to override the contrib version while keeping the same task name?
Or perhaps another task could be written that can alias an already loaded task to be named something else?
Or I can probably change my task name.
Any info would be appreciated.

I have not seen a way you can overload task names. It sounds like the easiest thing to do would be to change the name of your tasks to something that is not msbuild.

A bit of a hack I found that seem to work is basically giving your task a namespace prefix.
[TaskName("zz:msbuild")]
public class MsbuildTask : ExternalProgramBase
{
}
Then in your nant build file:
<project default="Build" name="TestBuild"
xmlns="http://nant.sf.net/schemas/nant-0.85.win32.net-1.0.xsd"
xmlns:zz="http://nant.sf.net/schemas/nant-0.85.win32.net-1.0.xsd">
<target name="Build">
<zz:msbuild target="Build" project="TestBuild.sln"
verbosity="${msbuild.verbosity}">
<property name="Configuration" value="${build.configuration}" />
</zz:msbuild>
</target>
</project>
The only thing I don't like is the fact that I have to embed the prefix in the TaskNameAttribute. This is necessary because internally NAnt uses the string "zz:msbuild" to hash the task and looks up based on XmlNode.Name property.
Also notice that the prefix is pointing to the default namespace. This is because NAnt discards nodes with namespaces other than the default NAnt namespace. I can kind of understand why they did that but I don't know that it's absolutely necessary.
Looking at the NAnt source, it seems very much feasible to make the adjustments so that I don't have to embed the prefix and to give the task a different namespace.
The alternative to this approach, the one that I've been using previously, was to use task name like "zz.msbuild". But that didn't really seem correct. I remember reading somewhere having a period in element names is not recommended. But more than that, it didn't look good :P

Related

Secrets as environmental variables in vstest

In my tests I'm using environmental variable. Due to security reasons, when setting it in azure devops pipeline I need to mark it as secret. Is it possible to pass it as argument, within vstest task?
Apparently lacking enough reputation to comment on the answer by #daniel-mann, I need to follow-up through an answer myself.
Regarding the use of a runsettings file;
Yes, I'm sure you can do it like this, but there's a much simpler way. In the task definition, you have the option to override test run parameters.
For a non-YAML pipeline, you do this in the "Override test run parameters" option (the tooltip says "Override parameters defined in the TestRunParameters section of runsettings file or Properties section of testsettings file. For example: -key1 value1 -key2 value2."), so like this then:
-SomeSecret $(SomeSecret)
For a YAML-pipeline, you can do this:
overrideTestrunParameters: '-SomeSecret $(SomeSecret)'
The nice thing is that the test run parameter that you'd like to override DOES NOT need to exist in your runsettings file. In fact, there's not even need for a runsettings file at all!
The bad thing about this approach in general is that you'll have to access your secrets through the "TestContext.Properties" collection (for NUnit), which really sucks if you want a transparent solution that works equally well both for local development (using "user secrets") and in an Azure pipeline.
Another potentially bad thing about this is that these "overridden" test run parameters are just that - parameters for THAT test run only. If you happen to have a mixture of .NET Framework and .NET Core tests, you would want to execute those in two different test runs (for it to work at all...), and then you'd need to duplicate those overrides (given that some of the same secrets are needed, that is).
Regarding adding an additional task in your pipeline to set the appropriate environment variables;
Absolutely. Using the "Batch script" task is well suited for this, where you just pass your secret-based variables as parameters to the script and pick them up inside the script file.
For this to work as expected, though, you will need to allow the task to modify the environment.
For a non-YAML pipeline, this is done by ticking off the "Modify Environment" checkbox.
For a YAML pipeline, you could do it like this:
- task: BatchScript#1
displayName: 'Export key vault vars as env. vars (for tests)'
inputs:
filename: 'ExportKeyVaultEnvironmentVariables.cmd'
arguments: '$(SomeSecret)'
modifyEnvironment: true
Where in the "ExportKeyVaultEnvironmentVariables.cmd" script, you just do this:
set SomeSecret=%1
Note: If your secret by chance has some funny characters, especially having a trailing "=" character, you might experience that what you get when collecting the parameter inside the script is NOT what you sent in.
You can avoid this problem by enclosing the parameters in double quotes, like this:
arguments: '"$(SomeSecret)"'
And then collect the parameter by removing those surrounding quotes by using the "~" parameter modifier, like this:
set SomeSecret=%~1
A nice bonus effect of this approach is that your shiny new full-blown environment variables persist for the remainder of your pipeline. Referencing back to my "bad thing" about having to potentially duplicate the test run parameter overrides, that would not be needed here.
Regarding the additional option mentioned by the OP;
Absolutely, in which case you wouldn't need a "Batch script" task (that needs to call a script file), but just a "Command line" task.
BUT, be aware of a possible gotcha! Yes, this will create an environment variable for your test code to pick up, if you access it through "Environment.GetEnvironmentVariable".
In my case, I was building an "IConfigurationRoot" instance, like this:
/// <summary>
/// Gets a configuration instance, based on user secrets (for local test execution) and environment variables (for Azure pipeline execution).
/// </summary>
/// <typeparam name="T">The type of the class that represents the "runtime" (and thus is able to get hold of any configuration).</typeparam>
/// <returns>An <see cref="IConfigurationRoot"/> instance.</returns>
public static IConfigurationRoot GetConfigurationRoot<T>()
where T : class =>
new ConfigurationBuilder()
//// Note: The "AddUserSecrets" method requires the "Microsoft.Extensions.Configuration.UserSecrets" package
.AddUserSecrets<T>()
//// Note: The "AddEnvironmentVariables" method requires the "Microsoft.Extensions.Configuration.EnvironmentVariables" package
.AddEnvironmentVariables()
.Build();
This works by adding various "configuration providers", which eventually allows you to access them all seamlessly through "configuration["SomeSecret"]".
What I found, though, if I'm not seriously mistaken, is that "SomeSecret" was still not available in the "EnvironmentVariablesConfigurationProvider" that's added, even though I could perfectly fine access it directly with the above mentioned method. Go figure (but I might be mistaken...).
Possible alternative approach (but for YAML pipelines only?);
It seems that for a YAML pipeline, you can explicitly set environment variables for a task, like this:
- task: VSTest#2
env:
SomeSecret: $(SomeSecret)
[...]
I haven't tested this myself, but seen a colleague do it (myself, I currently don't have a YAML pipeline). At least this variable can be picked up with "Environment.GetEnvironmentVariable", but I don't know if this can be picked up through an "IConfigurationRoot" instance.
I haven't seen any option to achieve the same in a non-YAML pipeline.
But again, this also suffers from the same possible "having to duplicate the environment variables across several test runs" problem.
See also my solution to my own question over at the Azure DevOps guys
I've been through this. There's no way to pass variables to VSTest on the command line, which means you have to jump through a few hoops.
You have a few options:
Use a runsettings file with a TestRunParameters section, then access it via TestContext.Properties["variableName"] within the tests themselves. You can use standard token replacement patterns to transform the XML file.
Use an app.config or appsettings.json (depending on your platform). This works pretty much the same as above, except, of course, you use the standard configuration classes to retrieve the values.
Add a step to your pipeline that sets the appropriate environment variables. Secrets don't get automatically mapped to environment variables for security purposes, but there's nothing that's stopping you from doing it yourself.
Move the secret values into a keyvault or some other sort of external secret storage and configure the test to pull the secrets at runtime.

Can file time stamps be used to define dependencies in Psake PowerShell makefiles?

From what I have seen Psake domain specific PowerShell scripts do not evaluate if dependent objects really need to be built - instead the dependent objects are always evaluated in order.
Is there a way to implement dependencies so that the script to build a make target, such as a file, is only executed if any of the dependent files are newer than the target file?
I experimented with precondition and post condition, with limited success but this seems like a standard requirement and is in every UNIX style "make" I've used in the past. It feels like I am missing something obvious. Help!
As far as I know, Psake does not have such tools. The similar PowerShell build tool Invoke-Build does. You may try it if "incremental" tasks are important for your build scripts. See its wiki pages
Incremental Tasks
Partial Incremental Tasks

CruiseControl.Net: Run NUnit task with parameters

My NUnit tests fail unless the nunit runner is launched with /noshadow parameter.
But in CC.net, it seems to be impossible to supply this parameter in the <nunit> block.
I know I always can fall back to generic <exec> block, but is there really no way to configure the <nunit> block?
I would surmise that if this switch/flag isn't documented, then it isn't available in the that you mention.
The thing to keep in mind with these custom tasks, is that usually they are just friendly-wrappers for what eventually becomes a command-line call.
The task-author is just making things simpler for you. They take on the onus of creating the correct commandline, and pass that to the original .exe.
Now, it looks like somebody did address the command line of your interest here:
https://github.com/loresoft/msbuildtasks/blob/master/Source/MSBuild.Community.Tasks/NUnit.cs
Note the code:
if (DisableShadowCopy)
{
builder.AppendSwitch(c+"noshadow");
}
So I would see if you can get this task working.
In fact, I barely use any of the built in CC.NET tasks, except for source-code download and starting up msbuild.exe...and then the publishing. I leave the hard stuff to msbuild.
Aka, I pull source-code, which includes a MyBuild.proj file.
Then I have cc.net execute "msbuild.exe MyBuild.proj"
Then I have cc.net do some of the publishing.
Why?
If most of my logic is in a msbuild .proj file, then if I ever switch to another CI tool, the transition is much less traumatic. In fact, I recently learned that an old job of mine went to TFS, and because I wrote most of the build logic in msbuild (and not a lot of cc.net tasks)....the transition to TFS was fairly painless. If I had used cc.net tasks instead......every single one of those would have had to been translated to a corresponding tfs task.... :<
Anyways. Back to your question. Keep in mind...that somebody is basically (via a task) is usually just writing up a nice way to wire up things, and doing the command line arguments/syntax sugar for you. So they sometimes miss a flag, or a flag gets added later, but the original task is not updated.
So you'll either need to modify the source code yourself........ :< Or pick a library that keeps more up to date.
Good luck.

Is it possible to exclude a target from a NAnt build?

I am looking for a way to tell NAnt not to execute a specified target. I've not found anything online so I'm not hopeful but perhaps the good people of SO can help.
The situation is: we have targets A, B, C, D, as well as an overall target T that calls all the others. A is independent of the others so is not really necessary; consider it a sort of an expensive sanity check.
I want to be able to run T and but exclude A - so it would only run B, C, D.
Is this possible, or do I have to declare a new target to specifically run those jobs?
I am thinking in very simple way. You situation might be different.
I would have made T depend on B, C and D only.
When A is also needed I can call it in form of:
NAnt buildfile:Blah.build A T
#NotAgain's answer is useful if the target to be excluded is at the beginning or the end of the chain, but it does involve setting up a new target, or at least editing the existing one.
The best solution I've found so far is to use the conditional logic expressions.
Example:
<property name="runThis" value="true" />
<target name="runIfPropTrue" if="${runThis}">
<exec program="test.bat" />
</target>
This way you can set up any targets you may wish to exclude and switch them on and off by editing the property or by passing it in as a parameter instead.
I assume you can use the unless expression just as easily but I haven't tested it.

How to Edit/Update Nant script

I need to update an Nant script automatically by fetching some data from database. The solution I can think of is to be done through a service which fetches the data from DB and update the Nant script.
Can this be done? If yes, how?
In theory, if you need to change how the script works then you could create a program to generate the NAnt build file, run it with the exec task, include that file and then call a target.
That seems a bit over-complicated though. I suppose it depends on how much the script will change based on the data.
If the data is simply configuration, then you can use the data to set properties in your build script (either by the same mechanism above, or by creating a custom task to create a property value based on the result of a SQL statement). Then use those properties to determine control flow in the build script using standard things like if statements and foreach loops.
I don't think that there's anything built-in that will do this for you, but custom tasks are very easy to create if you can program.
If you update/edit a nant script it does not change the current execution. Instead you can generate .build files and execute them via <nant> task, for example using a <foreach> loop or <style> xsl-transformation. An alternative would be to write a small <script>, in particular if you can program it comfortably in C#. If you wish more specific answers more information would be helpful. (database used, what tools you can use to extract data)