diff --git a/build/Program.cs b/build/Program.cs index 3ee87d7d54..c1d38e2429 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -25,122 +25,124 @@ namespace ConsoleApplication //lets find the repo var repo = new LibGit2Sharp.Repository(root); - //lets find all the project.json files in the src folder (don't care about versioning tests) + //lets find all the project.json files in the src folder (don't care about versioning `tests`) var projectFiles = Directory.EnumerateFiles(Path.Combine(root, "src"), Project.FileName, SearchOption.AllDirectories); //open them and convert them to source projects var projects = projectFiles.Select(x => ProjectReader.GetProject(x)) .Select(x => new SourceProject(x, repo.Info.WorkingDirectory)) .ToList(); + if (resetmode) { - if (File.Exists("build-inner.bak")) - { - File.Copy("build-inner.bak", "build-inner.cmd", true); - File.Delete("build-inner.bak"); - } - - //revert the project.json change be reverting it but skipp all the git stuff as its not needed - foreach (var p in projects) - { - if (File.Exists($"{p.FullProjectFilePath}.bak")) - { - File.Copy($"{p.FullProjectFilePath}.bak", p.FullProjectFilePath, true); - File.Delete($"{p.FullProjectFilePath}.bak"); - } - Console.WriteLine($"{p.Name} {p.Version.ToFullString()}"); - } + ResetProject(projects); } else - { - // populate the dependency chains - projects.ForEach(x => x.PopulateDependencies(projects)); + { + CaclulateProjectVersionNumber(projects, repo); + + UpdateVersionNumbers(projects); - + CreateBuildScript(projects); + foreach (var p in projects) { - foreach (var c in repo.Commits) - { - // lets apply each commit to the projects looking to see if they effect it or a - // dependency and detect if the commit was one that sent the current version - if (!p.ApplyCommit(c, repo)) - { - // if it did create the version then stop looking this one wins - break; - } - } + Console.WriteLine($"{p.Name} {p.FinalVersionNumber}"); } + } + } - // lets build version friendly commit - string branch = repo.Head.FriendlyName; + private static void CreateBuildScript(IEnumerable projects) + { + var sb = new StringBuilder(); + foreach (var p in projects) + { - // lets see if we are running in appveyor - var appveryorBranch = Environment.GetEnvironmentVariable("APPVEYOR_REPO_BRANCH"); - if (!string.IsNullOrWhiteSpace(appveryorBranch)) - { - branch = appveryorBranch; - } + sb.AppendLine($@"dotnet pack --configuration Release --output ""artifacts\bin\ImageSharp"" ""{p.ProjectFilePath}"""); + } + File.Copy("build-inner.cmd", "build-inner.bak", true); + File.WriteAllText("build-inner.cmd", sb.ToString()); + } - var prNumber = Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER"); - if (!string.IsNullOrWhiteSpace(prNumber)) - { - branch = "PR" + prNumber; - } - if(branch == "(no branch)") - { - throw new Exception("unable to find branch"); - } - branch = branch.Replace("/", "-").Replace("--", "-"); - if (branch == "master") - { - branch = ""; - } + private static void UpdateVersionNumbers(IEnumerable projects) + { + foreach (var p in projects) + { + //TODO force update of all dependent projects to point to the newest build. - var sb = new StringBuilder(); - foreach (var p in projects) - { - //TODO force update of all dependent projects to point to the newest build. + //we skip the build number and standard CI prefix on first commits + var newVersion = p.FinalVersionNumber; + + // create a backup file so we can rollback later without breaking formatting + File.Copy(p.FullProjectFilePath, $"{p.FullProjectFilePath}.bak", true); - //we skip the build number and standard CI prefix on first commits - var newVersion = p.CalculateVersionNumber(branch); - File.Copy(p.FullProjectFilePath, $"{p.FullProjectFilePath}.bak", true); - dynamic projectFile = JsonConvert.DeserializeObject(File.ReadAllText(p.FullProjectFilePath)); + dynamic projectFile = JsonConvert.DeserializeObject(File.ReadAllText(p.FullProjectFilePath)); - projectFile.version = $"{newVersion}-*"; - File.WriteAllText(p.FullProjectFilePath, JsonConvert.SerializeObject(projectFile, Formatting.Indented)); - - Console.WriteLine($"{p.Name} {newVersion}"); + projectFile.version = $"{newVersion}-*"; + File.WriteAllText(p.FullProjectFilePath, JsonConvert.SerializeObject(projectFile, Formatting.Indented)); + } + } - sb.AppendLine($@"dotnet pack --configuration Release --output ""artifacts\bin\ImageSharp"" ""{p.ProjectFilePath}"""); - } + private static string CurrentBranch(Repository repo) + { + // lets build version friendly commit + string branch = repo.Head.FriendlyName; + + // lets see if we are running in appveyor and if we are use the environment variables instead of the head + var appveryorBranch = Environment.GetEnvironmentVariable("APPVEYOR_REPO_BRANCH"); + if (!string.IsNullOrWhiteSpace(appveryorBranch)) + { + branch = appveryorBranch; + } + + var prNumber = Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER"); + if (!string.IsNullOrWhiteSpace(prNumber)) + { + branch = $"PR{int.Parse(prNumber):0000}"; + } - File.Copy("build-inner.cmd", "build-inner.bak", true); - File.WriteAllText("build-inner.cmd", sb.ToString()); + // this will happen when checking out a comit directly and not a branch (like appveryor does when it builds) + if (branch == "(no branch)") + { + throw new Exception("unable to find branch"); } + + // clean branch names (might need to be improved) + branch = branch.Replace("/", "-").Replace("--", "-"); + + return branch; } - + private static void CaclulateProjectVersionNumber(List projects, Repository repo) + { + var branch = CurrentBranch(repo); - //find project root - public static string GetProjectRoot() + // populate the dependency chains + projects.ForEach(x => x.PopulateDependencies(projects)); + + // update the final version based on the repo history and the currentr branch name + projects.ForEach(x => x.CalculateVersion(repo, branch)); + } + + private static void ResetProject(List projects) { - var path = Path.GetFullPath("."); - do + if (File.Exists("build-inner.bak")) { - var jsonPath = Path.Combine(path, "global.json"); - if (File.Exists(jsonPath)) - { - return path; - } - else + File.Copy("build-inner.bak", "build-inner.cmd", true); + File.Delete("build-inner.bak"); + } + + //revert the project.json change be reverting it but skipp all the git stuff as its not needed + foreach (var p in projects) + { + if (File.Exists($"{p.FullProjectFilePath}.bak")) { - path = Path.GetDirectoryName(path); + File.Copy($"{p.FullProjectFilePath}.bak", p.FullProjectFilePath, true); + File.Delete($"{p.FullProjectFilePath}.bak"); } - } while (!string.IsNullOrWhiteSpace(path)); - - return null; + } } - + public class SourceProject { private readonly IEnumerable dependencies; @@ -153,8 +155,9 @@ namespace ConsoleApplication public string Name { get; private set; } public string ProjectFilePath { get; private set; } - private int commitCount = 0; + public int CommitCountSinceVersionChange { get; private set; } = 0; public string FullProjectFilePath { get; private set; } + public string FinalVersionNumber { get; private set; } public SourceProject(Project project, string root) { @@ -164,6 +167,7 @@ namespace ConsoleApplication this.FullProjectFilePath = project.ProjectFilePath; this.Version = project.Version; this.dependencies = project.Dependencies.Select(x => x.Name); + this.FinalVersionNumber = Version.ToFullString(); } public void PopulateDependencies(IEnumerable projects) @@ -186,9 +190,9 @@ namespace ConsoleApplication return false; } - internal bool ApplyCommitInternal(Commit commit, TreeChanges changes, Repository repo) + private bool ApplyCommitInternal(Commit commit, TreeChanges changes, Repository repo) { - commitCount++; + CommitCountSinceVersionChange++; //return false if this is a version number root var projectFileChange = changes.Where(x => x.Path?.Equals(this.ProjectFilePath, StringComparison.OrdinalIgnoreCase) == true).FirstOrDefault(); @@ -212,13 +216,30 @@ namespace ConsoleApplication } } } + // version must have been the same lets carry on return true; } return true; } - internal bool ApplyCommit(Commit commit, Repository repo) + + internal void CalculateVersion(Repository repo, string branch) + { + foreach(var c in repo.Commits) + { + if(!ApplyCommit(c, repo)) + { + + //we have finished lets populate the final version number + this.FinalVersionNumber = CalculateVersionNumber(branch); + + return; + } + } + } + + private bool ApplyCommit(Commit commit, Repository repo) { foreach (var parent in commit.Parents) { @@ -247,53 +268,63 @@ namespace ConsoleApplication return true; } - internal string CalculateVersionNumber(string branch) + private string CalculateVersionNumber(string branch) { var version = this.Version.ToFullString(); - if (this.commitCount == 1 && branch == "") //master only + if (this.CommitCountSinceVersionChange == 1 && branch == "master") //master only { if (this.Version.IsPrerelease) { //prerelease always needs the build counter just not on a branch name - return $"{version}-{this.commitCount:00000}"; + return $"{version}-{this.CommitCountSinceVersionChange:00000}"; } - //only 1 commit (the changing one) we will skip appending suffix + // this is the full release happy path, first commit after changing the version number return version; } - var rootSpecialVersion = ""; if (this.Version.IsPrerelease) { + // probably a much easy way for doing this but it work sell enough for a build script var parts = version.Split(new[] { '-' }, 2); version = parts[0]; rootSpecialVersion = parts[1]; } - if(rootSpecialVersion.Length > 0) + + // if master and the version doesn't manually specify a prerelease tag force one on for CI builds + if (branch == "master") { - rootSpecialVersion = "-" + rootSpecialVersion; + if (!this.Version.IsPrerelease) + { + branch = fallbackTag; + }else + { + branch = ""; + } } - if (branch == "" && !this.Version.IsPrerelease) + + if (rootSpecialVersion.Length > 0) { - branch = fallbackTag; + rootSpecialVersion = "-" + rootSpecialVersion; } if (branch.Length > 0) { branch = "-" + branch; } - var maxLength = 20; - maxLength -= rootSpecialVersion.Length; + var maxLength = 20; // dotnet will fail to populate the package if the tag is > 20 + maxLength -= rootSpecialVersion.Length; // this is a required tag maxLength -= 7; // for the counter and dashes + if(branch.Length > maxLength) { branch = branch.Substring(0, maxLength); } - return $"{version}{rootSpecialVersion}{branch}-{this.commitCount:00000}"; + return $"{version}{rootSpecialVersion}{branch}-{this.CommitCountSinceVersionChange:00000}"; } } }