version.mjs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import path from "node:path";
  2. import { spawn } from "node:child_process";
  3. import fs from "node:fs/promises";
  4. const git = async (command, options) => {
  5. // create a promise based git wrapper around a spawned process
  6. return new Promise((resolve, reject) => {
  7. const currentPwd = process.cwd();
  8. const child = spawn("git", [command, ...options], {
  9. cwd: currentPwd,
  10. stdio: ["ignore", "pipe", "pipe"],
  11. });
  12. let stdout = "";
  13. let stderr = "";
  14. child.stdout.on("data", (data) => {
  15. stdout += data;
  16. });
  17. child.stderr.on("data", (data) => {
  18. stderr += data;
  19. });
  20. child.on("close", (code) => {
  21. if (code === 0) {
  22. resolve(stdout);
  23. } else {
  24. reject(stderr);
  25. }
  26. });
  27. });
  28. };
  29. /**
  30. * Get the commits affecting the current project
  31. * @param options
  32. * @returns {Promise<string>}
  33. */
  34. const gitLog = async (options = []) => {
  35. const log = await git("log", options);
  36. return log.trim();
  37. };
  38. /**
  39. * Get the branch info of the current project
  40. * @param options
  41. * @returns {Promise<string>}
  42. */
  43. const gitBranch = async (options = []) => {
  44. const branch = await git("branch", options);
  45. return branch.trim();
  46. };
  47. /**
  48. * @typedef {Object} CommitVersion
  49. * @property {string} message - The commit message of the latest commit to affect the current project
  50. * @property {string} commit - The commit hash of the latest commit to affect the current project
  51. * @property {string} date - The date of the latest commit to affect the current project
  52. * @property {string} branch - The current branch
  53. */
  54. /**
  55. * Get the last commit data to have affected the current project
  56. * @returns {Promise<CommitVersion>}
  57. */
  58. const getVersionData = async () => {
  59. const latestCommitInfo = await gitLog(["-n 1", "-p", "src/*"]);
  60. const commitInfo = latestCommitInfo.split("\n");
  61. const commit =
  62. commitInfo
  63. .find((line) => line.startsWith("commit"))
  64. ?.trim()
  65. .replace("commit", "")
  66. .trim() ?? "";
  67. let date = commitInfo.find((line) => line.startsWith("Date:")) ?? "";
  68. // First non-empty line after the Date: line is the commit message
  69. const message =
  70. commitInfo
  71. .slice(commitInfo.indexOf(date) + 1)
  72. .find((line) => line.trim().length > 0)
  73. ?.trim() ?? "";
  74. // Remove the Date: prefix from the date
  75. date = date.replace("Date:", "").trim();
  76. // Get the current branch of the latest commit
  77. const contains = (await gitBranch(["--contains", commit])).split("\n");
  78. let branch = (contains.find((line) => line.startsWith("develop") || line.startsWith("*")) ?? "")
  79. .replace("*", "")
  80. .trim();
  81. if (branch === "" || branch.includes("HEAD")) {
  82. branch = "develop";
  83. }
  84. return {
  85. message,
  86. commit,
  87. date: new Date(date).toISOString(),
  88. branch,
  89. };
  90. };
  91. const versionLib = async () => {
  92. const currentPwd = process.cwd();
  93. // if the currentPwd includes 'node_modules', we are running from within the monorepo package itself
  94. // and we have to account for the difference
  95. let workspaceRoot;
  96. let currentProjectPath;
  97. if (currentPwd.includes("node_modules")) {
  98. const [_workspaceRoot, nodeModulesPath, _currentProjectPath] = currentPwd.split("web");
  99. workspaceRoot = path.join(_workspaceRoot, "web", nodeModulesPath);
  100. currentProjectPath = _currentProjectPath;
  101. } else {
  102. const [_workspaceRoot, _currentProjectPath] = currentPwd.split("web");
  103. workspaceRoot = _workspaceRoot;
  104. currentProjectPath = _currentProjectPath;
  105. }
  106. const distPath = path.join(workspaceRoot, "web", "dist", currentProjectPath);
  107. try {
  108. await fs.mkdir(distPath, { recursive: true });
  109. } catch {
  110. /* ignore */
  111. }
  112. const versionData = await getVersionData();
  113. const versionJson = JSON.stringify(versionData, null, 2);
  114. const versionFile = path.join(distPath, "version.json");
  115. await fs.writeFile(versionFile, versionJson);
  116. };
  117. versionLib().then(() => {
  118. console.log("Versioning complete");
  119. });