Skip to content
Open
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions .github/workflows/detect-netsdk-diagnostics.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Workflow to detect new NETSDK diagnostic codes in pull requests
#
# This workflow automatically:
# 1. Uses git diff to get changes from .xlf files only
# 2. Scans for newly added NETSDK diagnostic codes (e.g., NETSDK1234:)
# 3. Applies the 'sdk-diagnostic-docs-needed' label when new diagnostics are found
# 4. Posts/updates a comment reminding contributors to update SDK diagnostic documentation
# in the dotnet/docs repository at docs/core/tools/sdk-errors/
#
# Prerequisites: The 'sdk-diagnostic-docs-needed' label must exist in the repository
#
# Security: Uses pull_request_target to run in the base branch context (safe for forks)
# The workflow checks out the base branch and fetches the PR head, but does not execute any code from the PR.
#
name: Detect New NETSDK Diagnostics

on:
# Use pull_request_target for safe execution from forks
# This runs in the context of the base branch, not the PR head
pull_request_target:
types: [opened, synchronize]
branches:
- main
- release/*

permissions:
contents: read
pull-requests: write
issues: write

jobs:
detect-diagnostics:
name: Detect New NETSDK Diagnostic Codes
runs-on: ubuntu-latest
# Only run on the main repository, not forks
if: github.repository == 'dotnet/sdk'

steps:
- name: Checkout base branch
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
ref: ${{ github.event.pull_request.base.ref }}
fetch-depth: 0
persist-credentials: false

- name: Fetch PR head
run: |
git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-${{ github.event.pull_request.number }}

- name: Get diff from .xlf files only
id: get-diff
run: |
# Get diff only from .xlf files between base and PR head
git diff ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} -- '*.xlf' > /tmp/xlf-diff.txt
# Store for next step
cat /tmp/xlf-diff.txt

- name: Detect new NETSDK diagnostics
id: detect
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
script: |
const fs = require('fs');
const diff = fs.readFileSync('/tmp/xlf-diff.txt', 'utf8');

// Pattern to match NETSDK diagnostic codes
const diagnosticPattern = /NETSDK(\d{4}):/g;

// Extract only added lines from the diff (lines starting with +)
const addedLines = diff.split('\n').filter(line => line.startsWith('+') && !line.startsWith('+++'));

// Find all NETSDK codes in added lines
const diagnosticCodes = new Set();
for (const line of addedLines) {
// Reset regex state for each line to avoid issues with global flag
diagnosticPattern.lastIndex = 0;
let match;
while ((match = diagnosticPattern.exec(line)) !== null) {
diagnosticCodes.add(`NETSDK${match[1]}`);
}
}

const foundDiagnostics = Array.from(diagnosticCodes).sort();

core.setOutput('found_diagnostics', foundDiagnostics.join(','));
core.setOutput('has_diagnostics', foundDiagnostics.length > 0 ? 'true' : 'false');

if (foundDiagnostics.length > 0) {
core.info(`Found ${foundDiagnostics.length} new NETSDK diagnostic(s) in .xlf files: ${foundDiagnostics.join(', ')}`);
} else {
core.info('No new NETSDK diagnostics found in .xlf files in this PR.');
}

return foundDiagnostics;

- name: Add label to PR
if: steps.detect.outputs.has_diagnostics == 'true'
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
script: |
const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const labelName = 'sdk-diagnostic-docs-needed';

await github.rest.issues.addLabels({
owner,
repo,
issue_number,
labels: [labelName]
});

core.info(`Label '${labelName}' added to PR #${issue_number}`);

- name: Post or update comment
if: steps.detect.outputs.has_diagnostics == 'true'
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
DIAGNOSTICS: ${{ steps.detect.outputs.found_diagnostics }}
with:
script: |
const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const diagnostics = process.env.DIAGNOSTICS.split(',');

// Create the comment body
const commentIdentifier = '<!-- netsdk-diagnostics-reminder -->';
const commentBody = `${commentIdentifier}
## 📋 SDK Diagnostic Documentation Reminder

This PR introduces **${diagnostics.length}** new SDK diagnostic code${diagnostics.length > 1 ? 's' : ''}:
${diagnostics.map(code => `- \`${code}\``).join('\n')}

### Action Required
Please ensure that documentation for ${diagnostics.length > 1 ? 'these diagnostics' : 'this diagnostic'} is added or updated in the [dotnet/docs](https://github.com/dotnet/docs) repository at:
- Path: \`docs/core/tools/sdk-errors/\`
- Documentation: https://learn.microsoft.com/dotnet/core/tools/sdk-errors/

Each diagnostic should have:
- A clear description of the error/warning
- Possible causes
- Recommended solutions
- Code examples where applicable

Thank you for helping keep our documentation up to date! 🙏`;

// Check if we already have a comment from this workflow
const { data: comments } = await github.rest.issues.listComments({
owner,
repo,
issue_number
});

const existingComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes(commentIdentifier)
);

if (existingComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existingComment.id,
body: commentBody
});
core.info(`Updated existing comment #${existingComment.id}`);
} else {
// Create new comment
await github.rest.issues.createComment({
owner,
repo,
issue_number,
body: commentBody
});
core.info('Created new comment');
}
Loading