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
5 changes: 5 additions & 0 deletions .changeset/red-coats-create.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@changesets/cli": minor
---

Remove confirmation prompt when adding a changeset. It will always add a changeset instead, and if the changeset is not desired, the user can edit or delete the file directly.
100 changes: 0 additions & 100 deletions packages/cli/src/commands/add/__tests__/add.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ const mockUserResponses = (mockResponses: {
return returnValues[callCount++];
});

const confirmAnswers: Record<string, boolean> = {
"Is this your desired changeset?": true,
};

if (
mockResponses.consoleSummaries != null &&
mockResponses.editorSummaries != null
Expand All @@ -75,13 +71,6 @@ const mockUserResponses = (mockResponses: {
} else {
mockedUtils.askQuestion.mockResolvedValue(summary);
}
mockedUtils.askConfirm.mockImplementation(async (question) => {
question = stripVTControlCharacters(question);
if (confirmAnswers[question]) {
return confirmAnswers[question];
}
throw new Error(`An answer could not be found for ${question}`);
});
};

beforeEach(() => {
Expand Down Expand Up @@ -183,18 +172,8 @@ describe("Add command", () => {
const summary = "summary message mock";
mockedUtils.askList.mockResolvedValueOnce("minor");

const confirmAnswers: Record<string, boolean> = {
"Is this your desired changeset?": true,
};
mockedUtils.askQuestion.mockResolvedValue("");
mockedAskWithEditor.mockResolvedValueOnce(summary);
mockedUtils.askConfirm.mockImplementation(async (question) => {
question = stripVTControlCharacters(question);
if (confirmAnswers[question]) {
return confirmAnswers[question];
}
throw new Error(`An answer could not be found for ${question}`);
});

await addChangeset({ cwd });

Expand Down Expand Up @@ -292,7 +271,6 @@ describe("Add command", () => {
});

mockedUtils.askList.mockReturnValueOnce(Promise.resolve("minor"));
mockedUtils.askConfirm.mockReturnValueOnce(Promise.resolve(true));

await addChangeset({ cwd, message: "summary from message" });

Expand All @@ -304,9 +282,6 @@ describe("Add command", () => {
releases: [{ name: "single-package", type: "minor" }],
}),
);
expect(mockedUtils.askConfirm).toHaveBeenCalledWith(
"Is this your desired changeset?",
);
expect(mockedUtils.askQuestion).not.toHaveBeenCalled();
expect(mockedAskWithEditor).not.toHaveBeenCalled();
});
Expand All @@ -322,7 +297,6 @@ describe("Add command", () => {
});

mockedUtils.askList.mockReturnValueOnce(Promise.resolve("patch"));
mockedUtils.askConfirm.mockReturnValueOnce(Promise.resolve(true));

await addChangeset({ cwd, message: "" });

Expand Down Expand Up @@ -368,84 +342,10 @@ describe("Add command", () => {
releases: [{ name: "pkg-a", type: "patch" }],
}),
);
expect(mockedUtils.askConfirm).toHaveBeenCalledWith(
"Is this your desired changeset?",
);
expect(mockedUtils.askQuestion).not.toHaveBeenCalled();
expect(mockedAskWithEditor).not.toHaveBeenCalled();
});

it("should skip confirmation when release type flags and message are passed", async () => {
const cwd = await testdir({
"package.json": JSON.stringify({
private: true,
workspaces: ["packages/*"],
}),
"package-lock.json": "",
"packages/pkg-a/package.json": JSON.stringify({
name: "pkg-a",
version: "1.0.0",
}),
".changeset/config.json": JSON.stringify(defaultConfig),
});

await addChangeset({
cwd,
message: "summary from message",
patch: ["pkg-a"],
});

const changesets = await getChangesets(cwd);
expect(changesets.length).toBe(1);
expect(changesets[0]).toEqual(
expect.objectContaining({
summary: "summary from message",
releases: [{ name: "pkg-a", type: "patch" }],
}),
);
expect(mockedUtils.askConfirm).not.toHaveBeenCalled();
expect(mockedUtils.askQuestion).not.toHaveBeenCalled();
expect(mockedUtils.askMultiselect).not.toHaveBeenCalled();
expect(mockedAskWithEditor).not.toHaveBeenCalled();
});

it("should keep confirmation flow when only release type flags are passed", async () => {
const cwd = await testdir({
"package.json": JSON.stringify({
private: true,
workspaces: ["packages/*"],
}),
"package-lock.json": "",
"packages/pkg-a/package.json": JSON.stringify({
name: "pkg-a",
version: "1.0.0",
}),
".changeset/config.json": JSON.stringify(defaultConfig),
});

mockedUtils.askQuestion.mockResolvedValue("summary from prompt");
mockedUtils.askConfirm.mockResolvedValue(true);

await addChangeset({
cwd,
patch: ["pkg-a"],
});

const changesets = await getChangesets(cwd);
expect(changesets.length).toBe(1);
expect(changesets[0]).toEqual(
expect.objectContaining({
summary: "summary from prompt",
releases: [{ name: "pkg-a", type: "patch" }],
}),
);
expect(mockedUtils.askConfirm).toHaveBeenCalledWith(
"Is this your desired changeset?",
);
expect(mockedUtils.askQuestion).toHaveBeenCalledOnce();
expect(mockedUtils.askMultiselect).not.toHaveBeenCalled();
});

it("should allow using message with empty changesets", async () => {
const cwd = await testdir({
"package.json": JSON.stringify({
Expand Down
20 changes: 3 additions & 17 deletions packages/cli/src/commands/add/createChangeset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,10 @@ export async function createChangeset(
changedPackages: Array<string>,
allPackages: Array<Package>,
optionsFromCli?: OptionsFromCli,
): Promise<{ confirmed: boolean; summary: string; releases: Array<Release> }> {
): Promise<{ summary: string; releases: Array<Release> }> {
const releases: Array<Release> = [];

let confirmed = false;

if (optionsFromCli?.major || optionsFromCli?.minor || optionsFromCli?.patch) {
confirmed = true;
const pkgNames = new Set(
allPackages.map(({ packageJson }) => packageJson.name),
);
Expand Down Expand Up @@ -266,14 +263,11 @@ ${c.gray(patchBumpedPackages.join(", "))}

if (optionsFromCli?.message != null) {
return {
confirmed,
summary: optionsFromCli.message,
releases,
};
}

confirmed = false;

let summary = await cli.askQuestion(
"Please enter a summary for this change (this will be in the changelogs).",
{ placeholder: " (submit nothing to open an external editor)" },
Expand All @@ -285,11 +279,7 @@ ${c.gray(patchBumpedPackages.join(", "))}
"\n\n# Please enter a summary for your changes.\n# An empty message aborts the editor.",
);
if (summary.length > 0) {
return {
confirmed: true,
summary,
releases,
};
return { summary, releases };
}
} catch {
summary = await cli.askQuestion(
Expand All @@ -306,9 +296,5 @@ ${c.gray(patchBumpedPackages.join(", "))}
);
}

return {
confirmed,
summary,
releases,
};
return { summary, releases };
}
115 changes: 52 additions & 63 deletions packages/cli/src/commands/add/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { log } from "@clack/prompts";
import { getPackages } from "@manypkg/get-packages";
import launchEditor from "launch-editor";
import { getCommitFunctions } from "../../commit/getCommitFunctions.ts";
import * as cli from "../../utils/cli-utilities.ts";
import { importantWarning } from "../../utils/cli-utilities.ts";
import { readConfig } from "../../utils/read-config.ts";
import { getVersionableChangedPackages } from "../../utils/versionablePackages.ts";
Expand Down Expand Up @@ -66,7 +65,6 @@ No versionable packages found
let newChangeset: Awaited<ReturnType<typeof createChangeset>>;
if (options?.empty) {
newChangeset = {
confirmed: true,
releases: [],
summary: options?.message ?? "",
};
Expand Down Expand Up @@ -101,82 +99,73 @@ ${(error as Error).toString()}
},
);
printConfirmationMessage(newChangeset, versionablePackages.length > 1);

if (!newChangeset.confirmed) {
newChangeset = {
...newChangeset,
confirmed: await cli.askConfirm("Is this your desired changeset?"),
};
}
}

if (newChangeset.confirmed) {
const changesetID = await writeChangeset(
newChangeset,
const changesetID = await writeChangeset(
newChangeset,
packages.rootDir,
config,
);
const [{ getAddMessage }, commitOpts] = await getCommitFunctions(
config.commit,
packages.rootDir,
path.dirname(fileURLToPath(import.meta.url)),
);

const finalLogMessageLines: string[] = [];

if (getAddMessage) {
await git.add(
path.resolve(changesetBase, `${changesetID}.md`),
packages.rootDir,
config,
);
const [{ getAddMessage }, commitOpts] = await getCommitFunctions(
config.commit,
await git.commit(
await getAddMessage(newChangeset, commitOpts),
packages.rootDir,
path.dirname(fileURLToPath(import.meta.url)),
);

const finalLogMessageLines: string[] = [];

if (getAddMessage) {
await git.add(
path.resolve(changesetBase, `${changesetID}.md`),
packages.rootDir,
);
await git.commit(
await getAddMessage(newChangeset, commitOpts),
packages.rootDir,
);
finalLogMessageLines.push(
c.green(
`${options?.empty ? "Empty " : ""}Changeset added and committed!`,
),
);
} else {
finalLogMessageLines.push(
c.green(
`${options?.empty ? "Empty " : ""}Changeset added - you can now commit it!`,
),
);
}

const hasMajorChange = [...newChangeset.releases].find(
(c) => c.type === "major",
finalLogMessageLines.push(
c.green(
`${options?.empty ? "Empty " : ""}Changeset added and committed!`,
),
);
} else {
finalLogMessageLines.push(
c.green(
`${options?.empty ? "Empty " : ""}Changeset added - you can now commit it!`,
),
);
}

if (hasMajorChange) {
importantWarning(
`
const hasMajorChange = [...newChangeset.releases].find(
(c) => c.type === "major",
);

if (hasMajorChange) {
importantWarning(
`
This Changeset includes a major change and we STRONGLY recommend adding more information to the changeset:
WHAT the breaking change is
WHY the change was made
HOW a consumer should update their code
`,
);
} else {
finalLogMessageLines.push(
c.green(
"If you want to modify or expand on the changeset summary, you can find it here:",
),
);
}

const changesetPath = path.relative(
process.cwd(),
path.join(changesetBase, `${changesetID}.md`),
);
finalLogMessageLines.push(c.blue(changesetPath));
} else {
finalLogMessageLines.push(
c.green(
"If you want to modify or expand on the changeset summary, you can find it here:",
),
);
}

log.success(finalLogMessageLines.join("\n"));
const changesetPath = path.relative(
process.cwd(),
path.join(changesetBase, `${changesetID}.md`),
);
finalLogMessageLines.push(c.blue(changesetPath));

if (options?.open) {
launchEditor(changesetPath);
}
log.success(finalLogMessageLines.join("\n"));

if (options?.open) {
launchEditor(changesetPath);
}
}