From 4c20e317841501df4407e5eafa34ab2974af3b3b Mon Sep 17 00:00:00 2001 From: Caelan Sayler Date: Sat, 20 Aug 2022 17:52:31 +0100 Subject: [PATCH] Add github-up sync command --- .../Squirrel.CommandLine.csproj | 1 + src/Squirrel.CommandLine/SquirrelHost.cs | 1 + .../Sync/GitHubRepository.cs | 92 ++++++++++++++++++- src/Squirrel.CommandLine/SyncOptions.cs | 2 +- 4 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/Squirrel.CommandLine/Squirrel.CommandLine.csproj b/src/Squirrel.CommandLine/Squirrel.CommandLine.csproj index 4c81f8976..d27a1f21a 100644 --- a/src/Squirrel.CommandLine/Squirrel.CommandLine.csproj +++ b/src/Squirrel.CommandLine/Squirrel.CommandLine.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Squirrel.CommandLine/SquirrelHost.cs b/src/Squirrel.CommandLine/SquirrelHost.cs index 62e228dd9..b1800ff82 100644 --- a/src/Squirrel.CommandLine/SquirrelHost.cs +++ b/src/Squirrel.CommandLine/SquirrelHost.cs @@ -68,6 +68,7 @@ public static int Main(string[] args) { "s3-down", "Download latest release from S3 API", new SyncS3Options(), o => Download(new S3Repository(o)) }, { "s3-up", "Upload releases to S3 API", new SyncS3Options(), o => Upload(new S3Repository(o)) }, { "github-down", "Download latest release from GitHub", new SyncGithubOptions(), o => Download(new GitHubRepository(o)) }, + { "github-up", "Upload latest release to GitHub", new SyncGithubOptions(), o => Upload(new GitHubRepository(o)) }, }; if (verbose) { diff --git a/src/Squirrel.CommandLine/Sync/GitHubRepository.cs b/src/Squirrel.CommandLine/Sync/GitHubRepository.cs index 1645c3841..f7150ae6a 100644 --- a/src/Squirrel.CommandLine/Sync/GitHubRepository.cs +++ b/src/Squirrel.CommandLine/Sync/GitHubRepository.cs @@ -1,7 +1,9 @@ -using System; +using System; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; +using Octokit; using Squirrel.SimpleSplat; using Squirrel.Sources; @@ -60,9 +62,91 @@ public async Task DownloadRecentPackages() Log.Info("Done."); } - public Task UploadMissingPackages() + public async Task UploadMissingPackages() { - throw new NotImplementedException(); + if (String.IsNullOrWhiteSpace(_options.token)) + throw new InvalidOperationException("Must provide access token to create a GitHub release."); + + var releaseDirectoryInfo = _options.GetReleaseDirectory(); + + var repoUri = new Uri(_options.repoUrl); + var repoParts = repoUri.AbsolutePath.Trim('/').Split('/'); + if (repoParts.Length != 2) + throw new Exception($"Invalid GitHub URL, '{repoUri.AbsolutePath}' should be in the format 'owner/repo'"); + + var repoOwner = repoParts[0]; + var repoName = repoParts[1]; + + var client = new GitHubClient(new ProductHeaderValue("Clowd.Squirrel")) { + Credentials = new Credentials(_options.token) + }; + + var releasesPath = Path.Combine(releaseDirectoryInfo.FullName, "RELEASES"); + if (!File.Exists(releasesPath)) + ReleaseEntry.BuildReleasesFile(releaseDirectoryInfo.FullName); + + var releases = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasesPath)).ToArray(); + if (releases.Length == 0) + throw new Exception("There are no nupkg's in the releases directory to upload"); + + var ver = releases.Select(r => r.Version).Max(); + if (ver == null) + throw new Exception("There are no nupkg's in the releases directory to upload"); + + Log.Info($"Preparing to upload latest local release to GitHub"); + + var newReleaseReq = new NewRelease(ver.ToString()) { + Body = "TODO", // TODO, this should use the release notes from the release, if they exist + Draft = true, + Prerelease = ver.HasMetadata || ver.IsPrerelease, + Name = ver.ToString(), + }; + + Log.Info($"Creating draft release titled '{ver.ToString()}'"); + + var existingReleases = await client.Repository.Release.GetAll(repoOwner, repoName); + if (existingReleases.Any(r => r.Name == ver.ToString())) { + throw new Exception($"There is already an existing release titled '{ver}'. Please delete this release or choose a new version number."); + } + + var release = await client.Repository.Release.Create(repoOwner, repoName, newReleaseReq); + + // locate files to upload + var files = releaseDirectoryInfo.GetFiles("*", SearchOption.TopDirectoryOnly); + var msiFile = files.Where(f => f.FullName.EndsWith(".msi", StringComparison.InvariantCultureIgnoreCase)).SingleOrDefault(); + var setupFile = files.Where(f => f.FullName.EndsWith("Setup.exe", StringComparison.InvariantCultureIgnoreCase)) + .ContextualSingle("release directory", "Setup.exe file"); + + var releasesToUpload = releases.Where(x => x.Version == ver).ToArray(); + MemoryStream releasesFileToUpload = new MemoryStream(); + ReleaseEntry.WriteReleaseFile(releasesToUpload, releasesFileToUpload); + var releasesBytes = releasesFileToUpload.ToArray(); + + // upload nupkg's + foreach (var r in releasesToUpload) { + var path = Path.Combine(releaseDirectoryInfo.FullName, r.Filename); + await UploadFileAsAsset(client, release, path); + } + + // other files + await UploadFileAsAsset(client, release, setupFile.FullName); + if (msiFile != null) await UploadFileAsAsset(client, release, msiFile.FullName); + + // RELEASES + Log.Info($"Uploading RELEASES"); + var data = new ReleaseAssetUpload("RELEASES", "application/octet-stream", new MemoryStream(releasesBytes), TimeSpan.FromMinutes(1)); + await client.Repository.Release.UploadAsset(release, data, CancellationToken.None); + + Log.Info($"Done creating draft GitHub release."); + Log.Info("Release URL: " + release.HtmlUrl); + } + + private async Task UploadFileAsAsset(GitHubClient client, Release release, string filePath) + { + Log.Info($"Uploading asset '{Path.GetFileName(filePath)}'"); + using var stream = File.OpenRead(filePath); + var data = new ReleaseAssetUpload(Path.GetFileName(filePath), "application/octet-stream", stream, TimeSpan.FromMinutes(30)); + await client.Repository.Release.UploadAsset(release, data, CancellationToken.None); } } -} +} \ No newline at end of file diff --git a/src/Squirrel.CommandLine/SyncOptions.cs b/src/Squirrel.CommandLine/SyncOptions.cs index 4e723825b..af3218220 100644 --- a/src/Squirrel.CommandLine/SyncOptions.cs +++ b/src/Squirrel.CommandLine/SyncOptions.cs @@ -90,7 +90,7 @@ public SyncGithubOptions() { Add("repoUrl=", "Full url to the github repository\nexample: 'https://github.com/myname/myrepo'", v => repoUrl = v); Add("token=", "OAuth token to use as login credentials", v => token = v); - Add("pre", "Fetch the latest pre-release instead of stable", v => pre = true); + Add("pre", "Download pre-release instead of stable", v => pre = true); } public override void Validate()