| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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-09 14:42:34 +02:00
										 |  |  | import {ReleaseConfig} from '../../../release/config/index'; | 
					
						
							|  |  |  | import {fetchActiveReleaseTrains, isVersionBranch, nextBranchName} from '../../../release/versioning'; | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  | import {GithubConfig} from '../../../utils/config'; | 
					
						
							|  |  |  | import {GithubClient} from '../../../utils/git/github'; | 
					
						
							|  |  |  | import {TargetLabel} from '../config'; | 
					
						
							|  |  |  | import {InvalidTargetBranchError, InvalidTargetLabelError} from '../target-label'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import {assertActiveLtsBranch} from './lts-branch'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Gets a label configuration for the merge tooling that reflects the default Angular | 
					
						
							|  |  |  |  * organization-wide labeling and branching semantics as outlined in the specification. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU
 | 
					
						
							| 
									
										
										
										
											2020-09-09 14:42:34 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @param api Instance of an authenticated Github client. | 
					
						
							|  |  |  |  * @param githubConfig Configuration for the Github remote. Used as Git remote | 
					
						
							|  |  |  |  *   for the release train branches. | 
					
						
							|  |  |  |  * @param releaseConfig Configuration for the release packages. Used to fetch | 
					
						
							|  |  |  |  *   NPM version data when LTS version branches are validated. | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | export async function getDefaultTargetLabelConfiguration( | 
					
						
							| 
									
										
										
										
											2020-09-09 14:42:34 +02:00
										 |  |  |     api: GithubClient, githubConfig: GithubConfig, | 
					
						
							|  |  |  |     releaseConfig: ReleaseConfig): Promise<TargetLabel[]> { | 
					
						
							|  |  |  |   const repo = {owner: githubConfig.owner, name: githubConfig.name, api}; | 
					
						
							|  |  |  |   const {latest, releaseCandidate, next} = await fetchActiveReleaseTrains(repo); | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return [ | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       pattern: 'target: major', | 
					
						
							|  |  |  |       branches: () => { | 
					
						
							|  |  |  |         // If `next` is currently not designated to be a major version, we do not
 | 
					
						
							|  |  |  |         // allow merging of PRs with `target: major`.
 | 
					
						
							| 
									
										
										
										
											2020-09-09 14:42:34 +02:00
										 |  |  |         if (!next.isMajor) { | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |           throw new InvalidTargetLabelError( | 
					
						
							| 
									
										
										
										
											2020-10-10 22:02:47 +03:00
										 |  |  |               `Unable to merge pull request. The "${nextBranchName}" branch will be released as ` + | 
					
						
							|  |  |  |               'a minor version.'); | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         return [nextBranchName]; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       pattern: 'target: minor', | 
					
						
							|  |  |  |       // Changes labeled with `target: minor` are merged most commonly into the next branch
 | 
					
						
							|  |  |  |       // (i.e. `master`). In rare cases of an exceptional minor version while being already
 | 
					
						
							|  |  |  |       // on a major release train, this would need to be overridden manually.
 | 
					
						
							|  |  |  |       // TODO: Consider handling this automatically by checking if the NPM version matches
 | 
					
						
							|  |  |  |       // the last-minor. If not, then an exceptional minor might be in progress. See:
 | 
					
						
							|  |  |  |       // https://docs.google.com/document/d/197kVillDwx-RZtSVOBtPb4BBIAw0E9RT3q3v6DZkykU/edit#heading=h.h7o5pjq6yqd0
 | 
					
						
							|  |  |  |       branches: [nextBranchName], | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       pattern: 'target: patch', | 
					
						
							|  |  |  |       branches: githubTargetBranch => { | 
					
						
							|  |  |  |         // If a PR is targeting the latest active version-branch through the Github UI,
 | 
					
						
							|  |  |  |         // and is also labeled with `target: patch`, then we merge it directly into the
 | 
					
						
							|  |  |  |         // branch without doing any cherry-picking. This is useful if a PR could not be
 | 
					
						
							|  |  |  |         // applied cleanly, and a separate PR for the patch branch has been created.
 | 
					
						
							| 
									
										
										
										
											2020-09-01 10:46:41 +02:00
										 |  |  |         if (githubTargetBranch === latest.branchName) { | 
					
						
							|  |  |  |           return [latest.branchName]; | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         // Otherwise, patch changes are always merged into the next and patch branch.
 | 
					
						
							| 
									
										
										
										
											2020-09-01 10:46:41 +02:00
										 |  |  |         const branches = [nextBranchName, latest.branchName]; | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |         // Additionally, if there is a release-candidate/feature-freeze release-train
 | 
					
						
							|  |  |  |         // currently active, also merge the PR into that version-branch.
 | 
					
						
							| 
									
										
										
										
											2020-09-01 10:46:41 +02:00
										 |  |  |         if (releaseCandidate !== null) { | 
					
						
							|  |  |  |           branches.push(releaseCandidate.branchName); | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         return branches; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       pattern: 'target: rc', | 
					
						
							|  |  |  |       branches: githubTargetBranch => { | 
					
						
							|  |  |  |         // The `target: rc` label cannot be applied if there is no active feature-freeze
 | 
					
						
							|  |  |  |         // or release-candidate release train.
 | 
					
						
							| 
									
										
										
										
											2020-09-01 10:46:41 +02:00
										 |  |  |         if (releaseCandidate === null) { | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |           throw new InvalidTargetLabelError( | 
					
						
							|  |  |  |               `No active feature-freeze/release-candidate branch. ` + | 
					
						
							|  |  |  |               `Unable to merge pull request using "target: rc" label.`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // If the PR is targeting the active release-candidate/feature-freeze version branch
 | 
					
						
							|  |  |  |         // directly through the Github UI and has the `target: rc` label applied, merge it
 | 
					
						
							|  |  |  |         // only into the release candidate branch. This is useful if a PR did not apply cleanly
 | 
					
						
							|  |  |  |         // into the release-candidate/feature-freeze branch, and a separate PR has been created.
 | 
					
						
							| 
									
										
										
										
											2020-09-01 10:46:41 +02:00
										 |  |  |         if (githubTargetBranch === releaseCandidate.branchName) { | 
					
						
							|  |  |  |           return [releaseCandidate.branchName]; | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         // Otherwise, merge into the next and active release-candidate/feature-freeze branch.
 | 
					
						
							| 
									
										
										
										
											2020-09-01 10:46:41 +02:00
										 |  |  |         return [nextBranchName, releaseCandidate.branchName]; | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |       }, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // LTS changes are rare enough that we won't worry about cherry-picking changes into all
 | 
					
						
							|  |  |  |       // active LTS branches for PRs created against any other branch. Instead, PR authors need
 | 
					
						
							|  |  |  |       // to manually create separate PRs for desired LTS branches. Additionally, active LT branches
 | 
					
						
							|  |  |  |       // commonly diverge quickly. This makes cherry-picking not an option for LTS changes.
 | 
					
						
							|  |  |  |       pattern: 'target: lts', | 
					
						
							|  |  |  |       branches: async githubTargetBranch => { | 
					
						
							| 
									
										
										
										
											2020-09-09 14:42:34 +02:00
										 |  |  |         if (!isVersionBranch(githubTargetBranch)) { | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |           throw new InvalidTargetBranchError( | 
					
						
							|  |  |  |               `PR cannot be merged as it does not target a long-term support ` + | 
					
						
							|  |  |  |               `branch: "${githubTargetBranch}"`); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-09-01 10:46:41 +02:00
										 |  |  |         if (githubTargetBranch === latest.branchName) { | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |           throw new InvalidTargetBranchError( | 
					
						
							|  |  |  |               `PR cannot be merged with "target: lts" into patch branch. ` + | 
					
						
							|  |  |  |               `Consider changing the label to "target: patch" if this is intentional.`); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-09-01 10:46:41 +02:00
										 |  |  |         if (releaseCandidate !== null && githubTargetBranch === releaseCandidate.branchName) { | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |           throw new InvalidTargetBranchError( | 
					
						
							|  |  |  |               `PR cannot be merged with "target: lts" into feature-freeze/release-candidate ` + | 
					
						
							|  |  |  |               `branch. Consider changing the label to "target: rc" if this is intentional.`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // Assert that the selected branch is an active LTS branch.
 | 
					
						
							| 
									
										
										
										
											2020-09-09 14:42:34 +02:00
										 |  |  |         await assertActiveLtsBranch(repo, releaseConfig, githubTargetBranch); | 
					
						
							| 
									
										
										
										
											2020-07-24 17:47:30 +02:00
										 |  |  |         return [githubTargetBranch]; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | } |