Skip to content
Open
Changes from all 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
121 changes: 121 additions & 0 deletions .github/workflows/docs-from-code.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Process Docs from Code Issues

on:
issues:
types: [labeled]

permissions:
contents: read
issues: write

jobs:
assign-to-copilot:
name: Assign to Copilot Agent
runs-on: ubuntu-latest
if: github.event.action == 'labeled' && github.event.label.name == 'docs-from-code'

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Extract SME from issue body
id: extract-sme
uses: actions/github-script@v7
with:
script: |
const issueBody = context.payload.issue.body || '';

// Look for PR author pattern in issue body
// Expected format from dotnet/aspire workflow: "PR Author: @username" or similar
const authorPatterns = [
/PR\s*Author:\s*@?([\w-]+)/i,
/Author:\s*@?([\w-]+)/i,
/SME:\s*@?([\w-]+)/i,
/Original\s*PR\s*by\s*@?([\w-]+)/i,
/Created\s*by\s*@([\w-]+)/i
];

let smeUsername = null;

for (const pattern of authorPatterns) {
const match = issueBody.match(pattern);
if (match) {
smeUsername = match[1];
break;
}
}

// Also try to extract PR URL if present
const prUrlPattern = /https:\/\/github\.com\/dotnet\/aspire\/pull\/(\d+)/;
const prUrlMatch = issueBody.match(prUrlPattern);

if (!smeUsername && prUrlMatch) {
// If we found a PR URL but no author, we'll need to fetch it
const prNumber = prUrlMatch[1];
try {
const { data: pr } = await github.rest.pulls.get({
owner: 'dotnet',
repo: 'aspire',
pull_number: parseInt(prNumber)
});
smeUsername = pr.user.login;
console.log(`Extracted SME from PR #${prNumber}: ${smeUsername}`);
} catch (error) {
console.log(`Could not fetch PR details: ${error.message}`);
}
}

core.setOutput('sme', smeUsername || '');
core.setOutput('found', smeUsername ? 'true' : 'false');

console.log(`SME Username: ${smeUsername || 'not found'}`);

- name: Assign issue to Copilot
uses: actions/github-script@v7
with:
script: |
const issueNumber = context.payload.issue.number;

// Assign the issue to copilot
try {
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
assignees: ['copilot']
});
console.log(`Successfully assigned issue #${issueNumber} to copilot`);
} catch (error) {
console.log(`Could not assign to copilot: ${error.message}`);
// Continue even if assignment fails
}

- name: Add comment mentioning SME
uses: actions/github-script@v7
with:
script: |
const issueNumber = context.payload.issue.number;
const smeUsername = '${{ steps.extract-sme.outputs.sme }}';
const smeFound = '${{ steps.extract-sme.outputs.found }}' === 'true';

let commentBody = '## 🤖 Copilot Agent Assigned\n\n';
commentBody += 'This issue has been assigned to GitHub Copilot to draft a pull request based on the description provided.\n\n';

if (smeFound) {
commentBody += '### 📋 SME Review Required\n\n';
commentBody += `@${smeUsername} - You have been identified as the subject matter expert (SME) for this documentation update based on the originating PR in the [dotnet/aspire](https://github.com/dotnet/aspire) repository. Please review the draft PR once Copilot has completed its work.\n`;
} else {
commentBody += '### ⚠️ SME Not Found\n\n';
commentBody += 'Could not automatically identify the SME from the issue body. Please manually tag the appropriate reviewer once the draft PR is created.\n';
}

commentBody += '\n---\n*This comment was automatically generated by the docs-from-code workflow.*';

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: commentBody
});

console.log(`Added comment to issue #${issueNumber}`);
Loading