diff --git a/scripts/release/__tests__/create-release-issue.test.ts b/scripts/release/__tests__/create-release-issue.test.ts index 586bda3f0a..738beb6e0a 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', + scope: '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', + scope: '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', + scope: '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', + scope: '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', + scope: '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', + scope: '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', + scope: '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', + scope: 'php', + message: 'fix some descriptions', + raw: 'b2501882 feat(php): fix some descriptions', + }, + { + hash: 'b2501882', + type: 'feat', + scope: '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', + scope: '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..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,16 +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 allowedScopes = [...LANGUAGES, ...COMMON_SCOPES]; - if (!LANGUAGES.includes(lang)) { + if (!allowedScopes.includes(scope)) { return { error: 'unknown-language-scope' }; } return { hash, type, // `fix` | `feat` | `chore` | ... - lang, // `javascript` | `php` | `java` | ... + scope, // `specs` | `javascript` | `php` | `java` | ... message, raw: commit, }; @@ -126,7 +130,10 @@ 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.scope === lang || COMMON_SCOPES.includes(commit.scope) + ); const currentVersion = versions[lang].current; if (commitsPerLang.length === 0) { @@ -268,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 56097d644e..955eea5b62 100644 --- a/scripts/release/types.ts +++ b/scripts/release/types.ts @@ -17,10 +17,15 @@ export type VersionsWithoutReleaseType = { [lang: string]: Omit; }; +export type Scope = Language | 'specs'; + export type PassedCommit = { hash: string; type: string; - lang: Language; + /** + * A commit can be scoped to a language, or the specs, which impacts all clients. + */ + scope: Scope; message: string; raw: string; };