Skip to content

Commit 1fe4b7e

Browse files
committed
revise the logic
1 parent e47da9a commit 1fe4b7e

1 file changed

Lines changed: 118 additions & 39 deletions

File tree

scripts/release-orchestrator.js

Lines changed: 118 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -765,14 +765,108 @@ function getAheadBehind(repoDir, branch) {
765765
return { behind, ahead };
766766
}
767767

768+
function resolvePackageInstallSpec(packageName, tag, repoDir, dryRun) {
769+
if (dryRun) {
770+
// Cannot query npm in dry-run; use tag as best-effort
771+
return `${packageName}@${tag}`;
772+
}
773+
774+
let tagVersion = null;
775+
try {
776+
const r = runQuiet(`npm view ${packageName}@${tag} version`, repoDir);
777+
if (r && r.trim()) tagVersion = r.trim();
778+
} catch (err) {
779+
// @tag doesn't exist
780+
}
781+
782+
let latestVersion = null;
783+
try {
784+
const r = runQuiet(`npm view ${packageName}@latest version`, repoDir);
785+
if (r && r.trim()) latestVersion = r.trim();
786+
} catch (err) {
787+
// no @latest
788+
}
789+
790+
if (!tagVersion) {
791+
console.log(` ${packageName}: no @${tag} found → using @latest`);
792+
return `${packageName}@latest`;
793+
}
794+
795+
if (!latestVersion) {
796+
console.log(` ${packageName}: @${tag}=${tagVersion}, no @latest → using @${tag}`);
797+
return `${packageName}@${tagVersion}`;
798+
}
799+
800+
// Extract base X.Y.Z from the test version (strip prerelease suffix)
801+
const tagBaseMatch = tagVersion.match(/^(\d+\.\d+\.\d+)/);
802+
const tagBase = tagBaseMatch ? tagBaseMatch[1] : null;
803+
const cmp = tagBase ? compareBaseSemver(tagBase, latestVersion) : null;
804+
805+
if (cmp !== null && cmp > 0) {
806+
// @test base is strictly newer than @latest → prefer @test
807+
console.log(` ${packageName}: @${tag}=${tagVersion} > @latest=${latestVersion} → using @${tag}`);
808+
return `${packageName}@${tagVersion}`;
809+
}
810+
811+
// @test base is equal to or behind @latest → use @latest
812+
console.log(` ${packageName}: @${tag}=${tagVersion} <= @latest=${latestVersion} → using @latest`);
813+
return `${packageName}@latest`;
814+
}
815+
816+
function runAfterInstallStep(rawCmd, tag, repoDir, dryRun) {
817+
const parts = rawCmd.trim().split(/\s+/);
818+
if (parts[0] !== 'npm' || parts[1] !== 'install') {
819+
run(rawCmd, repoDir, dryRun);
820+
return;
821+
}
822+
823+
const flags = [];
824+
const untaggedPkgs = [];
825+
const taggedItems = [];
826+
827+
for (let i = 2; i < parts.length; i++) {
828+
const part = parts[i];
829+
if (!part) continue;
830+
if (part.startsWith('-')) { flags.push(part); continue; }
831+
832+
const isScoped = part.startsWith('@');
833+
const hasExplicitTag = isScoped
834+
? part.lastIndexOf('@') > part.indexOf('/')
835+
: part.includes('@');
836+
837+
if (hasExplicitTag) {
838+
taggedItems.push(part);
839+
} else {
840+
untaggedPkgs.push(part);
841+
}
842+
}
843+
844+
const flagStr = flags.length > 0 ? `${flags.join(' ')} ` : '';
845+
846+
// Resolve each untagged package individually via npm registry
847+
const resolvedSpecs = [];
848+
if (untaggedPkgs.length > 0) {
849+
console.log(`Resolving afterInstall versions (tag=@${tag}):`);
850+
for (const pkg of untaggedPkgs) {
851+
resolvedSpecs.push(resolvePackageInstallSpec(pkg, tag, repoDir, dryRun));
852+
}
853+
}
854+
855+
// Build one combined install command with precise version specs
856+
const allSpecs = [...resolvedSpecs, ...taggedItems];
857+
if (allSpecs.length > 0) {
858+
run(`npm install ${flagStr}${allSpecs.join(' ')}`.trim(), repoDir, dryRun);
859+
}
860+
}
861+
768862
function runSteps(steps, repoDir, dryRun, tag = null) {
769863
if (!steps || !Array.isArray(steps)) return;
770-
for (let cmd of steps) {
771-
// Inject npm tag into npm install commands if tag is provided
772-
if (tag && cmd.startsWith('npm install')) {
773-
cmd = parseNpmInstallCmd(cmd, tag);
864+
for (const rawCmd of steps) {
865+
if (tag && rawCmd.trim().startsWith('npm install')) {
866+
runAfterInstallStep(rawCmd, tag, repoDir, dryRun);
867+
} else {
868+
run(rawCmd, repoDir, dryRun);
774869
}
775-
run(cmd, repoDir, dryRun);
776870
}
777871
}
778872

@@ -862,14 +956,13 @@ function buildLockedVersion(installedVersion, prefix) {
862956
return `${normalizedPrefix}${installedVersion}`;
863957
}
864958

865-
function collectAfterInstallPackageNames(repo, npmTag) {
959+
function collectAfterInstallPackageNames(repo) {
866960
const commands = Array.isArray(repo.afterInstall) ? repo.afterInstall : [];
867961
const packageNames = new Set();
868962

869963
for (const rawCmd of commands) {
870-
const installCmd = parseNpmInstallCmd(rawCmd, npmTag);
871-
const firstSegment = installCmd.split('||')[0].trim();
872-
const parsedPackages = extractNpmInstallPackages(firstSegment);
964+
// Parse package names from the raw command — no tag injection needed for name extraction
965+
const parsedPackages = extractNpmInstallPackages(rawCmd);
873966
for (const pkgName of parsedPackages) {
874967
packageNames.add(pkgName);
875968
}
@@ -892,8 +985,7 @@ function getDeclaredPackageSpecs(packageJson, packageName) {
892985
}
893986

894987
function logBuildDependencyVersions(repoDir, repo, modeConfig) {
895-
const npmTag = modeConfig.npmTag || 'latest';
896-
const packageNames = collectAfterInstallPackageNames(repo, npmTag);
988+
const packageNames = collectAfterInstallPackageNames(repo);
897989
if (packageNames.length === 0) return;
898990

899991
const pkg = getPackageJson(repoDir);
@@ -910,8 +1002,7 @@ function logBuildDependencyVersions(repoDir, repo, modeConfig) {
9101002
}
9111003

9121004
function lockStableDependencyVersions(repoDir, repo, config, modeConfig, dryRun) {
913-
const npmTag = modeConfig.npmTag || 'latest';
914-
const packageNames = collectAfterInstallPackageNames(repo, npmTag);
1005+
const packageNames = collectAfterInstallPackageNames(repo);
9151006

9161007
if (packageNames.length === 0) {
9171008
return;
@@ -980,40 +1071,28 @@ function lockStableDependencyVersions(repoDir, repo, config, modeConfig, dryRun)
9801071
}
9811072

9821073
function parseNpmInstallCmd(cmd, tag) {
1074+
// Simple tag injection — used for package-name extraction in lock/log helpers.
1075+
// Actual execution goes through runAfterInstallStep → resolvePackageInstallSpec.
9831076
const parts = cmd.split(/\s+/);
9841077
if (parts[0] !== 'npm' || parts[1] !== 'install') {
985-
return cmd; // Not an npm install command
1078+
return cmd;
9861079
}
9871080

9881081
const result = ['npm', 'install'];
989-
const fallback = ['npm', 'install'];
990-
9911082
for (let i = 2; i < parts.length; i++) {
9921083
const part = parts[i];
993-
994-
// Keep flags, option values, and packages with existing tags as-is
995-
if (part.startsWith('-') || part.startsWith('@') || part.includes('@')) {
996-
result.push(part);
997-
fallback.push(part);
998-
} else if (part === '') {
999-
// Skip empty strings
1000-
continue;
1001-
} else {
1002-
// This is a package name, add tag
1003-
result.push(`${part}@${tag}`);
1004-
fallback.push(`${part}@latest`);
1005-
}
1006-
}
1007-
1008-
const mainCmd = result.join(' ');
1009-
1010-
// For test mode, add fallback to @latest if @test doesn't exist
1011-
if (tag === 'test') {
1012-
const fallbackCmd = fallback.join(' ');
1013-
return `${mainCmd} || ${fallbackCmd}`;
1084+
if (!part) continue;
1085+
if (part.startsWith('-')) { result.push(part); continue; }
1086+
1087+
const isScoped = part.startsWith('@');
1088+
const hasExplicitTag = isScoped
1089+
? part.lastIndexOf('@') > part.indexOf('/')
1090+
: part.includes('@');
1091+
1092+
result.push(hasExplicitTag ? part : `${part}@${tag}`);
10141093
}
1015-
1016-
return mainCmd;
1094+
1095+
return result.join(' ');
10171096
}
10181097

10191098
function packageVersionExists(name, version, repoDir) {

0 commit comments

Comments
 (0)