gen-release-notes.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // METADATA // {"ai-commented":{"service":"claude"}}
  2. /*
  3. * Copyright (C) 2024-present Puter Technologies Inc.
  4. *
  5. * This file is part of Puter.
  6. *
  7. * Puter is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as published
  9. * by the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. */
  20. import { simpleGit } from 'simple-git';
  21. // GitHub repository URL for generating commit links in release notes
  22. const REPO_URL = 'https://github.com/HeyPuter/puter';
  23. const params = {
  24. from: 'v2.4.1',
  25. // from: 'v2.4.0',
  26. to: 'v2.4.2',
  27. date: '2024-07-22',
  28. };
  29. const git = simpleGit();
  30. const log = await git.log({ from: params.from });
  31. const commits = log.all;
  32. // Array of all commits from git log between specified versions
  33. const CC_REGEX = /^([a-z0-9]+)(\([a-z0-9]+\))?:\s(.*)/;
  34. const parse_conventional_commit = message => {
  35. const parts = CC_REGEX.exec(message);
  36. if ( ! parts ) return null;
  37. let [match, type, scope, summary] = parts;
  38. if ( ! match ) return null;
  39. if ( scope ) scope = scope.slice(1, -1);
  40. return { type, scope, summary };
  41. };
  42. const types = {
  43. feat: {
  44. label: 'Features'
  45. },
  46. i18n: {
  47. label: 'Translations'
  48. },
  49. fix: {
  50. label: 'Bug Fixes'
  51. },
  52. };
  53. const scopes = {
  54. puter: {
  55. label: 'Puter'
  56. },
  57. phoenix: {
  58. label: 'Phoenix Shell'
  59. },
  60. git: {
  61. label: 'Puter Git'
  62. },
  63. backend: {
  64. label: 'Backend'
  65. },
  66. api: {
  67. label: 'API',
  68. },
  69. gui: {
  70. label: 'GUI'
  71. },
  72. puterjs: {
  73. label: 'Puter JS'
  74. },
  75. tools: {
  76. ignore: true,
  77. },
  78. security: {
  79. label: 'Security',
  80. },
  81. ai: {
  82. label: 'AI',
  83. },
  84. putility: {
  85. label: 'Putility',
  86. },
  87. docker: {
  88. label: 'Docker',
  89. },
  90. };
  91. const scope_aliases = {
  92. main: 'puter',
  93. ui: 'gui',
  94. parsely: 'phoenix',
  95. };
  96. const complicated_cases = [
  97. /**
  98. * Handles special cases for commit message transformations
  99. * @type {Array<function>}
  100. */
  101. function fix_i18n ({ commit, meta }) {
  102. if ( meta.scope === 'i18n' ) {
  103. meta.type = 'i18n';
  104. meta.scope = undefined;
  105. }
  106. },
  107. function deps_scope ({ commit, meta }) {
  108. if ( meta.scope === 'deps' ) {
  109. meta.type = 'chore';
  110. meta.scope = undefined;
  111. }
  112. },
  113. function puterai_is_ai ({ commit, meta }) {
  114. const ai_scopes = ['puterai', 'puerai', 'puter-ai'];
  115. if ( ai_scopes.includes(meta.scope) ) {
  116. meta.scope = 'ai';
  117. }
  118. },
  119. function doc_scopes ({ commit, meta }) {
  120. const doc_scopes = ['readme'];
  121. if ( doc_scopes.includes(meta.scope) ) {
  122. meta.type = 'doc';
  123. meta.scope = undefined;
  124. }
  125. }
  126. ];
  127. const retro_prefixes_0 = {
  128. i18n: [
  129. '883601142873f10d69c84874499065a7d29af054',
  130. '17145d0be6a9a1445947cc0c4bec8f16a475144c',
  131. 'e61039faf409b0ad85c7513b0123f3f2e92ebe32',
  132. 'bffa192805216fc17045cd8d629f34784dca7f3f',
  133. 'fe5be7f3cf7f336730137293ba86a637e8d8591d',
  134. '78a0acea6980b6d491da4874edbd98e17c0d9577',
  135. 'a96abb5793528d0dc56d75f95d771e1dcf5960d1',
  136. 'f5a8ee1c6ab950d62c90b6257791f026a508b4e4',
  137. '47ec74f0aa6adb3952e6460909029a4acb0c3039',
  138. '473b6512c697854e3f3badae1eb7b87742954da5',
  139. '8440f566b91c9eb4f01addcb850061e3fbe3afc7',
  140. '92abc9947f811f94f17a5ee5a4b73ee2b210900a',
  141. 'cff488f4f4378ca6c7568a585a665f2a3b87b89c',
  142. '3b8af7cc5c1be8ed67be827360bbfe0f0b5027e9',
  143. '84e31eff2f58584d8fab7dd10606f2f6ced933a2',
  144. '81781f80afc07cd1e6278906cdc68c8092fbfedf',
  145. '56820cf6ee56ff810a6b495a281ccbb2e7f9d8fb',
  146. '69a80ab3d2c94ee43d96021c3bcbdab04a4b5dc6',
  147. '8e297cd7e30757073e2f96593c363a273b639466',
  148. '151527825f1eb4b060aaf97feb7d18af4fcddbf2',
  149. '8bece96f6224a060d5b408e08c58865fadb8b79c',
  150. '333d6e3b651e460caca04a896cbc8c175555b79b',
  151. '8a3d0430f39f872b8a460c344cce652c340b700b',
  152. 'b9e73b7288aebb14e6bbf1915743e9157fc950b1',
  153. 'c2d3d69dbe33f36fcae13bcbc8e2a31a86025af9',
  154. '382fb24dbb1737a8a54ed2491f80b2e2276cde61',
  155. ],
  156. fix: [
  157. '535475b3c36a37e3319ed067a24fb671790dcda3',
  158. '45f131f8eaf94cf3951ca7ffeb6f311590233b8a',
  159. '02e1b1e8f5f8e22d7ab39ebff99f7dd8e08a4221',
  160. ],
  161. doc: [
  162. '338004474f078a00608af1d0ebf8a7f9534bad28',
  163. '6c4c73a9e85ff8eb5e7663dcce11f4d1f824032b',
  164. 'c19c18bfcf163b37e3d173b8fa50393dfb9f540f',
  165. ],
  166. feat: [
  167. '8e7306c23be01ee6c31cdb4c99f2fb1f71a2247f',
  168. ],
  169. meta: [
  170. 'b3c1b128e2d8519bc816cdcd3220c8f40e05bb01',
  171. '452b7495b1736df90bc748dbf818407488875754',
  172. ],
  173. };
  174. const message_changes = {
  175. '1f7f094282fae915a2436701cfb756444cd3f781': 'feat: add new file templates',
  176. '64e4299ac0a4c9e1de7a9d089e2d7529a9530818': 'doc: docker instructions for Windows',
  177. 'f897e844989083b0b369ba0ce4d2c5a9f3db5ad8': 'fix: #432',
  178. };
  179. const retro_prefixes = {};
  180. for ( const prefix in retro_prefixes_0 ) {
  181. for ( const commit_hash of retro_prefixes_0[prefix] ) {
  182. console.log('PREFIX', commit_hash, prefix);
  183. retro_prefixes[commit_hash] = prefix;
  184. }
  185. }
  186. const data = {};
  187. const ensure_scope = name => {
  188. if ( data[name] ) return;
  189. const o = data[name] = {};
  190. for ( const k in types ) o[k] = [];
  191. };
  192. for ( const commit of commits ) {
  193. if ( message_changes.hasOwnProperty(commit.hash) ) {
  194. commit.message = message_changes[commit.hash];
  195. }
  196. if ( retro_prefixes.hasOwnProperty(commit.hash) ) {
  197. commit.message = retro_prefixes[commit.hash] + ': ' +
  198. commit.message;
  199. }
  200. const meta = parse_conventional_commit(commit.message);
  201. if ( ! meta ) continue;
  202. for ( const transformer of complicated_cases ) {
  203. transformer({ commit, meta });
  204. }
  205. let scope = meta.scope ?? 'puter';
  206. while ( scope in scope_aliases ) {
  207. scope = scope_aliases[scope];
  208. }
  209. if ( ! scopes[scope] ) {
  210. console.log(commit);
  211. throw new Error(`missing scope: ${scope}`);
  212. }
  213. if ( scopes[scope].ignore ) continue;
  214. ensure_scope(scope);
  215. if ( types.hasOwnProperty(meta.type) ) {
  216. data[scope][meta.type].push({ meta, commit });
  217. }
  218. }
  219. let s = '';
  220. s += `## ${params.to} (${params.date})\n\n`;
  221. for ( const scope_name in data ) {
  222. const scope = data[scope_name];
  223. s += `### ${scopes[scope_name].label}\n\n`;
  224. for ( const type_name in types ) {
  225. const type = types[type_name];
  226. const items = scope[type_name];
  227. if ( items.length == 0 ) continue;
  228. s += `\n#### ${type.label}\n\n`;
  229. for ( const { meta, commit } of items ) {
  230. const shorthash = commit.hash.slice(0,7)
  231. s += `- ${meta.summary} ([${shorthash}](${REPO_URL}/commit/${commit.hash}))\n`;
  232. }
  233. }
  234. }
  235. console.log(s);