Wednesday, October 15, 2014

PowerShell Oddities, Take 4

I ran into another PowerShell oddity today. This one wasted a lot of time. It boils down to the fact that you can index anything. If you use [0] on something that is not a collection, it will return itself. If you use anything else, it will return nothing. Again, this was buried deep in a nested code that was called from functions and it took a long time to figure out what was going on.

Take the following:

$settings = @{"Env1" = "VarValue1"; "Env2" = "VarValue2" }
Write-Output "Count: $($settings.Values.Count)"
Write-Output "Value 0: '$($settings.Values[0])'"
Write-Output "Value 1: '$($settings.Values[1])'"

It sure looks like you are getting a proper collection. Values, is indeed a collection, which makes this result all the more confusing:

Count: 2
Value 0 : 'VarValue2 VarValue1'
Value 1 : ''

Solution:

$values = $setting.Value.Values -as [string[]]
Write-Output "Count: $($values.Count)"
Write-Output "Value 0: '$($values[0])'"
Write-Output "Value 1: '$($values[1])'"

Output:

Count: 2
Value 0 : 'VarValue2'
Value 1 : 'VarValue1'

The weird thing is that I swear I tried doing an implicit cast before I posted my question to http://stackoverflow.com

After posting my question I realized I had not tried an explicit cast. So, I tried that and it worked. That seemed weird, so I tried the implicit cast again and it also worked. I guess I am going crazy. For reference here is the implicit cast:

[string[]]$values = $setting.Value.Values

Wednesday, October 8, 2014

Command 'nuget spec' Sucks for Dlls

Not So Nice

The NuGet documentation says that you can create a nuspec file from an existing DLL if you run the following command:

nuget spec [path to dll]

Unfortunately, the output is less than satisfying:

<?xml version="1.0"?>
<package >
    <metadata>
        <id>SomeAwesomeDll.dll</id>
        <version>1.0.0</version>
        <authors>swoogan</authors>
        <owners>swoogan</owners>
        <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
        <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
        <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>Package description</description>
        <releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
        <copyright>Copyright 2014</copyright>
        <tags>Tag1 Tag2</tags>
        <dependencies>
        <dependency id="SampleDependency" version="1.0" />
        </dependencies>
    </metadata>
</package>

Note: the version is always 1.0.0, because it does not bother to extract the version number from the assembly.

All and all not what I was hoping for!

The three glaring problems are:

  1. It does not add the version, as mentioned above
  2. It does not add <files> section with the name of the DLL you pointed too!!!
  3. As a result of the second point, it does not produce a valid nuspec file that can be packaged.

A Better Way

Here is a preferable default template:

<?xml version="1.0" encoding="utf-8"?>
<package mlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>[[DllNameWithoutExtension]]</id>
        <version>[[Version]]</version>
        <authors>swoogan</authors>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>[[DllNameWithoutExtension]]</description>        
    </metadata>
    <files>
      <file src="[[DllName]]" target="lib\[[DllName]]" />
    </files>
</package>

Using the above template and a powershell template engine that I adapted from
http://bricelam.net/2012/09/simple-template-engine-for-powershell.html

   function Merge-Tokens
   {
       <#
       .SYNOPSIS
       Replaces tokens in a template
       .DESCRIPTION
       Replaces tokens found in a template file with values from the supplied hashtable
       .EXAMPLE
       Merge-Tokens -Path mytemplate.xml -Tokens @{ Variable = Value }
       .EXAMPLE
       Merge-Tokens -Path mytemplate.xml -Tokens @{ Variable = Value } -OutputPath newfile.xml
       .PARAMETER Path
       Path to the template file to replace the tokens in
       .PARAMETER Tokens
       Hashtable with the names of the tokens in the file and their replacement values
       .PARAMETER OutputPath
       Path to write the output to. If not supplied, the replacement is returned as a string
       #>
       [CmdletBinding()]
       param 
       (
           [Parameter(
               Mandatory=$True,
               Position=0)]
           [string] $Path, 

           [Parameter(
               Mandatory=$True,
               Position=1)]
           [hashtable] $Tokens,

           [Parameter(
               Mandatory=$False,
               Position=2)]
           [string] $OutputPath
       )

       $template = Get-Content $Path -Raw

       $output = [regex]::Replace(
           $template,
           '\[\[(?\w+)\]\]',
           {
               param($match)
               $tokenName = $match.Groups['tokenName'].Value
               return $Tokens[$tokenName]
           })

       if ($OutputPath -ne "") {
           Set-Content -Path $OutputPath -Value $output
       } else {
           Write-Output $output
       }
   }

I was able to write the following script:

   param (
       [string] $Path
   )

   Import-Module .\Merge-Tokens.ps1

   $nuspecTemplate = "$PSScriptRoot\nuspec.tpl"

   $fileInfo = gci $Path

   $tokens =  @{ 
                DllName = $fileInfo.Name
                Version = $fileInfo.VersionInfo.ProductVersion
                DllNameWithoutExtension = $fileInfo.BaseName
               }

   $outputPath = ($fileInfo.Directory.FullName + "\" + $fileInfo.BaseName + ".nuspec" )

   Merge-Tokens -Path $nuspecTemplate -Tokens $tokens -OutputPath $outputPath
  

In this case, I stored the above sane template in nuspec.tpl. Notice that I passed the -Raw parameter to Get-Content this causes it to get the entire file as a single string rather than an array of strings. This helps [regex]::Replace return a string that preserves the newlines found in the original file.

Putting it all together, I was able to generate nuget packages from dozens of existing DLLs stored on a network share.

Wednesday, October 1, 2014

What the Heck Happened to my LTS?

In around May I updated my workstation and my wife’s laptop up to Kubuntu LTS 14.04. I had lots of trouble with each version of Kubuntu leading up to 12.04 LTS. There was always something that did not work and I would wait very impatiently for the next release hoping it would fix all my issues. While they usually did, I invariably ended up with some new issue or issues. When 12.04 finally came out it was the first time everything worked. As they say, if it isn’t broke don’t fix it.

And I did not. That is until 14.04 came out. Along with it being another LTS (long term service) release, it had updates for a few applications that included features I wanted.

The weird thing was, ever since the release, my wife’s laptop seemed to need a lot of updates. Since it was a new release I did not think too much of it. New versions often have a lot of updates in the first couple of months. Also, because I updated my workstation a little later (I figured why not throw the wife to the wolves first) I could not compare the two machines directly.

Well, in the last couple of weeks we have been having a lot of trouble with the wife’s machine. After one update, local DNS stopped working. After another, her X-server would not use the nVidia drivers. Then in the last couple of days I have noticed hundreds of MBs of downloads that my machine was not getting. And kernel versions that were several ahead of mine.

I have not been keeping track of the silly codenames for Ubuntu for awhile now. After seven or eight they really start to blur together. But tonight when once again updating my wife’s machine and seeing that mine had none, I noticed utopic/main in the package list retrieval. I quickly ran an update on my machine and sure enough it was trusty/main. A quick check of the wiki and I found the problem. Utopic is from the future!!!

Sure enough:

swoogan@workstation:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.1 LTS
Release:        14.04
Codename:       trusty

swoogan@laptop:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu Utopic Unicorn (development branch)
Release:        14.10
Codename:       utopic

Somehow the last person I want running a beta release of Kubuntu, is. So that explains all the problems we have been having. How this happened remains unexplained…