Skip to content

Proposal - module build & publish via the task runner and psake #107

@rkeithhill

Description

@rkeithhill

There is interest in being able to publish a module to the PowerShell Gallery from VSCode using this extension. I have been doing a fair amount of work with ASP.NET Core projects. Those projects use gulp as a task runner to perform during the "publish" of a web site. VSCode has a task runner that can be configured to run various tools including gulp. However for PowerShell module publish, I think we should use something more "PowerShell oriented". So I've been playing with PSake to build and publish modules within VSCode.

Here is the psake.ps1 file (equivalent to gulpfile.js) that would be in the workspace folder:

Properties {    
    $NugetApiKeyEnc = @'XXXX'@
    $ReleaseNotesPath = "$PSScriptRoot\ReleaseNotes.md"

    $PublishDir = "$PSScriptRoot\.publish"
    $Exclude = @(
        'psake.ps1',
        '*Tests.ps1',
        '.git*',
        '.publish',
        '.vscode'
    )
}

Task default -depends Build

Task Publish -depends Test {
    $secureString = $NugetApiKeyEnc | ConvertTo-SecureString
    $nugetApiKeyBstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
    $nugetApiKey = [Runtime.InteropServices.Marshal]::PtrToStringAuto($nugetApiKeyBstr)
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($nugetApiKeyBstr)

    $ReleaseNotes = Get-Content $ReleaseNotesPath -Raw
    Publish-Module -Path $PublishDir -NuGetApiKey $nugetApiKey `
                   -ReleaseNotes $ReleaseNotes -WhatIf
}

Task Test -depends Build {
    Import-Module Pester
    Invoke-Pester $PSScriptRoot
}

Task Build -depends Clean {
    Copy-Item $PSScriptRoot\* -Destination $PublishDir -Exclude $Exclude -Recurse -Container 
}

Task Clean -depends Init {
    Remove-Item $PublishDir\* -Recurse -Force
}

Task Init {
   if (!(Test-Path $PublishDir)) {
       New-Item -ItemType Directory $PublishDir
   }
}

Note that most of this file is boilerplate except for the Properties section. That is what the user will spend the most time configuring for their module. The NuGetApi key is something that should be protected and not checked in. What you see above is the output from:

$ss = Read-Host 'Enter NuGet Api Key' -AsSecureString
$ss | ConvertFrom-SecureString | Out-Clipboard

Now to get this to work from VSCode, I've modified the tasks.json file to:

// A task runner that invokes Pester to run all Pester tests under the
// current workspace folder.

// NOTE: This Pester task runner requires an updated version of Pester (>=3.4.0)
// in order for the problemMatcher to find failed test information (message, line, file).
// If you don't have that version, you can update Pester from the PSGallery like so:
//
// PS C:\> Update-Module Pester
//
// If that gives an error like:
// "Module 'Pester' was not installed by using Install-Module, so it cannot be updated."
// then execute:
//
// PS C:\> Install-Module Pester -Scope CurrentUser -Force
//

// Available variables which can be used inside of strings.
// ${workspaceRoot}: the root folder of the team
// ${file}: the current opened file
// ${fileBasename}: the current opened file's basename
// ${fileDirname}: the current opened file's dirname
// ${fileExtname}: the current opened file's extension
// ${cwd}: the current working directory of the spawned process
{
    "version": "0.1.0",

    // Start PowerShell
    "command": "${env.windir}\\sysnative\\windowspowershell\\v1.0\\PowerShell.exe",

    // The command is a shell script
    "isShellCommand": true,

    // Show the output window always
    "showOutput": "always",

    "args": [
        "-NoProfile", "-ExecutionPolicy", "Bypass"        
    ],

    // Associate with test task runner
    "tasks": [
        {
            "taskName": "Build",
            "suppressTaskName": true,
            "isBuildCommand": true,
            "args": [
                "Write-Host 'Invoking PSake...'; Invoke-PSake psake.ps1;",
                "Invoke-Command { Write-Host \"Completed Build task in task runner\" }"                
            ]
        },
        {
            "taskName": "Publish",
            "suppressTaskName": true,
            "args": [
                "Write-Host 'Invoking PSake...'; Invoke-PSake psake.ps1 Publish;",
                "Invoke-Command { Write-Host \"Completed Publish task in task runner\" }"                
            ]
        },
        {
            "taskName": "Pester",
            "suppressTaskName": true,
            "isTestCommand": true,
            "args": [
                "Write-Host 'Invoking Pester...'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};",
                "Invoke-Command { Write-Host \"Completed all tests tasks in task runner\" }"
            ],
            "problemMatcher": [
                {
                    "owner": "powershell",
                    "fileLocation": ["absolute"],
                    "severity": "error",
                    "pattern": [
                        {
                            "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$",
                            "message": 1
                        },
                        {
                            "regexp": "^\\s+at\\s+[^,]+,\\s*(.*?):\\s+line\\s+(\\d+)$",
                            "file": 1,
                            "line": 2
                        }
                    ]
                }
            ]
        }
    ]
}

The result looks like this:

vscodepublish

Still a ways to go. The Build's copy task isn't copying the en-US dir correctly. And I have no problemMatcher for the psake output yet. Still this shows some promise.

What we still need though is a command for scaffolding a new module manifest file from the VSCode UI. And there should be a .gitignore file with .publish in it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions