diff --git a/README.md b/README.md index ac3ceca..080906f 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,16 @@ This action validates that pull requests and commits contain Azure DevOps work i 2. **Validates Commits** - Ensures each commit in a pull request has an Azure DevOps work item link (e.g. `AB#123`) in the commit message 3. **Automatically Links PRs to Work Items** - When a work item is referenced in a commit message, the action adds a GitHub Pull Request link to that work item in Azure DevOps - 🎯 **This is the key differentiator**: By default, Azure DevOps only adds the Pull Request link to work items mentioned directly in the PR title or body, but this action also links work items found in commit messages! +4. **Visibility & Tracking** - Work item linkages are displayed as GitHub Actions notices and added to the job summary for easy visibility + +## Action Output + +The action provides visibility into linked work items through: + +- **GitHub Actions Notices**: Work item links are displayed as notice annotations in the workflow run, making it easy to see which work items are linked + - Example: `Work item AB#12345 (from commit abc123d) linked to pull request #42` +- **Job Summary**: A summary of all linked work items is added to the workflow run's job summary page, providing a quick reference of work items associated with the PR + - Includes clickable links to commits and work items ## Usage diff --git a/__tests__/index.test.js b/__tests__/index.test.js index c6f7f94..d637be4 100644 --- a/__tests__/index.test.js +++ b/__tests__/index.test.js @@ -9,12 +9,19 @@ const mockGetInput = jest.fn(); const mockSetFailed = jest.fn(); const mockInfo = jest.fn(); const mockError = jest.fn(); +const mockNotice = jest.fn(); +const mockSummary = { + addRaw: jest.fn().mockReturnThis(), + write: jest.fn().mockResolvedValue(undefined) +}; jest.unstable_mockModule('@actions/core', () => ({ getInput: mockGetInput, setFailed: mockSetFailed, info: mockInfo, - error: mockError + error: mockError, + notice: mockNotice, + summary: mockSummary })); // Mock @actions/github @@ -64,6 +71,10 @@ describe('Azure DevOps Commit Validator', () => { // Clear all mocks jest.clearAllMocks(); + // Reset summary mock + mockSummary.addRaw.mockClear().mockReturnThis(); + mockSummary.write.mockClear().mockResolvedValue(undefined); + // Setup default mock implementations mockGetInput.mockImplementation(name => { const defaults = { diff --git a/src/index.js b/src/index.js index fa41214..cf440d0 100644 --- a/src/index.js +++ b/src/index.js @@ -355,7 +355,17 @@ async function checkCommitsForWorkItems( process.env.GITHUB_SERVER_URL = process.env.GITHUB_SERVER_URL || 'https://github.com'; await linkWorkItem(); + + // Add notice annotation and job summary for visibility + const commitInfo = workItemToCommitMap.get(workItemId); + if (commitInfo) { + core.notice(`Work item AB#${workItemId} (from commit ${commitInfo.shortSha}) linked to pull request #${pullNumber}`, { + title: 'Work Item Linked' + }); + core.summary.addRaw(`- Work item AB#${workItemId} (from commit [\`${commitInfo.shortSha}\`](${context.payload.repository?.html_url}/commit/${commitInfo.sha})) linked to pull request #${pullNumber}\n`); + } } + await core.summary.write(); } // Return the workItemToCommitMap and validation results for use in PR validation @@ -481,10 +491,36 @@ async function checkPullRequestForWorkItems( return invalidWorkItems; } + // All work items valid - add notice and job summary for each + for (const workItem of uniqueWorkItems) { + const workItemNumber = workItem.substring(3); // Remove "AB#" prefix + core.notice(`Pull request linked to work item AB#${workItemNumber}`, { + title: 'Work Item Linked' + }); + core.summary.addRaw(`- Pull request #${pullNumber} linked to work item AB#${workItemNumber}\n`); + } + await core.summary.write(); + // All work items valid - return empty array return []; } + // Validation disabled - add notice and job summary for each work item + for (const workItem of uniqueWorkItems) { + const workItemNumber = workItem.substring(3); // Remove "AB#" prefix + + // Add to the workItemToCommitMap if not already there + if (!workItemToCommitMap.has(workItemNumber)) { + workItemToCommitMap.set(workItemNumber, null); // null indicates it's from PR title/body + } + + core.notice(`Pull request linked to work item AB#${workItemNumber}`, { + title: 'Work Item Linked' + }); + core.summary.addRaw(`- Pull request #${pullNumber} linked to work item AB#${workItemNumber}\n`); + } + await core.summary.write(); + // Validation disabled - return empty array return []; }