Skip to content

Commit 907c748

Browse files
authored
feat(scripts): allow subset release and major (#3174)
1 parent 5759543 commit 907c748

File tree

8 files changed

+165
-48
lines changed

8 files changed

+165
-48
lines changed

.github/workflows/scheduled-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ jobs:
2626
type: minimal
2727
language: dart # we need the dart deps because we use melos to bump dependencies for its client
2828

29-
- run: yarn release
29+
- run: yarn cli release
3030
env:
3131
GITHUB_TOKEN: ${{ secrets.ALGOLIA_BOT_TOKEN }}

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
"github-actions:lint": "eslint --ext=yml .github/",
1919
"postinstall": "husky && yarn workspace eslint-plugin-automation-custom build && yarn workspace scripts build:cli",
2020
"playground:browser": "yarn workspace javascript-browser-playground start",
21-
"release": "yarn workspace scripts createReleasePR",
2221
"scripts:build": "yarn workspace scripts build",
2322
"scripts:lint": "yarn workspace scripts lint",
2423
"scripts:test": "yarn workspace scripts test",

scripts/cli/index.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import { startTestServer } from '../cts/testServer';
99
import { formatter } from '../formatter.js';
1010
import { generate } from '../generate.js';
1111
import { playground } from '../playground.js';
12+
import { createReleasePR } from '../release/createReleasePR.js';
1213
import { snippetsGenerateMany } from '../snippets/generate.js';
14+
import type { Language } from '../types.js';
1315

1416
import type { LangArg } from './utils.js';
1517
import {
@@ -23,6 +25,7 @@ import {
2325

2426
const args = {
2527
language: new Argument('[language]', 'The language').choices(PROMPT_LANGUAGES),
28+
languages: new Argument('[language...]', 'The language').choices(PROMPT_LANGUAGES),
2629
clients: new Argument('[client...]', 'The client').choices(getClientChoices('all')),
2730
client: new Argument('[client]', 'The client').choices(PROMPT_CLIENTS),
2831
};
@@ -44,6 +47,10 @@ const flags = {
4447
flag: '-d, --docs',
4548
description: 'generates the doc specs with the code snippets',
4649
},
50+
major: {
51+
flag: '-m, --major',
52+
description: 'triggers a major release for the given language list',
53+
},
4754
};
4855

4956
program.name('cli');
@@ -200,4 +207,23 @@ program
200207
await snippetsGenerateMany(generatorList({ language, client, clientList }));
201208
});
202209

210+
program
211+
.command('release')
212+
.description('Releases the client')
213+
.addArgument(args.languages)
214+
.option(flags.verbose.flag, flags.verbose.description)
215+
.option(flags.major.flag, flags.major.description)
216+
.action(async (langArgs: LangArg[], { verbose, major }) => {
217+
setVerbose(Boolean(verbose));
218+
219+
if (langArgs.length === 0) {
220+
langArgs = [ALL];
221+
}
222+
223+
await createReleasePR({
224+
languages: langArgs.includes(ALL) ? LANGUAGES : (langArgs as Language[]),
225+
major,
226+
});
227+
});
228+
203229
program.parse();

scripts/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
"build:cli": "tsc && tsc-alias",
99
"cleanGeneratedBranch": "NODE_NO_WARNINGS=1 node dist/scripts/ci/codegen/cleanGeneratedBranch.js",
1010
"createMatrix": "NODE_NO_WARNINGS=1 node dist/scripts/ci/githubActions/createMatrix.js",
11-
"createReleasePR": "NODE_NO_WARNINGS=1 RELEASE=true node dist/scripts/release/createReleasePR.js",
1211
"lint": "eslint --ext=ts,js,mjs,cjs .",
1312
"lint:deadcode": "knip",
1413
"pre-commit": "node ./ci/husky/pre-commit.mjs",

scripts/release/__tests__/createReleasePR.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { afterAll, describe, expect, it, vi } from "vitest";
22

33
import releaseConfig from '../../../config/release.config.json' assert { type: 'json' };
44
import type { PassedCommit } from '../types.js';
5+
import { LANGUAGES } from "../../common.js";
56

67
const gitAuthor = releaseConfig.gitAuthor;
78

@@ -450,12 +451,87 @@ describe('createReleasePR', () => {
450451
})
451452
)) as PassedCommit,
452453
],
454+
languages: LANGUAGES,
453455
});
454456

455457
expect(versions.javascript.releaseType).toEqual('major');
456458
expect(versions.javascript.next).toEqual('1.0.0');
457459
});
458460

461+
it('allows forcing major releases', async () => {
462+
const versions = await decideReleaseStrategy({
463+
versions: {
464+
javascript: {
465+
current: '0.0.1',
466+
},
467+
java: {
468+
current: '0.0.1',
469+
},
470+
php: {
471+
current: '0.0.1',
472+
},
473+
},
474+
commits: [],
475+
languages: LANGUAGES,
476+
major: true
477+
});
478+
479+
expect(versions.javascript.releaseType).toEqual('major');
480+
expect(versions.javascript.next).toEqual('1.0.0');
481+
482+
expect(versions.php.releaseType).toEqual('major');
483+
expect(versions.php.next).toEqual('1.0.0');
484+
485+
expect(versions.java.releaseType).toEqual('major');
486+
expect(versions.java.next).toEqual('1.0.0');
487+
});
488+
489+
it('allows releasing subset of clients', async () => {
490+
const versions = await decideReleaseStrategy({
491+
versions: {
492+
javascript: {
493+
current: '0.0.1',
494+
},
495+
java: {
496+
current: '0.0.1',
497+
},
498+
php: {
499+
current: '0.0.1',
500+
},
501+
},
502+
commits: [
503+
(await parseCommit(
504+
buildTestCommit({
505+
type: 'feat',
506+
scope: 'javascript',
507+
message: 'update the API',
508+
})
509+
)) as PassedCommit,
510+
(await parseCommit(
511+
buildTestCommit({
512+
type: 'feat',
513+
scope: 'java',
514+
message: 'update the API',
515+
})
516+
)) as PassedCommit,
517+
(await parseCommit(
518+
buildTestCommit({
519+
type: 'feat',
520+
scope: 'php',
521+
message: 'update the API',
522+
})
523+
)) as PassedCommit,
524+
],
525+
languages: ['php'],
526+
});
527+
528+
expect(versions.javascript.skipRelease).toEqual(true);
529+
expect(versions.java.skipRelease).toEqual(true);
530+
expect(versions.php.skipRelease).toEqual(false);
531+
expect(versions.php.releaseType).toEqual('minor');
532+
expect(versions.php.next).toEqual('0.1.0');
533+
});
534+
459535
it('bumps minor version for feat', async () => {
460536
const versions = await decideReleaseStrategy({
461537
versions: {
@@ -478,6 +554,7 @@ describe('createReleasePR', () => {
478554
})
479555
)) as PassedCommit,
480556
],
557+
languages: LANGUAGES,
481558
});
482559

483560
expect(versions.php.releaseType).toEqual('minor');
@@ -506,6 +583,7 @@ describe('createReleasePR', () => {
506583
})
507584
)) as PassedCommit,
508585
],
586+
languages: LANGUAGES,
509587
});
510588

511589
expect(versions.java.releaseType).toEqual('patch');
@@ -534,6 +612,7 @@ describe('createReleasePR', () => {
534612
})
535613
)) as PassedCommit,
536614
],
615+
languages: LANGUAGES,
537616
});
538617

539618
expect(versions.javascript.noCommit).toEqual(true);
@@ -565,6 +644,7 @@ describe('createReleasePR', () => {
565644
})
566645
)) as PassedCommit,
567646
],
647+
languages: LANGUAGES,
568648
});
569649

570650
expect(versions.javascript.noCommit).toBeUndefined();
@@ -600,6 +680,7 @@ describe('createReleasePR', () => {
600680
})
601681
)) as PassedCommit,
602682
],
683+
languages: LANGUAGES,
603684
});
604685

605686
expect(versions.javascript.noCommit).toBeUndefined();
@@ -642,6 +723,7 @@ describe('createReleasePR', () => {
642723
})
643724
)) as PassedCommit,
644725
],
726+
languages: LANGUAGES,
645727
});
646728

647729
expect(versions.javascript.noCommit).toBeUndefined();
@@ -677,6 +759,7 @@ describe('createReleasePR', () => {
677759
})
678760
)) as PassedCommit,
679761
],
762+
languages: LANGUAGES,
680763
});
681764
expect(versions.javascript.skipRelease).toEqual(true);
682765
expect(versions.java.skipRelease).toBeUndefined();
@@ -705,6 +788,7 @@ describe('createReleasePR', () => {
705788
})
706789
)) as PassedCommit,
707790
],
791+
languages: LANGUAGES,
708792
});
709793

710794
expect(versions.javascript.noCommit).toBeUndefined();
@@ -740,6 +824,7 @@ describe('createReleasePR', () => {
740824
})
741825
)) as PassedCommit,
742826
],
827+
languages: LANGUAGES,
743828
});
744829

745830
expect(versions.javascript.noCommit).toBeUndefined();

scripts/release/createReleasePR.ts

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -208,18 +208,37 @@ export function getNextVersion(current: string, releaseType: semver.ReleaseType
208208
export async function decideReleaseStrategy({
209209
versions,
210210
commits,
211+
languages,
212+
major,
211213
}: {
212214
versions: VersionsBeforeBump;
213215
commits: PassedCommit[];
216+
languages: Language[];
217+
major?: boolean;
214218
}): Promise<Versions> {
215219
const versionsToPublish: Versions = {};
216220

217221
for (const [lang, version] of Object.entries(versions)) {
222+
const currentVersion = versions[lang].current;
223+
224+
if (!languages.includes(lang as Language)) {
225+
console.log(`${lang} is not in the given language list, skipping release`);
226+
227+
versionsToPublish[lang] = {
228+
...version,
229+
noCommit: true,
230+
releaseType: null,
231+
skipRelease: true,
232+
next: getNextVersion(currentVersion, null),
233+
};
234+
235+
continue;
236+
}
237+
218238
const commitsPerLang = commits.filter(
219239
(commit) => commit.scope === lang || COMMON_SCOPES.includes(commit.scope),
220240
);
221241

222-
const currentVersion = versions[lang].current;
223242
let nbGitDiff = await getNbGitDiff({
224243
branch: await getLastReleasedTag(),
225244
head: null,
@@ -233,7 +252,7 @@ export async function decideReleaseStrategy({
233252
nbGitDiff = 1;
234253
}
235254

236-
if (nbGitDiff === 0 || commitsPerLang.length === 0) {
255+
if (!major && (nbGitDiff === 0 || commitsPerLang.length === 0)) {
237256
versionsToPublish[lang] = {
238257
...version,
239258
noCommit: true,
@@ -253,47 +272,33 @@ export async function decideReleaseStrategy({
253272
continue;
254273
}
255274

256-
// snapshots should not be bumped as prerelease
257-
if (semver.prerelease(currentVersion) && !currentVersion.endsWith('-SNAPSHOT')) {
258-
// if version is like 0.1.2-beta.1, it increases to 0.1.2-beta.2, even if there's a breaking change.
259-
versionsToPublish[lang] = {
260-
...version,
261-
releaseType: 'prerelease',
262-
next: getNextVersion(currentVersion, 'prerelease'),
263-
};
264-
265-
continue;
266-
}
267-
268-
if (commitsPerLang.some((commit) => commit.message.includes('BREAKING CHANGE'))) {
269-
versionsToPublish[lang] = {
270-
...version,
271-
releaseType: 'major',
272-
next: getNextVersion(currentVersion, 'major'),
273-
};
274-
275-
continue;
276-
}
277-
275+
let releaseType: semver.ReleaseType = 'patch';
276+
let skipRelease = false;
278277
const commitTypes = new Set(commitsPerLang.map(({ type }) => type));
279-
if (commitTypes.has('feat')) {
280-
versionsToPublish[lang] = {
281-
...version,
282-
releaseType: 'minor',
283-
next: getNextVersion(currentVersion, 'minor'),
284-
};
285278

286-
continue;
279+
switch (true) {
280+
case major || commitsPerLang.some((commit) => commit.message.includes('BREAKING CHANGE')):
281+
releaseType = 'major';
282+
break;
283+
case semver.prerelease(currentVersion) && !currentVersion.endsWith('-SNAPSHOT'):
284+
releaseType = 'prerelease';
285+
break;
286+
case commitTypes.has('feat'):
287+
releaseType = 'minor';
288+
break;
289+
case !commitTypes.has('fix'):
290+
skipRelease = true;
291+
break;
292+
default:
293+
releaseType = 'patch';
287294
}
288295

289296
versionsToPublish[lang] = {
290297
...version,
291-
releaseType: 'patch',
292-
...(commitTypes.has('fix') ? undefined : { skipRelease: true }),
293-
next: getNextVersion(currentVersion, 'patch'),
298+
releaseType,
299+
skipRelease,
300+
next: getNextVersion(currentVersion, releaseType),
294301
};
295-
296-
continue;
297302
}
298303

299304
return versionsToPublish;
@@ -475,7 +480,13 @@ async function updateLTS(versions: Versions, withGraphs?: boolean): Promise<void
475480
);
476481
}
477482

478-
async function createReleasePR(): Promise<void> {
483+
export async function createReleasePR({
484+
languages,
485+
major,
486+
}: {
487+
languages: Language[];
488+
major?: boolean;
489+
}): Promise<void> {
479490
await prepareGitEnvironment();
480491

481492
console.log('Searching for commits since last release...');
@@ -484,11 +495,13 @@ async function createReleasePR(): Promise<void> {
484495
const versions = await decideReleaseStrategy({
485496
versions: readVersions(),
486497
commits: validCommits,
498+
languages,
499+
major,
487500
});
488501

489502
const versionChanges = getVersionChangesText(versions);
490503

491-
console.log('Creating changelogs for all languages...');
504+
console.log(`Creating changelogs for languages: ${languages}...`);
492505
const changelog: Changelog = LANGUAGES.reduce((newChangelog, lang) => {
493506
if (versions[lang].noCommit) {
494507
return newChangelog;
@@ -571,8 +584,3 @@ async function createReleasePR(): Promise<void> {
571584
console.log(`Release PR #${data.number} is ready for review.`);
572585
console.log(` > ${data.html_url}`);
573586
}
574-
575-
if (import.meta.url.endsWith(process.argv[1])) {
576-
setVerbose(false);
577-
createReleasePR();
578-
}

0 commit comments

Comments
 (0)