Skip to content

Commit fdadb6b

Browse files
committed
feat: add release branches
1 parent de0d75a commit fdadb6b

4 files changed

Lines changed: 118 additions & 41 deletions

File tree

lib/git/changelog.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import GitRelease from "#core/api/git/release";
21
import ejs from "#core/ejs";
32
import Markdown from "#core/markdown";
3+
import SemanticVersion from "#core/semantic-version";
44
import { resolve } from "#core/utils";
55

66
const TITLES = {
@@ -181,14 +181,16 @@ export default class GitChangelog {
181181
}
182182

183183
getNextVersion ( preReleaseTag ) {
184-
const previousRelease = this.previousRelease || GitRelease.initialVersion;
184+
const previousRelease = this.previousRelease || SemanticVersion.initialVersion;
185185

186186
try {
187-
const nextVersion = previousRelease.increment( this.hasBreakingChanges
188-
? "major"
189-
: this.#changes.hasFeatureChanges
190-
? "minor"
191-
: "patch", {
187+
const nextVersion = previousRelease.increment( previousRelease.isInitialVersion
188+
? "patch"
189+
: this.hasBreakingChanges
190+
? "major"
191+
: this.#changes.hasFeatureChanges
192+
? "minor"
193+
: "patch", {
192194
preReleaseTag,
193195
"defaultPreReleaseTag": "alpha",
194196
} );

lib/package/release.js

Lines changed: 108 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ export default class Publish {
2020
#repeatOnError;
2121

2222
#currentRelease;
23+
#isMajorRelease;
24+
#currentBranch;
25+
#previousBranch;
2326
#changelog;
2427
#changelogMarkdown;
2528
#changelogText;
@@ -68,30 +71,35 @@ export default class Publish {
6871
if ( !res.ok ) return res;
6972
const status = res.data;
7073

74+
this.#previousBranch = `v${ this.#changelog.previousRelease?.majorNumber || "0" }.branch`;
75+
7176
// check branch
7277
if ( !status.head.isBranchHead ) {
7378
return result( [ 500, "Release on tbe detached head is not possible" ] );
7479
}
75-
76-
if ( this.#pkg.cliConfig.release.versionBranch ) {
77-
if ( !SemanticVersion.isValid( status.head.branch ) ) {
78-
return result( [ 500, "Release on not version branch is not possible" ] );
80+
else {
81+
if ( status.head.branch !== this.#previousBranch ) {
82+
return result( [ 500, `You should be on the release branch "${ this.#previousBranch }" to make this release` ] );
7983
}
8084
}
81-
else if ( status.head.branch !== this.#pkg.cliConfig.release.releaseBranch ) {
82-
return result( [ 500, `Release on not "${ this.#pkg.cliConfig.release.releaseBranch }" branch is not possible` ] );
83-
}
8485

8586
// check for uncommited changes
8687
if ( status.isDirty ) return result( [ 500, "working copy or sub-repositories has uncommited changes or untracked files" ] );
8788

88-
// define new version
89+
// create new version
8990
res = this.#changelog.getNextVersion( this.#stable
9091
? false
9192
: this.#preReleaseTag );
9293
if ( !res.ok ) return res;
9394
this.#currentRelease = res.data;
9495

96+
this.#currentBranch = `v${ this.#currentRelease.majorNumber }.branch`;
97+
98+
// detect major release
99+
if ( this.#currentRelease.isMajor && this.#changelog.previousRelease && this.#currentRelease.majorNumber !== this.#changelog.previousRelease.majorNumber ) {
100+
this.#isMajorRelease = true;
101+
}
102+
95103
// check version can be released
96104
res = status.releases.canRelease( this.#currentRelease );
97105
if ( !res.ok ) return res;
@@ -148,6 +156,10 @@ export default class Publish {
148156

149157
console.log( `New version: ${ ansi.underline( this.#currentRelease.versionString ) }, tags: ${ this.#createTagsText() || "-" }` );
150158
console.log( `Previous version: ${ this.#changelog.previousRelease?.versionString || "-" }` );
159+
160+
if ( this.#isMajorRelease ) {
161+
console.log( `New release branch "${ this.#currentBranch }" will be created` );
162+
}
151163
console.log();
152164

153165
if ( subPackages.length ) {
@@ -193,6 +205,27 @@ export default class Publish {
193205
}
194206
}
195207

208+
// major release
209+
if ( this.#isMajorRelease ) {
210+
211+
// create and switch major release branch
212+
res = await this.#pkg.git.exec( [ "switch", "--create", this.#currentBranch ] );
213+
if ( !res.ok ) return res;
214+
215+
// move previous release branch head
216+
if ( this.#changelog.previousRelease ) {
217+
res = await this.#pkg.git.exec( [
218+
219+
//
220+
"branch",
221+
"--force",
222+
this.#previousBranch,
223+
this.#changelog.previousRelease.versionString,
224+
] );
225+
if ( !res.ok ) return res;
226+
}
227+
}
228+
196229
// update documentation
197230
res = await this.#updateDocs();
198231
if ( !res.ok ) return res;
@@ -238,37 +271,85 @@ export default class Publish {
238271

239272
// set version tag
240273
res = await this.#setTag( this.#currentRelease.versionString, {
274+
"force": false,
241275
"annotation": `Release ${ this.#currentRelease.versionString }\n\n${ this.#changelogText }\n`,
242276
} );
243277
if ( !res.ok ) return res;
244278

245-
// set "latest" tag
246-
res = await this.#setTag( this.#latestTag, { "force": true } );
247-
if ( !res.ok ) return res;
248-
249-
// set next tag
250-
res = await this.#setTag( this.#nextTag, { "force": true } );
251-
if ( !res.ok ) return res;
279+
// set tags
280+
for ( const tag of [ this.#latestTag, this.#nextTag, this.#majorTag, this.#majorLatestTag, this.#majorNextTag ] ) {
281+
if ( !tag ) continue;
252282

253-
// set major tag
254-
res = await this.#setTag( this.#majorTag, { "force": true } );
255-
if ( !res.ok ) return res;
256-
257-
// set major.latest tag
258-
res = await this.#setTag( this.#majorLatestTag, { "force": true } );
259-
if ( !res.ok ) return res;
283+
res = await this.#setTag( tag, {
284+
"force": true,
285+
"annotation": this.#pkg.createReleaseTagAnnotation( tag ),
286+
} );
260287

261-
// set major.next tag
262-
res = await this.#setTag( this.#majorNextTag, { "force": true } );
263-
if ( !res.ok ) return res;
288+
if ( !res.ok ) return res;
289+
}
264290

265291
// push, if has upstream
266292
if ( this.#pkg.git.upstream ) {
293+
294+
// track current branch
295+
if ( this.#isMajorRelease ) {
296+
res = await repeatAction(
297+
async () => {
298+
process.stdout.write( `Setting upstream for branch "${ this.#currentBranch }" ... ` );
299+
300+
const params = [
301+
302+
//
303+
"push",
304+
"--set-upstream",
305+
"origin",
306+
this.#currentBranch,
307+
];
308+
309+
const res = await this.#pkg.git.exec( params );
310+
311+
console.log( res + "" );
312+
313+
// repeat
314+
if ( !res.ok ) {
315+
throw res;
316+
}
317+
else {
318+
return res;
319+
}
320+
},
321+
{
322+
"repeatOnError": this.#repeatOnError,
323+
}
324+
);
325+
if ( !res.ok ) return res;
326+
}
327+
328+
// push references
267329
res = await repeatAction(
268330
async () => {
269331
process.stdout.write( "Pushing ... " );
270332

271-
const params = [ "push", "--atomic", "--force", "origin", status.head.branch, this.#currentRelease.versionString, this.#latestTag, this.#nextTag, this.#majorTag, this.#majorLatestTag, this.#majorNextTag ].filter( param => param != null && param !== "" );
333+
const params = [
334+
335+
//
336+
"push",
337+
"--atomic",
338+
"--force",
339+
"origin",
340+
...new Set( [
341+
342+
//
343+
this.#currentBranch,
344+
this.#previousBranch,
345+
this.#currentRelease.versionString,
346+
this.#latestTag,
347+
this.#nextTag,
348+
this.#majorTag,
349+
this.#majorLatestTag,
350+
this.#majorNextTag,
351+
].filter( param => param != null && param !== "" ) ),
352+
];
272353

273354
const res = await this.#pkg.git.exec( params );
274355

@@ -503,7 +584,7 @@ ${ this.#changelog.linkifyMarkdown( changelogMarkdown ) }
503584
`.trim() + "\n";
504585

505586
// append CHANGELOG.md
506-
if ( !this.#currentRelease.isMajor ) {
587+
if ( !this.#isMajorRelease ) {
507588
if ( await pathExists( this.#pkg.root + "/CHANGELOG.md" ) ) {
508589
fullChangelog +=
509590
fs
@@ -541,8 +622,6 @@ ${ this.#changelog.linkifyMarkdown( changelogMarkdown ) }
541622
async #setTag ( tag, { annotation, force } = {} ) {
542623
if ( !tag ) return result( 200 );
543624

544-
annotation ||= this.#pkg.createReleaseTagAnnotation( tag );
545-
546625
return repeatAction(
547626
async () => {
548627
process.stdout.write( `Adding "${ tag }" tag ... ` );

resources/cli.config.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ meta:
5353
release:
5454
enabled: false
5555
allowMajorTag: false
56-
versionBranch: false
57-
releaseBranch: main
5856

5957
docker:
6058
composeFile: compose.yaml

resources/schemas/cli.config.schema.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,8 @@ properties:
110110
properties:
111111
enabled: { type: boolean }
112112
allowMajorTag: { type: boolean }
113-
versionBranch: { type: boolean }
114-
releaseBranch: { type: string }
115113
additionalProperties: false
116-
required: [enabled, allowMajorTag, versionBranch, releaseBranch]
114+
required: [enabled, allowMajorTag]
117115

118116
docker:
119117
type: object

0 commit comments

Comments
 (0)