diff --git a/.configurations/configuration.dsc.yaml b/.configurations/configuration.dsc.yaml new file mode 100644 index 00000000..51049b00 --- /dev/null +++ b/.configurations/configuration.dsc.yaml @@ -0,0 +1,102 @@ +# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2 + +properties: + resources: + ######################################################################## + # Section: Install Git + ######################################################################## + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: install-git + directives: + description: Install Git # Although the user probably already has git installed, it's possible that they don't + allowPrerelease: true + settings: + id: Git.Git + source: winget + Ensure: Present + - resource: Microsoft.WinGet.DSC/WinGetPackage + dependsOn: + - install-git + id: install-github-cli + directives: + description: Install GitHub CLI + allowPrerelease: true + securityContext: elevated + settings: + id: GitHub.cli + source: winget + Ensure: Present + ######################################################################## + # Section: Configure Git Remotes + ######################################################################## + - resource: GitDSC/GitRemote + id: add-microsoft-upstream + directives: + description: Add microsoft/winget-dsc as the upstream remote + allowPrerelease: true + settings: + ProjectDirectory: '${WinGetConfigRoot}\..' + RemoteName: upstream + RemoteUrl: https://github.com/microsoft/winget-pkgs.git + ######################################################################## + # Section: Install VS-Code + ######################################################################## + - resource: Microsoft.WinGet.DSC/WinGetPackage + id: install-vs-code + directives: + description: Install Microsoft Visual Studio Code + allowPrerelease: true + settings: + id: Microsoft.VisualStudioCode + source: winget + Ensure: Present + - resource: Microsoft.VSCode.Dsc/VSCodeExtension + id: install_vscode-yaml + dependsOn: + - install-vs-code + directives: + description: Install YAML extension for VSCode + allowPrerelease: true + settings: + Name: redhat.vscode-yaml + Exist: true + - resource: Microsoft.VSCode.Dsc/VSCodeExtension + id: install_editorconfig.editorconfig + dependsOn: + - install-vs-code + directives: + description: Install EditorConfig extension for VSCode + allowPrerelease: true + settings: + Name: EditorConfig.EditorConfig + Exist: true + - resource: Microsoft.VSCode.Dsc/VSCodeExtension + id: install_ms-vscode.powershell + dependsOn: + - install-vs-code + directives: + description: Install PowerShell extension for VSCode + allowPrerelease: true + settings: + Name: ms-vscode.powershell + Exist: true + ######################################################################## + # Section: Install PowerShell Modules + ######################################################################## + - resource: PowerShellModule/PSModuleResource + id: install-pester + directives: + description: Install Pester module + allowPrerelease: true + settings: + Module_Name: Pester + Ensure: Present + - resource: PowerShellModule/PSModuleResource + id: install-psscriptanalyzer + directives: + description: Install PSScriptAnalyzer module + allowPrerelease: true + settings: + Module_Name: PSScriptAnalyzer + Ensure: Present + configurationVersion: 0.2.0 diff --git a/.github/actions/spelling/advice.md b/.github/actions/spelling/advice.md index 1004eeaa..483b4e16 100644 --- a/.github/actions/spelling/advice.md +++ b/.github/actions/spelling/advice.md @@ -2,6 +2,7 @@
If the flagged items are :exploding_head: false positives If items relate to a ... + * binary file (or some other file you wouldn't want to check at all). Please add a file path to the `excludes.txt` file matching the containing file. diff --git a/.github/actions/spelling/candidate.patterns b/.github/actions/spelling/candidate.patterns index 6671c8b7..f8d98951 100644 --- a/.github/actions/spelling/candidate.patterns +++ b/.github/actions/spelling/candidate.patterns @@ -509,12 +509,7 @@ customObjectInstantitationMethod # version suffix v# (?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_])) -# Compiler flags (Scala) -(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) -# Compiler flags -(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,}) -# Compiler flags (linker) -,-B + # curl arguments \b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)* # set arguments diff --git a/.github/actions/spelling/expect/generic_terms.txt b/.github/actions/spelling/expect/generic_terms.txt index 5a88fd33..5180255f 100644 --- a/.github/actions/spelling/expect/generic_terms.txt +++ b/.github/actions/spelling/expect/generic_terms.txt @@ -1,14 +1,21 @@ -wildcards -ssh +AKV Amd -usr -screenshots +Authenticode +automerge currentstate +esrp +gtm +msft +NPH +Peet +rfc +screenshots Scrollbars Searchbox -VGpu -versioning -worktree +SFP +Signtool sortby -msft -automerge +ssh +usr +versioning +VGpu \ No newline at end of file diff --git a/.github/actions/spelling/expect/software.txt b/.github/actions/spelling/expect/software.txt index 8ed24f0c..eb235278 100644 --- a/.github/actions/spelling/expect/software.txt +++ b/.github/actions/spelling/expect/software.txt @@ -2,7 +2,6 @@ vscode Linux dotnet dotnettool -cspell NUnit reportgenerator -Toolpackage +markdownlint \ No newline at end of file diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt index 95f329bf..b8011434 100644 --- a/.github/actions/spelling/patterns.txt +++ b/.github/actions/spelling/patterns.txt @@ -68,3 +68,6 @@ name\:\s+.+$ # Commit Hashes [0-9a-f]{40} + +# Github User Content +/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]* diff --git a/.github/workflows/markdownLint.yaml b/.github/workflows/markdownLint.yaml new file mode 100644 index 00000000..2862dfbc --- /dev/null +++ b/.github/workflows/markdownLint.yaml @@ -0,0 +1,35 @@ +name: Markdown Lint + +on: + push: + branches: + - '**' + - '!main' + tags-ignore: + - '**' + pull_request_target: + branches: + - '**' + tags-ignore: + - '**' + types: + - 'opened' + - 'reopened' + - 'synchronize' + +jobs: + lint: + name: Check Markdown linting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: tj-actions/changed-files@v45 + id: changed-files + with: + files: '**/*.md' + - uses: DavidAnson/markdownlint-cli2-action@v17 + if: steps.changed-files.outputs.any_changed == 'true' + with: + globs: ${{ steps.changed-files.outputs.all_changed_files }} diff --git a/.github/workflows/spellCheck.yaml b/.github/workflows/spellCheck.yaml index 5893eb94..2adc3093 100644 --- a/.github/workflows/spellCheck.yaml +++ b/.github/workflows/spellCheck.yaml @@ -77,11 +77,11 @@ jobs: use_magic_file: 1 extra_dictionary_limit: 10 extra_dictionaries: - https://github.com/streetsidesoftware/cspell-dicts/raw/098e323325a389a5d1cebcd7770807b9d11d0a17/dictionaries/software-terms/src/software-terms.txt + https://raw.githubusercontent.com/streetsidesoftware/cspell-dicts/098e323325a389a5d1cebcd7770807b9d11d0a17/dictionaries/software-terms/src/software-terms.txt https://raw.githubusercontent.com/streetsidesoftware/cspell-dicts/098e323325a389a5d1cebcd7770807b9d11d0a17/dictionaries/filetypes/src/filetypes.txt https://raw.githubusercontent.com/streetsidesoftware/cspell-dicts/098e323325a389a5d1cebcd7770807b9d11d0a17/dictionaries/powershell/src/powershell.txt https://raw.githubusercontent.com/streetsidesoftware/cspell-dicts/098e323325a389a5d1cebcd7770807b9d11d0a17/dictionaries/win32/src/generator/win32.txt - https://github.com/streetsidesoftware/cspell-dicts/raw/098e323325a389a5d1cebcd7770807b9d11d0a17/dictionaries/python/src/common_packages.txt + https://raw.githubusercontent.com/streetsidesoftware/cspell-dicts/098e323325a389a5d1cebcd7770807b9d11d0a17/dictionaries/python/src/common_packages.txt check_extra_dictionaries: '' comment-push: diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 00000000..76170d4a --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,11 @@ +globs: + - "**/*.md" + +noProgress: true + +# Show found files on stdout (only valid at root) +showFound: true + +ignores: + - "PRIVACY.md" + - ".github/*.md" diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 00000000..2d37bf1e --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,35 @@ +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +MD007: + # Spaces for indent + indent: 4 + # Whether to indent the first level of the list + start_indented: false + # Spaces for first level indent (when start_indented is set) + start_indent: 2 + +# MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md013.md +MD013: + # Number of characters + line_length: 500 + # Number of characters for headings + heading_line_length: 120 + # Number of characters for code blocks + code_block_line_length: 500 + # Include code blocks + code_blocks: true + # Include tables + tables: true + # Include headings + headings: true + # Strict length checking + strict: false + # Stern length checking + stern: false + +# MD025/single-title : Help examples contain metadata +MD025: false diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 33d1ff21..2e40c1f6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,8 @@ { "recommendations": [ "redhat.vscode-yaml", - "EditorConfig.EditorConfig" + "EditorConfig.EditorConfig", + "ms-vscode.powershell", + "davidanson.vscode-markdownlint" ] } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a33cac4..065da743 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,9 @@ The Windows Package Manager team is active in this GitHub Repository. In fact, w When the team finds issues we file them in the repository. When we propose new ideas or think-up new features, we file new feature requests. When we work on fixes or features, we create branches and work on those improvements. And when PRs are reviewed, we review in public - including all the good, the bad, and the ugly parts. -The point of doing all this work in public is to ensure that we are holding ourselves to a high degree of transparency, and so that the community sees that we apply the same processes and hold ourselves to the same quality-bar as we do to community-submitted issues and PRs. We also want to make sure that we expose our team culture and "tribal knowledge" that is inherent in any closely-knit team, which often contains considerable value to those new to the project who are trying to figure out "why the heck does this thing look/work like this???" +The point of doing all this work in public is to ensure that we are holding ourselves to a high degree of transparency, and so that the community sees that we apply the same processes and hold ourselves to the same quality-bar as we do to community-submitted issues and PRs. + +We also want to make sure that we expose our team culture and "tribal knowledge" that is inherent in any closely-knit team, which often contains considerable value to those new to the project who are trying to figure out "why the heck does this thing look/work like this???" ### Repository Bot @@ -21,6 +23,7 @@ We drive the bot by tagging issues with specific labels which cause the bot engi Therefore, if you do file issues, or create PRs, please keep an eye on your GitHub notifications. If you do not respond to requests for information, your issues/PRs may be closed automatically. --- + ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC). See [SECURITY.md](./SECURITY.md) for more information. @@ -109,14 +112,30 @@ Once the team have approved an issue/spec, development can proceed. If no develo ### Fork, Clone, Branch and Create your PR -Once you've discussed your proposed feature/fix/etc. with a team member, and you've agreed an approach or a spec has been written and approved, it's time to start development: +Once you've discussed your proposed feature/fix/etc. with a team member, and you've agreed an approach or a spec has been written and approved, it's time to start development. There are two flows you can follow depending on the proposed feature or fix. + +If you're feature (or module) has not yet been created, follow these steps: + +1. Fork the repository if you haven't already. +2. Clone your fork locally. +3. Open a PowerShell terminal session and execute: `.\utilities\tools\New-DscResourceModule.ps1 -DscResourceModule '' -Description 'DSC Resource for '` +4. Work on your changes and write tests. +5. Build and test to see if it works. +6. Create & push a feature branch. +7. Create a [Draft Pull Request (PR)](https://github.blog/2019-02-14-introducing-draft-pull-requests/). +8. If you are finished with your changes and you want a review, change the state. + +> [!TIP] +> Don't forget to add the `DscResourcesToExport` and `Tags`. + +When you are working on a fix or you want to add additional features to an existing module, you can follow the below steps: 1. Fork the repository if you haven't already. -1. Clone your fork locally. -1. Create & push a feature branch. -1. Create a [Draft Pull Request (PR)](https://github.blog/2019-02-14-introducing-draft-pull-requests/). -1. Work on your changes. -1. Build and see if it works. +2. Clone your fork locally. +3. Work on your fix or feature, and _optionally_ write tests +4. Build and test to see if it works. +5. Create & push a feature branch. +6. Create a [Pull Request (PR)](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) when you are finished with your changes ### Testing @@ -126,7 +145,8 @@ Testing is a key component in the development workflow. When you'd like the team to take a look, (even if the work is not yet fully-complete), mark the Draft PR as 'Ready For Review' so that the team can review your work and provide comments, suggestions, and request changes. It may take several cycles, but the end result will be solid, testable, conformant code that is safe for us to merge. -> ⚠ Remember: **changes you make may affect both the Windows Package Manager and the schema support implemented in our validation pipelines!** Because of this, we will treat community PR's with the same level of scrutiny and rigor as commits submitted to the official Windows source by team members and partners. +> [!CAUTION] +> Remember: **changes you make may affect both the Windows Package Manager and the schema support implemented in our validation pipelines!** Because of this, we will treat community PR's with the same level of scrutiny and rigor as commits submitted to the official Windows source by team members and partners. ### Merge diff --git a/SECURITY.md b/SECURITY.md index 82db58aa..d49d89e4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,6 +1,6 @@ -## Security +# Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). @@ -18,13 +18,13 @@ You should receive a response within 24 hours. If for some reason you do not, pl Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue +* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) +* Full paths of source file(s) related to the manifestation of the issue +* The location of the affected source code (tag/branch/commit or direct URL) +* Any special configuration required to reproduce the issue +* Step-by-step instructions to reproduce the issue +* Proof-of-concept or exploit code (if possible) +* Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. diff --git a/doc/index.md b/doc/index.md index 1aa1c739..9066091f 100644 --- a/doc/index.md +++ b/doc/index.md @@ -7,7 +7,7 @@ ms.topic: overview WinGet Desired State Configuration (DSC) consist out of PowerShell class-based DSC resources targeting PowerShell 7.2+. Each module exposes classes that help you configure your machine in the desired state. The DSC resources is developed around the `Get-Test-Set` methods. If applicable, the team attempts to implement new methods known to newer versions of DSC. -> To learn more about the newer DSC version, check out https://learn.microsoft.com/en-us/powershell/dsc/overview?view=dsc-3.0 +> To learn more about the newer DSC version, check out ## Getting started diff --git a/pipelines/azure-pipelines.publish.yml b/pipelines/azure-pipelines.publish.yml new file mode 100644 index 00000000..0066d882 --- /dev/null +++ b/pipelines/azure-pipelines.publish.yml @@ -0,0 +1,153 @@ +# winget-dsc pipeline to publish module artifacts +name: '$(Build.DefinitionName)-$(Build.DefinitionVersion)-$(Date:yyyyMMdd)-$(Rev:r)' + +trigger: none + +parameters: # parameters are shown up in ADO UI in a build queue time + +- name: moduleName + displayName: 'Name of the module to publish to the PSGallery' + type: string + +- name: moduleVersion + displayName: 'Version of the module' + type: string + +resources: + repositories: + - repository: self + type: git + ref: refs/heads/main + - repository: 1ESPipelineTemplates + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates + parameters: + pool: + name: Azure-Pipelines-1ESPT-ExDShared + image: windows-2022 + os: windows + customBuildTags: + - ES365AIMigrationTooling + settings: + skipBuildTagsForGitHubPullRequests: true + + stages: + - stage: Prepare + jobs: + - job: Prepare_Sign + displayName: Prepare and sign ${{ parameters.moduleName }} + steps: + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet 6.x' + inputs: + versionSpec: 6.x + - task: PowerShell@2 + displayName: Replace module version + inputs: + targetType: inline + pwsh: true + script: | + $manifestContent = (Get-Content -path $(Build.SourcesDirectory)\resources\${{ parameters.moduleName }}\${{ parameters.moduleName }}.psd1 -Raw) + + $newManifestContent = $manifestContent -replace "'0.1.0'", "'${{ parameters.moduleVersion }}'" + + Set-Content -path $(Build.SourcesDirectory)\resources\${{ parameters.moduleName }}\${{ parameters.moduleName }}.psd1 -Value $newManifestContent + + New-Item ToSign -Type Directory + Set-Content -path ToSign\${{ parameters.moduleName }}.psd1 -Value $newManifestContent + Get-Content ToSign\${{ parameters.moduleName }}.psd1 -Raw + + Copy-Item -Path "$(Build.SourcesDirectory)\resources\${{ parameters.moduleName }}\${{ parameters.moduleName }}.psm1" -Destination "ToSign\${{ parameters.moduleName }}.psm1" -Force + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@5 + displayName: 'Sign manifest' + inputs: + ConnectedServiceName: AppInstallerESRPCodeSigning + AppRegistrationClientId: '32216f16-efc9-4013-9fae-c6a2c54a3fc0' + AppRegistrationTenantId: '72f988bf-86f1-41af-91ab-2d7cd011db47' + AuthAKVName: PeetDevOpsKeyVault + AuthCertName: ESRPAuth + AuthSignCertName: ESRPRequestSigning + FolderPath: '$(System.DefaultWorkingDirectory)\ToSign\' + Pattern: '*' + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "KeyCode" : "CP-230012", + "OperationCode" : "SigntoolSign", + "Parameters" : { + "OpusName" : "Microsoft", + "OpusInfo" : "http://www.microsoft.com", + "FileDigest" : "/fd \"SHA256\"", + "PageHash" : "/NPH", + "TimeStamp" : "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-230012", + "OperationCode" : "SigntoolVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + - task: PowerShell@2 + displayName: Copy Signed Files and Validate signature + inputs: + targetType: inline + pwsh: true + script: | + New-Item ToPublish\${{ parameters.moduleName }} -Type Directory + $moduleFolder = "ToPublish\${{ parameters.moduleName }}" + Copy-Item -Path "ToSign\${{ parameters.moduleName }}.psm1" -Destination "ToPublish\${{ parameters.moduleName }}\${{ parameters.moduleName }}.psm1" -Force + Copy-Item -Path "ToSign\${{ parameters.moduleName }}.psd1" -Destination "ToPublish\${{ parameters.moduleName }}\${{ parameters.moduleName }}.psd1" -Force + + $notValid = Get-ChildItem $moduleFolder -Recurse -Attributes !Directory | Get-AuthenticodeSignature | where { $_.Status -ne 'Valid' } + if ($null -ne $notValid) + { + $notValid + throw "A file is not signed" + } + - task: CopyFiles@2 + displayName: Copy files to be published to staging directory + inputs: + SourceFolder: ToPublish\${{ parameters.moduleName }} + targetFolder: $(Build.ArtifactStagingDirectory)/${{ parameters.moduleName }} + flattenFolders: true + contents: | + *.psm1 + *.psd1 + - task: 1ES.PublishPipelineArtifact@1 + inputs: + targetPath: $(Build.ArtifactStagingDirectory)/${{ parameters.moduleName }} + artifactName: ${{ parameters.moduleName }} + displayName: Publish Module Artifact + + - stage: Publish + displayName: Manual Approval + trigger: manual + jobs: + - job: PublishToGallery + steps: + - task: DownloadPipelineArtifact@2 + inputs: + buildType: current + artifactName: ${{ parameters.moduleName }} + targetPath: $(System.DefaultWorkingDirectory)/ModuleToPublish/${{ parameters.moduleName }} + itemPattern: | + *.psm1 + *.psd1 + - pwsh: | + $moduleFolder = "$(System.DefaultWorkingDirectory)/ModuleToPublish/${{ parameters.moduleName }}" + Get-ChildItem -Path $moduleFolder -Recurse + $moduleFolderPath = (Resolve-Path $moduleFolder).Path + Publish-Module -Path $moduleFolderPath -Repository PSGallery -NuGetApiKey $env:api_key -verbose + displayName: Publish ${{ parameters.moduleName }}' + env: + api_key: $(DscSamplesNugetApiKey) + \ No newline at end of file diff --git a/resources/Help/Microsoft.Dotnet.Dsc/DotNetToolPackage.md b/resources/Help/Microsoft.Dotnet.Dsc/DotNetToolPackage.md index 65a39961..a84bcc70 100644 --- a/resources/Help/Microsoft.Dotnet.Dsc/DotNetToolPackage.md +++ b/resources/Help/Microsoft.Dotnet.Dsc/DotNetToolPackage.md @@ -19,14 +19,14 @@ The `DotNetToolPackage` DSC Resource allows you to install, update, and uninstal ## PARAMETERS -**Parameter**|**Attribute**|**DataType**|**Description**|**Allowed Values** -:-----|:-----|:-----|:-----|:----- -`PackageId`|Key|String|The ID of the .NET tool package to manage.|N/A -`Version`|Optional|String|The version of the .NET tool package to install. If not specified, the latest version will be installed.|N/A -`Commands`|Optional|String[]|An array of commands provided by the .NET tool package.|N/A -`Prerelease`|Optional|Boolean|Indicates whether to include prerelease versions of the .NET tool package. The default value is `$false`.|`$true`, `$false` -`ToolPathDirectory`|Optional|String|The directory where the .NET tool package will be installed. If not specified, the package will be installed globally.| Use custom directory when you have `env:Path` set on that directory -`Exist`|Optional|Boolean|Indicates whether the package should exist. Defaults to `$true`.| `$true` or `$false` +| **Parameter** | **Attribute** | **DataType** | **Description** | **Allowed Values** | +| ------------------- | ------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | +| `PackageId` | Key | String | The ID of the .NET tool package to manage. | N/A | +| `Version` | Optional | String | The version of the .NET tool package to install. If not specified, the latest version will be installed. | N/A | +| `Commands` | Optional | String[] | An array of commands provided by the .NET tool package. | N/A | +| `Prerelease` | Optional | Boolean | Indicates whether to include prerelease versions of the .NET tool package. The default value is `$false`. | `$true`, `$false` | +| `ToolPathDirectory` | Optional | String | The directory where the .NET tool package will be installed. If not specified, the package will be installed globally. | Use custom directory when you have | +| `Exist` | Optional | Boolean | Indicates whether the package should exist. Defaults to `$true`. | `$true` or `$false` | ## EXAMPLES diff --git a/resources/Help/Microsoft.VSCode.Dsc/VSCodeExtension.md b/resources/Help/Microsoft.VSCode.Dsc/VSCodeExtension.md index 7d98592b..4f5e62b1 100644 --- a/resources/Help/Microsoft.VSCode.Dsc/VSCodeExtension.md +++ b/resources/Help/Microsoft.VSCode.Dsc/VSCodeExtension.md @@ -19,12 +19,12 @@ The `VSCodeExtension` DSC Resource allows you to install, update, and remove Vis ## PARAMETERS -**Parameter**|**Attribute**|**DataType**|**Description**|**Allowed Values** -:-----|:-----|:-----|:-----|:----- -`Name`|Key|String|The name of the Visual Studio Code extension to manage.|To find extensions in VSCode, check out: https://code.visualstudio.com/docs/editor/extension-marketplace#_find-and-install-an-extension -`Version`|Optional|String|The version of the Visual Studio Code extension to install. If not specified, the latest version will be installed.| For example: `1.0.0` -`Exist`|Optional|Boolean|Indicates whether the extension should exist. The default value is `$true`.|`$true`, `$false` -`Insiders`|Optional|Boolean|Indicates whether to manage the extension for the Insiders version of Visual Studio Code. The default value is `$false`.|`$true`, `$false` +| **Parameter** | **Attribute** | **DataType** | **Description** | **Allowed Values** | +| ------------- | ------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `Name` | Key | String | The name of the Visual Studio Code extension to manage. | To find extensions in VSCode, check out: | +| `Version` | Optional | String | The version of the Visual Studio Code extension to install. If not specified, the latest version will be installed. | For example: `1.0.0` | +| `Exist` | Optional | Boolean | Indicates whether the extension should exist. The default value is `$true`. | `$true`, `$false` | +| `Insiders` | Optional | Boolean | Indicates whether to manage the extension for the Insiders version of Visual Studio Code. The default value is `$false`. | `$true`, `$false` | ## EXAMPLES diff --git a/resources/Help/PythonPip3Dsc/Pip3Package.md b/resources/Help/PythonPip3Dsc/Pip3Package.md index 6b6af440..10b567d9 100644 --- a/resources/Help/PythonPip3Dsc/Pip3Package.md +++ b/resources/Help/PythonPip3Dsc/Pip3Package.md @@ -19,13 +19,13 @@ The `Pip3Package` DSC Resource allows you to install, update, and uninstall Pyth ## PARAMETERS -**Parameter**|**Attribute**|**DataType**|**Description**|**Allowed Values** -:-----|:-----|:-----|:-----|:----- -`SID`|Key, Mandatory|String|The security identifier. This is a key property and should not be set manually.| -`Exist`|Optional|Boolean|Indicates whether the package should exist. Defaults to `$true`.| `$true` or `$false` -`Package`|Mandatory|String|The name of the Python package to manage. This is a mandatory property.| For a list of Python packages, see https://pypi.org/. -`Version`|Optional|String|The version of the Python package to manage. If not specified, the latest version will be used.| For example: `5.1.2` -`Arguments`|Optional|String|Additional arguments to pass to pip3.| Add arguments like `--debug` +| **Parameter** | **Attribute** | **DataType** | **Description** | **Allowed Values** | +| ------------- | -------------- | ------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------- | +| `SID` | Key, Mandatory | String | The security identifier. This is a key property and should not be set manually. | pack have been installed. | +| `Exist` | Optional | Boolean | Indicates whether the package should exist. Defaults to `$true`. | `$true` or `$false` | +| `Package` | Mandatory | String | The name of the Python package to manage. This is a mandatory property. | For a list of Python packages, see | +| `Version` | Optional | String | The version of the Python package to manage. If not specified, the latest version will be used. | For example: `5.1.2` | +| `Arguments` | Optional | String | Additional arguments to pass to pip3. | Add arguments like `--debug` | ## EXAMPLES diff --git a/resources/Microsoft.VSCode.Dsc/Microsoft.VSCode.Dsc.psm1 b/resources/Microsoft.VSCode.Dsc/Microsoft.VSCode.Dsc.psm1 index ea43f49d..77a52b93 100644 --- a/resources/Microsoft.VSCode.Dsc/Microsoft.VSCode.Dsc.psm1 +++ b/resources/Microsoft.VSCode.Dsc/Microsoft.VSCode.Dsc.psm1 @@ -95,11 +95,32 @@ function Invoke-VSCode { [string]$Command ) - try { - Invoke-Expression "& `"$VSCodeCLIPath`" $Command" - } catch { - throw ("Executing {0} with {$Command} failed." -f $VSCodeCLIPath) + $stdErrTempFile = "$env:TEMP\$((New-Guid).Guid)" + $stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)" + $invocationSuccess = $true + + $processParams = @{ + FilePath = $VSCodeCLIPath + ArgumentList = "$Command" + RedirectStandardError = $stdErrTempFile + RedirectStandardOutput = $stdOutTempFile + Wait = $true + PassThru = $true + NoNewWindow = $true } + + $invocation = Start-Process @processParams + $invocationErrors = Get-Content $stdErrTempFile -Raw -ErrorAction SilentlyContinue + $invocationErrors = $invocationErrors -Replace '\n', '\n ' + $invocationOutput = Get-Content $stdOutTempFile -ErrorAction SilentlyContinue + Remove-Item -Path $stdErrTempFile -ErrorAction Ignore + Remove-Item -Path $stdOutTempFile -ErrorAction Ignore + + if (![string]::IsNullOrWhiteSpace($invocationErrors)) { $invocationSuccess = $false } + if ($invocation.ExitCode) { $invocationSuccess = $false } + if (!$invocationSuccess) { throw [System.Configuration.ConfigurationException]::new("Executing '$VSCodeCLIPath $Command' failed. Command Output: '$invocationErrors'") } + + return $invocationOutput } function TryGetRegistryValue { diff --git a/resources/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.psd1 b/resources/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.psd1 index b7c18fa1..e11cea5e 100644 --- a/resources/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.psd1 +++ b/resources/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.psd1 @@ -18,7 +18,8 @@ 'TextCursor', 'StickyKeys', 'ToggleKeys', - 'FilterKeys' + 'FilterKeys', + 'EyeControl' ) PrivateData = @{ PSData = @{ @@ -32,7 +33,8 @@ 'PSDscResource_TextCursor', 'PSDscResource_StickyKeys', 'PSDscResource_ToggleKeys', - 'PSDscResource_FilterKeys' + 'PSDscResource_FilterKeys', + 'PSDscResource_EyeControl' ) # Prerelease string of this module diff --git a/resources/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.psm1 b/resources/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.psm1 index 1e98a833..e6070e27 100644 --- a/resources/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.psm1 +++ b/resources/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.psm1 @@ -4,6 +4,11 @@ $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest +enum Ensure { + Absent + Present +} + enum TextSize { KeepCurrentValue Small @@ -78,8 +83,9 @@ if ([string]::IsNullOrEmpty($env:TestRegistryPath)) { $global:StickyKeysRegistryPath = 'HKCU:\Control Panel\Accessibility\StickyKeys' $global:ToggleKeysRegistryPath = 'HKCU:\Control Panel\Accessibility\ToggleKeys' $global:FilterKeysRegistryPath = 'HKCU:\Control Panel\Accessibility\Keyboard Response' + $global:EyeControlRegistryPath = 'HKCU:\Software\Microsoft\input\EC\' } else { - $global:AccessibilityRegistryPath = $global:MagnifierRegistryPath = $global:PointerRegistryPath = $global:ControlPanelAccessibilityRegistryPath = $global:AudioRegistryPath = $global:PersonalizationRegistryPath = $global:NTAccessibilityRegistryPath = $global:CursorIndicatorAccessibilityRegistryPath = $global:ControlPanelDesktopRegistryPath = $global:StickyKeysRegistryPath = $global:ToggleKeysRegistryPath = $global:FilterKeysRegistryPath = $env:TestRegistryPath + $global:AccessibilityRegistryPath = $global:MagnifierRegistryPath = $global:PointerRegistryPath = $global:ControlPanelAccessibilityRegistryPath = $global:AudioRegistryPath = $global:PersonalizationRegistryPath = $global:NTAccessibilityRegistryPath = $global:CursorIndicatorAccessibilityRegistryPath = $global:ControlPanelDesktopRegistryPath = $global:StickyKeysRegistryPath = $global:ToggleKeysRegistryPath = $global:FilterKeysRegistryPath = $global:EyeControlRegistryPath = $env:TestRegistryPath } [DSCResource()] @@ -891,6 +897,37 @@ class FilterKeys { } } +[DscResource()] +class EyeControl { + [DscProperty(Key)] [Ensure] $Ensure + hidden [string] $SettingsProperty = 'Enabled' + + [EyeControl] Get() { + $currentState = [EyeControl]::new() + + if (-not(DoesRegistryKeyPropertyExist -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty)) { + $currentState.Ensure = [Ensure]::Absent + } else { + $currentState.Ensure = [int]((Get-ItemPropertyValue -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty) -eq 1) + } + + return $currentState + } + + [bool] Test() { + return $this.Get().Ensure -eq $this.Ensure + } + + [void] Set() { + # Only make changes if changes are needed + if ($this.Test()) { return } + if (-not (DoesRegistryKeyPropertyExist -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty)) { + New-ItemProperty -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty -PropertyType DWord + } + Set-ItemProperty -Path $global:EyeControlRegistryPath -Name $this.SettingsProperty -Value $([int]$this.Ensure) + } +} + #region Functions function DoesRegistryKeyPropertyExist { param ( diff --git a/tests/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.Tests.ps1 b/tests/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.Tests.ps1 index dd22de6d..85e78709 100644 --- a/tests/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.Tests.ps1 +++ b/tests/Microsoft.Windows.Setting.Accessibility/Microsoft.Windows.Setting.Accessibility.Tests.ps1 @@ -26,9 +26,9 @@ BeforeAll { Describe 'List available DSC resources' { It 'Shows DSC Resources' { - $expectedDSCResources = 'Text', 'Magnifier', 'MousePointer', 'VisualEffect', 'Audio', 'TextCursor', 'StickyKeys', 'ToggleKeys', 'FilterKeys' + $expectedDSCResources = 'Text', 'Magnifier', 'MousePointer', 'VisualEffect', 'Audio', 'TextCursor', 'StickyKeys', 'ToggleKeys', 'FilterKeys', 'EyeControl' $availableDSCResources = (Get-DscResource -Module Microsoft.Windows.Setting.Accessibility).Name - $availableDSCResources.length | Should -Be 9 + $availableDSCResources.length | Should -Be 10 $availableDSCResources | Where-Object { $expectedDSCResources -notcontains $_ } | Should -BeNullOrEmpty -ErrorAction Stop } } @@ -401,6 +401,28 @@ Describe 'FilterKeys' { } } +Describe 'EyeControl' { + It 'Enable EyeControl.' { + Invoke-DscResource -Name EyeControl -ModuleName Microsoft.Windows.Setting.Accessibility -Method Set -Property @{ Ensure = 'Absent' } + + $initialState = Invoke-DscResource -Name EyeControl -ModuleName Microsoft.Windows.Setting.Accessibility -Method Get -Property @{} + $initialState.Ensure | Should -Be 'Absent' + + # Test enabled + $parameters = @{ Ensure = 'Present' } + $testResult = Invoke-DscResource -Name EyeControl -ModuleName Microsoft.Windows.Setting.Accessibility -Method Test -Property $parameters + $testResult.InDesiredState | Should -Be $false + + # Set enabled + Invoke-DscResource -Name EyeControl -ModuleName Microsoft.Windows.Setting.Accessibility -Method Set -Property $parameters + $finalState = Invoke-DscResource -Name EyeControl -ModuleName Microsoft.Windows.Setting.Accessibility -Method Get -Property @{} + $finalState.Ensure | Should -Be 'Present' + + $testResult2 = Invoke-DscResource -Name EyeControl -ModuleName Microsoft.Windows.Setting.Accessibility -Method Test -Property $parameters + $testResult2.InDesiredState | Should -Be $true + } +} + AfterAll { $env:TestRegistryPath = '' } diff --git a/tests/PythonPip3Dsc/PythonPip3Dsc.Tests.ps1 b/tests/PythonPip3Dsc/PythonPip3Dsc.Tests.ps1 index 425340a4..607bd8a6 100644 --- a/tests/PythonPip3Dsc/PythonPip3Dsc.Tests.ps1 +++ b/tests/PythonPip3Dsc/PythonPip3Dsc.Tests.ps1 @@ -120,7 +120,6 @@ Describe 'Pip3Package' { # Call whatif to see if it "will" install $whatIf = $pipPackage.WhatIf() | ConvertFrom-Json - $whatIf.PackageName | Should -Be 'itsdangerous' $whatIf._metaData.whatIf | Should -Contain "Would install itsdangerous-$($whatIfState.Version)" } @@ -133,7 +132,6 @@ Describe 'Pip3Package' { $pipPackage = [Pip3Package]$whatIfState $whatIf = $pipPackage.WhatIf() | ConvertFrom-Json - $whatIf.PackageName | Should -Be 'invalidPackageName' $whatIf._metaData.whatIf | Should -Contain "ERROR: No matching distribution found for $($whatIfState.PackageName)" } diff --git a/utilities/tools/New-DscResourceModule.ps1 b/utilities/tools/New-DscResourceModule.ps1 new file mode 100644 index 00000000..5d2e9222 --- /dev/null +++ b/utilities/tools/New-DscResourceModule.ps1 @@ -0,0 +1,98 @@ +<# +.SYNOPSIS + Creates a new DSC (Desired State Configuration) resource module structure. + +.DESCRIPTION + The function New-DscResourceModule function creates a new DSC resource module structure with the specified name and description. + It sets up the necessary directory structure for resources and tests within the given base path. + +.PARAMETER DscResourceModule + The name of the DSC resource module to create. + +.PARAMETER Description + A description of the DSC resource module. + +.PARAMETER BasePath + The base path where the DSC resource module structure will be created. The default value is the parent directory of the script. + +.EXAMPLE + PS C:\> New-DscResourceModule -DscResourceModule 'Microsoft.Windows.Language' -Description 'DSC Resource for Windows Language' + + This command creates a new DSC resource module named 'Microsoft.Windows.Language' with the specified description in the default base path. +#> +#Requires -Version 7 +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPositionalParameters', '', Justification = 'Positional parameters are used for simplicity. Targeting PS 7+')] +param ( + [Parameter(Mandatory)] + [string]$DscResourceModule, + + [Parameter(Mandatory)] + [string]$Description, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string[]] $DscResourceToExport +) + +$basePath = "$PSScriptRoot\..\.." + +if (Test-Path $basePath) { + $resourcePath = Join-Path $basePath 'resources' $DscResourceModule + $testsPath = Join-Path $basePath 'tests' $DscResourceModule +} else { + $basePath = $resourcePath = $testsPath = (Resolve-Path '.\').Path +} + +# Create directories if they do not exist +if (-not (Test-Path -Path $resourcePath)) { + Write-Verbose -Message "Creating directory: $resourcePath" + New-Item -ItemType Directory -Path $resourcePath -Force | Out-Null +} + +if (-not (Test-Path -Path $testsPath)) { + Write-Verbose -Message "Creating test directory: $testsPath" + New-Item -ItemType Directory -Path $testsPath -Force | Out-Null +} + +$moduleManifestPath = (Join-Path $basePath 'resources' $DscResourceModule "$DscResourceModule.psd1") + +$moduleManifestParams = @{ + Path = $moduleManifestPath + RootModule = "$DscResourceModule.psm1" + ModuleVersion = '0.1.0' + Author = 'Microsoft Corporation' + CompanyName = 'Microsoft Corporation' + Copyright = '(c) Microsoft Corporation. All rights reserved.' + Description = $Description + PowerShellVersion = '7.2' + FunctionsToExport = @() + CmdletsToExport = @() + VariablesToExport = @() + AliasesToExport = @() + LicenseUri = 'https://github.com/microsoft/winget-dsc/blob/main/LICENSE' + ProjectUri = 'https://github.com/microsoft/winget-dsc' + Prerelease = 'alpha' +} + +if ($DscResourceToExport) { + # New module manifest does not properly handle arrays of strings :( + $moduleManifestParams.Add('DscResourcesToExport', @($DscResourceToExport)) +} + +if (-not (Test-Path $moduleManifestPath)) { + if ($PSCmdlet.ShouldProcess($moduleManifestPath, 'Create module manifest')) { + Write-Verbose -Message ($moduleManifestParams | ConvertTo-Json -Depth 10 | Out-String) + New-ModuleManifest @moduleManifestParams + } +} + +$psm1Path = Join-Path -Path $resourcePath -ChildPath "$DscResourceModule.psm1" +if (-not (Test-Path $psm1Path)) { + New-Item -ItemType File -Path $psm1Path -Force | Out-Null +} + +$testsFilePath = Join-Path -Path $testsPath -ChildPath "$DscResourceModule.Tests.ps1" +if (-not (Test-Path $testsFilePath)) { + New-Item -ItemType File -Path $testsFilePath -Force | Out-Null +} +