• Using the Developer PowerShell Visual Studio with PowerShell 7

    One of the nice new features introduced in Visual Studio 2019 16.2 was the Developer PowerShell for VS 2019 - a nice accompaniment to the existing cmd.exe based Developer Command Prompt for VS 2019.

    Windows Start Menu showing Developer PowerShell.

    I use PowerShell as much as possible, and for a long time now I’ve made a habit of updating my profile.ps1 so that all the Visual Studio tools are available from the PowerShell command prompt. Previously this required running the old VsDevCmd.bat batch file and capturing the environment variables it set to then bring them into the PowerShell process. You can see an example here.

    But now there’s first class support for integrating Visual Studio tooling into your PowerShell environment.

    If you take a look at the Windows Start Menu shortcut that’s added, you’ll see it’s defined with a target similar to this:

    C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -noe -c "&{Import-Module """C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"""; Enter-VsDevShell f9f5056f}"
    

    With the release of PowerShell Core 6, and now PowerShell 7, I’m now favouring these latest releases of PowerShell over the ‘legacy’ Windows PowerShell 5.1. The problem was until recently, the assembly you see referenced in the shortcut above only worked in Windows PowerShell. It wasn’t compatible with PowerShell Core. Pleasingly this was fixed in Visual Studio 2019 16.5.

    So now in your PowerShell 7 profile, you can add:

    Import-Module "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
    Enter-VsDevShell -InstanceId 9034d7ab
    

    And you’ll get the full DevShell experience: !Windows Terminal with PowerShell 7 and Visual Studio integration

    There’s just one catch - notice that InstanceId? That’s unique for every machine. You can either grab the value out of the properties of the Start Menu shortcut, or run vswhere -property instanceId which will return the instanceId of the newest instance of Visual Studio.

  • 4 doesn't go into 3 part 2

    Last time, we’d figured out a strategy for getting our data up into Azure Artifacts. The problem was that we are using SemVer pre-release notation, and the Azure Artifacts command line and Azure Pipelines task don’t support querying pre-release versions using wildcards.

    The CLI tools and Pipelines task will work if we know the exact pre-release version required, but how to find that out? The Azure DevOps Services REST API.

    Here’s an example PowerShell script you could use to find out the latest version of a package named ‘mypackage’. It sets a pipeline variable that can be used in subsequent Azure Pipeline tasks.

    $url = "https://feeds.dev.azure.com/{organization}/{project}/_apis/packaging/Feeds/{feedId}/packages?api-version=5.1-preview.1&packageNameQuery=mypackage"
    
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "",$env:SYSTEM_ACCESSTOKEN)))
    $result = Invoke-RestMethod -Uri $url -Method Get -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}
    
    $packageVersion = $result.value.versions.normalizedVersion
    Write-Host "##vso[task.setvariable variable=packageVersion]$packageVersion"
    

    We’re making use of the packageNameQuery parameter to filter by the package name, but there are other filtering options available. You could also do more filtering on the JSON data that the REST API returns.

    Now we can use the Universal Package task, requesting the specific version. eg.

    # Download Universal Package
    steps:
    - task: UniversalPackages@0
      displayName: 'Universal download'
      inputs:
        downloadDirectory: Application
        vstsFeed: '00000000-0000-0000-0000-000000000000/00000000-0000-0000-0000-000000000001'
        vstsFeedPackage: imagemagick
        vstsPackageVersion: '$(packageVersion)'
    

  • 4 doesn't go into 3

    There’s different ways of versioning things. Windows has had a 4-part version scheme that is used by itself and most Windows applications. The convention is <major version>.<minor version>.<build number>.<revision>. .NET supports this through classes like AssemblyVersionAttribute and Version.

    SemVer is a 3-part scheme (MAJOR.MINOR.PATCH) with optional pre-release or metadata that has seen wide industry adoption, especially by package management tools (npm, NuGet etc).

    Using SemVer-versioned components in an ecosystem that uses 4-part versioning is pretty straight forward. Just tack an extra “.0” on the end if it really wants to see 4 parts.

    On the other hand, using 4-part versions in a SemVer ecosystem is hard. You can’t just drop one of the parts unless you know with certainty that it will never, ever be significant.

    And that’s the problem. How do you preserve information from 4 things when you’ve only got 3 places to put it?

    I hit this problem recently when looking to push some versioned data that’s generated on-prem up to an Azure Pipeline so it could be used in the build process. I decided that Azure Artifacts would be a good way to get the data safely into a place where the Pipeline could access it.

    Azure Artifacts has built-in support for a number of package formats (NuGet, npm, Maven and others), but this data wasn’t any of those, so the ‘Universal Package’ type seemed most appropriate. But as I was about to discover, Universal Packages are versioned with SemVer 2 (and no metadata allowed).

    As the third part of the 4-part version number was zero, I was thinking I could just drop that to convert to a 3-part version number. But after some investigation it turns out that assumption was wrong - the third part can change.

    I’m not the first to encounter this problem, and the final comment on that article by Mitch Denny (an Australian developer who I’m honoured to know, and who was the Program Manager for Azure Artifacts at the time) gives an interesting workaround.

    1.2.3.4 could be encoded in SemVer as 1.2.3-4

    I’m more used to seeing the pre-release with text (eg. 1.2.3-beta.4), but reviewing the SemVer site (point 9), they actually give 1.0.0-0.3.7 as an example, so this should be ok. It is technically a ‘pre-release’ version, but it has preserved all the data.

    There’s one final gotcha, and it relates to the version being pre-release.

    I mentioned earlier that I was going to use the Universal Package in an Azure Pipeline. To download the package in the pipeline, you use the Universal Package task.

    The problem is that this task, like the CLI tooling doesn’t support pre-release versions using wildcards. This is explicitly called out in the documentation for the Universal Packages Quickstart “Wildcard expressions do not currently support pre-release versions. It is not possible to get the latest pre-release version of a package.”

    Next steps, see if using I can use the REST API to obtain the version and pass that to the Pipelines task.

    Continued in part 2