1- import childProcess from 'child_process ' ;
2- import { promisify } from 'util ' ;
1+ import * as fs from 'node:fs/promises ' ;
2+ import * as path from 'node:path ' ;
33import { Octokit } from '@octokit/rest' ;
4- import chalk from 'chalk' ;
4+ import {
5+ fetchCommitsBetweenRefs ,
6+ findLatestTaggedVersion ,
7+ } from '@mui/internal-code-infra/changelog' ;
58import yargs from 'yargs' ;
69
7- const exec = promisify ( childProcess . exec ) ;
10+ /**
11+ * @TODO : Add it to @mui/internal-code-infra/changelog
12+ *
13+ * @param {string } login
14+ * @returns {boolean }
15+ */
16+ function isBot ( login ) {
17+ return login . endsWith ( '[bot]' ) && ! login . includes ( 'copilot' ) ;
18+ }
819
920/**
1021 * @param {string } commitMessage
@@ -27,168 +38,109 @@ function parseTags(commitMessage) {
2738 . join ( ',' ) ;
2839}
2940
41+ // Match commit messages like:
42+ // "[docs] Fix small typo on Grid2 page (#44062)"
43+ const prLinkRegEx = / \( # [ 0 - 9 ] + \) $ / ;
44+
3045/**
31- * @param {Octokit.ReposCompareCommitsResponseCommitsItem } commitsItem
46+ *
47+ * @param {import('@mui/internal-code-infra/changelog').FetchedCommitDetails[] } commits
48+ * @returns {string[] }
3249 */
33- function filterCommit ( commitsItem ) {
34- const commitMessage = commitsItem . commit . message ;
35- // TODO: Use labels
36- return (
37- // Filter renovate dependencies updates
38- ! commitMessage . startsWith ( 'Bump' ) &&
39- ! commitMessage . startsWith ( 'Lock file maintenance' ) &&
40- // Filter website changes, no implications for library users
41- ! commitMessage . startsWith ( '[website]' )
42- ) ;
43- }
44-
45- async function findLatestTaggedVersion ( ) {
46- const { stdout } = await exec (
47- [
48- 'git' ,
49- 'describe' ,
50- // Earlier tags used lightweight tags + commit.
51- // We switched to annotated tags later.
52- '--tags' ,
53- '--abbrev=0' ,
54- // only include "version-tags"
55- '--match "v*"' ,
56- ] . join ( ' ' ) ,
50+ function getAllContributors ( commits ) {
51+ const authors = Array . from (
52+ new Set (
53+ commits
54+ . filter ( ( commit ) => ! ! commit . author ?. login )
55+ . map ( ( commit ) => {
56+ return commit . author . login ;
57+ } ) ,
58+ ) ,
5759 ) ;
5860
59- return stdout . trim ( ) ;
61+ return authors . sort ( ( a , b ) => a . localeCompare ( b ) ) . map ( ( author ) => `@ ${ author } ` ) ;
6062}
6163
62- // Match commit messages like:
63- // "[docs] Fix small typo on Grid2 page (#44062)"
64- const prLinkRegEx = / \( # [ 0 - 9 ] + \) $ / ;
65-
6664async function main ( argv ) {
67- const { githubToken , lastRelease : lastReleaseInput , release, repo } = argv ;
65+ const { lastRelease : previousReleaseParam , release } = argv ;
6866
69- if ( ! githubToken ) {
70- throw new TypeError (
71- 'Unable to authenticate. Make sure you either call the script with `--githubToken $token` or set `process.env.GITHUB_TOKEN`. The token needs `public_repo` permissions.' ,
72- ) ;
73- }
74- const octokit = new Octokit ( {
75- auth : githubToken ,
76- request : {
77- fetch,
78- } ,
67+ const latestTaggedVersion = await findLatestTaggedVersion ( {
68+ cwd : process . cwd ( ) ,
69+ fetchAll : false ,
7970 } ) ;
80-
81- const latestTaggedVersion = await findLatestTaggedVersion ( ) ;
82- const lastRelease = lastReleaseInput !== undefined ? lastReleaseInput : latestTaggedVersion ;
83- if ( lastRelease !== latestTaggedVersion ) {
71+ const previousRelease =
72+ previousReleaseParam !== undefined ? previousReleaseParam : latestTaggedVersion ;
73+ if ( previousRelease !== latestTaggedVersion ) {
8474 console . warn (
85- `Creating changelog for ${ lastRelease } ..${ release } when latest tagged version is '${ latestTaggedVersion } '.` ,
75+ `Creating changelog for ${ previousRelease } ..${ release } while the latest tagged version is '${ latestTaggedVersion } '.` ,
8676 ) ;
8777 }
8878
89- /**
90- * @type {AsyncIterableIterator<Octokit.Response<Octokit.ReposCompareCommitsResponse>> }
91- */
92- const timeline = octokit . paginate . iterator (
93- octokit . repos . compareCommits . endpoint . merge ( {
94- owner : 'mui' ,
95- repo,
96- base : lastRelease ,
97- head : release ,
98- } ) ,
99- ) ;
100-
101- /**
102- * @type {Octokit.ReposCompareCommitsResponseCommitsItem[] }
103- */
104- const commitsItems = [ ] ;
105- for await ( const response of timeline ) {
106- const { data : compareCommits } = response ;
107- commitsItems . push ( ...compareCommits . commits . filter ( filterCommit ) ) ;
79+ if ( process . env . GITHUB_TOKEN ) {
80+ console . warn (
81+ `Using GITHUB_TOKEN from environment variables have been deprecated. Please remove it if set locally.` ,
82+ ) ;
10883 }
10984
110- let warnedOnce = false ;
111-
112- const getAuthor = ( commit ) => {
113- if ( ! commit . author ) {
114- if ( ! warnedOnce ) {
115- console . warn (
116- `The author of the commit: ${ commit . commit . tree . url } cannot be retrieved. Please add the github username manually.` ,
117- ) ;
118- }
119- warnedOnce = true ;
120- return chalk . red ( "TODO INSERT AUTHOR'S USERNAME" ) ;
121- }
122-
123- const authorLogin = commit . author . login ;
124-
125- if ( authorLogin === 'github-actions[bot]' ) {
126- const authorFromMessage = / \( @ (?< author > [ a - z A - Z 0 - 9 - _ ] + ) \) \( # [ \d ] + \) / . exec (
127- commit . commit . message . split ( '\n' ) [ 0 ] ,
128- ) ;
129- if ( authorFromMessage . groups ?. author ) {
130- return authorFromMessage . groups . author ;
131- }
132- }
85+ const commitsItems = (
86+ await fetchCommitsBetweenRefs ( {
87+ lastRelease : previousRelease ,
88+ release,
89+ repo : 'material-ui' ,
90+ octokit : process . env . GITHUB_TOKEN
91+ ? new Octokit ( { auth : process . env . GITHUB_TOKEN } )
92+ : undefined ,
93+ } )
94+ ) . filter ( ( commit ) => ! isBot ( commit . author . login ) && ! commit . message . startsWith ( '[website]' ) ) ;
13395
134- return authorLogin ;
135- } ;
136-
137- const authors = Array . from (
138- new Set (
139- commitsItems . map ( ( commitsItem ) => {
140- return getAuthor ( commitsItem ) ;
141- } ) ,
142- ) ,
143- ) ;
144- const contributorHandles = authors
145- . sort ( ( a , b ) => a . localeCompare ( b ) )
146- . map ( ( author ) => `@${ author } ` )
147- . join ( ', ' ) ;
96+ const contributorHandles = getAllContributors ( commitsItems ) ;
14897
14998 // We don't know when a particular commit was made from the API.
15099 // Only that the commits are ordered by date ASC
151- const commitsItemsByDateDesc = commitsItems . slice ( ) . reverse ( ) ;
100+ const commitsItemsByOrder = new Map ( commitsItems . map ( ( item , index ) => [ item , index ] ) ) ;
152101 // Sort by tags ASC, date desc
153102 // Will only consider exact matches of tags so `[Slider]` will not be grouped with `[Slider][Modal]`
154103 commitsItems . sort ( ( a , b ) => {
155- const aTags = parseTags ( a . commit . message ) ;
156- const bTags = parseTags ( b . commit . message ) ;
104+ const aTags = parseTags ( a . message ) ;
105+ const bTags = parseTags ( b . message ) ;
157106 if ( aTags === bTags ) {
158- return commitsItemsByDateDesc . indexOf ( a ) - commitsItemsByDateDesc . indexOf ( b ) ;
107+ return commitsItemsByOrder . get ( b ) - commitsItemsByOrder . get ( a ) ;
159108 }
160109 return aTags . localeCompare ( bTags ) ;
161110 } ) ;
162111 const changes = commitsItems . map ( ( commitsItem ) => {
163- let shortMessage = commitsItem . commit . message . split ( '\n' ) [ 0 ] ;
112+ let shortMessage = commitsItem . message . split ( '\n' ) [ 0 ] ;
164113
165114 // If the commit message doesn't have an associated PR, add the commit sha for reference.
166115 if ( ! prLinkRegEx . test ( shortMessage ) ) {
167116 shortMessage += ` (${ commitsItem . sha . substring ( 0 , 7 ) } )` ;
168117 }
169118
170- return `- ${ shortMessage } @${ getAuthor ( commitsItem ) } ` ;
119+ return `- ${ shortMessage } @${ commitsItem . author . login } ` ;
171120 } ) ;
172- const nowFormatted = new Date ( ) . toLocaleDateString ( 'en-US' , {
121+ const generationDate = new Date ( ) . toLocaleDateString ( 'en-US' , {
173122 month : 'short' ,
174123 day : 'numeric' ,
175124 year : 'numeric' ,
176125 } ) ;
126+ const releaseName = /** @type {string } */ (
127+ JSON . parse ( await fs . readFile ( path . join ( process . cwd ( ) , 'package.json' ) , 'utf-8' ) ) . version
128+ ) ;
177129
178130 const changelog = `
179- ## TODO RELEASE NAME
180- <!-- generated comparing ${ lastRelease } ..${ release } -->
181- _${ nowFormatted } _
131+ ## ${ releaseName }
132+
133+ <!-- generated comparing ${ previousRelease } ..${ release } -->
134+
135+ _${ generationDate } _
182136
183- A big thanks to the ${
184- authors . length
185- } contributors who made this release possible. Here are some highlights ✨:
137+ A big thanks to the ${ contributorHandles . length } contributors who made this release possible. Here are some highlights ✨:
186138
187139TODO INSERT HIGHLIGHTS
188140
189141${ changes . join ( '\n' ) }
190142
191- All contributors of this release in alphabetical order: ${ contributorHandles }
143+ All contributors of this release in alphabetical order: ${ contributorHandles . join ( ', ' ) }
192144` ;
193145
194146 // eslint-disable-next-line no-console -- output of this script
@@ -199,31 +151,19 @@ yargs(process.argv.slice(2))
199151 . command ( {
200152 command : '$0' ,
201153 description : 'Creates a changelog' ,
202- builder : ( command ) => {
203- return command
154+ builder : ( command ) =>
155+ command
204156 . option ( 'lastRelease' , {
205157 describe :
206158 'The release to compare against e.g. `v5.0.0-alpha.23`. Default: The latest tag on the current branch.' ,
207159 type : 'string' ,
208160 } )
209- . option ( 'githubToken' , {
210- default : process . env . GITHUB_TOKEN ,
211- describe :
212- 'The personal access token to use for authenticating with GitHub. Needs public_repo permissions.' ,
213- type : 'string' ,
214- } )
215161 . option ( 'release' , {
216162 // #target-branch-reference
217163 default : 'master' ,
218164 describe : 'Ref which we want to release' ,
219165 type : 'string' ,
220- } )
221- . option ( 'repo' , {
222- default : 'material-ui' ,
223- describe : 'Repository to generate a changelog for' ,
224- type : 'string' ,
225- } ) ;
226- } ,
166+ } ) ,
227167 handler : main ,
228168 } )
229169 . help ( )
0 commit comments