PageSourceSearch

https://psake.netlify.app/assets/js/8e60563f.f6570787.js

js psake.netlify.app collected 2026-10-03 11:20:29 UTC 14,335 bytes, 1 lines download raw bytes

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.