├── .github
├── CODEOWNERS
├── FUNDING.yml
├── pull_request_template.md
├── ISSUE_TEMPLATE
│ ├── feature_request.yml
│ └── bug_report.yml
└── workflows
│ ├── build-test-and-deploy-powershell-module.yml
│ └── build-and-test-powershell-module.yml
├── .vscode
├── extensions.json
└── tasks.json
├── src
└── ScriptModuleRepositoryTemplate
│ ├── TemplateRepoFiles
│ ├── _.vscode
│ │ ├── extensions.json
│ │ └── tasks.json
│ ├── src
│ │ └── __NewModuleName__
│ │ │ ├── __NewModuleName__.psm1
│ │ │ ├── __NewModuleName__.Tests.ps1
│ │ │ └── __NewModuleName__.psd1
│ ├── Changelog.md
│ ├── _.github
│ │ ├── pull_request_template.md
│ │ ├── ISSUE_TEMPLATE
│ │ │ ├── feature_request.yml
│ │ │ └── bug_report.yml
│ │ └── workflows
│ │ │ ├── build-and-test-powershell-module.yml
│ │ │ └── build-test-and-deploy-powershell-module.yml
│ ├── _.editorconfig
│ ├── License.md
│ ├── _.gitattributes
│ ├── deploy
│ │ └── Invoke-SmokeTests.ps1
│ ├── _.cspell.json
│ ├── _.devcontainer
│ │ └── devcontainer.json
│ ├── docs
│ │ └── Contributing.md
│ ├── ReadMe.md
│ └── _.gitignore
│ ├── ScriptModuleRepositoryTemplate.Tests.ps1
│ ├── ScriptModuleRepositoryTemplate.psd1
│ └── ScriptModuleRepositoryTemplate.psm1
├── Changelog.md
├── .editorconfig
├── License.md
├── .gitattributes
├── .cspell.json
├── .devcontainer
└── devcontainer.json
├── deploy
└── Invoke-SmokeTests.ps1
├── docs
├── ArchitectureDecisionRecords
│ ├── 001-Use-separate-repo-files-for-template.md
│ └── ArchitectureDecisionRecords.md
└── Contributing.md
├── _InitializeRepository.ps1
├── .gitignore
└── ReadMe.md
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Auto-include these teams/individuals on all PRs.
2 | * @deadlydog
3 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "EditorConfig.EditorConfig",
4 | "ms-vscode.powershell",
5 | "streetsidesoftware.code-spell-checker",
6 | "TylerLeonhardt.vscode-inline-values-powershell",
7 | "yzhang.markdown-all-in-one"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "EditorConfig.EditorConfig",
4 | "ms-vscode.powershell",
5 | "streetsidesoftware.code-spell-checker",
6 | "TylerLeonhardt.vscode-inline-values-powershell"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/src/__NewModuleName__/__NewModuleName__.psm1:
--------------------------------------------------------------------------------
1 | # UPDATE ME: This is just example code. Replace this file's contents with your module code.
2 |
3 | function Get-HelloWorld {
4 | [CmdletBinding()]
5 | Param ()
6 |
7 | Write-Output "Hello, World!"
8 | }
9 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/Changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | This page is a list of _notable_ changes made in each version.
4 |
5 | ## v1.0.0 - January 1, 2099
6 |
7 | Features:
8 |
9 | - Initial release
10 |
11 | Fixes:
12 |
13 | - Fixes
14 |
15 | Community contributions:
16 |
17 | - @person: Contribution
18 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/src/__NewModuleName__/__NewModuleName__.Tests.ps1:
--------------------------------------------------------------------------------
1 | using module '.\__NewModuleName__.psm1'
2 |
3 | # UPDATE ME: This is just example code. Replace the code below with your module's tests.
4 | Describe 'Get-HelloWorld' {
5 | It 'Should return "Hello, World!"' {
6 | $expected = 'Hello, World!'
7 | $result = Get-HelloWorld
8 | $result | Should -Be $expected
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: deadlydog # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: https://www.paypal.me/deadlydogDan # Replace with a single custom sponsorship URL
13 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ### Summary
2 |
3 | A brief summary of the change this PR brings.
4 |
5 | ### Checklist
6 |
7 | Addresses issue #ISSUE_NUMBER (if applicable)
8 |
9 | - [ ] Tests have been added for this code change (if applicable)
10 | - [ ] Docs have been added or updated (if applicable)
11 | - [ ] Code format follows the project style
12 | - [ ] All new and existing tests passed
13 |
14 | ### What type of changes does this PR include
15 |
16 | - [ ] Bug fix (non-breaking change which fixes an issue)
17 | - [ ] New feature (non-breaking change which adds functionality)
18 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
19 |
20 | ### Describe the change in more detail
21 |
22 | What is the motivation for this change? What does it do? What problem does it solve?
23 |
24 | ### Additional information
25 |
26 | Any other information about the change, such as screenshots, example flows, etc.
27 |
--------------------------------------------------------------------------------
/Changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | This page is a list of _notable_ changes made in each version.
4 |
5 | ## v1.2.0 - March 27, 2025
6 |
7 | Features:
8 |
9 | - Add VS Code task to run CSpell spellcheck when building, as well as a stand-alone VS Code task.
10 | - Update dev container to the latest PowerShell image and have it install PSScriptAnalyzer, Pester, and CSpell.
11 | - Update default Contributing.md to include information around local development and building.
12 |
13 | ## v1.1.0 - April 20, 2024
14 |
15 | Features:
16 |
17 | - Run Pester tests during the build on Windows PowerShell too, to catch backward-incompatible changes earlier.
18 |
19 | Fixes:
20 |
21 | - Use backslash instead of forward-slash to import module in Pester tests for Windows PowerShell backward compatibility.
22 |
23 | ## v1.0.0 - April 13, 2024
24 |
25 | Features:
26 |
27 | - Initial release.
28 | Currently only supports creating repos for GitHub Actions workflows that publish to the public PowerShell Gallery.
29 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file should only include settings that affect the physical contents of the file, not just how it appears in an editor.
2 | # Do not include personal preference presentation settings like a tab's `indent_size` in this file; those should be specified
3 | # in a parent .editorconfig file outside of the repository.
4 | # v1.7 - Source: https://gist.github.com/deadlydog/bd000162e85c155b243a712c16f7411c
5 |
6 | # Ensure that personal preference presentation settings can be inherited from parent .editorconfig files.
7 | root = false
8 |
9 | #### Core EditorConfig Options ####
10 |
11 | [*]
12 | charset = utf-8
13 | indent_style = tab
14 | end_of_line = crlf
15 | insert_final_newline = true
16 | trim_trailing_whitespace = true
17 |
18 | # For some languages, the number of spaces used for indentation matters.
19 | [*.{tf,md,psd1,pp,yml,yaml}]
20 | indent_style = space
21 | indent_size = 2
22 |
23 | #### .NET Coding Conventions ####
24 |
25 | csharp_style_namespace_declarations = file_scoped
26 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ### Summary
2 |
3 | A brief summary of the change this PR brings.
4 |
5 | ### Checklist
6 |
7 | Addresses issue #ISSUE_NUMBER (if applicable)
8 |
9 | - [ ] Tests have been added for this code change (if applicable)
10 | - [ ] Docs have been added or updated (if applicable)
11 | - [ ] Code format follows the project style
12 | - [ ] All new and existing tests passed
13 |
14 | ### What type of changes does this PR include
15 |
16 | - [ ] Bug fix (non-breaking change which fixes an issue)
17 | - [ ] New feature (non-breaking change which adds functionality)
18 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
19 |
20 | ### Describe the change in more detail
21 |
22 | What is the motivation for this change? What does it do? What problem does it solve?
23 |
24 | ### Additional information
25 |
26 | Any other information about the change, such as screenshots, example flows, etc.
27 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.editorconfig:
--------------------------------------------------------------------------------
1 | # This file should only include settings that affect the physical contents of the file, not just how it appears in an editor.
2 | # Do not include personal preference presentation settings like a tab's `indent_size` in this file; those should be specified
3 | # in a parent .editorconfig file outside of the repository.
4 | # v1.7 - Source: https://gist.github.com/deadlydog/bd000162e85c155b243a712c16f7411c
5 |
6 | # Ensure that personal preference presentation settings can be inherited from parent .editorconfig files.
7 | root = false
8 |
9 | #### Core EditorConfig Options ####
10 |
11 | [*]
12 | charset = utf-8
13 | indent_style = tab
14 | end_of_line = crlf
15 | insert_final_newline = true
16 | trim_trailing_whitespace = true
17 |
18 | # For some languages, the number of spaces used for indentation matters.
19 | [*.{tf,md,psd1,pp,yml,yaml}]
20 | indent_style = space
21 | indent_size = 2
22 |
23 | #### .NET Coding Conventions ####
24 |
25 | csharp_style_namespace_declarations = file_scoped
26 |
--------------------------------------------------------------------------------
/License.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Daniel Schroeder
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Use 'git add --renormalize .' to apply rules to files added to the repository before the .gitattributes file.
2 |
3 | # Have GitHub language statistics ignore generated files.
4 | # More info: https://github.com/github-linguist/linguist/blob/master/docs/overrides.md
5 | #generatedFiles/** linguist-generated=true
6 |
7 | # Auto detect text files and perform LF normalization.
8 | * text=auto
9 |
10 | # Documents
11 | *.doc diff=astextplain
12 | *.DOC diff=astextplain
13 | *.docx diff=astextplain
14 | *.DOCX diff=astextplain
15 | *.dot diff=astextplain
16 | *.DOT diff=astextplain
17 | *.pdf diff=astextplain
18 | *.PDF diff=astextplain
19 | *.rtf diff=astextplain
20 | *.RTF diff=astextplain
21 | *.md text
22 | *.adoc text
23 | *.textile text
24 | *.mustache text
25 | *.csv text
26 | *.tab text
27 | *.tsv text
28 | *.sql text
29 |
30 | # Graphics
31 | *.png binary
32 | *.jpg binary
33 | *.jpeg binary
34 | *.gif binary
35 | *.tif binary
36 | *.tiff binary
37 | *.ico binary
38 | # SVG treated as an asset (binary) by default, but it's actually just a text file so let's treat it as one.
39 | #*.svg binary
40 | *.svg text
41 | *.eps binary
42 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/License.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) __CurrentYear__ __IndividualOrOrganizationName__
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.gitattributes:
--------------------------------------------------------------------------------
1 | # Use 'git add --renormalize .' to apply rules to files added to the repository before the .gitattributes file.
2 |
3 | # Have GitHub language statistics ignore generated files.
4 | # More info: https://github.com/github-linguist/linguist/blob/master/docs/overrides.md
5 | #generatedFiles/** linguist-generated=true
6 |
7 | # Auto detect text files and perform LF normalization.
8 | * text=auto
9 |
10 | # Documents
11 | *.doc diff=astextplain
12 | *.DOC diff=astextplain
13 | *.docx diff=astextplain
14 | *.DOCX diff=astextplain
15 | *.dot diff=astextplain
16 | *.DOT diff=astextplain
17 | *.pdf diff=astextplain
18 | *.PDF diff=astextplain
19 | *.rtf diff=astextplain
20 | *.RTF diff=astextplain
21 | *.md text
22 | *.adoc text
23 | *.textile text
24 | *.mustache text
25 | *.csv text
26 | *.tab text
27 | *.tsv text
28 | *.sql text
29 |
30 | # Graphics
31 | *.png binary
32 | *.jpg binary
33 | *.jpeg binary
34 | *.gif binary
35 | *.tif binary
36 | *.tiff binary
37 | *.ico binary
38 | # SVG treated as an asset (binary) by default, but it's actually just a text file so let's treat it as one.
39 | #*.svg binary
40 | *.svg text
41 | *.eps binary
42 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/deploy/Invoke-SmokeTests.ps1:
--------------------------------------------------------------------------------
1 | # These tests are runs as part of the deployment process to ensure the newly published module is working as expected.
2 | # These tests run against the installed module, not the source code, so they are a real-world test and should not use mocks.
3 | # Since mocks are not used, be careful to not rely on state stored on the machine, such as a module configuration file.
4 | # This is a great place to put tests that differ between operating systems, since they will be ran on multiple platforms.
5 | # Keep in mind that these tests can only call the public functions in the module, not the private functions.
6 | # To run these tests on your local machine, see the comments in the BeforeAll block.
7 |
8 | BeforeAll {
9 | Import-Module -Name '__NewModuleName__' -Force
10 |
11 | # To run these tests on your local machine, comment out the Import-Module command above and uncomment the one below.
12 | # Do this to use the module version from source code, not the installed version.
13 | # This is necessary to test functionality that you've added to the module, but have not yet published and installed.
14 | # Import-Module "$PSScriptRoot\..\src\__NewModuleName__" -Force
15 | }
16 |
17 | Describe 'Get-HelloWorld' {
18 | It 'Should return "Hello, World!"' {
19 | $expected = 'Hello, World!'
20 | $result = Get-HelloWorld
21 | $result | Should -Be $expected
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.cspell.json:
--------------------------------------------------------------------------------
1 | // This is the configuration file for cspell (code spell check).
2 | // Use this to define legitimate words not in the dictionary, and other words the spellcheck should ignore.
3 | // Used by the streetsidesoftware.code-spell-checker VS Code extension and streetsidesoftware/cspell-action@v3 GitHub Action.
4 | // For more information on configuration, see https://cspell.org/configuration/.
5 | {
6 | "version": "0.2",
7 | "language": "en",
8 | "ignorePaths": [
9 | ".devcontainer/devcontainer.json",
10 | ".github/workflows/*.yml",
11 | ".gitignore"
12 | ],
13 | "words": [
14 | "behaviour",
15 | "gif",
16 | "gifs",
17 | "hashtable",
18 | "remoting",
19 | "runspace",
20 | "runspaces"
21 | ],
22 | "ignoreWords": [
23 | "chsh", // Change shell
24 | "CICD", // Continuous Integration/Continuous Deployment
25 | "CODEOWNERS", // GitHub CODEOWNERS
26 | "Codespace", // GitHub Codespaces
27 | "Codespaces", // GitHub Codespaces
28 | "devcontainer", // VS Code devcontainer
29 | "devcontainers", // VS Code devcontainers
30 | "gittools", // GitHub action author
31 | "Hmmss", // Time format
32 | "jacoco", // Java code coverage
33 | "Leonhardt", // GitHub action author
34 | "madrapps", // GitHub action author
35 | "nupkg", // NuGet package
36 | "nunit", // .NET unit testing framework
37 | "pwsh", // PowerShell Core
38 | "yzhang" // GitHub action author
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/.cspell.json:
--------------------------------------------------------------------------------
1 | // This is the configuration file for cspell (code spell check).
2 | // Use this to define legitimate words not in the dictionary, and other words the spellcheck should ignore.
3 | // Used by the streetsidesoftware.code-spell-checker VS Code extension and streetsidesoftware/cspell-action@v3 GitHub Action.
4 | // For more information on configuration, see https://cspell.org/configuration/.
5 | {
6 | "version": "0.2",
7 | "language": "en",
8 | "ignorePaths": [
9 | "**/_.gitattributes",
10 | "**/_.gitignore",
11 | "**/devcontainer.json",
12 | "**/build-and-test-powershell-module.yml",
13 | "**/build-test-and-deploy-powershell-module.yml",
14 | "**/bin/**", // Ignore C# build output files.
15 | "**/obj/**", // Ignore C# build output files.
16 | ".gitignore"
17 | ],
18 | "words": [
19 | "behaviour",
20 | "gif",
21 | "gifs",
22 | "hashtable",
23 | "remoting",
24 | "runspace",
25 | "runspaces"
26 | ],
27 | "ignoreWords": [
28 | "chsh", // Change shell
29 | "CICD", // Continuous Integration/Continuous Deployment
30 | "CODEOWNERS", // GitHub CODEOWNERS
31 | "Codespace", // GitHub Codespaces
32 | "Codespaces", // GitHub Codespaces
33 | "devcontainer", // VS Code devcontainer
34 | "devcontainers", // VS Code devcontainers
35 | "gittools", // GitHub action author
36 | "Hmmss", // Time format
37 | "jacoco", // Java code coverage
38 | "Leonhardt", // GitHub action author
39 | "madrapps", // GitHub action author
40 | "nupkg", // NuGet package
41 | "nunit", // .NET unit testing framework
42 | "pwsh", // PowerShell Core
43 | "yzhang" // GitHub action author
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: ✨ Feature request
2 | description: Suggest an idea for this project
3 | title: "Feature Request:
"
4 | labels: ["enhancement"]
5 | body:
6 | - type: checkboxes
7 | attributes:
8 | label: Is there an existing issue for this?
9 | description: Please search to see if a feature request already exists for what you are suggesting.
10 | options:
11 | - label: I have searched the existing issues
12 | required: true
13 | - type: textarea
14 | attributes:
15 | label: Problem to solve
16 | description: Is your feature request related to a problem? Please describe.
17 | placeholder: |
18 | Ex. I'm always frustrated when '...'.
19 | Ex. I want to be able to '...'.
20 | validations:
21 | required: false
22 | - type: textarea
23 | attributes:
24 | label: Summary
25 | description: Describe the solution you'd like.
26 | placeholder: |
27 | A clear and concise description of what you want to happen.
28 | validations:
29 | required: true
30 | - type: textarea
31 | attributes:
32 | label: Alternative solutions
33 | description: Describe any alternative solutions you've considered that may address the issue.
34 | validations:
35 | required: false
36 | - type: textarea
37 | attributes:
38 | label: Visuals
39 | description: If applicable, add screenshots, gifs, or videos to help explain the feature you are requesting.
40 | placeholder: |
41 | Here is a mockup of the feature I am suggesting:
42 | 
43 | validations:
44 | required: false
45 | - type: textarea
46 | attributes:
47 | label: Anything else?
48 | description: Add any other context or information about the feature request here.
49 | validations:
50 | required: false
51 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: ✨ Feature request
2 | description: Suggest an idea for this project
3 | title: "Feature Request: "
4 | labels: ["enhancement"]
5 | body:
6 | - type: checkboxes
7 | attributes:
8 | label: Is there an existing issue for this?
9 | description: Please search to see if a feature request already exists for what you are suggesting.
10 | options:
11 | - label: I have searched the existing issues
12 | required: true
13 | - type: textarea
14 | attributes:
15 | label: Problem to solve
16 | description: Is your feature request related to a problem? Please describe.
17 | placeholder: |
18 | Ex. I'm always frustrated when '...'.
19 | Ex. I want to be able to '...'.
20 | validations:
21 | required: false
22 | - type: textarea
23 | attributes:
24 | label: Summary
25 | description: Describe the solution you'd like.
26 | placeholder: |
27 | A clear and concise description of what you want to happen.
28 | validations:
29 | required: true
30 | - type: textarea
31 | attributes:
32 | label: Alternative solutions
33 | description: Describe any alternative solutions you've considered that may address the issue.
34 | validations:
35 | required: false
36 | - type: textarea
37 | attributes:
38 | label: Visuals
39 | description: If applicable, add screenshots, gifs, or videos to help explain the feature you are requesting.
40 | placeholder: |
41 | Here is a mockup of the feature I am suggesting:
42 | 
43 | validations:
44 | required: false
45 | - type: textarea
46 | attributes:
47 | label: Anything else?
48 | description: Add any other context or information about the feature request here.
49 | validations:
50 | required: false
51 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see: https://aka.ms/devcontainer.json.
2 | // For config options, see: https://github.com/devcontainers/templates/tree/main/src/powershell.
3 | {
4 | "name": "PowerShell",
5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6 | "image": "mcr.microsoft.com/dotnet/sdk:9.0",
7 | "features": {
8 | "ghcr.io/devcontainers/features/common-utils:2": {
9 | "installZsh": "true",
10 | "username": "vscode",
11 | "upgradePackages": "false",
12 | "nonFreePackages": "true"
13 | } //, // Uncomment this line and the one below to install the dotnet CLI.
14 | //"ghcr.io/devcontainers/features/dotnet:2": "latest" // Installs the dotnet CLI.
15 | },
16 |
17 | // Set pwsh as the default shell for the devcontainer, install required PowerShell modules, and install NPM and CSpell.
18 | // If you do not plan to use CSpell, you can remove everything after and including 'sudo apt update'.
19 | "postCreateCommand": "sudo chsh vscode -s \"$(which pwsh)\"; pwsh -c \"Install-Module Pester -Force\"; pwsh -c \"Install-Module PSScriptAnalyzer -Force\"; sudo apt update; sudo DEBIAN_FRONTEND=noninteractive apt install -y npm; npm install cspell",
20 |
21 | // Configure tool-specific properties.
22 | "customizations": {
23 | // Configure properties specific to VS Code.
24 | "vscode": {
25 | // Set *default* container specific settings.json values on container create.
26 | "settings": {
27 | "terminal.integrated.defaultProfile.linux": "pwsh"
28 | },
29 | // Add the IDs of extensions you want installed when the container is created.
30 | "extensions": [
31 | "EditorConfig.EditorConfig",
32 | "ms-vscode.powershell",
33 | "streetsidesoftware.code-spell-checker",
34 | "TylerLeonhardt.vscode-inline-values-powershell",
35 | "yzhang.markdown-all-in-one"
36 | ]
37 | }
38 | }
39 |
40 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
41 | // "forwardPorts": [],
42 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
43 | // "remoteUser": "root"
44 | }
45 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see: https://aka.ms/devcontainer.json.
2 | // For config options, see: https://github.com/devcontainers/templates/tree/main/src/powershell.
3 | {
4 | "name": "PowerShell",
5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6 | "image": "mcr.microsoft.com/dotnet/sdk:9.0",
7 | "features": {
8 | "ghcr.io/devcontainers/features/common-utils:2": {
9 | "installZsh": "true",
10 | "username": "vscode",
11 | "upgradePackages": "false",
12 | "nonFreePackages": "true"
13 | }//, // Uncomment this line and the one below to install the dotnet CLI.
14 | //"ghcr.io/devcontainers/features/dotnet:2": "latest" // Installs the dotnet CLI.
15 | },
16 |
17 | // Set pwsh as the default shell for the devcontainer, install required PowerShell modules, and install NPM and CSpell.
18 | // If you do not plan to use CSpell, you can remove everything after and including 'sudo apt update'.
19 | "postCreateCommand": "sudo chsh vscode -s \"$(which pwsh)\"; pwsh -c \"Install-Module Pester -Force\"; pwsh -c \"Install-Module PSScriptAnalyzer -Force\"; sudo apt update; sudo DEBIAN_FRONTEND=noninteractive apt install -y npm; npm install cspell",
20 |
21 | // Configure tool-specific properties.
22 | "customizations": {
23 | // Configure properties specific to VS Code.
24 | "vscode": {
25 | // Set *default* container specific settings.json values on container create.
26 | "settings": {
27 | "terminal.integrated.defaultProfile.linux": "pwsh"
28 | },
29 | // Add the IDs of extensions you want installed when the container is created.
30 | "extensions": [
31 | "EditorConfig.EditorConfig",
32 | "ms-vscode.powershell",
33 | "streetsidesoftware.code-spell-checker",
34 | "TylerLeonhardt.vscode-inline-values-powershell",
35 | "yzhang.markdown-all-in-one"
36 | ]
37 | }
38 | }
39 |
40 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
41 | // "forwardPorts": [],
42 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
43 | // "remoteUser": "root"
44 | }
45 |
--------------------------------------------------------------------------------
/deploy/Invoke-SmokeTests.ps1:
--------------------------------------------------------------------------------
1 | # These tests are runs as part of the deployment process to ensure the newly published module is working as expected.
2 | # These tests run against the installed module, not the source code, so they are a real-world test and should not use mocks.
3 | # Since mocks are not used, be careful to not rely on state stored on the machine, such as a module configuration file.
4 | # This is a great place to put tests that differ between operating systems, since they will be ran on multiple platforms.
5 | # Keep in mind that these tests can only call the public functions in the module, not the private functions.
6 | # To run these tests on your local machine, see the comments in the BeforeAll block.
7 |
8 | BeforeAll {
9 | Import-Module -Name 'ScriptModuleRepositoryTemplate' -Force
10 |
11 | # To run these tests on your local machine, comment out the Import-Module command above and uncomment the one below.
12 | # Do this to use the module version from source code, not the installed version.
13 | # This is necessary to test functionality that you've added to the module, but have not yet published and installed.
14 | # Import-Module "$PSScriptRoot\..\src\ScriptModuleRepositoryTemplate" -Force
15 | }
16 |
17 | Describe 'New-PowerShellScriptModuleRepository' {
18 | It 'Should create a new directory with the module repository files' {
19 | # Arrange.
20 | $repositoryDirectoryPath = "$TestDrive\NewModuleRepo"
21 | $moduleName = 'NewModule'
22 | $organizationName = 'My Organization'
23 |
24 | $expectedModuleDirectoryPath = Join-Path -Path $repositoryDirectoryPath -ChildPath "src\$moduleName"
25 | $expectedModuleFilePath = Join-Path -Path $expectedModuleDirectoryPath -ChildPath "$moduleName.psm1"
26 | $expectedModuleManifestFilePath = Join-Path -Path $expectedModuleDirectoryPath -ChildPath "$moduleName.psd1"
27 | $expectedModuleTestsFilePath = Join-Path -Path $expectedModuleDirectoryPath -ChildPath "$moduleName.Tests.ps1"
28 |
29 | # Act.
30 | New-PowerShellScriptModuleRepository -RepositoryDirectoryPath $repositoryDirectoryPath -ModuleName $moduleName -OrganizationName $organizationName
31 |
32 | # Assert.
33 | $expectedModuleDirectoryPath | Should -Exist
34 | $expectedModuleFilePath | Should -Exist
35 | $expectedModuleManifestFilePath | Should -Exist
36 | $expectedModuleTestsFilePath | Should -Exist
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/docs/ArchitectureDecisionRecords/001-Use-separate-repo-files-for-template.md:
--------------------------------------------------------------------------------
1 | # 001 - Use separate repo files for template
2 |
3 | ## Status
4 |
5 | Accepted 2024-02-25
6 |
7 | ## Context
8 |
9 | When I originally conceived of this template repository I was just going to have the template files be this actual repo's files, and I started out the implementation that way.
10 | When writing the script that would be used to initialize the template repository with the user's module information, I had to keep track of which files in the repo would need to be updated (e.g. the new module directory and files, License, Changelog, Smoke Tests).
11 | This had me questioning if it would be better to use totally separate files for all of the template files, and just have the initialization script overwrite all of the repo contents with the template files.
12 |
13 | ## Options Considered
14 |
15 | 1. Use this repo's files as the template files.
16 | 1. Use separate files for all of the template files (stored in their own directory).
17 |
18 | ### Use this repo's files as the template files
19 |
20 | Pros:
21 |
22 | - Fewer files in the repo.
23 | - Possibly less confusing to the user, as it follows the traditional idea of what a template repository is.
24 | e.g. the files in the repo are where you start from.
25 |
26 | ### Use separate files for all of the template files
27 |
28 | Pros:
29 |
30 | - Less logic in the initialization script, as we don't need to keep track of which files are for the repo and which are for the template.
31 | - Easier to update the template files without having to worry about the repo files.
32 | - Less confusion about which files are for this repo vs. which are for the template.
33 | - Allows users to more easily create their own custom template files easier if they want to; they just need to update the template files.
34 | - Allows for the PowerShell module to be able to create new starter repos, rather than having to clone the GitHub repo template.
35 |
36 | Cons:
37 |
38 | - Some of the files in the repo will be duplicates of the template files, so when adding new feature/changes we may need to remember to make them in both this repo and the template files.
39 |
40 | ## Decision
41 |
42 | We are going to store all of the template repo files in their own directory, as this separation makes it easier to understand which files are part of the template and which are part of this repo.
43 | It also allows for users to more easily create their own custom templates, and to use the PowerShell module to create new starter repos.
44 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/ScriptModuleRepositoryTemplate.Tests.ps1:
--------------------------------------------------------------------------------
1 | using module '.\ScriptModuleRepositoryTemplate.psm1'
2 |
3 | Describe 'New-PowerShellScriptModuleRepository' {
4 | BeforeEach {
5 | [string] $TemporaryRepoPath = "$TestDrive\NewModule"
6 | if (Test-Path -Path $TemporaryRepoPath) {
7 | Remove-Item -Path $TemporaryRepoPath -Recurse -Force
8 | }
9 | }
10 |
11 | It 'Should create a new directory with the module repository files using the specified module name' {
12 | # Arrange.
13 | $repositoryDirectoryPath = $TemporaryRepoPath
14 | $moduleName = 'NewModule'
15 | $organizationName = 'My Organization'
16 |
17 | $expectedModuleDirectoryPath = Join-Path -Path $repositoryDirectoryPath -ChildPath "src\$moduleName"
18 | $expectedModuleFilePath = Join-Path -Path $expectedModuleDirectoryPath -ChildPath "$moduleName.psm1"
19 | $expectedModuleManifestFilePath = Join-Path -Path $expectedModuleDirectoryPath -ChildPath "$moduleName.psd1"
20 | $expectedModuleTestsFilePath = Join-Path -Path $expectedModuleDirectoryPath -ChildPath "$moduleName.Tests.ps1"
21 |
22 | # Act.
23 | New-PowerShellScriptModuleRepository -RepositoryDirectoryPath $repositoryDirectoryPath -ModuleName $moduleName -OrganizationName $organizationName
24 |
25 | # Assert.
26 | $expectedModuleDirectoryPath | Should -Exist
27 | $expectedModuleFilePath | Should -Exist
28 | $expectedModuleManifestFilePath | Should -Exist
29 | $expectedModuleTestsFilePath | Should -Exist
30 | }
31 |
32 | It 'Should replace all dot-files and dot-directories prefixed with an underscore to remove the underscore' {
33 | # Arrange.
34 | $repositoryDirectoryPath = $TemporaryRepoPath
35 | $moduleName = 'NewModule'
36 | $organizationName = 'My Organization'
37 |
38 | $expectedDotDirectoryPath = Join-Path -Path $repositoryDirectoryPath -ChildPath ".vscode"
39 | $expectedDotFilePath = Join-Path -Path $repositoryDirectoryPath -ChildPath ".gitignore"
40 |
41 | # Act.
42 | New-PowerShellScriptModuleRepository -RepositoryDirectoryPath $repositoryDirectoryPath -ModuleName $moduleName -OrganizationName $organizationName
43 |
44 | # Assert.
45 |
46 | # No files should start with '_.'
47 | $repoFilePaths = Get-ChildItem -Path $repositoryDirectoryPath -Recurse -Force
48 | $repoFilePaths | ForEach-Object {
49 | [string] $fileName = $_.Name
50 | $fileName | Should -Not -Match '^_\.'
51 | }
52 |
53 | # Verify at least one Dot directory and file were renamed properly.
54 | $expectedDotDirectoryPath | Should -Exist
55 | $expectedDotFilePath | Should -Exist
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/docs/ArchitectureDecisionRecords/ArchitectureDecisionRecords.md:
--------------------------------------------------------------------------------
1 | # Architecture Decision Records (ADR)
2 |
3 | Here is where we log decisions we make about the architecture of the project.
4 | Each decision will have it's own dedicated ADR file in this directory.
5 |
6 | See the [Architecture Decision Record](https://github.com/joelparkerhenderson/architecture-decision-record) for more details of what an ADR is and common formats for it.
7 |
8 | ## Document format
9 |
10 | The ADR file name should be prefixed with an incrementing, sequential number.
11 | This shows the order in which decisions were made over the lifetime of the project.
12 | e.g. `001-Use-ADRs-for-decisions.md`.
13 |
14 | Here is an example of the sections that an ADR typically includes:
15 |
16 | ```markdown
17 | ## Status
18 |
19 | What is the status, such as _proposed_, _accepted_, _rejected_, _superseded_, etc.? If _superseded_ by a subsequent decision, link to the subsequent decision. Include the date that the status change was made.
20 |
21 | ## Context
22 |
23 | What is the issue that we're seeing that is motivating this decision or change? Are there any social or budgetary concerns that must be factored into the decision? Hyperlinks to supporting documentation are encouraged.
24 |
25 | ## Options Considered
26 |
27 | What options are available to solve the aforementioned issue? What are the tradeoffs associated with each option? Hyperlinks to supporting documentation are encouraged.
28 |
29 | ## Decision
30 |
31 | What is the change that we're proposing and/or doing to solve the aforementioned issue?
32 |
33 | ## Consequences
34 |
35 | What becomes easier or more difficult to do because of this change? What are the immediate action items?
36 |
37 | ## References / Additional Reading
38 |
39 | What individuals, teams, vendor documentation, and/or articles were consulted when gathering information throughout the decision-making process?
40 | ```
41 |
42 | And an example of what an ADR might actually look like:
43 |
44 | ```markdown
45 | # 001 - Record Architecture Decisions
46 |
47 | ## Status
48 |
49 | Accepted 2022-10-28
50 |
51 | ## Context
52 |
53 | As the project is an example of a more advanced monolith architecture, it is necessary to save all architectural decisions in one place.
54 |
55 | ## Decision
56 |
57 | For all architectural decisions Architecture Decision Log (ADL) is created. All decisions will be recorded as Architecture Decision Records (ADR).
58 |
59 | Each ADR will be recorded in the repository and contain following sections: __Status__, __Context__, __Options Considered__ (optional if decision is straightforward), __Decision__, __Consequences__, and __References / Additional Reading__ (optional, but strongly encouraged).
60 |
61 | ## Consequences
62 |
63 | - All significant architectural decisions shall be recorded.
64 | - Old decisions should be recorded with an approximate decision date.
65 | - New decisions shall be recorded on a regular basis.
66 | ```
67 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more info about out the tasks.json format see: https://code.visualstudio.com/docs/editor/tasks
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "label": "Run all build tasks",
7 | "group": {
8 | "kind": "build",
9 | "isDefault": true
10 | },
11 | "dependsOn": [
12 | "Run PSScriptAnalyzer linter"
13 | ]
14 | },
15 | {
16 | "label": "Run all test tasks",
17 | "group": {
18 | "kind": "test",
19 | "isDefault": true
20 | },
21 | "dependsOn": [
22 | "Run all Pester tests"
23 | ]
24 | },
25 | {
26 | "label": "Run PSScriptAnalyzer linter",
27 | "type": "shell",
28 | "options": {
29 | "shell": {
30 | "executable": "pwsh",
31 | "args": [
32 | "-NoProfile",
33 | "-Command"
34 | ]
35 | }
36 | },
37 | "command": "Invoke-ScriptAnalyzer -Path . -Recurse -EnableExit",
38 | "group": "build",
39 | "presentation": {
40 | "reveal": "always",
41 | "panel": "dedicated",
42 | "clear": true,
43 | "group": "build"
44 | },
45 | "problemMatcher": [
46 | "$func-powershell-watch"
47 | ]
48 | },
49 | {
50 | "label": "Run all Pester tests",
51 | "type": "shell",
52 | "options": {
53 | "shell": {
54 | "executable": "pwsh",
55 | "args": [
56 | "-NoProfile",
57 | "-Command"
58 | ]
59 | }
60 | },
61 | "command": "Invoke-Pester -Configuration (New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' }})",
62 | "group": "test",
63 | "presentation": {
64 | "reveal": "always",
65 | "panel": "dedicated",
66 | "clear": true
67 | },
68 | "problemMatcher": [
69 | "$func-powershell-watch"
70 | ]
71 | },
72 | {
73 | "label": "Run Pester code coverage",
74 | "type": "shell",
75 | "options": {
76 | "shell": {
77 | "executable": "pwsh",
78 | "args": [
79 | "-NoProfile",
80 | "-Command"
81 | ]
82 | }
83 | },
84 | "command": "Invoke-Pester -Configuration (New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' }; CodeCoverage = @{ Enabled = $true }})",
85 | "group": "test",
86 | "presentation": {
87 | "reveal": "always",
88 | "panel": "dedicated",
89 | "clear": true
90 | },
91 | "problemMatcher": [
92 | "$func-powershell-watch"
93 | ]
94 | },
95 | {
96 | "label": "Test module manifest validity",
97 | "type": "shell",
98 | "options": {
99 | "shell": {
100 | "executable": "pwsh",
101 | "args": [
102 | "-NoProfile",
103 | "-Command"
104 | ]
105 | }
106 | },
107 | "command": "Get-ChildItem -Path ./src -Include *.psd1 -Recurse | Test-ModuleManifest",
108 | "group": "test",
109 | "presentation": {
110 | "reveal": "always",
111 | "panel": "dedicated",
112 | "clear": true
113 | },
114 | "problemMatcher": [
115 | "$func-powershell-watch"
116 | ]
117 | }
118 | ]
119 | }
120 |
--------------------------------------------------------------------------------
/docs/Contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing to this project
2 |
3 | Feel free to open an issue or pull request.
4 |
5 | ## 🚀 Publishing new versions
6 |
7 | This project acts as a template repository in GitHub, meaning that as soon as a change is pushed to the `main` branch, it will be used when new GitHub repositories are created from this template.
8 |
9 | This project also creates a PowerShell module that can be used to create new repositories.
10 | A prerelease version of the module is published to the PowerShell Gallery automatically on every commit to the `main` branch.
11 | The [GitHub Actions `deploy` workflow run](https://github.com/deadlydog/PowerShell.ScriptModuleRepositoryTemplate/actions/workflows/build-test-and-deploy-powershell-module.yml) must be manually approved to publish a stable version.
12 |
13 | ### Incrementing the version number
14 |
15 | The version number is of the format Major.Minor.Patch and follows [semantic versioning](https://semver.org).
16 | By default, every commit to the `main` branch will increment the Patch version number.
17 |
18 | If you want to increment the Major or Minor version number, you have 2 options:
19 |
20 | 1. Manually start a [`deploy` workflow](https://github.com/deadlydog/PowerShell.ScriptModuleRepositoryTemplate/actions/workflows/build-test-and-deploy-powershell-module.yml) run and specify the version number to use.
21 | e.g. Specifying `2.4.0` will produce a new version of `2.4.0`.
22 | 1. Create a new version tag and pushing it up to GitHub.
23 | Builds are not triggered on tags, and thus the version tag will be used as the starting point for the next version.
24 | e.g. Creating a new tag of `v2.4.0` will produce a new version of `2.4.1` on the next commit to the `main` branch.
25 |
26 | ## 📄 Why are the template dot-files filenames prefixed with an underscore?
27 |
28 | `Publish-Module` has a bug where it does not include any files or directories starting with `.` in the module NuGet package.
29 | The newer `Publish-PSResource` has fixed this issue somewhat so the directories and some of the files are included, but it still leaves out some dot-files, like the `.gitignore` and `.editorconfig` files.
30 | To work around these issues, we prefix the files with an underscore (e.g. `_.gitignore`) so that they are included in the module package, and then remove the underscore prefix during the file copy process of the `New-PowerShellScriptModuleRepository` cmdlet.
31 |
32 | ## 🧪 Smoke tests
33 |
34 | [The Smoke tests](/deploy/Invoke-SmokeTests.ps1) are used during the CI/CD workflow to verify that the module is working as expected after it is published to the gallery.
35 | The smoke tests are ran on Windows, MacOS, and Linux agents to ensure cross-platform compatibility, as well as against a Windows PowerShell 5.1 agent to ensure backward compatibility.
36 |
37 | The difference between the smoke tests and the regular tests is that the smoke tests rely on the module actually being installed, not just the files being present.
38 | This means they can only test the functions and aliases that are exported from the module manifest and publicly accessible.
39 | This is the reason why we must use different files for the smoke tests than the regular tests; regular tests can test private functions and variables, but smoke tests cannot.
40 |
41 | ## ⁉ Why was a specific decision made
42 |
43 | Curious about some of the choices made in this project?
44 | The reasons may be documented in the [Architecture Decision Records](/docs/ArchitectureDecisionRecords/).
45 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/docs/Contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing to this project
2 |
3 | Feel free to open an issue or pull request.
4 |
5 | ## 💻 Local development
6 |
7 | This PowerShell module is developed using Visual Studio Code.
8 | If you encounter any issues developing on your local machine, you can use [Docker Desktop](https://www.docker.com/products/docker-desktop/) and the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) VS Code extension to develop in a Docker container with all of the required dependencies, so that you do not have to install them locally.
9 | You may also develop in your web browser with GitHub Codespaces to avoid needing to put any code or dependencies on your local machine.
10 |
11 | ### 🛠️ Building and testing
12 |
13 | The code is built and tested by CI/CD pipelines on every commit to the `main` branch and every PR opened against the `main` branch.
14 |
15 | When developing locally, you can use [the VS Code tasks](/.vscode/tasks.json) to simulate the build and test process and be notified of any problems before pushing your code up to the remote repository.
16 | In VS Code, open the command palette (Ctrl+Shift+P) and select `Tasks: Run Build Task` or `Tasks: Run Test Task`.
17 |
18 | When you run the build task, it will run PSScriptAnalyzer and CSpell spellcheck.
19 | If CSpell flags a word as `unknown` that is not misspelled, you can add it to the `.cspell.json` file in the root of the repository to have it ignore the word.
20 |
21 | ## 🚀 Publishing new versions
22 |
23 | A prerelease version of the module is published automatically on every commit to the `main` branch.
24 | The [GitHub Actions `deploy` workflow](/.github/workflows/build-test-and-deploy-powershell-module.yml) run must be manually approved to publish a stable version.
25 |
26 | ### Incrementing the version number
27 |
28 | The version number is of the format Major.Minor.Patch and follows [semantic versioning](https://semver.org).
29 | By default, every commit to the `main` branch will increment the Patch version number.
30 |
31 | If you want to increment the Major or Minor version number, you have 2 options:
32 |
33 | 1. Manually start a [`deploy` workflow](/.github/workflows/build-test-and-deploy-powershell-module.yml) run and specify the version number to use.
34 | e.g. Specifying `2.4.0` will produce a new version of `2.4.0`.
35 | 1. Create a new version tag and pushing it up to GitHub.
36 | Builds are not triggered on tags, and thus the version tag will be used as the starting point for the next version.
37 | e.g. Creating a new tag of `v2.4.0` will produce a new version of `2.4.1` on the next commit to the `main` branch.
38 |
39 | ## 🧪 Smoke tests
40 |
41 | [The Smoke tests](/deploy/Invoke-SmokeTests.ps1) are used during the CI/CD workflow to verify that the module is working as expected after it is published to the gallery.
42 | The smoke tests are ran on Windows, MacOS, and Linux agents to ensure cross-platform compatibility, as well as against a Windows PowerShell 5.1 agent to ensure backward compatibility.
43 |
44 | The difference between the smoke tests and the regular tests is that the smoke tests rely on the module actually being installed, not just the files being present.
45 | This means they can only test the functions and aliases that are exported from the module manifest and publicly accessible.
46 | This is the reason why we must use different files for the smoke tests than the regular tests; regular tests can test private functions and variables, but smoke tests cannot.
47 |
48 | You do not need to run every public function test in the smoke tests, but it can help give you confidence that the module is truly cross-platform and backward compatible.
49 | If you do not want to run any smoke tests, you can comment out all of the code in the smoke tests file.
50 | The CI/CD workflow will still validate that the module can be installed on each platform.
51 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // For more info about out the tasks.json format see: https://code.visualstudio.com/docs/editor/tasks
3 | "version": "2.0.0",
4 | "tasks": [
5 | {
6 | "label": "Run all build tasks",
7 | "group": {
8 | "kind": "build",
9 | "isDefault": true
10 | },
11 | "dependsOn": [
12 | "Run PSScriptAnalyzer linter",
13 | "Run CSpell spell checker"
14 | ]
15 | },
16 | {
17 | "label": "Run all test tasks",
18 | "group": {
19 | "kind": "test",
20 | "isDefault": true
21 | },
22 | "dependsOn": [
23 | "Run all Pester tests"
24 | ]
25 | },
26 | {
27 | "label": "Run PSScriptAnalyzer linter",
28 | "type": "shell",
29 | "options": {
30 | "shell": {
31 | "executable": "pwsh",
32 | "args": [
33 | "-NoProfile",
34 | "-Command"
35 | ]
36 | }
37 | },
38 | "command": "Invoke-ScriptAnalyzer -Path . -Recurse -EnableExit",
39 | "group": "build",
40 | "presentation": {
41 | "reveal": "always",
42 | "panel": "dedicated",
43 | "clear": true,
44 | "group": "build"
45 | },
46 | "problemMatcher": [
47 | "$func-powershell-watch"
48 | ]
49 | },
50 | {
51 | "label": "Run CSpell spell checker",
52 | "type": "shell",
53 | "options": {
54 | "shell": {
55 | "executable": "pwsh",
56 | "args": [
57 | "-NoProfile",
58 | "-Command"
59 | ]
60 | }
61 | },
62 | // If npx is not available, warn that Node.js is not installed. If we cannot run cspell, try to install and run it, and warn if we still cannot run it.
63 | "command": "try { & npx -v > $null } catch {}; if (-not $?) { Write-Warning 'Node.js is not installed, so cannot download and run npx cspell.' } else { try { & npx cspell . } catch {}; if (-not $?) { & npm install cspell; & npx cspell . }; if (-not $?) { Write-Warning 'There was a problem installing or running cspell' } }",
64 | "group": "build",
65 | "presentation": {
66 | "reveal": "always",
67 | "panel": "dedicated",
68 | "clear": true,
69 | "group": "build"
70 | },
71 | "problemMatcher": [
72 | "$func-powershell-watch"
73 | ]
74 | },
75 | {
76 | "label": "Run all Pester tests",
77 | "type": "shell",
78 | "options": {
79 | "shell": {
80 | "executable": "pwsh",
81 | "args": [
82 | "-NoProfile",
83 | "-Command"
84 | ]
85 | }
86 | },
87 | "command": "Invoke-Pester -Configuration (New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' }})",
88 | "group": "test",
89 | "presentation": {
90 | "reveal": "always",
91 | "panel": "dedicated",
92 | "clear": true
93 | },
94 | "problemMatcher": [
95 | "$func-powershell-watch"
96 | ]
97 | },
98 | {
99 | "label": "Run Pester code coverage",
100 | "type": "shell",
101 | "options": {
102 | "shell": {
103 | "executable": "pwsh",
104 | "args": [
105 | "-NoProfile",
106 | "-Command"
107 | ]
108 | }
109 | },
110 | "command": "Invoke-Pester -Configuration (New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' }; CodeCoverage = @{ Enabled = $true }})",
111 | "group": "test",
112 | "presentation": {
113 | "reveal": "always",
114 | "panel": "dedicated",
115 | "clear": true
116 | },
117 | "problemMatcher": [
118 | "$func-powershell-watch"
119 | ]
120 | },
121 | {
122 | "label": "Test module manifest validity",
123 | "type": "shell",
124 | "options": {
125 | "shell": {
126 | "executable": "pwsh",
127 | "args": [
128 | "-NoProfile",
129 | "-Command"
130 | ]
131 | }
132 | },
133 | "command": "Get-ChildItem -Path ./src -Include *.psd1 -Recurse | Test-ModuleManifest",
134 | "group": "test",
135 | "presentation": {
136 | "reveal": "always",
137 | "panel": "dedicated",
138 | "clear": true
139 | },
140 | "problemMatcher": [
141 | "$func-powershell-watch"
142 | ]
143 | }
144 | ]
145 | }
146 |
--------------------------------------------------------------------------------
/_InitializeRepository.ps1:
--------------------------------------------------------------------------------
1 | # Run this script to setup the repository for your module.
2 |
3 | [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')]
4 | Param()
5 |
6 | Process
7 | {
8 | Write-Host -ForegroundColor Yellow "
9 | This script will delete all files in this repo and replace them with template files specific to your module.
10 | If you have made changes to any files you may want to commit them before continuing, as this script will likely overwrite them.
11 | "
12 |
13 | [string] $moduleName = Read-Host -Prompt "Enter the name of your module without spaces (e.g. 'YourModuleName')"
14 |
15 | [string] $organizationName = Read-Host -Prompt "Enter your name, or the the name of your organization (e.g. 'My Company'). This will be used in the module manifest and repository license"
16 |
17 | Write-Verbose "Copying template repository module files to a temporary location to run it from."
18 | [string] $tempModuleDirectoryPath = CopyTemplateModuleFilesToTemporaryDirectory -templateModuleDirectoryPath $TemplateModuleDirectoryPath
19 |
20 | Write-Information "Removing all files from this repository so they can be replaced with template repository files."
21 | RemoveAllUnnecessaryRepositoryFiles -repositoryDirectoryPath $RepositoryDirectoryPath
22 |
23 | Write-Information "Creating the template repository files."
24 | Import-Module -Name $tempModuleDirectoryPath -Force
25 | New-PowerShellScriptModuleRepository -RepositoryDirectoryPath $RepositoryDirectoryPath -ModuleName $moduleName -OrganizationName $organizationName
26 | Remove-Module -Name $TemplateModuleName -Force
27 |
28 | Write-Verbose "Removing the temporary template module files since we are done using it to create the template repository files."
29 | RemoveTemporaryModuleDirectory -tempModuleDirectoryPath $tempModuleDirectoryPath
30 |
31 | Write-Verbose "Deleting this script as it is no longer needed."
32 | DeleteThisScript
33 |
34 | Write-Host -ForegroundColor Green "Repo initialization complete. You can now commit the changes to your repository."
35 | }
36 |
37 | Begin
38 | {
39 | $InformationPreference = 'Continue'
40 | [string] $RepositoryDirectoryPath = Resolve-Path -Path $PSScriptRoot
41 | [string] $TemplateModuleName = 'ScriptModuleRepositoryTemplate'
42 | [string] $TemplateModuleDirectoryPath = "$RepositoryDirectoryPath\src\$TemplateModuleName"
43 |
44 | function CopyTemplateModuleFilesToTemporaryDirectory([string] $templateModuleDirectoryPath)
45 | {
46 | [string] $templateModuleName = Split-Path -Path $templateModuleDirectoryPath -Leaf
47 | [string] $tempModuleDirectoryPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), (New-Guid).Guid, $templateModuleName)
48 | Copy-Item -Path $templateModuleDirectoryPath -Destination $tempModuleDirectoryPath -Recurse -Force
49 | return $tempModuleDirectoryPath
50 | }
51 |
52 | function RemoveAllUnnecessaryRepositoryFiles([string] $repositoryDirectoryPath)
53 | {
54 | # Delete all files except the ones we want to keep.
55 | Get-ChildItem -Path $repositoryDirectoryPath -Recurse -File |
56 | Where-Object {
57 | $_.FullName -notlike "$repositoryDirectoryPath\.git\*" -and # Don't delete the .git directory.
58 | $_.FullName -notlike "$repositoryDirectoryPath\_InitializeRepository.ps1" # Don't delete this script.
59 | } |
60 | Remove-Item -Force
61 |
62 | # Delete all empty directories that were left behind.
63 | Get-ChildItem -Path $repositoryDirectoryPath -Recurse -Force -Directory |
64 | Sort-Object -Property FullName -Descending | # Delete child directories before parent directories.
65 | Where-Object { $_.GetFileSystemInfos().Count -eq 0 } |
66 | Remove-Item -Force
67 | }
68 |
69 | function RemoveTemporaryModuleDirectory([string] $tempModuleDirectoryPath)
70 | {
71 | if (Test-Path -Path $tempModuleDirectoryPath -PathType Container)
72 | {
73 | Remove-Item -Path $tempModuleDirectoryPath -Recurse -Force -ErrorAction SilentlyContinue
74 | }
75 | }
76 |
77 | function DeleteThisScript
78 | {
79 | [string] $scriptPath = Join-Path -Path $PSScriptRoot -ChildPath '_InitializeRepository.ps1'
80 | Remove-Item -Path $scriptPath -Force
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: 🐞 Bug report
2 | description: File a bug/issue
3 | title: "Bug: "
4 | labels: ["bug"]
5 | body:
6 | - type: checkboxes
7 | attributes:
8 | label: Is there an existing issue for this?
9 | description: Please search to see if an issue already exists for the bug you encountered.
10 | options:
11 | - label: I have searched the existing issues
12 | required: true
13 | - type: textarea
14 | attributes:
15 | label: Summary
16 | description: A concise description of the problem you're experiencing.
17 | placeholder: |
18 | When I do '...', I expect '...' to happen, but instead '...' happens.
19 | validations:
20 | required: true
21 | - type: textarea
22 | attributes:
23 | label: Steps To Reproduce
24 | description: Steps to reproduce the behavior.
25 | placeholder: |
26 | 1. Open PowerShell v7 prompt as admin...
27 | 2. Navigate to the repo directory...
28 | 3. Run the command '...'
29 | 4. See error...
30 | validations:
31 | required: true
32 | - type: textarea
33 | id: powershell-version
34 | attributes:
35 | label: PowerShell Version
36 | description: Paste verbatim output from `$PSVersionTable; $Host` below. **Please include `$Host`** so we know this version is from the Extension Terminal!
37 | render: console
38 | placeholder: |
39 | PS> $PSVersionTable; $Host
40 |
41 | Name Value
42 | ---- -----
43 | PSVersion 7.4.0
44 | PSEdition Core
45 | GitCommitId 7.4.0
46 | OS Microsoft Windows 10.0.22631
47 | Platform Win32NT
48 | PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
49 | PSRemotingProtocolVersion 2.3
50 | SerializationVersion 1.1.0.1
51 | WSManStackVersion 3.0
52 |
53 | Name : Visual Studio Code Host
54 | Version : 2023.11.0
55 | InstanceId : 803ce61b-6187-4574-9c1f-827ebb11b8b6
56 | UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
57 | CurrentCulture : en-US
58 | CurrentUICulture : en-US
59 | PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
60 | DebuggerEnabled : True
61 | IsRunspacePushed : False
62 | Runspace : System.Management.Automation.Runspaces.LocalRunspace
63 | validations:
64 | required: true
65 | - type: textarea
66 | id: vscode-version
67 | attributes:
68 | label: Visual Studio Code Version
69 | description: Paste verbatim output from `code --version` below.
70 | render: console
71 | placeholder: |
72 | PS> code --version
73 |
74 | 1.57.1
75 | 507ce72a4466fbb27b715c3722558bb15afa9f48
76 | arm64
77 | validations:
78 | required: true
79 | - type: textarea
80 | id: extension-version
81 | attributes:
82 | label: Extension Version
83 | description: Paste verbatim output from `code --list-extensions --show-versions | Select-String powershell` below.
84 | render: console
85 | placeholder: |
86 | PS> code --list-extensions --show-versions | Select-String powershell
87 |
88 | ms-vscode.powershell@2021.8.0
89 | validations:
90 | required: true
91 | - type: textarea
92 | attributes:
93 | label: Visuals
94 | description: Add screenshots, gifs, or videos to help explain the problem.
95 | placeholder: |
96 | Here is a screenshot of the problem after I do '...':
97 | 
98 |
99 | Here is a screenshot of the expected behaviour using an older version:
100 | 
101 | validations:
102 | required: false
103 | - type: textarea
104 | attributes:
105 | label: Anything else?
106 | description: |
107 | Any other context or information about the problem or how to reproduce it.
108 | e.g. Logs files (with sensitive information removed), error messages, links, references, etc.
109 |
110 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
111 | validations:
112 | required: false
113 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: 🐞 Bug report
2 | description: File a bug/issue
3 | title: "Bug: "
4 | labels: ["bug"]
5 | body:
6 | - type: checkboxes
7 | attributes:
8 | label: Is there an existing issue for this?
9 | description: Please search to see if an issue already exists for the bug you encountered.
10 | options:
11 | - label: I have searched the existing issues
12 | required: true
13 | - type: textarea
14 | attributes:
15 | label: Summary
16 | description: A concise description of the problem you're experiencing.
17 | placeholder: |
18 | When I do '...', I expect '...' to happen, but instead '...' happens.
19 | validations:
20 | required: true
21 | - type: textarea
22 | attributes:
23 | label: Steps To Reproduce
24 | description: Steps to reproduce the behavior.
25 | placeholder: |
26 | 1. Open PowerShell v7 prompt as admin...
27 | 2. Navigate to the repo directory...
28 | 3. Run the command '...'
29 | 4. See error...
30 | validations:
31 | required: true
32 | - type: textarea
33 | id: powershell-version
34 | attributes:
35 | label: PowerShell Version
36 | description: Paste verbatim output from `$PSVersionTable; $Host` below. **Please include `$Host`** so we know this version is from the Extension Terminal!
37 | render: console
38 | placeholder: |
39 | PS> $PSVersionTable; $Host
40 |
41 | Name Value
42 | ---- -----
43 | PSVersion 7.4.0
44 | PSEdition Core
45 | GitCommitId 7.4.0
46 | OS Microsoft Windows 10.0.22631
47 | Platform Win32NT
48 | PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
49 | PSRemotingProtocolVersion 2.3
50 | SerializationVersion 1.1.0.1
51 | WSManStackVersion 3.0
52 |
53 | Name : Visual Studio Code Host
54 | Version : 2023.11.0
55 | InstanceId : 803ce61b-6187-4574-9c1f-827ebb11b8b6
56 | UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
57 | CurrentCulture : en-US
58 | CurrentUICulture : en-US
59 | PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
60 | DebuggerEnabled : True
61 | IsRunspacePushed : False
62 | Runspace : System.Management.Automation.Runspaces.LocalRunspace
63 | validations:
64 | required: true
65 | - type: textarea
66 | id: vscode-version
67 | attributes:
68 | label: Visual Studio Code Version
69 | description: Paste verbatim output from `code --version` below.
70 | render: console
71 | placeholder: |
72 | PS> code --version
73 |
74 | 1.57.1
75 | 507ce72a4466fbb27b715c3722558bb15afa9f48
76 | arm64
77 | validations:
78 | required: true
79 | - type: textarea
80 | id: extension-version
81 | attributes:
82 | label: Extension Version
83 | description: Paste verbatim output from `code --list-extensions --show-versions | Select-String powershell` below.
84 | render: console
85 | placeholder: |
86 | PS> code --list-extensions --show-versions | Select-String powershell
87 |
88 | ms-vscode.powershell@2021.8.0
89 | validations:
90 | required: true
91 | - type: textarea
92 | attributes:
93 | label: Visuals
94 | description: Add screenshots, gifs, or videos to help explain the problem.
95 | placeholder: |
96 | Here is a screenshot of the problem after I do '...':
97 | 
98 |
99 | Here is a screenshot of the expected behaviour using an older version:
100 | 
101 | validations:
102 | required: false
103 | - type: textarea
104 | attributes:
105 | label: Anything else?
106 | description: |
107 | Any other context or information about the problem or how to reproduce it.
108 | e.g. Logs files (with sensitive information removed), error messages, links, references, etc.
109 |
110 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
111 | validations:
112 | required: false
113 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/src/__NewModuleName__/__NewModuleName__.psd1:
--------------------------------------------------------------------------------
1 | # Module manifest docs: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_module_manifests
2 |
3 | @{
4 |
5 | # Script module or binary module file associated with this manifest.
6 | RootModule = '__NewModuleName__.psm1'
7 |
8 | # Version number of this module. This will be updated automatically by the build and deployment pipelines.
9 | ModuleVersion = '0.0.0'
10 |
11 | # Supported PSEditions
12 | # CompatiblePSEditions = @()
13 |
14 | # ID used to uniquely identify this module
15 | GUID = '__NewModuleGuid__'
16 |
17 | # Author of this module
18 | Author = '__IndividualOrOrganizationName__'
19 |
20 | # Company or vendor of this module
21 | CompanyName = '__IndividualOrOrganizationName__'
22 |
23 | # Copyright statement for this module
24 | Copyright = '(c) __IndividualOrOrganizationName__. All rights reserved.'
25 |
26 | # Description of the functionality provided by this module
27 | # TODO: Update the description and delete this TODO comment.
28 | Description = '__NewModuleName__ module.'
29 |
30 | # Minimum version of the PowerShell engine required by this module
31 | # PowerShellVersion = ''
32 |
33 | # Name of the PowerShell host required by this module
34 | # PowerShellHostName = ''
35 |
36 | # Minimum version of the PowerShell host required by this module
37 | # PowerShellHostVersion = ''
38 |
39 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
40 | # DotNetFrameworkVersion = ''
41 |
42 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
43 | # ClrVersion = ''
44 |
45 | # Processor architecture (None, X86, Amd64) required by this module
46 | # ProcessorArchitecture = ''
47 |
48 | # Modules that must be imported into the global environment prior to importing this module
49 | # RequiredModules = @()
50 |
51 | # Assemblies that must be loaded prior to importing this module
52 | # RequiredAssemblies = @()
53 |
54 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
55 | # ScriptsToProcess = @()
56 |
57 | # Type files (.ps1xml) to be loaded when importing this module
58 | # TypesToProcess = @()
59 |
60 | # Format files (.ps1xml) to be loaded when importing this module
61 | # FormatsToProcess = @()
62 |
63 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
64 | # NestedModules = @()
65 |
66 | # Functions to export from this module. For best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
67 | # TODO: Update the functions list and delete this TODO comment.
68 | FunctionsToExport = @(
69 | 'Get-HelloWorld'
70 | )
71 |
72 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
73 | CmdletsToExport = @()
74 |
75 | # Variables to export from this module
76 | VariablesToExport = '*'
77 |
78 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
79 | AliasesToExport = @()
80 |
81 | # DSC resources to export from this module
82 | # DscResourcesToExport = @()
83 |
84 | # List of all modules packaged with this module
85 | # ModuleList = @()
86 |
87 | # List of all files packaged with this module
88 | # FileList = @()
89 |
90 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
91 | PrivateData = @{
92 |
93 | PSData = @{
94 |
95 | # Tags applied to this module. These help with module discovery in online galleries.
96 | # TODO: Add appropriate module tags and delete this TODO comment.
97 | # Tags = @()
98 |
99 | # A URL to the license for this module.
100 | # TODO: Add a link to the module license and delete this TODO comment.
101 | # LicenseUri = ''
102 |
103 | # A URL to the main website for this project.
104 | # TODO: Add a link to your git repository or website and delete this TODO comment.
105 | # ProjectUri = ''
106 |
107 | # A URL to an icon representing this module.
108 | # IconUri = ''
109 |
110 | # ReleaseNotes of this module
111 | # TODO: Add a link to the Changelog.md page in the git repository and delete this TODO comment.
112 | # ReleaseNotes = ''
113 |
114 | # Prerelease string of this module
115 | # Prerelease = ''
116 |
117 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
118 | # RequireLicenseAcceptance = $false
119 |
120 | # External dependent modules of this module
121 | # ExternalModuleDependencies = @()
122 |
123 | } # End of PSData hashtable
124 |
125 | } # End of PrivateData hashtable
126 |
127 | # HelpInfo URI of this module
128 | # HelpInfoURI = ''
129 |
130 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
131 | # DefaultCommandPrefix = ''
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/ScriptModuleRepositoryTemplate.psd1:
--------------------------------------------------------------------------------
1 | # Module manifest docs: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_module_manifests
2 |
3 | @{
4 |
5 | # Script module or binary module file associated with this manifest.
6 | RootModule = 'ScriptModuleRepositoryTemplate.psm1'
7 |
8 | # Version number of this module.
9 | ModuleVersion = '0.0.0'
10 |
11 | # Supported PSEditions
12 | # CompatiblePSEditions = @()
13 |
14 | # ID used to uniquely identify this module
15 | GUID = '643e6cf9-a844-4a1e-9771-643226c06943'
16 |
17 | # Author of this module
18 | Author = 'Daniel Schroeder'
19 |
20 | # Company or vendor of this module
21 | CompanyName = 'Daniel Schroeder'
22 |
23 | # Copyright statement for this module
24 | Copyright = '(c) Daniel Schroeder. All rights reserved.'
25 |
26 | # Description of the functionality provided by this module
27 | Description = 'Create new PowerShell script module repositories quickly and easily with boilerplate files and CI/CD workflows already defined. See the project site for more information.'
28 |
29 | # Minimum version of the PowerShell engine required by this module
30 | # PowerShellVersion = ''
31 |
32 | # Name of the PowerShell host required by this module
33 | # PowerShellHostName = ''
34 |
35 | # Minimum version of the PowerShell host required by this module
36 | # PowerShellHostVersion = ''
37 |
38 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
39 | # DotNetFrameworkVersion = ''
40 |
41 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
42 | # ClrVersion = ''
43 |
44 | # Processor architecture (None, X86, Amd64) required by this module
45 | # ProcessorArchitecture = ''
46 |
47 | # Modules that must be imported into the global environment prior to importing this module
48 | # RequiredModules = @()
49 |
50 | # Assemblies that must be loaded prior to importing this module
51 | # RequiredAssemblies = @()
52 |
53 | # Script files (.ps1) that are run in the caller's environment prior to importing this module.
54 | # ScriptsToProcess = @()
55 |
56 | # Type files (.ps1xml) to be loaded when importing this module
57 | # TypesToProcess = @()
58 |
59 | # Format files (.ps1xml) to be loaded when importing this module
60 | # FormatsToProcess = @()
61 |
62 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
63 | # NestedModules = @()
64 |
65 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
66 | FunctionsToExport = @(
67 | 'New-PowerShellScriptModuleRepository'
68 | )
69 |
70 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
71 | CmdletsToExport = @()
72 |
73 | # Variables to export from this module
74 | VariablesToExport = '*'
75 |
76 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
77 | AliasesToExport = @()
78 |
79 | # DSC resources to export from this module
80 | # DscResourcesToExport = @()
81 |
82 | # List of all modules packaged with this module
83 | # ModuleList = @()
84 |
85 | # List of all files packaged with this module
86 | # FileList = @()
87 |
88 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
89 | PrivateData = @{
90 |
91 | PSData = @{
92 |
93 | # Tags applied to this module. These help with module discovery in online galleries.
94 | Tags = @(
95 | 'PowerShell'
96 | 'Module'
97 | 'Repository'
98 | 'Template'
99 | 'ScriptModule'
100 | 'Repo'
101 | 'GitHub'
102 | 'Actions'
103 | 'AzureDevOps'
104 | 'DevOps'
105 | 'Pipelines'
106 | 'CICD'
107 | 'Deploy'
108 | 'Windows'
109 | 'MacOS'
110 | 'Linux'
111 | )
112 |
113 | # A URL to the license for this module.
114 | LicenseUri = 'https://github.com/deadlydog/PowerShell.ScriptModuleRepositoryTemplate/blob/main/License.md'
115 |
116 | # A URL to the main website for this project.
117 | ProjectUri = 'https://github.com/deadlydog/PowerShell.ScriptModuleRepositoryTemplate'
118 |
119 | # A URL to an icon representing this module.
120 | # IconUri = ''
121 |
122 | # ReleaseNotes of this module
123 | ReleaseNotes = 'Changelog: https://github.com/deadlydog/PowerShell.ScriptModuleRepositoryTemplate/blob/main/Changelog.md'
124 |
125 | # Prerelease string of this module
126 | # Prerelease = ''
127 |
128 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save
129 | # RequireLicenseAcceptance = $false
130 |
131 | # External dependent modules of this module
132 | # ExternalModuleDependencies = @()
133 |
134 | } # End of PSData hashtable
135 |
136 | } # End of PrivateData hashtable
137 |
138 | # HelpInfo URI of this module
139 | # HelpInfoURI = ''
140 |
141 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
142 | # DefaultCommandPrefix = ''
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/ScriptModuleRepositoryTemplate.psm1:
--------------------------------------------------------------------------------
1 | function New-PowerShellScriptModuleRepository
2 | {
3 | <#
4 | .SYNOPSIS
5 | Creates a new PowerShell script module repository directory with boilerplate files and CI/CD workflows already defined.
6 |
7 | .DESCRIPTION
8 | This function creates a new PowerShell script module repository with boilerplate files and CI/CD workflows already defined. This allows you to create new PowerShell script modules quickly and easily.
9 |
10 | Once the directory is created, you can run `git init` in it to initialize it as a git repository, and then push it to your own git server.
11 |
12 | You will then need to follow the instructions in the ReadMe.md file to finish setting up the repository.
13 |
14 | .PARAMETER RepositoryDirectoryPath
15 | The path to the new directory that should be created for the module repository.
16 |
17 | .PARAMETER ModuleName
18 | The name of the module to create.
19 |
20 | .PARAMETER OrganizationName
21 | The name of the individual or organization that owns the module.
22 |
23 | .EXAMPLE
24 | PS> New-PowerShellScriptModuleRepository -RepositoryDirectoryPath 'C:\MyNewModule' -ModuleName 'MyNewModule' -OrganizationName 'My Name'
25 |
26 | Creates a new module repository at 'C:\MyNewModule' with the module name 'MyNewModule' and the organization name 'My Name'.
27 |
28 | .INPUTS
29 | None. You cannot pipe objects to New-PowerShellScriptModuleRepository.
30 |
31 | .OUTPUTS
32 | None. New-PowerShellScriptModuleRepository does not return any output.
33 | It creates a new directory with the module repository files.
34 |
35 | .LINK
36 | https://github.com/deadlydog/PowerShell.ScriptModuleRepositoryTemplate
37 | #>
38 | [CmdletBinding(SupportsShouldProcess)]
39 | [Alias('New-PSRepository')]
40 | Param
41 | (
42 | [Parameter(Mandatory = $true, HelpMessage = "The path to the new directory that should be created for the module repository.")]
43 | [ValidateNotNullOrEmpty()]
44 | [string] $RepositoryDirectoryPath,
45 |
46 | [Parameter(Mandatory = $true, HelpMessage = "The name of the module to create.")]
47 | [ValidateNotNullOrEmpty()]
48 | [string] $ModuleName,
49 |
50 | [Parameter(Mandatory = $true, HelpMessage = "The name of the individual or organization that owns the module.")]
51 | [ValidateNotNullOrEmpty()]
52 | [string] $OrganizationName
53 | )
54 |
55 | CopyTemplateFilesToRepositoryRoot -repositoryDirectoryPath $RepositoryDirectoryPath
56 | SetModuleFileNames -repositoryDirectoryPath $RepositoryDirectoryPath -moduleName $ModuleName
57 | SetTemplateTokenValuesInAllRepositoryFiles -repositoryDirectoryPath $RepositoryDirectoryPath -moduleName $ModuleName -organizationName $OrganizationName
58 | }
59 |
60 | function CopyTemplateFilesToRepositoryRoot([string] $repositoryDirectoryPath)
61 | {
62 | if (-not (Test-Path -Path $repositoryDirectoryPath -PathType Container))
63 | {
64 | Write-Verbose "Creating the repository directory '$repositoryDirectoryPath'."
65 | New-Item -Path $repositoryDirectoryPath -ItemType Directory > $null
66 | }
67 |
68 | [string] $templateModuleDirectoryPath = "$PSScriptRoot\TemplateRepoFiles"
69 | if (Test-Path -Path $templateModuleDirectoryPath -PathType Container)
70 | {
71 | Write-Verbose "Copying the template repository files from '$templateModuleDirectoryPath' to the repository directory '$repositoryDirectoryPath'."
72 | Copy-Item -Path $templateModuleDirectoryPath\* -Destination $repositoryDirectoryPath -Recurse -Force
73 | }
74 |
75 | # Rename all dot-files prefixed with an underscore to remove the underscore.
76 | # The underscore prefix is a workaround to a bug with Publish-Module and Publish-PSResource that exclude dot-files
77 | # and dot-directories from being included in the module package.
78 | $repoDotFiles = Get-ChildItem -Path $repositoryDirectoryPath -Recurse -Force -Filter '_.*'
79 | $repoDotFiles | ForEach-Object {
80 | [string] $filePath = $_.FullName
81 | [string] $parentDirectory = Split-Path -Path $filePath -Parent
82 | [string] $newFileName = $_.Name -replace '^_\.', '.'
83 | [string] $newFilePath = Join-Path -Path $parentDirectory -ChildPath $newFileName
84 | Move-Item -Path $filePath -Destination $newFilePath -Force
85 | }
86 | }
87 |
88 | function SetModuleFileNames([string] $repositoryDirectoryPath, [string] $moduleName)
89 | {
90 | [string] $moduleDirectoryPath = "$repositoryDirectoryPath\src\__NewModuleName__"
91 | [string] $moduleFilePath = "$moduleDirectoryPath\__NewModuleName__.psm1"
92 | [string] $moduleManifestFilePath = "$moduleDirectoryPath\__NewModuleName__.psd1"
93 | [string] $moduleTestsFilePath = "$moduleDirectoryPath\__NewModuleName__.Tests.ps1"
94 |
95 | if (Test-Path -Path $moduleFilePath -PathType Leaf)
96 | {
97 | Rename-Item -Path $moduleFilePath -NewName "$moduleName.psm1" -Force
98 | }
99 |
100 | if (Test-Path -Path $moduleManifestFilePath -PathType Leaf)
101 | {
102 | Rename-Item -Path $moduleManifestFilePath -NewName "$moduleName.psd1" -Force
103 | }
104 |
105 | if (Test-Path -Path $moduleTestsFilePath -PathType Leaf)
106 | {
107 | Rename-Item -Path $moduleTestsFilePath -NewName "$moduleName.Tests.ps1" -Force
108 | }
109 |
110 | # Rename the directory last.
111 | if (Test-Path -Path $moduleDirectoryPath -PathType Container)
112 | {
113 | Rename-Item -Path $moduleDirectoryPath -NewName $moduleName -Force
114 | }
115 | }
116 |
117 | function SetTemplateTokenValuesInAllRepositoryFiles([string] $repositoryDirectoryPath, [string] $moduleName, [string] $organizationName)
118 | {
119 | $repositoryFiles = Get-ChildItem -Path $repositoryDirectoryPath -Recurse -File
120 | foreach ($file in $repositoryFiles)
121 | {
122 | $filePath = $file.FullName
123 | $contents = Get-Content -Path $filePath
124 | $contents = $contents -replace '__NewModuleName__', $moduleName
125 | $contents = $contents -replace '__IndividualOrOrganizationName__', $organizationName
126 | $contents = $contents -replace '__NewModuleGuid__', (New-Guid).ToString()
127 | $contents = $contents -replace '__CurrentYear__', (Get-Date).Year
128 | Set-Content -Path $filePath -Value $contents
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/ReadMe.md:
--------------------------------------------------------------------------------
1 | # Finish setting up your repo
2 |
3 | > [!IMPORTANT]
4 | > Congratulations on initializing your repository! 🎉
5 | >
6 | > 🚧 You are not quite done yet though. 🚧
7 | >
8 | > Complete the following steps to finish setting up your repository.
9 |
10 | ## 📄 Template setup instructions
11 |
12 | Steps 1 and 2 were already performed to get this far.
13 |
14 | ### ➕ Step 3: Add your module (if applicable)
15 |
16 | If you already have the module, manifest, and tests written, replace the following files with your module's files:
17 |
18 | - [__NewModuleName__.psm1](/src/__NewModuleName__/__NewModuleName__.psm1)
19 | - [__NewModuleName__.psd1](/src/__NewModuleName__/__NewModuleName__.psd1)
20 | - [__NewModuleName__.Tests.ps1](/src/__NewModuleName__/__NewModuleName__.Tests.ps1)
21 |
22 | Otherwise, use these files as a starting point for your new module.
23 |
24 | #### Smoke tests
25 |
26 | When you change the module and tests, you will also need to update [the Smoke tests](/deploy/Invoke-SmokeTests.ps1), otherwise they may fail the CI/CD workflow.
27 | See the [Contributing docs](/docs/Contributing.md) for more information on smoke tests.
28 |
29 | ### 🚀 Step 4: Update your CI/CD workflows
30 |
31 | #### 🔑 Create a PowerShell Gallery API Key
32 |
33 | In order to publish the module to the gallery, you need to get an API key.
34 | If you already have an API key that you want to use, you can skip to the next step.
35 |
36 | > [!CAUTION]
37 | > It is considered best practice to use a different API key for each module you publish.
38 | > It reduces the impact scope if one of the API keys becomes compromised.
39 |
40 | If you will be publishing the module to a custom PowerShell Gallery feed, you will need to get an API key for that feed. Otherwise, follow the steps below.
41 |
42 |
43 | Click to view steps to create a new API Key for the PowerShell Gallery...
44 |
45 | 1. Navigate to , and login if necessary.
46 | 1. Click `Create` to create a new API key for this module.
47 | 1. For the `Key Name` it is a good idea to include the name of your module.
48 | e.g. `__NewModuleName__ module CI/CD pipeline`
49 | 1. Ensure the `Push new packages and package versions` scope is selected.
50 | 1. For the `Glob Pattern` enter the name of your module: `__NewModuleName__`
51 | 1. Click the `Create` button to create the API key.
52 | 1. Click the `Copy` button on the new API key to copy it to your clipboard, as you will need it for the next section.
53 |
54 | You may want to leave this page open in your browser until you have the API key saved in your repository secrets in the next section.
55 |
56 |
57 |
58 | #### ▶ GitHub Actions and Azure DevOps Pipelines setup instructions
59 |
60 | Building and publishing the PowerShell module using GitHub Actions and Azure DevOps Pipelines are both supported.
61 | Follow the instructions for the CI/CD platform you plan to use.
62 |
63 |
64 | Click to see GitHub setup instructions...
65 |
66 | If using GitHub Actions for your CI/CD workflows, perform the following steps to setup your API key as a repository secret:
67 |
68 | 1. Navigate to your GitHub repository in your browser.
69 | 1. Go to the `Settings` tab for your repository.
70 | 1. In the left-hand menu, in the `Security` section, click on `Secrets and variables` and select `Actions`.
71 | 1. In the `Secrets` tab click the `New repository secret` button.
72 | 1. Set the `Secret` value to the API key value that you copied in the previous section.
73 | 1. Set the `Name` to: `POWERSHELL_GALLERY_API_KEY`
74 | 1. Click the `Add secret` button to save the repository secret.
75 |
76 | If you do not provide a valid API key, you will get an error like the following in the `Publish prerelease PowerShell module` step of the deployment workflow:
77 |
78 | ```text
79 | Failed to publish module '': 'dotnet cli failed to nuget push Pushing .nupkg to ''...
80 | PUT Forbidden
81 | error: Response status code does not indicate success: 403 (The specified API key is invalid, has expired, or does not have permission to access the specified package.).
82 | ```
83 |
84 | Next we want to create an Environment so that stable module versions require manual approval before being published to the gallery:
85 |
86 | 1. You should still be in the `Settings` section of your repository.
87 | 1. In the left-hand menu, in the `Code and automation` section, click on `Environments`.
88 | 1. Click the `New environment` button.
89 | 1. Set the `Name` to (all lowercase): `production`
90 | 1. Click the `Configure environment` button.
91 | 1. Check the `Required reviewers` checkbox and add the usernames of the people allowed to approve new stable version deployments.
92 | e.g. your GitHub username.
93 | 1. Click the `Save protection rules` button.
94 |
95 | If your GitHub account does not meet [the requirements to use `Environments`](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment), the `Environments` section or `Required reviewers` options may not be available.
96 | Deployments will still work, but they will not pause for manual approval and will automatically deploy the stable version directly after the prerelease version is published.
97 | You will instead need to add [the Manual Workflow Approval action](https://github.com/marketplace/actions/manual-workflow-approval) to [the deployment workflow](/.github/workflows/build-test-and-deploy-powershell-module.yml) to block deployments until they are approved.
98 |
99 | Finally, we will need to grant GitHub Actions permission to add git tags to the repository so it can keep track of the version number:
100 |
101 | 1. You should still be in the `Settings` section of your repository.
102 | 1. In the left-hand menu, in the `Code and automation` section, click on `Actions` and select `General`.
103 | 1. Scroll down to `Workflow permissions` and ensure `Read and write permissions` is selected.
104 | 1. Click the `Save` button for the Workflow permissions.
105 |
106 | If you do not do this you will get the following error in the `Set the new version tag` step of the deployment workflow:
107 |
108 | ```text
109 | fatal: unable to access 'https://github.com///': The requested URL returned error: 403
110 | ```
111 |
112 |
113 |
114 |
115 | Click to see Azure DevOps setup instructions...
116 |
117 | Coming soon.
118 |
119 |
120 |
121 | ### ✔ Step 5: Review and update boilerplate repo files
122 |
123 | The following boilerplate git repository files should be reviewed and updated or removed as needed.
124 |
125 | - [__NewModuleName__.psd1](/src/__NewModuleName__/__NewModuleName__.psd1): Update the module manifest with your module's information, such as the Description and have the ProjectUri link to this repository, etc.
126 | - If using the default generated manifest, search for "TODO" to find the properties that are recommended to update.
127 | - [Changelog](/Changelog.md): If you don't plan to track a changelog, remove this file and it's reference from the ReadMe.
128 | - [License](/License.md): Update to match your module's license, and ensure it uses the correct name and year in the copyright.
129 | - [Contributing](/docs/Contributing.md): Update to match your module's contributing guidelines, or remove it.
130 | - [bug_report](/.github/ISSUE_TEMPLATE/bug_report.md), [feature_request](/.github/ISSUE_TEMPLATE/feature_request.md), [pull_request_template](/.github/pull_request_template.md): Update these GitHub templates as needed to meet your requirements, or remove them.
131 | - Build and deployment workflows: The workflows include extra steps that you may not want, such as spell check, code coverage, etc.
132 | Review the workflows and remove any steps that you don't want to include in your CI/CD pipeline.
133 | - [ReadMe](/ReadMe.md): Update this file with your module's information.
134 | Some example template content is provided below; fill it out, or remove it and write your own.
135 |
136 | > [!IMPORTANT]
137 | > If you've made it this far, your repository is now ready for use! 🎉
138 | >
139 | > You may delete this message and all of the file's content above it and commit any changes you've made.
140 |
141 | # __NewModuleName__ PowerShell Module
142 |
143 | ## 💬 Description
144 |
145 | A short description of what this project does.
146 | Include a link to the module in the gallery.
147 |
148 | ## ❓ Why this exists
149 |
150 | A short description of why this project exists.
151 | What use-case is it meant to solve?
152 |
153 | ## ✨ Features
154 |
155 | List the features of this project:
156 |
157 | - Feature 1
158 | - Feature 2
159 |
160 | ## 🚀 Quick start
161 |
162 | A quick guide on how to get started with this module, including installation and usage:
163 |
164 | - A link to the module in the PowerShell Gallery.
165 | - Code examples of installing and and using the module.
166 | - Links to wiki or other documentation.
167 |
168 | ## ➕ How to contribute
169 |
170 | Issues and Pull Requests are welcome.
171 | See [the Contributing page](docs/Contributing.md) for more details.
172 |
173 | ## 📃 Changelog
174 |
175 | See what's changed in the application over time by viewing [the changelog](Changelog.md).
176 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/powershell,visualstudiocode,visualstudio
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=powershell,visualstudiocode,visualstudio
3 |
4 | ### PowerShell ###
5 | # Exclude packaged modules
6 | *.zip
7 |
8 | # Exclude .NET assemblies from source
9 | *.dll
10 |
11 | ### VisualStudioCode ###
12 | .vscode/*
13 | !.vscode/settings.json
14 | !.vscode/tasks.json
15 | !.vscode/launch.json
16 | !.vscode/extensions.json
17 | !.vscode/*.code-snippets
18 |
19 | # Local History for Visual Studio Code
20 | .history/
21 |
22 | # Built Visual Studio Code Extensions
23 | *.vsix
24 |
25 | ### VisualStudioCode Patch ###
26 | # Ignore all local history of files
27 | .history
28 | .ionide
29 |
30 | ### VisualStudio ###
31 | ## Ignore Visual Studio temporary files, build results, and
32 | ## files generated by popular Visual Studio add-ons.
33 | ##
34 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
35 |
36 | # User-specific files
37 | *.rsuser
38 | *.suo
39 | *.user
40 | *.userosscache
41 | *.sln.docstates
42 |
43 | # User-specific files (MonoDevelop/Xamarin Studio)
44 | *.userprefs
45 |
46 | # Mono auto generated files
47 | mono_crash.*
48 |
49 | # Build results
50 | [Dd]ebug/
51 | [Dd]ebugPublic/
52 | [Rr]elease/
53 | [Rr]eleases/
54 | x64/
55 | x86/
56 | [Ww][Ii][Nn]32/
57 | [Aa][Rr][Mm]/
58 | [Aa][Rr][Mm]64/
59 | bld/
60 | [Bb]in/
61 | [Oo]bj/
62 | [Ll]og/
63 | [Ll]ogs/
64 |
65 | # Visual Studio 2015/2017 cache/options directory
66 | .vs/
67 | # Uncomment if you have tasks that create the project's static files in wwwroot
68 | #wwwroot/
69 |
70 | # Visual Studio 2017 auto generated files
71 | Generated\ Files/
72 |
73 | # MSTest test Results
74 | [Tt]est[Rr]esult*/
75 | [Bb]uild[Ll]og.*
76 |
77 | # NUnit
78 | *.VisualState.xml
79 | TestResult.xml
80 | nunit-*.xml
81 |
82 | # Build Results of an ATL Project
83 | [Dd]ebugPS/
84 | [Rr]eleasePS/
85 | dlldata.c
86 |
87 | # Benchmark Results
88 | BenchmarkDotNet.Artifacts/
89 |
90 | # .NET Core
91 | project.lock.json
92 | project.fragment.lock.json
93 | artifacts/
94 |
95 | # ASP.NET Scaffolding
96 | ScaffoldingReadMe.txt
97 |
98 | # StyleCop
99 | StyleCopReport.xml
100 |
101 | # Files built by Visual Studio
102 | *_i.c
103 | *_p.c
104 | *_h.h
105 | *.ilk
106 | *.meta
107 | *.obj
108 | *.iobj
109 | *.pch
110 | *.pdb
111 | *.ipdb
112 | *.pgc
113 | *.pgd
114 | *.rsp
115 | *.sbr
116 | *.tlb
117 | *.tli
118 | *.tlh
119 | *.tmp
120 | *.tmp_proj
121 | *_wpftmp.csproj
122 | *.log
123 | *.tlog
124 | *.vspscc
125 | *.vssscc
126 | .builds
127 | *.pidb
128 | *.svclog
129 | *.scc
130 |
131 | # Chutzpah Test files
132 | _Chutzpah*
133 |
134 | # Visual C++ cache files
135 | ipch/
136 | *.aps
137 | *.ncb
138 | *.opendb
139 | *.opensdf
140 | *.sdf
141 | *.cachefile
142 | *.VC.db
143 | *.VC.VC.opendb
144 |
145 | # Visual Studio profiler
146 | *.psess
147 | *.vsp
148 | *.vspx
149 | *.sap
150 |
151 | # Visual Studio Trace Files
152 | *.e2e
153 |
154 | # TFS 2012 Local Workspace
155 | $tf/
156 |
157 | # Guidance Automation Toolkit
158 | *.gpState
159 |
160 | # ReSharper is a .NET coding add-in
161 | _ReSharper*/
162 | *.[Rr]e[Ss]harper
163 | *.DotSettings.user
164 |
165 | # TeamCity is a build add-in
166 | _TeamCity*
167 |
168 | # DotCover is a Code Coverage Tool
169 | *.dotCover
170 |
171 | # AxoCover is a Code Coverage Tool
172 | .axoCover/*
173 | !.axoCover/settings.json
174 |
175 | # Coverlet is a free, cross platform Code Coverage Tool
176 | coverage*.json
177 | coverage*.xml
178 | coverage*.info
179 |
180 | # Visual Studio code coverage results
181 | *.coverage
182 | *.coveragexml
183 |
184 | # NCrunch
185 | _NCrunch_*
186 | .*crunch*.local.xml
187 | nCrunchTemp_*
188 |
189 | # MightyMoose
190 | *.mm.*
191 | AutoTest.Net/
192 |
193 | # Web workbench (sass)
194 | .sass-cache/
195 |
196 | # Installshield output folder
197 | [Ee]xpress/
198 |
199 | # DocProject is a documentation generator add-in
200 | DocProject/buildhelp/
201 | DocProject/Help/*.HxT
202 | DocProject/Help/*.HxC
203 | DocProject/Help/*.hhc
204 | DocProject/Help/*.hhk
205 | DocProject/Help/*.hhp
206 | DocProject/Help/Html2
207 | DocProject/Help/html
208 |
209 | # Click-Once directory
210 | publish/
211 |
212 | # Publish Web Output
213 | *.[Pp]ublish.xml
214 | *.azurePubxml
215 | # Note: Comment the next line if you want to checkin your web deploy settings,
216 | # but database connection strings (with potential passwords) will be unencrypted
217 | *.pubxml
218 | *.publishproj
219 |
220 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
221 | # checkin your Azure Web App publish settings, but sensitive information contained
222 | # in these scripts will be unencrypted
223 | PublishScripts/
224 |
225 | # NuGet Packages
226 | *.nupkg
227 | # NuGet Symbol Packages
228 | *.snupkg
229 | # The packages folder can be ignored because of Package Restore
230 | **/[Pp]ackages/*
231 | # except build/, which is used as an MSBuild target.
232 | !**/[Pp]ackages/build/
233 | # Uncomment if necessary however generally it will be regenerated when needed
234 | #!**/[Pp]ackages/repositories.config
235 | # NuGet v3's project.json files produces more ignorable files
236 | *.nuget.props
237 | *.nuget.targets
238 |
239 | # Microsoft Azure Build Output
240 | csx/
241 | *.build.csdef
242 |
243 | # Microsoft Azure Emulator
244 | ecf/
245 | rcf/
246 |
247 | # Windows Store app package directories and files
248 | AppPackages/
249 | BundleArtifacts/
250 | Package.StoreAssociation.xml
251 | _pkginfo.txt
252 | *.appx
253 | *.appxbundle
254 | *.appxupload
255 |
256 | # Visual Studio cache files
257 | # files ending in .cache can be ignored
258 | *.[Cc]ache
259 | # but keep track of directories ending in .cache
260 | !?*.[Cc]ache/
261 |
262 | # Others
263 | ClientBin/
264 | ~$*
265 | *~
266 | *.dbmdl
267 | *.dbproj.schemaview
268 | *.jfm
269 | *.pfx
270 | *.publishsettings
271 | orleans.codegen.cs
272 |
273 | # Including strong name files can present a security risk
274 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
275 | #*.snk
276 |
277 | # Since there are multiple workflows, uncomment next line to ignore bower_components
278 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
279 | #bower_components/
280 |
281 | # RIA/Silverlight projects
282 | Generated_Code/
283 |
284 | # Backup & report files from converting an old project file
285 | # to a newer Visual Studio version. Backup files are not needed,
286 | # because we have git ;-)
287 | _UpgradeReport_Files/
288 | Backup*/
289 | UpgradeLog*.XML
290 | UpgradeLog*.htm
291 | ServiceFabricBackup/
292 | *.rptproj.bak
293 |
294 | # SQL Server files
295 | *.mdf
296 | *.ldf
297 | *.ndf
298 |
299 | # Business Intelligence projects
300 | *.rdl.data
301 | *.bim.layout
302 | *.bim_*.settings
303 | *.rptproj.rsuser
304 | *- [Bb]ackup.rdl
305 | *- [Bb]ackup ([0-9]).rdl
306 | *- [Bb]ackup ([0-9][0-9]).rdl
307 |
308 | # Microsoft Fakes
309 | FakesAssemblies/
310 |
311 | # GhostDoc plugin setting file
312 | *.GhostDoc.xml
313 |
314 | # Node.js Tools for Visual Studio
315 | .ntvs_analysis.dat
316 | node_modules/
317 |
318 | # Visual Studio 6 build log
319 | *.plg
320 |
321 | # Visual Studio 6 workspace options file
322 | *.opt
323 |
324 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
325 | *.vbw
326 |
327 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
328 | *.vbp
329 |
330 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
331 | *.dsw
332 | *.dsp
333 |
334 | # Visual Studio 6 technical files
335 |
336 | # Visual Studio LightSwitch build output
337 | **/*.HTMLClient/GeneratedArtifacts
338 | **/*.DesktopClient/GeneratedArtifacts
339 | **/*.DesktopClient/ModelManifest.xml
340 | **/*.Server/GeneratedArtifacts
341 | **/*.Server/ModelManifest.xml
342 | _Pvt_Extensions
343 |
344 | # Paket dependency manager
345 | .paket/paket.exe
346 | paket-files/
347 |
348 | # FAKE - F# Make
349 | .fake/
350 |
351 | # CodeRush personal settings
352 | .cr/personal
353 |
354 | # Python Tools for Visual Studio (PTVS)
355 | __pycache__/
356 | *.pyc
357 |
358 | # Cake - Uncomment if you are using it
359 | # tools/**
360 | # !tools/packages.config
361 |
362 | # Tabs Studio
363 | *.tss
364 |
365 | # Telerik's JustMock configuration file
366 | *.jmconfig
367 |
368 | # BizTalk build output
369 | *.btp.cs
370 | *.btm.cs
371 | *.odx.cs
372 | *.xsd.cs
373 |
374 | # OpenCover UI analysis results
375 | OpenCover/
376 |
377 | # Azure Stream Analytics local run output
378 | ASALocalRun/
379 |
380 | # MSBuild Binary and Structured Log
381 | *.binlog
382 |
383 | # NVidia Nsight GPU debugger configuration file
384 | *.nvuser
385 |
386 | # MFractors (Xamarin productivity tool) working folder
387 | .mfractor/
388 |
389 | # Local History for Visual Studio
390 | .localhistory/
391 |
392 | # Visual Studio History (VSHistory) files
393 | .vshistory/
394 |
395 | # BeatPulse healthcheck temp database
396 | healthchecksdb
397 |
398 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
399 | MigrationBackup/
400 |
401 | # Ionide (cross platform F# VS Code tools) working folder
402 | .ionide/
403 |
404 | # Fody - auto-generated XML schema
405 | FodyWeavers.xsd
406 |
407 | # VS Code files for those working on multiple tools
408 | *.code-workspace
409 |
410 | # Local History for Visual Studio Code
411 |
412 | # Windows Installer files from build outputs
413 | *.cab
414 | *.msi
415 | *.msix
416 | *.msm
417 | *.msp
418 |
419 | # JetBrains Rider
420 | *.sln.iml
421 |
422 | ### VisualStudio Patch ###
423 | # Additional files built by Visual Studio
424 |
425 | # End of https://www.toptal.com/developers/gitignore/api/powershell,visualstudiocode,visualstudio
426 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Custom additions:
2 |
3 | # Ignore NPM files.
4 | package-lock.json
5 | package.json
6 |
7 | # Created by https://www.toptal.com/developers/gitignore/api/powershell,visualstudiocode,visualstudio
8 | # Edit at https://www.toptal.com/developers/gitignore?templates=powershell,visualstudiocode,visualstudio
9 |
10 | ### PowerShell ###
11 | # Exclude packaged modules
12 | *.zip
13 |
14 | # Exclude .NET assemblies from source
15 | *.dll
16 |
17 | ### VisualStudioCode ###
18 | .vscode/*
19 | !.vscode/settings.json
20 | !.vscode/tasks.json
21 | !.vscode/launch.json
22 | !.vscode/extensions.json
23 | !.vscode/*.code-snippets
24 |
25 | # Local History for Visual Studio Code
26 | .history/
27 |
28 | # Built Visual Studio Code Extensions
29 | *.vsix
30 |
31 | ### VisualStudioCode Patch ###
32 | # Ignore all local history of files
33 | .history
34 | .ionide
35 |
36 | ### VisualStudio ###
37 | ## Ignore Visual Studio temporary files, build results, and
38 | ## files generated by popular Visual Studio add-ons.
39 | ##
40 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
41 |
42 | # User-specific files
43 | *.rsuser
44 | *.suo
45 | *.user
46 | *.userosscache
47 | *.sln.docstates
48 |
49 | # User-specific files (MonoDevelop/Xamarin Studio)
50 | *.userprefs
51 |
52 | # Mono auto generated files
53 | mono_crash.*
54 |
55 | # Build results
56 | [Dd]ebug/
57 | [Dd]ebugPublic/
58 | [Rr]elease/
59 | [Rr]eleases/
60 | x64/
61 | x86/
62 | [Ww][Ii][Nn]32/
63 | [Aa][Rr][Mm]/
64 | [Aa][Rr][Mm]64/
65 | bld/
66 | [Bb]in/
67 | [Oo]bj/
68 | [Ll]og/
69 | [Ll]ogs/
70 |
71 | # Visual Studio 2015/2017 cache/options directory
72 | .vs/
73 | # Uncomment if you have tasks that create the project's static files in wwwroot
74 | #wwwroot/
75 |
76 | # Visual Studio 2017 auto generated files
77 | Generated\ Files/
78 |
79 | # MSTest test Results
80 | [Tt]est[Rr]esult*/
81 | [Bb]uild[Ll]og.*
82 |
83 | # NUnit
84 | *.VisualState.xml
85 | TestResult.xml
86 | nunit-*.xml
87 |
88 | # Build Results of an ATL Project
89 | [Dd]ebugPS/
90 | [Rr]eleasePS/
91 | dlldata.c
92 |
93 | # Benchmark Results
94 | BenchmarkDotNet.Artifacts/
95 |
96 | # .NET Core
97 | project.lock.json
98 | project.fragment.lock.json
99 | artifacts/
100 |
101 | # ASP.NET Scaffolding
102 | ScaffoldingReadMe.txt
103 |
104 | # StyleCop
105 | StyleCopReport.xml
106 |
107 | # Files built by Visual Studio
108 | *_i.c
109 | *_p.c
110 | *_h.h
111 | *.ilk
112 | *.meta
113 | *.obj
114 | *.iobj
115 | *.pch
116 | *.pdb
117 | *.ipdb
118 | *.pgc
119 | *.pgd
120 | *.rsp
121 | *.sbr
122 | *.tlb
123 | *.tli
124 | *.tlh
125 | *.tmp
126 | *.tmp_proj
127 | *_wpftmp.csproj
128 | *.log
129 | *.tlog
130 | *.vspscc
131 | *.vssscc
132 | .builds
133 | *.pidb
134 | *.svclog
135 | *.scc
136 |
137 | # Chutzpah Test files
138 | _Chutzpah*
139 |
140 | # Visual C++ cache files
141 | ipch/
142 | *.aps
143 | *.ncb
144 | *.opendb
145 | *.opensdf
146 | *.sdf
147 | *.cachefile
148 | *.VC.db
149 | *.VC.VC.opendb
150 |
151 | # Visual Studio profiler
152 | *.psess
153 | *.vsp
154 | *.vspx
155 | *.sap
156 |
157 | # Visual Studio Trace Files
158 | *.e2e
159 |
160 | # TFS 2012 Local Workspace
161 | $tf/
162 |
163 | # Guidance Automation Toolkit
164 | *.gpState
165 |
166 | # ReSharper is a .NET coding add-in
167 | _ReSharper*/
168 | *.[Rr]e[Ss]harper
169 | *.DotSettings.user
170 |
171 | # TeamCity is a build add-in
172 | _TeamCity*
173 |
174 | # DotCover is a Code Coverage Tool
175 | *.dotCover
176 |
177 | # AxoCover is a Code Coverage Tool
178 | .axoCover/*
179 | !.axoCover/settings.json
180 |
181 | # Coverlet is a free, cross platform Code Coverage Tool
182 | coverage*.json
183 | coverage*.xml
184 | coverage*.info
185 |
186 | # Visual Studio code coverage results
187 | *.coverage
188 | *.coveragexml
189 |
190 | # NCrunch
191 | _NCrunch_*
192 | .*crunch*.local.xml
193 | nCrunchTemp_*
194 |
195 | # MightyMoose
196 | *.mm.*
197 | AutoTest.Net/
198 |
199 | # Web workbench (sass)
200 | .sass-cache/
201 |
202 | # Installshield output folder
203 | [Ee]xpress/
204 |
205 | # DocProject is a documentation generator add-in
206 | DocProject/buildhelp/
207 | DocProject/Help/*.HxT
208 | DocProject/Help/*.HxC
209 | DocProject/Help/*.hhc
210 | DocProject/Help/*.hhk
211 | DocProject/Help/*.hhp
212 | DocProject/Help/Html2
213 | DocProject/Help/html
214 |
215 | # Click-Once directory
216 | publish/
217 |
218 | # Publish Web Output
219 | *.[Pp]ublish.xml
220 | *.azurePubxml
221 | # Note: Comment the next line if you want to checkin your web deploy settings,
222 | # but database connection strings (with potential passwords) will be unencrypted
223 | *.pubxml
224 | *.publishproj
225 |
226 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
227 | # checkin your Azure Web App publish settings, but sensitive information contained
228 | # in these scripts will be unencrypted
229 | PublishScripts/
230 |
231 | # NuGet Packages
232 | *.nupkg
233 | # NuGet Symbol Packages
234 | *.snupkg
235 | # The packages folder can be ignored because of Package Restore
236 | **/[Pp]ackages/*
237 | # except build/, which is used as an MSBuild target.
238 | !**/[Pp]ackages/build/
239 | # Uncomment if necessary however generally it will be regenerated when needed
240 | #!**/[Pp]ackages/repositories.config
241 | # NuGet v3's project.json files produces more ignorable files
242 | *.nuget.props
243 | *.nuget.targets
244 |
245 | # Microsoft Azure Build Output
246 | csx/
247 | *.build.csdef
248 |
249 | # Microsoft Azure Emulator
250 | ecf/
251 | rcf/
252 |
253 | # Windows Store app package directories and files
254 | AppPackages/
255 | BundleArtifacts/
256 | Package.StoreAssociation.xml
257 | _pkginfo.txt
258 | *.appx
259 | *.appxbundle
260 | *.appxupload
261 |
262 | # Visual Studio cache files
263 | # files ending in .cache can be ignored
264 | *.[Cc]ache
265 | # but keep track of directories ending in .cache
266 | !?*.[Cc]ache/
267 |
268 | # Others
269 | ClientBin/
270 | ~$*
271 | *~
272 | *.dbmdl
273 | *.dbproj.schemaview
274 | *.jfm
275 | *.pfx
276 | *.publishsettings
277 | orleans.codegen.cs
278 |
279 | # Including strong name files can present a security risk
280 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
281 | #*.snk
282 |
283 | # Since there are multiple workflows, uncomment next line to ignore bower_components
284 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
285 | #bower_components/
286 |
287 | # RIA/Silverlight projects
288 | Generated_Code/
289 |
290 | # Backup & report files from converting an old project file
291 | # to a newer Visual Studio version. Backup files are not needed,
292 | # because we have git ;-)
293 | _UpgradeReport_Files/
294 | Backup*/
295 | UpgradeLog*.XML
296 | UpgradeLog*.htm
297 | ServiceFabricBackup/
298 | *.rptproj.bak
299 |
300 | # SQL Server files
301 | *.mdf
302 | *.ldf
303 | *.ndf
304 |
305 | # Business Intelligence projects
306 | *.rdl.data
307 | *.bim.layout
308 | *.bim_*.settings
309 | *.rptproj.rsuser
310 | *- [Bb]ackup.rdl
311 | *- [Bb]ackup ([0-9]).rdl
312 | *- [Bb]ackup ([0-9][0-9]).rdl
313 |
314 | # Microsoft Fakes
315 | FakesAssemblies/
316 |
317 | # GhostDoc plugin setting file
318 | *.GhostDoc.xml
319 |
320 | # Node.js Tools for Visual Studio
321 | .ntvs_analysis.dat
322 | node_modules/
323 |
324 | # Visual Studio 6 build log
325 | *.plg
326 |
327 | # Visual Studio 6 workspace options file
328 | *.opt
329 |
330 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
331 | *.vbw
332 |
333 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
334 | *.vbp
335 |
336 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
337 | *.dsw
338 | *.dsp
339 |
340 | # Visual Studio 6 technical files
341 |
342 | # Visual Studio LightSwitch build output
343 | **/*.HTMLClient/GeneratedArtifacts
344 | **/*.DesktopClient/GeneratedArtifacts
345 | **/*.DesktopClient/ModelManifest.xml
346 | **/*.Server/GeneratedArtifacts
347 | **/*.Server/ModelManifest.xml
348 | _Pvt_Extensions
349 |
350 | # Paket dependency manager
351 | .paket/paket.exe
352 | paket-files/
353 |
354 | # FAKE - F# Make
355 | .fake/
356 |
357 | # CodeRush personal settings
358 | .cr/personal
359 |
360 | # Python Tools for Visual Studio (PTVS)
361 | __pycache__/
362 | *.pyc
363 |
364 | # Cake - Uncomment if you are using it
365 | # tools/**
366 | # !tools/packages.config
367 |
368 | # Tabs Studio
369 | *.tss
370 |
371 | # Telerik's JustMock configuration file
372 | *.jmconfig
373 |
374 | # BizTalk build output
375 | *.btp.cs
376 | *.btm.cs
377 | *.odx.cs
378 | *.xsd.cs
379 |
380 | # OpenCover UI analysis results
381 | OpenCover/
382 |
383 | # Azure Stream Analytics local run output
384 | ASALocalRun/
385 |
386 | # MSBuild Binary and Structured Log
387 | *.binlog
388 |
389 | # NVidia Nsight GPU debugger configuration file
390 | *.nvuser
391 |
392 | # MFractors (Xamarin productivity tool) working folder
393 | .mfractor/
394 |
395 | # Local History for Visual Studio
396 | .localhistory/
397 |
398 | # Visual Studio History (VSHistory) files
399 | .vshistory/
400 |
401 | # BeatPulse healthcheck temp database
402 | healthchecksdb
403 |
404 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
405 | MigrationBackup/
406 |
407 | # Ionide (cross platform F# VS Code tools) working folder
408 | .ionide/
409 |
410 | # Fody - auto-generated XML schema
411 | FodyWeavers.xsd
412 |
413 | # VS Code files for those working on multiple tools
414 | *.code-workspace
415 |
416 | # Local History for Visual Studio Code
417 |
418 | # Windows Installer files from build outputs
419 | *.cab
420 | *.msi
421 | *.msix
422 | *.msm
423 | *.msp
424 |
425 | # JetBrains Rider
426 | *.sln.iml
427 |
428 | ### VisualStudio Patch ###
429 | # Additional files built by Visual Studio
430 |
431 | # End of https://www.toptal.com/developers/gitignore/api/powershell,visualstudiocode,visualstudio
432 |
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | # PowerShell Script Module Repository Template
22 |
23 | A template repository and module for creating new PowerShell script module repos quickly with boilerplate files and CI/CD workflows already defined.
24 |
25 | ## ✨ Features
26 |
27 | Use this repo template or module for your new git repository to get the following features out-of-the-box:
28 |
29 | - GitHub Actions workflows (Azure DevOps Pipelines YAML support coming soon) that:
30 | - Publish a prerelease version on every commit to the `main` branch, and a stable version once manually approved.
31 | - Can also manually trigger deployments of feature branches.
32 | - Version the module.
33 | - Run PSScriptAnalyzer to ensure best practices are followed.
34 | - Run build tests with Pwsh and PowerShell to ensure backward compatibility.
35 | - Run smoke tests on multiple platforms (Windows, Linux, MacOS) to ensure the module works as expected after being installed on all platforms.
36 | - Publish the module to the PowerShell Gallery (custom feed support coming soon).
37 | - Spell check all files in the repository.
38 | - Display test code coverage results on PRs.
39 | - Visual Studio Code tasks to easily run Pester tests and PSScriptAnalyzer locally.
40 | - A `.devcontainer` for use with Visual Studio Code's [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) and [GitHub Codespaces](https://github.com/features/codespaces).
41 | - Boilerplate repository files, such as ReadMe, License, Changelog, .gitignore, .editorconfig, PR and Issue templates, and more.
42 |
43 | This template/module does not require any additional modules or dependencies to be installed on your machine, or force you to learn a new build framework or language.
44 |
45 | I plan on creating a dedicated video tutorial for this module and template, but until then you can [watch the demo portion of this presentation](https://www.youtube.com/watch?v=oM_2sOE9Y6g&t=1420s) that shows setting it up and using it, and talks about some of the benefits it provides.
46 |
47 | ## 🚀 Get started
48 |
49 | There are two ways to create your new PowerShell module repository:
50 |
51 | 1. Use the `New-PowerShellScriptModuleRepository` cmdlet to create a new repository, or
52 | 1. Create a new repository from this template in GitHub.
53 |
54 | Both of these methods are described in more detail below.
55 |
56 | Once the repository is created, follow the instructions in the repo's ReadMe file to complete the setup.
57 | The non-transformed instructions can also be [viewed here](/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/ReadMe.md).
58 |
59 | ### 📂 Method 1: Use the New-PowerShellScriptModuleRepository cmdlet
60 |
61 | Step 1: Install the `ScriptModuleRepositoryTemplate` module [from the PowerShell Gallery](https://www.powershellgallery.com/packages/ScriptModuleRepositoryTemplate):
62 |
63 | ```powershell
64 | Install-Module -Name ScriptModuleRepositoryTemplate -Scope CurrentUser
65 | ```
66 |
67 | Step 2: Create the new repository files:
68 |
69 | ```powershell
70 | New-PowerShellScriptModuleRepository -RepositoryDirectoryPath 'C:\MyRepoName' -ModuleName 'MyModuleName' -OrganizationName 'My Name'
71 | ```
72 |
73 | The above command will create a new directory at `C:\MyRepoName` with the boilerplate files and workflows for publishing your module already set up.
74 |
75 | You can then perform a `git init` in that directory and push it to where you want your git repository hosted (e.g. Azure DevOps or GitHub).
76 |
77 | To complete the setup, follow the instructions in the module repo's ReadMe file.
78 |
79 | ### 📄 Method 2: Create repository from GitHub template
80 |
81 | If your repository will be hosted on GitHub, you can follow the steps below:
82 |
83 | #### 🗍 Step 1: Create a new repo from this template
84 |
85 | The official docs for creating a new repository from a template can [be found here](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template).
86 | In short, the steps are:
87 |
88 | 1. Click the `Use this template` button at the top of the repository and choose `Create a new repository`.
89 | 1. Name your new repository (including your module's name is a good idea) and give it a description.
90 | 1. Click the `Create repository` button.
91 | 1. You should now have the new repository in your account with the name you chose.
92 | 1. Clone your new repository to your local machine to start making changes to it.
93 |
94 | #### 🤖 Step 2: Replace repo template information
95 |
96 | Run the [_InitializeRepository.ps1](/_InitializeRepository.ps1) script to update the repository files with your module's information.
97 | You will be prompted to enter some information, such as:
98 |
99 | - Your module's name (no spaces)
100 | - Your name or organization name (may contain spaces)
101 |
102 | Once the script completes, most of the repo files will be replaced.
103 | You should commit the changes.
104 |
105 | To complete the setup, follow the instructions in the repo's new ReadMe file (that replaced this one).
106 |
107 | ## 📋 Create your own template (optional)
108 |
109 | Not happy with some of the default template configurations?
110 | Maybe you don't like the .editorconfig settings, or want it to publish to your own internal PowerShell Gallery feed by default?
111 | You can derive your own template from this repository and use it for your future modules, minimizing the custom changes you need to make every time you create a new repo.
112 |
113 | To create your own template:
114 |
115 | 1. Fork [this repository on GitHub](https://github.com/deadlydog/PowerShell.ScriptModuleRepositoryTemplate).
116 | 1. In GitHub, from your repo's `Settings` tab under the `General` section, rename the repository to reflect that it is a template and check the box to make it a `Template repository`.
117 | 1. Modify [the template repo files](/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/) with whatever customizations you want.
118 | 1. If you are introducing more replacement tokens in the files, you will need to update the `Set-TemplateTokenValuesInAllRepositoryFiles` function in the [ScriptModuleRepositoryTemplate.psm1](/src/ScriptModuleRepositoryTemplate/ScriptModuleRepositoryTemplate.psm1) file to handle them.
119 |
120 | You can now create new repositories from your GitHub template in the same way you would use this one.
121 |
122 | If you want to be able to create new repositories from a module, you will need to publish your module under a different name.
123 |
124 | ## ⏪ Changelog
125 |
126 | See what's changed in the module and template over time by viewing [the changelog](Changelog.md).
127 |
128 | ## ❤ Donate to support this module and template
129 |
130 | Buy me a hot apple cider for providing this module and template open source and for free 🙂
131 |
132 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5MWSTSXNYEJWW)
133 |
134 | ## TODO
135 |
136 | Things to still do:
137 |
138 | - Add support for Azure DevOps.
139 | - Allow user to create single .psm1 file or Public/Private directory structure for separate files per function.
140 | - Allow using a custom PowerShell Gallery feed URL.
141 | - Have screenshots and/or recordings of manual steps to setup GitHub and Azure DevOps, since they involve clicking around in the UI.
142 | Perhaps link to [this tutorial for Azure DevOps](https://dev.to/olalekan_oladiran_d74b7a6/how-to-enable-continuous-integration-with-azure-pipelines-1doi)?
143 | - Perhaps we can automate this to avoid the manual steps altogether?
144 | - Maybe support new deployments on tag creation.
145 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.github/workflows/build-and-test-powershell-module.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | pull_request:
5 | branches: main
6 |
7 | # Allows you to run this workflow manually from the Actions tab.
8 | workflow_dispatch:
9 |
10 | # Allows the deployment workflow to call this workflow.
11 | workflow_call:
12 | inputs:
13 | versionNumber:
14 | description: 'The version number to use for the module. This should be in the format of "Major.Minor.Patch". e.g. "1.0.0". Future builds will increment from this version number. This input is optional. If not provided, the previous version numbers Patch will be incremented.'
15 | required: false
16 | type: string
17 | default: ''
18 | # Outputs required by the deployment workflow.
19 | outputs:
20 | powerShellModuleName:
21 | description: 'The name of the PowerShell module being built.'
22 | value: ${{ jobs.build-and-test.outputs.powerShellModuleName }}
23 | stableVersionNumber:
24 | description: 'The stable version number of the PowerShell module created by the build.'
25 | value: ${{ jobs.build-and-test.outputs.stableVersionNumber }}
26 | prereleaseVersionNumber:
27 | description: 'The full prerelease version number of the PowerShell module created by the build.'
28 | value: ${{ jobs.build-and-test.outputs.prereleaseVersionNumber }}
29 | prereleaseVersionLabel:
30 | description: 'The prerelease label of the PowerShell module created by the build.'
31 | value: ${{ jobs.build-and-test.outputs.prereleaseVersionLabel }}
32 | moduleArtifactName:
33 | description: 'The name of the module artifact created by the build.'
34 | value: ${{ jobs.build-and-test.outputs.moduleArtifactName }}
35 | deployFilesArtifactName:
36 | description: 'The name of the deploy files artifact created by the build.'
37 | value: ${{ jobs.build-and-test.outputs.deployFilesArtifactName }}
38 |
39 | env:
40 | powerShellModuleName: '__NewModuleName__'
41 | powerShellModuleDirectoryPath: './src/__NewModuleName__'
42 | deployFilesDirectoryPath: './deploy'
43 | moduleArtifactName: 'ModuleArtifact'
44 | moduleArtifactDirectoryPath: './artifacts/Module'
45 | deployFilesArtifactName: 'DeployFilesArtifact'
46 | deployFilesArtifactDirectoryPath: './artifacts/deploy'
47 |
48 | jobs:
49 | build-and-test:
50 | runs-on: windows-latest # Use Windows agent so we can run Pester tests on Windows PowerShell 5.1 as well.
51 | outputs:
52 | powerShellModuleName: ${{ env.powerShellModuleName }}
53 | stableVersionNumber: ${{ steps.version-number.outputs.majorMinorPatch }}
54 | prereleaseVersionNumber: ${{ steps.version-number.outputs.majorMinorPatch }}-${{ steps.version-number.outputs.prereleaseLabel }}
55 | prereleaseVersionLabel: ${{ steps.version-number.outputs.prereleaseLabel}}
56 | moduleArtifactName: ${{ env.moduleArtifactName }}
57 | deployFilesArtifactName: ${{ env.deployFilesArtifactName }}
58 | steps:
59 | - name: Checkout the repo source code
60 | uses: actions/checkout@v4
61 | with:
62 | fetch-depth: 0 # Fetch all history so that GitVersion can determine the version number.
63 |
64 | - name: Display PowerShell version and OS details in case needed for troubleshooting
65 | shell: pwsh
66 | run: $PSVersionTable
67 |
68 | # If you do not want to use spellcheck, delete this step and the .cspell.json file in the repository root.
69 | - name: Run spellcheck
70 | uses: streetsidesoftware/cspell-action@v5
71 |
72 | - name: Install GitVersion
73 | uses: gittools/actions/gitversion/setup@v0
74 | with:
75 | versionSpec: '5.x'
76 |
77 | - name: Get git metadata used to determine new version number
78 | id: git-version
79 | uses: gittools/actions/gitversion/execute@v0
80 |
81 | - name: Determine the new version number
82 | id: version-number
83 | shell: pwsh
84 | run: |
85 | [string] $newVersionNumber = '${{ steps.git-version.outputs.majorMinorPatch }}'
86 | [string] $prereleaseLabel = '${{ steps.git-version.outputs.preReleaseTag }}'
87 |
88 | [string] $manuallyProvidedVersionNumber = '${{ inputs.versionNumber }}'
89 | if (-not [string]::IsNullOrWhiteSpace($manuallyProvidedVersionNumber)) {
90 | Write-Output "Using manually provided version number '$manuallyProvidedVersionNumber'."
91 | $newVersionNumber = $manuallyProvidedVersionNumber
92 | }
93 |
94 | # The preReleaseTag is empty when building the default branch, so manually create a prerelease version number if needed.
95 | if ([string]::IsNullOrWhiteSpace($prereleaseLabel)) {
96 | [string] $dateTime = (Get-Date -Format 'yyyyMMddTHHmmss')
97 | $prereleaseLabel = 'CI' + $dateTime + 'SHA' + '${{ steps.git-version.outputs.shortSha }}'
98 | }
99 | # PowerShell prerelease labels can only contain the characters 'a-zA-Z0-9', so sanitize it if needed.
100 | $newVersionNumberPrereleaseLabel = $prereleaseLabel -replace '[^a-zA-Z0-9]', ''
101 |
102 | Write-Output "Setting step output variables 'majorMinorPatch=$newVersionNumber' and 'prereleaseLabel=$newVersionNumberPrereleaseLabel'."
103 | "majorMinorPatch=$newVersionNumber" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf8 -Append
104 | "prereleaseLabel=$newVersionNumberPrereleaseLabel" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf8 -Append
105 |
106 | # Suppress rules if needed: https://learn.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/using-scriptanalyzer
107 | - name: Run PowerShell linter with PSScriptAnalyzer
108 | shell: pwsh
109 | run: Invoke-ScriptAnalyzer -Path . -Recurse -EnableExit
110 |
111 | - name: Run Pester tests on Windows PowerShell to ensure backward compatibility
112 | shell: powershell
113 | run: |
114 | Write-Output "Pester version being used:"
115 | Import-Module -Name Pester
116 | Get-Module -Name Pester
117 |
118 | Write-Output "Running all Pester tests in the repo:"
119 | $pesterConfig = New-PesterConfiguration @{
120 | Output = @{ Verbosity = 'Detailed' }
121 | Run = @{ Throw = $true }
122 | TestResult = @{
123 | Enabled = $true
124 | OutputPath = 'test-results-windows-powershell-nunit.xml'
125 | }
126 | CodeCoverage = @{ Enabled = $false }
127 | }
128 | Invoke-Pester -Configuration $pesterConfig
129 |
130 | - name: Run Pester tests and generate code coverage report
131 | shell: pwsh
132 | run: |
133 | Write-Output "Pester version being used:"
134 | Import-Module -Name Pester
135 | Get-Module -Name Pester
136 |
137 | Write-Output "Running all Pester tests in the repo:"
138 | $pesterConfig = New-PesterConfiguration @{
139 | Output = @{ Verbosity = 'Detailed' }
140 | Run = @{ Throw = $true }
141 | TestResult = @{
142 | Enabled = $true
143 | OutputPath = 'test-results-pwsh-nunit.xml'
144 | }
145 | CodeCoverage = @{
146 | Enabled = $true
147 | OutputPath = 'code-coverage-jacoco.xml'
148 | Path = 'src/' # Only include code coverage for the module's source code, not build or deployment scripts.
149 | }
150 | }
151 | Invoke-Pester -Configuration $pesterConfig
152 |
153 | - name: Add code coverage report to PR
154 | # Adding the code coverage report is not supported for manual workflow runs.
155 | if: github.event_name != 'workflow_dispatch'
156 | uses: madrapps/jacoco-report@v1.6.1
157 | with:
158 | paths: code-coverage-jacoco.xml
159 | token: ${{ secrets.GITHUB_TOKEN }}
160 | # If you want to fail the build if the coverage is below a certain threshold, you can use the following options.
161 | # min-coverage-overall: 60
162 | # min-coverage-changed-files: 60
163 |
164 | - name: Create the module artifact
165 | shell: pwsh
166 | run: |
167 | Write-Output "Reading in environment variables."
168 | [string] $moduleName = $Env:powerShellModuleName
169 | [string] $moduleDirectoryPath = $Env:powerShellModuleDirectoryPath
170 | [string] $moduleManifestFileName = $moduleName + '.psd1'
171 | [string] $moduleManifestFilePath = Join-Path -Path $moduleDirectoryPath -ChildPath $moduleManifestFileName
172 | [string] $moduleArtifactDirectoryPath = Join-Path -Path $Env:moduleArtifactDirectoryPath -ChildPath $moduleName
173 | [string] $newVersionNumber = '${{ steps.version-number.outputs.majorMinorPatch}}'
174 |
175 | Write-Output "Updating the version number of the module manifest file '$moduleManifestFilePath' to '$newVersionNumber'."
176 | Update-ModuleManifest -Path $moduleManifestFilePath -ModuleVersion $newVersionNumber
177 |
178 | Write-Output "Testing the module manifest file '$moduleManifestFilePath' to ensure it is valid."
179 | Test-ModuleManifest -Path $moduleManifestFilePath
180 |
181 | Write-Output "Copying the module files to the module artifact directory '$moduleArtifactDirectoryPath'."
182 | Copy-Item -Path $moduleDirectoryPath -Destination $moduleArtifactDirectoryPath -Exclude '*.Tests.ps1' -Recurse -Force
183 |
184 | - name: Create deploy files artifact
185 | shell: pwsh
186 | run: |
187 | [string] $deployFilesDirectoryPath = $Env:deployFilesDirectoryPath
188 | [string] $deployFilesArtifactDirectoryPath = $Env:deployFilesArtifactDirectoryPath
189 |
190 | Write-Output "Copying the deployment files '$deployFilesDirectoryPath' to the deployment artifact directory '$deployFilesArtifactDirectoryPath'."
191 | Copy-Item -Path $deployFilesDirectoryPath -Destination $deployFilesArtifactDirectoryPath -Recurse -Force
192 |
193 | - name: Set the new version tag
194 | # Only run this step if we are doing a push (not a PR) to the default branch (e.g. main).
195 | if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
196 | shell: pwsh
197 | run: |
198 | [string] $newVersionNumber = '${{ steps.version-number.outputs.majorMinorPatch}}'
199 | [string] $newVersionTag = "v$newVersionNumber"
200 |
201 | # To avoid a 403 error on 'git push', ensure you have granted your GitHub Actions workflow read/write permission.
202 | # In your GitHub repo: Settings > Actions > General > Workflow permissions > Read and write permissions
203 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#configuring-the-default-github_token-permissions
204 |
205 | Write-Output "Tagging commit with new version tag '$newVersionTag'."
206 | & git tag $newVersionTag
207 | & git push origin $newVersionTag
208 |
209 | - name: Upload module artifact
210 | uses: actions/upload-artifact@v4
211 | with:
212 | name: ${{ env.moduleArtifactName }}
213 | path: ${{ env.moduleArtifactDirectoryPath }}
214 |
215 | - name: Upload deploy files artifact
216 | uses: actions/upload-artifact@v4
217 | with:
218 | name: ${{ env.deployFilesArtifactName }}
219 | path: ${{ env.deployFilesArtifactDirectoryPath }}
220 |
--------------------------------------------------------------------------------
/.github/workflows/build-test-and-deploy-powershell-module.yml:
--------------------------------------------------------------------------------
1 | name: build-and-deploy
2 |
3 | on:
4 | push:
5 | branches: main
6 | paths: [ "src/**", "build/**", "deploy/**", ".github/workflows/**", ".cspell.json" ]
7 |
8 | # Allows you to run this workflow manually from the Actions tab.
9 | workflow_dispatch:
10 | inputs:
11 | versionNumber:
12 | description: 'The version number to use for the module. This should be in the format of "Major.Minor.Patch". e.g. "1.0.0". Future builds will increment from this version number. This input is optional. If not provided, the previous version numbers Patch will be incremented.'
13 | required: false
14 | type: string
15 | default: ''
16 |
17 | env:
18 | artifactsDirectoryPath: './artifacts'
19 |
20 | jobs:
21 | run-build-and-test:
22 | uses: ./.github/workflows/build-and-test-powershell-module.yml
23 | with:
24 | versionNumber: ${{ github.event.inputs.versionNumber }}
25 |
26 | publish-prerelease-module:
27 | needs: run-build-and-test
28 | runs-on: ubuntu-latest
29 | steps:
30 | - name: Download module artifact
31 | uses: actions/download-artifact@v4
32 | with:
33 | name: ${{ needs.run-build-and-test.outputs.moduleArtifactName }}
34 | path: ${{ env.artifactsDirectoryPath }}
35 |
36 | - name: Publish prerelease PowerShell module
37 | shell: pwsh
38 | run: |
39 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
40 | [string] $moduleDirectoryPath = "$Env:artifactsDirectoryPath/$moduleName"
41 | [string] $moduleManifestFilePath = Join-Path -Path $moduleDirectoryPath -ChildPath "$moduleName.psd1"
42 | [string] $prereleaseVersionLabel = '${{ needs.run-build-and-test.outputs.prereleaseVersionLabel}}'
43 |
44 | Write-Output "Updating the module manifest version number's prerelease label to '$prereleaseVersionLabel'."
45 | Update-ModuleManifest -Path $moduleManifestFilePath -Prerelease $prereleaseVersionLabel
46 |
47 | Write-Output "Testing the prerelease module manifest file '$moduleManifestFilePath' to ensure it is still valid."
48 | Test-ModuleManifest -Path $moduleManifestFilePath
49 |
50 | Write-Output "Publishing the prerelease version of the module."
51 | Publish-Module -Path $moduleDirectoryPath -NuGetApiKey '${{ secrets.POWERSHELL_GALLERY_API_KEY }}' -Verbose
52 |
53 | - name: Wait a short while for the module to be available on the PowerShell Gallery before continuing
54 | shell: pwsh
55 | run: Start-Sleep -Seconds 30
56 |
57 | test-prerelease-module-in-pwsh:
58 | needs: [run-build-and-test, publish-prerelease-module]
59 | strategy:
60 | matrix:
61 | os: [ubuntu-latest, windows-latest, macOS-latest]
62 | runs-on: ${{ matrix.os }}
63 | steps:
64 | - name: Display PowerShell version being used
65 | shell: pwsh
66 | run: $PSVersionTable
67 |
68 | - name: Install prerelease module from PowerShell Gallery
69 | shell: pwsh
70 | run: |
71 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
72 | [string] $prereleaseVersionNumber = '${{ needs.run-build-and-test.outputs.prereleaseVersionNumber}}'
73 |
74 | Write-Output "Installing the module '$moduleName' prerelease version '$prereleaseVersionNumber' from the PowerShell Gallery."
75 | Install-Module -Name $moduleName -AllowPrerelease -RequiredVersion $prereleaseVersionNumber -Force -Scope CurrentUser -Repository PSGallery -ErrorAction Stop -Verbose
76 |
77 | - name: Download deploy files artifact
78 | uses: actions/download-artifact@v4
79 | with:
80 | name: ${{ needs.run-build-and-test.outputs.deployFilesArtifactName}}
81 | path: ${{ env.artifactsDirectoryPath }}
82 |
83 | - name: Run smoke tests
84 | shell: pwsh
85 | run: |
86 | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1"
87 |
88 | Write-Output "Running Pester smoke tests from file '$smokeTestsScriptPath'."
89 | $pesterConfig = New-PesterConfiguration @{
90 | Output = @{ Verbosity = 'Detailed' }
91 | Run = @{
92 | Path = $smokeTestsScriptPath
93 | Throw = $true
94 | }
95 | }
96 | Invoke-Pester -Configuration $pesterConfig
97 |
98 | Write-Output "Displaying the installed module version that was used for the smoke tests."
99 | Get-Module -Name '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
100 |
101 | test-prerelease-module-in-windows-powershell:
102 | needs: [run-build-and-test, publish-prerelease-module]
103 | runs-on: windows-latest
104 | steps:
105 | - name: Display PowerShell version being used
106 | shell: powershell
107 | run: $PSVersionTable
108 |
109 | - name: Install prerelease module from PowerShell Gallery
110 | shell: powershell
111 | run: |
112 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
113 | [string] $prereleaseVersionNumber = '${{ needs.run-build-and-test.outputs.prereleaseVersionNumber}}'
114 |
115 | Write-Output "Installing the module '$moduleName' prerelease version '$prereleaseVersionNumber' from the PowerShell Gallery."
116 | Install-Module -Name $moduleName -AllowPrerelease -RequiredVersion $prereleaseVersionNumber -Force -Scope CurrentUser -Repository PSGallery -ErrorAction Stop -Verbose
117 |
118 | - name: Download deploy files artifact from triggered workflow
119 | uses: actions/download-artifact@v4
120 | with:
121 | name: ${{ needs.run-build-and-test.outputs.deployFilesArtifactName}}
122 | path: ${{ env.artifactsDirectoryPath }}
123 |
124 | - name: Run smoke tests
125 | shell: powershell
126 | run: |
127 | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1"
128 |
129 | Write-Output "Running Pester smoke tests from file '$smokeTestsScriptPath'."
130 | $pesterConfig = New-PesterConfiguration @{
131 | Output = @{ Verbosity = 'Detailed' }
132 | Run = @{
133 | Path = $smokeTestsScriptPath
134 | Throw = $true
135 | }
136 | }
137 | Invoke-Pester -Configuration $pesterConfig
138 |
139 | Write-Output "Displaying the installed module version that was used for the smoke tests."
140 | Get-Module -Name '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
141 |
142 | publish-stable-module:
143 | needs: [run-build-and-test, test-prerelease-module-in-pwsh, test-prerelease-module-in-windows-powershell]
144 | runs-on: ubuntu-latest
145 | environment: production # Used for deployment approvals.
146 | steps:
147 | - name: Download module artifact from triggered workflow
148 | uses: actions/download-artifact@v4
149 | with:
150 | name: ${{ needs.run-build-and-test.outputs.moduleArtifactName}}
151 | path: ${{ env.artifactsDirectoryPath }}
152 |
153 | - name: Publish stable PowerShell module
154 | shell: pwsh
155 | run: |
156 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
157 | [string] $moduleDirectoryPath = "$Env:artifactsDirectoryPath/$moduleName"
158 | Publish-Module -Path $moduleDirectoryPath -NuGetApiKey '${{ secrets.POWERSHELL_GALLERY_API_KEY }}' -Verbose
159 |
160 | - name: Wait a short while for the module to be available on the PowerShell Gallery before continuing
161 | shell: pwsh
162 | run: Start-Sleep -Seconds 30
163 |
164 | test-stable-module-in-pwsh:
165 | needs: [run-build-and-test, publish-stable-module]
166 | strategy:
167 | matrix:
168 | os: [ubuntu-latest, windows-latest, macOS-latest]
169 | runs-on: ${{ matrix.os }}
170 | steps:
171 | - name: Display PowerShell version being used
172 | shell: pwsh
173 | run: $PSVersionTable
174 |
175 | - name: Install stable module from PowerShell Gallery
176 | shell: pwsh
177 | run: |
178 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
179 | [string] $stableVersionNumber = '${{ needs.run-build-and-test.outputs.stableVersionNumber}}'
180 |
181 | Write-Output "Installing the module '$moduleName' stable version '$stableVersionNumber' from the PowerShell Gallery."
182 | Install-Module -Name $moduleName -RequiredVersion $stableVersionNumber -Force -Scope CurrentUser -Repository PSGallery -ErrorAction Stop -Verbose
183 |
184 | - name: Download deploy files artifact from triggered workflow
185 | uses: actions/download-artifact@v4
186 | with:
187 | name: ${{ needs.run-build-and-test.outputs.deployFilesArtifactName}}
188 | path: ${{ env.artifactsDirectoryPath }}
189 |
190 | - name: Run smoke tests
191 | shell: pwsh
192 | run: |
193 | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1"
194 |
195 | Write-Output "Running Pester smoke tests from file '$smokeTestsScriptPath'."
196 | $pesterConfig = New-PesterConfiguration @{
197 | Output = @{ Verbosity = 'Detailed' }
198 | Run = @{
199 | Path = $smokeTestsScriptPath
200 | Throw = $true
201 | }
202 | }
203 | Invoke-Pester -Configuration $pesterConfig
204 |
205 | Write-Output "Displaying the installed module version that was used for the smoke tests."
206 | Get-Module -Name '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
207 |
208 | test-stable-module-in-windows-powershell:
209 | needs: [run-build-and-test, publish-stable-module]
210 | runs-on: windows-latest
211 | steps:
212 | - name: Display PowerShell version being used
213 | shell: powershell
214 | run: $PSVersionTable
215 |
216 | - name: Install stable module from PowerShell Gallery
217 | shell: powershell
218 | run: |
219 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
220 | [string] $stableVersionNumber = '${{ needs.run-build-and-test.outputs.stableVersionNumber}}'
221 |
222 | Write-Output "Installing the module '$moduleName' stable version '$stableVersionNumber' from the PowerShell Gallery."
223 | Install-Module -Name $moduleName -RequiredVersion $stableVersionNumber -Force -Scope CurrentUser -Repository PSGallery -ErrorAction Stop -Verbose
224 |
225 | - name: Download deploy files artifact from triggered workflow
226 | uses: actions/download-artifact@v4
227 | with:
228 | name: ${{ needs.run-build-and-test.outputs.deployFilesArtifactName}}
229 | path: ${{ env.artifactsDirectoryPath }}
230 |
231 | - name: Run smoke tests
232 | shell: powershell
233 | run: |
234 | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1"
235 |
236 | Write-Output "Running Pester smoke tests from file '$smokeTestsScriptPath'."
237 | $pesterConfig = New-PesterConfiguration @{
238 | Output = @{ Verbosity = 'Detailed' }
239 | Run = @{
240 | Path = $smokeTestsScriptPath
241 | Throw = $true
242 | }
243 | }
244 | Invoke-Pester -Configuration $pesterConfig
245 |
246 | Write-Output "Displaying the installed module version that was used for the smoke tests."
247 | Get-Module -Name '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
248 |
--------------------------------------------------------------------------------
/src/ScriptModuleRepositoryTemplate/TemplateRepoFiles/_.github/workflows/build-test-and-deploy-powershell-module.yml:
--------------------------------------------------------------------------------
1 | name: build-and-deploy
2 |
3 | on:
4 | push:
5 | branches: main
6 | paths: [ "src/**", "build/**", "deploy/**", ".github/workflows/**", ".cspell.json" ]
7 |
8 | # Allows you to run this workflow manually from the Actions tab.
9 | workflow_dispatch:
10 | inputs:
11 | versionNumber:
12 | description: 'The version number to use for the module. This should be in the format of "Major.Minor.Patch". e.g. "1.0.0". Future builds will increment from this version number. This input is optional. If not provided, the previous version numbers Patch will be incremented.'
13 | required: false
14 | type: string
15 | default: ''
16 |
17 | env:
18 | artifactsDirectoryPath: './artifacts'
19 |
20 | jobs:
21 | run-build-and-test:
22 | uses: ./.github/workflows/build-and-test-powershell-module.yml
23 | with:
24 | versionNumber: ${{ github.event.inputs.versionNumber }}
25 |
26 | publish-prerelease-module:
27 | needs: run-build-and-test
28 | runs-on: ubuntu-latest
29 | steps:
30 | - name: Download module artifact
31 | uses: actions/download-artifact@v4
32 | with:
33 | name: ${{ needs.run-build-and-test.outputs.moduleArtifactName }}
34 | path: ${{ env.artifactsDirectoryPath }}
35 |
36 | - name: Publish prerelease PowerShell module
37 | shell: pwsh
38 | run: |
39 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
40 | [string] $moduleDirectoryPath = "$Env:artifactsDirectoryPath/$moduleName"
41 | [string] $moduleManifestFilePath = Join-Path -Path $moduleDirectoryPath -ChildPath "$moduleName.psd1"
42 | [string] $prereleaseVersionLabel = '${{ needs.run-build-and-test.outputs.prereleaseVersionLabel}}'
43 |
44 | Write-Output "Updating the module manifest version number's prerelease label to '$prereleaseVersionLabel'."
45 | Update-ModuleManifest -Path $moduleManifestFilePath -Prerelease $prereleaseVersionLabel
46 |
47 | Write-Output "Testing the prerelease module manifest file '$moduleManifestFilePath' to ensure it is still valid."
48 | Test-ModuleManifest -Path $moduleManifestFilePath
49 |
50 | Write-Output "Publishing the prerelease version of the module."
51 | Publish-Module -Path $moduleDirectoryPath -NuGetApiKey '${{ secrets.POWERSHELL_GALLERY_API_KEY }}' -Verbose
52 |
53 | - name: Wait a short while for the module to be available on the PowerShell Gallery before continuing
54 | shell: pwsh
55 | run: Start-Sleep -Seconds 30
56 |
57 | test-prerelease-module-in-pwsh:
58 | needs: [run-build-and-test, publish-prerelease-module]
59 | strategy:
60 | matrix:
61 | os: [ubuntu-latest, windows-latest, macOS-latest]
62 | runs-on: ${{ matrix.os }}
63 | steps:
64 | - name: Display PowerShell version being used
65 | shell: pwsh
66 | run: $PSVersionTable
67 |
68 | - name: Install prerelease module from PowerShell Gallery
69 | shell: pwsh
70 | run: |
71 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
72 | [string] $prereleaseVersionNumber = '${{ needs.run-build-and-test.outputs.prereleaseVersionNumber}}'
73 |
74 | Write-Output "Installing the module '$moduleName' prerelease version '$prereleaseVersionNumber' from the PowerShell Gallery."
75 | Install-Module -Name $moduleName -AllowPrerelease -RequiredVersion $prereleaseVersionNumber -Force -Scope CurrentUser -Repository PSGallery -ErrorAction Stop -Verbose
76 |
77 | - name: Download deploy files artifact
78 | uses: actions/download-artifact@v4
79 | with:
80 | name: ${{ needs.run-build-and-test.outputs.deployFilesArtifactName}}
81 | path: ${{ env.artifactsDirectoryPath }}
82 |
83 | - name: Run smoke tests
84 | shell: pwsh
85 | run: |
86 | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1"
87 |
88 | Write-Output "Running Pester smoke tests from file '$smokeTestsScriptPath'."
89 | $pesterConfig = New-PesterConfiguration @{
90 | Output = @{ Verbosity = 'Detailed' }
91 | Run = @{
92 | Path = $smokeTestsScriptPath
93 | Throw = $true
94 | }
95 | }
96 | Invoke-Pester -Configuration $pesterConfig
97 |
98 | Write-Output "Displaying the installed module version that was used for the smoke tests."
99 | Get-Module -Name '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
100 |
101 | test-prerelease-module-in-windows-powershell:
102 | needs: [run-build-and-test, publish-prerelease-module]
103 | runs-on: windows-latest
104 | steps:
105 | - name: Display PowerShell version being used
106 | shell: powershell
107 | run: $PSVersionTable
108 |
109 | - name: Install prerelease module from PowerShell Gallery
110 | shell: powershell
111 | run: |
112 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
113 | [string] $prereleaseVersionNumber = '${{ needs.run-build-and-test.outputs.prereleaseVersionNumber}}'
114 |
115 | Write-Output "Installing the module '$moduleName' prerelease version '$prereleaseVersionNumber' from the PowerShell Gallery."
116 | Install-Module -Name $moduleName -AllowPrerelease -RequiredVersion $prereleaseVersionNumber -Force -Scope CurrentUser -Repository PSGallery -ErrorAction Stop -Verbose
117 |
118 | - name: Download deploy files artifact from triggered workflow
119 | uses: actions/download-artifact@v4
120 | with:
121 | name: ${{ needs.run-build-and-test.outputs.deployFilesArtifactName}}
122 | path: ${{ env.artifactsDirectoryPath }}
123 |
124 | - name: Run smoke tests
125 | shell: powershell
126 | run: |
127 | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1"
128 |
129 | Write-Output "Running Pester smoke tests from file '$smokeTestsScriptPath'."
130 | $pesterConfig = New-PesterConfiguration @{
131 | Output = @{ Verbosity = 'Detailed' }
132 | Run = @{
133 | Path = $smokeTestsScriptPath
134 | Throw = $true
135 | }
136 | }
137 | Invoke-Pester -Configuration $pesterConfig
138 |
139 | Write-Output "Displaying the installed module version that was used for the smoke tests."
140 | Get-Module -Name '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
141 |
142 | publish-stable-module:
143 | needs: [run-build-and-test, test-prerelease-module-in-pwsh, test-prerelease-module-in-windows-powershell]
144 | runs-on: ubuntu-latest
145 | environment: production # Used for deployment approvals.
146 | steps:
147 | - name: Download module artifact from triggered workflow
148 | uses: actions/download-artifact@v4
149 | with:
150 | name: ${{ needs.run-build-and-test.outputs.moduleArtifactName}}
151 | path: ${{ env.artifactsDirectoryPath }}
152 |
153 | - name: Publish stable PowerShell module
154 | shell: pwsh
155 | run: |
156 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
157 | [string] $moduleDirectoryPath = "$Env:artifactsDirectoryPath/$moduleName"
158 | Publish-Module -Path $moduleDirectoryPath -NuGetApiKey '${{ secrets.POWERSHELL_GALLERY_API_KEY }}' -Verbose
159 |
160 | - name: Wait a short while for the module to be available on the PowerShell Gallery before continuing
161 | shell: pwsh
162 | run: Start-Sleep -Seconds 30
163 |
164 | test-stable-module-in-pwsh:
165 | needs: [run-build-and-test, publish-stable-module]
166 | strategy:
167 | matrix:
168 | os: [ubuntu-latest, windows-latest, macOS-latest]
169 | runs-on: ${{ matrix.os }}
170 | steps:
171 | - name: Display PowerShell version being used
172 | shell: pwsh
173 | run: $PSVersionTable
174 |
175 | - name: Install stable module from PowerShell Gallery
176 | shell: pwsh
177 | run: |
178 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
179 | [string] $stableVersionNumber = '${{ needs.run-build-and-test.outputs.stableVersionNumber}}'
180 |
181 | Write-Output "Installing the module '$moduleName' stable version '$stableVersionNumber' from the PowerShell Gallery."
182 | Install-Module -Name $moduleName -RequiredVersion $stableVersionNumber -Force -Scope CurrentUser -Repository PSGallery -ErrorAction Stop -Verbose
183 |
184 | - name: Download deploy files artifact from triggered workflow
185 | uses: actions/download-artifact@v4
186 | with:
187 | name: ${{ needs.run-build-and-test.outputs.deployFilesArtifactName}}
188 | path: ${{ env.artifactsDirectoryPath }}
189 |
190 | - name: Run smoke tests
191 | shell: pwsh
192 | run: |
193 | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1"
194 |
195 | Write-Output "Running Pester smoke tests from file '$smokeTestsScriptPath'."
196 | $pesterConfig = New-PesterConfiguration @{
197 | Output = @{ Verbosity = 'Detailed' }
198 | Run = @{
199 | Path = $smokeTestsScriptPath
200 | Throw = $true
201 | }
202 | }
203 | Invoke-Pester -Configuration $pesterConfig
204 |
205 | Write-Output "Displaying the installed module version that was used for the smoke tests."
206 | Get-Module -Name '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
207 |
208 | test-stable-module-in-windows-powershell:
209 | needs: [run-build-and-test, publish-stable-module]
210 | runs-on: windows-latest
211 | steps:
212 | - name: Display PowerShell version being used
213 | shell: powershell
214 | run: $PSVersionTable
215 |
216 | - name: Install stable module from PowerShell Gallery
217 | shell: powershell
218 | run: |
219 | [string] $moduleName = '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
220 | [string] $stableVersionNumber = '${{ needs.run-build-and-test.outputs.stableVersionNumber}}'
221 |
222 | Write-Output "Installing the module '$moduleName' stable version '$stableVersionNumber' from the PowerShell Gallery."
223 | Install-Module -Name $moduleName -RequiredVersion $stableVersionNumber -Force -Scope CurrentUser -Repository PSGallery -ErrorAction Stop -Verbose
224 |
225 | - name: Download deploy files artifact from triggered workflow
226 | uses: actions/download-artifact@v4
227 | with:
228 | name: ${{ needs.run-build-and-test.outputs.deployFilesArtifactName}}
229 | path: ${{ env.artifactsDirectoryPath }}
230 |
231 | - name: Run smoke tests
232 | shell: powershell
233 | run: |
234 | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1"
235 |
236 | Write-Output "Running Pester smoke tests from file '$smokeTestsScriptPath'."
237 | $pesterConfig = New-PesterConfiguration @{
238 | Output = @{ Verbosity = 'Detailed' }
239 | Run = @{
240 | Path = $smokeTestsScriptPath
241 | Throw = $true
242 | }
243 | }
244 | Invoke-Pester -Configuration $pesterConfig
245 |
246 | Write-Output "Displaying the installed module version that was used for the smoke tests."
247 | Get-Module -Name '${{ needs.run-build-and-test.outputs.powerShellModuleName }}'
248 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-test-powershell-module.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | pull_request:
5 | branches: main
6 |
7 | # Allows you to run this workflow manually from the Actions tab.
8 | workflow_dispatch:
9 |
10 | # Allows the deployment workflow to call this workflow.
11 | workflow_call:
12 | inputs:
13 | versionNumber:
14 | description: 'The version number to use for the module. This should be in the format of "Major.Minor.Patch". e.g. "1.0.0". Future builds will increment from this version number. This input is optional. If not provided, the previous version numbers Patch will be incremented.'
15 | required: false
16 | type: string
17 | default: ''
18 | # Outputs required by the deployment workflow.
19 | outputs:
20 | powerShellModuleName:
21 | description: 'The name of the PowerShell module being built.'
22 | value: ${{ jobs.build-and-test.outputs.powerShellModuleName }}
23 | stableVersionNumber:
24 | description: 'The stable version number of the PowerShell module created by the build.'
25 | value: ${{ jobs.build-and-test.outputs.stableVersionNumber }}
26 | prereleaseVersionNumber:
27 | description: 'The full prerelease version number of the PowerShell module created by the build.'
28 | value: ${{ jobs.build-and-test.outputs.prereleaseVersionNumber }}
29 | prereleaseVersionLabel:
30 | description: 'The prerelease label of the PowerShell module created by the build.'
31 | value: ${{ jobs.build-and-test.outputs.prereleaseVersionLabel }}
32 | moduleArtifactName:
33 | description: 'The name of the module artifact created by the build.'
34 | value: ${{ jobs.build-and-test.outputs.moduleArtifactName }}
35 | deployFilesArtifactName:
36 | description: 'The name of the deploy files artifact created by the build.'
37 | value: ${{ jobs.build-and-test.outputs.deployFilesArtifactName }}
38 |
39 | env:
40 | powerShellModuleName: 'ScriptModuleRepositoryTemplate'
41 | powerShellModuleDirectoryPath: './src/ScriptModuleRepositoryTemplate'
42 | deployFilesDirectoryPath: './deploy'
43 | moduleArtifactName: 'ModuleArtifact'
44 | moduleArtifactDirectoryPath: './artifacts/Module'
45 | deployFilesArtifactName: 'DeployFilesArtifact'
46 | deployFilesArtifactDirectoryPath: './artifacts/deploy'
47 |
48 | jobs:
49 | build-and-test:
50 | runs-on: windows-latest # Use Windows agent so we can run Pester tests on Windows PowerShell 5.1 as well.
51 | outputs:
52 | powerShellModuleName: ${{ env.powerShellModuleName }}
53 | stableVersionNumber: ${{ steps.version-number.outputs.majorMinorPatch }}
54 | prereleaseVersionNumber: ${{ steps.version-number.outputs.majorMinorPatch }}-${{ steps.version-number.outputs.prereleaseLabel }}
55 | prereleaseVersionLabel: ${{ steps.version-number.outputs.prereleaseLabel}}
56 | moduleArtifactName: ${{ env.moduleArtifactName }}
57 | deployFilesArtifactName: ${{ env.deployFilesArtifactName }}
58 | steps:
59 | - name: Checkout the repo source code
60 | uses: actions/checkout@v4
61 | with:
62 | fetch-depth: 0 # Fetch all history so that GitVersion can determine the version number.
63 |
64 | - name: Display PowerShell version and OS details in case needed for troubleshooting
65 | shell: pwsh
66 | run: $PSVersionTable
67 |
68 | - name: Run spellcheck
69 | uses: streetsidesoftware/cspell-action@v5
70 |
71 | - name: Install GitVersion
72 | uses: gittools/actions/gitversion/setup@v0
73 | with:
74 | versionSpec: '5.x'
75 |
76 | - name: Get git metadata used to determine new version number
77 | id: git-version
78 | uses: gittools/actions/gitversion/execute@v0
79 |
80 | - name: Determine the new version number
81 | id: version-number
82 | shell: pwsh
83 | run: |
84 | [string] $newVersionNumber = '${{ steps.git-version.outputs.majorMinorPatch }}'
85 | [string] $prereleaseLabel = '${{ steps.git-version.outputs.preReleaseTag }}'
86 |
87 | [string] $manuallyProvidedVersionNumber = '${{ inputs.versionNumber }}'
88 | if (-not [string]::IsNullOrWhiteSpace($manuallyProvidedVersionNumber)) {
89 | Write-Output "Using manually provided version number '$manuallyProvidedVersionNumber'."
90 | $newVersionNumber = $manuallyProvidedVersionNumber
91 | }
92 |
93 | # The preReleaseTag is empty when building the default branch, so manually create a prerelease version number if needed.
94 | if ([string]::IsNullOrWhiteSpace($prereleaseLabel)) {
95 | [string] $dateTime = (Get-Date -Format 'yyyyMMddTHHmmss')
96 | $prereleaseLabel = 'CI' + $dateTime + 'SHA' + '${{ steps.git-version.outputs.shortSha }}'
97 | }
98 | # PowerShell prerelease labels can only contain the characters 'a-zA-Z0-9', so sanitize it if needed.
99 | $newVersionNumberPrereleaseLabel = $prereleaseLabel -replace '[^a-zA-Z0-9]', ''
100 |
101 | Write-Output "Setting step output variables 'majorMinorPatch=$newVersionNumber' and 'prereleaseLabel=$newVersionNumberPrereleaseLabel'."
102 | "majorMinorPatch=$newVersionNumber" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf8 -Append
103 | "prereleaseLabel=$newVersionNumberPrereleaseLabel" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf8 -Append
104 |
105 | # Suppress rules if needed: https://learn.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/using-scriptanalyzer
106 | - name: Run PowerShell linter with PSScriptAnalyzer
107 | shell: pwsh
108 | run: Invoke-ScriptAnalyzer -Path . -Recurse -EnableExit
109 |
110 | - name: Run Pester tests on Windows PowerShell to ensure backward compatibility
111 | shell: powershell
112 | run: |
113 | Write-Output "Pester version being used:"
114 | Import-Module -Name Pester
115 | Get-Module -Name Pester
116 |
117 | Write-Output "Running all Pester tests in the repo:"
118 | $pesterConfig = New-PesterConfiguration @{
119 | Output = @{ Verbosity = 'Detailed' }
120 | Run = @{ Throw = $true }
121 | TestResult = @{
122 | Enabled = $true
123 | OutputPath = 'test-results-windows-powershell-nunit.xml'
124 | }
125 | CodeCoverage = @{ Enabled = $false }
126 | }
127 | Invoke-Pester -Configuration $pesterConfig
128 |
129 | - name: Run Pester tests and generate code coverage report
130 | shell: pwsh
131 | run: |
132 | Write-Output "Pester version being used:"
133 | Import-Module -Name Pester
134 | Get-Module -Name Pester
135 |
136 | Write-Output "Running all Pester tests in the repo:"
137 | $pesterConfig = New-PesterConfiguration @{
138 | Output = @{ Verbosity = 'Detailed' }
139 | Run = @{ Throw = $true }
140 | TestResult = @{
141 | Enabled = $true
142 | OutputPath = 'test-results-pwsh-nunit.xml'
143 | }
144 | CodeCoverage = @{
145 | Enabled = $true
146 | OutputPath = 'code-coverage-jacoco.xml'
147 | Path = 'src/' # Only include code coverage for the module's source code, not build or deployment scripts.
148 | }
149 | }
150 | Invoke-Pester -Configuration $pesterConfig
151 |
152 | - name: Add code coverage report to PR
153 | # Adding the code coverage report is not supported for manual workflow runs.
154 | if: github.event_name != 'workflow_dispatch'
155 | uses: madrapps/jacoco-report@v1.6.1
156 | with:
157 | paths: code-coverage-jacoco.xml
158 | token: ${{ secrets.GITHUB_TOKEN }}
159 | min-coverage-overall: 60
160 | min-coverage-changed-files: 60
161 |
162 | - name: Create the module artifact
163 | shell: pwsh
164 | run: |
165 | Write-Output "Reading in environment variables."
166 | [string] $moduleName = $Env:powerShellModuleName
167 | [string] $moduleDirectoryPath = $Env:powerShellModuleDirectoryPath
168 | [string] $moduleManifestFileName = $moduleName + '.psd1'
169 | [string] $moduleManifestFilePath = Join-Path -Path $moduleDirectoryPath -ChildPath $moduleManifestFileName
170 | [string] $moduleArtifactDirectoryPath = Join-Path -Path $Env:moduleArtifactDirectoryPath -ChildPath $moduleName
171 | [string] $newVersionNumber = '${{ steps.version-number.outputs.majorMinorPatch}}'
172 |
173 | Write-Output "Updating the version number of the module manifest file '$moduleManifestFilePath' to '$newVersionNumber'."
174 | Update-ModuleManifest -Path $moduleManifestFilePath -ModuleVersion $newVersionNumber
175 |
176 | Write-Output "Testing the module manifest file '$moduleManifestFilePath' to ensure it is valid."
177 | Test-ModuleManifest -Path $moduleManifestFilePath
178 |
179 | Write-Output "Copying the module files to the module artifact directory '$moduleArtifactDirectoryPath'."
180 | Copy-Item -Path $moduleDirectoryPath -Destination $moduleArtifactDirectoryPath -Exclude '*.Tests.ps1' -Recurse -Force
181 |
182 | [string] $moduleTemplateRepoFilesDirectoryPath = Join-Path -Path $moduleDirectoryPath -ChildPath 'TemplateRepoFiles'
183 | Write-Output "Copying the module template repo files '$moduleTemplateRepoFilesDirectoryPath' to the module artifact directory '$moduleArtifactDirectoryPath' verbatim, to ensure test files are included."
184 | Copy-Item -Path $moduleTemplateRepoFilesDirectoryPath -Destination $moduleArtifactDirectoryPath -Recurse -Force
185 |
186 | - name: Create deploy files artifact
187 | shell: pwsh
188 | run: |
189 | [string] $deployFilesDirectoryPath = $Env:deployFilesDirectoryPath
190 | [string] $deployFilesArtifactDirectoryPath = $Env:deployFilesArtifactDirectoryPath
191 |
192 | Write-Output "Copying the deployment files '$deployFilesDirectoryPath' to the deployment artifact directory '$deployFilesArtifactDirectoryPath'."
193 | Copy-Item -Path $deployFilesDirectoryPath -Destination $deployFilesArtifactDirectoryPath -Recurse -Force
194 |
195 | - name: Set the new version tag
196 | # Only run this step if we are doing a push (not a PR) to the default branch (e.g. main).
197 | if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
198 | shell: pwsh
199 | run: |
200 | [string] $newVersionNumber = '${{ steps.version-number.outputs.majorMinorPatch}}'
201 | [string] $newVersionTag = "v$newVersionNumber"
202 |
203 | # To avoid a 403 error on 'git push', ensure you have granted your GitHub Actions workflow read/write permission.
204 | # In your GitHub repo: Settings > Actions > General > Workflow permissions > Read and write permissions
205 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#configuring-the-default-github_token-permissions
206 |
207 | Write-Output "Tagging commit with new version tag '$newVersionTag'."
208 | & git tag $newVersionTag
209 | & git push origin $newVersionTag
210 |
211 | - name: Upload module artifact
212 | uses: actions/upload-artifact@v4
213 | with:
214 | name: ${{ env.moduleArtifactName }}
215 | path: ${{ env.moduleArtifactDirectoryPath }}
216 |
217 | - name: Upload deploy files artifact
218 | uses: actions/upload-artifact@v4
219 | with:
220 | name: ${{ env.deployFilesArtifactName }}
221 | path: ${{ env.deployFilesArtifactDirectoryPath }}
222 |
--------------------------------------------------------------------------------