Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ One of the following, depending on the token type:

- [Create a workflow dispatch event](https://docs.github.com/en/rest/actions/workflows#create-a-workflow-dispatch-event)
- POST `/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches`
- Sent with `return_run_details: true`, so the response carries the new run's ID and URL
- Sent with `X-GitHub-Api-Version: 2026-03-10`, so the response carries the new run's ID and URL
- Requires github.com or GitHub Enterprise Server 3.21+, older servers cannot return the run details

For more information please see [api.ts](./src/api.ts).
Expand Down
851 changes: 586 additions & 265 deletions dist/index.mjs

Large diffs are not rendered by default.

102 changes: 57 additions & 45 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 18 additions & 10 deletions src/api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,16 @@ describe("API", () => {
url: "https://github.com/owner/repo/actions/runs/123456",
});

// The run details are opt-in, so the request must ask for them
expect(dispatchedRequest?.return_run_details).toStrictEqual(true);
// Only the caller's inputs are forwarded, nothing is injected
expect(dispatchedRequest?.inputs).toStrictEqual({ testInput: "test" });
// Only the pinned API version returns the run details, and only the
// caller's inputs are forwarded
expect(dispatchedRequest).toStrictEqual({
owner: "owner",
repo: "repo",
workflow_id: "workflow",
ref: "ref",
inputs: { testInput: "test" },
headers: { "x-github-api-version": "2026-03-10" },
});

// Logging
assertOnlyCalled(coreInfoLogMock);
Expand All @@ -119,7 +125,7 @@ describe("API", () => {
`);
});

it("should throw for an empty 204, as returned by servers without return_run_details support", async () => {
it("should throw for an empty 204, as returned by servers that ignore the API version", async () => {
vi.spyOn(
mockOctokit.rest.actions,
"createWorkflowDispatch",
Expand Down Expand Up @@ -147,11 +153,13 @@ describe("API", () => {
);
});

it("should restate the server requirement for a 400 rejecting the unknown field", async () => {
// Servers predating `return_run_details` reject it outright rather than
// ignoring it, so the empty 204 path is never reached.
it("should restate the server requirement for a 400 rejecting the API version", async () => {
// Servers predating the pinned version reject it outright rather than
// falling back, so the empty 204 path is never reached.
const requestError = Object.assign(
new Error('Unknown request body field: "return_run_details"'),
new Error(
'The version you specified in the "X-GitHub-API-Version" request header, "2026-03-10", is not a supported version.',
),
{ status: 400 },
);
vi.spyOn(
Expand All @@ -165,7 +173,7 @@ describe("API", () => {
);
// The original message is retained for diagnosis
await expect(dispatchWorkflow()).rejects.toThrow(
'Unknown request body field: "return_run_details"',
"is not a supported version",
);

// Logging
Expand Down
31 changes: 17 additions & 14 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ export function init(cfg?: ActionConfig): void {
}

/**
* The 200 response body of the workflow dispatch endpoint, returned when
* `return_run_details` is requested.
* The version that returns the created run. The default `2022-11-28` answers
* 204 with no body and reports the endpoint as deprecated on every request.
*
* Declared locally because `@octokit/openapi-types` still describes this
* endpoint as 204-only, so the response shape cannot be taken from the types
* and is validated at runtime instead.
* Served by github.com and GHES 3.21 or newer.
*
* See: https://docs.github.com/en/rest/about-the-rest-api/api-versions
*/
const API_VERSION = "2026-03-10";

/**
* The 200 response body of the workflow dispatch endpoint.
*
* Declared locally because `@octokit/openapi-types` still types this endpoint
* as 204-only, so the shape is validated at runtime instead.
*
* See: https://docs.github.com/en/rest/actions/workflows#create-a-workflow-dispatch-event--status-codes
*/
Expand Down Expand Up @@ -55,14 +63,12 @@ const RUN_DETAILS_UNSUPPORTED =
"Dispatch did not return the run details, this action requires github.com or GHES >=3.21";

/**
* Servers predating `return_run_details` reject the unknown field with a 400
* rather than ignoring it, so the empty 204 path is never reached on them.
* Restate the requirement, keeping the original message for diagnosis.
* Servers without `API_VERSION` reject the request with a 400 rather than
* falling back, so the empty 204 path is never reached on them. Restate the
* requirement, keeping the original message for diagnosis.
*
* Matching on the status alone is deliberate. The 400 message shape is
* undocumented and may differ between GHES versions.
*
* https://github.com/cli/cli/issues/12672
*/
function asUnsupportedRunDetailsError(error: unknown): Error | undefined {
if (!(error instanceof Error) || !("status" in error)) {
Expand Down Expand Up @@ -93,10 +99,7 @@ export async function dispatchWorkflow(): Promise<DispatchedWorkflowRun> {
workflow_id: config.workflow,
ref: config.ref,
inputs: config.workflowInputs,
// The docs omit `return_run_details`. It is specified only in the OpenAPI
// description, which is what conditions the 200 and 204 responses on it.
// see: https://github.com/github/rest-api-description/tree/main/descriptions/api.github.com
return_run_details: true,
headers: { "x-github-api-version": API_VERSION },
});

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
Expand Down
Loading