Skip to content

Fix dispatch_workflow to use PR branch ref and resolve default branch correctly#14062

Merged
pelikhan merged 3 commits intomainfrom
copilot/fix-pr-branch-ref-dispatch
Feb 6, 2026
Merged

Fix dispatch_workflow to use PR branch ref and resolve default branch correctly#14062
pelikhan merged 3 commits intomainfrom
copilot/fix-pr-branch-ref-dispatch

Conversation

Copy link
Contributor

Copilot AI commented Feb 6, 2026

dispatch_workflow was using GITHUB_REF in all contexts, which points to refs/pull/{PR_NUMBER}/merge when 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:
    • Detect PR context via GITHUB_HEAD_REF and construct refs/heads/{branch_name} instead of using merge ref
    • Properly resolve repository default branch by checking context.payload.repository.default_branch first, then falling back to github.rest.repos.get() API, and only using "refs/heads/main" as last resort if API call fails
  • dispatch_workflow.test.cjs:
    • Add test coverage for PR context, non-PR context, and branch names with slashes
    • Add tests for default branch resolution from context and API fallback

Implementation

// Before: Always used GITHUB_REF with hardcoded "main" fallback
const ref = process.env.GITHUB_REF || context.ref || "refs/heads/main";

// After: Detect PR context and properly resolve default branch
const getDefaultBranchRef = async () => {
  // Try to get from context payload first
  if (context.payload.repository?.default_branch) {
    return `refs/heads/${context.payload.repository.default_branch}`;
  }
  
  // Fall back to querying the repository
  try {
    const { data: repoData } = await github.rest.repos.get({
      owner: repo.owner,
      repo: repo.repo,
    });
    return `refs/heads/${repoData.default_branch}`;
  } catch (error) {
    core.warning(`Failed to fetch default branch: ${getErrorMessage(error)}`);
    return "refs/heads/main";
  }
};

let ref;
if (process.env.GITHUB_HEAD_REF) {
  ref = `refs/heads/${process.env.GITHUB_HEAD_REF}`;
} else if (process.env.GITHUB_REF || context.ref) {
  ref = process.env.GITHUB_REF || context.ref;
} else {
  ref = await getDefaultBranchRef();
}

This ensures compatibility with repositories using non-standard default branches (e.g., "develop", "master", "staging") while GitHub Actions sets GITHUB_HEAD_REF only in PR contexts, making it a reliable indicator for PR detection.

Original prompt

Reference: https://github.com/github/gh-aw/actions/runs/21740597360/job/62715358674#step:6:1

When calling dispatch_worflow from a pull request, you need to resolve the PR branch ref and not the merge ref created for the run.

See error in the safe output logs.
Also review the step to checkout the PR branch.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

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>
Copilot AI changed the title [WIP] Fix dispatch workflow branch reference in pull request Fix dispatch_workflow to use PR branch ref instead of merge ref Feb 6, 2026
Copilot AI requested a review from pelikhan February 6, 2026 06:25
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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot you need to resolve the default branch correctly if needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7ed0b69. Now properly resolves the repository's default branch by:

  1. Checking context.payload.repository.default_branch first
  2. Falling back to github.rest.repos.get() API if context unavailable
  3. 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.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

🔍 PR Triage Results

Category: bug | Risk: high | Priority: 53/100

Scores Breakdown

  • Impact: 35/50 - Critical bug fix for dispatch_workflow functionality in PR contexts. Prevents workflow dispatch failures when triggered from PRs. High impact - blocks PR-based workflow automation.
  • Urgency: 10/30 - Very recent PR (< 1 hour old). Fixes workflow dispatch in PR contexts but not yet ready (draft). Age penalty: 0 hours. Low urgency due to draft status.
  • Quality: 8/20 - Draft status with pending CI. Good description and comprehensive test coverage (3 test cases). Touches security-sensitive dispatch code. Needs CI validation and review.

📋 Recommended Action: defer

Draft 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

AI generated by PR Triage Agent

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>
Copilot AI changed the title Fix dispatch_workflow to use PR branch ref instead of merge ref Fix dispatch_workflow to use PR branch ref and resolve default branch correctly Feb 6, 2026
Copilot AI requested a review from pelikhan February 6, 2026 06:37
@pelikhan pelikhan added the smoke label Feb 6, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Changeset Generator failed. Please review the logs for details.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing...

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

🎬 THE ENDSmoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

🌑 The shadows whisper... Smoke Codex failed. The oracle requires further meditation...

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Agent Container Tool Check Results

Tool Status Version/Notes
bash 5.2.21
sh available (basic shell works)
git 2.52.0
jq 1.7
yq 4.50.1
curl 8.5.0
gh 2.86.0
node 20.20.0
python3 3.12.3
go 1.24.12
java binary exists but execution blocked
dotnet binary exists but execution blocked

Result: 10/12 tools fully functional ⚠️

Notes:

  • Java and .NET binaries are present in the container but cannot execute
  • This appears to be a container security restriction or configuration issue
  • Error: "cannot execute dotnet when renamed to bash" suggests a runtime protection mechanism
  • All other development tools (shell, version control, JSON/YAML processing, HTTP, GitHub CLI, Node.js, Python, Go) work correctly

AI generated by Agent Container Smoke Test

@github-actions github-actions bot removed the smoke label Feb 6, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

🔥 Smoke Test Results

PRs Tested:

Results:
✅ GitHub MCP | ✅ Safe Inputs GH | ⚠️ Serena MCP | ✅ Playwright | ✅ File I/O | ✅ Bash | ✅ Discussion Query | ✅ Discussion Comment | ✅ Build | ✅ Workflow Dispatch

Overall: PASS (9/10)

§21741356357

cc @pelikhan @Copilot

AI generated by Smoke Copilot

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@pelikhan pelikhan marked this pull request as ready for review February 6, 2026 06:49
Copilot AI review requested due to automatic review settings February 6, 2026 06:49
@pelikhan pelikhan merged commit 9269b6f into main Feb 6, 2026
164 of 167 checks passed
@pelikhan pelikhan deleted the copilot/fix-pr-branch-ref-dispatch branch February 6, 2026 06:50
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_REF to 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.ref and global.context.payload 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: {},
    });
  });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +370 to +400
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: {},
});
});
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +402 to +443
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: {},
});
});
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants