diff --git a/.github/PSModule.yml b/.github/PSModule.yml index 0e0770314..9a91761c3 100644 --- a/.github/PSModule.yml +++ b/.github/PSModule.yml @@ -1,3 +1,7 @@ +# This file is used to configure the Process-PSModule workflow. +# Reference: +# - https://github.com/PSModule/Process-PSModule?tab=readme-ov-file#configuration + Test: CodeCoverage: PercentTarget: 50 diff --git a/.github/chatmodes/subfolder/cake.chatmode.md b/.github/chatmodes/subfolder/cake.chatmode.md new file mode 100644 index 000000000..f53782bf2 --- /dev/null +++ b/.github/chatmodes/subfolder/cake.chatmode.md @@ -0,0 +1,5 @@ +--- +description: "Test chat mode" +--- + +This is a test diff --git a/.github/chatmodes/test.chatmode.md b/.github/chatmodes/test.chatmode.md new file mode 100644 index 000000000..f53782bf2 --- /dev/null +++ b/.github/chatmodes/test.chatmode.md @@ -0,0 +1,5 @@ +--- +description: "Test chat mode" +--- + +This is a test diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f57e1e959..53188fef9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,5 +7,8 @@ version: 2 updates: - package-ecosystem: github-actions # See documentation for possible values directory: / # Location of package manifests + labels: + - dependencies + - github-actions schedule: interval: weekly diff --git a/.github/instructions/PSModule.instructions.md b/.github/instructions/PSModule.instructions.md new file mode 100644 index 000000000..376d442b2 --- /dev/null +++ b/.github/instructions/PSModule.instructions.md @@ -0,0 +1,320 @@ +# GitHub Copilot Instructions for Process-PSModule + +This repository provides a comprehensive GitHub Actions workflow system for PowerShell module development, testing, building, and publishing. When contributing to this project or using it as a reference, please follow these guidelines and patterns. + +## Repository Overview + +**Process-PSModule** is a reusable workflow that: +- Builds, tests, and publishes PowerShell modules using the PSModule framework +- Provides multi-OS testing (Linux, macOS, Windows) +- Generates documentation and publishes to GitHub Pages +- Publishes modules to PowerShell Gallery +- Follows Test-Driven Development and Continuous Delivery practices + +## PowerShell Development Standards + +### Code Structure +- **Public Functions**: Place in `src/functions/public/` +- **Private Functions**: Place in `src/functions/private/` +- **Classes**: Place in `src/classes/public/` or `src/classes/private/` +- **Data Files**: Place in `src/data/` (`.psd1` files) +- **Module Manifest**: `src/manifest.psd1` + +### Function Standards +```powershell +function Verb-Noun { + <# + .SYNOPSIS + Brief description of what the function does. + + .DESCRIPTION + Detailed description of the function's purpose and behavior. + + .PARAMETER ParameterName + Description of the parameter. + + .EXAMPLE + Verb-Noun -ParameterName 'Value' + + Description of what this example does. + + .NOTES + Additional notes about the function. + + .LINK + https://github.com/PSModule/Process-PSModule + #> + [CmdletBinding()] + param ( + # Parameter description + [Parameter(Mandatory)] + [string] $ParameterName + ) + + begin { + # Initialization code + } + + process { + # Main function logic + } + + end { + # Cleanup code + } +} +``` + +### Code Style Requirements + +Based on PSScriptAnalyzer configuration: + +#### Indentation and Spacing +- Use **4 spaces** for indentation (no tabs) +- Maximum line length: **150 characters** +- Place opening braces on the same line +- Place closing braces on new line +- Use consistent whitespace around operators and separators + +#### Bracing Style +```powershell +# Correct +if ($condition) { + Write-Output "Good" +} + +# Incorrect +if ($condition) +{ + Write-Output "Bad" +} +``` + +#### Variable and Parameter Naming +- Use PascalCase for parameters: `$ModuleName` +- Use camelCase for local variables: `$tempPath` +- Use meaningful, descriptive names + +#### Error Handling +```powershell +try { + # Main logic +} +catch { + Write-Error "Detailed error message: $($_.Exception.Message)" + throw +} +``` + +## Testing Patterns + +### Pester Test Structure +```powershell +Describe 'Function-Name' { + Context 'When condition is met' { + It 'Should perform expected action' { + # Arrange + $input = 'test value' + + # Act + $result = Function-Name -Parameter $input + + # Assert + $result | Should -Be 'expected value' + } + } + + Context 'When validation fails' { + It 'Should throw meaningful error' { + { Function-Name -Parameter $null } | Should -Throw + } + } +} +``` + +### Test File Organization +- Unit tests: `tests/Unit/` +- Integration tests: `tests/Integration/` +- Test files should mirror source structure +- Name test files: `[FunctionName].Tests.ps1` + +## Documentation Standards + +### Comment-Based Help +Every public function must include: +- `.SYNOPSIS` - Brief one-line description +- `.DESCRIPTION` - Detailed explanation +- `.PARAMETER` - For each parameter +- `.EXAMPLE` - At least one working example +- `.NOTES` - Additional context if needed +- `.LINK` - Reference links + +### Markdown Documentation +- Function documentation: `src/functions/public/[Category]/[Category].md` +- Use clear headings and code examples +- Include real-world usage scenarios + +## Configuration Patterns + +### PSModule.yml Structure +```yaml +Name: ModuleName + +Test: + Skip: false + SourceCode: + Skip: false + PSModule: + Skip: false + Module: + Skip: false + CodeCoverage: + PercentTarget: 80 + +Build: + Skip: false + Module: + Skip: false + Docs: + Skip: false + +Publish: + Module: + Skip: false + AutoCleanup: true + AutoPatching: true +``` + +## Workflow Understanding + +### Pipeline Stages +1. **Get-Settings** - Read configuration +2. **Build-Module** - Compile source into module +3. **Test-SourceCode** - Test source files +4. **Lint-SourceCode** - PSScriptAnalyzer validation +5. **Test-Module** - PSModule framework tests +6. **Test-ModuleLocal** - Pester tests from repo +7. **Get-TestResults** - Aggregate test results +8. **Get-CodeCoverage** - Calculate coverage +9. **Build-Docs** - Generate documentation +10. **Build-Site** - Create static site +11. **Publish** - Release to PowerShell Gallery and GitHub + +### Conditional Execution +- Tests run on multiple OS platforms (matrix strategy) +- Steps can be skipped via configuration +- Publishing only occurs on merged PRs + +## GitHub Actions Patterns + +### Workflow Inputs +```yaml +inputs: + Name: + type: string + description: Module name + required: false + SettingsPath: + type: string + description: Path to settings file + default: .github/PSModule.yml + Debug: + type: boolean + description: Enable debug output + default: false +``` + +### Secrets Required +- `APIKEY` - PowerShell Gallery API key for publishing + +## Common Patterns + +### Module Requirements +Use `#Requires` statements at the top of files that need specific modules: +```powershell +#Requires -Modules @{ModuleName='RequiredModule'; ModuleVersion='1.0.0'} +``` + +### Alias Definitions +```powershell +# Multiple alias methods supported +[Alias('Short-Name1')] +[Alias('Short-Name2')] +function Long-FunctionName { + # Function body +} + +# Alternative alias definitions at end of file +New-Alias Short-Name3 Long-FunctionName +New-Alias -Name Short-Name4 -Value Long-FunctionName +Set-Alias Short-Name5 Long-FunctionName +``` + +### PSScriptAnalyzer Suppressions +When suppression is necessary, use the standard format: +```powershell +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'RuleName', 'Target', Scope = 'Function', + Justification = 'Clear reason for suppression' +)] +function Function-Name { + # Function body +} +``` + +### Validation +```powershell +[Parameter(Mandatory)] +[ValidateNotNullOrEmpty()] +[ValidatePattern('^[a-zA-Z0-9-]+$')] +[string] $ModuleName +``` + +## File Patterns to Follow + +### Directory Structure +``` +.github/ + workflows/ # GitHub Actions workflows + linters/ # Linter configurations + PSModule.yml # Project configuration +src/ + functions/ + public/ # Exported functions + private/ # Internal functions + classes/ + public/ # Exported classes + private/ # Internal classes + data/ # Configuration files + assemblies/ # Binary dependencies + modules/ # Nested modules +tests/ + Unit/ # Unit tests + Integration/ # Integration tests +docs/ # Additional documentation +``` + +## Best Practices + +1. **Follow PowerShell naming conventions** - Use approved verbs (`Get-Verb`) +2. **Write comprehensive tests first** (TDD approach) +3. **Include proper error handling** with meaningful messages +4. **Document all public interfaces** with comment-based help +5. **Use semantic versioning** for releases +6. **Validate inputs thoroughly** using parameter validation +7. **Write clean, readable code** that follows the style guide +8. **Test on multiple platforms** when making changes + +## Troubleshooting + +### Common Issues +- **PSScriptAnalyzer violations** - Check against rules in `.powershell-psscriptanalyzer.psd1` +- **Test failures** - Ensure tests follow Pester v5 syntax +- **Build failures** - Verify module manifest and dependencies +- **Documentation errors** - Check mkdocs.yml configuration + +When making changes, always: +1. Run PSScriptAnalyzer locally +2. Execute relevant tests +3. Update documentation +4. Follow the established patterns in existing code diff --git a/.github/instructions/Repository/GitAttributes/main.instructions.md b/.github/instructions/Repository/GitAttributes/main.instructions.md new file mode 100644 index 000000000..02240fe26 --- /dev/null +++ b/.github/instructions/Repository/GitAttributes/main.instructions.md @@ -0,0 +1,18 @@ +--- +description: "How to write .gitattributes in this specific project" +applyTo: "**/.gitattributes" +--- + +## Conventions +- Ensure LF normalization for cross-platform contributors. +- Mark PowerShell as text for consistent diffs. + +## Current Baseline (append to file if missing) +``` +* text=auto eol=lf +*.ps1 text eol=lf +*.ps1xml text eol=lf +``` + +## Anti-Patterns +- ❌ Adding binary flag to text formats. diff --git a/.github/instructions/Repository/GitIgnore/main.instructions.md b/.github/instructions/Repository/GitIgnore/main.instructions.md new file mode 100644 index 000000000..d1481df85 --- /dev/null +++ b/.github/instructions/Repository/GitIgnore/main.instructions.md @@ -0,0 +1,21 @@ +--- +description: "How to write .gitignore in this specific project" +applyTo: "**/.gitignore" +--- + +## Conventions +- Only ignore artifacts not meant for source control (build outputs, local env files). +- Group related patterns with blank line separation + comment header. + +## Example Block +``` +# PowerShell +*.ps1xml + +# Local tooling +*.local.ps1 +``` + +## Anti-Patterns +- ❌ Ignoring scripts under `tools/`. +- ❌ Using wildcards that capture future source paths unintentionally. diff --git a/.github/instructions/Repository/JSON/main.instructions.md b/.github/instructions/Repository/JSON/main.instructions.md new file mode 100644 index 000000000..a369cce22 --- /dev/null +++ b/.github/instructions/Repository/JSON/main.instructions.md @@ -0,0 +1,23 @@ +--- +description: "How to write JSON code in this specific project" +applyTo: "**/*.json" +--- + +## Scope +Currently only analyzer / duplication configs. + +## Conventions +- Keep config minimal; remove unused default keys. +- Document non-obvious thresholds in adjacent README section. + +## Example +```json +{ + "threshold": 5, + "reporters": ["console", "json"] +} +``` + +## Anti-Patterns +- ❌ Adding comments (breaks strict JSON) → Document separately. +- ❌ Large unrelated reorders in same PR. diff --git a/.github/instructions/Repository/Markdown/main.instructions.md b/.github/instructions/Repository/Markdown/main.instructions.md new file mode 100644 index 000000000..c66b5ad6b --- /dev/null +++ b/.github/instructions/Repository/Markdown/main.instructions.md @@ -0,0 +1,30 @@ +--- +description: "How to write Markdown code in this specific project" +applyTo: "**/*.md" +--- + +## Project Conventions +- Primary audience: users scripting with module + contributors. +- Each example must be executable as-is (assume module imported) or include import line. + +## Sections +- README intro sequence: Badge Block → Short Value Prop → Quick Start → Features → Examples → Development. + +## Code Blocks +- Prefer `powershell` language tag. +- Multi-step examples: comment headings inside block rather than multiple blocks. + +## Example Pattern +```markdown +```powershell +# Get a repository +Get-GitHubRepository -Owner Contoso -Name Portal | Format-List * +``` +``` + +## Anti-Patterns +- ❌ Screenshots for output that can be text. +- ❌ Example without parameter explanation. + +## Overrides +- Organization rule on 120 char wrap may be exceeded for one-line commands showing multiple parameters. diff --git a/.github/instructions/Repository/PowerShell/main.instructions.md b/.github/instructions/Repository/PowerShell/main.instructions.md new file mode 100644 index 000000000..fc9fb7ad6 --- /dev/null +++ b/.github/instructions/Repository/PowerShell/main.instructions.md @@ -0,0 +1,993 @@ +--- +description: "How to write PowerShell code in this specific project" +applyTo: "**/*.ps1, **/*.psm1, **/*.psd1" +--- + +# PowerShell Code Patterns for GitHub Module + +This document provides concrete examples of how to write PowerShell code in the GitHub PowerShell module. These examples are extracted from the actual codebase and demonstrate the project-specific implementation patterns. + + +## Context Resolution Pattern + +### Implementation in Resolve-GitHubContext + +The context resolution pattern is central to the module's authentication system. Here's how it's implemented: + +```powershell +filter Resolve-GitHubContext { + [OutputType([GitHubContext])] + [CmdletBinding()] + param( + [Parameter(Mandatory, ValueFromPipeline)] + [AllowNull()] + [object] $Context, + + [Parameter()] + [bool] $Anonymous + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + Initialize-GitHubConfig + } + + process { + Write-Verbose "Context:" + $Context | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + Write-Verbose "Anonymous: [$Anonymous]" + + if ($Anonymous -or $Context -eq 'Anonymous') { + Write-Verbose 'Returning Anonymous context.' + return [GitHubContext]::new( + [pscustomobject]@{ + Name = 'Anonymous' + AuthType = 'Anonymous' + } + ) + } + + if ($Context -is [string]) { + $contextName = $Context + Write-Verbose "Getting context: [$contextName]" + $Context = Get-GitHubContext -Context $contextName + } + + if ($null -eq $Context) { + Write-Verbose 'Context is null, returning default context.' + $Context = Get-GitHubContext + } + + switch ($Context.TokenType) { + 'ghu' { + Write-Verbose 'Update GitHub User Access Token.' + $Context = Update-GitHubUserAccessToken -Context $Context -PassThru + } + 'JWT' { + Write-Verbose 'Update GitHub App JWT Token.' + $Context = Update-GitHubAppJWT -Context $Context -PassThru + } + } + + $Context + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +**Key Pattern Elements:** + +- **`filter` keyword**: Used instead of `function` for streaming pipeline behavior +- **`[AllowNull()]` attribute**: Permits null values for context parameter (important for defaulting) +- **String to object resolution**: Handles both string context names and GitHubContext objects +- **Token refresh logic**: Automatically updates expiring tokens based on TokenType +- **Null handling**: Returns default context when no context provided + +**Usage in Public Functions:** + +```powershell +begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + + # Resolve context - converts string names or null to GitHubContext objects + $Context = Resolve-GitHubContext -Context $Context + + # Validate authentication type for this operation + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT +} +``` + +--- + +## API Call Pattern + +### Implementation in Invoke-GitHubAPI + +The `Invoke-GitHubAPI` function is the foundation for all REST API calls. Here's the core pattern: + +```powershell +function Invoke-GitHubAPI { + [CmdletBinding(DefaultParameterSetName = 'ApiEndpoint')] + param( + [Parameter()] + [ValidateSet('GET', 'POST', 'PUT', 'DELETE', 'PATCH')] + $Method = 'GET', + + [Parameter( + Mandatory, + ParameterSetName = 'ApiEndpoint' + )] + [string] $ApiEndpoint, + + [Parameter()] + [Alias('Query')] + [object] $Body, + + [Parameter()] + [string] $Accept = 'application/vnd.github+json; charset=utf-8', + + [Parameter()] + [object] $Context + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $debug = $DebugPreference -eq 'Continue' + + # Resolve context with anonymous support + $Context = Resolve-GitHubContext -Context $Context -Anonymous $Anonymous + + if ($debug) { + Write-Debug 'Invoking GitHub API...' + Write-Debug 'Parent function parameters:' + Get-FunctionParameter -Scope 1 | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug 'Parameters:' + Get-FunctionParameter | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } + } + + process { + # Resolve all settings from context + $Token = $Context.Token + $HttpVersion = Resolve-GitHubContextSetting -Name 'HttpVersion' -Value $HttpVersion -Context $Context + $ApiBaseUri = Resolve-GitHubContextSetting -Name 'ApiBaseUri' -Value $ApiBaseUri -Context $Context + $ApiVersion = Resolve-GitHubContextSetting -Name 'ApiVersion' -Value $ApiVersion -Context $Context + $RetryCount = Resolve-GitHubContextSetting -Name 'RetryCount' -Value $RetryCount -Context $Context + $RetryInterval = Resolve-GitHubContextSetting -Name 'RetryInterval' -Value $RetryInterval -Context $Context + + # Build headers + $headers = @{ + Accept = $Accept + 'X-GitHub-Api-Version' = $ApiVersion + 'User-Agent' = $script:UserAgent + } + $headers | Remove-HashtableEntry -NullOrEmptyValues + + # Build URI + if (-not $Uri) { + $Uri = New-Uri -BaseUri $ApiBaseUri -Path $ApiEndpoint -AsString + $Uri = $Uri -replace '//$', '/' + } + + # Build API call splat + $APICall = @{ + Uri = $Uri + Method = [string]$Method + Headers = $Headers + ContentType = $ContentType + InFile = $UploadFilePath + HttpVersion = [string]$HttpVersion + MaximumRetryCount = $RetryCount + RetryIntervalSec = $RetryInterval + } + $APICall | Remove-HashtableEntry -NullOrEmptyValues + + # Add authentication for non-anonymous requests + if (-not $Anonymous -and $Context.Name -ne 'Anonymous') { + $APICall['Authentication'] = 'Bearer' + $APICall['Token'] = $Token + } + + # Handle GET parameters as query string + if ($Method -eq 'GET') { + if (-not $Body) { + $Body = @{} + } + $Body['per_page'] = Resolve-GitHubContextSetting -Name 'PerPage' -Value $PerPage -Context $Context + $APICall.Uri = New-Uri -BaseUri $Uri -Query $Body -AsString + } + # Handle POST with file upload + elseif (($Method -eq 'POST') -and -not [string]::IsNullOrEmpty($UploadFilePath)) { + $APICall.Uri = New-Uri -BaseUri $Uri -Query $Body -AsString + } + # Handle body for other methods + elseif ($Body) { + if ($Body -is [hashtable]) { + $APICall.Body = $Body | ConvertTo-Json -Depth 100 + } else { + $APICall.Body = $Body + } + } + + try { + do { + $response = Invoke-WebRequest @APICall -ProgressAction 'SilentlyContinue' -Debug:$false -Verbose:$false + + # Process response headers + $headers = @{} + foreach ($item in $response.Headers.GetEnumerator()) { + $headers[$item.Key] = ($item.Value).Trim() -join ', ' + } + + # Parse content based on Content-Type + switch -Regex ($headers.'Content-Type') { + 'application/.*json' { + $results = $response.Content | ConvertFrom-Json + } + 'application/octocat-stream' { + [byte[]]$byteArray = $response.Content + $results = [System.Text.Encoding]::UTF8.GetString($byteArray) + } + default { + $results = $response.Content + } + } + + # Return structured response + [pscustomobject]@{ + Request = $APICall + Response = $results + Headers = $headers + StatusCode = $response.StatusCode + StatusDescription = $response.StatusDescription + } + + # Follow pagination + $APICall['Uri'] = $response.RelationLink.next + } while ($APICall['Uri']) + } catch { + # Detailed error handling (see Error Handling section) + $errordetails = $_.ErrorDetails | ConvertFrom-Json -AsHashtable + $errorResult = [pscustomobject]@{ + Message = $errordetails.message + Resource = $errordetails.errors.resource + Code = $errordetails.errors.code + Details = $errordetails.errors.message + Information = $errordetails.documentation_url + Status = $_.Exception.Message + StatusCode = $errordetails.status + ErrorTime = Get-Date -Format 's' + } + + # Build comprehensive exception message + $exception = @" +---------------------------------- +Context: +$($Context | Format-List | Out-String) +---------------------------------- +Request: +$([pscustomobject]$APICall | Select-Object -ExcludeProperty Body, Headers | Format-List | Out-String) +---------------------------------- +Error: +$($errorResult | Format-List | Out-String) +---------------------------------- +"@ + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.Exception]::new($exception), + 'GitHubAPIError', + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $errorResult + ) + ) + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +**Key Pattern Elements:** + +- **Setting resolution**: Use `Resolve-GitHubContextSetting` to get values from context with parameter overrides +- **Header construction**: Build headers hashtable, then remove null/empty values +- **URI construction**: Use `New-Uri` helper for consistent URI building +- **Splat pattern**: Build `$APICall` hashtable, clean it, then splat to `Invoke-WebRequest` +- **Method-specific handling**: Different logic for GET (query string), POST with upload (query string), and other methods (JSON body) +- **Pagination**: Automatic do-while loop following `RelationLink.next` +- **Response processing**: Content-Type-aware deserialization +- **Structured output**: Always return `[pscustomobject]` with Request, Response, Headers, StatusCode, StatusDescription + +--- + +## Public Function Structure + +### Complete Example from Get-GitHubRepository + +Here's the full pattern for a public function with multiple parameter sets: + +```powershell +filter Get-GitHubRepository { + <# + .SYNOPSIS + Get a repository or list of repositories + + .DESCRIPTION + Gets a repository or list of repositories based on the provided parameters. + + .EXAMPLE + Get-GitHubRepository -Owner 'octocat' -Name 'Hello-World' + + Gets the repository 'Hello-World' for the organization 'octocat'. + + .OUTPUTS + GitHubRepository + #> + [OutputType([GitHubRepository])] + [CmdletBinding(DefaultParameterSetName = 'List')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Parameters are used in different parameter sets')] + param( + # The account owner of the repository. + [Parameter( + Mandatory, + ParameterSetName = 'ByName' + )] + [Parameter(ParameterSetName = 'List')] + [Alias('Organization', 'Username')] + [string] $Owner, + + # The name of the repository without the .git extension. + [Parameter( + Mandatory, + ParameterSetName = 'ByName', + ValueFromPipelineByPropertyName + )] + [Alias('Repository', 'Repo')] + [string] $Name, + + # The unique identifier of the repository. + [Parameter( + Mandatory, + ParameterSetName = 'ByID', + ValueFromPipelineByPropertyName + )] + [Alias('RepositoryID', 'RepoID')] + [int] $ID, + + # Properties to include in the returned object. + [Parameter()] + [string[]] $Property, + + # Additional properties to include in the returned object. + [Parameter()] + [string[]] $AdditionalProperty, + + # The context to run the command in. + [Parameter()] + [object] $Context + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + + # Resolve and validate context + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT + + # Default Owner from context if not provided + if ([string]::IsNullOrEmpty($Owner)) { + $Owner = $Context.Owner + } + } + + process { + # Route to appropriate private function based on parameter set + $params = @{ + Context = $Context + } + + if ($Property) { + $params['Property'] = $Property + } + + if ($AdditionalProperty) { + $params['AdditionalProperty'] = $AdditionalProperty + } + + switch ($PSCmdlet.ParameterSetName) { + 'ByName' { + Write-Debug "Getting repository by name: [$Owner/$Name]" + $params['Owner'] = $Owner + $params['Name'] = $Name + Get-GitHubRepositoryByName @params + } + 'ByID' { + Write-Debug "Getting repository by ID: [$ID]" + $params['ID'] = $ID + Get-GitHubRepositoryByID @params + } + 'List' { + Write-Debug "Listing repositories for owner: [$Owner]" + $params['Owner'] = $Owner + Get-GitHubRepositoryList @params + } + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +**Key Pattern Elements:** + +- **`filter` keyword**: Enables streaming pipeline behavior +- **`[OutputType()]`**: Always declare output type for IntelliSense and documentation +- **`[CmdletBinding(DefaultParameterSetName = '...')]`**: Specify default parameter set +- **`[SuppressMessageAttribute]`**: Suppress false PSScriptAnalyzer warnings with justification +- **Standard aliases**: `Owner` aliased to `Organization`/`Username`, `Name` aliased to `Repository`/`Repo` +- **Context resolution in `begin` block**: Resolve and assert context once before processing pipeline +- **Parameter defaulting from context**: `if ([string]::IsNullOrEmpty($Owner)) { $Owner = $Context.Owner }` +- **Switch on parameter set**: Route to different private functions based on `$PSCmdlet.ParameterSetName` +- **Parameter splatting**: Build `$params` hashtable conditionally, then splat to private function + +--- + +## Private Function Structure + +### Complete Example from Get-GitHubRepositoryByName + +Here's the full pattern for a private function: + +```powershell +filter Get-GitHubRepositoryByName { + <# + .SYNOPSIS + Get a repository + + .DESCRIPTION + The `parent` and `source` objects are present when the repository is a fork. + `parent` is the repository this repository was forked from, `source` is the ultimate source for the network. + + .EXAMPLE + Get-GitHubRepositoryByName -Owner 'octocat' -Name 'Hello-World' + + Gets the repository 'Hello-World' for the organization 'octocat'. + + .OUTPUTS + GitHubRepository + #> + [OutputType([GitHubRepository])] + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidLongLines', '', Justification = 'Contains a long link.')] + param( + # The account owner of the repository. The name is not case sensitive. + [Parameter(Mandatory)] + [string] $Owner, + + # The name of the repository without the .git extension. The name is not case sensitive. + [Parameter(Mandatory)] + [string] $Name, + + # Properties to include in the returned object. + [Parameter()] + [string[]] $Property = @( + 'ID', + 'NodeID' + 'Name', + 'Owner', + 'FullName', + 'Url', + 'Description', + 'CreatedAt', + 'UpdatedAt', + 'PushedAt', + 'ArchivedAt', + 'Homepage', + 'Size', + 'Language', + 'HasIssues', + 'HasProjects', + 'HasWiki', + 'HasDiscussions', + 'HasSponsorships', + 'IsArchived', + 'IsTemplate', + 'IsFork', + 'License', + 'AllowForking', + 'RequireWebCommitSignoff', + 'Topics', + 'Visibility', + 'OpenIssues', + 'OpenPullRequests', + 'Stargazers', + 'Watchers', + 'Forks', + 'DefaultBranch', + 'Permission', + 'AllowSquashMerge', + 'AllowMergeCommit', + 'AllowRebaseMerge', + 'AllowAutoMerge', + 'DeleteBranchOnMerge', + 'SuggestUpdateBranch', + 'SquashMergeCommitTitle', + 'SquashMergeCommitMessage', + 'MergeCommitTitle', + 'MergeCommitMessage', + 'TemplateRepository', + 'ForkRepository', + 'CustomProperties', + 'CloneUrl', + 'SshUrl', + 'GitUrl' + ), + + # Additional properties to include in the returned object. + [Parameter()] + [string[]] $AdditionalProperty, + + # The context to run the command in. Used to get the details for the API call. + # Can be either a string or a GitHubContext object. + [Parameter(Mandatory)] + [object] $Context + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + + # Assert context authentication type + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT + } + + process { + # Build GraphQL field list from property names + $graphParams = @{ + PropertyList = $Property + $AdditionalProperty + PropertyToGraphQLMap = [GitHubRepository]::PropertyToGraphQLMap + } + $graphQLFields = ConvertTo-GitHubGraphQLField @graphParams + + # Call GraphQL API + $apiParams = @{ + Query = @" +query( + `$Owner: String!, + `$Name: String! +) { + repositoryOwner( + login: `$Owner + ) { + repository( + name: `$Name + ) { +$graphQLFields + } + } +} +"@ + Variables = @{ + Owner = $Owner + Name = $Name + } + Context = $Context + } + + Invoke-GitHubGraphQLQuery @apiParams | ForEach-Object { + [GitHubRepository]::new($_.repositoryOwner.repository) + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +**Key Pattern Elements:** + +- **`[Parameter(Mandatory)]` on Context**: Private functions always require context (no defaulting) +- **Default property list**: Comprehensive default array of properties to fetch +- **GraphQL field mapping**: Convert PowerShell property names to GraphQL fields using class mapping +- **Here-string for queries**: Use `@"..."@` for multi-line GraphQL queries +- **Escape dollar signs**: Use backtick before `$` in GraphQL variables: `` `$Owner `` +- **Type constructor**: Create typed object from API response: `[GitHubRepository]::new($_.repositoryOwner.repository)` +- **Pipeline support**: Use `ForEach-Object` to stream results through pipeline + +--- + +## Parameter Splatting + +### Cleaning Hashtables Before Splatting + +This module uses the `Remove-HashtableEntry` helper to clean hashtables before splatting: + +```powershell +# Build parameter hashtable conditionally +$params = @{ + Context = $Context +} + +if ($Property) { + $params['Property'] = $Property +} + +if ($AdditionalProperty) { + $params['AdditionalProperty'] = $AdditionalProperty +} + +# No need to clean - only add properties that exist +Get-GitHubRepositoryByName @params +``` + +**Alternative pattern for API calls:** + +```powershell +# Build API call hashtable with all possible parameters +$APICall = @{ + Uri = $Uri + Method = [string]$Method + Headers = $Headers + ContentType = $ContentType + InFile = $UploadFilePath + HttpVersion = [string]$HttpVersion + MaximumRetryCount = $RetryCount + RetryIntervalSec = $RetryInterval +} + +# Remove null or empty values before splatting +$APICall | Remove-HashtableEntry -NullOrEmptyValues + +Invoke-WebRequest @APICall +``` + +**Key Pattern Elements:** + +- **Conditional addition**: Only add parameters to hashtable if they have values +- **Remove-HashtableEntry**: Use `-NullOrEmptyValues` switch to clean before splatting +- **Splat with `@`**: Use `@params` not `$params` when calling function + +--- + +## Error Handling + +### Comprehensive Error Handling from Invoke-GitHubAPI + +```powershell +try { + $response = Invoke-WebRequest @APICall -ProgressAction 'SilentlyContinue' -Debug:$false -Verbose:$false + + # Process successful response... + +} catch { + $failure = $_ + + # Parse error details from API response + $errordetails = $failure.ErrorDetails | ConvertFrom-Json -AsHashtable + $errors = $errordetails.errors + + # Build structured error object + $errorResult = [pscustomobject]@{ + Message = $errordetails.message + Resource = $errors.resource + Code = $errors.code + Details = $errors.message + Information = $errordetails.documentation_url + Status = $failure.Exception.Message + StatusCode = $errordetails.status + ErrorTime = Get-Date -Format 's' + } + + # Build comprehensive exception message with context + $exception = @" + +---------------------------------- +Context: +$($Context | Format-List | Out-String) +---------------------------------- +Request: +$([pscustomobject]$APICall | Select-Object -ExcludeProperty Body, Headers | Format-List | Out-String) +---------------------------------- +Request headers: +$([pscustomobject]$APICall.Headers | Format-List | Out-String) +---------------------------------- +Request body: +$("$($APICall.Body | Out-String -Stream)".Split('\n') -split '\n') +---------------------------------- +Response headers: +$($headers | Format-List | Out-String) +---------------------------------- +Error: +$($errorResult | Format-List | Out-String) +---------------------------------- + +"@ + + # Throw terminating error with all context + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.Exception]::new($exception), + 'GitHubAPIError', + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $errorResult + ) + ) +} +``` + +**Key Pattern Elements:** + +- **Parse ErrorDetails**: GitHub API returns structured error in `$_.ErrorDetails` as JSON +- **Structured error object**: Create `[pscustomobject]` with all error properties +- **Context in error message**: Include context, request, headers, body, and response in exception +- **Here-string for formatting**: Use `@"..."@` for multi-line error message with embedded expressions +- **ThrowTerminatingError**: Use `$PSCmdlet.ThrowTerminatingError()` not `throw` for proper error records +- **Include TargetObject**: Pass `$errorResult` as TargetObject for error record + +--- + +## GraphQL Query Pattern + +### Building and Executing GraphQL Queries + +```powershell +# Build field list from properties +$graphParams = @{ + PropertyList = $Property + $AdditionalProperty + PropertyToGraphQLMap = [GitHubRepository]::PropertyToGraphQLMap +} +$graphQLFields = ConvertTo-GitHubGraphQLField @graphParams + +# Build GraphQL query with here-string +$apiParams = @{ + Query = @" +query( + `$Owner: String!, + `$Name: String! +) { + repositoryOwner( + login: `$Owner + ) { + repository( + name: `$Name + ) { +$graphQLFields + } + } +} +"@ + Variables = @{ + Owner = $Owner + Name = $Name + } + Context = $Context +} + +# Execute query and create typed objects +Invoke-GitHubGraphQLQuery @apiParams | ForEach-Object { + [GitHubRepository]::new($_.repositoryOwner.repository) +} +``` + +**Key Pattern Elements:** + +- **Property mapping**: Use class's `PropertyToGraphQLMap` to convert PowerShell names to GraphQL fields +- **Escape variables**: Use backtick before `$` in GraphQL query: `` `$Owner `` +- **Variables hashtable**: Separate query from variables for security and reusability +- **Indented field list**: Insert `$graphQLFields` with proper indentation in query +- **Type constructor**: Create typed object from nested response: `[GitHubRepository]::new($_.repositoryOwner.repository)` + +--- + +## Filter Usage + +### When to Use `filter` Instead of `function` + +This module prefers `filter` for **all** public and private functions to enable streaming pipeline behavior: + +```powershell +# ✅ CORRECT: Use filter for pipeline support +filter Get-GitHubRepository { + [CmdletBinding()] + param( + [Parameter(ValueFromPipelineByPropertyName)] + [string] $Name, + + [Parameter()] + [object] $Context + ) + + begin { + # Resolve context once + $Context = Resolve-GitHubContext -Context $Context + } + + process { + # This runs for each pipeline object + Get-GitHubRepositoryByName -Owner $Owner -Name $Name -Context $Context + } + + end { + # Cleanup if needed + } +} + +# ❌ WRONG: Don't use function unless you have a specific reason +function Get-GitHubRepository { + # This doesn't support streaming pipeline +} +``` + +**Key Pattern Elements:** + +- **`filter` = `function` + automatic `process` block**: Filter wraps entire body in `process {}` +- **Explicit blocks with filter**: Can still use `begin`/`process`/`end` with filter +- **Context in `begin`**: Resolve context in `begin` block to avoid repeating for each pipeline object +- **Stream results**: Don't collect in array - output directly to pipeline + +--- + +## Pipeline Support + +### ValueFromPipelineByPropertyName Pattern + +```powershell +filter Get-GitHubRepository { + param( + # Bind from Owner property of pipeline object + [Parameter( + Mandatory, + ParameterSetName = 'ByName' + )] + [Alias('Organization', 'Username')] + [string] $Owner, + + # Bind from Name/Repository/Repo property of pipeline object + [Parameter( + Mandatory, + ParameterSetName = 'ByName', + ValueFromPipelineByPropertyName + )] + [Alias('Repository', 'Repo')] + [string] $Name, + + # Context does NOT come from pipeline + [Parameter()] + [object] $Context + ) + + begin { + # Resolve context once before processing pipeline + $Context = Resolve-GitHubContext -Context $Context + } + + process { + # This runs for each pipeline object + # $Name is bound from pipeline object's Name/Repository/Repo property + Get-GitHubRepositoryByName -Owner $Owner -Name $Name -Context $Context + } +} +``` + +**Usage:** + +```powershell +# Pipeline array of repository names +@('repo1', 'repo2', 'repo3') | ForEach-Object { + [pscustomobject]@{ Owner = 'octocat'; Name = $_ } +} | Get-GitHubRepository + +# Pipeline from another command +Get-GitHubRepository -Owner 'octocat' | Get-GitHubRepositoryBranch +``` + +**Key Pattern Elements:** + +- **`ValueFromPipelineByPropertyName`**: Bind parameter from pipeline object property with matching name or alias +- **Aliases for binding**: Use `[Alias()]` to accept multiple property names +- **Context not from pipeline**: Context parameter should never use `ValueFromPipeline` or `ValueFromPipelineByPropertyName` +- **Resolve context in `begin`**: Only resolve context once, not for each pipeline object + +--- + +## Debug Output Pattern + +### Comprehensive Debug Logging + +```powershell +begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $debug = $DebugPreference -eq 'Continue' + + if ($debug) { + Write-Debug 'Parent function parameters:' + Get-FunctionParameter -Scope 1 | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug 'Parameters:' + Get-FunctionParameter | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } +} + +process { + if ($debug) { + Write-Debug '----------------------------------' + Write-Debug 'Request:' + [pscustomobject]$APICall | Select-Object -ExcludeProperty Body, Headers | Format-List | + Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + Write-Debug 'Request headers:' + [pscustomobject]$APICall.Headers | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + Write-Debug 'Request body:' + ($APICall.Body | Out-String).Split('\n') -split '\n' | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + } + + # ... do work ... + + if ($debug) { + Write-Debug '----------------------------------' + Write-Debug 'Response:' + $response | Select-Object -ExcludeProperty Content, Headers | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '---------------------------' + Write-Debug 'Response headers:' + $headers | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '---------------------------' + Write-Debug 'Response content:' + $results | ConvertTo-Json -Depth 5 -WarningAction SilentlyContinue | Out-String -Stream | ForEach-Object { + $content = $_ + $content = $content -split '\n' + $content = $content.Split('\n') + foreach ($item in $content) { + Write-Debug $item + } + } + Write-Debug '---------------------------' + } +} + +end { + Write-Debug "[$stackPath] - End" +} +``` + +**Key Pattern Elements:** + +- **Stack path**: Use `Get-PSCallStackPath` to show function call hierarchy +- **Debug preference check**: Store `$debug = $DebugPreference -eq 'Continue'` to avoid repeated checks +- **Format for readability**: Use `Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ }` +- **Separators**: Use `Write-Debug '----...'` to visually separate sections +- **Exclude large properties**: Use `-ExcludeProperty Body, Headers` when showing request object +- **Line-by-line output**: Split multi-line strings and debug each line separately +- **Begin and end markers**: Always include `Write-Debug "[$stackPath] - Start"` and `Write-Debug "[$stackPath] - End"` + +--- + +## Summary + +These patterns are extracted from the actual GitHub PowerShell module codebase. They demonstrate: + +1. **Context Resolution**: How to resolve string/null/object contexts with token refresh +2. **API Calls**: Complete pattern for REST API calls with pagination, error handling, and structured responses +3. **Public Functions**: Filter-based with parameter sets, context resolution, and routing +4. **Private Functions**: Direct API mapping with mandatory context and type constructors +5. **Parameter Splatting**: Conditional hashtable building and cleaning +6. **Error Handling**: Structured error objects with comprehensive context +7. **GraphQL Queries**: Field mapping, variable separation, and type construction +8. **Filter Usage**: Streaming pipeline behavior for all functions +9. **Pipeline Support**: Property binding with aliases and context handling +10. **Debug Output**: Comprehensive logging with stack paths and structured output + +When writing code for this module, refer to these concrete examples and follow the established patterns. diff --git a/.github/instructions/Repository/XML/main.instructions.md b/.github/instructions/Repository/XML/main.instructions.md new file mode 100644 index 000000000..b65515fc5 --- /dev/null +++ b/.github/instructions/Repository/XML/main.instructions.md @@ -0,0 +1,27 @@ +--- +description: "How to write XML code in this specific project" +applyTo: "**/*.xml, **/*.ps1xml" +--- + +## Scope +Covers PowerShell `.Format.ps1xml` and `.Types.ps1xml` for custom views & alias properties. + +## Conventions +- 2-space indentation. +- Add new type entries alphabetically by class name. +- Use alias properties for convenience accessors (e.g., `Repository` → `Name`). + +## Example Alias +```xml + + Repository + Name + +``` + +## Validation +- Import module locally to ensure no XML schema errors: `Import-Module ./src/manifest.psd1 -Force`. + +## Anti-Patterns +- ❌ Duplicate alias names. +- ❌ Out-of-order `` entries. diff --git a/.github/instructions/Repository/YAML/main.instructions.md b/.github/instructions/Repository/YAML/main.instructions.md new file mode 100644 index 000000000..505f4690f --- /dev/null +++ b/.github/instructions/Repository/YAML/main.instructions.md @@ -0,0 +1,41 @@ +--- +description: "How to write YAML code in this specific project" +applyTo: "**/*.yml, **/*.yaml" +--- + +## Project Conventions +- Workflows orchestrate module tests, lint, release. Logic lives in PowerShell scripts under `tools/` or `tests/`. +- Reusable workflows not yet adopted; prefer composite action only after >2 reuse cases. + +## Naming +- File names: `Pascal-UseCase.yml` (e.g., `Process-PSModule.yml`) +- Workflow `name:` mirrors filename (spaces allowed). + +## Permissions +- Explicit `permissions:` block in every workflow with least privileges (often `contents: read`). + +## Caching +- Cache PSModule path & PowerShell module downloads keyed by module version + OS. + +## Example Snippet +```yaml +jobs: + test: + runs-on: ubuntu-22.04 + permissions: + contents: read + steps: + - uses: actions/checkout@ + - name: Install PowerShell + uses: PowerShell/PowerShell-For-GitHub-Actions@ + - name: Run Tests + run: pwsh -File tools/utilities/Local-Testing.ps1 +``` + +## Anti-Patterns +- ❌ Embedding >30 lines script in `run:` → ✅ Move to tracked `.ps1` file. +- ❌ Using default token permissions → ✅ Declare minimal `permissions:`. +- ❌ Unpinned action versions → ✅ Pin to commit SHA. + +## Overrides (vs Org Rules) +- Allow longer step names (up to 80 chars) for clarity. diff --git a/.github/instructions/Repository/languages.instructions.md b/.github/instructions/Repository/languages.instructions.md new file mode 100644 index 000000000..e8e00342c --- /dev/null +++ b/.github/instructions/Repository/languages.instructions.md @@ -0,0 +1,30 @@ +--- +description: "Auto-generated language mapping summary" +applyTo: "**/*" +--- + +## Discovered Language Mapping (Project View) + +```json +{ + "Languages": [ + {"Language": "PowerShell", "Extensions": ["ps1","psd1","psm1"], "RepresentativeFiles": ["src/functions/public/Repositories/Get-GitHubRepository.ps1","src/manifest.psd1","tests/Repositories.Tests.ps1"], "InstructionFilesPresent": true}, + {"Language": "YAML", "Extensions": ["yml","yaml"], "RepresentativeFiles": [".github/workflows/Linter.yml",".github/dependabot.yml"], "InstructionFilesPresent": true}, + {"Language": "Markdown", "Extensions": ["md"], "RepresentativeFiles": ["README.md","CodingStandard.md"], "InstructionFilesPresent": true}, + {"Language": "JSON", "Extensions": ["json"], "RepresentativeFiles": [".github/linters/.jscpd.json"], "InstructionFilesPresent": true}, + {"Language": "XML", "Extensions": ["ps1xml","xml"], "RepresentativeFiles": ["src/formats/GitHubRepository.Format.ps1xml"], "InstructionFilesPresent": true}, + {"Language": "GitAttributes", "Extensions": ["gitattributes"], "RepresentativeFiles": [".gitattributes"], "InstructionFilesPresent": true}, + {"Language": "GitIgnore", "Extensions": ["gitignore"], "RepresentativeFiles": [".gitignore"], "InstructionFilesPresent": true} + ] +} +``` + +| Language | Repo-Specific Notes | +|----------|---------------------| +| PowerShell | Context + public/private function split is enforced (see PowerShell repository instructions). | +| YAML | Workflows must call PowerShell scripts instead of large inline `run` blocks. | +| Markdown | Include runnable examples; prefer PowerShell fenced blocks. | +| JSON | Only tooling configs currently; keep minimal & documented. | +| XML | Only format/types metadata; add new types when introducing new classes. | +| GitAttributes | Ensure LF normalization for cross-platform contributors. | +| GitIgnore | Keep patterns tight; review when adding tooling. | diff --git a/.github/instructions/Repository/main.instructions.md b/.github/instructions/Repository/main.instructions.md new file mode 100644 index 000000000..53009ed0d --- /dev/null +++ b/.github/instructions/Repository/main.instructions.md @@ -0,0 +1,726 @@ +--- +description: "Project-specific code-writing guidance for GitHub PowerShell module" +applyTo: "**/*" +--- + +# GitHub PowerShell Module Instructions + +## Repository Purpose + +The **GitHub PowerShell** module provides a PowerShell-native interface for managing and automating GitHub environments. It supports: + +- **Multiple GitHub environments**: GitHub.com, GitHub Enterprise Cloud (GHEC), and GitHub Enterprise Server (GHES) +- **GitHub Actions integration**: Context-aware execution with automatic workflow integration +- **Multiple authentication methods**: PAT, UAT, IAT, GitHub Apps +- **Pipeline-friendly commands**: Full support for PowerShell pipeline operations + +The module is designed as both a local scripting companion and a GitHub Actions workflow companion, understanding its execution context and providing appropriate defaults. + +## Architecture Overview + +### Module Structure + +``` +src/ + functions/ + public/ # Exported commands (Verb-GitHubNoun) + {ObjectType}/ # Grouped by GitHub object type (Repository, User, etc.) + private/ # Internal API wrappers and helpers + {ObjectType}/ # Mirror public structure + classes/ + public/ # GitHubRepository, GitHubUser, GitHubOwner, etc. + formats/ # Format.ps1xml for custom display + types/ # Types.ps1xml for type extensions and aliases + variables/ # Module-scope variables + loader.ps1 # Module initialization and auto-discovery + header.ps1 # Module requirements + manifest.psd1 # Module manifest + completers.ps1 # Argument completers +``` + +### Component Relationships + +``` +┌─────────────────┐ +│ Public Funcs │ (Verb-GitHubNoun) +│ - Pipeline │ - Calls Resolve-GitHubContext +│ - Context │ - Parameter validation +│ - Param sets │ - Routes to private functions +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Private Funcs │ (Verb-GitHubNoun) +│ - No pipeline │ - Mandatory Context parameter +│ - API calls │ - Direct API endpoint mapping +│ - No defaults │ - Returns API responses +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Invoke-GitHub- │ +│ API │ - REST API wrapper +│ - Auth handling│ - Error handling +│ - Pagination │ - Rate limiting +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ GitHub API │ +└─────────────────┘ +``` + +### Data Flow + +1. **User calls public function** → `Get-GitHubRepository -Owner 'octocat' -Name 'Hello-World'` +2. **Context resolution** → `Resolve-GitHubContext` ensures Context is `[GitHubContext]` object +3. **Parameter defaulting** → If `$Repository` missing, default from `$Context.Repo` +4. **Parameter set routing** → Switch on `$PSCmdlet.ParameterSetName` to select private function +5. **Private function call** → Pass mandatory parameters including resolved Context +6. **API invocation** → `Invoke-GitHubAPI` with method, endpoint, body +7. **Response processing** → Transform API response to typed objects (e.g., `[GitHubRepository]`) +8. **Output** → Return typed objects to pipeline + +## Project-Specific Rules + +### Function Organization + +- **Group by object type, NOT by API endpoint** + - ✅ `src/functions/public/Repositories/Get-GitHubRepository.ps1` + - ❌ `src/functions/public/Repos/Get-GitHubRepository.ps1` + +- **Object types as folder names**: + - `Repositories/` - Repository operations + - `Users/` - User operations + - `Organizations/` - Organization operations + - `Teams/` - Team operations + - `Releases/` - Release operations + - `Secrets/`, `Variables/` - GitHub Actions resources + - `Webhooks/` - Webhook operations + - `Workflows/` - GitHub Actions workflows + - `Apps/` - GitHub App operations + - `Artifacts/` - GitHub Actions artifacts + - `Environments/` - GitHub Environments + +### API Coverage Philosophy + +- **We do NOT need 100% GitHub API coverage** +- Maintain exclusion list for endpoints intentionally not covered +- Focus on commonly used operations +- Mark non-implemented endpoints as ⚠️ in coverage reports +- Examples of deliberately excluded endpoints: + - Hovercards API + - Legacy endpoints + - Rarely-used administrative operations + +### Context System + +The module uses a `GitHubContext` system to manage authentication and connection state: + +#### Context Resolution + +Every public function must: +1. Accept a `$Context` parameter of type `[object]` (allows string or GitHubContext) +2. Call `Resolve-GitHubContext -Context $Context` in `begin` block +3. Call `Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT` to validate auth + +**Example:** +```powershell +param( + [Parameter()] + [object] $Context +) + +begin { + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT +} +``` + +#### Context Properties + +The resolved `[GitHubContext]` object provides: +- `$Context.Owner` - Current owner (user or organization) +- `$Context.Repo` - Current repository +- `$Context.Type` - Authentication type (PAT, UAT, IAT, APP) +- Additional context from GitHub Actions environment + +#### Parameter Defaulting from Context + +Public functions should default missing parameters from context: + +```powershell +process { + if (-not $Repository) { + $Repository = $Context.Repo + } + if (-not $Repository) { + throw "Repository not specified and not found in the context." + } + + if (-not $Owner) { + $Owner = $Context.Owner + } +} +``` + +**Private functions must NOT do this** - they receive already-resolved parameters. + +### Public vs Private Function Contract + +#### Public Functions + +- **Purpose**: User-facing API with convenience features +- **Pipeline**: Support `ValueFromPipelineByPropertyName` +- **Context**: Accept `[object] $Context`, resolve to `[GitHubContext]` +- **Parameter defaulting**: Default from context if not provided +- **Multiple parameter sets**: Route to appropriate private functions +- **Aliases**: Support common aliases on functions and parameters +- **Error handling**: User-friendly error messages + +**Structure:** +```powershell +filter Get-GitHubSomething { + [OutputType([GitHubSomething])] + [CmdletBinding(DefaultParameterSetName = 'List all')] + param( + [Parameter(Mandatory, ParameterSetName = 'Get by ID')] + [int] $ID, + + [Parameter(Mandatory, ParameterSetName = 'Get by name')] + [string] $Name, + + [Parameter()] + [object] $Context + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT + } + + process { + $params = @{ + Context = $Context + ID = $ID + Name = $Name + } + $params | Remove-HashtableEntry -NullOrEmptyValues + + switch ($PSCmdlet.ParameterSetName) { + 'Get by ID' { + Get-GitHubSomethingByID @params + } + 'Get by name' { + Get-GitHubSomethingByName @params + } + 'List all' { + Get-GitHubSomethingList @params + } + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +#### Private Functions + +- **Purpose**: Direct API endpoint wrappers +- **Pipeline**: NO pipeline support +- **Context**: Mandatory `[GitHubContext] $Context` parameter +- **Parameters**: All required parameters are mandatory +- **No defaulting**: Caller must provide all values +- **No aliases**: No function or parameter aliases +- **One API call = One function**: Each endpoint gets its own function + +**Structure:** +```powershell +function Get-GitHubSomethingByID { + [OutputType([GitHubSomething])] + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [GitHubContext] $Context, + + [Parameter(Mandatory)] + [int] $ID + ) + + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + + $inputObject = @{ + Context = $Context + Method = 'Get' + APIEndpoint = "/something/$ID" + } + + Invoke-GitHubAPI @inputObject | ForEach-Object { + Write-Output $_.Response + } +} +``` + +### Parameter Naming Conventions + +#### Object-Oriented Naming + +Function name implies the object type, so don't repeat it in parameters: + +- ✅ `Get-GitHubRepository -ID 123` (not `-RepositoryID`) +- ✅ `Get-GitHubUser -Name 'octocat'` (not `-Username`) +- ✅ `Remove-GitHubTeam -Team 'developers'` (not `-TeamName`) + +#### Standard Aliases + +Consistently apply these aliases across all functions: + +- `$Owner` → `[Alias('Organization', 'Username')]` (never use `[Alias('org')]`) +- `$Username` → `[Alias('Login')]` where applicable +- `$Repository` → `[Alias('Repo')]` for convenience +- `$ID` → `[Alias('Id', 'id')]` if needed for compatibility + +#### API Parameter Conversion + +Transform GitHub API snake_case to PowerShell PascalCase: + +- `per_page` → `$PerPage` +- `node_id` → `$NodeID` +- `html_url` → `$Url` +- `created_at` → `$CreatedAt` +- `allow_forking` → `$AllowForking` + +### API Call Pattern + +All API calls follow this standard pattern: + +```powershell +try { + $inputObject = @{ + Context = $Context + Method = 'Post' # Use PascalCase: Get, Post, Put, Patch, Delete + APIEndpoint = "/repos/$Owner/$Repository/issues" + Body = @{ + title = $Title + body = $Body + labels = $Labels + } + } + + # Remove null values from body + $inputObject.Body | Remove-HashtableEntry -NullOrEmptyValues + + Invoke-GitHubAPI @inputObject | ForEach-Object { + Write-Output $_.Response + } +} catch { + if ($_.Exception.Response.StatusCode -eq 404) { + Write-Error "Repository '$Repository' not found for owner '$Owner'" + return + } + throw +} +``` + +**Splat order (consistent across module):** +1. `Context` +2. `Method` +3. `APIEndpoint` +4. `Body` (for POST, PUT, PATCH) + +### Class Design + +#### Base Classes + +- **GitHubNode**: Base class for objects with both `id` (database ID) and `node_id` (GraphQL ID) + - Properties: `ID` (database ID), `NodeID` (GraphQL node ID) + - All GitHub resources with these IDs should extend GitHubNode + +#### Scope Properties + +Objects that belong within a scope include properties for that scope: + +- `Enterprise` - For enterprise-scoped resources +- `Owner`/`Organization`/`Account` - For owner-scoped resources +- `Repository` - For repository-scoped resources +- `Environment` - For environment-scoped resources + +**Example:** +```powershell +class GitHubSecret : GitHubNode { + [string] $Name + [GitHubOwner] $Owner + [GitHubRepository] $Repository + [datetime] $CreatedAt + [datetime] $UpdatedAt +} +``` + +#### Convenience Properties + +Classes should have a convenience property matching the class name: + +- `GitHubRepository` has `Repository` property (alias to `Name` via types file) +- `GitHubUser` has `User` property (alias to `Login` via types file) +- `GitHubTeam` has `Team` property (alias to `Slug` via types file) + +This is done via types files (`src/types/*.Types.ps1xml`): + +```xml + + GitHubRepository + + + Repository + Name + + + +``` + +#### Property Standardization + +- **ID Properties**: + - `ID` - Primary resource ID (database ID) + - `NodeID` - GraphQL node ID + - Never use `RepositoryID`, `UserID` in class names - just `ID` + +- **Size Properties**: + - All disk size properties → `Size` in bytes + - Convert from KB/MB if needed + - Type: `[System.Nullable[uint64]]` + +- **URL Properties**: + - `Url` - Primary web URL (not `HtmlUrl`) + - `ApiUrl` - API endpoint URL if different + - Specific URLs: `Homepage`, `DocumentationUrl`, etc. + +- **Date Properties**: + - Use `[System.Nullable[datetime]]` type + - Common names: `CreatedAt`, `UpdatedAt`, `PushedAt`, `ArchivedAt` + +#### Example Class Structure + +```powershell +class GitHubRepository : GitHubNode { + # Primary identifiers + [string] $Name + [GitHubOwner] $Owner + [string] $FullName # Owner/Name format + + # URLs + [string] $Url + + # Metadata + [string] $Description + [System.Nullable[datetime]] $CreatedAt + [System.Nullable[datetime]] $UpdatedAt + [System.Nullable[uint64]] $Size + + # Features + [System.Nullable[bool]] $HasIssues + [System.Nullable[bool]] $IsPrivate + [System.Nullable[bool]] $IsArchived + + # Relationships + [GitHubLicense] $License + [string[]] $Topics +} +``` + +### Testing Patterns + +#### Test Organization + +Tests use authentication case matrix for comprehensive coverage: + +```powershell +Describe 'Feature' { + $authCases = . "$PSScriptRoot/Data/AuthCases.ps1" + + Context 'As using on ' -ForEach $authCases { + BeforeAll { + $context = Connect-GitHubAccount @connectParams -PassThru -Silent + LogGroup 'Context' { + Write-Host ($context | Format-List | Out-String) + } + } + + AfterAll { + Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount -Silent + Write-Host ('-' * 60) + } + + It 'Should perform operation' { + # Test implementation + } + } +} +``` + +#### GitHub Actions Integration + +Tests are designed to run both locally and in GitHub Actions: + +- Use `LogGroup` helper for collapsible GitHub Actions output +- Check authentication type for conditional tests +- Skip tests not applicable to certain auth types + +### GitHub Actions Integration + +#### Context Awareness + +When running in GitHub Actions, the module automatically: + +1. Imports `GITHUB_EVENT_PATH` data +2. Sets environment variables for common values +3. Detects repository, owner, workflow context +4. Provides defaults from the triggering event + +**Automatic setup in loader.ps1:** +```powershell +if ($script:IsGitHubActions) { + Write-Verbose 'Detected running on a GitHub Actions runner, preparing environment...' + $env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' + Set-GitHubEnvironmentVariable -Name 'GITHUB_REPOSITORY_NAME' -Value $env:GITHUB_REPOSITORY_NAME + $env:GITHUB_HOST_NAME = ($env:GITHUB_SERVER_URL ?? 'github.com') -replace '^https?://' + Set-GitHubEnvironmentVariable -Name 'GITHUB_HOST_NAME' -Value $env:GITHUB_HOST_NAME + Import-GitHubEventData + Import-GitHubRunnerData +} +``` + +#### Workflow Commands + +Use module-specific functions for workflow commands: + +- `Set-GitHubOutput -Name 'result' -Value 'success'` +- `Set-GitHubEnvironmentVariable -Name 'VAR' -Value 'value'` +- `Write-GitHubNotice 'Information message'` +- `Write-GitHubWarning 'Warning message'` +- `Write-GitHubError 'Error message'` + +These use the correct workflow command syntax internally. + +### Formatting and Type Extensions + +#### Format Files (`.Format.ps1xml`) + +Define custom table and list views for display: + +```xml + + + + + GitHubRepository + + GitHubRepository + + + + + + + + + + + Name + Owner + IsPrivate + + + + + + + +``` + +#### Type Files (`.Types.ps1xml`) + +Define property aliases and computed properties: + +```xml + + + + GitHubRepository + + + Repository + Name + + + + +``` + +## Development Workflows + +### Adding a New Function + +1. Determine object type (Repository, User, Team, etc.) +2. Create public function in `src/functions/public/{ObjectType}/` +3. Create corresponding private function(s) in `src/functions/private/{ObjectType}/` +4. Add class definition if new object type in `src/classes/public/{ObjectType}/` +5. Add format file in `src/formats/` for display +6. Add type file in `src/types/` for convenience properties +7. Add tests in `tests/{Feature}.Tests.ps1` +8. Add examples in `examples/{Feature}/` + +### Function Checklist + +- [ ] Public function uses `filter` with `begin`, `process`, `end` +- [ ] All parameters have `[Parameter()]` attribute +- [ ] Context parameter is `[object]` type, resolved in `begin` +- [ ] Parameters default from context where appropriate +- [ ] Switch on parameter set name to route to private functions +- [ ] Private function has mandatory `[GitHubContext] $Context` parameter +- [ ] API call uses standard splat pattern +- [ ] Error handling with try-catch +- [ ] Comment-based help with examples +- [ ] Test coverage with auth case matrix +- [ ] No `ShouldProcess` on `Get-` commands +- [ ] Aliases applied consistently + +### Versioned Resources + +For resources with versions (like workflow files), follow this pattern: + +- **Default behavior**: Get latest version +- **Optional parameters**: + - `-Name` - Get specific version by name + - `-AllVersions` - Get all versions + +Example: `Get-GitHubWorkflow` gets the latest, `Get-GitHubWorkflow -AllVersions` lists all. + +## Key Differences from Organization Standards + +### Module-Specific Overrides + +1. **Filter over Function**: This module prefers `filter` for public functions (organization default is `function`) +2. **Context System**: Unique authentication and context management system +3. **Object Type Grouping**: Strict enforcement of grouping by object type vs endpoint +4. **Two-Tier Function Model**: Public/private split with different contracts +5. **GitHub Actions Integration**: Built-in workflow command support + +### Additional Module Patterns + +- `Get-PSCallStackPath` - Standard debug tracing +- `Remove-HashtableEntry -NullOrEmptyValues` - Clean up splatted parameters +- `LogGroup` helper for GitHub Actions output grouping +- API coverage tracking with exclusion list + +## API Coverage Philosophy + +### Intentional Exclusions + +- **We do NOT need 100% coverage of the GitHub API** +- Maintain a separate file listing endpoints you intentionally do not cover +- Coverage reports can mark excluded endpoints with ⚠️ +- Examples of commonly excluded endpoints: hovercards, rarely used features + +### Function Design Rules + +1. **One API Call = One Function**: If a single function handles multiple distinct API calls, split it into multiple functions +2. **DefaultParameterSetName Prohibition**: Do NOT declare `DefaultParameterSetName = '__AllParameterSets'` - only specify if it's actually different from the first parameter set +3. **Permissions Comment Required**: In the `begin` block, add a comment stating which permissions are required for the API call + +Example: +```powershell +begin { + # Requires: repo, read:org permissions + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType PAT, UAT +} +``` + +## Versioned Resource Pattern + +Functions that get versioned objects return the **latest version** by default: + +- **Default behavior**: Get latest version +- **`-Name` parameter**: Get specific version by name +- **`-AllVersions` switch**: Get all versions + +Example: +```powershell +Get-GitHubWorkflow # Gets latest +Get-GitHubWorkflow -AllVersions # Gets all versions +``` + +## Class Design Rules + +### ID vs NodeID Distinction + +- **`ID` property**: The main resource identifier (GitHub's databaseID) +- **`NodeID` property**: GitHub's GraphQL node_id +- All classes using both should extend `GitHubNode` base class + +### Class Property Standards + +1. **Scope Properties**: Objects belonging inside another scope include scope properties: + - `Enterprise` - For enterprise-scoped resources + - `Owner` / `Organization` / `Account` - For organization-scoped resources + - `Repository` - For repository-scoped resources + - `Environment` - For environment-scoped resources + +2. **Convenience Property Pattern**: Classes have their class name as a property aliased to the typical command value + - Example: `GitHubRepository` class has `Repository` property (aliased via types file) with value from `Name` property + - This enables: `$repo.Repository` to get the name used in commands + +3. **Interface Consistency**: Remove properties that are purely "API wrapper" fields (e.g., raw HTTP artifacts not relevant to users) + +4. **Size Properties**: All properties referencing size on disk should: + - Be converted to bytes + - Be named `Size` + - Use type `[System.Nullable[uint64]]` + +Example class structure: +```powershell +class GitHubRepository : GitHubNode { + [string] $Name # Source for Repository alias + [GitHubOwner] $Owner # Scope property + [System.Nullable[uint64]] $Size # Size in bytes + [System.Nullable[datetime]] $CreatedAt + [System.Nullable[datetime]] $UpdatedAt + [string[]] $Topics + [GitHubLicense] $License +} +``` + +Corresponding types file entry: +```xml + + GitHubRepository + + + Repository + Name + + + +``` + +## Common Pitfalls to Avoid + +1. ❌ **Don't group by API endpoint** - Use object type folders +2. ❌ **Don't use `[Alias('org')]` on $Organization** - Use `Owner` parameter with `[Alias('Organization', 'User')]` instead +3. ❌ **Don't put defaulting logic in private functions** - Only in public functions +4. ❌ **Don't forget context resolution** - Every public function needs it +5. ❌ **Don't repeat object type in parameter names** - Function name implies it (use `Get-GitHubRepository -ID` not `-RepositoryID`) +6. ❌ **Don't use Write-Host** - Use Write-Output/Debug/Verbose (except workflow commands) +7. ❌ **Don't add ShouldProcess to Get commands** - Only for destructive operations +8. ❌ **Don't use pipeline in private functions** - Public functions only +9. ❌ **Don't forget to remove null values from body** - Use `Remove-HashtableEntry` +10. ❌ **Don't use string redundant checks** - Use `-not $Param` or validation attributes +11. ❌ **Don't create multiple API calls in one function** - Split into separate functions +12. ❌ **Don't use `DefaultParameterSetName = '__AllParameterSets'`** - Only use if actually different from first set +13. ❌ **Don't forget permissions comment in begin block** - Document required permissions diff --git a/.github/instructions/instructions/framework/JSON/main.instructions.md b/.github/instructions/instructions/framework/JSON/main.instructions.md new file mode 100644 index 000000000..5e1e26d60 --- /dev/null +++ b/.github/instructions/instructions/framework/JSON/main.instructions.md @@ -0,0 +1,53 @@ +--- +applyTo: '**/*.json' +description: Framework-level JSON patterns for configuration, tooling, and data assets. +--- + +# Framework JSON Guidelines + +JSON files underpin tooling configs, API payloads, and data artifacts across PSModule repositories; these rules keep them consistent and machine-friendly. + +## Goal +- Define formatting, structure, and validation expectations so JSON remains deterministic and easy to consume across platforms. +- Prevent accidental drift in schema usage, sensitive value handling, or tooling integration. + +## Execution Steps +1. Format the document with two-space indentation, UTF-8 + LF endings, and double-quoted strings. +2. Group related keys logically and confirm casing consistency (camelCase, PascalCase, or snake_case per schema). +3. Attach schema references or validation commands when available and run them as part of your change. +4. Review arrays/objects for unnecessary nesting and ensure null handling matches consumer expectations. +5. For configuration files, document non-default overrides and keep companion README entries synchronized. + +## Behavior Rules +- **Formatting** + - Enforce two-space indentation, single trailing newline, and no trailing whitespace; limit lines to ≤ 150 characters when feasible. +- **Structure** + - Use meaningful keys, keep related keys adjacent, and avoid deep hierarchies without value. + - Represent collections with arrays and maintain consistent ordering when order matters to tooling. +- **Configuration & Tooling** + - Reference schemas (e.g., via `$schema`) or document validators; align naming conventions across similar configs. + - Capture linter/tool overrides explicitly and document the rationale. +- **Data Files** + - Include version metadata when datasets evolve, maintain type consistency, and treat `null` explicitly. + - Consider file size—compress or split data when large payloads degrade performance. +- **Security** + - Exclude secrets and personal data; rely on environment variables or vault abstractions instead. + - Guard against injection by encoding untrusted input before serialization. +- **Integration** + - Ensure CI/CD automation can parse the file; provide fallback behavior when keys are optional or new. + - Maintain backward compatibility unless a breaking change is intentional and documented. + +## Output Format +- JSON artifacts must validate against their schema (when provided), parse with standard libraries, and present deterministic key ordering if consumed by version control comparisons. +- Configuration changes should update related documentation or changelog entries describing the impact. + +## Error Handling +- Treat parse errors or schema violations as blockers; resolve before merging. +- Document any temporary schema gaps in repo instructions and plan remediation. + +## Definitions +| Term | Description | +| --- | --- | +| **Schema** | JSON Schema or similar definition that constrains permitted structure and values. | +| **Deterministic ordering** | Stable ordering of keys/arrays to minimize differences between runs or environments. | +| **Injection** | Malicious or accidental insertion of executable content via improperly sanitized JSON fields. | diff --git a/.github/instructions/instructions/framework/Markdown.instructions.md b/.github/instructions/instructions/framework/Markdown.instructions.md new file mode 100644 index 000000000..050eb41ec --- /dev/null +++ b/.github/instructions/instructions/framework/Markdown.instructions.md @@ -0,0 +1,60 @@ +--- +applyTo: '**/*.md' +description: Framework-level Markdown patterns for all PSModule documentation. +--- + +# Framework Markdown Guidelines + +PSModule documentation must stay readable in raw form, render cleanly in MkDocs and GitHub, and remain easy to diff—these rules secure that baseline. + +## Goal +- Provide cross-repository Markdown conventions that keep tone, structure, and tooling compatibility aligned. +- Minimize churn in diffs by prescribing consistent heading usage, spacing, and fenced code presentation. + +## Execution Steps +1. Check whether the page needs YAML frontmatter; add only when navigation or metadata requires it. +2. Draft content following the heading hierarchy (single H1, sequential levels) and paragraph guidance. +3. Format lists, code blocks, and tables using the rules below; ensure examples are copy-ready. +4. Validate links (prefer relative paths) and confirm the document builds under MkDocs without warnings. +5. Run Markdown linting (where available) or manually spot-check for spacing, line endings, and language tone. + +## Behavior Rules +- **Universal Standards** + - Encode as UTF-8 with LF endings, strip trailing whitespace, end with a single newline, and keep lines ≤ 150 characters when practical. +- **Front Matter** + - Include YAML front matter only when required by the build; limit keys to essentials (`title`, `description`, overrides). + - Place front matter at the top with no preceding blank lines. +- **Headings & Structure** + - Use exactly one H1 immediately after front matter. + - Step through heading levels sequentially and avoid terminal punctuation in heading text. +- **Paragraphs & Line Handling** + - Keep paragraphs as single logical lines; avoid arbitrary hard wraps except for clarity improvements. + - Don't hard-wrap inside code fences or tables; insert blank lines between paragraphs and lists as needed. +- **Lists** + - Prefer `*` for unordered items with two-space indentation for nested content. + - Leave a blank line before/after lists unless the list follows a heading directly. +- **Code Blocks** + - Always specify a fence language; use `powershell` for module examples, avoid `PS>` prompts, and trim to essential lines. + - Use inline backticks for commands or filenames. +- **Links & References** + - Use descriptive link text, relative paths for internal references, and reference-style links for reusable URLs. +- **Cross-Platform & Integration** + - Show forward-slash paths and note OS-specific nuances where needed. + - Ensure documents remain compatible with MkDocs, search indexing, and PSModule documentation navigation. +- **Style Consistency** + - Maintain consistent voice, terminology, and formatting choices across documents. + +## Output Format +- Markdown files should preview cleanly in MkDocs and GitHub, include working code samples, and respect the prescribed heading/list formatting. +- Navigation metadata (front matter, `mkdocs.yml`) must stay synchronized with document titles and hierarchy. + +## Error Handling +- When tooling or legacy pages prevent full compliance, document the exception inline and create a follow-up task to remediate. +- Treat broken links, malformed tables, or fenced code without language hints as blocking issues. + +## Definitions +| Term | Description | +| --- | --- | +| **MkDocs** | Static site generator used for PSModule documentation sites. | +| **Front matter** | Optional YAML metadata block at the top of a Markdown file controlling navigation or page settings. | +| **Reference-style link** | Markdown pattern that separates link usage from URL definition to encourage reuse (`[text][ref]` and `[ref]: url`). | diff --git a/.github/instructions/instructions/framework/PowerShell.instructions.md b/.github/instructions/instructions/framework/PowerShell.instructions.md new file mode 100644 index 000000000..609afa7f2 --- /dev/null +++ b/.github/instructions/instructions/framework/PowerShell.instructions.md @@ -0,0 +1,61 @@ +--- +applyTo: + - '**/*.ps1' + - '**/*.psm1' + - '**/*.psd1' +description: Framework-level PowerShell patterns that every PSModule repository must follow. +--- + +# Framework PowerShell Guidelines + +These rules define the baseline for all PowerShell authored within the PSModule ecosystem, regardless of repository or module specialization. + +## Goal +- Establish consistent formatting, help, and module architecture expectations for every PowerShell source file. +- Ensure code remains portable between Windows PowerShell 5.1, PowerShell 7+, and GitHub-hosted runners. + +## Execution Steps +1. Identify the PowerShell artifact you are modifying (function, script, module manifest, etc.). +2. Apply the formatting and naming rules listed below before committing. +3. Confirm comment-based help and error handling align with the guidance. +4. Run PSScriptAnalyzer (or repository equivalent) and address violations. +5. Validate module load by importing with `Import-Module -Force` in both Windows PowerShell 5.1 (when available) and PowerShell 7+. + +## Behavior Rules +- **Formatting & Encoding** + - Use UTF-8 with LF endings; trim trailing whitespace and limit lines to 150 characters. + - End each file with a single newline and avoid tabs (4-space indentation unless a repo override states otherwise). +- **Comment-Based Help** + - Provide full help for all public functions, including `.SYNOPSIS`, `.DESCRIPTION`, `.EXAMPLE`, parameter entries, and `.LINK` references where applicable. + - Keep examples executable and reflective of supported scenarios. +- **Error Handling** + - Use consistent terminating/non-terminating patterns, preferring `[ValidateNotNullOrEmpty()]` and other attributes for upfront validation. + - Emit actionable guidance and documentation links in error messages. +- **Code Quality** + - Enforce approved PowerShell verbs, strong typing, and clear parameter naming (PascalCase). + - Suppress analyzer violations only with justification and scope them narrowly. +- **Module Architecture** + - Separate public and private functions; structure modules into semantic folders. + - Utilize classes for complex data structures and ensure module initialization handles both 5.1 and 7+ environments. +- **Pipeline & Performance** + - Support streaming via `begin/process/end` blocks; avoid unnecessary array accumulation. + - Minimize per-iteration allocations and favor efficient idioms (splatting, implicit output). +- **Security** + - Protect credentials (SecureString where appropriate) and validate inputs rigorously. + - Follow least-privilege principles when invoking external services or modifying system state. + +## Output Format +- PowerShell files should import without errors, expose help accessible via `Get-Help`, and pass analyzer/test suites defined in the repository. +- Refactors must preserve module manifests, exported members, and formatting conventions. + +## Error Handling +- If a rule must be relaxed (e.g., due to legacy compatibility), document the exception in repo-specific instructions and annotate the source with justification comments. +- Treat analyzer or import failures as blocking issues; resolve before final hand-off. + +## Definitions +| Term | Description | +| --- | --- | +| **PSScriptAnalyzer** | Static analysis tool used to enforce PowerShell coding standards in PSModule projects. | +| **Comment-based help** | Inline documentation block that powers `Get-Help` output for functions and scripts. | +| **SupportsShouldProcess** | Cmdlet binding feature enabling `-WhatIf`/`-Confirm` semantics for destructive actions. | +| **Pipeline-supporting function** | A function that accepts input via `ValueFromPipeline` and processes items in the `process {}` block without array buffering. | diff --git a/.github/instructions/instructions/framework/PowerShell/classes.instructions.md b/.github/instructions/instructions/framework/PowerShell/classes.instructions.md new file mode 100644 index 000000000..086a32701 --- /dev/null +++ b/.github/instructions/instructions/framework/PowerShell/classes.instructions.md @@ -0,0 +1,94 @@ +--- +applyTo: '**/classes/**/*.{ps1,psm1}' +description: Framework-level PowerShell class patterns and object-oriented design expectations. +--- + +# Framework PowerShell Classes Guidelines + +Use this guidance when authoring or updating PowerShell classes that back PSModule modules, formatters, or API abstractions. + +## Goal +- Encourage consistent, maintainable class implementations that align with PSModule’s single-responsibility philosophy. +- Provide reusable patterns for constructors, serialization helpers, and GitHub-centric models. + +## Execution Steps +1. Determine the class’s responsibility and ensure it models a single concept (data or behavior cluster). +2. Pick PascalCase names for the class and public members, avoiding abbreviations. +3. Sketch constructors, property defaults, and helper methods, referencing the template below. +4. Implement serialization (`ToHashtable`, `FromHashtable`) and pipeline-friendly output as needed. +5. Add comment-based help (or XML documentation if required), run tests, and verify usage across modules. + +## Behavior Rules +- **Design Principles** + - Adhere to single responsibility, keep method names descriptive, and mark internal-only members as `hidden`. +- **Template Reference** + - Base new classes on this structure: + +```powershell +class ObjectName { + [string] $Property1 + [int] $Property2 + hidden [datetime] $CreatedAt + + ObjectName([string] $property1, [int] $property2) { + $this.Property1 = $property1 + $this.Property2 = $property2 + $this.CreatedAt = [datetime]::UtcNow + } + + ObjectName() { + $this.CreatedAt = [datetime]::UtcNow + } + + [string] ToString() { + "$($this.Property1): $($this.Property2)" + } + + [hashtable] ToHashtable() { + @{ + Property1 = $this.Property1 + Property2 = $this.Property2 + CreatedAt = $this.CreatedAt + } + } + + static [ObjectName] FromHashtable([hashtable] $data) { + $obj = [ObjectName]::new() + $obj.Property1 = $data.Property1 + $obj.Property2 = $data.Property2 + if ($data.ContainsKey('CreatedAt')) { + $obj.CreatedAt = $data.CreatedAt + } + return $obj + } +} +``` +- **Properties & Constructors** + - Choose precise .NET types, set defaults in constructors, and validate input parameters. + - Offer overloads or factory methods when callers may supply different shapes of data. +- **Methods & Serialization** + - Implement `ToString()` for debugging and `ToHashtable()`/`FromHashtable()` for serialization. + - Handle circular references carefully; prefer IDs or summaries over nesting entire objects. +- **Inheritance & Interfaces** + - Prefer composition; when inheriting, document base-class expectations and override virtual members deliberately. + - Implement interfaces for shared behavior across modules (e.g., `IJsonSerializable`). +- **Error Handling** + - Throw meaningful exceptions on invalid state, guard property assignments, and keep messages actionable. +- **GitHub Integration** + - When modeling GitHub API objects, store raw payloads or metadata as hidden properties and expose pipeline-friendly data. + - Surface helper methods that wrap common API operations while respecting authentication context. + +## Output Format +- Classes must compile on Windows PowerShell 5.1 and PowerShell 7+, integrate into modules without export conflicts, and be covered by targeted tests. +- Serialization helpers should round-trip sample payloads used in documentation or unit tests. + +## Error Handling +- Treat validation failures or inconsistent serialization as blocking; update tests and constructors to enforce invariants. +- Document temporary exceptions (e.g., incomplete API fields) via TODO comments with linked issues. + +## Definitions +| Term | Description | +| --- | --- | +| **Single responsibility** | Design principle where a class addresses one cohesive concern, simplifying maintenance. | +| **Hidden property** | PowerShell class member marked `hidden` to restrict exposure while keeping data accessible internally. | +| **Round-trip** | Ability for serialization/deserialization helpers to return an equivalent object without data loss. | diff --git a/.github/instructions/instructions/framework/PowerShell/main.instructions.md b/.github/instructions/instructions/framework/PowerShell/main.instructions.md new file mode 100644 index 000000000..8f6d35192 --- /dev/null +++ b/.github/instructions/instructions/framework/PowerShell/main.instructions.md @@ -0,0 +1,133 @@ +--- +applyTo: '**/*.{ps1,psm1,psd1}' +description: Framework-level PowerShell patterns governing function authoring and module layout. +--- + +# Framework PowerShell Guidelines + +This file dives deeper into day-to-day function authoring, parameter design, and module implementation details for PSModule repositories. + +## Goal +- Provide a repeatable blueprint for writing public and private functions that align with PSModule expectations. +- Ensure naming, help, pipeline behavior, and cross-platform execution are consistent across modules. + +## Execution Steps +1. Before editing, confirm the function/module purpose and choose an approved verb-noun name. +2. Apply the formatting baseline (UTF-8 with BOM when 5.1 compatibility is needed, 4-space indentation, ≤150-character lines). +3. Flesh out parameters using validation attributes and pipeline support rules, then scaffold comment-based help. +4. Implement logic with appropriate pipeline sections (`begin/process/end`), error handling, and logging hooks. +5. Run linters (`Invoke-ScriptAnalyzer`), unit tests (Pester), and import the module (`Import-Module -Force`) to verify exports and behavior. + +## Behavior Rules +- **Naming & Scope** + - Use approved `Verb-Noun` cmdlet names (PascalCase) with unambiguous nouns; avoid abbreviations. + - Parameters follow PascalCase; variables use PascalCase for module scope and camelCase for locals. + - Avoid aliases unless required for backward compatibility, and document any that remain. +- **Formatting & Encoding** + - Default to 4-space indentation, braces on the same line, and UTF-8 encoding (with BOM when Windows PowerShell 5.1 consumers demand it). + - Trim trailing whitespace and close every file with a single newline. +- **Function Structure** + - Start functions with `[CmdletBinding()]`, `[OutputType()]`, and comprehensive comment-based help. + - Use the template below as a reference implementation: + +```powershell +function Verb-Noun { + <#! + .SYNOPSIS + Brief description. + + .DESCRIPTION + Detailed description with context and usage. + + .PARAMETER Parameter + Parameter description with context and examples. + + .EXAMPLE + Verb-Noun -Parameter Value + + Description of what this example demonstrates. + + .OUTPUTS + [OutputType] - Description of output object properties. + + .NOTES + Additional context or usage notes. + #> + [OutputType([ExpectedType])] + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Parameter + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + } + + process { + try { + # Implementation + } catch { + Write-Debug "Error: $_" + throw + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +- **Parameter Design** + - Prefer `[switch]` for flags, leverage validation attributes, and support pipeline binding when natural. + - Minimize mandatory parameters when context detection can supply defaults. + - Example pattern: + +```powershell +function Set-ResourceConfiguration { + [CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory)] + [string]$Name, + [ValidateSet('Dev', 'Test', 'Prod')] + [string]$Environment = 'Dev', + [switch]$Force, + [ValidateNotNullOrEmpty()] + [string[]]$Tags + ) + process { } +} +``` +- **Pipeline & Output** + - Stream items through `process {}` without accumulating arrays; emit objects, not formatted strings. + - Provide `-PassThru` on action cmdlets when returning modified objects adds value. +- **Error Handling & Logging** + - Use `SupportsShouldProcess`/`ShouldProcess` for state changes and choose appropriate `ConfirmImpact`. + - Catch specific exceptions, rethrow with context, and leverage `Write-Error` for per-item failures. + - Integrate PSModule logging helpers (e.g., `Set-GitHubLogGroup`) when running in CI. +- **Help & Documentation** + - Include `.LINK` entries for external docs, ensure examples are runnable, and keep help synchronized with parameter definitions. +- **Cross-Platform & Performance** + - Use forward slashes in paths, test on Windows PowerShell 5.1 and PowerShell 7+, and minimize per-iteration allocations. + - Favor splatting and efficient cmdlets (`ForEach-Object -Parallel` only when concurrency justified). +- **Security** + - Handle secrets with `SecureString` or other secure abstractions, validate inputs thoroughly, and respect least privilege when calling external services. + +## Output Format +- PowerShell artifacts must import cleanly, expose accurate help via `Get-Help`, pass analyzer rules, and integrate with Pester suites defined for the module. +- Public functions should remain discoverable through module manifests and respect semantic versioning commitments. + +## Error Handling +- Treat analyzer violations, failed imports, or broken pipeline behavior as blocking; resolve or document remediation steps before merge. +- If legacy constraints require bending a rule, annotate the rationale with TODO/follow-up and capture it in repo instructions. + +## Definitions +| Term | Description | +| --- | --- | +| **Approved verbs** | List returned by `Get-Verb` denoting sanctioned PowerShell verb choices. | +| **Comment-based help** | Inline documentation that feeds `Get-Help`, covering synopsis, description, parameters, examples, outputs, and notes. | +| **SupportsShouldProcess** | CmdletBinding capability enabling `-WhatIf`/`-Confirm` support for potentially destructive operations. | +| **Splatting** | Technique of passing parameter dictionaries (`@{}`) to cmdlets for readability and reuse. | diff --git a/.github/instructions/instructions/framework/PowerShell/tests.instructions.md b/.github/instructions/instructions/framework/PowerShell/tests.instructions.md new file mode 100644 index 000000000..ca50e99c0 --- /dev/null +++ b/.github/instructions/instructions/framework/PowerShell/tests.instructions.md @@ -0,0 +1,81 @@ +--- +applyTo: '**/*.Tests.ps1,**/tests/**/*.ps1' +description: Framework-level PowerShell test patterns using Pester. +--- + +# Framework PowerShell Testing Guidelines + +Use this when writing Pester tests for PSModule modules—consistent structure keeps suites predictable and CI-friendly. + +## Goal +- Standardize test organization, naming, and setup so suites mirror production code structure. +- Ensure tests cover both happy paths and failure scenarios across local and GitHub Actions environments. + +## Execution Steps +1. Create or update a `.Tests.ps1` file that mirrors the source path (e.g., `src/functions/Public/Get-Item.ps1` → `tests/functions/Public/Get-Item.Tests.ps1`). +2. Scaffold `Describe`/`Context` blocks capturing functionality and scenario boundaries. +3. Implement Arrange-Act-Assert using the template below, adding setup/cleanup hooks as required. +4. Exercise different authentication or runtime contexts (local vs GitHub Actions) when relevant. +5. Run `Invoke-Pester` locally (and optionally in PowerShell 5.1 + 7+) to confirm the suite passes before committing. + +## Behavior Rules +- **Structure** + - Keep one primary `Describe` per function or feature and use `Context` blocks for scenario variations. + - Store test data at the top-level or within `BeforeAll`; clean up with `AfterAll/AfterEach`. +- **Template Reference** + - Base suites on this skeleton: + +```powershell +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param() + +BeforeDiscovery { + # Discovery-time setup (mock data, environment checks) +} + +Describe 'Module-Function Tests' { + BeforeAll { + $testData = @{ + Parameter1 = 'TestValue' + Parameter2 = 42 + } + } + + Context 'When function is called with valid parameters' { + It 'Should return expected result' { + $result = Module-Function -Parameter $testData.Parameter1 + $result | Should -Be 'Expected' + } + } + + Context 'When function is called with invalid parameters' { + It 'Should throw appropriate error' { + { Module-Function -Parameter $null } | Should -Throw + } + } +} +``` +- **Assertions** + - Use precise assertions (`Should -Be`, `-Match`, `-Throw`), covering positive, negative, and pipeline behaviors. + - Validate error messages or types when they matter for consumers. +- **Context Awareness** + - Simulate GitHub Actions runs (matrix combinations, environment variables, secrets) as part of tests when feasible. + - Use `Set-GitHubLogGroup` or equivalent logging helpers to keep CI output structured. +- **Performance & Integration** + - Benchmark critical paths (e.g., repeated API calls) and include smoke tests for module import/export. + - Exercise cross-function flows and real API interactions in dedicated integration suites, gating them with tags if slow. + +## Output Format +- Test suites must pass under `Invoke-Pester`, generate NUnit/XML reports when CI expects them, and respect naming conventions so dashboards categorize results correctly. +- Document required environment variables or secrets in accompanying README/test docs. + +## Error Handling +- Treat failing assertions, leaked resources, or missing cleanup as blockers; fix before merge. +- If a scenario cannot be automated, document manual validation steps and track automation backlog items. + +## Definitions +| Term | Description | +| --- | --- | +| **Arrange-Act-Assert** | Canonical testing pattern for structuring setup, execution, and verification. | +| **BeforeDiscovery** | Pester hook executed during discovery phase, useful for locating data or skipping suites. | +| **Tagging** | Mechanism to categorize tests (e.g., `-Tag Integration`) for selective execution. | diff --git a/.github/instructions/instructions/framework/XML/main.instructions.md b/.github/instructions/instructions/framework/XML/main.instructions.md new file mode 100644 index 000000000..c26f97514 --- /dev/null +++ b/.github/instructions/instructions/framework/XML/main.instructions.md @@ -0,0 +1,54 @@ +--- +applyTo: '**/*.xml' +description: Framework-level XML patterns for configuration payloads and test outputs. +--- + +# Framework XML Guidelines + +XML remains common for tool configuration and reporting in PSModule repos; these rules ensure the documents remain parseable and cross-platform. + +## Goal +- Provide consistent encoding, structure, and schema practices for XML artifacts. +- Keep CI/CD tooling (test runners, coverage reports, deployment manifests) interoperable across environments. + +## Execution Steps +1. Start each file with an XML declaration specifying UTF-8 encoding and ensure LF line endings. +2. Organize elements under a single root, following schema or tool conventions for ordering and naming. +3. Apply consistent indentation (2 spaces recommended) and remove trailing whitespace before saving. +4. Validate the document against its schema or DTD where available; capture validation steps in comments or automation. +5. Review namespaces, links, and file paths to confirm they behave on both Windows and Unix runners. + +## Behavior Rules +- **Formatting** + - Use UTF-8 encoding, include ``, and end the file with a single newline. + - Maintain two-space indentation (unless a consuming tool enforces four) and keep lines ≤ 150 characters. +- **Structure** + - Choose meaningful element/attribute names, maintain consistent casing, and avoid unnecessary nesting. + - For configuration files, place metadata (version, description) near the top and group functional sections logically. +- **Test & Report Files** + - Follow standard formats (JUnit, JaCoCo, Pester NUnit) including timestamps, durations, and failure details. + - Preserve ordering of suites/cases when consumers rely on deterministic layouts. +- **Configuration Files** + - Comment complex sections, provide sensible defaults, and validate with schemas or XSDs when supplied. + - Keep cross-platform paths normalized (forward slashes) and document platform-specific requirements. +- **Security** + - Disable or avoid external entities (XXE) unless explicitly required and vetted. + - Escape special characters properly and sanitize untrusted input before serialization. +- **Integration** + - Ensure files load in standard processors (System.Xml, xmllint) and surface informative errors if parsing fails. + - Maintain backward compatibility or note breaking changes in release notes when altering structure. + +## Output Format +- XML artifacts should validate successfully, open in standard viewers without warnings, and align with the consuming tool's expectations (e.g., GitHub integration, Azure DevOps reports). +- Test outputs must include full failure context (message, stack trace) for troubleshooting. + +## Error Handling +- Treat validation or parsing failures as blockers; fix structure or schema references before merge. +- Document exceptions (e.g., third-party schema bugs) in repo instructions and plan remediation. + +## Definitions +| Term | Description | +| --- | --- | +| **XML declaration** | First line specifying version and encoding (e.g., ``). | +| **XXE** | XML External Entity processing; potential security vulnerability when untrusted entities are loaded. | +| **JUnit format** | Widely used XML schema for test results consumed by CI dashboards. | diff --git a/.github/instructions/instructions/framework/YAML.instructions.md b/.github/instructions/instructions/framework/YAML.instructions.md new file mode 100644 index 000000000..b29011d64 --- /dev/null +++ b/.github/instructions/instructions/framework/YAML.instructions.md @@ -0,0 +1,62 @@ +--- +applyTo: '**/*.{yml,yaml}' +description: Framework-level YAML patterns covering workflows, actions, and shared configuration. +--- + +# Framework YAML Guidelines + +Every PSModule YAML file—from GitHub workflows to documentation config—must remain readable, lintable, and secure across repositories. + +## Goal +- Define universal YAML formatting, naming, and validation practices that keep automation predictable and diff-friendly. +- Outline GitHub Actions specific patterns so reusable actions and workflows share a common contract. + +## Execution Steps +1. Confirm the file name follows lowercase-hyphenated conventions (e.g., `ci-build.yml`). +2. Apply formatting rules (UTF-8 + LF, two-space indent, single trailing newline) and organise sections logically. +3. Validate schema compatibility (yaml-language-server directive or dedicated validator) and resolve warnings. +4. For Actions/Workflows, ensure inputs, outputs, permissions, and branding align with PSModule expectations. +5. Re-run linting or workflow dry-runs after modifications to guarantee correctness. + +## Behavior Rules +- **Formatting & Structure** + - Use UTF-8 with LF endings, no tabs, two-space indentation, and keep lines ≤ 150 characters. + - Group related keys, separate sections with a single blank line, and maintain metadata keys (`name`, `description`, etc.) first. +- **Naming & Organization** + - Choose descriptive file names (`deployment-config.yml`, `mkdocs.yml`) and avoid generic labels. + - Keep nesting shallow when possible; prefer flattened keys for clarity. +- **Scalars & Quoting** + - Use plain scalars when safe and quote values that could be misinterpreted (`on`, `yes`, `007`, values starting with special characters). + - Prefer double quotes; leverage `|` and `>` block styles intentionally to control wrapping. +- **Lists & Sequences** + - Align dashes with the parent key indentation; use block lists for multi-line content and concise inline lists only for short sequences (< 80 chars). +- **Schemas & Validation** + - Include `# yaml-language-server` schema hints when supported (GitHub workflow, Dependabot, etc.). + - Treat schema or linter errors as blockers—update content or schema references instead of suppressing. +- **Comments** + - Start comments with a space (`# Explanation`), keep them current, and format TODOs as `# TODO(owner): summary`. +- **Security & Secrets** + - Never commit actual secrets; reference them via `${{ secrets.NAME }}` or other vault abstractions. + - Redact examples with placeholders and pin external actions to specific versions. +- **GitHub Actions Patterns** + - Supply metadata (name, description, author, branding) for composite actions and define explicit input/output contracts. + - Structure repositories with `scripts/` and `tests/` folders, provide CI logging (`pwsh` with `Set-GitHubLogGroup`), and support Windows/Linux/macOS runners. +- **Workflows & Config** + - Use descriptive workflow/job names, matrix strategies for multi-environment coverage, artifacts for cross-job data, and caching where beneficial. + - Document options in accompanying README files and keep PSModule helper integrations consistent. + +## Output Format +- YAML artifacts should lint cleanly, load under the documented schema, and, for workflows/actions, execute successfully in a dry run or targeted test. +- Action READMEs and repository docs must describe inputs/outputs exactly as defined in YAML. + +## Error Handling +- If a conflicting third-party requirement forces deviation (e.g., different indentation), capture the reason in repo instructions and cite upstream constraints in comments. +- Fail fast on schema validation, permission mismatches, or unpinned external dependencies. + +## Definitions +| Term | Description | +| --- | --- | +| **Composite action** | GitHub Action defined in YAML that orchestrates steps (often PowerShell) for reuse across repositories. | +| **Schema hint** | `yaml-language-server` directive that points editors to JSON schemas for validation and IntelliSense. | +| **Matrix strategy** | GitHub Actions feature allowing jobs to run across permutations of inputs (OS, PowerShell version, etc.). | +| **Pinned action** | External action referenced with a full version (`@v1.2.3` or commit SHA) to prevent supply-chain drift. | diff --git a/.github/instructions/instructions/framework/YAML/actions.instructions.md b/.github/instructions/instructions/framework/YAML/actions.instructions.md new file mode 100644 index 000000000..e05c99833 --- /dev/null +++ b/.github/instructions/instructions/framework/YAML/actions.instructions.md @@ -0,0 +1,87 @@ +--- +applyTo: + - '**/action.yml' + - '**/action.yaml' +description: Framework-level GitHub Actions definition patterns for PSModule composite actions. +--- + +# Framework GitHub Actions Guidelines + +Composite actions standardize how PSModule scripts are packaged for reuse; this guidance keeps inputs, outputs, and branding consistent. + +## Goal +- Provide a reusable action scaffold that integrates seamlessly with PSModule helpers and CI pipelines. +- Enforce naming, branding, and security patterns so actions remain trustworthy across repositories. + +## Execution Steps +1. Start from the PSModule composite template below and adjust metadata (name, description, icon) to match the action’s purpose. +2. Define inputs/outputs with descriptive names, defaults, and documentation; ensure they map cleanly to environment variables. +3. Reference `PSModule/Install-PSModuleHelpers` (pinned version) before invoking repository scripts. +4. Add simulation steps or tests that verify the action works on Windows, Linux, and macOS runners. +5. Update accompanying README files with the same inputs/outputs and usage examples. + +## Behavior Rules +- **Template Reference** + +```yaml +name: ActionName (by PSModule) +description: Clear summary of the action’s purpose. +author: PSModule +branding: + icon: package + color: gray-dark + +inputs: + Name: + description: Name of the resource to process. + required: false + WorkingDirectory: + description: Directory in which the script runs. + required: false + default: '.' + +outputs: + OutputName: + description: Description of the output. + +runs: + using: composite + steps: + - name: Install PSModule helpers + uses: PSModule/Install-PSModuleHelpers@v1 + + - name: Execute action + shell: pwsh + working-directory: ${{ inputs.WorkingDirectory }} + env: + PSMODULE_ACTIONNAME_INPUT_Name: ${{ inputs.Name }} + run: | + & "${{ github.action_path }}/scripts/main.ps1" +``` +- **Inputs & Outputs** + - Use PascalCase input keys, provide defaults when safe, and mark required inputs sparingly. + - Document outputs with structure details (type, example values) and consume `::set-output` alternatives (environment files) as required by GitHub updates. +- **Environment Variable Mapping** + - Map each input to an environment variable named `PSMODULE__INPUT_` to keep scripts consistent. +- **Branding & Naming** + - Append “(by PSModule)” to action names, use `gray-dark` branding, and pick Feather icons matching purpose. +- **Dependencies & Scripts** + - Pin helper and third-party actions to explicit versions; call scripts via `${{ github.action_path }}` to support relative resources. +- **Security** + - Never embed secrets; expect callers to pass them via `${{ secrets.NAME }}` and validate inputs. + - Restrict permissions in the consuming workflow (document recommended settings). + +## Output Format +- Action metadata must parse via `act`/GitHub validators, include README documentation, and provide working sample workflows. +- Steps should emit structured logs suitable for CI grouping and set outputs using the GitHub environment file mechanism. + +## Error Handling +- Fail the action with clear messages when required inputs are missing or operations error; bubble PowerShell exceptions with context. +- Document known limitations or platform restrictions in the README and TODO comments. + +## Definitions +| Term | Description | +| --- | --- | +| **Composite action** | YAML-defined action bundling multiple steps for reuse. | +| **Environment file** | GitHub Actions mechanism for setting outputs, env vars, and path modifications (`$GITHUB_OUTPUT`, `$GITHUB_ENV`). | +| **Feather icon** | Icon set used for GitHub Action branding (`branding.icon`). | diff --git a/.github/instructions/instructions/framework/YAML/main.instructions.md b/.github/instructions/instructions/framework/YAML/main.instructions.md new file mode 100644 index 000000000..754079977 --- /dev/null +++ b/.github/instructions/instructions/framework/YAML/main.instructions.md @@ -0,0 +1,52 @@ +--- +applyTo: + - '**/*.yml' + - '**/*.yaml' +description: Framework-level YAML practices for shared configuration components. +--- + +# Framework YAML Guidelines + +This file complements the top-level YAML guidance with patterns for generic configuration files that aren’t specific to workflows or composite actions. + +## Goal +- Keep YAML configuration readable, validated, and modular across PSModule repositories. +- Ensure environmental integration (local + CI) without duplicating workflow-specific rules. + +## Execution Steps +1. Name the file with lowercase hyphenated words reflecting purpose (`mkdocs.yml`, `settings-release.yml`). +2. Format with UTF-8, LF endings, two-space indentation, and a single trailing newline. +3. Organize logical sections with metadata first, then functional blocks separated by a single blank line. +4. Insert schema hints or validation commands if the consuming tool supports them and run the validation. +5. Update accompanying documentation or comments to explain complex settings. + +## Behavior Rules +- **Structure & Formatting** + - Group related keys, avoid excessive nesting, and keep key names descriptive but succinct. + - Maintain deterministic key ordering to reduce diff churn. +- **Comments & Documentation** + - Use comments sparingly for non-obvious behavior; format TODOs as `# TODO(owner): summary`. + - Document default values and overrides near their definitions. +- **Environment Integration** + - Leverage environment variables or templating features for values that differ between environments. + - Validate configuration under Windows, Linux, and macOS contexts when applicable. +- **Versioning & Compatibility** + - Track schema versions or tool version compatibility; note breaking changes in release notes. + - Prefer additive changes over destructive reorganizations to preserve backward compatibility. +- **Security** + - Keep secrets out of configuration files; reference secure stores or placeholder tokens. + - Validate external inputs before interpolation to prevent injection. + +## Output Format +- Configuration YAML must parse without warnings in the target tool, and any generated artifacts (site configs, templates) should remain functional. +- Documented defaults and overrides should stay synchronized with README or docs references. + +## Error Handling +- Treat schema validation failures or parse exceptions as blockers—resolve before delivery. +- If a third-party tool enforces conflicting formatting, document the exception and scope it narrowly. + +## Definitions +| Term | Description | +| --- | --- | +| **Deterministic ordering** | Keeping keys in a consistent order for predictable diffs and tooling consumption. | +| **Schema hint** | Metadata (comment or key) pointing a YAML file to a validation schema. | diff --git a/.github/instructions/instructions/framework/YAML/workflows.instructions.md b/.github/instructions/instructions/framework/YAML/workflows.instructions.md new file mode 100644 index 000000000..765709a16 --- /dev/null +++ b/.github/instructions/instructions/framework/YAML/workflows.instructions.md @@ -0,0 +1,87 @@ +--- +applyTo: + - '**/.github/workflows/*.yml' + - '**/.github/workflows/*.yaml' +description: Framework-level GitHub workflow patterns for CI/CD. +--- + +# Framework GitHub Workflows Guidelines + +Workflows orchestrate PSModule automation; keeping them consistent ensures reliable CI/CD across repositories. + +## Goal +- Provide a canonical workflow skeleton, key ordering, and permission strategy. +- Balance robustness, security, and performance for GitHub-hosted and self-hosted runners. + +## Execution Steps +1. Base new workflows on the template below, adjusting triggers, permissions, and concurrency to fit the scenario. +2. Define jobs with descriptive names, `needs` dependencies, and matrix strategies where cross-platform coverage is required. +3. Use pinned action versions, PSModule helpers, and scripts stored in `.github/scripts` for multi-line logic. +4. Tune caching, artifacts, and secrets management for the workload. +5. Test via `act` or selective dispatch events before merging, monitoring logs for unexpected warnings. + +## Behavior Rules +- **Template Reference** + +```yaml +name: Workflow Name +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + job-name: + name: Job Display Name + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PowerShell + uses: PSModule/Setup-PowerShell@v1 + + - name: Execute Action + uses: PSModule/ActionName@v1 +``` +- **Key Ordering & Structure** + - Order top-level keys as `name`, `on`, `permissions`, `env`, `defaults`, `concurrency`, `jobs`; separate sections with single blank lines. +- **Permissions & Security** + - Grant least privilege (read-only by default); escalate at job level when necessary and document justification. + - Never output secrets; rely on `${{ secrets.NAME }}` or OIDC tokens for external auth. +- **Scripts & Steps** + - Keep inline scripts short; move longer logic to `.github/scripts` and reference with `run: pwsh` and path. + - Pin all third-party actions to versions/tags (avoid `@main`). +- **Job Design** + - Use `needs` for ordering, matrices for OS/PowerShell coverage, and descriptive `name` values for readability. + - Incorporate caching (PowerShell modules, npm/pip dependencies) and artifact handling with explicit retention. +- **Error Handling & Resilience** + - Avoid `continue-on-error` except for non-blocking tasks; include cleanup steps and actionable failure messages. +- **Performance** + - Cancel superseded runs via concurrency, minimize redundant jobs, and leverage parallelization judiciously to control usage. +- **PSModule Integration** + - Use PSModule helper actions consistently, align logging (`Set-GitHubLogGroup`), and ensure modules import with required versions. + +## Output Format +- Workflows must pass YAML validation, run successfully on target runners, and emit structured logs/artifacts consumable by maintainers. +- Document triggers, environment assumptions, and required secrets in repository README or `.github/workflows/README.md`. + +## Error Handling +- Treat failed steps as blocking unless explicitly non-critical; surface failure causes clearly (e.g., `Write-Error` with guidance in PowerShell steps). +- For flaky upstream services, add retries with exponential backoff and log references to tracking issues. + +## Definitions +| Term | Description | +| --- | --- | +| **Concurrency group** | GitHub Actions feature preventing overlapping runs based on workflow/ref keys. | +| **Matrix** | Job expansion technique for running the same steps across environments. | +| **OIDC token** | OpenID Connect token issued by GitHub for requesting cloud credentials without stored secrets. | diff --git a/.github/instructions/instructions/framework/main.instructions.md b/.github/instructions/instructions/framework/main.instructions.md new file mode 100644 index 000000000..71866f583 --- /dev/null +++ b/.github/instructions/instructions/framework/main.instructions.md @@ -0,0 +1,65 @@ +--- +applyTo: '**/*' +description: Universal cross-language guidelines for PSModule framework development. +--- + +# PSModule Framework Guidelines + +Universal patterns and conventions that apply across all programming languages and file types within the PSModule ecosystem. + +## Goal +- Establish consistent development patterns across all PSModule repositories +- Provide foundational guidelines that language-specific instructions build upon +- Ensure compatibility with PSModule build processes, tooling, and ecosystem integration + +## Execution Steps +1. Review target file type and apply language-specific instructions in addition to these universal guidelines +2. Follow PSModule naming conventions and file organization patterns +3. Integrate with established build processes, authentication patterns, and logging standards +4. Validate changes against both local tooling and CI automation +5. Document new patterns for potential promotion to framework-level guidance + +## Behavior Rules +- **Naming Conventions** + - Use PascalCase for public functions, types, and exported members + - Use kebab-case for file names and directory structures + - Use descriptive, intention-revealing names that align with domain terminology +- **File Organization** + - Maintain consistent directory structures across repositories + - Separate public/private components clearly + - Group related functionality logically +- **Documentation Standards** + - All public APIs must have complete documentation + - Include examples that demonstrate real-world usage patterns + - Link to related documentation and external references +- **Build Integration** + - Support cross-platform compatibility (Windows, Linux, macOS) + - Integrate with existing PSModule build and deployment pipelines + - Ensure compatibility with automated testing and validation processes +- **Authentication & Security** + - Use PSModule authentication abstractions for external service integration + - Follow principle of least privilege for permissions and access + - Implement proper error handling for authentication failures +- **Logging & Observability** + - Use structured logging with LogGroup for consistency + - Include appropriate level of detail for debugging and monitoring + - Support pipeline-friendly output formats + +## Output Format +- All artifacts must be compatible with PSModule tooling and build processes +- Follow established patterns for the target file type +- Maintain consistency with existing repository structure and conventions + +## Error Handling +- Provide clear, actionable error messages +- Include context for debugging and resolution +- Follow established patterns for error handling in the target technology +- Document known issues and workarounds + +## Definitions +| Term | Description | +| --- | --- | +| **PSModule ecosystem** | Collection of PowerShell modules, documentation, and tooling following consistent patterns | +| **LogGroup** | Structured logging system used across PSModule repositories | +| **Cross-platform compatibility** | Support for Windows, Linux, and macOS operating systems | +| **Authentication abstractions** | Standardized patterns for handling external service authentication | diff --git a/.github/instructions/instructions/framework/universal.instructions.md b/.github/instructions/instructions/framework/universal.instructions.md new file mode 100644 index 000000000..50f08cf7f --- /dev/null +++ b/.github/instructions/instructions/framework/universal.instructions.md @@ -0,0 +1,57 @@ +--- +applyTo: '**/*' +description: Universal framework patterns for every PSModule repository. +--- + +# PSModule Framework Guidelines + +These cross-language expectations keep every PSModule component consistent, pipeline-friendly, and ready for automation across local and CI environments. + +## Goal +- Provide a single source of truth for ecosystem-wide authoring, organization, and integration requirements. +- Ensure code, docs, and automation remain portable between Windows PowerShell 5.1, PowerShell 7+, and GitHub Actions runners. + +## Execution Steps +1. Map the change you are making to the relevant PSModule component (module code, docs, automation, etc.). +2. Review the framework behavior rules below and identify which ones influence your task. +3. Apply those rules while editing, validating encoding/line-endings and structural conventions before finalizing changes. +4. Confirm your update keeps PSModule tooling (Pester, MkDocs, GitHub Actions) functional by running targeted validations. +5. Document any notable deviations so repository instructions can extend or override these defaults if required. + +## Behavior Rules +- **Architecture & Convention** + - Favor modular design with single-responsibility components and semantic folders (`src/`, `tests/`, `docs/`). + - Prefer convention over configuration to minimize bespoke setup. + - Keep all deliverables cross-platform and context-aware; auto-detect CI/CD environments when possible. +- **Authoring Standards** + - Encode files as UTF-8 with LF endings, no BOM unless a language-specific rule demands it. + - Remove trailing whitespace, keep line length ≤ 150 characters, and end files with a single newline. + - Write in English and use forward slashes (`/`) for paths. +- **Workflow Integration** + - Develop and test PowerShell modules with `Import-Module ... -Force` and validate via `Invoke-Pester`. + - Maintain semantic versioning and automated release flows through GitHub Actions. + - Provide current documentation and ensure examples execute as written. +- **Error Handling & Resilience** + - Prefer attribute-based validation (for example `[ValidateNotNullOrEmpty()]`) over manual checks. + - Emit actionable error messages that link to resolution guidance when available. + - Use standard PowerShell error surfaces (`throw`, `Write-Error`) while preserving pipeline semantics. +- **Ecosystem Integration** + - Honour PSModule authentication abstractions and shared environment variables (`GITHUB_*`, `PSMODULE_*`). + - Produce logs compatible with CI viewers (e.g., `LogGroup` sections) and consider cross-repo dependencies. + +## Output Format +- Deliverables must retain the repository's semantic structure, respect encoding/line-ending rules, and include runnable examples or tests where prescribed. +- Documentation updates should remain MkDocs-compatible with intact navigation metadata. + +## Error Handling +- When a rule cannot be satisfied (e.g., tooling limitation), document the deviation in repo-specific instructions and surface a warning in pull-request notes. +- Fail fast on structural violations (incorrect encoding, missing newline, or inconsistent naming) and correct them before submission. + +## Definitions +| Term | Description | +| --- | --- | +| **PSModule ecosystem** | Collection of interoperable PowerShell modules, docs, and automation maintained under the PSModule banner. | +| **Pipeline-friendly** | Design that supports streaming input/output via the PowerShell pipeline without forcing array materialization. | +| **Context awareness** | Ability to detect execution environment (local PowerShell, GitHub Actions, enterprise runners) and adjust behavior automatically. | +| **LogGroup** | Structured logging helper used to create collapsible sections in CI/CD output. | +| **Semantic folders** | Conventional top-level directories (`src`, `tests`, `docs`, etc.) shared across PSModule repositories. | diff --git a/.github/instructions/instructions/instructions/repo/PowerShell/main.instructions.md b/.github/instructions/instructions/instructions/repo/PowerShell/main.instructions.md new file mode 100644 index 000000000..25da6b551 --- /dev/null +++ b/.github/instructions/instructions/instructions/repo/PowerShell/main.instructions.md @@ -0,0 +1,83 @@ +--- +applyTo: "**/*.{ps1,psm1,psd1}" +description: GitHub module PowerShell specific patterns and conventions. +--- + +# GitHub Module PowerShell Instructions + +## Function Structure Pattern +All public functions in this module follow this specific pattern: + +### Authentication Context Pattern +```powershell +function Get-GitHubExample { + [CmdletBinding()] + param( + [Parameter()] + [object] $Context + ) + + begin { + # Always start public functions with context resolution and validation + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context + + # Include permission requirements comment + # Requires permissions: repository:read + } + + process { + # Implementation with consistent API call pattern + $splat = @{ + Method = 'GET' + APIEndpoint = '/repos/{owner}/{repo}/example' + Body = $Body + Context = $Context + } + Invoke-GitHubAPI @splat | ForEach-Object { Write-Output $_.Response } + } +} +``` + +## Parameter Conventions +- Use PascalCase for all parameters +- Always include `[Parameter()]` attribute (even if empty) +- Use `[ValidateNotNullOrEmpty()]` instead of string null checks +- Owner parameters: `[Alias('User','Organization')]` +- Repository parameters: `[Alias('Repo')]` if needed +- Context parameter: `[object] $Context` for all public functions + +## Authentication Context +- **Public functions**: Handle context resolution and validation with `Resolve-GitHubContext` and `Assert-GitHubContext` +- **Private functions**: Expect resolved `[GitHubContext] $Context` parameter +- Support all auth types: PAT, UAT, IAT, GitHub App JWT + +## API Call Patterns +- Always splat API calls with consistent parameter order: Method, APIEndpoint, Body, Context +- Use PascalCase for HTTP methods: `Get`, `Post`, `Put`, `Delete`, `Patch` +- Handle pipeline output with `ForEach-Object { Write-Output $_.Response }` +- Include permission requirements in function begin block + +## Naming Standards +- **Public functions**: `Verb-GitHubNoun` (e.g., `Get-GitHubRepository`) with aliases where appropriate +- **Private functions**: Same pattern but NO aliases +- **Classes**: `GitHubObjectType` (e.g., `GitHubRepository`) +- Object-oriented parameter naming (don't repeat function context in parameter names) + +## Error Handling & Validation +- Use `[ValidateNotNullOrEmpty()]` instead of string null checks +- Throw meaningful errors when required parameters missing from context +- Follow PowerShell error handling patterns +- Provide actionable error messages that guide users to solutions +- Include relevant GitHub API documentation links in error messages + +## GitHub Actions Integration +- Always consider whether code is running in GitHub Actions environment +- Respect existing authentication context before establishing new connections +- Support environment variable-based configuration +- Handle both local development and CI/CD scenarios + +## Reference Implementation +- See `src/functions/public/Auth/Connect-GitHubAccount.ps1` for authentication patterns +- See `src/functions/public/Repositories/` for standard function structure +- Follow patterns documented in `CodingStandard.md` diff --git a/.github/instructions/instructions/instructions/repo/PowerShell/xml.instructions.md b/.github/instructions/instructions/instructions/repo/PowerShell/xml.instructions.md new file mode 100644 index 000000000..f034c2991 --- /dev/null +++ b/.github/instructions/instructions/instructions/repo/PowerShell/xml.instructions.md @@ -0,0 +1,54 @@ +--- +applyTo: "**/*.{xml,ps1xml}" +--- + +# PowerShell XML Configuration Guidelines + +## Format Definition Files +- Use proper PowerShell formatting XML schema +- Define table views with appropriate column widths +- Consider terminal display constraints +- Include meaningful headers and labels + +## Type Definition Files +- Define property aliases for common alternate names +- Add script properties for calculated values +- Maintain consistency with class definitions +- Use proper PowerShell types XML schema + +## Common Patterns +```xml + + + + GitHubRepository-Table + + GitHubRepository + + + + + + 25 + + + + + + + Name + + + + + + + + +``` + +## File Organization +- One XML file per GitHub object type +- Match file names to corresponding classes +- Place format files in `src/formats/` +- Place type files in `src/types/` \ No newline at end of file diff --git a/.github/instructions/instructions/instructions/repo/main.instructions.md b/.github/instructions/instructions/instructions/repo/main.instructions.md new file mode 100644 index 000000000..72b6d2176 --- /dev/null +++ b/.github/instructions/instructions/instructions/repo/main.instructions.md @@ -0,0 +1,71 @@ +--- +applyTo: "**/*" +description: GitHub PowerShell Module specific patterns and conventions. +--- + +# GitHub Module Repository Instructions + +> **Context**: This is the GitHub PowerShell module repository - a comprehensive PowerShell wrapper for the GitHub API. + +## Repository Architecture + +### Module Structure +- `src/functions/public/` - User-facing cmdlets organized by GitHub object type (Repositories, Issues, etc.) +- `src/functions/private/` - Internal helper functions +- `src/classes/public/` - PowerShell classes for GitHub objects (GitHubRepository, GitHubUser, etc.) +- `src/formats/` - Custom formatting for display output +- `src/types/` - Type extensions and aliases + +### Core Design Patterns +- **Object-oriented organization**: Functions grouped by GitHub entity type, not API endpoints +- **Context-aware**: Automatically detects GitHub Actions environment and loads context +- **Pipeline-friendly**: All public functions support pipeline input where appropriate +- **Authentication abstraction**: Single connection model supporting multiple auth types + +## Development Workflows + +### Function Creation +Use the utility script to scaffold new functions: +```powershell +.\tools\utilities\New-Function.ps1 -Path "Repositories" -Method "GET" +``` + +### Local Development +```powershell +# Import module for testing +Import-Module ./src/GitHub.psm1 -Force + +# Connect and test +Connect-GitHubAccount +Get-GitHubRepository -Owner PSModule -Name GitHub +``` + +### Testing +```powershell +# Run all tests +pwsh -Command "Invoke-Pester" + +# Run specific test suite +pwsh -Command "Invoke-Pester -Path tests/Repositories.Tests.ps1" +``` + +## File Organization Rules +- Group by GitHub object type (Repository, Issue, etc.), not by API operation +- One API endpoint = one function +- Public functions in `public/ObjectType/`, private in `private/ObjectType/` +- Classes mirror the object hierarchy from GitHub API + +## Key Integration Points +- **GitHub Actions**: Auto-detects runner environment, imports event data +- **Azure Key Vault**: Supports GitHub App key storage in Key Vault +- **PSModule Framework**: Follows PSModule.io conventions and build system +- **Pester Testing**: Comprehensive test suite with auth context scenarios + +## Module Capabilities +This module serves as both a local automation tool and a GitHub Actions companion, with special handling for CI/CD scenarios and enterprise environments. + +### Supported Features +- Multiple authentication methods (PATs, GitHub Apps, OAuth, device flow) +- GitHub Actions context awareness +- Enterprise GitHub (GHES, GHEC) support +- Pipeline-friendly cmdlets with standard PowerShell conventions diff --git a/.github/instructions/instructions/repo/Markdown/main.instructions.md b/.github/instructions/instructions/repo/Markdown/main.instructions.md new file mode 100644 index 000000000..ea0e9beb6 --- /dev/null +++ b/.github/instructions/instructions/repo/Markdown/main.instructions.md @@ -0,0 +1,51 @@ +--- +applyTo: '**/*.md' +description: Markdown standards specific to the PSModule documentation site and sample content. +--- + +# Repository Markdown Guidelines + +These rules extend the framework Markdown guidance with MkDocs-specific navigation, PSModule voice, and example requirements. + +## Goal +- Keep documentation pages, blog posts, and READMEs consistent with PSModule brand and site structure. +- Ensure examples remain executable against current modules and workflows. + +## Execution Steps +1. Confirm the document’s placement in the MkDocs navigation and update `mkdocs.yml` if structure changes. +2. Start the page with a descriptive H1, then follow the heading hierarchy established for the site section. +3. Embed runnable examples (PowerShell or YAML) using fenced code blocks with language annotations. +4. Add cross-links to relevant PSModule modules, actions, or dictionary entries using relative references defined in `includes/links.md`. +5. Run `mkdocs serve` (or build) locally to verify navigation, formatting, and link integrity. + +## Behavior Rules +- **Document Structure** + - Provide a table of contents when sections exceed three H2 headings. + - Close with related links, references, or next steps to support discoverability. +- **README Standards** + - Summarize purpose, installation, usage, contribution, and license; keep them aligned with module repositories. +- **Code Blocks & Samples** + - Use ```powershell, ```yaml, or appropriate language hints; show inputs and expected outputs when relevant. + - Prefer examples that authenticate via PSModule helpers or demonstrate GitHub Actions integration. +- **Linking** + - Use descriptive link text, relative paths for internal content, and shared link definitions from `includes/links.md` when possible. +- **Formatting** + - Bold key terms sparingly, use inline code for commands/filenames, blockquotes for callouts, and tables for structured data. +- **Integration** + - Align content with MkDocs metadata/frontmatter conventions and ensure navigation breadcrumbs remain accurate. +- **Examples & Scenarios** + - Cover common use cases, include error-handling guidance, and highlight PSModule best practices. + +## Output Format +- Markdown pages must render cleanly in MkDocs, appear in navigation, and reference any updated assets (images, includes). +- Example code should be copy/paste ready and validated against current module versions. + +## Error Handling +- Treat broken links, missing includes, or failing MkDocs builds as blocking issues. +- Document temporary discrepancies (e.g., pending module release) with visible callouts and follow-up tasks. + +## Definitions +| Term | Description | +| --- | --- | +| **MkDocs navigation** | Menu structure defined in `mkdocs.yml` that controls page placement. | +| **Dictionary entries** | Shared glossary pages under `docs/Dictionary/` referenced throughout the site. | diff --git a/.github/instructions/instructions/repo/PowerShell/classes.instructions.md b/.github/instructions/instructions/repo/PowerShell/classes.instructions.md new file mode 100644 index 000000000..0941df40f --- /dev/null +++ b/.github/instructions/instructions/repo/PowerShell/classes.instructions.md @@ -0,0 +1,52 @@ +--- +applyTo: '**/classes/**/*.ps1' +description: Repository-specific PowerShell class patterns for GitHub objects. +--- + +# GitHub Module Class Guidelines + +## GitHub Object Class Structure +- One class per file, named after the GitHub resource type +- Use PascalCase for all public properties and methods +- Extend `GitHubNode` for objects with database/node IDs from GitHub API + +## GitHub-Specific Property Standards +- `ID` property for GitHub database ID (aliased to `DatabaseID` via types) +- `NodeID` property for GitHub GraphQL node ID +- Size properties in bytes, named `Size` (GitHub API standard) +- Include GitHub scope properties (Enterprise, Owner, Repository, etc.) + +## GitHub Class Aliases +- Add class name as alias property pointing to primary identifier +- Example: `GitHubRepository` has `Repository` property pointing to `Name` +- Use types files (`.Types.ps1xml`) for property aliases matching GitHub API + +## GitHub Object Documentation +- Include comprehensive property descriptions matching GitHub API documentation +- Document expected value formats and examples from GitHub API responses +- Reference official GitHub API documentation for complex properties +- Include permission requirements and availability information + +## GitHub API Integration +- Properties should match GitHub API response field names where possible +- Handle GitHub API timestamp formats and convert to PowerShell DateTime +- Support both REST API and GraphQL API response patterns +- Include calculated properties for derived GitHub data + +## Inheritance Hierarchy for GitHub Objects +- Use `GitHubNode` base class for objects with ID and URL properties +- Implement common GitHub interfaces consistently across related classes +- Override methods only when necessary for GitHub-specific behavior +- Maintain serialization compatibility with GitHub API responses + +## JSON Integration for GitHub API +- Ensure classes serialize/deserialize correctly with GitHub API responses +- Use appropriate property names that match GitHub API field names +- Handle null values gracefully during GitHub API deserialization +- Support round-trip serialization for GitHub API data + +## Reference Implementation Files +- See `src/classes/public/GitHubNode.ps1` for base class pattern +- See `src/classes/public/Repositories/` for GitHub repository object examples +- Check `src/types/` for GitHub-specific property alias patterns +- Reference GitHub API documentation for object structure diff --git a/.github/instructions/instructions/repo/PowerShell/formats.instructions.md b/.github/instructions/instructions/repo/PowerShell/formats.instructions.md new file mode 100644 index 000000000..fa594b3c5 --- /dev/null +++ b/.github/instructions/instructions/repo/PowerShell/formats.instructions.md @@ -0,0 +1,47 @@ +--- +applyTo: '**/src/formats/**/*.Format.ps1xml' +description: PowerShell formatting files for custom display of objects. +--- + +# PowerShell Format Guidelines + +## File Structure +- XML format files define how objects appear in console output +- Name format: `ObjectType.Format.ps1xml` (e.g., `GitHubRepository.Format.ps1xml`) +- Include both table and list views where appropriate + +## Table Views +- Use descriptive column headers with `