summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs
blob: 9818885741cee96c7ff50779527a2e493aff84e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace DotNetOpenAuth.BuildTasks {
	public class GetBuildVersion : Task {
		/// <summary>
		/// Initializes a new instance of the <see cref="GetBuildVersion"/> class.
		/// </summary>
		public GetBuildVersion() {
		}

		/// <summary>
		/// Gets the version string to use in the compiled assemblies.
		/// </summary>
		[Output]
		public string Version { get; private set; }

		/// <summary>
		/// Gets the version string to use in the official release name (lacks revision number).
		/// </summary>
		[Output]
		public string SimpleVersion { get; private set; }

		/// <summary>
		/// Gets the Git revision control commit id for HEAD (the current source code version).
		/// </summary>
		[Output]
		public string GitCommitId { get; private set; }

		/// <summary>
		/// The file that contains the version base (Major.Minor.Build) to use.
		/// </summary>
		[Required]
		public string VersionFile { get; set; }

		/// <summary>
		/// Gets or sets the parent directory of the .git directory.
		/// </summary>
		public string GitRepoRoot { get; set; }

		public override bool Execute() {
			try {
				Version typedVersion = ReadVersionFromFile();
				SimpleVersion = typedVersion.ToString();

				var fullVersion = new Version(typedVersion.Major, typedVersion.Minor, typedVersion.Build, CalculateJDate(DateTime.Now));
				Version = fullVersion.ToString();
	
				this.GitCommitId = GetGitHeadCommitId();
			} catch (ArgumentOutOfRangeException ex) {
				Log.LogErrorFromException(ex);
				return false;
			}

			return true;
		}

		private string GetGitHeadCommitId() {
			if (string.IsNullOrEmpty(this.GitRepoRoot)) {
				return string.Empty;
			}

			string commitId = string.Empty;

			// First try asking Git for the HEAD commit id
			try {
				string cmdPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
				var psi = new ProcessStartInfo(cmdPath, "/c git rev-parse HEAD") {
					WindowStyle = ProcessWindowStyle.Hidden,
					CreateNoWindow = true,
					RedirectStandardOutput = true,
					UseShellExecute = false
				};
				var git = Process.Start(psi);
				commitId = git.StandardOutput.ReadLine();
				git.WaitForExit();
				if (git.ExitCode != 0) {
					commitId = string.Empty;
				}
				if (commitId != null) {
					commitId = commitId.Trim();
					if (commitId.Length == 40) {
						return commitId;
					}
				}
			} catch (InvalidOperationException) {
			} catch (Win32Exception) {
			}

			// Failing being able to use the git command to figure out the HEAD commit ID, try the filesystem directly.
			try {
				string headContent = File.ReadAllText(Path.Combine(this.GitRepoRoot, @".git/HEAD")).Trim();
				if (headContent.StartsWith("ref:", StringComparison.Ordinal)) {
					string refName = headContent.Substring(5).Trim();
					string refPath = Path.Combine(this.GitRepoRoot, ".git/" + refName);
					if (File.Exists(refPath)) {
						commitId = File.ReadAllText(refPath).Trim();
					} else {
						string packedRefPath = Path.Combine(this.GitRepoRoot, ".git/packed-refs");
						string matchingLine = File.ReadAllLines(packedRefPath).FirstOrDefault(line => line.EndsWith(refName));
						if (matchingLine != null) {
							commitId = matchingLine.Substring(0, matchingLine.IndexOf(' '));
						}
					}
				} else {
					commitId = headContent;
				}
			} catch (FileNotFoundException) {
			} catch (DirectoryNotFoundException) {
			}

			commitId = commitId ?? String.Empty; // doubly-be sure it's not null, since in some error cases it can be.
			return commitId.Trim();
		}

		private Version ReadVersionFromFile() {
			string[] lines = File.ReadAllLines(VersionFile);
			string versionLine = lines[0];
			return new Version(versionLine);
		}

		private int CalculateJDate(DateTime date) {
			int yearLastDigit = date.Year - 2000; // can actually be two digits in or after 2010
			DateTime firstOfYear = new DateTime(date.Year, 1, 1);
			int dayOfYear = (date - firstOfYear).Days + 1;
			int jdate = yearLastDigit * 1000 + dayOfYear;
			return jdate;
		}
	}
}