@@ -230,6 +230,101 @@ public void PrepareRelease(string projectDirectory, string releaseUnstableTag =
230230 }
231231 }
232232
233+ /// <summary>
234+ /// Simulates the prepare-release operation and returns the versions that would be set without making any changes.
235+ /// </summary>
236+ /// <param name="projectDirectory">
237+ /// The path to the directory that may (or its ancestors may) define the version file.
238+ /// </param>
239+ /// <param name="releaseUnstableTag">
240+ /// An optional prerelease tag to apply on the release branch.
241+ /// If not specified, any existing prerelease tag will be removed from the release.
242+ /// The preceding hyphen may be omitted.
243+ /// </param>
244+ /// <param name="nextVersion">
245+ /// The version to use for the next release.
246+ /// If not specified, the next version will be determined automatically by incrementing the current
247+ /// version based on the current version and the <paramref name="versionIncrement"/> setting in <c>version.json</c>.
248+ /// Parameter will be ignored if the current branch is a release branch.
249+ /// </param>
250+ /// <param name="versionIncrement">
251+ /// The increment to apply in order to determine the next version on the current branch.
252+ /// If specified, value will be used instead of the increment specified in <c>version.json</c>.
253+ /// Parameter will be ignored if the current branch is a release branch.
254+ /// </param>
255+ /// <param name="outputMode">
256+ /// The output format to use for writing to stdout.
257+ /// </param>
258+ /// <returns>
259+ /// A <see cref="ReleaseInfo"/> object containing information about the simulated release.
260+ /// </returns>
261+ public ReleaseInfo SimulatePrepareRelease ( string projectDirectory , string releaseUnstableTag = null , Version nextVersion = null , VersionOptions . ReleaseVersionIncrement ? versionIncrement = null , ReleaseManagerOutputMode outputMode = default )
262+ {
263+ Requires . NotNull ( projectDirectory , nameof ( projectDirectory ) ) ;
264+
265+ // open the git repository
266+ LibGit2Context context = this . GetRepository ( projectDirectory ) ;
267+ Repository repository = context . Repository ;
268+
269+ if ( repository . Info . IsHeadDetached )
270+ {
271+ this . stderr . WriteLine ( "Detached head. Check out a branch first." ) ;
272+ throw new ReleasePreparationException ( ReleasePreparationError . DetachedHead ) ;
273+ }
274+
275+ // get the current version
276+ VersionOptions versionOptions = context . VersionFile . GetVersion ( ) ;
277+ if ( versionOptions is null )
278+ {
279+ this . stderr . WriteLine ( $ "Failed to load version file for directory '{ projectDirectory } '.") ;
280+ throw new ReleasePreparationException ( ReleasePreparationError . NoVersionFile ) ;
281+ }
282+
283+ string releaseBranchName = this . GetReleaseBranchName ( versionOptions ) ;
284+ string originalBranchName = repository . Head . FriendlyName ;
285+ SemanticVersion releaseVersion = string . IsNullOrEmpty ( releaseUnstableTag )
286+ ? versionOptions . Version . WithoutPrepreleaseTags ( )
287+ : versionOptions . Version . SetFirstPrereleaseTag ( releaseUnstableTag ) ;
288+
289+ // check if the current branch is the release branch
290+ if ( string . Equals ( originalBranchName , releaseBranchName , StringComparison . OrdinalIgnoreCase ) )
291+ {
292+ if ( outputMode == ReleaseManagerOutputMode . Text )
293+ {
294+ this . stdout . WriteLine ( $ "What-if: { releaseBranchName } branch would be advanced from { versionOptions . Version } to { releaseVersion } .") ;
295+ }
296+
297+ return new ReleaseInfo ( new ReleaseBranchInfo ( releaseBranchName , repository . Head . Tip . Id . ToString ( ) , releaseVersion ) ) ;
298+ }
299+
300+ SemanticVersion nextDevVersion = this . GetNextDevVersion ( versionOptions , nextVersion , versionIncrement ) ;
301+
302+ // check if the current version on the current branch is different from the next version
303+ // otherwise, both the release branch and the dev branch would have the same version
304+ if ( versionOptions . Version . Version == nextDevVersion . Version )
305+ {
306+ this . stderr . WriteLine ( $ "Version on '{ originalBranchName } ' is already set to next version { nextDevVersion . Version } .") ;
307+ throw new ReleasePreparationException ( ReleasePreparationError . NoVersionIncrement ) ;
308+ }
309+
310+ // check if the release branch already exists
311+ if ( repository . Branches [ releaseBranchName ] is not null )
312+ {
313+ this . stderr . WriteLine ( $ "Cannot create branch '{ releaseBranchName } ' because it already exists.") ;
314+ throw new ReleasePreparationException ( ReleasePreparationError . BranchAlreadyExists ) ;
315+ }
316+
317+ if ( outputMode == ReleaseManagerOutputMode . Text )
318+ {
319+ this . stdout . WriteLine ( $ "What-if: { releaseBranchName } branch would track v{ releaseVersion } stabilization and release.") ;
320+ this . stdout . WriteLine ( $ "What-if: { originalBranchName } branch would track v{ nextDevVersion } development.") ;
321+ }
322+
323+ var originalBranchInfo = new ReleaseBranchInfo ( originalBranchName , repository . Head . Tip . Sha , nextDevVersion ) ;
324+ var releaseBranchInfo = new ReleaseBranchInfo ( releaseBranchName , repository . Head . Tip . Id . ToString ( ) , releaseVersion ) ;
325+ return new ReleaseInfo ( originalBranchInfo , releaseBranchInfo ) ;
326+ }
327+
233328 private static bool IsVersionDecrement ( SemanticVersion oldVersion , SemanticVersion newVersion )
234329 {
235330 if ( newVersion . Version > oldVersion . Version )
@@ -384,7 +479,7 @@ private SemanticVersion GetNextDevVersion(VersionOptions versionOptions, Version
384479 return nextDevVersion . SetFirstPrereleaseTag ( versionOptions . ReleaseOrDefault . FirstUnstableTagOrDefault ) ;
385480 }
386481
387- private void WriteToOutput ( ReleaseInfo releaseInfo )
482+ public void WriteToOutput ( ReleaseInfo releaseInfo )
388483 {
389484 string json = JsonConvert . SerializeObject ( releaseInfo , Formatting . Indented , new SemanticVersionJsonConverter ( ) ) ;
390485 this . stdout . WriteLine ( json ) ;
0 commit comments