Skip to content

Commit 31b5bd5

Browse files
🩹 [Patch]: Don't require the source code to be in a folder with name of module (#44)
## Description - Don't require the source code to be in a folder with name of module - Fixes #43 ## Type of change <!-- Use the check-boxes [x] on the options that are relevant. --> - [ ] 📖 [Docs] - [ ] 🪲 [Fix] - [x] 🩹 [Patch] - [ ] ⚠️ [Security fix] - [ ] 🚀 [Feature] - [ ] 🌟 [Breaking change] ## Checklist <!-- Use the check-boxes [x] on the options that are relevant. --> - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas
1 parent 613e59c commit 31b5bd5

27 files changed

+77
-192
lines changed

README.md

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,41 +33,42 @@ During the build process the following steps are performed:
3333

3434
## Root module
3535

36+
The `src` folder may contain a 'root module' file. If present, the build function will disregard this file
37+
and build a new root module file based on the source code in the module folder.
38+
3639
The root module file is the main file that is loaded when the module is imported.
3740
It is built from the source code files in the module folder in the following order:
3841

39-
1. Adds module headers from `header.ps1`.
42+
1. Adds module headers from `header.ps1` if it exists and removes the file from the module folder.
4043
1. Adds data loader automation that loads files from the `data` folder as variables in the module scope, if it exists. The variables are available using the ´$script:<filename>´ syntax.
41-
1. Adds content from subfolders, in the order:
42-
- Init
43-
- Private
44-
- Public
45-
- *.ps1 on module root
46-
1. Adds the Export-ModuleMember function to the end of the file, to make sure that only the functions, cmdlets, variables and aliases that are defined in the module are exported.
47-
48-
### The root module in the `src` folder
49-
50-
The root module file that is included in the source files contains the same functionality but is not optimized for performance.
51-
The goal with this is to have a quick way to import and test the module without having to build it.
44+
1. Adds content from subfolders, if they exists, and removes them from the module folder in the following order:
45+
- `init`
46+
- `classes`
47+
- `private`
48+
- `public`
49+
- `*.ps1` on module root
50+
1. Adds a `class` and `enum` exporter that exports all classes and enums in the module to the caller session, using TypeAccelerators.
51+
1. Adds the `Export-ModuleMember` function to the end of the file, to make sure that only the functions, cmdlets, variables and aliases that are defined in the module are exported.
5252

5353
## Module manifest
5454

5555
The module manifest file is the file that describes the module and its content. It is used by PowerShell to load the module and its prerequisites.
56-
The file also contains important metadata that is used by the PowerShell Gallery.
56+
The file also contains important metadata that is used by the PowerShell Gallery. If a file exists in the source code folder `src` it will be used as a base for the module manifest file.
57+
Most of the values in the module manifest file are calculated during the build process however some of these will not be touched if specified in the source manifest file.
5758

5859
During the module manifest build process the following steps are performed:
5960

60-
1. Get the manifest file from the source code. Content from this file overrides any value that would be calculated based on the source code.
61-
1. Find and set the `RootModule` based on filename and extension.
61+
1. Get the manifest file from the source code. If it does not exist, a new manifest file is created.
62+
1. Generate and set the `RootModule` based module name.
6263
1. Set a temporary `ModuleVersion`, as this is set during the release process by [Publish-PSModule](https://github.com/PSModule/Publish-PSModule).
63-
1. Set the `Author` and `CompanyName` based on GitHub Owner.
64-
1. Set the `Copyright` information based on a default text (`(c) 2024 >>OwnerName<<. All rights reserved.`) and adds either the `Author`, `CompanyName` or both (`Author | CompanyName`) when these are different.
65-
1. Set the `Description` based on the GitHub repository description.
66-
1. Set various properties in the manifest such as `PowerShellHostName`, `PowerShellHostVersion`, `DotNetFrameworkVersion`, `ClrVersion`, and `ProcessorArchitecture`. There is currently no automation for these properties.
64+
1. Set the `Author` and `CompanyName` based on GitHub Owner. If a value exists in the source manifest file, this value is used.
65+
1. Set the `Copyright` information based on a default text (`(c) 2024 >>OwnerName<<. All rights reserved.`) and adds either the `Author`, `CompanyName` or both (`Author | CompanyName`) when these are different. If a value exists in the source manifest file, this value is used.
66+
1. Set the `Description` based on the GitHub repository description. If a value exists in the source manifest file, this value is used.
67+
1. Set various properties in the manifest such as `PowerShellHostName`, `PowerShellHostVersion`, `DotNetFrameworkVersion`, `ClrVersion`, and `ProcessorArchitecture`. There is currently no automation for these properties. If a value exists in the source manifest file, this value is used.
6768
1. Get the list of files in the module source folder and set the `FileList` property in the manifest.
6869
1. Get the list of required assemblies (`*.dll` files) from the `assemblies` folder and set the `RequiredAssemblies` property in the manifest.
6970
1. Get the list of nested modules (`*.psm1` files) from the `modules` folder and set the `NestedModules` property in the manifest.
70-
1. Get the list of scripts to process (`*.ps1` files) from the `classes` and `scripts` folders and set the `ScriptsToProcess` property in the manifest. This ensures that the scripts are loaded to the caller session (parent of module session).
71+
1. Get the list of scripts to process (`*.ps1` files) from the `scripts` folders and set the `ScriptsToProcess` property in the manifest. This ensures that the scripts are loaded to the caller session (parent of module session).
7172
1. Get the list of types to process by searching for `*.Types.ps1xml` files in the entire module source folder and set the `TypesToProcess` property in the manifest.
7273
1. Get the list of formats to process by searching for `*.Format.ps1xml` files in the entire module source folder and set the `FormatsToProcess` property in the manifest.
7374
1. Get the list of DSC resources to export by searching for `*.psm1` files in the `resources` folder and set the `DscResourcesToExport` property in the manifest.
@@ -76,22 +77,22 @@ During the module manifest build process the following steps are performed:
7677
1. Gather information from source files to update `RequiredModules`, `PowerShellVersion`, and `CompatiblePSEditions` properties.
7778
1. The following values are gathered from the GitHub repository:
7879
- `Tags` are generated from Repository topics in addition to compatability tags gathered from the source code.
79-
- `LicenseUri` is generated assuming there is a `LICENSE` file on the root of the repository.
80-
- `ProjectUri` is the URL to the GitHub repository
81-
- `IconUri` is generated assuming there is a `icon.png` file in the `icon` folder on the repository root.
80+
- `LicenseUri` is generated assuming there is a `LICENSE` file on the root of the repository. If a value exists in the source manifest file, this value is used.
81+
- `ProjectUri` is the URL to the GitHub repository. If a value exists in the source manifest file, this value is used.
82+
- `IconUri` is generated assuming there is a `icon.png` file in the `icon` folder on the repository root. If a value exists in the source manifest file, this value is used.
8283
1. `ReleaseNotes` currently not automated, but could be the PR description or release description.
8384
1. `PreRelease` is not managed here, but is managed from [Publish-PSModule](https://github.com/PSModule/Publish-PSModule)
84-
1. `RequireLicenseAcceptance` is not automated and defaults to `false`, and
85-
1. `ExternalModuleDependencies` is currenlty not automated.
86-
1. `HelpInfoURI` is not automated.
85+
1. `RequireLicenseAcceptance` is not automated and defaults to `false`. If a value exists in the source manifest file, this value is used.
86+
1. `ExternalModuleDependencies` is currenlty not automated. If a value exists in the source manifest file, this value is used.
87+
1. `HelpInfoURI` is not automated. If a value exists in the source manifest file, this value is used.
8788
1. Create a new manifest file in the output folder with the gathered info above. This also generates a new `GUID` for the module.
8889
1. Format the manifest file using the `Set-ModuleManifest` function from the [Utilities](https://github.com/PSModule/Utilities) module.
8990

9091
Linking the description to the module manifest file might show more how this works:
9192

9293
```powershell
9394
@{
94-
RootModule = 'Utilities.psm1' # Get files from root of folder wher name is same as the folder and file extension is .psm1, .ps1, .psd1, .dll, .cdxml, .xaml. Error if there are multiple files that meet the criteria.
95+
RootModule = 'Utilities.psm1' # Generated from the module name, <moduleName>.psm1
9596
ModuleVersion = '0.0.1' # Set during release using Publish-PSModule.
9697
CompatiblePSEditions = @() # Get from source files, REQUIRES -PSEdition <PSEdition-Name>, null if not provided.
9798
GUID = '<GUID>' # Generated when finally saving the manifest using New-ModuleManifest.
@@ -142,16 +143,9 @@ Linking the description to the module manifest file might show more how this wor
142143
}
143144
```
144145

145-
### The module manifest in the `src` folder
146-
147-
The module manifest file that is included in the source files contains the same functionality but is not optimized for performance and does not automatically gather all the information that is gathered during the build process.
148-
The goal with this is to have a quick way to import and test the module without having to build it.
149-
150-
The source module manifest is also the only place where some of the values can be controlled. These values are typically difficult to calculate and are not automated.
151-
152146
## Module documentation
153147

154-
The module documentation is built using platyPS and comment based help in the source code.
148+
The module documentation is built using `platyPS` and comment based help in the source code.
155149
The documentation is currently not published anywhere, but should be published to GitHub Pages in a future release.
156150

157151
## Permissions

scripts/helpers/Build-PSModule.ps1

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ function Build-PSModule {
1010
#>
1111
[CmdletBinding()]
1212
param(
13+
# Name of the module.
14+
[Parameter(Mandatory)]
15+
[string] $ModuleName,
16+
1317
# Path to the folder where the modules are located.
1418
[Parameter(Mandatory)]
1519
[string] $ModuleSourceFolderPath,
@@ -23,20 +27,18 @@ function Build-PSModule {
2327
[string] $DocsOutputFolderPath
2428
)
2529

26-
$moduleName = Split-Path -Path $ModuleSourceFolderPath -Leaf
27-
28-
Start-LogGroup "Building module [$moduleName]"
30+
Start-LogGroup "Building module [$ModuleName]"
2931
Write-Verbose "Source path: [$ModuleSourceFolderPath]"
3032
if (-not (Test-Path -Path $ModuleSourceFolderPath)) {
3133
Write-Error "Source folder not found at [$ModuleSourceFolderPath]"
3234
exit 1
3335
}
3436
$moduleSourceFolder = Get-Item -Path $ModuleSourceFolderPath
3537

36-
$moduleOutputFolder = New-Item -Path $ModulesOutputFolderPath -Name $moduleName -ItemType Directory -Force
38+
$moduleOutputFolder = New-Item -Path $ModulesOutputFolderPath -Name $ModuleName -ItemType Directory -Force
3739
Write-Verbose "Module output folder: [$ModulesOutputFolderPath]"
3840

39-
$docsOutputFolder = New-Item -Path $DocsOutputFolderPath -Name $moduleName -ItemType Directory -Force
41+
$docsOutputFolder = New-Item -Path $DocsOutputFolderPath -Name $ModuleName -ItemType Directory -Force
4042
Write-Verbose "Docs output folder: [$DocsOutputFolderPath]"
4143
Stop-LogGroup
4244

scripts/helpers/Build/Build-PSModuleManifest.ps1

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,17 @@ function Build-PSModuleManifest {
2626
#region Build manifest file
2727
Start-LogGroup 'Build manifest file'
2828
$moduleName = Split-Path -Path $ModuleOutputFolder -Leaf
29-
$manifestFileName = "$moduleName.psd1"
30-
$manifestOutputPath = Join-Path -Path $ModuleOutputFolder -ChildPath $manifestFileName
31-
$manifestFile = Get-Item -Path $manifestOutputPath
32-
Write-Verbose ($manifestFile | Format-List | Out-String)
33-
$manifest = Get-ModuleManifest -Path $manifestFile -Verbose:$false
34-
35-
$rootModule = Get-PSModuleRootModule -SourceFolderPath $ModuleOutputFolder
36-
$manifest.RootModule = $rootModule
29+
$sourceManifestFilePath = Join-Path -Path $ModuleOutputFolder -ChildPath "$moduleName.psd1"
30+
if (-not (Test-Path -Path $sourceManifestFilePath)) {
31+
$sourceManifestFilePath = Join-Path -Path $ModuleOutputFolder -ChildPath 'manifest.psd1'
32+
}
33+
$manifest = if (-not (Test-Path -Path $sourceManifestFilePath)) {
34+
@{}
35+
} else {
36+
Get-ModuleManifest -Path $sourceManifestFilePath -Verbose:$false
37+
}
38+
39+
$manifest.RootModule = "$moduleName.psm1"
3740
$manifest.ModuleVersion = '999.0.0'
3841

3942
$manifest.Author = $manifest.Keys -contains 'Author' ? ($manifest.Author | IsNotNullOrEmpty) ? $manifest.Author : $env:GITHUB_REPOSITORY_OWNER : $env:GITHUB_REPOSITORY_OWNER
@@ -71,10 +74,19 @@ function Build-PSModuleManifest {
7174
$pathSeparator = [System.IO.Path]::DirectorySeparatorChar
7275

7376
Write-Verbose '[FileList]'
74-
$files = $ModuleOutputFolder | Get-ChildItem -File -ErrorAction SilentlyContinue | Where-Object -Property Name -NotLike '*.ps1'
75-
$files += $ModuleOutputFolder | Get-ChildItem -Directory | Get-ChildItem -Recurse -File -ErrorAction SilentlyContinue
77+
$files = [System.Collections.Generic.List[System.IO.FileInfo]]::new()
78+
79+
# Get files on module root
80+
$ModuleOutputFolder | Get-ChildItem -File -ErrorAction SilentlyContinue | Where-Object -Property Name -NotLike '*.ps1' |
81+
ForEach-Object { $files.Add($_) }
82+
83+
# Get files on module subfolders, excluding the following folders 'init', 'classes', 'public', 'private'
84+
$skipList = @('init', 'classes', 'public', 'private')
85+
$ModuleOutputFolder | Get-ChildItem -Directory | Where-Object { $_.Name -NotIn $skipList } |
86+
Get-ChildItem -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { $files.Add($_) }
87+
88+
# Get the relative file path and store it in the manifest
7689
$files = $files | Select-Object -ExpandProperty FullName | ForEach-Object { $_.Replace($ModuleOutputFolder, '').TrimStart($pathSeparator) }
77-
$fileList = $files | Where-Object { $_ -NotLike 'init*' -and $_ -NotLike 'classes*' -and $_ -NotLike 'public*' -and $_ -NotLike 'private*' }
7890
$manifest.FileList = $fileList.count -eq 0 ? @() : @($fileList)
7991
$manifest.FileList | ForEach-Object { Write-Verbose "[FileList] - [$_]" }
8092

@@ -142,9 +154,9 @@ function Build-PSModuleManifest {
142154

143155

144156
Write-Verbose '[Gather]'
145-
$capturedModules = @()
146-
$capturedVersions = @()
147-
$capturedPSEdition = @()
157+
$capturedModules = [System.Collections.Generic.List[System.Object]]::new()
158+
$capturedVersions = [System.Collections.Generic.List[string]]::new()
159+
$capturedPSEdition = [System.Collections.Generic.List[string]]::new()
148160

149161
$files = $ModuleOutputFolder | Get-ChildItem -Recurse -File -ErrorAction SilentlyContinue
150162
Write-Verbose "[Gather] - Processing [$($files.Count)] files"
@@ -165,22 +177,22 @@ function Build-PSModuleManifest {
165177
$hashtable = '@\{[^}]*\}'
166178
if ($_ -match $hashtable) {
167179
Write-Verbose " - [#Requires -Modules] - [$_] - Hashtable"
168-
$capturedModules += ConvertTo-Hashtable -InputString $_
180+
$capturedModules.Add((ConvertTo-Hashtable -InputString $_))
169181
} else {
170182
Write-Verbose " - [#Requires -Modules] - [$_] - String"
171-
$capturedModules += $_
183+
$capturedModules.Add($_)
172184
}
173185
}
174186
}
175187
# PowerShellVersion -> REQUIRES -Version <N>[.<n>], $null if not provided
176188
'^\s*#Requires -Version (.+)$' {
177189
Write-Verbose " - [#Requires -Version] - [$($matches[1])]"
178-
$capturedVersions += $matches[1]
190+
$capturedVersions.Add($matches[1])
179191
}
180192
#CompatiblePSEditions -> REQUIRES -PSEdition <PSEdition-Name>, $null if not provided
181193
'^\s*#Requires -PSEdition (.+)$' {
182194
Write-Verbose " - [#Requires -PSEdition] - [$($matches[1])]"
183-
$capturedPSEdition += $matches[1]
195+
$capturedPSEdition.Add($matches[1])
184196
}
185197
}
186198
}
@@ -244,7 +256,7 @@ function Build-PSModuleManifest {
244256
} catch {
245257
$repoLabels = @()
246258
}
247-
$manifestTags = [Collections.Generic.List[string]]::new()
259+
$manifestTags = [System.Collections.Generic.List[string]]::new()
248260
$tags = $PSData.Keys -contains 'Tags' ? ($PSData.Tags).Count -gt 0 ? $PSData.Tags : $repoLabels : $repoLabels
249261
$tags | ForEach-Object { $manifestTags.Add($_) }
250262
# Add tags for compatability mode. https://docs.microsoft.com/en-us/powershell/scripting/developer/module/how-to-write-a-powershell-module-manifest?view=powershell-7.1#compatibility-tags
@@ -322,7 +334,7 @@ function Build-PSModuleManifest {
322334
}
323335

324336
Write-Verbose 'Creating new manifest file in outputs folder'
325-
$outputManifestPath = Join-Path -Path $ModuleOutputFolder $manifestFileName
337+
$outputManifestPath = Join-Path -Path $ModuleOutputFolder -ChildPath "$moduleName.psd1"
326338
Write-Verbose "OutputManifestPath - [$outputManifestPath]"
327339
New-ModuleManifest -Path $outputManifestPath @manifest
328340
Stop-LogGroup

scripts/helpers/Build/Get-PSModuleRootModule.ps1

Lines changed: 0 additions & 54 deletions
This file was deleted.

scripts/main.ps1

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@ Stop-LogGroup
1010

1111
Start-LogGroup 'Loading inputs'
1212
$moduleName = ($env:GITHUB_ACTION_INPUT_Name | IsNullOrEmpty) ? $env:GITHUB_REPOSITORY_NAME : $env:GITHUB_ACTION_INPUT_Name
13-
$moduleSourceFolderPath = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath $env:GITHUB_ACTION_INPUT_Path $moduleName
1413
Write-Verbose "Module name: [$moduleName]"
14+
15+
$moduleSourceFolderPath = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath $env:GITHUB_ACTION_INPUT_Path $moduleName
16+
if (-not (Test-Path -Path $moduleSourceFolderPath)) {
17+
$moduleSourceFolderPath = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath $env:GITHUB_ACTION_INPUT_Path
18+
}
1519
Write-Verbose "Source module path: [$moduleSourceFolderPath]"
1620
if (-not (Test-Path -Path $moduleSourceFolderPath)) {
1721
throw "Module path [$moduleSourceFolderPath] does not exist."
@@ -23,6 +27,7 @@ $docsOutputFolderPath = Join-Path $env:GITHUB_WORKSPACE $env:GITHUB_ACTION_INPUT
2327
Write-Verbose "Docs output path: [$docsOutputFolderPath]"
2428
Stop-LogGroup
2529
$params = @{
30+
ModuleName = $moduleName
2631
ModuleSourceFolderPath = $moduleSourceFolderPath
2732
ModulesOutputFolderPath = $modulesOutputFolderPath
2833
DocsOutputFolderPath = $docsOutputFolderPath

tests/src/PSModuleTest/PSModuleTest.psd1

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)