| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | #!/usr/bin/env node
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | const { GitProcess } = require('dugite'); | 
					
						
							|  |  |  | const minimist = require('minimist'); | 
					
						
							| 
									
										
										
										
											2023-06-22 16:21:42 +02:00
										 |  |  | const path = require('node:path'); | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | const semver = require('semver'); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | const { ELECTRON_DIR } = require('../../lib/utils'); | 
					
						
							|  |  |  | const notesGenerator = require('./notes.js'); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-30 17:22:33 -07:00
										 |  |  | const { Octokit } = require('@octokit/rest'); | 
					
						
							|  |  |  | const octokit = new Octokit({ | 
					
						
							|  |  |  |   auth: process.env.ELECTRON_GITHUB_TOKEN | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 13:55:27 -07:00
										 |  |  | const semverify = version => version.replace(/^origin\//, '').replace(/[xy]/g, '0').replace(/-/g, '.'); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | const runGit = async (args) => { | 
					
						
							| 
									
										
										
										
											2020-08-10 10:18:08 -07:00
										 |  |  |   console.info(`Running: git ${args.join(' ')}`); | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   const response = await GitProcess.exec(args, ELECTRON_DIR); | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   if (response.exitCode !== 0) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     throw new Error(response.stderr.trim()); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   return response.stdout.trim(); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | const tagIsSupported = tag => tag && !tag.includes('nightly') && !tag.includes('unsupported'); | 
					
						
							| 
									
										
										
										
											2021-07-19 17:58:15 -07:00
										 |  |  | const tagIsAlpha = tag => tag && tag.includes('alpha'); | 
					
						
							| 
									
										
										
										
											2020-08-10 10:18:08 -07:00
										 |  |  | const tagIsBeta = tag => tag && tag.includes('beta'); | 
					
						
							| 
									
										
										
										
											2021-07-19 17:58:15 -07:00
										 |  |  | const tagIsStable = tag => tagIsSupported(tag) && !tagIsBeta(tag) && !tagIsAlpha(tag); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | const getTagsOf = async (point) => { | 
					
						
							| 
									
										
										
										
											2020-08-10 10:18:08 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     const tags = await runGit(['tag', '--merged', point]); | 
					
						
							|  |  |  |     return tags.split('\n') | 
					
						
							|  |  |  |       .map(tag => tag.trim()) | 
					
						
							|  |  |  |       .filter(tag => semver.valid(tag)) | 
					
						
							|  |  |  |       .sort(semver.compare); | 
					
						
							|  |  |  |   } catch (err) { | 
					
						
							|  |  |  |     console.error(`Failed to fetch tags for point ${point}`); | 
					
						
							|  |  |  |     throw err; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | const getTagsOnBranch = async (point) => { | 
					
						
							| 
									
										
										
										
											2021-05-30 17:22:33 -07:00
										 |  |  |   const { data: { default_branch: defaultBranch } } = await octokit.repos.get({ | 
					
						
							|  |  |  |     owner: 'electron', | 
					
						
							|  |  |  |     repo: 'electron' | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   const mainTags = await getTagsOf(defaultBranch); | 
					
						
							|  |  |  |   if (point === defaultBranch) { | 
					
						
							| 
									
										
										
										
											2021-05-28 12:14:50 -07:00
										 |  |  |     return mainTags; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-28 12:14:50 -07:00
										 |  |  |   const mainTagsSet = new Set(mainTags); | 
					
						
							|  |  |  |   return (await getTagsOf(point)).filter(tag => !mainTagsSet.has(tag)); | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | const getBranchOf = async (point) => { | 
					
						
							| 
									
										
										
										
											2020-08-10 10:18:08 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     const branches = (await runGit(['branch', '-a', '--contains', point])) | 
					
						
							|  |  |  |       .split('\n') | 
					
						
							|  |  |  |       .map(branch => branch.trim()) | 
					
						
							|  |  |  |       .filter(branch => !!branch); | 
					
						
							|  |  |  |     const current = branches.find(branch => branch.startsWith('* ')); | 
					
						
							|  |  |  |     return current ? current.slice(2) : branches.shift(); | 
					
						
							|  |  |  |   } catch (err) { | 
					
						
							|  |  |  |     console.error(`Failed to fetch branch for ${point}: `, err); | 
					
						
							|  |  |  |     throw err; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | const getAllBranches = async () => { | 
					
						
							| 
									
										
										
										
											2020-08-10 10:18:08 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     const branches = await runGit(['branch', '--remote']); | 
					
						
							|  |  |  |     return branches.split('\n') | 
					
						
							|  |  |  |       .map(branch => branch.trim()) | 
					
						
							|  |  |  |       .filter(branch => !!branch) | 
					
						
							| 
									
										
										
										
											2023-01-12 18:05:26 +01:00
										 |  |  |       .filter(branch => branch !== 'origin/HEAD -> origin/main') | 
					
						
							| 
									
										
										
										
											2020-08-10 10:18:08 -07:00
										 |  |  |       .sort(); | 
					
						
							|  |  |  |   } catch (err) { | 
					
						
							|  |  |  |     console.error('Failed to fetch all branches'); | 
					
						
							|  |  |  |     throw err; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | const getStabilizationBranches = async () => { | 
					
						
							| 
									
										
										
										
											2023-01-12 18:05:26 +01:00
										 |  |  |   return (await getAllBranches()).filter(branch => /^origin\/\d+-x-y$/.test(branch)); | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | const getPreviousStabilizationBranch = async (current) => { | 
					
						
							|  |  |  |   const stabilizationBranches = (await getStabilizationBranches()) | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     .filter(branch => branch !== current && branch !== `origin/${current}`); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   if (!semver.valid(current)) { | 
					
						
							|  |  |  |     // since we don't seem to be on a stabilization branch right now,
 | 
					
						
							|  |  |  |     // pick a placeholder name that will yield the newest branch
 | 
					
						
							|  |  |  |     // as a comparison point.
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     current = 'v999.999.999'; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   let newestMatch = null; | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   for (const branch of stabilizationBranches) { | 
					
						
							|  |  |  |     if (semver.gte(semverify(branch), semverify(current))) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |       continue; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |     if (newestMatch && semver.lte(semverify(branch), semverify(newestMatch))) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |       continue; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     newestMatch = branch; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   return newestMatch; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | const getPreviousPoint = async (point) => { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   const currentBranch = await getBranchOf(point); | 
					
						
							|  |  |  |   const currentTag = (await getTagsOf(point)).filter(tag => tagIsSupported(tag)).pop(); | 
					
						
							|  |  |  |   const currentIsStable = tagIsStable(currentTag); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   try { | 
					
						
							|  |  |  |     // First see if there's an earlier tag on the same branch
 | 
					
						
							|  |  |  |     // that can serve as a reference point.
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     let tags = (await getTagsOnBranch(`${point}^`)).filter(tag => tagIsSupported(tag)); | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |     if (currentIsStable) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |       tags = tags.filter(tag => tagIsStable(tag)); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |     if (tags.length) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |       return tags.pop(); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   } catch (error) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     console.log('error', error); | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-16 03:46:11 -04:00
										 |  |  |   // Otherwise, use the newest stable release that precedes this branch.
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   // To reach that you may have to walk past >1 branch, e.g. to get past
 | 
					
						
							|  |  |  |   // 2-1-x which never had a stable release.
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   let branch = currentBranch; | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   while (branch) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     const prevBranch = await getPreviousStabilizationBranch(branch); | 
					
						
							|  |  |  |     const tags = (await getTagsOnBranch(prevBranch)).filter(tag => tagIsStable(tag)); | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |     if (tags.length) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |       return tags.pop(); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     branch = prevBranch; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-06 00:51:10 -05:00
										 |  |  | async function getReleaseNotes (range, newVersion, unique) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   const rangeList = range.split('..') || ['HEAD']; | 
					
						
							|  |  |  |   const to = rangeList.pop(); | 
					
						
							|  |  |  |   const from = rangeList.pop() || (await getPreviousPoint(to)); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-10 14:01:38 -06:00
										 |  |  |   if (!newVersion) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     newVersion = to; | 
					
						
							| 
									
										
										
										
											2019-01-10 14:01:38 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   const notes = await notesGenerator.get(from, to, newVersion); | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   const ret = { | 
					
						
							| 
									
										
										
										
											2022-06-06 00:51:10 -05:00
										 |  |  |     text: notesGenerator.render(notes, unique) | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   if (notes.unknown.length) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     ret.warning = `You have ${notes.unknown.length} unknown release notes. Please fix them before releasing.`; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   return ret; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-21 18:13:19 +10:00
										 |  |  | async function main () { | 
					
						
							| 
									
										
										
										
											2019-01-10 14:01:38 -06:00
										 |  |  |   const opts = minimist(process.argv.slice(2), { | 
					
						
							| 
									
										
										
										
											2022-06-06 00:51:10 -05:00
										 |  |  |     boolean: ['help', 'unique'], | 
					
						
							| 
									
										
										
										
											2020-03-20 08:12:18 -07:00
										 |  |  |     string: ['version'] | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  |   opts.range = opts._.shift(); | 
					
						
							| 
									
										
										
										
											2019-01-10 14:01:38 -06:00
										 |  |  |   if (opts.help || !opts.range) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     const name = path.basename(process.argv[1]); | 
					
						
							| 
									
										
										
										
											2019-01-10 14:01:38 -06:00
										 |  |  |     console.log(`
 | 
					
						
							|  |  |  | easy usage: ${name} version | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-06 00:51:10 -05:00
										 |  |  | full usage: ${name} [begin..]end [--version version] [--unique] | 
					
						
							| 
									
										
										
										
											2019-01-10 14:01:38 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |  * 'begin' and 'end' are two git references -- tags, branches, etc -- | 
					
						
							|  |  |  |    from which the release notes are generated. | 
					
						
							|  |  |  |  * if omitted, 'begin' defaults to the previous tag in end's branch. | 
					
						
							|  |  |  |  * if omitted, 'version' defaults to 'end'. Specifying a version is | 
					
						
							|  |  |  |    useful if you're making notes on a new version that isn't tagged yet. | 
					
						
							| 
									
										
										
										
											2022-06-06 00:51:10 -05:00
										 |  |  |  * '--unique' omits changes that also landed in other branches. | 
					
						
							| 
									
										
										
										
											2019-01-10 14:01:38 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | For example, these invocations are equivalent: | 
					
						
							|  |  |  |   ${process.argv[1]} v4.0.1 | 
					
						
							|  |  |  |   ${process.argv[1]} v4.0.0..v4.0.1 --version v4.0.1 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | `);
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-06 00:51:10 -05:00
										 |  |  |   const notes = await getReleaseNotes(opts.range, opts.version, opts.unique); | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |   console.log(notes.text); | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  |   if (notes.warning) { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     throw new Error(notes.warning); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-12 15:55:36 +08:00
										 |  |  | if (require.main === module) { | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  |   main().catch((err) => { | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  |     console.error('Error Occurred:', err); | 
					
						
							|  |  |  |     process.exit(1); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-06-21 18:06:23 +10:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-11-06 14:06:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 13:28:31 -07:00
										 |  |  | module.exports = getReleaseNotes; |