Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: CI

on:
push:
pull_request:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions: {}

jobs:
Test-Module:
name: Test module (Pester)
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- name: Run Pester
shell: pwsh
run: |
$config = New-PesterConfiguration
$config.Run.Path = 'tests'
$config.Run.Exit = $true
$config.Output.Verbosity = 'Detailed'
Invoke-Pester -Configuration $config

Action-Test:
name: Action-Test (modes at branch)
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- name: Settings
id: settings
uses: ./
with:
Mode: Settings
Name: PSMATest

- name: Version
id: version
uses: ./
with:
Mode: Version
Version: 1.2.3

- name: Build
id: build
uses: ./
with:
Mode: Build
Name: PSMATest
Version: 1.2.3

- name: Verify outputs
shell: pwsh
env:
SETTINGS: ${{ steps.settings.outputs.Settings }}
VERSION: ${{ steps.version.outputs.Version }}
MODULEPATH: ${{ steps.build.outputs.ModulePath }}
run: |
Write-Host "Settings = $env:SETTINGS"
Write-Host "Version = $env:VERSION"
Write-Host "ModulePath = $env:MODULEPATH"
if ($env:VERSION -ne '1.2.3') { throw "Version output = '$env:VERSION', expected '1.2.3'." }
$manifest = Join-Path $env:MODULEPATH 'PSMATest.psd1'
if (-not (Test-Path $manifest)) { throw "Manifest not found at $manifest." }
$actual = (Import-PowerShellDataFile $manifest).ModuleVersion
if ($actual -ne '1.2.3') { throw "Manifest ModuleVersion = '$actual', expected '1.2.3'." }
Write-Host 'Action-Test passed.'

Lint:
name: Lint (PSScriptAnalyzer)
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- name: PSScriptAnalyzer
shell: pwsh
run: |
$findings = Invoke-ScriptAnalyzer -Path . -Recurse -Severity Error
$findings | Format-Table -AutoSize | Out-String | Write-Host
if ($findings) { throw "$($findings.Count) error-level finding(s)." }
64 changes: 64 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: PSMA
description: PSModule Actions — one action and one library, dispatched by Mode.
author: PSModule

inputs:
Mode:
description: |
The task to run. One of: Settings, Version, Build, Test, CodeCoverage,
TestResults, Document, Publish.
required: true
Name:
description: Module name. Defaults to the repository name.
required: false
default: ''
Version:
description: Module version to stamp (Build) or publish (Publish).
required: false
default: ''
Prerelease:
description: Prerelease label (Build / Publish).
required: false
default: ''
SettingsPath:
description: Path to the settings file (Settings).
required: false
default: .github/PSModule.yml
APIKey:
description: PowerShell Gallery API key (Publish).
required: false
default: ''
WorkingDirectory:
description: The working directory the script runs from.
required: false
default: '.'

outputs:
Settings:
description: The resolved settings as JSON (Settings).
value: ${{ steps.psma.outputs.Settings }}
Version:
description: The resolved version (Version).
value: ${{ steps.psma.outputs.Version }}
ModulePath:
description: Path to the built module (Build).
value: ${{ steps.psma.outputs.ModulePath }}
Passed:
description: Whether the tests passed (Test).
value: ${{ steps.psma.outputs.Passed }}

runs:
using: composite
steps:
- name: PSMA
id: psma
shell: pwsh
working-directory: ${{ inputs.WorkingDirectory }}
env:
PSMODULE_PSMA_INPUT_Mode: ${{ inputs.Mode }}
PSMODULE_PSMA_INPUT_Name: ${{ inputs.Name }}
PSMODULE_PSMA_INPUT_Version: ${{ inputs.Version }}
PSMODULE_PSMA_INPUT_Prerelease: ${{ inputs.Prerelease }}
PSMODULE_PSMA_INPUT_SettingsPath: ${{ inputs.SettingsPath }}
PSMODULE_PSMA_INPUT_APIKey: ${{ inputs.APIKey }}
run: ${{ github.action_path }}/src/main.ps1
24 changes: 24 additions & 0 deletions src/PSMA/PSMA.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@{
RootModule = 'PSMA.psm1'
ModuleVersion = '0.1.0'
GUID = 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'
Author = 'PSModule'
CompanyName = 'PSModule'
Copyright = '(c) 2026 PSModule. All rights reserved.'
Description = 'Internal library backing the PSMA action - all framework action logic in one module.'
PowerShellVersion = '7.4'
FunctionsToExport = @(
'Initialize-PSMA'
'Get-PSMASettings'
'Resolve-PSMAVersion'
'Build-PSMAModule'
'Test-PSMAModule'
'Get-PSMACodeCoverage'
'Get-PSMATestResult'
'Build-PSMADocumentation'
'Publish-PSMAModule'
)
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
}
17 changes: 17 additions & 0 deletions src/PSMA/PSMA.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<#
.SYNOPSIS
Root module for the PSMA library.

.DESCRIPTION
Dot-sources every private (shared) and public (per-mode) function, then exports
the public functions. Private helpers stay internal but are reused by every mode.
#>

$private = @(Get-ChildItem -Path (Join-Path $PSScriptRoot 'functions' 'private') -Filter '*.ps1' -ErrorAction SilentlyContinue)
$public = @(Get-ChildItem -Path (Join-Path $PSScriptRoot 'functions' 'public') -Filter '*.ps1' -ErrorAction SilentlyContinue)

foreach ($file in @($private + $public)) {
. $file.FullName
}

Export-ModuleMember -Function $public.BaseName
23 changes: 23 additions & 0 deletions src/PSMA/functions/private/Get-PSMAInput.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
function Get-PSMAInput {
<#
.SYNOPSIS
Reads an action input from the environment with an optional default.

.DESCRIPTION
Inputs are passed by action.yml as PSMODULE_PSMA_INPUT_<Name> environment
variables. Shared by every mode so input handling is defined once.
#>
[CmdletBinding()]
[OutputType([string])]
param(
[Parameter(Mandatory)]
[string] $Name,

[Parameter()]
[string] $Default = ''
)

$value = [Environment]::GetEnvironmentVariable("PSMODULE_PSMA_INPUT_$Name")
if ([string]::IsNullOrEmpty($value)) { return $Default }
$value
}
17 changes: 17 additions & 0 deletions src/PSMA/functions/private/Get-PSMAModuleName.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function Get-PSMAModuleName {
<#
.SYNOPSIS
Resolves the module name from the Name input, falling back to the repository name.

.DESCRIPTION
Shared context helper reused by the modes that need the module name.
#>
[CmdletBinding()]
[OutputType([string])]
param()

$name = Get-PSMAInput -Name 'Name'
if (-not [string]::IsNullOrEmpty($name)) { return $name }
if ($env:GITHUB_REPOSITORY) { return ($env:GITHUB_REPOSITORY -split '/')[-1] }
'UnknownModule'
}
23 changes: 23 additions & 0 deletions src/PSMA/functions/private/Set-PSMAOutput.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
function Set-PSMAOutput {
<#
.SYNOPSIS
Writes a GitHub Actions step output.

.DESCRIPTION
Shared output writer used by every mode. No-ops safely when not running inside
a GitHub Actions runner (e.g. during local tests).
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string] $Name,

[Parameter()]
[string] $Value = ''
)

if ($env:GITHUB_OUTPUT) {
"$Name=$Value" | Add-Content -Path $env:GITHUB_OUTPUT
}
Write-Verbose "output: $Name=$Value"
}
30 changes: 30 additions & 0 deletions src/PSMA/functions/private/Write-PSMALog.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
function Write-PSMALog {
<#
.SYNOPSIS
Emits a collapsible log group around a block of work.

.DESCRIPTION
Shared grouped-logging helper reused by every mode so diagnostics read the same
way across all action calls. Falls back to a plain line when no block is given.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory, Position = 0)]
[string] $Title,

[Parameter(Position = 1)]
[scriptblock] $ScriptBlock
)

if (-not $ScriptBlock) {
Write-Host $Title
return
}

Write-Host "::group::$Title"
try {
& $ScriptBlock
} finally {
Write-Host '::endgroup::'
}
}
16 changes: 16 additions & 0 deletions src/PSMA/functions/public/Build-PSMADocumentation.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
function Build-PSMADocumentation {
<#
.SYNOPSIS
Builds the module documentation.

.DESCRIPTION
PORT FROM: PSModule/Document-PSModule - generate the PSModule-flavored docs. The
scaffold logs a placeholder.
#>
[CmdletBinding()]
param()

Write-PSMALog 'Build-PSMADocumentation' {
Write-Host 'Documentation step (scaffold).'
}
}
26 changes: 26 additions & 0 deletions src/PSMA/functions/public/Build-PSMAModule.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
function Build-PSMAModule {
<#
.SYNOPSIS
Builds the module and its manifest.

.DESCRIPTION
PORT FROM: PSModule/Build-PSModule - assemble the module and stamp the manifest.
The scaffold writes a stub manifest so the build/test/publish pipeline can be
exercised end to end.
#>
[CmdletBinding()]
param()

Write-PSMALog 'Build-PSMAModule' {
$name = Get-PSMAModuleName
$version = Get-PSMAInput -Name 'Version' -Default '0.0.0'
$outputRoot = Join-Path (Get-Location) 'outputs' 'module' $name

New-Item -ItemType Directory -Path $outputRoot -Force | Out-Null
$manifest = Join-Path $outputRoot "$name.psd1"
"@{ ModuleVersion = '$version' }" | Set-Content -Path $manifest

Set-PSMAOutput -Name 'ModulePath' -Value $outputRoot
Write-Host "Built stub module '$name' $version at: $outputRoot"
}
}
16 changes: 16 additions & 0 deletions src/PSMA/functions/public/Get-PSMACodeCoverage.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
function Get-PSMACodeCoverage {
<#
.SYNOPSIS
Gathers code coverage from a Pester run.

.DESCRIPTION
PORT FROM: PSModule/Get-PesterCodeCoverage - aggregate coverage and compare it to
the target. The scaffold logs a placeholder.
#>
[CmdletBinding()]
param()

Write-PSMALog 'Get-PSMACodeCoverage' {
Write-Host 'Code coverage step (scaffold).'
}
}
25 changes: 25 additions & 0 deletions src/PSMA/functions/public/Get-PSMASettings.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
function Get-PSMASettings {
<#
.SYNOPSIS
Resolves the framework settings and run conditions.

.DESCRIPTION
PORT FROM: PSModule/Get-PSModuleSettings - read the settings file, merge defaults,
and compute the Run.* conditions. The scaffold emits a minimal settings object.
#>
[CmdletBinding()]
param()

Write-PSMALog 'Get-PSMASettings' {
$path = Get-PSMAInput -Name 'SettingsPath' -Default '.github/PSModule.yml'
Write-Host "Settings path: $path"

$settings = [ordered]@{
Name = Get-PSMAModuleName
Run = [ordered]@{}
}
$json = $settings | ConvertTo-Json -Compress -Depth 10
Set-PSMAOutput -Name 'Settings' -Value $json
Write-Host "Settings: $json"
}
}
Loading