Skip to content

Commit 5f6cd32

Browse files
Merge pull request #1 from mod-posh/dev
V1.1.0 Update
2 parents e9e65c0 + 607b8ee commit 5f6cd32

File tree

9 files changed

+334
-33
lines changed

9 files changed

+334
-33
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ This release contains additional documentation and more scripts
1111

1212
1. Vscode
1313
1. settings.json : How my vscode environment is set up
14+
2. C-Sharp Module
15+
3. Calculate expiration on tokens outside of tasks
1416

1517
---
1618

CSharpModule/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Simple Modules
2+
3+
Enclosed are the files I use to build and deploy PowerShell script modules that are composed of multiple Nested PowerShell modules. The basic file structure is included so you have an idea of what it should look like. There are several Modules that are required and the Tasks will check for them at the start.
4+
5+
## Dependencies
6+
7+
You will need to have the following modules installed :
8+
9+
1. BuildHelpers : This is used to help build the Module Manifest as well as the Module file
10+
2. PowerShellForGitHub : This is used for the ReleaseNotes task
11+
3. PlatyPS : This is used to set up and build the help documents
12+
4. Pester : This is the testing framework
13+
14+
Please see the [psakefile](psakefile.ps1) for the versions currently used.
15+
16+
## Supporting Files
17+
18+
There are several files used to help authenticate to various services and depending on your needs you may not need them or you may need something different. You can use them as is, but you will need to populate the various tokens and keys yourself. Also if there is not something you need you can use them as a template.
19+
20+
### ado.json
21+
22+
This file is used to authenticate into the Azure DevOps Rest API, you will need a token for this so please consult the [Documentation](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows). I include three items :
23+
24+
1. Orgname : The name of the Azure DevOps organization
25+
2. Token : The PAT Token
26+
3. Expiration : The expiration date
27+
28+
I include the Expiration so I can get a visual indication of how long before I need to renew the token, there is logic within the psakefile to display that out when you run it.
29+
30+
### discord.json
31+
32+
This file is used to post a message to Discord, I have a server that I set up and I post updates to channels for each of the modules that I use. If you wish to use the Post2Discord you will need to have a Discord account, set up a personal server and get the webhook url, please consult the [Documentation](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks).
33+
34+
### github.json
35+
36+
This file is used to authenticate into the Github API, you will need to set up a token for this so please consult the [Documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token). This is used for the ReleaseNotes task.
37+
38+
### nuget.config
39+
40+
This file was originally setup to publish to nuget.org and the PowerShell Gallery, I've left it as is, as I use the same file for publishing some of my code to nuget.org. You will need to create an API Key for this, please consult the [Documentation](https://learn.microsoft.com/en-us/powershell/scripting/gallery/concepts/publishing-guidelines?view=powershell-7.3).
41+
42+
## PsakeFile
43+
44+
This is the main file from which all automation is kicked off. I will go through the basic usage of this file, but for details on setting up for your use, please refer to the file itself. The Psakefile contains a collection of Tasks for more details on how to setup and use Psake please consult the [Documentation](https://psake.readthedocs.io/en/latest/). I will cover the basic tasks that I use so you have an understanding of how they work.
45+
46+
### Localuse
47+
48+
This task is run regularly, this is used to compile the module after local changes so I can test functionality. It basically runs a Build but does not do any of the testing that would normally go along with it.
49+
50+
### Build
51+
52+
This task runs LocalUse but also runs the PesterTests, so make sure you have some of those, otherwise remove the reference to the PesterTest Task. For more information on setting up Tests please refer to the [Documentation](https://pester.dev/docs/quick-start).
53+
54+
### Package
55+
56+
This task builds the help files, packages them up for deployment as well as updates the README.
57+
58+
### Deploy
59+
60+
This task will check to make sure we have checked out the Deployment Branch, the deployment should fail if we are not in the deployment branch. It will then create the Realease Notes, these are compiled from the Github Milestones, if you are not using them remove the reference to this task. It will then Publish the module to the PowerShellGallery, create and push a Tagged release as well are create the Github Release, and finally post a message to Discord, if you are not using Discord remove the reference to this task.

CSharpModule/discord.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"uri": "https://discord.com/api/webhooks/",
3+
"message": {
4+
"content": "This is a test message"
5+
}
6+
}

CSharpModule/github.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"Token": ""
3+
}

CSharpModule/nuget.config

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<config>
4+
<add key="defaultPushSource" value="https://www.powershellgallery.com/api/v2/package/" />
5+
</config>
6+
<packageSources>
7+
<clear />
8+
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
9+
<add key="PowershellGallery" value="https://www.powershellgallery.com/api/v2/package/" />
10+
</packageSources>
11+
<apikeys>
12+
<add key="PowershellGallery" value="" />
13+
</apikeys>
14+
</configuration>

CSharpModule/psakefile.ps1

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
$script:ModuleName = ''; # The name of your PowerShell module
2+
$script:ProjectName = ""; # The name of your C# Project
3+
$script:DotnetVersion = "net6.0"; # The version of .Net the project is targeted to
4+
$script:GithubOrg = '' # This could be your github username if you're not working in a Github Org
5+
$script:Repository = "https://github.com/$($script:GithubOrg)"; # This is the Github Repo
6+
$script:DeployBranch = 'master'; # The branch that we deploy from, typically master or main
7+
$script:Source = Join-Path $PSScriptRoot $script:ModuleName; # This will be the root of your Module Project, not the Repository Root
8+
$script:Output = Join-Path $PSScriptRoot 'output'; # The module will be output into this folder
9+
$script:Docs = Join-Path $PSScriptRoot 'docs'; # The root folder for the PowerShell Module
10+
$script:Destination = Join-Path $Output $script:ModuleName; # The PowerShell module folder that contains the manifest and other files
11+
$script:ModulePath = "$Destination\$script:ModuleName.psm1"; # The main PowerShell Module file
12+
$script:ManifestPath = "$Destination\$script:ModuleName.psd1"; # The main PowerShell Module Manifest
13+
$script:TestFile = ("TestResults_$(Get-Date -Format s).xml").Replace(':', '-'); # The Pester Test output file
14+
$script:PoshGallery = "https://www.powershellgallery.com/packages/$($script:ModuleName)" # The PowerShell Gallery URL
15+
16+
$BuildHelpers = Get-Module -ListAvailable | Where-Object -Property Name -eq BuildHelpers;
17+
if ($BuildHelpers)
18+
{
19+
Write-Host -ForegroundColor Blue "Info: BuildHelpers Version $($BuildHelpers.Version) Found";
20+
Write-Host -ForegroundColor Blue "Info: This automation built with BuildHelpers Version 2.0.16";
21+
Import-Module BuildHelpers;
22+
}
23+
else
24+
{
25+
throw "Please Install-Module -Name BuildHelpers";
26+
}
27+
$PowerShellForGitHub = Get-Module -ListAvailable | Where-Object -Property Name -eq PowerShellForGitHub;
28+
if ($PowerShellForGitHub)
29+
{
30+
Write-Host -ForegroundColor Blue "Info: PowerShellForGitHub Version $($PowerShellForGitHub.Version) Found";
31+
Write-Host -ForegroundColor Blue "Info: This automation built with PowerShellForGitHub Version 0.16.1";
32+
Import-Module PowerShellForGitHub;
33+
}
34+
else
35+
{
36+
throw "Please Install-Module -Name PowerShellForGitHub";
37+
}
38+
$PlatyPS = Get-Module -ListAvailable | Where-Object -Property Name -eq PlatyPS;
39+
if ($PlatyPS)
40+
{
41+
Write-Host -ForegroundColor Blue "Info: PowerShellForGitHub Version $($PlatyPS.Version) Found";
42+
Write-Host -ForegroundColor Blue "Info: This automation built with PowerShellForGitHub Version 0.14.2";
43+
Import-Module PlatyPS;
44+
}
45+
else
46+
{
47+
throw "Please Install-Module -Name PlatyPS";
48+
}
49+
$Pester = Get-Module -ListAvailable | Where-Object -Property Name -eq Pester;
50+
if ($Pester)
51+
{
52+
Write-Host -ForegroundColor Blue "Info: PowerShellForGitHub Version $($Pester.Version) Found";
53+
Write-Host -ForegroundColor Blue "Info: This automation built with PowerShellForGitHub Version 3.4.0";
54+
Import-Module Pester;
55+
}
56+
else
57+
{
58+
throw "Please Install-Module -Name Pester";
59+
}
60+
61+
Write-Host -ForegroundColor Green "ModuleName : $($script:ModuleName)";
62+
Write-Host -ForegroundColor Green "Githuborg : $($script:Source)";
63+
Write-Host -ForegroundColor Green "Source : $($script:Source)";
64+
Write-Host -ForegroundColor Green "Output : $($script:Output)";
65+
Write-Host -ForegroundColor Green "Docs : $($script:Docs)";
66+
Write-Host -ForegroundColor Green "Destination : $($script:Destination)";
67+
Write-Host -ForegroundColor Green "ModulePath : $($script:ModulePath)";
68+
Write-Host -ForegroundColor Green "ManifestPath : $($script:ManifestPath)";
69+
Write-Host -ForegroundColor Green "TestFile : $($script:TestFile)";
70+
Write-Host -ForegroundColor Green "Repository : $($script:Repository)";
71+
Write-Host -ForegroundColor Green "PoshGallery : $($script:PoshGallery)";
72+
Write-Host -ForegroundColor Green "DeployBranch : $($script:DeployBranch)";
73+
74+
Task default -depends LocalUser
75+
76+
Task LocalUse -Description "Setup for local use and testing" -depends Clean, BuildProject, CopyModuleFiles
77+
78+
Task Build -depends LocalUse, PesterTest
79+
Task Package -depends CreateExternalHelp, CreateCabFile, UpdateReadme
80+
Task Deploy -depends CheckBranch, ReleaseNotes, PublishModule, NewTaggedRelease, Post2Discord
81+
82+
Task Clean -depends CleanProject {
83+
$null = Remove-Item $Output -Recurse -ErrorAction Ignore
84+
$null = New-Item -Type Directory -Path $Destination
85+
}
86+
87+
Task UpdateReadme -Description "Update the README file" -Action {
88+
$readMe = Get-Item .\README.md
89+
90+
$TableHeaders = "| Latest Version | PowerShell Gallery | Issues | License | Discord |"
91+
$Columns = "|-----------------|----------------|----------------|----------------|----------------|"
92+
$VersionBadge = "[![Latest Version](https://img.shields.io/github/v/tag/$($script:GithubOrg)/$($script:ModuleName))]($($script:Repository)/$($script:ModuleName)/tags)"
93+
$GalleryBadge = "[![Powershell Gallery](https://img.shields.io/powershellgallery/dt/$($script:ModuleName))](https://www.powershellgallery.com/packages/$($script:ModuleName))"
94+
$IssueBadge = "[![GitHub issues](https://img.shields.io/github/issues/$($script:GithubOrg)/$($script:ModuleName))]($($script:Repository)/$($script:ModuleName)/issues)"
95+
$LicenseBadge = "[![GitHub license](https://img.shields.io/github/license/$($script:GithubOrg)/$($script:ModuleName))]($($script:Repository)/$($script:ModuleName)/blob/master/LICENSE)"
96+
$DiscordBadge = "[![Discord Server](https://assets-global.website-files.com/6257adef93867e50d84d30e2/636e0b5493894cf60b300587_full_logo_white_RGB.svg)]($($DiscordChannel))"
97+
98+
if (!(Get-Module -Name $script:ModuleName )) { Import-Module -Name $script:Destination }
99+
100+
Write-Output $TableHeaders | Out-File $readMe.FullName -Force
101+
Write-Output $Columns | Out-File $readMe.FullName -Append
102+
Write-Output "| $($VersionBadge) | $($GalleryBadge) | $($IssueBadge) | $($LicenseBadge) | $($DiscordBadge) |" | Out-File $readMe.FullName -Append
103+
104+
Get-Content "$($script:Docs)\$($script:ModuleName).md" | Select-Object -Skip 8 | ForEach-Object { $_.Replace('(', '(Docs/') } | Out-File $readMe.FullName -Append
105+
Write-Output "" | Out-File $readMe.FullName -Append
106+
}
107+
108+
Task NewTaggedRelease -Description "Create a tagged release" -Action {
109+
$Github = (Get-Content -Path "$($PSScriptRoot)\github.json") | ConvertFrom-Json
110+
$Credential = New-Credential -Username ignoreme -Password $Github.Token
111+
Set-GitHubAuthentication -Credential $Credential
112+
if (!(Get-Module -Name $script:ModuleName )) { Import-Module -Name "$($script:Output)\$($script:ModuleName)" }
113+
$Version = (Get-Module -Name $script:ModuleName | Select-Object -Property Version).Version.ToString()
114+
git add .
115+
git commit . -m "Updated ExternalHelp for $($Version) Release"
116+
git push
117+
git tag -a v$version -m "$($script:ModuleName) Version $($Version)"
118+
git push origin v$version
119+
New-GitHubRelease -OwnerName $script:GithubOrg -RepositoryName $script:ModuleName -Tag "v$($Version)" -Name "v$($Version)"
120+
}
121+
122+
Task Post2Discord -Description "Post a message to discord" -Action {
123+
$version = (Get-Module -Name $($script:ModuleName) | Select-Object -Property Version).Version.ToString()
124+
$Discord = Get-Content -Path "$($PSScriptRoot)\discord.json" | ConvertFrom-Json
125+
$Discord.message.content = "Version $($version) of $($script:ModuleName) released. Please visit Github ($($script:Repository)/$($script:ModuleName)) or PowershellGallery ($($script:PoshGallery)) to download."
126+
Invoke-RestMethod -Uri $Discord.uri -Body ($Discord.message | ConvertTo-Json -Compress) -Method Post -ContentType 'application/json; charset=UTF-8'
127+
}
128+
129+
Task CleanProject -Description "Clean the project before building" -Action {
130+
dotnet clean "$($PSScriptRoot)\$($script:ProjectName)\$($script:ProjectName).csproj"
131+
}
132+
133+
Task BuildProject -Description "Build the project" -Action {
134+
dotnet build "$($PSScriptRoot)\$($script:ProjectName)\$($script:ProjectName).csproj" -c Release
135+
}
136+
137+
Task CopyModuleFiles -Description "Copy files for the module" -Action {
138+
Copy-Item "$($PSScriptRoot)\$($script:ProjectName)\bin\Release\$($script:DotnetVersion)\*.dll" $script:Destination -Force
139+
Copy-Item "$($PSScriptRoot)\$($script:ModuleName).psd1" $script:Destination -Force
140+
}
141+
142+
Task CreateExternalHelp -Description "Create external help file" -Action {
143+
New-ExternalHelp -Path $script:Docs -OutputPath $script:Destination -Force
144+
}
145+
146+
Task CreateCabFile -Description "Create cab file for download" -Action {
147+
New-ExternalHelpCab -CabFilesFolder $script:Destination -LandingPagePath "$($script:Docs)\$($script:ModuleName).md" -OutputFolder "$($PSScriptRoot)\cabs\"
148+
}
149+
150+
Task PesterTest -description "Test module" -action {
151+
$TestResults = Invoke-Pester -OutputFormat NUnitXml -OutputFile "$($PSScriptRoot)\$($script:TestFile)";
152+
if ($TestResults.FailedCount -gt 0)
153+
{
154+
Write-Error "Failed [$($TestResults.FailedCount)] Pester tests"
155+
}
156+
}
157+
158+
Task CheckBranch -Description "A test that should fail if we deploy while not on master" -Action {
159+
$branch = git branch --show-current
160+
if ($branch -ne $script:DeployBranch)
161+
{
162+
[System.Net.WebSockets.WebSocketException]$Exception = "You are not on the deployment branch: $($script:DeployBranch)"
163+
[string]$ErrorId = "Git.WrongBranch"
164+
[System.Management.Automation.ErrorCategory]$Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
165+
$PSCmdlet.ThrowTerminatingError(
166+
[System.Management.Automation.ErrorRecord]::new(
167+
$Exception,
168+
$ErrorId,
169+
$Category,
170+
$null
171+
)
172+
)
173+
}
174+
}
175+
176+
Task ReleaseNotes -Description "Create release notes file for module manifest" -Action {
177+
$Github = (Get-Content -Path "$($PSScriptRoot)\github.token") | ConvertFrom-Json
178+
$Credential = New-Credential -Username ignoreme -Password $Github.Token
179+
Set-GitHubAuthentication -Credential $Credential
180+
$Milestone = (Get-GitHubMilestone -OwnerName $script:GithubOrg -RepositoryName $script:ModuleName -State Closed | Sort-Object -Property ClosedAt)[0]
181+
if ($Milestone)
182+
{
183+
[System.Text.StringBuilder]$stringbuilder = [System.Text.StringBuilder]::new()
184+
[void]$stringbuilder.AppendLine( "# $($Milestone.title)" )
185+
[void]$stringbuilder.AppendLine( "$($Milestone.description)" )
186+
$i = Get-GitHubIssue -OwnerName $script:GithubOrg -RepositoryName $script:ModuleName -RepositoryType All -Filter All -State Closed -MilestoneNumber $Milestone.Number;
187+
$headings = $i | ForEach-Object { $_.Labels.Name } | Sort-Object -Unique;
188+
foreach ($heading in $headings)
189+
{
190+
[void]$stringbuilder.AppendLine( "" )
191+
[void]$stringbuilder.AppendLine( "## $($heading.ToUpper())" )
192+
[void]$stringbuilder.AppendLine( "" )
193+
$issues = $i | ForEach-Object { if ($_.Labels.Name -eq $Heading) { $_ } }
194+
foreach ($issue in $issues)
195+
{
196+
[void]$stringbuilder.AppendLine( "* $($issue.title) #$($issue.issuenumber)" )
197+
}
198+
}
199+
Out-File -FilePath "$($PSScriptRoot)\RELEASE.md" -InputObject $stringbuilder.ToString() -Encoding ascii -Force
200+
}
201+
}
202+
203+
Task PublishModule -Description "Publish module to PowerShell Gallery" -Action {
204+
$config = [xml](Get-Content "$($PSScriptRoot)\nuget.config");
205+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
206+
$Parameters = @{
207+
Path = $script:Destination
208+
NuGetApiKey = "$($config.configuration.apikeys.add.value)"
209+
}
210+
Publish-Module @Parameters;
211+
}

ComplexModules/psakefile.ps1

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,23 @@ else
5757
throw "Please Install-Module -Name Pester";
5858
}
5959

60+
$Global:settings = Get-Content -Path "$($PSScriptRoot)\ado.json" | ConvertFrom-Json;
61+
foreach ($Name in ($Global:settings | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name))
62+
{
63+
$Org = $Global:settings.$Name;
64+
$ExpirationDate = Get-Date($Org.Expiration);
65+
$Today = (Get-Date(Get-Date -Format yyyy-MM-dd));
66+
$DateDiff = New-TimeSpan -Start $Today -End $ExpirationDate;
67+
if ($DateDiff.TotalDays -le 7)
68+
{
69+
Write-Host -ForegroundColor Red "Warning: $($Org.OrgName) Token Expires : $($Org.Expiration)";
70+
}
71+
else
72+
{
73+
Write-Host -ForegroundColor Blue "Info: $($Org.OrgName) Token Expires in $($DateDiff.TotalDays) days"
74+
}
75+
}
76+
6077
Write-Host -ForegroundColor Green "ModuleName : $($script:ModuleName)";
6178
Write-Host -ForegroundColor Green "Githuborg : $($script:Source)";
6279
Write-Host -ForegroundColor Green "Source : $($script:Source)";
@@ -80,22 +97,6 @@ Task Deploy -depends CheckBranch, ReleaseNotes, PublishModule, NewTaggedRelease,
8097
Task Clean {
8198
$null = Remove-Item $Output -Recurse -ErrorAction Ignore
8299
$null = New-Item -Type Directory -Path $Destination
83-
$Global:settings = Get-Content -Path "$($PSScriptRoot)\ado.json" | ConvertFrom-Json;
84-
foreach ($Name in ($Global:settings | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name))
85-
{
86-
$Org = $Global:settings.$Name;
87-
$ExpirationDate = Get-Date($Org.Expiration);
88-
$Today = (Get-Date(Get-Date -Format yyyy-MM-dd));
89-
$DateDiff = New-TimeSpan -Start $Today -End $ExpirationDate;
90-
if ($DateDiff.TotalDays -le 7)
91-
{
92-
Write-Host -ForegroundColor Red "Warning: $($Org.OrgName) Token Expires : $($Org.Expiration)";
93-
}
94-
else
95-
{
96-
Write-Host -ForegroundColor Blue "Info: $($Org.OrgName) Token Expires in $($DateDiff.TotalDays) days"
97-
}
98-
}
99100
}
100101

101102
Task BuildModule -description "Compile the Build Module" -action {

0 commit comments

Comments
 (0)