Skip to content
Open
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
66 changes: 66 additions & 0 deletions src/run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,4 +487,70 @@ fluminis divesque vulnere aquis parce lapsis rabie si visa fulmineis.

expect(mockedGraphql.mock.calls[0]).toMatchSnapshot();
});

it.each([true, false])(
'uses consumer given PR Body instead of default, also respects to PacakgesInfo included "%s"',
async (hasPacakgesInfo) => {
await using fixture = await createSimpleProjectFixture();
const cwd = fixture.path;

mockedGithubMethods.pulls.list.mockImplementationOnce(() => ({
data: [],
}));

mockedGithubMethods.pulls.create.mockImplementationOnce(() => ({
data: { number: 123 },
}));

await writeChangesets(
[
{
releases: [
{
name: "changesets-dev-simple-project-pkg-a",
type: "minor",
},
{
name: "changesets-dev-simple-project-pkg-b",
type: "minor",
},
],
summary: "Awesome feature",
},
],
cwd,
);

await runVersion({
github: createGithub(cwd),
prBody: hasPacakgesInfo
? "THIS_AND_THAT \n{PACKAGES_INFO}"
: "THIS_AND_THAT",
cwd,
});

if (hasPacakgesInfo) {
const prBody: string = mockedGithubMethods.pulls.create.mock.calls
.at(0)
?.at(0).body;

expect(prBody.startsWith("THIS_AND_THAT \n")).toBe(true);
expect(prBody).toContain("changesets-dev-simple-project-pkg-a");
expect(prBody).toContain("changesets-dev-simple-project-pkg-b");
expect(prBody).toContain("Awesome feature");
} else {
expect(
mockedGithubMethods.pulls.create.mock.calls.at(0)?.at(0),
).toStrictEqual({
base: "some-branch",
body: "THIS_AND_THAT",
draft: false,
head: "changeset-release/some-branch",
owner: "changesets",
repo: "action",
title: "Version Packages",
});
}
},
);
});
95 changes: 43 additions & 52 deletions src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ import {
type ExecOutput,
} from "@actions/exec";
import { context } from "@actions/github";
import type { PreState } from "@changesets/types";
import { type Package, getPackages } from "@manypkg/get-packages";
import type { GitHub } from "./github.ts";
import type { Octokit } from "./octokit.ts";
import readChangesetState from "./readChangesetState.ts";
import type {
GetVersionPrBodyProps,
RunVersionProps,
RunVersionResult,
} from "./run.types.ts";
import {
execChangesetsCli,
getChangedPackages,
Expand All @@ -23,6 +27,8 @@ import {
getVersionsByDirectory,
isErrorWithCode,
sortTheThings,
isObject,
isString,
} from "./utils.ts";

// GitHub Issues/PRs messages have a max size limit on the
Expand Down Expand Up @@ -92,10 +98,6 @@ type PublishResult =
exitCode: number;
};

function isObject(value: unknown) {
return typeof value === "object" && value !== null;
}

function isChangesetsOutputEvent(
value: unknown,
): value is ChangesetsOutputEvent {
Expand Down Expand Up @@ -259,33 +261,33 @@ export async function runPublish({
return { published: false, exitCode: changesetPublishOutput.exitCode };
}

type GetMessageOptions = {
hasPublishScript: boolean;
branch: string;
changedPackagesInfo: {
highestLevel: number;
private: boolean;
content: string;
header: string;
}[];
prBodyMaxCharacters: number;
preState?: PreState;
};

export async function getVersionPrBody({
hasPublishScript,
preState,
changedPackagesInfo,
prBody,
prBodyMaxCharacters,
branch,
}: GetMessageOptions) {
}: GetVersionPrBodyProps): Promise<string> {
if (isString(prBody)) {
const packagesInfo: string = changedPackagesInfo
.map(({ header, content }) => `${header}\n\n${content}`)
.join("\n");

const prBodyText = prBody.replace("{PACKAGES_INFO}", packagesInfo);

return prBodyText.length > prBodyMaxCharacters
? "This PR Body content exceeds the size limit."
: prBodyText;
}

let messageHeader = `This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and ${
hasPublishScript
? `the packages will be published to npm automatically`
: `publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing)`
}. If you're not ready to do a release yet, that's fine, whenever you add more changesets to ${branch}, this PR will be updated.
`;
let messagePrestate = !!preState
let messagePrestate = isObject(preState)
? `⚠️⚠️⚠️⚠️⚠️⚠️

\`${branch}\` is currently in **pre mode** so this branch has prereleases rather than normal releases. If you want to exit prereleases, run \`changeset pre exit\` on \`${branch}\`.
Expand Down Expand Up @@ -328,41 +330,26 @@ export async function getVersionPrBody({
return fullMessage;
}

type VersionOptions = {
script?: string;
github: GitHub;
cwd?: string;
prTitle?: string;
commitMessage?: string;
hasPublishScript?: boolean;
prBodyMaxCharacters?: number;
prDraft?: "always" | "create";
branch?: string;
};

type RunVersionResult = {
pullRequestNumber: number;
};

export async function runVersion({
script,
github,
cwd = process.cwd(),
prTitle = "Version Packages",
prBody = undefined,
commitMessage = "Version Packages",
hasPublishScript = false,
prBodyMaxCharacters = MAX_CHARACTERS_PER_MESSAGE,
branch = context.ref.replace("refs/heads/", ""),
prDraft,
}: VersionOptions): Promise<RunVersionResult> {
}: RunVersionProps): Promise<RunVersionResult> {
const { octokit } = github;
let versionBranch = `changeset-release/${branch}`;
const versionBranch = `changeset-release/${branch}`;

let { preState } = await readChangesetState(cwd);
const { preState } = await readChangesetState(cwd);

await github.prepareBranch(versionBranch);

let versionsByDirectory = await getVersionsByDirectory(cwd);
const versionsByDirectory = await getVersionsByDirectory(cwd);

const env = { ...process.env, GITHUB_TOKEN: github.getToken() };

Expand All @@ -372,15 +359,18 @@ export async function runVersion({
await execChangesetsCli(["version"], { cwd, env });
}

let changedPackages = await getChangedPackages(cwd, versionsByDirectory);
let changedPackagesInfoPromises = Promise.all(
const changedPackages = await getChangedPackages(cwd, versionsByDirectory);
const changedPackagesInfoPromises = Promise.all(
changedPackages.map(async (pkg) => {
let changelogContents = await fs.readFile(
const changelogContents = await fs.readFile(
path.join(pkg.dir, "CHANGELOG.md"),
"utf8",
);

let entry = getChangelogEntry(changelogContents, pkg.packageJson.version);
const entry = getChangelogEntry(
changelogContents,
pkg.packageJson.version,
);
return {
highestLevel: entry.highestLevel,
private: !!pkg.packageJson.private,
Expand All @@ -390,10 +380,10 @@ export async function runVersion({
}),
);

const finalPrTitle = `${prTitle}${!!preState ? ` (${preState.tag})` : ""}`;
const finalCommitMessage = `${commitMessage}${
!!preState ? ` (${preState.tag})` : ""
}`;
const isPreState = isObject(preState);
const finalPreState = isPreState ? ` (${preState.tag})` : "";
const finalPrTitle = `${prTitle}${finalPreState}`;
const finalCommitMessage = `${commitMessage}${finalPreState}`;

const existingPullRequests = await octokit.rest.pulls.list({
...context.repo,
Expand All @@ -415,14 +405,15 @@ export async function runVersion({
});

const changedPackagesInfo = (await changedPackagesInfoPromises)
.filter((x) => x)
.filter(Boolean)
.sort(sortTheThings);

let prBody = await getVersionPrBody({
const finalPrBody = await getVersionPrBody({
hasPublishScript,
preState,
branch,
changedPackagesInfo,
prBody,
prBodyMaxCharacters,
});

Expand All @@ -432,7 +423,7 @@ export async function runVersion({
base: branch,
head: versionBranch,
title: finalPrTitle,
body: prBody,
body: finalPrBody,
draft: prDraft !== undefined,
...context.repo,
});
Expand Down Expand Up @@ -483,7 +474,7 @@ export async function runVersion({
await octokit.graphql(updatePullRequestMutation, {
pullRequestId: pullRequest.node_id,
title: finalPrTitle,
body: prBody,
body: finalPrBody,
});

return {
Expand Down
33 changes: 33 additions & 0 deletions src/run.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { PreState } from "@changesets/types";
import type { GitHub } from "./github.ts";

export type GetVersionPrBodyProps = {
hasPublishScript: boolean;
branch: string;
changedPackagesInfo: {
highestLevel: number;
private: boolean;
content: string;
header: string;
}[];
prBody?: string;
prBodyMaxCharacters: number;
preState?: PreState;
};

export type RunVersionProps = {
script?: string;
github: GitHub;
cwd?: string;
prTitle?: string;
prBody?: string;
commitMessage?: string;
hasPublishScript?: boolean;
prBodyMaxCharacters?: number;
prDraft?: "always" | "create";
branch?: string;
};

export type RunVersionResult = {
pullRequestNumber: number;
};
72 changes: 72 additions & 0 deletions src/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
sortTheThings,
throwOnRemovedCommitModeInput,
validateChangesetsCliVersion,
isObject,
isString,
} from "./utils.ts";

let changelog = `# @keystone-alpha/email
Expand Down Expand Up @@ -170,3 +172,73 @@ test("throws when the project has Changesets CLI v2 installed", async () => {
"This version of the Changesets action is designed to work with Changesets CLI v3. Changesets CLI v2 is not supported; use Changesets action v1 instead, which is compatible with CLI v2.",
);
});

test.each([
{
input: null,
expected: false,
},
{
input: undefined,
expected: false,
},
{
input: "changesets",
expected: false,
},
{
input: 1,
expected: false,
},
{
input: true,
expected: false,
},
{
input: [],
expected: false,
},
{
input: {},
expected: true,
},
])("isObject: $input should return $expected", ({ input, expected }) => {
expect(isObject(input)).toBe(expected);
});

test.each([
{
input: null,
expected: false,
},
{
input: undefined,
expected: false,
},
{
input: "changesets",
expected: true,
},
{
input: 1,
expected: false,
},
{
input: true,
expected: false,
},
{
input: [],
expected: false,
},
{
input: {},
expected: false,
},
{
input: () => "test",
expected: false,
},
])("isString: $input should return $expected", ({ input, expected }) => {
expect(isString(input)).toBe(expected);
});
Loading