1"use strict";(self.webpackChunkpsake=self.webpackChunkpsake||[]).push([[9086],{28453:(e,n,t)=>{t.d(n,{R:()=>o,x:()=>i});var s=t(96540);const r={},l=s.createContext(r);function o(e){const n=s.useContext(l);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),s.createElement(l.Provider,{value:n},e.children)}},32351:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>a,contentTitle:()=>i,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>d});const s=JSON.parse('{"id":"powershellbuild/real-world-example","title":"Real-World Example","description":"A complete PowerShellBuild project with custom tasks, CI/CD integration, and PSGallery publishing.","source":"@site/docs/powershellbuild/real-world-example.md","sourceDirName":"powershellbuild","slug":"/powershellbuild/real-world-example","permalink":"/docs/powershellbuild/real-world-example","draft":false,"unlisted":false,"editUrl":"https://github.com/psake/docs/blob/main/docs/powershellbuild/real-world-example.md","tags":[],"version":"current","frontMatter":{"title":"Real-World Example","description":"A complete PowerShellBuild project with custom tasks, CI/CD integration, and PSGallery publishing."},"sidebar":"powershellBuildSidebar","previous":{"title":"Configuration Reference","permalink":"/docs/powershellbuild/configuration"}}');var r=t(74848),l=t(28453);const o={title:"Real-World Example",description:"A complete PowerShellBuild project with custom tasks, CI/CD integration, and PSGallery publishing."},i="Real-World Example",a={},d=[{value:"Project Structure",id:"project-structure",level:2},{value:"<code>requirements.psd1</code>",id:"requirementspsd1",level:2},{value:"<code>build.ps1</code>",id:"buildps1",level:2},{value:"<code>psakeFile.ps1</code>",id:"psakefileps1",level:2},{value:"<code>PSScriptAnalyzerSettings.psd1</code>",id:"psscriptanalyzersettingspsd1",level:2},{value:"GitHub Actions Workflow",id:"github-actions-workflow",level:2},{value:"Local Development Workflow",id:"local-development-workflow",level:2},{value:"See Also",id:"see-also",level:2}];function u(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",ul:"ul",...(0,l.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"real-world-example",children:"Real-World Example"})}),"\n",(0,r.jsx)(n.p,{children:"This page shows a production-ready PowerShell module project that uses PowerShellBuild for its build pipeline. It covers the full lifecycle: local development, automated testing, and publishing to the PowerShell Gallery via GitHub Actions."}),"\n",(0,r.jsx)(n.h2,{id:"project-structure",children:"Project Structure"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"MyModule/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 MyModule.psd1\n\u2502 \u251c\u2500\u2500 MyModule.psm1\n\u2502 \u251c\u2500\u2500 Public/\n\u2502 \u2502 \u251c\u2500\u2500 Get-Widget.ps1\n\u2502 \u2502 \u2514\u2500\u2500 New-Widget.ps1\n\u2502 \u2514\u2500\u2500 Private/\n\u2502 \u2514\u2500\u2500 Invoke-WidgetHelper.ps1\n\u251c\u2500\u2500 tests/\n\u2502 \u251c\u2500\u2500 Get-Widget.Tests.ps1\n\u2502 \u2514\u2500\u2500 New-Widget.Tests.ps1\n\u251c\u2500\u2500 docs/ # PlatyPS markdown (auto-generated by GenerateMarkdown)\n\u251c\u2500\u2500 .github/\n\u2502 \u2514\u2500\u2500 workflows/\n\u2502 \u2514\u2500\u2500 build.yml\n\u251c\u2500\u2500 psakeFile.ps1\n\u251c\u2500\u2500 build.ps1\n\u251c\u2500\u2500 PSScriptAnalyzerSettings.psd1 # Auto-detected by PowerShellBuild\n\u2514\u2500\u2500 requirements.psd1\n"})}),"\n",(0,r.jsx)(n.h2,{id:"requirementspsd1",children:(0,r.jsx)(n.code,{children:"requirements.psd1"})}),"\n",(0,r.jsxs)(n.p,{children:["Declare all PowerShell module dependencies. ",(0,r.jsx)(n.a,{href:"https://github.com/RamblingCookieMonster/PSDepend",children:"PSDepend"})," installs these during bootstrap."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-powershell",metastring:'title="requirements.psd1"',children:"@{\n psake = 'latest'\n PowerShellBuild = 'latest'\n Pester = 'latest'\n PSScriptAnalyzer = 'latest'\n platyPS = '0.14.2'\n}\n"})}),"\n",(0,r.jsx)(n.h2,{id:"buildps1",children:(0,r.jsx)(n.code,{children:"build.ps1"})}),"\n",(0,r.jsx)(n.p,{children:"The bootstrap entry point that handles dependency installation and task dispatch."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-powershell",metastring:'title="build.ps1"',children:"#Requires -Version 5.1\n\n[CmdletBinding()]\nparam(\n [string[]]$Task = 'default',\n\n [switch]$Bootstrap,\n\n [hashtable]$Properties = @{}\n)\n\nSet-StrictMode -Version Latest\n\nif ($Bootstrap) {\n Write-Host 'Bootstrapping build dependencies...' -ForegroundColor Cyan\n\n Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null\n Set-PSRepository -Name PSGallery -InstallationPolicy Trusted\n\n if (-not (Get-Module -Name PSDepend -ListAvailable)) {\n Install-Module -Name PSDepend -Scope CurrentUser -Force\n }\n\n Import-Module PSDepend\n Invoke-PSDepend -Path \"$PSScriptRoot/requirements.psd1\" -Install -Import -Force\n Write-Host 'Bootstrap complete.' -ForegroundColor Green\n}\n\nImport-Module psake -ErrorAction Stop\n\nInvoke-psake `\n -buildFile \"$PSScriptRoot/psakeFile.ps1\" `\n -taskList $Task `\n -properties $Properties `\n -nologo\n\nexit ([int](-not $psake.build_success))\n"})}),"\n",(0,r.jsx)(n.h2,{id:"psakefileps1",children:(0,r.jsx)(n.code,{children:"psakeFile.ps1"})}),"\n",(0,r.jsxs)(n.p,{children:["A realistic build file with customized preferences, custom tasks alongside PowerShellBuild tasks, and a ",(0,r.jsx)(n.code,{children:"Deploy"})," task that wraps the release workflow."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-powershell",metastring:'title="psakeFile.ps1"',children:"#Requires -Version 5.1\n\n# --- Task dependency overrides (must be set before referencing PowerShellBuild tasks) ---\n# Publish only requires a successful build, not the full test suite in this example.\n# Tests are enforced in CI; the Publish task is only invoked from GitHub Actions.\n$PSBPublishDependency = 'Build'\n\n# ---\nproperties {\n # General\n $PSBPreference.General.SrcRootDir = \"$PSScriptRoot/src\"\n $PSBPreference.General.ModuleName = 'MyModule'\n\n # Build\n $PSBPreference.Build.OutDir = \"$PSScriptRoot/build\"\n $PSBPreference.Build.CompileModule = $true\n $PSBPreference.Build.CompileDirectories = @('Enum', 'Classes', 'Private', 'Public')\n\n # Test \u2014 Pester\n $PSBPreference.Test.Enabled = $true\n $PSBPreference.Test.RootDir = \"$PSScriptRoot/tests\"\n $PSBPreference.Test.OutputFile = \"$PSScriptRoot/build/TestResults.xml\"\n $PSBPreference.Test.OutputFormat = 'NUnitXml'\n $PSBPreference.Test.ImportModule = $true\n\n # Test \u2014 PSScriptAnalyzer\n # SettingsPath defaults to ./PSScriptAnalyzerSettings.psd1 in the project root\n $PSBPreference.Test.ScriptAnalysis.Enabled = $true\n $PSBPreference.Test.ScriptAnalysis.FailBuildOnSeverityLevel = 'Error'\n\n # Test \u2014 Code Coverage\n $PSBPreference.Test.CodeCoverage.Enabled = $true\n $PSBPreference.Test.CodeCoverage.Threshold = 0.80\n $PSBPreference.Test.CodeCoverage.OutputFile = \"$PSScriptRoot/build/coverage.xml\"\n $PSBPreference.Test.CodeCoverage.OutputFileFormat = 'JaCoCo'\n\n # Help\n $PSBPreference.Help.DefaultLocale = 'en-US'\n $PSBPreference.Docs.RootDir = \"$PSScriptRoot/docs\"\n\n # Publish \u2014 API key supplied by CI; falls back to env var for local releases\n $PSBPreference.Publish.PSRepository = 'PSGallery'\n $PSBPreference.Publish.PSRepositoryApiKey = $env:PSGALLERY_API_KEY\n}\n\n# ---\n# Entry points\n# ---\n\ntask default -depends Test\n\n# Import PowerShellBuild tasks\ntask Init -FromModule PowerShellBuild -Version '0.7.1'\ntask Clean -FromModule PowerShellBuild -Version '0.7.1'\ntask StageFiles -FromModule PowerShellBuild -Version '0.7.1'\ntask Build -FromModule PowerShellBuild -Version '0.7.1'\ntask Analyze -FromModule PowerShellBuild -Version '0.7.1'\ntask Pester -FromModule PowerShellBuild -Version '0.7.1'\ntask Test -FromModule PowerShellBuild -Version '0.7.1'\ntask Publish -FromModule PowerShellBuild -Version '0.7.1'\n\n# ---\n# Custom tasks\n# ---\n\ntask BumpVersion -depends Init {\n param([string]$BumpType = 'Patch')\n\n $manifestPath = $PSBPreference.General.ModuleManifestPath\n $manifest = Import-PowerShellDataFile $manifestPath\n $current = [System.Version]$manifest.ModuleVersion\n\n $next = switch ($BumpType) {\n 'Major' { [System.Version]::new($current.Major + 1, 0, 0) }\n 'Minor' { [System.Version]::new($current.Major, $current.Minor + 1, 0) }\n 'Patch' { [System.Version]::new($current.Major, $current.Minor, $current.Build + 1) }\n }\n\n Update-ModuleManifest -Path $manifestPath -ModuleVersion $next.ToString()\n Write-Host \"Version bumped $current -> $next\" -ForegroundColor Green\n}\n\ntask ValidateReadme -precondition { Test-Path \"$PSScriptRoot/README.md\" } {\n $content = Get-Content \"$PSScriptRoot/README.md\" -Raw\n\n $requiredSections = @('## Installation', '## Usage', '## Contributing')\n foreach ($section in $requiredSections) {\n if ($content -notmatch [regex]::Escape($section)) {\n throw \"README.md is missing section: $section\"\n }\n }\n\n Write-Host 'README.md validation passed.' -ForegroundColor Green\n}\n\n# Deploy = bump version + validate docs + publish\ntask Deploy -depends BumpVersion, ValidateReadme, Publish {\n $manifest = Import-PowerShellDataFile $PSBPreference.General.ModuleManifestPath\n Write-Host \"MyModule v$($manifest.ModuleVersion) deployed to PSGallery.\" -ForegroundColor Green\n}\n"})}),"\n",(0,r.jsx)(n.h2,{id:"psscriptanalyzersettingspsd1",children:(0,r.jsx)(n.code,{children:"PSScriptAnalyzerSettings.psd1"})}),"\n",(0,r.jsxs)(n.p,{children:["PowerShellBuild looks for ",(0,r.jsx)(n.code,{children:"PSScriptAnalyzerSettings.psd1"})," in the project root by default. Place your custom rules here and they will be picked up automatically \u2014 no need to set ",(0,r.jsx)(n.code,{children:"SettingsPath"}),"."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-powershell",metastring:'title="PSScriptAnalyzerSettings.psd1"',children:"@{\n ExcludeRules = @(\n 'PSAvoidUsingWriteHost' # Write-Host is acceptable in build scripts\n )\n Severity = @('Error', 'Warning')\n}\n"})}),"\n",(0,r.jsx)(n.h2,{id:"github-actions-workflow",children:"GitHub Actions Workflow"}),"\n",(0,r.jsx)(n.p,{children:"This workflow runs the full test suite on every push and pull request, and publishes to PSGallery when a tag is pushed."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-yaml",metastring:'title=".github/workflows/build.yml"',children:"name: Build\n\non:\n push:\n branches: [main]\n tags: ['v*']\n pull_request:\n branches: [main]\n\njobs:\n test:\n name: Test (PS ${{ matrix.ps-version }} on ${{ matrix.os }})\n runs-on: ${{ matrix.os }}\n strategy:\n matrix:\n os: [ubuntu-latest, windows-latest]\n ps-version: ['7.4']\n\n steps:\n - uses: actions/checkout@v4\n\n - name: Bootstrap depen
1dencies\n shell: pwsh\n run: .\\build.ps1 -Bootstrap\n\n - name: Run tests\n shell: pwsh\n run: .\\build.ps1 -Task Test\n\n - name: Upload test results\n if: always()\n uses: actions/upload-artifact@v4\n with:\n name: test-results-${{ matrix.os }}\n path: build/TestResults.xml\n\n - name: Upload coverage report\n if: always()\n uses: actions/upload-artifact@v4\n with:\n name: coverage-${{ matrix.os }}\n path: build/coverage.xml\n\n publish:\n name: Publish to PSGallery\n needs: test\n runs-on: ubuntu-latest\n if: startsWith(github.ref, 'refs/tags/v')\n\n steps:\n - uses: actions/checkout@v4\n\n - name: Bootstrap dependencies\n shell: pwsh\n run: .\\build.ps1 -Bootstrap\n\n - name: Publish module\n shell: pwsh\n env:\n PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }}\n run: .\\build.ps1 -Task Publish\n"})}),"\n",(0,r.jsx)(n.h2,{id:"local-development-workflow",children:"Local Development Workflow"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-powershell",children:"# First-time setup\n.\\build.ps1 -Bootstrap\n\n# Day-to-day: run tests\n.\\build.ps1\n\n# Check code quality only\n.\\build.ps1 -Task Analyze\n\n# Rebuild from scratch\n.\\build.ps1 -Task Clean, Build\n\n# Bump the patch version and publish a release\n.\\build.ps1 -Task Deploy\n"})}),"\n",(0,r.jsx)(n.h2,{id:"see-also",children:"See Also"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"./getting-started",children:"Getting Started"})," \u2014 Minimal project setup"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"./task-reference",children:"Task Reference"})," \u2014 All available tasks"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"./configuration",children:"Configuration"})," \u2014 Full ",(0,r.jsx)(n.code,{children:"$PSBPreference"})," reference"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/docs/ci-examples/github-actions",children:"GitHub Actions Integration"})," \u2014 General psake CI/CD patterns"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/powershell/gallery/how-to/publishing-packages/publishing-a-package",children:"Publishing PowerShell Modules"})," \u2014 PSGallery publishing guide"]}),"\n"]})]})}function c(e={}){const{wrapper:n}={...(0,l.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(u,{...e})}):u(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.