Skip to content

Commit f6d2777

Browse files
committed
Modified Examples dir to provide a preview of PSake based module publishing.
1 parent 7cd3f99 commit f6d2777

16 files changed

+450
-73
lines changed

examples/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Don't checkin this output dir
2+
Release/
3+
4+
# Auto-generated test file
5+
foo[1].ps1

examples/.vscode/tasks.json

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// A task runner that invokes Pester to run all Pester tests under the
22
// current workspace folder.
33

4-
// NOTE: This Pester task runner requires an updated version of Pester (>=3.4.0)
4+
// NOTE: This Test task runner requires an updated version of Pester (>=3.4.0)
55
// in order for the problemMatcher to find failed test information (message, line, file).
6-
// If you don't have that version, you can update Pester from the PSGallery like so:
6+
// If you don't have that version, you can update Pester from the PowerShell Gallery
7+
// with this command:
78
//
89
// PS C:\> Update-Module Pester
910
//
@@ -14,14 +15,19 @@
1415
// PS C:\> Install-Module Pester -Scope CurrentUser -Force
1516
//
1617

18+
// NOTE: The Clean, Build and Publish tasks require PSake. PSake can be installed
19+
// from the PowerShell Gallery with this command:
20+
//
21+
// PS C:\> Install-Module PSake -Scope CurrentUser -Force
22+
//
23+
1724
// Available variables which can be used inside of strings.
1825
// ${workspaceRoot}: the root folder of the team
1926
// ${file}: the current opened file
2027
// ${fileBasename}: the current opened file's basename
2128
// ${fileDirname}: the current opened file's dirname
2229
// ${fileExtname}: the current opened file's extension
2330
// ${cwd}: the current working directory of the spawned process
24-
2531
{
2632
"version": "0.1.0",
2733

@@ -34,17 +40,45 @@
3440
// Show the output window always
3541
"showOutput": "always",
3642

37-
"args": [
38-
"-NoProfile", "-ExecutionPolicy", "Bypass",
39-
"Write-Host 'Invoking Pester...'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};",
40-
"Invoke-Command { Write-Host \"Completed all tasks in task runner: $($args[0])\" } -args"
41-
],
43+
"args": [
44+
"-NoProfile", "-ExecutionPolicy", "Bypass"
45+
],
4246

4347
// Associate with test task runner
4448
"tasks": [
4549
{
46-
"taskName": "Pester",
50+
"taskName": "Clean",
51+
"suppressTaskName": true,
52+
"args": [
53+
"Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Clean;",
54+
"Invoke-Command { Write-Host \"Completed Clean task in task runner.\" }"
55+
]
56+
},
57+
{
58+
"taskName": "Build",
59+
"suppressTaskName": true,
60+
"isBuildCommand": true,
61+
"args": [
62+
"Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Build;",
63+
"Invoke-Command { Write-Host \"Completed Build task in task runner.\" }"
64+
]
65+
},
66+
{
67+
"taskName": "Publish",
68+
"suppressTaskName": true,
69+
"args": [
70+
"Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Publish;",
71+
"Invoke-Command { Write-Host \"Completed Publish task in task runner.\" }"
72+
]
73+
},
74+
{
75+
"taskName": "Test",
76+
"suppressTaskName": true,
4777
"isTestCommand": true,
78+
"args": [
79+
"Write-Host 'Invoking Pester...'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};",
80+
"Invoke-Command { Write-Host \"Completed Test task in task runner.\" }"
81+
],
4882
"problemMatcher": [
4983
{
5084
"owner": "powershell",

examples/Build.ps1

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
##############################################################################
2+
# PREVIEW VERSION OF PSAKE SCRIPT FOR MODULE BUILD & PUBLISH TO THE PSGALLERY
3+
##############################################################################
4+
#
5+
# We are hoping to add support for publishing modules to the PowerShell gallery
6+
# and private repositories in a future release of this extension. This is an
7+
# early look at the approach we are considering which is to supply a
8+
# PSake-based script that will:
9+
#
10+
# 1. Create a directory from which to publish your module.
11+
# 2. Copy the appropriate module files to that directory excluding items like
12+
# the .vscode directory, Pester tests, etc. These are configurable in Build.ps1.
13+
# 3. Verify all existing Pester tests pass.
14+
# 4. Publish the module to the desired repository (defaulting to the PSGallery).
15+
#
16+
# Requirements: PSake. If you don't have this module installed use the following
17+
# command to install it:
18+
#
19+
# PS C:\> Install-Module PSake -Scope CurrentUser
20+
#
21+
##############################################################################
22+
# This is a PSake script that supports the following tasks:
23+
# clean, build, test and publish. The default task is build.
24+
#
25+
# The publish task uses the Publish-Module command to publish
26+
# to either the PowerShell Gallery (the default) or you can change
27+
# the $Repository property to the name of an alternate repository.
28+
#
29+
# The test task invokes Pester to run any Pester tests in your
30+
# workspace folder. Name your test scripts <TestName>.Tests.ps1
31+
# and Pester will find and run the tests contained in the files.
32+
#
33+
# You can run this build script directly using the invoke-psake
34+
# command which will execute the build task. This task "builds"
35+
# a temporary folder from which the module can be published.
36+
#
37+
# PS C:\> invoke-psake build.ps1
38+
#
39+
# You can run your Pester tests (if any) by running the following command.
40+
#
41+
# PS C:\> invoke-psake build.ps1 -taskList test
42+
#
43+
# You can execute the publish task with the following command. Note that
44+
# the publish task will run the test task first. The Pester tests must pass
45+
# before the publish task will run. The first time you run the publish
46+
# command, you will be prompted to enter your PowerShell Gallery NuGetApiKey.
47+
# After entering the key, it is encrypted and stored so you will not have to
48+
# enter it again.
49+
#
50+
# PS C:\> invoke-psake build.ps1 -taskList publish
51+
#
52+
# You can verify the stored and encrypted NuGetApiKey by running the following
53+
# command. This will display your NuGetApiKey in plain text!
54+
#
55+
# PS C:\> invoke-psake build.ps1 -taskList showKey
56+
#
57+
# You can store a new NuGetApiKey with this command. You can leave off
58+
# the -properties parameter and you'll be prompted for the key.
59+
#
60+
# PS C:\> invoke-psake build.ps1 -taskList storeKey -properties @{NuGetApiKey='test123'}
61+
#
62+
63+
###############################################################################
64+
# Customize these properties for your module.
65+
###############################################################################
66+
Properties {
67+
# The name of your module should match the basename of the PSD1 file.
68+
$ModuleName = (Get-Item $PSScriptRoot\*.psd1)[0].BaseName
69+
70+
# Path to the release notes file. Set to $null if the release notes reside in the manifest file.
71+
$ReleaseNotesPath = "$PSScriptRoot\ReleaseNotes.md"
72+
73+
# The directory used to publish the module from. If you are using Git, the
74+
# $PublishDir should be ignored if it is under the workspace directory.
75+
$PublishDir = "$PSScriptRoot\Release\$ModuleName"
76+
77+
# The following items will not be copied to the $PublishDir.
78+
# Add items that should not be published with the module.
79+
$Exclude = @(
80+
'Release',
81+
'Tests',
82+
'.git*',
83+
'.vscode',
84+
# The next three files are unique to this examples dir.
85+
'DebugTest.ps1',
86+
'Stop*.ps1',
87+
'Readme.md',
88+
(Split-Path $PSCommandPath -Leaf)
89+
)
90+
91+
# Name of the repository you wish to publish to. Default repo is the PSGallery.
92+
$PublishRepository = $null
93+
94+
# Your NuGet API key for the PSGallery. Leave it as $null and the first time
95+
# you publish you will be prompted to enter your API key. The build will
96+
# store the key encrypted in a file, so that on subsequent publishes you
97+
# will no longer be prompted for the API key.
98+
$NuGetApiKey = $null
99+
$EncryptedApiKeyPath = "$env:LOCALAPPDATA\vscode-powershell\NuGetApiKey.clixml"
100+
}
101+
102+
###############################################################################
103+
# Customize these tasks for performing operations before and/or after publish.
104+
###############################################################################
105+
Task PrePublish {
106+
}
107+
108+
Task PostPublish {
109+
}
110+
111+
###############################################################################
112+
# Core task implementations - this possibly "could" ship as part of the
113+
# vscode-powershell extension and then get dot sourced into this file.
114+
###############################################################################
115+
Task default -depends Build
116+
117+
Task Publish -depends Test, PrePublish, PublishImpl, PostPublish {
118+
}
119+
120+
Task PublishImpl -depends Test -requiredVariables PublishDir, EncryptedApiKeyPath {
121+
$NuGetApiKey = Get-NuGetApiKey $NuGetApiKey $EncryptedApiKeyPath
122+
123+
$publishParams = @{
124+
Path = $PublishDir
125+
NuGetApiKey = $NuGetApiKey
126+
}
127+
128+
if ($PublishRepository) {
129+
$publishParams['Repository'] = $PublishRepository
130+
}
131+
132+
# Consider not using -ReleaseNotes parameter when Update-ModuleManifest has been fixed.
133+
if ($ReleaseNotesPath) {
134+
$publishParams['ReleaseNotes'] = @(Get-Content $ReleaseNotesPath)
135+
}
136+
137+
Publish-Module @publishParams -WhatIf
138+
}
139+
140+
Task Test -depends Build {
141+
Import-Module Pester
142+
Invoke-Pester $PSScriptRoot
143+
}
144+
145+
Task Build -depends Clean -requiredVariables PublishDir, Exclude, ModuleName {
146+
Copy-Item $PSScriptRoot\* -Destination $PublishDir -Recurse -Exclude $Exclude
147+
148+
# Get contents of the ReleaseNotes file and update the copied module manifest file
149+
# with the release notes.
150+
# DO NOT USE UNTIL UPDATE-MODULEMANIFEST IS FIXED - HORRIBLY BROKEN RIGHT NOW.
151+
# if ($ReleaseNotesPath) {
152+
# $releaseNotes = @(Get-Content $ReleaseNotesPath)
153+
# Update-ModuleManifest -Path $PublishDir\${ModuleName}.psd1 -ReleaseNotes $releaseNotes
154+
# }
155+
}
156+
157+
Task Clean -depends Init -requiredVariables PublishDir {
158+
# Sanity check the dir we are about to "clean". If $PublishDir were to
159+
# inadvertently get set to $null, the Remove-Item commmand removes the
160+
# contents of \*. That's a bad day. Ask me how I know? :-(
161+
if ($PublishDir.Contains($PSScriptRoot)) {
162+
Remove-Item $PublishDir\* -Recurse -Force
163+
}
164+
}
165+
166+
Task Init -requiredVariables PublishDir {
167+
if (!(Test-Path $PublishDir)) {
168+
$null = New-Item $PublishDir -ItemType Directory
169+
}
170+
}
171+
172+
Task StoreKey -requiredVariables EncryptedApiKeyPath {
173+
if (Test-Path $EncryptedApiKeyPath) {
174+
Remove-Item $EncryptedApiKeyPath
175+
}
176+
177+
$null = Get-NuGetApiKey $NuGetApiKey $EncryptedApiKeyPath
178+
"The NuGetApiKey has been stored in $EncryptedApiKeyPath"
179+
}
180+
181+
Task ShowKey -requiredVariables EncryptedApiKeyPath {
182+
$NuGetApiKey = Get-NuGetApiKey $NuGetApiKey $EncryptedApiKeyPath
183+
"The stored NuGetApiKey is: $NuGetApiKey"
184+
}
185+
186+
Task ? -description 'Lists the available tasks' {
187+
"Available tasks:"
188+
$psake.context.Peek().tasks.Keys | Sort
189+
}
190+
191+
###############################################################################
192+
# Helper functions
193+
###############################################################################
194+
function Get-NuGetApiKey($NuGetApiKey, $EncryptedApiKeyPath) {
195+
$storedKey = $null
196+
if (!$NuGetApiKey) {
197+
if (Test-Path $EncryptedApiKeyPath) {
198+
$storedKey = Import-Clixml $EncryptedApiKeyPath | ConvertTo-SecureString
199+
$cred = New-Object -TypeName PSCredential -ArgumentList 'kh',$storedKey
200+
$NuGetApiKey = $cred.GetNetworkCredential().Password
201+
Write-Verbose "Retrieved encrypted NuGetApiKey from $EncryptedApiKeyPath"
202+
}
203+
else {
204+
$cred = Get-Credential -Message "Enter your NuGet API Key in the password field" -UserName "user"
205+
$apiKeySS = $cred.Password
206+
$NuGetApiKey = $cred.GetNetworkCredential().Password
207+
}
208+
}
209+
210+
if (!$storedKey) {
211+
# Store encrypted NuGet API key to use for future invocations
212+
if (!$apiKeySS) {
213+
$apiKeySS = ConvertTo-SecureString -String $NuGetApiKey -AsPlainText -Force
214+
}
215+
216+
$parentDir = Split-Path $EncryptedApiKeyPath -Parent
217+
if (!(Test-Path -Path $parentDir)) {
218+
$null = New-Item -Path $parentDir -ItemType Directory
219+
}
220+
221+
$apiKeySS | ConvertFrom-SecureString | Export-Clixml $EncryptedApiKeyPath
222+
Write-Verbose "Stored encrypted NuGetApiKey to $EncryptedApiKeyPath"
223+
}
224+
225+
$NuGetApiKey
226+
}

examples/DebugTest.ps1

262 Bytes
Binary file not shown.

examples/PathProcessingNoWildcards.ps1

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
<#
2+
.SYNOPSIS
3+
Demonstrates how to write a command that works with paths that do
4+
not allow wildards but must exist.
5+
.DESCRIPTION
6+
This command does not require a LiteralPath parameter because the
7+
Path parameter can handle paths that use wildcard characters. That's
8+
because this command does not "resolve" the supplied path.
9+
.EXAMPLE
10+
C:\PS> Import-FileNoWildcard -Path ..\..\Tests\foo[1].txt -WhatIf
11+
This example shows how the Path parameter can handle a path that happens
12+
to use the wildcard chars "[" and "]".
13+
#>
114
function Import-FileNoWildcard {
215
[CmdletBinding(SupportsShouldProcess=$true)]
316
param(
@@ -13,10 +26,10 @@ function Import-FileNoWildcard {
1326
[string[]]
1427
$Path
1528
)
16-
29+
1730
begin {
1831
}
19-
32+
2033
process {
2134
# Modify [CmdletBinding()] to [CmdletBinding(SupportsShouldProcess=$true)]
2235
$paths = @()
@@ -28,19 +41,19 @@ function Import-FileNoWildcard {
2841
$psCmdlet.WriteError($errRecord)
2942
continue
3043
}
31-
44+
3245
# Resolve any relative paths
3346
$paths += $psCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath($aPath)
3447
}
35-
48+
3649
foreach ($aPath in $paths) {
3750
if ($pscmdlet.ShouldProcess($aPath, 'Operation')) {
3851
# Process each path
3952
$aPath
4053
}
4154
}
4255
}
43-
56+
4457
end {
4558
}
4659
}

0 commit comments

Comments
 (0)