| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-09-03 14:54:31 -07:00
										 |  |  | import {error, info} from '../utils/console'; | 
					
						
							| 
									
										
										
										
											2020-06-05 11:03:32 +03:00
										 |  |  | import {exec} from '../utils/shelljs'; | 
					
						
							| 
									
										
										
										
											2020-05-20 14:22:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-12 09:40:37 -07:00
										 |  |  | import {parseCommitMessage} from './parse'; | 
					
						
							| 
									
										
										
										
											2020-09-03 14:54:31 -07:00
										 |  |  | import {printValidationErrors, validateCommitMessage, ValidateCommitMessageOptions} from './validate'; | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Whether the provided commit is a fixup commit.
 | 
					
						
							|  |  |  | const isNonFixup = (m: string) => !parseCommitMessage(m).isFixup; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-20 16:50:25 -07:00
										 |  |  | // Extracts commit header (first line of commit message).
 | 
					
						
							|  |  |  | const extractCommitHeader = (m: string) => parseCommitMessage(m).header; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  | /** Validate all commits in a provided git commit range. */ | 
					
						
							|  |  |  | export function validateCommitRange(range: string) { | 
					
						
							| 
									
										
										
										
											2020-09-03 14:54:31 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * A random value is used as a string to allow for a definite split point in the git log result. | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  |   const randomValueSeparator = `${Math.random()}`; | 
					
						
							| 
									
										
										
										
											2020-09-03 14:54:31 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Custom git log format that provides the commit header and body, separated as expected with the | 
					
						
							|  |  |  |    * custom separator as the trailing value. | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  |   const gitLogFormat = `%s%n%n%b${randomValueSeparator}`; | 
					
						
							| 
									
										
										
										
											2020-09-03 14:54:31 -07:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * A list of tuples containing a commit header string and the list of error messages for the | 
					
						
							|  |  |  |    * commit. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   const errors: [commitHeader: string, errors: string[]][] = []; | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Retrieve the commits in the provided range.
 | 
					
						
							| 
									
										
										
										
											2020-06-05 11:03:32 +03:00
										 |  |  |   const result = exec(`git log --reverse --format=${gitLogFormat} ${range}`); | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  |   if (result.code) { | 
					
						
							|  |  |  |     throw new Error(`Failed to get all commits in the range: \n  ${result.stderr}`); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Separate the commits from a single string into individual commits
 | 
					
						
							|  |  |  |   const commits = result.split(randomValueSeparator).map(l => l.trim()).filter(line => !!line); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 14:22:29 -07:00
										 |  |  |   info(`Examining ${commits.length} commit(s) in the provided range: ${range}`); | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Check each commit in the commit range.  Commits are allowed to be fixup commits for other
 | 
					
						
							|  |  |  |   // commits in the provided commit range.
 | 
					
						
							|  |  |  |   const allCommitsInRangeValid = commits.every((m, i) => { | 
					
						
							|  |  |  |     const options: ValidateCommitMessageOptions = { | 
					
						
							|  |  |  |       disallowSquash: true, | 
					
						
							| 
									
										
										
										
											2020-04-20 16:50:25 -07:00
										 |  |  |       nonFixupCommitHeaders: isNonFixup(m) ? | 
					
						
							|  |  |  |           undefined : | 
					
						
							|  |  |  |           commits.slice(0, i).filter(isNonFixup).map(extractCommitHeader) | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2020-09-03 14:54:31 -07:00
										 |  |  |     const {valid, errors: localErrors, commit} = validateCommitMessage(m, options); | 
					
						
							|  |  |  |     if (localErrors.length) { | 
					
						
							|  |  |  |       errors.push([commit.header, localErrors]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return valid; | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-04-20 19:42:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  |   if (allCommitsInRangeValid) { | 
					
						
							| 
									
										
										
										
											2020-05-20 14:22:29 -07:00
										 |  |  |     info('√  All commit messages in range valid.'); | 
					
						
							| 
									
										
										
										
											2020-04-20 19:42:09 +02:00
										 |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2020-09-03 14:54:31 -07:00
										 |  |  |     error('✘  Invalid commit message'); | 
					
						
							|  |  |  |     errors.forEach(([header, validationErrors]) => { | 
					
						
							|  |  |  |       error.group(header); | 
					
						
							|  |  |  |       printValidationErrors(validationErrors); | 
					
						
							|  |  |  |       error.groupEnd(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-20 19:42:09 +02:00
										 |  |  |     // Exit with a non-zero exit code if invalid commit messages have
 | 
					
						
							|  |  |  |     // been discovered.
 | 
					
						
							|  |  |  |     process.exit(1); | 
					
						
							| 
									
										
										
										
											2020-03-20 12:24:12 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } |