diff --git a/scripts/release/__tests__/createReleasePR.test.ts b/scripts/release/__tests__/createReleasePR.test.ts index a670d066e44..9a392cdacde 100644 --- a/scripts/release/__tests__/createReleasePR.test.ts +++ b/scripts/release/__tests__/createReleasePR.test.ts @@ -101,7 +101,13 @@ describe('createReleasePR', () => { getFileChangesMock.mockResolvedValueOnce(''); expect(await parseCommit(buildTestCommit())).toEqual( expect.objectContaining({ - error: 'missing-language-scope', + author: '[@algolia-bot](https://github.com/algolia-bot/)', + hash: 'b2501882', + languages: [], + message: 'fix: fix the thing', + prNumber: 123, + scope: undefined, + type: 'fix', }), ); }); @@ -110,7 +116,13 @@ describe('createReleasePR', () => { getFileChangesMock.mockResolvedValueOnce('specs/search/something.json'); expect(await parseCommit(buildTestCommit())).toEqual( expect.objectContaining({ - error: 'missing-language-scope', + author: '[@algolia-bot](https://github.com/algolia-bot/)', + hash: 'b2501882', + languages: [], + message: 'fix: fix the thing', + prNumber: 123, + scope: undefined, + type: 'fix', }), ); }); @@ -138,7 +150,8 @@ describe('createReleasePR', () => { }); }); - it('returns error when it is a generated commit', async () => { + it('returns early when it is a generated commit', async () => { + getFileChangesMock.mockResolvedValueOnce('clients/algoliasearch-client-javascript/package.json'); expect( await parseCommit( buildTestCommit({ @@ -147,7 +160,9 @@ describe('createReleasePR', () => { }), ), ).toEqual({ - error: 'generation-commit', + generated: true, + languages: ['javascript'], + message: 'feat(specs): foo bar baz (generated)', }); }); }); diff --git a/scripts/release/createReleasePR.ts b/scripts/release/createReleasePR.ts index 0e70a958641..e930c569b68 100755 --- a/scripts/release/createReleasePR.ts +++ b/scripts/release/createReleasePR.ts @@ -86,20 +86,24 @@ export async function parseCommit(commit: string): Promise { const prNumberMatch = message.match(/#(\d+)/); const prNumber = prNumberMatch ? parseInt(prNumberMatch[1], 10) : 0; - // We skip generation commits as they do not appear in changelogs - if (isGeneratedCommit(message)) { - return { - error: 'generation-commit', - }; + let commitType = typeAndScope ? typeAndScope[1] : 'fix'; // default to fix. + if (!['feat', 'fix', 'chore'].includes(commitType)) { + commitType = 'fix'; } // get the scope of the commit by checking the changes. // any changes in the folder of a language will be scoped to that language const diff = await getFileChanges(hash); if (!diff) { + // for empty commits, they will be filtered out later return { - error: 'missing-language-scope', - message, + hash, + type: commitType as CommitType, + languages: [], + scope: typeAndScope ? (typeAndScope[2] as Scope) : undefined, + message: message.replace(`(#${prNumber})`, '').trim(), + prNumber, + author: fetchedUsers[authorEmail], }; } @@ -111,9 +115,11 @@ export async function parseCommit(commit: string): Promise { } } - if (languageScopes.size === 0) { + // for generated commits, we just report the languages so that the changes are attributed to the correct language and commit + if (isGeneratedCommit(message)) { return { - error: 'missing-language-scope', + generated: true, + languages: [...languageScopes] as Language[], message, }; } @@ -132,11 +138,6 @@ export async function parseCommit(commit: string): Promise { } } - let commitType = typeAndScope ? typeAndScope[1] : 'fix'; // default to fix. - if (!['feat', 'fix', 'chore'].includes(commitType)) { - commitType = 'fix'; - } - return { hash, type: commitType as CommitType, // `fix` | `feat` | `chore` | ..., default to `fix` @@ -246,19 +247,23 @@ async function getCommits(force?: boolean): Promise<{ skippedCommits: string; }> { // Reading commits since last release - const latestCommits = (await run(`git log --pretty=format:"%h|%ae|%s" ${await getLastReleasedTag()}..${MAIN_BRANCH}`)) + const latestCommits = ( + await run(`git log --reverse --pretty=format:"%h|%ae|%s" ${await getLastReleasedTag()}..${MAIN_BRANCH}`) + ) .split('\n') .filter(Boolean); - const commitsWithoutLanguageScope: string[] = []; - const validCommits: ParsedCommit[] = []; + let validCommits: ParsedCommit[] = []; for (const commitMessage of latestCommits) { const commit = await parseCommit(commitMessage); - if ('error' in commit) { - if (commit.error === 'missing-language-scope') { - commitsWithoutLanguageScope.push(commit.message); + if ('generated' in commit) { + const originalCommit = validCommits.findIndex((c) => commit.message.includes(c.message)); + if (originalCommit !== -1) { + validCommits[originalCommit].languages = [ + ...new Set([...validCommits[originalCommit].languages, ...commit.languages]), + ]; } continue; @@ -267,6 +272,12 @@ async function getCommits(force?: boolean): Promise<{ validCommits.push(commit); } + // redo a pass to filter out commits without language scope + const commitsWithoutLanguageScope = validCommits + .filter((commit) => commit.languages.length === 0) + .map((commit) => commit.message); + validCommits = validCommits.filter((commit) => commit.languages.length > 0); + if (!force && validCommits.length === 0) { console.log( chalk.black.bgYellow('[INFO]'), @@ -351,7 +362,7 @@ export async function createReleasePR({ // sometimes the scope of the commits is not set correctly and concerns another language, we can fix it. if (LANGUAGES.includes(validCommit.scope as Language) && validCommit.scope !== lang) { - validCommit.message = validCommit.message.replace(`(${validCommit.scope}):`, `(${lang}):`); + validCommit.message = validCommit.message.replace(`(${validCommit.scope}):`, '(clients):'); } const changelogCommit = [ diff --git a/scripts/release/types.ts b/scripts/release/types.ts index e6ab660a671..bb368e3c5dd 100644 --- a/scripts/release/types.ts +++ b/scripts/release/types.ts @@ -28,10 +28,7 @@ export type ParsedCommit = { prNumber: number; }; -export type Commit = - | ParsedCommit - | { error: 'generation-commit' } - | { error: 'missing-language-scope'; message: string }; +export type Commit = ParsedCommit | { generated: true; languages: Language[]; message: string }; export type Changelog = { [lang in Language]?: string;