2424using Microsoft . Extensions . DependencyInjection ;
2525using NuGet . Configuration ;
2626using Nuke . Common . CI . GitHubActions ;
27+ using Nuke . Common . Tools . GitVersion ;
2728using Nuke . Common . Tools . PowerShell ;
2829using Nuke . Common . Utilities ;
30+ using Octokit ;
2931using Rewrite . Core ;
3032using Rewrite . MSBuild ;
3133using Rewrite . RewriteXml . Tree ;
3234using Serilog ;
3335using Spectre . Console ;
3436using ReflectionMagic ;
3537using static GradleTasks ;
38+ using Commit = LibGit2Sharp . Commit ;
39+ using Credentials = LibGit2Sharp . Credentials ;
40+ using NotFoundException = LibGit2Sharp . NotFoundException ;
3641
3742// ReSharper disable UnusedMember.Local
3843[ HandleHelpRequests ( Priority = 20 ) ]
3944partial class Build : NukeBuild
4045{
46+
4147 static Build ( )
4248 {
4349 Environment . SetEnvironmentVariable ( "NUKE_TELEMETRY_OPTOUT" , "true" ) ;
@@ -87,13 +93,16 @@ FigletFont LoadFont(string fontName)
8793
8894 [ Parameter ( "Configuration to build - Default is 'Debug' (local) or 'Release' (server)" ) ]
8995 readonly Configuration Configuration = IsLocalBuild ? Configuration . Debug : Configuration . Release ;
96+ [ Parameter ( "GitHub personal access token with access to the repo" ) ] readonly string GitHubToken ;
97+
9098 AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts" ;
9199 AbsolutePath TestResultsDirectory => ArtifactsDirectory / "test-results" ;
92100 AbsolutePath TestFixturesDirectory => RootDirectory / "Rewrite" / "tests" / "fixtures" ;
93101 AbsolutePath TestFixturesFile => TestFixturesDirectory / "fixtures.txt" ;
94102 [ Solution ( GenerateProjects = true ) ] Solution Solution ;
95103 [ NerdbankGitVersioning ] NerdbankGitVersioning Version ;
96104 [ GitRepositoryExt ] LibGit2Sharp . Repository GitRepository ;
105+ AbsolutePath DotnetServerFilePath => ArtifactsDirectory / "DotnetServer.zip" ;
97106
98107 const string TargetFramework = "net9.0" ;
99108
@@ -205,9 +214,8 @@ IEnumerable<AbsolutePath> GetSubDirectories(params string[] patterns)
205214 var publishDir = Solution . src . Rewrite_Server . Directory / "bin" / "Release" / TargetFramework / "publish" ;
206215 var extension = IsWin ? ".exe" : "" ;
207216 Environment . SetEnvironmentVariable ( "ROSLYN_RECIPE_EXECUTABLE" , publishDir ) ;
208- var zipFilePath = ArtifactsDirectory / "DotnetServer.zip" ;
209- zipFilePath . DeleteFile ( ) ;
210- publishDir . ZipTo ( ArtifactsDirectory / "DotnetServer.zip" ) ;
217+ DotnetServerFilePath . DeleteFile ( ) ;
218+ publishDir . ZipTo ( DotnetServerFilePath ) ;
211219 } ) ;
212220
213221
@@ -421,7 +429,7 @@ void InjectLogsIntoTrx()
421429 } ) ;
422430
423431
424- Target SetTestOutputFlags => _ => _
432+ Target SignalIfTestcaseOutputExists => _ => _
425433 . Unlisted ( )
426434 . Description ( "Sets github actions flags if any java or c# test output files were generated" )
427435 . After ( Test , GradleAssembleAndTest )
@@ -436,20 +444,21 @@ void InjectLogsIntoTrx()
436444 [ Category ( "CI" ) ]
437445 Target CIBuild => _ => _
438446 . Description ( "Builds, tests and produces test reports for regular builds on CI" )
439- . DependsOn ( Pack , PublishServer , Test , GradleAssembleAndTest , SetTestOutputFlags ) ;
447+ . DependsOn ( Pack , PublishServer , Test , GradleAssembleAndTest , GithubRelease , SignalIfTestcaseOutputExists ) ;
440448
441449
442450
443451 [ Category ( "CI" ) ]
444452 Target CIRelease => _ => _
445453 . Description ( "Creates and publishes release artifacts to maven & nuget" )
446- . DependsOn ( Pack , PublishServer , GradlePublish , NugetPush , SetTestOutputFlags ) ;
454+ . DependsOn ( Pack , PublishServer , GradlePublish , NugetPush , SignalIfTestcaseOutputExists ) ;
447455
448456 Target CreateGithubRelease => _ => _
449457 . Unlisted ( )
450458 . DependsOn ( PublishServer )
451459 . Executes ( ( ) =>
452460 {
461+
453462 } ) ;
454463
455464 [ Category ( "Test" ) ]
@@ -533,6 +542,72 @@ GradleSettings ApplyCommonGradleSettings(GradleSettings options) => options
533542
534543 } ) ;
535544
545+ Target GithubRelease => _ => _
546+ . Description ( "Creates a GitHub release (or amends existing)" )
547+ . Requires ( ( ) => GitHubToken )
548+ . Executes ( async ( ) =>
549+ {
550+ await CreateGitHubRelease ( $ "v{ Version . SemVer1 } ") ;
551+ if ( string . Equals ( GitRepository . Head . FriendlyName , "main" , StringComparison . OrdinalIgnoreCase ) && ! GitRepository . Info . IsHeadDetached )
552+ {
553+ await CreateGitHubRelease ( $ "latest") ;
554+ }
555+ } ) ;
556+
557+ public async Task CreateGitHubRelease ( string releaseName )
558+ {
559+ var client = new GitHubClient ( new ProductHeaderValue ( "OpenRewrite" ) )
560+ {
561+ Credentials = new Octokit . Credentials ( GitHubToken , AuthenticationType . Bearer )
562+ } ;
563+ var ( owner , repoName ) = GetGitHubOwnerAndRepo ( ) ;
564+
565+ Release release ;
566+ try
567+ {
568+ release = await client . Repository . Release . Get ( owner , repoName , releaseName ) ;
569+ }
570+ catch ( Octokit . NotFoundException )
571+ {
572+ var newRelease = new NewRelease ( releaseName )
573+ {
574+ Name = releaseName ,
575+ Draft = false ,
576+ Prerelease = false
577+ } ;
578+ release = await client . Repository . Release . Create ( owner , repoName , newRelease ) ;
579+ }
580+
581+ var existingAsset = release . Assets . FirstOrDefault ( x => x . Name == DotnetServerFilePath . Name ) ;
582+ if ( existingAsset != null )
583+ {
584+ await client . Repository . Release . DeleteAsset ( owner , repoName , existingAsset . Id ) ;
585+ }
586+
587+ var stream = File . OpenRead ( DotnetServerFilePath ) ;
588+ var releaseAssetUpload = new ReleaseAssetUpload ( DotnetServerFilePath . Name , "application/zip" , stream , TimeSpan . FromHours ( 1 ) ) ;
589+ var releaseAsset = await client . Repository . Release . UploadAsset ( release , releaseAssetUpload ) ;
590+
591+ }
592+ public ( string Owner , string Repo ) GetGitHubOwnerAndRepo ( )
593+ {
594+ var originRemote = GitRepository . Network . Remotes [ "origin" ] ;
595+ if ( originRemote == null )
596+ throw new Exception ( "No origin remote" ) ;
597+
598+ var url = originRemote . Url ;
599+
600+ // Handle SSH and HTTPS GitHub URLs
601+ // Examples:
602+ // - [email protected] :owner/repo.git 603+ // - https://github.com/owner/repo.git
604+ string pattern = @"github\.com[:/](?<owner>[^/]+?)/(?<repo>[^/]+?)(\.git)?$" ;
605+ var match = System . Text . RegularExpressions . Regex . Match ( url , pattern , System . Text . RegularExpressions . RegexOptions . IgnoreCase ) ;
606+ if ( ! match . Success )
607+ throw new Exception ( "Origin not set to github remote url" ) ;
608+
609+ return ( match . Groups [ "owner" ] . Value , match . Groups [ "repo" ] . Value ) ;
610+ }
536611
537612 string [ ] GetTagsForCurrentCheckout ( )
538613 {
0 commit comments