relizy 0.2.8 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -1093
- package/dist/cli.mjs +18 -3
- package/dist/index.d.mts +415 -14
- package/dist/index.d.ts +415 -14
- package/dist/index.mjs +2 -2
- package/dist/shared/{relizy.DwP99Z7x.mjs → relizy.C2HlcnZB.mjs} +2194 -1449
- package/package.json +21 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { logger, execPromise } from '@maz-ui/node';
|
|
2
|
+
import process$1 from 'node:process';
|
|
2
3
|
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
4
|
import path, { join, relative } from 'node:path';
|
|
4
5
|
import { getGitDiff, parseCommits, formatCompareChanges, formatReference, resolveRepoConfig, getRepoConfig, createGithubRelease } from 'changelogen';
|
|
@@ -7,7 +8,6 @@ import { confirm, input } from '@inquirer/prompts';
|
|
|
7
8
|
import { upperFirst, formatJson } from '@maz-ui/utils';
|
|
8
9
|
import * as semver from 'semver';
|
|
9
10
|
import { execSync } from 'node:child_process';
|
|
10
|
-
import process$1, { exit } from 'node:process';
|
|
11
11
|
import { setupDotenv, loadConfig } from 'c12';
|
|
12
12
|
import { defu } from 'defu';
|
|
13
13
|
import { convert } from 'convert-gitmoji';
|
|
@@ -276,11 +276,11 @@ function getPackageReleaseType({
|
|
|
276
276
|
});
|
|
277
277
|
}
|
|
278
278
|
async function getPackages({
|
|
279
|
-
patterns,
|
|
280
279
|
config,
|
|
281
280
|
suffix,
|
|
282
281
|
force
|
|
283
282
|
}) {
|
|
283
|
+
const patterns = config.monorepo?.packages;
|
|
284
284
|
const readedPackages = readPackages({
|
|
285
285
|
cwd: config.cwd,
|
|
286
286
|
patterns,
|
|
@@ -445,14 +445,13 @@ async function getPackageCommits({
|
|
|
445
445
|
if (!isAllowedCommit({ commit, type, changelog })) {
|
|
446
446
|
return false;
|
|
447
447
|
}
|
|
448
|
-
const
|
|
449
|
-
if ((pkg.path === changelogConfig.cwd || pkg.name === rootPackage.name) &&
|
|
448
|
+
const isTrackedPackage = isCommitOfTrackedPackages({ commit, config });
|
|
449
|
+
if ((pkg.path === changelogConfig.cwd || pkg.name === rootPackage.name) && isTrackedPackage) {
|
|
450
450
|
return true;
|
|
451
451
|
}
|
|
452
452
|
const packageRelativePath = relative(changelogConfig.cwd, pkg.path);
|
|
453
|
-
const scopeMatches = commit.scope === pkg.name;
|
|
454
453
|
const bodyContainsPath = commit.body.includes(packageRelativePath);
|
|
455
|
-
return
|
|
454
|
+
return bodyContainsPath && isTrackedPackage;
|
|
456
455
|
});
|
|
457
456
|
logger.debug(`Found ${commits.length} commit(s) for ${pkg.name} from ${from} to ${to}`);
|
|
458
457
|
if (commits.length > 0) {
|
|
@@ -477,7 +476,7 @@ async function executeHook(hook, config, dryRun, params) {
|
|
|
477
476
|
logger.info(`Executing hook ${hook}`);
|
|
478
477
|
const result = await hookInput(config, dryRun, params);
|
|
479
478
|
if (result)
|
|
480
|
-
logger.debug(`Hook ${hook} returned: ${result}`);
|
|
479
|
+
logger.debug(`Hook ${hook} returned: ${typeof result === "object" ? JSON.stringify(result) : result}`);
|
|
481
480
|
logger.info(`Hook ${hook} executed`);
|
|
482
481
|
return result;
|
|
483
482
|
}
|
|
@@ -490,62 +489,62 @@ async function executeHook(hook, config, dryRun, params) {
|
|
|
490
489
|
noStdout: true
|
|
491
490
|
});
|
|
492
491
|
if (result)
|
|
493
|
-
logger.debug(`Hook ${hook} returned: ${result}`);
|
|
492
|
+
logger.debug(`Hook ${hook} returned: ${typeof result === "object" ? JSON.stringify(result) : result}`);
|
|
494
493
|
logger.info(`Hook ${hook} executed`);
|
|
495
494
|
return result;
|
|
496
495
|
}
|
|
497
496
|
}
|
|
498
497
|
function isInCI() {
|
|
499
498
|
return Boolean(
|
|
500
|
-
process.env.CI === "true" || process.env.CONTINUOUS_INTEGRATION === "true" || process.env.GITHUB_ACTIONS === "true" || process.env.GITHUB_WORKFLOW || process.env.GITLAB_CI === "true" || process.env.CIRCLECI === "true" || process.env.TRAVIS === "true" || process.env.JENKINS_HOME || process.env.JENKINS_URL || process.env.BUILD_ID || process.env.TF_BUILD === "True" || process.env.AZURE_PIPELINES === "true" || process.env.TEAMCITY_VERSION || process.env.BITBUCKET_BUILD_NUMBER || process.env.DRONE === "true" || process.env.APPVEYOR === "True" || process.env.APPVEYOR === "true" || process.env.BUILDKITE === "true" || process.env.CODEBUILD_BUILD_ID || process.env.NETLIFY === "true" || process.env.VERCEL === "1" || process.env.HEROKU_TEST_RUN_ID || process.env.BUDDY === "true" || process.env.SEMAPHORE === "true" || process.env.CF_BUILD_ID || process.env.bamboo_buildKey || process.env.BUILD_ID && process.env.PROJECT_ID || process.env.SCREWDRIVER === "true" || process.env.STRIDER === "true"
|
|
499
|
+
process$1.env.CI === "true" || process$1.env.CONTINUOUS_INTEGRATION === "true" || process$1.env.GITHUB_ACTIONS === "true" || process$1.env.GITHUB_WORKFLOW || process$1.env.GITLAB_CI === "true" || process$1.env.CIRCLECI === "true" || process$1.env.TRAVIS === "true" || process$1.env.JENKINS_HOME || process$1.env.JENKINS_URL || process$1.env.BUILD_ID || process$1.env.TF_BUILD === "True" || process$1.env.AZURE_PIPELINES === "true" || process$1.env.TEAMCITY_VERSION || process$1.env.BITBUCKET_BUILD_NUMBER || process$1.env.DRONE === "true" || process$1.env.APPVEYOR === "True" || process$1.env.APPVEYOR === "true" || process$1.env.BUILDKITE === "true" || process$1.env.CODEBUILD_BUILD_ID || process$1.env.NETLIFY === "true" || process$1.env.VERCEL === "1" || process$1.env.HEROKU_TEST_RUN_ID || process$1.env.BUDDY === "true" || process$1.env.SEMAPHORE === "true" || process$1.env.CF_BUILD_ID || process$1.env.bamboo_buildKey || process$1.env.BUILD_ID && process$1.env.PROJECT_ID || process$1.env.SCREWDRIVER === "true" || process$1.env.STRIDER === "true"
|
|
501
500
|
);
|
|
502
501
|
}
|
|
503
502
|
function getCIName() {
|
|
504
|
-
if (process.env.GITHUB_ACTIONS === "true")
|
|
503
|
+
if (process$1.env.GITHUB_ACTIONS === "true")
|
|
505
504
|
return "GitHub Actions";
|
|
506
|
-
if (process.env.GITLAB_CI === "true")
|
|
505
|
+
if (process$1.env.GITLAB_CI === "true")
|
|
507
506
|
return "GitLab CI";
|
|
508
|
-
if (process.env.CIRCLECI === "true")
|
|
507
|
+
if (process$1.env.CIRCLECI === "true")
|
|
509
508
|
return "CircleCI";
|
|
510
|
-
if (process.env.TRAVIS === "true")
|
|
509
|
+
if (process$1.env.TRAVIS === "true")
|
|
511
510
|
return "Travis CI";
|
|
512
|
-
if (process.env.JENKINS_HOME || process.env.JENKINS_URL)
|
|
511
|
+
if (process$1.env.JENKINS_HOME || process$1.env.JENKINS_URL)
|
|
513
512
|
return "Jenkins";
|
|
514
|
-
if (process.env.TF_BUILD === "True")
|
|
513
|
+
if (process$1.env.TF_BUILD === "True")
|
|
515
514
|
return "Azure Pipelines";
|
|
516
|
-
if (process.env.TEAMCITY_VERSION)
|
|
515
|
+
if (process$1.env.TEAMCITY_VERSION)
|
|
517
516
|
return "TeamCity";
|
|
518
|
-
if (process.env.BITBUCKET_BUILD_NUMBER)
|
|
517
|
+
if (process$1.env.BITBUCKET_BUILD_NUMBER)
|
|
519
518
|
return "Bitbucket Pipelines";
|
|
520
|
-
if (process.env.DRONE === "true")
|
|
519
|
+
if (process$1.env.DRONE === "true")
|
|
521
520
|
return "Drone";
|
|
522
|
-
if (process.env.APPVEYOR)
|
|
521
|
+
if (process$1.env.APPVEYOR)
|
|
523
522
|
return "AppVeyor";
|
|
524
|
-
if (process.env.BUILDKITE === "true")
|
|
523
|
+
if (process$1.env.BUILDKITE === "true")
|
|
525
524
|
return "Buildkite";
|
|
526
|
-
if (process.env.CODEBUILD_BUILD_ID)
|
|
525
|
+
if (process$1.env.CODEBUILD_BUILD_ID)
|
|
527
526
|
return "AWS CodeBuild";
|
|
528
|
-
if (process.env.NETLIFY === "true")
|
|
527
|
+
if (process$1.env.NETLIFY === "true")
|
|
529
528
|
return "Netlify";
|
|
530
|
-
if (process.env.VERCEL === "1")
|
|
529
|
+
if (process$1.env.VERCEL === "1")
|
|
531
530
|
return "Vercel";
|
|
532
|
-
if (process.env.HEROKU_TEST_RUN_ID)
|
|
531
|
+
if (process$1.env.HEROKU_TEST_RUN_ID)
|
|
533
532
|
return "Heroku CI";
|
|
534
|
-
if (process.env.BUDDY === "true")
|
|
533
|
+
if (process$1.env.BUDDY === "true")
|
|
535
534
|
return "Buddy";
|
|
536
|
-
if (process.env.SEMAPHORE === "true")
|
|
535
|
+
if (process$1.env.SEMAPHORE === "true")
|
|
537
536
|
return "Semaphore";
|
|
538
|
-
if (process.env.CF_BUILD_ID)
|
|
537
|
+
if (process$1.env.CF_BUILD_ID)
|
|
539
538
|
return "Codefresh";
|
|
540
|
-
if (process.env.bamboo_buildKey)
|
|
539
|
+
if (process$1.env.bamboo_buildKey)
|
|
541
540
|
return "Bamboo";
|
|
542
|
-
if (process.env.BUILD_ID && process.env.PROJECT_ID)
|
|
541
|
+
if (process$1.env.BUILD_ID && process$1.env.PROJECT_ID)
|
|
543
542
|
return "Google Cloud Build";
|
|
544
|
-
if (process.env.SCREWDRIVER === "true")
|
|
543
|
+
if (process$1.env.SCREWDRIVER === "true")
|
|
545
544
|
return "Screwdriver";
|
|
546
|
-
if (process.env.STRIDER === "true")
|
|
545
|
+
if (process$1.env.STRIDER === "true")
|
|
547
546
|
return "Strider";
|
|
548
|
-
if (process.env.CI === "true")
|
|
547
|
+
if (process$1.env.CI === "true")
|
|
549
548
|
return "Unknown CI";
|
|
550
549
|
return null;
|
|
551
550
|
}
|
|
@@ -611,17 +610,19 @@ async function getPackagesOrBumpedPackages({
|
|
|
611
610
|
}
|
|
612
611
|
return await getPackages({
|
|
613
612
|
config,
|
|
614
|
-
patterns: config.monorepo?.packages,
|
|
615
613
|
suffix,
|
|
616
614
|
force
|
|
617
615
|
});
|
|
618
616
|
}
|
|
619
617
|
|
|
620
|
-
function getGitStatus(cwd) {
|
|
621
|
-
|
|
618
|
+
function getGitStatus(cwd, trim = true) {
|
|
619
|
+
const status = execSync("git status --porcelain", {
|
|
622
620
|
cwd,
|
|
623
621
|
encoding: "utf8"
|
|
624
|
-
})
|
|
622
|
+
});
|
|
623
|
+
if (trim)
|
|
624
|
+
return status.trim();
|
|
625
|
+
return status;
|
|
625
626
|
}
|
|
626
627
|
function checkGitStatusIfDirty() {
|
|
627
628
|
logger.debug("Checking git status");
|
|
@@ -661,6 +662,9 @@ function detectGitProvider(cwd = process.cwd()) {
|
|
|
661
662
|
if (remoteUrl.includes("gitlab.com") || remoteUrl.includes("gitlab")) {
|
|
662
663
|
return "gitlab";
|
|
663
664
|
}
|
|
665
|
+
if (remoteUrl.includes("bitbucket.org") || remoteUrl.includes("bitbucket")) {
|
|
666
|
+
return "bitbucket";
|
|
667
|
+
}
|
|
664
668
|
return null;
|
|
665
669
|
} catch {
|
|
666
670
|
return null;
|
|
@@ -668,7 +672,7 @@ function detectGitProvider(cwd = process.cwd()) {
|
|
|
668
672
|
}
|
|
669
673
|
function parseGitRemoteUrl(remoteUrl) {
|
|
670
674
|
const sshRegex = /git@[\w.-]+:([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
|
|
671
|
-
const httpsRegex = /https?:\/\/[\w.-]+\/(
|
|
675
|
+
const httpsRegex = /https?:\/\/[\w.-]+\/(.+?)\/([^/]+?)(?:\.git)?$/;
|
|
672
676
|
const sshMatch = remoteUrl.match(sshRegex);
|
|
673
677
|
if (sshMatch) {
|
|
674
678
|
return {
|
|
@@ -685,6 +689,27 @@ function parseGitRemoteUrl(remoteUrl) {
|
|
|
685
689
|
}
|
|
686
690
|
return null;
|
|
687
691
|
}
|
|
692
|
+
function getModifiedReleaseFilePatterns({ config }) {
|
|
693
|
+
const gitStatusRaw = getGitStatus(config.cwd, false);
|
|
694
|
+
if (!gitStatusRaw) {
|
|
695
|
+
logger.debug("No modified files in git status");
|
|
696
|
+
return [];
|
|
697
|
+
}
|
|
698
|
+
const modifiedFiles = gitStatusRaw.split("\n").filter((line) => line.length > 0).map((line) => {
|
|
699
|
+
if (line.length < 4)
|
|
700
|
+
return null;
|
|
701
|
+
const filename = line.substring(3).trim();
|
|
702
|
+
return filename || null;
|
|
703
|
+
}).filter((file) => file !== null);
|
|
704
|
+
const releaseFiles = modifiedFiles.filter((file) => {
|
|
705
|
+
const isPackageJson = file === "package.json" || file.endsWith("/package.json");
|
|
706
|
+
const isChangelog = file === "CHANGELOG.md" || file.endsWith("/CHANGELOG.md");
|
|
707
|
+
const isLerna = file === "lerna.json";
|
|
708
|
+
return isPackageJson || isChangelog || isLerna;
|
|
709
|
+
});
|
|
710
|
+
logger.debug(`Found ${releaseFiles.length} modified release files:`, releaseFiles.join(", "));
|
|
711
|
+
return releaseFiles;
|
|
712
|
+
}
|
|
688
713
|
async function createCommitAndTags({
|
|
689
714
|
config,
|
|
690
715
|
noVerify,
|
|
@@ -696,13 +721,7 @@ async function createCommitAndTags({
|
|
|
696
721
|
const internalConfig = config || await loadRelizyConfig();
|
|
697
722
|
try {
|
|
698
723
|
await executeHook("before:commit-and-tag", internalConfig, dryRun ?? false);
|
|
699
|
-
const filePatternsToAdd =
|
|
700
|
-
"package.json",
|
|
701
|
-
"lerna.json",
|
|
702
|
-
"CHANGELOG.md",
|
|
703
|
-
"**/CHANGELOG.md",
|
|
704
|
-
"**/package.json"
|
|
705
|
-
];
|
|
724
|
+
const filePatternsToAdd = getModifiedReleaseFilePatterns({ config: internalConfig });
|
|
706
725
|
logger.start("Start commit and tag");
|
|
707
726
|
logger.debug("Adding files to git staging area...");
|
|
708
727
|
for (const pattern of filePatternsToAdd) {
|
|
@@ -821,6 +840,47 @@ async function pushCommitAndTags({ config, dryRun, logLevel, cwd }) {
|
|
|
821
840
|
}
|
|
822
841
|
logger.success("Pushing changes and tags completed!");
|
|
823
842
|
}
|
|
843
|
+
async function rollbackModifiedFiles({
|
|
844
|
+
config
|
|
845
|
+
}) {
|
|
846
|
+
const modifiedFiles = getModifiedReleaseFilePatterns({ config });
|
|
847
|
+
if (modifiedFiles.length === 0) {
|
|
848
|
+
logger.debug("No modified files to rollback");
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
logger.debug(`Rolling back ${modifiedFiles.length} modified file(s)...`);
|
|
852
|
+
logger.debug(`Files to rollback: ${modifiedFiles.join(", ")}`);
|
|
853
|
+
try {
|
|
854
|
+
const fileList = modifiedFiles.join(" ");
|
|
855
|
+
logger.debug(`Restoring specific files from HEAD: ${fileList}`);
|
|
856
|
+
await execPromise(`git checkout HEAD -- ${fileList}`, {
|
|
857
|
+
cwd: config.cwd,
|
|
858
|
+
logLevel: config.logLevel,
|
|
859
|
+
noStderr: true
|
|
860
|
+
});
|
|
861
|
+
logger.debug("Checking for untracked release files to remove...");
|
|
862
|
+
for (const file of modifiedFiles) {
|
|
863
|
+
const filePath = join(config.cwd, file);
|
|
864
|
+
if (existsSync(filePath)) {
|
|
865
|
+
try {
|
|
866
|
+
execSync(`git ls-files --error-unmatch "${file}"`, {
|
|
867
|
+
cwd: config.cwd,
|
|
868
|
+
encoding: "utf8",
|
|
869
|
+
stdio: "pipe"
|
|
870
|
+
});
|
|
871
|
+
} catch {
|
|
872
|
+
logger.debug(`Removing untracked file: ${file}`);
|
|
873
|
+
execSync(`rm "${filePath}"`, { cwd: config.cwd });
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
logger.success(`Successfully rolled back ${modifiedFiles.length} release file(s)`);
|
|
878
|
+
} catch (error) {
|
|
879
|
+
logger.error("Failed to rollback modified files automatically");
|
|
880
|
+
logger.warn(`Please manually restore these files: ${modifiedFiles.join(", ")}`);
|
|
881
|
+
throw error;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
824
884
|
function getFirstCommit(cwd) {
|
|
825
885
|
const result = execSync(
|
|
826
886
|
"git rev-list --max-parents=0 HEAD",
|
|
@@ -848,7 +908,8 @@ async function generateMarkDown({
|
|
|
848
908
|
config,
|
|
849
909
|
from,
|
|
850
910
|
to,
|
|
851
|
-
isFirstCommit
|
|
911
|
+
isFirstCommit,
|
|
912
|
+
minify
|
|
852
913
|
}) {
|
|
853
914
|
const typeGroups = groupBy(commits, "type");
|
|
854
915
|
const markdown = [];
|
|
@@ -861,7 +922,7 @@ async function generateMarkDown({
|
|
|
861
922
|
const versionTitle = updatedConfig.to;
|
|
862
923
|
const changelogTitle = `${updatedConfig.from}...${updatedConfig.to}`;
|
|
863
924
|
markdown.push("", `## ${changelogTitle}`, "");
|
|
864
|
-
if (updatedConfig.repo && updatedConfig.from && versionTitle) {
|
|
925
|
+
if (updatedConfig.repo && updatedConfig.from && versionTitle && !minify) {
|
|
865
926
|
const formattedCompareLink = formatCompareChanges(versionTitle, {
|
|
866
927
|
...updatedConfig,
|
|
867
928
|
from: isFirstCommit ? getFirstCommit(updatedConfig.cwd) : updatedConfig.from
|
|
@@ -878,7 +939,11 @@ async function generateMarkDown({
|
|
|
878
939
|
}
|
|
879
940
|
markdown.push("", `### ${updatedConfig.types[type]?.title}`, "");
|
|
880
941
|
for (const commit of group.reverse()) {
|
|
881
|
-
const line = formatCommit(
|
|
942
|
+
const line = formatCommit({
|
|
943
|
+
commit,
|
|
944
|
+
config: updatedConfig,
|
|
945
|
+
minify
|
|
946
|
+
});
|
|
882
947
|
markdown.push(line);
|
|
883
948
|
if (commit.isBreaking) {
|
|
884
949
|
breakingChanges.push(line);
|
|
@@ -890,7 +955,7 @@ async function generateMarkDown({
|
|
|
890
955
|
}
|
|
891
956
|
const _authors = /* @__PURE__ */ new Map();
|
|
892
957
|
for (const commit of commits) {
|
|
893
|
-
if (!commit.author) {
|
|
958
|
+
if (!commit.author || minify) {
|
|
894
959
|
continue;
|
|
895
960
|
}
|
|
896
961
|
const name = formatName(commit.author.name);
|
|
@@ -970,9 +1035,9 @@ function getCommitBody(commit) {
|
|
|
970
1035
|
${indentedBody}
|
|
971
1036
|
`;
|
|
972
1037
|
}
|
|
973
|
-
function formatCommit(commit, config) {
|
|
974
|
-
const body = config.changelog.includeCommitBody ? getCommitBody(commit) : "";
|
|
975
|
-
return `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ""}${commit.isBreaking ? "\u26A0\uFE0F " : ""}${upperFirst(commit.description)}${formatReferences(commit.references, config)}${body}`;
|
|
1038
|
+
function formatCommit({ commit, config, minify }) {
|
|
1039
|
+
const body = config.changelog.includeCommitBody && !minify ? getCommitBody(commit) : "";
|
|
1040
|
+
return `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ""}${commit.isBreaking ? "\u26A0\uFE0F " : ""}${upperFirst(commit.description)}${minify ? "" : formatReferences(commit.references, config)}${body}`;
|
|
976
1041
|
}
|
|
977
1042
|
function formatReferences(references, config) {
|
|
978
1043
|
const pr = references.filter((ref) => ref.type === "pull-request");
|
|
@@ -997,1472 +1062,1500 @@ function groupBy(items, key) {
|
|
|
997
1062
|
return groups;
|
|
998
1063
|
}
|
|
999
1064
|
|
|
1000
|
-
function
|
|
1001
|
-
|
|
1065
|
+
function isGraduatingToStableBetweenVersion(version, newVersion) {
|
|
1066
|
+
const isSameBase = semver.major(version) === semver.major(newVersion) && semver.minor(version) === semver.minor(newVersion) && semver.patch(version) === semver.patch(newVersion);
|
|
1067
|
+
const fromPrerelease = semver.prerelease(version) !== null;
|
|
1068
|
+
const toStable = semver.prerelease(newVersion) === null;
|
|
1069
|
+
return isSameBase && fromPrerelease && toStable;
|
|
1002
1070
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1071
|
+
function determineSemverChange(commits, types) {
|
|
1072
|
+
let [hasMajor, hasMinor, hasPatch] = [false, false, false];
|
|
1073
|
+
for (const commit of commits) {
|
|
1074
|
+
const commitType = types[commit.type];
|
|
1075
|
+
if (!commitType) {
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
const semverType = commitType.semver;
|
|
1079
|
+
if (semverType === "major" || commit.isBreaking) {
|
|
1080
|
+
hasMajor = true;
|
|
1081
|
+
} else if (semverType === "minor") {
|
|
1082
|
+
hasMinor = true;
|
|
1083
|
+
} else if (semverType === "patch") {
|
|
1084
|
+
hasPatch = true;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
return hasMajor ? "major" : hasMinor ? "minor" : hasPatch ? "patch" : void 0;
|
|
1088
|
+
}
|
|
1089
|
+
function detectReleaseTypeFromCommits(commits, types) {
|
|
1090
|
+
return determineSemverChange(commits, types);
|
|
1091
|
+
}
|
|
1092
|
+
function validatePrereleaseDowngrade(currentVersion, targetPreid, configuredType) {
|
|
1093
|
+
if (configuredType !== "prerelease" || !targetPreid || !isPrerelease(currentVersion)) {
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
const testVersion = semver.inc(currentVersion, "prerelease", targetPreid);
|
|
1097
|
+
const isNotUpgrade = testVersion && !semver.gt(testVersion, currentVersion);
|
|
1098
|
+
if (isNotUpgrade) {
|
|
1099
|
+
throw new Error(`Unable to graduate from ${currentVersion} to ${testVersion}, it's not a valid prerelease`);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
function handleStableVersionWithReleaseType(commits, types, force) {
|
|
1103
|
+
if (!commits?.length && !force) {
|
|
1104
|
+
logger.debug('No commits found for stable version with "release" type, skipping bump');
|
|
1105
|
+
return void 0;
|
|
1106
|
+
}
|
|
1107
|
+
const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, types) : void 0;
|
|
1108
|
+
if (!detectedType && !force) {
|
|
1109
|
+
logger.debug("No significant commits found, skipping bump");
|
|
1110
|
+
return void 0;
|
|
1111
|
+
}
|
|
1112
|
+
logger.debug(`Auto-detected release type from commits: ${detectedType}`);
|
|
1113
|
+
return detectedType;
|
|
1114
|
+
}
|
|
1115
|
+
function handleStableVersionWithPrereleaseType(commits, types, force) {
|
|
1116
|
+
if (!commits?.length && !force) {
|
|
1117
|
+
logger.debug('No commits found for stable version with "prerelease" type, skipping bump');
|
|
1118
|
+
return void 0;
|
|
1119
|
+
}
|
|
1120
|
+
const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, types) : void 0;
|
|
1121
|
+
if (!detectedType) {
|
|
1122
|
+
logger.debug("No significant commits found, using prepatch as default");
|
|
1123
|
+
return "prepatch";
|
|
1124
|
+
}
|
|
1125
|
+
const prereleaseType = `pre${detectedType}`;
|
|
1126
|
+
logger.debug(`Auto-detected prerelease type from commits: ${prereleaseType}`);
|
|
1127
|
+
return prereleaseType;
|
|
1128
|
+
}
|
|
1129
|
+
function handlePrereleaseVersionToStable(currentVersion) {
|
|
1130
|
+
logger.debug(`Graduating from prerelease ${currentVersion} to stable release`);
|
|
1131
|
+
return "release";
|
|
1132
|
+
}
|
|
1133
|
+
function handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force }) {
|
|
1134
|
+
const currentPreid = getPreid(currentVersion);
|
|
1135
|
+
const hasChangedPreid = preid && currentPreid && currentPreid !== preid;
|
|
1136
|
+
if (hasChangedPreid) {
|
|
1137
|
+
const testVersion = semver.inc(currentVersion, "prerelease", preid);
|
|
1138
|
+
if (!testVersion) {
|
|
1139
|
+
throw new Error(`Unable to change preid from ${currentPreid} to ${preid} for version ${currentVersion}`);
|
|
1140
|
+
}
|
|
1141
|
+
const isUpgrade = semver.gt(testVersion, currentVersion);
|
|
1142
|
+
if (!isUpgrade) {
|
|
1143
|
+
throw new Error(`Unable to change preid from ${currentVersion} to ${testVersion}, it's not a valid upgrade (cannot downgrade from ${currentPreid} to ${preid})`);
|
|
1144
|
+
}
|
|
1145
|
+
return "prerelease";
|
|
1146
|
+
}
|
|
1147
|
+
if (!commits?.length && !force) {
|
|
1148
|
+
logger.debug("No commits found for prerelease version, skipping bump");
|
|
1149
|
+
return void 0;
|
|
1150
|
+
}
|
|
1151
|
+
logger.debug(`Incrementing prerelease version: ${currentVersion}`);
|
|
1152
|
+
return "prerelease";
|
|
1153
|
+
}
|
|
1154
|
+
function handleExplicitReleaseType({
|
|
1155
|
+
releaseType,
|
|
1156
|
+
currentVersion
|
|
1008
1157
|
}) {
|
|
1009
|
-
|
|
1010
|
-
const
|
|
1011
|
-
if (
|
|
1012
|
-
|
|
1158
|
+
const isCurrentPrerelease = isPrerelease(currentVersion);
|
|
1159
|
+
const isGraduatingToStable = isCurrentPrerelease && isStableReleaseType(releaseType);
|
|
1160
|
+
if (isGraduatingToStable) {
|
|
1161
|
+
logger.debug(`Graduating from prerelease ${currentVersion} to stable with type: ${releaseType}`);
|
|
1162
|
+
} else {
|
|
1163
|
+
logger.debug(`Using explicit release type: ${releaseType}`);
|
|
1013
1164
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1165
|
+
return releaseType;
|
|
1166
|
+
}
|
|
1167
|
+
function determineReleaseType({
|
|
1168
|
+
currentVersion,
|
|
1169
|
+
commits,
|
|
1170
|
+
releaseType,
|
|
1171
|
+
preid,
|
|
1172
|
+
types,
|
|
1173
|
+
force
|
|
1174
|
+
}) {
|
|
1175
|
+
if (releaseType === "release" && preid) {
|
|
1176
|
+
throw new Error('You cannot use a "release" type with a "preid", to use a preid you must use a "prerelease" type');
|
|
1017
1177
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1178
|
+
validatePrereleaseDowngrade(currentVersion, preid, releaseType);
|
|
1179
|
+
if (force) {
|
|
1180
|
+
logger.debug(`Force flag enabled, using configured type: ${releaseType}`);
|
|
1181
|
+
return releaseType;
|
|
1020
1182
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
from: fromTag,
|
|
1026
|
-
to: toTag
|
|
1027
|
-
};
|
|
1028
|
-
const generatedChangelog = await generateMarkDown({
|
|
1029
|
-
commits: pkg.commits,
|
|
1030
|
-
config,
|
|
1031
|
-
from: fromTag,
|
|
1032
|
-
isFirstCommit,
|
|
1033
|
-
to: toTag
|
|
1034
|
-
});
|
|
1035
|
-
let changelog = generatedChangelog;
|
|
1036
|
-
if (pkg.commits.length === 0) {
|
|
1037
|
-
changelog = `${changelog}
|
|
1038
|
-
|
|
1039
|
-
${config.templates.emptyChangelogContent}`;
|
|
1183
|
+
const isCurrentPrerelease = isPrerelease(currentVersion);
|
|
1184
|
+
if (!isCurrentPrerelease) {
|
|
1185
|
+
if (releaseType === "release") {
|
|
1186
|
+
return handleStableVersionWithReleaseType(commits, types, force);
|
|
1040
1187
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
});
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1188
|
+
if (releaseType === "prerelease") {
|
|
1189
|
+
return handleStableVersionWithPrereleaseType(commits, types, force);
|
|
1190
|
+
}
|
|
1191
|
+
return handleExplicitReleaseType({ releaseType, currentVersion });
|
|
1192
|
+
}
|
|
1193
|
+
if (releaseType === "release") {
|
|
1194
|
+
return handlePrereleaseVersionToStable(currentVersion);
|
|
1195
|
+
}
|
|
1196
|
+
if (releaseType === "prerelease") {
|
|
1197
|
+
return handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force });
|
|
1198
|
+
}
|
|
1199
|
+
return handleExplicitReleaseType({ releaseType, currentVersion });
|
|
1200
|
+
}
|
|
1201
|
+
function writeVersion(pkgPath, newVersion, dryRun = false) {
|
|
1202
|
+
const packageJsonPath = join(pkgPath, "package.json");
|
|
1203
|
+
try {
|
|
1204
|
+
logger.debug(`Writing ${newVersion} to ${pkgPath}`);
|
|
1205
|
+
const content = readFileSync(packageJsonPath, "utf8");
|
|
1206
|
+
const packageJson = JSON.parse(content);
|
|
1207
|
+
const oldVersion = packageJson.version;
|
|
1208
|
+
packageJson.version = newVersion;
|
|
1054
1209
|
if (dryRun) {
|
|
1055
|
-
logger.
|
|
1210
|
+
logger.debug(`[dry-run] Updated ${packageJson.name}: ${oldVersion} \u2192 ${newVersion}`);
|
|
1211
|
+
return;
|
|
1056
1212
|
}
|
|
1057
|
-
|
|
1213
|
+
writeFileSync(packageJsonPath, `${formatJson(packageJson)}
|
|
1214
|
+
`, "utf8");
|
|
1215
|
+
logger.debug(`Updated ${packageJson.name}: ${oldVersion} \u2192 ${newVersion}`);
|
|
1058
1216
|
} catch (error) {
|
|
1059
|
-
throw new Error(`
|
|
1217
|
+
throw new Error(`Unable to write version to ${packageJsonPath}: ${error}`);
|
|
1060
1218
|
}
|
|
1061
1219
|
}
|
|
1062
|
-
function
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1220
|
+
function getPackageNewVersion({
|
|
1221
|
+
name,
|
|
1222
|
+
currentVersion,
|
|
1223
|
+
releaseType,
|
|
1224
|
+
preid,
|
|
1225
|
+
suffix
|
|
1067
1226
|
}) {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1227
|
+
let newVersion = semver.inc(currentVersion, releaseType, preid);
|
|
1228
|
+
if (!newVersion) {
|
|
1229
|
+
throw new Error(`Unable to bump "${name}" version "${currentVersion}" with release type "${releaseType}"
|
|
1230
|
+
|
|
1231
|
+
You should use an explicit release type (use flag: --major, --minor, --patch, --premajor, --preminor, --prepatch, --prerelease)`);
|
|
1072
1232
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
let updatedChangelog;
|
|
1076
|
-
if (titleIndex !== -1) {
|
|
1077
|
-
const beforeTitle = lines.slice(0, titleIndex + 1);
|
|
1078
|
-
const afterTitle = lines.slice(titleIndex + 1);
|
|
1079
|
-
updatedChangelog = [...beforeTitle, "", changelog, "", ...afterTitle].join("\n");
|
|
1080
|
-
} else {
|
|
1081
|
-
const title = "# Changelog\n";
|
|
1082
|
-
updatedChangelog = `${title}
|
|
1083
|
-
${changelog}
|
|
1084
|
-
${existingChangelog}`;
|
|
1233
|
+
if (isPrereleaseReleaseType(releaseType) && suffix) {
|
|
1234
|
+
newVersion = newVersion.replace(/\.(\d+)$/, `.${suffix}`);
|
|
1085
1235
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
} else {
|
|
1090
|
-
logger.debug(`Writing changelog to ${changelogPath}`);
|
|
1091
|
-
writeFileSync(changelogPath, updatedChangelog, "utf8");
|
|
1092
|
-
logger.info(`Changelog updated for ${pkg.name} (${"newVersion" in pkg && pkg.newVersion || pkg.version})`);
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
function getDefaultConfig() {
|
|
1097
|
-
return {
|
|
1098
|
-
cwd: process$1.cwd(),
|
|
1099
|
-
types: {
|
|
1100
|
-
feat: { title: "\u{1F680} Enhancements", semver: "minor" },
|
|
1101
|
-
perf: { title: "\u{1F525} Performance", semver: "patch" },
|
|
1102
|
-
fix: { title: "\u{1FA79} Fixes", semver: "patch" },
|
|
1103
|
-
refactor: { title: "\u{1F485} Refactors", semver: "patch" },
|
|
1104
|
-
docs: { title: "\u{1F4D6} Documentation", semver: "patch" },
|
|
1105
|
-
build: { title: "\u{1F4E6} Build", semver: "patch" },
|
|
1106
|
-
types: { title: "\u{1F30A} Types", semver: "patch" },
|
|
1107
|
-
chore: { title: "\u{1F3E1} Chore" },
|
|
1108
|
-
examples: { title: "\u{1F3C0} Examples" },
|
|
1109
|
-
test: { title: "\u2705 Tests" },
|
|
1110
|
-
style: { title: "\u{1F3A8} Styles" },
|
|
1111
|
-
ci: { title: "\u{1F916} CI" }
|
|
1112
|
-
},
|
|
1113
|
-
templates: {
|
|
1114
|
-
commitMessage: "chore(release): bump version to {{newVersion}}",
|
|
1115
|
-
tagMessage: "Bump version to {{newVersion}}",
|
|
1116
|
-
tagBody: "v{{newVersion}}",
|
|
1117
|
-
emptyChangelogContent: "No relevant changes for this release"
|
|
1118
|
-
},
|
|
1119
|
-
excludeAuthors: [],
|
|
1120
|
-
noAuthors: false,
|
|
1121
|
-
bump: {
|
|
1122
|
-
type: "release",
|
|
1123
|
-
clean: true,
|
|
1124
|
-
dependencyTypes: ["dependencies"],
|
|
1125
|
-
yes: false
|
|
1126
|
-
},
|
|
1127
|
-
changelog: {
|
|
1128
|
-
rootChangelog: true,
|
|
1129
|
-
includeCommitBody: true
|
|
1130
|
-
},
|
|
1131
|
-
publish: {
|
|
1132
|
-
private: false,
|
|
1133
|
-
args: [],
|
|
1134
|
-
safetyCheck: false
|
|
1135
|
-
},
|
|
1136
|
-
tokens: {
|
|
1137
|
-
gitlab: process$1.env.RELIZY_GITLAB_TOKEN || process$1.env.GITLAB_TOKEN || process$1.env.GITLAB_API_TOKEN || process$1.env.CI_JOB_TOKEN,
|
|
1138
|
-
github: process$1.env.RELIZY_GITHUB_TOKEN || process$1.env.GITHUB_TOKEN || process$1.env.GH_TOKEN
|
|
1139
|
-
},
|
|
1140
|
-
scopeMap: {},
|
|
1141
|
-
release: {
|
|
1142
|
-
commit: true,
|
|
1143
|
-
publish: true,
|
|
1144
|
-
changelog: true,
|
|
1145
|
-
push: true,
|
|
1146
|
-
clean: true,
|
|
1147
|
-
providerRelease: true,
|
|
1148
|
-
noVerify: false,
|
|
1149
|
-
gitTag: true
|
|
1150
|
-
},
|
|
1151
|
-
logLevel: "default",
|
|
1152
|
-
safetyCheck: true
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
function setupLogger(logLevel) {
|
|
1156
|
-
if (logLevel) {
|
|
1157
|
-
logger.setLevel(logLevel);
|
|
1158
|
-
logger.debug(`Log level set to: ${logLevel}`);
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
async function resolveConfig(config, cwd) {
|
|
1162
|
-
if (!config.repo) {
|
|
1163
|
-
const resolvedRepoConfig = await resolveRepoConfig(cwd);
|
|
1164
|
-
config.repo = {
|
|
1165
|
-
...resolvedRepoConfig,
|
|
1166
|
-
provider: resolvedRepoConfig.provider
|
|
1167
|
-
};
|
|
1236
|
+
const isValidVersion = semver.gt(newVersion, currentVersion);
|
|
1237
|
+
if (!isValidVersion) {
|
|
1238
|
+
throw new Error(`Unable to bump "${name}" version "${currentVersion}" to "${newVersion}", new version is not greater than current version`);
|
|
1168
1239
|
}
|
|
1169
|
-
if (
|
|
1170
|
-
|
|
1171
|
-
config.repo = {
|
|
1172
|
-
...resolvedRepoConfig,
|
|
1173
|
-
provider: resolvedRepoConfig.provider
|
|
1174
|
-
};
|
|
1240
|
+
if (isGraduating(currentVersion, releaseType)) {
|
|
1241
|
+
logger.info(`Graduating "${name}" from prerelease ${currentVersion} to stable ${newVersion}`);
|
|
1175
1242
|
}
|
|
1176
|
-
|
|
1177
|
-
}
|
|
1178
|
-
async function loadRelizyConfig(options) {
|
|
1179
|
-
const cwd = options?.overrides?.cwd ?? process$1.cwd();
|
|
1180
|
-
await setupDotenv({ cwd });
|
|
1181
|
-
const configFile = options?.configFile ?? "relizy";
|
|
1182
|
-
const defaultConfig = getDefaultConfig();
|
|
1183
|
-
const overridesConfig = defu(options?.overrides, options?.baseConfig);
|
|
1184
|
-
const results = await loadConfig({
|
|
1185
|
-
dotenv: true,
|
|
1186
|
-
cwd,
|
|
1187
|
-
name: configFile,
|
|
1188
|
-
packageJson: true,
|
|
1189
|
-
defaults: defaultConfig,
|
|
1190
|
-
overrides: overridesConfig
|
|
1191
|
-
});
|
|
1192
|
-
if (typeof results._configFile !== "string") {
|
|
1193
|
-
logger.debug(`No config file found with name "${configFile}"`);
|
|
1194
|
-
if (options?.configFile) {
|
|
1195
|
-
logger.error(`No config file found with name "${configFile}"`);
|
|
1196
|
-
process$1.exit(1);
|
|
1197
|
-
}
|
|
1243
|
+
if (isChangedPreid(currentVersion, preid)) {
|
|
1244
|
+
logger.debug(`Graduating "${name}" from ${getPreid(currentVersion)} to ${preid}`);
|
|
1198
1245
|
}
|
|
1199
|
-
|
|
1200
|
-
logger.verbose("User config:", formatJson(results.config.changelog));
|
|
1201
|
-
const resolvedConfig = await resolveConfig(results.config, cwd);
|
|
1202
|
-
logger.debug("Resolved config:", formatJson(resolvedConfig));
|
|
1203
|
-
return resolvedConfig;
|
|
1204
|
-
}
|
|
1205
|
-
function defineConfig(config) {
|
|
1206
|
-
return config;
|
|
1246
|
+
return newVersion;
|
|
1207
1247
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
force,
|
|
1214
|
-
suffix
|
|
1248
|
+
function updateLernaVersion({
|
|
1249
|
+
rootDir,
|
|
1250
|
+
versionMode,
|
|
1251
|
+
version,
|
|
1252
|
+
dryRun = false
|
|
1215
1253
|
}) {
|
|
1216
|
-
const
|
|
1217
|
-
if (!
|
|
1218
|
-
|
|
1254
|
+
const lernaJsonExists = hasLernaJson(rootDir);
|
|
1255
|
+
if (!lernaJsonExists) {
|
|
1256
|
+
return;
|
|
1219
1257
|
}
|
|
1220
|
-
|
|
1221
|
-
if (!
|
|
1222
|
-
|
|
1258
|
+
const lernaJsonPath = join(rootDir, "lerna.json");
|
|
1259
|
+
if (!existsSync(lernaJsonPath)) {
|
|
1260
|
+
return;
|
|
1223
1261
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
for (const pkg of packages) {
|
|
1233
|
-
const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
|
|
1234
|
-
const from = config.from || pkg.fromTag;
|
|
1235
|
-
const to = config.to || getIndependentTag({ version: newVersion, name: pkg.name });
|
|
1236
|
-
if (!from) {
|
|
1237
|
-
logger.warn(`No from tag found for ${pkg.name}, skipping release`);
|
|
1238
|
-
continue;
|
|
1262
|
+
try {
|
|
1263
|
+
logger.debug("Updating lerna.json version");
|
|
1264
|
+
const content = readFileSync(lernaJsonPath, "utf8");
|
|
1265
|
+
const lernaJson = JSON.parse(content);
|
|
1266
|
+
const oldVersion = lernaJson.version;
|
|
1267
|
+
if (lernaJson.version === "independent" || versionMode === "independent") {
|
|
1268
|
+
logger.debug("Lerna version is independent or version mode is independent, skipping update");
|
|
1269
|
+
return;
|
|
1239
1270
|
}
|
|
1240
|
-
|
|
1241
|
-
logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${toTag}`);
|
|
1242
|
-
const changelog = await generateChangelog({
|
|
1243
|
-
pkg,
|
|
1244
|
-
config,
|
|
1245
|
-
dryRun,
|
|
1246
|
-
newVersion
|
|
1247
|
-
});
|
|
1248
|
-
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
1249
|
-
const release = {
|
|
1250
|
-
tag_name: to,
|
|
1251
|
-
name: to,
|
|
1252
|
-
body: releaseBody,
|
|
1253
|
-
prerelease: isPrerelease(newVersion)
|
|
1254
|
-
};
|
|
1255
|
-
logger.debug(`Creating release for ${to}${release.prerelease ? " (prerelease)" : ""}`);
|
|
1271
|
+
lernaJson.version = version;
|
|
1256
1272
|
if (dryRun) {
|
|
1257
|
-
logger.info(`[dry-run]
|
|
1258
|
-
|
|
1259
|
-
name: pkg.name,
|
|
1260
|
-
tag: release.tag_name,
|
|
1261
|
-
version: newVersion,
|
|
1262
|
-
prerelease: release.prerelease
|
|
1263
|
-
});
|
|
1264
|
-
} else {
|
|
1265
|
-
logger.debug(`Publishing release ${to} to GitHub...`);
|
|
1266
|
-
await createGithubRelease({
|
|
1267
|
-
...config,
|
|
1268
|
-
from,
|
|
1269
|
-
to,
|
|
1270
|
-
repo: repoConfig
|
|
1271
|
-
}, release);
|
|
1272
|
-
postedReleases.push({
|
|
1273
|
-
name: pkg.name,
|
|
1274
|
-
tag: release.tag_name,
|
|
1275
|
-
version: newVersion,
|
|
1276
|
-
prerelease: release.prerelease
|
|
1277
|
-
});
|
|
1273
|
+
logger.info(`[dry-run] update lerna.json: ${oldVersion} \u2192 ${version}`);
|
|
1274
|
+
return;
|
|
1278
1275
|
}
|
|
1276
|
+
writeFileSync(lernaJsonPath, `${formatJson(lernaJson)}
|
|
1277
|
+
`, "utf8");
|
|
1278
|
+
logger.success(`Updated lerna.json: ${oldVersion} \u2192 ${version}`);
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
logger.fail(`Unable to update lerna.json: ${error}`);
|
|
1279
1281
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1282
|
+
}
|
|
1283
|
+
function extractVersionFromPackageTag(tag) {
|
|
1284
|
+
const atIndex = tag.lastIndexOf("@");
|
|
1285
|
+
if (atIndex === -1) {
|
|
1286
|
+
return null;
|
|
1284
1287
|
}
|
|
1285
|
-
return
|
|
1288
|
+
return tag.slice(atIndex + 1);
|
|
1286
1289
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1290
|
+
function isPrerelease(version) {
|
|
1291
|
+
if (!version)
|
|
1292
|
+
return false;
|
|
1293
|
+
const prerelease = semver.prerelease(version);
|
|
1294
|
+
return prerelease ? prerelease.length > 0 : false;
|
|
1295
|
+
}
|
|
1296
|
+
function isStableReleaseType(releaseType) {
|
|
1297
|
+
const stableTypes = ["release", "major", "minor", "patch"];
|
|
1298
|
+
return stableTypes.includes(releaseType);
|
|
1299
|
+
}
|
|
1300
|
+
function isPrereleaseReleaseType(releaseType) {
|
|
1301
|
+
const prereleaseTypes = ["prerelease", "premajor", "preminor", "prepatch"];
|
|
1302
|
+
return prereleaseTypes.includes(releaseType);
|
|
1303
|
+
}
|
|
1304
|
+
function isGraduating(currentVersion, releaseType) {
|
|
1305
|
+
return isPrerelease(currentVersion) && isStableReleaseType(releaseType);
|
|
1306
|
+
}
|
|
1307
|
+
function getPreid(version) {
|
|
1308
|
+
if (!version)
|
|
1309
|
+
return null;
|
|
1310
|
+
const prerelease = semver.prerelease(version);
|
|
1311
|
+
if (!prerelease || prerelease.length === 0) {
|
|
1312
|
+
return null;
|
|
1296
1313
|
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1314
|
+
return prerelease[0];
|
|
1315
|
+
}
|
|
1316
|
+
function isChangedPreid(currentVersion, targetPreid) {
|
|
1317
|
+
if (!targetPreid || !isPrerelease(currentVersion)) {
|
|
1318
|
+
return false;
|
|
1300
1319
|
}
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1320
|
+
const currentPreid = getPreid(currentVersion);
|
|
1321
|
+
if (!currentPreid) {
|
|
1322
|
+
return false;
|
|
1323
|
+
}
|
|
1324
|
+
return currentPreid !== targetPreid;
|
|
1325
|
+
}
|
|
1326
|
+
function getBumpedPackageIndependently({
|
|
1327
|
+
pkg,
|
|
1328
|
+
dryRun
|
|
1329
|
+
}) {
|
|
1330
|
+
logger.debug(`Analyzing ${pkg.name}`);
|
|
1331
|
+
const currentVersion = pkg.version || "0.0.0";
|
|
1332
|
+
const newVersion = pkg.newVersion;
|
|
1333
|
+
if (!newVersion) {
|
|
1334
|
+
return { bumped: false };
|
|
1335
|
+
}
|
|
1336
|
+
logger.debug(`Bumping ${pkg.name} from ${currentVersion} to ${newVersion}`);
|
|
1337
|
+
writeVersion(pkg.path, newVersion, dryRun);
|
|
1338
|
+
return { bumped: true, newVersion, oldVersion: currentVersion };
|
|
1339
|
+
}
|
|
1340
|
+
function displayRootAndLernaUpdates({
|
|
1341
|
+
versionMode,
|
|
1342
|
+
currentVersion,
|
|
1343
|
+
newVersion,
|
|
1344
|
+
dryRun,
|
|
1345
|
+
lernaJsonExists
|
|
1346
|
+
}) {
|
|
1347
|
+
if (versionMode !== "independent" && currentVersion && newVersion) {
|
|
1348
|
+
logger.log(`${dryRun ? "[dry-run] " : ""}Root package.json: ${currentVersion} \u2192 ${newVersion}`);
|
|
1349
|
+
logger.log("");
|
|
1350
|
+
if (lernaJsonExists) {
|
|
1351
|
+
logger.log(`${dryRun ? "[dry-run] " : ""}lerna.json: ${currentVersion} \u2192 ${newVersion}`);
|
|
1352
|
+
logger.log("");
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
function displayUnifiedModePackages({
|
|
1357
|
+
packages,
|
|
1358
|
+
newVersion,
|
|
1359
|
+
force
|
|
1360
|
+
}) {
|
|
1361
|
+
logger.log(`${packages.length} package(s):`);
|
|
1362
|
+
packages.forEach((pkg) => {
|
|
1363
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} (${pkg.commits.length} commits) ${force ? "(force)" : ""}`);
|
|
1308
1364
|
});
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
if (dryRun) {
|
|
1323
|
-
logger.info("[dry-run] Publish GitHub release for", release.tag_name);
|
|
1365
|
+
logger.log("");
|
|
1366
|
+
}
|
|
1367
|
+
function displaySelectiveModePackages({
|
|
1368
|
+
packages,
|
|
1369
|
+
newVersion,
|
|
1370
|
+
force
|
|
1371
|
+
}) {
|
|
1372
|
+
if (force) {
|
|
1373
|
+
logger.log(`${packages.length} package(s):`);
|
|
1374
|
+
packages.forEach((pkg) => {
|
|
1375
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} (force)`);
|
|
1376
|
+
});
|
|
1377
|
+
logger.log("");
|
|
1324
1378
|
} else {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1379
|
+
const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
|
|
1380
|
+
const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
|
|
1381
|
+
const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
|
|
1382
|
+
if (packagesWithCommits.length > 0) {
|
|
1383
|
+
logger.log(`${packagesWithCommits.length} package(s) with commits:`);
|
|
1384
|
+
packagesWithCommits.forEach((pkg) => {
|
|
1385
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} (${pkg.commits.length} commits) ${force ? "(force)" : ""}`);
|
|
1386
|
+
});
|
|
1387
|
+
logger.log("");
|
|
1388
|
+
}
|
|
1389
|
+
if (packagesAsDependents.length > 0) {
|
|
1390
|
+
logger.log(`${packagesAsDependents.length} dependent package(s):`);
|
|
1391
|
+
packagesAsDependents.forEach((pkg) => {
|
|
1392
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
1393
|
+
});
|
|
1394
|
+
logger.log("");
|
|
1395
|
+
}
|
|
1396
|
+
if (packagesAsGraduation.length > 0) {
|
|
1397
|
+
logger.log(`${packagesAsGraduation.length} graduation package(s):`);
|
|
1398
|
+
packagesAsGraduation.forEach((pkg) => {
|
|
1399
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
1400
|
+
});
|
|
1401
|
+
logger.log("");
|
|
1402
|
+
}
|
|
1332
1403
|
}
|
|
1333
|
-
logger.success(`Release ${to} published to GitHub!`);
|
|
1334
|
-
return [{
|
|
1335
|
-
name: to,
|
|
1336
|
-
tag: to,
|
|
1337
|
-
version: to,
|
|
1338
|
-
prerelease: release.prerelease
|
|
1339
|
-
}];
|
|
1340
1404
|
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
from: options.from,
|
|
1350
|
-
to: options.to,
|
|
1351
|
-
logLevel: options.logLevel,
|
|
1352
|
-
tokens: {
|
|
1353
|
-
github: options.token
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1405
|
+
function displayIndependentModePackages({
|
|
1406
|
+
packages,
|
|
1407
|
+
force
|
|
1408
|
+
}) {
|
|
1409
|
+
if (force) {
|
|
1410
|
+
logger.log(`${packages.length} package(s):`);
|
|
1411
|
+
packages.forEach((pkg) => {
|
|
1412
|
+
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} (force)`);
|
|
1356
1413
|
});
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1414
|
+
logger.log("");
|
|
1415
|
+
} else {
|
|
1416
|
+
const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
|
|
1417
|
+
const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
|
|
1418
|
+
const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
|
|
1419
|
+
if (packagesWithCommits.length > 0) {
|
|
1420
|
+
logger.log(`${packagesWithCommits.length} package(s) with commits:`);
|
|
1421
|
+
packagesWithCommits.forEach((pkg) => {
|
|
1422
|
+
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} (${pkg.commits.length} commits) ${force ? "(force)" : ""}`);
|
|
1364
1423
|
});
|
|
1424
|
+
logger.log("");
|
|
1365
1425
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1426
|
+
if (packagesAsDependents.length > 0) {
|
|
1427
|
+
logger.log(`${packagesAsDependents.length} dependent package(s):`);
|
|
1428
|
+
packagesAsDependents.forEach((pkg) => {
|
|
1429
|
+
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} ${force ? "(force)" : ""}`);
|
|
1430
|
+
});
|
|
1431
|
+
logger.log("");
|
|
1432
|
+
}
|
|
1433
|
+
if (packagesAsGraduation.length > 0) {
|
|
1434
|
+
logger.log(`${packagesAsGraduation.length} graduation package(s):`);
|
|
1435
|
+
packagesAsGraduation.forEach((pkg) => {
|
|
1436
|
+
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} ${force ? "(force)" : ""}`);
|
|
1437
|
+
});
|
|
1438
|
+
logger.log("");
|
|
1369
1439
|
}
|
|
1370
|
-
const newVersion = options.bumpResult?.newVersion || rootPackageBase.version;
|
|
1371
|
-
const { from, to } = await resolveTags({
|
|
1372
|
-
config,
|
|
1373
|
-
step: "provider-release",
|
|
1374
|
-
newVersion,
|
|
1375
|
-
pkg: rootPackageBase
|
|
1376
|
-
});
|
|
1377
|
-
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
1378
|
-
config,
|
|
1379
|
-
force: options.force ?? false,
|
|
1380
|
-
suffix: options.suffix,
|
|
1381
|
-
changelog: true,
|
|
1382
|
-
from,
|
|
1383
|
-
to
|
|
1384
|
-
});
|
|
1385
|
-
return await githubUnified({
|
|
1386
|
-
config,
|
|
1387
|
-
dryRun,
|
|
1388
|
-
rootPackage,
|
|
1389
|
-
bumpResult: options.bumpResult
|
|
1390
|
-
});
|
|
1391
|
-
} catch (error) {
|
|
1392
|
-
logger.error("Error publishing GitHub release:", error);
|
|
1393
|
-
throw error;
|
|
1394
1440
|
}
|
|
1395
1441
|
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1442
|
+
async function confirmBump({
|
|
1443
|
+
versionMode,
|
|
1398
1444
|
config,
|
|
1399
|
-
|
|
1445
|
+
packages,
|
|
1446
|
+
force,
|
|
1447
|
+
currentVersion,
|
|
1448
|
+
newVersion,
|
|
1400
1449
|
dryRun
|
|
1401
1450
|
}) {
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
"No GitLab token found. Set GITLAB_TOKEN or CI_JOB_TOKEN environment variable or configure tokens.gitlab"
|
|
1406
|
-
);
|
|
1451
|
+
if (packages.length === 0) {
|
|
1452
|
+
logger.debug("No packages to bump");
|
|
1453
|
+
return;
|
|
1407
1454
|
}
|
|
1408
|
-
const
|
|
1409
|
-
|
|
1410
|
-
|
|
1455
|
+
const lernaJsonExists = hasLernaJson(config.cwd);
|
|
1456
|
+
logger.log("");
|
|
1457
|
+
logger.info(`${dryRun ? "[dry-run] " : ""}The following packages will be updated:
|
|
1458
|
+
`);
|
|
1459
|
+
displayRootAndLernaUpdates({
|
|
1460
|
+
versionMode,
|
|
1461
|
+
currentVersion,
|
|
1462
|
+
newVersion,
|
|
1463
|
+
lernaJsonExists,
|
|
1464
|
+
dryRun
|
|
1465
|
+
});
|
|
1466
|
+
if (versionMode === "unified") {
|
|
1467
|
+
if (!newVersion) {
|
|
1468
|
+
throw new Error("Cannot confirm bump in unified mode without a new version");
|
|
1469
|
+
}
|
|
1470
|
+
displayUnifiedModePackages({ packages, newVersion, force });
|
|
1471
|
+
} else if (versionMode === "selective") {
|
|
1472
|
+
if (!newVersion) {
|
|
1473
|
+
throw new Error("Cannot confirm bump in selective mode without a new version");
|
|
1474
|
+
}
|
|
1475
|
+
displaySelectiveModePackages({ packages, newVersion, force });
|
|
1476
|
+
} else if (versionMode === "independent") {
|
|
1477
|
+
displayIndependentModePackages({ packages, force });
|
|
1411
1478
|
}
|
|
1412
|
-
logger.debug(`Parsed repository URL: ${repoConfig}`);
|
|
1413
|
-
const projectPath = encodeURIComponent(repoConfig);
|
|
1414
|
-
const gitlabDomain = config.repo?.domain || "gitlab.com";
|
|
1415
|
-
const apiUrl = `https://${gitlabDomain}/api/v4/projects/${projectPath}/releases`;
|
|
1416
|
-
logger.info(`Creating GitLab release at: ${apiUrl}`);
|
|
1417
|
-
const payload = {
|
|
1418
|
-
tag_name: release.tag_name,
|
|
1419
|
-
name: release.name || release.tag_name,
|
|
1420
|
-
description: release.description || "",
|
|
1421
|
-
ref: release.ref || "main"
|
|
1422
|
-
};
|
|
1423
1479
|
try {
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
tag_name: release.tag_name,
|
|
1428
|
-
name: release.name || release.tag_name,
|
|
1429
|
-
description: release.description || "",
|
|
1430
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1431
|
-
released_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1432
|
-
_links: {
|
|
1433
|
-
self: `${apiUrl}/${encodeURIComponent(release.tag_name)}`
|
|
1434
|
-
}
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
1437
|
-
logger.debug(`POST GitLab release to ${apiUrl} with payload: ${formatJson(payload)}`);
|
|
1438
|
-
const response = await fetch(apiUrl, {
|
|
1439
|
-
method: "POST",
|
|
1440
|
-
headers: {
|
|
1441
|
-
"Content-Type": "application/json",
|
|
1442
|
-
"PRIVATE-TOKEN": token || ""
|
|
1443
|
-
},
|
|
1444
|
-
body: JSON.stringify(payload)
|
|
1480
|
+
const confirmed = await confirm({
|
|
1481
|
+
message: `${dryRun ? "[dry-run] " : ""}Do you want to proceed with these version updates?`,
|
|
1482
|
+
default: true
|
|
1445
1483
|
});
|
|
1446
|
-
if (!
|
|
1447
|
-
|
|
1448
|
-
|
|
1484
|
+
if (!confirmed) {
|
|
1485
|
+
logger.log("");
|
|
1486
|
+
logger.fail("Bump refused");
|
|
1487
|
+
process.exit(0);
|
|
1449
1488
|
}
|
|
1450
|
-
const result = await response.json();
|
|
1451
|
-
logger.debug(`Created GitLab release: ${result._links.self}`);
|
|
1452
|
-
return result;
|
|
1453
1489
|
} catch (error) {
|
|
1454
|
-
|
|
1455
|
-
|
|
1490
|
+
const userHasExited = error instanceof Error && error.name === "ExitPromptError";
|
|
1491
|
+
if (userHasExited) {
|
|
1492
|
+
logger.log("");
|
|
1493
|
+
logger.fail("Bump cancelled");
|
|
1494
|
+
process.exit(0);
|
|
1495
|
+
}
|
|
1496
|
+
logger.fail("Error while confirming bump");
|
|
1497
|
+
process.exit(1);
|
|
1456
1498
|
}
|
|
1499
|
+
logger.log("");
|
|
1457
1500
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
dryRun
|
|
1461
|
-
bumpResult,
|
|
1462
|
-
suffix,
|
|
1463
|
-
force
|
|
1501
|
+
function getBumpedIndependentPackages({
|
|
1502
|
+
packages,
|
|
1503
|
+
dryRun
|
|
1464
1504
|
}) {
|
|
1465
|
-
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
});
|
|
1472
|
-
logger.info(`Creating ${packages.length} GitLab release(s) for independent packages`);
|
|
1473
|
-
logger.debug("Getting current branch...");
|
|
1474
|
-
const { stdout: currentBranch } = await execPromise("git rev-parse --abbrev-ref HEAD", {
|
|
1475
|
-
noSuccess: true,
|
|
1476
|
-
noStdout: true,
|
|
1477
|
-
logLevel: config.logLevel,
|
|
1478
|
-
cwd: config.cwd
|
|
1479
|
-
});
|
|
1480
|
-
const postedReleases = [];
|
|
1481
|
-
for (const pkg of packages) {
|
|
1482
|
-
const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
|
|
1483
|
-
const from = config.from || pkg.fromTag;
|
|
1484
|
-
const to = getIndependentTag({ version: newVersion, name: pkg.name });
|
|
1485
|
-
if (!from) {
|
|
1486
|
-
logger.warn(`No from tag found for ${pkg.name}, skipping release`);
|
|
1487
|
-
continue;
|
|
1488
|
-
}
|
|
1489
|
-
logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${to}`);
|
|
1490
|
-
const changelog = await generateChangelog({
|
|
1491
|
-
pkg,
|
|
1492
|
-
config,
|
|
1493
|
-
dryRun,
|
|
1494
|
-
newVersion
|
|
1505
|
+
const bumpedPackages = [];
|
|
1506
|
+
for (const pkgToBump of packages) {
|
|
1507
|
+
logger.debug(`Bumping ${pkgToBump.name} from ${pkgToBump.version} to ${pkgToBump.newVersion} (reason: ${pkgToBump.reason})`);
|
|
1508
|
+
const result = getBumpedPackageIndependently({
|
|
1509
|
+
pkg: pkgToBump,
|
|
1510
|
+
dryRun
|
|
1495
1511
|
});
|
|
1496
|
-
if (
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
1501
|
-
const release = {
|
|
1502
|
-
tag_name: to,
|
|
1503
|
-
name: to,
|
|
1504
|
-
description: releaseBody,
|
|
1505
|
-
ref: currentBranch.trim()
|
|
1506
|
-
};
|
|
1507
|
-
logger.debug(`Creating release for ${to} (ref: ${release.ref})`);
|
|
1508
|
-
if (dryRun) {
|
|
1509
|
-
logger.info(`[dry-run] Publish GitLab release for ${to}`);
|
|
1510
|
-
} else {
|
|
1511
|
-
logger.debug(`Publishing release ${to} to GitLab...`);
|
|
1512
|
-
await createGitlabRelease({
|
|
1513
|
-
config,
|
|
1514
|
-
release,
|
|
1515
|
-
dryRun
|
|
1516
|
-
});
|
|
1517
|
-
postedReleases.push({
|
|
1518
|
-
name: pkg.name,
|
|
1519
|
-
tag: release.tag_name,
|
|
1520
|
-
version: newVersion,
|
|
1521
|
-
prerelease: isPrerelease(newVersion)
|
|
1512
|
+
if (result.bumped) {
|
|
1513
|
+
bumpedPackages.push({
|
|
1514
|
+
...pkgToBump,
|
|
1515
|
+
version: result.oldVersion
|
|
1522
1516
|
});
|
|
1523
1517
|
}
|
|
1524
1518
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1519
|
+
return bumpedPackages;
|
|
1520
|
+
}
|
|
1521
|
+
function shouldFilterPrereleaseTags(currentVersion, graduating) {
|
|
1522
|
+
return !isPrerelease(currentVersion) && !graduating;
|
|
1523
|
+
}
|
|
1524
|
+
function extractVersionFromTag(tag, packageName) {
|
|
1525
|
+
if (!tag) {
|
|
1526
|
+
return null;
|
|
1529
1527
|
}
|
|
1530
|
-
|
|
1528
|
+
if (packageName) {
|
|
1529
|
+
const prefix = `${packageName}@`;
|
|
1530
|
+
if (tag.startsWith(prefix)) {
|
|
1531
|
+
return tag.slice(prefix.length);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
const atIndex = tag.lastIndexOf("@");
|
|
1535
|
+
if (atIndex !== -1) {
|
|
1536
|
+
return tag.slice(atIndex + 1);
|
|
1537
|
+
}
|
|
1538
|
+
if (tag.startsWith("v") && /^v\d/.test(tag)) {
|
|
1539
|
+
return tag.slice(1);
|
|
1540
|
+
}
|
|
1541
|
+
if (/^\d+\.\d+\.\d+/.test(tag)) {
|
|
1542
|
+
return tag;
|
|
1543
|
+
}
|
|
1544
|
+
return null;
|
|
1531
1545
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1546
|
+
function isTagVersionCompatibleWithCurrent(tagVersion, currentVersion) {
|
|
1547
|
+
try {
|
|
1548
|
+
const tagMajor = semver.major(tagVersion);
|
|
1549
|
+
const currentMajor = semver.major(currentVersion);
|
|
1550
|
+
return tagMajor <= currentMajor;
|
|
1551
|
+
} catch {
|
|
1552
|
+
return false;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
function getIndependentTag({ version, name }) {
|
|
1557
|
+
return `${name}@${version}`;
|
|
1558
|
+
}
|
|
1559
|
+
async function getLastStableTag({ logLevel, cwd }) {
|
|
1560
|
+
const { stdout } = await execPromise(
|
|
1561
|
+
`git tag --sort=-creatordate | grep -E '^[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+$' | head -n 1`,
|
|
1562
|
+
{
|
|
1563
|
+
logLevel,
|
|
1564
|
+
noStderr: true,
|
|
1565
|
+
noStdout: true,
|
|
1566
|
+
noSuccess: true,
|
|
1567
|
+
cwd
|
|
1568
|
+
}
|
|
1569
|
+
);
|
|
1570
|
+
const lastTag = stdout.trim();
|
|
1571
|
+
logger.debug("Last stable tag:", lastTag || "No stable tags found");
|
|
1572
|
+
return lastTag;
|
|
1573
|
+
}
|
|
1574
|
+
async function getLastTag({ logLevel, cwd }) {
|
|
1575
|
+
const { stdout } = await execPromise(`git tag --sort=-creatordate | head -n 1`, {
|
|
1576
|
+
logLevel,
|
|
1577
|
+
noStderr: true,
|
|
1551
1578
|
noStdout: true,
|
|
1552
|
-
|
|
1553
|
-
cwd
|
|
1579
|
+
noSuccess: true,
|
|
1580
|
+
cwd
|
|
1554
1581
|
});
|
|
1555
|
-
const
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
description: releaseBody,
|
|
1559
|
-
ref: currentBranch.trim()
|
|
1560
|
-
};
|
|
1561
|
-
logger.info(`Creating release for ${to} (ref: ${release.ref})`);
|
|
1562
|
-
logger.debug("Release details:", formatJson({
|
|
1563
|
-
tag_name: release.tag_name,
|
|
1564
|
-
name: release.name,
|
|
1565
|
-
ref: release.ref
|
|
1566
|
-
}));
|
|
1567
|
-
if (dryRun) {
|
|
1568
|
-
logger.info("[dry-run] Publish GitLab release for", release.tag_name);
|
|
1569
|
-
} else {
|
|
1570
|
-
logger.debug("Publishing release to GitLab...");
|
|
1571
|
-
await createGitlabRelease({
|
|
1572
|
-
config,
|
|
1573
|
-
release,
|
|
1574
|
-
dryRun
|
|
1575
|
-
});
|
|
1576
|
-
}
|
|
1577
|
-
logger.success(`Release ${to} published to GitLab!`);
|
|
1578
|
-
return [{
|
|
1579
|
-
name: to,
|
|
1580
|
-
tag: to,
|
|
1581
|
-
version: to,
|
|
1582
|
-
prerelease: isPrerelease(newVersion)
|
|
1583
|
-
}];
|
|
1582
|
+
const lastTag = stdout.trim();
|
|
1583
|
+
logger.debug("Last tag:", lastTag || "No tags found");
|
|
1584
|
+
return lastTag;
|
|
1584
1585
|
}
|
|
1585
|
-
async function
|
|
1586
|
+
async function getAllRecentRepoTags(options) {
|
|
1587
|
+
const limit = options?.limit;
|
|
1586
1588
|
try {
|
|
1587
|
-
const
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
return await gitlabIndependentMode({
|
|
1603
|
-
config,
|
|
1604
|
-
dryRun,
|
|
1605
|
-
bumpResult: options.bumpResult,
|
|
1606
|
-
suffix: options.suffix,
|
|
1607
|
-
force: options.force ?? false
|
|
1608
|
-
});
|
|
1609
|
-
}
|
|
1610
|
-
const rootPackageBase = readPackageJson(config.cwd);
|
|
1611
|
-
if (!rootPackageBase) {
|
|
1612
|
-
throw new Error("Failed to read root package.json");
|
|
1613
|
-
}
|
|
1614
|
-
const newVersion = options.bumpResult?.newVersion || rootPackageBase.version;
|
|
1615
|
-
const { from, to } = await resolveTags({
|
|
1616
|
-
config,
|
|
1617
|
-
step: "provider-release",
|
|
1618
|
-
newVersion,
|
|
1619
|
-
pkg: rootPackageBase
|
|
1620
|
-
});
|
|
1621
|
-
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
1622
|
-
config,
|
|
1623
|
-
force: options.force ?? false,
|
|
1624
|
-
suffix: options.suffix,
|
|
1625
|
-
changelog: true,
|
|
1626
|
-
from,
|
|
1627
|
-
to
|
|
1628
|
-
});
|
|
1629
|
-
logger.debug(`Root package: ${getIndependentTag({ name: rootPackage.name, version: newVersion })}`);
|
|
1630
|
-
return await gitlabUnified({
|
|
1631
|
-
config,
|
|
1632
|
-
dryRun,
|
|
1633
|
-
rootPackage,
|
|
1634
|
-
bumpResult: options.bumpResult
|
|
1635
|
-
});
|
|
1636
|
-
} catch (error) {
|
|
1637
|
-
logger.error("Error publishing GitLab release:", error);
|
|
1638
|
-
throw error;
|
|
1589
|
+
const { stdout } = await execPromise(
|
|
1590
|
+
`git tag --sort=-creatordate | head -n ${limit}`,
|
|
1591
|
+
{
|
|
1592
|
+
logLevel: options?.logLevel,
|
|
1593
|
+
noStderr: true,
|
|
1594
|
+
noStdout: true,
|
|
1595
|
+
noSuccess: true,
|
|
1596
|
+
cwd: options?.cwd
|
|
1597
|
+
}
|
|
1598
|
+
);
|
|
1599
|
+
const tags = stdout.trim().split("\n").filter((tag) => tag.length > 0);
|
|
1600
|
+
logger.debug(`Retrieved ${tags.length} recent repo tags`);
|
|
1601
|
+
return tags;
|
|
1602
|
+
} catch {
|
|
1603
|
+
return [];
|
|
1639
1604
|
}
|
|
1640
1605
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1606
|
+
async function getAllRecentPackageTags({
|
|
1607
|
+
packageName,
|
|
1608
|
+
limit = 50,
|
|
1609
|
+
logLevel,
|
|
1610
|
+
cwd
|
|
1611
|
+
}) {
|
|
1612
|
+
try {
|
|
1613
|
+
const escapedPackageName = packageName.replace(/[@/]/g, "\\$&");
|
|
1614
|
+
const { stdout } = await execPromise(
|
|
1615
|
+
`git tag --sort=-creatordate | grep -E '^${escapedPackageName}@' | head -n ${limit}`,
|
|
1616
|
+
{
|
|
1617
|
+
logLevel,
|
|
1618
|
+
noStderr: true,
|
|
1619
|
+
noStdout: true,
|
|
1620
|
+
noSuccess: true,
|
|
1621
|
+
cwd
|
|
1622
|
+
}
|
|
1623
|
+
);
|
|
1624
|
+
const tags = stdout.trim().split("\n").filter((tag) => tag.length > 0);
|
|
1625
|
+
logger.debug(`Retrieved ${tags.length} recent tags for package ${packageName}`);
|
|
1626
|
+
return tags;
|
|
1627
|
+
} catch {
|
|
1628
|
+
return [];
|
|
1629
|
+
}
|
|
1647
1630
|
}
|
|
1648
|
-
function
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1631
|
+
function filterCompatibleTags({
|
|
1632
|
+
tags,
|
|
1633
|
+
currentVersion,
|
|
1634
|
+
onlyStable,
|
|
1635
|
+
packageName
|
|
1636
|
+
}) {
|
|
1637
|
+
const filtered = tags.filter((tag) => {
|
|
1638
|
+
const tagVersion = extractVersionFromTag(tag, packageName);
|
|
1639
|
+
if (!tagVersion) {
|
|
1640
|
+
logger.debug(`Skipping tag ${tag}: cannot extract version`);
|
|
1641
|
+
return false;
|
|
1654
1642
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
} else if (semverType === "minor") {
|
|
1659
|
-
hasMinor = true;
|
|
1660
|
-
} else if (semverType === "patch") {
|
|
1661
|
-
hasPatch = true;
|
|
1643
|
+
if (onlyStable && isPrerelease(tagVersion)) {
|
|
1644
|
+
logger.debug(`Skipping tag ${tag}: prerelease version ${tagVersion} (onlyStable=${onlyStable})`);
|
|
1645
|
+
return false;
|
|
1662
1646
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1647
|
+
if (!isTagVersionCompatibleWithCurrent(tagVersion, currentVersion)) {
|
|
1648
|
+
logger.debug(`Skipping tag ${tag}: version ${tagVersion} has higher major than current ${currentVersion}`);
|
|
1649
|
+
return false;
|
|
1650
|
+
}
|
|
1651
|
+
logger.debug(`Tag ${tag} with version ${tagVersion} is compatible`);
|
|
1652
|
+
return true;
|
|
1653
|
+
});
|
|
1654
|
+
logger.debug(`Filtered ${tags.length} tags down to ${filtered.length} compatible tags`);
|
|
1655
|
+
return filtered;
|
|
1668
1656
|
}
|
|
1669
|
-
function
|
|
1670
|
-
if (
|
|
1671
|
-
return
|
|
1657
|
+
function getLastRepoTag(options) {
|
|
1658
|
+
if (options?.currentVersion) {
|
|
1659
|
+
return getLastRepoTagWithFiltering({
|
|
1660
|
+
currentVersion: options.currentVersion,
|
|
1661
|
+
onlyStable: options.onlyStable ?? false,
|
|
1662
|
+
logLevel: options.logLevel,
|
|
1663
|
+
cwd: options.cwd
|
|
1664
|
+
});
|
|
1672
1665
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
if (isNotUpgrade) {
|
|
1676
|
-
throw new Error(`Unable to graduate from ${currentVersion} to ${testVersion}, it's not a valid prerelease`);
|
|
1666
|
+
if (options?.onlyStable) {
|
|
1667
|
+
return getLastStableTag({ logLevel: options?.logLevel, cwd: options?.cwd });
|
|
1677
1668
|
}
|
|
1669
|
+
return getLastTag({ logLevel: options?.logLevel, cwd: options?.cwd });
|
|
1678
1670
|
}
|
|
1679
|
-
function
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1671
|
+
async function getLastRepoTagWithFiltering({
|
|
1672
|
+
currentVersion,
|
|
1673
|
+
onlyStable,
|
|
1674
|
+
logLevel,
|
|
1675
|
+
cwd
|
|
1676
|
+
}) {
|
|
1677
|
+
const recentTags = await getAllRecentRepoTags({ limit: 50, logLevel, cwd });
|
|
1678
|
+
if (recentTags.length === 0) {
|
|
1679
|
+
logger.debug("No tags found in repository");
|
|
1680
|
+
return null;
|
|
1683
1681
|
}
|
|
1684
|
-
const
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1682
|
+
const compatibleTags = filterCompatibleTags({
|
|
1683
|
+
tags: recentTags,
|
|
1684
|
+
currentVersion,
|
|
1685
|
+
onlyStable
|
|
1686
|
+
});
|
|
1687
|
+
if (compatibleTags.length === 0) {
|
|
1688
|
+
logger.debug("No compatible tags found");
|
|
1689
|
+
return null;
|
|
1688
1690
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
+
const lastTag = compatibleTags[0];
|
|
1692
|
+
logger.debug(`Last compatible repo tag: ${lastTag}`);
|
|
1693
|
+
return lastTag;
|
|
1691
1694
|
}
|
|
1692
|
-
function
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
return
|
|
1695
|
+
async function getLastPackageTag({
|
|
1696
|
+
packageName,
|
|
1697
|
+
onlyStable,
|
|
1698
|
+
currentVersion,
|
|
1699
|
+
logLevel,
|
|
1700
|
+
cwd
|
|
1701
|
+
}) {
|
|
1702
|
+
if (currentVersion) {
|
|
1703
|
+
return getLastPackageTagWithFiltering({
|
|
1704
|
+
packageName,
|
|
1705
|
+
currentVersion,
|
|
1706
|
+
onlyStable: onlyStable ?? false,
|
|
1707
|
+
logLevel,
|
|
1708
|
+
cwd
|
|
1709
|
+
});
|
|
1701
1710
|
}
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
}
|
|
1710
|
-
function handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force }) {
|
|
1711
|
-
const currentPreid = getPreid(currentVersion);
|
|
1712
|
-
const hasChangedPreid = preid && currentPreid && currentPreid !== preid;
|
|
1713
|
-
if (hasChangedPreid) {
|
|
1714
|
-
const testVersion = semver.inc(currentVersion, "prerelease", preid);
|
|
1715
|
-
if (!testVersion) {
|
|
1716
|
-
throw new Error(`Unable to change preid from ${currentPreid} to ${preid} for version ${currentVersion}`);
|
|
1717
|
-
}
|
|
1718
|
-
const isUpgrade = semver.gt(testVersion, currentVersion);
|
|
1719
|
-
if (!isUpgrade) {
|
|
1720
|
-
throw new Error(`Unable to change preid from ${currentVersion} to ${testVersion}, it's not a valid upgrade (cannot downgrade from ${currentPreid} to ${preid})`);
|
|
1711
|
+
try {
|
|
1712
|
+
const escapedPackageName = packageName.replace(/[@/]/g, "\\$&");
|
|
1713
|
+
let grepPattern;
|
|
1714
|
+
if (onlyStable) {
|
|
1715
|
+
grepPattern = `^${escapedPackageName}@[0-9]+\\.[0-9]+\\.[0-9]+$`;
|
|
1716
|
+
} else {
|
|
1717
|
+
grepPattern = `^${escapedPackageName}@`;
|
|
1721
1718
|
}
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1719
|
+
const { stdout } = await execPromise(
|
|
1720
|
+
`git tag --sort=-creatordate | grep -E '${grepPattern}' | sed -n '1p'`,
|
|
1721
|
+
{
|
|
1722
|
+
logLevel,
|
|
1723
|
+
noStderr: true,
|
|
1724
|
+
noStdout: true,
|
|
1725
|
+
noSuccess: true,
|
|
1726
|
+
cwd
|
|
1727
|
+
}
|
|
1728
|
+
);
|
|
1729
|
+
const tag = stdout.trim();
|
|
1730
|
+
return tag || null;
|
|
1731
|
+
} catch {
|
|
1732
|
+
return null;
|
|
1727
1733
|
}
|
|
1728
|
-
logger.debug(`Incrementing prerelease version: ${currentVersion}`);
|
|
1729
|
-
return "prerelease";
|
|
1730
1734
|
}
|
|
1731
|
-
function
|
|
1732
|
-
|
|
1733
|
-
currentVersion
|
|
1735
|
+
async function getLastPackageTagWithFiltering({
|
|
1736
|
+
packageName,
|
|
1737
|
+
currentVersion,
|
|
1738
|
+
onlyStable,
|
|
1739
|
+
logLevel,
|
|
1740
|
+
cwd
|
|
1734
1741
|
}) {
|
|
1735
|
-
const
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1742
|
+
const recentTags = await getAllRecentPackageTags({
|
|
1743
|
+
packageName,
|
|
1744
|
+
limit: 50,
|
|
1745
|
+
logLevel,
|
|
1746
|
+
cwd
|
|
1747
|
+
});
|
|
1748
|
+
if (recentTags.length === 0) {
|
|
1749
|
+
logger.debug(`No tags found for package ${packageName}`);
|
|
1750
|
+
return null;
|
|
1741
1751
|
}
|
|
1742
|
-
|
|
1752
|
+
const compatibleTags = filterCompatibleTags({
|
|
1753
|
+
tags: recentTags,
|
|
1754
|
+
currentVersion,
|
|
1755
|
+
onlyStable,
|
|
1756
|
+
packageName
|
|
1757
|
+
});
|
|
1758
|
+
if (compatibleTags.length === 0) {
|
|
1759
|
+
logger.debug(`No compatible tags found for package ${packageName}`);
|
|
1760
|
+
return null;
|
|
1761
|
+
}
|
|
1762
|
+
const lastTag = compatibleTags[0];
|
|
1763
|
+
logger.debug(`Last compatible package tag for ${packageName}: ${lastTag}`);
|
|
1764
|
+
return lastTag;
|
|
1743
1765
|
}
|
|
1744
|
-
function
|
|
1766
|
+
async function resolveFromTagIndependent({
|
|
1767
|
+
cwd,
|
|
1768
|
+
packageName,
|
|
1745
1769
|
currentVersion,
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
preid,
|
|
1749
|
-
types,
|
|
1750
|
-
force
|
|
1770
|
+
graduating,
|
|
1771
|
+
logLevel
|
|
1751
1772
|
}) {
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
if (!
|
|
1762
|
-
|
|
1763
|
-
return handleStableVersionWithReleaseType(commits, types, force);
|
|
1764
|
-
}
|
|
1765
|
-
if (releaseType === "prerelease") {
|
|
1766
|
-
return handleStableVersionWithPrereleaseType(commits, types, force);
|
|
1767
|
-
}
|
|
1768
|
-
return handleExplicitReleaseType({ releaseType, currentVersion });
|
|
1769
|
-
}
|
|
1770
|
-
if (releaseType === "release") {
|
|
1771
|
-
return handlePrereleaseVersionToStable(currentVersion);
|
|
1772
|
-
}
|
|
1773
|
-
if (releaseType === "prerelease") {
|
|
1774
|
-
return handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force });
|
|
1773
|
+
const filterPrereleases = shouldFilterPrereleaseTags(currentVersion, graduating);
|
|
1774
|
+
const onlyStable = graduating || filterPrereleases;
|
|
1775
|
+
const lastPackageTag = await getLastPackageTag({
|
|
1776
|
+
packageName,
|
|
1777
|
+
currentVersion,
|
|
1778
|
+
onlyStable,
|
|
1779
|
+
logLevel,
|
|
1780
|
+
cwd
|
|
1781
|
+
});
|
|
1782
|
+
if (!lastPackageTag) {
|
|
1783
|
+
return getFirstCommit(cwd);
|
|
1775
1784
|
}
|
|
1776
|
-
return
|
|
1785
|
+
return lastPackageTag;
|
|
1777
1786
|
}
|
|
1778
|
-
function
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
} catch (error) {
|
|
1794
|
-
throw new Error(`Unable to write version to ${packageJsonPath}: ${error}`);
|
|
1795
|
-
}
|
|
1787
|
+
async function resolveFromTagUnified({
|
|
1788
|
+
config,
|
|
1789
|
+
currentVersion,
|
|
1790
|
+
graduating,
|
|
1791
|
+
logLevel
|
|
1792
|
+
}) {
|
|
1793
|
+
const filterPrereleases = shouldFilterPrereleaseTags(currentVersion, graduating);
|
|
1794
|
+
const onlyStable = graduating || filterPrereleases;
|
|
1795
|
+
const from = await getLastRepoTag({
|
|
1796
|
+
currentVersion,
|
|
1797
|
+
onlyStable,
|
|
1798
|
+
logLevel,
|
|
1799
|
+
cwd: config.cwd
|
|
1800
|
+
}) || getFirstCommit(config.cwd);
|
|
1801
|
+
return from;
|
|
1796
1802
|
}
|
|
1797
|
-
function
|
|
1798
|
-
|
|
1803
|
+
async function resolveFromTag({
|
|
1804
|
+
config,
|
|
1805
|
+
versionMode,
|
|
1806
|
+
step,
|
|
1807
|
+
packageName,
|
|
1799
1808
|
currentVersion,
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
suffix
|
|
1809
|
+
graduating,
|
|
1810
|
+
logLevel
|
|
1803
1811
|
}) {
|
|
1804
|
-
let
|
|
1805
|
-
if (
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1812
|
+
let from;
|
|
1813
|
+
if (versionMode === "independent") {
|
|
1814
|
+
if (!packageName) {
|
|
1815
|
+
throw new Error("Package name is required for independent version mode");
|
|
1816
|
+
}
|
|
1817
|
+
from = await resolveFromTagIndependent({
|
|
1818
|
+
cwd: config.cwd,
|
|
1819
|
+
packageName,
|
|
1820
|
+
currentVersion,
|
|
1821
|
+
graduating,
|
|
1822
|
+
logLevel
|
|
1823
|
+
});
|
|
1824
|
+
} else {
|
|
1825
|
+
from = await resolveFromTagUnified({
|
|
1826
|
+
config,
|
|
1827
|
+
currentVersion,
|
|
1828
|
+
graduating,
|
|
1829
|
+
logLevel
|
|
1830
|
+
});
|
|
1822
1831
|
}
|
|
1823
|
-
|
|
1832
|
+
logger.debug(`[${versionMode}](${step}) Using from tag: ${from}`);
|
|
1833
|
+
return config.from || from;
|
|
1824
1834
|
}
|
|
1825
|
-
function
|
|
1826
|
-
|
|
1835
|
+
function resolveToTag({
|
|
1836
|
+
config,
|
|
1827
1837
|
versionMode,
|
|
1828
|
-
|
|
1829
|
-
|
|
1838
|
+
newVersion,
|
|
1839
|
+
step,
|
|
1840
|
+
packageName
|
|
1830
1841
|
}) {
|
|
1831
|
-
const
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
}
|
|
1839
|
-
try {
|
|
1840
|
-
logger.debug("Updating lerna.json version");
|
|
1841
|
-
const content = readFileSync(lernaJsonPath, "utf8");
|
|
1842
|
-
const lernaJson = JSON.parse(content);
|
|
1843
|
-
const oldVersion = lernaJson.version;
|
|
1844
|
-
if (lernaJson.version === "independent" || versionMode === "independent") {
|
|
1845
|
-
logger.debug("Lerna version is independent or version mode is independent, skipping update");
|
|
1846
|
-
return;
|
|
1842
|
+
const isUntaggedStep = step === "bump" || step === "changelog";
|
|
1843
|
+
let to;
|
|
1844
|
+
if (isUntaggedStep) {
|
|
1845
|
+
to = getCurrentGitRef(config.cwd);
|
|
1846
|
+
} else if (versionMode === "independent") {
|
|
1847
|
+
if (!packageName) {
|
|
1848
|
+
throw new Error("Package name is required for independent version mode");
|
|
1847
1849
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
logger.info(`[dry-run] update lerna.json: ${oldVersion} \u2192 ${version}`);
|
|
1851
|
-
return;
|
|
1850
|
+
if (!newVersion) {
|
|
1851
|
+
throw new Error("New version is required for independent version mode");
|
|
1852
1852
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
} catch (error) {
|
|
1857
|
-
logger.fail(`Unable to update lerna.json: ${error}`);
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
function extractVersionFromPackageTag(tag) {
|
|
1861
|
-
const atIndex = tag.lastIndexOf("@");
|
|
1862
|
-
if (atIndex === -1) {
|
|
1863
|
-
return null;
|
|
1864
|
-
}
|
|
1865
|
-
return tag.slice(atIndex + 1);
|
|
1866
|
-
}
|
|
1867
|
-
function isPrerelease(version) {
|
|
1868
|
-
if (!version)
|
|
1869
|
-
return false;
|
|
1870
|
-
const prerelease = semver.prerelease(version);
|
|
1871
|
-
return prerelease ? prerelease.length > 0 : false;
|
|
1872
|
-
}
|
|
1873
|
-
function isStableReleaseType(releaseType) {
|
|
1874
|
-
const stableTypes = ["release", "major", "minor", "patch"];
|
|
1875
|
-
return stableTypes.includes(releaseType);
|
|
1876
|
-
}
|
|
1877
|
-
function isPrereleaseReleaseType(releaseType) {
|
|
1878
|
-
const prereleaseTypes = ["prerelease", "premajor", "preminor", "prepatch"];
|
|
1879
|
-
return prereleaseTypes.includes(releaseType);
|
|
1880
|
-
}
|
|
1881
|
-
function isGraduating(currentVersion, releaseType) {
|
|
1882
|
-
return isPrerelease(currentVersion) && isStableReleaseType(releaseType);
|
|
1883
|
-
}
|
|
1884
|
-
function getPreid(version) {
|
|
1885
|
-
if (!version)
|
|
1886
|
-
return null;
|
|
1887
|
-
const prerelease = semver.prerelease(version);
|
|
1888
|
-
if (!prerelease || prerelease.length === 0) {
|
|
1889
|
-
return null;
|
|
1890
|
-
}
|
|
1891
|
-
return prerelease[0];
|
|
1892
|
-
}
|
|
1893
|
-
function isChangedPreid(currentVersion, targetPreid) {
|
|
1894
|
-
if (!targetPreid || !isPrerelease(currentVersion)) {
|
|
1895
|
-
return false;
|
|
1896
|
-
}
|
|
1897
|
-
const currentPreid = getPreid(currentVersion);
|
|
1898
|
-
if (!currentPreid) {
|
|
1899
|
-
return false;
|
|
1853
|
+
to = getIndependentTag({ version: newVersion, name: packageName });
|
|
1854
|
+
} else {
|
|
1855
|
+
to = newVersion ? config.templates.tagBody.replace("{{newVersion}}", newVersion) : getCurrentGitRef(config.cwd);
|
|
1900
1856
|
}
|
|
1901
|
-
|
|
1857
|
+
logger.debug(`[${versionMode}](${step}) Using to tag: ${to}`);
|
|
1858
|
+
return config.to || to;
|
|
1902
1859
|
}
|
|
1903
|
-
function
|
|
1860
|
+
async function resolveTags({
|
|
1861
|
+
config,
|
|
1862
|
+
step,
|
|
1904
1863
|
pkg,
|
|
1905
|
-
|
|
1864
|
+
newVersion
|
|
1906
1865
|
}) {
|
|
1907
|
-
|
|
1908
|
-
const
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
newVersion,
|
|
1921
|
-
dryRun,
|
|
1922
|
-
lernaJsonExists
|
|
1923
|
-
}) {
|
|
1924
|
-
if (versionMode !== "independent" && currentVersion && newVersion) {
|
|
1925
|
-
logger.log(`${dryRun ? "[dry-run] " : ""}Root package.json: ${currentVersion} \u2192 ${newVersion}`);
|
|
1926
|
-
logger.log("");
|
|
1927
|
-
if (lernaJsonExists) {
|
|
1928
|
-
logger.log(`${dryRun ? "[dry-run] " : ""}lerna.json: ${currentVersion} \u2192 ${newVersion}`);
|
|
1929
|
-
logger.log("");
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
function displayUnifiedModePackages({
|
|
1934
|
-
packages,
|
|
1935
|
-
newVersion,
|
|
1936
|
-
force
|
|
1937
|
-
}) {
|
|
1938
|
-
logger.log(`${packages.length} package(s):`);
|
|
1939
|
-
packages.forEach((pkg) => {
|
|
1940
|
-
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
1866
|
+
const versionMode = config.monorepo?.versionMode || "standalone";
|
|
1867
|
+
const logLevel = config.logLevel;
|
|
1868
|
+
logger.debug(`[${versionMode}](${step}) Resolving tags`);
|
|
1869
|
+
const releaseType = config.bump.type;
|
|
1870
|
+
const graduating = typeof newVersion === "string" ? isGraduatingToStableBetweenVersion(pkg.version, newVersion) : isGraduating(pkg.version, releaseType);
|
|
1871
|
+
const from = await resolveFromTag({
|
|
1872
|
+
config,
|
|
1873
|
+
versionMode,
|
|
1874
|
+
step,
|
|
1875
|
+
packageName: pkg.name,
|
|
1876
|
+
currentVersion: pkg.version,
|
|
1877
|
+
graduating,
|
|
1878
|
+
logLevel
|
|
1941
1879
|
});
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
})
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
packages.forEach((pkg) => {
|
|
1952
|
-
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} (force)`);
|
|
1953
|
-
});
|
|
1954
|
-
logger.log("");
|
|
1955
|
-
} else {
|
|
1956
|
-
const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
|
|
1957
|
-
const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
|
|
1958
|
-
const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
|
|
1959
|
-
if (packagesWithCommits.length > 0) {
|
|
1960
|
-
logger.log(`${packagesWithCommits.length} package(s) with commits:`);
|
|
1961
|
-
packagesWithCommits.forEach((pkg) => {
|
|
1962
|
-
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} (${pkg.commits.length} commits) ${force ? "(force)" : ""}`);
|
|
1963
|
-
});
|
|
1964
|
-
logger.log("");
|
|
1965
|
-
}
|
|
1966
|
-
if (packagesAsDependents.length > 0) {
|
|
1967
|
-
logger.log(`${packagesAsDependents.length} dependent package(s):`);
|
|
1968
|
-
packagesAsDependents.forEach((pkg) => {
|
|
1969
|
-
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
1970
|
-
});
|
|
1971
|
-
logger.log("");
|
|
1972
|
-
}
|
|
1973
|
-
if (packagesAsGraduation.length > 0) {
|
|
1974
|
-
logger.log(`${packagesAsGraduation.length} graduation package(s):`);
|
|
1975
|
-
packagesAsGraduation.forEach((pkg) => {
|
|
1976
|
-
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
|
|
1977
|
-
});
|
|
1978
|
-
logger.log("");
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1880
|
+
const to = resolveToTag({
|
|
1881
|
+
config,
|
|
1882
|
+
versionMode,
|
|
1883
|
+
newVersion,
|
|
1884
|
+
step,
|
|
1885
|
+
packageName: pkg.name
|
|
1886
|
+
});
|
|
1887
|
+
logger.debug(`[${versionMode}](${step}) Using tags: ${from} \u2192 ${to}`);
|
|
1888
|
+
return { from, to };
|
|
1981
1889
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
}) {
|
|
1986
|
-
if (force) {
|
|
1987
|
-
logger.log(`${packages.length} package(s):`);
|
|
1988
|
-
packages.forEach((pkg) => {
|
|
1989
|
-
logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} (force)`);
|
|
1990
|
-
});
|
|
1991
|
-
logger.log("");
|
|
1992
|
-
} else {
|
|
1993
|
-
const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
|
|
1994
|
-
const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
|
|
1995
|
-
const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
|
|
1996
|
-
if (packagesWithCommits.length > 0) {
|
|
1997
|
-
logger.log(`${packagesWithCommits.length} package(s) with commits:`);
|
|
1998
|
-
packagesWithCommits.forEach((pkg) => {
|
|
1999
|
-
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} (${pkg.commits.length} commits) ${force ? "(force)" : ""}`);
|
|
2000
|
-
});
|
|
2001
|
-
logger.log("");
|
|
2002
|
-
}
|
|
2003
|
-
if (packagesAsDependents.length > 0) {
|
|
2004
|
-
logger.log(`${packagesAsDependents.length} dependent package(s):`);
|
|
2005
|
-
packagesAsDependents.forEach((pkg) => {
|
|
2006
|
-
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} ${force ? "(force)" : ""}`);
|
|
2007
|
-
});
|
|
2008
|
-
logger.log("");
|
|
2009
|
-
}
|
|
2010
|
-
if (packagesAsGraduation.length > 0) {
|
|
2011
|
-
logger.log(`${packagesAsGraduation.length} graduation package(s):`);
|
|
2012
|
-
packagesAsGraduation.forEach((pkg) => {
|
|
2013
|
-
pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} ${force ? "(force)" : ""}`);
|
|
2014
|
-
});
|
|
2015
|
-
logger.log("");
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
1890
|
+
|
|
1891
|
+
function fromTagIsFirstCommit(fromTag, cwd) {
|
|
1892
|
+
return fromTag === getFirstCommit(cwd);
|
|
2018
1893
|
}
|
|
2019
|
-
async function
|
|
2020
|
-
|
|
1894
|
+
async function generateChangelog({
|
|
1895
|
+
pkg,
|
|
2021
1896
|
config,
|
|
2022
|
-
|
|
2023
|
-
force,
|
|
2024
|
-
currentVersion,
|
|
1897
|
+
dryRun,
|
|
2025
1898
|
newVersion,
|
|
2026
|
-
|
|
1899
|
+
minify
|
|
2027
1900
|
}) {
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
1901
|
+
let fromTag = config.from || pkg.fromTag || getFirstCommit(config.cwd);
|
|
1902
|
+
const isFirstCommit = fromTagIsFirstCommit(fromTag, config.cwd);
|
|
1903
|
+
if (isFirstCommit) {
|
|
1904
|
+
fromTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: "0.0.0", name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", "0.0.0");
|
|
2031
1905
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
currentVersion,
|
|
2039
|
-
newVersion,
|
|
2040
|
-
lernaJsonExists,
|
|
2041
|
-
dryRun
|
|
2042
|
-
});
|
|
2043
|
-
if (versionMode === "unified") {
|
|
2044
|
-
if (!newVersion) {
|
|
2045
|
-
throw new Error("Cannot confirm bump in unified mode without a new version");
|
|
2046
|
-
}
|
|
2047
|
-
displayUnifiedModePackages({ packages, newVersion, force });
|
|
2048
|
-
} else if (versionMode === "selective") {
|
|
2049
|
-
if (!newVersion) {
|
|
2050
|
-
throw new Error("Cannot confirm bump in selective mode without a new version");
|
|
2051
|
-
}
|
|
2052
|
-
displaySelectiveModePackages({ packages, newVersion, force });
|
|
2053
|
-
} else if (versionMode === "independent") {
|
|
2054
|
-
displayIndependentModePackages({ packages, force });
|
|
1906
|
+
let toTag = config.to;
|
|
1907
|
+
if (!toTag) {
|
|
1908
|
+
toTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: newVersion, name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
1909
|
+
}
|
|
1910
|
+
if (!toTag) {
|
|
1911
|
+
throw new Error(`No tag found for ${pkg.name}`);
|
|
2055
1912
|
}
|
|
1913
|
+
logger.debug(`Generating changelog for ${pkg.name} - from ${fromTag} to ${toTag}`);
|
|
2056
1914
|
try {
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
1915
|
+
config = {
|
|
1916
|
+
...config,
|
|
1917
|
+
from: fromTag,
|
|
1918
|
+
to: toTag
|
|
1919
|
+
};
|
|
1920
|
+
const generatedChangelog = await generateMarkDown({
|
|
1921
|
+
commits: pkg.commits,
|
|
1922
|
+
config,
|
|
1923
|
+
from: fromTag,
|
|
1924
|
+
isFirstCommit,
|
|
1925
|
+
to: toTag,
|
|
1926
|
+
minify
|
|
2060
1927
|
});
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
} catch (error) {
|
|
2067
|
-
const userHasExited = error instanceof Error && error.name === "ExitPromptError";
|
|
2068
|
-
if (userHasExited) {
|
|
2069
|
-
logger.log("");
|
|
2070
|
-
logger.fail("Bump cancelled");
|
|
2071
|
-
process.exit(0);
|
|
1928
|
+
let changelog = generatedChangelog;
|
|
1929
|
+
if (pkg.commits.length === 0) {
|
|
1930
|
+
changelog = `${changelog}
|
|
1931
|
+
|
|
1932
|
+
${config.templates.emptyChangelogContent}`;
|
|
2072
1933
|
}
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
logger.log("");
|
|
2077
|
-
}
|
|
2078
|
-
function getBumpedIndependentPackages({
|
|
2079
|
-
packages,
|
|
2080
|
-
dryRun
|
|
2081
|
-
}) {
|
|
2082
|
-
const bumpedPackages = [];
|
|
2083
|
-
for (const pkgToBump of packages) {
|
|
2084
|
-
logger.debug(`Bumping ${pkgToBump.name} from ${pkgToBump.version} to ${pkgToBump.newVersion} (reason: ${pkgToBump.reason})`);
|
|
2085
|
-
const result = getBumpedPackageIndependently({
|
|
2086
|
-
pkg: pkgToBump,
|
|
2087
|
-
dryRun
|
|
1934
|
+
const changelogResult = await executeHook("generate:changelog", config, dryRun, {
|
|
1935
|
+
commits: pkg.commits,
|
|
1936
|
+
changelog
|
|
2088
1937
|
});
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
1938
|
+
changelog = changelogResult || changelog;
|
|
1939
|
+
logger.verbose(`Output changelog for ${pkg.name}:
|
|
1940
|
+
${changelog}`);
|
|
1941
|
+
logger.debug(`Changelog generated for ${pkg.name} (${pkg.commits.length} commits)`);
|
|
1942
|
+
logger.verbose(`Final changelog for ${pkg.name}:
|
|
1943
|
+
|
|
1944
|
+
${changelog}
|
|
1945
|
+
|
|
1946
|
+
`);
|
|
1947
|
+
if (dryRun) {
|
|
1948
|
+
logger.info(`[dry-run] ${pkg.name} - Generate changelog ${fromTag}...${toTag}`);
|
|
2094
1949
|
}
|
|
1950
|
+
return changelog;
|
|
1951
|
+
} catch (error) {
|
|
1952
|
+
throw new Error(`Error generating changelog for ${pkg.name} (${fromTag}...${toTag}): ${error}`);
|
|
2095
1953
|
}
|
|
2096
|
-
return bumpedPackages;
|
|
2097
1954
|
}
|
|
2098
|
-
function
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
1955
|
+
function writeChangelogToFile({
|
|
1956
|
+
cwd,
|
|
1957
|
+
pkg,
|
|
1958
|
+
changelog,
|
|
1959
|
+
dryRun = false
|
|
1960
|
+
}) {
|
|
1961
|
+
const changelogPath = join(pkg.path, "CHANGELOG.md");
|
|
1962
|
+
let existingChangelog = "";
|
|
1963
|
+
if (existsSync(changelogPath)) {
|
|
1964
|
+
existingChangelog = readFileSync(changelogPath, "utf8");
|
|
2104
1965
|
}
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
}
|
|
2118
|
-
if (/^\d+\.\d+\.\d+/.test(tag)) {
|
|
2119
|
-
return tag;
|
|
1966
|
+
const lines = existingChangelog.split("\n");
|
|
1967
|
+
const titleIndex = lines.findIndex((line) => line.startsWith("# "));
|
|
1968
|
+
let updatedChangelog;
|
|
1969
|
+
if (titleIndex !== -1) {
|
|
1970
|
+
const beforeTitle = lines.slice(0, titleIndex + 1);
|
|
1971
|
+
const afterTitle = lines.slice(titleIndex + 1);
|
|
1972
|
+
updatedChangelog = [...beforeTitle, "", changelog, "", ...afterTitle].join("\n");
|
|
1973
|
+
} else {
|
|
1974
|
+
const title = "# Changelog\n";
|
|
1975
|
+
updatedChangelog = `${title}
|
|
1976
|
+
${changelog}
|
|
1977
|
+
${existingChangelog}`;
|
|
2120
1978
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
} catch {
|
|
2129
|
-
return false;
|
|
1979
|
+
if (dryRun) {
|
|
1980
|
+
const relativeChangelogPath = relative(cwd, changelogPath);
|
|
1981
|
+
logger.info(`[dry-run] ${pkg.name} - Write changelog to ${relativeChangelogPath}`);
|
|
1982
|
+
} else {
|
|
1983
|
+
logger.debug(`Writing changelog to ${changelogPath}`);
|
|
1984
|
+
writeFileSync(changelogPath, updatedChangelog, "utf8");
|
|
1985
|
+
logger.info(`Changelog updated for ${pkg.name} (${"newVersion" in pkg && pkg.newVersion || pkg.version})`);
|
|
2130
1986
|
}
|
|
2131
1987
|
}
|
|
2132
1988
|
|
|
2133
|
-
function
|
|
2134
|
-
return
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
1989
|
+
function getDefaultConfig() {
|
|
1990
|
+
return {
|
|
1991
|
+
cwd: process$1.cwd(),
|
|
1992
|
+
types: {
|
|
1993
|
+
feat: { title: "\u{1F680} Enhancements", semver: "minor" },
|
|
1994
|
+
perf: { title: "\u{1F525} Performance", semver: "patch" },
|
|
1995
|
+
fix: { title: "\u{1FA79} Fixes", semver: "patch" },
|
|
1996
|
+
refactor: { title: "\u{1F485} Refactors", semver: "patch" },
|
|
1997
|
+
docs: { title: "\u{1F4D6} Documentation", semver: "patch" },
|
|
1998
|
+
build: { title: "\u{1F4E6} Build", semver: "patch" },
|
|
1999
|
+
types: { title: "\u{1F30A} Types", semver: "patch" },
|
|
2000
|
+
chore: { title: "\u{1F3E1} Chore" },
|
|
2001
|
+
examples: { title: "\u{1F3C0} Examples" },
|
|
2002
|
+
test: { title: "\u2705 Tests" },
|
|
2003
|
+
style: { title: "\u{1F3A8} Styles" },
|
|
2004
|
+
ci: { title: "\u{1F916} CI" }
|
|
2005
|
+
},
|
|
2006
|
+
templates: {
|
|
2007
|
+
commitMessage: "chore(release): bump version to {{newVersion}}",
|
|
2008
|
+
tagMessage: "Bump version to {{newVersion}}",
|
|
2009
|
+
tagBody: "v{{newVersion}}",
|
|
2010
|
+
emptyChangelogContent: "No relevant changes for this release",
|
|
2011
|
+
twitterMessage: "\u{1F680} {{projectName}} {{version}} is out!\n\n{{changelog}}\n\n{{releaseUrl}}\n{{changelogUrl}}",
|
|
2012
|
+
slackMessage: void 0
|
|
2013
|
+
// Use rich blocks format by default (no template)
|
|
2014
|
+
},
|
|
2015
|
+
excludeAuthors: [],
|
|
2016
|
+
noAuthors: false,
|
|
2017
|
+
bump: {
|
|
2018
|
+
type: "release",
|
|
2019
|
+
clean: true,
|
|
2020
|
+
dependencyTypes: ["dependencies"],
|
|
2021
|
+
yes: false
|
|
2022
|
+
},
|
|
2023
|
+
changelog: {
|
|
2024
|
+
rootChangelog: true,
|
|
2025
|
+
includeCommitBody: true
|
|
2026
|
+
},
|
|
2027
|
+
publish: {
|
|
2028
|
+
private: false,
|
|
2029
|
+
args: [],
|
|
2030
|
+
token: process$1.env.RELIZY_NPM_TOKEN || process$1.env.NPM_TOKEN || process$1.env.NODE_AUTH_TOKEN,
|
|
2031
|
+
registry: "https://registry.npmjs.org/",
|
|
2032
|
+
safetyCheck: false
|
|
2033
|
+
},
|
|
2034
|
+
tokens: {
|
|
2035
|
+
registry: process$1.env.RELIZY_NPM_TOKEN || process$1.env.NPM_TOKEN || process$1.env.NODE_AUTH_TOKEN,
|
|
2036
|
+
gitlab: process$1.env.RELIZY_GITLAB_TOKEN || process$1.env.GITLAB_TOKEN || process$1.env.GITLAB_API_TOKEN || process$1.env.CI_JOB_TOKEN,
|
|
2037
|
+
github: process$1.env.RELIZY_GITHUB_TOKEN || process$1.env.GITHUB_TOKEN || process$1.env.GH_TOKEN,
|
|
2038
|
+
twitter: {
|
|
2039
|
+
apiKey: process$1.env.RELIZY_TWITTER_API_KEY || process$1.env.TWITTER_API_KEY,
|
|
2040
|
+
apiKeySecret: process$1.env.RELIZY_TWITTER_API_KEY_SECRET || process$1.env.TWITTER_API_KEY_SECRET,
|
|
2041
|
+
accessToken: process$1.env.RELIZY_TWITTER_ACCESS_TOKEN || process$1.env.TWITTER_ACCESS_TOKEN,
|
|
2042
|
+
accessTokenSecret: process$1.env.RELIZY_TWITTER_ACCESS_TOKEN_SECRET || process$1.env.TWITTER_ACCESS_TOKEN_SECRET
|
|
2043
|
+
},
|
|
2044
|
+
slack: process$1.env.RELIZY_SLACK_TOKEN || process$1.env.SLACK_TOKEN
|
|
2045
|
+
},
|
|
2046
|
+
scopeMap: {},
|
|
2047
|
+
release: {
|
|
2048
|
+
commit: true,
|
|
2049
|
+
publish: true,
|
|
2050
|
+
changelog: true,
|
|
2051
|
+
push: true,
|
|
2052
|
+
clean: true,
|
|
2053
|
+
providerRelease: true,
|
|
2054
|
+
noVerify: false,
|
|
2055
|
+
gitTag: true,
|
|
2056
|
+
social: true
|
|
2057
|
+
},
|
|
2058
|
+
social: {
|
|
2059
|
+
twitter: {
|
|
2060
|
+
enabled: false,
|
|
2061
|
+
onlyStable: true
|
|
2062
|
+
},
|
|
2063
|
+
slack: {
|
|
2064
|
+
enabled: false,
|
|
2065
|
+
onlyStable: true
|
|
2174
2066
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
} catch {
|
|
2180
|
-
return [];
|
|
2181
|
-
}
|
|
2067
|
+
},
|
|
2068
|
+
logLevel: "default",
|
|
2069
|
+
safetyCheck: true
|
|
2070
|
+
};
|
|
2182
2071
|
}
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
cwd
|
|
2188
|
-
}) {
|
|
2189
|
-
try {
|
|
2190
|
-
const escapedPackageName = packageName.replace(/[@/]/g, "\\$&");
|
|
2191
|
-
const { stdout } = await execPromise(
|
|
2192
|
-
`git tag --sort=-creatordate | grep -E '^${escapedPackageName}@' | head -n ${limit}`,
|
|
2193
|
-
{
|
|
2194
|
-
logLevel,
|
|
2195
|
-
noStderr: true,
|
|
2196
|
-
noStdout: true,
|
|
2197
|
-
noSuccess: true,
|
|
2198
|
-
cwd
|
|
2199
|
-
}
|
|
2200
|
-
);
|
|
2201
|
-
const tags = stdout.trim().split("\n").filter((tag) => tag.length > 0);
|
|
2202
|
-
logger.debug(`Retrieved ${tags.length} recent tags for package ${packageName}`);
|
|
2203
|
-
return tags;
|
|
2204
|
-
} catch {
|
|
2205
|
-
return [];
|
|
2072
|
+
function setupLogger(logLevel) {
|
|
2073
|
+
if (logLevel) {
|
|
2074
|
+
logger.setLevel(logLevel);
|
|
2075
|
+
logger.debug(`Log level set to: ${logLevel}`);
|
|
2206
2076
|
}
|
|
2207
2077
|
}
|
|
2208
|
-
function
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
const tagVersion = extractVersionFromTag(tag, packageName);
|
|
2216
|
-
if (!tagVersion) {
|
|
2217
|
-
logger.debug(`Skipping tag ${tag}: cannot extract version`);
|
|
2218
|
-
return false;
|
|
2219
|
-
}
|
|
2220
|
-
if (onlyStable && isPrerelease(tagVersion)) {
|
|
2221
|
-
logger.debug(`Skipping tag ${tag}: prerelease version ${tagVersion} (onlyStable=${onlyStable})`);
|
|
2222
|
-
return false;
|
|
2223
|
-
}
|
|
2224
|
-
if (!isTagVersionCompatibleWithCurrent(tagVersion, currentVersion)) {
|
|
2225
|
-
logger.debug(`Skipping tag ${tag}: version ${tagVersion} has higher major than current ${currentVersion}`);
|
|
2226
|
-
return false;
|
|
2227
|
-
}
|
|
2228
|
-
logger.debug(`Tag ${tag} with version ${tagVersion} is compatible`);
|
|
2229
|
-
return true;
|
|
2230
|
-
});
|
|
2231
|
-
logger.debug(`Filtered ${tags.length} tags down to ${filtered.length} compatible tags`);
|
|
2232
|
-
return filtered;
|
|
2233
|
-
}
|
|
2234
|
-
function getLastRepoTag(options) {
|
|
2235
|
-
if (options?.currentVersion) {
|
|
2236
|
-
return getLastRepoTagWithFiltering({
|
|
2237
|
-
currentVersion: options.currentVersion,
|
|
2238
|
-
onlyStable: options.onlyStable ?? false,
|
|
2239
|
-
logLevel: options.logLevel,
|
|
2240
|
-
cwd: options.cwd
|
|
2241
|
-
});
|
|
2078
|
+
async function resolveConfig(config, cwd) {
|
|
2079
|
+
if (!config.repo) {
|
|
2080
|
+
const resolvedRepoConfig = await resolveRepoConfig(cwd);
|
|
2081
|
+
config.repo = {
|
|
2082
|
+
...resolvedRepoConfig,
|
|
2083
|
+
provider: resolvedRepoConfig.provider
|
|
2084
|
+
};
|
|
2242
2085
|
}
|
|
2243
|
-
if (
|
|
2244
|
-
|
|
2086
|
+
if (typeof config.repo === "string") {
|
|
2087
|
+
const resolvedRepoConfig = getRepoConfig(config.repo);
|
|
2088
|
+
config.repo = {
|
|
2089
|
+
...resolvedRepoConfig,
|
|
2090
|
+
provider: resolvedRepoConfig.provider
|
|
2091
|
+
};
|
|
2245
2092
|
}
|
|
2246
|
-
return
|
|
2093
|
+
return config;
|
|
2247
2094
|
}
|
|
2248
|
-
async function
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
const
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
currentVersion,
|
|
2262
|
-
onlyStable
|
|
2095
|
+
async function loadRelizyConfig(options) {
|
|
2096
|
+
const cwd = options?.overrides?.cwd ?? process$1.cwd();
|
|
2097
|
+
await setupDotenv({ cwd });
|
|
2098
|
+
const configFile = options?.configFile ?? "relizy";
|
|
2099
|
+
const defaultConfig = getDefaultConfig();
|
|
2100
|
+
const overridesConfig = defu(options?.overrides, options?.baseConfig);
|
|
2101
|
+
const results = await loadConfig({
|
|
2102
|
+
dotenv: true,
|
|
2103
|
+
cwd,
|
|
2104
|
+
name: configFile,
|
|
2105
|
+
packageJson: true,
|
|
2106
|
+
defaults: defaultConfig,
|
|
2107
|
+
overrides: overridesConfig
|
|
2263
2108
|
});
|
|
2264
|
-
if (
|
|
2265
|
-
logger.debug(
|
|
2266
|
-
|
|
2109
|
+
if (typeof results._configFile !== "string") {
|
|
2110
|
+
logger.debug(`No config file found with name "${configFile}"`);
|
|
2111
|
+
if (options?.configFile) {
|
|
2112
|
+
logger.error(`No config file found with name "${configFile}"`);
|
|
2113
|
+
process$1.exit(1);
|
|
2114
|
+
}
|
|
2267
2115
|
}
|
|
2268
|
-
|
|
2269
|
-
logger.
|
|
2270
|
-
|
|
2116
|
+
setupLogger(options?.overrides?.logLevel || results.config.logLevel);
|
|
2117
|
+
logger.verbose("User config:", formatJson(results.config.changelog));
|
|
2118
|
+
const resolvedConfig = await resolveConfig(results.config, cwd);
|
|
2119
|
+
logger.debug("Resolved config:", formatJson(resolvedConfig));
|
|
2120
|
+
return resolvedConfig;
|
|
2271
2121
|
}
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2122
|
+
function defineConfig(config) {
|
|
2123
|
+
return config;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
async function githubIndependentMode({
|
|
2127
|
+
config,
|
|
2128
|
+
dryRun,
|
|
2129
|
+
bumpResult,
|
|
2130
|
+
force,
|
|
2131
|
+
suffix
|
|
2278
2132
|
}) {
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
currentVersion,
|
|
2283
|
-
onlyStable: onlyStable ?? false,
|
|
2284
|
-
logLevel,
|
|
2285
|
-
cwd
|
|
2286
|
-
});
|
|
2133
|
+
const repoConfig = config.repo;
|
|
2134
|
+
if (!repoConfig) {
|
|
2135
|
+
throw new Error("No repository configuration found. Please check your changelog config.");
|
|
2287
2136
|
}
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2137
|
+
logger.debug(`GitHub token: ${config.tokens.github || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
2138
|
+
if (!config.tokens.github && !config.repo?.token) {
|
|
2139
|
+
throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
|
|
2140
|
+
}
|
|
2141
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
2142
|
+
config,
|
|
2143
|
+
bumpResult,
|
|
2144
|
+
suffix,
|
|
2145
|
+
force
|
|
2146
|
+
});
|
|
2147
|
+
logger.info(`Creating ${packages.length} GitHub release(s)`);
|
|
2148
|
+
const postedReleases = [];
|
|
2149
|
+
for (const pkg of packages) {
|
|
2150
|
+
const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
|
|
2151
|
+
const from = config.from || pkg.fromTag;
|
|
2152
|
+
const to = config.to || getIndependentTag({ version: newVersion, name: pkg.name });
|
|
2153
|
+
if (!from) {
|
|
2154
|
+
logger.warn(`No from tag found for ${pkg.name}, skipping release`);
|
|
2155
|
+
continue;
|
|
2295
2156
|
}
|
|
2296
|
-
const
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2157
|
+
const toTag = dryRun ? "HEAD" : to;
|
|
2158
|
+
logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${toTag}`);
|
|
2159
|
+
const changelog = await generateChangelog({
|
|
2160
|
+
pkg,
|
|
2161
|
+
config,
|
|
2162
|
+
dryRun,
|
|
2163
|
+
newVersion
|
|
2164
|
+
});
|
|
2165
|
+
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
2166
|
+
const release = {
|
|
2167
|
+
tag_name: to,
|
|
2168
|
+
name: to,
|
|
2169
|
+
body: releaseBody,
|
|
2170
|
+
prerelease: isPrerelease(newVersion)
|
|
2171
|
+
};
|
|
2172
|
+
logger.debug(`Creating release for ${to}${release.prerelease ? " (prerelease)" : ""}`);
|
|
2173
|
+
if (dryRun) {
|
|
2174
|
+
logger.info(`[dry-run] Publish GitHub release for ${to}`);
|
|
2175
|
+
postedReleases.push({
|
|
2176
|
+
name: pkg.name,
|
|
2177
|
+
tag: release.tag_name,
|
|
2178
|
+
version: newVersion,
|
|
2179
|
+
prerelease: release.prerelease
|
|
2180
|
+
});
|
|
2181
|
+
} else {
|
|
2182
|
+
logger.debug(`Publishing release ${to} to GitHub...`);
|
|
2183
|
+
await createGithubRelease({
|
|
2184
|
+
...config,
|
|
2185
|
+
from,
|
|
2186
|
+
to,
|
|
2187
|
+
repo: repoConfig
|
|
2188
|
+
}, release);
|
|
2189
|
+
postedReleases.push({
|
|
2190
|
+
name: pkg.name,
|
|
2191
|
+
tag: release.tag_name,
|
|
2192
|
+
version: newVersion,
|
|
2193
|
+
prerelease: release.prerelease
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
if (postedReleases.length === 0) {
|
|
2198
|
+
logger.warn("No releases created");
|
|
2199
|
+
} else {
|
|
2200
|
+
logger.success(`Releases ${postedReleases.map((r) => r.tag).join(", ")} published to GitHub!`);
|
|
2310
2201
|
}
|
|
2202
|
+
return postedReleases;
|
|
2311
2203
|
}
|
|
2312
|
-
async function
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
cwd
|
|
2204
|
+
async function githubUnified({
|
|
2205
|
+
config,
|
|
2206
|
+
dryRun,
|
|
2207
|
+
rootPackage,
|
|
2208
|
+
bumpResult
|
|
2318
2209
|
}) {
|
|
2319
|
-
const
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
logLevel,
|
|
2323
|
-
cwd
|
|
2324
|
-
});
|
|
2325
|
-
if (recentTags.length === 0) {
|
|
2326
|
-
logger.debug(`No tags found for package ${packageName}`);
|
|
2327
|
-
return null;
|
|
2210
|
+
const repoConfig = config.repo;
|
|
2211
|
+
if (!repoConfig) {
|
|
2212
|
+
throw new Error("No repository configuration found. Please check your changelog config.");
|
|
2328
2213
|
}
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
onlyStable,
|
|
2333
|
-
packageName
|
|
2334
|
-
});
|
|
2335
|
-
if (compatibleTags.length === 0) {
|
|
2336
|
-
logger.debug(`No compatible tags found for package ${packageName}`);
|
|
2337
|
-
return null;
|
|
2214
|
+
logger.debug(`GitHub token: ${config.tokens.github || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
2215
|
+
if (!config.tokens.github && !config.repo?.token) {
|
|
2216
|
+
throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
|
|
2338
2217
|
}
|
|
2339
|
-
const
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
currentVersion,
|
|
2347
|
-
graduating,
|
|
2348
|
-
logLevel
|
|
2349
|
-
}) {
|
|
2350
|
-
const filterPrereleases = shouldFilterPrereleaseTags(currentVersion, graduating);
|
|
2351
|
-
const onlyStable = graduating || filterPrereleases;
|
|
2352
|
-
const lastPackageTag = await getLastPackageTag({
|
|
2353
|
-
packageName,
|
|
2354
|
-
currentVersion,
|
|
2355
|
-
onlyStable,
|
|
2356
|
-
logLevel,
|
|
2357
|
-
cwd
|
|
2218
|
+
const newVersion = bumpResult?.newVersion || rootPackage.version;
|
|
2219
|
+
const to = config.to || config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
2220
|
+
const changelog = await generateChangelog({
|
|
2221
|
+
pkg: rootPackage,
|
|
2222
|
+
config,
|
|
2223
|
+
dryRun,
|
|
2224
|
+
newVersion
|
|
2358
2225
|
});
|
|
2359
|
-
|
|
2360
|
-
|
|
2226
|
+
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
2227
|
+
const release = {
|
|
2228
|
+
tag_name: to,
|
|
2229
|
+
name: to,
|
|
2230
|
+
body: releaseBody,
|
|
2231
|
+
prerelease: isPrerelease(to)
|
|
2232
|
+
};
|
|
2233
|
+
logger.debug(`Creating release for ${to}${release.prerelease ? " (prerelease)" : ""}`);
|
|
2234
|
+
logger.debug("Release details:", formatJson({
|
|
2235
|
+
tag_name: release.tag_name,
|
|
2236
|
+
name: release.name,
|
|
2237
|
+
prerelease: release.prerelease
|
|
2238
|
+
}));
|
|
2239
|
+
if (dryRun) {
|
|
2240
|
+
logger.info("[dry-run] Publish GitHub release for", release.tag_name);
|
|
2241
|
+
} else {
|
|
2242
|
+
logger.debug("Publishing release to GitHub...");
|
|
2243
|
+
const releaseConfig = {
|
|
2244
|
+
...config,
|
|
2245
|
+
from: bumpResult?.bumped && bumpResult.fromTag || "v0.0.0",
|
|
2246
|
+
to,
|
|
2247
|
+
repo: repoConfig
|
|
2248
|
+
};
|
|
2249
|
+
await createGithubRelease(releaseConfig, release);
|
|
2361
2250
|
}
|
|
2362
|
-
|
|
2251
|
+
logger.success(`Release ${to} published to GitHub!`);
|
|
2252
|
+
return [{
|
|
2253
|
+
name: to,
|
|
2254
|
+
tag: to,
|
|
2255
|
+
version: to,
|
|
2256
|
+
prerelease: release.prerelease
|
|
2257
|
+
}];
|
|
2363
2258
|
}
|
|
2364
|
-
async function
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2259
|
+
async function github(options) {
|
|
2260
|
+
logger.debug("Config:", options);
|
|
2261
|
+
try {
|
|
2262
|
+
const dryRun = options.dryRun ?? false;
|
|
2263
|
+
logger.debug(`Dry run: ${dryRun}`);
|
|
2264
|
+
const config = await loadRelizyConfig({
|
|
2265
|
+
configFile: options.configName,
|
|
2266
|
+
baseConfig: options.config,
|
|
2267
|
+
overrides: {
|
|
2268
|
+
from: options.from,
|
|
2269
|
+
to: options.to,
|
|
2270
|
+
logLevel: options.logLevel,
|
|
2271
|
+
tokens: {
|
|
2272
|
+
github: options.token
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
});
|
|
2276
|
+
if (config.monorepo?.versionMode === "independent") {
|
|
2277
|
+
return await githubIndependentMode({
|
|
2278
|
+
config,
|
|
2279
|
+
dryRun,
|
|
2280
|
+
bumpResult: options.bumpResult,
|
|
2281
|
+
force: options.force ?? false,
|
|
2282
|
+
suffix: options.suffix
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
const rootPackageBase = readPackageJson(config.cwd);
|
|
2286
|
+
if (!rootPackageBase) {
|
|
2287
|
+
throw new Error("Failed to read root package.json");
|
|
2288
|
+
}
|
|
2289
|
+
const newVersion = options.bumpResult?.newVersion || rootPackageBase.version;
|
|
2290
|
+
const { from, to } = await resolveTags({
|
|
2291
|
+
config,
|
|
2292
|
+
step: "provider-release",
|
|
2293
|
+
newVersion,
|
|
2294
|
+
pkg: rootPackageBase
|
|
2295
|
+
});
|
|
2296
|
+
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
2297
|
+
config,
|
|
2298
|
+
force: options.force ?? false,
|
|
2299
|
+
suffix: options.suffix,
|
|
2300
|
+
changelog: true,
|
|
2301
|
+
from,
|
|
2302
|
+
to
|
|
2303
|
+
});
|
|
2304
|
+
return await githubUnified({
|
|
2305
|
+
config,
|
|
2306
|
+
dryRun,
|
|
2307
|
+
rootPackage,
|
|
2308
|
+
bumpResult: options.bumpResult
|
|
2309
|
+
});
|
|
2310
|
+
} catch (error) {
|
|
2311
|
+
logger.error("Error publishing GitHub release:", error);
|
|
2312
|
+
throw error;
|
|
2313
|
+
}
|
|
2379
2314
|
}
|
|
2380
|
-
|
|
2315
|
+
|
|
2316
|
+
async function createGitlabRelease({
|
|
2381
2317
|
config,
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
packageName,
|
|
2385
|
-
currentVersion,
|
|
2386
|
-
graduating,
|
|
2387
|
-
logLevel
|
|
2318
|
+
release,
|
|
2319
|
+
dryRun
|
|
2388
2320
|
}) {
|
|
2389
|
-
|
|
2390
|
-
if (
|
|
2391
|
-
|
|
2392
|
-
|
|
2321
|
+
const token = config.tokens.gitlab || config.repo?.token;
|
|
2322
|
+
if (!token && !dryRun) {
|
|
2323
|
+
throw new Error(
|
|
2324
|
+
"No GitLab token found. Set GITLAB_TOKEN or CI_JOB_TOKEN environment variable or configure tokens.gitlab"
|
|
2325
|
+
);
|
|
2326
|
+
}
|
|
2327
|
+
const repoConfig = config.repo?.repo;
|
|
2328
|
+
if (!repoConfig) {
|
|
2329
|
+
throw new Error("No repository URL found in config");
|
|
2330
|
+
}
|
|
2331
|
+
logger.debug(`Parsed repository URL: ${repoConfig}`);
|
|
2332
|
+
const projectPath = encodeURIComponent(repoConfig);
|
|
2333
|
+
const gitlabDomain = config.repo?.domain || "gitlab.com";
|
|
2334
|
+
const apiUrl = `https://${gitlabDomain}/api/v4/projects/${projectPath}/releases`;
|
|
2335
|
+
logger.info(`Creating GitLab release at: ${apiUrl}`);
|
|
2336
|
+
const payload = {
|
|
2337
|
+
tag_name: release.tag_name,
|
|
2338
|
+
name: release.name || release.tag_name,
|
|
2339
|
+
description: release.description || "",
|
|
2340
|
+
ref: release.ref || "main"
|
|
2341
|
+
};
|
|
2342
|
+
try {
|
|
2343
|
+
if (dryRun) {
|
|
2344
|
+
logger.info("[dry-run] GitLab release:", formatJson(payload));
|
|
2345
|
+
return {
|
|
2346
|
+
tag_name: release.tag_name,
|
|
2347
|
+
name: release.name || release.tag_name,
|
|
2348
|
+
description: release.description || "",
|
|
2349
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2350
|
+
released_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2351
|
+
_links: {
|
|
2352
|
+
self: `${apiUrl}/${encodeURIComponent(release.tag_name)}`
|
|
2353
|
+
}
|
|
2354
|
+
};
|
|
2393
2355
|
}
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
from = await resolveFromTagUnified({
|
|
2403
|
-
config,
|
|
2404
|
-
currentVersion,
|
|
2405
|
-
graduating,
|
|
2406
|
-
logLevel
|
|
2356
|
+
logger.debug(`POST GitLab release to ${apiUrl} with payload: ${formatJson(payload)}`);
|
|
2357
|
+
const response = await fetch(apiUrl, {
|
|
2358
|
+
method: "POST",
|
|
2359
|
+
headers: {
|
|
2360
|
+
"Content-Type": "application/json",
|
|
2361
|
+
"PRIVATE-TOKEN": token || ""
|
|
2362
|
+
},
|
|
2363
|
+
body: JSON.stringify(payload)
|
|
2407
2364
|
});
|
|
2365
|
+
if (!response.ok) {
|
|
2366
|
+
const errorText = await response.text();
|
|
2367
|
+
throw new Error(`GitLab API error (${response.status}): ${errorText}`);
|
|
2368
|
+
}
|
|
2369
|
+
const result = await response.json();
|
|
2370
|
+
logger.debug(`Created GitLab release: ${result._links.self}`);
|
|
2371
|
+
return result;
|
|
2372
|
+
} catch (error) {
|
|
2373
|
+
logger.error("Failed to create GitLab release:", error);
|
|
2374
|
+
throw error;
|
|
2408
2375
|
}
|
|
2409
|
-
logger.debug(`[${versionMode}](${step}) Using from tag: ${from}`);
|
|
2410
|
-
return config.from || from;
|
|
2411
2376
|
}
|
|
2412
|
-
function
|
|
2377
|
+
async function gitlabIndependentMode({
|
|
2413
2378
|
config,
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2379
|
+
dryRun,
|
|
2380
|
+
bumpResult,
|
|
2381
|
+
suffix,
|
|
2382
|
+
force
|
|
2418
2383
|
}) {
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2384
|
+
logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
2385
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
2386
|
+
config,
|
|
2387
|
+
bumpResult,
|
|
2388
|
+
suffix,
|
|
2389
|
+
force
|
|
2390
|
+
});
|
|
2391
|
+
logger.info(`Creating ${packages.length} GitLab release(s) for independent packages`);
|
|
2392
|
+
logger.debug("Getting current branch...");
|
|
2393
|
+
const { stdout: currentBranch } = await execPromise("git rev-parse --abbrev-ref HEAD", {
|
|
2394
|
+
noSuccess: true,
|
|
2395
|
+
noStdout: true,
|
|
2396
|
+
logLevel: config.logLevel,
|
|
2397
|
+
cwd: config.cwd
|
|
2398
|
+
});
|
|
2399
|
+
const postedReleases = [];
|
|
2400
|
+
for (const pkg of packages) {
|
|
2401
|
+
const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
|
|
2402
|
+
const from = config.from || pkg.fromTag;
|
|
2403
|
+
const to = getIndependentTag({ version: newVersion, name: pkg.name });
|
|
2404
|
+
if (!from) {
|
|
2405
|
+
logger.warn(`No from tag found for ${pkg.name}, skipping release`);
|
|
2406
|
+
continue;
|
|
2426
2407
|
}
|
|
2427
|
-
|
|
2428
|
-
|
|
2408
|
+
logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${to}`);
|
|
2409
|
+
const changelog = await generateChangelog({
|
|
2410
|
+
pkg,
|
|
2411
|
+
config,
|
|
2412
|
+
dryRun,
|
|
2413
|
+
newVersion
|
|
2414
|
+
});
|
|
2415
|
+
if (!changelog) {
|
|
2416
|
+
logger.warn(`No changelog found for ${pkg.name}`);
|
|
2417
|
+
continue;
|
|
2429
2418
|
}
|
|
2430
|
-
|
|
2419
|
+
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
2420
|
+
const release = {
|
|
2421
|
+
tag_name: to,
|
|
2422
|
+
name: to,
|
|
2423
|
+
description: releaseBody,
|
|
2424
|
+
ref: currentBranch.trim()
|
|
2425
|
+
};
|
|
2426
|
+
logger.debug(`Creating release for ${to} (ref: ${release.ref})`);
|
|
2427
|
+
if (dryRun) {
|
|
2428
|
+
logger.info(`[dry-run] Publish GitLab release for ${to}`);
|
|
2429
|
+
} else {
|
|
2430
|
+
logger.debug(`Publishing release ${to} to GitLab...`);
|
|
2431
|
+
await createGitlabRelease({
|
|
2432
|
+
config,
|
|
2433
|
+
release,
|
|
2434
|
+
dryRun
|
|
2435
|
+
});
|
|
2436
|
+
postedReleases.push({
|
|
2437
|
+
name: pkg.name,
|
|
2438
|
+
tag: release.tag_name,
|
|
2439
|
+
version: newVersion,
|
|
2440
|
+
prerelease: isPrerelease(newVersion)
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
if (postedReleases.length === 0) {
|
|
2445
|
+
logger.warn("No releases created");
|
|
2431
2446
|
} else {
|
|
2432
|
-
|
|
2447
|
+
logger.success(`Releases ${postedReleases.map((r) => r.tag).join(", ")} published to GitLab!`);
|
|
2433
2448
|
}
|
|
2434
|
-
|
|
2435
|
-
return config.to || to;
|
|
2449
|
+
return postedReleases;
|
|
2436
2450
|
}
|
|
2437
|
-
async function
|
|
2451
|
+
async function gitlabUnified({
|
|
2438
2452
|
config,
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2453
|
+
dryRun,
|
|
2454
|
+
rootPackage,
|
|
2455
|
+
bumpResult
|
|
2442
2456
|
}) {
|
|
2443
|
-
|
|
2444
|
-
const
|
|
2445
|
-
|
|
2446
|
-
const
|
|
2447
|
-
|
|
2448
|
-
const from = await resolveFromTag({
|
|
2457
|
+
logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
|
|
2458
|
+
const newVersion = bumpResult?.newVersion || rootPackage.newVersion || rootPackage.version;
|
|
2459
|
+
const to = config.templates.tagBody.replace("{{newVersion}}", newVersion);
|
|
2460
|
+
const changelog = await generateChangelog({
|
|
2461
|
+
pkg: rootPackage,
|
|
2449
2462
|
config,
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
packageName: pkg.name,
|
|
2453
|
-
currentVersion: pkg.version,
|
|
2454
|
-
graduating,
|
|
2455
|
-
logLevel
|
|
2463
|
+
dryRun,
|
|
2464
|
+
newVersion
|
|
2456
2465
|
});
|
|
2457
|
-
const
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2466
|
+
const releaseBody = changelog.split("\n").slice(2).join("\n");
|
|
2467
|
+
logger.debug("Getting current branch...");
|
|
2468
|
+
const { stdout: currentBranch } = await execPromise("git rev-parse --abbrev-ref HEAD", {
|
|
2469
|
+
noSuccess: true,
|
|
2470
|
+
noStdout: true,
|
|
2471
|
+
logLevel: config.logLevel,
|
|
2472
|
+
cwd: config.cwd
|
|
2463
2473
|
});
|
|
2464
|
-
|
|
2465
|
-
|
|
2474
|
+
const release = {
|
|
2475
|
+
tag_name: to,
|
|
2476
|
+
name: to,
|
|
2477
|
+
description: releaseBody,
|
|
2478
|
+
ref: currentBranch.trim()
|
|
2479
|
+
};
|
|
2480
|
+
logger.info(`Creating release for ${to} (ref: ${release.ref})`);
|
|
2481
|
+
logger.debug("Release details:", formatJson({
|
|
2482
|
+
tag_name: release.tag_name,
|
|
2483
|
+
name: release.name,
|
|
2484
|
+
ref: release.ref
|
|
2485
|
+
}));
|
|
2486
|
+
if (dryRun) {
|
|
2487
|
+
logger.info("[dry-run] Publish GitLab release for", release.tag_name);
|
|
2488
|
+
} else {
|
|
2489
|
+
logger.debug("Publishing release to GitLab...");
|
|
2490
|
+
await createGitlabRelease({
|
|
2491
|
+
config,
|
|
2492
|
+
release,
|
|
2493
|
+
dryRun
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
logger.success(`Release ${to} published to GitLab!`);
|
|
2497
|
+
return [{
|
|
2498
|
+
name: to,
|
|
2499
|
+
tag: to,
|
|
2500
|
+
version: to,
|
|
2501
|
+
prerelease: isPrerelease(newVersion)
|
|
2502
|
+
}];
|
|
2503
|
+
}
|
|
2504
|
+
async function gitlab(options = {}) {
|
|
2505
|
+
try {
|
|
2506
|
+
const dryRun = options.dryRun ?? false;
|
|
2507
|
+
logger.debug(`Dry run: ${dryRun}`);
|
|
2508
|
+
const config = await loadRelizyConfig({
|
|
2509
|
+
configFile: options.configName,
|
|
2510
|
+
baseConfig: options.config,
|
|
2511
|
+
overrides: {
|
|
2512
|
+
from: options.from,
|
|
2513
|
+
to: options.to,
|
|
2514
|
+
logLevel: options.logLevel,
|
|
2515
|
+
tokens: {
|
|
2516
|
+
gitlab: options.token
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
});
|
|
2520
|
+
if (config.monorepo?.versionMode === "independent") {
|
|
2521
|
+
return await gitlabIndependentMode({
|
|
2522
|
+
config,
|
|
2523
|
+
dryRun,
|
|
2524
|
+
bumpResult: options.bumpResult,
|
|
2525
|
+
suffix: options.suffix,
|
|
2526
|
+
force: options.force ?? false
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
const rootPackageBase = readPackageJson(config.cwd);
|
|
2530
|
+
if (!rootPackageBase) {
|
|
2531
|
+
throw new Error("Failed to read root package.json");
|
|
2532
|
+
}
|
|
2533
|
+
const newVersion = options.bumpResult?.newVersion || rootPackageBase.version;
|
|
2534
|
+
const { from, to } = await resolveTags({
|
|
2535
|
+
config,
|
|
2536
|
+
step: "provider-release",
|
|
2537
|
+
newVersion,
|
|
2538
|
+
pkg: rootPackageBase
|
|
2539
|
+
});
|
|
2540
|
+
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
2541
|
+
config,
|
|
2542
|
+
force: options.force ?? false,
|
|
2543
|
+
suffix: options.suffix,
|
|
2544
|
+
changelog: true,
|
|
2545
|
+
from,
|
|
2546
|
+
to
|
|
2547
|
+
});
|
|
2548
|
+
logger.debug(`Root package: ${getIndependentTag({ name: rootPackage.name, version: newVersion })}`);
|
|
2549
|
+
return await gitlabUnified({
|
|
2550
|
+
config,
|
|
2551
|
+
dryRun,
|
|
2552
|
+
rootPackage,
|
|
2553
|
+
bumpResult: options.bumpResult
|
|
2554
|
+
});
|
|
2555
|
+
} catch (error) {
|
|
2556
|
+
logger.error("Error publishing GitLab release:", error);
|
|
2557
|
+
throw error;
|
|
2558
|
+
}
|
|
2466
2559
|
}
|
|
2467
2560
|
|
|
2468
2561
|
let sessionOtp;
|
|
@@ -2570,7 +2663,7 @@ function getCommandArgs({
|
|
|
2570
2663
|
args.push("--registry", registry);
|
|
2571
2664
|
}
|
|
2572
2665
|
const isPnpmOrNpm = packageManager === "pnpm" || packageManager === "npm";
|
|
2573
|
-
const publishToken = config.publish.token;
|
|
2666
|
+
const publishToken = config.publish.token || config.tokens.registry;
|
|
2574
2667
|
if (publishToken) {
|
|
2575
2668
|
if (!registry) {
|
|
2576
2669
|
logger.warn("Publish token provided but no registry specified");
|
|
@@ -2611,7 +2704,18 @@ function isOtpError(error) {
|
|
|
2611
2704
|
if (typeof error !== "object" || error === null)
|
|
2612
2705
|
return false;
|
|
2613
2706
|
const errorMessage = "message" in error && typeof error.message === "string" ? error.message.toLowerCase() : "";
|
|
2614
|
-
|
|
2707
|
+
const fullErrorString = String(error).toLowerCase();
|
|
2708
|
+
const searchText = `${errorMessage} ${fullErrorString}`;
|
|
2709
|
+
const otpPatterns = [
|
|
2710
|
+
"otp",
|
|
2711
|
+
"one-time password",
|
|
2712
|
+
"eotp",
|
|
2713
|
+
"two-factor authentication",
|
|
2714
|
+
"2fa",
|
|
2715
|
+
"two factor"
|
|
2716
|
+
];
|
|
2717
|
+
const isOtp = otpPatterns.some((pattern) => searchText.includes(pattern));
|
|
2718
|
+
return isOtp;
|
|
2615
2719
|
}
|
|
2616
2720
|
function promptOtpWithTimeout(timeout = 9e4) {
|
|
2617
2721
|
return new Promise((resolve, reject) => {
|
|
@@ -2640,7 +2744,7 @@ async function handleOtpError() {
|
|
|
2640
2744
|
logger.debug("OTP received, retrying publish...");
|
|
2641
2745
|
return otp;
|
|
2642
2746
|
} catch (promptError) {
|
|
2643
|
-
logger.
|
|
2747
|
+
logger.fail("Failed to get OTP");
|
|
2644
2748
|
throw promptError;
|
|
2645
2749
|
}
|
|
2646
2750
|
}
|
|
@@ -2663,6 +2767,7 @@ async function executePublishCommand({
|
|
|
2663
2767
|
noStderr: true,
|
|
2664
2768
|
noStdout: true,
|
|
2665
2769
|
noSuccess: true,
|
|
2770
|
+
noError: true,
|
|
2666
2771
|
logLevel: config.logLevel,
|
|
2667
2772
|
cwd: pkg.path
|
|
2668
2773
|
});
|
|
@@ -2743,9 +2848,302 @@ async function publishPackage({
|
|
|
2743
2848
|
logger.error(`Failed to publish ${packageNameAndVersion}:`, error);
|
|
2744
2849
|
throw error;
|
|
2745
2850
|
}
|
|
2746
|
-
} finally {
|
|
2747
|
-
process.chdir(config.cwd);
|
|
2851
|
+
} finally {
|
|
2852
|
+
process.chdir(config.cwd);
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
function extractChangelogSummary(changelog, maxLength = 150) {
|
|
2858
|
+
if (changelog.trim() === "") {
|
|
2859
|
+
return "";
|
|
2860
|
+
}
|
|
2861
|
+
const cleaned = changelog.split("\n").filter((line) => !line.startsWith("#")).join("\n").trim();
|
|
2862
|
+
let cleanedResult = cleaned;
|
|
2863
|
+
if (cleanedResult.endsWith("?") || cleanedResult.endsWith("!") || cleanedResult.endsWith(".")) {
|
|
2864
|
+
cleanedResult = cleanedResult.slice(0, -1);
|
|
2865
|
+
}
|
|
2866
|
+
const sentences = cleanedResult.split(/[.!?]\s+/);
|
|
2867
|
+
let summary = "";
|
|
2868
|
+
for (const sentence of sentences) {
|
|
2869
|
+
if ((summary + sentence).length > maxLength || sentence.trim() === "") {
|
|
2870
|
+
break;
|
|
2871
|
+
}
|
|
2872
|
+
summary += `${sentence}. `;
|
|
2873
|
+
}
|
|
2874
|
+
return summary.trim() || cleaned.substring(0, maxLength);
|
|
2875
|
+
}
|
|
2876
|
+
function getReleaseUrl(config, tag) {
|
|
2877
|
+
const repo = config.repo;
|
|
2878
|
+
if (!repo?.domain || !repo?.repo) {
|
|
2879
|
+
return void 0;
|
|
2880
|
+
}
|
|
2881
|
+
const provider = repo.provider || "github";
|
|
2882
|
+
if (provider === "github") {
|
|
2883
|
+
return `https://${repo.domain}/${repo.repo}/releases/tag/${tag}`;
|
|
2884
|
+
} else if (provider === "gitlab") {
|
|
2885
|
+
return `https://${repo.domain}/${repo.repo}/-/releases/${tag}`;
|
|
2886
|
+
} else if (provider === "bitbucket") {
|
|
2887
|
+
return `https://${repo.domain}/${repo.repo}/commits/tag/${tag}`;
|
|
2888
|
+
}
|
|
2889
|
+
return void 0;
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
function getSlackToken(options) {
|
|
2893
|
+
const { socialCredentials, tokenCredential } = options;
|
|
2894
|
+
const token = socialCredentials?.token || tokenCredential;
|
|
2895
|
+
if (!token) {
|
|
2896
|
+
return null;
|
|
2897
|
+
}
|
|
2898
|
+
return token;
|
|
2899
|
+
}
|
|
2900
|
+
function formatChangelogForSlack(changelog, maxLength = 500) {
|
|
2901
|
+
let formatted = changelog.replace(/^### (.+)$/gm, "*$1*").replace(/^## (.+)$/gm, "*$1*").replace(/^# (.+)$/gm, "*$1*").replace(/\*\*(.+?)\*\*/g, "*$1*");
|
|
2902
|
+
const linkPattern = /\[([^\]]*)]\(([^)]*)\)/g;
|
|
2903
|
+
formatted = formatted.replace(linkPattern, (_, text, url) => `<${url}|${text}>`);
|
|
2904
|
+
if (formatted.length > maxLength) {
|
|
2905
|
+
formatted = `${formatted.substring(0, maxLength - 3)}...`;
|
|
2906
|
+
}
|
|
2907
|
+
return formatted;
|
|
2908
|
+
}
|
|
2909
|
+
function formatSlackMessage({ projectName, version, changelog, releaseUrl, changelogUrl, template }) {
|
|
2910
|
+
if (template) {
|
|
2911
|
+
const summary = extractChangelogSummary(changelog, 500);
|
|
2912
|
+
let message = template.replace("{{projectName}}", projectName).replace("{{version}}", version).replace("{{changelog}}", summary);
|
|
2913
|
+
if (releaseUrl) {
|
|
2914
|
+
message = message.replace("{{releaseUrl}}", releaseUrl);
|
|
2915
|
+
} else {
|
|
2916
|
+
message = message.replace("{{releaseUrl}}", "").trim();
|
|
2917
|
+
}
|
|
2918
|
+
if (changelogUrl) {
|
|
2919
|
+
message = message.replace("{{changelogUrl}}", changelogUrl);
|
|
2920
|
+
} else {
|
|
2921
|
+
message = message.replace("{{changelogUrl}}", "").trim();
|
|
2922
|
+
}
|
|
2923
|
+
return [
|
|
2924
|
+
{
|
|
2925
|
+
type: "section",
|
|
2926
|
+
text: {
|
|
2927
|
+
type: "mrkdwn",
|
|
2928
|
+
text: message
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
];
|
|
2932
|
+
}
|
|
2933
|
+
const blocks = [
|
|
2934
|
+
{
|
|
2935
|
+
type: "header",
|
|
2936
|
+
text: {
|
|
2937
|
+
type: "plain_text",
|
|
2938
|
+
text: `\u{1F680} ${projectName} ${version} is out!`,
|
|
2939
|
+
emoji: true
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
];
|
|
2943
|
+
const formattedChangelog = formatChangelogForSlack(changelog, 500);
|
|
2944
|
+
if (formattedChangelog) {
|
|
2945
|
+
blocks.push({
|
|
2946
|
+
type: "section",
|
|
2947
|
+
text: {
|
|
2948
|
+
type: "mrkdwn",
|
|
2949
|
+
text: formattedChangelog
|
|
2950
|
+
}
|
|
2951
|
+
});
|
|
2952
|
+
}
|
|
2953
|
+
blocks.push({
|
|
2954
|
+
type: "divider"
|
|
2955
|
+
});
|
|
2956
|
+
const elements = [];
|
|
2957
|
+
if (releaseUrl) {
|
|
2958
|
+
elements.push({
|
|
2959
|
+
type: "button",
|
|
2960
|
+
text: {
|
|
2961
|
+
type: "plain_text",
|
|
2962
|
+
text: "\u{1F4E6} View Release",
|
|
2963
|
+
emoji: true
|
|
2964
|
+
},
|
|
2965
|
+
url: releaseUrl,
|
|
2966
|
+
action_id: "view_release"
|
|
2967
|
+
});
|
|
2968
|
+
}
|
|
2969
|
+
if (changelogUrl) {
|
|
2970
|
+
elements.push({
|
|
2971
|
+
type: "button",
|
|
2972
|
+
text: {
|
|
2973
|
+
type: "plain_text",
|
|
2974
|
+
text: "\u{1F4CB} Full Changelog",
|
|
2975
|
+
emoji: true
|
|
2976
|
+
},
|
|
2977
|
+
url: changelogUrl,
|
|
2978
|
+
action_id: "view_changelog"
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
if (elements.length > 0) {
|
|
2982
|
+
blocks.push({
|
|
2983
|
+
type: "actions",
|
|
2984
|
+
elements
|
|
2985
|
+
});
|
|
2986
|
+
}
|
|
2987
|
+
return blocks;
|
|
2988
|
+
}
|
|
2989
|
+
async function postReleaseToSlack({
|
|
2990
|
+
version,
|
|
2991
|
+
projectName,
|
|
2992
|
+
changelog,
|
|
2993
|
+
releaseUrl,
|
|
2994
|
+
changelogUrl,
|
|
2995
|
+
channel,
|
|
2996
|
+
token,
|
|
2997
|
+
template,
|
|
2998
|
+
dryRun = false
|
|
2999
|
+
}) {
|
|
3000
|
+
logger.debug("Preparing Slack post...");
|
|
3001
|
+
const blocks = formatSlackMessage({
|
|
3002
|
+
template,
|
|
3003
|
+
projectName,
|
|
3004
|
+
version,
|
|
3005
|
+
changelog,
|
|
3006
|
+
releaseUrl,
|
|
3007
|
+
changelogUrl
|
|
3008
|
+
});
|
|
3009
|
+
logger.debug(`Message blocks (${blocks.length} blocks)`);
|
|
3010
|
+
if (dryRun) {
|
|
3011
|
+
logger.info("[dry-run] Would post to Slack:", JSON.stringify(blocks, null, 2));
|
|
3012
|
+
return;
|
|
3013
|
+
}
|
|
3014
|
+
try {
|
|
3015
|
+
const { WebClient } = await import('@slack/web-api');
|
|
3016
|
+
const client = new WebClient(token);
|
|
3017
|
+
logger.debug(`Posting message to Slack channel: ${channel}`);
|
|
3018
|
+
const result = await client.chat.postMessage({
|
|
3019
|
+
channel,
|
|
3020
|
+
blocks,
|
|
3021
|
+
text: `${projectName} ${version} is out!`
|
|
3022
|
+
// Fallback text for notifications
|
|
3023
|
+
});
|
|
3024
|
+
logger.success(`Message posted successfully! Channel: ${result.channel}, Timestamp: ${result.ts}`);
|
|
3025
|
+
return result;
|
|
3026
|
+
} catch (error) {
|
|
3027
|
+
if (error.code === "ERR_MODULE_NOT_FOUND" || error.message?.includes("@slack/web-api")) {
|
|
3028
|
+
logger.error("Slack Web API dependency not found. Please install it with: pnpm add @slack/web-api");
|
|
3029
|
+
throw new Error("Missing dependency: @slack/web-api. Install it with: pnpm add @slack/web-api");
|
|
3030
|
+
}
|
|
3031
|
+
logger.error("Failed to post message:", error.message || error);
|
|
3032
|
+
if (error.data) {
|
|
3033
|
+
logger.error("Slack API error:", error.data.error);
|
|
3034
|
+
switch (error.data.error) {
|
|
3035
|
+
case "channel_not_found":
|
|
3036
|
+
throw new Error("Slack channel not found. Make sure the channel ID or name is correct.");
|
|
3037
|
+
case "not_in_channel":
|
|
3038
|
+
throw new Error("Bot is not in the channel. Invite the bot to the channel first.");
|
|
3039
|
+
case "invalid_auth":
|
|
3040
|
+
throw new Error("Invalid Slack token. Check your credentials.");
|
|
3041
|
+
case "missing_scope":
|
|
3042
|
+
throw new Error('Missing required OAuth scope. The bot needs "chat:write" permission.');
|
|
3043
|
+
default:
|
|
3044
|
+
throw error;
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
throw error;
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
function getTwitterCredentials({ socialCredentials, tokenCredentials }) {
|
|
3052
|
+
const apiKey = socialCredentials?.apiKey || tokenCredentials?.apiKey;
|
|
3053
|
+
const apiKeySecret = socialCredentials?.apiKeySecret || tokenCredentials?.apiKeySecret;
|
|
3054
|
+
const accessToken = socialCredentials?.accessToken || tokenCredentials?.accessToken;
|
|
3055
|
+
const accessTokenSecret = socialCredentials?.accessTokenSecret || tokenCredentials?.accessTokenSecret;
|
|
3056
|
+
if (!apiKey || !apiKeySecret || !accessToken || !accessTokenSecret) {
|
|
3057
|
+
logger.warn("Twitter is enabled but credentials are missing.");
|
|
3058
|
+
logger.log("Set the following environment variables or configure them in social.twitter.credentials or tokens.twitter:");
|
|
3059
|
+
logger.log(" - TWITTER_API_KEY or RELIZY_TWITTER_API_KEY");
|
|
3060
|
+
logger.log(" - TWITTER_API_KEY_SECRET or RELIZY_TWITTER_API_KEY_SECRET");
|
|
3061
|
+
logger.log(" - TWITTER_ACCESS_TOKEN or RELIZY_TWITTER_ACCESS_TOKEN");
|
|
3062
|
+
logger.log(" - TWITTER_ACCESS_TOKEN_SECRET or RELIZY_TWITTER_ACCESS_TOKEN_SECRET");
|
|
3063
|
+
logger.info("Skipping Twitter post");
|
|
3064
|
+
return null;
|
|
3065
|
+
}
|
|
3066
|
+
return {
|
|
3067
|
+
apiKey,
|
|
3068
|
+
apiKeySecret,
|
|
3069
|
+
accessToken,
|
|
3070
|
+
accessTokenSecret
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
function formatTweetMessage({ template, projectName, version, changelog, releaseUrl, changelogUrl }) {
|
|
3074
|
+
const TWITTER_MAX_LENGTH = 280;
|
|
3075
|
+
const ELLIPSIS = "...";
|
|
3076
|
+
let templateWithValues = template.replace("{{projectName}}", projectName).replace("{{version}}", version);
|
|
3077
|
+
if (releaseUrl) {
|
|
3078
|
+
templateWithValues = templateWithValues.replace("{{releaseUrl}}", releaseUrl);
|
|
3079
|
+
} else {
|
|
3080
|
+
templateWithValues = templateWithValues.replace("{{releaseUrl}}", "");
|
|
3081
|
+
}
|
|
3082
|
+
if (changelogUrl) {
|
|
3083
|
+
templateWithValues = templateWithValues.replace("{{changelogUrl}}", changelogUrl);
|
|
3084
|
+
} else {
|
|
3085
|
+
templateWithValues = templateWithValues.replace("{{changelogUrl}}", "");
|
|
3086
|
+
}
|
|
3087
|
+
const templateWithoutChangelog = templateWithValues.replace("{{changelog}}", "");
|
|
3088
|
+
const availableForChangelog = TWITTER_MAX_LENGTH - templateWithoutChangelog.length;
|
|
3089
|
+
let finalChangelog = changelog;
|
|
3090
|
+
if (changelog.length > availableForChangelog) {
|
|
3091
|
+
const maxLength = Math.max(0, availableForChangelog - ELLIPSIS.length);
|
|
3092
|
+
finalChangelog = changelog.substring(0, maxLength) + ELLIPSIS;
|
|
3093
|
+
}
|
|
3094
|
+
let message = templateWithValues.replace("{{changelog}}", finalChangelog).trim();
|
|
3095
|
+
if (message.length > TWITTER_MAX_LENGTH) {
|
|
3096
|
+
message = message.substring(0, TWITTER_MAX_LENGTH - ELLIPSIS.length) + ELLIPSIS;
|
|
3097
|
+
}
|
|
3098
|
+
return message;
|
|
3099
|
+
}
|
|
3100
|
+
async function postReleaseToTwitter({
|
|
3101
|
+
version,
|
|
3102
|
+
projectName,
|
|
3103
|
+
changelog,
|
|
3104
|
+
releaseUrl,
|
|
3105
|
+
changelogUrl,
|
|
3106
|
+
credentials,
|
|
3107
|
+
template,
|
|
3108
|
+
dryRun = false
|
|
3109
|
+
}) {
|
|
3110
|
+
logger.debug("Preparing Twitter post...");
|
|
3111
|
+
const changelogSummary = extractChangelogSummary(changelog, 150);
|
|
3112
|
+
const message = formatTweetMessage({
|
|
3113
|
+
template,
|
|
3114
|
+
projectName,
|
|
3115
|
+
version,
|
|
3116
|
+
changelog: changelogSummary,
|
|
3117
|
+
releaseUrl,
|
|
3118
|
+
changelogUrl
|
|
3119
|
+
});
|
|
3120
|
+
logger.debug(`Tweet message (${message.length} chars):
|
|
3121
|
+
${message}`);
|
|
3122
|
+
if (dryRun) {
|
|
3123
|
+
logger.info("[dry-run] Would post tweet:", message);
|
|
3124
|
+
return;
|
|
3125
|
+
}
|
|
3126
|
+
try {
|
|
3127
|
+
const { TwitterApi } = await import('twitter-api-v2');
|
|
3128
|
+
const client = new TwitterApi({
|
|
3129
|
+
appKey: credentials.apiKey,
|
|
3130
|
+
appSecret: credentials.apiKeySecret,
|
|
3131
|
+
accessToken: credentials.accessToken,
|
|
3132
|
+
accessSecret: credentials.accessTokenSecret
|
|
3133
|
+
});
|
|
3134
|
+
const rwClient = client.readWrite;
|
|
3135
|
+
logger.debug(`Posting tweet: ${message}`);
|
|
3136
|
+
const tweet = await rwClient.v2.tweet(message);
|
|
3137
|
+
logger.info(`Tweet posted successfully! Tweet ID: ${tweet.data.id}`);
|
|
3138
|
+
logger.info(`Tweet URL: https://twitter.com/i/web/status/${tweet.data.id}`);
|
|
3139
|
+
return tweet;
|
|
3140
|
+
} catch (error) {
|
|
3141
|
+
if (error.code === "ERR_MODULE_NOT_FOUND" || error.message?.includes("twitter-api-v2")) {
|
|
3142
|
+
logger.error("Twitter API dependency not found. Please install it with: pnpm add twitter-api-v2");
|
|
3143
|
+
throw new Error("Missing dependency: twitter-api-v2. Install it with: pnpm add twitter-api-v2");
|
|
2748
3144
|
}
|
|
3145
|
+
logger.error("Failed to post tweet:", error.message || error);
|
|
3146
|
+
throw error;
|
|
2749
3147
|
}
|
|
2750
3148
|
}
|
|
2751
3149
|
|
|
@@ -2783,7 +3181,6 @@ async function bumpUnifiedMode({
|
|
|
2783
3181
|
logger.debug(`${currentVersion} \u2192 ${newVersion} (${config.monorepo?.versionMode || "standalone"} mode)`);
|
|
2784
3182
|
const packages = await getPackages({
|
|
2785
3183
|
config,
|
|
2786
|
-
patterns: config.monorepo?.packages,
|
|
2787
3184
|
suffix,
|
|
2788
3185
|
force
|
|
2789
3186
|
});
|
|
@@ -2804,7 +3201,8 @@ async function bumpUnifiedMode({
|
|
|
2804
3201
|
} else {
|
|
2805
3202
|
logger.info(`${packages.length === 1 ? packages[0].name : packages.length} package(s) bumped from ${currentVersion} to ${newVersion} (${config.monorepo?.versionMode || "standalone"} mode)`);
|
|
2806
3203
|
}
|
|
2807
|
-
|
|
3204
|
+
const packagesToWrite = [rootPackage, ...packages];
|
|
3205
|
+
for (const pkg of packagesToWrite) {
|
|
2808
3206
|
writeVersion(pkg.path, newVersion, dryRun);
|
|
2809
3207
|
}
|
|
2810
3208
|
updateLernaVersion({
|
|
@@ -2865,7 +3263,6 @@ async function bumpSelectiveMode({
|
|
|
2865
3263
|
logger.debug("Determining packages to bump...");
|
|
2866
3264
|
const packages = await getPackages({
|
|
2867
3265
|
config,
|
|
2868
|
-
patterns: config.monorepo?.packages,
|
|
2869
3266
|
suffix,
|
|
2870
3267
|
force
|
|
2871
3268
|
});
|
|
@@ -2896,7 +3293,8 @@ async function bumpSelectiveMode({
|
|
|
2896
3293
|
}
|
|
2897
3294
|
}
|
|
2898
3295
|
logger.debug(`Writing version to ${packages.length} package(s)`);
|
|
2899
|
-
|
|
3296
|
+
const packagesToWrite = [rootPackage, ...packages];
|
|
3297
|
+
for (const pkg of packagesToWrite) {
|
|
2900
3298
|
writeVersion(pkg.path, newVersion, dryRun);
|
|
2901
3299
|
}
|
|
2902
3300
|
updateLernaVersion({
|
|
@@ -2933,7 +3331,6 @@ async function bumpIndependentMode({
|
|
|
2933
3331
|
logger.debug("Starting bump in independent mode");
|
|
2934
3332
|
const packagesToBump = await getPackages({
|
|
2935
3333
|
config,
|
|
2936
|
-
patterns: config.monorepo?.packages,
|
|
2937
3334
|
suffix,
|
|
2938
3335
|
force
|
|
2939
3336
|
});
|
|
@@ -3005,7 +3402,7 @@ async function bump(options = {}) {
|
|
|
3005
3402
|
checkGitStatusIfDirty();
|
|
3006
3403
|
}
|
|
3007
3404
|
await fetchGitTags(config.cwd);
|
|
3008
|
-
logger.
|
|
3405
|
+
logger.debug(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
|
|
3009
3406
|
const packages = readPackages({
|
|
3010
3407
|
cwd: config.cwd,
|
|
3011
3408
|
patterns: config.monorepo?.packages,
|
|
@@ -3031,7 +3428,7 @@ async function bump(options = {}) {
|
|
|
3031
3428
|
logger.success(`${dryRun ? "[dry-run] " : ""}Version bump completed (${resultLog} package${resultLog === 1 || typeof resultLog === "string" ? "" : "s"} bumped)`);
|
|
3032
3429
|
} else {
|
|
3033
3430
|
logger.fail("No packages to bump, no relevant commits found");
|
|
3034
|
-
exit(1);
|
|
3431
|
+
process$1.exit(1);
|
|
3035
3432
|
}
|
|
3036
3433
|
await executeHook("success:bump", config, dryRun);
|
|
3037
3434
|
return result;
|
|
@@ -3154,20 +3551,20 @@ async function changelog(options = {}) {
|
|
|
3154
3551
|
});
|
|
3155
3552
|
const dryRun = options.dryRun ?? false;
|
|
3156
3553
|
logger.debug(`Dry run: ${dryRun}`);
|
|
3157
|
-
logger.
|
|
3554
|
+
logger.debug(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
|
|
3158
3555
|
try {
|
|
3159
3556
|
await executeHook("before:changelog", config, dryRun);
|
|
3160
3557
|
logger.start("Start generating changelogs");
|
|
3558
|
+
const packages = await getPackagesOrBumpedPackages({
|
|
3559
|
+
config,
|
|
3560
|
+
bumpResult: options.bumpResult,
|
|
3561
|
+
suffix: options.suffix,
|
|
3562
|
+
force: options.force ?? false
|
|
3563
|
+
});
|
|
3161
3564
|
if (config.changelog?.rootChangelog && config.monorepo) {
|
|
3162
3565
|
if (config.monorepo.versionMode === "independent") {
|
|
3163
|
-
const packages2 = await getPackagesOrBumpedPackages({
|
|
3164
|
-
config,
|
|
3165
|
-
bumpResult: options.bumpResult,
|
|
3166
|
-
suffix: options.suffix,
|
|
3167
|
-
force: options.force ?? false
|
|
3168
|
-
});
|
|
3169
3566
|
await generateIndependentRootChangelog({
|
|
3170
|
-
packages
|
|
3567
|
+
packages,
|
|
3171
3568
|
config,
|
|
3172
3569
|
dryRun
|
|
3173
3570
|
});
|
|
@@ -3184,12 +3581,6 @@ async function changelog(options = {}) {
|
|
|
3184
3581
|
logger.debug("Skipping root changelog generation");
|
|
3185
3582
|
}
|
|
3186
3583
|
logger.debug("Generating package changelogs...");
|
|
3187
|
-
const packages = await getPackagesOrBumpedPackages({
|
|
3188
|
-
config,
|
|
3189
|
-
bumpResult: options.bumpResult,
|
|
3190
|
-
suffix: options.suffix,
|
|
3191
|
-
force: options.force ?? false
|
|
3192
|
-
});
|
|
3193
3584
|
logger.debug(`Processing ${packages.length} package(s)`);
|
|
3194
3585
|
let generatedCount = 0;
|
|
3195
3586
|
for await (const pkg of packages) {
|
|
@@ -3236,22 +3627,28 @@ async function changelog(options = {}) {
|
|
|
3236
3627
|
|
|
3237
3628
|
function providerReleaseSafetyCheck({ config, provider }) {
|
|
3238
3629
|
if (!config.safetyCheck || !config.release.providerRelease) {
|
|
3630
|
+
logger.debug("Safety check disabled or provider release disabled");
|
|
3239
3631
|
return;
|
|
3240
3632
|
}
|
|
3633
|
+
logger.debug("Start checking provider release config");
|
|
3241
3634
|
const internalProvider = provider || config.repo?.provider || detectGitProvider();
|
|
3635
|
+
if (internalProvider === "bitbucket") {
|
|
3636
|
+
logger.warn("Bitbucket does not support releases via API");
|
|
3637
|
+
logger.info("Relizy will skip the release creation step for Bitbucket");
|
|
3638
|
+
return;
|
|
3639
|
+
}
|
|
3242
3640
|
let token;
|
|
3243
3641
|
if (internalProvider === "github") {
|
|
3244
3642
|
token = config.tokens?.github || config.repo?.token;
|
|
3245
3643
|
} else if (internalProvider === "gitlab") {
|
|
3246
3644
|
token = config.tokens?.gitlab || config.repo?.token;
|
|
3247
3645
|
} else {
|
|
3248
|
-
|
|
3249
|
-
process.exit(1);
|
|
3646
|
+
throw new Error(`Unsupported Git provider: ${internalProvider || "unknown"}`);
|
|
3250
3647
|
}
|
|
3251
3648
|
if (!token) {
|
|
3252
|
-
|
|
3253
|
-
process.exit(1);
|
|
3649
|
+
throw new Error(`No token provided for ${internalProvider || "unknown"} - The release will not be published - Please refer to the documentation: https://louismazel.github.io/relizy/guide/installation#environment-setup`);
|
|
3254
3650
|
}
|
|
3651
|
+
logger.info("provider release config checked successfully");
|
|
3255
3652
|
}
|
|
3256
3653
|
async function providerRelease(options = {}) {
|
|
3257
3654
|
const config = await loadRelizyConfig({
|
|
@@ -3270,9 +3667,10 @@ async function providerRelease(options = {}) {
|
|
|
3270
3667
|
});
|
|
3271
3668
|
const dryRun = options.dryRun ?? false;
|
|
3272
3669
|
logger.debug(`Dry run: ${dryRun}`);
|
|
3273
|
-
logger.
|
|
3670
|
+
logger.debug(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
|
|
3671
|
+
let detectedProvider = null;
|
|
3274
3672
|
try {
|
|
3275
|
-
|
|
3673
|
+
detectedProvider = options.provider || detectGitProvider();
|
|
3276
3674
|
providerReleaseSafetyCheck({ config, provider: detectedProvider });
|
|
3277
3675
|
await executeHook("before:provider-release", config, dryRun);
|
|
3278
3676
|
logger.start("Start provider release");
|
|
@@ -3285,6 +3683,16 @@ async function providerRelease(options = {}) {
|
|
|
3285
3683
|
);
|
|
3286
3684
|
}
|
|
3287
3685
|
let postedReleases = [];
|
|
3686
|
+
if (detectedProvider === "bitbucket") {
|
|
3687
|
+
logger.warn("\u26A0\uFE0F Bitbucket does not support releases via API");
|
|
3688
|
+
logger.info("Skipping release creation for Bitbucket");
|
|
3689
|
+
logger.info("Git tags will still be created during the commit step");
|
|
3690
|
+
await executeHook("success:provider-release", config, dryRun);
|
|
3691
|
+
return {
|
|
3692
|
+
detectedProvider,
|
|
3693
|
+
postedReleases: []
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3288
3696
|
const payload = {
|
|
3289
3697
|
from: config.from || options.bumpResult?.fromTag,
|
|
3290
3698
|
to: config.to,
|
|
@@ -3312,20 +3720,24 @@ async function providerRelease(options = {}) {
|
|
|
3312
3720
|
logger.error("Error publishing releases!\n\n", error);
|
|
3313
3721
|
}
|
|
3314
3722
|
await executeHook("error:provider-release", config, dryRun);
|
|
3315
|
-
|
|
3723
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3724
|
+
return {
|
|
3725
|
+
detectedProvider: detectedProvider || "github",
|
|
3726
|
+
postedReleases: [],
|
|
3727
|
+
error: errorMessage
|
|
3728
|
+
};
|
|
3316
3729
|
}
|
|
3317
3730
|
}
|
|
3318
3731
|
|
|
3319
3732
|
async function publishSafetyCheck({ config }) {
|
|
3320
|
-
logger.debug("[publish-safety-check] Running publish safety check");
|
|
3321
3733
|
if (!config.safetyCheck || !config.release.publish || !config.publish.safetyCheck) {
|
|
3322
|
-
logger.debug("
|
|
3734
|
+
logger.debug("Safety check disabled or publish disabled");
|
|
3323
3735
|
return;
|
|
3324
3736
|
}
|
|
3737
|
+
logger.debug("Start checking auth config to package registry");
|
|
3325
3738
|
const packageManager = config.publish.packageManager || detectPackageManager(config.cwd);
|
|
3326
3739
|
if (!packageManager) {
|
|
3327
|
-
|
|
3328
|
-
process.exit(1);
|
|
3740
|
+
throw new Error("Unable to detect package manager");
|
|
3329
3741
|
}
|
|
3330
3742
|
const isPnpmOrNpm = packageManager === "pnpm" || packageManager === "npm";
|
|
3331
3743
|
if (isPnpmOrNpm) {
|
|
@@ -3335,7 +3747,7 @@ async function publishSafetyCheck({ config }) {
|
|
|
3335
3747
|
otp: config.publish.otp
|
|
3336
3748
|
});
|
|
3337
3749
|
try {
|
|
3338
|
-
logger.debug("
|
|
3750
|
+
logger.debug("Authenticating to package registry...");
|
|
3339
3751
|
await execPromise(authCommand, {
|
|
3340
3752
|
cwd: config.cwd,
|
|
3341
3753
|
noStderr: true,
|
|
@@ -3343,10 +3755,9 @@ async function publishSafetyCheck({ config }) {
|
|
|
3343
3755
|
logLevel: config.logLevel,
|
|
3344
3756
|
noSuccess: true
|
|
3345
3757
|
});
|
|
3346
|
-
logger.info("
|
|
3758
|
+
logger.info("Successfully authenticated to package registry");
|
|
3347
3759
|
} catch (error) {
|
|
3348
|
-
|
|
3349
|
-
process.exit(1);
|
|
3760
|
+
throw new Error("Failed to authenticate to package registry", { cause: error });
|
|
3350
3761
|
}
|
|
3351
3762
|
}
|
|
3352
3763
|
}
|
|
@@ -3371,7 +3782,7 @@ async function publish(options = {}) {
|
|
|
3371
3782
|
logger.debug(`Dry run: ${dryRun}`);
|
|
3372
3783
|
const packageManager = config.publish.packageManager || detectPackageManager(config.cwd);
|
|
3373
3784
|
logger.debug(`Package manager: ${packageManager}`);
|
|
3374
|
-
logger.
|
|
3785
|
+
logger.debug(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
|
|
3375
3786
|
if (config.publish.registry) {
|
|
3376
3787
|
logger.debug(`Registry: ${config.publish.registry}`);
|
|
3377
3788
|
}
|
|
@@ -3418,7 +3829,7 @@ async function publish(options = {}) {
|
|
|
3418
3829
|
config,
|
|
3419
3830
|
dryRun
|
|
3420
3831
|
});
|
|
3421
|
-
for (const pkg of sortedPackages) {
|
|
3832
|
+
for await (const pkg of sortedPackages) {
|
|
3422
3833
|
if (publishedPackages.some((p) => p.name === pkg.name)) {
|
|
3423
3834
|
logger.debug(`Publishing ${getIndependentTag({ name: pkg.name, version: pkg.newVersion || pkg.version })}...`);
|
|
3424
3835
|
await publishPackage({
|
|
@@ -3446,6 +3857,286 @@ async function publish(options = {}) {
|
|
|
3446
3857
|
}
|
|
3447
3858
|
}
|
|
3448
3859
|
|
|
3860
|
+
function socialSafetyCheck({ config }) {
|
|
3861
|
+
const socialMediaDisabled = !config.release.social && !config.social.twitter.enabled && !config.social.slack.enabled;
|
|
3862
|
+
if (!config.safetyCheck || socialMediaDisabled) {
|
|
3863
|
+
logger.debug("Safety check disabled or social disabled");
|
|
3864
|
+
return;
|
|
3865
|
+
}
|
|
3866
|
+
logger.debug("Start checking social config");
|
|
3867
|
+
const errors = {
|
|
3868
|
+
twitter: false,
|
|
3869
|
+
slack: false
|
|
3870
|
+
};
|
|
3871
|
+
const twitterConfig = config.social?.twitter;
|
|
3872
|
+
if (twitterConfig?.enabled) {
|
|
3873
|
+
const credentials = getTwitterCredentials({
|
|
3874
|
+
socialCredentials: twitterConfig.credentials,
|
|
3875
|
+
tokenCredentials: config.tokens?.twitter
|
|
3876
|
+
});
|
|
3877
|
+
if (!credentials) {
|
|
3878
|
+
errors.twitter = true;
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
const slackConfig = config.social?.slack;
|
|
3882
|
+
if (slackConfig?.enabled) {
|
|
3883
|
+
const token = getSlackToken({
|
|
3884
|
+
socialCredentials: slackConfig.credentials,
|
|
3885
|
+
tokenCredential: config.tokens?.slack
|
|
3886
|
+
});
|
|
3887
|
+
if (!token) {
|
|
3888
|
+
logger.log("Slack is enabled but credentials are missing.");
|
|
3889
|
+
logger.log("Set the following environment variables or configure them in social.slack.credentials or tokens.slack:");
|
|
3890
|
+
logger.log(" - SLACK_TOKEN or RELIZY_SLACK_TOKEN");
|
|
3891
|
+
errors.slack = true;
|
|
3892
|
+
}
|
|
3893
|
+
if (!slackConfig.channel) {
|
|
3894
|
+
logger.warn("Slack is enabled but no channel is configured.");
|
|
3895
|
+
logger.log('Set the channel in social.slack.channel (e.g., "#releases" or "C1234567890")');
|
|
3896
|
+
errors.slack = true;
|
|
3897
|
+
}
|
|
3898
|
+
}
|
|
3899
|
+
if (errors.twitter || errors.slack) {
|
|
3900
|
+
throw new Error("Social config checked with errors");
|
|
3901
|
+
}
|
|
3902
|
+
logger.info("Social config checked successfully");
|
|
3903
|
+
}
|
|
3904
|
+
async function handleTwitterPost({
|
|
3905
|
+
config,
|
|
3906
|
+
changelog,
|
|
3907
|
+
dryRun,
|
|
3908
|
+
newVersion,
|
|
3909
|
+
tag
|
|
3910
|
+
}) {
|
|
3911
|
+
const twitterConfig = config.social?.twitter;
|
|
3912
|
+
if (!twitterConfig?.enabled) {
|
|
3913
|
+
logger.debug("Twitter posting is disabled in configuration");
|
|
3914
|
+
return { success: true, response: void 0 };
|
|
3915
|
+
}
|
|
3916
|
+
logger.debug("Twitter posting is enabled");
|
|
3917
|
+
try {
|
|
3918
|
+
const credentials = getTwitterCredentials({
|
|
3919
|
+
socialCredentials: twitterConfig.credentials,
|
|
3920
|
+
tokenCredentials: config.tokens?.twitter
|
|
3921
|
+
});
|
|
3922
|
+
if (!credentials) {
|
|
3923
|
+
return { success: false, error: "Twitter credentials not found" };
|
|
3924
|
+
}
|
|
3925
|
+
logger.debug("Credentials found \u2713");
|
|
3926
|
+
logger.debug("Preparing tweet for release");
|
|
3927
|
+
const onlyStable = twitterConfig.onlyStable;
|
|
3928
|
+
if (onlyStable && isPrerelease(newVersion)) {
|
|
3929
|
+
logger.info(`Skipping Twitter post for prerelease version ${newVersion} (social.twitter.onlyStable is enabled)`);
|
|
3930
|
+
return { success: true, response: void 0 };
|
|
3931
|
+
}
|
|
3932
|
+
await executeHook("before:twitter", config, dryRun);
|
|
3933
|
+
try {
|
|
3934
|
+
const rootPackageBase = readPackageJson(config.cwd);
|
|
3935
|
+
if (!rootPackageBase) {
|
|
3936
|
+
throw new Error("Failed to read root package.json");
|
|
3937
|
+
}
|
|
3938
|
+
logger.debug(`Project: ${rootPackageBase.name}`);
|
|
3939
|
+
const releaseUrl = getReleaseUrl(config, tag);
|
|
3940
|
+
logger.debug(`Release URL: ${releaseUrl || "none"}`);
|
|
3941
|
+
const changelogUrl = config.social?.changelogUrl;
|
|
3942
|
+
logger.debug(`Changelog URL: ${changelogUrl || "none"}`);
|
|
3943
|
+
logger.debug(`Changelog generated (${changelog.length} chars)`);
|
|
3944
|
+
const changelogSummary = extractChangelogSummary(changelog, 150);
|
|
3945
|
+
logger.debug(`Changelog summary: ${changelogSummary.substring(0, 50)}...`);
|
|
3946
|
+
const response = await postReleaseToTwitter({
|
|
3947
|
+
template: config.social.twitter.template || config.templates.twitterMessage,
|
|
3948
|
+
version: newVersion,
|
|
3949
|
+
projectName: rootPackageBase.name,
|
|
3950
|
+
changelog: changelogSummary,
|
|
3951
|
+
releaseUrl,
|
|
3952
|
+
changelogUrl,
|
|
3953
|
+
credentials,
|
|
3954
|
+
dryRun
|
|
3955
|
+
});
|
|
3956
|
+
await executeHook("success:twitter", config, dryRun);
|
|
3957
|
+
return { success: true, response };
|
|
3958
|
+
} catch (error) {
|
|
3959
|
+
await executeHook("error:twitter", config, dryRun);
|
|
3960
|
+
logger.error("Error posting to Twitter:", error);
|
|
3961
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3962
|
+
return { success: false, error: `Error posting to Twitter: ${errorMessage}` };
|
|
3963
|
+
}
|
|
3964
|
+
} catch (error) {
|
|
3965
|
+
logger.error("Error during Twitter posting:", error);
|
|
3966
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3967
|
+
return { success: false, error: `Error during Twitter posting: ${errorMessage}` };
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
async function handleSlackPost({
|
|
3971
|
+
config,
|
|
3972
|
+
changelog,
|
|
3973
|
+
dryRun,
|
|
3974
|
+
newVersion,
|
|
3975
|
+
tag
|
|
3976
|
+
}) {
|
|
3977
|
+
const slackConfig = config.social?.slack;
|
|
3978
|
+
if (!slackConfig?.enabled) {
|
|
3979
|
+
logger.debug("Slack posting is disabled in configuration");
|
|
3980
|
+
return { success: true, response: void 0 };
|
|
3981
|
+
}
|
|
3982
|
+
logger.debug("Slack posting is enabled");
|
|
3983
|
+
try {
|
|
3984
|
+
const token = getSlackToken({
|
|
3985
|
+
socialCredentials: slackConfig.credentials,
|
|
3986
|
+
tokenCredential: config.tokens?.slack
|
|
3987
|
+
});
|
|
3988
|
+
if (!token) {
|
|
3989
|
+
logger.warn("Slack token not found. Set SLACK_TOKEN or RELIZY_SLACK_TOKEN environment variable or configure it in social.slack.credentials or tokens.slack.");
|
|
3990
|
+
logger.info("Skipping Slack post");
|
|
3991
|
+
return { success: false, error: "Slack token not found" };
|
|
3992
|
+
}
|
|
3993
|
+
logger.debug("Token found \u2713");
|
|
3994
|
+
if (!slackConfig.channel) {
|
|
3995
|
+
logger.warn("Slack channel not configured. Set it in social.slack.channel.");
|
|
3996
|
+
logger.info("Skipping Slack post");
|
|
3997
|
+
return { success: false, error: "Slack channel not configured" };
|
|
3998
|
+
}
|
|
3999
|
+
logger.debug(`Channel configured: ${slackConfig.channel}`);
|
|
4000
|
+
logger.debug(`Preparing Slack message for release: ${tag} (${newVersion})`);
|
|
4001
|
+
const onlyStable = slackConfig.onlyStable ?? true;
|
|
4002
|
+
if (onlyStable && isPrerelease(newVersion)) {
|
|
4003
|
+
logger.info(`Skipping Slack post for prerelease version ${newVersion} (social.slack.onlyStable is enabled)`);
|
|
4004
|
+
return { success: true, response: void 0 };
|
|
4005
|
+
}
|
|
4006
|
+
try {
|
|
4007
|
+
await executeHook("before:slack", config, dryRun);
|
|
4008
|
+
const rootPackageBase = readPackageJson(config.cwd);
|
|
4009
|
+
if (!rootPackageBase) {
|
|
4010
|
+
throw new Error("Failed to read root package.json");
|
|
4011
|
+
}
|
|
4012
|
+
logger.debug(`Project: ${rootPackageBase.name}`);
|
|
4013
|
+
const releaseUrl = getReleaseUrl(config, tag);
|
|
4014
|
+
logger.debug(`Release URL: ${releaseUrl || "none"}`);
|
|
4015
|
+
const changelogUrl = config.social?.changelogUrl;
|
|
4016
|
+
logger.debug(`Changelog URL: ${changelogUrl || "none"}`);
|
|
4017
|
+
logger.debug(`Changelog generated (${changelog.length} chars)`);
|
|
4018
|
+
const template = slackConfig.template || config.templates.slackMessage;
|
|
4019
|
+
const response = await postReleaseToSlack({
|
|
4020
|
+
version: newVersion,
|
|
4021
|
+
projectName: rootPackageBase.name,
|
|
4022
|
+
changelog,
|
|
4023
|
+
releaseUrl,
|
|
4024
|
+
changelogUrl,
|
|
4025
|
+
channel: slackConfig.channel,
|
|
4026
|
+
token,
|
|
4027
|
+
template,
|
|
4028
|
+
dryRun
|
|
4029
|
+
});
|
|
4030
|
+
await executeHook("success:slack", config, dryRun);
|
|
4031
|
+
return { success: true, response };
|
|
4032
|
+
} catch (error) {
|
|
4033
|
+
await executeHook("error:slack", config, dryRun);
|
|
4034
|
+
logger.error("Error posting to Slack:", error);
|
|
4035
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4036
|
+
return { success: false, error: `Error posting to Slack: ${errorMessage}` };
|
|
4037
|
+
}
|
|
4038
|
+
} catch (error) {
|
|
4039
|
+
logger.error("Error during Slack posting:", error);
|
|
4040
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4041
|
+
return { success: false, error: `Error during Slack posting: ${errorMessage}` };
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
async function social(options = {}) {
|
|
4045
|
+
try {
|
|
4046
|
+
const dryRun = options.dryRun ?? false;
|
|
4047
|
+
logger.debug(`Dry run: ${dryRun}`);
|
|
4048
|
+
const config = await loadRelizyConfig({
|
|
4049
|
+
configFile: options.configName,
|
|
4050
|
+
baseConfig: options.config,
|
|
4051
|
+
overrides: {
|
|
4052
|
+
from: options.from,
|
|
4053
|
+
to: options.to,
|
|
4054
|
+
logLevel: options.logLevel
|
|
4055
|
+
}
|
|
4056
|
+
});
|
|
4057
|
+
logger.debug(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
|
|
4058
|
+
socialSafetyCheck({ config });
|
|
4059
|
+
if (!config.release.social && !config.social?.twitter?.enabled && !config.social?.slack?.enabled) {
|
|
4060
|
+
logger.warn("Social media posting is disabled in configuration.");
|
|
4061
|
+
logger.info("Enable it with release.social: true or social.twitter.enabled: true or social.slack.enabled: true");
|
|
4062
|
+
return { results: [], hasErrors: false };
|
|
4063
|
+
}
|
|
4064
|
+
await executeHook("before:social", config, dryRun);
|
|
4065
|
+
const rootPackageRead = readPackageJson(config.cwd);
|
|
4066
|
+
if (!rootPackageRead) {
|
|
4067
|
+
throw new Error("Failed to read root package.json");
|
|
4068
|
+
}
|
|
4069
|
+
const newVersion = options.bumpResult?.newVersion || rootPackageRead.version;
|
|
4070
|
+
const { from, to } = await resolveTags({
|
|
4071
|
+
config,
|
|
4072
|
+
step: "social",
|
|
4073
|
+
newVersion,
|
|
4074
|
+
pkg: rootPackageRead
|
|
4075
|
+
});
|
|
4076
|
+
const fromTag = options.bumpResult?.fromTag || from;
|
|
4077
|
+
const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
|
|
4078
|
+
config,
|
|
4079
|
+
force: false,
|
|
4080
|
+
suffix: void 0,
|
|
4081
|
+
changelog: true,
|
|
4082
|
+
from: fromTag,
|
|
4083
|
+
to
|
|
4084
|
+
});
|
|
4085
|
+
const changelog = await generateChangelog({
|
|
4086
|
+
pkg: rootPackage,
|
|
4087
|
+
config,
|
|
4088
|
+
dryRun,
|
|
4089
|
+
newVersion,
|
|
4090
|
+
minify: true
|
|
4091
|
+
});
|
|
4092
|
+
const twitterResponse = await handleTwitterPost({
|
|
4093
|
+
config,
|
|
4094
|
+
changelog,
|
|
4095
|
+
dryRun,
|
|
4096
|
+
newVersion,
|
|
4097
|
+
tag: to
|
|
4098
|
+
});
|
|
4099
|
+
const slackResponse = await handleSlackPost({
|
|
4100
|
+
config,
|
|
4101
|
+
changelog,
|
|
4102
|
+
dryRun,
|
|
4103
|
+
newVersion,
|
|
4104
|
+
tag: to
|
|
4105
|
+
});
|
|
4106
|
+
const results = [];
|
|
4107
|
+
if (config.social?.twitter?.enabled) {
|
|
4108
|
+
results.push({
|
|
4109
|
+
platform: "twitter",
|
|
4110
|
+
success: twitterResponse.success,
|
|
4111
|
+
error: twitterResponse.success ? void 0 : twitterResponse.error
|
|
4112
|
+
});
|
|
4113
|
+
}
|
|
4114
|
+
if (config.social?.slack?.enabled) {
|
|
4115
|
+
results.push({
|
|
4116
|
+
platform: "slack",
|
|
4117
|
+
success: slackResponse.success,
|
|
4118
|
+
error: slackResponse.success ? void 0 : slackResponse.error
|
|
4119
|
+
});
|
|
4120
|
+
}
|
|
4121
|
+
const hasErrors = results.some((r) => !r.success);
|
|
4122
|
+
if (hasErrors) {
|
|
4123
|
+
await executeHook("error:social", config, dryRun);
|
|
4124
|
+
logger.warn("Some social media posts failed");
|
|
4125
|
+
} else {
|
|
4126
|
+
logger.success("Social media posts completed!");
|
|
4127
|
+
await executeHook("success:social", config, dryRun);
|
|
4128
|
+
}
|
|
4129
|
+
return { results, hasErrors };
|
|
4130
|
+
} catch (error) {
|
|
4131
|
+
logger.error("Error during social media posting:", error);
|
|
4132
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4133
|
+
return {
|
|
4134
|
+
results: [{ platform: "unknown", success: false, error: errorMessage }],
|
|
4135
|
+
hasErrors: true
|
|
4136
|
+
};
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
|
|
3449
4140
|
function getReleaseConfig(options = {}) {
|
|
3450
4141
|
return loadRelizyConfig({
|
|
3451
4142
|
configFile: options.configName,
|
|
@@ -3483,7 +4174,8 @@ function getReleaseConfig(options = {}) {
|
|
|
3483
4174
|
noVerify: options.noVerify,
|
|
3484
4175
|
providerRelease: options.providerRelease,
|
|
3485
4176
|
clean: options.clean,
|
|
3486
|
-
gitTag: options.gitTag
|
|
4177
|
+
gitTag: options.gitTag,
|
|
4178
|
+
social: options.social
|
|
3487
4179
|
},
|
|
3488
4180
|
safetyCheck: options.safetyCheck
|
|
3489
4181
|
}
|
|
@@ -3496,8 +4188,19 @@ async function releaseSafetyCheck({
|
|
|
3496
4188
|
if (!config.safetyCheck) {
|
|
3497
4189
|
return;
|
|
3498
4190
|
}
|
|
3499
|
-
|
|
3500
|
-
|
|
4191
|
+
logger.box("Safety checks");
|
|
4192
|
+
logger.start("Start safety checks");
|
|
4193
|
+
try {
|
|
4194
|
+
await Promise.all([
|
|
4195
|
+
providerReleaseSafetyCheck({ config, provider }),
|
|
4196
|
+
publishSafetyCheck({ config }),
|
|
4197
|
+
socialSafetyCheck({ config })
|
|
4198
|
+
]);
|
|
4199
|
+
logger.success("Safety checks passed");
|
|
4200
|
+
} catch (error) {
|
|
4201
|
+
logger.error("Safety checks failed");
|
|
4202
|
+
throw error;
|
|
4203
|
+
}
|
|
3501
4204
|
}
|
|
3502
4205
|
async function release(options = {}) {
|
|
3503
4206
|
const dryRun = options.dryRun ?? false;
|
|
@@ -3510,7 +4213,7 @@ async function release(options = {}) {
|
|
|
3510
4213
|
await releaseSafetyCheck({ config, provider: options.provider });
|
|
3511
4214
|
try {
|
|
3512
4215
|
await executeHook("before:release", config, dryRun);
|
|
3513
|
-
logger.box("
|
|
4216
|
+
logger.box("Bump versions");
|
|
3514
4217
|
const bumpResult = await bump({
|
|
3515
4218
|
type: config.bump.type,
|
|
3516
4219
|
preid: config.bump.preid,
|
|
@@ -3525,7 +4228,7 @@ async function release(options = {}) {
|
|
|
3525
4228
|
logger.debug("No packages bumped");
|
|
3526
4229
|
return;
|
|
3527
4230
|
}
|
|
3528
|
-
logger.box("
|
|
4231
|
+
logger.box("Generate changelogs");
|
|
3529
4232
|
if (config.release.changelog) {
|
|
3530
4233
|
await changelog({
|
|
3531
4234
|
from: config.from,
|
|
@@ -3543,7 +4246,32 @@ async function release(options = {}) {
|
|
|
3543
4246
|
} else {
|
|
3544
4247
|
logger.info("Skipping changelog generation (--no-changelog)");
|
|
3545
4248
|
}
|
|
3546
|
-
logger.box("
|
|
4249
|
+
logger.box("Publish packages to registry");
|
|
4250
|
+
let publishResponse;
|
|
4251
|
+
if (config.release.publish) {
|
|
4252
|
+
try {
|
|
4253
|
+
publishResponse = await publish({
|
|
4254
|
+
registry: config.publish.registry,
|
|
4255
|
+
tag: config.publish.tag,
|
|
4256
|
+
access: config.publish.access,
|
|
4257
|
+
otp: config.publish.otp,
|
|
4258
|
+
bumpResult,
|
|
4259
|
+
dryRun,
|
|
4260
|
+
config,
|
|
4261
|
+
configName: options.configName,
|
|
4262
|
+
suffix: options.suffix,
|
|
4263
|
+
force,
|
|
4264
|
+
safetyCheck: false
|
|
4265
|
+
});
|
|
4266
|
+
} catch (error) {
|
|
4267
|
+
logger.fail("Publish failed, rolling back modified files...");
|
|
4268
|
+
await rollbackModifiedFiles({ config });
|
|
4269
|
+
throw error;
|
|
4270
|
+
}
|
|
4271
|
+
} else {
|
|
4272
|
+
logger.info("Skipping publish (--no-publish)");
|
|
4273
|
+
}
|
|
4274
|
+
logger.box("Commit changes and create tag");
|
|
3547
4275
|
let createdTags = [];
|
|
3548
4276
|
if (config.release.commit) {
|
|
3549
4277
|
createdTags = await createCommitAndTags({
|
|
@@ -3557,7 +4285,7 @@ async function release(options = {}) {
|
|
|
3557
4285
|
} else {
|
|
3558
4286
|
logger.info("Skipping commit and tag (--no-commit)");
|
|
3559
4287
|
}
|
|
3560
|
-
logger.box("
|
|
4288
|
+
logger.box("Push changes and tags");
|
|
3561
4289
|
if (config.release.push && config.release.commit) {
|
|
3562
4290
|
await executeHook("before:push", config, dryRun);
|
|
3563
4291
|
try {
|
|
@@ -3575,60 +4303,77 @@ async function release(options = {}) {
|
|
|
3575
4303
|
} else {
|
|
3576
4304
|
logger.info("Skipping push (--no-push or --no-commit)");
|
|
3577
4305
|
}
|
|
3578
|
-
|
|
3579
|
-
let
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
4306
|
+
let provider = config.repo?.provider;
|
|
4307
|
+
let postedReleases = [];
|
|
4308
|
+
let providerError;
|
|
4309
|
+
logger.box("Publish Git release");
|
|
4310
|
+
if (config.release.providerRelease) {
|
|
4311
|
+
logger.debug(`Provider from config: ${provider}`);
|
|
4312
|
+
const response = await providerRelease({
|
|
4313
|
+
from: config.from,
|
|
4314
|
+
to: config.to,
|
|
4315
|
+
token: options.token,
|
|
4316
|
+
provider,
|
|
3587
4317
|
dryRun,
|
|
3588
4318
|
config,
|
|
4319
|
+
logLevel: config.logLevel,
|
|
4320
|
+
bumpResult,
|
|
3589
4321
|
configName: options.configName,
|
|
4322
|
+
force,
|
|
3590
4323
|
suffix: options.suffix,
|
|
3591
|
-
|
|
4324
|
+
safetyCheck: false
|
|
3592
4325
|
});
|
|
4326
|
+
provider = response.detectedProvider;
|
|
4327
|
+
postedReleases = response.postedReleases;
|
|
4328
|
+
providerError = response.error;
|
|
3593
4329
|
} else {
|
|
3594
|
-
logger.info("Skipping
|
|
4330
|
+
logger.info("Skipping release (--no-provider-release)");
|
|
3595
4331
|
}
|
|
3596
|
-
|
|
3597
|
-
let
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
bumpResult,
|
|
3611
|
-
configName: options.configName,
|
|
3612
|
-
force,
|
|
3613
|
-
suffix: options.suffix
|
|
3614
|
-
});
|
|
3615
|
-
provider = response.detectedProvider;
|
|
3616
|
-
postedReleases = response.postedReleases;
|
|
3617
|
-
} catch (error) {
|
|
3618
|
-
logger.error("Error during release publication:", error);
|
|
3619
|
-
}
|
|
4332
|
+
logger.box("Post release to social media");
|
|
4333
|
+
let socialResults;
|
|
4334
|
+
if (config.release.social && (config.social?.twitter?.enabled || config.social?.slack?.enabled)) {
|
|
4335
|
+
socialResults = await social({
|
|
4336
|
+
from: config.from,
|
|
4337
|
+
to: config.to,
|
|
4338
|
+
config,
|
|
4339
|
+
configName: options.configName,
|
|
4340
|
+
bumpResult,
|
|
4341
|
+
dryRun,
|
|
4342
|
+
logLevel: config.logLevel,
|
|
4343
|
+
safetyCheck: false
|
|
4344
|
+
// Already checked in releaseSafetyCheck
|
|
4345
|
+
});
|
|
3620
4346
|
} else {
|
|
3621
|
-
logger.info("Skipping
|
|
4347
|
+
logger.info("Skipping social media posts (--no-social or no social media enabled)");
|
|
3622
4348
|
}
|
|
3623
4349
|
const publishedPackageCount = publishResponse?.publishedPackages.length ?? 0;
|
|
3624
4350
|
const versionDisplay = config.monorepo?.versionMode === "independent" ? `${bumpResult.bumpedPackages.length} packages bumped independently` : bumpResult.newVersion || readPackageJson(config.cwd)?.version;
|
|
4351
|
+
let providerDisplay = "Disabled";
|
|
4352
|
+
if (config.release.providerRelease) {
|
|
4353
|
+
if (providerError) {
|
|
4354
|
+
providerDisplay = `Failed: ${providerError}`;
|
|
4355
|
+
} else {
|
|
4356
|
+
providerDisplay = `${postedReleases.length} release${postedReleases.length !== 1 ? "s" : ""}`;
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
let socialDisplay = "Disabled";
|
|
4360
|
+
if (config.release.social && socialResults) {
|
|
4361
|
+
if (socialResults.hasErrors) {
|
|
4362
|
+
const failed = socialResults.results.filter((r) => !r.success).map((r) => r.platform);
|
|
4363
|
+
const succeeded = socialResults.results.filter((r) => r.success).map((r) => r.platform);
|
|
4364
|
+
socialDisplay = `${succeeded.length} succeeded, ${failed.length} failed (${failed.join(", ")})`;
|
|
4365
|
+
} else {
|
|
4366
|
+
socialDisplay = `${socialResults.results.length} succeeded`;
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
3625
4369
|
logger.box(`Release workflow completed!
|
|
3626
4370
|
|
|
3627
4371
|
Version: ${versionDisplay ?? "Unknown"}
|
|
3628
|
-
Tag(s): ${createdTags
|
|
4372
|
+
Tag(s): ${createdTags?.length ? createdTags.join(", ") : "None"}
|
|
3629
4373
|
Pushed: ${config.release.push ? "Yes" : "Disabled"}
|
|
3630
4374
|
Published packages: ${config.release.publish ? publishedPackageCount : "Disabled"}
|
|
3631
|
-
|
|
4375
|
+
Provider release: ${providerDisplay}
|
|
4376
|
+
Social media: ${socialDisplay}
|
|
3632
4377
|
Git provider: ${provider}`);
|
|
3633
4378
|
await executeHook("success:release", config, dryRun);
|
|
3634
4379
|
} catch (error) {
|
|
@@ -3638,4 +4383,4 @@ Git provider: ${provider}`);
|
|
|
3638
4383
|
}
|
|
3639
4384
|
}
|
|
3640
4385
|
|
|
3641
|
-
export {
|
|
4386
|
+
export { getLastTag as $, rollbackModifiedFiles as A, getFirstCommit as B, getCurrentGitBranch as C, getCurrentGitRef as D, github as E, createGitlabRelease as F, gitlab as G, detectPackageManager as H, determinePublishTag as I, getPackagesToPublishInSelectiveMode as J, getPackagesToPublishInIndependentMode as K, getAuthCommand as L, publishPackage as M, readPackageJson as N, getRootPackage as O, readPackages as P, getPackages as Q, getPackageCommits as R, hasLernaJson as S, getSlackToken as T, formatChangelogForSlack as U, formatSlackMessage as V, postReleaseToSlack as W, extractChangelogSummary as X, getReleaseUrl as Y, getIndependentTag as Z, getLastStableTag as _, providerRelease as a, getLastRepoTag as a0, getLastPackageTag as a1, resolveTags as a2, getTwitterCredentials as a3, formatTweetMessage as a4, postReleaseToTwitter as a5, executeHook as a6, isInCI as a7, getCIName as a8, executeFormatCmd as a9, executeBuildCmd as aa, isBumpedPackage as ab, getPackagesOrBumpedPackages as ac, isGraduatingToStableBetweenVersion as ad, determineSemverChange as ae, determineReleaseType as af, writeVersion as ag, getPackageNewVersion as ah, updateLernaVersion as ai, extractVersionFromPackageTag as aj, isPrerelease as ak, isStableReleaseType as al, isPrereleaseReleaseType as am, isGraduating as an, getPreid as ao, isChangedPreid as ap, getBumpedPackageIndependently as aq, confirmBump as ar, getBumpedIndependentPackages as as, shouldFilterPrereleaseTags as at, extractVersionFromTag as au, isTagVersionCompatibleWithCurrent as av, bump as b, changelog as c, publishSafetyCheck as d, publish as e, social as f, generateChangelog as g, getDefaultConfig as h, defineConfig as i, getPackageDependencies as j, getDependentsOf as k, loadRelizyConfig as l, expandPackagesToBumpWithDependents as m, getGitStatus as n, checkGitStatusIfDirty as o, providerReleaseSafetyCheck as p, fetchGitTags as q, release as r, socialSafetyCheck as s, topologicalSort as t, detectGitProvider as u, parseGitRemoteUrl as v, writeChangelogToFile as w, getModifiedReleaseFilePatterns as x, createCommitAndTags as y, pushCommitAndTags as z };
|