mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-21 09:38:01 +02:00
Tools: Include packages that have been updated by Renovate in changelog
This commit is contained in:
parent
99c6c9b411
commit
d871b3c7d6
@ -846,6 +846,7 @@ packages/tools/convertThemesToCss.js
|
||||
packages/tools/generate-database-types.js
|
||||
packages/tools/generate-images.js
|
||||
packages/tools/git-changelog.js
|
||||
packages/tools/git-changelog.test.js
|
||||
packages/tools/licenseChecker.js
|
||||
packages/tools/release-android.js
|
||||
packages/tools/release-cli.js
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -834,6 +834,7 @@ packages/tools/convertThemesToCss.js
|
||||
packages/tools/generate-database-types.js
|
||||
packages/tools/generate-images.js
|
||||
packages/tools/git-changelog.js
|
||||
packages/tools/git-changelog.test.js
|
||||
packages/tools/licenseChecker.js
|
||||
packages/tools/release-android.js
|
||||
packages/tools/release-cli.js
|
||||
|
@ -71,7 +71,7 @@ async function main() {
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
|
||||
// eslint-disable-next-line promise/prefer-await-to-then
|
||||
main().catch((error) => {
|
||||
console.error('Fatal error');
|
||||
console.error(error);
|
||||
|
84
packages/tools/git-changelog.test.ts
Normal file
84
packages/tools/git-changelog.test.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { expectThrow } from '@joplin/lib/testing/test-utils';
|
||||
import { filesApplyToPlatform, parseRenovateMessage, RenovateMessage, summarizeRenovateMessages } from './git-changelog';
|
||||
|
||||
describe('git-changelog', () => {
|
||||
|
||||
test('should find out if a file path is relevant to a platform', async () => {
|
||||
type TestCase = [string[], string, boolean];
|
||||
|
||||
const testCases: TestCase[] = [
|
||||
[['packages/app-mobile/package.json'], 'ios', true],
|
||||
[['packages/app-mobile/package.json'], 'android', true],
|
||||
[['packages/app-mobile/package.json'], 'destop', false],
|
||||
[[], 'destop', false],
|
||||
[['packages/server/package.json'], 'server', true],
|
||||
[['packages/app-mobile/package.json', 'packages/server/package.json'], 'server', true],
|
||||
[['packages/app-mobile/package.json', 'packages/server/package.json'], 'android', true],
|
||||
[['packages/app-mobile/package.json', 'packages/server/package.json'], 'desktop', false],
|
||||
[['packages/server/package.json'], 'desktop', false],
|
||||
[['packages/lib/package.json'], 'server', true],
|
||||
[['packages/lib/package.json'], 'desktop', true],
|
||||
[['packages/lib/package.json'], 'android', true],
|
||||
[['packages/lib/package.json'], 'clipper', false],
|
||||
[['packages/app-clipper/package.json'], 'clipper', true],
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const [files, platform, expected] = testCase;
|
||||
const actual = filesApplyToPlatform(files, platform);
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
||||
test('should parse Renovate messages', async () => {
|
||||
type TestCase = [string, string, string];
|
||||
|
||||
const testCases: TestCase[] = [
|
||||
['Update typescript-eslint monorepo to v5 (#7291)', 'typescript-eslint', 'v5'],
|
||||
['Update aws-sdk-js-v3 monorepo to v3.215.0', 'aws-sdk-js-v3', 'v3.215.0'],
|
||||
['Update dependency moment to v2.29.4 (#7087)', 'moment', 'v2.29.4'],
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const [message, pkg, version] = testCase;
|
||||
const actual = parseRenovateMessage(message);
|
||||
expect(actual.package).toBe(pkg);
|
||||
expect(actual.version).toBe(version);
|
||||
}
|
||||
|
||||
await expectThrow(async () => parseRenovateMessage('not a renovate message'));
|
||||
});
|
||||
|
||||
test('should summarize Renovate messages', async () => {
|
||||
type TestCase = [RenovateMessage[], string];
|
||||
|
||||
const testCases: TestCase[] = [
|
||||
[
|
||||
[
|
||||
{ package: 'sas', version: 'v1.0' },
|
||||
{ package: 'sas', version: 'v1.2' },
|
||||
{ package: 'moment', version: 'v3.4' },
|
||||
{ package: 'eslint', version: 'v1.2' },
|
||||
],
|
||||
'Updated packages moment (v3.4), sas (v1.2)',
|
||||
],
|
||||
[
|
||||
[
|
||||
{ package: 'eslint', version: 'v1.2' },
|
||||
],
|
||||
'',
|
||||
],
|
||||
[
|
||||
[],
|
||||
'',
|
||||
],
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const [messages, expected] = testCase;
|
||||
const actual = summarizeRenovateMessages(messages);
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
@ -8,6 +8,7 @@ interface LogEntry {
|
||||
message: string;
|
||||
commit: string;
|
||||
author: Author;
|
||||
files: string[];
|
||||
}
|
||||
|
||||
enum Platform {
|
||||
@ -58,6 +59,10 @@ async function gitLog(sinceTag: string) {
|
||||
const authorEmail = splitted[1];
|
||||
const authorName = splitted[2];
|
||||
const message = splitted[3].trim();
|
||||
let files: string[] = [];
|
||||
|
||||
const filesResult = await execCommand(`git diff-tree --no-commit-id --name-only ${commit} -r`);
|
||||
files = filesResult.split('\n').map(s => s.trim()).filter(s => !!s);
|
||||
|
||||
output.push({
|
||||
commit: commit,
|
||||
@ -67,6 +72,7 @@ async function gitLog(sinceTag: string) {
|
||||
name: authorName,
|
||||
login: await githubUsername(authorEmail, authorName),
|
||||
},
|
||||
files,
|
||||
});
|
||||
}
|
||||
|
||||
@ -91,6 +97,102 @@ function platformFromTag(tagName: string): Platform {
|
||||
throw new Error(`Could not determine platform from tag: "${tagName}"`);
|
||||
}
|
||||
|
||||
export const filesApplyToPlatform = (files: string[], platform: string): boolean => {
|
||||
const isMainApp = ['android', 'ios', 'desktop', 'cli', 'server'].includes(platform);
|
||||
const isMobile = ['android', 'ios'].includes(platform);
|
||||
|
||||
for (const file of files) {
|
||||
if (file.startsWith('packages/app-cli') && platform === 'cli') return true;
|
||||
if (file.startsWith('packages/app-clipper') && platform === 'clipper') return true;
|
||||
if (file.startsWith('packages/app-mobile') && isMobile) return true;
|
||||
if (file.startsWith('packages/app-desktop') && platform === 'desktop') return true;
|
||||
if (file.startsWith('packages/fork-htmlparser2') && isMainApp) return true;
|
||||
if (file.startsWith('packages/fork-uslug') && isMainApp) return true;
|
||||
if (file.startsWith('packages/htmlpack') && isMainApp) return true;
|
||||
if (file.startsWith('packages/lib') && isMainApp) return true;
|
||||
if (file.startsWith('packages/pdf-viewer') && platform === 'desktop') return true;
|
||||
if (file.startsWith('packages/react-native-') && isMobile) return true;
|
||||
if (file.startsWith('packages/renderer') && isMainApp) return true;
|
||||
if (file.startsWith('packages/server') && platform === 'server') return true;
|
||||
if (file.startsWith('packages/tools') && isMainApp) return true;
|
||||
if (file.startsWith('packages/turndown') && isMainApp) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export interface RenovateMessage {
|
||||
package: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export const parseRenovateMessage = (message: string): RenovateMessage => {
|
||||
const regexes = [
|
||||
/^Update dependency ([^\s]+) to ([^\s]+)/,
|
||||
/^Update ([^\s]+) monorepo to ([^\s]+)/,
|
||||
];
|
||||
|
||||
for (const regex of regexes) {
|
||||
const m = message.match(regex);
|
||||
|
||||
if (m) {
|
||||
return {
|
||||
package: m[1],
|
||||
version: m[2],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Not a Renovate message: ${message}`);
|
||||
};
|
||||
|
||||
export const summarizeRenovateMessages = (messages: RenovateMessage[]): string => {
|
||||
// Exclude some dev dependencies
|
||||
messages = messages.filter(m => {
|
||||
if ([
|
||||
'yeoman-generator',
|
||||
'madge',
|
||||
'lint-staged',
|
||||
'gettext-extractor',
|
||||
'gettext-extractor',
|
||||
'ts-jest',
|
||||
'ts-node',
|
||||
'typescript',
|
||||
'eslint',
|
||||
'jest',
|
||||
].includes(m.package)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m.package.startsWith('@types/')) return false;
|
||||
if (m.package.startsWith('typescript-')) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
const temp: Record<string, string> = {};
|
||||
for (const message of messages) {
|
||||
if (!temp[message.package]) {
|
||||
temp[message.package] = message.version;
|
||||
} else {
|
||||
if (message.version > temp[message.package]) {
|
||||
temp[message.package] = message.version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const temp2: string[] = [];
|
||||
for (const [pkg, version] of Object.entries(temp)) {
|
||||
temp2.push(`${pkg} (${version})`);
|
||||
}
|
||||
|
||||
temp2.sort();
|
||||
|
||||
if (temp2.length) return `Updated packages ${temp2.join(', ')}`;
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
function filterLogs(logs: LogEntry[], platform: Platform) {
|
||||
const output: LogEntry[] = [];
|
||||
const revertedLogs = [];
|
||||
@ -98,6 +200,8 @@ function filterLogs(logs: LogEntry[], platform: Platform) {
|
||||
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
||||
// let updatedTranslations = false;
|
||||
|
||||
const renovateMessages: RenovateMessage[] = [];
|
||||
|
||||
for (const log of logs) {
|
||||
|
||||
// Save to an array any commit that has been reverted, and exclude
|
||||
@ -110,9 +214,18 @@ function filterLogs(logs: LogEntry[], platform: Platform) {
|
||||
|
||||
if (revertedLogs.indexOf(log.message) >= 0) continue;
|
||||
|
||||
let isRenovate = false;
|
||||
let prefix = log.message.trim().toLowerCase().split(':');
|
||||
if (prefix.length <= 1) continue;
|
||||
if (prefix.length <= 1) {
|
||||
if (log.author && log.author.name === 'renovate[bot]') {
|
||||
prefix = ['renovate'];
|
||||
isRenovate = true;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
prefix = prefix[0].split(',').map(s => s.trim());
|
||||
}
|
||||
|
||||
let addIt = false;
|
||||
|
||||
@ -128,6 +241,11 @@ function filterLogs(logs: LogEntry[], platform: Platform) {
|
||||
if (platform === 'server' && prefix.indexOf('server') >= 0) addIt = true;
|
||||
if (platform === 'cloud' && (prefix.indexOf('cloud') >= 0 || prefix.indexOf('server') >= 0)) addIt = true;
|
||||
|
||||
if (isRenovate && filesApplyToPlatform(log.files, platform)) {
|
||||
renovateMessages.push(parseRenovateMessage(log.message));
|
||||
addIt = false;
|
||||
}
|
||||
|
||||
// Translation updates often comes in format "Translation: Update pt_PT.po"
|
||||
// but that's not useful in a changelog especially since most people
|
||||
// don't know country and language codes. So we catch all these and
|
||||
@ -148,6 +266,16 @@ function filterLogs(logs: LogEntry[], platform: Platform) {
|
||||
// Actually we don't really need this info - translations are being updated all the time
|
||||
// if (updatedTranslations) output.push({ message: 'Updated translations' });
|
||||
|
||||
const renovateSummary = summarizeRenovateMessages(renovateMessages);
|
||||
if (renovateSummary) {
|
||||
output.push({
|
||||
author: { name: '', email: '', login: '' },
|
||||
commit: '',
|
||||
files: [],
|
||||
message: renovateSummary,
|
||||
});
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@ -281,9 +409,11 @@ function formatCommitMessage(commit: string, msg: string, author: Author, option
|
||||
} else {
|
||||
const commitStrings = [commit.substr(0, 7)];
|
||||
if (authorMd) commitStrings.push(`by ${authorMd}`);
|
||||
if (commitStrings.join('').length) {
|
||||
output += ` (${commitStrings.join(' ')})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.publishFormat !== 'full') {
|
||||
output = output.replace(issueRegex, '');
|
||||
@ -379,8 +509,11 @@ async function main() {
|
||||
console.info(changelogString.join('\n'));
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
// eslint-disable-next-line promise/prefer-await-to-then
|
||||
main().catch((error) => {
|
||||
console.error('Fatal error');
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user