diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 078dd4095f..0b07e08a81 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -1040,9 +1040,42 @@ jobs: name: packaging-windows path: packaging/build/package/winget/Tgstation.Server.Host.Service.Msi/Release/tgstation-server.msi + check-winget-pr-template: + name: Check winget-pkgs Pull Request Template is up to date + needs: start-ci-run-gate + runs-on: ubuntu-latest + steps: + - name: Setup dotnet + uses: actions/setup-dotnet@v2 + with: + dotnet-version: ${{ env.TGS_DOTNET_VERSION }} + + - name: Retrieve winget-pkgs PULL_REQUEST_TEMPLATE commit history from GitHub API + id: get-json + run: | + echo "commits-response=$(curl -L -u "${{ vars.DEV_PUSH_USERNAME }}:${{ secrets.DEV_PUSH_TOKEN }}" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" https://api.github.com/repos/microsoft/winget-pkgs/commits?path=.github/PULL_REQUEST_TEMPLATE.md)" >> $GITHUB_OUTPUT + + - name: Output Latest SHA for winget-pkgs PULL_REQUEST_TEMPLATE + id: get-sha + run: | + echo "pr_template_sha=$(echo ${{ steps.get-json.outputs.commits-response[0].sha }})" >> $GITHUB_OUTPUT + + - name: Checkout (Branch) + uses: actions/checkout@v3 + if: github.event_name == 'push' || github.event_name == 'schedule' + + - name: Checkout (PR Merge) + uses: actions/checkout@v3 + if: github.event_name != 'push' && github.event_name != 'schedule' + with: + ref: "refs/pull/${{ github.event.number }}/merge" + + - name: Run ReleaseNotes Check + run: dotnet run -c Release --project tools/ReleaseNotes --winget-template-check ${{ steps.get-sha.outputs.pr_template_sha }} + deployment-gate: name: Deployment Gate - needs: [ dox-build, docker-build, build-deb, validate-openapi-spec, upload-code-coverage, build-msi ] + needs: [ dox-build, docker-build, build-deb, validate-openapi-spec, upload-code-coverage, build-msi, check-winget-pr-template ] runs-on: ubuntu-latest if: "!(cancelled() || failure()) && needs.dox-build.result == 'success' && needs.docker-build.result == 'success' && needs.build-deb.result == 'success' && needs.build-msi.result == 'success' && needs.validate-openapi-spec.result == 'success' && needs.upload-code-coverage.result == 'success' && github.event_name == 'push'" steps: diff --git a/tools/ReleaseNotes/Program.cs b/tools/ReleaseNotes/Program.cs index 7a6d8cc000..8c5904704b 100644 --- a/tools/ReleaseNotes/Program.cs +++ b/tools/ReleaseNotes/Program.cs @@ -31,9 +31,11 @@ namespace ReleaseNotes } var versionString = args[0]; - var ensureRelease = versionString == "--ensure-release"; + var ensureRelease = versionString.Equals("--ensure-release", StringComparison.OrdinalIgnoreCase); + var linkWinget = versionString.Equals("--link-winget", StringComparison.OrdinalIgnoreCase); + var shaCheck = versionString.Equals("--winget-template-check", StringComparison.OrdinalIgnoreCase); - if ((!Version.TryParse(versionString, out var version) || version.Revision != -1) && !ensureRelease) + if ((!Version.TryParse(versionString, out var version) || version.Revision != -1) && !ensureRelease && !linkWinget && !shaCheck) { Console.WriteLine("Invalid version: " + versionString); return 2; @@ -60,6 +62,28 @@ namespace ReleaseNotes if (ensureRelease) return await EnsureRelease(client); + if (linkWinget) + { + if (args.Length < 2 || !Uri.TryCreate(args[1], new UriCreationOptions(), out var actionsUrl)) + { + Console.WriteLine("Missing/Invalid actions URL!"); + return 30; + } + + return await Winget(client, actionsUrl, null); + } + + if (shaCheck) + { + if(args.Length < 2) + { + Console.WriteLine("Missing SHA for PR template!"); + return 32; + } + + return await Winget(client, null, args[1]); + } + var releasesTask = client.Repository.Release.GetAll(RepoOwner, RepoName); Console.WriteLine("Getting merged pull requests in milestone " + versionString + "..."); @@ -479,5 +503,72 @@ namespace ReleaseNotes return 0; } + + static async Task Winget(IGitHubClient client, Uri actionUrl, string expectedTemplateSha) + { + const string PropsPath = "build/Version.props"; + + var doc = XDocument.Load(PropsPath); + var project = doc.Root; + var xmlNamespace = project.GetDefaultNamespace(); + var versionsPropertyGroup = project.Elements().First(x => x.Name == xmlNamespace + "PropertyGroup"); + var coreVersion = Version.Parse(versionsPropertyGroup.Element(xmlNamespace + "TgsCoreVersion").Value); + + const string BodyForPRSha = "596da68f8da0926ae17a1497328e368d7b83aac2"; + var prBody = $@"# Automated Pull Request + +This pull request was generated by our [deployment pipeline]({actionUrl}) as a result of the release of [tgstation-server-v{coreVersion}](https://github.com/tgstation/tgstation-server/releases/tag/tgstation-server-v{coreVersion}). Validation was performed as part of the process. + +The user account that created this pull request is available to correct any issues. + +- [x] Have you signed the [Contributor License Agreement](https://cla.opensource.microsoft.com/microsoft/winget-pkgs)? +- [x] Have you checked that there aren't other open [pull requests](https://github.com/microsoft/winget-pkgs/pulls) for the same manifest update/change? + - This PR is generated as a direct result of a new release of `tgstation-server` this should be impossible +- [x] This PR only modifies one (1) manifest +- [x] Have you [validated](https://github.com/microsoft/winget-pkgs/blob/master/AUTHORING_MANIFESTS.md#validation) your manifest locally with `winget validate --manifest `? + - Validation is performed as a prerequisite to deployment. +- [x] Have you tested your manifest locally with `winget install --manifest `? + - Manifest installation and uninstallation is performed as a prerequisite to deployment. +- [x] Does your manifest conform to the [1.4 schema](https://github.com/microsoft/winget-pkgs/tree/master/doc/manifest/schema/1.4.0)?"; + + if (expectedTemplateSha != null) + { + if (expectedTemplateSha != BodyForPRSha) + { + Console.WriteLine("winget-pkgs pull request template has updated. This tool will need to be updated to match!"); + Console.WriteLine($"Expected {BodyForPRSha} found {expectedTemplateSha}"); + return 33; + } + + return 0; + } + + var clientUser = await client.User.Current(); + + var userPrsOnWingetRepo = await client.Search.SearchIssues(new SearchIssuesRequest + { + Author = clientUser.Name, + Is = new List { IssueIsQualifier.PullRequest }, + State = ItemState.Open, + Repos = new RepositoryCollection + { + { "microsoft", "winget-pkgs" }, + }, + }); + + var prToModify = userPrsOnWingetRepo.Items.OrderByDescending(pr => pr.Number).FirstOrDefault(); + if(prToModify == null) + { + Console.WriteLine("Could not find open winget-pkgs PR!"); + return 31; + } + + await client.Issue.Update(prToModify.Repository.Id, prToModify.Number, new IssueUpdate + { + Body = prBody, + }); + + return 0; + } } } diff --git a/tools/ReleaseNotes/README.md b/tools/ReleaseNotes/README.md index 63aa900ef3..2a71aad6e0 100644 --- a/tools/ReleaseNotes/README.md +++ b/tools/ReleaseNotes/README.md @@ -1,7 +1,21 @@ -This is a small tool to generate TGS release notes from PR descriptions +This is a small tool to automate generating TGS releases Requires environment variable `TGS_RELEASE_NOTES_TOKEN` Run it with `dotnet run [--no-close (optionally doesn't close the milestone, USE WHILE DEBUGGING)]` Will close the release milestone and output `release_notes.md` with the updated release notes + +Alternative modes + +`dotnet run --ensure-release` + +Ensures the latest GitHub release is a TGS release + +`dotnet run --link-winget ` + +Updates an existing https://github.com/microsoft/winget-pkgs manifest update pull request with the TGS template. The PR updated is the last one opened by the user + +`dotnet run --winget-template-check ` + +Validates the template we are PRing is up-to-date.