Move update-dependencies under build_projects.

This commit is contained in:
Eric Erhardt 2016-05-25 17:28:11 -05:00
parent c97648e684
commit ee7372c2a1
11 changed files with 20 additions and 20 deletions

View file

@ -1,57 +0,0 @@
#
# Copyright (c) .NET Foundation and contributors. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#
param(
[string[]]$Targets=@("Default"),
[switch]$Help)
if($Help)
{
Write-Host "Usage: .\update-dependencies.ps1 [-Targets <TARGETS...>]"
Write-Host ""
Write-Host "Options:"
Write-Host " -Targets <TARGETS...> Comma separated build targets to run (UpdateFiles, PushPR; Default is everything)"
Write-Host " -Help Display this help message"
exit 0
}
# Use a repo-local install directory (but not the artifacts directory because that gets cleaned a lot
if (!$env:DOTNET_INSTALL_DIR)
{
$env:DOTNET_INSTALL_DIR="$PSScriptRoot\..\.dotnet_stage0\Windows\$Architecture"
}
# Install a stage 0
Write-Host "Installing .NET Core CLI Stage 0"
& "$PSScriptRoot\obtain\dotnet-install.ps1" -Architecture x64
# Put the stage0 on the path
$env:PATH = "$env:DOTNET_INSTALL_DIR;$env:PATH"
$appPath = "$PSScriptRoot\update-dependencies"
# Restore the build_projects
Write-Host "Restoring Microsoft.DotNet.Cli.Build.Framework..."
pushd $PSScriptRoot\..\build_projects\Microsoft.DotNet.Cli.Build.Framework
dotnet restore --infer-runtimes
if($LASTEXITCODE -ne 0) { throw "Failed to restore" }
popd
# Restore update-dependencies
pushd $appPath
dotnet restore --infer-runtimes
if($LASTEXITCODE -ne 0) { throw "Failed to restore" }
popd
# Publish the app
Write-Host "Compiling App $appPath..."
dotnet publish "$appPath" -o "$appPath\bin" --framework netcoreapp1.0
if($LASTEXITCODE -ne 0) { throw "Failed to compile build scripts" }
# Run the app
Write-Host "Invoking App $appPath..."
Write-Host " Configuration: $env:CONFIGURATION"
& "$appPath\bin\update-dependencies.exe" @Targets
if($LASTEXITCODE -ne 0) { throw "Build failed" }

View file

@ -1,30 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using Microsoft.DotNet.Cli.Build.Framework;
namespace Microsoft.DotNet.Scripts
{
public static class BuildContextProperties
{
public static List<DependencyInfo> GetDependencyInfos(this BuildTargetContext c)
{
const string propertyName = "DependencyInfos";
List<DependencyInfo> dependencyInfos;
object dependencyInfosObj;
if (c.BuildContext.Properties.TryGetValue(propertyName, out dependencyInfosObj))
{
dependencyInfos = (List<DependencyInfo>)dependencyInfosObj;
}
else
{
dependencyInfos = new List<DependencyInfo>();
c.BuildContext[propertyName] = dependencyInfos;
}
return dependencyInfos;
}
}
}

View file

@ -1,76 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.DotNet.Cli.Build.Framework;
namespace Microsoft.DotNet.Scripts
{
/// <summary>
/// Holds the configuration information for the update-dependencies script.
/// </summary>
/// <remarks>
/// The following Environment Variables are required by this script:
///
/// GITHUB_USER - The user to commit the changes as.
/// GITHUB_EMAIL - The user's email to commit the changes as.
/// GITHUB_PASSWORD - The password/personal access token of the GitHub user.
///
/// The following Environment Variables can optionally be specified:
///
/// COREFX_VERSION_URL - The Url to get the current CoreFx package versions. (ex. "https://raw.githubusercontent.com/dotnet/versions/master/build-info/dotnet/corefx/release/1.0.0/Latest_Packages.txt")
/// GITHUB_ORIGIN_OWNER - The owner of the GitHub fork to push the commit and create the PR from. (ex. "dotnet-bot")
/// GITHUB_UPSTREAM_OWNER - The owner of the GitHub base repo to create the PR to. (ex. "dotnet")
/// GITHUB_PROJECT - The repo name under the ORIGIN and UPSTREAM owners. (ex. "cli")
/// GITHUB_UPSTREAM_BRANCH - The branch in the GitHub base repo to create the PR to. (ex. "rel/1.0.0")
/// GITHUB_PULL_REQUEST_NOTIFICATIONS - A semi-colon ';' separated list of GitHub users to notify on the PR.
/// </remarks>
public class Config
{
public static Config Instance { get; } = new Config();
private Lazy<string> _userName = new Lazy<string>(() => GetEnvironmentVariable("GITHUB_USER"));
private Lazy<string> _email = new Lazy<string>(() => GetEnvironmentVariable("GITHUB_EMAIL"));
private Lazy<string> _password = new Lazy<string>(() => GetEnvironmentVariable("GITHUB_PASSWORD"));
private Lazy<string> _coreFxVersionUrl = new Lazy<string>(() => GetEnvironmentVariable("COREFX_VERSION_URL", "https://raw.githubusercontent.com/dotnet/versions/master/build-info/dotnet/corefx/release/1.0.0/Latest_Packages.txt"));
private Lazy<string> _gitHubOriginOwner;
private Lazy<string> _gitHubUpstreamOwner = new Lazy<string>(() => GetEnvironmentVariable("GITHUB_UPSTREAM_OWNER", "dotnet"));
private Lazy<string> _gitHubProject = new Lazy<string>(() => GetEnvironmentVariable("GITHUB_PROJECT", "cli"));
private Lazy<string> _gitHubUpstreamBranch = new Lazy<string>(() => GetEnvironmentVariable("GITHUB_UPSTREAM_BRANCH", "rel/1.0.0"));
private Lazy<string[]> _gitHubPullRequestNotifications = new Lazy<string[]>(() =>
GetEnvironmentVariable("GITHUB_PULL_REQUEST_NOTIFICATIONS", "")
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
private Config()
{
_gitHubOriginOwner = new Lazy<string>(() => GetEnvironmentVariable("GITHUB_ORIGIN_OWNER", UserName));
}
public string UserName => _userName.Value;
public string Email => _email.Value;
public string Password => _password.Value;
public string CoreFxVersionUrl => _coreFxVersionUrl.Value;
public string GitHubOriginOwner => _gitHubOriginOwner.Value;
public string GitHubUpstreamOwner => _gitHubUpstreamOwner.Value;
public string GitHubProject => _gitHubProject.Value;
public string GitHubUpstreamBranch => _gitHubUpstreamBranch.Value;
public string[] GitHubPullRequestNotifications => _gitHubPullRequestNotifications.Value;
private static string GetEnvironmentVariable(string name, string defaultValue = null)
{
string value = Environment.GetEnvironmentVariable(name);
if (value == null)
{
value = defaultValue;
}
if (value == null)
{
throw new BuildFailureException($"Can't find environment variable '{name}'.");
}
return value;
}
}
}

View file

@ -1,23 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using NuGet.Versioning;
namespace Microsoft.DotNet.Scripts
{
public class DependencyInfo
{
public string Name { get; set; }
public List<PackageInfo> NewVersions { get; set; }
public string NewReleaseVersion { get; set; }
public bool IsUpdated { get; set; }
}
public class PackageInfo
{
public string Id { get; set; }
public NuGetVersion Version { get; set; }
}
}

View file

@ -1,12 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.IO;
namespace Microsoft.DotNet.Scripts
{
public static class Dirs
{
public static readonly string RepoRoot = Directory.GetCurrentDirectory();
}
}

View file

@ -1,25 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.Cli.Build.Framework;
namespace Microsoft.DotNet.Scripts
{
public class Program
{
public static int Main(string[] args)
{
DebugHelper.HandleDebugSwitch(ref args);
return BuildSetup.Create(".NET CLI Dependency Updater")
.UseTargets(new[]
{
new BuildTarget("Default", "Dependency Updater Goals", new [] { "UpdateFiles", "PushPR" }),
new BuildTarget("UpdateFiles", "Dependency Updater Goals"),
new BuildTarget("PushPR", "Dependency Updater Goals"),
})
.UseAllTargetsFromAssembly<Program>()
.Run(args);
}
}
}

View file

@ -1,162 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Linq;
using Microsoft.DotNet.Cli.Build.Framework;
using Octokit;
using static Microsoft.DotNet.Cli.Build.Framework.BuildHelpers;
namespace Microsoft.DotNet.Scripts
{
/// <summary>
/// Creates a GitHub Pull Request for the current changes in the repo.
/// </summary>
public static class PushPRTargets
{
private static readonly Config s_config = Config.Instance;
[Target(nameof(CommitChanges), nameof(CreatePR))]
public static BuildTargetResult PushPR(BuildTargetContext c) => c.Success();
/// <summary>
/// Commits all the current changes in the repo and pushes the commit to a remote
/// so a PR can be created for it.
/// </summary>
[Target]
public static BuildTargetResult CommitChanges(BuildTargetContext c)
{
CommandResult statusResult = Cmd("git", "status", "--porcelain")
.CaptureStdOut()
.Execute();
statusResult.EnsureSuccessful();
bool hasModifiedFiles = !string.IsNullOrWhiteSpace(statusResult.StdOut);
bool hasUpdatedDependencies = c.GetDependencyInfos().Where(d => d.IsUpdated).Any();
if (hasModifiedFiles != hasUpdatedDependencies)
{
return c.Failed($"'git status' does not match DependencyInfo information. Git has modified files: {hasModifiedFiles}. DependencyInfo is updated: {hasUpdatedDependencies}.");
}
if (!hasUpdatedDependencies)
{
c.Warn("Dependencies are currently up to date");
return c.Success();
}
string userName = s_config.UserName;
string email = s_config.Email;
string commitMessage = GetCommitMessage(c);
Cmd("git", "commit", "-a", "-m", commitMessage, "--author", $"{userName} <{email}>")
.EnvironmentVariable("GIT_COMMITTER_NAME", userName)
.EnvironmentVariable("GIT_COMMITTER_EMAIL", email)
.Execute()
.EnsureSuccessful();
string remoteUrl = $"github.com/{s_config.GitHubOriginOwner}/{s_config.GitHubProject}.git";
string remoteBranchName = $"UpdateDependencies{DateTime.UtcNow.ToString("yyyyMMddhhmmss")}";
string refSpec = $"HEAD:refs/heads/{remoteBranchName}";
string logMessage = $"git push https://{remoteUrl} {refSpec}";
BuildReporter.BeginSection("EXEC", logMessage);
CommandResult pushResult =
Cmd("git", "push", $"https://{userName}:{s_config.Password}@{remoteUrl}", refSpec)
.QuietBuildReporter() // we don't want secrets showing up in our logs
.CaptureStdErr() // git push will write to StdErr upon success, disable that
.CaptureStdOut()
.Execute();
var message = logMessage + $" exited with {pushResult.ExitCode}";
if (pushResult.ExitCode == 0)
{
BuildReporter.EndSection("EXEC", message.Green(), success: true);
}
else
{
BuildReporter.EndSection("EXEC", message.Red().Bold(), success: false);
}
pushResult.EnsureSuccessful(suppressOutput: true);
c.SetRemoteBranchName(remoteBranchName);
return c.Success();
}
/// <summary>
/// Creates a GitHub PR for the remote branch created above.
/// </summary>
[Target]
public static BuildTargetResult CreatePR(BuildTargetContext c)
{
string remoteBranchName = c.GetRemoteBranchName();
string commitMessage = c.GetCommitMessage();
NewPullRequest prInfo = new NewPullRequest(
commitMessage,
s_config.GitHubOriginOwner + ":" + remoteBranchName,
s_config.GitHubUpstreamBranch);
string[] prNotifications = s_config.GitHubPullRequestNotifications;
if (prNotifications.Length > 0)
{
prInfo.Body = $"/cc @{string.Join(" @", prNotifications)}";
}
GitHubClient gitHub = new GitHubClient(new ProductHeaderValue("dotnetDependencyUpdater"));
gitHub.Credentials = new Credentials(s_config.Password);
PullRequest createdPR = gitHub.PullRequest.Create(s_config.GitHubUpstreamOwner, s_config.GitHubProject, prInfo).Result;
c.Info($"Created Pull Request: {createdPR.HtmlUrl}");
return c.Success();
}
private static string GetRemoteBranchName(this BuildTargetContext c)
{
return (string)c.BuildContext["RemoteBranchName"];
}
private static void SetRemoteBranchName(this BuildTargetContext c, string value)
{
c.BuildContext["RemoteBranchName"] = value;
}
private static string GetCommitMessage(this BuildTargetContext c)
{
const string commitMessagePropertyName = "CommitMessage";
string message;
object messageObject;
if (c.BuildContext.Properties.TryGetValue(commitMessagePropertyName, out messageObject))
{
message = (string)messageObject;
}
else
{
DependencyInfo[] updatedDependencies = c.GetDependencyInfos()
.Where(d => d.IsUpdated)
.ToArray();
string updatedDependencyNames = string.Join(", ", updatedDependencies.Select(d => d.Name));
string updatedDependencyVersions = string.Join(", ", updatedDependencies.Select(d => d.NewReleaseVersion));
message = $"Updating {updatedDependencyNames} to {updatedDependencyVersions}";
if (updatedDependencies.Count() > 1)
{
message += " respectively";
}
c.BuildContext[commitMessagePropertyName] = message;
}
return message;
}
}
}

View file

@ -1,236 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.DotNet.Cli.Build.Framework;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NuGet.Versioning;
namespace Microsoft.DotNet.Scripts
{
public static class UpdateFilesTargets
{
private static HttpClient s_client = new HttpClient();
[Target(nameof(GetDependencies), nameof(ReplaceVersions))]
public static BuildTargetResult UpdateFiles(BuildTargetContext c) => c.Success();
/// <summary>
/// Gets all the dependency information and puts it in the build properties.
/// </summary>
[Target]
public static BuildTargetResult GetDependencies(BuildTargetContext c)
{
List<DependencyInfo> dependencyInfos = c.GetDependencyInfos();
dependencyInfos.Add(CreateDependencyInfo("CoreFx", Config.Instance.CoreFxVersionUrl).Result);
return c.Success();
}
private static async Task<DependencyInfo> CreateDependencyInfo(string name, string packageVersionsUrl)
{
List<PackageInfo> newPackageVersions = new List<PackageInfo>();
using (Stream versionsStream = await s_client.GetStreamAsync(packageVersionsUrl))
using (StreamReader reader = new StreamReader(versionsStream))
{
string currentLine;
while ((currentLine = await reader.ReadLineAsync()) != null)
{
int spaceIndex = currentLine.IndexOf(' ');
newPackageVersions.Add(new PackageInfo()
{
Id = currentLine.Substring(0, spaceIndex),
Version = new NuGetVersion(currentLine.Substring(spaceIndex + 1))
});
}
}
string newReleaseVersion = newPackageVersions
.Where(p => p.Version.IsPrerelease)
.Select(p => p.Version.Release)
.FirstOrDefault()
??
// if there are no prerelease versions, just grab the first version
newPackageVersions
.Select(p => p.Version.ToNormalizedString())
.FirstOrDefault();
return new DependencyInfo()
{
Name = name,
NewVersions = newPackageVersions,
NewReleaseVersion = newReleaseVersion
};
}
[Target(nameof(ReplaceProjectJson), nameof(ReplaceCrossGen))]
public static BuildTargetResult ReplaceVersions(BuildTargetContext c) => c.Success();
/// <summary>
/// Replaces all the dependency versions in the project.json files.
/// </summary>
[Target]
public static BuildTargetResult ReplaceProjectJson(BuildTargetContext c)
{
List<DependencyInfo> dependencyInfos = c.GetDependencyInfos();
const string noUpdateFileName = ".noautoupdate";
IEnumerable<string> projectJsonFiles = Enumerable.Union(
Directory.GetFiles(Dirs.RepoRoot, "project.json", SearchOption.AllDirectories),
Directory.GetFiles(Path.Combine(Dirs.RepoRoot, @"src\dotnet\commands\dotnet-new"), "project.json.pretemplate", SearchOption.AllDirectories))
.Where(p => !File.Exists(Path.Combine(Path.GetDirectoryName(p), noUpdateFileName)));
JObject projectRoot;
foreach (string projectJsonFile in projectJsonFiles)
{
try
{
projectRoot = ReadProject(projectJsonFile);
}
catch (Exception e)
{
c.Warn($"Non-fatal exception occurred reading '{projectJsonFile}'. Skipping file. Exception: {e}. ");
continue;
}
if (projectRoot == null)
{
c.Warn($"A non valid JSON file was encountered '{projectJsonFile}'. Skipping file.");
continue;
}
bool changedAnyPackage = FindAllDependencyProperties(projectRoot)
.Select(dependencyProperty => ReplaceDependencyVersion(dependencyProperty, dependencyInfos))
.ToArray()
.Any(shouldWrite => shouldWrite);
if (changedAnyPackage)
{
c.Info($"Writing changes to {projectJsonFile}");
WriteProject(projectRoot, projectJsonFile);
}
}
return c.Success();
}
/// <summary>
/// Replaces the single dependency with the updated version, if it matches any of the dependencies that need to be updated.
/// </summary>
private static bool ReplaceDependencyVersion(JProperty dependencyProperty, List<DependencyInfo> dependencyInfos)
{
string id = dependencyProperty.Name;
foreach (DependencyInfo dependencyInfo in dependencyInfos)
{
foreach (PackageInfo packageInfo in dependencyInfo.NewVersions)
{
if (id == packageInfo.Id)
{
if (dependencyProperty.Value is JObject)
{
dependencyProperty.Value["version"] = packageInfo.Version.ToNormalizedString();
}
else
{
dependencyProperty.Value = packageInfo.Version.ToNormalizedString();
}
// mark the DependencyInfo as updated so we can tell which dependencies were updated
dependencyInfo.IsUpdated = true;
return true;
}
}
}
return false;
}
private static JObject ReadProject(string projectJsonPath)
{
using (TextReader projectFileReader = File.OpenText(projectJsonPath))
{
var projectJsonReader = new JsonTextReader(projectFileReader);
var serializer = new JsonSerializer();
return serializer.Deserialize<JObject>(projectJsonReader);
}
}
private static void WriteProject(JObject projectRoot, string projectJsonPath)
{
string projectJson = JsonConvert.SerializeObject(projectRoot, Formatting.Indented);
File.WriteAllText(projectJsonPath, projectJson + Environment.NewLine);
}
private static IEnumerable<JProperty> FindAllDependencyProperties(JObject projectJsonRoot)
{
return projectJsonRoot
.Descendants()
.OfType<JProperty>()
.Where(property => property.Name == "dependencies")
.Select(property => property.Value)
.SelectMany(o => o.Children<JProperty>());
}
/// <summary>
/// Replaces version number that is hard-coded in the CrossGen script.
/// </summary>
[Target]
public static BuildTargetResult ReplaceCrossGen(BuildTargetContext c)
{
ReplaceFileContents(@"build_projects\shared-build-targets-utils\DependencyVersions.cs", compileTargetsContent =>
{
DependencyInfo coreFXInfo = c.GetCoreFXDependency();
Regex regex = new Regex(@"CoreCLRVersion = ""(?<version>\d.\d.\d)-(?<release>.*)"";");
return regex.ReplaceGroupValue(compileTargetsContent, "release", coreFXInfo.NewReleaseVersion);
});
return c.Success();
}
private static DependencyInfo GetCoreFXDependency(this BuildTargetContext c)
{
return c.GetDependencyInfos().Single(d => d.Name == "CoreFx");
}
private static void ReplaceFileContents(string repoRelativePath, Func<string, string> replacement)
{
string fullPath = Path.Combine(Dirs.RepoRoot, repoRelativePath);
string contents = File.ReadAllText(fullPath);
contents = replacement(contents);
File.WriteAllText(fullPath, contents, Encoding.UTF8);
}
private static string ReplaceGroupValue(this Regex regex, string input, string groupName, string newValue)
{
return regex.Replace(input, m =>
{
string replacedValue = m.Value;
Group group = m.Groups[groupName];
int startIndex = group.Index - m.Index;
replacedValue = replacedValue.Remove(startIndex, group.Length);
replacedValue = replacedValue.Insert(startIndex, newValue);
return replacedValue;
});
}
}
}

View file

@ -1,28 +0,0 @@
{
"version": "1.0.0-*",
"description": "Updates the repos dependencies",
"buildOptions": {
"emitEntryPoint": true
},
"dependencies": {
"NETStandard.Library": "1.5.0-rc3-24123-01",
"Microsoft.CSharp": "4.0.1-rc3-24123-01",
"Microsoft.NETCore.Runtime": "1.0.2-rc3-24123-01",
"System.Runtime.Serialization.Primitives": "4.1.1-rc3-24123-01",
"Microsoft.DotNet.Cli.Build.Framework": {
"target": "project"
},
"NuGet.Versioning": "3.5.0-rc-1285",
"Newtonsoft.Json": "7.0.1",
"Octokit": "0.18.0",
"Microsoft.Net.Http": "2.2.29"
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"dnxcore50",
"portable-net45+win"
]
}
}
}

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>a28bd8ac-df15-4f58-8299-98a9ae2b8726</ProjectGuid>
<RootNamespace>Microsoft.DotNet.Scripts</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>