diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index a862cd4..1b4eaf0 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,75 +1,75 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at **[EMAIL REQUIRED]**. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project email +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at **[EMAIL REQUIRED]**. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html) + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index abfbdde..ddca9ed 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,24 +1,23 @@ -# Contributing - -Thanks for your interest in contributing to the **PSReddit Project**. - -Whether it's a bug report, new feature, correction, or additional documentation, your feedback and contributions are appreciated. - -Please read through this document before submitting any issues or pull requests to ensure all the necessary information is provided to effectively respond to your bug report or contribution. - -Please note there is a code of conduct, please follow it in all your interactions with the project. - -## Reporting Bugs/Feature Requests - -## Contributing via Pull Requests - -## Finding contributions to work on - -## Code of Conduct - -This project has a [Code of Conduct](CODE_OF_CONDUCT.md). - -## Licensing - -See the [LICENSE](LICENSE.txt) file for our project's licensing. +# Contributing +Thanks for your interest in contributing to the **PSReddit Project**. + +Whether it's a bug report, new feature, correction, or additional documentation, your feedback and contributions are appreciated. + +Please read through this document before submitting any issues or pull requests to ensure all the necessary information is provided to effectively respond to your bug report or contribution. + +Please note there is a code of conduct, please follow it in all your interactions with the project. + +## Reporting Bugs/Feature Requests + +## Contributing via Pull Requests + +## Finding contributions to work on + +## Code of Conduct + +This project has a [Code of Conduct](CODE_OF_CONDUCT.md). + +## Licensing + +See the [LICENSE](LICENSE.txt) file for our project's licensing. diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 67f3c29..0960be4 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,39 +1,45 @@ ---- -name: Bug report -about: Submit a new bug -title: Bug report -labels: 'bug' -assignees: '' - ---- - - - -### Expected Behavior - - -### Current Behavior - - -### Possible Solution - - -### Steps to Reproduce - - - -1. -2. -3. -4. - -### Context (Environment) - - -* Operating System and version as reported by `$PSVersionTable.OS`: -* PowerShell versions as reported by `$PSVersionTable.PSEdition`: - - - -### Detailed Description - +--- +name: Bug report +about: Submit a new bug +title: Bug report +labels: "bug" +assignees: "" +--- + + + +## Expected Behavior + + + +## Current Behavior + + + +## Possible Solution + + + +## Steps to Reproduce + + + + +1. +2. +3. +4. + +## Context (Environment) + + + + +- Operating System and version as reported by `$PSVersionTable.OS`: +- PowerShell versions as reported by `$PSVersionTable.PSEdition`: + + + +## Detailed Description + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index cb1a9fb..239d905 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,20 +1,23 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: 'Feature request' -labels: 'enhancement' -assignees: '' - ---- - -### Description - - -### Describe the solution you'd like - - -### Describe any alternatives you've considered - - -### Additional context - +--- +name: Feature request +about: Suggest an idea for this project +title: "Feature request" +labels: "enhancement" +assignees: "" +--- + +## Description + + + +## Describe the solution you'd like + + + +## Describe any alternatives you've considered + + + +## Additional context + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a7660f8..4b6ea22 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,13 @@ -# Pull Request - -## Issue - -Issue #, if available: - -## Description - -Description of changes: - -## License - -By submitting this pull request, I confirm that my contribution is made under the terms of the projects associated license. +# Pull Request + +## Issue + +Issue #, if available: + +## Description + +Description of changes: + +## License + +By submitting this pull request, I confirm that my contribution is made under the terms of the projects associated license. diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 184ed7d..0d935ce 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1,18 +1,18 @@ -# Security Policy - -## Reporting a Vulnerability - - - -If you discover a vulnerability in PSReddit, please follow the _following process_: - -1. Open a generic bug issue advising you have discovered a vulnerability. - - Avoid sharing specifics or details of the vulnerability in an open GitHub issue. -2. A repo owner will reach out to you to establish a private form of communication. -3. We will evaluate the vulnerability and, if necessary, release a fix or mitigating steps to address it. We will contact you to let you know the outcome, and will credit you in the report. - - Please **do not disclose the vulnerability publicly** until a fix is released! - -4. Once we have either a) published a fix, or b) declined to address the vulnerability for whatever reason, you are free to publicly disclose it. \ No newline at end of file +# Security Policy + +## Reporting a Vulnerability + + + +If you discover a vulnerability in PSReddit, please follow the _following process_: + +1. Open a generic bug issue advising you have discovered a vulnerability. + - Avoid sharing specifics or details of the vulnerability in an open GitHub issue. +2. A repository owner will reach out to you to establish a private form of communication. +3. We will evaluate the vulnerability and, if necessary, release a fix or mitigating steps to address it. We will contact you to let you know the outcome, and will credit you in the report. + + Please **do not disclose the vulnerability publicly** until a fix is released! + +4. Once we have either a) published a fix, or b) declined to address the vulnerability for whatever reason, you are free to publicly disclose it. diff --git a/.github/linters/.powershell-psscriptanalyzer.psd1 b/.github/linters/.powershell-psscriptanalyzer.psd1 new file mode 100644 index 0000000..4bb30ee --- /dev/null +++ b/.github/linters/.powershell-psscriptanalyzer.psd1 @@ -0,0 +1,13 @@ +@{ + # Mirrors src/PSScriptAnalyzerSettings.psd1 so super-linter applies the same + # rules the repo already uses locally (super-linter reads its PSScriptAnalyzer + # settings from .github/linters/, not from src/). + IncludeDefaultRules = $true + Severity = @('Error', 'Warning') + # PSUseDeclaredVarsMoreThanAssignments excluded due to false positives with + # Pester v5 scoping: assignments in a test file are consumed inside It/Should + # blocks that PSScriptAnalyzer cannot see across scope. + ExcludeRules = @( + 'PSUseDeclaredVarsMoreThanAssignments' + ) +} diff --git a/.github/linters/.stylelintrc.json b/.github/linters/.stylelintrc.json new file mode 100644 index 0000000..297131f --- /dev/null +++ b/.github/linters/.stylelintrc.json @@ -0,0 +1,13 @@ +{ + "extends": ["stylelint-config-standard"], + "overrides": [ + { + "extends": ["stylelint-config-recommended-scss"], + "files": ["*.scss", "**/*.scss"], + "customSyntax": "postcss-scss" + } + ], + "rules": { + "selector-class-pattern": null + } +} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0409790..2f42893 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,3 +18,6 @@ jobs: uses: LukeEvansTech/shared-workflows/.github/workflows/super-linter.yml@3771e1f9da1b3f6132eec9d63aacc0acb74660bc # v1 with: soft-launch: false + # .vscode/*.json are JSONC (comments are valid there); exclude them so the + # JSON linter's jsonc/no-comments rule doesn't fail on editor config. + filter-regex-exclude: .*/\.vscode/.* diff --git a/.gitignore b/.gitignore index 2cf5ecf..9fc8b9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,29 @@ -Archive -Artifacts -cov.xml -coverage.xml - -# OS generated files # -###################### -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# Rules from original module -########################### -# Exclude secrets and build artifacts -*.secret -*.env -*.user -*.nupkg -node_modules/ -reddit_secrets.json - -# Zensical documentation build artifacts -######################################### -docs/site/ +Archive +Artifacts +cov.xml +coverage.xml + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Rules from original module +########################### +# Exclude secrets and build artifacts +*.secret +*.env +*.user +*.nupkg +node_modules/ +reddit_secrets.json + +# Zensical documentation build artifacts +######################################### +docs/site/ .zensical/ \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 7380068..52609dd 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,9 +1,9 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "ms-vscode.PowerShell", - "ryanluker.vscode-coverage-gutters", - "DavidAnson.vscode-markdownlint" - ] +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "ms-vscode.PowerShell", + "ryanluker.vscode-coverage-gutters", + "DavidAnson.vscode-markdownlint" + ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index b6d2d10..f26afb2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,9 @@ -{ - // When enabled, will trim trailing whitespace when you save a file. - "files.trimTrailingWhitespace": true, - // specifies the location of the explicitly ScriptAnalyzer settings file - "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1", - // specifies the PowerShell coding style used in this project (https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81) - "powershell.codeFormatting.preset":"OTBS" -} +{ + // When enabled, will trim trailing whitespace when you save a file. + "files.trimTrailingWhitespace": true, + // specifies the location of the explicitly ScriptAnalyzer settings file + "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1", + // specifies the PowerShell coding style used in this project (https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81) + "powershell.codeFormatting.preset":"OTBS" +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a40ffa9..0040c07 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,269 +1,269 @@ -// Portions of this file were generated using New-VSCodeTask.ps1 -// Modify the build script or tasks-merge.json and recreate. -// https://code.visualstudio.com/docs/editor/tasks -// Available variables which can be used inside of strings. -// https://code.visualstudio.com/docs/editor/variables-reference#_predefined-variables -// ${workspaceFolder}: path of the folder opened in VS Code -// ${workspaceFolderBasename} - name of the folder opened in VS Code without any slashes (/) -// ${file}: current opened file -// ${fileWorkspaceFolder} - current opened file's workspace folder -// ${relativeFile}: the current opened file relative to workspaceFolder -// ${relativeFileDirname}: current opened file's dirname relative to workspaceFolder -// ${fileBasename}: current opened file's basename -// ${fileBasenameNoExtension} - current opened file's basename with no file extension -// ${fileDirname}: current opened file's dirname -// ${fileExtname}: current opened file's extension -// ${cwd}: the current working directory of the spawned process -// ${lineNumber} - current selected line number in the active file -// ${selectedText} - current selected text in the active file -// ${execPath} - path to the running VS Code executable -// ${defaultBuildTask} - name of the default build task -// ${pathSeparator} - character used by the operating system to separate components in file paths -{ - "version": "2.0.0", - "windows": { - "options": { - "shell": { - // "executable": "powershell.exe", - "executable": "pwsh.exe", - "args": [ - "-NoProfile", - "-ExecutionPolicy", - "Bypass", - "-Command" - ] - } - } - }, - "linux": { - "options": { - "shell": { - "executable": "/usr/bin/pwsh", - "args": [ - "-NoProfile", - "-Command" - ] - } - } - }, - "osx": { - "options": { - "shell": { - "executable": "/usr/local/bin/pwsh", - "args": [ - "-NoProfile", - "-Command" - ] - } - } - }, - "tasks": [ - { - "label": ".", - "type": "shell", - "command": "$PSVersionTable;Invoke-Build -Task . -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": { - "kind": "build", - "isDefault": true - } - }, - { - "label": "TestLocal", - "type": "shell", - "command": "Invoke-Build -Task TestLocal -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "test" - }, - { - "label": "HelpLocal", - "type": "shell", - "command": "Invoke-Build -Task HelpLocal -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "none" - }, - { - "label": "Clean", - "type": "shell", - "command": "Invoke-Build -Task Clean -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "none" - }, - { - "label": "ValidateRequirements", - "type": "shell", - "command": "Invoke-Build -Task ValidateRequirements -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "none" - }, - { - "label": "Analyze", - "type": "shell", - "problemMatcher": "$msCompile", - "command": "Invoke-Build -Task Analyze -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "none" - }, - { - "label": "AnalyzeTests", - "type": "shell", - "command": "Invoke-Build -Task AnalyzeTests -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "none" - }, - { - "label": "FormattingCheck", - "type": "shell", - "command": "Invoke-Build -Task FormattingCheck -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "none" - }, - { - "label": "Test", - "type": "shell", - "command": "Invoke-Build -Task Test -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": { - "kind": "test", - "isDefault": true - } - }, - { - "label": "DevCC", - "type": "shell", - "command": "Invoke-Build -Task DevCC -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "test" - }, - { - "label": "Build", - "type": "shell", - "command": "Invoke-Build -Task Build -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "none" - }, - { - "label": "BuildNoIntegration", - "type": "shell", - "command": "Invoke-Build -Task BuildNoIntegration -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "build" - }, - { - "label": "IntegrationTest", - "type": "shell", - "command": "Invoke-Build -Task IntegrationTest -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", - "problemMatcher": "$msCompile", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "test" - }, - { - "label": "PesterTest", - "type": "shell", - "command": "Invoke-Pester '${workspaceFolder}/src/Tests/Unit' -Output Detailed", - "problemMatcher": "$pester", - "group": "test" - }, - { - "label": "Pester-Single-Coverage", - "type": "shell", - "command": "Import-Module -Name '${workspaceFolder}/src/${workspaceFolderBasename}/${workspaceFolderBasename}.psm1';$pesterConfig = New-PesterConfiguration;$pesterConfig.run.Path = '${workspaceFolder}/src/Tests/Unit/*/${input:functionName}.Tests.ps1';$pesterConfig.CodeCoverage.Enabled = $true;$pesterConfig.CodeCoverage.Path = '${workspaceFolder}/src/${workspaceFolderBasename}/*/${input:functionName}.ps1';Invoke-Pester -Configuration $pesterConfig", - "problemMatcher": "$pester", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "test" - }, - { - "label": "Pester-Single-Detailed", - "type": "shell", - "command": "Import-Module -Name '${workspaceFolder}/src/${workspaceFolderBasename}/${workspaceFolderBasename}.psm1';Invoke-Pester '${workspaceFolder}/src/Tests/Unit/*/${input:functionName}.Tests.ps1' -Output Detailed", - "problemMatcher": "$pester", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "test" - }, - { - "label": "DevCC-Single", - "type": "shell", - "command": "Import-Module -Name '${workspaceFolder}/src/${workspaceFolderBasename}/${workspaceFolderBasename}.psm1';$pesterConfig = New-PesterConfiguration;$pesterConfig.run.Path = '${workspaceFolder}/src/Tests/Unit/*/${input:functionName}.Tests.ps1';$pesterConfig.CodeCoverage.Enabled = $true;$pesterConfig.CodeCoverage.Path = '${workspaceFolder}/src/${workspaceFolderBasename}/*/${input:functionName}.ps1';$pesterConfig.CodeCoverage.OutputPath = '${workspaceFolder}/cov.xml';$pesterConfig.CodeCoverage.OutputFormat = 'CoverageGutters';Invoke-Pester -Configuration $pesterConfig", - "problemMatcher": "$pester", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "test" - }, - { - "label": "Integration-Single-Detailed", - "type": "shell", - "command": "Import-Module -Name '${workspaceFolder}/src/${workspaceFolderBasename}/${workspaceFolderBasename}.psm1';Invoke-Pester '${workspaceFolder}/src/Tests/Integration/*/${input:functionName}.Tests.ps1' -Output Detailed", - "problemMatcher": "$pester", - "presentation": { - "echo": false, - "showReuseMessage": false - }, - "group": "test" - } - ], - "inputs": [ - { - "type": "promptString", - "id": "functionName", - "description": "Name of PowerShell function you want to test" - } - ] +// Portions of this file were generated using New-VSCodeTask.ps1 +// Modify the build script or tasks-merge.json and recreate. +// https://code.visualstudio.com/docs/editor/tasks +// Available variables which can be used inside of strings. +// https://code.visualstudio.com/docs/editor/variables-reference#_predefined-variables +// ${workspaceFolder}: path of the folder opened in VS Code +// ${workspaceFolderBasename} - name of the folder opened in VS Code without any slashes (/) +// ${file}: current opened file +// ${fileWorkspaceFolder} - current opened file's workspace folder +// ${relativeFile}: the current opened file relative to workspaceFolder +// ${relativeFileDirname}: current opened file's dirname relative to workspaceFolder +// ${fileBasename}: current opened file's basename +// ${fileBasenameNoExtension} - current opened file's basename with no file extension +// ${fileDirname}: current opened file's dirname +// ${fileExtname}: current opened file's extension +// ${cwd}: the current working directory of the spawned process +// ${lineNumber} - current selected line number in the active file +// ${selectedText} - current selected text in the active file +// ${execPath} - path to the running VS Code executable +// ${defaultBuildTask} - name of the default build task +// ${pathSeparator} - character used by the operating system to separate components in file paths +{ + "version": "2.0.0", + "windows": { + "options": { + "shell": { + // "executable": "powershell.exe", + "executable": "pwsh.exe", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command" + ] + } + } + }, + "linux": { + "options": { + "shell": { + "executable": "/usr/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "osx": { + "options": { + "shell": { + "executable": "/usr/local/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "tasks": [ + { + "label": ".", + "type": "shell", + "command": "$PSVersionTable;Invoke-Build -Task . -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "TestLocal", + "type": "shell", + "command": "Invoke-Build -Task TestLocal -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "test" + }, + { + "label": "HelpLocal", + "type": "shell", + "command": "Invoke-Build -Task HelpLocal -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "none" + }, + { + "label": "Clean", + "type": "shell", + "command": "Invoke-Build -Task Clean -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "none" + }, + { + "label": "ValidateRequirements", + "type": "shell", + "command": "Invoke-Build -Task ValidateRequirements -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "none" + }, + { + "label": "Analyze", + "type": "shell", + "problemMatcher": "$msCompile", + "command": "Invoke-Build -Task Analyze -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "none" + }, + { + "label": "AnalyzeTests", + "type": "shell", + "command": "Invoke-Build -Task AnalyzeTests -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "none" + }, + { + "label": "FormattingCheck", + "type": "shell", + "command": "Invoke-Build -Task FormattingCheck -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "none" + }, + { + "label": "Test", + "type": "shell", + "command": "Invoke-Build -Task Test -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "DevCC", + "type": "shell", + "command": "Invoke-Build -Task DevCC -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "test" + }, + { + "label": "Build", + "type": "shell", + "command": "Invoke-Build -Task Build -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "none" + }, + { + "label": "BuildNoIntegration", + "type": "shell", + "command": "Invoke-Build -Task BuildNoIntegration -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "build" + }, + { + "label": "IntegrationTest", + "type": "shell", + "command": "Invoke-Build -Task IntegrationTest -File '${workspaceFolder}/src/${workspaceFolderBasename}.build.ps1'", + "problemMatcher": "$msCompile", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "test" + }, + { + "label": "PesterTest", + "type": "shell", + "command": "Invoke-Pester '${workspaceFolder}/src/Tests/Unit' -Output Detailed", + "problemMatcher": "$pester", + "group": "test" + }, + { + "label": "Pester-Single-Coverage", + "type": "shell", + "command": "Import-Module -Name '${workspaceFolder}/src/${workspaceFolderBasename}/${workspaceFolderBasename}.psm1';$pesterConfig = New-PesterConfiguration;$pesterConfig.run.Path = '${workspaceFolder}/src/Tests/Unit/*/${input:functionName}.Tests.ps1';$pesterConfig.CodeCoverage.Enabled = $true;$pesterConfig.CodeCoverage.Path = '${workspaceFolder}/src/${workspaceFolderBasename}/*/${input:functionName}.ps1';Invoke-Pester -Configuration $pesterConfig", + "problemMatcher": "$pester", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "test" + }, + { + "label": "Pester-Single-Detailed", + "type": "shell", + "command": "Import-Module -Name '${workspaceFolder}/src/${workspaceFolderBasename}/${workspaceFolderBasename}.psm1';Invoke-Pester '${workspaceFolder}/src/Tests/Unit/*/${input:functionName}.Tests.ps1' -Output Detailed", + "problemMatcher": "$pester", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "test" + }, + { + "label": "DevCC-Single", + "type": "shell", + "command": "Import-Module -Name '${workspaceFolder}/src/${workspaceFolderBasename}/${workspaceFolderBasename}.psm1';$pesterConfig = New-PesterConfiguration;$pesterConfig.run.Path = '${workspaceFolder}/src/Tests/Unit/*/${input:functionName}.Tests.ps1';$pesterConfig.CodeCoverage.Enabled = $true;$pesterConfig.CodeCoverage.Path = '${workspaceFolder}/src/${workspaceFolderBasename}/*/${input:functionName}.ps1';$pesterConfig.CodeCoverage.OutputPath = '${workspaceFolder}/cov.xml';$pesterConfig.CodeCoverage.OutputFormat = 'CoverageGutters';Invoke-Pester -Configuration $pesterConfig", + "problemMatcher": "$pester", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "test" + }, + { + "label": "Integration-Single-Detailed", + "type": "shell", + "command": "Import-Module -Name '${workspaceFolder}/src/${workspaceFolderBasename}/${workspaceFolderBasename}.psm1';Invoke-Pester '${workspaceFolder}/src/Tests/Integration/*/${input:functionName}.Tests.ps1' -Output Detailed", + "problemMatcher": "$pester", + "presentation": { + "echo": false, + "showReuseMessage": false + }, + "group": "test" + } + ], + "inputs": [ + { + "type": "promptString", + "id": "functionName", + "description": "Name of PowerShell function you want to test" + } + ] } diff --git a/LICENSE b/LICENSE index 45b7e36..2415024 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2025 Luke Evans - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +MIT License + +Copyright (c) 2025 Luke Evans + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 8c66747..d293824 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,73 @@ -# PSReddit - -## Synopsis - -A cross-platform PowerShell 7+ module for authenticating with Reddit and retrieving posts from subreddits. - -## Description - -This module provides cmdlets to authenticate with the Reddit API using OAuth 2.0 client credentials flow and fetch posts based on subreddit, sort order, and timeframe. It requires PowerShell 7+ and relies on environment variables for storing Reddit API client ID and secret. - -## Why - - - -## Documentation - -Full documentation is available at: [https://lukeevanstech.github.io/PSReddit/](https://lukeevanstech.github.io/PSReddit/) - -## Features - -- OAuth authentication (using environment variables for secrets) -- Retrieve posts by subreddit with `Get-RedditSubredditPost` -- Retrieve posts from specific users with `Get-RedditUserPost` -- Support for various sort types (Top, New, Hot, etc.) and timeframes (for Top/Controversial) -- Pester tests for integration testing -- Comprehensive MkDocs documentation - -## Getting Started - -### Prerequisites - -- PowerShell 7+ -- Reddit API Client ID and Secret stored in environment variables: - - `REDDIT_CLIENT_ID` - - `REDDIT_CLIENT_SECRET` - -### Installation - -```powershell -# Import the module locally from the source directory -Import-Module ./src/PSReddit/PSReddit.psd1 -Force -``` - -### Quick start (Usage Example) - -```powershell -# Set environment variables (replace with your actual credentials) -$env:REDDIT_CLIENT_ID = 'your-client-id' -$env:REDDIT_CLIENT_SECRET = 'your-client-secret' - -# Get an OAuth token -$token = Get-RedditOAuthToken - -# Get the top 5 posts from the 'powershell' subreddit from the last day -$posts = Get-RedditSubredditPost -Subreddit 'powershell' -Sort Top -LastDay -Count 5 - -# Display post titles -$posts.title -``` - -## Docs -See the [documentation](./docs/index.md) for full usage, installation, and development information. - -## Development -- Run tests: `Invoke-Pester ./src/Tests -CI` (Ensure Pester module is installed) -- Style Guides: Refer to project configuration files (e.g., `.editorconfig`, `PSScriptAnalyzerSettings.psd1`). -- Contributions welcome! Please follow standard fork & pull request workflow. - -## Author - -Luke Evans +# PSReddit + +## Synopsis + +A cross-platform PowerShell 7+ module for authenticating with Reddit and retrieving posts from subreddits. + +## Description + +This module provides cmdlets to authenticate with the Reddit API using OAuth 2.0 client credentials flow and fetch posts based on subreddit, sort order, and timeframe. It requires PowerShell 7+ and relies on environment variables for storing Reddit API client ID and secret. + +## Why + + + +## Documentation + +Full documentation is available at: [https://lukeevanstech.github.io/PSReddit/](https://lukeevanstech.github.io/PSReddit/) + +## Features + +- OAuth authentication (using environment variables for secrets) +- Retrieve posts by subreddit with `Get-RedditSubredditPost` +- Retrieve posts from specific users with `Get-RedditUserPost` +- Support for various sort types (Top, New, Hot, etc.) and timeframes (for Top/Controversial) +- Pester tests for integration testing +- Comprehensive MkDocs documentation + +## Getting Started + +### Prerequisites + +- PowerShell 7+ +- Reddit API Client ID and Secret stored in environment variables: + - `REDDIT_CLIENT_ID` + - `REDDIT_CLIENT_SECRET` + +### Installation + +```powershell +# Import the module locally from the source directory +Import-Module ./src/PSReddit/PSReddit.psd1 -Force +``` + +### Quick start (Usage Example) + +```powershell +# Set environment variables (replace with your actual credentials) +$env:REDDIT_CLIENT_ID = 'your-client-id' +$env:REDDIT_CLIENT_SECRET = 'your-client-secret' + +# Get an OAuth token +$token = Get-RedditOAuthToken + +# Get the top 5 posts from the 'powershell' subreddit from the last day +$posts = Get-RedditSubredditPost -Subreddit 'powershell' -Sort Top -LastDay -Count 5 + +# Display post titles +$posts.title +``` + +## Docs + +See the [documentation](./docs/index.md) for full usage, installation, and development information. + +## Development + +- Run tests: `Invoke-Pester ./src/Tests -CI` (Ensure Pester module is installed) +- Style Guides: Refer to project configuration files (e.g., `.editorconfig`, `PSScriptAnalyzerSettings.psd1`). +- Contributions welcome! Please follow standard fork & pull request workflow. + +## Author + +Luke Evans diff --git a/actions_bootstrap.ps1 b/actions_bootstrap.ps1 index db2b4a3..24c9869 100644 --- a/actions_bootstrap.ps1 +++ b/actions_bootstrap.ps1 @@ -1,63 +1,63 @@ -# Bootstrap dependencies - -# https://docs.microsoft.com/powershell/module/packagemanagement/get-packageprovider -Get-PackageProvider -Name Nuget -ForceBootstrap | Out-Null - -# https://docs.microsoft.com/powershell/module/powershellget/set-psrepository -Set-PSRepository -Name PSGallery -InstallationPolicy Trusted - -# List of PowerShell Modules required for the build -$modulesToInstall = New-Object System.Collections.Generic.List[object] -# https://github.com/pester/Pester -[void]$modulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'Pester' - ModuleVersion = '5.7.1' - })) -# https://github.com/nightroman/Invoke-Build -[void]$modulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'InvokeBuild' - ModuleVersion = '5.12.1' - })) -# https://github.com/PowerShell/PSScriptAnalyzer -[void]$modulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'PSScriptAnalyzer' - ModuleVersion = '1.23.0' - })) -# https://github.com/PowerShell/platyPS -# older version used due to: https://github.com/PowerShell/platyPS/issues/457 -[void]$modulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'platyPS' - ModuleVersion = '0.12.0' - })) - - - -'Installing PowerShell Modules' -foreach ($module in $modulesToInstall) { - $installSplat = @{ - Name = $module.ModuleName - RequiredVersion = $module.ModuleVersion - Repository = 'PSGallery' - SkipPublisherCheck = $true - Force = $true - ErrorAction = 'Stop' - } - try { - if ($module.ModuleName -eq 'Pester' -and ($IsWindows -or $PSVersionTable.PSVersion -le [version]'5.1')) { - # special case for Pester certificate mismatch with older Pester versions - https://github.com/pester/Pester/issues/2389 - # this only affects windows builds - Install-Module @installSplat -SkipPublisherCheck - } - else { - Install-Module @installSplat - } - Import-Module -Name $module.ModuleName -ErrorAction Stop - ' - Successfully installed {0}' -f $module.ModuleName - } - catch { - $message = 'Failed to install {0}' -f $module.ModuleName - " - $message" - throw - } -} +# Bootstrap dependencies + +# https://docs.microsoft.com/powershell/module/packagemanagement/get-packageprovider +Get-PackageProvider -Name Nuget -ForceBootstrap | Out-Null + +# https://docs.microsoft.com/powershell/module/powershellget/set-psrepository +Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + +# List of PowerShell Modules required for the build +$modulesToInstall = New-Object System.Collections.Generic.List[object] +# https://github.com/pester/Pester +[void]$modulesToInstall.Add(([PSCustomObject]@{ + ModuleName = 'Pester' + ModuleVersion = '5.7.1' + })) +# https://github.com/nightroman/Invoke-Build +[void]$modulesToInstall.Add(([PSCustomObject]@{ + ModuleName = 'InvokeBuild' + ModuleVersion = '5.12.1' + })) +# https://github.com/PowerShell/PSScriptAnalyzer +[void]$modulesToInstall.Add(([PSCustomObject]@{ + ModuleName = 'PSScriptAnalyzer' + ModuleVersion = '1.23.0' + })) +# https://github.com/PowerShell/platyPS +# older version used due to: https://github.com/PowerShell/platyPS/issues/457 +[void]$modulesToInstall.Add(([PSCustomObject]@{ + ModuleName = 'platyPS' + ModuleVersion = '0.12.0' + })) + + + +'Installing PowerShell Modules' +foreach ($module in $modulesToInstall) { + $installSplat = @{ + Name = $module.ModuleName + RequiredVersion = $module.ModuleVersion + Repository = 'PSGallery' + SkipPublisherCheck = $true + Force = $true + ErrorAction = 'Stop' + } + try { + if ($module.ModuleName -eq 'Pester' -and ($IsWindows -or $PSVersionTable.PSVersion -le [version]'5.1')) { + # special case for Pester certificate mismatch with older Pester versions - https://github.com/pester/Pester/issues/2389 + # this only affects windows builds + Install-Module @installSplat -SkipPublisherCheck + } + else { + Install-Module @installSplat + } + Import-Module -Name $module.ModuleName -ErrorAction Stop + ' - Successfully installed {0}' -f $module.ModuleName + } + catch { + $message = 'Failed to install {0}' -f $module.ModuleName + " - $message" + throw + } +} diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md index ecaa21c..bdd2024 100644 --- a/docs/MIGRATION.md +++ b/docs/MIGRATION.md @@ -1,208 +1,224 @@ -# Migration from MkDocs to Zensical - -This document outlines the migration process from MkDocs to Zensical for the PSReddit documentation. - -## Why Zensical? - -Zensical is a modern documentation site generator built by the creators of Material for MkDocs. Key benefits include: - -- **Better Performance**: Optimized for large documentation sites (10,000+ pages) -- **Simpler Configuration**: TOML-based configuration instead of YAML -- **Modern Architecture**: Built on the ZRX differential build engine -- **Maintained by Material Team**: From the creators of Material for MkDocs -- **Native Markdown**: No need for JSX or React knowledge -- **Built-in Material Design**: Modern, responsive theme out of the box - -## What Changed - -### New Files -- `zensical.toml` - Main Zensical configuration (replaces `mkdocs.yml`) -- `docs/package.json` - NPM scripts for running Zensical commands -- `docs/assets/stylesheets/custom.css` - Custom styling to hide Zensical branding - -### Updated Files -- `docs/requirements.txt` - Now uses `zensical>=0.0.4` instead of MkDocs packages -- `.github/workflows/psreddit-deploy-docs.yml` - Updated to use Zensical build process -- `.gitignore` - Added `docs/site/` and `.zensical/` to ignore build artifacts -- `.readthedocs.yaml` - Updated to use Zensical build commands - -### Preserved Files -- All markdown documentation files (`.md`) remain unchanged -- Documentation structure and navigation remain the same -- All content, examples, and code snippets are preserved - -## Configuration Mapping - -### MkDocs → Zensical Configuration - -**Site Information:** -```yaml -# mkdocs.yml -site_name: PSReddit -site_url: https://LukeEvansTech.github.io/PSReddit/ -``` - -```toml -# zensical.toml -[project] -site_name = "PSReddit" -site_url = "https://LukeEvansTech.github.io/PSReddit/" -``` - -**Theme Configuration:** -```yaml -# mkdocs.yml -theme: - name: material - palette: - - media: "(prefers-color-scheme: light)" - scheme: default - primary: blue -``` - -```toml -# zensical.toml -[project.theme] -name = "material" -variant = "modern" - -[[project.theme.palette]] -media = "(prefers-color-scheme: light)" -scheme = "default" -primary = "blue" -``` - -**Navigation:** -```yaml -# mkdocs.yml -nav: - - Home: index.md - - Cmdlets: - - Get-RedditOAuthToken: Get-RedditOAuthToken.md -``` - -```toml -# zensical.toml -[[nav]] -Home = "index.md" - -[[nav]] -Cmdlets = [ - { "Get-RedditOAuthToken" = "Get-RedditOAuthToken.md" } -] -``` - -## Development Workflow Changes - -### Before (MkDocs) -```bash -# Install dependencies -pip install -r docs/requirements.txt - -# Start dev server -mkdocs serve - -# Build documentation -mkdocs build - -# Deploy to GitHub Pages -mkdocs gh-deploy -``` - -### After (Zensical) -```bash -# Install dependencies -pip install -r docs/requirements.txt - -# Start dev server -cd docs -npm start -# or: zensical serve - -# Build documentation -npm run build -# or: zensical build - -# Deploy (handled automatically by GitHub Actions) -``` - -## Deployment Changes - -### GitHub Actions -The workflow now uses a two-job approach: -1. **Build Job**: Builds the site with Zensical and uploads artifact -2. **Deploy Job**: Deploys the artifact to GitHub Pages - -Benefits: -- Better separation of concerns -- Pip caching for faster builds -- Only triggers on changes to `docs/**` or workflow file -- Manual trigger support via `workflow_dispatch` - -### ReadTheDocs -ReadTheDocs configuration updated to use custom build commands: -```yaml -build: - commands: - - pip install -r docs/requirements.txt - - cd docs && zensical build - - mkdir -p $READTHEDOCS_OUTPUT/html - - cp -r docs/site/* $READTHEDOCS_OUTPUT/html/ -``` - -## Breaking Changes - -None! The migration is designed to be seamless: -- All URLs remain the same -- All documentation content is preserved -- Navigation structure unchanged -- Same Material Design theme - -## Rollback Instructions - -If you need to rollback to MkDocs: - -1. Restore the original `mkdocs.yml` file -2. Revert `docs/requirements.txt` to use MkDocs packages -3. Revert `.github/workflows/psreddit-deploy-docs.yml` -4. Delete `zensical.toml` and `docs/package.json` -5. Revert `.readthedocs.yaml` changes - -The original MkDocs configuration can be found in the git history before this migration commit. - -## Testing the Migration - -To verify the migration was successful: - -1. **Local Build Test:** - ```bash - cd docs - pip install -r requirements.txt - zensical build - ``` - -2. **Check Output:** - - Verify `docs/site/` directory was created - - Open `docs/site/index.html` in a browser - - Test all navigation links - - Verify code syntax highlighting works - - Check light/dark mode toggle - -3. **GitHub Actions Test:** - - Push changes to a branch - - Verify the workflow runs successfully - - Check GitHub Pages deployment - -## Resources - -- [Zensical Official Documentation](https://zensical.org/) -- [Material for MkDocs Documentation](https://squidfunk.github.io/mkdocs-material/) -- [github-infrastructure Migration Reference](https://github.com/LukeEvansTech/github-infrastructure/blob/main/docs/MIGRATION.md) - -## Questions or Issues? - -If you encounter any issues with the migration: -1. Check the build logs in GitHub Actions -2. Verify local build works correctly -3. Review the Zensical documentation -4. Compare with the github-infrastructure repository for reference +# Migration from MkDocs to Zensical + +This document outlines the migration process from MkDocs to Zensical for the PSReddit documentation. + +## Why Zensical? + +Zensical is a modern documentation site generator built by the creators of Material for MkDocs. Key benefits include: + +- **Better Performance**: Optimized for large documentation sites (10,000+ pages) +- **Simpler Configuration**: TOML-based configuration instead of YAML +- **Modern Architecture**: Built on the ZRX differential build engine +- **Maintained by Material Team**: From the creators of Material for MkDocs +- **Native Markdown**: No need for JSX or React knowledge +- **Built-in Material Design**: Modern, responsive theme out of the box + +## What Changed + +### New Files + +- `zensical.toml` - Main Zensical configuration (replaces `mkdocs.yml`) +- `docs/package.json` - npm scripts for running Zensical commands +- `docs/assets/stylesheets/custom.css` - Custom styling to hide Zensical branding + +### Updated Files + +- `docs/requirements.txt` - Now uses `zensical>=0.0.4` instead of MkDocs packages +- `.github/workflows/psreddit-deploy-docs.yml` - Updated to use Zensical build process +- `.gitignore` - Added `docs/site/` and `.zensical/` to ignore build artifacts +- `.readthedocs.yaml` - Updated to use Zensical build commands + +### Preserved Files + +- All Markdown documentation files (`.md`) remain unchanged +- Documentation structure and navigation remain the same +- All content, examples, and code snippets are preserved + +## Configuration Mapping + +### MkDocs → Zensical Configuration + +**Site Information:** + +```yaml +# mkdocs.yml +site_name: PSReddit +site_url: https://LukeEvansTech.github.io/PSReddit/ +``` + +```toml +# zensical.toml +[project] +site_name = "PSReddit" +site_url = "https://LukeEvansTech.github.io/PSReddit/" +``` + +**Theme Configuration:** + +```yaml +# mkdocs.yml +theme: + name: material + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: blue +``` + +```toml +# zensical.toml +[project.theme] +name = "material" +variant = "modern" + +[[project.theme.palette]] +media = "(prefers-color-scheme: light)" +scheme = "default" +primary = "blue" +``` + +**Navigation:** + +```yaml +# mkdocs.yml +nav: + - Home: index.md + - Cmdlets: + - Get-RedditOAuthToken: Get-RedditOAuthToken.md +``` + +```toml +# zensical.toml +[[nav]] +Home = "index.md" + +[[nav]] +Cmdlets = [ + { "Get-RedditOAuthToken" = "Get-RedditOAuthToken.md" } +] +``` + +## Development Workflow Changes + +### Before (MkDocs) + +```bash +# Install dependencies +pip install -r docs/requirements.txt + +# Start dev server +mkdocs serve + +# Build documentation +mkdocs build + +# Deploy to GitHub Pages +mkdocs gh-deploy +``` + +### After (Zensical) + +```bash +# Install dependencies +pip install -r docs/requirements.txt + +# Start dev server +cd docs +npm start +# or: zensical serve + +# Build documentation +npm run build +# or: zensical build + +# Deploy (handled automatically by GitHub Actions) +``` + +## Deployment Changes + +### GitHub Actions + +The workflow now uses a two-job approach: + +1. **Build Job**: Builds the site with Zensical and uploads artifact +2. **Deploy Job**: Deploys the artifact to GitHub Pages + +Benefits: + +- Better separation of concerns +- Pip caching for faster builds +- Only triggers on changes to `docs/**` or workflow file +- Manual trigger support via `workflow_dispatch` + +### ReadTheDocs + +ReadTheDocs configuration updated to use custom build commands: + +```yaml +build: + commands: + - pip install -r docs/requirements.txt + - cd docs && zensical build + - mkdir -p $READTHEDOCS_OUTPUT/html + - cp -r docs/site/* $READTHEDOCS_OUTPUT/html/ +``` + +## Breaking Changes + +None! The migration is designed to be seamless: + +- All URLs remain the same +- All documentation content is preserved +- Navigation structure unchanged +- Same Material Design theme + +## Rollback Instructions + +If you need to rollback to MkDocs: + +1. Restore the original `mkdocs.yml` file +2. Revert `docs/requirements.txt` to use MkDocs packages +3. Revert `.github/workflows/psreddit-deploy-docs.yml` +4. Delete `zensical.toml` and `docs/package.json` +5. Revert `.readthedocs.yaml` changes + +The original MkDocs configuration can be found in the Git history before this migration commit. + +## Testing the Migration + +To verify the migration was successful: + +1. **Local Build Test:** + + ```bash + cd docs + pip install -r requirements.txt + zensical build + ``` + +2. **Check Output:** + - Verify `docs/site/` directory was created + - Open `docs/site/index.html` in a browser + - Test all navigation links + - Verify code syntax highlighting works + - Check light/dark mode toggle + +3. **GitHub Actions Test:** + - Push changes to a branch + - Verify the workflow runs successfully + - Check GitHub Pages deployment + +## Resources + +- [Zensical Official Documentation](https://zensical.org/) +- [Material for MkDocs Documentation](https://squidfunk.github.io/mkdocs-material/) +- [github-infrastructure Migration Reference](https://github.com/LukeEvansTech/github-infrastructure/blob/main/docs/MIGRATION.md) + +## Questions or Issues? + +If you encounter any issues with the migration: + +1. Check the build logs in GitHub Actions +2. Verify local build works correctly +3. Review the Zensical documentation +4. Compare with the github-infrastructure repository for reference diff --git a/docs/docs/Get-RedditOAuthToken.md b/docs/docs/Get-RedditOAuthToken.md index d43d1ee..ee79366 100644 --- a/docs/docs/Get-RedditOAuthToken.md +++ b/docs/docs/Get-RedditOAuthToken.md @@ -1,47 +1,47 @@ ---- -external help file: PSReddit-help.xml -Module Name: PSReddit -online version: -schema: 2.0.0 ---- - -# Get-RedditOAuthToken - -## SYNOPSIS - -Retrieves an OAuth2 bearer token from Reddit. - -## SYNTAX - -```powershell -Get-RedditOAuthToken [] -``` - -## DESCRIPTION - -Authenticates with Reddit using client credentials from environment variables (REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET). - -## EXAMPLES - -### EXAMPLE 1 - -```powershell -$token = Get-RedditOAuthToken -``` - -## PARAMETERS - -### CommonParameters - -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. -For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES - -Requires environment variables REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET to be set. - +--- +external help file: PSReddit-help.xml +Module Name: PSReddit +online version: +schema: 2.0.0 +--- + +# Get-RedditOAuthToken + +## SYNOPSIS + +Retrieves an OAuth2 bearer token from Reddit. + +## SYNTAX + +```powershell +Get-RedditOAuthToken [] +``` + +## DESCRIPTION + +Authenticates with Reddit using client credentials from environment variables (REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET). + +## EXAMPLES + +### EXAMPLE 1 + +```powershell +$token = Get-RedditOAuthToken +``` + +## PARAMETERS + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. +For more information, see about_CommonParameters (). + +## INPUTS + +## OUTPUTS + +## NOTES + +Requires environment variables REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET to be set. + ## RELATED LINKS diff --git a/docs/docs/Get-RedditSubredditPost.md b/docs/docs/Get-RedditSubredditPost.md index d2135c4..50d637c 100644 --- a/docs/docs/Get-RedditSubredditPost.md +++ b/docs/docs/Get-RedditSubredditPost.md @@ -1,222 +1,222 @@ ---- -external help file: PSReddit-help.xml -Module Name: PSReddit -online version: -schema: 2.0.0 ---- - -# Get-RedditSubredditPost - -## SYNOPSIS - -Retrieves posts from one or more subreddits. - -## SYNTAX - -```powershell -Get-RedditSubredditPost [-Subreddit] [[-Sort] ] [-LastHour] [-LastDay] [-LastWeek] - [-LastMonth] [-LastYear] [-AllTime] [[-Count] ] [-DebugApi] - [] -``` - -## DESCRIPTION - -Uses Reddit's API and OAuth2 to fetch posts from specified subreddit(s). -You can specify the sort type (Top, New, Rising, Hot, Controversial). -Timeframe switches (-LastHour, -LastDay, etc.) only apply to 'Top' and 'Controversial' sorts, per Reddit API rules. -Only one timeframe or sort can be used per call, as the Reddit API does not support combining them. -Use -Count to specify how many posts to retrieve (max 100). - -## EXAMPLES - -### EXAMPLE 1 - -```powershell -Get-RedditSubredditPost -Subreddit 'powershell' -Sort New -Count 10 -``` - -### EXAMPLE 2 - -```powershell -Get-RedditSubredditPost -Subreddit 'powershell' -Sort Top -LastWeek -``` - -## PARAMETERS - -### -Subreddit - -One or more subreddit names (without /r/). - -```yaml -Type: String[] -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Sort - -Sort type: Top, New, Rising, Hot, or Controversial. -Default is Top. - -```yaml -Type: String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 2 -Default value: Top -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastHour - -Retrieve posts from the last hour (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastDay - -Retrieve posts from the last day (default; Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastWeek - -Retrieve posts from the last week (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastMonth - -Retrieve posts from the last month (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastYear - -Retrieve posts from the last year (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -AllTime - -Retrieve posts from all time (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Count - -Number of posts to retrieve per subreddit (max 100, default 25). - -```yaml -Type: Int32 -Parameter Sets: (All) -Aliases: - -Required: False -Position: 3 -Default value: 25 -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DebugApi - -If specified, outputs verbose debugging information about the API requests and responses. - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters - -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. -For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES - -Reddit's API only allows one sort and (if applicable) one timeframe per request. -Timeframes only apply to 'Top' and 'Controversial' sorts. -Other sorts (New, Rising, Hot) ignore timeframe and always return the latest/rising/hot posts. - +--- +external help file: PSReddit-help.xml +Module Name: PSReddit +online version: +schema: 2.0.0 +--- + +# Get-RedditSubredditPost + +## SYNOPSIS + +Retrieves posts from one or more subreddits. + +## SYNTAX + +```powershell +Get-RedditSubredditPost [-Subreddit] [[-Sort] ] [-LastHour] [-LastDay] [-LastWeek] + [-LastMonth] [-LastYear] [-AllTime] [[-Count] ] [-DebugApi] + [] +``` + +## DESCRIPTION + +Uses Reddit's API and OAuth2 to fetch posts from specified subreddit(s). +You can specify the sort type (Top, New, Rising, Hot, Controversial). +Timeframe switches (-LastHour, -LastDay, etc.) only apply to 'Top' and 'Controversial' sorts, per Reddit API rules. +Only one timeframe or sort can be used per call, as the Reddit API does not support combining them. +Use -Count to specify how many posts to retrieve (max 100). + +## EXAMPLES + +### EXAMPLE 1 + +```powershell +Get-RedditSubredditPost -Subreddit 'powershell' -Sort New -Count 10 +``` + +### EXAMPLE 2 + +```powershell +Get-RedditSubredditPost -Subreddit 'powershell' -Sort Top -LastWeek +``` + +## PARAMETERS + +### -Subreddit + +One or more subreddit names (without /r/). + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Sort + +Sort type: Top, New, Rising, Hot, or Controversial. +Default is Top. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: Top +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastHour + +Retrieve posts from the last hour (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastDay + +Retrieve posts from the last day (default; Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastWeek + +Retrieve posts from the last week (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastMonth + +Retrieve posts from the last month (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastYear + +Retrieve posts from the last year (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AllTime + +Retrieve posts from all time (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Count + +Number of posts to retrieve per subreddit (max 100, default 25). + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: 25 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DebugApi + +If specified, outputs verbose debugging information about the API requests and responses. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. +For more information, see about_CommonParameters (). + +## INPUTS + +## OUTPUTS + +## NOTES + +Reddit's API only allows one sort and (if applicable) one timeframe per request. +Timeframes only apply to 'Top' and 'Controversial' sorts. +Other sorts (New, Rising, Hot) ignore timeframe and always return the latest/rising/hot posts. + ## RELATED LINKS diff --git a/docs/docs/Get-RedditUserPost.md b/docs/docs/Get-RedditUserPost.md index 5abfe66..edc8601 100644 --- a/docs/docs/Get-RedditUserPost.md +++ b/docs/docs/Get-RedditUserPost.md @@ -1,219 +1,219 @@ ---- -external help file: PSReddit-help.xml -Module Name: PSReddit -online version: -schema: 2.0.0 ---- - -# Get-RedditUserPost - -## SYNOPSIS - -Retrieves posts submitted by specific Reddit users. - -## SYNTAX - -```powershell -Get-RedditUserPost [-Username] [[-Sort] ] [-LastHour] [-LastDay] [-LastWeek] [-LastMonth] - [-LastYear] [-AllTime] [[-Count] ] [-DebugApi] - [] -``` - -## DESCRIPTION - -Uses Reddit's API and OAuth2 to fetch posts made by specified Reddit user(s). -You can specify the sort type (Top, New, Hot, Controversial) and limit the number of posts. -Timeframe switches (-LastHour, -LastDay, etc.) apply to 'Top' and 'Controversial' sorts, per Reddit API rules. - -## EXAMPLES - -### EXAMPLE 1 - -```powershell -Get-RedditUserPost -Username 'LukeEvansTech' -Sort New -Count 10 -``` - -### EXAMPLE 2 - -```powershell -Get-RedditUserPost -Username 'LukeEvansTech' -Sort Top -LastWeek -``` - -## PARAMETERS - -### -Username - -One or more Reddit usernames (without u/). - -```yaml -Type: String[] -Parameter Sets: (All) -Aliases: - -Required: True -Position: 1 -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Sort - -Sort type: Top, New, Hot, or Controversial. -Default is New. - -```yaml -Type: String -Parameter Sets: (All) -Aliases: - -Required: False -Position: 2 -Default value: New -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastHour - -Retrieve posts from the last hour (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastDay - -Retrieve posts from the last day (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastWeek - -Retrieve posts from the last week (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastMonth - -Retrieve posts from the last month (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -LastYear - -Retrieve posts from the last year (Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -AllTime - -Retrieve posts from all time (default; Top/Controversial only). - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Count - -Number of posts to retrieve per user (max 100, default 25). - -```yaml -Type: Int32 -Parameter Sets: (All) -Aliases: - -Required: False -Position: 3 -Default value: 25 -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -DebugApi - -If specified, outputs verbose debugging information about the API requests and responses. - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: False -Accept pipeline input: False -Accept wildcard characters: False -``` - -### CommonParameters - -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. -For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -## OUTPUTS - -## NOTES - -Reddit's API only allows one sort and (if applicable) one timeframe per request. -Timeframes only apply to 'Top' and 'Controversial' sorts. - +--- +external help file: PSReddit-help.xml +Module Name: PSReddit +online version: +schema: 2.0.0 +--- + +# Get-RedditUserPost + +## SYNOPSIS + +Retrieves posts submitted by specific Reddit users. + +## SYNTAX + +```powershell +Get-RedditUserPost [-Username] [[-Sort] ] [-LastHour] [-LastDay] [-LastWeek] [-LastMonth] + [-LastYear] [-AllTime] [[-Count] ] [-DebugApi] + [] +``` + +## DESCRIPTION + +Uses Reddit's API and OAuth2 to fetch posts made by specified Reddit user(s). +You can specify the sort type (Top, New, Hot, Controversial) and limit the number of posts. +Timeframe switches (-LastHour, -LastDay, etc.) apply to 'Top' and 'Controversial' sorts, per Reddit API rules. + +## EXAMPLES + +### EXAMPLE 1 + +```powershell +Get-RedditUserPost -Username 'LukeEvansTech' -Sort New -Count 10 +``` + +### EXAMPLE 2 + +```powershell +Get-RedditUserPost -Username 'LukeEvansTech' -Sort Top -LastWeek +``` + +## PARAMETERS + +### -Username + +One or more Reddit usernames (without u/). + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Sort + +Sort type: Top, New, Hot, or Controversial. +Default is New. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: New +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastHour + +Retrieve posts from the last hour (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastDay + +Retrieve posts from the last day (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastWeek + +Retrieve posts from the last week (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastMonth + +Retrieve posts from the last month (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -LastYear + +Retrieve posts from the last year (Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AllTime + +Retrieve posts from all time (default; Top/Controversial only). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Count + +Number of posts to retrieve per user (max 100, default 25). + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: 25 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DebugApi + +If specified, outputs verbose debugging information about the API requests and responses. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutBuffer, -OutVariable, -PipelineVariable, -Verbose, -WarningAction, -WarningVariable, and -ProgressAction. +For more information, see about_CommonParameters (). + +## INPUTS + +## OUTPUTS + +## NOTES + +Reddit's API only allows one sort and (if applicable) one timeframe per request. +Timeframes only apply to 'Top' and 'Controversial' sorts. + ## RELATED LINKS diff --git a/docs/docs/assets/stylesheets/custom.css b/docs/docs/assets/stylesheets/custom.css index df2845a..6573741 100644 --- a/docs/docs/assets/stylesheets/custom.css +++ b/docs/docs/assets/stylesheets/custom.css @@ -1,14 +1,14 @@ -/* Custom styles for PSReddit documentation */ - -/* Hide Zensical branding in footer */ -.md-footer-meta__inner .md-copyright a[href*="zensical"] { - display: none !important; -} - -.md-footer-meta__inner .md-copyright > br { - display: none !important; -} - -.md-footer-meta__inner .md-copyright > a[href*="zensical"] ~ * { - display: none !important; -} +/* Custom styles for PSReddit documentation */ + +/* Hide Zensical branding in footer */ +.md-footer-meta__inner .md-copyright a[href*="zensical"] { + display: none !important; +} + +.md-footer-meta__inner .md-copyright > br { + display: none !important; +} + +.md-footer-meta__inner .md-copyright > a[href*="zensical"] ~ * { + display: none !important; +} diff --git a/docs/docs/module-manifest.md b/docs/docs/module-manifest.md index c3d30e5..5e7d64d 100644 --- a/docs/docs/module-manifest.md +++ b/docs/docs/module-manifest.md @@ -1,21 +1,27 @@ ---- -Module Name: PSReddit -Module Guid: b8f3e5d1-2b7b-4e8b-9c6e-123456789abc -Download Help Link: NA -Help Version: 0.1.0 -Locale: en-US ---- - -# PSReddit Module -## Description -A PowerShell module (PSReddit) for authenticating with Reddit and retrieving posts. - -## PSReddit Cmdlets -### [Get-RedditOAuthToken](Get-RedditOAuthToken.md) -Retrieves an OAuth2 bearer token from Reddit. - -### [Get-RedditSubredditPost](Get-RedditSubredditPost.md) -Retrieves posts from one or more subreddits. - -### [Get-RedditUserPost](Get-RedditUserPost.md) +--- +Module Name: PSReddit +Module Guid: b8f3e5d1-2b7b-4e8b-9c6e-123456789abc +Download Help Link: NA +Help Version: 0.1.0 +Locale: en-US +--- + +# PSReddit Module + +## Description + +A PowerShell module (PSReddit) for authenticating with Reddit and retrieving posts. + +## PSReddit Cmdlets + +### [Get-RedditOAuthToken](Get-RedditOAuthToken.md) + +Retrieves an OAuth2 bearer token from Reddit. + +### [Get-RedditSubredditPost](Get-RedditSubredditPost.md) + +Retrieves posts from one or more subreddits. + +### [Get-RedditUserPost](Get-RedditUserPost.md) + Retrieves posts submitted by specific Reddit users. diff --git a/docs/package.json b/docs/package.json index 36e0f5e..f14b689 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,14 +1,14 @@ -{ - "name": "psreddit-docs", - "version": "0.0.0", - "private": true, - "scripts": { - "start": "zensical serve", - "build": "zensical build", - "serve": "zensical serve", - "clean": "zensical build --clean" - }, - "engines": { - "node": ">=20.0" - } -} +{ + "name": "psreddit-docs", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "zensical serve", + "build": "zensical build", + "serve": "zensical serve", + "clean": "zensical build --clean" + }, + "engines": { + "node": ">=20.0" + } +} diff --git a/src/MarkdownRepair.ps1 b/src/MarkdownRepair.ps1 index cedfac8..c973c64 100644 --- a/src/MarkdownRepair.ps1 +++ b/src/MarkdownRepair.ps1 @@ -1,136 +1,136 @@ -<# -.SYNOPSIS - Repair PlatyPS generated markdown files. -.NOTES - This file is temporarily required to handle platyPS help generation. - https://github.com/PowerShell/platyPS/issues/595 - This is a result of a breaking change introduced in PowerShell 7.4.0: - https://learn.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-74?view=powershell-7.4 - Breaking Changes: Added the ProgressAction parameter to the Common Parameters - modified from source: https://github.com/PowerShell/platyPS/issues/595#issuecomment-1820971702 -#> - -function Remove-CommonParameterFromMarkdown { - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] - <# - .SYNOPSIS - Remove a PlatyPS generated parameter block. - .DESCRIPTION - Removes parameter block for the provided parameter name from the markdown file provided. - #> - param( - [Parameter(Mandatory)] - [string[]] - $Path, - - [Parameter(Mandatory = $false)] - [string[]] - $ParameterName = @('ProgressAction') - ) - $ErrorActionPreference = 'Stop' - foreach ($p in $Path) { - $content = (Get-Content -Path $p -Raw).TrimEnd() - $updateFile = $false - foreach ($param in $ParameterName) { - if (-not ($Param.StartsWith('-'))) { - $param = "-$($param)" - } - # Remove the parameter block - $pattern = "(?m)^### $param\r?\n[\S\s]*?(?=#{2,3}?)" - $newContent = $content -replace $pattern, '' - # Remove the parameter from the syntax block - $pattern = " \[$param\s?.*?]" - $newContent = $newContent -replace $pattern, '' - if ($null -ne (Compare-Object -ReferenceObject $content -DifferenceObject $newContent)) { - Write-Verbose "Added $param to $p" - # Update file content - $content = $newContent - $updateFile = $true - } - } - # Save file if content has changed - if ($updateFile) { - $newContent | Out-File -Encoding utf8 -FilePath $p - Write-Verbose "Updated file: $p" - } - } - return -} - -function Add-MissingCommonParameterToMarkdown { - param( - [Parameter(Mandatory)] - [string[]] - $Path, - - [Parameter(Mandatory = $false)] - [string[]] - $ParameterName = @('ProgressAction') - ) - $ErrorActionPreference = 'Stop' - foreach ($p in $Path) { - $content = (Get-Content -Path $p -Raw).TrimEnd() - $updateFile = $false - foreach ($NewParameter in $ParameterName) { - if (-not ($NewParameter.StartsWith('-'))) { - $NewParameter = "-$($NewParameter)" - } - $pattern = '(?m)^This cmdlet supports the common parameters:(.+?)\.' - $replacement = { - $Params = $_.Groups[1].Captures[0].ToString() -split ' ' - $CommonParameters = @() - foreach ($CommonParameter in $Params) { - if ($CommonParameter.StartsWith('-')) { - if ($CommonParameter.EndsWith(',')) { - $CleanParam = $CommonParameter.Substring(0, $CommonParameter.Length - 1) - } - elseif ($p.EndsWith('.')) { - $CleanParam = $CommonParameter.Substring(0, $CommonParameter.Length - 1) - } - else { - $CleanParam = $CommonParameter - } - $CommonParameters += $CleanParam - } - } - if ($NewParameter -notin $CommonParameters) { - $CommonParameters += $NewParameter - } - $CommonParameters[-1] = "and $($CommonParameters[-1]). " - return "This cmdlet supports the common parameters: " + (($CommonParameters | Sort-Object) -join ', ') - } - $newContent = $content -replace $pattern, $replacement - if ($null -ne (Compare-Object -ReferenceObject $content -DifferenceObject $newContent)) { - Write-Verbose "Added $NewParameter to $p" - $updateFile = $true - $content = $newContent - } - } - # Save file if content has changed - if ($updateFile) { - $newContent | Out-File -Encoding utf8 -FilePath $p - Write-Verbose "Updated file: $p" - } - } - return -} - -function Repair-PlatyPSMarkdown { - param( - [Parameter(Mandatory)] - [string[]] - $Path, - - [Parameter()] - [string[]] - $ParameterName = @('ProgressAction') - ) - $ErrorActionPreference = 'Stop' - $Parameters = @{ - Path = $Path - ParameterName = $ParameterName - } - $null = Remove-CommonParameterFromMarkdown @Parameters - $null = Add-MissingCommonParameterToMarkdown @Parameters - return -} +<# +.SYNOPSIS + Repair PlatyPS generated markdown files. +.NOTES + This file is temporarily required to handle platyPS help generation. + https://github.com/PowerShell/platyPS/issues/595 + This is a result of a breaking change introduced in PowerShell 7.4.0: + https://learn.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-74?view=powershell-7.4 + Breaking Changes: Added the ProgressAction parameter to the Common Parameters + modified from source: https://github.com/PowerShell/platyPS/issues/595#issuecomment-1820971702 +#> + +function Remove-CommonParameterFromMarkdown { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] + <# + .SYNOPSIS + Remove a PlatyPS generated parameter block. + .DESCRIPTION + Removes parameter block for the provided parameter name from the markdown file provided. + #> + param( + [Parameter(Mandatory)] + [string[]] + $Path, + + [Parameter(Mandatory = $false)] + [string[]] + $ParameterName = @('ProgressAction') + ) + $ErrorActionPreference = 'Stop' + foreach ($p in $Path) { + $content = (Get-Content -Path $p -Raw).TrimEnd() + $updateFile = $false + foreach ($param in $ParameterName) { + if (-not ($Param.StartsWith('-'))) { + $param = "-$($param)" + } + # Remove the parameter block + $pattern = "(?m)^### $param\r?\n[\S\s]*?(?=#{2,3}?)" + $newContent = $content -replace $pattern, '' + # Remove the parameter from the syntax block + $pattern = " \[$param\s?.*?]" + $newContent = $newContent -replace $pattern, '' + if ($null -ne (Compare-Object -ReferenceObject $content -DifferenceObject $newContent)) { + Write-Verbose "Added $param to $p" + # Update file content + $content = $newContent + $updateFile = $true + } + } + # Save file if content has changed + if ($updateFile) { + $newContent | Out-File -Encoding utf8 -FilePath $p + Write-Verbose "Updated file: $p" + } + } + return +} + +function Add-MissingCommonParameterToMarkdown { + param( + [Parameter(Mandatory)] + [string[]] + $Path, + + [Parameter(Mandatory = $false)] + [string[]] + $ParameterName = @('ProgressAction') + ) + $ErrorActionPreference = 'Stop' + foreach ($p in $Path) { + $content = (Get-Content -Path $p -Raw).TrimEnd() + $updateFile = $false + foreach ($NewParameter in $ParameterName) { + if (-not ($NewParameter.StartsWith('-'))) { + $NewParameter = "-$($NewParameter)" + } + $pattern = '(?m)^This cmdlet supports the common parameters:(.+?)\.' + $replacement = { + $Params = $_.Groups[1].Captures[0].ToString() -split ' ' + $CommonParameters = @() + foreach ($CommonParameter in $Params) { + if ($CommonParameter.StartsWith('-')) { + if ($CommonParameter.EndsWith(',')) { + $CleanParam = $CommonParameter.Substring(0, $CommonParameter.Length - 1) + } + elseif ($p.EndsWith('.')) { + $CleanParam = $CommonParameter.Substring(0, $CommonParameter.Length - 1) + } + else { + $CleanParam = $CommonParameter + } + $CommonParameters += $CleanParam + } + } + if ($NewParameter -notin $CommonParameters) { + $CommonParameters += $NewParameter + } + $CommonParameters[-1] = "and $($CommonParameters[-1]). " + return "This cmdlet supports the common parameters: " + (($CommonParameters | Sort-Object) -join ', ') + } + $newContent = $content -replace $pattern, $replacement + if ($null -ne (Compare-Object -ReferenceObject $content -DifferenceObject $newContent)) { + Write-Verbose "Added $NewParameter to $p" + $updateFile = $true + $content = $newContent + } + } + # Save file if content has changed + if ($updateFile) { + $newContent | Out-File -Encoding utf8 -FilePath $p + Write-Verbose "Updated file: $p" + } + } + return +} + +function Repair-PlatyPSMarkdown { + param( + [Parameter(Mandatory)] + [string[]] + $Path, + + [Parameter()] + [string[]] + $ParameterName = @('ProgressAction') + ) + $ErrorActionPreference = 'Stop' + $Parameters = @{ + Path = $Path + ParameterName = $ParameterName + } + $null = Remove-CommonParameterFromMarkdown @Parameters + $null = Add-MissingCommonParameterToMarkdown @Parameters + return +} diff --git a/src/PSReddit.Settings.ps1 b/src/PSReddit.Settings.ps1 index a0e647a..dc0e7d9 100644 --- a/src/PSReddit.Settings.ps1 +++ b/src/PSReddit.Settings.ps1 @@ -1,2 +1,2 @@ -# specify the minimum required major PowerShell version that the build script should validate -[version]$script:requiredPSVersion = '5.1.0' +# specify the minimum required major PowerShell version that the build script should validate +[version]$script:requiredPSVersion = '5.1.0' diff --git a/src/PSReddit.build.ps1 b/src/PSReddit.build.ps1 index 0a2e952..c6c064f 100644 --- a/src/PSReddit.build.ps1 +++ b/src/PSReddit.build.ps1 @@ -1,618 +1,618 @@ -<# -.SYNOPSIS - An Invoke-Build Build file. -.DESCRIPTION - Build steps can include: - - ValidateRequirements - - ImportModuleManifest - - Clean - - Analyze - - FormattingCheck - - Test - - DevCC - - CreateHelpStart - - Build - - IntegrationTest - - Archive -.EXAMPLE - Invoke-Build - - This will perform the default build Add-BuildTasks: see below for the default Add-BuildTask execution -.EXAMPLE - Invoke-Build -Add-BuildTask Analyze,Test - - This will perform only the Analyze and Test Add-BuildTasks. -.NOTES - This build file by Catesta will pull in configurations from the ".Settings.ps1" file as well, where users can more easily customize the build process if required. - https://github.com/nightroman/Invoke-Build - https://github.com/nightroman/Invoke-Build/wiki/Build-Scripts-Guidelines - If using VSCode you can use the generated tasks.json to execute the various tasks in this build file. - Ctrl + P | then type task (add space) - you will then be presented with a list of available tasks to run - The 'InstallDependencies' Add-BuildTask isn't present here. - Module dependencies are installed at a previous step in the pipeline. - If your manifest has module dependencies include all required modules in your CI/CD bootstrap file: - AWS - install_modules.ps1 - Azure - actions_bootstrap.ps1 - GitHub Actions - actions_bootstrap.ps1 - AppVeyor - actions_bootstrap.ps1 -#> - -#Include: Settings -$ModuleName = [regex]::Match((Get-Item $BuildFile).Name, '^(.*)\.build\.ps1$').Groups[1].Value -. "./$ModuleName.Settings.ps1" - -function Test-ManifestBool ($Path) { - Get-ChildItem $Path | Test-ModuleManifest -ErrorAction SilentlyContinue | Out-Null; $? -} - -#Default Build -$str = @() -$str = 'Clean', 'ValidateRequirements', 'ImportModuleManifest' -$str += 'FormattingCheck' -$str += 'Analyze', 'Test' -$str += 'CreateHelpStart' -$str2 = $str -$str2 += 'Build', 'Archive' -$str += 'Build', 'IntegrationTest', 'Archive' -Add-BuildTask -Name . -Jobs $str - -#Local testing build process -Add-BuildTask TestLocal Clean, ImportModuleManifest, Analyze, Test - -#Local help file creation process -Add-BuildTask HelpLocal Clean, ImportModuleManifest, CreateHelpStart - -#Full build sans integration tests -Add-BuildTask BuildNoIntegration -Jobs $str2 - -# Pre-build variables to be used by other portions of the script -Enter-Build { - $script:ModuleName = [regex]::Match((Get-Item $BuildFile).Name, '^(.*)\.build\.ps1$').Groups[1].Value - - # Identify other required paths - $script:ModuleSourcePath = Join-Path -Path $BuildRoot -ChildPath $script:ModuleName - $script:ModuleFiles = Join-Path -Path $script:ModuleSourcePath -ChildPath '*' - - $script:ModuleManifestFile = Join-Path -Path $script:ModuleSourcePath -ChildPath "$($script:ModuleName).psd1" - - $manifestInfo = Import-PowerShellDataFile -Path $script:ModuleManifestFile - $script:ModuleVersion = $manifestInfo.ModuleVersion - $script:ModuleDescription = $manifestInfo.Description - $script:FunctionsToExport = $manifestInfo.FunctionsToExport - - $script:TestsPath = Join-Path -Path $BuildRoot -ChildPath 'Tests' - $script:UnitTestsPath = Join-Path -Path $script:TestsPath -ChildPath 'Unit' - $script:IntegrationTestsPath = Join-Path -Path $script:TestsPath -ChildPath 'Integration' - - $script:ArtifactsPath = Join-Path -Path $BuildRoot -ChildPath 'Artifacts' - $script:ArchivePath = Join-Path -Path $BuildRoot -ChildPath 'Archive' - - $script:BuildModuleRootFile = Join-Path -Path $script:ArtifactsPath -ChildPath "$($script:ModuleName).psm1" - - # Ensure our builds fail until if below a minimum defined code test coverage threshold - $script:coverageThreshold = 30 - - [version]$script:MinPesterVersion = '5.2.2' - [version]$script:MaxPesterVersion = '5.99.99' - $script:testOutputFormat = 'NUnitXML' -} #Enter-Build - -# Define headers as separator, task path, synopsis, and location, e.g. for Ctrl+Click in VSCode. -# Also change the default color to Green. If you need task start times, use `$Task.Started`. -Set-BuildHeader { - param($Path) - # separator line - Write-Build DarkMagenta ('=' * 79) - # default header + synopsis - Write-Build DarkGray "Task $Path : $(Get-BuildSynopsis $Task)" - # task location in a script - Write-Build DarkGray "At $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" - Write-Build Yellow "Manifest File: $script:ModuleManifestFile" - Write-Build Yellow "Manifest Version: $($manifestInfo.ModuleVersion)" -} #Set-BuildHeader - -# Define footers similar to default but change the color to DarkGray. -Set-BuildFooter { - param($Path) - Write-Build DarkGray "Done $Path, $($Task.Elapsed)" - # # separator line - # Write-Build Gray ('=' * 79) -} #Set-BuildFooter - -#Synopsis: Validate system requirements are met -Add-BuildTask ValidateRequirements { - # this setting comes from the *.Settings.ps1 - Write-Build White " Verifying at least PowerShell $script:requiredPSVersion..." - Assert-Build ($PSVersionTable.PSVersion -ge $script:requiredPSVersion) "At least Powershell $script:requiredPSVersion is required for this build to function properly" - Write-Build Green ' ...Verification Complete!' -} #ValidateRequirements - -# Synopsis: Import the current module manifest file for processing -Add-BuildTask TestModuleManifest -Before ImportModuleManifest { - Write-Build White ' Running module manifest tests...' - Assert-Build (Test-Path $script:ModuleManifestFile) 'Unable to locate the module manifest file.' - Assert-Build (Test-ManifestBool -Path $script:ModuleManifestFile) 'Module Manifest test did not pass verification.' - Write-Build Green ' ...Module Manifest Verification Complete!' -} #f5b33218-bde4-4028-b2a1-9c206f089503 - -# Synopsis: Load the module project -Add-BuildTask ImportModuleManifest { - Write-Build White ' Attempting to load the project module.' - try { - Import-Module $script:ModuleManifestFile -Force -PassThru -ErrorAction Stop - } catch { - Write-Build Red " ...$_`n" - throw 'Unable to load the project module' - } - Write-Build Green " ...$script:ModuleName imported successfully" -} - -#Synopsis: Clean and reset Artifacts/Archive Directory -Add-BuildTask Clean { - Write-Build White ' Clean up our Artifacts/Archive directory...' - - $null = Remove-Item $script:ArtifactsPath -Force -Recurse -ErrorAction 0 - $null = New-Item $script:ArtifactsPath -ItemType:Directory - $null = Remove-Item $script:ArchivePath -Force -Recurse -ErrorAction 0 - $null = New-Item $script:ArchivePath -ItemType:Directory - - Write-Build Green ' ...Clean Complete!' -} #Clean - -#Synopsis: Invokes PSScriptAnalyzer against the Module source path -Add-BuildTask Analyze { - - $scriptAnalyzerParams = @{ - Path = $script:ModuleSourcePath - Setting = 'PSScriptAnalyzerSettings.psd1' - Recurse = $true - Verbose = $false - } - - Write-Build White ' Performing Module ScriptAnalyzer checks...' - $scriptAnalyzerResults = Invoke-ScriptAnalyzer @scriptAnalyzerParams - - if ($scriptAnalyzerResults) { - $scriptAnalyzerResults | Format-Table - throw ' One or more PSScriptAnalyzer errors/warnings where found.' - } else { - Write-Build Green ' ...Module Analyze Complete!' - } -} #Analyze - -#Synopsis: Invokes Script Analyzer against the Tests path if it exists -Add-BuildTask AnalyzeTests -After Analyze { - if (Test-Path -Path $script:TestsPath) { - - $scriptAnalyzerParams = @{ - Path = $script:TestsPath - Setting = 'PSScriptAnalyzerSettings.psd1' - ExcludeRule = 'PSUseDeclaredVarsMoreThanAssignments' - Recurse = $true - Verbose = $false - } - - Write-Build White ' Performing Test ScriptAnalyzer checks...' - $scriptAnalyzerResults = Invoke-ScriptAnalyzer @scriptAnalyzerParams - - if ($scriptAnalyzerResults) { - $scriptAnalyzerResults | Format-Table - throw ' One or more PSScriptAnalyzer errors/warnings where found.' - } else { - Write-Build Green ' ...Test Analyze Complete!' - } - } -} #AnalyzeTests - -#Synopsis: Analyze scripts to verify if they adhere to desired coding format (Stroustrup / OTBS / Allman) -Add-BuildTask FormattingCheck { - - - $scriptAnalyzerParams = @{ - Setting = 'CodeFormattingOTBS' - ExcludeRule = 'PSUseConsistentWhitespace' - Recurse = $true - Verbose = $false - } - - - Write-Build White ' Performing script formatting checks...' - $scriptAnalyzerResults = Get-ChildItem -Path $script:ModuleSourcePath -Exclude "*.psd1" | Invoke-ScriptAnalyzer @scriptAnalyzerParams - - if ($scriptAnalyzerResults) { - $scriptAnalyzerResults | Format-Table - throw ' PSScriptAnalyzer code formatting check did not adhere to {0} standards' -f $scriptAnalyzerParams.Setting - } else { - Write-Build Green ' ...Formatting Analyze Complete!' - } -} #FormattingCheck - -#Synopsis: Invokes all Pester Unit Tests in the Tests\Unit folder (if it exists) -Add-BuildTask Test { - - Write-Build White " Importing desired Pester version. Min: $script:MinPesterVersion Max: $script:MaxPesterVersion" - Remove-Module -Name Pester -Force -ErrorAction 'SilentlyContinue'# there are instances where some containers have Pester already in the session - Import-Module -Name Pester -MinimumVersion $script:MinPesterVersion -MaximumVersion $script:MaxPesterVersion -ErrorAction 'Stop' - - $codeCovPath = "$script:ArtifactsPath\ccReport\" - $testOutPutPath = "$script:ArtifactsPath\testOutput\" - if (-not(Test-Path $codeCovPath)) { - New-Item -Path $codeCovPath -ItemType Directory | Out-Null - } - if (-not(Test-Path $testOutPutPath)) { - New-Item -Path $testOutPutPath -ItemType Directory | Out-Null - } - if (Test-Path -Path $script:UnitTestsPath) { - $pesterConfiguration = New-PesterConfiguration - $pesterConfiguration.run.Path = $script:UnitTestsPath - $pesterConfiguration.Run.PassThru = $true - $pesterConfiguration.Run.Exit = $false - $pesterConfiguration.CodeCoverage.Enabled = $true - $pesterConfiguration.CodeCoverage.Path = "$script:ModuleSourcePath\*\*.ps1" - $pesterConfiguration.CodeCoverage.CoveragePercentTarget = $script:coverageThreshold - $pesterConfiguration.CodeCoverage.OutputPath = "$codeCovPath\CodeCoverage.xml" - $pesterConfiguration.CodeCoverage.OutputFormat = 'JaCoCo' - $pesterConfiguration.TestResult.Enabled = $true - $pesterConfiguration.TestResult.OutputPath = "$testOutPutPath\PesterTests.xml" - $pesterConfiguration.TestResult.OutputFormat = $script:testOutputFormat - $pesterConfiguration.Output.Verbosity = 'Detailed' - - Write-Build White ' Performing Pester Unit Tests...' - # Publish Test Results - $testResults = Invoke-Pester -Configuration $pesterConfiguration - - # This will output a nice json for each failed test (if running in CodeBuild) - if ($env:CODEBUILD_BUILD_ARN) { - $testResults.TestResult | ForEach-Object { - if ($_.Result -ne 'Passed') { - ConvertTo-Json -InputObject $_ -Compress - } - } - } - - $numberFails = $testResults.FailedCount - Assert-Build($numberFails -eq 0) ('Failed "{0}" unit tests.' -f $numberFails) - - Write-Build Gray (' ...CODE COVERAGE - CommandsExecutedCount: {0}' -f $testResults.CodeCoverage.CommandsExecutedCount) - Write-Build Gray (' ...CODE COVERAGE - CommandsAnalyzedCount: {0}' -f $testResults.CodeCoverage.CommandsAnalyzedCount) - - if ($testResults.CodeCoverage.NumberOfCommandsExecuted -ne 0) { - $coveragePercent = '{0:N2}' -f ($testResults.CodeCoverage.CommandsExecutedCount / $testResults.CodeCoverage.CommandsAnalyzedCount * 100) - - <# - if ($testResults.CodeCoverage.NumberOfCommandsMissed -gt 0) { - 'Failed to analyze "{0}" commands' -f $testResults.CodeCoverage.NumberOfCommandsMissed - } - Write-Host "PowerShell Commands not tested:`n$(ConvertTo-Json -InputObject $testResults.CodeCoverage.MissedCommands)" - #> - if ([Int]$coveragePercent -lt $coverageThreshold) { - throw ('Failed to meet code coverage threshold of {0}% with only {1}% coverage' -f $coverageThreshold, $coveragePercent) - } else { - Write-Build Cyan " $('Covered {0}% of {1} analyzed commands in {2} files.' -f $coveragePercent,$testResults.CodeCoverage.CommandsAnalyzedCount,$testResults.CodeCoverage.FilesAnalyzedCount)" - Write-Build Green ' ...Pester Unit Tests Complete!' - } - } else { - # account for new module build condition - Write-Build Yellow ' Code coverage check skipped. No commands to execute...' - } - - } -} #Test - -#Synopsis: Used primarily during active development to generate xml file to graphically display code coverage in VSCode using Coverage Gutters -Add-BuildTask DevCC { - Write-Build White ' Generating code coverage report at root...' - Write-Build White " Importing desired Pester version. Min: $script:MinPesterVersion Max: $script:MaxPesterVersion" - Remove-Module -Name Pester -Force -ErrorAction SilentlyContinue # there are instances where some containers have Pester already in the session - Import-Module -Name Pester -MinimumVersion $script:MinPesterVersion -MaximumVersion $script:MaxPesterVersion -ErrorAction 'Stop' - $pesterConfiguration = New-PesterConfiguration - $pesterConfiguration.run.Path = $script:UnitTestsPath - $pesterConfiguration.CodeCoverage.Enabled = $true - $pesterConfiguration.CodeCoverage.Path = "$PSScriptRoot\$ModuleName\*\*.ps1" - $pesterConfiguration.CodeCoverage.CoveragePercentTarget = $script:coverageThreshold - $pesterConfiguration.CodeCoverage.OutputPath = '..\..\..\cov.xml' - $pesterConfiguration.CodeCoverage.OutputFormat = 'CoverageGutters' - - Invoke-Pester -Configuration $pesterConfiguration - Write-Build Green ' ...Code Coverage report generated!' -} #DevCC - -# Synopsis: Build help for module -Add-BuildTask CreateHelpStart { - Write-Build White ' Performing all help related actions.' - - Write-Build Gray ' Importing platyPS v0.12.0 ...' - Import-Module platyPS -RequiredVersion 0.12.0 -ErrorAction Stop - Write-Build Gray ' ...platyPS imported successfully.' -} #CreateHelpStart - -# Synopsis: Build markdown help files for module and fail if help information is missing -Add-BuildTask CreateMarkdownHelp -After CreateHelpStart { - $ModulePage = "$script:ArtifactsPath\docs\$($ModuleName).md" - - $markdownParams = @{ - Module = $ModuleName - OutputFolder = "$script:ArtifactsPath\docs\" - Force = $true - WithModulePage = $true - Locale = 'en-US' - FwLink = "NA" - HelpVersion = $script:ModuleVersion - } - - Write-Build Gray ' Generating markdown files...' - $null = New-MarkdownHelp @markdownParams - Write-Build Gray ' ...Markdown generation completed.' - - Write-Build Gray ' Replacing markdown elements...' - Write-Build DarkGray ' Replace multi-line EXAMPLES' - $OutputDir = "$script:ArtifactsPath\docs\" - $OutputDir | Get-ChildItem -File | ForEach-Object { - # fix formatting in multiline examples - $content = Get-Content $_.FullName -Raw - $newContent = $content -replace '(## EXAMPLE [^`]+?```\r\n[^`\r\n]+?\r\n)(```\r\n\r\n)([^#]+?\r\n)(\r\n)([^#]+)(#)', '$1$3$2$4$5$6' - if ($newContent -ne $content) { - Set-Content -Path $_.FullName -Value $newContent -Force - } - } - Write-Build DarkGray ' Replace each missing element we need for a proper generic module page .md file' - $ModulePageFileContent = Get-Content -Raw $ModulePage - $ModulePageFileContent = $ModulePageFileContent -replace '{{Manually Enter Description Here}}', $script:ModuleDescription - $script:FunctionsToExport | ForEach-Object { - Write-Build DarkGray " Updating definition for the following function: $($_)" - $TextToReplace = "{{Manually Enter $($_) Description Here}}" - $ReplacementText = (Get-Help -Detailed $_).Synopsis - $ModulePageFileContent = $ModulePageFileContent -replace $TextToReplace, $ReplacementText - } - Write-Build DarkGray ' Evaluating if running 7.4.0 or higher...' - # https://github.com/PowerShell/platyPS/issues/595 - if ($PSVersionTable.PSVersion -ge [version]'7.4.0') { - Write-Build DarkGray ' Performing Markdown repair' - # dot source markdown repair - . $BuildRoot\MarkdownRepair.ps1 - $OutputDir | Get-ChildItem -File | ForEach-Object { - Repair-PlatyPSMarkdown -Path $_.FullName - } - } - Write-Build DarkGray ' Add blank line after headers.' - $OutputDir | Get-ChildItem -File | ForEach-Object { - $content = Get-Content $_.FullName -Raw - $newContent = $content -replace '(?m)^(#{1,6}\s+.*)$\r?\n(?!\r?\n)', "`$1`r`n" - # $newContent = $content -replace '(?m)^(#{1,6}\s+.+?)$\r?\n(?!\r?\n)', "`$1`n" - # $newContent = $content -replace '(?m)^(#{1,6}\s+.*)$\r?\n(?!\r?\n)', "`$1`n" - if ($newContent -ne $content) { - Set-Content -Path $_.FullName -Value $newContent -Force - } - } - # *NOTE: it is not possible to adjust fenced code block at this location as conversion to MAML does not support language tags. - - $ModulePageFileContent | Out-File $ModulePage -Force -Encoding:utf8 - Write-Build Gray ' ...Markdown replacements complete.' - - Write-Build Gray ' Verifying GUID...' - $MissingGUID = Select-String -Path "$script:ArtifactsPath\docs\*.md" -Pattern "(00000000-0000-0000-0000-000000000000)" - if ($MissingGUID.Count -gt 0) { - Write-Build Yellow ' The documentation that got generated resulted in a generic GUID. Check the GUID entry of your module manifest.' - throw 'Missing GUID. Please review and rebuild.' - } - - Write-Build Gray ' Checking for missing documentation in md files...' - $MissingDocumentation = Select-String -Path "$script:ArtifactsPath\docs\*.md" -Pattern "({{.*}})" - if ($MissingDocumentation.Count -gt 0) { - Write-Build Yellow ' The documentation that got generated resulted in missing sections which should be filled out.' - Write-Build Yellow ' Please review the following sections in your comment based help, fill out missing information and rerun this build:' - Write-Build Yellow ' (Note: This can happen if the .EXTERNALHELP CBH is defined for a function before running this build.)' - Write-Build Yellow " Path of files with issues: $script:ArtifactsPath\docs\" - $MissingDocumentation | Select-Object FileName, LineNumber, Line | Format-Table -AutoSize - throw 'Missing documentation. Please review and rebuild.' - } - - Write-Build Gray ' Checking for missing SYNOPSIS in md files...' - $fSynopsisOutput = @() - # $synopsisEval = Select-String -Path "$script:ArtifactsPath\docs\*.md" -Pattern "^## SYNOPSIS$" -Context 0, 1 - $synopsisEval = Select-String -Path "$script:ArtifactsPath\docs\*.md" -Pattern "^## SYNOPSIS$\r?\n$" -Context 0, 2 - $synopsisEval | ForEach-Object { - $chAC = $_.Context.DisplayPostContext.ToCharArray() - if ($null -eq $chAC) { - $fSynopsisOutput += $_.FileName - } - } - if ($fSynopsisOutput) { - Write-Build Yellow " The following files are missing SYNOPSIS:" - $fSynopsisOutput - throw 'SYNOPSIS information missing. Please review.' - } - - Write-Build Gray ' ...Markdown generation complete.' -} #CreateMarkdownHelp - -# Synopsis: Build the external xml help file from markdown help files with PlatyPS -Add-BuildTask CreateExternalHelp -After CreateMarkdownHelp { - Write-Build Gray ' Creating external xml help file...' - $null = New-ExternalHelp "$script:ArtifactsPath\docs" -OutputPath "$script:ArtifactsPath\en-US\" -Force - Write-Build Gray ' ...External xml help file created!' -} #CreateExternalHelp - -Add-BuildTask CreateHelpComplete -After CreateExternalHelp { - Write-Build Gray ' Finalizing markdown documentation now that external help has been created...' - Write-Build DarkGray ' Add powershell language to unspecified fenced code blocks.' - - $OutputDir = "$script:ArtifactsPath\docs\" - - Get-ChildItem -Path $OutputDir -File | ForEach-Object { - $lines = Get-Content -Path $_.FullName - $insideCodeBlock = $false - - for ($i = 0; $i -lt $lines.Count; $i++) { - # Regex captures everything after triple backticks (if present). - # e.g. ```yaml => captured group = "yaml" - # ``` => captured group = "" - if ($lines[$i] -match '^\s*```(\S*)\s*$') { - $lang = $Matches[1] - - if (-not $insideCodeBlock) { - # We found an opening fence - if ([string]::IsNullOrWhiteSpace($lang)) { - # Bare triple backticks => add powershell - $lines[$i] = '```powershell' - } - # Toggle "inside code block" on - $insideCodeBlock = $true - } else { - # We found the closing fence -> set $insideCodeBlock off - $insideCodeBlock = $false - # Do *not* modify closing fence, leave it exactly as it is - } - } - } - - Set-Content -Path $_.FullName -Value $lines - } - Write-Build DarkGray ' Ensuring exactly one trailing newline in final markdown file.' - Get-ChildItem -Path $OutputDir -File -Filter *.md | ForEach-Object { - # Read the file as an array of lines - $lines = Get-Content -Path $_.FullName - - # Remove all blank lines at the end, but do not remove actual content - while ($lines.Count -gt 0 -and $lines[-1] -match '^\s*$') { - $lines = $lines[0..($lines.Count - 2)] - } - - # Re-join with Windows line endings and add exactly one trailing newline - $content = ($lines -join "`r`n") - Set-Content -Path $_.FullName -Value $content -Force - } - Write-Build Gray ' ...Markdown documentation finalized.' - - Write-Build Green ' ...CreateHelp Complete!' -} #CreateHelpStart - -# Synopsis: Replace comment based help (CBH) with external help in all public functions for this project -Add-BuildTask UpdateCBH -After AssetCopy { - $ExternalHelp = @" -<# -.EXTERNALHELP $($ModuleName)-help.xml -#> -"@ - - $CBHPattern = "(?ms)(\<#.*\.SYNOPSIS.*?#>)" - Get-ChildItem -Path "$script:ArtifactsPath\Public\*.ps1" -File | ForEach-Object { - $FormattedOutFile = $_.FullName - Write-Output " Replacing CBH in file: $($FormattedOutFile)" - $UpdatedFile = (Get-Content $FormattedOutFile -Raw) -replace $CBHPattern, $ExternalHelp - $UpdatedFile | Out-File -FilePath $FormattedOutFile -Force -Encoding:utf8 - } -} #UpdateCBH - -# Synopsis: Copies module assets to Artifacts folder -Add-BuildTask AssetCopy -Before Build { - Write-Build Gray ' Copying assets to Artifacts...' - Copy-Item -Path "$script:ModuleSourcePath\*" -Destination $script:ArtifactsPath -Exclude *.psd1, *.psm1 -Recurse -ErrorAction Stop - Write-Build Gray ' ...Assets copy complete.' -} #AssetCopy - -# Synopsis: Builds the Module to the Artifacts folder -Add-BuildTask Build { - Write-Build White ' Performing Module Build' - - Write-Build Gray ' Copying manifest file to Artifacts...' - Copy-Item -Path $script:ModuleManifestFile -Destination $script:ArtifactsPath -Recurse -ErrorAction Stop - #Copy-Item -Path $script:ModuleSourcePath\bin -Destination $script:ArtifactsPath -Recurse -ErrorAction Stop - Write-Build Gray ' ...manifest copy complete.' - - Write-Build Gray ' Merging Public and Private functions to one module file...' - #$private = "$script:ModuleSourcePath\Private" - $scriptContent = [System.Text.StringBuilder]::new() - #$powerShellScripts = Get-ChildItem -Path $script:ModuleSourcePath -Filter '*.ps1' -Recurse - $powerShellScripts = Get-ChildItem -Path $script:ArtifactsPath -Recurse | Where-Object { $_.Name -match '^*.ps1$' } - foreach ($script in $powerShellScripts) { - $null = $scriptContent.Append((Get-Content -Path $script.FullName -Raw)) - $null = $scriptContent.AppendLine('') - $null = $scriptContent.AppendLine('') - } - $scriptContent.ToString() | Out-File -FilePath $script:BuildModuleRootFile -Encoding utf8 -Force - # Cleanup the combined root module and remove extra trailing lines at the end of the file. - Invoke-Formatter $script:BuildModuleRootFile -ErrorAction SilentlyContinue - Write-Build Gray ' ...Module creation complete.' - - Write-Build Gray ' Cleaning up leftover artifacts...' - #cleanup artifacts that are no longer required - if (Test-Path "$script:ArtifactsPath\Public") { - Remove-Item "$script:ArtifactsPath\Public" -Recurse -Force -ErrorAction Stop - } - if (Test-Path "$script:ArtifactsPath\Private") { - Remove-Item "$script:ArtifactsPath\Private" -Recurse -Force -ErrorAction Stop - } - if (Test-Path "$script:ArtifactsPath\Imports.ps1") { - Remove-Item "$script:ArtifactsPath\Imports.ps1" -Force -ErrorAction SilentlyContinue - } - - if (Test-Path "$script:ArtifactsPath\docs") { - #here we update the parent level docs. If you would prefer not to update them, comment out this section. - Write-Build Gray ' Overwriting docs output...' - if (-not (Test-Path '..\docs\')) { - New-Item -Path '..\docs\' -ItemType Directory -Force | Out-Null - } - Move-Item "$script:ArtifactsPath\docs\*.md" -Destination '..\docs\' -Force - Remove-Item "$script:ArtifactsPath\docs" -Recurse -Force -ErrorAction Stop - Write-Build Gray ' ...Docs output completed.' - } - - Write-Build Green ' ...Build Complete!' -} #Build - -#Synopsis: Invokes all Pester Integration Tests in the Tests\Integration folder (if it exists) -Add-BuildTask IntegrationTest { - if (Test-Path -Path $script:IntegrationTestsPath) { - Write-Build White " Importing desired Pester version. Min: $script:MinPesterVersion Max: $script:MaxPesterVersion" - Remove-Module -Name Pester -Force -ErrorAction SilentlyContinue # there are instances where some containers have Pester already in the session - Import-Module -Name Pester -MinimumVersion $script:MinPesterVersion -MaximumVersion $script:MaxPesterVersion -ErrorAction 'Stop' - - Write-Build White " Performing Pester Integration Tests in $($invokePesterParams.path)" - - $pesterConfiguration = New-PesterConfiguration - $pesterConfiguration.run.Path = $script:IntegrationTestsPath - $pesterConfiguration.Run.PassThru = $true - $pesterConfiguration.Run.Exit = $false - $pesterConfiguration.CodeCoverage.Enabled = $false - $pesterConfiguration.TestResult.Enabled = $false - $pesterConfiguration.Output.Verbosity = 'Detailed' - - $testResults = Invoke-Pester -Configuration $pesterConfiguration - # This will output a nice json for each failed test (if running in CodeBuild) - if ($env:CODEBUILD_BUILD_ARN) { - $testResults.TestResult | ForEach-Object { - if ($_.Result -ne 'Passed') { - ConvertTo-Json -InputObject $_ -Compress - } - } - } - - $numberFails = $testResults.FailedCount - Assert-Build($numberFails -eq 0) ('Failed "{0}" unit tests.' -f $numberFails) - Write-Build Green ' ...Pester Integration Tests Complete!' - } -} #IntegrationTest - -#Synopsis: Creates an archive of the built Module -Add-BuildTask Archive { - Write-Build White ' Performing Archive...' - - $archivePath = Join-Path -Path $BuildRoot -ChildPath 'Archive' - if (Test-Path -Path $archivePath) { - $null = Remove-Item -Path $archivePath -Recurse -Force - } - - $null = New-Item -Path $archivePath -ItemType Directory -Force - - $zipFileName = '{0}_{1}_{2}.{3}.zip' -f $script:ModuleName, $script:ModuleVersion, ([DateTime]::UtcNow.ToString("yyyyMMdd")), ([DateTime]::UtcNow.ToString("hhmmss")) - $zipFile = Join-Path -Path $archivePath -ChildPath $zipFileName - - if ($PSEdition -eq 'Desktop') { - Add-Type -AssemblyName 'System.IO.Compression.FileSystem' - } - [System.IO.Compression.ZipFile]::CreateFromDirectory($script:ArtifactsPath, $zipFile) - - Write-Build Green ' ...Archive Complete!' -} #Archive - +<# +.SYNOPSIS + An Invoke-Build Build file. +.DESCRIPTION + Build steps can include: + - ValidateRequirements + - ImportModuleManifest + - Clean + - Analyze + - FormattingCheck + - Test + - DevCC + - CreateHelpStart + - Build + - IntegrationTest + - Archive +.EXAMPLE + Invoke-Build + + This will perform the default build Add-BuildTasks: see below for the default Add-BuildTask execution +.EXAMPLE + Invoke-Build -Add-BuildTask Analyze,Test + + This will perform only the Analyze and Test Add-BuildTasks. +.NOTES + This build file by Catesta will pull in configurations from the ".Settings.ps1" file as well, where users can more easily customize the build process if required. + https://github.com/nightroman/Invoke-Build + https://github.com/nightroman/Invoke-Build/wiki/Build-Scripts-Guidelines + If using VSCode you can use the generated tasks.json to execute the various tasks in this build file. + Ctrl + P | then type task (add space) - you will then be presented with a list of available tasks to run + The 'InstallDependencies' Add-BuildTask isn't present here. + Module dependencies are installed at a previous step in the pipeline. + If your manifest has module dependencies include all required modules in your CI/CD bootstrap file: + AWS - install_modules.ps1 + Azure - actions_bootstrap.ps1 + GitHub Actions - actions_bootstrap.ps1 + AppVeyor - actions_bootstrap.ps1 +#> + +#Include: Settings +$ModuleName = [regex]::Match((Get-Item $BuildFile).Name, '^(.*)\.build\.ps1$').Groups[1].Value +. "./$ModuleName.Settings.ps1" + +function Test-ManifestBool ($Path) { + Get-ChildItem $Path | Test-ModuleManifest -ErrorAction SilentlyContinue | Out-Null; $? +} + +#Default Build +$str = @() +$str = 'Clean', 'ValidateRequirements', 'ImportModuleManifest' +$str += 'FormattingCheck' +$str += 'Analyze', 'Test' +$str += 'CreateHelpStart' +$str2 = $str +$str2 += 'Build', 'Archive' +$str += 'Build', 'IntegrationTest', 'Archive' +Add-BuildTask -Name . -Jobs $str + +#Local testing build process +Add-BuildTask TestLocal Clean, ImportModuleManifest, Analyze, Test + +#Local help file creation process +Add-BuildTask HelpLocal Clean, ImportModuleManifest, CreateHelpStart + +#Full build sans integration tests +Add-BuildTask BuildNoIntegration -Jobs $str2 + +# Pre-build variables to be used by other portions of the script +Enter-Build { + $script:ModuleName = [regex]::Match((Get-Item $BuildFile).Name, '^(.*)\.build\.ps1$').Groups[1].Value + + # Identify other required paths + $script:ModuleSourcePath = Join-Path -Path $BuildRoot -ChildPath $script:ModuleName + $script:ModuleFiles = Join-Path -Path $script:ModuleSourcePath -ChildPath '*' + + $script:ModuleManifestFile = Join-Path -Path $script:ModuleSourcePath -ChildPath "$($script:ModuleName).psd1" + + $manifestInfo = Import-PowerShellDataFile -Path $script:ModuleManifestFile + $script:ModuleVersion = $manifestInfo.ModuleVersion + $script:ModuleDescription = $manifestInfo.Description + $script:FunctionsToExport = $manifestInfo.FunctionsToExport + + $script:TestsPath = Join-Path -Path $BuildRoot -ChildPath 'Tests' + $script:UnitTestsPath = Join-Path -Path $script:TestsPath -ChildPath 'Unit' + $script:IntegrationTestsPath = Join-Path -Path $script:TestsPath -ChildPath 'Integration' + + $script:ArtifactsPath = Join-Path -Path $BuildRoot -ChildPath 'Artifacts' + $script:ArchivePath = Join-Path -Path $BuildRoot -ChildPath 'Archive' + + $script:BuildModuleRootFile = Join-Path -Path $script:ArtifactsPath -ChildPath "$($script:ModuleName).psm1" + + # Ensure our builds fail until if below a minimum defined code test coverage threshold + $script:coverageThreshold = 30 + + [version]$script:MinPesterVersion = '5.2.2' + [version]$script:MaxPesterVersion = '5.99.99' + $script:testOutputFormat = 'NUnitXML' +} #Enter-Build + +# Define headers as separator, task path, synopsis, and location, e.g. for Ctrl+Click in VSCode. +# Also change the default color to Green. If you need task start times, use `$Task.Started`. +Set-BuildHeader { + param($Path) + # separator line + Write-Build DarkMagenta ('=' * 79) + # default header + synopsis + Write-Build DarkGray "Task $Path : $(Get-BuildSynopsis $Task)" + # task location in a script + Write-Build DarkGray "At $($Task.InvocationInfo.ScriptName):$($Task.InvocationInfo.ScriptLineNumber)" + Write-Build Yellow "Manifest File: $script:ModuleManifestFile" + Write-Build Yellow "Manifest Version: $($manifestInfo.ModuleVersion)" +} #Set-BuildHeader + +# Define footers similar to default but change the color to DarkGray. +Set-BuildFooter { + param($Path) + Write-Build DarkGray "Done $Path, $($Task.Elapsed)" + # # separator line + # Write-Build Gray ('=' * 79) +} #Set-BuildFooter + +#Synopsis: Validate system requirements are met +Add-BuildTask ValidateRequirements { + # this setting comes from the *.Settings.ps1 + Write-Build White " Verifying at least PowerShell $script:requiredPSVersion..." + Assert-Build ($PSVersionTable.PSVersion -ge $script:requiredPSVersion) "At least Powershell $script:requiredPSVersion is required for this build to function properly" + Write-Build Green ' ...Verification Complete!' +} #ValidateRequirements + +# Synopsis: Import the current module manifest file for processing +Add-BuildTask TestModuleManifest -Before ImportModuleManifest { + Write-Build White ' Running module manifest tests...' + Assert-Build (Test-Path $script:ModuleManifestFile) 'Unable to locate the module manifest file.' + Assert-Build (Test-ManifestBool -Path $script:ModuleManifestFile) 'Module Manifest test did not pass verification.' + Write-Build Green ' ...Module Manifest Verification Complete!' +} #f5b33218-bde4-4028-b2a1-9c206f089503 + +# Synopsis: Load the module project +Add-BuildTask ImportModuleManifest { + Write-Build White ' Attempting to load the project module.' + try { + Import-Module $script:ModuleManifestFile -Force -PassThru -ErrorAction Stop + } catch { + Write-Build Red " ...$_`n" + throw 'Unable to load the project module' + } + Write-Build Green " ...$script:ModuleName imported successfully" +} + +#Synopsis: Clean and reset Artifacts/Archive Directory +Add-BuildTask Clean { + Write-Build White ' Clean up our Artifacts/Archive directory...' + + $null = Remove-Item $script:ArtifactsPath -Force -Recurse -ErrorAction 0 + $null = New-Item $script:ArtifactsPath -ItemType:Directory + $null = Remove-Item $script:ArchivePath -Force -Recurse -ErrorAction 0 + $null = New-Item $script:ArchivePath -ItemType:Directory + + Write-Build Green ' ...Clean Complete!' +} #Clean + +#Synopsis: Invokes PSScriptAnalyzer against the Module source path +Add-BuildTask Analyze { + + $scriptAnalyzerParams = @{ + Path = $script:ModuleSourcePath + Setting = 'PSScriptAnalyzerSettings.psd1' + Recurse = $true + Verbose = $false + } + + Write-Build White ' Performing Module ScriptAnalyzer checks...' + $scriptAnalyzerResults = Invoke-ScriptAnalyzer @scriptAnalyzerParams + + if ($scriptAnalyzerResults) { + $scriptAnalyzerResults | Format-Table + throw ' One or more PSScriptAnalyzer errors/warnings where found.' + } else { + Write-Build Green ' ...Module Analyze Complete!' + } +} #Analyze + +#Synopsis: Invokes Script Analyzer against the Tests path if it exists +Add-BuildTask AnalyzeTests -After Analyze { + if (Test-Path -Path $script:TestsPath) { + + $scriptAnalyzerParams = @{ + Path = $script:TestsPath + Setting = 'PSScriptAnalyzerSettings.psd1' + ExcludeRule = 'PSUseDeclaredVarsMoreThanAssignments' + Recurse = $true + Verbose = $false + } + + Write-Build White ' Performing Test ScriptAnalyzer checks...' + $scriptAnalyzerResults = Invoke-ScriptAnalyzer @scriptAnalyzerParams + + if ($scriptAnalyzerResults) { + $scriptAnalyzerResults | Format-Table + throw ' One or more PSScriptAnalyzer errors/warnings where found.' + } else { + Write-Build Green ' ...Test Analyze Complete!' + } + } +} #AnalyzeTests + +#Synopsis: Analyze scripts to verify if they adhere to desired coding format (Stroustrup / OTBS / Allman) +Add-BuildTask FormattingCheck { + + + $scriptAnalyzerParams = @{ + Setting = 'CodeFormattingOTBS' + ExcludeRule = 'PSUseConsistentWhitespace' + Recurse = $true + Verbose = $false + } + + + Write-Build White ' Performing script formatting checks...' + $scriptAnalyzerResults = Get-ChildItem -Path $script:ModuleSourcePath -Exclude "*.psd1" | Invoke-ScriptAnalyzer @scriptAnalyzerParams + + if ($scriptAnalyzerResults) { + $scriptAnalyzerResults | Format-Table + throw ' PSScriptAnalyzer code formatting check did not adhere to {0} standards' -f $scriptAnalyzerParams.Setting + } else { + Write-Build Green ' ...Formatting Analyze Complete!' + } +} #FormattingCheck + +#Synopsis: Invokes all Pester Unit Tests in the Tests\Unit folder (if it exists) +Add-BuildTask Test { + + Write-Build White " Importing desired Pester version. Min: $script:MinPesterVersion Max: $script:MaxPesterVersion" + Remove-Module -Name Pester -Force -ErrorAction 'SilentlyContinue'# there are instances where some containers have Pester already in the session + Import-Module -Name Pester -MinimumVersion $script:MinPesterVersion -MaximumVersion $script:MaxPesterVersion -ErrorAction 'Stop' + + $codeCovPath = "$script:ArtifactsPath\ccReport\" + $testOutPutPath = "$script:ArtifactsPath\testOutput\" + if (-not(Test-Path $codeCovPath)) { + New-Item -Path $codeCovPath -ItemType Directory | Out-Null + } + if (-not(Test-Path $testOutPutPath)) { + New-Item -Path $testOutPutPath -ItemType Directory | Out-Null + } + if (Test-Path -Path $script:UnitTestsPath) { + $pesterConfiguration = New-PesterConfiguration + $pesterConfiguration.run.Path = $script:UnitTestsPath + $pesterConfiguration.Run.PassThru = $true + $pesterConfiguration.Run.Exit = $false + $pesterConfiguration.CodeCoverage.Enabled = $true + $pesterConfiguration.CodeCoverage.Path = "$script:ModuleSourcePath\*\*.ps1" + $pesterConfiguration.CodeCoverage.CoveragePercentTarget = $script:coverageThreshold + $pesterConfiguration.CodeCoverage.OutputPath = "$codeCovPath\CodeCoverage.xml" + $pesterConfiguration.CodeCoverage.OutputFormat = 'JaCoCo' + $pesterConfiguration.TestResult.Enabled = $true + $pesterConfiguration.TestResult.OutputPath = "$testOutPutPath\PesterTests.xml" + $pesterConfiguration.TestResult.OutputFormat = $script:testOutputFormat + $pesterConfiguration.Output.Verbosity = 'Detailed' + + Write-Build White ' Performing Pester Unit Tests...' + # Publish Test Results + $testResults = Invoke-Pester -Configuration $pesterConfiguration + + # This will output a nice json for each failed test (if running in CodeBuild) + if ($env:CODEBUILD_BUILD_ARN) { + $testResults.TestResult | ForEach-Object { + if ($_.Result -ne 'Passed') { + ConvertTo-Json -InputObject $_ -Compress + } + } + } + + $numberFails = $testResults.FailedCount + Assert-Build($numberFails -eq 0) ('Failed "{0}" unit tests.' -f $numberFails) + + Write-Build Gray (' ...CODE COVERAGE - CommandsExecutedCount: {0}' -f $testResults.CodeCoverage.CommandsExecutedCount) + Write-Build Gray (' ...CODE COVERAGE - CommandsAnalyzedCount: {0}' -f $testResults.CodeCoverage.CommandsAnalyzedCount) + + if ($testResults.CodeCoverage.NumberOfCommandsExecuted -ne 0) { + $coveragePercent = '{0:N2}' -f ($testResults.CodeCoverage.CommandsExecutedCount / $testResults.CodeCoverage.CommandsAnalyzedCount * 100) + + <# + if ($testResults.CodeCoverage.NumberOfCommandsMissed -gt 0) { + 'Failed to analyze "{0}" commands' -f $testResults.CodeCoverage.NumberOfCommandsMissed + } + Write-Host "PowerShell Commands not tested:`n$(ConvertTo-Json -InputObject $testResults.CodeCoverage.MissedCommands)" + #> + if ([Int]$coveragePercent -lt $coverageThreshold) { + throw ('Failed to meet code coverage threshold of {0}% with only {1}% coverage' -f $coverageThreshold, $coveragePercent) + } else { + Write-Build Cyan " $('Covered {0}% of {1} analyzed commands in {2} files.' -f $coveragePercent,$testResults.CodeCoverage.CommandsAnalyzedCount,$testResults.CodeCoverage.FilesAnalyzedCount)" + Write-Build Green ' ...Pester Unit Tests Complete!' + } + } else { + # account for new module build condition + Write-Build Yellow ' Code coverage check skipped. No commands to execute...' + } + + } +} #Test + +#Synopsis: Used primarily during active development to generate xml file to graphically display code coverage in VSCode using Coverage Gutters +Add-BuildTask DevCC { + Write-Build White ' Generating code coverage report at root...' + Write-Build White " Importing desired Pester version. Min: $script:MinPesterVersion Max: $script:MaxPesterVersion" + Remove-Module -Name Pester -Force -ErrorAction SilentlyContinue # there are instances where some containers have Pester already in the session + Import-Module -Name Pester -MinimumVersion $script:MinPesterVersion -MaximumVersion $script:MaxPesterVersion -ErrorAction 'Stop' + $pesterConfiguration = New-PesterConfiguration + $pesterConfiguration.run.Path = $script:UnitTestsPath + $pesterConfiguration.CodeCoverage.Enabled = $true + $pesterConfiguration.CodeCoverage.Path = "$PSScriptRoot\$ModuleName\*\*.ps1" + $pesterConfiguration.CodeCoverage.CoveragePercentTarget = $script:coverageThreshold + $pesterConfiguration.CodeCoverage.OutputPath = '..\..\..\cov.xml' + $pesterConfiguration.CodeCoverage.OutputFormat = 'CoverageGutters' + + Invoke-Pester -Configuration $pesterConfiguration + Write-Build Green ' ...Code Coverage report generated!' +} #DevCC + +# Synopsis: Build help for module +Add-BuildTask CreateHelpStart { + Write-Build White ' Performing all help related actions.' + + Write-Build Gray ' Importing platyPS v0.12.0 ...' + Import-Module platyPS -RequiredVersion 0.12.0 -ErrorAction Stop + Write-Build Gray ' ...platyPS imported successfully.' +} #CreateHelpStart + +# Synopsis: Build markdown help files for module and fail if help information is missing +Add-BuildTask CreateMarkdownHelp -After CreateHelpStart { + $ModulePage = "$script:ArtifactsPath\docs\$($ModuleName).md" + + $markdownParams = @{ + Module = $ModuleName + OutputFolder = "$script:ArtifactsPath\docs\" + Force = $true + WithModulePage = $true + Locale = 'en-US' + FwLink = "NA" + HelpVersion = $script:ModuleVersion + } + + Write-Build Gray ' Generating markdown files...' + $null = New-MarkdownHelp @markdownParams + Write-Build Gray ' ...Markdown generation completed.' + + Write-Build Gray ' Replacing markdown elements...' + Write-Build DarkGray ' Replace multi-line EXAMPLES' + $OutputDir = "$script:ArtifactsPath\docs\" + $OutputDir | Get-ChildItem -File | ForEach-Object { + # fix formatting in multiline examples + $content = Get-Content $_.FullName -Raw + $newContent = $content -replace '(## EXAMPLE [^`]+?```\r\n[^`\r\n]+?\r\n)(```\r\n\r\n)([^#]+?\r\n)(\r\n)([^#]+)(#)', '$1$3$2$4$5$6' + if ($newContent -ne $content) { + Set-Content -Path $_.FullName -Value $newContent -Force + } + } + Write-Build DarkGray ' Replace each missing element we need for a proper generic module page .md file' + $ModulePageFileContent = Get-Content -Raw $ModulePage + $ModulePageFileContent = $ModulePageFileContent -replace '{{Manually Enter Description Here}}', $script:ModuleDescription + $script:FunctionsToExport | ForEach-Object { + Write-Build DarkGray " Updating definition for the following function: $($_)" + $TextToReplace = "{{Manually Enter $($_) Description Here}}" + $ReplacementText = (Get-Help -Detailed $_).Synopsis + $ModulePageFileContent = $ModulePageFileContent -replace $TextToReplace, $ReplacementText + } + Write-Build DarkGray ' Evaluating if running 7.4.0 or higher...' + # https://github.com/PowerShell/platyPS/issues/595 + if ($PSVersionTable.PSVersion -ge [version]'7.4.0') { + Write-Build DarkGray ' Performing Markdown repair' + # dot source markdown repair + . $BuildRoot\MarkdownRepair.ps1 + $OutputDir | Get-ChildItem -File | ForEach-Object { + Repair-PlatyPSMarkdown -Path $_.FullName + } + } + Write-Build DarkGray ' Add blank line after headers.' + $OutputDir | Get-ChildItem -File | ForEach-Object { + $content = Get-Content $_.FullName -Raw + $newContent = $content -replace '(?m)^(#{1,6}\s+.*)$\r?\n(?!\r?\n)', "`$1`r`n" + # $newContent = $content -replace '(?m)^(#{1,6}\s+.+?)$\r?\n(?!\r?\n)', "`$1`n" + # $newContent = $content -replace '(?m)^(#{1,6}\s+.*)$\r?\n(?!\r?\n)', "`$1`n" + if ($newContent -ne $content) { + Set-Content -Path $_.FullName -Value $newContent -Force + } + } + # *NOTE: it is not possible to adjust fenced code block at this location as conversion to MAML does not support language tags. + + $ModulePageFileContent | Out-File $ModulePage -Force -Encoding:utf8 + Write-Build Gray ' ...Markdown replacements complete.' + + Write-Build Gray ' Verifying GUID...' + $MissingGUID = Select-String -Path "$script:ArtifactsPath\docs\*.md" -Pattern "(00000000-0000-0000-0000-000000000000)" + if ($MissingGUID.Count -gt 0) { + Write-Build Yellow ' The documentation that got generated resulted in a generic GUID. Check the GUID entry of your module manifest.' + throw 'Missing GUID. Please review and rebuild.' + } + + Write-Build Gray ' Checking for missing documentation in md files...' + $MissingDocumentation = Select-String -Path "$script:ArtifactsPath\docs\*.md" -Pattern "({{.*}})" + if ($MissingDocumentation.Count -gt 0) { + Write-Build Yellow ' The documentation that got generated resulted in missing sections which should be filled out.' + Write-Build Yellow ' Please review the following sections in your comment based help, fill out missing information and rerun this build:' + Write-Build Yellow ' (Note: This can happen if the .EXTERNALHELP CBH is defined for a function before running this build.)' + Write-Build Yellow " Path of files with issues: $script:ArtifactsPath\docs\" + $MissingDocumentation | Select-Object FileName, LineNumber, Line | Format-Table -AutoSize + throw 'Missing documentation. Please review and rebuild.' + } + + Write-Build Gray ' Checking for missing SYNOPSIS in md files...' + $fSynopsisOutput = @() + # $synopsisEval = Select-String -Path "$script:ArtifactsPath\docs\*.md" -Pattern "^## SYNOPSIS$" -Context 0, 1 + $synopsisEval = Select-String -Path "$script:ArtifactsPath\docs\*.md" -Pattern "^## SYNOPSIS$\r?\n$" -Context 0, 2 + $synopsisEval | ForEach-Object { + $chAC = $_.Context.DisplayPostContext.ToCharArray() + if ($null -eq $chAC) { + $fSynopsisOutput += $_.FileName + } + } + if ($fSynopsisOutput) { + Write-Build Yellow " The following files are missing SYNOPSIS:" + $fSynopsisOutput + throw 'SYNOPSIS information missing. Please review.' + } + + Write-Build Gray ' ...Markdown generation complete.' +} #CreateMarkdownHelp + +# Synopsis: Build the external xml help file from markdown help files with PlatyPS +Add-BuildTask CreateExternalHelp -After CreateMarkdownHelp { + Write-Build Gray ' Creating external xml help file...' + $null = New-ExternalHelp "$script:ArtifactsPath\docs" -OutputPath "$script:ArtifactsPath\en-US\" -Force + Write-Build Gray ' ...External xml help file created!' +} #CreateExternalHelp + +Add-BuildTask CreateHelpComplete -After CreateExternalHelp { + Write-Build Gray ' Finalizing markdown documentation now that external help has been created...' + Write-Build DarkGray ' Add powershell language to unspecified fenced code blocks.' + + $OutputDir = "$script:ArtifactsPath\docs\" + + Get-ChildItem -Path $OutputDir -File | ForEach-Object { + $lines = Get-Content -Path $_.FullName + $insideCodeBlock = $false + + for ($i = 0; $i -lt $lines.Count; $i++) { + # Regex captures everything after triple backticks (if present). + # e.g. ```yaml => captured group = "yaml" + # ``` => captured group = "" + if ($lines[$i] -match '^\s*```(\S*)\s*$') { + $lang = $Matches[1] + + if (-not $insideCodeBlock) { + # We found an opening fence + if ([string]::IsNullOrWhiteSpace($lang)) { + # Bare triple backticks => add powershell + $lines[$i] = '```powershell' + } + # Toggle "inside code block" on + $insideCodeBlock = $true + } else { + # We found the closing fence -> set $insideCodeBlock off + $insideCodeBlock = $false + # Do *not* modify closing fence, leave it exactly as it is + } + } + } + + Set-Content -Path $_.FullName -Value $lines + } + Write-Build DarkGray ' Ensuring exactly one trailing newline in final markdown file.' + Get-ChildItem -Path $OutputDir -File -Filter *.md | ForEach-Object { + # Read the file as an array of lines + $lines = Get-Content -Path $_.FullName + + # Remove all blank lines at the end, but do not remove actual content + while ($lines.Count -gt 0 -and $lines[-1] -match '^\s*$') { + $lines = $lines[0..($lines.Count - 2)] + } + + # Re-join with Windows line endings and add exactly one trailing newline + $content = ($lines -join "`r`n") + Set-Content -Path $_.FullName -Value $content -Force + } + Write-Build Gray ' ...Markdown documentation finalized.' + + Write-Build Green ' ...CreateHelp Complete!' +} #CreateHelpStart + +# Synopsis: Replace comment based help (CBH) with external help in all public functions for this project +Add-BuildTask UpdateCBH -After AssetCopy { + $ExternalHelp = @" +<# +.EXTERNALHELP $($ModuleName)-help.xml +#> +"@ + + $CBHPattern = "(?ms)(\<#.*\.SYNOPSIS.*?#>)" + Get-ChildItem -Path "$script:ArtifactsPath\Public\*.ps1" -File | ForEach-Object { + $FormattedOutFile = $_.FullName + Write-Output " Replacing CBH in file: $($FormattedOutFile)" + $UpdatedFile = (Get-Content $FormattedOutFile -Raw) -replace $CBHPattern, $ExternalHelp + $UpdatedFile | Out-File -FilePath $FormattedOutFile -Force -Encoding:utf8 + } +} #UpdateCBH + +# Synopsis: Copies module assets to Artifacts folder +Add-BuildTask AssetCopy -Before Build { + Write-Build Gray ' Copying assets to Artifacts...' + Copy-Item -Path "$script:ModuleSourcePath\*" -Destination $script:ArtifactsPath -Exclude *.psd1, *.psm1 -Recurse -ErrorAction Stop + Write-Build Gray ' ...Assets copy complete.' +} #AssetCopy + +# Synopsis: Builds the Module to the Artifacts folder +Add-BuildTask Build { + Write-Build White ' Performing Module Build' + + Write-Build Gray ' Copying manifest file to Artifacts...' + Copy-Item -Path $script:ModuleManifestFile -Destination $script:ArtifactsPath -Recurse -ErrorAction Stop + #Copy-Item -Path $script:ModuleSourcePath\bin -Destination $script:ArtifactsPath -Recurse -ErrorAction Stop + Write-Build Gray ' ...manifest copy complete.' + + Write-Build Gray ' Merging Public and Private functions to one module file...' + #$private = "$script:ModuleSourcePath\Private" + $scriptContent = [System.Text.StringBuilder]::new() + #$powerShellScripts = Get-ChildItem -Path $script:ModuleSourcePath -Filter '*.ps1' -Recurse + $powerShellScripts = Get-ChildItem -Path $script:ArtifactsPath -Recurse | Where-Object { $_.Name -match '^*.ps1$' } + foreach ($script in $powerShellScripts) { + $null = $scriptContent.Append((Get-Content -Path $script.FullName -Raw)) + $null = $scriptContent.AppendLine('') + $null = $scriptContent.AppendLine('') + } + $scriptContent.ToString() | Out-File -FilePath $script:BuildModuleRootFile -Encoding utf8 -Force + # Cleanup the combined root module and remove extra trailing lines at the end of the file. + Invoke-Formatter $script:BuildModuleRootFile -ErrorAction SilentlyContinue + Write-Build Gray ' ...Module creation complete.' + + Write-Build Gray ' Cleaning up leftover artifacts...' + #cleanup artifacts that are no longer required + if (Test-Path "$script:ArtifactsPath\Public") { + Remove-Item "$script:ArtifactsPath\Public" -Recurse -Force -ErrorAction Stop + } + if (Test-Path "$script:ArtifactsPath\Private") { + Remove-Item "$script:ArtifactsPath\Private" -Recurse -Force -ErrorAction Stop + } + if (Test-Path "$script:ArtifactsPath\Imports.ps1") { + Remove-Item "$script:ArtifactsPath\Imports.ps1" -Force -ErrorAction SilentlyContinue + } + + if (Test-Path "$script:ArtifactsPath\docs") { + #here we update the parent level docs. If you would prefer not to update them, comment out this section. + Write-Build Gray ' Overwriting docs output...' + if (-not (Test-Path '..\docs\')) { + New-Item -Path '..\docs\' -ItemType Directory -Force | Out-Null + } + Move-Item "$script:ArtifactsPath\docs\*.md" -Destination '..\docs\' -Force + Remove-Item "$script:ArtifactsPath\docs" -Recurse -Force -ErrorAction Stop + Write-Build Gray ' ...Docs output completed.' + } + + Write-Build Green ' ...Build Complete!' +} #Build + +#Synopsis: Invokes all Pester Integration Tests in the Tests\Integration folder (if it exists) +Add-BuildTask IntegrationTest { + if (Test-Path -Path $script:IntegrationTestsPath) { + Write-Build White " Importing desired Pester version. Min: $script:MinPesterVersion Max: $script:MaxPesterVersion" + Remove-Module -Name Pester -Force -ErrorAction SilentlyContinue # there are instances where some containers have Pester already in the session + Import-Module -Name Pester -MinimumVersion $script:MinPesterVersion -MaximumVersion $script:MaxPesterVersion -ErrorAction 'Stop' + + Write-Build White " Performing Pester Integration Tests in $($invokePesterParams.path)" + + $pesterConfiguration = New-PesterConfiguration + $pesterConfiguration.run.Path = $script:IntegrationTestsPath + $pesterConfiguration.Run.PassThru = $true + $pesterConfiguration.Run.Exit = $false + $pesterConfiguration.CodeCoverage.Enabled = $false + $pesterConfiguration.TestResult.Enabled = $false + $pesterConfiguration.Output.Verbosity = 'Detailed' + + $testResults = Invoke-Pester -Configuration $pesterConfiguration + # This will output a nice json for each failed test (if running in CodeBuild) + if ($env:CODEBUILD_BUILD_ARN) { + $testResults.TestResult | ForEach-Object { + if ($_.Result -ne 'Passed') { + ConvertTo-Json -InputObject $_ -Compress + } + } + } + + $numberFails = $testResults.FailedCount + Assert-Build($numberFails -eq 0) ('Failed "{0}" unit tests.' -f $numberFails) + Write-Build Green ' ...Pester Integration Tests Complete!' + } +} #IntegrationTest + +#Synopsis: Creates an archive of the built Module +Add-BuildTask Archive { + Write-Build White ' Performing Archive...' + + $archivePath = Join-Path -Path $BuildRoot -ChildPath 'Archive' + if (Test-Path -Path $archivePath) { + $null = Remove-Item -Path $archivePath -Recurse -Force + } + + $null = New-Item -Path $archivePath -ItemType Directory -Force + + $zipFileName = '{0}_{1}_{2}.{3}.zip' -f $script:ModuleName, $script:ModuleVersion, ([DateTime]::UtcNow.ToString("yyyyMMdd")), ([DateTime]::UtcNow.ToString("hhmmss")) + $zipFile = Join-Path -Path $archivePath -ChildPath $zipFileName + + if ($PSEdition -eq 'Desktop') { + Add-Type -AssemblyName 'System.IO.Compression.FileSystem' + } + [System.IO.Compression.ZipFile]::CreateFromDirectory($script:ArtifactsPath, $zipFile) + + Write-Build Green ' ...Archive Complete!' +} #Archive + diff --git a/src/PSReddit/Imports.ps1 b/src/PSReddit/Imports.ps1 index 45056b1..ad7b7d9 100644 --- a/src/PSReddit/Imports.ps1 +++ b/src/PSReddit/Imports.ps1 @@ -1,6 +1,6 @@ -# This is a locally sourced Imports file for local development. -# It can be imported by the psm1 in local development to add script level variables. -# It will merged in the build process. This is for local development only. - -# region script variables -# $script:resourcePath = "$PSScriptRoot\Resources" +# This is a locally sourced Imports file for local development. +# It can be imported by the psm1 in local development to add script level variables. +# It will merged in the build process. This is for local development only. + +# region script variables +# $script:resourcePath = "$PSScriptRoot\Resources" diff --git a/src/PSReddit/PSReddit.psm1 b/src/PSReddit/PSReddit.psm1 index d9013f8..9babdcb 100644 --- a/src/PSReddit/PSReddit.psm1 +++ b/src/PSReddit/PSReddit.psm1 @@ -1,31 +1,31 @@ -# this psm1 is for local testing and development use only - -# dot source the parent import for local development variables -. $PSScriptRoot\Imports.ps1 - -# discover all ps1 file(s) in Public and Private paths - -$itemSplat = @{ - Filter = '*.ps1' - Recurse = $true - ErrorAction = 'Stop' -} -try { - $public = @(Get-ChildItem -Path "$PSScriptRoot\Public" @itemSplat) - $private = @(Get-ChildItem -Path "$PSScriptRoot\Private" @itemSplat) -} catch { - Write-Error $_ - throw 'Unable to get get file information from Public & Private src.' -} - -# dot source all .ps1 file(s) found -foreach ($file in @($public + $private)) { - try { - . $file.FullName - } catch { - throw ('Unable to dot source {0}' -f $file.FullName) - } -} - -# export all public functions -Export-ModuleMember -Function $public.Basename +# this psm1 is for local testing and development use only + +# dot source the parent import for local development variables +. $PSScriptRoot\Imports.ps1 + +# discover all ps1 file(s) in Public and Private paths + +$itemSplat = @{ + Filter = '*.ps1' + Recurse = $true + ErrorAction = 'Stop' +} +try { + $public = @(Get-ChildItem -Path "$PSScriptRoot\Public" @itemSplat) + $private = @(Get-ChildItem -Path "$PSScriptRoot\Private" @itemSplat) +} catch { + Write-Error $_ + throw 'Unable to get get file information from Public & Private src.' +} + +# dot source all .ps1 file(s) found +foreach ($file in @($public + $private)) { + try { + . $file.FullName + } catch { + throw ('Unable to dot source {0}' -f $file.FullName) + } +} + +# export all public functions +Export-ModuleMember -Function $public.Basename diff --git a/src/PSScriptAnalyzerSettings.psd1 b/src/PSScriptAnalyzerSettings.psd1 index 26e4277..e204206 100644 --- a/src/PSScriptAnalyzerSettings.psd1 +++ b/src/PSScriptAnalyzerSettings.psd1 @@ -1,61 +1,61 @@ -@{ - #________________________________________ - #IncludeDefaultRules - IncludeDefaultRules = $true - #________________________________________ - #Severity - #Specify Severity when you want to limit generated diagnostic records to a specific subset: [ Error | Warning | Information ] - Severity = @('Error', 'Warning') - #________________________________________ - #CustomRulePath - #Specify CustomRulePath when you have a large set of custom rules you'd like to reference - #CustomRulePath = "Module\InjectionHunter\1.0.0\InjectionHunter.psd1" - #________________________________________ - #IncludeRules - #Specify IncludeRules when you only want to run specific subset of rules instead of the default rule set. - #IncludeRules = @('PSShouldProcess', - # 'PSUseApprovedVerbs') - #________________________________________ - #ExcludeRules - #Specify ExcludeRules when you want to exclude a certain rule from the the default set of rules. - # PSUseDeclaredVarsMoreThanAssignments excluded due to false positives with Pester v5 scoping - ExcludeRules = @( - 'PSUseDeclaredVarsMoreThanAssignments' - ) - #________________________________________ - #Rules - #Here you can specify customizations for particular rules. Several examples are included below: - #Rules = @{ - # PSUseCompatibleCmdlets = @{ - # compatibility = @('core-6.1.0-windows', 'desktop-4.0-windows') - # } - # PSUseCompatibleSyntax = @{ - # Enable = $true - # TargetVersions = @( - # '3.0', - # '5.1', - # '6.2' - # ) - # } - # PSUseCompatibleCommands = @{ - # Enable = $true - # TargetProfiles = @( - # 'win-8_x64_10.0.14393.0_6.1.3_x64_4.0.30319.42000_core', # PS 6.1 on WinServer-2019 - # 'win-8_x64_10.0.17763.0_5.1.17763.316_x64_4.0.30319.42000_framework', # PS 5.1 on WinServer-2019 - # 'win-8_x64_6.2.9200.0_3.0_x64_4.0.30319.42000_framework' # PS 3 on WinServer-2012 - # ) - # } - # PSUseCompatibleTypes = @{ - # Enable = $true - # TargetProfiles = @( - # 'ubuntu_x64_18.04_6.1.3_x64_4.0.30319.42000_core', - # 'win-48_x64_10.0.17763.0_5.1.17763.316_x64_4.0.30319.42000_framework' - # ) - # # You can specify types to not check like this, which will also ignore methods and members on it: - # IgnoreTypes = @( - # 'System.IO.Compression.ZipFile' - # ) - # } - #} - #________________________________________ -} +@{ + #________________________________________ + #IncludeDefaultRules + IncludeDefaultRules = $true + #________________________________________ + #Severity + #Specify Severity when you want to limit generated diagnostic records to a specific subset: [ Error | Warning | Information ] + Severity = @('Error', 'Warning') + #________________________________________ + #CustomRulePath + #Specify CustomRulePath when you have a large set of custom rules you'd like to reference + #CustomRulePath = "Module\InjectionHunter\1.0.0\InjectionHunter.psd1" + #________________________________________ + #IncludeRules + #Specify IncludeRules when you only want to run specific subset of rules instead of the default rule set. + #IncludeRules = @('PSShouldProcess', + # 'PSUseApprovedVerbs') + #________________________________________ + #ExcludeRules + #Specify ExcludeRules when you want to exclude a certain rule from the the default set of rules. + # PSUseDeclaredVarsMoreThanAssignments excluded due to false positives with Pester v5 scoping + ExcludeRules = @( + 'PSUseDeclaredVarsMoreThanAssignments' + ) + #________________________________________ + #Rules + #Here you can specify customizations for particular rules. Several examples are included below: + #Rules = @{ + # PSUseCompatibleCmdlets = @{ + # compatibility = @('core-6.1.0-windows', 'desktop-4.0-windows') + # } + # PSUseCompatibleSyntax = @{ + # Enable = $true + # TargetVersions = @( + # '3.0', + # '5.1', + # '6.2' + # ) + # } + # PSUseCompatibleCommands = @{ + # Enable = $true + # TargetProfiles = @( + # 'win-8_x64_10.0.14393.0_6.1.3_x64_4.0.30319.42000_core', # PS 6.1 on WinServer-2019 + # 'win-8_x64_10.0.17763.0_5.1.17763.316_x64_4.0.30319.42000_framework', # PS 5.1 on WinServer-2019 + # 'win-8_x64_6.2.9200.0_3.0_x64_4.0.30319.42000_framework' # PS 3 on WinServer-2012 + # ) + # } + # PSUseCompatibleTypes = @{ + # Enable = $true + # TargetProfiles = @( + # 'ubuntu_x64_18.04_6.1.3_x64_4.0.30319.42000_core', + # 'win-48_x64_10.0.17763.0_5.1.17763.316_x64_4.0.30319.42000_framework' + # ) + # # You can specify types to not check like this, which will also ignore methods and members on it: + # IgnoreTypes = @( + # 'System.IO.Compression.ZipFile' + # ) + # } + #} + #________________________________________ +} diff --git a/src/Tests/Unit/ExportedFunctions.Tests.ps1 b/src/Tests/Unit/ExportedFunctions.Tests.ps1 index 5260252..39c7e1c 100644 --- a/src/Tests/Unit/ExportedFunctions.Tests.ps1 +++ b/src/Tests/Unit/ExportedFunctions.Tests.ps1 @@ -1,154 +1,154 @@ -BeforeAll { - Set-Location -Path $PSScriptRoot - $ModuleName = 'PSReddit' - $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") - Get-Module $ModuleName -ErrorAction SilentlyContinue | Remove-Module -Force - Import-Module $PathToManifest -Force - $manifestContent = Test-ModuleManifest -Path $PathToManifest - # Get all exported functions - $moduleExported = Get-Command -Module $ModuleName -CommandType Function | Select-Object -ExpandProperty Name - $manifestExported = ($manifestContent.ExportedFunctions).Keys -} -BeforeDiscovery { - Set-Location -Path $PSScriptRoot - $ModuleName = 'PSReddit' - $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") - $manifestContent = Test-ModuleManifest -Path $PathToManifest - # Get all exported functions - $moduleExported = Get-Command -Module $ModuleName -CommandType Function | Select-Object -ExpandProperty Name - $manifestExported = ($manifestContent.ExportedFunctions).Keys -} -Describe $ModuleName { - - Context 'Exported Commands' -Fixture { - - Context 'Number of commands' -Fixture { - - It 'Exports the same number of public functions as what is listed in the Module Manifest' { - $manifestExported.Count | Should -BeExactly $moduleExported.Count - } - - - - } - - Context 'Explicitly exported commands' { - - It 'Includes <_> in the Module Manifest ExportedFunctions' -ForEach $moduleExported { - $manifestExported -contains $_ | Should -BeTrue - } - - - - } - } #context_ExportedCommands - - Context 'Command Help' -Fixture { - Context '<_>' -ForEach $moduleExported { - - BeforeEach { - $help = Get-Help -Name $_ -Full - } - - It -Name 'Includes a Synopsis' -Test { - $help.Synopsis | Should -Not -BeNullOrEmpty - } - - It -Name 'Includes a Description' -Test { - $help.description.Text | Should -Not -BeNullOrEmpty - } - - It -Name 'Includes an Example' -Test { - $help.examples.example | Should -Not -BeNullOrEmpty - } - } - } #context_CommandHelp - Context 'Function Logic' { - - # Mock Invoke-RestMethod for Get-RedditOAuthToken - # Mocks moved inside individual 'It' blocks for better scoping - It 'Get-RedditOAuthToken should return a token when credentials are present (mocked)' { - # Mock Invoke-RestMethod specifically for the token endpoint for this test - Mock Invoke-RestMethod -ModuleName PSReddit -ParameterFilter { $Uri -like '*api/v1/access_token*' -and $Method -eq 'Post' } -MockWith { - Write-Verbose "Mocking Invoke-RestMethod for Token Request: $Uri" - return @{ access_token = 'mock_token_123' } - } - - # Set dummy environment variables for the test scope - $env:REDDIT_CLIENT_ID = 'mock_client_id' - $env:REDDIT_CLIENT_SECRET = 'mock_client_secret' - - $token = Get-RedditOAuthToken - $token | Should -Be 'mock_token_123' - - # Clean up environment variables - Remove-Item Env:\REDDIT_CLIENT_ID - Remove-Item Env:\REDDIT_CLIENT_SECRET - } - - It 'Get-RedditSubredditPost should return posts (mocked)' { - # Mock Invoke-RestMethod specifically for the posts endpoint for this test - Mock Invoke-RestMethod -ModuleName PSReddit -ParameterFilter { $Uri -like '*oauth.reddit.com*' } -MockWith { - Write-Verbose "Mocking Invoke-RestMethod for Posts Request: $Uri" - # Simulate successful post retrieval - return @{ - data = @{ - children = @( - @{ data = @{ title = 'Mock Post 1'; score = 10; id = 'mp1' } } - @{ data = @{ title = 'Mock Post 2'; score = 20; id = 'mp2' } } - ) - } - } - } - - # Mock Invoke-RestMethod specifically for the posts endpoint for this test - Mock Invoke-RestMethod -ModuleName PSReddit -ParameterFilter { $Uri -like '*oauth.reddit.com*' } -MockWith { - Write-Verbose "Mocking Invoke-RestMethod for Posts Request: $Uri" - # Simulate successful post retrieval - return @{ - data = @{ - children = @( - @{ data = @{ title = 'Mock Post 1'; score = 10; id = 'mp1' } } - @{ data = @{ title = 'Mock Post 2'; score = 20; id = 'mp2' } } - ) - } - } - } - - # Mock Get-RedditOAuthToken specifically for this test - Mock Get-RedditOAuthToken -ModuleName PSReddit -MockWith { return 'mock_token_for_getpost' } - - $posts = Get-RedditSubredditPost -Subreddit 'powershell' -Count 2 - $posts | Should -Not -BeNullOrEmpty - $posts.Count | Should -Be 2 - $posts[0].title | Should -Be 'Mock Post 1' - $posts[1].score | Should -Be 20 - } - - It 'Get-RedditUserPost should return user posts (mocked)' { - # Mock Invoke-RestMethod specifically for the user posts endpoint - Mock Invoke-RestMethod -ModuleName PSReddit -ParameterFilter { $Uri -like '*oauth.reddit.com/user/*' } -MockWith { - Write-Verbose "Mocking Invoke-RestMethod for User Posts Request: $Uri" - # Simulate successful user post retrieval - return @{ - data = @{ - children = @( - @{ data = @{ title = 'User Post 1'; score = 15; id = 'up1' } } - @{ data = @{ title = 'User Post 2'; score = 25; id = 'up2' } } - ) - } - } - } - - # Mock Get-RedditOAuthToken specifically for this test - Mock Get-RedditOAuthToken -ModuleName PSReddit -MockWith { return 'mock_token_for_userpost' } - - $posts = Get-RedditUserPost -Username 'LukeEvansTech' -Count 2 - $posts | Should -Not -BeNullOrEmpty - $posts.Count | Should -Be 2 - $posts[0].title | Should -Be 'User Post 1' - $posts[1].score | Should -Be 25 - } - } #context_FunctionLogic -} - +BeforeAll { + Set-Location -Path $PSScriptRoot + $ModuleName = 'PSReddit' + $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") + Get-Module $ModuleName -ErrorAction SilentlyContinue | Remove-Module -Force + Import-Module $PathToManifest -Force + $manifestContent = Test-ModuleManifest -Path $PathToManifest + # Get all exported functions + $moduleExported = Get-Command -Module $ModuleName -CommandType Function | Select-Object -ExpandProperty Name + $manifestExported = ($manifestContent.ExportedFunctions).Keys +} +BeforeDiscovery { + Set-Location -Path $PSScriptRoot + $ModuleName = 'PSReddit' + $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") + $manifestContent = Test-ModuleManifest -Path $PathToManifest + # Get all exported functions + $moduleExported = Get-Command -Module $ModuleName -CommandType Function | Select-Object -ExpandProperty Name + $manifestExported = ($manifestContent.ExportedFunctions).Keys +} +Describe $ModuleName { + + Context 'Exported Commands' -Fixture { + + Context 'Number of commands' -Fixture { + + It 'Exports the same number of public functions as what is listed in the Module Manifest' { + $manifestExported.Count | Should -BeExactly $moduleExported.Count + } + + + + } + + Context 'Explicitly exported commands' { + + It 'Includes <_> in the Module Manifest ExportedFunctions' -ForEach $moduleExported { + $manifestExported -contains $_ | Should -BeTrue + } + + + + } + } #context_ExportedCommands + + Context 'Command Help' -Fixture { + Context '<_>' -ForEach $moduleExported { + + BeforeEach { + $help = Get-Help -Name $_ -Full + } + + It -Name 'Includes a Synopsis' -Test { + $help.Synopsis | Should -Not -BeNullOrEmpty + } + + It -Name 'Includes a Description' -Test { + $help.description.Text | Should -Not -BeNullOrEmpty + } + + It -Name 'Includes an Example' -Test { + $help.examples.example | Should -Not -BeNullOrEmpty + } + } + } #context_CommandHelp + Context 'Function Logic' { + + # Mock Invoke-RestMethod for Get-RedditOAuthToken + # Mocks moved inside individual 'It' blocks for better scoping + It 'Get-RedditOAuthToken should return a token when credentials are present (mocked)' { + # Mock Invoke-RestMethod specifically for the token endpoint for this test + Mock Invoke-RestMethod -ModuleName PSReddit -ParameterFilter { $Uri -like '*api/v1/access_token*' -and $Method -eq 'Post' } -MockWith { + Write-Verbose "Mocking Invoke-RestMethod for Token Request: $Uri" + return @{ access_token = 'mock_token_123' } + } + + # Set dummy environment variables for the test scope + $env:REDDIT_CLIENT_ID = 'mock_client_id' + $env:REDDIT_CLIENT_SECRET = 'mock_client_secret' + + $token = Get-RedditOAuthToken + $token | Should -Be 'mock_token_123' + + # Clean up environment variables + Remove-Item Env:\REDDIT_CLIENT_ID + Remove-Item Env:\REDDIT_CLIENT_SECRET + } + + It 'Get-RedditSubredditPost should return posts (mocked)' { + # Mock Invoke-RestMethod specifically for the posts endpoint for this test + Mock Invoke-RestMethod -ModuleName PSReddit -ParameterFilter { $Uri -like '*oauth.reddit.com*' } -MockWith { + Write-Verbose "Mocking Invoke-RestMethod for Posts Request: $Uri" + # Simulate successful post retrieval + return @{ + data = @{ + children = @( + @{ data = @{ title = 'Mock Post 1'; score = 10; id = 'mp1' } } + @{ data = @{ title = 'Mock Post 2'; score = 20; id = 'mp2' } } + ) + } + } + } + + # Mock Invoke-RestMethod specifically for the posts endpoint for this test + Mock Invoke-RestMethod -ModuleName PSReddit -ParameterFilter { $Uri -like '*oauth.reddit.com*' } -MockWith { + Write-Verbose "Mocking Invoke-RestMethod for Posts Request: $Uri" + # Simulate successful post retrieval + return @{ + data = @{ + children = @( + @{ data = @{ title = 'Mock Post 1'; score = 10; id = 'mp1' } } + @{ data = @{ title = 'Mock Post 2'; score = 20; id = 'mp2' } } + ) + } + } + } + + # Mock Get-RedditOAuthToken specifically for this test + Mock Get-RedditOAuthToken -ModuleName PSReddit -MockWith { return 'mock_token_for_getpost' } + + $posts = Get-RedditSubredditPost -Subreddit 'powershell' -Count 2 + $posts | Should -Not -BeNullOrEmpty + $posts.Count | Should -Be 2 + $posts[0].title | Should -Be 'Mock Post 1' + $posts[1].score | Should -Be 20 + } + + It 'Get-RedditUserPost should return user posts (mocked)' { + # Mock Invoke-RestMethod specifically for the user posts endpoint + Mock Invoke-RestMethod -ModuleName PSReddit -ParameterFilter { $Uri -like '*oauth.reddit.com/user/*' } -MockWith { + Write-Verbose "Mocking Invoke-RestMethod for User Posts Request: $Uri" + # Simulate successful user post retrieval + return @{ + data = @{ + children = @( + @{ data = @{ title = 'User Post 1'; score = 15; id = 'up1' } } + @{ data = @{ title = 'User Post 2'; score = 25; id = 'up2' } } + ) + } + } + } + + # Mock Get-RedditOAuthToken specifically for this test + Mock Get-RedditOAuthToken -ModuleName PSReddit -MockWith { return 'mock_token_for_userpost' } + + $posts = Get-RedditUserPost -Username 'LukeEvansTech' -Count 2 + $posts | Should -Not -BeNullOrEmpty + $posts.Count | Should -Be 2 + $posts[0].title | Should -Be 'User Post 1' + $posts[1].score | Should -Be 25 + } + } #context_FunctionLogic +} + diff --git a/src/Tests/Unit/PSReddit-Module.Tests.ps1 b/src/Tests/Unit/PSReddit-Module.Tests.ps1 index bec858c..78bdb3b 100644 --- a/src/Tests/Unit/PSReddit-Module.Tests.ps1 +++ b/src/Tests/Unit/PSReddit-Module.Tests.ps1 @@ -1,49 +1,49 @@ -BeforeAll { - #------------------------------------------------------------------------- - Set-Location -Path $PSScriptRoot - #------------------------------------------------------------------------- - $ModuleName = 'PSReddit' - $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") - $PathToModule = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psm1") - #------------------------------------------------------------------------- -} -Describe 'Module Tests' -Tag Unit { - Context "Module Tests" { - $script:manifestEval = $null - It 'Passes Test-ModuleManifest' { - { $script:manifestEval = Test-ModuleManifest -Path $PathToManifest } | Should -Not -Throw - $? | Should -BeTrue - } #manifestTest - It 'root module PSReddit.psm1 should exist' { - $PathToModule | Should -Exist - $? | Should -BeTrue - } #psm1Exists - It 'manifest should contain PSReddit.psm1' { - $PathToManifest | - Should -FileContentMatchExactly "PSReddit.psm1" - } #validPSM1 - It 'should have a matching module name in the manifest' { - $script:manifestEval.Name | Should -BeExactly $ModuleName - } #name - It 'should have a valid description in the manifest' { - $script:manifestEval.Description | Should -Not -BeNullOrEmpty - } #description - It 'should have a valid author in the manifest' { - $script:manifestEval.Author | Should -Not -BeNullOrEmpty - } #author - It 'should have a valid version in the manifest' { - $script:manifestEval.Version -as [Version] | Should -Not -BeNullOrEmpty - } #version - It 'should have a valid guid in the manifest' { - { [guid]::Parse($script:manifestEval.Guid) } | Should -Not -Throw - } #guid - It 'should not have any spaces in the tags' { - foreach ($tag in $script:manifestEval.Tags) { - $tag | Should -Not -Match '\s' - } - } #tagSpaces - It 'should have a valid project Uri' { - $script:manifestEval.ProjectUri | Should -Not -BeNullOrEmpty - } #uri - } #context_ModuleTests +BeforeAll { + #------------------------------------------------------------------------- + Set-Location -Path $PSScriptRoot + #------------------------------------------------------------------------- + $ModuleName = 'PSReddit' + $PathToManifest = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psd1") + $PathToModule = [System.IO.Path]::Combine('..', '..', $ModuleName, "$ModuleName.psm1") + #------------------------------------------------------------------------- +} +Describe 'Module Tests' -Tag Unit { + Context "Module Tests" { + $script:manifestEval = $null + It 'Passes Test-ModuleManifest' { + { $script:manifestEval = Test-ModuleManifest -Path $PathToManifest } | Should -Not -Throw + $? | Should -BeTrue + } #manifestTest + It 'root module PSReddit.psm1 should exist' { + $PathToModule | Should -Exist + $? | Should -BeTrue + } #psm1Exists + It 'manifest should contain PSReddit.psm1' { + $PathToManifest | + Should -FileContentMatchExactly "PSReddit.psm1" + } #validPSM1 + It 'should have a matching module name in the manifest' { + $script:manifestEval.Name | Should -BeExactly $ModuleName + } #name + It 'should have a valid description in the manifest' { + $script:manifestEval.Description | Should -Not -BeNullOrEmpty + } #description + It 'should have a valid author in the manifest' { + $script:manifestEval.Author | Should -Not -BeNullOrEmpty + } #author + It 'should have a valid version in the manifest' { + $script:manifestEval.Version -as [Version] | Should -Not -BeNullOrEmpty + } #version + It 'should have a valid guid in the manifest' { + { [guid]::Parse($script:manifestEval.Guid) } | Should -Not -Throw + } #guid + It 'should not have any spaces in the tags' { + foreach ($tag in $script:manifestEval.Tags) { + $tag | Should -Not -Match '\s' + } + } #tagSpaces + It 'should have a valid project Uri' { + $script:manifestEval.ProjectUri | Should -Not -BeNullOrEmpty + } #uri + } #context_ModuleTests } #describe_ModuleTests