Skip to content

Commit cb2c42c

Browse files
authored
fix(ci): Fix release workspace (#7245)
1 parent 7fd5667 commit cb2c42c

File tree

1 file changed

+90
-41
lines changed

1 file changed

+90
-41
lines changed

release_workspace.js

+90-41
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@ const semver = require("semver");
99
const RELEASE_BRANCH = "release";
1010
const MAIN_BRANCH = "main";
1111

12+
/**
13+
* Handles execSync errors and logs them in a readable format.
14+
* @param {string} command
15+
* @param {{ doNotExit?: boolean }} [options] - Optional configuration
16+
* @param {boolean} [options.doNotExit] - Whether or not to exit the process on error
17+
*/
18+
function execSyncWithErrorHandling(command, options = {}) {
19+
try {
20+
execSync(
21+
command,
22+
{ stdio: "inherit" } // This will stream output in real-time
23+
);
24+
} catch (error) {
25+
console.error(error.message);
26+
if (!options.doNotExit) {
27+
process.exit(1);
28+
}
29+
}
30+
}
31+
1232
/**
1333
* Get the version of a workspace inside a directory.
1434
*
@@ -131,33 +151,22 @@ async function runYarnRelease(packageDirectory, npm2FACode, tag) {
131151

132152
console.log(`Running command: "yarn ${args.join(" ")}"`);
133153

134-
const yarnReleaseProcess = spawn("yarn", args, { cwd: workingDirectory });
135-
136-
let stdout = "";
137-
let stderr = "";
138-
139-
yarnReleaseProcess.stdout.on("data", (data) => {
140-
stdout += data;
141-
// Still show output in real-time
142-
process.stdout.write(data);
143-
});
144-
145-
yarnReleaseProcess.stderr.on("data", (data) => {
146-
stderr += data;
147-
// Still show errors in real-time
148-
process.stderr.write(data);
154+
// Use 'inherit' for stdio to allow direct CLI interaction
155+
const yarnReleaseProcess = spawn("yarn", args, {
156+
stdio: "inherit",
157+
cwd: workingDirectory,
149158
});
150159

151160
yarnReleaseProcess.on("close", (code) => {
152161
if (code === 0) {
153162
resolve();
154163
} else {
155-
reject(`Process exited with code ${code}.\nError: ${stderr}`);
164+
reject(`Process exited with code ${code}`);
156165
}
157166
});
158167

159168
yarnReleaseProcess.on("error", (err) => {
160-
reject(`Failed to start process: ${err.message}\nError: ${stderr}`);
169+
reject(`Failed to start process: ${err.message}`);
161170
});
162171
});
163172
}
@@ -194,7 +203,7 @@ function bumpDeps(
194203
console.log(
195204
"Updated version is not greater than the pre-release version. Pulling from github and checking again."
196205
);
197-
execSync(`git pull origin ${RELEASE_BRANCH}`);
206+
execSyncWithErrorHandling(`git pull origin ${RELEASE_BRANCH}`);
198207
updatedWorkspaceVersion = getWorkspaceVersion(workspaceDirectory);
199208
if (!semver.gt(updatedWorkspaceVersion, preReleaseVersion)) {
200209
console.warn(
@@ -213,10 +222,10 @@ function bumpDeps(
213222
versionString = `${updatedWorkspaceVersion}-${tag}`;
214223
}
215224

216-
execSync(`git checkout ${MAIN_BRANCH}`);
225+
execSyncWithErrorHandling(`git checkout ${MAIN_BRANCH}`);
217226
const newBranchName = `bump-${workspaceName}-to-${versionString}`;
218227
console.log(`Checking out new branch: ${newBranchName}`);
219-
execSync(`git checkout -b ${newBranchName}`);
228+
execSyncWithErrorHandling(`git checkout -b ${newBranchName}`);
220229

221230
const allWorkspacesWhichDependOn = allWorkspaces.filter(({ packageJSON }) =>
222231
Object.keys(packageJSON.dependencies ?? {}).includes(workspaceName)
@@ -268,7 +277,7 @@ Workspaces:
268277
console.log("Updated package.json's! Running yarn install.");
269278

270279
try {
271-
execSync(`yarn install`);
280+
execSyncWithErrorHandling(`yarn install`);
272281
} catch (_) {
273282
console.log(
274283
"Yarn install failed. Likely because NPM has not finished publishing the new version. Continuing."
@@ -277,12 +286,12 @@ Workspaces:
277286

278287
// Add all current changes, commit, push and log branch URL.
279288
console.log("Adding and committing all changes.");
280-
execSync(`git add -A`);
281-
execSync(
289+
execSyncWithErrorHandling(`git add -A`);
290+
execSyncWithErrorHandling(
282291
`git commit -m "all[minor]: bump deps on ${workspaceName} to ${versionString}"`
283292
);
284293
console.log("Pushing changes.");
285-
execSync(`git push -u origin ${newBranchName}`);
294+
execSyncWithErrorHandling(`git push -u origin ${newBranchName}`);
286295
console.log(
287296
"🔗 Open %s and merge the bump-deps PR.",
288297
`\x1b[34mhttps://github.com/langchain-ai/langchainjs/compare/${newBranchName}?expand=1\x1b[0m`
@@ -299,23 +308,37 @@ Workspaces:
299308
* @param {string} version
300309
*/
301310
function createCommitMessage(workspaceName, version) {
302-
return `release(${workspaceName}): ${version}`;
311+
const cleanedWorkspaceName = workspaceName.replace("@langchain/", "");
312+
return `release(${cleanedWorkspaceName}): ${version}`;
303313
}
304314

305315
/**
306316
* Commits all changes and pushes to the current branch.
307317
*
308318
* @param {string} workspaceName The name of the workspace being released
309319
* @param {string} version The new version being released
320+
* @param {boolean} onlyPush Whether or not to only push the changes, and not commit
310321
* @returns {void}
311322
*/
312-
function commitAndPushChanges(workspaceName, version) {
313-
console.log("Committing and pushing changes...");
314-
const commitMsg = createCommitMessage(workspaceName, version);
315-
execSync("git add -A");
316-
execSync(`git commit -m "${commitMsg}"`);
323+
function commitAndPushChanges(workspaceName, version, onlyPush) {
324+
if (!onlyPush) {
325+
console.log("Committing changes...");
326+
const commitMsg = createCommitMessage(workspaceName, version);
327+
try {
328+
execSyncWithErrorHandling("git add -A", { doNotExit: true });
329+
execSyncWithErrorHandling(`git commit -m "${commitMsg}"`, {
330+
doNotExit: true,
331+
});
332+
} catch (_) {
333+
// No-op. Likely erroring because there are no unstaged changes.
334+
}
335+
}
336+
337+
console.log("Pushing changes...");
317338
// Pushes to the current branch
318-
execSync("git push -u origin $(git rev-parse --abbrev-ref HEAD)");
339+
execSyncWithErrorHandling(
340+
"git push -u origin $(git rev-parse --abbrev-ref HEAD)"
341+
);
319342
console.log("Successfully committed and pushed changes.");
320343
}
321344

@@ -330,8 +353,8 @@ function checkoutReleaseBranch() {
330353
const currentBranch = execSync("git branch --show-current").toString().trim();
331354
if (currentBranch === MAIN_BRANCH || currentBranch === RELEASE_BRANCH) {
332355
console.log(`Checking out '${RELEASE_BRANCH}' branch.`);
333-
execSync(`git checkout -B ${RELEASE_BRANCH}`);
334-
execSync(`git push -u origin ${RELEASE_BRANCH}`);
356+
execSyncWithErrorHandling(`git checkout -B ${RELEASE_BRANCH}`);
357+
execSyncWithErrorHandling(`git push -u origin ${RELEASE_BRANCH}`);
335358
} else {
336359
throw new Error(
337360
`Current branch is not ${MAIN_BRANCH} or ${RELEASE_BRANCH}. Current branch: ${currentBranch}`
@@ -361,15 +384,34 @@ async function getUserInput(question) {
361384
}
362385

363386
/**
364-
* Checks if there are any uncommitted changes in the git repository
387+
* Checks if there are any uncommitted changes in the git repository.
365388
*
366389
* @returns {boolean} True if there are uncommitted changes, false otherwise
367390
*/
368391
function hasUncommittedChanges() {
369392
try {
370-
// This command returns empty string if no changes, or a string with changes if there are any
371-
const output = execSync("git status --porcelain").toString();
372-
return output.length > 0;
393+
// Check for uncommitted changes (both staged and unstaged)
394+
const uncommittedOutput = execSync("git status --porcelain").toString();
395+
396+
return uncommittedOutput.length > 0;
397+
} catch (error) {
398+
console.error("Error checking git status:", error);
399+
// If we can't check, better to assume there are changes
400+
return true;
401+
}
402+
}
403+
404+
/**
405+
* Checks if there are any staged commits in the git repository.
406+
*
407+
* @returns {boolean} True if there are staged changes, false otherwise
408+
*/
409+
function hasStagedChanges() {
410+
try {
411+
// Check for staged but unpushed changes
412+
const unPushedOutput = execSync("git log '@{u}..'").toString();
413+
414+
return unPushedOutput.length > 0;
373415
} catch (error) {
374416
console.error("Error checking git status:", error);
375417
// If we can't check, better to assume there are changes
@@ -419,7 +461,7 @@ async function main() {
419461

420462
// Run build, lint, tests
421463
console.log("Running build, lint, and tests.");
422-
execSync(
464+
execSyncWithErrorHandling(
423465
`yarn turbo:command run --filter ${options.workspace} build lint test --concurrency 1`
424466
);
425467
console.log("Successfully ran build, lint, and tests.");
@@ -433,9 +475,13 @@ async function main() {
433475
// Run `release-it` on workspace
434476
await runYarnRelease(matchingWorkspace.dir, npm2FACode, options.tag);
435477

436-
if (hasUncommittedChanges()) {
478+
const hasStaged = hasStagedChanges();
479+
const hasUnCommitted = hasUncommittedChanges();
480+
if (hasStaged || hasUnCommitted) {
437481
const updatedVersion = getWorkspaceVersion(matchingWorkspace.dir);
438-
commitAndPushChanges(options.workspace, updatedVersion);
482+
// Only push and do not commit if there are staged changes and no uncommitted changes
483+
const onlyPush = hasStaged && !hasUnCommitted;
484+
commitAndPushChanges(options.workspace, updatedVersion, onlyPush);
439485
}
440486

441487
// Log release branch URL
@@ -458,4 +504,7 @@ async function main() {
458504
}
459505
}
460506

461-
main();
507+
main().catch((error) => {
508+
console.error(error);
509+
process.exit(1);
510+
});

0 commit comments

Comments
 (0)