Donnerstag, 13. Juli 2017

TFS 2015 - setting the test run working directory of a Visual Studio Test task

We're moving from ccnet to TFS 2015. So it's the TFS 2015 build system, doing integration and nightly full builds.
Of course there's also tests, which are run by vstest.console.exe via a Visual Studio Test Task.
Unfortunately, many of our tests use resources which are parked outside of the test lib, in some "TestDataFolder". This works all great as long as your current working directory is what you think it is. (side note: I strongly disagree with this. Imho, all test resources should be included in the test lib. If you need too many resources, you're likely doing something wrong.).

Note: It looks like I experienced this only for test assemblies that had used [DeploymentItem(..)] attribute. Without such an attribute, the test dir seems to be the directory where the library is build to.

Anyhow. These builds are run by TFS agents. And those have a working directory, like:
E:\agentX\_work\13\
13 being .. the 13th checked out source branch? Don't know yet.

The sources are in
E:\agentX\_work\13\s\

And the test lib in e.g. E:\agentX\_work\13\s\MySolution\MegaLib.Tests\bin\MegaLib.Tests.dll is executed with vstest.console.exe from the path:
E:\agentX\_work\13\TestResults\<deploy_id_date>\out

That sucks, cause being outside of the source tree, the logic for finding the "TestDataFolder" doesn't work anymore.
It took a while to find out how to set the base directory of each test run.

Solution: use a myTestSettings.runsettings file, and set the ResultsDirectory for the TestResults, which also manipulates the current working directory when the tests are run.
<RunSettings>  
 <RunConfiguration>
  <!-- Path relative to solution directory.
                        It also makes the directory traversal
                        for finding the unittestdata folder work -->  
  <ResultsDirectory>.\TestResults</ResultsDirectory>
 </RunConfiguration>  
</RunSettings>  
The path for myTestSettings.runsettings is relative to the source root directory (for which even a variable exists: $(Build.SourcesDirectory))
Well, there's probably also a way to set the Common.TestResultsDirectory, but I haven't found out yet how to do that via such a VSTest task (from TFS build - predefined variables).

Freitag, 9. Juni 2017

Using shareProcess instead of ownProcess for windows services can yield a very confusing message

When registering a windows service with an msi, using wix, one is usually doing this:
<wix:ServiceInstall Id="MyServiceInstall"
     Name="MyService (Hostfunc1)"
     DisplayName="My Super Service"
     Description="Some Description"
     ErrorControl="ignore" 
     Start="auto"
     Type="ownProcess" 
     Vital="yes" 
     Interactive="no" 
     Account="[SERVICEUSER]"
     Password="[SERVICEPWD]" />

I did the mistake of using
Type="shareProcess"
which gave me the perfectly correct error message that didn't help me at all:
"Error 1083: The executable program that this service is configured to run in does not implement the service."

Setting the Type to "ownProcess" again solved the problem.

Dienstag, 13. September 2016

Hyper-V Host does not set ICACLS permissions for new virtual machines

We have a Windows 2012R2 Hyper-V Host, and as things go, someone apparently did something and suddenly we had to resort to hacks to make new VMs run.

So, how did the problem manifest? Whenever someone added a new VM, either using a new virtual disk or a template, this exception would pop up:
'TestVm' could not initialize.
An attempt to initialize VM saved state failed.
'TestVm' could not initialize. (Virtual machine ID ....)
'TestVm' could not create or access saved state file 80F7C822-455B-4D70-9D73-B250196B36A9.vsv.

We had a fix/hack for solving this.
Run the following command in a cmd-prompt (not PowerShell!):

D:\Hyper-V\02250683-9FD8-4286-95CC-5C131F6A09DE>
icacls 02250683-9FD8-4286-95CC-5C131F6A09DE.vsv /grant "NT VIRTUAL MACHINE\02250683-9FD8-4286-95CC-5C131F6A09DE":(F)

However, remembering that for every new VM is not much fun.

The problem was, that the folder permissions on the folder with the Hyper-V VM disks and configurations lacked a permission for the "Hyper-V Administrators".

I changed the permission on the base folder holding the disks and the configs by adding the Hyper-V Administrators group of the local machine, and the problem was gone:

Dienstag, 1. Dezember 2015

Installing .NET versions with wix bundles (and determine which one is already installed)

There's a nice help page from MS:
Which .NET does come with which OS and how do I determine what is installed: https://msdn.microsoft.com/en-us/library/bb822049(v=vs.110).aspx

For msi packages, there's an extension which does this work for you and determines whether e.g. .net 4.5 is installed: WixNetfxExtension, but the suggested PropertyRef (<PropertyRef Id="NETFRAMEWORK45"/>) doesn't work with burn.

So, for the bundle, the game is slightly different. Just do a:
<util:RegistrySearchRef Id="NETFRAMEWORK45"/>
this needs the utils extension.

And it'll only give you a version number, so you'll have to check against that. Which .net has which version number can be gleaned from the first link to MS, or from here.

Of course, there's already a stackoverflow answer for that :)

And finally, to install e.g. .NET 4.5 with your bundle, you can use this magic package group id in the chain if you use the NetFxExtension:

<Chain> <PackageGroupRef Id="NetFx45Web"/> </Chain>

That should make the bundle download the appropriate .NET framework. See also here: http://wixtoolset.org/documentation/manual/v3/howtos/redistributables_and_install_checks/install_dotnet.html

Donnerstag, 2. Juli 2015

Wix and msi installers: downgrade a library during a msi upgrade

Here's a tricky one I came across at work:

If you look closely, with an upgrade of OurProduct, we downgrade a library. So what? you say. (Hint: try not to ever do a library version downgrade! It's painful.)

Well, it turns out, that with xcopy, this problem would easily be solved. Your forfeit all the other kinda cool things that msi can do, though. Not the path we'd like to go.

So, what to do?

Just to show the baseline: this is the component of the initial OurProduct:
    <Component Id="Newtonsoft.Json" Guid="SOMEGUID-42FD-44B5-8C93-C245F85DF84E">
      <File Source="Newtonsoft.Json.dll" KeyPath="yes" /> <!-- version 5.0 -->
    </Component>

How do we downgrade that?
Ok, easy, just pack in the new lib, take the old component guid, and do configure major upgrades just like this:
<MajorUpgrade
     DowngradeErrorMessage="A newer version of [ProductName] is already installed." 
     Schedule="afterInstallValidate" />
this is the default scheduling used by Wix now, it will remove the old product first (that's the 'RemoveExistingProducts' Action), and then install the new product. That means, it removes this Newtonsoft.Json lib before it puts in the version of the upgrade.

Try #1 - fail

We also pack in the new version
<!-- Try #1 -->
    <Component Id="Newtonsoft.Json" Guid="SOMEGUID-42FD-44B5-8C93-C245F85DF84E">
      <File Source="Newtonsoft.Json.dll" KeyPath="yes" /> <!-- version 4.5 -->
    </Component>
same guid, same thing, lower version.
And it doesn't work:
Action start 17:32:45: CostFinalize.
.....
MSI (c) (38:C0) [17:32:45:977]: Disallowing installation of component: {SOMEGUID-42FD-44B5-8C93-C245F85DF84E} since the same component with higher versioned keyfile exists
Action ended 17:32:45: CostFinalize. Return value 1.

What please? Why does it exist?

As you can see, the msi plans the installation of components in the CostFinalize Action, which is pretty early in the whole InstallExecute sequence. And it's even before the first possible scheduling location of the removal of the old product. CostFinalize runs before InstallValidate. Damn.

What did I have on the system, after this upgrade above finished? The file was gone: the upgrade says it won't install it, the previous version removes it, the upgrade will not install it again => the file is gone. Solution? Repair the new product. But then it'd be easier to uninstall the previous product, and then install the new version. I looked on StackOverflow for help: http://stackoverflow.com/questions/15138731/wix-major-upgrade-not-installing-all-files, but the first answer didn't work for me. The file was even left in the higher version (5.0).

Try #2 - fail again

Ok, let's give it a new guid, then the old component is removed
<!-- Try #2, new guid!! -->
    <Component Id="Newtonsoft.Json" Guid="SOMEGUID-DE0F-4CC9-BC16-2FF543B2FA90">
      <File Source="Newtonsoft.Json.dll" KeyPath="yes" /> <!-- Version 4.5 -->
    </Component>
Well, we will still get
Disallowing installation of component: {SOMEGUID-DE0F-4CC9-BC16-2FF543B2FA90} since the same component with higher versioned keyfile exists
What please? This component is supposed to be new!?!
What the windows installer does here is compare keyfile paths (which probably means it compares the path of the key resource). So it sees the resource already lying there, in a higher version, and doesn't want to overwrite it.

Try #3 - finally succeed

In the end, I found out that there's still a chance how you can do it, but it's not nice:
<!-- Try #3, with still another guid -->
    <Component Id="Newtonsoft.Json.Fix" Guid="SOMEGUID-A46A-4C3E-801A-6F79B16CD0B4">
      <File Source="CarryOn.dll" KeyPath="yes" />
      <File Source="Newtonsoft.Json.dll" />
    </Component>
This works. It's not my idea, I found it somewhere, kudos to whoever wrote it.
This will make the installer look for the CarryOn dll, which can be any other dll where you go for higher versions with upgrades. The old installer will remove the old, higher versioned, Newtonsoft.Json, and the new installer will place the new, lower versioned, library.

There seem to be other answers to this problem out there. I didn't try them. They seem to require InstallShield, some other wix control elements like RemoveFile, scheduling of the RemoveExistingPRoducts to before CostFinalize or they even manipulate the msi directly by convincing it that the lib it has actually has a higher version. The latter will probably just create problems later:
http://stackoverflow.com/questions/30377613/downgrade-file-in-majorupgrade (schedule after CostFinalize)
http://stackoverflow.com/questions/4227456/windows-installer-deletes-versioned-file-during-product-upgrade-instead-of-down (schedule after CostFinalize)
http://stackoverflow.com/questions/14122136/how-can-i-make-sure-a-downgraded-library-is-installed-by-my-msi (hack the msi database)
http://stackoverflow.com/questions/25311969/how-to-downgrade-a-third-party-file-in-a-wix-msi (spoiler: the answer does not work)

It seems, that this is a seldom occurring problem, which doesn't really concern those who are in charge of the msi installer itself. So, rather try to not run into this problem. If you do custom compiles of open source stuff, maybe even chose to take a lower version than the current release which you probably end up using some time later.

Freitag, 22. Mai 2015

Downloading the Microsoft Azure SDK 2.3 (aka: why doesn't MS provide direct download links???)

I need this every now and then, and I'll always run into the Microsoft website's bad installation experience, which doesn't seem to work with chrome or Firefox - no, you need IE.
There is a web platform installer making this easier, but that unfortunately doesn't install the v2.3 Azure SDK.

I took those links from this MS site.

So here they are, and a little hint for me what I need for VS2013 and compiling Apps into Azure Roles:

WebToolsExtensionsVS.msi
WebToolsExtensionsVS2013.msi
WebToolsExtensionsVWD.msi
WebToolsExtensionsVWD2013.msi
WindowsAzureTools.vs110.exe
WindowsAzureTools.vs120.exe - my pick
WebToolsExtensionsVWD2013.msi
WindowsAzureStorageEmulator.msi - my pick
WindowsAzureLibsForNet-x64.msi - my pick
WindowsAzureLibsForNet-x86.msi
WindowsAzureEmulator-x64.exe - my pick
WindowsAzureEmulator-x86.exe
WindowsAzureAuthoringTools-x64.msi - my pick
WindowsAzureAuthoringTools-x86.msi

Mittwoch, 25. Februar 2015

File associations with windows - especially: I can't set the default program for an extension anymore

This might expand in the future.. windows registry is deep ;)

My problem was, that using either of those 2 dialogs didn't help to set the default program. I chose a file, but no association was made. Why???
Neither here: Control Panel\All Control Panel Items\Default Programs\Set Associations
Nor here: Open with dialog
In the end, this link gave me the tip:
fire up regedit, go to
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts
and then delete the "OpenWithProgids" subkey from your extension:
The id is probably broken, who knows?
In any case, seems like the id prevented me from assigning a different default program. Once that's gone, you can re-assign any program with the extension that you want.