From 94ea151642927e35a23f926ba7654a465792bd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Vannicatte?= Date: Fri, 20 May 2022 09:40:49 +0200 Subject: [PATCH 1/2] feat(scripts): consider `specs` commits for versioning --- .../__tests__/create-release-issue.test.ts | 467 +++++++++++------- scripts/release/create-release-issue.ts | 10 +- scripts/release/types.ts | 5 +- 3 files changed, 288 insertions(+), 194 deletions(-) diff --git a/scripts/release/__tests__/create-release-issue.test.ts b/scripts/release/__tests__/create-release-issue.test.ts index 586bda3f0a..563587c011 100644 --- a/scripts/release/__tests__/create-release-issue.test.ts +++ b/scripts/release/__tests__/create-release-issue.test.ts @@ -17,242 +17,329 @@ describe('create release issue', () => { }); }); - it('parses commit', () => { - expect(parseCommit(`b2501882 fix(javascript): fix the thing`)).toEqual({ - hash: 'b2501882', - lang: 'javascript', - message: 'fix the thing', - raw: 'b2501882 fix(javascript): fix the thing', - type: 'fix', + describe('parseCommit', () => { + it('parses commit', () => { + expect(parseCommit(`b2501882 fix(javascript): fix the thing`)).toEqual({ + hash: 'b2501882', + lang: 'javascript', + message: 'fix the thing', + raw: 'b2501882 fix(javascript): fix the thing', + type: 'fix', + }); }); - }); - it('returns error when language scope is missing', () => { - expect(parseCommit(`b2501882 fix: fix the thing`)).toEqual({ - error: 'missing-language-scope', + it('considers `specs` as a lang commit', () => { + expect(parseCommit(`b2501882 fix(specs): fix the thing`)).toEqual({ + hash: 'b2501882', + lang: 'specs', + message: 'fix the thing', + raw: 'b2501882 fix(specs): fix the thing', + type: 'fix', + }); }); - }); - it('returns error when language scope is unknown', () => { - expect(parseCommit(`b2501882 fix(basic): fix the thing`)).toEqual({ - error: 'unknown-language-scope', + it('returns error when language scope is missing', () => { + expect(parseCommit(`b2501882 fix: fix the thing`)).toEqual({ + error: 'missing-language-scope', + }); + }); + + it('returns error when language scope is unknown', () => { + expect(parseCommit(`b2501882 fix(basic): fix the thing`)).toEqual({ + error: 'unknown-language-scope', + }); }); }); - it('generates text for version changes', () => { - expect( - getVersionChangesText({ - javascript: { - current: '0.0.1', - releaseType: 'patch', - }, + describe('getVersionChangesText', () => { + it('generates text for version changes', () => { + expect( + getVersionChangesText({ + javascript: { + current: '0.0.1', + releaseType: 'patch', + }, - php: { - current: '0.0.1', - releaseType: 'patch', - }, + php: { + current: '0.0.1', + releaseType: 'patch', + }, - java: { - current: '0.0.1', - releaseType: 'patch', - }, - }) - ).toMatchInlineSnapshot(` + java: { + current: '0.0.1', + releaseType: 'patch', + }, + }) + ).toMatchInlineSnapshot(` "- [x] javascript: 0.0.1 -> \`patch\` _(e.g. 0.0.2)_ - [x] java: 0.0.1 -> \`patch\` _(e.g. 0.0.2)_ - [x] php: 0.0.1 -> \`patch\` _(e.g. 0.0.2)_" `); - }); + }); - it('generates text for version changes with a language with no commit', () => { - expect( - getVersionChangesText({ - javascript: { - current: '0.0.1', - releaseType: 'patch', - }, + it('generates text for version changes with a language with no commit', () => { + expect( + getVersionChangesText({ + javascript: { + current: '0.0.1', + releaseType: 'patch', + }, - php: { - current: '0.0.1', - releaseType: null, - noCommit: true, - }, + php: { + current: '0.0.1', + releaseType: null, + noCommit: true, + }, - java: { - current: '0.0.1', - releaseType: 'patch', - }, - }) - ).toMatchInlineSnapshot(` + java: { + current: '0.0.1', + releaseType: 'patch', + }, + }) + ).toMatchInlineSnapshot(` "- [x] javascript: 0.0.1 -> \`patch\` _(e.g. 0.0.2)_ - [x] java: 0.0.1 -> \`patch\` _(e.g. 0.0.2)_ - ~php: 0.0.1 (no commit)~" `); - }); + }); - it('generates text for version changes with a language to skip', () => { - expect( - getVersionChangesText({ - javascript: { - current: '0.0.1', - releaseType: 'patch', - }, + it('generates text for version changes with a language to skip', () => { + expect( + getVersionChangesText({ + javascript: { + current: '0.0.1', + releaseType: 'patch', + }, - php: { - current: '0.0.1', - releaseType: 'minor', - }, + php: { + current: '0.0.1', + releaseType: 'minor', + }, - java: { - current: '0.0.1', - releaseType: 'patch', - skipRelease: true, - }, - }) - ).toMatchInlineSnapshot(` + java: { + current: '0.0.1', + releaseType: 'patch', + skipRelease: true, + }, + }) + ).toMatchInlineSnapshot(` "- [x] javascript: 0.0.1 -> \`patch\` _(e.g. 0.0.2)_ - [ ] java: 0.0.1 -> \`patch\` _(e.g. 0.0.2)_ - No \`feat\` or \`fix\` commit, thus unchecked by default. - [x] php: 0.0.1 -> \`minor\` _(e.g. 0.1.0)_" `); + }); }); - it('bumps major version for BREAKING CHANGE', () => { - const versions = decideReleaseStrategy({ - versions: { - javascript: { - current: '0.0.1', + describe('decideReleaseStrategy', () => { + it('bumps major version for BREAKING CHANGE', () => { + const versions = decideReleaseStrategy({ + versions: { + javascript: { + current: '0.0.1', + }, + java: { + current: '0.0.1', + }, + php: { + current: '0.0.1', + }, }, - java: { - current: '0.0.1', - }, - php: { - current: '0.0.1', - }, - }, - commits: [ - { - hash: 'b2501882', - type: 'feat', - lang: 'javascript', - message: 'update the API (BREAKING CHANGE)', - raw: 'b2501882 feat(javascript): update the API (BREAKING CHANGE)', - }, - ], - }); + commits: [ + { + hash: 'b2501882', + type: 'feat', + lang: 'javascript', + message: 'update the API (BREAKING CHANGE)', + raw: 'b2501882 feat(javascript): update the API (BREAKING CHANGE)', + }, + ], + }); - expect(versions.javascript.releaseType).toEqual('major'); - }); + expect(versions.javascript.releaseType).toEqual('major'); + }); - it('bumps minor version for feat', () => { - const versions = decideReleaseStrategy({ - versions: { - javascript: { - current: '0.0.1', + it('bumps minor version for feat', () => { + const versions = decideReleaseStrategy({ + versions: { + javascript: { + current: '0.0.1', + }, + java: { + current: '0.0.1', + }, + php: { + current: '0.0.1', + }, }, - java: { - current: '0.0.1', - }, - php: { - current: '0.0.1', - }, - }, - commits: [ - { - hash: 'b2501882', - type: 'feat', - lang: 'php', - message: 'update the API', - raw: 'b2501882 feat(php): update the API', - }, - ], - }); + commits: [ + { + hash: 'b2501882', + type: 'feat', + lang: 'php', + message: 'update the API', + raw: 'b2501882 feat(php): update the API', + }, + ], + }); - expect(versions.php.releaseType).toEqual('minor'); - }); + expect(versions.php.releaseType).toEqual('minor'); + }); - it('bumps patch version for fix', () => { - const versions = decideReleaseStrategy({ - versions: { - javascript: { - current: '0.0.1', - }, - java: { - current: '0.0.1', + it('bumps patch version for fix', () => { + const versions = decideReleaseStrategy({ + versions: { + javascript: { + current: '0.0.1', + }, + java: { + current: '0.0.1', + }, + php: { + current: '0.0.1', + }, }, - php: { - current: '0.0.1', - }, - }, - commits: [ - { - hash: 'b2501882', - type: 'fix', - lang: 'java', - message: 'fix some bug', - raw: 'b2501882 fix(java): fix some bug', - }, - ], - }); + commits: [ + { + hash: 'b2501882', + type: 'fix', + lang: 'java', + message: 'fix some bug', + raw: 'b2501882 fix(java): fix some bug', + }, + ], + }); - expect(versions.java.releaseType).toEqual('patch'); - }); + expect(versions.java.releaseType).toEqual('patch'); + }); - it('marks noCommit for languages without any commit', () => { - const versions = decideReleaseStrategy({ - versions: { - javascript: { - current: '0.0.1', - }, - java: { - current: '0.0.1', - }, - php: { - current: '0.0.1', + it('marks noCommit for languages without any commit', () => { + const versions = decideReleaseStrategy({ + versions: { + javascript: { + current: '0.0.1', + }, + java: { + current: '0.0.1', + }, + php: { + current: '0.0.1', + }, }, - }, - commits: [ - { - hash: 'b2501882', - type: 'fix', - lang: 'java', - message: 'fix some bug', - raw: 'b2501882 fix(java): fix some bug', - }, - ], - }); + commits: [ + { + hash: 'b2501882', + type: 'fix', + lang: 'java', + message: 'fix some bug', + raw: 'b2501882 fix(java): fix some bug', + }, + ], + }); - expect(versions.javascript.noCommit).toEqual(true); - expect(versions.php.noCommit).toEqual(true); - expect(versions.java.noCommit).toBeUndefined(); - }); + expect(versions.javascript.noCommit).toEqual(true); + expect(versions.php.noCommit).toEqual(true); + expect(versions.java.noCommit).toBeUndefined(); + }); - it('marks skipRelease for patch upgrade without fix commit', () => { - const versions = decideReleaseStrategy({ - versions: { - javascript: { - current: '0.0.1', - }, - java: { - current: '0.0.1', + it('releases every languages if a `specs` commit is present', () => { + const versions = decideReleaseStrategy({ + versions: { + javascript: { + current: '0.0.1', + }, + java: { + current: '0.0.1', + }, + php: { + current: '0.0.1', + }, }, - php: { - current: '0.0.1', + commits: [ + { + hash: 'b2501882', + type: 'fix', + lang: 'specs', + message: 'fix some descriptions', + raw: 'b2501882 fix(specs): fix some descriptions', + }, + ], + }); + + expect(versions.javascript.noCommit).toBeUndefined(); + expect(versions.javascript.releaseType).toEqual('patch'); + expect(versions.php.noCommit).toBeUndefined(); + expect(versions.php.releaseType).toEqual('patch'); + expect(versions.java.noCommit).toBeUndefined(); + expect(versions.java.releaseType).toEqual('patch'); + }); + + it('bumps for `specs` feat with only language `fix` commits', () => { + const versions = decideReleaseStrategy({ + versions: { + javascript: { + current: '0.0.1', + }, + java: { + current: '0.0.1', + }, + php: { + current: '0.0.1', + }, }, - }, - commits: [ - { - hash: 'b2501882', - type: 'chore', - lang: 'javascript', - message: 'update devDevpendencies', - raw: 'b2501882 chore(javascript): update devDevpendencies', + commits: [ + { + hash: 'b2501882', + type: 'fix', + lang: 'php', + message: 'fix some descriptions', + raw: 'b2501882 feat(php): fix some descriptions', + }, + { + hash: 'b2501882', + type: 'feat', + lang: 'specs', + message: 'add some descriptions', + raw: 'b2501882 feat(specs): add some descriptions', + }, + ], + }); + + expect(versions.javascript.noCommit).toBeUndefined(); + expect(versions.javascript.releaseType).toEqual('minor'); + expect(versions.php.noCommit).toBeUndefined(); + expect(versions.php.releaseType).toEqual('minor'); + expect(versions.java.noCommit).toBeUndefined(); + expect(versions.java.releaseType).toEqual('minor'); + }); + + it('marks skipRelease for patch upgrade without fix commit', () => { + const versions = decideReleaseStrategy({ + versions: { + javascript: { + current: '0.0.1', + }, + java: { + current: '0.0.1', + }, + php: { + current: '0.0.1', + }, }, - ], + commits: [ + { + hash: 'b2501882', + type: 'chore', + lang: 'javascript', + message: 'update devDevpendencies', + raw: 'b2501882 chore(javascript): update devDevpendencies', + }, + ], + }); + expect(versions.javascript.skipRelease).toEqual(true); + expect(versions.java.skipRelease).toBeUndefined(); + expect(versions.php.skipRelease).toBeUndefined(); }); - expect(versions.javascript.skipRelease).toEqual(true); - expect(versions.java.skipRelease).toBeUndefined(); - expect(versions.php.skipRelease).toBeUndefined(); }); it('generates text for skipped commits', () => { diff --git a/scripts/release/create-release-issue.ts b/scripts/release/create-release-issue.ts index 66e6fc4cdf..57278e80d2 100755 --- a/scripts/release/create-release-issue.ts +++ b/scripts/release/create-release-issue.ts @@ -102,15 +102,17 @@ export function parseCommit(commit: string): Commit { message = message.slice(message.indexOf(':') + 1).trim(); type = matchResult[1]; const lang = matchResult[2] as Language; + // A spec commit should be added to every clients, as it mostly imply a client change. + const allowedLanguages = [...LANGUAGES, 'specs']; - if (!LANGUAGES.includes(lang)) { + if (!allowedLanguages.includes(lang)) { return { error: 'unknown-language-scope' }; } return { hash, type, // `fix` | `feat` | `chore` | ... - lang, // `javascript` | `php` | `java` | ... + lang, // `specs` | `javascript` | `php` | `java` | ... message, raw: commit, }; @@ -126,7 +128,9 @@ export function decideReleaseStrategy({ }): Versions { return Object.entries(versions).reduce( (versionsWithReleaseType: Versions, [lang, version]) => { - const commitsPerLang = commits.filter((commit) => commit.lang === lang); + const commitsPerLang = commits.filter( + (commit) => commit.lang === lang || commit.lang === 'specs' + ); const currentVersion = versions[lang].current; if (commitsPerLang.length === 0) { diff --git a/scripts/release/types.ts b/scripts/release/types.ts index 56097d644e..a0d3dac646 100644 --- a/scripts/release/types.ts +++ b/scripts/release/types.ts @@ -20,7 +20,10 @@ export type VersionsWithoutReleaseType = { export type PassedCommit = { hash: string; type: string; - lang: Language; + /** + * A commit can be scoped to a language, or the specs, which impacts all clients. + */ + lang: Language | 'specs'; message: string; raw: string; }; From 8bb2039038d541c7bdf3655c2196355047e49a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Vannicatte?= Date: Fri, 20 May 2022 11:56:20 +0200 Subject: [PATCH 2/2] rename `lang` to scope --- .../__tests__/create-release-issue.test.ts | 20 +++++++++---------- scripts/release/create-release-issue.ts | 20 ++++++++++++------- scripts/release/types.ts | 4 +++- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/scripts/release/__tests__/create-release-issue.test.ts b/scripts/release/__tests__/create-release-issue.test.ts index 563587c011..738beb6e0a 100644 --- a/scripts/release/__tests__/create-release-issue.test.ts +++ b/scripts/release/__tests__/create-release-issue.test.ts @@ -21,7 +21,7 @@ describe('create release issue', () => { it('parses commit', () => { expect(parseCommit(`b2501882 fix(javascript): fix the thing`)).toEqual({ hash: 'b2501882', - lang: 'javascript', + scope: 'javascript', message: 'fix the thing', raw: 'b2501882 fix(javascript): fix the thing', type: 'fix', @@ -31,7 +31,7 @@ describe('create release issue', () => { it('considers `specs` as a lang commit', () => { expect(parseCommit(`b2501882 fix(specs): fix the thing`)).toEqual({ hash: 'b2501882', - lang: 'specs', + scope: 'specs', message: 'fix the thing', raw: 'b2501882 fix(specs): fix the thing', type: 'fix', @@ -149,7 +149,7 @@ describe('create release issue', () => { { hash: 'b2501882', type: 'feat', - lang: 'javascript', + scope: 'javascript', message: 'update the API (BREAKING CHANGE)', raw: 'b2501882 feat(javascript): update the API (BREAKING CHANGE)', }, @@ -176,7 +176,7 @@ describe('create release issue', () => { { hash: 'b2501882', type: 'feat', - lang: 'php', + scope: 'php', message: 'update the API', raw: 'b2501882 feat(php): update the API', }, @@ -203,7 +203,7 @@ describe('create release issue', () => { { hash: 'b2501882', type: 'fix', - lang: 'java', + scope: 'java', message: 'fix some bug', raw: 'b2501882 fix(java): fix some bug', }, @@ -230,7 +230,7 @@ describe('create release issue', () => { { hash: 'b2501882', type: 'fix', - lang: 'java', + scope: 'java', message: 'fix some bug', raw: 'b2501882 fix(java): fix some bug', }, @@ -259,7 +259,7 @@ describe('create release issue', () => { { hash: 'b2501882', type: 'fix', - lang: 'specs', + scope: 'specs', message: 'fix some descriptions', raw: 'b2501882 fix(specs): fix some descriptions', }, @@ -291,14 +291,14 @@ describe('create release issue', () => { { hash: 'b2501882', type: 'fix', - lang: 'php', + scope: 'php', message: 'fix some descriptions', raw: 'b2501882 feat(php): fix some descriptions', }, { hash: 'b2501882', type: 'feat', - lang: 'specs', + scope: 'specs', message: 'add some descriptions', raw: 'b2501882 feat(specs): add some descriptions', }, @@ -330,7 +330,7 @@ describe('create release issue', () => { { hash: 'b2501882', type: 'chore', - lang: 'javascript', + scope: 'javascript', message: 'update devDevpendencies', raw: 'b2501882 chore(javascript): update devDevpendencies', }, diff --git a/scripts/release/create-release-issue.ts b/scripts/release/create-release-issue.ts index 57278e80d2..82b175ff05 100755 --- a/scripts/release/create-release-issue.ts +++ b/scripts/release/create-release-issue.ts @@ -12,7 +12,6 @@ import { REPO, } from '../common'; import { getPackageVersionDefault } from '../config'; -import type { Language } from '../types'; import { RELEASED_TAG, getOctokit } from './common'; import TEXT from './text'; @@ -21,10 +20,13 @@ import type { VersionsWithoutReleaseType, PassedCommit, Commit, + Scope, } from './types'; dotenv.config({ path: ROOT_ENV_PATH }); +const COMMON_SCOPES = ['specs']; + export function readVersions(): VersionsWithoutReleaseType { return Object.fromEntries( LANGUAGES.map((lang) => [lang, { current: getPackageVersionDefault(lang) }]) @@ -101,18 +103,18 @@ export function parseCommit(commit: string): Commit { } message = message.slice(message.indexOf(':') + 1).trim(); type = matchResult[1]; - const lang = matchResult[2] as Language; + const scope = matchResult[2] as Scope; // A spec commit should be added to every clients, as it mostly imply a client change. - const allowedLanguages = [...LANGUAGES, 'specs']; + const allowedScopes = [...LANGUAGES, ...COMMON_SCOPES]; - if (!allowedLanguages.includes(lang)) { + if (!allowedScopes.includes(scope)) { return { error: 'unknown-language-scope' }; } return { hash, type, // `fix` | `feat` | `chore` | ... - lang, // `specs` | `javascript` | `php` | `java` | ... + scope, // `specs` | `javascript` | `php` | `java` | ... message, raw: commit, }; @@ -129,7 +131,8 @@ export function decideReleaseStrategy({ return Object.entries(versions).reduce( (versionsWithReleaseType: Versions, [lang, version]) => { const commitsPerLang = commits.filter( - (commit) => commit.lang === lang || commit.lang === 'specs' + (commit) => + commit.scope === lang || COMMON_SCOPES.includes(commit.scope) ); const currentVersion = versions[lang].current; @@ -272,7 +275,10 @@ async function createReleaseIssue(): Promise { return [ `### ${lang}`, ...validCommits - .filter((commit) => commit.lang === lang) + .filter( + (commit) => + commit.scope === lang || COMMON_SCOPES.includes(commit.scope) + ) .map((commit) => `- ${commit.raw}`), ]; }) diff --git a/scripts/release/types.ts b/scripts/release/types.ts index a0d3dac646..955eea5b62 100644 --- a/scripts/release/types.ts +++ b/scripts/release/types.ts @@ -17,13 +17,15 @@ export type VersionsWithoutReleaseType = { [lang: string]: Omit; }; +export type Scope = Language | 'specs'; + export type PassedCommit = { hash: string; type: string; /** * A commit can be scoped to a language, or the specs, which impacts all clients. */ - lang: Language | 'specs'; + scope: Scope; message: string; raw: string; };