@storm-software/git-tools 2.124.0 → 2.124.1

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/bin/git.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import './chunk-Q3DQKTOI.js';
2
3
  import { run } from './chunk-G3YPGVPS.js';
3
4
  import { getConfig, handleProcess, writeSuccess, exitWithSuccess, exitWithError, writeInfo, findWorkspaceRootSafe, writeFatal, getWorkspaceConfig, joinPaths, writeDebug, isVerbose, writeWarning, defu, writeTrace, STORM_DEFAULT_RELEASE_BANNER } from './chunk-VBJB46HW.js';
4
5
  import TOML from '@ltd/j-toml';
@@ -26,22 +27,21 @@ import _ from 'underscore';
26
27
  import updateSection from 'update-section';
27
28
  import { readCachedProjectGraph as readCachedProjectGraph$1, createProjectGraphAsync as createProjectGraphAsync$2, readProjectsConfigurationFromProjectGraph as readProjectsConfigurationFromProjectGraph$2, output, joinPathFragments } from '@nx/devkit';
28
29
  import axios from 'axios';
29
- import 'enquirer';
30
30
  import { homedir } from 'node:os';
31
- import { printAndFlushChanges } from 'nx/src/command-line/release/utils/print-changes';
32
31
  import { defaultCreateReleaseProvider, GithubRemoteReleaseClient } from 'nx/src/command-line/release/utils/remote-release-clients/github';
33
- import { noDiffInChangelogMessage, handleDuplicateGitTags, createCommitMessageValues, isPrerelease, shouldPreferDockerVersionForReleaseGroup, ReleaseVersion } from 'nx/src/command-line/release/utils/shared';
34
32
  import { parse } from 'yaml';
35
33
  import chalk from 'chalk';
34
+ import { printAndFlushChanges } from 'nx/src/command-line/release/utils/print-changes';
35
+ import { createGitTagValues, handleDuplicateGitTags, createCommitMessageValues, isPrerelease, shouldPreferDockerVersionForReleaseGroup, ReleaseVersion, noDiffInChangelogMessage } from 'nx/src/command-line/release/utils/shared';
36
36
  import { interpolate } from 'nx/src/tasks-runner/utils';
37
37
  import { resolveConfig, format } from 'prettier';
38
- import { ReleaseClient } from 'nx/release';
38
+ import { execCommand } from 'nx/src/command-line/release/utils/exec-command.js';
39
39
  import { getCommitHash, getLatestGitTagForPattern, getFirstGitCommit, gitPush, getGitDiff, parseCommits, gitAdd } from 'nx/src/command-line/release/utils/git';
40
+ import { prerelease, major } from 'semver';
41
+ import { ReleaseClient } from 'nx/release';
40
42
  import { readNxJson } from 'nx/src/config/nx-json';
41
43
  import { FsTree } from 'nx/src/generators/tree';
42
44
  import { createFileMapUsingProjectGraph } from 'nx/src/project-graph/file-map-utils';
43
- import { execCommand } from 'nx/src/command-line/release/utils/exec-command.js';
44
- import { prerelease, major } from 'semver';
45
45
  import DefaultChangelogRenderer from 'nx/release/changelog-renderer';
46
46
  import { DEFAULT_CONVENTIONAL_COMMITS_CONFIG } from 'nx/src/command-line/release/config/conventional-commits';
47
47
 
@@ -1947,7 +1947,7 @@ var createRegExp = (sectionName) => {
1947
1947
  var runReadme = async ({
1948
1948
  templates = "./tools/readme-templates",
1949
1949
  project,
1950
- output: output4,
1950
+ output: output3,
1951
1951
  clean = true,
1952
1952
  prettier = true
1953
1953
  }) => {
@@ -1958,7 +1958,7 @@ var runReadme = async ({
1958
1958
  if (project) {
1959
1959
  await runProjectReadme(project, {
1960
1960
  templates,
1961
- output: output4,
1961
+ output: output3,
1962
1962
  clean,
1963
1963
  prettier
1964
1964
  });
@@ -1966,14 +1966,14 @@ var runReadme = async ({
1966
1966
  for (const projectName of Object.keys(projectConfigs.projects)) {
1967
1967
  await runProjectReadme(projectName, {
1968
1968
  templates,
1969
- output: output4,
1969
+ output: output3,
1970
1970
  clean,
1971
1971
  prettier
1972
1972
  });
1973
1973
  }
1974
1974
  }
1975
1975
  };
1976
- var runProjectReadme = async (projectName, { templates, output: output4, clean = true, prettier = true }) => {
1976
+ var runProjectReadme = async (projectName, { templates, output: output3, clean = true, prettier = true }) => {
1977
1977
  const projectGraph = await createProjectGraphAsync({
1978
1978
  exitOnError: true
1979
1979
  });
@@ -1982,7 +1982,7 @@ var runProjectReadme = async (projectName, { templates, output: output4, clean =
1982
1982
  const inputFile = join$1(project?.root ?? "./", "README.md");
1983
1983
  if (existsSync$1(inputFile)) {
1984
1984
  console.info(`Formatting ${projectName}'s README file at "${inputFile}"`);
1985
- const outputFilePath = output4 ? output4.includes("README.md") ? output4 : join$1(findFilePath(output4), "README.md") : inputFile;
1985
+ const outputFilePath = output3 ? output3.includes("README.md") ? output3 : join$1(findFilePath(output3), "README.md") : inputFile;
1986
1986
  if (clean && existsSync$1(outputFilePath)) {
1987
1987
  if (outputFilePath === inputFile) {
1988
1988
  console.warn(
@@ -2391,7 +2391,7 @@ function generateChangelogTitle(version, project, workspaceConfig) {
2391
2391
  if (!workspaceConfig?.name || !project) {
2392
2392
  return version;
2393
2393
  }
2394
- return `[${version}](https://github.com/${typeof workspaceConfig.organization === "string" ? workspaceConfig.organization : workspaceConfig.organization?.name}/${workspaceConfig.name}/releases/tag/${project}%40${version}) (${(/* @__PURE__ */ new Date()).getMonth() + 1}/${(/* @__PURE__ */ new Date()).getDate()}/${(/* @__PURE__ */ new Date()).getFullYear()})`;
2394
+ return `[${version}](https://github.com/${typeof workspaceConfig.organization === "string" ? workspaceConfig.organization : workspaceConfig.organization?.name}/${workspaceConfig.name}/releases/tag/${project}%40${version}) (${(/* @__PURE__ */ new Date()).getMonth() + 1 < 10 ? `0${(/* @__PURE__ */ new Date()).getMonth() + 1}` : (/* @__PURE__ */ new Date()).getMonth() + 1}/${(/* @__PURE__ */ new Date()).getDate() < 10 ? `0${(/* @__PURE__ */ new Date()).getDate()}` : (/* @__PURE__ */ new Date()).getDate()}/${(/* @__PURE__ */ new Date()).getFullYear()})`;
2395
2395
  }
2396
2396
  function parseChangelogMarkdown(contents) {
2397
2397
  const CHANGELOG_RELEASE_HEAD_RE = new RegExp(
@@ -2431,8 +2431,10 @@ function filterHiddenChanges(changes, conventionalCommitsConfig) {
2431
2431
  });
2432
2432
  }
2433
2433
  async function generateChangelogForProjects({
2434
+ tree,
2434
2435
  args,
2435
2436
  changes,
2437
+ projectGraph,
2436
2438
  projectsVersionData,
2437
2439
  releaseGroup,
2438
2440
  projects,
@@ -2446,9 +2448,21 @@ async function generateChangelogForProjects({
2446
2448
  return;
2447
2449
  }
2448
2450
  const dryRun = !!args.dryRun;
2449
- const remoteReleaseClient = await createGithubRemoteReleaseClient(
2450
- workspaceConfig,
2451
- args.gitRemote
2451
+ const repoData = getGitHubRepoData(args.gitRemote, "github");
2452
+ if (!repoData) {
2453
+ throw new Error(
2454
+ `Unable to create a remote release client because the GitHub repo slug could not be determined. Please ensure you have a valid GitHub remote configured.`
2455
+ );
2456
+ }
2457
+ const remoteReleaseClient = new StormGithubRemoteReleaseClient(
2458
+ repoData,
2459
+ {
2460
+ provider: "github",
2461
+ hostname: repoData.hostname,
2462
+ apiBaseUrl: repoData.apiBaseUrl
2463
+ },
2464
+ await resolveTokenData(repoData.hostname),
2465
+ workspaceConfig
2452
2466
  );
2453
2467
  const projectChangelogs = {};
2454
2468
  for (const project of projects) {
@@ -2507,227 +2521,48 @@ async function generateChangelogForProjects({
2507
2521
  postGitTask
2508
2522
  };
2509
2523
  }
2510
- return projectChangelogs;
2511
- }
2512
-
2513
- // src/release/github.ts
2514
- var StormGithubRemoteReleaseClient = class extends GithubRemoteReleaseClient {
2515
- #remoteRepoData;
2516
- #workspaceConfig;
2517
- /**
2518
- * Creates an instance of {@link StormGithubRemoteReleaseClient}.
2519
- *
2520
- * @param remoteRepoData - Data about the remote repository
2521
- * @param createReleaseConfig - Configuration for creating releases
2522
- * @param tokenData - Token data for authentication
2523
- * @param workspaceConfig - The Storm workspace configuration object, which is loaded from the storm-workspace.json file.
2524
- */
2525
- constructor(remoteRepoData, createReleaseConfig, tokenData, workspaceConfig) {
2526
- super(remoteRepoData, createReleaseConfig, tokenData);
2527
- this.#remoteRepoData = remoteRepoData;
2528
- this.#workspaceConfig = workspaceConfig;
2529
- }
2530
- createPostGitTask(releaseVersion, changelogContents, dryRun) {
2531
- return async (latestCommit) => {
2532
- if (!this.#workspaceConfig) {
2533
- this.#workspaceConfig = await getWorkspaceConfig();
2534
- }
2535
- output.logSingleLine(`Creating GitHub Release`);
2536
- const name = releaseVersion.gitTag.includes("@") ? releaseVersion.gitTag.replace(new RegExp(`^@${this.#workspaceConfig.name}/`), "").replace(/@.*$/, "") : releaseVersion.gitTag;
2537
- await this.createOrUpdateRelease(
2538
- releaseVersion,
2539
- `![${(typeof this.#workspaceConfig.release.banner === "string" ? this.#workspaceConfig.organization ? titleCase(
2540
- typeof this.#workspaceConfig.organization === "string" ? this.#workspaceConfig.organization : this.#workspaceConfig.organization.name
2541
- ) : void 0 : this.#workspaceConfig.release.banner.alt) || "Release banner header"}](${typeof this.#workspaceConfig.release.banner === "string" ? this.#workspaceConfig.release.banner : this.#workspaceConfig.release.banner?.url})
2542
- ${this.#workspaceConfig.release.header || ""}
2543
-
2544
- # ${name ? `${titleCase(name)} ` : ""}v${releaseVersion.rawVersion}
2545
-
2546
- We at [${this.#workspaceConfig.organization ? titleCase(
2547
- typeof this.#workspaceConfig.organization === "string" ? this.#workspaceConfig.organization : this.#workspaceConfig.organization.name
2548
- ) : ""}](${this.#workspaceConfig.homepage}) are very excited to announce the v${releaseVersion.rawVersion} release of the ${name ? this.#workspaceConfig.name ? `${titleCase(this.#workspaceConfig.name)} - ${titleCase(name)}` : titleCase(name) : this.#workspaceConfig.name ? titleCase(this.#workspaceConfig.name) : "Storm Software"} project! \u{1F680}
2549
-
2550
- These changes are released under the ${this.#workspaceConfig.license.includes("license") ? this.#workspaceConfig.license : `${this.#workspaceConfig.license} license`}. You can find more details on [our licensing page](${this.#workspaceConfig.licensing}). You can find guides, API references, and other documentation around this release (and much more) on [our documentation site](${this.#workspaceConfig.docs}).
2551
-
2552
- If you have any questions or comments, feel free to reach out to the team on [Discord](${this.#workspaceConfig.socials.discord}) or [our contact page](${this.#workspaceConfig.contact}). Please help us spread the word by giving [this repository](https://github.com/${typeof this.#workspaceConfig.organization === "string" ? this.#workspaceConfig.organization : this.#workspaceConfig.organization?.name}/${this.#workspaceConfig.name}) a star \u2B50 on GitHub or [posting on X (Twitter)](https://x.com/intent/tweet?text=Check%20out%20the%20latest%20@${this.#workspaceConfig.socials.twitter}%20release%20${name ? `${titleCase(name)?.replaceAll(" ", "%20")}%20` : ""}v${releaseVersion.rawVersion}%20%F0%9F%9A%80%0D%0A%0D%0Ahttps://github.com/${typeof this.#workspaceConfig.organization === "string" ? this.#workspaceConfig.organization : this.#workspaceConfig.organization?.name}/${this.#workspaceConfig.name}/releases/tag/${releaseVersion.gitTag}) about this release!
2553
-
2554
- ## Release Notes
2555
-
2556
- ${changelogContents.replaceAll(
2557
- `## ${generateChangelogTitle(
2558
- releaseVersion.rawVersion,
2559
- name,
2560
- this.#workspaceConfig
2561
- )}`,
2562
- ""
2563
- ).replaceAll(
2564
- `# ${generateChangelogTitle(releaseVersion.rawVersion, name, this.#workspaceConfig)}`,
2565
- ""
2566
- )}
2567
-
2568
- ---
2569
-
2570
- ${this.#workspaceConfig.release.footer}
2571
- `,
2572
- latestCommit,
2573
- { dryRun }
2524
+ for (const [projectName, projectChangelog] of Object.entries(
2525
+ projectChangelogs
2526
+ )) {
2527
+ if (!projectGraph.nodes[projectName]?.data.root) {
2528
+ writeWarning(
2529
+ `A changelog was generated for ${projectName}, but it could not be found in the project graph. Skipping writing changelog file.`,
2530
+ workspaceConfig
2574
2531
  );
2575
- };
2576
- }
2577
- /**
2578
- * Get remote repository data, attempting to resolve it if not already set.
2579
- */
2580
- getRemoteRepoData() {
2581
- if (!this.#remoteRepoData) {
2582
- let githubRepoData = super.getRemoteRepoData();
2583
- if (!githubRepoData) {
2584
- githubRepoData = getGitHubRepoData();
2585
- if (!githubRepoData) {
2586
- output.error({
2587
- title: `Unable to create a GitHub release because the GitHub repo slug could not be determined.`,
2588
- bodyLines: [
2589
- `Please ensure you have a valid GitHub remote configured. You can run \`git remote -v\` to list your current remotes.`
2590
- ]
2591
- });
2592
- process.exit(1);
2593
- }
2594
- }
2595
- this.#remoteRepoData = githubRepoData;
2596
- }
2597
- return this.#remoteRepoData;
2598
- }
2599
- };
2600
- function getGitHubRepoData(remoteName = "origin", createReleaseConfig = "github") {
2601
- try {
2602
- const remoteUrl = execSync(`git remote get-url ${remoteName}`, {
2603
- encoding: "utf8",
2604
- stdio: "pipe"
2605
- }).trim();
2606
- let hostname = defaultCreateReleaseProvider.hostname;
2607
- let apiBaseUrl = defaultCreateReleaseProvider.apiBaseUrl;
2608
- if (createReleaseConfig && typeof createReleaseConfig !== "string") {
2609
- hostname = createReleaseConfig.hostname;
2610
- apiBaseUrl = createReleaseConfig.apiBaseUrl;
2611
- }
2612
- const escapedHostname = hostname.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2613
- const regexString = `${escapedHostname}[/:]([\\w.-]+/[\\w.-]+)(\\.git)?`;
2614
- const regex = new RegExp(regexString);
2615
- const match = remoteUrl?.match(regex);
2616
- if (match && match[1]) {
2617
- return {
2618
- hostname,
2619
- apiBaseUrl,
2620
- // Ensure any trailing .git is stripped
2621
- slug: match[1].replace(/\.git$/, "")
2622
- };
2623
- } else {
2624
- throw new Error(
2625
- `Could not extract "user/repo" data from the resolved remote URL: ${remoteUrl}`
2532
+ } else if (projectChangelog.contents) {
2533
+ const filePath = joinPaths(
2534
+ projectGraph.nodes[projectName].data.root,
2535
+ "CHANGELOG.md"
2626
2536
  );
2627
- }
2628
- } catch (error) {
2629
- output.error({
2630
- title: `Failed to get GitHub repo data`,
2631
- bodyLines: [error.message]
2632
- });
2633
- return void 0;
2634
- }
2635
- }
2636
- async function resolveTokenData(hostname) {
2637
- const tokenFromEnv = process.env.STORM_BOT_GITHUB_TOKEN || process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
2638
- if (tokenFromEnv) {
2639
- return { token: tokenFromEnv, headerName: "Authorization" };
2640
- }
2641
- const ghCLIPath = joinPathFragments(
2642
- process.env.XDG_CONFIG_HOME || joinPathFragments(homedir(), ".config"),
2643
- "gh",
2644
- "hosts.yml"
2645
- );
2646
- if (existsSync$1(ghCLIPath)) {
2647
- const yamlContents = await promises.readFile(ghCLIPath, "utf8");
2648
- const ghCLIConfig = parse(yamlContents);
2649
- if (ghCLIConfig[hostname]) {
2650
- if (ghCLIConfig[hostname].oauth_token) {
2651
- return ghCLIConfig[hostname].oauth_token;
2537
+ let currentContent;
2538
+ if (existsSync$1(filePath)) {
2539
+ currentContent = await readFile$1(filePath, "utf8");
2652
2540
  }
2653
- if (ghCLIConfig[hostname].user && ghCLIConfig[hostname].git_protocol === "ssh") {
2654
- const token = execSync(`gh auth token`, {
2655
- encoding: "utf8",
2656
- stdio: "pipe",
2657
- windowsHide: false
2658
- }).trim();
2659
- return { token, headerName: "Authorization" };
2660
- }
2661
- }
2662
- }
2663
- if (hostname !== "github.com") {
2664
- console.log(
2665
- `Warning: It was not possible to automatically resolve a GitHub token from your environment for hostname ${hostname}. If you set the GITHUB_TOKEN or GH_TOKEN environment variable, that will be used for GitHub API requests.`
2666
- );
2667
- }
2668
- throw new Error(
2669
- `Unable to resolve a GitHub token for hostname ${hostname}. Please set the GITHUB_TOKEN or GH_TOKEN environment variable, or ensure you have an active session via the official gh CLI tool (https://cli.github.com).`
2670
- );
2671
- }
2672
- async function makeGithubRequest(config5, url, opts = {}) {
2673
- return await axios(url, {
2674
- ...opts,
2675
- baseURL: config5.apiBaseUrl,
2676
- headers: {
2677
- ...opts.headers,
2678
- Authorization: config5.token ? `Bearer ${config5.token}` : void 0
2679
- }
2680
- });
2681
- }
2682
- async function createGithubRemoteReleaseClient(workspaceConfig, remoteName = "origin") {
2683
- const repoData = getGitHubRepoData(remoteName, "github");
2684
- if (!repoData) {
2685
- throw new Error(
2686
- `Unable to create a remote release client because the GitHub repo slug could not be determined. Please ensure you have a valid GitHub remote configured.`
2687
- );
2688
- }
2689
- return new StormGithubRemoteReleaseClient(
2690
- repoData,
2691
- {
2692
- provider: "github",
2693
- hostname: repoData.hostname,
2694
- apiBaseUrl: repoData.apiBaseUrl
2695
- },
2696
- await resolveTokenData(repoData.hostname),
2697
- workspaceConfig
2698
- );
2699
- }
2700
- async function isUserAnOrganizationMember(userId, config5, remoteName = "origin") {
2701
- try {
2702
- const repoData = getGitHubRepoData(remoteName, "github");
2703
- if (!repoData) {
2704
- throw new Error(
2705
- `Unable to validate GitHub actor because the GitHub repo slug could not be determined. Please ensure you have a valid GitHub remote configured.`
2541
+ writeDebug(
2542
+ `\u270D\uFE0F Writing changelog for project ${projectName} to ${filePath}`,
2543
+ workspaceConfig
2706
2544
  );
2707
- }
2708
- const tokenData = await resolveTokenData(repoData.hostname);
2709
- if (!tokenData.token) {
2710
- throw new Error(
2711
- `Unable to validate GitHub actor because no token was provided. Please set the GITHUB_TOKEN or GH_TOKEN environment variable, or ensure you have an active session via the official gh CLI tool (https://cli.github.com).`
2545
+ const content = await generateChangelogContent(
2546
+ projectChangelog.releaseVersion,
2547
+ filePath,
2548
+ projectChangelog.contents,
2549
+ currentContent,
2550
+ projectName,
2551
+ workspaceConfig
2552
+ );
2553
+ tree.write(filePath, content);
2554
+ printAndFlushChanges(
2555
+ tree,
2556
+ !!args.dryRun,
2557
+ 3,
2558
+ false,
2559
+ noDiffInChangelogMessage,
2560
+ // Only print the change for the current changelog file at this point
2561
+ (f) => f.path === filePath
2712
2562
  );
2713
2563
  }
2714
- const result = await makeGithubRequest(
2715
- {
2716
- repo: repoData.slug,
2717
- hostname: repoData.hostname,
2718
- apiBaseUrl: repoData.apiBaseUrl,
2719
- token: tokenData?.token || null
2720
- },
2721
- `/orgs/${typeof config5.organization === "string" ? config5.organization : config5.organization?.name}/members/${userId}`,
2722
- {}
2723
- );
2724
- if (result.status !== 204) {
2725
- return false;
2726
- }
2727
- return true;
2728
- } catch {
2729
- return false;
2730
2564
  }
2565
+ return projectChangelogs;
2731
2566
  }
2732
2567
  async function getCommits(fromSHA, toSHA) {
2733
2568
  const rawCommits = await getGitDiff(fromSHA, toSHA);
@@ -2771,40 +2606,6 @@ function extractPreid(version) {
2771
2606
  }
2772
2607
  return void 0;
2773
2608
  }
2774
- function createGitTagValues(releaseGroups, releaseGroupToFilteredProjects, versionData) {
2775
- const tags = [];
2776
- for (const releaseGroup of releaseGroups) {
2777
- const releaseGroupProjectNames = Array.from(
2778
- releaseGroupToFilteredProjects.get(releaseGroup) ?? []
2779
- );
2780
- if (releaseGroup.projectsRelationship === "independent") {
2781
- for (const project of releaseGroupProjectNames) {
2782
- const projectVersionData = versionData[project];
2783
- if (projectVersionData?.newVersion) {
2784
- tags.push(
2785
- interpolate(releaseGroup.releaseTagPattern, {
2786
- version: projectVersionData.newVersion,
2787
- projectName: project
2788
- })
2789
- );
2790
- }
2791
- }
2792
- continue;
2793
- }
2794
- if (releaseGroupProjectNames.length > 0 && releaseGroupProjectNames[0]) {
2795
- const projectVersionData = versionData[releaseGroupProjectNames[0]];
2796
- if (projectVersionData?.newVersion) {
2797
- tags.push(
2798
- interpolate(releaseGroup.releaseTagPattern, {
2799
- version: projectVersionData.newVersion,
2800
- releaseGroupName: releaseGroup.name
2801
- })
2802
- );
2803
- }
2804
- }
2805
- }
2806
- return tags;
2807
- }
2808
2609
  async function gitTag({
2809
2610
  tag,
2810
2611
  message,
@@ -2929,6 +2730,216 @@ function omit(obj, keys) {
2929
2730
  }
2930
2731
  return result;
2931
2732
  }
2733
+
2734
+ // src/release/github.ts
2735
+ var StormGithubRemoteReleaseClient = class extends GithubRemoteReleaseClient {
2736
+ repoData;
2737
+ workspaceConfig;
2738
+ /**
2739
+ * Creates an instance of {@link StormGithubRemoteReleaseClient}.
2740
+ *
2741
+ * @param remoteRepoData - Data about the remote repository
2742
+ * @param createReleaseConfig - Configuration for creating releases
2743
+ * @param tokenData - Token data for authentication
2744
+ * @param workspaceConfig - The Storm workspace configuration object, which is loaded from the storm-workspace.json file.
2745
+ */
2746
+ constructor(repoData, createReleaseConfig, tokenData, workspaceConfig) {
2747
+ super(repoData, createReleaseConfig, tokenData);
2748
+ this.repoData = repoData;
2749
+ this.workspaceConfig = workspaceConfig;
2750
+ }
2751
+ createPostGitTask(releaseVersion, changelogContents, dryRun) {
2752
+ return async (latestCommit) => {
2753
+ output.logSingleLine(`Creating GitHub Release`);
2754
+ await this.createOrUpdateRelease(
2755
+ releaseVersion,
2756
+ changelogContents,
2757
+ latestCommit,
2758
+ { dryRun }
2759
+ );
2760
+ };
2761
+ }
2762
+ async createOrUpdateRelease(releaseVersion, changelogContents, latestCommit, { dryRun }) {
2763
+ if (!this.workspaceConfig) {
2764
+ this.workspaceConfig = await getWorkspaceConfig();
2765
+ }
2766
+ const name = releaseVersion.gitTag.includes("@") ? releaseVersion.gitTag.replace(new RegExp(`^@${this.workspaceConfig.name}/`), "").replace(/@.*$/, "") : releaseVersion.gitTag;
2767
+ return super.createOrUpdateRelease(
2768
+ releaseVersion,
2769
+ `![${(typeof this.workspaceConfig.release.banner === "string" ? this.workspaceConfig.organization ? titleCase(
2770
+ typeof this.workspaceConfig.organization === "string" ? this.workspaceConfig.organization : this.workspaceConfig.organization.name
2771
+ ) : void 0 : this.workspaceConfig.release.banner.alt) || "Release banner header"}](${typeof this.workspaceConfig.release.banner === "string" ? this.workspaceConfig.release.banner : this.workspaceConfig.release.banner?.url})
2772
+ ${this.workspaceConfig.release.header || ""}
2773
+
2774
+ # ${name ? `${titleCase(name)} ` : ""}v${releaseVersion.rawVersion}
2775
+
2776
+ We at [${this.workspaceConfig.organization ? titleCase(
2777
+ typeof this.workspaceConfig.organization === "string" ? this.workspaceConfig.organization : this.workspaceConfig.organization.name
2778
+ ) : ""}](${this.workspaceConfig.homepage}) are very excited to announce the v${releaseVersion.rawVersion} release of the ${name ? this.workspaceConfig.name ? `${titleCase(this.workspaceConfig.name)} - ${titleCase(name)}` : titleCase(name) : this.workspaceConfig.name ? titleCase(this.workspaceConfig.name) : "Storm Software"} project! \u{1F680}
2779
+
2780
+ These changes are released under the ${this.workspaceConfig.license.includes("license") ? this.workspaceConfig.license : `${this.workspaceConfig.license} license`}. You can find more details on [our licensing page](${this.workspaceConfig.licensing}). You can find guides, API references, and other documentation around this release (and much more) on [our documentation site](${this.workspaceConfig.docs}).
2781
+
2782
+ If you have any questions or comments, feel free to reach out to the team on [Discord](${this.workspaceConfig.socials.discord}) or [our contact page](${this.workspaceConfig.contact}). Please help us spread the word by giving [this repository](https://github.com/${typeof this.workspaceConfig.organization === "string" ? this.workspaceConfig.organization : this.workspaceConfig.organization?.name}/${this.workspaceConfig.name}) a star \u2B50 on GitHub or [posting on X (Twitter)](https://x.com/intent/tweet?text=Check%20out%20the%20latest%20@${this.workspaceConfig.socials.twitter}%20release%20${name ? `${titleCase(name)?.replaceAll(" ", "%20")}%20` : ""}v${releaseVersion.rawVersion}%20%F0%9F%9A%80%0D%0A%0D%0Ahttps://github.com/${typeof this.workspaceConfig.organization === "string" ? this.workspaceConfig.organization : this.workspaceConfig.organization?.name}/${this.workspaceConfig.name}/releases/tag/${releaseVersion.gitTag}) about this release!
2783
+
2784
+ ## Release Notes
2785
+
2786
+ ${changelogContents.replaceAll(
2787
+ `## ${generateChangelogTitle(
2788
+ releaseVersion.rawVersion,
2789
+ name,
2790
+ this.workspaceConfig
2791
+ )}`,
2792
+ ""
2793
+ ).replaceAll(
2794
+ `# ${generateChangelogTitle(releaseVersion.rawVersion, name, this.workspaceConfig)}`,
2795
+ ""
2796
+ )}
2797
+
2798
+ ---
2799
+
2800
+ ${this.workspaceConfig.release.footer}
2801
+ `,
2802
+ latestCommit,
2803
+ { dryRun }
2804
+ );
2805
+ }
2806
+ /**
2807
+ * Get remote repository data, attempting to resolve it if not already set.
2808
+ */
2809
+ getRemoteRepoData() {
2810
+ if (!this.repoData) {
2811
+ let githubRepoData = super.getRemoteRepoData();
2812
+ if (!githubRepoData) {
2813
+ githubRepoData = getGitHubRepoData();
2814
+ if (!githubRepoData) {
2815
+ output.error({
2816
+ title: `Unable to create a GitHub release because the GitHub repo slug could not be determined.`,
2817
+ bodyLines: [
2818
+ `Please ensure you have a valid GitHub remote configured. You can run \`git remote -v\` to list your current remotes.`
2819
+ ]
2820
+ });
2821
+ process.exit(1);
2822
+ }
2823
+ }
2824
+ this.repoData = githubRepoData;
2825
+ }
2826
+ return this.repoData;
2827
+ }
2828
+ };
2829
+ function getGitHubRepoData(remoteName = "origin", createReleaseConfig = "github") {
2830
+ try {
2831
+ const remoteUrl = execSync(`git remote get-url ${remoteName}`, {
2832
+ encoding: "utf8",
2833
+ stdio: "pipe"
2834
+ }).trim();
2835
+ let hostname = defaultCreateReleaseProvider.hostname;
2836
+ let apiBaseUrl = defaultCreateReleaseProvider.apiBaseUrl;
2837
+ if (createReleaseConfig && typeof createReleaseConfig !== "string") {
2838
+ hostname = createReleaseConfig.hostname;
2839
+ apiBaseUrl = createReleaseConfig.apiBaseUrl;
2840
+ }
2841
+ const escapedHostname = hostname.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2842
+ const regexString = `${escapedHostname}[/:]([\\w.-]+/[\\w.-]+)(\\.git)?`;
2843
+ const regex = new RegExp(regexString);
2844
+ const match = remoteUrl?.match(regex);
2845
+ if (match && match[1]) {
2846
+ return {
2847
+ hostname,
2848
+ apiBaseUrl,
2849
+ // Ensure any trailing .git is stripped
2850
+ slug: match[1].replace(/\.git$/, "")
2851
+ };
2852
+ } else {
2853
+ throw new Error(
2854
+ `Could not extract "user/repo" data from the resolved remote URL: ${remoteUrl}`
2855
+ );
2856
+ }
2857
+ } catch (error) {
2858
+ output.error({
2859
+ title: `Failed to get GitHub repo data`,
2860
+ bodyLines: [error.message]
2861
+ });
2862
+ return void 0;
2863
+ }
2864
+ }
2865
+ async function resolveTokenData(hostname) {
2866
+ const tokenFromEnv = process.env.STORM_BOT_GITHUB_TOKEN || process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
2867
+ if (tokenFromEnv) {
2868
+ return { token: tokenFromEnv, headerName: "Authorization" };
2869
+ }
2870
+ const ghCLIPath = joinPathFragments(
2871
+ process.env.XDG_CONFIG_HOME || joinPathFragments(homedir(), ".config"),
2872
+ "gh",
2873
+ "hosts.yml"
2874
+ );
2875
+ if (existsSync$1(ghCLIPath)) {
2876
+ const yamlContents = await promises.readFile(ghCLIPath, "utf8");
2877
+ const ghCLIConfig = parse(yamlContents);
2878
+ if (ghCLIConfig[hostname]) {
2879
+ if (ghCLIConfig[hostname].oauth_token) {
2880
+ return ghCLIConfig[hostname].oauth_token;
2881
+ }
2882
+ if (ghCLIConfig[hostname].user && ghCLIConfig[hostname].git_protocol === "ssh") {
2883
+ const token = execSync(`gh auth token`, {
2884
+ encoding: "utf8",
2885
+ stdio: "pipe",
2886
+ windowsHide: false
2887
+ }).trim();
2888
+ return { token, headerName: "Authorization" };
2889
+ }
2890
+ }
2891
+ }
2892
+ if (hostname !== "github.com") {
2893
+ console.log(
2894
+ `Warning: It was not possible to automatically resolve a GitHub token from your environment for hostname ${hostname}. If you set the GITHUB_TOKEN or GH_TOKEN environment variable, that will be used for GitHub API requests.`
2895
+ );
2896
+ }
2897
+ throw new Error(
2898
+ `Unable to resolve a GitHub token for hostname ${hostname}. Please set the GITHUB_TOKEN or GH_TOKEN environment variable, or ensure you have an active session via the official gh CLI tool (https://cli.github.com).`
2899
+ );
2900
+ }
2901
+ async function makeGithubRequest(config5, url, opts = {}) {
2902
+ return await axios(url, {
2903
+ ...opts,
2904
+ baseURL: config5.apiBaseUrl,
2905
+ headers: {
2906
+ ...opts.headers,
2907
+ Authorization: config5.token ? `Bearer ${config5.token}` : void 0
2908
+ }
2909
+ });
2910
+ }
2911
+ async function isUserAnOrganizationMember(userId, config5, remoteName = "origin") {
2912
+ try {
2913
+ const repoData = getGitHubRepoData(remoteName, "github");
2914
+ if (!repoData) {
2915
+ throw new Error(
2916
+ `Unable to validate GitHub actor because the GitHub repo slug could not be determined. Please ensure you have a valid GitHub remote configured.`
2917
+ );
2918
+ }
2919
+ const tokenData = await resolveTokenData(repoData.hostname);
2920
+ if (!tokenData.token) {
2921
+ throw new Error(
2922
+ `Unable to validate GitHub actor because no token was provided. Please set the GITHUB_TOKEN or GH_TOKEN environment variable, or ensure you have an active session via the official gh CLI tool (https://cli.github.com).`
2923
+ );
2924
+ }
2925
+ const result = await makeGithubRequest(
2926
+ {
2927
+ repo: repoData.slug,
2928
+ hostname: repoData.hostname,
2929
+ apiBaseUrl: repoData.apiBaseUrl,
2930
+ token: tokenData?.token || null
2931
+ },
2932
+ `/orgs/${typeof config5.organization === "string" ? config5.organization : config5.organization?.name}/members/${userId}`,
2933
+ {}
2934
+ );
2935
+ if (result.status !== 204) {
2936
+ return false;
2937
+ }
2938
+ return true;
2939
+ } catch {
2940
+ return false;
2941
+ }
2942
+ }
2932
2943
  var StormChangelogRenderer = class extends DefaultChangelogRenderer {
2933
2944
  /**
2934
2945
  * The Storm workspace configuration object, which is loaded from the storm-workspace.json file.
@@ -3469,10 +3480,15 @@ var StormReleaseClient = class _StormReleaseClient extends ReleaseClient {
3469
3480
  })),
3470
3481
  this.config.conventionalCommits
3471
3482
  );
3483
+ writeDebug(
3484
+ `Running changelog generation for the ${releaseGroup.name} release group`,
3485
+ this.workspaceConfig
3486
+ );
3472
3487
  projectChangelogs = await generateChangelogForProjects({
3473
3488
  tree: this.tree,
3474
3489
  args: options,
3475
3490
  changes,
3491
+ projectGraph: this.projectGraph,
3476
3492
  projectsVersionData: options.versionData,
3477
3493
  releaseGroup,
3478
3494
  projects: [project],
@@ -3535,10 +3551,15 @@ var StormReleaseClient = class _StormReleaseClient extends ReleaseClient {
3535
3551
  })),
3536
3552
  this.config.conventionalCommits
3537
3553
  );
3554
+ writeDebug(
3555
+ `Running changelog generation for the ${releaseGroup.name} release group`,
3556
+ this.workspaceConfig
3557
+ );
3538
3558
  projectChangelogs = await generateChangelogForProjects({
3539
3559
  tree: this.tree,
3540
3560
  args: options,
3541
3561
  changes,
3562
+ projectGraph: this.projectGraph,
3542
3563
  projectsVersionData: options.versionData,
3543
3564
  releaseGroup,
3544
3565
  projects: projectNodes,
@@ -3559,80 +3580,26 @@ var StormReleaseClient = class _StormReleaseClient extends ReleaseClient {
3559
3580
  }
3560
3581
  }
3561
3582
  }
3562
- if (projectChangelogs) {
3563
- await Promise.all(
3564
- Object.entries(projectChangelogs).map(async ([project, changelog]) => {
3565
- if (!this.projectGraph.nodes[project]?.data.root) {
3566
- writeWarning(
3567
- `A changelog was generated for ${project}, but it could not be found in the project graph. Skipping writing changelog file.`,
3568
- this.workspaceConfig
3569
- );
3570
- } else if (changelog.contents) {
3571
- const filePath = joinPaths(
3572
- this.projectGraph.nodes[project].data.root,
3573
- "CHANGELOG.md"
3574
- );
3575
- let currentContent;
3576
- if (existsSync$1(filePath)) {
3577
- currentContent = await readFile$1(filePath, "utf8");
3578
- }
3579
- writeDebug(
3580
- `\u270D\uFE0F Writing changelog for project ${project} to ${filePath}`,
3581
- this.workspaceConfig
3582
- );
3583
- const content = await generateChangelogContent(
3584
- changelog.releaseVersion,
3585
- filePath,
3586
- changelog.contents,
3587
- currentContent,
3588
- project,
3589
- this.workspaceConfig
3590
- );
3591
- this.tree.write(filePath, content);
3592
- printAndFlushChanges(
3593
- this.tree,
3594
- !!options.dryRun,
3595
- 3,
3596
- false,
3597
- noDiffInChangelogMessage,
3598
- // Only print the change for the current changelog file at this point
3599
- (f) => f.path === filePath
3600
- );
3601
- }
3602
- })
3603
- );
3604
- this.applyChangesAndExit(options, postGitTasks);
3605
- }
3583
+ writeDebug(
3584
+ `Generated changelogs for ${Object.keys(allProjectChangelogs).length} projects:
3585
+ ${Object.keys(allProjectChangelogs).map((p) => ` - ${p}`).join("\n")}
3586
+ `,
3587
+ this.workspaceConfig
3588
+ );
3589
+ await this.applyChangesAndExit(options, postGitTasks);
3606
3590
  return {
3607
3591
  workspaceChangelog: void 0,
3608
3592
  projectChangelogs: allProjectChangelogs
3609
3593
  };
3610
3594
  };
3611
- checkChangelogFilesEnabled() {
3612
- if (this.config.changelog?.workspaceChangelog && (this.config.changelog?.workspaceChangelog === true || this.config.changelog?.workspaceChangelog.file)) {
3613
- return true;
3614
- }
3615
- for (const releaseGroup of Object.values(this.config.groups)) {
3616
- if (releaseGroup.changelog && releaseGroup.changelog !== true && releaseGroup.changelog.file) {
3617
- return true;
3618
- }
3619
- }
3620
- return false;
3621
- }
3622
- isCI = () => {
3623
- if (process.env.CI === "false") {
3624
- return false;
3625
- }
3626
- return process.env.CI || process.env.TF_BUILD === "true" || process.env.GITHUB_ACTIONS === "true" || process.env.BUILDKITE === "true" || process.env.CIRCLECI === "true" || process.env.CIRRUS_CI === "true" || process.env.TRAVIS === "true" || !!process.env["bamboo.buildKey"] || !!process.env["bamboo_buildKey"] || !!process.env.CODEBUILD_BUILD_ID || !!process.env.GITLAB_CI || !!process.env.HEROKU_TEST_RUN_ID || !!process.env.BUILD_ID || !!process.env.BUILD_NUMBER || !!process.env.BUILD_BUILDID || !!process.env.TEAMCITY_VERSION || !!process.env.JENKINS_URL || !!process.env.HUDSON_URL;
3627
- };
3628
3595
  applyChangesAndExit = async (options, postGitTasks) => {
3629
3596
  const to = options.to || "HEAD";
3630
3597
  let latestCommit = await getCommitHash(to);
3631
- const gitTagValues = options.gitTag ?? this.config.changelog?.git?.tag ? createGitTagValues(
3598
+ const gitTagValues = createGitTagValues(
3632
3599
  options.releaseGraph.releaseGroups,
3633
3600
  options.releaseGraph.releaseGroupToFilteredProjects,
3634
3601
  options.versionData
3635
- ) : [];
3602
+ );
3636
3603
  handleDuplicateGitTags(gitTagValues);
3637
3604
  const commitMessageValues = createCommitMessageValues(
3638
3605
  options.releaseGraph.releaseGroups,
@@ -3641,13 +3608,11 @@ var StormReleaseClient = class _StormReleaseClient extends ReleaseClient {
3641
3608
  options.gitCommitMessage || this.config.changelog?.git?.commitMessage || "release(monorepo): Publish workspace release updates"
3642
3609
  );
3643
3610
  const changes = this.tree.listChanges();
3644
- if (this.checkChangelogFilesEnabled() && !changes.length) {
3645
- output.warn({
3646
- title: `No changes detected for changelogs`,
3647
- bodyLines: [
3648
- `No changes were detected for any changelog files, so no changelog entries will be generated.`
3649
- ]
3650
- });
3611
+ if (!changes.length) {
3612
+ writeWarning(
3613
+ "No changes were detected for any changelog files, so no changelog entries will be generated.",
3614
+ this.workspaceConfig
3615
+ );
3651
3616
  if (!postGitTasks.length) {
3652
3617
  return;
3653
3618
  }
@@ -3666,7 +3631,10 @@ var StormReleaseClient = class _StormReleaseClient extends ReleaseClient {
3666
3631
  gitCommitArgs: options.gitCommitArgs || this.config.changelog?.git?.commitArgs
3667
3632
  });
3668
3633
  latestCommit = await getCommitHash("HEAD");
3669
- output.logSingleLine(`Tagging commit with git`);
3634
+ writeDebug(
3635
+ `Creating git tags: ${gitTagValues.join(", ")}`,
3636
+ this.workspaceConfig
3637
+ );
3670
3638
  for (const tag of gitTagValues) {
3671
3639
  await gitTag({
3672
3640
  tag,
@@ -3676,17 +3644,16 @@ var StormReleaseClient = class _StormReleaseClient extends ReleaseClient {
3676
3644
  verbose: options.verbose
3677
3645
  });
3678
3646
  }
3679
- if (options.gitPush ?? this.config.changelog?.git?.push) {
3680
- output.logSingleLine(
3681
- `Pushing to git remote "${options.gitRemote ?? "origin"}"`
3682
- );
3683
- await gitPush({
3684
- gitRemote: options.gitRemote,
3685
- dryRun: options.dryRun,
3686
- verbose: options.verbose,
3687
- additionalArgs: options.gitPushArgs || this.config.changelog?.git?.pushArgs
3688
- });
3689
- }
3647
+ writeDebug(
3648
+ `Pushing to git remote "${options.gitRemote ?? "origin"}"`,
3649
+ this.workspaceConfig
3650
+ );
3651
+ await gitPush({
3652
+ gitRemote: options.gitRemote,
3653
+ dryRun: options.dryRun,
3654
+ verbose: options.verbose,
3655
+ additionalArgs: options.gitPushArgs || this.config.changelog?.git?.pushArgs
3656
+ });
3690
3657
  for (const postGitTask of postGitTasks) {
3691
3658
  await postGitTask(latestCommit);
3692
3659
  }