Skip to content

chore(ci): support independent versioning #248

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/release.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
"java": "next"
},
"defaultTargetBranch": "next",
"mainGenerator": {
"javascript": "javascript-search",
"java": "java-search",
"php": "php-search"
},
"gitAuthor": {
"name": "api-clients-bot",
"email": "[email protected]"
Expand Down
5 changes: 5 additions & 0 deletions scripts/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ Object.entries(openapitools['generator-cli'].generators).forEach(
}
);

export function getPackageVersion(generator: string): string {
return openapitools['generator-cli'].generators[generator]
.additionalProperties.packageVersion;
}

export const LANGUAGES = [
...new Set(Object.values(GENERATORS).map((gen) => gen.language)),
];
Expand Down
40 changes: 20 additions & 20 deletions scripts/release/__tests__/create-release-issue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,21 @@ describe('create release issue', () => {
getVersionChangesText({
javascript: {
current: '0.0.1',
next: '0.0.2',
releaseType: 'patch',
},
php: {
current: '0.0.1',
next: '0.0.2',
releaseType: 'patch',
},
java: {
current: '0.0.1',
next: '0.0.2',
releaseType: 'patch',
},
})
).toMatchInlineSnapshot(`
"- [x] javascript: v0.0.1 -> v0.0.2
- [x] java: v0.0.1 -> v0.0.2
- [x] php: v0.0.1 -> v0.0.2"
"- [x] javascript: v0.0.1 -> \`patch\` _(e.g. v0.0.2)_
- [x] java: v0.0.1 -> \`patch\` _(e.g. v0.0.2)_
- [x] php: v0.0.1 -> \`patch\` _(e.g. v0.0.2)_"
`);
});

Expand All @@ -55,21 +55,21 @@ describe('create release issue', () => {
getVersionChangesText({
javascript: {
current: '0.0.1',
next: '0.0.2',
releaseType: 'patch',
},
php: {
current: '0.0.1',
next: '0.0.1',
releaseType: null,
noCommit: true,
},
java: {
current: '0.0.1',
next: '0.0.2',
releaseType: 'patch',
},
})
).toMatchInlineSnapshot(`
"- [x] javascript: v0.0.1 -> v0.0.2
- [x] java: v0.0.1 -> v0.0.2
"- [x] javascript: v0.0.1 -> \`patch\` _(e.g. v0.0.2)_
- [x] java: v0.0.1 -> \`patch\` _(e.g. v0.0.2)_
- ~php: v0.0.1 (no commit)~"
`);
});
Expand All @@ -79,23 +79,23 @@ describe('create release issue', () => {
getVersionChangesText({
javascript: {
current: '0.0.1',
next: '0.0.2',
releaseType: 'patch',
},
php: {
current: '0.0.1',
next: '0.0.1',
releaseType: 'minor',
},
java: {
current: '0.0.1',
next: '0.0.2',
releaseType: 'patch',
skipRelease: true,
},
})
).toMatchInlineSnapshot(`
"- [x] javascript: v0.0.1 -> v0.0.2
- [ ] java: v0.0.1 -> v0.0.2
"- [x] javascript: v0.0.1 -> \`patch\` _(e.g. v0.0.2)_
- [ ] java: v0.0.1 -> \`patch\` _(e.g. v0.0.2)_
- No \`feat\` or \`fix\` commit, thus unchecked by default.
- [x] php: v0.0.1 -> v0.0.1"
- [x] php: v0.0.1 -> \`minor\` _(e.g. v0.1.0)_"
`);
});

Expand Down Expand Up @@ -123,7 +123,7 @@ describe('create release issue', () => {
],
});

expect(versions.javascript.next).toEqual('1.0.0');
expect(versions.javascript.releaseType).toEqual('major');
});

it('bumps minor version for feat', () => {
Expand All @@ -150,7 +150,7 @@ describe('create release issue', () => {
],
});

expect(versions.php.next).toEqual('0.1.0');
expect(versions.php.releaseType).toEqual('minor');
});

it('bumps patch version for fix', () => {
Expand All @@ -177,7 +177,7 @@ describe('create release issue', () => {
],
});

expect(versions.java.next).toEqual('0.0.2');
expect(versions.java.releaseType).toEqual('patch');
});

it('marks noCommit for languages without any commit', () => {
Expand Down
47 changes: 39 additions & 8 deletions scripts/release/__tests__/process-release.test.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,62 @@
import { getVersionChangesText } from '../create-release-issue';
import { getVersionsToRelease, getLangsToUpdateRepo } from '../process-release';
import TEXT from '../text';

describe('process release', () => {
it('gets versions to release', () => {
const versions = getVersionsToRelease(`
## Version Changes

- [x] javascript: v1.0.0 -> v1.1.0
- [x] php: v2.0.0 -> v2.0.1
- [ ] java: v3.0.0 -> v3.0.1
- [x] javascript: v1.0.0 -> \`minor\` (e.g. v1.1.0)
- [x] php: v2.0.0 -> \`patch\` (e.g. v2.0.1)
- [ ] java: v3.0.0 -> \`patch\` (e.g. v3.0.1)
`);

expect(Object.keys(versions)).toEqual(['javascript', 'php']);
expect(versions.javascript.current).toEqual('1.0.0');
expect(versions.javascript.next).toEqual('1.1.0');
expect(versions.javascript.releaseType).toEqual('minor');
expect(versions.php.current).toEqual('2.0.0');
expect(versions.php.next).toEqual('2.0.1');
expect(versions.php.releaseType).toEqual('patch');
});

it('gets langs to update', () => {
expect(
getLangsToUpdateRepo(`
## Version Changes

- [ ] javascript: v1.0.0 -> v1.1.0
- [x] php: v2.0.0 -> v2.0.1
- [ ] java: v3.0.0 -> v3.0.1
- [ ] javascript: v1.0.0 -> \`minor\` (e.g. v1.1.0)
- [x] php: v2.0.0 -> \`patch\` (e.g. v2.0.1)
- [ ] java: v3.0.0 -> \`patch\` (e.g. v3.0.1)
`)
).toEqual(['javascript', 'java']);
});

it('parses parses issue body correctly', () => {
// This test is a glue between create-release-issue and process-release.
const issueBody = [
TEXT.versionChangeHeader,
getVersionChangesText({
javascript: {
current: '0.0.1',
releaseType: 'patch',
},
php: {
current: '0.0.1',
releaseType: 'minor',
},
java: {
current: '0.0.1',
releaseType: 'patch',
skipRelease: true,
},
}),
].join('\n');

const versions = getVersionsToRelease(issueBody);
expect(Object.keys(versions)).toEqual(['javascript', 'php']);
expect(versions.javascript.current).toEqual('0.0.1');
expect(versions.javascript.releaseType).toEqual('patch');
expect(versions.php.current).toEqual('0.0.1');
expect(versions.php.releaseType).toEqual('minor');
});
});
1 change: 1 addition & 0 deletions scripts/release/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const RELEASED_TAG = config.releasedTag;
export const MAIN_BRANCH = config.mainBranch;
export const OWNER = config.owner;
export const REPO = config.repo;
export const MAIN_GENERATOR = config.mainGenerator;

export function getTargetBranch(language: string): string {
return config.targetBranch[language] || config.defaultTargetBranch;
Expand Down
96 changes: 59 additions & 37 deletions scripts/release/create-release-issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,49 @@
import { Octokit } from '@octokit/rest';
import dotenv from 'dotenv';
import semver from 'semver';
import type { ReleaseType } from 'semver';

import { GENERATORS, LANGUAGES, ROOT_ENV_PATH, run } from '../common';
import { LANGUAGES, ROOT_ENV_PATH, run, getPackageVersion } from '../common';

import { RELEASED_TAG, MAIN_BRANCH, OWNER, REPO } from './common';
import {
RELEASED_TAG,
MAIN_BRANCH,
OWNER,
REPO,
MAIN_GENERATOR,
} from './common';
import TEXT from './text';

dotenv.config({ path: ROOT_ENV_PATH });

type Version = {
current: string;
next?: string | null;
noCommit?: boolean;
releaseType: ReleaseType | null;
skipRelease?: boolean;
noCommit?: boolean;
};

type Versions = {
[lang: string]: Version;
};

function readVersions(): Versions {
const versions = {};
type VersionsWithoutReleaseType = {
[lang: string]: Omit<Version, 'releaseType'>;
};

Object.values(GENERATORS).forEach((gen) => {
if (!versions[gen.language]) {
versions[gen.language] = {
current: gen.additionalProperties?.packageVersion,
next: undefined,
};
}
});
return versions;
function readVersions(): VersionsWithoutReleaseType {
return Object.keys(MAIN_GENERATOR).reduce((acc, lang) => {
// eslint-disable-next-line no-param-reassign
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's this one also 😇

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what a hawk eye. 69a1db1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀 🐦

acc[lang] = {
current: getPackageVersion(lang),
};
return acc;
}, {});
}

export function getVersionChangesText(versions: Versions): string {
return LANGUAGES.map((lang) => {
const { current, next, noCommit, skipRelease } = versions[lang];
const { current, releaseType, noCommit, skipRelease } = versions[lang];

if (noCommit) {
return `- ~${lang}: v${current} (${TEXT.noCommit})~`;
Expand All @@ -47,9 +54,10 @@ export function getVersionChangesText(versions: Versions): string {
return `- ~${lang}: (${TEXT.currentVersionNotFound})~`;
}

const next = semver.inc(current, releaseType!);
const checked = skipRelease ? ' ' : 'x';
return [
`- [${checked}] ${lang}: v${current} -> v${next}`,
`- [${checked}] ${lang}: v${current} -> \`${releaseType}\` _(e.g. v${next})_`,
skipRelease && TEXT.descriptionForSkippedLang,
]
.filter(Boolean)
Expand Down Expand Up @@ -97,54 +105,66 @@ export function parseCommit(commit: string): Commit {
};
}

/* eslint-disable no-param-reassign */
export function decideReleaseStrategy({
versions,
commits,
}: {
versions: Versions;
versions: VersionsWithoutReleaseType;
commits: PassedCommit[];
}): Versions {
const ret: Versions = { ...versions };

LANGUAGES.forEach((lang) => {
return Object.entries(versions).reduce((acc: Versions, [lang, version]) => {
const commitsPerLang = commits.filter((commit) => commit.lang === lang);
const currentVersion = versions[lang].current;

if (commitsPerLang.length === 0) {
ret[lang].next = currentVersion;
ret[lang].noCommit = true;
return;
acc[lang] = {
...version,
noCommit: true,
releaseType: null,
};
return acc;
}

if (semver.prerelease(currentVersion)) {
// if version is like 0.1.2-beta.1, it increases to 0.1.2-beta.2, even if there's a breaking change.
ret[lang].next = semver.inc(currentVersion, 'prerelease');
return;
acc[lang] = {
...version,
releaseType: 'prerelease',
};
return acc;
}

if (
commitsPerLang.some((commit) =>
commit.message.includes('BREAKING CHANGE')
)
) {
ret[lang].next = semver.inc(currentVersion, 'major');
return;
acc[lang] = {
...version,
releaseType: 'major',
};
return acc;
}

const commitTypes = new Set(commitsPerLang.map(({ type }) => type));
if (commitTypes.has('feat')) {
ret[lang].next = semver.inc(currentVersion, 'minor');
return;
}

ret[lang].next = semver.inc(currentVersion, 'patch');
if (!commitTypes.has('fix')) {
ret[lang].skipRelease = true;
acc[lang] = {
...version,
releaseType: 'minor',
};
return acc;
}
});

return ret;
acc[lang] = {
...version,
releaseType: 'patch',
...(commitTypes.has('fix') ? undefined : { skipRelease: true }),
};
return acc;
}, {});
}
/* eslint-enable no-param-reassign */

async function createReleaseIssue(): Promise<void> {
if (!process.env.GITHUB_TOKEN) {
Expand Down Expand Up @@ -241,6 +261,7 @@ async function createReleaseIssue(): Promise<void> {
TEXT.versionChangeHeader,
versionChanges,
TEXT.descriptionVersionChanges,
TEXT.indenpendentVersioning,
TEXT.changelogHeader,
TEXT.changelogDescription,
changelogs,
Expand Down Expand Up @@ -270,6 +291,7 @@ async function createReleaseIssue(): Promise<void> {
});
}

// JS version of `if __name__ == '__main__'`
if (require.main === module) {
createReleaseIssue();
}
Loading