PageRenderTime 25ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs

http://github.com/AArnott/dotnetopenid
C# | 196 lines | 132 code | 25 blank | 39 comment | 19 complexity | 94b232232e8dc57ca0193ea1c2407230 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using Microsoft.Build.Framework;
  9. using Microsoft.Build.Utilities;
  10. namespace DotNetOpenAuth.BuildTasks {
  11. public class GetBuildVersion : Task {
  12. /// <summary>
  13. /// Initializes a new instance of the <see cref="GetBuildVersion"/> class.
  14. /// </summary>
  15. public GetBuildVersion() {
  16. }
  17. /// <summary>
  18. /// Gets the version string to use in the compiled assemblies.
  19. /// </summary>
  20. [Output]
  21. public string Version { get; private set; }
  22. /// <summary>
  23. /// Gets the version string to use in the official release name (lacks revision number).
  24. /// </summary>
  25. [Output]
  26. public string SimpleVersion { get; private set; }
  27. /// <summary>
  28. /// Gets or sets the major.minor version string.
  29. /// </summary>
  30. /// <value>
  31. /// The x.y string (no build number or revision number).
  32. /// </value>
  33. [Output]
  34. public string MajorMinorVersion { get; set; }
  35. /// <summary>
  36. /// Gets or sets the prerelease version, or empty if this is a final release.
  37. /// </summary>
  38. /// <value>
  39. /// The prerelease version.
  40. /// </value>
  41. [Output]
  42. public string PrereleaseVersion { get; set; }
  43. /// <summary>
  44. /// Gets or sets the version string to use for NuGet packages containing OAuth 2 components.
  45. /// </summary>
  46. [Output]
  47. public string OAuth2PackagesVersion { get; set; }
  48. /// <summary>
  49. /// Gets the Git revision control commit id for HEAD (the current source code version).
  50. /// </summary>
  51. [Output]
  52. public string GitCommitId { get; private set; }
  53. /// <summary>
  54. /// Gets the build number (JDate) for this version.
  55. /// </summary>
  56. [Output]
  57. public int BuildNumber { get; private set; }
  58. /// <summary>
  59. /// The file that contains the version base (Major.Minor.Build) to use.
  60. /// </summary>
  61. [Required]
  62. public string VersionFile { get; set; }
  63. /// <summary>
  64. /// Gets or sets the parent directory of the .git directory.
  65. /// </summary>
  66. public string GitRepoRoot { get; set; }
  67. public override bool Execute() {
  68. try {
  69. Version typedVersion;
  70. string prerelease, oauth2PackagesVersion;
  71. this.ReadVersionFromFile(out typedVersion, out prerelease, out oauth2PackagesVersion);
  72. this.PrereleaseVersion = prerelease;
  73. this.OAuth2PackagesVersion = oauth2PackagesVersion;
  74. this.SimpleVersion = typedVersion.ToString();
  75. this.MajorMinorVersion = new Version(typedVersion.Major, typedVersion.Minor).ToString();
  76. this.BuildNumber = this.CalculateJDate(DateTime.Now);
  77. var fullVersion = new Version(typedVersion.Major, typedVersion.Minor, typedVersion.Build, this.BuildNumber);
  78. this.Version = fullVersion.ToString();
  79. this.GitCommitId = GetGitHeadCommitId();
  80. } catch (ArgumentOutOfRangeException ex) {
  81. Log.LogErrorFromException(ex);
  82. return false;
  83. }
  84. return true;
  85. }
  86. private string GetGitHeadCommitId() {
  87. if (string.IsNullOrEmpty(this.GitRepoRoot)) {
  88. return string.Empty;
  89. }
  90. string commitId = string.Empty;
  91. // First try asking Git for the HEAD commit id
  92. try {
  93. string cmdPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
  94. var psi = new ProcessStartInfo(cmdPath, "/c git rev-parse HEAD") {
  95. WindowStyle = ProcessWindowStyle.Hidden,
  96. CreateNoWindow = true,
  97. RedirectStandardOutput = true,
  98. UseShellExecute = false
  99. };
  100. var git = Process.Start(psi);
  101. commitId = git.StandardOutput.ReadLine();
  102. git.WaitForExit();
  103. if (git.ExitCode != 0) {
  104. commitId = string.Empty;
  105. }
  106. if (commitId != null) {
  107. commitId = commitId.Trim();
  108. if (commitId.Length == 40) {
  109. return commitId;
  110. }
  111. }
  112. } catch (InvalidOperationException) {
  113. } catch (Win32Exception) {
  114. }
  115. // Failing being able to use the git command to figure out the HEAD commit ID, try the filesystem directly.
  116. try {
  117. string headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/HEAD")).Trim();
  118. if (headContent.StartsWith("ref:", StringComparison.Ordinal)) {
  119. string refName = headContent.Substring(5).Trim();
  120. string refPath = Path.Combine(this.GitRepoRoot, ".git/" + refName);
  121. if (File.Exists(refPath)) {
  122. commitId = File.ReadAllText(refPath).Trim();
  123. } else {
  124. string packedRefPath = Path.Combine(this.GitRepoRoot, ".git/packed-refs");
  125. string matchingLine = File.ReadAllLines(packedRefPath).FirstOrDefault(line => line.EndsWith(refName));
  126. if (matchingLine != null) {
  127. commitId = matchingLine.Substring(0, matchingLine.IndexOf(' '));
  128. }
  129. }
  130. } else {
  131. commitId = headContent;
  132. }
  133. } catch (FileNotFoundException) {
  134. } catch (DirectoryNotFoundException) {
  135. }
  136. commitId = commitId ?? String.Empty; // doubly-be sure it's not null, since in some error cases it can be.
  137. return commitId.Trim();
  138. }
  139. private void ReadVersionFromFile(out Version typedVersion, out string prereleaseVersion, out string oauth2PackagesVersion) {
  140. string[] lines = File.ReadAllLines(VersionFile);
  141. string versionLine = lines[0];
  142. prereleaseVersion = lines.Length >= 2 ? lines[1] : null;
  143. oauth2PackagesVersion = lines.Length >= 3 ? lines[2] : null;
  144. if (!String.IsNullOrEmpty(prereleaseVersion)) {
  145. if (!prereleaseVersion.StartsWith("-")) {
  146. // SemVer requires that prerelease suffixes begin with a hyphen, so add one if it's missing.
  147. prereleaseVersion = "-" + prereleaseVersion;
  148. }
  149. this.VerifyValidPrereleaseVersion(prereleaseVersion);
  150. }
  151. typedVersion = new Version(versionLine);
  152. }
  153. private int CalculateJDate(DateTime date) {
  154. int yearLastDigit = date.Year - 2000; // can actually be two digits in or after 2010
  155. DateTime firstOfYear = new DateTime(date.Year, 1, 1);
  156. int dayOfYear = (date - firstOfYear).Days + 1;
  157. int jdate = yearLastDigit * 1000 + dayOfYear;
  158. return jdate;
  159. }
  160. private void VerifyValidPrereleaseVersion(string prerelease) {
  161. if (prerelease[0] != '-') {
  162. throw new ArgumentOutOfRangeException("The prerelease string must begin with a hyphen.");
  163. }
  164. for (int i = 1; i < prerelease.Length; i++) {
  165. if (!char.IsLetterOrDigit(prerelease[i])) {
  166. throw new ArgumentOutOfRangeException("The prerelease string must be alphanumeric.");
  167. }
  168. }
  169. }
  170. }
  171. }