1
+ $script :ProjectName = " CSharpProject" ; # The name of your C# Project
2
+ $script :DotnetVersion = " net7.0" ; # The version of .Net the project is targeted to
3
+ $script :GithubOrg = ' Org-or-Username' # This could be your github username if you're not working in a Github Org
4
+ $script :Repository = " https://github.com/$ ( $script :GithubOrg ) " ; # This is the Github Repo
5
+ $script :DeployBranch = ' main' ; # The branch that we deploy from, typically master or main
6
+ $script :Root = $PSScriptRoot ; # This will be the root of your Module Project, not the Repository Root
7
+ $script :Source = Join-Path $script :Root $script :ProjectName ; # This will be the root of your Module Project, not the Repository Root
8
+ $script :Output = Join-Path $script :Root ' output' ; # The module will be output into this folder
9
+ $script :Docs = Join-Path $script :Root ' docs' ; # The root folder for the PowerShell Module
10
+ $script :TestFile = (" TestResults_$ ( Get-Date - Format s) .xml" ).Replace(' :' , ' -' ); # The Pester Test output file
11
+ $script :DiscordChannel = " https://discord.com/channels/" # Discord Channel
12
+ $script :NugetOrg = " https://www.nuget.org/packages" # The Nuget.org URL
13
+
14
+ $PowerShellForGitHub = Get-Module - ListAvailable | Where-Object - Property Name -eq PowerShellForGitHub;
15
+ if ($PowerShellForGitHub )
16
+ {
17
+ Write-Host - ForegroundColor Blue " Info: PowerShellForGitHub Version $ ( $PowerShellForGitHub.Version ) Found" ;
18
+ Write-Host - ForegroundColor Blue " Info: This automation built with PowerShellForGitHub Version 0.16.1" ;
19
+ Import-Module PowerShellForGitHub;
20
+ }
21
+ else
22
+ {
23
+ throw " Please Install-Module -Name PowerShellForGitHub" ;
24
+ }
25
+ $DotnetTools = dotnet tool list -- global;
26
+ $DefaultDocumentationArray = ($DotnetTools | Select-String ' defaultdocumentation' ).ToString() -split ' \s+' ;
27
+
28
+ $DefaultDocumentation = New-Object PSObject - Property @ {
29
+ PackageId = $DefaultDocumentationArray [0 ]
30
+ Version = $DefaultDocumentationArray [1 ]
31
+ Commands = $DefaultDocumentationArray [2 ]
32
+ }
33
+
34
+ if ($DefaultDocumentation )
35
+ {
36
+ Write-Host - ForegroundColor Blue " Info: DefaultDocumentation Version $ ( $DefaultDocumentation.Version ) Found" ;
37
+ Write-Host - ForegroundColor Blue " Info: This automation built with PowerShellForGitHub Version 0.8.2" ;
38
+ }
39
+ else
40
+ {
41
+ throw " Please dotnet tool install DefaultDocumentation.Console -g" ;
42
+ }
43
+
44
+ $gitConfigList = (git config -- list) -split ' \n' ;
45
+ $gitConfig = @ {};
46
+ foreach ($str in $gitConfigList ) {
47
+ # Split the string into a key and a value
48
+ $keyValue = $str -split ' ='
49
+
50
+ # Add the key-value pair to the hashtable
51
+ $gitConfig [$keyValue [0 ]] = $keyValue [1 ]
52
+ }
53
+
54
+ if ($gitConfig [' user.email' ] -and $gitConfig [' user.name' ])
55
+ {
56
+ Write-Host - ForegroundColor Blue " Info: Git Config has been setup" ;
57
+ }
58
+ else
59
+ {
60
+ throw " please run git config user.email, git config user.name"
61
+ }
62
+
63
+ Write-Host - ForegroundColor Green " ProjectName : $ ( $script :ProjectName ) " ;
64
+ Write-Host - ForegroundColor Green " DotnetVersion : $ ( $script :DotnetVersion ) " ;
65
+ Write-Host - ForegroundColor Green " GithubOrg : $ ( $script :GithubOrg ) " ;
66
+ Write-Host - ForegroundColor Green " Root : $ ( $script :Root ) " ;
67
+ Write-Host - ForegroundColor Green " Source : $ ( $script :Source ) " ;
68
+ Write-Host - ForegroundColor Green " Output : $ ( $script :Output ) " ;
69
+ Write-Host - ForegroundColor Green " Docs : $ ( $script :Docs ) " ;
70
+ Write-Host - ForegroundColor Green " TestFile : $ ( $script :TestFile ) " ;
71
+ Write-Host - ForegroundColor Green " Repository : $ ( $script :Repository ) " ;
72
+ Write-Host - ForegroundColor Green " DiscordChannel : $ ( $script :DiscordChannel ) " ;
73
+ Write-Host - ForegroundColor Green " NugetOrg : $ ( $script :NugetOrg ) " ;
74
+ Write-Host - ForegroundColor Green " DeployBranch : $ ( $script :DeployBranch ) " ;
75
+
76
+ Task default - depends LocalUse
77
+ Task LocalUse - Description " Setup for local use and testing" - depends Clean , BuildProject
78
+ Task Build - depends LocalUse, TestProject
79
+ Task Package - depends UpdateReadme, CreateDocumentation, PackageProject
80
+ Task Deploy - depends CheckBranch, ReleaseNotes, PublishProject, NewTaggedRelease
81
+ Task Notifications - depends Post2Discord, Post2Bluesky
82
+
83
+ Task Clean - depends CleanProject {
84
+ $null = Remove-Item $script :Output - Recurse - ErrorAction Ignore
85
+ $null = New-Item - Type Directory - Path $script :Output - ErrorAction Ignore
86
+ $null = New-Item - Type Directory - Path $script :Docs - ErrorAction Ignore
87
+ $null = Remove-Item " $ ( $script :Root ) \TestResults*.xml"
88
+ }
89
+
90
+ Task UpdateReadme - Description " Update the README file" - Action {
91
+ $Project = [xml ](Get-Content - Path " $ ( $script :Source ) \$ ( $script :ProjectName ) .csproj" );
92
+ $PackageId = $Project.Project.PropertyGroup.PackageId ;
93
+ $readMe = Get-Item - Path " $ ( $script :Root ) \README.md"
94
+
95
+ $TableHeaders = " | Latest Version | Nuget.org | Issues | License | Discord |"
96
+ $Columns = " |-----------------|----------------|----------------|----------------|----------------|"
97
+ $VersionBadge = " [ /$ ( $script :ProjectName ) )]($ ( $script :Repository ) /$ ( $script :ProjectName ) /tags)"
98
+ $GalleryBadge = " [ )]($ ( $script :NugetOrg ) /$ ( $PackageId ) )"
99
+ $IssueBadge = " [ /$ ( $script :ProjectName ) )]($ ( $script :Repository ) /$ ( $script :ProjectName ) /issues)"
100
+ $LicenseBadge = " [ /$ ( $script :ProjectName ) )]($ ( $script :Repository ) /$ ( $script :ProjectName ) /blob/master/LICENSE)"
101
+ $DiscordBadge = " []($ ( $script :DiscordChannel ) )"
102
+
103
+ Write-Output $TableHeaders | Out-File $readMe.FullName - Force
104
+ Write-Output $Columns | Out-File $readMe.FullName - Append
105
+ Write-Output " | $ ( $VersionBadge ) | $ ( $GalleryBadge ) | $ ( $IssueBadge ) | $ ( $LicenseBadge ) | $ ( $DiscordBadge ) |" | Out-File $readMe.FullName - Append
106
+
107
+ Get-Content - Path " $ ( $script :Root ) \$ ( $script :ProjectName ) .md" | Out-File - FilePath $readMe.FullName - Append
108
+ }
109
+
110
+ Task NewTaggedRelease - Description " Create a tagged release" - Action {
111
+ $Github = (Get-Content - Path " $ ( $script :Root ) \github.json" ) | ConvertFrom-Json
112
+ $Credential = New-Credential - Username ignoreme - Password $Github.Token
113
+ Set-GitHubAuthentication - Credential $Credential
114
+ $Project = [xml ](Get-Content - Path " $ ( $script :Source ) \$ ( $script :ProjectName ) .csproj" );
115
+ $Version = $Project.Project.PropertyGroup.Version.ToString ();
116
+ git add .
117
+ git commit . - m " $ ( $script :ProjectName ) $ ( $Version ) Release"
118
+ git push
119
+ git tag - a v$version - m " $ ( $script :ProjectName ) Version $ ( $Version ) "
120
+ git push origin v$version
121
+ New-GitHubRelease - OwnerName $script :GithubOrg - RepositoryName $script :ProjectName - Tag " v$ ( $Version ) " - Name " v$ ( $Version ) "
122
+ }
123
+
124
+ Task Post2Discord - Description " Post a message to discord" - Action {
125
+ $Project = [xml ](Get-Content - Path " $ ( $script :Source ) \$ ( $script :ProjectName ) .csproj" );
126
+ $Version = $Project.Project.PropertyGroup.Version.ToString ();
127
+ $PackageId = $Project.Project.PropertyGroup.PackageId ;
128
+ $Discord = Get-Content - Path " $ ( $script :Root ) \discord.json" | ConvertFrom-Json
129
+ $Discord.message.content = " Version $ ( $version ) of $ ( $script :ProjectName ) released. Please visit Github ($ ( $script :Repository ) /$ ( $script :ProjectName ) ) or Nuget.org ($ ( $script :NugetOrg ) /$ ( $PackageId ) ) to download."
130
+ Invoke-RestMethod - Uri $Discord.uri - Body ($Discord.message | ConvertTo-Json - Compress) - Method Post - ContentType ' application/json; charset=UTF-8'
131
+ }
132
+
133
+ Task Post2Bluesky - Description " Post a message to bsky.app" - Action {
134
+ $Project = [xml ](Get-Content - Path " $ ( $script :Source ) \$ ( $script :ProjectName ) .csproj" );
135
+ $Version = $Project.Project.PropertyGroup.Version.ToString ();
136
+ $PackageId = $Project.Project.PropertyGroup.PackageId ;
137
+ $createdAt = Get-Date - Format " yyyy-MM-ddTHH:mm:ss.ffffffZ"
138
+ # Authenticate
139
+ $AuthBody = Get-Content - Path " $ ( $script :Root ) \bluesky.json"
140
+ $Handle = $AuthBody | ConvertFrom-Json | Select-Object - ExpandProperty Identifier
141
+ $Headers = @ {}
142
+ $Headers.Add (' Content-Type' , ' application/json' )
143
+ $Response = Invoke-RestMethod - Uri " https://bsky.social/xrpc/com.atproto.server.createSession" - Method Post - Body $AuthBody - Headers $Headers
144
+ # Create post
145
+ $Headers.Add (' Authorization' , " Bearer $ ( $Response.accessJwt ) " )
146
+ $Record = New-Object - TypeName psobject - Property @ {
147
+ ' $type' = " app.bsky.feed.post"
148
+ ' text' = " Version $ ( $version ) of $ ( $script :ProjectName ) released. Please visit Github ($ ( $script :Repository ) /$ ( $script :ProjectName ) ) or Nuget.org ($ ( $script :NugetOrg ) /$ ( $PackageId ) ) to download."
149
+ " createdAt" = $createdAt
150
+ }
151
+ $Post = New-Object - TypeName psobject - Property @ {
152
+ ' repo' = $Handle
153
+ ' collection' = ' app.bsky.feed.post'
154
+ record = $Record
155
+ }
156
+
157
+ Invoke-RestMethod - Uri " https://bsky.social/xrpc/com.atproto.repo.createRecord" - Method Post - Body ($Post | ConvertTo-Json - Compress) - Headers $Headers
158
+ }
159
+
160
+ Task CleanProject - Description " Clean the project before building" - Action {
161
+ dotnet clean " $ ( $script :Source ) \$ ( $script :ProjectName ) .sln" - c Release
162
+ dotnet clean " $ ( $script :Source ) \$ ( $script :ProjectName ) .sln" - c Debug
163
+ }
164
+
165
+ Task CreateDocumentation - Description " Create Docs" - Action {
166
+ defaultdocumentation - a " $ ( $script :Source ) \bin\Release\$ ( $script :DotnetVersion ) \$ ( $script :ProjectName ) .dll" - o $script :Docs
167
+ }
168
+
169
+ Task BuildProject - Description " Build the project" - Action {
170
+ dotnet build " $ ( $script :Root ) \$ ( $script :ProjectName ) \$ ( $script :ProjectName ) .csproj" - c Release
171
+ }
172
+
173
+ Task CheckBranch - Description " A test that should fail if we deploy while not on master" - Action {
174
+ $branch = git branch -- show-current
175
+ if ($branch -ne $script :DeployBranch )
176
+ {
177
+ [System.Net.WebSockets.WebSocketException ]$Exception = " You are not on the deployment branch: $ ( $script :DeployBranch ) "
178
+ [string ]$ErrorId = " Git.WrongBranch"
179
+ [System.Management.Automation.ErrorCategory ]$Category = [System.Management.Automation.ErrorCategory ]::InvalidOperation
180
+ $PSCmdlet.ThrowTerminatingError (
181
+ [System.Management.Automation.ErrorRecord ]::new(
182
+ $Exception ,
183
+ $ErrorId ,
184
+ $Category ,
185
+ $null
186
+ )
187
+ )
188
+ }
189
+ }
190
+
191
+ Task ReleaseNotes - Description " Create release notes file for project" - Action {
192
+ $Github = (Get-Content - Path " $ ( $script :Root ) \github.json" ) | ConvertFrom-Json
193
+ $Credential = New-Credential - Username ignoreme - Password $Github.Token
194
+ Set-GitHubAuthentication - Credential $Credential
195
+ $Milestone = (Get-GitHubMilestone - OwnerName $script :GithubOrg - RepositoryName $script :ProjectName - State Closed | Sort-Object - Descending - Property Number)[0 ]
196
+ if ($Milestone )
197
+ {
198
+ [System.Text.StringBuilder ]$stringbuilder = [System.Text.StringBuilder ]::new()
199
+ [void ]$stringbuilder.AppendLine ( " # $ ( $Milestone.title ) " )
200
+ [void ]$stringbuilder.AppendLine ( " " )
201
+ if ($Milestone.description )
202
+ {
203
+ [void ]$stringbuilder.AppendLine ( " $ ( $Milestone.description ) " )
204
+ }
205
+ $i = Get-GitHubIssue - OwnerName $script :GithubOrg - RepositoryName $script :ProjectName - RepositoryType All - Filter All - State Closed - MilestoneNumber $Milestone.Number ;
206
+ $headings = $i | ForEach-Object { $_.Labels.Name } | Sort-Object - Unique;
207
+ foreach ($heading in $headings )
208
+ {
209
+ [void ]$stringbuilder.AppendLine ( " " )
210
+ [void ]$stringbuilder.AppendLine ( " ## $ ( $heading.ToUpper ()) " )
211
+ [void ]$stringbuilder.AppendLine ( " " )
212
+ $issues = $i | ForEach-Object { if ($_.Labels.Name -eq $Heading ) { $_ } }
213
+ foreach ($issue in $issues )
214
+ {
215
+ [void ]$stringbuilder.AppendLine ( " * $ ( $issue.title ) #$ ( $issue.issuenumber ) " )
216
+ }
217
+ }
218
+ Out-File - FilePath " $ ( $script :Root ) \RELEASE.md" - InputObject $stringbuilder.ToString () - Encoding ascii - Force
219
+ $Project = [xml ](Get-Content - Path " $ ( $script :Source ) \$ ( $script :ProjectName ) .csproj" );
220
+ $ReleaseNotes = (Get-Content - Path " $ ( $script :Root ) \RELEASE.md" ).Replace(' ## ' , ' -' ).Replace(' # ' , ' ' ).Replace(' *' , ' -' )
221
+ $Project.Project.PropertyGroup.PackageReleaseNotes = $ReleaseNotes
222
+ $Project.Save (" $ ( $script :Source ) \$ ( $script :ProjectName ) .csproj" )
223
+ }
224
+ }
225
+
226
+ Task TestProject - Description " Test project" - Action {
227
+ dotnet test $script :Source \$script :ProjectName.sln -- logger " trx;LogFileName=$ ( $script :Root ) \$ ( $script :TestFile ) "
228
+ }
229
+
230
+ Task PackageProject - Description " Package the project" - Action {
231
+ dotnet pack $script :Source \$script :ProjectName.sln - o $script :Output - c Release
232
+ }
233
+
234
+ Task PublishProject - Description " Publish project to Nuget.org" - Action {
235
+ $Project = [xml ](Get-Content - Path " $ ( $script :Source ) \$ ( $script :ProjectName ) .csproj" );
236
+ $PackageId = $Project.Project.PropertyGroup.PackageId ;
237
+ $Version = $Project.Project.PropertyGroup.Version.ToString ();
238
+
239
+ $PackageFile = " $ ( $script :Output ) \$ ( $PackageId ) .$ ( $Version ) .nupkg"
240
+ dotnet nuget push $PackageFile
241
+ }
0 commit comments