提交 cd00a632 编写于 作者: B Brett V. Forsgren

update auto-merge tool to support multiple repositories and options

上级 4b4ed766
......@@ -33,8 +33,8 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Octokit, Version=0.16.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Octokit.0.16.0\lib\net45\Octokit.dll</HintPath>
<Reference Include="Octokit, Version=0.17.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\packages\Octokit.0.17.0\lib\net45\Octokit.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
......@@ -47,6 +47,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Mono.Options\Options.cs" />
<Compile Include="Options.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
......
此差异已折叠。
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
namespace GitMergeBot
{
internal sealed class Options
{
public string AuthToken { get; set; }
public string RepoName { get; set; }
public string SourceBranch { get; set; }
public string DestinationBranch { get; set; }
public string SourceUser { get; set; }
public string DestinationUser { get; set; }
public bool Debug { get; set; }
public bool ShowHelp { get; set; }
public bool AreValid => new[] { AuthToken, RepoName, SourceBranch, DestinationBranch, SourceUser, DestinationUser }.All(s => s != null);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Octokit;
using System;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using Mono.Options;
using Octokit;
namespace ConsoleApplication2
namespace GitMergeBot
{
class Program
{
static int Main(string[] args)
{
var exeName = Assembly.GetExecutingAssembly().GetName().Name;
var options = new Options();
// default to using an environment variable, but allow an explicitly provided value to override
options.AuthToken = Environment.GetEnvironmentVariable("AUTH_CODE");
var parameters = new OptionSet()
{
$"Usage: {exeName} [options]",
"Create a pull request from the specified user and branch to another specified user and branch.",
"",
"Options:",
{ "a|auth=", "The GitHub authentication token.", value => options.AuthToken = value },
{ "r|repo=", "The name of the remote repository.", value => options.RepoName = value },
{ "s|source=", "The source branch of the merge operation.", value => options.SourceBranch = value },
{ "d|dest=", "The destination branch of the merge operation.", value => options.DestinationBranch = value },
{ "su|sourceuser=", "The user hosting the source branch of the merge operation.", value => options.SourceUser = value },
{ "du|destuser=", "The user hosting the destination branch of the merge operation.", value => options.DestinationUser = value },
{ "debug", "Print debugging information about the merge but don't actually create the pull request.", value => options.Debug = value != null },
{ "h|help", "Show this message and exit.", value => options.ShowHelp = value != null }
};
try
{
parameters.Parse(args);
}
catch (OptionException e)
{
Console.WriteLine($"{exeName}: {e.Message}");
Console.WriteLine($"Try `{exeName} --help` for more information.");
return 1;
}
if (options.ShowHelp || !options.AreValid)
{
parameters.WriteOptionDescriptions(Console.Out);
return options.AreValid ? 0 : 1;
}
else
{
var github = new GitHubClient(new ProductHeaderValue(options.SourceUser));
github.Credentials = new Credentials(options.AuthToken);
new Program().MakePullRequest(github, options).GetAwaiter().GetResult();
return 0;
}
}
public async Task MakePullRequest(GitHubClient github, Options options)
{
var remoteIntoBranch = await GetShaFromBranch(github, options.DestinationUser, options.RepoName, options.SourceBranch);
var newBranchName = await MakePrBranch(github, options, options.SourceUser, options.RepoName, remoteIntoBranch, $"merge-{options.SourceBranch}-into-{options.DestinationBranch}");
await SubmitPullRequest(github, options, newBranchName);
return;
}
/// <returns> The SHA at the tip of `branchName` in the repository `user/repo` </returns>
static async Task<string> GetShaFromBranch(GitHubClient github, string user, string repo, string branchName)
private async Task<string> GetShaFromBranch(GitHubClient github, string user, string repo, string branchName)
{
var refs = await github.GitDatabase.Reference.Get(user, repo, $"heads/{branchName}");
return refs.Object.Sha;
......@@ -20,19 +79,26 @@ static async Task<string> GetShaFromBranch(GitHubClient github, string user, str
/// Creates a PR branch on the bot account with the branch head at `sha`.
/// </summary>
/// <returns> The name of the branch that was created </returns>
static async Task<string> MakePrBranch(GitHubClient github, string user, string repo, string sha, string branchNamePrefix)
private async Task<string> MakePrBranch(GitHubClient github, Options options, string user, string repo, string sha, string branchNamePrefix)
{
var branchName = branchNamePrefix + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss");
var resp = await github.Connection.Post<string>(
uri: new Uri($"https://api.github.com/repos/{user}/{repo}/git/refs"),
body: $"{{\"ref\": \"refs/heads/{branchName}\", \"sha\": \"{sha}\"",
accepts: "*/*",
contentType: "application/json");
var statusCode = resp.HttpResponse.StatusCode;
if (statusCode != System.Net.HttpStatusCode.Created)
if (options.Debug)
{
WriteDebugLine($"Create remote branch '{user}/{repo}/{branchName}' at {sha}");
}
else
{
throw new Exception($"Failed creating a new branch {branchName} on {user}/{repo} with code {statusCode}");
var resp = await github.Connection.Post<string>(
uri: new Uri($"https://api.github.com/repos/{user}/{repo}/git/refs"),
body: $"{{\"ref\": \"refs/heads/{branchName}\", \"sha\": \"{sha}\"",
accepts: "*/*",
contentType: "application/json");
var statusCode = resp.HttpResponse.StatusCode;
if (statusCode != HttpStatusCode.Created)
{
throw new Exception($"Failed creating a new branch {branchName} on {user}/{repo} with code {statusCode}");
}
}
return branchName;
......@@ -41,83 +107,53 @@ static async Task<string> MakePrBranch(GitHubClient github, string user, string
/// <summary>
/// Creates a pull request
/// </summary>
static async Task SubmitPullRequest(GitHubClient github, string remoteUser, string myUser, string repoName,
string newBranchName, string fromBranch, string intoBranch)
private async Task SubmitPullRequest(GitHubClient github, Options options, string newBranchName)
{
await github.PullRequest.Create(remoteUser, repoName,
new NewPullRequest($"Merge {fromBranch} into {intoBranch}", head: $"{myUser}:{newBranchName}", baseRef: intoBranch) {
Body = $@"
This is an automatically generated pull request from {fromBranch} into {intoBranch}.
var remoteName = $"{options.SourceUser}-{options.RepoName}";
var prTitle = $"Merge {options.SourceBranch} into {options.DestinationBranch}";
var prMessage = $@"
This is an automatically generated pull request from {options.SourceBranch} into {options.DestinationBranch}.
@dotnet/roslyn-infrastructure:
```bash
git remote add roslyn-bot ""https://github.com/roslyn-bot/roslyn.git""
git fetch roslyn-bot
``` bash
git remote add {remoteName} ""https://github.com/{options.SourceUser}/{options.RepoName}.git""
git fetch {remoteName}
git checkout {newBranchName}
git reset --hard upstream/{intoBranch}
git merge upstream/{fromBranch}
git reset --hard upstream/{options.DestinationBranch}
git merge upstream/{options.SourceBranch}
# Fix merge conflicts
git commit
git push roslyn-bot {newBranchName} --force
git push {remoteName} {newBranchName} --force
```
Once the merge can be made and all the tests pass, you are free to merge the pull request.
".Trim();
".Trim()
});
}
static async Task MakePullRequest(GitHubClient github, string remoteUser, string myUser, string repoName, string fromBranch, string intoBranch)
{
var remoteIntoBranch = await GetShaFromBranch(github, remoteUser, repoName, fromBranch);
var newBranchName = await MakePrBranch(github, myUser, repoName, remoteIntoBranch, $"merge-{fromBranch}-into-{intoBranch}");
await SubmitPullRequest(github, remoteUser, myUser, repoName, newBranchName, fromBranch, intoBranch);
return;
}
static async Task _Main(GitHubClient github)
{
await MakePullRequest(github, remoteUser: "dotnet", myUser: "roslyn-bot", repoName: "roslyn", fromBranch: "master", intoBranch: "future");
}
static void PrintUsage()
{
Console.WriteLine("Usage:");
Console.WriteLine(" roslyn-merge-bot [auth-code]");
Console.WriteLine();
Console.WriteLine("If the auth-code parameter is not set, an environment variable named AUTH_CODE must be set.");
}
static int Main(string[] args)
{
string auth = null;
if (args.Length == 1)
if (options.Debug)
{
if (args[0] == "--help" || args[0] == "/help")
{
PrintUsage();
return 0;
}
auth = args[0];
WriteDebugLine($"Create PR with title: {prTitle}.");
WriteDebugLine($"Create PR with body:\r\n{prMessage}");
}
else
{
auth = (string) Environment.GetEnvironmentVariables()["AUTH_CODE"];
await github.PullRequest.Create(
owner: options.DestinationUser,
name: options.RepoName,
newPullRequest: new NewPullRequest(
title: prTitle,
head: $"{options.SourceUser}:{newBranchName}",
baseRef: options.DestinationBranch)
{
Body = prMessage
}
);
}
}
if (auth != null) {
var github = new GitHubClient(new ProductHeaderValue("roslyn-bot"));
github.Credentials = new Credentials(auth);
_Main(github).GetAwaiter().GetResult();
return 0;
}
else
{
PrintUsage();
return 1;
}
private void WriteDebugLine(string line)
{
Console.WriteLine("Debug: " + line);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Mono.Options" version="1.1" targetFramework="net452" />
<package id="Octokit" version="0.17.0" targetFramework="net452" />
</packages>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册