|
| 1 | +# Coding Standards for `GitHub` |
| 2 | + |
| 3 | +Start by reading the general coding standards for [`PSModule`](https://psmodule.io/docs) which is the basis for all modules in the framework. |
| 4 | +Additions or adjustments to those defaults are covered in this document to ensure that the modules drive consistancy for all developers. |
| 5 | + |
| 6 | +## General Coding Standards |
| 7 | + |
| 8 | +1. **PowerShell Keywords** |
| 9 | + - All PowerShell keywords (e.g. `if`, `return`, `throw`, `function`, `param`) **must** be in lowercase. |
| 10 | + |
| 11 | +2. **Brace Style** |
| 12 | + - Use **One True Bracing Style (OTBS)**. |
| 13 | + - Opening brace on the same line as the statement; closing brace on its own line. |
| 14 | + |
| 15 | +3. **Coverage** |
| 16 | + - We do **not** need 100% coverage of the GitHub API. |
| 17 | + - Maintain a separate file listing the endpoints you intentionally **do not** cover, so the coverage report can mark them accordingly (e.g., ⚠️). |
| 18 | + |
| 19 | +4. **Convert Filter Types** |
| 20 | + - Wherever filters are used, ensure they are implemented as standard PowerShell functions with `begin`, `process`, and `end` blocks. |
| 21 | + |
| 22 | +--- |
| 23 | + |
| 24 | +## Functions |
| 25 | + |
| 26 | +- **Grouping** |
| 27 | + - Group functions by the *object type* they handle, using folders named for that object type (e.g. “Repository”), **not** by the API endpoint URL. |
| 28 | + |
| 29 | +- **Naming** |
| 30 | + - Public function name format: **`Verb-GitHubNoun`**. |
| 31 | + - Private function name format: **`Verb-GitHubNoun`** (same style but no aliases). |
| 32 | + - **`Get-`** functions must **not** include `[CmdletBinding(SupportsShouldProcess)]`. You only use `SupportsShouldProcess` on commands that change or remove data (`Set-`, `Remove-`, `Add-`, etc.). |
| 33 | + |
| 34 | +- **Default Parameter Sets** |
| 35 | + - Do **not** declare `DefaultParameterSetName = '__AllParameterSets'`. |
| 36 | + - Only specify a `DefaultParameterSetName` if it is actually different from the first parameter set. |
| 37 | + |
| 38 | +- **One API Call = One Function** |
| 39 | + - If you find that a single function is handling multiple distinct API calls, split it into multiple functions. |
| 40 | + |
| 41 | +- **Public vs. Private** |
| 42 | + 1. **Public Functions** |
| 43 | + - Support pipeline input if appropriate. |
| 44 | + - Should begin by calling `Resolve-GitHubContext` to handle the `Context` parameter, which can be either a string or a `GitHubContext` object. |
| 45 | + - Use parameter sets (with `begin`, `process`, `end`) if you have multiple ways to call the same logical operation. |
| 46 | + - If choosing among multiple underlying private functions, use a `switch` statement in the `process` block keyed on the parameter set name. |
| 47 | + - If a parameter like `$Repository` is missing, you can default to `$Context.Repo`. If no value is found, **throw** an error. |
| 48 | + 2. **Private Functions** |
| 49 | + - **No pipeline input**. |
| 50 | + - No aliases on either the function or its parameters. |
| 51 | + - **`Context` is mandatory** (type `GitHubContext`), since public functions should already have resolved it. |
| 52 | + - **`Owner`, `Organization`, `ID`, `Repository`** are also mandatory if required by the endpoint. |
| 53 | + - Must not contain logic to default parameters from `Context`; that is resolved in public functions. |
| 54 | + |
| 55 | +--- |
| 56 | + |
| 57 | +## Documentation for Functions |
| 58 | + |
| 59 | +All function documentation follows standard PowerShell help conventions, with some notes: |
| 60 | + |
| 61 | +1. **.SYNOPSIS**, **.DESCRIPTION**, **.EXAMPLES** |
| 62 | + - Examples in your code should include fencing (e.g., triple backticks) because the PSModule framework removes default fences. |
| 63 | + |
| 64 | +2. **.PARAMETER** |
| 65 | + - Do **not** store parameter documentation in a comment block separate from the parameter. Instead, use inline parameter documentation via the `[Parameter()]` attribute and descriptions in triple-slash (`///`) comments above each parameter. |
| 66 | + |
| 67 | +3. **.NOTES** |
| 68 | + - Include a link to the official documentation (if any) that the function is based on, so it’s discoverable via online help. |
| 69 | + |
| 70 | +4. **.LINK** |
| 71 | + - First link should be the function’s own local documentation (generated for the PowerShell module). |
| 72 | + - Additional links can point to official GitHub or API documentation. |
| 73 | + |
| 74 | +--- |
| 75 | + |
| 76 | +## Parameter Guidelines |
| 77 | + |
| 78 | +1. **Always Declare [Parameter()]** |
| 79 | + - Every parameter must explicitly have a `[Parameter()]` attribute, even if empty. |
| 80 | + - Place these attributes in a consistent order (see **Parameter Attributes Order** below). |
| 81 | + |
| 82 | +2. **Parameter Types** |
| 83 | + - Always specify a type, e.g. `[string] $Owner` (rather than `$Owner` alone). |
| 84 | + |
| 85 | +3. **Parameter Naming** |
| 86 | + - Use **PascalCase** for parameters. |
| 87 | + - Convert snake_case from the API docs to **PascalCase** in the function. |
| 88 | + - **`ID`** should be the short name. If needed, add an alias for a long form (e.g., `[Alias('SomeLongName')]`) or for a different style (`'id'`, `'Id'`), depending on user expectations. |
| 89 | + - If the function name implies the object (e.g., `Get-GitHubRepository`), do **not** name the parameter `RepositoryId`. Just `ID` (or `Name`, etc.) suffices. Keep it object-oriented rather than repeating the context. |
| 90 | + - `Owner` should always have the aliases: `Organization` and `User`. |
| 91 | + - `Username` can have the alias `Login` if relevant for a particular API. |
| 92 | + - Use `Repository` (not `Repo`). If you need an alias for backward compatibility, add `[Alias('Repo')]`. |
| 93 | + |
| 94 | +4. **Parameter Attribute Order** |
| 95 | + 1. `[Parameter()]` |
| 96 | + 2. `[ValidateNotNullOrEmpty()]` or other validation attributes |
| 97 | + 3. `[Alias()]` if any |
| 98 | + 4. Then the parameter definition itself: `[string] $ParamName` |
| 99 | + |
| 100 | +5. **Parameter Defaulting** |
| 101 | + - For **public** functions, if the user hasn’t provided a parameter (like `$Repository`), default it from the context: |
| 102 | + ```powershell |
| 103 | + if (-not $Repository) { |
| 104 | + $Repository = $Context.Repo |
| 105 | + } |
| 106 | + if (-not $Repository) { |
| 107 | + throw "Repository not specified and not found in the context." |
| 108 | + } |
| 109 | + ``` |
| 110 | + - For **private** functions, the calling function should already have done this. Private functions assume mandatory parameters. |
| 111 | +
|
| 112 | +6. **Remove `[org]` Alias** |
| 113 | + - Do not use `[Alias('org')]` on the `$Organization` parameter. Use `[Alias('User','Organization')]` on `$Owner` instead. |
| 114 | +
|
| 115 | +--- |
| 116 | +
|
| 117 | +## Function Content & Flow |
| 118 | +
|
| 119 | +1. **Structure** |
| 120 | + - Always use `begin`, `process`, and `end` blocks. |
| 121 | + - **`begin`**: Validate parameters, call `Assert-GitHubContext` if needed, set up any local state. |
| 122 | + - Add a comment stating which permissions are required for the API call. |
| 123 | + - **`process`**: Main logic, including pipeline handling if public. |
| 124 | + - **`end`**: Cleanup if necessary. |
| 125 | +
|
| 126 | +2. **ShouldProcess** |
| 127 | + - Only use `[CmdletBinding(SupportsShouldProcess)]` for commands that create, update, or remove data. **Do not** apply it to `Get-` commands. |
| 128 | +
|
| 129 | +3. **API Method Naming** |
| 130 | + - Use PascalCase for the method in your splat (e.g., `Post`, `Delete`, `Put`, `Get`). |
| 131 | + - The `Method` property in your hashtable to `Invoke-GitHubAPI` (or other REST calls) should reflect that standard. |
| 132 | +
|
| 133 | +4. **Splatting** |
| 134 | + - Always splat the API call. The standard order in the splat is: |
| 135 | + 1. `Method` |
| 136 | + 2. `APIEndpoint` (or `Endpoint`, with `APIEndpoint` as an alias if necessary) |
| 137 | + 3. `Body` |
| 138 | + 4. `Context` |
| 139 | + - Body is always a hashtable containing the payload for `POST`, `PATCH`, or `PUT` calls. |
| 140 | +
|
| 141 | +5. **Removing String Checks** |
| 142 | + - Do **not** use `if ([string]::IsNullOrEmpty($Param))`. Instead, check `-not $Param` or rely on `[ValidateNotNullOrEmpty()]`. |
| 143 | +
|
| 144 | +6. **Pipeline Output** |
| 145 | + - After calling `Invoke-GitHubAPI @inputObject`, you can **either**: |
| 146 | + - `ForEach-Object { Write-Output $_.Response }` |
| 147 | + - or `Select-Object -ExpandProperty Response` |
| 148 | + - Choose which pattern best fits your scenario, but be consistent within a function. |
| 149 | +
|
| 150 | +--- |
| 151 | +
|
| 152 | +## Classes |
| 153 | +
|
| 154 | +1. **One Class per Resource** |
| 155 | + - Each distinct resource type gets its own `.ps1` or `.psm1` with a single class definition. |
| 156 | +
|
| 157 | +2. **Property and Method Naming** |
| 158 | + - Use PascalCase for all public properties and methods. |
| 159 | +
|
| 160 | +3. **Return Types / Interfaces** |
| 161 | + - Each class that you return should have a consistent interface. |
| 162 | + - Remove any properties that are purely “API wrapper” fields (e.g., raw HTTP artifacts that aren’t relevant to the user). |
| 163 | +
|
| 164 | +--- |
| 165 | +
|
| 166 | +## Additional Notes |
| 167 | +
|
| 168 | +1. **Endpoint Coverage File** |
| 169 | + - Maintain a list of endpoints you’re deliberately **not** implementing, so that your coverage reporting can include a ⚠️ for them. |
| 170 | +
|
| 171 | +2. **Parameter Name Design** |
| 172 | + - Use object-oriented naming that reflects the entity. For example, if the function is `Remove-GitHubRepository`, simply use `-ID` (or `-Name`) rather than `-RepositoryID`. |
| 173 | +
|
| 174 | +3. **Aliases** |
| 175 | + - Private functions have **no** aliases (function-level or parameter-level). |
| 176 | + - Public functions can add aliases where it makes sense (`Owner` has `-User`/`-Organization`, `Repository` might have `-Repo` alias if needed, `Username` might have `-Login`). |
| 177 | +
|
| 178 | +4. **Mandatory Context for Private** |
| 179 | + - Private functions must always expect a resolved `[GitHubContext] $Context`. Public functions handle any string-based or null context resolution logic. |
| 180 | +
|
| 181 | +5. **We Do Not Have to Cover Every Possible API** |
| 182 | + - Some endpoints (e.g., “hovercards” or other rarely used features) can be excluded. |
| 183 | +
|
| 184 | +--- |
| 185 | +
|
| 186 | +That’s it. This spec captures all the bullet points and original guidelines in one place. Use it as the authoritative reference for coding style, function naming, parameter declarations, and general best practices in your module. |
0 commit comments