Skip to content

Commit bff7852

Browse files
committed
chore(release): add gh checks and include changelogen in the script
1 parent 0b0a3ae commit bff7852

File tree

1 file changed

+93
-28
lines changed

1 file changed

+93
-28
lines changed

scripts/release.mjs

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import fs from "node:fs";
22
import path from "node:path";
33
import { createInterface } from "node:readline/promises";
44
import { execa } from "execa";
5-
import pkg from "../package.json" with { type: "json" };
65

76
// --- Configuration ---
87
const CHANGELOG_PATH = path.resolve(process.cwd(), "CHANGELOG.md");
@@ -52,35 +51,77 @@ async function askForConfirmation(question) {
5251
return answer.toLowerCase().trim();
5352
}
5453

54+
async function checkPrerequisites() {
55+
log.info("Checking prerequisites...");
56+
57+
// Check for gh CLI installation
58+
try {
59+
await execa("gh", ["--version"], { stdio: "ignore" });
60+
} catch (error) {
61+
if (error.code === "ENOENT") {
62+
log.error(
63+
"GitHub CLI (gh) is not installed. Please install it and try again).",
64+
);
65+
process.exit(1);
66+
}
67+
log.error("An unexpected error occurred while checking for gh CLI:");
68+
console.error(error);
69+
process.exit(1);
70+
}
71+
72+
// Check for gh auth status
73+
const authStatus = await execa("gh", ["auth", "status"], { reject: false });
74+
if (authStatus.failed) {
75+
log.error(
76+
'You are not logged into the GitHub CLI. Please run "gh auth login" and try again.',
77+
);
78+
process.exit(1);
79+
}
80+
81+
log.success("Prerequisites met.");
82+
}
83+
5584
// --- Main Script ---
5685
async function main() {
5786
log.info("Starting release process...");
5887
if (DRY_RUN) {
5988
log.info("Running in dry-run mode. No commands will be executed.");
6089
}
6190

62-
// 1. Check for staged changes
63-
const { stdout: stagedFiles } = await execa("git", [
64-
"diff",
65-
"--staged",
66-
"--name-only",
67-
]);
68-
if (!stagedFiles) {
69-
log.error(
70-
"No staged changes to commit. Stage your changes for the release first (e.g., package.json, CHANGELOG.md).",
91+
await checkPrerequisites();
92+
93+
// 1. Run changelogen to bump version and update changelog
94+
log.info("Bumping version and generating changelog with changelogen...");
95+
if (DRY_RUN) {
96+
log.info("Previewing changelog generation...");
97+
// We use execa directly here to bypass the dry-run logic of our `run` helper
98+
// and show a preview of what changelogen will do.
99+
await execa("pnpx", ["changelogen@latest", "--no-output"], {
100+
stdio: "inherit",
101+
});
102+
const continueAnswer = await askForConfirmation(
103+
"Apply file changes and continue with dry-run?",
71104
);
72-
process.exit(1);
105+
if (continueAnswer !== "y" && continueAnswer !== "yes") {
106+
log.info("Dry-run aborted by user.");
107+
process.exit(0);
108+
}
109+
log.info("Applying changelog changes to proceed with dry-run...");
110+
await execa("pnpx", ["changelogen@latest", "--bump"], { stdio: "inherit" });
111+
} else {
112+
await run("pnpx", ["changelogen@latest", "--bump"]);
73113
}
74-
log.info(`Staged files for release: \n${stagedFiles}`);
75114

76-
// 2. Get version and construct tag
115+
// 2. Get version from the updated package.json
116+
const packageJsonPath = path.resolve(process.cwd(), "package.json");
117+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
77118
const { version } = pkg;
78119
if (!version) {
79120
log.error("Could not read version from package.json");
80121
process.exit(1);
81122
}
82123
const tag = `v${version}`;
83-
log.info(`Found version: ${version}`);
124+
log.info(`Version to be released: ${version}`);
84125

85126
// 3. Extract release notes from CHANGELOG.md
86127
log.info("Extracting release notes from CHANGELOG.md...");
@@ -126,28 +167,45 @@ async function main() {
126167
}
127168
log.success("Successfully extracted release notes.");
128169

129-
if (DRY_RUN) {
130-
log.info("--- Extracted Release Notes (for review) ---");
131-
const indentedNotes = releaseNotes
132-
.split("\n")
133-
.map((line) => ` ${line}`)
134-
.join("\n");
135-
console.log(indentedNotes);
136-
log.info("--------------------------------------------");
170+
// 4. Show extracted notes for review
171+
log.info("--- Release Notes (for review) ---");
172+
const indentedNotes = releaseNotes
173+
.split("\n")
174+
.map((line) => ` ${line}`)
175+
.join("\n");
176+
console.log(indentedNotes);
177+
log.info("----------------------------------");
178+
179+
// 5. Ask to continue with commit and tag
180+
if (!DRY_RUN) {
181+
const answer = await askForConfirmation("Proceed with commit and tag?");
182+
if (answer !== "y" && answer !== "yes") {
183+
log.info("Release aborted by user.");
184+
const revertAnswer = await askForConfirmation(
185+
"Revert changes to package.json and CHANGELOG.md?",
186+
);
187+
if (revertAnswer === "y" || revertAnswer === "yes") {
188+
await run("git", ["checkout", "package.json", "CHANGELOG.md"]);
189+
log.success("Changes have been reverted.");
190+
}
191+
process.exit(0);
192+
}
137193
}
138194

139-
// 4. Commit the staged changes
195+
// 6. Stage and commit the changes
196+
log.info("Staging package.json and CHANGELOG.md...");
197+
await run("git", ["add", "package.json", "CHANGELOG.md"]);
140198
const commitMessage = `chore(release): ${tag}`;
141199
log.info(`Committing with message: "${commitMessage}"`);
142200
await run("git", ["commit", "-m", commitMessage]);
143201

144-
// 5. Create an annotated tag
202+
// 7. Create an annotated tag
145203
log.info(`Creating annotated tag: ${tag}`);
146204
await run("git", ["tag", "-a", tag, "-m", tag]);
147205

148206
log.success("Local commit and tag created successfully!");
149207

150-
// 6. Ask to push
208+
// 8. Ask to push
151209
if (!DRY_RUN) {
152210
const answer = await askForConfirmation("Push commit and tags to remote?");
153211
if (answer !== "y" && answer !== "yes") {
@@ -170,13 +228,13 @@ async function main() {
170228
}
171229
}
172230

173-
// 7. Push to remote
231+
// 9. Push to remote
174232
log.info("Pushing commit and tags to remote...");
175233
await run("git", ["push"]);
176234
await run("git", ["push", "--tags"]);
177235
log.success("Commit and tags pushed to remote.");
178236

179-
// 8. Create a GitHub release
237+
// 10. Create a GitHub release
180238
log.info("Creating GitHub release...");
181239
await run("gh", [
182240
"release",
@@ -188,7 +246,14 @@ async function main() {
188246
releaseNotes,
189247
]);
190248

191-
log.success("Release process completed and published successfully!");
249+
if (DRY_RUN) {
250+
await execa("git", ["checkout", "package.json", "CHANGELOG.md"], {
251+
stdio: "inherit",
252+
});
253+
log.success("Dry-run completed and temporary changes have been reverted.");
254+
} else {
255+
log.success("Release process completed and published successfully!");
256+
}
192257
}
193258

194259
main().catch((err) => {

0 commit comments

Comments
 (0)