Skip to content
This repository was archived by the owner on May 30, 2026. It is now read-only.
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
7 changes: 7 additions & 0 deletions .changeset/webhook-payload-null-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@savvy-web/github-action-effects": patch
---

## Bug Fixes

- Accept `null` in `WebhookPayload`'s `IssueRef.body`, `IssueRef.html_url`, `Repository.full_name`, and `Repository.html_url` fields. GitHub webhook payloads carry these as `null` (not absent) when the issue or PR has no description, or when an event payload omits the rendered URL. Previously decoding any such payload through `ActionEnvironment.payload` failed with `ActionEnvironmentError: Event payload did not match the expected shape: WebhookPayload — Expected string, actual null`.
2 changes: 1 addition & 1 deletion .github/workflows/pnpm-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
fetch-depth: 0
- name: Setup Runtime
id: runtime
uses: savvy-web/workflow-runtime-action@main
uses: savvy-web/silk-runtime-action@v1
- name: Update Config Dependencies
id: update
uses: savvy-web/pnpm-config-dependency-action@v1
Expand Down
18 changes: 18 additions & 0 deletions src/schemas/EventPayload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ describe("WebhookPayload", () => {
expect(result.repository?.owner.login).toBe("owner");
});

it("decodes a pull_request with null body and null html_url", () => {
const result = decode({
pull_request: { number: 7, body: null, html_url: null },
});
expect(result.pull_request?.number).toBe(7);
expect(result.pull_request?.body).toBeNull();
expect(result.pull_request?.html_url).toBeNull();
});

it("decodes a repository with null full_name and null html_url", () => {
const result = decode({
repository: { name: "repo", full_name: null, html_url: null, owner: { login: "owner" } },
});
expect(result.repository?.name).toBe("repo");
expect(result.repository?.full_name).toBeNull();
expect(result.repository?.html_url).toBeNull();
});

it("decodes an issues payload", () => {
const result = decode({ issue: { number: 12 } });
expect(result.issue?.number).toBe(12);
Expand Down
13 changes: 9 additions & 4 deletions src/schemas/EventPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,25 @@ import { Schema } from "effect";
*/
const Repository = Schema.Struct({
name: Schema.String,
full_name: Schema.optional(Schema.String),
full_name: Schema.optional(Schema.NullOr(Schema.String)),
owner: Schema.Struct({ login: Schema.String }),
html_url: Schema.optional(Schema.String),
html_url: Schema.optional(Schema.NullOr(Schema.String)),
});

/**
* An issue / pull-request reference on a webhook payload (typed subset).
*
* @remarks
* `body` and `html_url` accept `null` because GitHub webhook payloads carry
* those fields as `null` (not absent) when the issue or PR has no description,
* or for events whose payload omits the rendered URL.
*
* @internal
*/
const IssueRef = Schema.Struct({
number: Schema.Number,
html_url: Schema.optional(Schema.String),
body: Schema.optional(Schema.String),
html_url: Schema.optional(Schema.NullOr(Schema.String)),
body: Schema.optional(Schema.NullOr(Schema.String)),
});

/**
Expand Down
2 changes: 1 addition & 1 deletion src/services/Attest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const noopOidcLayer: Layer.Layer<OidcTokenIssuer> = Layer.succeed(OidcTokenIssue

const makeGitHubClientLayer = (
restResponse: { data: unknown } | undefined,
repo = { owner: "savvy-web", repo: "workflow-release-action" },
repo = { owner: "savvy-web", repo: "silk-release-action" },
): Layer.Layer<GitHubClient> =>
GitHubClientTest.layer({
restResponses: restResponse ? new Map([["repos.createAttestation", restResponse]]) : new Map(),
Expand Down
2 changes: 1 addition & 1 deletion src/services/Attest.wrappers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe("Attest.provenance — step 6", () => {

const slsaPredicate = {
buildDefinition: {
buildType: "https://github.com/savvy-web/workflow-release-action/release/v1",
buildType: "https://github.com/savvy-web/silk-release-action/release/v1",
externalParameters: { workflow: { ref: "refs/heads/main" } },
},
runDetails: {
Expand Down
6 changes: 3 additions & 3 deletions src/utils/slsa.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const fullClaims: OidcClaims = {
sha: "deadbeef".repeat(5),
repository: "savvy-web/silk-integration",
event_name: "push",
job_workflow_ref: "savvy-web/workflow-release-action/.github/workflows/release.yml@refs/heads/main",
job_workflow_ref: "savvy-web/silk-release-action/.github/workflows/release.yml@refs/heads/main",
workflow_ref: "savvy-web/silk-integration/.github/workflows/release.yml@refs/heads/main",
repository_id: "123456",
repository_owner_id: "654321",
Expand Down Expand Up @@ -101,7 +101,7 @@ describe("buildSLSAProvenancePredicate — step 8a", () => {
digest: { gitCommit: "deadbeef".repeat(5) },
});
expect(predicate.runDetails.builder.id).toBe(
"https://github.com/savvy-web/workflow-release-action/.github/workflows/release.yml@refs/heads/main",
"https://github.com/savvy-web/silk-release-action/.github/workflows/release.yml@refs/heads/main",
);
expect(predicate.runDetails.metadata.invocationId).toBe(
"https://github.com/savvy-web/silk-integration/actions/runs/987654/attempts/1",
Expand All @@ -128,7 +128,7 @@ describe("buildSLSAProvenancePredicate — step 8a", () => {
"https://github.example.com/savvy-web/silk-integration",
);
expect(predicate.runDetails.builder.id).toBe(
"https://github.example.com/savvy-web/workflow-release-action/.github/workflows/release.yml@refs/heads/main",
"https://github.example.com/savvy-web/silk-release-action/.github/workflows/release.yml@refs/heads/main",
);
});

Expand Down
Loading