/*
         *     This command does the necessary preparations for releasing an rc-branch:
         *         1. Re-pointing all <RepoName>Version.txt files
         */
        public int Run()
        {
            Common.VerifySemanticVersioningFormat(options.Version);
            var(repoName, pullRequestId) = Common.ExtractPullRequestInfo(options.PullRequestUrl);
            var remoteUrl = string.Format(Common.RepoUrlTemplate, options.GithubOrgName, repoName);

            try
            {
                // 1. Clones the source repo.
                using (var gitClient = GitClient.FromRemote(remoteUrl))
                {
                    // 2. Checks out the candidate branch, which defaults to 4.xx-SpatialOSUnrealGDK-x.y.z-rc in UnrealEngine and x.y.z-rc in all other repos.
                    gitClient.CheckoutRemoteBranch(options.CandidateBranch);

                    bool madeChanges = false;

                    // 3. Makes repo-specific changes for prepping the release (e.g. updating version files, formatting the CHANGELOG).
                    switch (repoName)
                    {
                    case "UnrealEngine":
                        madeChanges |= Common.UpdateVersionFile(gitClient, options.Version, UnrealGDKVersionFile, Logger);
                        madeChanges |= Common.UpdateVersionFile(gitClient, options.Version, UnrealGDKExampleProjectVersionFile, Logger);
                        break;

                    case "UnrealGDK":
                        Logger.Info("Updating {0}...", ChangeLogFilename);
                        madeChanges |= Common.UpdateChangeLog(ChangeLogFilename, options.Version, gitClient, ChangeLogReleaseHeadingTemplate);
                        if (!madeChanges)
                        {
                            Logger.Info("{0} was already up-to-date.", ChangeLogFilename);
                        }
                        break;

                    case "UnrealGDKExampleProject":
                        madeChanges |= Common.UpdateVersionFile(gitClient, options.Version, UnrealGDKVersionFile, Logger);
                        break;

                    case "UnrealGDKTestGyms":
                        madeChanges |= Common.UpdateVersionFile(gitClient, options.Version, UnrealGDKVersionFile, Logger);
                        break;

                    case "UnrealGDKEngineNetTest":
                        madeChanges |= Common.UpdateVersionFile(gitClient, options.Version, UnrealGDKVersionFile, Logger);
                        break;

                    case "TestGymBuildKite":
                        madeChanges |= Common.UpdateVersionFile(gitClient, options.Version, UnrealGDKVersionFile, Logger);
                        break;
                    }

                    if (madeChanges)
                    {
                        // 4. Commit changes and push them to a remote candidate branch.
                        gitClient.Commit(string.Format(CandidateCommitMessageTemplate, options.Version));
                        gitClient.ForcePush(options.CandidateBranch);
                        Logger.Info($"Updated branch '${options.CandidateBranch}' in preparation for the full release.");
                    }
                    else
                    {
                        Logger.Info($"Tried to update branch '${options.CandidateBranch}' in preparation for the full release, but it was already up-to-date.");
                    }
                }
            }
            catch (Exception e)
            {
                Logger.Error(e, $"ERROR: Unable to update {options.CandidateBranch}. Error: {0}", e.Message);
                return(1);
            }

            return(0);
        }
Exemple #2
0
        /*
         *     This tool is designed to be used with a robot Github account. When we prep a release:
         *         1. Clones the source repo.
         *         2. Checks out the source branch, which defaults to 4.xx-SpatialOSUnrealGDK in UnrealEngine and master in all other repos.
         *         3. Makes repo-specific changes for prepping the release (e.g. updating version files, formatting the CHANGELOG).
         *         4. Commit changes and push them to a remote candidate branch.
         *         5. IF the release branch does not exist, creates it from the source branch and pushes it to the remote.
         *         6. Opens a PR for merging the RC branch into the release branch.
         */
        public int Run()
        {
            Common.VerifySemanticVersioningFormat(options.Version);

            var remoteUrl = string.Format(Common.RepoUrlTemplate, options.GithubOrgName, options.GitRepoName);

            try
            {
                var gitHubClient = new GitHubClient(options);
                // 1. Clones the source repo.
                using (var gitClient = GitClient.FromRemote(remoteUrl))
                {
                    // 2. Checks out the source branch, which defaults to 4.xx-SpatialOSUnrealGDK in UnrealEngine and master in all other repos.
                    gitClient.CheckoutRemoteBranch(options.SourceBranch);

                    if (!gitClient.LocalBranchExists($"origin/{options.CandidateBranch}"))
                    {
                        // 3. Makes repo-specific changes for prepping the release (e.g. updating version files, formatting the CHANGELOG).
                        switch (options.GitRepoName)
                        {
                        case "UnrealGDK":
                            UpdateChangeLog(ChangeLogFilename, options, gitClient);
                            UpdatePluginFile(pluginFileName, gitClient);

                            var engineCandidateBranches = options.EngineVersions.Split(" ")
                                                          .Select(engineVersion => $"HEAD {engineVersion.Trim()}-{options.Version}-rc")
                                                          .ToList();
                            UpdateUnrealEngineVersionFile(engineCandidateBranches, gitClient);
                            break;

                        case "UnrealEngine":
                            UpdateVersionFile(gitClient, $"{options.Version}-rc", UnrealGDKVersionFile);
                            UpdateVersionFile(gitClient, $"{options.Version}-rc", UnrealGDKExampleProjectVersionFile);
                            break;

                        case "UnrealGDKExampleProject":
                            UpdateVersionFile(gitClient, $"{options.Version}-rc", UnrealGDKVersionFile);
                            break;

                        case "UnrealGDKTestGyms":
                            UpdateVersionFile(gitClient, $"{options.Version}-rc", UnrealGDKVersionFile);
                            break;

                        case "UnrealGDKEngineNetTest":
                            UpdateVersionFile(gitClient, $"{options.Version}-rc", UnrealGDKVersionFile);
                            break;

                        case "TestGymBuildKite":
                            UpdateVersionFile(gitClient, $"{options.Version}-rc", UnrealGDKVersionFile);
                            break;
                        }

                        // 4. Commit changes and push them to a remote candidate branch.
                        gitClient.Commit(string.Format(CandidateCommitMessageTemplate, options.Version));
                        gitClient.ForcePush(options.CandidateBranch);
                    }

                    // 5. IF the release branch does not exist, creates it from the source branch and pushes it to the remote.
                    if (!gitClient.LocalBranchExists($"origin/{options.ReleaseBranch}"))
                    {
                        gitClient.Fetch();
                        gitClient.CheckoutRemoteBranch(options.CandidateBranch);
                        gitClient.Commit(string.Format(ReleaseBranchCreationCommitMessageTemplate, options.Version));
                        gitClient.ForcePush(options.ReleaseBranch);
                    }

                    // 6. Opens a PR for merging the RC branch into the release branch.
                    var gitHubRepo = gitHubClient.GetRepositoryFromUrl(remoteUrl);
                    var githubOrg  = options.GithubOrgName;
                    var branchFrom = options.CandidateBranch;
                    var branchTo   = options.ReleaseBranch;

                    // Only open a PR if one does not exist yet.
                    if (!gitHubClient.TryGetPullRequest(gitHubRepo, githubOrg, branchFrom, branchTo, out var pullRequest))
                    {
                        Logger.Info("No PR exists. Attempting to open a new PR");
                        pullRequest = gitHubClient.CreatePullRequest(gitHubRepo,
                                                                     branchFrom,
                                                                     branchTo,
                                                                     string.Format(PullRequestTemplate, options.Version),
                                                                     GetPullRequestBody(options.GitRepoName, options.CandidateBranch, options.ReleaseBranch));
                    }

                    BuildkiteAgent.SetMetaData($"{options.GitRepoName}-{options.SourceBranch}-pr-url", pullRequest.HtmlUrl);

                    var prAnnotation = string.Format(prAnnotationTemplate,
                                                     pullRequest.HtmlUrl, options.GitRepoName, options.CandidateBranch, options.ReleaseBranch);
                    BuildkiteAgent.Annotate(AnnotationLevel.Info, "candidate-into-release-prs", prAnnotation, true);

                    Logger.Info("Pull request available: {0}", pullRequest.HtmlUrl);
                    Logger.Info("Successfully created release!");
                    Logger.Info("Release hash: {0}", gitClient.GetHeadCommit().Sha);
                }
            }
            catch (Exception e)
            {
                Logger.Error(e, "ERROR: Unable to prep release candidate branch. Error: {0}", e);
                return(1);
            }

            return(0);
        }
Exemple #3
0
        internal static void UpdateChangeLog(string ChangeLogFilePath, Options options, GitClient gitClient)
        {
            using (new WorkingDirectoryScope(gitClient.RepositoryPath))
            {
                if (File.Exists(ChangeLogFilePath))
                {
                    Logger.Info("Updating {0}...", ChangeLogFilePath);

                    var changelog = File.ReadAllLines(ChangeLogFilePath).ToList();

                    // If we already have a changelog entry for this release. Skip this step.
                    if (changelog.Any(line => IsMarkdownHeading(line, 2, $"[`{options.Version}`] - ")))
                    {
                        Logger.Info($"Changelog already has release version {options.Version}. Skipping..", ChangeLogFilePath);
                        return;
                    }

                    // First add the new release heading under the "## Unreleased" one.
                    // Assuming that this is the first heading.
                    var unreleasedIndex = changelog.FindIndex(line => IsMarkdownHeading(line, 2));
                    var releaseHeading  = string.Format(ChangeLogReleaseHeadingTemplate, options.Version,
                                                        DateTime.Now);

                    changelog.InsertRange(unreleasedIndex + 1, new[]
                    {
                        string.Empty,
                        releaseHeading
                    });

                    File.WriteAllLines(ChangeLogFilePath, changelog);
                    gitClient.StageFile(ChangeLogFilePath);
                }
            }
        }
        /*
         *     This tool is designed to be used with a robot Github account. When we prep a release:
         *         1. Clones the source repo.
         *         2. Checks out the source branch, which defaults to 4.xx-SpatialOSUnrealGDK in UnrealEngine and master in all other repos.
         *         3. Makes repo-specific changes for prepping the release (e.g. updating version files, formatting the CHANGELOG).
         *         4. Commit changes and push them to a remote candidate branch.
         *         5. IF the release branch does not exist, creates it from the source branch and pushes it to the remote.
         *         6. Opens a PR for merging the RC branch into the release branch.
         */
        public int Run()
        {
            Common.VerifySemanticVersioningFormat(options.Version);
            var gitRepoName = options.GitRepoName;
            var remoteUrl   = Common.makeRepoUrl(options.GithubOrgName, gitRepoName);

            try
            {
                // 1. Clones the source repo.
                using (var gitClient = GitClient.FromRemote(remoteUrl))
                {
                    if (!gitClient.LocalBranchExists($"origin/{options.CandidateBranch}"))
                    {
                        // 2. Checks out the source branch, which defaults to 4.xx-SpatialOSUnrealGDK in UnrealEngine and master in all other repos.
                        gitClient.CheckoutRemoteBranch(options.SourceBranch);

                        // 3. Makes repo-specific changes for prepping the release (e.g. updating version files, formatting the CHANGELOG).
                        // UpdateVersionFilesWithEngine returns a bool to indicate if anything has changed, we could use this to only push when
                        // version files etc have changed which may be reasonable but might have side-effects as our github ci interactions are fragile
                        Common.UpdateVersionFilesWithEngine(gitClient, gitRepoName, options.Version, options.EngineVersions, Logger, "-rc");
                        // 4. Commit changes and push them to a remote candidate branch.
                        gitClient.Commit(string.Format(CandidateCommitMessageTemplate, options.Version));
                        gitClient.ForcePush(options.CandidateBranch);
                        Logger.Info($"Updated branch '{options.CandidateBranch}' for the release candidate release.");
                    }

                    // 5. IF the release branch does not exist, creates it from the source branch and pushes it to the remote.
                    if (!gitClient.LocalBranchExists($"origin/{options.ReleaseBranch}"))
                    {
                        Logger.Info("The release branch {0} does not exist! Going ahead with the PR-less release process.", options.ReleaseBranch);
                        Logger.Info("Release candidate head hash: {0}", gitClient.GetHeadCommit().Sha);
                        var branchAnnotation = string.Format(branchAnnotationTemplate,
                                                             $"https://github.com/{options.GithubOrgName}/{options.GitRepoName}/tree/{options.CandidateBranch}", options.GitRepoName, options.ReleaseBranch);
                        BuildkiteAgent.Annotate(AnnotationLevel.Info, "candidate-into-release-prs", branchAnnotation, true);
                        return(0);
                    }

                    // 6. Opens a PR for merging the RC branch into the release branch.
                    var gitHubClient = new GitHubClient(options);
                    var gitHubRepo   = gitHubClient.GetRepositoryFromUrl(remoteUrl);
                    var githubOrg    = options.GithubOrgName;
                    var branchFrom   = options.CandidateBranch;
                    var branchTo     = options.ReleaseBranch;

                    // Only open a PR if one does not exist yet.
                    if (!gitHubClient.TryGetPullRequest(gitHubRepo, githubOrg, branchFrom, branchTo, out var pullRequest))
                    {
                        Logger.Info("No PR exists. Attempting to open a new PR");
                        pullRequest = gitHubClient.CreatePullRequest(gitHubRepo,
                                                                     branchFrom,
                                                                     branchTo,
                                                                     string.Format(PullRequestTemplate, options.Version),
                                                                     GetPullRequestBody(options.GitRepoName, options.CandidateBranch, options.ReleaseBranch));
                    }

                    BuildkiteAgent.SetMetaData($"{options.GitRepoName}-{options.SourceBranch}-pr-url", pullRequest.HtmlUrl);

                    var prAnnotation = string.Format(prAnnotationTemplate,
                                                     pullRequest.HtmlUrl, options.GitRepoName, options.CandidateBranch, options.ReleaseBranch);
                    BuildkiteAgent.Annotate(AnnotationLevel.Info, "candidate-into-release-prs", prAnnotation, true);

                    Logger.Info("Pull request available: {0}", pullRequest.HtmlUrl);
                    Logger.Info("Successfully created pull request for the release!");
                    Logger.Info("PR head hash: {0}", gitClient.GetHeadCommit().Sha);
                }
            }
            catch (Exception e)
            {
                Logger.Error(e, "ERROR: Unable to prep release candidate branch. Error: {0}", e);
                return(1);
            }

            return(0);
        }
Exemple #5
0
        private void CreatePRFromReleaseToSource(GitHubClient gitHubClient, Repository gitHubRepo, string repoUrl, string repoName, GitClient gitClient)
        {
            // Check if a PR has already been opened from release branch into source branch.
            // If it has, log the PR URL and move on.
            // This ensures the idempotence of the pipeline.
            var githubOrg  = options.GithubOrgName;
            var branchFrom = $"{options.CandidateBranch}-cleanup";
            var branchTo   = options.SourceBranch;

            if (!gitHubClient.TryGetPullRequest(gitHubRepo, githubOrg, branchFrom, branchTo, out var pullRequest))
            {
                try
                {
                    if (gitClient == null)
                    {
                        using (gitClient = GitClient.FromRemote(repoUrl))
                        {
                            gitClient.CheckoutRemoteBranch(options.ReleaseBranch);
                            gitClient.ForcePush(branchFrom);
                        }
                    }
                    else
                    {
                        gitClient.CheckoutRemoteBranch(options.ReleaseBranch);
                        gitClient.ForcePush(branchFrom);
                    }
                    pullRequest = gitHubClient.CreatePullRequest(gitHubRepo,
                                                                 branchFrom,
                                                                 branchTo,
                                                                 string.Format(PullRequestNameTemplate, options.Version, options.ReleaseBranch, options.SourceBranch),
                                                                 string.Format(pullRequestBody, options.ReleaseBranch, options.SourceBranch));
                }
                catch (Octokit.ApiValidationException e)
                {
                    // Handles the case where source-branch (default master) and release-branch (default release) are identical, so there is no need to merge source-branch back into release-branch.
                    if (e.ApiError.Errors.Count > 0 && e.ApiError.Errors[0].Message.Contains("No commits between"))
                    {
                        Logger.Info(e.ApiError.Errors[0].Message);
                        Logger.Info("No PR will be created.");
                        return;
                    }

                    throw;
                }
            }
            else
            {
                Logger.Info("A PR has already been opened from release branch into source branch: {0}", pullRequest.HtmlUrl);
            }

            var prAnnotation = string.Format(prAnnotationTemplate,
                                             pullRequest.HtmlUrl, repoName, options.ReleaseBranch, options.SourceBranch);

            BuildkiteAgent.Annotate(AnnotationLevel.Info, "release-into-source-prs", prAnnotation, true);

            Logger.Info("Pull request available: {0}", pullRequest.HtmlUrl);
            Logger.Info($"Successfully created PR for merging {options.ReleaseBranch} into {options.SourceBranch}.");
        }
Exemple #6
0
        private Release CreateRelease(GitHubClient gitHubClient, Repository gitHubRepo, GitClient gitClient, string repoName)
        {
            var headCommit = gitClient.GetHeadCommit().Sha;

            var engineVersion = options.SourceBranch.Trim();

            string tag = options.Version; // Default tag, only changed for Engine versions currently
            string name;
            string releaseBody;

            switch (repoName)
            {
            case "UnrealGDK":
                string changelog;
                using (new WorkingDirectoryScope(gitClient.RepositoryPath))
                {
                    changelog = GetReleaseNotesFromChangeLog();
                }
                name        = $"GDK for Unreal Release {options.Version}";
                releaseBody =
                    $@"The release notes are published in both English and Chinese. To view the Chinese version, scroll down a bit for details. Thanks!

Release notes 将同时提供中英文。要浏览中文版本,向下滚动页面查看详情。感谢!

# English version

**Unreal GDK version {options.Version} has been released!**

## Release Notes

* **Release sheriff:** Your human labour is required to populate this section with the headline new features and breaking changes from the CHANGELOG.

## Upgrading

* You can find the corresponding UnrealEngine version(s) [here](https://github.com/improbableio/UnrealEngine/releases).
* You can find the corresponding UnrealGDKExampleProject version [here](https://github.com/spatialos/UnrealGDKExampleProject/releases/tag/{options.Version}).

Follow **[these](https://documentation.improbable.io/gdk-for-unreal/docs/keep-your-gdk-up-to-date)** steps to upgrade your GDK, Engine fork and Example Project to the latest release.

You can read the full release notes [here](https://github.com/spatialos/UnrealGDK/blob/release/CHANGELOG.md) or below.

Join the community on our [forums](https://forums.improbable.io/), or on [Discord](https://discordapp.com/invite/vAT7RSU).

Happy developing,

*The GDK team*

---

{changelog}

# 中文版本

**[虚幻引擎开发套件 (GDK) {options.Version} 版本已发布!**

## Release Notes

* **Tech writer:** Your human labour is required to translate the above and include it here.

";
                break;

            case "UnrealEngine":
                tag         = $"{engineVersion}-{options.Version}";
                name        = $"{engineVersion}-{options.Version}";
                releaseBody =
                    $@"Unreal GDK version {options.Version} has been released!

* This Engine version corresponds to GDK version: [{options.Version}](https://github.com/spatialos/UnrealGDK/releases/tag/{options.Version}).
* You can find the corresponding UnrealGDKExampleProject version [here](https://github.com/spatialos/UnrealGDKExampleProject/releases/tag/{options.Version}).

Follow [these steps](https://documentation.improbable.io/gdk-for-unreal/docs/keep-your-gdk-up-to-date) to upgrade your GDK, Unreal Engine fork and your Project to the latest release.

You can read the full release notes [here](https://github.com/spatialos/UnrealGDK/blob/release/CHANGELOG.md).

Join the community on our [forums](https://forums.improbable.io/), or on [Discord](https://discordapp.com/invite/vAT7RSU).

Happy developing!<br>
GDK team";
                break;

            case "UnrealGDKTestGyms":
                name        = $"Unreal GDK Test Gyms {options.Version}";
                releaseBody =
                    $@"Unreal GDK version {options.Version} has been released!

* This UnrealGDKTestGyms version corresponds to GDK version: [{options.Version}](https://github.com/spatialos/UnrealGDK/releases/tag/{options.Version}).
* You can find the corresponding UnrealGDKExampleProject version [here](https://github.com/spatialos/UnrealGDKExampleProject/releases/tag/{options.Version}).
* You can find the corresponding UnrealEngine version(s) [here](https://github.com/improbableio/UnrealEngine/releases).

Follow [these steps](https://documentation.improbable.io/gdk-for-unreal/docs/keep-your-gdk-up-to-date) to upgrade your GDK, Unreal Engine fork and your Project to the latest release.

You can read the full release notes [here](https://github.com/spatialos/UnrealGDK/blob/release/CHANGELOG.md).

Join the community on our [forums](https://forums.improbable.io/), or on [Discord](https://discordapp.com/invite/vAT7RSU).

Happy developing!<br>
GDK team";
                break;

            case "UnrealGDKEngineNetTest":
                name        = $"Unreal GDK EngineNetTest {options.Version}";
                releaseBody =
                    $@"Unreal GDK version {options.Version} has been released!

* This UnrealGDKEngineNetTest version corresponds to GDK version: [{options.Version}](https://github.com/spatialos/UnrealGDK/releases/tag/{options.Version}).
* You can find the corresponding UnrealGDKTestGyms version [here](https://github.com/improbable/UnrealGDKTestGyms/releases/tag/{options.Version}).
* You can find the corresponding UnrealGDKExampleProject version [here](https://github.com/spatialos/UnrealGDKExampleProject/releases/tag/{options.Version}).
* You can find the corresponding UnrealEngine version(s) [here](https://github.com/improbableio/UnrealEngine/releases).

Follow [these steps](https://documentation.improbable.io/gdk-for-unreal/docs/keep-your-gdk-up-to-date) to upgrade your GDK, Unreal Engine fork and your Project to the latest release.

You can read the full release notes [here](https://github.com/spatialos/UnrealGDK/blob/release/CHANGELOG.md).

Join the community on our [forums](https://forums.improbable.io/), or on [Discord](https://discordapp.com/invite/vAT7RSU).

Happy developing!<br>
GDK team";
                break;

            case "TestGymBuildKite":
                name        = $"Unreal GDK TestGymBuildKite {options.Version}";
                releaseBody =
                    $@"Unreal GDK version {options.Version} has been released!

* This TestGymBuildKite version corresponds to GDK version: [{options.Version}](https://github.com/spatialos/UnrealGDK/releases/tag/{options.Version}).
* You can find the corresponding UnrealGDKTestGyms version [here](https://github.com/improbable/UnrealGDKTestGyms/releases/tag/{options.Version}).
* You can find the corresponding UnrealGDKExampleProject version [here](https://github.com/spatialos/UnrealGDKExampleProject/releases/tag/{options.Version}).
* You can find the corresponding UnrealEngine version(s) [here](https://github.com/improbableio/UnrealEngine/releases).

Follow [these steps](https://documentation.improbable.io/gdk-for-unreal/docs/keep-your-gdk-up-to-date) to upgrade your GDK, Unreal Engine fork and your Project to the latest release.

You can read the full release notes [here](https://github.com/spatialos/UnrealGDK/blob/release/CHANGELOG.md).

Join the community on our [forums](https://forums.improbable.io/), or on [Discord](https://discordapp.com/invite/vAT7RSU).

Happy developing!<br>
GDK team";
                break;

            case "UnrealGDKExampleProject":
                name        = $"Unreal GDK Example Project {options.Version}";
                releaseBody =
                    $@"Unreal GDK version {options.Version} has been released!

* This UnrealGDKExampleProject version corresponds to GDK version: [{options.Version}](https://github.com/spatialos/UnrealGDK/releases/tag/{options.Version}).
* You can find the corresponding UnrealEngine version(s) [here](https://github.com/improbableio/UnrealEngine/releases).

Follow [these steps](https://documentation.improbable.io/gdk-for-unreal/docs/keep-your-gdk-up-to-date) to upgrade your GDK, Unreal Engine fork and your Project to the latest release.

You can read the full release notes [here](https://github.com/spatialos/UnrealGDK/blob/release/CHANGELOG.md).

Join the community on our [forums](https://forums.improbable.io/), or on [Discord](https://discordapp.com/invite/vAT7RSU).

Happy developing!<br>
GDK team";
                break;

            default:
                throw new ArgumentException("Unsupported repository.", nameof(repoName));
            }

            return(gitHubClient.CreateDraftRelease(gitHubRepo, tag, releaseBody, name, headCommit));
        }
Exemple #7
0
        /*
         *     This tool is designed to execute most of the git operations required when releasing:
         *         1. Merge the RC PR into the release branch.
         *         2. Draft a GitHub release using the changelog notes.
         *         3. Open a PR from the release-branch into source-branch.
         */
        public int Run()
        {
            Common.VerifySemanticVersioningFormat(options.Version);
            var(repoName, pullRequestId) = Common.ExtractPullRequestInfo(options.PullRequestUrl);
            var gitHubClient = new GitHubClient(options);
            var repoUrl      = string.Format(Common.RepoUrlTemplate, options.GithubOrgName, repoName);
            var gitHubRepo   = gitHubClient.GetRepositoryFromUrl(repoUrl);

            // Check if the PR has been merged already.
            // If it has, log the PR URL and move on.
            // This ensures the idempotence of the pipeline.
            if (gitHubClient.GetMergeState(gitHubRepo, pullRequestId) == GitHubClient.MergeState.AlreadyMerged)
            {
                Logger.Info("Candidate branch has already merged into release branch. No merge operation will be attempted.");

                // null for GitClient will let it create one if necessary
                CreatePRFromReleaseToSource(gitHubClient, gitHubRepo, repoUrl, repoName, null);

                return(0);
            }

            var remoteUrl = string.Format(Common.RepoUrlTemplate, options.GithubOrgName, repoName);

            try
            {
                // Only do something for the UnrealGDK, since the other repos should have been prepped by the PrepFullReleaseCommand.
                if (repoName == "UnrealGDK")
                {
                    // 1. Clones the source repo.
                    using (var gitClient = GitClient.FromRemote(remoteUrl))
                    {
                        // 2. Checks out the candidate branch, which defaults to 4.xx-SpatialOSUnrealGDK-x.y.z-rc in UnrealEngine and x.y.z-rc in all other repos.
                        gitClient.CheckoutRemoteBranch(options.CandidateBranch);

                        // 3. Makes repo-specific changes for prepping the release (e.g. updating version files, formatting the CHANGELOG).
                        Common.UpdateChangeLog(ChangeLogFilename, options.Version, gitClient, ChangeLogReleaseHeadingTemplate);

                        var releaseHashes = options.EngineVersions.Split(" ")
                                            .Select(version => $"{version.Trim()}-release")
                                            .Select(BuildkiteAgent.GetMetadata)
                                            .Select(hash => $"UnrealEngine-{hash}")
                                            .ToList();

                        UpdateUnrealEngineVersionFile(releaseHashes, gitClient);

                        // 4. Commit changes and push them to a remote candidate branch.
                        gitClient.Commit(string.Format(CandidateCommitMessageTemplate, options.Version));
                        gitClient.ForcePush(options.CandidateBranch);
                    }
                }

                // Since we've (maybe) pushed changes, we need to wait for all checks to pass before attempting to merge it.
                var startTime = DateTime.Now;
                while (true)
                {
                    if (DateTime.Now.Subtract(startTime) > TimeSpan.FromHours(12))
                    {
                        throw new Exception($"Exceeded timeout waiting for PR to be mergeable: {options.PullRequestUrl}");
                    }

                    if (gitHubClient.GetMergeState(gitHubRepo, pullRequestId) == GitHubClient.MergeState.ReadyToMerge)
                    {
                        Logger.Info($"{options.PullRequestUrl} is mergeable. Attempting to merge.");
                        break;
                    }

                    Logger.Info($"{options.PullRequestUrl} is not in a mergeable state, will query mergeability again in one minute.");
                    Thread.Sleep(TimeSpan.FromMinutes(1));
                }

                PullRequestMerge mergeResult = null;
                while (true)
                {
                    // Merge into release
                    try
                    {
                        mergeResult = gitHubClient.MergePullRequest(gitHubRepo, pullRequestId, PullRequestMergeMethod.Merge, $"Merging final GDK for Unreal {options.Version} release");
                    }
                    catch (Octokit.PullRequestNotMergeableException e) {
                        Logger.Info($"Was unable to merge pull request at: {options.PullRequestUrl}. Received error: {e.Message}");
                    }
                    if (DateTime.Now.Subtract(startTime) > TimeSpan.FromHours(12))
                    {
                        throw new Exception($"Exceeded timeout waiting for PR to be mergeable: {options.PullRequestUrl}");
                    }
                    if (!mergeResult.Merged)
                    {
                        Logger.Info($"{options.PullRequestUrl} is not in a mergeable state, will query mergeability again in one minute.");
                        Thread.Sleep(TimeSpan.FromMinutes(1));
                    }
                    else
                    {
                        break;
                    }
                }

                Logger.Info($"{options.PullRequestUrl} had been merged.");

                // This uploads the commit hashes of the merge into release.
                // When run against UnrealGDK, the UnrealEngine hashes are used to update the unreal-engine.version file to include the UnrealEngine release commits.
                BuildkiteAgent.SetMetaData(options.ReleaseBranch, mergeResult.Sha);

                // TODO: UNR-3615 - Fix this so it does not throw Octokit.ApiValidationException: Reference does not exist.
                // Delete candidate branch.
                //gitHubClient.DeleteBranch(gitHubClient.GetRepositoryFromUrl(repoUrl), options.CandidateBranch);

                using (var gitClient = GitClient.FromRemote(repoUrl))
                {
                    // Create GitHub release in the repo
                    gitClient.Fetch();
                    gitClient.CheckoutRemoteBranch(options.ReleaseBranch);
                    var release = CreateRelease(gitHubClient, gitHubRepo, gitClient, repoName);

                    BuildkiteAgent.Annotate(AnnotationLevel.Info, "draft-releases",
                                            string.Format(releaseAnnotationTemplate, release.HtmlUrl, repoName), true);

                    Logger.Info("Release Successful!");
                    Logger.Info("Release hash: {0}", gitClient.GetHeadCommit().Sha);
                    Logger.Info("Draft release: {0}", release.HtmlUrl);

                    CreatePRFromReleaseToSource(gitHubClient, gitHubRepo, repoUrl, repoName, gitClient);
                }
            }
            catch (Exception e)
            {
                Logger.Error(e, $"ERROR: Unable to merge {options.CandidateBranch} into {options.ReleaseBranch} and/or clean up by merging {options.ReleaseBranch} into {options.SourceBranch}. Error: {0}", e.Message);
                return(1);
            }

            return(0);
        }
Exemple #8
0
 public static bool UpdateVersionFilesButNotEngine(GitClient gitClient, string gitRepoName, string versionRaw, NLog.Logger logger, string versionSuffix = "")
 {
     return(UpdateVersionFiles_Internal(gitClient, gitRepoName, versionRaw, logger, versionSuffix));
 }