diff --git a/azure-pipelines/common-templates/create-pr.yml b/azure-pipelines/common-templates/create-pr.yml index ed1a64e51f49..5c585ab594e7 100644 --- a/azure-pipelines/common-templates/create-pr.yml +++ b/azure-pipelines/common-templates/create-pr.yml @@ -14,10 +14,14 @@ parameters: - name: Body type: string default: "" + - name: Condition + type: string + default: "succeeded()" steps: - task: PowerShell@2 displayName: Create Pull Request for generated build + condition: ${{ parameters.Condition }} env: GhAppId: $(microsoft-graph-devx-bot-appid) GhAppKey: $(microsoft-graph-devx-bot-privatekey) diff --git a/azure-pipelines/common-templates/generate-docs-steps.yml b/azure-pipelines/common-templates/generate-docs-steps.yml new file mode 100644 index 000000000000..1b3a44f80c47 --- /dev/null +++ b/azure-pipelines/common-templates/generate-docs-steps.yml @@ -0,0 +1,198 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +# +# Reusable steps that generate the Microsoft Graph PowerShell reference docs for a +# single SDK profile (v1.0 or beta), commit the result to a dedicated branch and +# open a pull request for that profile. Consumed by the parallel stages in +# powershell-docs.yml so that v1.0 and beta each produce an independent PR. + +parameters: + - name: GraphProfileFilter + type: string + values: + - v1.0 + - beta + - name: BranchPrefix + type: string + - name: PrTitle + type: string + - name: PrBody + type: string + default: "This pull request was automatically created by Azure Pipelines. **Important** Check for unexpected deletions or changes in this PR." + +steps: + - template: ../update-template.yml + - task: PowerShell@2 + name: "ComputeBranch" + displayName: 'Compute Branch Name' + inputs: + targetType: 'inline' + script: | + git submodule update --init --recursive + $branch = "{0}_{1}" -f "${{ parameters.BranchPrefix }}", (Get-Date -Format yyyyMMddHHmm) + Write-Host "Compute branch: $branch" + Write-Host "##vso[task.setvariable variable=WeeklyReferenceDocsBranch;isOutput=true]$branch" + - task: Bash@3 + name: "CreateBranch" + displayName: "Create weekly refresh branch" + inputs: + targetType: inline + script: | + git status + git branch $(ComputeBranch.WeeklyReferenceDocsBranch) + git checkout $(ComputeBranch.WeeklyReferenceDocsBranch) + # Record the commit the branch was created from so the push step can tell + # whether generation actually advanced the branch (the generation scripts + # commit their own output, so the working tree is clean by push time). + BASE_COMMIT=$(git rev-parse HEAD) + echo "Base commit: $BASE_COMMIT" + echo "##vso[task.setvariable variable=BaseCommit;isOutput=true]$BASE_COMMIT" + git status + - task: PowerShell@2 + displayName: 'Create missing external docs folder' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/CreateExternalDocsFolder.ps1 + - task: PowerShell@2 + displayName: 'Generate markdown files from PowerShell help files' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/GenerateMarkDown.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + - task: PowerShell@2 + displayName: 'Escape disallowed html tags' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/EscapeDisallowedHtmlTags.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + - task: PowerShell@2 + displayName: 'Update metadata header' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/MsSubserviceUpdate.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + - task: PowerShell@2 + displayName: 'Generate permissions table' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/GeneratePermissionsTable.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + - task: PowerShell@2 + displayName: 'Update Links' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/UpdateLinks.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + errorActionPreference: 'stop' + - task: PowerShell@2 + displayName: 'Repair Examples and Descriptions and Title' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/RepairExamplesDescriptionsAndTitle.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + - task: PowerShell@2 + displayName: 'Remove boiler plate code' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/RemoveBoilerPlateCode.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + - task: PowerShell@2 + displayName: 'Remove invalid fullstops' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/RemoveInvalidFullStops.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + - task: PowerShell@2 + displayName: 'Generate missing docs for aliased cmdlets' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/GenerateAliasedDocs.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + - task: PowerShell@2 + displayName: 'Correct related links section' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/CorrectRelatedLinks-AllFiles.ps1 + arguments: -GraphProfileFilter '${{ parameters.GraphProfileFilter }}' + - task: PowerShell@2 + displayName: 'Stabilize ms.date values' + continueOnError: false + inputs: + targetType: 'filePath' + pwsh: true + filePath: scripts/StabilizeMsDate.ps1 + errorActionPreference: 'stop' + - task: AzureKeyVault@2 + displayName: "Azure Key Vault: Get GitHub App secrets" + inputs: + azureSubscription: "Federated AKV Managed Identity Connection" + KeyVaultName: akv-prod-eastus + SecretsFilter: "microsoft-graph-devx-bot-appid,microsoft-graph-devx-bot-privatekey" + - task: PowerShell@2 + name: "PushChanges" + displayName: Pushing to github + env: + GhAppId: $(microsoft-graph-devx-bot-appid) + GhAppKey: $(microsoft-graph-devx-bot-privatekey) + inputs: + targetType: inline + pwsh: true + script: | + git config --global user.email "GraphTooling@service.microsoft.com" + git config --global user.name "Microsoft Graph DevX Tooling" + git config --system core.longpaths true + # Commit anything the formatting steps left behind. The generation scripts + # already commit their own output, so this may legitimately be a no-op. + git add . + if (-not [string]::IsNullOrWhiteSpace((git status --porcelain))) { + git commit -m "Updating reference docs" + } + # Detect real changes by comparing the branch tip to the commit it was created + # from. Checking the working tree is not enough because earlier steps already + # committed their output, leaving the tree clean but the branch ahead of base. + $baseCommit = "$(CreateBranch.BaseCommit)" + $headCommit = (git rev-parse HEAD).Trim() + if ($headCommit -eq $baseCommit) { + Write-Host "No documentation changes detected (branch is at its base commit); skipping push and pull request." + Write-Host "##vso[task.setvariable variable=HasChanges;isOutput=true]false" + return + } + # The microsoft-graph-devx-bot GitHub App must have contents:write on this repo. + $token = & "$(Build.SourcesDirectory)/scripts/Generate-Github-Token.ps1" -AppClientId $env:GhAppId -AppPrivateKeyContents $env:GhAppKey -Repository "MicrosoftDocs/microsoftgraph-docs-powershell" + if ([string]::IsNullOrWhiteSpace($token)) { throw "Failed to generate GitHub App installation token." } + # Mask the token so it is never surfaced in pipeline logs. + Write-Host "##vso[task.setsecret]$token" + # A GitHub App installation token authenticates over HTTPS as user "x-access-token". + $pushUrl = "https://x-access-token:$token@github.com/MicrosoftDocs/microsoftgraph-docs-powershell.git" + git push --set-upstream $pushUrl $(ComputeBranch.WeeklyReferenceDocsBranch) + if ($LASTEXITCODE -ne 0) { throw "git push failed with exit code $LASTEXITCODE." } + Write-Host "##vso[task.setvariable variable=HasChanges;isOutput=true]true" + - template: create-pr.yml + parameters: + BaseBranch: "main" + TargetBranch: $(ComputeBranch.WeeklyReferenceDocsBranch) + Title: "${{ parameters.PrTitle }}" + Body: "${{ parameters.PrBody }}" + Condition: "and(succeeded(), eq(variables['PushChanges.HasChanges'], 'true'))" diff --git a/azure-pipelines/powershell-docs.yml b/azure-pipelines/powershell-docs.yml index c0304ad79ada..04d1c72ec610 100644 --- a/azure-pipelines/powershell-docs.yml +++ b/azure-pipelines/powershell-docs.yml @@ -47,156 +47,41 @@ extends: customBuildTags: - ES365AIMigrationTooling stages: - - stage: stage + - stage: GenerateV1 + displayName: 'Generate v1.0 reference docs' + dependsOn: [] jobs: - - job: PowerShellDocsUpdate + - job: PowerShellDocsUpdateV1 timeoutInMinutes: ${{ parameters.PipelineTimeout }} templateContext: outputs: - output: pipelineArtifact displayName: 'Publish Examples to be reviewed as artifact' targetPath: missingexternaldocsurl - artifactName: 'MissingExternalDocs' + artifactName: 'MissingExternalDocs-v1.0' publishLocation: 'Container' steps: - - template: azure-pipelines/update-template.yml@self - - task : PowerShell@2 - name: "ComputeBranch" - displayName: 'Compute Branch Name' - inputs: - targetType: 'inline' - script: | - git submodule update --init --recursive - $branch = "{0}_{1}" -f "WeeklyReferenceDocsUpdate", (Get-Date -Format yyyyMMddHHmm) - Write-Host "Compute branch: $branch" - Write-Host "##vso[task.setvariable variable=WeeklyReferenceDocsBranch;isOutput=true]$branch" - - task: Bash@3 - displayName: "Create weekly refresh branch" - inputs: - targetType: inline - script: | - git status - git branch $(ComputeBranch.WeeklyReferenceDocsBranch) - git checkout $(ComputeBranch.WeeklyReferenceDocsBranch) - git status - - task: PowerShell@2 - displayName: 'Create missing external docs folder' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/CreateExternalDocsFolder.ps1 - - - task: PowerShell@2 - displayName: 'Generate markdown files from PowerShell help files' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/GenerateMarkDown.ps1 - - task: PowerShell@2 - displayName: 'Escape disallowed html tags' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/EscapeDisallowedHtmlTags.ps1 - - task: PowerShell@2 - displayName: 'Update metadata header' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/MsSubserviceUpdate.ps1 - - task: PowerShell@2 - displayName: 'Generate permissions table' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/GeneratePermissionsTable.ps1 - - task: PowerShell@2 - displayName: 'Update Links' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/UpdateLinks.ps1 - errorActionPreference: 'stop' - - task: PowerShell@2 - displayName: 'Repair Examples and Descriptions and Title' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/RepairExamplesDescriptionsAndTitle.ps1 - - task: PowerShell@2 - displayName: 'Remove boiler plate code' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/RemoveBoilerPlateCode.ps1 - - task: PowerShell@2 - displayName: 'Remove invalid fullstops' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/RemoveInvalidFullStops.ps1 - - task: PowerShell@2 - displayName: 'Generate missing docs for aliased cmdlets' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/GenerateAliasedDocs.ps1 - - task: PowerShell@2 - displayName: 'Correct related links section' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/CorrectRelatedLinks-AllFiles.ps1 - - task: PowerShell@2 - displayName: 'Stabilize ms.date values' - continueOnError: false - inputs: - targetType: 'filePath' - pwsh: true - filePath: scripts/StabilizeMsDate.ps1 - errorActionPreference: 'stop' - - task: AzureKeyVault@2 - displayName: "Azure Key Vault: Get GitHub App secrets" - inputs: - azureSubscription: "Federated AKV Managed Identity Connection" - KeyVaultName: akv-prod-eastus - SecretsFilter: "microsoft-graph-devx-bot-appid,microsoft-graph-devx-bot-privatekey" - - task: PowerShell@2 - displayName: Pushing to github - env: - GhAppId: $(microsoft-graph-devx-bot-appid) - GhAppKey: $(microsoft-graph-devx-bot-privatekey) - inputs: - targetType: inline - pwsh: true - script: | - git config --global user.email "GraphTooling@service.microsoft.com" - git config --global user.name "Microsoft Graph DevX Tooling" - git config --system core.longpaths true - # The microsoft-graph-devx-bot GitHub App must have contents:write on this repo. - $token = & "$(Build.SourcesDirectory)/scripts/Generate-Github-Token.ps1" -AppClientId $env:GhAppId -AppPrivateKeyContents $env:GhAppKey -Repository "MicrosoftDocs/microsoftgraph-docs-powershell" - if ([string]::IsNullOrWhiteSpace($token)) { throw "Failed to generate GitHub App installation token." } - # Mask the token so it is never surfaced in pipeline logs. - Write-Host "##vso[task.setsecret]$token" - git status - git add . - git commit -m "Updating reference docs" - git push --set-upstream "https://x-access-token:$token@github.com/MicrosoftDocs/microsoftgraph-docs-powershell.git" $(ComputeBranch.WeeklyReferenceDocsBranch) - git status - - template: azure-pipelines/common-templates/create-pr.yml@self + - template: azure-pipelines/common-templates/generate-docs-steps.yml@self parameters: - BaseBranch: "main" - TargetBranch: $(ComputeBranch.WeeklyReferenceDocsBranch) - Title: "Weekly PowerShell Microsoft Graph Reference Docs Refresh" - Body: "This pull request was automatically created by Azure Pipelines. **Important** Check for unexpected deletions or changes in this PR." \ No newline at end of file + GraphProfileFilter: 'v1.0' + BranchPrefix: 'WeeklyReferenceDocsUpdate-v1' + PrTitle: 'Weekly PowerShell Microsoft Graph v1.0 Reference Docs Refresh' + - stage: GenerateBeta + displayName: 'Generate beta reference docs' + dependsOn: [] + jobs: + - job: PowerShellDocsUpdateBeta + timeoutInMinutes: ${{ parameters.PipelineTimeout }} + templateContext: + outputs: + - output: pipelineArtifact + displayName: 'Publish Examples to be reviewed as artifact' + targetPath: missingexternaldocsurl + artifactName: 'MissingExternalDocs-beta' + publishLocation: 'Container' + steps: + - template: azure-pipelines/common-templates/generate-docs-steps.yml@self + parameters: + GraphProfileFilter: 'beta' + BranchPrefix: 'WeeklyReferenceDocsUpdate-beta' + PrTitle: 'Weekly PowerShell Microsoft Graph beta Reference Docs Refresh' diff --git a/scripts/CorrectRelatedLinks-AllFiles.ps1 b/scripts/CorrectRelatedLinks-AllFiles.ps1 index 3f6a71383b9e..4fb92908cac6 100644 --- a/scripts/CorrectRelatedLinks-AllFiles.ps1 +++ b/scripts/CorrectRelatedLinks-AllFiles.ps1 @@ -19,6 +19,7 @@ param( [Parameter(Mandatory = $false)] [ValidateSet("v1.0", "beta", "both")] + [Alias("GraphProfileFilter")] [string]$GraphProfile = "both" ) diff --git a/scripts/EscapeDisallowedHtmlTags.ps1 b/scripts/EscapeDisallowedHtmlTags.ps1 index 7f2d4079ff9d..4c98cb950a33 100644 --- a/scripts/EscapeDisallowedHtmlTags.ps1 +++ b/scripts/EscapeDisallowedHtmlTags.ps1 @@ -2,7 +2,9 @@ # Licensed under the MIT License. Param( $ModulesToGenerate = @(), - [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "../microsoftgraph/config/ModulesMapping.jsonc") + [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "../microsoftgraph/config/ModulesMapping.jsonc"), + [ValidateSet("v1.0", "beta", "both")] + [string] $GraphProfileFilter = "both" ) function Get-GraphMapping { $graphMapping = @{} @@ -18,7 +20,8 @@ function EscapeDisallowedHtmlTags { $ModulePrefix = "Microsoft.Graph" $GraphMapping = Get-GraphMapping - $GraphMapping.Keys | ForEach-Object { + $profilesToProcess = if ($GraphProfileFilter -eq 'both') { $GraphMapping.Keys } else { @($GraphProfileFilter) } + $profilesToProcess | ForEach-Object { $graphProfile = $_ Get-FilesByProfile -GraphProfile $graphProfile -GraphProfilePath $GraphMapping[$graphProfile] -ModulePrefix $ModulePrefix -ModulesToGenerate $ModulesToGenerate } @@ -26,7 +29,18 @@ function EscapeDisallowedHtmlTags { git config --global user.email "timwamalwa@gmail.com" git config --global user.name "Timothy Wamalwa" git add . - git commit -m "Escaped disallowed html tags" + + # Check for staged changes; commit only if any exist + $pending = git status --porcelain + if (-not [string]::IsNullOrWhiteSpace($pending)) { + git commit -m "Escaped disallowed html tags" + Write-Host "Committed escaping of disallowed html tags." + } + else { + Write-Host "Nothing to commit; skipping commit step." + # Ensure a clean exit code even if earlier native commands returned 1 + $global:LASTEXITCODE = 0 + } } function Get-FilesByProfile { Param( diff --git a/scripts/GenerateAliasedDocs.ps1 b/scripts/GenerateAliasedDocs.ps1 index 97eba9685354..23052eb40eff 100644 --- a/scripts/GenerateAliasedDocs.ps1 +++ b/scripts/GenerateAliasedDocs.ps1 @@ -6,12 +6,17 @@ See related PR for more information. #> +param( + [ValidateSet("v1.0", "beta", "both")] + [string]$GraphProfileFilter = "both" +) + function Start-GenerateAliasedDocs { $BreakingChangeReportV1Report = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/docs/PowerShellBreakingChanges-V1.0.csv") $BreakingChangeReportBetaReport = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/docs/PowerShellBreakingChanges-beta.csv") $Reports = @() - $Reports += $BreakingChangeReportV1Report - $Reports += $BreakingChangeReportBetaReport + if ($GraphProfileFilter -ne 'beta') { $Reports += $BreakingChangeReportV1Report } + if ($GraphProfileFilter -ne 'v1.0') { $Reports += $BreakingChangeReportBetaReport } foreach ($BreakingChangeReport in $Reports) { Import-Csv $BreakingChangeReport | ForEach-Object { $Command = $_."NewCmdlet" diff --git a/scripts/GenerateMarkDown.ps1 b/scripts/GenerateMarkDown.ps1 index 52498f065e01..77fa2a8080f7 100644 --- a/scripts/GenerateMarkDown.ps1 +++ b/scripts/GenerateMarkDown.ps1 @@ -5,7 +5,9 @@ Param( $ModulesToGenerate = @(), [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "../microsoftgraph/config\ModulesMapping.jsonc"), [string] $WorkLoadDocsPath = (Join-Path $PSScriptRoot "../microsoftgraph"), - [string] $CmdletMetadataPath = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/src/Authentication/Authentication/custom/common/MgCommandMetadata.json") + [string] $CmdletMetadataPath = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/src/Authentication/Authentication/custom/common/MgCommandMetadata.json"), + [ValidateSet("v1.0", "beta", "both")] + [string] $GraphProfileFilter = "both" ) function Get-GraphMapping { $graphMapping = @{} @@ -81,7 +83,15 @@ function Start-GraphHelp { Param( $ModulesToGenerate = @() ) - + + # Track how much PlatyPS actually produced. New-MarkdownCommandHelp writes a temp + # file on every run (even when the content matches the committed docs), so a total + # of zero generated files means generation itself failed rather than the docs being + # up to date. This lets the pipeline tell a silent generation failure apart from a + # legitimate no-op (see the hard check at the end of this script). + $script:GeneratedFileCount = 0 + $script:GenerationFailures = [System.Collections.Generic.List[string]]::new() + #Generate for auth module first $ModulePrefix = "Microsoft.Graph" $AuthPath = "$ModulePrefix.Authentication" @@ -91,23 +101,32 @@ function Start-GraphHelp { $GraphMapping = Get-GraphMapping $TempAuthDir = Join-Path ([System.IO.Path]::GetTempPath()) "GraphDocsTempAuth_$([guid]::NewGuid().ToString('N'))" New-Item -Path $TempAuthDir -ItemType Directory -Force | Out-Null - $GraphMapping.Keys | ForEach-Object { + $profilesToProcess = if ($GraphProfileFilter -eq 'both') { $GraphMapping.Keys } else { @($GraphProfileFilter) } + $profilesToProcess | ForEach-Object { $graphProfile = $_ $profilePath = "graph-powershell-1.0" if ($graphProfile -eq "beta") { $profilePath = "graph-powershell-beta" } + # Authentication docs live only under graph-powershell-1.0, so generate them for the v1.0 profile only. + if ($graphProfile -eq "v1.0") { # Generate all auth module docs to temp directory using module-level generation Set-Help -ModuleDocsPath $TempAuthDir -Command "Connect-MgGraph" -Module "Microsoft.Graph.Authentication" # Compare and copy all generated auth files $TempAuthModuleDir = Join-Path $TempAuthDir $AuthPath - if (Test-Path $TempAuthModuleDir) { + $authFiles = if (Test-Path $TempAuthModuleDir) { @(Get-ChildItem -Path $TempAuthModuleDir -Filter "*.md" -File) } else { @() } + if ($authFiles.Count -eq 0) { + $script:GenerationFailures.Add("Microsoft.Graph.Authentication (module page)") + Write-Warning "Auth module generation produced no markdown files." + } + if ($TempAuthModuleDir -and (Test-Path $TempAuthModuleDir)) { if (-not (Test-Path $AuthDestination)) { New-Item -Path $AuthDestination -ItemType Directory -Force | Out-Null } - Get-ChildItem -Path $TempAuthModuleDir -Filter "*.md" -File | ForEach-Object { + $authFiles | ForEach-Object { + $script:GeneratedFileCount++ $tempFile = $_.FullName $existingFile = Join-Path $AuthDestination $_.Name if (Test-Path $existingFile) { @@ -122,6 +141,7 @@ function Start-GraphHelp { Write-Host "Added auth doc: $($_.BaseName)" } } + } } Get-FolderByProfile -GraphProfile $graphProfile -GraphProfilePath $profilePath -ModulePrefix $ModulePrefix -ModulesToGenerate $ModulesToGenerate } @@ -129,7 +149,18 @@ function Start-GraphHelp { git config --global user.email "GraphTooling@service.microsoft.com" git config --global user.name "Microsoft Graph DevX Tooling" git add . - git commit -m "Updated markdown help" + + # Check for staged changes; commit only if any exist + $pending = git status --porcelain + if (-not [string]::IsNullOrWhiteSpace($pending)) { + git commit -m "Updated markdown help" + Write-Host "Committed updated markdown help." + } + else { + Write-Host "Nothing to commit; skipping commit step." + # Ensure a clean exit code even if earlier native commands returned 1 + $global:LASTEXITCODE = 0 + } } function Get-FolderByProfile { @@ -202,6 +233,7 @@ function Get-FolderByProfile { Set-Help -ModuleDocsPath $TempOutputDir -Command $Command -Module $Path $TempFilePath = Join-Path $TempOutputDir $Path "$Command.md" if (Test-Path $TempFilePath) { + $script:GeneratedFileCount++ if (Test-Path $CmdletDocsPath) { $existingContent = Get-NormalizedContent -FilePath $CmdletDocsPath $newContent = Get-NormalizedContent -FilePath $TempFilePath @@ -213,6 +245,11 @@ function Get-FolderByProfile { Copy-Item -Path $TempFilePath -Destination $CmdletDocsPath -Force Write-Host "Added: $Command" } + } else { + # The command is available but PlatyPS produced no file: a real + # generation failure (its error was swallowed inside Set-Help). + $script:GenerationFailures.Add($Command) + Write-Warning "Generation produced no output for available command: $Command" } } elseif (-not (Test-Path $CmdletDocsPath)) { Write-Warning "Cmdlet $Command is not available." @@ -280,4 +317,16 @@ if ($ModulesToGenerate.Count -eq 0) { } Write-Host -ForegroundColor Green "-------------finished checking out to today's branch-------------" Start-GraphHelp -ModulesToGenerate $ModulesToGenerate + +# Fail loudly when generation produced nothing. Without this guard a broken generation +# (e.g. a PlatyPS/module error swallowed by Set-Help) looks exactly like an up-to-date +# no-op: the working tree stays clean, an empty branch is pushed and the PR step fails +# with a confusing "No commits between main and " error. +if ($script:GeneratedFileCount -eq 0) { + throw "Doc generation produced no markdown files for profile '$GraphProfileFilter'. This indicates a generation failure (PlatyPS/module error), not up-to-date docs. Failing the build so the empty result is not silently published." +} +if ($script:GenerationFailures.Count -gt 0) { + Write-Warning "Generation produced no output for $($script:GenerationFailures.Count) available command(s): $($script:GenerationFailures -join ', ')" +} +Write-Host "Generation summary for profile '$GraphProfileFilter': $($script:GeneratedFileCount) file(s) generated, $($script:GenerationFailures.Count) failure(s)." Write-Host -ForegroundColor Green "-------------Done-------------" \ No newline at end of file diff --git a/scripts/GeneratePermissionsTable.ps1 b/scripts/GeneratePermissionsTable.ps1 index aeca973f7f22..27815e137e4d 100644 --- a/scripts/GeneratePermissionsTable.ps1 +++ b/scripts/GeneratePermissionsTable.ps1 @@ -1,6 +1,8 @@ param( [string]$MgCommandMetadatJsonFile = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/src/Authentication/Authentication/custom/common/MgCommandMetadata.json"), - [string[]]$CmdList = @() + [string[]]$CmdList = @(), + [ValidateSet("v1.0", "beta", "both")] + [string]$GraphProfileFilter = "both" ) function Start-Generator { @@ -11,6 +13,7 @@ function Start-Generator { $CommandName = $_.Command; $ApiVersion = $_.ApiVersion $Module = $_.Module; + if ($GraphProfileFilter -ne 'both' -and $ApiVersion -ne $GraphProfileFilter) { return } #Array for DelegatedWork Permissions $DelegatedWorkPermissions = @(); #Array for Application Permissions @@ -70,7 +73,18 @@ function Start-Generator { git config --global user.email "GraphTooling@service.microsoft.com" git config --global user.name "Microsoft Graph DevX Tooling" git add . - git commit -m "Inserted permissions Table" + + # Check for staged changes; commit only if any exist + $pending = git status --porcelain + if (-not [string]::IsNullOrWhiteSpace($pending)) { + git commit -m "Inserted permissions Table" + Write-Host "Committed permissions table." + } + else { + Write-Host "Nothing to commit; skipping commit step." + # Ensure a clean exit code even if earlier native commands returned 1 + $global:LASTEXITCODE = 0 + } } catch { Write-Host "Error in $_"; diff --git a/scripts/MsSubserviceUpdate.ps1 b/scripts/MsSubserviceUpdate.ps1 index d9bbd826afe6..85cdacda82fb 100644 --- a/scripts/MsSubserviceUpdate.ps1 +++ b/scripts/MsSubserviceUpdate.ps1 @@ -5,7 +5,9 @@ Param( [string] $SDKDocsPath = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/src"), [string] $SDKOpenApiPath = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell"), [string] $WorkLoadDocsPath = (Join-Path $PSScriptRoot "../microsoftgraph"), - [string] $MissingMsSubserviceHeaderPath = (Join-Path $PSScriptRoot "../missingexternaldocsurl") + [string] $MissingMsSubserviceHeaderPath = (Join-Path $PSScriptRoot "../missingexternaldocsurl"), + [ValidateSet("v1.0", "beta", "both")] + [string] $GraphProfileFilter = "both" ) function Start-Generator { if (Test-Path $CommandMetadataPath) { @@ -14,6 +16,7 @@ function Start-Generator { $ModuleName = $_.Module $GraphProfile = $_.ApiVersion $Command = $_.Command + if ($GraphProfileFilter -ne 'both' -and $GraphProfile -ne $GraphProfileFilter) { return } $ExternalDocUrl = $_.ApiReferenceLink $GraphProfilePath = "graph-powershell-1.0" $ModulePrefix = "Microsoft.Graph" diff --git a/scripts/RemoveBoilerPlateCode.ps1 b/scripts/RemoveBoilerPlateCode.ps1 index 246e79ba3feb..a677f75f07dd 100644 --- a/scripts/RemoveBoilerPlateCode.ps1 +++ b/scripts/RemoveBoilerPlateCode.ps1 @@ -4,7 +4,9 @@ Param( $ModulesToGenerate = @(), [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "../microsoftgraph/config/ModulesMapping.jsonc"), [string] $WorkLoadDocsPath = (Join-Path $PSScriptRoot "../microsoftgraph"), - [string] $AuthLoadDocsPath = (Join-Path $PSScriptRoot "../microsoftgraph/graph-powershell-1.0/Microsoft.Graph.Authentication") + [string] $AuthLoadDocsPath = (Join-Path $PSScriptRoot "../microsoftgraph/graph-powershell-1.0/Microsoft.Graph.Authentication"), + [ValidateSet("v1.0", "beta", "both")] + [string] $GraphProfileFilter = "both" ) function Get-GraphMapping { $graphMapping = @{} @@ -19,18 +21,21 @@ function Start-Repair { $ModulesToGenerate = @() ) - #Cleanup Authentication Module first - $files = Get-ChildItem -Path $AuthLoadDocsPath -Filter *.md -Recurse - foreach ($file in $files) { - $content = Get-Content -Path $file.FullName - # Remove lines that contain '{{ Fill in the Description }}' or '### This' or '### *' or '### have' or '### certain' or '### the' - $cleanedContent = $content | Where-Object { $_ -notmatch '^\s*{{ Fill in the Description }}|^\s*### This|^\s*### \*|^\s*### have|^\s*### certain|^\s*### the' } - # Write the cleaned content back to the file - $cleanedContent | Set-Content -Path $file.FullName + #Cleanup Authentication Module first (Authentication docs live under the v1.0 folder) + if ($GraphProfileFilter -ne 'beta') { + $files = Get-ChildItem -Path $AuthLoadDocsPath -Filter *.md -Recurse + foreach ($file in $files) { + $content = Get-Content -Path $file.FullName + # Remove lines that contain '{{ Fill in the Description }}' or '### This' or '### *' or '### have' or '### certain' or '### the' + $cleanedContent = $content | Where-Object { $_ -notmatch '^\s*{{ Fill in the Description }}|^\s*### This|^\s*### \*|^\s*### have|^\s*### certain|^\s*### the' } + # Write the cleaned content back to the file + $cleanedContent | Set-Content -Path $file.FullName + } } $ModulePrefix = "Microsoft.Graph" $GraphMapping = Get-GraphMapping - $GraphMapping.Keys | ForEach-Object { + $profilesToProcess = if ($GraphProfileFilter -eq 'both') { $GraphMapping.Keys } else { @($GraphProfileFilter) } + $profilesToProcess | ForEach-Object { $graphProfile = $_ $profilePath = "graph-powershell-1.0" if ($graphProfile -eq "beta") { @@ -41,7 +46,18 @@ function Start-Repair { git config --global user.email "GraphTooling@service.microsoft.com" git config --global user.name "Microsoft Graph DevX Tooling" git add . - git commit -m "Remove boiler plate code injected by Autorest" + + # Check for staged changes; commit only if any exist + $pending = git status --porcelain + if (-not [string]::IsNullOrWhiteSpace($pending)) { + git commit -m "Remove boiler plate code injected by Autorest" + Write-Host "Committed removal of boiler plate code." + } + else { + Write-Host "Nothing to commit; skipping commit step." + # Ensure a clean exit code even if earlier native commands returned 1 + $global:LASTEXITCODE = 0 + } } function Get-FilesByProfile { Param( diff --git a/scripts/RemoveInvalidFullStops.ps1 b/scripts/RemoveInvalidFullStops.ps1 index 37be47e7c73d..c3aa6191e7f8 100644 --- a/scripts/RemoveInvalidFullStops.ps1 +++ b/scripts/RemoveInvalidFullStops.ps1 @@ -2,7 +2,9 @@ # Licensed under the MIT License. Param( $ModulesToGenerate = @(), - [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "../microsoftgraph/config/ModulesMapping.jsonc") + [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "../microsoftgraph/config/ModulesMapping.jsonc"), + [ValidateSet("v1.0", "beta", "both")] + [string] $GraphProfileFilter = "both" ) function Get-GraphMapping { $graphMapping = @{} @@ -18,7 +20,8 @@ function Remove-InvalidFullStops { $ModulePrefix = "Microsoft.Graph" $GraphMapping = Get-GraphMapping - $GraphMapping.Keys | ForEach-Object { + $profilesToProcess = if ($GraphProfileFilter -eq 'both') { $GraphMapping.Keys } else { @($GraphProfileFilter) } + $profilesToProcess | ForEach-Object { $graphProfile = $_ Get-FilesByProfile -GraphProfile $graphProfile -GraphProfilePath $GraphMapping[$graphProfile] -ModulePrefix $ModulePrefix -ModulesToGenerate $ModulesToGenerate } @@ -26,7 +29,18 @@ function Remove-InvalidFullStops { git config --global user.email "timwamalwa@gmail.com" git config --global user.name "Timothy Wamalwa" git add . - git commit -m "Removed invalid full stops from the beginning of lines" + + # Check for staged changes; commit only if any exist + $pending = git status --porcelain + if (-not [string]::IsNullOrWhiteSpace($pending)) { + git commit -m "Removed invalid full stops from the beginning of lines" + Write-Host "Committed removal of invalid full stops." + } + else { + Write-Host "Nothing to commit; skipping commit step." + # Ensure a clean exit code even if earlier native commands returned 1 + $global:LASTEXITCODE = 0 + } } function Get-FilesByProfile { Param( diff --git a/scripts/RepairExamplesDescriptionsAndTitle.ps1 b/scripts/RepairExamplesDescriptionsAndTitle.ps1 index c8e3c7407479..06eba0fcbbe2 100644 --- a/scripts/RepairExamplesDescriptionsAndTitle.ps1 +++ b/scripts/RepairExamplesDescriptionsAndTitle.ps1 @@ -4,7 +4,9 @@ Param( $ModulesToGenerate = @(), [string] $SDKDocsPath = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/src"), [string] $WorkLoadDocsPath = (Join-Path $PSScriptRoot "../microsoftgraph"), - [string] $CommandMetadataPath = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/src/Authentication/Authentication/custom/common/MgCommandMetadata.json") + [string] $CommandMetadataPath = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/src/Authentication/Authentication/custom/common/MgCommandMetadata.json"), + [ValidateSet("v1.0", "beta", "both")] + [string] $GraphProfileFilter = "both" ) function Start-Copy { @@ -15,6 +17,7 @@ function Start-Copy { $ModuleName = $_.Module $GraphProfile = $_.ApiVersion $Command = $_.Command + if ($GraphProfileFilter -ne 'both' -and $GraphProfile -ne $GraphProfileFilter) { return } $GraphProfilePath = "graph-powershell-1.0" $ModulePrefix = "Microsoft.Graph" if ($GraphProfile -eq "beta") { @@ -38,7 +41,18 @@ function Start-Copy { git config --global user.email "GraphTooling@service.microsoft.com" git config --global user.name "Microsoft Graph DevX Tooling" git add . - git commit -m "Repaired examples and descriptions" + + # Check for staged changes; commit only if any exist + $pending = git status --porcelain + if (-not [string]::IsNullOrWhiteSpace($pending)) { + git commit -m "Repaired examples and descriptions" + Write-Host "Committed repaired examples and descriptions." + } + else { + Write-Host "Nothing to commit; skipping commit step." + # Ensure a clean exit code even if earlier native commands returned 1 + $global:LASTEXITCODE = 0 + } } diff --git a/scripts/UpdateLinks.ps1 b/scripts/UpdateLinks.ps1 index f7edf3e93bcd..918ac4537ba0 100644 --- a/scripts/UpdateLinks.ps1 +++ b/scripts/UpdateLinks.ps1 @@ -4,7 +4,9 @@ Param( $ModulesToGenerate = @(), [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "../microsoftgraph/config/ModulesMapping.jsonc"), [string] $SDKDocsPath = (Join-Path $PSScriptRoot "../msgraph-sdk-powershell/src"), - [string] $WorkLoadDocsPath = (Join-Path $PSScriptRoot "../microsoftgraph") + [string] $WorkLoadDocsPath = (Join-Path $PSScriptRoot "../microsoftgraph"), + [ValidateSet("v1.0", "beta", "both")] + [string] $GraphProfileFilter = "both" ) function Get-GraphMapping { $graphMapping = @{} @@ -20,7 +22,8 @@ function Start-Update { $ModulePrefix = "Microsoft.Graph" $GraphMapping = Get-GraphMapping - $GraphMapping.Keys | ForEach-Object { + $profilesToProcess = if ($GraphProfileFilter -eq 'both') { $GraphMapping.Keys } else { @($GraphProfileFilter) } + $profilesToProcess | ForEach-Object { $GraphProfile = $_ $profilePath = "graph-powershell-1.0" if ($GraphProfile -eq "beta") { @@ -116,7 +119,8 @@ function Construct-Path { $BetaFilePath = Join-Path $WorkLoadDocsPath "graph-powershell-beta" "Microsoft.Graph.Beta.$Module" "$Command.md" $V1FilePath = Join-Path $WorkLoadDocsPath "graph-powershell-1.0" "Microsoft.Graph.$Module" "$Command.md" - if (Test-Path $BetaFilePath) { + # Adds the cross-profile note into the beta doc (skip when generating v1.0 only). + if ((Test-Path $BetaFilePath) -and ($GraphProfileFilter -ne 'v1.0')) { $V1Command = $Command.Replace("-MgBeta", "-Mg") $ConfirmV1Path = Join-Path $WorkLoadDocsPath "graph-powershell-1.0" "Microsoft.Graph.$Module" "$V1Command.md" if (Test-Path $ConfirmV1Path) { @@ -124,7 +128,8 @@ function Construct-Path { } } - if (Test-Path $V1FilePath) { + # Adds the cross-profile note into the v1.0 doc (skip when generating beta only). + if ((Test-Path $V1FilePath) -and ($GraphProfileFilter -ne 'beta')) { $BetaCommand = $Command.Replace("-Mg", "-MgBeta") $ConfirmBetaPath = Join-Path $WorkLoadDocsPath "graph-powershell-beta" "Microsoft.Graph.Beta.$Module" "$BetaCommand.md" if (Test-Path $ConfirmBetaPath) {