Browse Source

fix(help): ship the generated help file so Get-Help works (#93)

* fix(help): ship the generated help file so Get-Help works

Get-Help showed only the syntax of the cmdlets: the module shipped a
pre-4.x MAML file for the old command names under the wrong name
(NTFSSecurity-Help.xml), while PowerShell looks for
en-US\NTFSSecurity.dll-Help.xml.

- Generate en-US\NTFSSecurity.dll-Help.xml from Docs/Cmdlets with
  New-ExternalHelp and commit it. The csproj copies it to the output,
  so every build ships it, including the local Debug builds that
  releases are published from.
- List all runtime files, including the help file, in FileList.
- Remove the stale NTFSSecurity-Help.xml and the unused help editor
  project NTFSSecurity\Help\NTFSSecurity.Help.pshproj.
- Add Tests\Help.Tests.ps1 (Pester 5): Get-Help shows the synopsis,
  parameters, examples, and online link of every page, and
  Get-Help -Online resolves to the GitHub page.
- Reword six sentences in five cmdlet pages so that each link ends its
  sentence: platyPS drops the space after a link in the help text.
- CI regenerates the help file and fails when it differs from the
  committed file, then runs the Pester tests.
- Document the regeneration step and the link rule in the contributor
  guide.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>

* ci: report each Pester test once on AppVeyor

AppVeyor build 54834154 passed all 218 Pester tests but listed 870 on
its Tests tab: the NUnit import files a Pester 5 test under every block
that contains it (Pester, test file, Describe, and Context).

Report the results through the build worker API instead
(POST api/tests/batch): one entry per test with its outcome, duration,
and error message. Outside AppVeyor, and when no test ran, the step
sends nothing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/94/head
Raimund Andrée 1 week ago
committed by GitHub
parent
commit
14799fb465
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 37
      .memory-bank/activeContext.md
  2. 26
      .memory-bank/decisions/0008-commit-generated-help.md
  3. 32
      .memory-bank/progress.md
  4. 20
      .memory-bank/systemPatterns.md
  5. 38
      .memory-bank/techContext.md
  6. 4
      CHANGELOG.md
  7. 4
      Docs/Cmdlets/Add-NTFSAccess.md
  8. 2
      Docs/Cmdlets/Add-NTFSAudit.md
  9. 2
      Docs/Cmdlets/Get-NTFSAudit.md
  10. 2
      Docs/Cmdlets/Remove-NTFSAccess.md
  11. 2
      Docs/Cmdlets/Remove-NTFSAudit.md
  12. 22
      Docs/Contributing/02-Writing.md
  13. 7
      Docs/Contributing/04-Markdown-Specifics.md
  14. 2786
      NTFSSecurity/Help/NTFSSecurity.Help.pshproj
  15. 2333
      NTFSSecurity/NTFSSecurity-Help.xml
  16. 3
      NTFSSecurity/NTFSSecurity.csproj
  17. 14
      NTFSSecurity/NTFSSecurity.psd1
  18. 10020
      NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml
  19. 115
      Tests/Help.Tests.ps1
  20. 42
      appveyor.yml

37
.memory-bank/activeContext.md

@ -9,23 +9,32 @@ source: current task evidence
## Current focus
Work package 1 (housekeeping) on branch `ai/housekeeping`; the five work
packages and their order are in `progress.md`.
Work packages 1 and 2 are pushed; the maintainer opens their PRs:
`ai/housekeeping` into `master`, and `ai/ship-help` into
`ai/housekeeping`. The work packages and their order are in `progress.md`.
## Evidence
- PR #91 is merged into `master` as `690d8dd` (squash merge). The local
branch `ai/docs-alignment` had the same tree as `690d8dd` and is deleted;
GitHub had already deleted the remote branch.
- `.memory-bank/promptHistory.md` is ignored by git (`.gitignore`) and stays
a local file.
- The changelog policy is Decision 7. The inline Decisions moved to
`decisions/` records because `systemPatterns.md` was near its 110-line
budget.
- Read the Docs project `ntfssecurity` still builds the fork
`Sup3rlativ3/NTFSSecurity`.
- AppVeyor 54834155 (`ai/housekeeping`, `74abb0b`) passed. AppVeyor
54834154 (`ai/ship-help`, `fba3a7d`) passed all four `test_script`
steps; Pester passed 218 of 218 tests in Windows PowerShell 5.1,
including the `Get-Help -Online` tests.
- The same build listed 870 tests on the Tests tab: the NUnit import files
each Pester 5 test under every enclosing block (Pester, file, Describe,
Context), so 216 tests appear four times and 2 three times. The
follow-up commit on `ai/ship-help` reports the results through the build
worker API instead (`POST api/tests/batch`), one entry per test; a local
run of step 04 against a sample test file sent 11 entries for 11 tests.
- The AppVeyor job log API returns `application/octet-stream`; decode the
bytes as UTF-8 before searching it.
- Merging work package 1 with a merge commit keeps `ai/ship-help` valid;
after a squash merge it needs
`git rebase --onto origin/master ai/housekeeping ai/ship-help`.
- PR descriptions for both work packages are in the session folder
(`files/pr`), outside the repository.
## Next step
The maintainer pushes `ai/housekeeping` and opens the PR. After the
go-ahead, start work package 2 (ship the generated help).
The maintainer pushes the follow-up commit on `ai/ship-help`, opens both
PRs, and checks that AppVeyor lists 218 tests; then work package 3 (Read
the Docs).

26
.memory-bank/decisions/0008-commit-generated-help.md

@ -0,0 +1,26 @@
---
status: accepted
date: 2026-10-02
last-verified: 2026-10-02
owner: shared
source: maintainer choice in work package 2 (option A)
---
# Decision 8: Commit the generated help file and check it in CI
- Choice: `NTFSSecurity\en-US\NTFSSecurity.dll-Help.xml` is generated from
`Docs/Cmdlets` with `New-ExternalHelp` (platyPS 0.14.2, Windows
PowerShell 5.1) and committed. `NTFSSecurity.csproj` copies it to the
output as `Content`, the manifest `FileList` lists it, and `appveyor.yml`
regenerates it and fails on `git status --porcelain -- NTFSSecurity/en-US`.
- Rationale: Releases are built locally in Visual Studio (Debug, written to
`C:\Program Files\WindowsPowerShell\Modules\NTFSSecurity`) and published
by hand with `Publish-Module`. A committed file ships with every build
and needs no tool on the build machine. Generating it in an MSBuild step
would need platyPS on every build machine, or would silently ship without
help when platyPS is missing.
- Consequence: every change to `Docs/Cmdlets` must be followed by
`New-ExternalHelp -Path .\Docs\Cmdlets -OutputPath .\NTFSSecurity\en-US
-Force`. The output is deterministic (byte-identical between runs), so
the CI check is exact.
- Rejected: an MSBuild `AfterBuild` target that runs platyPS.

32
.memory-bank/progress.md

@ -33,7 +33,20 @@ source is unchanged since the 4.2.6 release except the
superseded: #91 ships the same two `learn.microsoft.com` links in
`Docs/index.md` and `README.md`. The maintainer decided to close it;
the remote-mutation hook denied the agent's `gh pr close`, so the
maintainer closes it by hand.
maintainer closed it by hand (2026-10-02 21:18 UTC).
- 2026-10-02: Work package 2 (ship help, `ai/ship-help`, stacked on
`ai/housekeeping`): `en-US\NTFSSecurity.dll-Help.xml` generated from
`Docs/Cmdlets`, committed, copied by the csproj, and listed in a complete
`FileList` (Decision 8); stale `NTFSSecurity-Help.xml` and the unused
`NTFSSecurity.Help.pshproj` removed; `Tests\Help.Tests.ps1` (Pester 5)
and two CI steps added; six inline links in five cmdlet pages reworded
because platyPS drops the space after a link in the help text. Tests: 218
of 218 pass in Windows PowerShell 5.1; without the help file 180 of 182
failed.
- 2026-10-04: Work packages 1 and 2 pushed; AppVeyor 54834155
(`74abb0b`) and 54834154 (`fba3a7d`, 218 of 218 Pester tests) passed. A
follow-up commit reports each test once on the AppVeyor Tests tab
(the NUnit upload listed 870 entries).
## Stable capabilities
@ -49,12 +62,11 @@ Work packages in the order agreed with the maintainer. Each gets one
`ai/<slug>` branch and PR, committed locally; the maintainer pushes, and
the next package starts only after the maintainer's go-ahead.
1. Housekeeping: committed on `ai/housekeeping`; awaiting push and PR.
2. Ship help: generate `en-US\NTFSSecurity.dll-Help.xml` from `Docs/Cmdlets`
with `New-ExternalHelp` and ship it (csproj `Content`, manifest
`FileList`); remove the stale `NTFSSecurity-Help.xml` and, if unused,
`NTFSSecurity\Help\NTFSSecurity.Help.pshproj`. Ask the maintainer:
commit the file plus a CI currency check, or generate it in the build.
1. Housekeeping: `ai/housekeeping` pushed at `74abb0b`, AppVeyor green;
PR into `master` to open.
2. Ship help: `ai/ship-help` pushed at `fba3a7d`, AppVeyor green; the
follow-up commit for the Tests tab awaits push. PR into
`ai/housekeeping` to open.
3. Read the Docs: project `ntfssecurity` (maintainer `Sup3rlativ3`) builds
the fork; switch it to this repository or import a new project. Ask
before installing Python for `mkdocs build --strict`.
@ -62,7 +74,11 @@ the next package starts only after the maintainer's go-ahead.
inheritance entries from `CmdletsToExport` (36 cmdlets remain); ask
about the next version (with or without a `PassThur` alias),
`PowerShellVersion`, and `DotNetFrameworkVersion`; align `AssemblyInfo`.
No tag or publish.
No tag or publish. Inputs found in work package 2: `Test-ModuleManifest`
already fails on `PowerShellVersion = '2.0'` with `CompatiblePSEditions`;
releases ship Debug builds plus `.pdb`, `.xml`, and
`System.Management.Automation.dll` (`Private=True` reference), which
`FileList` doesn't list.
5. Code defects, listed below: `review: on`, one PR per group, regression
test first. Pester 5 tests import `NTFSSecurity\bin\Release`, run in a
`$env:TEMP` sandbox and in `appveyor.yml`, and skip elevated cases when

20
.memory-bank/systemPatterns.md

@ -18,7 +18,9 @@ NTFSSecurity.psd1 ─┬─ ScriptsToProcess: NTFSSecurity.Init.ps1
│ (Owner, IsInheritanceBlocked, LengthOnDisk on
│ FileInfo/DirectoryInfo; AccountType on ACEs)
├─ ModuleToProcess: NTFSSecurity.psm1 (aliases)
└─ NestedModules: NTFSSecurity.dll (36 cmdlets)
├─ NestedModules: NTFSSecurity.dll (36 cmdlets)
└─ en-US\NTFSSecurity.dll-Help.xml (Get-Help; generated
from Docs/Cmdlets, Decision 8)
NTFSSecurity.dll ── cmdlets ──> Security2.dll (FileSystemAccessRule2,
FileSystemAuditRule2, IdentityReference2,
FileSystemInheritanceInfo, EffectiveAccess)
@ -52,6 +54,7 @@ Each Decision record is a file in `decisions/`; read only the relevant ones.
| 5 | [Document defects, don't fix them in docs work](decisions/0005-document-defects-separately.md) |
| 6 | [CI checks the docs against a build of the source](decisions/0006-ci-checks-docs-against-build.md) |
| 7 | [CHANGELOG lists user-visible changes only](decisions/0007-changelog-user-visible-only.md) |
| 8 | [Commit the generated help file and check it in CI](decisions/0008-commit-generated-help.md) |
## Patterns
@ -61,5 +64,20 @@ Each Decision record is a file in `decisions/`; read only the relevant ones.
`Docs/Cmdlets` must round-trip through `Update-MarkdownHelp` unchanged.
- platyPS rewrites non-ASCII punctuation such as em dashes; keep cmdlet pages
ASCII-only.
- In cmdlet pages, end a sentence with a link: platyPS renders a link as
`text (url)` in the help file and drops the space after it.
- Verify examples in a `$env:TEMP` sandbox, never on real data; parse every
example and check its parameters against `Get-Command` metadata.
### Testing the module
- Pester 5 tests in `Tests/*.Tests.ps1` import
`NTFSSecurity\bin\Release\NTFSSecurity.psd1` and run in Windows
PowerShell 5.1 (as in AppVeyor); PowerShell 7 runs are a secondary check.
- `Get-Help -Online` is tested with the internal test hook
`BypassOnlineHelpRetrieval`, which returns the URI instead of opening a
browser. In PowerShell 7 the hook also skips the help file, so that test
runs only in Windows PowerShell; PowerShell 7 resolves the same URI.
- Report Pester 5 results to AppVeyor through the build worker API, not as
an uploaded NUnit file: the NUnit import files each test under every
enclosing block (870 entries for 218 tests in build 54834154).

38
.memory-bank/techContext.md

@ -22,6 +22,10 @@ source: repository evidence
- Documentation: MkDocs (`mkdocs.yml`, theme `readthedocs`, `docs_dir: ./Docs`)
built by Read the Docs (`.readthedocs.yml` v2); cmdlet pages are platyPS
0.14 markdown (schema 2.0.0) in `Docs/Cmdlets`.
- Help: `NTFSSecurity\en-US\NTFSSecurity.dll-Help.xml`, generated from
`Docs/Cmdlets` and committed (Decision 8).
- Tests: Pester 5 tests in `Tests` (`Help.Tests.ps1`) against the Release
build.
## Environment
@ -34,6 +38,12 @@ source: repository evidence
`/p:CscToolPath` to the Roslyn `csc.exe` of the `Microsoft.Net.Compilers`
package; the legacy C# 5 compiler fails with CS0136. `dotnet msbuild`
fails on the binary resources in `Resources.resx` (MSB3822, MSB3823).
- platyPS 0.14.2, Pester 5.7.1, PSScriptAnalyzer, and powershell-yaml are
installed only for PowerShell 7. Windows PowerShell 5.1 imports platyPS
and Pester by full path
(`~\OneDrive\Documents\PowerShell\Modules\platyPS\0.14.2`,
`C:\Program Files\PowerShell\Modules\Pester\5.7.1`) with
`$env:PSModulePath` cleared. MarkdownLinkCheck is not installed.
## Constraints
@ -43,8 +53,15 @@ source: repository evidence
`-PassThru` rename and `CompatiblePSEditions` in the manifest.
- `CmdletsToExport` lists `Show-NTFSSimpleAccess`, which no longer exists
(WinForms code removed in `d3063de`), and repeats the inheritance cmdlets.
- `NTFSSecurity/NTFSSecurity-Help.xml` is a stale pre-4.x MAML file for old
command names; binary-module help must be named `NTFSSecurity.dll-Help.xml`.
- `Test-ModuleManifest` fails in Windows PowerShell 5.1:
`CompatiblePSEditions` requires `PowerShellVersion` 5.1 or higher, and the
manifest says `2.0` (work package 4).
- Releases have no script and no CI deployment. Evidence from 4.2.6: the
Gallery DLLs are Debug builds (`DebuggableAttribute` 263), the nuspec
comes from `Publish-Module`, the package holds the whole output folder
(`.pdb`, `AlphaFS.xml`, 7 MB `System.Management.Automation.dll`), and the
published manifest differs from the tag only by `ModuleVersion` (tags
carry the previous version). GitHub releases attach `NTFSSecurity.zip`.
- CI: AppVeyor project `raandree/ntfssecurity` builds branches and pull
requests. Read the Docs (`ntfssecurity`) and a second AppVeyor project are
attached to the fork `Sup3rlativ3/NTFSSecurity`.
@ -60,12 +77,23 @@ source: repository evidence
per project plus `Microsoft.NETFramework.ReferenceAssemblies.net452`
1.0.3, build `NTFSSecurity.csproj` in Release with
`TargetFrameworkRootPath`/`FrameworkPathOverride`, import
`NTFSSecurity\bin\Release\NTFSSecurity.psd1`, run `Update-MarkdownHelp`,
and fail on `git diff -- Docs/Cmdlets`; then `Get-MarkdownLink -BrokenOnly`.
`NTFSSecurity\bin\Release\NTFSSecurity.psd1`, then: 01 run
`Update-MarkdownHelp` and fail on `git diff -- Docs/Cmdlets`; 02
`Get-MarkdownLink -BrokenOnly`; 03 regenerate the help file and fail on
`git status --porcelain -- NTFSSecurity/en-US`; 04 Pester 5.7.1 on
`Tests`, each result reported once through the build worker API
(`POST $env:APPVEYOR_API_URL/api/tests/batch`).
- AppVeyor REST API (public, no token): build
`api/projects/raandree/ntfssecurity/builds/<buildId>`, job log
`api/buildjobs/<jobId>/log` (bytes; decode as UTF-8), and test list
`api/buildjobs/<jobId>/tests`.
- Run platyPS in Windows PowerShell 5.1 to avoid PowerShell 7.4+
`-ProgressAction` noise.
- Placeholder check: no `{{` left in `Docs/Cmdlets/*.md`.
- Help build check: `New-ExternalHelp -Path ./Docs/Cmdlets` to a temp folder.
- Help file: `New-ExternalHelp -Path .\Docs\Cmdlets -OutputPath
.\NTFSSecurity\en-US -Force` must leave `git status` unchanged.
- Pester: run detached (`Start-DetachedPowerShell.ps1`) in Windows
PowerShell 5.1; a run without `bin\Release\en-US` must fail.
- Markdown lint: `npx markdownlint-cli2` with `MD013` limited to prose
(tables, code, and headings excluded) on the conceptual pages.
- YAML: `ConvertFrom-Yaml` (powershell-yaml) on `mkdocs.yml`,

4
CHANGELOG.md

@ -23,6 +23,10 @@ in the wiki.
### Fixed
- Fix `Get-Help`, which showed only the syntax: ship the help file
`en-US\NTFSSecurity.dll-Help.xml` generated from the cmdlet documentation,
including the links that `Get-Help -Online` opens, instead of the outdated
`NTFSSecurity-Help.xml`
- Fix documentation examples that did not work, such as restoring
permissions from a CSV file and filtering entries by account
- Fix the documentation site navigation, the "Edit on GitHub" links, and the

4
Docs/Cmdlets/Add-NTFSAccess.md

@ -90,7 +90,7 @@ This command restores the access control entries that `Get-NTFSAccess` exported
### -AccessRights
Specifies the rights the ACE grants or denies. The parameter accepts basic rights such as `Read`, `ReadAndExecute`, `Modify`, and `FullControl`, granular rights such as `CreateFiles`, `Traverse`, or `WriteAttributes`, and any combination of them. An `Allow` ACE always receives `Synchronize` in addition to the specified rights. See [Concepts](../Concepts.md) for how the values relate to the Windows security dialog.
Specifies the rights the ACE grants or denies. The parameter accepts basic rights such as `Read`, `ReadAndExecute`, `Modify`, and `FullControl`, granular rights such as `CreateFiles`, `Traverse`, or `WriteAttributes`, and any combination of them. An `Allow` ACE always receives `Synchronize` in addition to the specified rights. For how the values relate to the Windows security dialog, see [Concepts](../Concepts.md).
```yaml
Type: FileSystemRights2
@ -159,7 +159,7 @@ Accept wildcard characters: False
Specifies which kind of child objects inherit the ACE. `ContainerInherit` passes the ACE on to child folders, `ObjectInherit` passes it on to child files, and `None` keeps the ACE on the item itself. The default is `ContainerInherit, ObjectInherit`. Inheritance flags have no effect on files, where the ACE is always created with `None`.
For details about the flags, see [InheritanceFlags Enum](https://learn.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.inheritanceflags) in the .NET documentation.
For details about the flags, see the .NET documentation of the [InheritanceFlags Enum](https://learn.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.inheritanceflags).
```yaml
Type: InheritanceFlags

2
Docs/Cmdlets/Add-NTFSAudit.md

@ -92,7 +92,7 @@ This command adds an audit entry for `CONTOSO\JohnDoe` to the in-memory security
### -AccessRights
Specifies the access rights to audit. The value accepts the basic rights such as `Read`, `Write`, `Modify`, and `FullControl` as well as the individual rights such as `Delete` or `WriteAttributes`, and it accepts a comma-separated list that combines them. See [Concepts](../Concepts.md) for the meaning of each right.
Specifies the access rights to audit. The value accepts the basic rights such as `Read`, `Write`, `Modify`, and `FullControl` as well as the individual rights such as `Delete` or `WriteAttributes`, and it accepts a comma-separated list that combines them. For the meaning of each right, see [Concepts](../Concepts.md).
```yaml
Type: FileSystemRights2

2
Docs/Cmdlets/Get-NTFSAudit.md

@ -27,7 +27,7 @@ Get-NTFSAudit [-SecurityDescriptor] <FileSystemSecurity2[]> [-Account <IdentityR
## DESCRIPTION
The `Get-NTFSAudit` cmdlet returns the audit entries that are stored in the system access control list (SACL) of a file or folder. Each entry is a `Security2.FileSystemAuditRule2` object that reports the audited account, the audited access rights, the audit flags (`Success`, `Failure`, or both), the inheritance and propagation flags, whether the entry is inherited, and the item it is inherited from. The access rights are the same values that `Add-NTFSAccess` and `Add-NTFSAudit` use; see [Concepts](../Concepts.md) for what each right permits.
The `Get-NTFSAudit` cmdlet returns the audit entries that are stored in the system access control list (SACL) of a file or folder. Each entry is a `Security2.FileSystemAuditRule2` object that reports the audited account, the audited access rights, the audit flags (`Success`, `Failure`, or both), the inheritance and propagation flags, whether the entry is inherited, and the item it is inherited from. The access rights are the same values that `Add-NTFSAccess` and `Add-NTFSAudit` use; for what each right permits, see [Concepts](../Concepts.md).
In the `Path` parameter set the cmdlet reads the security descriptor of every item in `-Path`. Relative paths are resolved against the current location, and when you omit `-Path` the cmdlet uses the current location. The parameter accepts pipeline input by value and by property name through its `FullName` alias, so the output of `Get-ChildItem`, `Get-ChildItem2`, and `Get-Item2` binds to it. In the `SD` parameter set the cmdlet reads the audit entries from an in-memory `Security2.FileSystemSecurity2` object that `Get-NTFSSecurityDescriptor` returned instead of reading the item again.

2
Docs/Cmdlets/Remove-NTFSAccess.md

@ -88,7 +88,7 @@ This command removes the access control entries of deleted accounts from all ite
### -AccessRights
Specifies the rights to remove from the matching access control entry. The parameter accepts basic rights such as `Read`, `ReadAndExecute`, `Modify`, and `FullControl`, granular rights such as `CreateFiles`, `Traverse`, or `WriteAttributes`, and any combination of them. Rights that the entry grants but that are not listed here remain in place. See [Concepts](../Concepts.md) for how the values relate to the Windows security dialog.
Specifies the rights to remove from the matching access control entry. The parameter accepts basic rights such as `Read`, `ReadAndExecute`, `Modify`, and `FullControl`, granular rights such as `CreateFiles`, `Traverse`, or `WriteAttributes`, and any combination of them. Rights that the entry grants but that are not listed here remain in place. For how the values relate to the Windows security dialog, see [Concepts](../Concepts.md).
```yaml
Type: FileSystemRights2

2
Docs/Cmdlets/Remove-NTFSAudit.md

@ -90,7 +90,7 @@ This command removes the entry from the in-memory security descriptor of `C:\Dat
### -AccessRights
Specifies the audited access rights to remove. The value accepts the basic rights such as `Read`, `Write`, `Modify`, and `FullControl` as well as the individual rights such as `Delete` or `WriteAttributes`, and it accepts a comma-separated list that combines them. Rights that an existing entry audits beyond the ones you specify stay in place. See [Concepts](../Concepts.md) for the meaning of each right.
Specifies the audited access rights to remove. The value accepts the basic rights such as `Read`, `Write`, `Modify`, and `FullControl` as well as the individual rights such as `Delete` or `WriteAttributes`, and it accepts a comma-separated list that combines them. Rights that an existing entry audits beyond the ones you specify stay in place. For the meaning of each right, see [Concepts](../Concepts.md).
```yaml
Type: FileSystemRights2

22
Docs/Contributing/02-Writing.md

@ -12,6 +12,7 @@ organized and how to change it.
| `Docs/Concepts.md` | Background on security descriptors, rights, inheritance, and privileges |
| `Docs/Examples.md` | Task-oriented examples |
| `Docs/Cmdlets/*.md` | One reference page per cmdlet, in platyPS format |
| `NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml` | Help file that `Get-Help` shows, generated from `Docs/Cmdlets` |
| `Docs/Contributing.md`, `Docs/Contributing/*.md` | This contributor guide |
| `mkdocs.yml` | Site settings and navigation |
| `.readthedocs.yml` | Build settings for Read the Docs |
@ -65,11 +66,13 @@ cmdlet:
New-MarkdownHelp -Command Get-NTFSExample -OutputFolder .\Docs\Cmdlets
```
To check that the pages can be converted to the help file that `Get-Help`
reads, run `New-ExternalHelp`:
`Get-Help` shows the help file `NTFSSecurity\en-US\NTFSSecurity.dll-Help.xml`,
which `New-ExternalHelp` generates from the pages and the build copies into
the module. Whenever you change a page in `Docs/Cmdlets`, generate the file
again and commit it together with the page:
```powershell
New-ExternalHelp -Path .\Docs\Cmdlets -OutputPath $env:TEMP\NTFSSecurityHelp
New-ExternalHelp -Path .\Docs\Cmdlets -OutputPath .\NTFSSecurity\en-US -Force
```
## Preview the website
@ -93,6 +96,19 @@ Before you open a pull request, check the following:
- No page in `Docs/Cmdlets` contains a `{{ ... }}` placeholder.
- `Update-MarkdownHelp` doesn't change any page in `Docs/Cmdlets`. The build
defined in `appveyor.yml` runs the same check.
- `New-ExternalHelp` doesn't change
`NTFSSecurity\en-US\NTFSSecurity.dll-Help.xml`. The build runs the same
check.
- The Pester tests in `Tests` pass. They test the module in
`NTFSSecurity\bin\Release`, for example that `Get-Help` shows every page.
The build runs them in Windows PowerShell 5.1 with Pester 5.7.1:
```powershell
Install-Module -Name Pester -RequiredVersion 5.7.1 -SkipPublisherCheck
Import-Module -Name Pester -RequiredVersion 5.7.1
Invoke-Pester -Path .\Tests -Output Detailed
```
- All links work. The build checks them with `Get-MarkdownLink` from the
MarkdownLinkCheck module:

7
Docs/Contributing/04-Markdown-Specifics.md

@ -18,8 +18,9 @@ the additional rules for the cmdlet reference pages, which platyPS processes.
## Cmdlet reference pages
platyPS converts the pages in `Docs/Cmdlets` to help files and updates them
from the module. Keep the structure that platyPS expects:
platyPS converts the pages in `Docs/Cmdlets` to the help file that `Get-Help`
shows and updates the pages from the module. Keep the structure that platyPS
expects:
- Keep the front matter. `external help file`, `Module Name`, and `schema`
must stay as they are. `online version` is the address of the page on
@ -32,6 +33,8 @@ from the module. Keep the structure that platyPS expects:
`Default value`, which platyPS keeps.
- Write each paragraph on a single line. platyPS carries line breaks into the
text that `Get-Help` shows.
- Put a link at the end of a sentence. In the text that `Get-Help` shows,
platyPS writes a link as `text (address)` and drops the space after it.
- Start each example with a level 3 heading such as
`### Example 1: Get the permissions of a folder`, followed by a code block
with the language `PowerShell` whose first line starts with `PS C:\>`.

2786
NTFSSecurity/Help/NTFSSecurity.Help.pshproj

File diff suppressed because it is too large

2333
NTFSSecurity/NTFSSecurity-Help.xml

File diff suppressed because it is too large

3
NTFSSecurity/NTFSSecurity.csproj

@ -118,7 +118,6 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Help\NTFSSecurity.Help.pshproj" />
<None Include="NTFSSecurity.format.ps1xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<SubType>Designer</SubType>
@ -144,7 +143,7 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Include="NTFSSecurity-Help.xml">
<Content Include="en-US\NTFSSecurity.dll-Help.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Resources\container.jpg" />

14
NTFSSecurity/NTFSSecurity.psd1

@ -80,7 +80,19 @@
'Get-DiskSpace',
'Get-FileHash2'
FileList = @('NTFSSecurity.dll', 'NTFSSecurity.types.ps1xml', 'NTFSSecurity.format.ps1xml', 'NTFSSecurity.Init.ps1', 'NTFSSecurity.psm1')
FileList = @(
'NTFSSecurity.psd1'
'NTFSSecurity.psm1'
'NTFSSecurity.Init.ps1'
'NTFSSecurity.dll'
'Security2.dll'
'PrivilegeControl.dll'
'ProcessPrivileges.dll'
'AlphaFS.dll'
'NTFSSecurity.types.ps1xml'
'NTFSSecurity.format.ps1xml'
'en-US\NTFSSecurity.dll-Help.xml'
)
PrivateData = @{
EnablePrivileges = $true

10020
NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml

File diff suppressed because it is too large

115
Tests/Help.Tests.ps1

@ -0,0 +1,115 @@
<#
Tests that Get-Help shows the help that is generated from Docs/Cmdlets for
every cmdlet of the module built in NTFSSecurity\bin\Release.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.'
)]
param ()
BeforeDiscovery {
$pagePath = Join-Path -Path $PSScriptRoot -ChildPath '..\Docs\Cmdlets'
$sectionPattern = '(?ms)^## (?<Heading>[A-Z ]+?)\s*$(?<Text>.*?)(?=^## |\z)'
$helpPages = foreach ($page in Get-ChildItem -Path $pagePath -Filter '*.md') {
$content = Get-Content -LiteralPath $page.FullName -Raw
$sections = @{}
foreach ($match in [regex]::Matches($content, $sectionPattern)) {
$sections[$match.Groups['Heading'].Value] = $match.Groups['Text'].Value
}
$parameterNames = [regex]::Matches("$($sections['PARAMETERS'])", '(?m)^### -(?<Name>\w+)') |
ForEach-Object -Process { $_.Groups['Name'].Value }
@{
CommandName = $page.BaseName
Synopsis = "$($sections['SYNOPSIS'])".Trim()
ExampleCount = [regex]::Matches("$($sections['EXAMPLES'])", '(?m)^### ').Count
ParameterNames = @($parameterNames)
OnlineUri = [regex]::Match($content, '(?m)^online version: (?<Uri>\S+)').Groups['Uri'].Value
}
}
}
Describe 'Help of the NTFSSecurity cmdlets' {
BeforeAll {
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\NTFSSecurity\bin\Release\NTFSSecurity.psd1'
$module = Import-Module -Name $modulePath -Force -PassThru -ErrorAction Stop
$pagePath = Join-Path -Path $PSScriptRoot -ChildPath '..\Docs\Cmdlets'
<#
With this test hook, Get-Help -Online returns the URI instead of opening a browser. In PowerShell 7,
the hook also makes Get-Help ignore the help file, so this test runs only in Windows PowerShell.
#>
$testHooks = [psobject].Assembly.GetType('System.Management.Automation.Internal.InternalTestHooks')
$bypassOnlineHelp = if ($testHooks -and $PSVersionTable.PSEdition -ne 'Core') {
$testHooks.GetField('BypassOnlineHelpRetrieval', [Reflection.BindingFlags] 'NonPublic, Static')
}
}
AfterAll {
Remove-Module -Name NTFSSecurity -Force -ErrorAction SilentlyContinue
}
It 'Should ship the help file in the en-US folder' {
Join-Path -Path $module.ModuleBase -ChildPath 'en-US\NTFSSecurity.dll-Help.xml' | Should -Exist
}
It 'Should have a page in Docs/Cmdlets for every exported cmdlet' {
$pageNames = (Get-ChildItem -Path $pagePath -Filter '*.md').BaseName | Sort-Object
$cmdletNames = (Get-Command -Module NTFSSecurity -CommandType Cmdlet).Name | Sort-Object
$cmdletNames -join ', ' | Should -BeExactly ($pageNames -join ', ')
}
Context '<CommandName>' -ForEach $helpPages {
BeforeAll {
$help = Get-Help -Name $CommandName -Full
}
It 'Should show the synopsis from Docs/Cmdlets' {
"$($help.Synopsis)".Trim() | Should -BeExactly $Synopsis
}
It 'Should describe the parameters from Docs/Cmdlets' {
$describedParameterNames = foreach ($parameter in $help.parameters.parameter) {
if (($parameter.description.Text -join '').Trim()) {
$parameter.name
}
}
($describedParameterNames | Sort-Object) -join ', ' |
Should -BeExactly (($ParameterNames | Sort-Object) -join ', ')
}
It 'Should show the <ExampleCount> examples from Docs/Cmdlets' {
@($help.examples.example | Where-Object -FilterScript { $_ }) | Should -HaveCount $ExampleCount
}
It 'Should link to the online version from Docs/Cmdlets' {
@($help.relatedLinks.navigationLink)[0].uri | Should -BeExactly $OnlineUri
}
It 'Should keep the space after each link in the help text' {
# platyPS writes an inline link as "text (url)" and drops the space that follows it.
$help | Out-String -Width 4096 | Should -Not -Match '\((?:\.\./|https?://)[^)\s]+\)\w'
}
It 'Should open the online version with Get-Help -Online' {
if (-not $bypassOnlineHelp) {
$reason = 'the test hook for Get-Help -Online reads the help file only in Windows PowerShell'
Set-ItResult -Skipped -Because $reason
return
}
$bypassOnlineHelp.SetValue($null, $true)
try {
$onlineHelp = Get-Help -Name $CommandName -Online
} finally {
$bypassOnlineHelp.SetValue($null, $false)
}
"$onlineHelp" | Should -Match ('{0}$' -f [regex]::Escape($OnlineUri))
}
}
}

42
appveyor.yml

@ -1,5 +1,6 @@
# Builds the NTFSSecurity module from source and checks that the cmdlet
# documentation in Docs/Cmdlets matches the cmdlets of that build.
# Builds the NTFSSecurity module from source, checks that the cmdlet
# documentation in Docs/Cmdlets and the help file generated from it match the
# cmdlets of that build, and runs the Pester tests against the build.
image: Visual Studio 2022
init:
@ -11,6 +12,8 @@ install:
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null
Install-Module -Name platyPS -RequiredVersion 0.14.2 -Force
Install-Module -Name MarkdownLinkCheck -RequiredVersion 0.2.0 -Force
# The image includes Pester 3.4.0, which is signed by a different publisher.
Install-Module -Name Pester -RequiredVersion 5.7.1 -Force -SkipPublisherCheck
before_build:
- nuget restore NTFSSecurity\packages.config -PackagesDirectory packages -NonInteractive
@ -46,3 +49,38 @@ test_script:
if ($brokenLinks) {
throw "Found broken hyperlinks $brokenLinks"
}
# 03. Test that the help file of the module matches the documentation
New-ExternalHelp -Path ./Docs/Cmdlets -OutputPath ./NTFSSecurity/en-US -Force | Out-Null
$helpChanges = git status --porcelain -- NTFSSecurity/en-US
if ($helpChanges) {
throw "The help file is not up-to-date, run New-ExternalHelp -Path .\Docs\Cmdlets -OutputPath .\NTFSSecurity\en-US -Force: $helpChanges"
}
# 04. Run the Pester tests against the module build
Import-Module -Name Pester -RequiredVersion 5.7.1
$configuration = New-PesterConfiguration
$configuration.Run.Path = '.\Tests'
$configuration.Run.PassThru = $true
$configuration.Output.Verbosity = 'Detailed'
$result = Invoke-Pester -Configuration $configuration
# Report every test once on the Tests tab. An uploaded NUnit file lists a
# Pester 5 test once for each block that contains it.
if ($env:APPVEYOR_API_URL -and $result.Tests) {
$tests = @(foreach ($test in $result.Tests) {
@{
testName = $test.ExpandedPath
testFramework = 'Pester'
fileName = Split-Path -Path $test.ScriptBlock.File -Leaf
outcome = if ($test.Result -eq 'NotRun') { 'NotRunnable' } else { "$($test.Result)" }
durationMilliseconds = [long] $test.Duration.TotalMilliseconds
ErrorMessage = @($test.ErrorRecord | ForEach-Object -Process { "$_" }) -join [Environment]::NewLine
}
})
$body = [Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject $tests -Compress))
Invoke-RestMethod -Method Post -Uri ($env:APPVEYOR_API_URL.TrimEnd('/') + '/api/tests/batch') -Body $body -ContentType 'application/json; charset=utf-8' | Out-Null
}
if ($result.FailedCount -gt 0) {
throw "$($result.FailedCount) Pester tests failed."
}

Loading…
Cancel
Save