|
| 1 | +/** |
| 2 | + * @module generateDescription |
| 3 | + * @description A gitStream plugin to auto-generate pull request descriptions based on commit messages and other criteria. |
| 4 | + * @param {Object} branch - The current branch object. |
| 5 | + * @param {Object} pr - The pull request object. |
| 6 | + * @param {Object} repo - The repository object. |
| 7 | + * @param {Object} source - The source object containing diff information. |
| 8 | + * @param {function} callback - The callback function. |
| 9 | + * @returns {Object} Returns the generated PR description. |
| 10 | + * @example {{ branch | generateDescription(pr, repo, source) }} |
| 11 | + * @license MIT |
| 12 | +**/ |
| 13 | + |
| 14 | + |
| 15 | +// Parse commit messages |
| 16 | +function parseCommitMessages(messages) { |
| 17 | + const commitTypes = { |
| 18 | + feat: [], |
| 19 | + fix: [], |
| 20 | + chore: [], |
| 21 | + docs: [], |
| 22 | + style: [], |
| 23 | + refactor: [], |
| 24 | + perf: [], |
| 25 | + test: [], |
| 26 | + build: [], |
| 27 | + ci: [], |
| 28 | + other: [], |
| 29 | + }; |
| 30 | + |
| 31 | + messages |
| 32 | + .filter((message) => !message.includes("Merge branch")) |
| 33 | + .forEach((message) => { |
| 34 | + const match = message.match( |
| 35 | + /^(feat|fix|chore|docs|style|refactor|perf|test|build|ci):/, |
| 36 | + ); |
| 37 | + if (match) { |
| 38 | + commitTypes[match[1]].push(message.replace(`${match[1]}:`, "").trim()); |
| 39 | + } else { |
| 40 | + commitTypes.other.push(message); |
| 41 | + } |
| 42 | + }); |
| 43 | + |
| 44 | + return commitTypes; |
| 45 | +} |
| 46 | + |
| 47 | +// Format commit section |
| 48 | +function formatCommitSection(type, commits) { |
| 49 | + return commits.length |
| 50 | + ? `> - **${type}:**\n${commits.map((msg) => `> - ${msg}`).join("\n")}\n` |
| 51 | + : ""; |
| 52 | +} |
| 53 | + |
| 54 | +function containsNewTests(files) { |
| 55 | + const testPattern = /(test_|spec_|__tests__|_test|_tests|\.test|\.spec)/i; |
| 56 | + const testDirectoryPattern = /[\\/]?(tests|test|__tests__)[\\/]/i; |
| 57 | + const testKeywords = /describe\(|it\(|test\(|expect\(/i; // Common test keywords for JavaScript |
| 58 | + |
| 59 | + for (const file of files) { |
| 60 | + const { new_file, diff, new_content } = file; |
| 61 | + |
| 62 | + // Check if the filename indicates it's a test file |
| 63 | + if (testPattern.test(new_file) || testDirectoryPattern.test(new_file)) { |
| 64 | + return true; |
| 65 | + } |
| 66 | + |
| 67 | + // Check if the diff or new content contains test-related code |
| 68 | + if (testKeywords.test(diff) || testKeywords.test(new_content)) { |
| 69 | + return true; |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + return false; |
| 74 | +} |
| 75 | + |
| 76 | +function extractUserAdditions(description) { |
| 77 | + const match = description.match( |
| 78 | + /<!--- user additions start --->([\s\S]*?)<!--- user additions end --->/, |
| 79 | + ); |
| 80 | + return match ? match[1].trim() : description.trim(); |
| 81 | +} |
| 82 | + |
| 83 | +// Generate PR description |
| 84 | +async function generateDescription(branch, pr, repo, source, callback) { |
| 85 | + if (process.env[__filename]) { |
| 86 | + return callback(null, process.env[__filename]); |
| 87 | + } |
| 88 | + |
| 89 | + const commitTypes = parseCommitMessages(branch.commits.messages); |
| 90 | + |
| 91 | + const addTests = containsNewTests(source.diff.files) ? "X" : " "; |
| 92 | + const codeApproved = pr.approvals > 0 ? "X" : " "; |
| 93 | + |
| 94 | + const changes = Object.entries(commitTypes) |
| 95 | + .map(([type, commits]) => formatCommitSection(type, commits)) |
| 96 | + .join(""); |
| 97 | + const changesWithoutLastBr = changes.slice(0, -1); |
| 98 | + const userAdditions = extractUserAdditions(pr.description); |
| 99 | + |
| 100 | + const result = ` |
| 101 | +<!--- user additions start ---> |
| 102 | +${userAdditions} |
| 103 | +<!--- user additions end ---> |
| 104 | +
|
| 105 | +
|
| 106 | +**PR description below is managed by gitStream** |
| 107 | +<!--- Auto-generated by gitStream---> |
| 108 | +> #### Commits Summary |
| 109 | +> This pull request includes the following changes: |
| 110 | +${changesWithoutLastBr} |
| 111 | +> #### Checklist |
| 112 | +> - [${addTests}] Add tests |
| 113 | +> - [${codeApproved}] Code Reviewed and approved |
| 114 | +<!--- Auto-generated by gitStream end ---> |
| 115 | +`; |
| 116 | + |
| 117 | + process.env[__filename] = result.split("\n").join("\n "); |
| 118 | + return callback(null, process.env[__filename]); |
| 119 | +} |
| 120 | + |
| 121 | +module.exports = { filter: generateDescription, async: true }; |
0 commit comments