Fix dispatch_workflow to use PR branch ref and resolve default branch correctly#14062
Fix dispatch_workflow to use PR branch ref and resolve default branch correctly#14062
Conversation
When running in a pull request context, GITHUB_REF points to the merge ref
(refs/pull/{PR_NUMBER}/merge) which is not a valid branch ref for dispatching
workflows. This change detects PR contexts using GITHUB_HEAD_REF and constructs
the proper branch ref (refs/heads/{branch_name}) for workflow dispatch.
Added comprehensive tests for:
- PR context branch ref resolution
- Non-PR context ref usage
- Branch names with slashes
- Proper cleanup of PR environment variables in tests
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
| core.info(`Using PR branch ref: ${ref}`); | ||
| } else { | ||
| // Use GITHUB_REF for non-PR contexts (push, workflow_dispatch, etc.) | ||
| ref = process.env.GITHUB_REF || context.ref || "refs/heads/main"; |
There was a problem hiding this comment.
@copilot you need to resolve the default branch correctly if needed
There was a problem hiding this comment.
Fixed in 7ed0b69. Now properly resolves the repository's default branch by:
- Checking
context.payload.repository.default_branchfirst - Falling back to
github.rest.repos.get()API if context unavailable - Only using "refs/heads/main" as last resort if API call fails
Added tests to verify default branch resolution from both context and API fallback.
🔍 PR Triage ResultsCategory: bug | Risk: high | Priority: 53/100 Scores Breakdown
📋 Recommended Action: deferDraft PR with high risk due to dispatch workflow changes. Defer until marked ready for review and CI passes. Well-tested but needs validation. Triaged by PR Triage Agent on 2026-02-06
|
Instead of hardcoding "refs/heads/main" as fallback, now properly resolves the repository's default branch by: 1. Checking context.payload.repository.default_branch first 2. Falling back to querying github.rest.repos.get() API 3. Only using "refs/heads/main" as last resort if API call fails Added tests for: - Using repository default branch from context when no GITHUB_REF - Falling back to API when context payload is missing - All existing tests continue to pass Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
❌ Changeset Generator failed. Please review the logs for details. |
|
📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing... |
|
🎬 THE END — Smoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨ |
|
🌑 The shadows whisper... Smoke Codex failed. The oracle requires further meditation... |
Agent Container Tool Check Results
Result: 10/12 tools fully functional Notes:
|
🔥 Smoke Test ResultsPRs Tested:
Results: Overall: PASS (9/10) cc @pelikhan
|
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
There was a problem hiding this comment.
Pull request overview
This PR fixes dispatch_workflow to correctly handle branch references in different GitHub Actions contexts, particularly when triggered from pull requests. Previously, the code used GITHUB_REF which points to a merge ref (refs/pull/{PR_NUMBER}/merge) in PR contexts, causing workflow dispatch failures. The PR also improves default branch resolution to properly detect the repository's actual default branch instead of hardcoding "main".
Changes:
- Implemented PR context detection using
GITHUB_HEAD_REFto construct proper branch refs (refs/heads/{branch_name}) - Added cascading default branch resolution: context payload → API call → "refs/heads/main" fallback
- Added comprehensive test coverage for PR contexts, non-PR contexts, branch names with slashes, and default branch resolution scenarios
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| actions/setup/js/dispatch_workflow.cjs | Implements PR branch ref detection using GITHUB_HEAD_REF and multi-level default branch resolution with proper error handling |
| actions/setup/js/dispatch_workflow.test.cjs | Adds test setup for repos.get API mock and 6 new test cases covering PR contexts, branch name edge cases, and default branch resolution paths |
Comments suppressed due to low confidence (1)
actions/setup/js/dispatch_workflow.test.cjs:443
- This test modifies
global.context.refandglobal.context.payloadbut doesn't restore them afterward. This can cause test pollution if subsequent tests rely on the original state. Consider adding anafterEachhook to restore the originalglobal.contextstate or save/restore these values within the test.
it("should fall back to API when context payload is missing", async () => {
delete process.env.GITHUB_REF;
delete process.env.GITHUB_HEAD_REF;
global.context.ref = undefined;
global.context.payload = {};
github.rest.repos.get.mockResolvedValueOnce({
data: {
default_branch: "staging",
},
});
const config = {
workflows: ["test-workflow"],
workflow_files: {
"test-workflow": ".lock.yml",
},
};
const handler = await main(config);
const message = {
type: "dispatch_workflow",
workflow_name: "test-workflow",
inputs: {},
};
await handler(message, {});
// Should fetch default branch from API
expect(github.rest.repos.get).toHaveBeenCalledWith({
owner: "test-owner",
repo: "test-repo",
});
expect(github.rest.actions.createWorkflowDispatch).toHaveBeenCalledWith({
owner: "test-owner",
repo: "test-repo",
workflow_id: "test-workflow.lock.yml",
ref: "refs/heads/staging",
inputs: {},
});
});
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| it("should use repository default branch when no GITHUB_REF is set", async () => { | ||
| delete process.env.GITHUB_REF; | ||
| delete process.env.GITHUB_HEAD_REF; | ||
| global.context.ref = undefined; | ||
| global.context.payload.repository.default_branch = "develop"; | ||
|
|
||
| const config = { | ||
| workflows: ["test-workflow"], | ||
| workflow_files: { | ||
| "test-workflow": ".lock.yml", | ||
| }, | ||
| }; | ||
| const handler = await main(config); | ||
|
|
||
| const message = { | ||
| type: "dispatch_workflow", | ||
| workflow_name: "test-workflow", | ||
| inputs: {}, | ||
| }; | ||
|
|
||
| await handler(message, {}); | ||
|
|
||
| // Should use the repository's default branch from context | ||
| expect(github.rest.actions.createWorkflowDispatch).toHaveBeenCalledWith({ | ||
| owner: "test-owner", | ||
| repo: "test-repo", | ||
| workflow_id: "test-workflow.lock.yml", | ||
| ref: "refs/heads/develop", | ||
| inputs: {}, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
This test modifies global.context.ref and global.context.payload.repository.default_branch but doesn't restore them afterward. This can cause test pollution if subsequent tests rely on the original state. Consider adding an afterEach hook to restore the original global.context state or save/restore these values within the test.
| it("should fall back to API when context payload is missing", async () => { | ||
| delete process.env.GITHUB_REF; | ||
| delete process.env.GITHUB_HEAD_REF; | ||
| global.context.ref = undefined; | ||
| global.context.payload = {}; | ||
|
|
||
| github.rest.repos.get.mockResolvedValueOnce({ | ||
| data: { | ||
| default_branch: "staging", | ||
| }, | ||
| }); | ||
|
|
||
| const config = { | ||
| workflows: ["test-workflow"], | ||
| workflow_files: { | ||
| "test-workflow": ".lock.yml", | ||
| }, | ||
| }; | ||
| const handler = await main(config); | ||
|
|
||
| const message = { | ||
| type: "dispatch_workflow", | ||
| workflow_name: "test-workflow", | ||
| inputs: {}, | ||
| }; | ||
|
|
||
| await handler(message, {}); | ||
|
|
||
| // Should fetch default branch from API | ||
| expect(github.rest.repos.get).toHaveBeenCalledWith({ | ||
| owner: "test-owner", | ||
| repo: "test-repo", | ||
| }); | ||
|
|
||
| expect(github.rest.actions.createWorkflowDispatch).toHaveBeenCalledWith({ | ||
| owner: "test-owner", | ||
| repo: "test-repo", | ||
| workflow_id: "test-workflow.lock.yml", | ||
| ref: "refs/heads/staging", | ||
| inputs: {}, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Missing test coverage for the error handling path when the API call to fetch the default branch fails. The implementation falls back to "refs/heads/main" and logs a warning (lines 54-55 in dispatch_workflow.cjs), but this scenario is not tested. Consider adding a test that mocks github.rest.repos.get to reject with an error and verifies the fallback behavior.
dispatch_workflowwas usingGITHUB_REFin all contexts, which points torefs/pull/{PR_NUMBER}/mergewhen triggered from PRs. The GitHub Actions API rejects merge refs for workflow dispatch, causing failures. Additionally, the fallback logic was hardcoding "main" as the default branch instead of properly resolving the repository's actual default branch.Changes
dispatch_workflow.cjs:GITHUB_HEAD_REFand constructrefs/heads/{branch_name}instead of using merge refcontext.payload.repository.default_branchfirst, then falling back togithub.rest.repos.get()API, and only using "refs/heads/main" as last resort if API call failsdispatch_workflow.test.cjs:Implementation
This ensures compatibility with repositories using non-standard default branches (e.g., "develop", "master", "staging") while GitHub Actions sets
GITHUB_HEAD_REFonly in PR contexts, making it a reliable indicator for PR detection.Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.