/// <summary> /// Validates the release specified. /// </summary> /// <param name="release">Reference to an instance of <see cref="github_release_poster.NewRelease"/> /// that contains the metadata for the release.</param> /// <param name="repoName">String containing the name of the repository to which this release is to be published.</param> /// <param name="repoOwner">String containing the GitHub username of the owner of the repository.</param> /// <param name="userAcessToken">String containing the user access token of a GitHub user with push access to the repository.</param> /// <param name="releaseAssetDir">String containing the path to the directory where release assets are stored.</param> /// <returns>True if the release is valid; false otherwise. Terminates the program with a nonzero exit code if the /// release does not pass validation.</returns> /// <exception cref="T:System.ArgumentNullException">Thrown if the <see cref="release"/> argument is a null /// reference of if any of the string arguments are blank.</exception> public static bool IsReleaseValid(NewRelease release, string repoName, string repoOwner, string userAcessToken, string releaseAssetDir) { Console.WriteLine(Resources.ValidatingReleaseMetadata); // reject a null reference to the release object itself if (release == null) { throw new ArgumentNullException(nameof(release)); } if (string.IsNullOrWhiteSpace(release.tag_name)) { throw new ArgumentNullException(nameof(release.tag_name)); } if (string.IsNullOrWhiteSpace(release.name)) { throw new ArgumentNullException(nameof(release.name)); } if (string.IsNullOrWhiteSpace(release.target_commitish)) { throw new ArgumentNullException(nameof(release.target_commitish)); } if (string.IsNullOrWhiteSpace(repoName)) { throw new ArgumentNullException(nameof(repoName)); } if (string.IsNullOrWhiteSpace(repoOwner)) { throw new ArgumentNullException(nameof(repoOwner)); } if (string.IsNullOrWhiteSpace(userAcessToken)) { throw new ArgumentNullException(nameof(userAcessToken)); } if (string.IsNullOrWhiteSpace(releaseAssetDir)) { throw new ArgumentNullException(nameof(releaseAssetDir)); } /* If the release asset directory does not yet exist, then the build must have failed! LOL. * However, let's tell the user just to be sure. */ if (!Directory.Exists(releaseAssetDir)) { Console.WriteLine(Resources.ReleaseAssetDirNotFound, releaseAssetDir); Environment.Exit(exitCode: Resources.ERROR_RELEASE_ASSET_DIR_NOT_EXISTS); } /* The GitHub release API allows an unlimited number of assets to be posted to a release. However, * it limits the size of any single file to be no more than 2 GB in size. Scan the release asset dir. * If it, or any of its subdirectories (that the current user can access, anyway) contains a file that is * 2 GB or more in size, stop this program after displaying an error.*/ if (FileSearcher.GetAllFilesInFolder(releaseAssetDir) .Where(fsi => (fsi.Attributes & FileAttributes.Directory) != FileAttributes.Directory) .Any(fsi => Convert.ToUInt64(new FileInfo(fsi.FullName).Length) >= Resources.TwoGigaBytes)) { Console.WriteLine(Resources.ReleaseAssetDirContainsTooBigFile); Environment.Exit(Resources.ERROR_RELEASE_ASSET_IS_TOO_BIG); } Console.WriteLine(Resources.ReleaseAssetsAndMetadataValid); return(true); }
/// <summary> /// Uploads the assets in the release asset directory individually to the newly-created release. Flattens any directory structure that might exist in the /// folder referred to by <see cref="releaseAssetDir"/>. /// </summary> /// <param name="releaseAssetDir">String containing the fully-qualified path to the directory containing release assets to be uploaded.</param> /// <param name="newRelease">Reference to an instance of a dynamic object which has been deserialized from the response JSON /// of the call to create the new release.</param> /// <param name="userAccessToken">String containing the user's API access token.</param> public static void UploadAssetsIndividually(string releaseAssetDir, dynamic newRelease, string userAccessToken) { if (string.IsNullOrWhiteSpace(releaseAssetDir)) { throw new ArgumentNullException(nameof(releaseAssetDir)); } if (!Directory.Exists(releaseAssetDir)) { throw new DirectoryNotFoundException(string.Format(Resources.DirectoryNotFound, releaseAssetDir)); } if (newRelease == null) { throw new ArgumentNullException(nameof(newRelease)); } if (string.IsNullOrWhiteSpace(userAccessToken)) { throw new ArgumentNullException(nameof(userAccessToken)); } // Iterate over all the files in the release asset directory and its subdirectories, // one by one. Flatten the directory tree into just a big ol' list of files. To preserve // the directory tree, distribute the release assets in ZIP format and then have your // Setup program un zip the assets into their directory structure. foreach (var assetFile in FileSearcher.GetAllFilesInFolder( releaseAssetDir).Where(fsi => (fsi.Attributes & FileAttributes.Directory) != FileAttributes.Directory)) { /* Make a new request each iteration of the loop over asset files */ var request = GitHubRequestFactory.PrepareGitHubRequest(Method.POST, userAccessToken); // Get just the name and extension of the asset for use in the // upload url later. If blank is returned, then something went wrong. // In that case, just skip the current file. var assetFileName = Path.GetFileName(assetFile.FullName); if (string.IsNullOrWhiteSpace(assetFileName)) { continue; } // If the file has zero bytes of length, do not upload it if (FileAndFolderHelper.FileHasZeroLength((FileInfo)assetFile)) { Console.WriteLine( $"ERROR: File '{assetFileName}' has zero bytes of length. Not uploading it."); // Just skip files that have zero length continue; } /* From Tavis.UriTemplates NuGet package -- works like Ruby uri_template gem */ var uploadUrl = new UriTemplate(newRelease.upload_url.Value) .AddParameters(new { name = assetFileName, label = assetFileName }) .Resolve(); if (string.IsNullOrWhiteSpace(uploadUrl)) { Console.WriteLine(Resources.UploadUrlNotObtainable); Environment.Exit(Resources.ERROR_NOT_OBTAINED_RELEASE_UPLOAD_URL); } // Prepare a REST request with the upload url from the create release API response // above var client = new RestClient( uploadUrl ); var assetMimeMapping = MimeMapping.GetMimeMapping(assetFile.FullName); request.AddHeader(Resources.ContentTypeHeaderName, assetMimeMapping ); var bytes = File.ReadAllBytes(assetFile.FullName); if (!bytes.Any()) { continue; // zero-length file } request.AddHeader("Content-Length", bytes.Length.ToString()); Console.WriteLine($"{bytes.Length} bytes read from file '{assetFile.FullName}'. Uploading..."); request.AddParameter( assetMimeMapping, bytes, ParameterType.RequestBody ); var response = client.Execute(request); if (response == null || response.StatusCode != HttpStatusCode.Created) { Console.WriteLine(Resources.FailedToUploadAsset, assetFileName); Environment.Exit(Resources.ERROR_ASSET_NOT_ACCEPTED); } else { Console.WriteLine(Resources.AssetAccepted, assetFileName); } } }