From 36cfa2c085c0e3c4b8f598c3535025fa43879748 Mon Sep 17 00:00:00 2001 From: Josh Johanning Date: Fri, 7 Nov 2025 15:12:45 -0600 Subject: [PATCH 1/2] chore: empty commit to sync branch From d5c490b263d108d4aca71dc505c4170a57ab2d3d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 21:19:06 +0000 Subject: [PATCH 2/2] feat: validate Azure DevOps org and PAT when link-commits-to-pull-request is true Co-authored-by: joshjohanning <19912012+joshjohanning@users.noreply.github.com> --- __tests__/index.test.js | 83 +++++++++++++++++++++++++++++++++++++++-- src/index.js | 12 ++++++ 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/__tests__/index.test.js b/__tests__/index.test.js index c6f7f94..8928a4a 100644 --- a/__tests__/index.test.js +++ b/__tests__/index.test.js @@ -128,6 +128,79 @@ describe('Azure DevOps Commit Validator', () => { // Restore context mockContext.payload.pull_request = originalPR; }); + + it('should fail when link-commits-to-pull-request is true but azure-devops-organization is missing', async () => { + mockGetInput.mockImplementation(name => { + const defaults = { + 'check-pull-request': 'false', + 'check-commits': 'true', + 'fail-if-missing-workitem-commit-link': 'true', + 'link-commits-to-pull-request': 'true', + 'azure-devops-token': 'test-token', + 'azure-devops-organization': '', // Missing org + 'github-token': 'github-token', + 'comment-on-failure': 'true', + 'validate-work-item-exists': 'false' + }; + return defaults[name] || ''; + }); + + await run(); + + expect(mockSetFailed).toHaveBeenCalledWith( + 'Azure DevOps organization is required when link-commits-to-pull-request is true' + ); + }); + + it('should fail when link-commits-to-pull-request is true but azure-devops-token is missing', async () => { + mockGetInput.mockImplementation(name => { + const defaults = { + 'check-pull-request': 'false', + 'check-commits': 'true', + 'fail-if-missing-workitem-commit-link': 'true', + 'link-commits-to-pull-request': 'true', + 'azure-devops-token': '', // Missing token + 'azure-devops-organization': 'test-org', + 'github-token': 'github-token', + 'comment-on-failure': 'true', + 'validate-work-item-exists': 'false' + }; + return defaults[name] || ''; + }); + + await run(); + + expect(mockSetFailed).toHaveBeenCalledWith('Azure DevOps token is required when link-commits-to-pull-request is true'); + }); + + it('should pass when link-commits-to-pull-request is false even if azure-devops config is missing', async () => { + mockGetInput.mockImplementation(name => { + const defaults = { + 'check-pull-request': 'false', + 'check-commits': 'true', + 'fail-if-missing-workitem-commit-link': 'true', + 'link-commits-to-pull-request': 'false', // Linking disabled + 'azure-devops-token': '', + 'azure-devops-organization': '', + 'github-token': 'github-token', + 'comment-on-failure': 'true', + 'validate-work-item-exists': 'false' + }; + return defaults[name] || ''; + }); + + mockOctokit.paginate.mockResolvedValueOnce([ + { + sha: '1234567890abcdef', + commit: { message: 'Fix: AB#123' } + } + ]); + + await run(); + + // Should not call setFailed for missing Azure DevOps config since linking is disabled + expect(mockSetFailed).not.toHaveBeenCalled(); + }); }); describe('Commit validation', () => { @@ -1097,7 +1170,7 @@ describe('Azure DevOps Commit Validator', () => { }); describe('Edge cases - Work item linking with missing credentials', () => { - it('should attempt linking without failing when credentials are present', async () => { + it('should fail when linking is enabled but credentials are missing', async () => { mockGetInput.mockImplementation(name => { if (name === 'check-commits') return 'true'; if (name === 'check-pull-request') return 'false'; @@ -1124,8 +1197,12 @@ describe('Azure DevOps Commit Validator', () => { await run(); - // Should still call linkWorkItem even with empty credentials - expect(mockLinkWorkItem).toHaveBeenCalled(); + // Should fail due to missing Azure DevOps organization + expect(mockSetFailed).toHaveBeenCalledWith( + 'Azure DevOps organization is required when link-commits-to-pull-request is true' + ); + // Should not call linkWorkItem since validation failed + expect(mockLinkWorkItem).not.toHaveBeenCalled(); }); }); diff --git a/src/index.js b/src/index.js index fa41214..cfee0a8 100644 --- a/src/index.js +++ b/src/index.js @@ -48,6 +48,18 @@ export async function run() { return; } + // Validate Azure DevOps configuration if linking is enabled + if (linkCommitsToPullRequest) { + if (!azureDevopsOrganization) { + core.setFailed('Azure DevOps organization is required when link-commits-to-pull-request is true'); + return; + } + if (!azureDevopsToken) { + core.setFailed('Azure DevOps token is required when link-commits-to-pull-request is true'); + return; + } + } + const octokit = github.getOctokit(githubToken); // Store work item to commit mapping and validation results