Skip to content

Commit

Permalink
feat(ng-dev): support exceptional minor release trains
Browse files Browse the repository at this point in the history
An exceptional minor may be cut when work already started on
a major release train. The major could still be in the `main`
branch, or already have moved into a dedicated version branch
as part of feature-freeze or release-candidate.

In such cases, if the Angular team suddenly decides that another
minor for the current "latest" major is needed. e.g. to account for
a TypeScript version range expansion- then a new version branch needs
to be created. This branch *CANNOT* be based on `main` or the rc/FF
branch because major features/breaking-changes might have landed
already.

To support this use-case though (named "exceptional minors"), we will
allow branching-off from the current patch branch. The minor should
not be directly relesed as there needs to be time to e.g. backport
minor changes already landed in `main` / or the new work needs to be
performed (e.g. the TS update). This means that the newly-created branch
will *NOT* directly become the new patch branch. Instead, the new branch
will operate similar to the feature-freeze/RC branches, allowing for
pre-releases. This is the new exceptional-minor release train.

As a workaround in the past we abused the FF/RC release train for this,
but this will not work when the major has already branched off into
RC/FF. To support this, a separate new release-train is needed.
  • Loading branch information
devversion committed Dec 21, 2022
1 parent d280147 commit 50b9d66
Show file tree
Hide file tree
Showing 18 changed files with 531 additions and 228 deletions.
8 changes: 8 additions & 0 deletions ng-dev/caretaker/check/ci.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ describe('CiModule', () => {
status: 'not found',
});
expect(data[1]).toEqual({
active: false,
name: 'exceptionalMinor',
label: '',
status: 'not found',
});
expect(data[2]).toEqual({
active: true,
name: 'latest-branch',
label: 'latest (latest-branch)',
Expand Down Expand Up @@ -120,5 +126,7 @@ function buildMockActiveReleaseTrains(withRc: boolean): ActiveReleaseTrains {
releaseCandidate: withRc ? {branchName: 'rc-branch', ...baseResult} : null,
latest: {branchName: 'latest-branch', ...baseResult},
next: {branchName: 'next-branch', ...baseResult},
// TODO: Consider testing exceptional minor status too.
exceptionalMinor: null,
});
}
6 changes: 4 additions & 2 deletions ng-dev/caretaker/check/ci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ export class CiModule extends BaseModule<CiData> {
...this.git.remoteConfig,
nextBranchName,
};
const {latest, next, releaseCandidate} = await ActiveReleaseTrains.fetch(repo);
const ciResultPromises = Object.entries({releaseCandidate, latest, next}).map(
const {latest, next, releaseCandidate, exceptionalMinor} = await ActiveReleaseTrains.fetch(
repo,
);
const ciResultPromises = Object.entries({releaseCandidate, exceptionalMinor, latest, next}).map(
async ([trainName, train]: [string, ReleaseTrain | null]) => {
if (train === null) {
return {
Expand Down
4 changes: 2 additions & 2 deletions ng-dev/pr/common/targeting/lts-branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
computeLtsEndDateOfMajor,
fetchProjectNpmPackageInfo,
getLtsNpmDistTagOfMajor,
getVersionOfBranch,
getVersionInfoForBranch,
ReleaseRepoWithApi,
} from '../../../release/versioning/index.js';
import {Prompt} from '../../../utils/prompt.js';
Expand All @@ -35,7 +35,7 @@ export async function assertActiveLtsBranch(
releaseConfig: ReleaseConfig,
branchName: string,
) {
const version = await getVersionOfBranch(repo, branchName);
const {version} = await getVersionInfoForBranch(repo, branchName);
const {'dist-tags': distTags, time} = await fetchProjectNpmPackageInfo(releaseConfig);

// LTS versions should be tagged in NPM in the following format: `v{major}-lts`.
Expand Down
4 changes: 1 addition & 3 deletions ng-dev/pr/merge/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,9 +441,7 @@ describe('default target labels', () => {
interceptBranchesListRequest(['10.3.x', '10.4.x']);

await expectAsync(getBranchesForLabel('target: patch')).toBeRejectedWithError(
'Unable to determine latest release-train. Found two consecutive ' +
'branches in feature-freeze/release-candidate phase. Did not expect both ' +
'"10.3.x" and "10.4.x" to be in feature-freeze/release-candidate mode.',
/No exceptional minors are allowed.+cannot be multiple feature-freeze\/release-candidate branches: "10.3.x"/,
);
});
});
Expand Down
3 changes: 3 additions & 0 deletions ng-dev/release/publish/test/common.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {

describe('common release action logic', () => {
const baseReleaseTrains = new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.1')),
Expand All @@ -47,6 +48,7 @@ describe('common release action logic', () => {
describe('version computation', () => {
it('should not modify release train versions and cause invalid other actions', async () => {
const testReleaseTrain = new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.3')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.1')),
Expand Down Expand Up @@ -76,6 +78,7 @@ describe('common release action logic', () => {

it('should properly show descriptions when a major is in RC-phase', async () => {
const testReleaseTrain = new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('15.0.x', parse('15.0.0-rc.1')),
next: new ReleaseTrain('main', parse('15.1.0-next.0')),
latest: new ReleaseTrain('14.3.x', parse('14.3.1')),
Expand Down
4 changes: 4 additions & 0 deletions ng-dev/release/publish/test/configure-next-as-major.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ describe('configure next as major action', () => {
expect(
await ConfigureNextAsMajorAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -29,6 +30,7 @@ describe('configure next as major action', () => {
expect(
await ConfigureNextAsMajorAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.1')),
next: new ReleaseTrain('master', parse('10.2.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -41,6 +43,7 @@ describe('configure next as major action', () => {
expect(
await ConfigureNextAsMajorAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('11.0.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -53,6 +56,7 @@ describe('configure next as major action', () => {
const action = setupReleaseActionForTesting(
ConfigureNextAsMajorAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand Down
6 changes: 6 additions & 0 deletions ng-dev/release/publish/test/cut-lts-patch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe('cut an LTS patch action', () => {
expect(
await CutLongTermSupportPatchAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -42,6 +43,7 @@ describe('cut an LTS patch action', () => {
expect(
await CutLongTermSupportPatchAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.3')),
next: new ReleaseTrain('master', parse('10.2.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -54,6 +56,7 @@ describe('cut an LTS patch action', () => {
expect(
await CutLongTermSupportPatchAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -66,6 +69,7 @@ describe('cut an LTS patch action', () => {
const action = setupReleaseActionForTesting(
CutLongTermSupportPatchAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand All @@ -85,6 +89,7 @@ describe('cut an LTS patch action', () => {
const action = setupReleaseActionForTesting(
CutLongTermSupportPatchAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand Down Expand Up @@ -127,6 +132,7 @@ describe('cut an LTS patch action', () => {
const {releaseConfig, githubConfig} = getTestConfigurationsForAction();
const gitClient = getMockGitClient(githubConfig, /* useSandboxGitClient */ false);
const activeReleaseTrains = new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand Down
5 changes: 5 additions & 0 deletions ng-dev/release/publish/test/cut-new-patch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ describe('cut new patch action', () => {
expect(
await CutNewPatchAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -34,6 +35,7 @@ describe('cut new patch action', () => {
const action = setupReleaseActionForTesting(
CutNewPatchAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand All @@ -47,6 +49,7 @@ describe('cut new patch action', () => {
const action = setupReleaseActionForTesting(
CutNewPatchAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.3')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.9')),
Expand All @@ -60,6 +63,7 @@ describe('cut new patch action', () => {
const action = setupReleaseActionForTesting(
CutNewPatchAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.9')),
Expand All @@ -73,6 +77,7 @@ describe('cut new patch action', () => {
const action = setupReleaseActionForTesting(
CutNewPatchAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.3')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand Down
7 changes: 7 additions & 0 deletions ng-dev/release/publish/test/cut-next-prerelease.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe('cut next pre-release action', () => {
const action = setupReleaseActionForTesting(
CutNextPrereleaseAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.1.x', parse('10.1.2')),
Expand All @@ -51,6 +52,7 @@ describe('cut next pre-release action', () => {
const action = setupReleaseActionForTesting(
CutNextPrereleaseAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.1.x', parse('10.1.0')),
Expand All @@ -75,6 +77,7 @@ describe('cut next pre-release action', () => {
const action = setupReleaseActionForTesting(
CutNextPrereleaseAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.1.x', parse('10.1.0')),
Expand Down Expand Up @@ -120,6 +123,7 @@ describe('cut next pre-release action', () => {
const action = setupReleaseActionForTesting(
CutNextPrereleaseAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.4')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand All @@ -133,6 +137,7 @@ describe('cut next pre-release action', () => {
const action = setupReleaseActionForTesting(
CutNextPrereleaseAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.4')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand Down Expand Up @@ -171,6 +176,7 @@ describe('cut next pre-release action', () => {
const action = setupReleaseActionForTesting(
CutNextPrereleaseAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand All @@ -184,6 +190,7 @@ describe('cut next pre-release action', () => {
const action = setupReleaseActionForTesting(
CutNextPrereleaseAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.2')),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ describe('cut release candidate for feature-freeze action', () => {
expect(
await CutReleaseCandidateForFeatureFreezeAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.1')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -34,6 +35,7 @@ describe('cut release candidate for feature-freeze action', () => {
expect(
await CutReleaseCandidateForFeatureFreezeAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
// No longer in feature-freeze but in release-candidate phase.
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
Expand All @@ -47,6 +49,7 @@ describe('cut release candidate for feature-freeze action', () => {
expect(
await CutReleaseCandidateForFeatureFreezeAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -59,6 +62,7 @@ describe('cut release candidate for feature-freeze action', () => {
const action = setupReleaseActionForTesting(
CutReleaseCandidateForFeatureFreezeAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.1')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -72,6 +76,7 @@ describe('cut release candidate for feature-freeze action', () => {
const action = setupReleaseActionForTesting(
CutReleaseCandidateForFeatureFreezeAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.1')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand Down
8 changes: 8 additions & 0 deletions ng-dev/release/publish/test/cut-stable.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('cut stable action', () => {
expect(
await CutStableAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-next.1')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -38,6 +39,7 @@ describe('cut stable action', () => {
expect(
await CutStableAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
// No longer in feature-freeze but in release-candidate phase.
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
Expand All @@ -51,6 +53,7 @@ describe('cut stable action', () => {
expect(
await CutStableAction.isActive(
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: null,
next: new ReleaseTrain('master', parse('10.1.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand All @@ -63,6 +66,7 @@ describe('cut stable action', () => {
const action = setupReleaseActionForTesting(
CutStableAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
// No longer in feature-freeze but in release-candidate phase.
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
Expand All @@ -77,6 +81,7 @@ describe('cut stable action', () => {
const action = setupReleaseActionForTesting(
CutStableAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
// No longer in feature-freeze but in release-candidate phase.
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
Expand All @@ -92,6 +97,7 @@ describe('cut stable action', () => {
const action = setupReleaseActionForTesting(
CutStableAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
// No longer in feature-freeze but in release-candidate phase.
releaseCandidate: new ReleaseTrain('11.0.x', parse('11.0.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
Expand Down Expand Up @@ -126,6 +132,7 @@ describe('cut stable action', () => {
const action = setupReleaseActionForTesting(
CutStableAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand Down Expand Up @@ -192,6 +199,7 @@ describe('cut stable action', () => {
const action = setupReleaseActionForTesting(
CutStableAction,
new ActiveReleaseTrains({
exceptionalMinor: null,
releaseCandidate: new ReleaseTrain('10.1.x', parse('10.1.0-rc.0')),
next: new ReleaseTrain('master', parse('10.2.0-next.0')),
latest: new ReleaseTrain('10.0.x', parse('10.0.3')),
Expand Down
Loading

0 comments on commit 50b9d66

Please sign in to comment.