Skip to content

Commit eb79948

Browse files
authored
Merge branch 'main' into chore/clean-models-pregen
2 parents 059915e + 2583871 commit eb79948

File tree

8 files changed

+240
-15
lines changed

8 files changed

+240
-15
lines changed

.github/workflows/check.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,16 @@ jobs:
351351
job: codegen
352352

353353
- name: Push generated code
354+
id: pushGeneratedCode
354355
run: yarn workspace scripts pushGeneratedCode
355356
env:
356357
GITHUB_TOKEN: ${{ secrets.TOKEN_GENERATE_BOT }}
357358
PR_NUMBER: ${{ github.event.number }}
359+
360+
- name: Spread generation to each repository
361+
if: |
362+
steps.pushGeneratedCode.exitcode == 0 &&
363+
github.ref == 'refs/heads/main'
364+
run: yarn workspace scripts spreadGeneration
365+
env:
366+
GITHUB_TOKEN: ${{ secrets.TOKEN_RELEASE_BOT }}

scripts/__tests__/common.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import execa from 'execa';
2+
3+
import { gitCommit } from '../common';
4+
5+
jest.mock('execa');
6+
7+
describe('gitCommit', () => {
8+
beforeEach(() => {
9+
jest.clearAllMocks();
10+
});
11+
12+
it('commits with message', () => {
13+
gitCommit({ message: 'chore: does something' });
14+
expect(execa).toHaveBeenCalledTimes(1);
15+
expect(execa).toHaveBeenCalledWith(
16+
'git',
17+
['commit', '-m', 'chore: does something'],
18+
{ cwd: expect.any(String) }
19+
);
20+
});
21+
22+
it('commits with co-author', () => {
23+
gitCommit({
24+
message: 'chore: does something',
25+
coauthor: { name: 'some', email: '[email protected]' },
26+
});
27+
expect(execa).toHaveBeenCalledTimes(1);
28+
expect(execa).toHaveBeenCalledWith(
29+
'git',
30+
[
31+
'commit',
32+
'-m',
33+
'chore: does something\n\n\nCo-authored-by: some <[email protected]>',
34+
],
35+
{ cwd: expect.any(String) }
36+
);
37+
});
38+
});
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { LANGUAGES } from '../../../common';
2+
import { decideWhereToSpread, cleanUpCommitMessage } from '../spreadGeneration';
3+
4+
describe('spread generation', () => {
5+
it('skips in case of release commit', () => {
6+
expect(decideWhereToSpread('chore: release 2022-03-15')).toEqual([]);
7+
});
8+
9+
it('spreads to all if scope is missing', () => {
10+
expect(decideWhereToSpread('chore: do something')).toEqual(LANGUAGES);
11+
});
12+
13+
it('spreads to javascript if the scope is javascript', () => {
14+
expect(decideWhereToSpread('fix(javascript): fix something')).toEqual([
15+
'javascript',
16+
]);
17+
});
18+
19+
it('spreads to all if scope is not specific language', () => {
20+
['cts', 'spec', 'script', 'ci'].forEach((scope) => {
21+
expect(decideWhereToSpread(`fix(${scope}): fix something`)).toEqual(
22+
LANGUAGES
23+
);
24+
});
25+
});
26+
27+
it('removes pull-request number from commit message', () => {
28+
expect(
29+
cleanUpCommitMessage(`feat(ci): make ci push generated code (#244)`)
30+
).toEqual(
31+
`feat(ci): make ci push generated code\n\nhttps://github.com/algolia/api-clients-automation/pull/244`
32+
);
33+
});
34+
35+
it('keeps the commit message even if it does not have PR number', () => {
36+
const commitMessage = `feat(ci): make ci push generated code`;
37+
expect(cleanUpCommitMessage(commitMessage)).toEqual(commitMessage);
38+
});
39+
40+
it('cleans up correctly even if the title contains a url', () => {
41+
const commitMessage = `fix(java): solve oneOf using a custom generator https://algolia.atlassian.net/browse/APIC-123 (#200)`;
42+
expect(cleanUpCommitMessage(commitMessage)).toMatchInlineSnapshot(`
43+
"fix(java): solve oneOf using a custom generator https://algolia.atlassian.net/browse/APIC-123
44+
45+
https://github.com/algolia/api-clients-automation/pull/200"
46+
`);
47+
});
48+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { gitCommit, LANGUAGES, run, toAbsolutePath } from '../../common';
2+
import { getLanguageFolder } from '../../config';
3+
import {
4+
cloneRepository,
5+
configureGitHubAuthor,
6+
OWNER,
7+
REPO,
8+
} from '../../release/common';
9+
10+
const GENERATED_MAIN_BRANCH = `generated/main`;
11+
12+
export function decideWhereToSpread(commitMessage: string): string[] {
13+
if (commitMessage.startsWith('chore: release')) {
14+
return [];
15+
}
16+
17+
const result = commitMessage.match(/(.+)\((.+)\):/);
18+
if (!result) {
19+
// no scope
20+
return LANGUAGES;
21+
}
22+
23+
const scope = result[2];
24+
return LANGUAGES.includes(scope) ? [scope] : LANGUAGES;
25+
}
26+
27+
export function cleanUpCommitMessage(commitMessage: string): string {
28+
const result = commitMessage.match(/(.+)\s\(#(\d+)\)$/);
29+
if (!result) {
30+
return commitMessage;
31+
}
32+
33+
return [
34+
result[1],
35+
`https://github.com/${OWNER}/${REPO}/pull/${result[2]}`,
36+
].join('\n\n');
37+
}
38+
39+
async function spreadGeneration(): Promise<void> {
40+
if (!process.env.GITHUB_TOKEN) {
41+
throw new Error('Environment variable `GITHUB_TOKEN` does not exist.');
42+
}
43+
44+
const lastCommitMessage = await run(`git log -1 --format="%s"`);
45+
const name = (await run(`git log -1 --format="%an"`)).trim();
46+
const email = (await run(`git log -1 --format="%ae"`)).trim();
47+
const commitMessage = cleanUpCommitMessage(lastCommitMessage);
48+
const langs = decideWhereToSpread(lastCommitMessage);
49+
50+
await run(`git checkout ${GENERATED_MAIN_BRANCH}`);
51+
52+
for (const lang of langs) {
53+
const { tempGitDir } = await cloneRepository({
54+
lang,
55+
githubToken: process.env.GITHUB_TOKEN,
56+
tempDir: process.env.RUNNER_TEMP!,
57+
});
58+
59+
const clientPath = toAbsolutePath(getLanguageFolder(lang));
60+
await run(`cp -r ${clientPath}/ ${tempGitDir}`);
61+
62+
await configureGitHubAuthor(tempGitDir);
63+
await run(`git add .`, { cwd: tempGitDir });
64+
await gitCommit({
65+
message: commitMessage,
66+
coauthor: { name, email },
67+
cwd: tempGitDir,
68+
});
69+
await run(`git push`, { cwd: tempGitDir });
70+
}
71+
}
72+
73+
if (require.main === module) {
74+
spreadGeneration();
75+
}

scripts/common.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,34 @@ export async function runIfExists(
154154
return '';
155155
}
156156

157+
export async function gitCommit({
158+
message,
159+
coauthor,
160+
cwd = ROOT_DIR,
161+
}: {
162+
message: string;
163+
coauthor?: {
164+
name: string;
165+
email: string;
166+
};
167+
cwd?: string;
168+
}): Promise<void> {
169+
await execa(
170+
'git',
171+
[
172+
'commit',
173+
'-m',
174+
message +
175+
(coauthor
176+
? `\n\n\nCo-authored-by: ${coauthor.name} <${coauthor.email}>`
177+
: ''),
178+
],
179+
{
180+
cwd,
181+
}
182+
);
183+
}
184+
157185
export async function buildCustomGenerators(verbose: boolean): Promise<void> {
158186
const spinner = createSpinner('building custom generators', verbose).start();
159187
await run('./gradle/gradlew --no-daemon -p generators assemble', {

scripts/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"processRelease": "ts-node release/process-release.ts",
77
"pushGeneratedCode": "ts-node ci/codegen/pushGeneratedCode.ts",
88
"cleanGeneratedBranch": "ts-node ci/codegen/cleanGeneratedBranch.ts",
9+
"spreadGeneration": "ts-node ci/codegen/spreadGeneration.ts",
910
"upsertGenerationComment": "ts-node ci/codegen/upsertGenerationComment.ts",
1011
"test": "jest"
1112
},

scripts/release/common.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import config from '../../config/release.config.json';
2-
import { run } from '../common';
2+
import { getGitHubUrl, run } from '../common';
33

44
export const RELEASED_TAG = config.releasedTag;
55
export const MAIN_BRANCH = config.mainBranch;
@@ -36,3 +36,26 @@ export async function configureGitHubAuthor(cwd?: string): Promise<void> {
3636
await run(`git config user.name "${name}"`, { cwd });
3737
await run(`git config user.email "${email}"`, { cwd });
3838
}
39+
40+
export async function cloneRepository({
41+
lang,
42+
githubToken,
43+
tempDir,
44+
}: {
45+
lang: string;
46+
githubToken: string;
47+
tempDir: string;
48+
}): Promise<{ tempGitDir: string }> {
49+
const targetBranch = getTargetBranch(lang);
50+
51+
const gitHubUrl = getGitHubUrl(lang, { token: githubToken });
52+
const tempGitDir = `${tempDir}/${lang}`;
53+
await run(`rm -rf ${tempGitDir}`);
54+
await run(
55+
`git clone --depth 1 --branch ${targetBranch} ${gitHubUrl} ${tempGitDir}`
56+
);
57+
58+
return {
59+
tempGitDir,
60+
};
61+
}

scripts/release/process-release.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
run,
1212
exists,
1313
getGitHubUrl,
14+
gitCommit,
1415
} from '../common';
1516
import { getLanguageFolder } from '../config';
1617

@@ -19,8 +20,8 @@ import {
1920
OWNER,
2021
REPO,
2122
getMarkdownSection,
22-
getTargetBranch,
2323
configureGitHubAuthor,
24+
cloneRepository,
2425
} from './common';
2526
import TEXT from './text';
2627

@@ -163,43 +164,45 @@ async function processRelease(): Promise<void> {
163164
await run(`git add ${changelogPath}`);
164165
}
165166

166-
// We push commits from submodules AFTER all the generations are done.
167+
// We push commits to each repository AFTER all the generations are done.
167168
// Otherwise, we will end up having broken release.
168169
for (const lang of langsToReleaseOrUpdate) {
169-
const clientPath = toAbsolutePath(getLanguageFolder(lang));
170-
const targetBranch = getTargetBranch(lang);
171-
172-
const gitHubUrl = getGitHubUrl(lang, { token: process.env.GITHUB_TOKEN });
173-
const tempGitDir = `${process.env.RUNNER_TEMP}/${lang}`;
174-
await run(`rm -rf ${tempGitDir}`);
175-
await run(
176-
`git clone --depth 1 --branch ${targetBranch} ${gitHubUrl} ${tempGitDir}`
177-
);
170+
const { tempGitDir } = await cloneRepository({
171+
lang,
172+
githubToken: process.env.GITHUB_TOKEN,
173+
tempDir: process.env.RUNNER_TEMP!,
174+
});
178175

176+
const clientPath = toAbsolutePath(getLanguageFolder(lang));
179177
await run(`cp -r ${clientPath}/ ${tempGitDir}`);
178+
180179
await configureGitHubAuthor(tempGitDir);
181180
await run(`git add .`, { cwd: tempGitDir });
182181

183182
const { next, dateStamp } = versionsToRelease[lang];
184183

185184
if (willReleaseLibrary(lang)) {
186-
await execa('git', ['commit', '-m', `chore: release ${next}`], {
185+
await gitCommit({
186+
message: `chore: release ${next}`,
187187
cwd: tempGitDir,
188188
});
189189
if (process.env.VERSION_TAG_ON_RELEASE === 'true') {
190190
await execa('git', ['tag', `v${next}`], { cwd: tempGitDir });
191191
await run(`git push --tags`, { cwd: tempGitDir });
192192
}
193193
} else {
194-
await execa('git', ['commit', '-m', `chore: update repo ${dateStamp}`], {
194+
await gitCommit({
195+
message: `chore: update repo ${dateStamp}`,
195196
cwd: tempGitDir,
196197
});
197198
}
198199
await run(`git push`, { cwd: tempGitDir });
199200
}
200201

201202
// Commit and push from the monorepo level.
202-
await execa('git', ['commit', '-m', `chore: release ${getDateStamp()}`]);
203+
await gitCommit({
204+
message: `chore: release ${getDateStamp()}`,
205+
});
203206
await run(`git push`);
204207

205208
// remove old `released` tag

0 commit comments

Comments
 (0)