-
Notifications
You must be signed in to change notification settings - Fork 193
Auto-update ms.date in docs-mslearn on PR changes #1985
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,69 @@ | ||||||
| name: Update ms.date in docs-mslearn | ||||||
|
|
||||||
| on: | ||||||
| pull_request: | ||||||
| paths: | ||||||
| - 'docs-mslearn/**/*.md' | ||||||
|
|
||||||
| permissions: | ||||||
| contents: write | ||||||
| pull-requests: write | ||||||
|
|
||||||
| jobs: | ||||||
| update-dates: | ||||||
| name: Update ms.date in changed markdown files | ||||||
| runs-on: ubuntu-latest | ||||||
| # Only run on PRs from the same repo (not forks) to allow pushing | ||||||
| if: github.event.pull_request.head.repo.full_name == github.repository | ||||||
| steps: | ||||||
| - name: Checkout PR branch | ||||||
| uses: actions/checkout@v4 | ||||||
| with: | ||||||
| ref: ${{ github.head_ref }} | ||||||
| fetch-depth: 0 | ||||||
| token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|
|
||||||
| - name: Get changed markdown files | ||||||
| id: changed-files | ||||||
| uses: tj-actions/changed-files@v44 | ||||||
| with: | ||||||
| files: 'docs-mslearn/**/*.md' | ||||||
|
|
||||||
| - name: Update ms.date in changed files | ||||||
| if: steps.changed-files.outputs.any_changed == 'true' | ||||||
| run: | | ||||||
| # Get current date in MM/DD/YYYY format | ||||||
| CURRENT_DATE=$(date +'%m/%d/%Y') | ||||||
| echo "Updating ms.date to: $CURRENT_DATE" | ||||||
|
|
||||||
| # Process each changed markdown file | ||||||
| for file in ${{ steps.changed-files.outputs.all_changed_files }}; do | ||||||
| echo "Processing: $file" | ||||||
|
|
||||||
| # Check if file has ms.date in frontmatter and update it | ||||||
| if grep -q "^ms\.date:" "$file"; then | ||||||
| # Use sed to replace the ms.date line | ||||||
| sed -i "s/^ms\.date:.*$/ms.date: $CURRENT_DATE/" "$file" | ||||||
| echo " Updated ms.date in $file" | ||||||
| else | ||||||
| echo " No ms.date found in $file, skipping" | ||||||
| fi | ||||||
| done | ||||||
|
|
||||||
| - name: Check for changes | ||||||
| id: check-changes | ||||||
| run: | | ||||||
| if git diff --quiet; then | ||||||
| echo "has_changes=false" >> $GITHUB_OUTPUT | ||||||
| else | ||||||
| echo "has_changes=true" >> $GITHUB_OUTPUT | ||||||
| fi | ||||||
|
|
||||||
| - name: Commit and push changes | ||||||
| if: steps.check-changes.outputs.has_changes == 'true' | ||||||
| run: | | ||||||
| git config --local user.email "github-actions[bot]@users.noreply.github.com" | ||||||
| git config --local user.name "github-actions[bot]" | ||||||
| git add docs-mslearn/**/*.md | ||||||
|
||||||
| git add docs-mslearn/**/*.md | |
| git add -u docs-mslearn/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| BeforeDiscovery { | ||
| $docsPath = Join-Path (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName 'docs-mslearn' | ||
| $markdownFiles = Get-ChildItem -Path $docsPath -Recurse -Filter '*.md' -File | ||
| } | ||
|
|
||
| Describe 'Microsoft Learn docs - [<_.Name>]' -ForEach $markdownFiles { | ||
| BeforeAll { | ||
| $file = $_ | ||
| $content = Get-Content -Path $file.FullName -Raw | ||
|
|
||
| # Extract frontmatter (content between first pair of ---) | ||
| $frontmatterMatch = [regex]::Match($content, '^---\r?\n([\s\S]*?)\r?\n---') | ||
| $hasFrontmatter = $frontmatterMatch.Success | ||
| $frontmatter = if ($hasFrontmatter) { $frontmatterMatch.Groups[1].Value } else { '' } | ||
|
|
||
| # Extract ms.date value | ||
| $msDateMatch = [regex]::Match($frontmatter, '^ms\.date:\s*(.+)$', [System.Text.RegularExpressions.RegexOptions]::Multiline) | ||
| $hasMsDate = $msDateMatch.Success | ||
| $msDateValue = if ($hasMsDate) { $msDateMatch.Groups[1].Value.Trim() } else { '' } | ||
|
|
||
| # Helper function to parse date | ||
| function Test-MsDateValid { | ||
| param([string]$DateString) | ||
| try { | ||
| $null = [datetime]::ParseExact($DateString, 'MM/dd/yyyy', [System.Globalization.CultureInfo]::InvariantCulture) | ||
| return $true | ||
| } catch { | ||
| return $false | ||
| } | ||
| } | ||
|
|
||
| function Get-MsDateParsed { | ||
| param([string]$DateString) | ||
| try { | ||
| return [datetime]::ParseExact($DateString, 'MM/dd/yyyy', [System.Globalization.CultureInfo]::InvariantCulture) | ||
| } catch { | ||
| return $null | ||
| } | ||
| } | ||
| } | ||
|
|
||
| It 'Should have YAML frontmatter' { | ||
| $hasFrontmatter | Should -BeTrue -Because "Microsoft Learn docs require YAML frontmatter" | ||
| } | ||
|
|
||
| It 'Should have ms.date field' { | ||
| $hasMsDate | Should -BeTrue -Because "Microsoft Learn docs require an ms.date field in frontmatter" | ||
| } | ||
|
|
||
| It 'Should have ms.date in MM/DD/YYYY format' { | ||
| if (-not $hasMsDate) { | ||
| Set-ItResult -Skipped -Because "ms.date field is missing" | ||
| return | ||
| } | ||
|
|
||
| # Validate format: MM/DD/YYYY | ||
| $msDateValue | Should -Match '^\d{2}/\d{2}/\d{4}$' -Because "ms.date must be in MM/DD/YYYY format (found: $msDateValue)" | ||
| } | ||
|
|
||
| It 'Should have a valid ms.date value' { | ||
| if (-not $hasMsDate) { | ||
| Set-ItResult -Skipped -Because "ms.date field is missing" | ||
| return | ||
| } | ||
|
|
||
| # Try to parse the date | ||
| $isValidDate = Test-MsDateValid -DateString $msDateValue | ||
| $isValidDate | Should -BeTrue -Because "ms.date must be a valid date (found: $msDateValue)" | ||
| } | ||
|
|
||
| It 'Should not have ms.date in the future' { | ||
| if (-not $hasMsDate) { | ||
| Set-ItResult -Skipped -Because "ms.date field is missing" | ||
| return | ||
| } | ||
|
|
||
| $parsedDate = Get-MsDateParsed -DateString $msDateValue | ||
| if (-not $parsedDate) { | ||
| Set-ItResult -Skipped -Because "ms.date is not a valid date" | ||
| return | ||
| } | ||
|
|
||
| $parsedDate | Should -BeLessOrEqual (Get-Date) -Because "ms.date should not be in the future" | ||
| } | ||
|
|
||
| It 'Should have ms.date within reasonable range (after 2020)' { | ||
| if (-not $hasMsDate) { | ||
| Set-ItResult -Skipped -Because "ms.date field is missing" | ||
| return | ||
| } | ||
|
|
||
| $parsedDate = Get-MsDateParsed -DateString $msDateValue | ||
| if (-not $parsedDate) { | ||
| Set-ItResult -Skipped -Because "ms.date is not a valid date" | ||
| return | ||
| } | ||
|
|
||
| $minDate = [datetime]::new(2020, 1, 1) | ||
| $parsedDate | Should -BeGreaterOrEqual $minDate -Because "ms.date seems too old (FinOps toolkit started in 2020)" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| Describe 'update-mslearn-dates GitHub Action' { | ||
| BeforeAll { | ||
| $workflowPath = Join-Path (Get-Item -Path $PSScriptRoot).Parent.Parent.Parent.Parent.FullName '.github/workflows/update-mslearn-dates.yml' | ||
| $workflowContent = Get-Content -Path $workflowPath -Raw | ||
| } | ||
|
|
||
| Context 'Workflow file structure' { | ||
| It 'Should exist' { | ||
| Test-Path $workflowPath | Should -BeTrue | ||
| } | ||
|
|
||
| It 'Should have a name' { | ||
| $workflowContent | Should -Match '^name:\s*.+' | ||
| } | ||
| } | ||
|
|
||
| Context 'Trigger configuration' { | ||
| It 'Should trigger on pull_request' { | ||
| $workflowContent | Should -Match 'on:\s*\r?\n\s+pull_request:' | ||
| } | ||
|
|
||
| It 'Should only trigger for docs-mslearn markdown files' { | ||
| $workflowContent | Should -Match "paths:\s*\r?\n\s+- 'docs-mslearn/\*\*/\*\.md'" | ||
| } | ||
|
|
||
| It 'Should not trigger on push events' { | ||
| # Ensure there's no 'push:' trigger at the top level | ||
| $workflowContent | Should -Not -Match 'on:\s*\r?\n\s+push:' | ||
| } | ||
| } | ||
|
|
||
| Context 'Permissions' { | ||
| It 'Should have contents write permission' { | ||
| $workflowContent | Should -Match 'contents:\s*write' | ||
| } | ||
|
|
||
| It 'Should have pull-requests write permission' { | ||
| $workflowContent | Should -Match 'pull-requests:\s*write' | ||
| } | ||
| } | ||
|
|
||
| Context 'Job configuration' { | ||
| It 'Should have update-dates job' { | ||
| $workflowContent | Should -Match 'jobs:\s*\r?\n\s+update-dates:' | ||
| } | ||
|
|
||
| It 'Should run on ubuntu-latest' { | ||
| $workflowContent | Should -Match 'runs-on:\s*ubuntu-latest' | ||
| } | ||
|
|
||
| It 'Should have fork protection condition' { | ||
| $workflowContent | Should -Match 'if:\s*github\.event\.pull_request\.head\.repo\.full_name\s*==\s*github\.repository' | ||
| } | ||
| } | ||
|
|
||
| Context 'Workflow steps' { | ||
| It 'Should checkout the PR branch' { | ||
| $workflowContent | Should -Match 'uses:\s*actions/checkout@v\d+' | ||
| $workflowContent | Should -Match 'ref:\s*\$\{\{\s*github\.head_ref\s*\}\}' | ||
| } | ||
|
|
||
| It 'Should use changed-files action' { | ||
| $workflowContent | Should -Match 'uses:\s*tj-actions/changed-files@v\d+' | ||
| } | ||
|
|
||
| It 'Should filter changed-files to docs-mslearn markdown' { | ||
| $workflowContent | Should -Match "files:\s*'docs-mslearn/\*\*/\*\.md'" | ||
| } | ||
|
|
||
| It 'Should have step to update ms.date' { | ||
| $workflowContent | Should -Match 'name:\s*Update ms\.date' | ||
| } | ||
|
|
||
| It 'Should use correct date format (MM/DD/YYYY)' { | ||
| $workflowContent | Should -Match "date \+'%m/%d/%Y'" | ||
| } | ||
|
|
||
| It 'Should use sed to replace ms.date line' { | ||
| $workflowContent | Should -Match 'sed -i' | ||
| $workflowContent | Should -Match 's/\^ms\\\.date:' | ||
| } | ||
|
|
||
| It 'Should have step to check for changes before committing' { | ||
| $workflowContent | Should -Match 'name:\s*Check for changes' | ||
| $workflowContent | Should -Match 'git diff --quiet' | ||
| } | ||
|
|
||
| It 'Should have step to commit and push' { | ||
| $workflowContent | Should -Match 'name:\s*Commit and push' | ||
| } | ||
|
|
||
| It 'Should only commit when there are changes' { | ||
| $workflowContent | Should -Match "if:\s*steps\.check-changes\.outputs\.has_changes\s*==\s*'true'" | ||
| } | ||
|
|
||
| It 'Should use github-actions bot for commits' { | ||
| $workflowContent | Should -Match 'github-actions\[bot\]@users\.noreply\.github\.com' | ||
| $workflowContent | Should -Match 'user\.name.*github-actions\[bot\]' | ||
| } | ||
|
|
||
| It 'Should use conventional commit format' { | ||
| $workflowContent | Should -Match 'git commit -m.*chore:' | ||
| } | ||
| } | ||
|
|
||
| Context 'Sed command validation' { | ||
| It 'Should have correct sed regex to match ms.date at line start' { | ||
| # The sed command should use ^ to anchor to start of line | ||
| $workflowContent | Should -Match 's/\^ms\\\.date:\.\*\$/ms\.date:' | ||
| } | ||
|
|
||
| It 'Should replace entire ms.date line' { | ||
| # Pattern should end with .* to match rest of line | ||
| $workflowContent | Should -Match 'ms\\\.date:\.\*\$' | ||
| } | ||
| } | ||
|
|
||
| Context 'Security considerations' { | ||
| It 'Should use GITHUB_TOKEN for checkout' { | ||
| $workflowContent | Should -Match 'token:\s*\$\{\{\s*secrets\.GITHUB_TOKEN\s*\}\}' | ||
| } | ||
|
|
||
| It 'Should only add docs-mslearn files to commit' { | ||
| $workflowContent | Should -Match 'git add docs-mslearn/' | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the FinOps toolkit coding guidelines, every change must have a changelog entry in docs-mslearn/toolkit/changelog.md with no exceptions for bug fixes, features, or improvements. This PR adds a new GitHub Action feature that automatically updates ms.date fields in docs-mslearn files, which is a customer-facing change that should be documented.
The changelog entry should be added under the next release version. Since package.json shows version 13.0.0, the changelog entry should be documented under the "PowerShell module v14" or similar appropriate section (or create a new unreleased section if one doesn't exist).
The changelog entry should briefly describe this new automation feature and its benefit to contributors.