Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
10 changes: 10 additions & 0 deletions .changeset/preview-pull-request-title.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@cloudflare/deploy-helpers": minor
"wrangler": minor
---

Add pull request title to `wrangler preview` deployment annotations

`wrangler preview` now also detects the title of the pull/merge request associated with the current CI run (GitHub Actions and GitLab CI, plus a generic `PULL_REQUEST_TITLE` fallback) and attaches it to the preview deployment as the `workers/pull_request_title` annotation, alongside the existing pull request number/URL, repository URL, and commit SHA annotations.

This is best effort: if no pull request title can be detected, nothing changes.
2 changes: 2 additions & 0 deletions packages/deploy-helpers/src/preview/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface DeploymentResource {
"workers/commit_sha"?: string;
"workers/message"?: string;
"workers/pull_request_number"?: string;
"workers/pull_request_title"?: string;
"workers/pull_request_url"?: string;
"workers/repository_url"?: string;
"workers/tag"?: string;
Expand Down Expand Up @@ -112,6 +113,7 @@ export type CreatePreviewDeploymentRequestParams = {
"workers/commit_sha"?: string;
"workers/message"?: string;
"workers/pull_request_number"?: string;
"workers/pull_request_title"?: string;
"workers/pull_request_url"?: string;
"workers/repository_url"?: string;
"workers/tag"?: string;
Expand Down
3 changes: 3 additions & 0 deletions packages/deploy-helpers/src/preview/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,9 @@ async function assemblePreviewDeploymentSettings(
...(pullRequest?.number && {
"workers/pull_request_number": pullRequest.number,
}),
...(pullRequest?.title && {
"workers/pull_request_title": pullRequest.title,
}),
...(pullRequest?.url && { "workers/pull_request_url": pullRequest.url }),
...(repositoryUrl && { "workers/repository_url": repositoryUrl }),
...(options.tag && { "workers/tag": options.tag }),
Expand Down
59 changes: 42 additions & 17 deletions packages/deploy-helpers/src/preview/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,14 @@ export function getRepositoryUrl(): string | undefined {
}

/**
* The pull/merge request number and URL detected from the current CI
* environment. Either field may be missing depending on what the detected
* CI provider exposes.
* The pull/merge request number, URL, and title detected from the current CI
* environment. Any field may be missing depending on what the detected CI
* provider exposes.
*/
export type PullRequestMetadata = {
number?: string;
url?: string;
title?: string;
};

/**
Expand All @@ -197,30 +198,51 @@ function normalizePullRequestNumber(number: string | number | undefined) {
return normalizedNumber ? normalizedNumber : undefined;
}

/**
* Trims a pull/merge request title, treating a blank value the same as a
* missing one.
*/
function normalizePullRequestTitle(
title: string | undefined
): string | undefined {
if (title === undefined) {
return undefined;
}

const trimmedTitle = title.trim();
return trimmedTitle ? trimmedTitle : undefined;
Comment thread
for-the-kidz marked this conversation as resolved.
Outdated
}

/**
* Detects pull request metadata from a GitHub Actions environment.
*
* Prefers the `pull_request` event payload at `GITHUB_EVENT_PATH` (available
* for `pull_request`/`pull_request_target`-triggered workflows), which
* directly provides the PR number and URL. Falls back to parsing the PR
* number out of `GITHUB_REF` (formatted `refs/pull/<number>/merge`) and
* directly provides the PR number, URL, and title. Falls back to parsing the
* PR number out of `GITHUB_REF` (formatted `refs/pull/<number>/merge`) and
* building the URL from `GITHUB_REPOSITORY`/`GITHUB_SERVER_URL`, which covers
* other trigger types where a `pull_request` payload isn't available.
* other trigger types where a `pull_request` payload isn't available — this
* fallback path can't recover a title, since that isn't encoded in the ref.
*/
function getGitHubPullRequestMetadata(): PullRequestMetadata | undefined {
if (process.env.GITHUB_EVENT_PATH) {
try {
const event = JSON.parse(
readFileSync(process.env.GITHUB_EVENT_PATH, "utf8")
) as {
pull_request?: { html_url?: string; number?: number };
pull_request?: {
html_url?: string;
number?: number;
title?: string;
};
};
const number = normalizePullRequestNumber(event.pull_request?.number);
const url = event.pull_request?.html_url
? normalizeRepositoryUrl(event.pull_request.html_url)
: undefined;
if (number || url) {
return { number, url };
const title = normalizePullRequestTitle(event.pull_request?.title);
if (number || url || title) {
return { number, url, title };
}
} catch {
// Fall back to environment-derived metadata below.
Expand All @@ -245,9 +267,9 @@ function getGitHubPullRequestMetadata(): PullRequestMetadata | undefined {

/**
* Detects merge request metadata from a GitLab CI merge request pipeline,
* using `CI_MERGE_REQUEST_IID` for the number and
* using `CI_MERGE_REQUEST_IID` for the number,
* `CI_MERGE_REQUEST_PROJECT_URL` (or `CI_PROJECT_URL` as a fallback) to build
* the merge request URL.
* the merge request URL, and `CI_MERGE_REQUEST_TITLE` for the title.
*/
function getGitLabPullRequestMetadata(): PullRequestMetadata | undefined {
const number = normalizePullRequestNumber(process.env.CI_MERGE_REQUEST_IID);
Expand All @@ -265,16 +287,18 @@ function getGitLabPullRequestMetadata(): PullRequestMetadata | undefined {
url: normalizeRepositoryUrl(
`${normalizedProjectUrl}/-/merge_requests/${number}`
),
title: normalizePullRequestTitle(process.env.CI_MERGE_REQUEST_TITLE),
};
}

/**
* Detects pull request metadata from generic, provider-agnostic env vars
* (`PULL_REQUEST_URL`/`PR_URL`/`CHANGE_URL`/`CIRCLE_PULL_REQUEST` for the URL,
* `PULL_REQUEST_NUMBER`/`PR_NUMBER`/`CHANGE_ID` for the number). These are
* conventions used by some CI providers and custom pipelines, but aren't
* officially documented, so this is a lower-confidence, best-effort fallback
* checked before the provider-specific detectors.
* `PULL_REQUEST_NUMBER`/`PR_NUMBER`/`CHANGE_ID` for the number,
* `PULL_REQUEST_TITLE` for the title). These are conventions used by some CI
* providers and custom pipelines, but aren't officially documented, so this
* is a lower-confidence, best-effort fallback checked before the
* provider-specific detectors.
*/
function getDirectPullRequestMetadata(): PullRequestMetadata | undefined {
const directUrl =
Expand All @@ -288,9 +312,10 @@ function getDirectPullRequestMetadata(): PullRequestMetadata | undefined {
process.env.CHANGE_ID
);
const url = directUrl ? normalizeRepositoryUrl(directUrl) : undefined;
const title = normalizePullRequestTitle(process.env.PULL_REQUEST_TITLE);

if (number || url) {
return { number, url };
if (number || url || title) {
return { number, url, title };
}

return undefined;
Expand Down
61 changes: 61 additions & 0 deletions packages/wrangler/src/__tests__/preview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ function clearPreviewMetadataEnvs() {
vi.stubEnv("CI_COMMIT_SHA", "");
vi.stubEnv("CIRCLE_SHA1", "");
vi.stubEnv("COMMIT_SHA", "");
vi.stubEnv("CI_MERGE_REQUEST_TITLE", "");
vi.stubEnv("PULL_REQUEST_TITLE", "");
}

describe("wrangler preview", () => {
Expand Down Expand Up @@ -388,10 +390,12 @@ describe("wrangler preview", () => {
"https://git.example.com/acme/worker-project/pulls/13"
);
vi.stubEnv("PULL_REQUEST_NUMBER", "13");
vi.stubEnv("PULL_REQUEST_TITLE", "Add a cool new feature");

expect(getPullRequestMetadata()).toEqual({
number: "13",
url: "https://git.example.com/acme/worker-project/pulls/13",
title: "Add a cool new feature",
});
});

Expand All @@ -402,11 +406,45 @@ describe("wrangler preview", () => {
pull_request: {
number: 13,
html_url: "https://github.com/acme/worker-project/pull/13",
title: "Add a cool new feature",
},
})
);
vi.stubEnv("GITHUB_EVENT_PATH", "github-event.json");

expect(getPullRequestMetadata()).toEqual({
number: "13",
url: "https://github.com/acme/worker-project/pull/13",
title: "Add a cool new feature",
});
});

test("should not fail when the GitHub event pull request has no title", ({
expect,
}) => {
writeFileSync(
"github-event.json",
JSON.stringify({
pull_request: {
number: 13,
html_url: "https://github.com/acme/worker-project/pull/13",
},
})
);
vi.stubEnv("GITHUB_EVENT_PATH", "github-event.json");

expect(getPullRequestMetadata()).toEqual({
number: "13",
url: "https://github.com/acme/worker-project/pull/13",
});
});

test("should not recover a title from the GITHUB_REF fallback", ({
expect,
}) => {
vi.stubEnv("GITHUB_REF", "refs/pull/13/merge");
vi.stubEnv("GITHUB_REPOSITORY", "acme/worker-project");

expect(getPullRequestMetadata()).toEqual({
number: "13",
url: "https://github.com/acme/worker-project/pull/13",
Expand All @@ -419,6 +457,24 @@ describe("wrangler preview", () => {
"https://gitlab.example.com/acme/worker-project"
);
vi.stubEnv("CI_MERGE_REQUEST_IID", "13");
vi.stubEnv("CI_MERGE_REQUEST_TITLE", "Add a cool new feature");

expect(getPullRequestMetadata()).toEqual({
number: "13",
url: "https://gitlab.example.com/acme/worker-project/-/merge_requests/13",
title: "Add a cool new feature",
});
});

test("should treat a blank title the same as a missing one", ({
expect,
}) => {
vi.stubEnv(
"CI_PROJECT_URL",
"https://gitlab.example.com/acme/worker-project"
);
vi.stubEnv("CI_MERGE_REQUEST_IID", "13");
vi.stubEnv("CI_MERGE_REQUEST_TITLE", " ");

expect(getPullRequestMetadata()).toEqual({
number: "13",
Expand Down Expand Up @@ -4689,6 +4745,7 @@ describe("wrangler preview", () => {
"https://gitlab.example.com/acme/worker-project.git"
);
vi.stubEnv("CI_MERGE_REQUEST_IID", "13");
vi.stubEnv("CI_MERGE_REQUEST_TITLE", "Add a cool new feature");
vi.stubEnv("CI_COMMIT_SHA", "abc123def456");

let deploymentRequestBody:
Expand All @@ -4697,6 +4754,7 @@ describe("wrangler preview", () => {
"workers/commit_sha"?: string;
"workers/message"?: string;
"workers/pull_request_number"?: string;
"workers/pull_request_title"?: string;
"workers/pull_request_url"?: string;
"workers/repository_url"?: string;
"workers/tag"?: string;
Expand Down Expand Up @@ -4767,6 +4825,7 @@ describe("wrangler preview", () => {
"workers/commit_sha": "abc123def456",
"workers/message": "preview note",
"workers/pull_request_number": "13",
"workers/pull_request_title": "Add a cool new feature",
"workers/pull_request_url":
"https://gitlab.example.com/acme/worker-project/-/merge_requests/13",
"workers/repository_url":
Expand All @@ -4778,6 +4837,8 @@ describe("wrangler preview", () => {
"https://gitlab.example.com/acme/worker-project/-/merge_requests/13"
);
expect(std.out).not.toContain("repository_url");
expect(std.out).not.toContain("pull_request_title");
expect(std.out).not.toContain("Add a cool new feature");
});

test("should fall back to HEAD commit metadata for annotations in CI", async ({
Expand Down
2 changes: 2 additions & 0 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@
"GITHUB_REF",
"CI_MERGE_REQUEST_IID",
"CI_MERGE_REQUEST_PROJECT_URL",
"CI_MERGE_REQUEST_TITLE",
"PULL_REQUEST_URL",
"PR_URL",
"CHANGE_URL",
"CIRCLE_PULL_REQUEST",
"PULL_REQUEST_NUMBER",
"PR_NUMBER",
"CHANGE_ID",
"PULL_REQUEST_TITLE",
"GITHUB_SHA",
"CI_COMMIT_SHA",
"CIRCLE_SHA1",
Expand Down
Loading