From 1f143fc192b902cd0562f7292d0aa0cf55832b1e Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 17 Dec 2025 09:32:30 -0500 Subject: [PATCH 1/3] Remove What's new We don't use this tool anymore. --- Whats-new/Whats-new.sln | 43 -- WhatsNew.Cli/.vscode/extensions.json | 5 - WhatsNew.Cli/CONTRIBUTING.md | 46 -- WhatsNew.Cli/Program.cs | 97 ---- WhatsNew.Cli/README.md | 155 ------ WhatsNew.Cli/WhatsNew.Cli.csproj | 44 -- WhatsNew.Cli/action.yml | 52 -- WhatsNew.Cli/nuget/images/docs-logo-ms.png | Bin 3841 -> 0 bytes .../Models/PageGeneratorInputTests.cs | 80 --- .../Services/ConfigurationServiceTests.cs | 62 --- .../Services/SchemaValidationServiceTests.cs | 84 --- .../WhatsNew.Infrastructure.Tests.csproj | 34 -- .../MicrosoftDocs/azure-devops-docs-pr.json | 17 - .../assets/MicrosoftDocs/cpp-docs-pr.json | 13 - .../assets/dotnet/AspNetCore.Docs.json | 13 - .../assets/dotnet/docs.json | 15 - .../xunit.runner.json | 4 - .../Configuration/reposettings.schema.json | 221 -------- WhatsNew.Infrastructure/Constants.cs | 24 - .../Models/DocLinkSettings.cs | 24 - .../Models/InclusionCriteria.cs | 47 -- WhatsNew.Infrastructure/Models/Landing.cs | 52 -- .../Models/NavigationDetails.cs | 36 -- .../Models/PageGeneratorInput.cs | 125 ----- .../Models/RepositoryArea.cs | 19 - .../Models/RepositoryDetail.cs | 89 ---- WhatsNew.Infrastructure/Models/Toc.cs | 39 -- .../Models/WhatsNewConfiguration.cs | 70 --- .../Services/ConfigurationService.cs | 103 ---- .../Services/IndexUpdateService.cs | 65 --- .../Services/PageGenerationService.cs | 500 ------------------ .../Services/SchemaValidationService.cs | 69 --- .../Services/TocUpdateService.cs | 75 --- .../WhatsNew.Infrastructure.csproj | 23 - docs-tools.sln | 24 +- whatsnew.Dockerfile | 10 - 36 files changed, 3 insertions(+), 2376 deletions(-) delete mode 100644 Whats-new/Whats-new.sln delete mode 100644 WhatsNew.Cli/.vscode/extensions.json delete mode 100644 WhatsNew.Cli/CONTRIBUTING.md delete mode 100644 WhatsNew.Cli/Program.cs delete mode 100644 WhatsNew.Cli/README.md delete mode 100644 WhatsNew.Cli/WhatsNew.Cli.csproj delete mode 100644 WhatsNew.Cli/action.yml delete mode 100644 WhatsNew.Cli/nuget/images/docs-logo-ms.png delete mode 100644 WhatsNew.Infrastructure.Tests/Models/PageGeneratorInputTests.cs delete mode 100644 WhatsNew.Infrastructure.Tests/Services/ConfigurationServiceTests.cs delete mode 100644 WhatsNew.Infrastructure.Tests/Services/SchemaValidationServiceTests.cs delete mode 100644 WhatsNew.Infrastructure.Tests/WhatsNew.Infrastructure.Tests.csproj delete mode 100644 WhatsNew.Infrastructure.Tests/assets/MicrosoftDocs/azure-devops-docs-pr.json delete mode 100644 WhatsNew.Infrastructure.Tests/assets/MicrosoftDocs/cpp-docs-pr.json delete mode 100644 WhatsNew.Infrastructure.Tests/assets/dotnet/AspNetCore.Docs.json delete mode 100644 WhatsNew.Infrastructure.Tests/assets/dotnet/docs.json delete mode 100644 WhatsNew.Infrastructure.Tests/xunit.runner.json delete mode 100644 WhatsNew.Infrastructure/Configuration/reposettings.schema.json delete mode 100644 WhatsNew.Infrastructure/Constants.cs delete mode 100644 WhatsNew.Infrastructure/Models/DocLinkSettings.cs delete mode 100644 WhatsNew.Infrastructure/Models/InclusionCriteria.cs delete mode 100644 WhatsNew.Infrastructure/Models/Landing.cs delete mode 100644 WhatsNew.Infrastructure/Models/NavigationDetails.cs delete mode 100644 WhatsNew.Infrastructure/Models/PageGeneratorInput.cs delete mode 100644 WhatsNew.Infrastructure/Models/RepositoryArea.cs delete mode 100644 WhatsNew.Infrastructure/Models/RepositoryDetail.cs delete mode 100644 WhatsNew.Infrastructure/Models/Toc.cs delete mode 100644 WhatsNew.Infrastructure/Models/WhatsNewConfiguration.cs delete mode 100644 WhatsNew.Infrastructure/Services/ConfigurationService.cs delete mode 100644 WhatsNew.Infrastructure/Services/IndexUpdateService.cs delete mode 100644 WhatsNew.Infrastructure/Services/PageGenerationService.cs delete mode 100644 WhatsNew.Infrastructure/Services/SchemaValidationService.cs delete mode 100644 WhatsNew.Infrastructure/Services/TocUpdateService.cs delete mode 100644 WhatsNew.Infrastructure/WhatsNew.Infrastructure.csproj delete mode 100644 whatsnew.Dockerfile diff --git a/Whats-new/Whats-new.sln b/Whats-new/Whats-new.sln deleted file mode 100644 index d35444a6..00000000 --- a/Whats-new/Whats-new.sln +++ /dev/null @@ -1,43 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.4.33213.308 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNet.DocsTools", "..\DotNet.DocsTools\DotNet.DocsTools.csproj", "{E0C6601E-92D4-4ABC-910F-1E821E6E50FF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhatsNew.Infrastructure", "..\WhatsNew.Infrastructure\WhatsNew.Infrastructure.csproj", "{685C95BC-48AD-409C-99D1-BFFECC49197B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhatsNew.Cli", "..\WhatsNew.Cli\WhatsNew.Cli.csproj", "{8B4F4F60-69E9-451B-86FF-0FFA1153463C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhatsNew.Infrastructure.Tests", "..\WhatsNew.Infrastructure.Tests\WhatsNew.Infrastructure.Tests.csproj", "{2180F87A-1276-4546-936F-CA4A900EE126}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E0C6601E-92D4-4ABC-910F-1E821E6E50FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E0C6601E-92D4-4ABC-910F-1E821E6E50FF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E0C6601E-92D4-4ABC-910F-1E821E6E50FF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E0C6601E-92D4-4ABC-910F-1E821E6E50FF}.Release|Any CPU.Build.0 = Release|Any CPU - {685C95BC-48AD-409C-99D1-BFFECC49197B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {685C95BC-48AD-409C-99D1-BFFECC49197B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {685C95BC-48AD-409C-99D1-BFFECC49197B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {685C95BC-48AD-409C-99D1-BFFECC49197B}.Release|Any CPU.Build.0 = Release|Any CPU - {8B4F4F60-69E9-451B-86FF-0FFA1153463C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8B4F4F60-69E9-451B-86FF-0FFA1153463C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8B4F4F60-69E9-451B-86FF-0FFA1153463C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8B4F4F60-69E9-451B-86FF-0FFA1153463C}.Release|Any CPU.Build.0 = Release|Any CPU - {2180F87A-1276-4546-936F-CA4A900EE126}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2180F87A-1276-4546-936F-CA4A900EE126}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2180F87A-1276-4546-936F-CA4A900EE126}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2180F87A-1276-4546-936F-CA4A900EE126}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {3734EC12-CDED-4DF3-B756-F2A07670C13D} - EndGlobalSection -EndGlobal diff --git a/WhatsNew.Cli/.vscode/extensions.json b/WhatsNew.Cli/.vscode/extensions.json deleted file mode 100644 index e1ec9524..00000000 --- a/WhatsNew.Cli/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "recommendations": [ - "ms-dotnettools.csharp" - ] -} diff --git a/WhatsNew.Cli/CONTRIBUTING.md b/WhatsNew.Cli/CONTRIBUTING.md deleted file mode 100644 index ae151a13..00000000 --- a/WhatsNew.Cli/CONTRIBUTING.md +++ /dev/null @@ -1,46 +0,0 @@ -## Prerequisites - -The following components are required to run the tool locally: - -- Visual Studio 2022 (17.4) with the **.NET Core cross-platform development** workload -- [.NET SDK 7.0](https://dotnet.microsoft.com/download/dotnet/7.0) or later - -## Set up a GitHub personal access token - -A GitHub personal access token (PAT) is required to access the GitHub API. Use the following steps to satisfy the PAT requirement: - -1. Generate a PAT per the instructions at [Creating a token](https://help.github.com/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line#creating-a-token). When selecting scopes for the PAT, check the following boxes: - - For private repos: - - **repo** - - **admin:org** > **read:org** - - For public repos: - - **repo** > **public_repo** - - **admin:org** > **read:org** -1. For private repos in the *MicrosoftDocs* GitHub organization, after generating the PAT, select **Enable SSO** and choose **Authorize** for both **microsoft** and **MicrosoftDocs**. -1. Store the PAT in an environment variable named `GitHubKey`. - -## Set up an OSPO personal access token - -> This step is required for versions 2.0 and above. - -The OSPO personal access token is required to access the OSPO API. This API determines community contributors and Microsoft employee and vendor contributors. - -1. Request a token at [Visual Studio Online](https://ossmsft.visualstudio.com/_usersSettings/tokens). You can disable all scopes *except* read:user profile. -1. Store the token in an environment variable named "OSPO_KEY". If you are using the GitHub Action to generate the PR automatically, add the key as a secret in your repository: - - Go to **Settings** on your repo. - - Select **Secrets** - - Add "OSPO_KEY" as a **Repository Secret**. - -## Build and run - -There are 2 supported approaches to running the tool locally, as outlined in the following sections. Choose the approach that best suits your needs. - -### Run from the generated DLL - -1. Build the *WhatsNew.Cli* project. -1. Navigate to the *WhatsNew.Cli* project's *bin\Debug\net5.0* directory. -1. Run the appropriate `dotnet whatsnew.dll` command from the aforementioned directory. For example: - - ```bash - dotnet whatsnew.dll -h - ``` diff --git a/WhatsNew.Cli/Program.cs b/WhatsNew.Cli/Program.cs deleted file mode 100644 index e50a65c6..00000000 --- a/WhatsNew.Cli/Program.cs +++ /dev/null @@ -1,97 +0,0 @@ -using WhatsNew.Infrastructure.Models; -using WhatsNew.Infrastructure.Services; - -namespace WhatsNew.Cli; - -/// -/// The entry point for the CLI tool. -/// -public class Program -{ - /// - /// Generate the Markdown file for a docs "what's new" page. For usage details, see https://aka.ms/whats-new-tool. - /// - /// The GitHub organization name. - /// The GitHub repository name within the provided organization. - /// The branch name within the provided repository. - /// The product name within the provided repository. - /// A range start date in a valid format. - /// A range end date in a valid format. - /// An absolute directory path to which the generated Markdown file should be written. - /// The path to the repository root folder. - /// An absolute file path to a local JSON configuration file. For local testing only. - /// The path to the single markdown file for repos that use one markdown file. - /// The generating the what's new page. - public static async Task Main( - string? startdate, string? enddate, string owner, string repo, - string? branch, string? docset, string? savedir, string? reporoot, string? localconfig, - string? savefile) - { - var today = DateTime.Now; - var firstOfMonth = new DateTime(today.Year, today.Month, 1) - .AddMonths(-1); - var lastOfMonth = firstOfMonth.AddMonths(1).AddDays(-1); - var LastMonth = firstOfMonth.ToString("MMMM yyyy"); - var input = new PageGeneratorInput - { - DateStart = string.IsNullOrWhiteSpace(startdate) ? firstOfMonth.ToShortDateString() : startdate, - DateEnd = string.IsNullOrWhiteSpace(enddate) ? lastOfMonth.ToShortDateString() : enddate, - MonthYear = string.IsNullOrWhiteSpace(startdate) ? LastMonth : null, - Owner = owner, - Repository = repo, - Branch = branch, - DocSet = docset, - SaveDir = savedir, - RepoRoot = reporoot ?? "./", - LocalConfig = localconfig, - }; - - // The config service makes one or two optional calls to GH to read - // the config file and get the current default branch. - // The GH API returns JSON on auth failures, so those queries fail - // with very cryptic error messages. This try/catch block catches - // those errors and provides a more helpful message. - WhatsNewConfiguration? whatsNewConfig; - try - { - var configService = new ConfigurationService(); - whatsNewConfig = await configService.GetConfiguration(input); - } - catch (Exception ex) - { - Console.WriteLine($"Could not initialize default branch and What's new configuration. Exiting."); - Console.WriteLine("The likely cause is an authentication failure. Check the following:"); - Console.WriteLine("\tIs the GitHub token valid?"); - Console.WriteLine("\tIs the GitHub token authorized for single sign-on (SSO)?"); - Console.WriteLine("\tDoes the GitHub token have the correct scopes?"); - Console.WriteLine("See https://github.com/dotnet/docs-tools/blob/main/WhatsNew.Cli/README.md#usage for detailed instructions"); - - Console.WriteLine($"Error message: {ex.Message}"); - return; - } - - Console.WriteLine(whatsNewConfig.LogConfigSettings()); - if (savefile is not null) - { - Console.WriteLine($"Writing output to {savefile}"); - } - else - { - Console.WriteLine($"Writing output to {whatsNewConfig.SaveDir}"); - } - - var pageGenService = new PageGenerationService(whatsNewConfig); - - await pageGenService.WriteMarkdownFile(savefile); - if (string.IsNullOrWhiteSpace(savefile)) - { - var tocService = new TocUpdateService(whatsNewConfig); - await tocService.UpdateWhatsNewToc(); - - var indexService = new IndexUpdateService(whatsNewConfig); - await indexService.UpdateWhatsNewLandingPage(); - } - whatsNewConfig?.OspoClient?.Dispose(); - whatsNewConfig?.GitHubClient?.Dispose(); - } -} diff --git a/WhatsNew.Cli/README.md b/WhatsNew.Cli/README.md deleted file mode 100644 index 6025e910..00000000 --- a/WhatsNew.Cli/README.md +++ /dev/null @@ -1,155 +0,0 @@ -## Onboard a repository / docset - -### Create a configuration file - -Complete the following steps in Visual Studio Code: - -1. Create a JSON configuration file within the GitHub repository: - - For docsets in the *azure-docs-pr* repository, the file's naming convention is *.\{SERVICE-NAME}.json*. The file should live in the repository's *.whatsnew* directory. Replace the *\{SERVICE-NAME}* placeholder with the directory name within *articles* in which the docs are stored. For example, name the file *.app-service.json* for Azure App Service docs. - - For all other docsets, the file should be named *.whatsnew.json* and live in the repository's root directory. -1. Associate your JSON configuration file with the JSON schema by adding a `$schema` property to line 2 of the JSON configuration file. The `$schema` property's value should be set as follows: - - ```json - "$schema": "https://github.com/dotnet/docs-tools/blob/main/WhatsNew.Infrastructure/Configuration/reposettings.schema.json", - ``` - -1. Populate the various properties in the JSON file with the help of the supporting JSON schema. For more information about JSON schema support in Visual Studio Code, see [JSON schemas and settings](https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings). See also the supported [configuration file settings](#configuration-file-settings). - -### Request a Power BI dashboard update - -Metrics for the "what's new" pages are made available at [aka.ms/whatsnewindocs](https://aka.ms/whatsnewindocs). Contact [whatsnewindocs@microsoft.com](mailto:whatsnewindocs@microsoft.com) to have your docset added to this Power BI dashboard. - -## Usage - -You can use the tool from the command line, or as a GitHub action. If you run on the command line, the tool generates the changes for a new article on your machine. If you run as a GitHub action, the action opens a PR for you. The only functional difference in the output is that the GitHub action filters Microsoft FTEs from the contributor list. The command line version doesn't due to access rights. - -To use the tool as a command line executable: - -1. Clone the `dotnet/docs-tools` repository. -1. Generate a PAT per the instructions at [Creating a token](https://help.github.com/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line#creating-a-token). When selecting scopes for the PAT, check the following boxes: - - For private repos: - - **repo** - - **admin:org** > **read:org** - - For public repos: - - **repo** > **public_repo** - - **admin:org** > **read:org** -1. For private repos in the *MicrosoftDocs* GitHub organization, after generating the PAT, select **Enable SSO** and choose **Authorize** for the **MicrosoftDocs**. -1. Store the PAT in an environment variable named `GitHubKey`. -1. Install the [.NET SDK 8.0](https://dotnet.microsoft.com/download/dotnet) or later. -1. build and publish the tool. The `WhatsNew.Cli` folder is at the root of the repository you closed in step 1. By default, this builds the *Debug* configuration. - - ```bash - cd ./WhatsNew.Cli - dotnet build - dotnet publish - ``` - -1. Run the appropriate `dotnet whatsnew` command. See the examples below: - - ```bash - cd work/visualstudio-docs-pr - dotnet ../docs-tools/WhatsNew.Cli/bin/Debug/net7.0/publish/WhatsNew.dll --owner MicrosoftDocs --repo visualstudio-docs-pr --savefile ./docs/ide/whats-new-visual-studio-docs.md - ``` - -To use the tool as an action: - -1. Configure the registered `CLIENT_ID`, `TENANT_ID` and `OSMP_API_AUDIENCE` keys in your repository. Ask the "what's new" admins for the values. -1. Request app registration for your repo / branch pair. -1. Create a the `whats-new.yml` file in the `.github/Workflows` folder of your repo. The file should generally follow the form in the [dotnet/docs](https://github.com/dotnet/docs/blob/main/.github/workflows/whats-new.yml) version. - -### Examples - -**Display the help menu:** - -```bash -dotnet WhatsNew.dll -h -``` - -**Generate the *dotnet/AspNetCore.Docs* repo's what's new page for the period starting 5/1/2020 and ending 5/31/2020. Process PRs in the *dev* branch.** - -```bash -dotnet WhatsNew.dll --owner dotnet --repo AspNetCore.Docs --branch dev --startdate 2020-05-01 --enddate 2020-05-31 -``` - -**Generate the Cognitive Services docset's what's new page for the period starting 5/1/2020 and ending 5/31/2020. Process PRs in the repository's default branch.** - -```bash -dotnet WhatsNew.dll --owner MicrosoftDocs --repo azure-docs-pr --docset cognitive-services --startdate 2020-05-01 --enddate 2020-05-31 -``` - -**Generate the *dotnet/docs* repo's what's new page for the period starting 7/1/2020 and ending 7/5/2020. Process PRs in the repository's default branch. Save the generated Markdown file in the */Users/janedoe/docs* directory:** - -```bash -dotnet WhatsNew.Cli.dll --owner dotnet --repo docs --startdate 7/1/2020 --enddate 7/5/2020 --savedir /Users/janedoe/docs -``` - -## Command line options - -| Option | Description | Example | -| --------------------- | ----------- | ------- | -| `owner`*| The GitHub organization name. | `--owner MicrosoftDocs` | -| `repo`* | The name of the GitHub repository within the provided organization. | `--repo azure-docs-pr` | -| `docset` | The product name within the provided repository. Required only for monolithic repos, such as *azure-docs-pr*. | `--docset cognitive-services` | -| `branch` | The branch name within the provided repository. If not provided, the repository's default branch is used. | `--branch dev` | -| `startdate` | A range start date in a valid format. For example, "yyyy-MM-dd" or "MM/dd/yyyy". Any format accepted by [`DateTime.Parse`](https://learn.microsoft.com/dotnet/api/system.datetime.parse)` is accepted. | `--startdate 7/1/2020` | -| `enddate` | A range end date in a valid format. For example, "yyyy-MM-dd" or "MM/dd/yyyy". Any format accepted by [`DateTime.Parse`](https://learn.microsoft.com/dotnet/api/system.datetime.parse)` is accepted. | `--enddate 7/15/2020` | -| `savedir` | An absolute directory path to which the generated Markdown file should be written. | `--savedir C:\whatsnew` | -| `savefile` | The path to an existing file that should be modified by the tool. If this option isn't supplied, the tool uses the [Navigation options](#navigationoptions-properties) to determine any index and TOC file that should be updated. | `--savefile ./docs/ide/whats-new-visual-studio-docs.md` | -| `reporoot` | An absolute directory path to the root folder of your repository. Default is "./" | `--reporoot C:\source\dotnet\docs`| -| `localconfig` | An absolute file path for a local JSON configuration file. Intended for local testing only. | `--localconfig C:\configs\.whatsnew.json` | - -*Indicates a required option - -When the `startdate` or `enddate` arguments are omitted, they default to the first and last days of the previous month, respectively. - -The `reporoot` setting is used to read the titles from changed files. If you run the tool from a folder other than the root of your repo, you must set this option to point to the local copy of your repo. - -## Configuration file settings - -The following properties are supported in the JSON configuration file. - -### Top-level properties - -| Property | Description | Example | -| --------------------| ----------- | ------- | -| `areas`* | A list of key-value pairs used to specify the directories within `rootDirectory` to process. The `names` property is an array that represents the directory name(s). The `heading` property represents the heading text to appear in the generated Markdown file. | `"areas": [{ "names": [ "getting-started", "quickstarts" ], "heading": "Getting started"}]` | -| `docLinkSettings`* | Settings to control the construction of links to docs in the generated Markdown. See [docLinkSettings properties](#doclinksettings-properties). | `"docLinkSettings": { "linkFormat": "relative", "relativeLinkPrefix": "/dotnet/" }` | -| `docSetProductName`*| The name of the product supported by this docset. This value is used in the H1 heading and other locations in the generated Markdown file. | `"docSetProductName": ".NET"` | -| `rootDirectory`* | The GitHub repository's root directory path containing the docs. | `"rootDirectory": "docs/"` | -| `inclusionCriteria` | Settings to control the inclusion/exclusion of PRs and community contributors. See [inclusionCriteria properties](#inclusioncriteria-properties). | `"inclusionCriteria": { "minAdditionsToFile": 60 }` | -| `navigationOptions` | Settings used to update the index.yml and toc.yml files. If null, those files aren't updated. | `"navigationOptions": { "maximumNumberOfArticles": 3, "tocParentNode": "What's new", "repoTocFolder": "docs/whats-new", "indexParentNode": "Find .NET updates", "repoIndexFolder": "docs/whats-new" }` | - -*Indicates a required property - -### `inclusionCriteria` properties - -| Property | Description | Example | -| --------------------------- | ----------- | ------- | -| `omitPullRequestTitles` | A flag indicating whether to display pull request titles in the generated Markdown file. Default value: `false` | `"omitPullRequestTitles": true` | -| `labels` | A list of GitHub label filters to apply. The label filters will be converted to a space-delimited string. Default value: `[]` | `"labels": [ "label:cognitive-services/svc" ]` | -| `maxFilesChanged` | The maximum number of changed files that a pull request can contain before being ignored. Default value: `75` | `"maxFilesChanged": 50` | -| `minAdditionsToFile` | The minimum number of lines changed that a pull request file must contain before being included. Default value: `75` | `"minAdditionsToFile": 62` | -| `pullRequestTitlesToIgnore` | A comma-delimited list of regular expressions matching pull request titles to ignore. | `"pullRequestTitlesToIgnore": [ "^Confirm merge from repo_sync_working_branch", "^Repo sync for protected CLA branch" ]` | - -### `docLinkSettings` properties - -| Property | Description | Example | -| -------------------- | ----------- | ------- | -| `linkFormat`* | The Markdown format to use when creating links to docs. Possible values: `relative`, `siteRelative`, `xref`.

`siteRelative` links don't include file extensions; `relative` links do. | `"linkFormat": "relative"` | -| `relativeLinkPrefix` | The path that prefixes the doc link. Required when `linkFormat` is set to `relative` or `siteRelative`. | `"relativeLinkPrefix": "/dotnet/"` | - -*Indicates a required property - -### `navigationOptions` properties - -These options apply in the 2.0 release of the What's New tool. Furthermore, these options are only used when the `--savefile` option isn't included on the command line. When the safe tile isn't included, the tool assumes separate files are created for each month. In addition to updating the files, the tool updates the associated TOC nodes, and any index nodes. - -The 2.0 release will update nodes in a TOC and an index file in addition to creating the What's New file. That requires config options for how many What's New files to keep publishing, and where to find the YML nodes to update. - -| Property | Description | Example | -| ------------------------- | ----------- | ------- | -| `maximumNumberOfArticles` | The maximum number of "What's new" articles or sections to keep. Once the value is reached, the older article or section gets deleted from your local repository storage. Default value: `3` | `"maximumNumberOfArticles": 6` | -| `repoTocFolder` | The folder where the TOC for what's new articles is located relative to the repo root. Unused when you keep one file with multiple entries. Default value: `null` | `"repoTocFolder": "docs/whats-new"` | -| `tocParentNode` | The parent node in your TOC file for the "What's New" articles. Unused when you keep one file with multiple entries. Default value: `null`. Set to null if you don't want the tool to update your TOC. | `"tocParentNode": "Latest documentation updates"` | -| `repoIndexFolder` | The folder where the `index.yml` for What's new" articles is stored relative to the repo root. Unused when you keep one file with multiple entries. Default value: `null`. Set to null if you don't want the tool to update your TOC. | `repoIndexFolder": "docs/whats-new"` | -| `indexParentNode` | The parent node in the `index.yml` file where "What's New" articles are listed. Unused when you keep one file with multiple entries. Default value: `null`. Set to null if you don't want the tool to update your TOC. | `"indexParentNode": "Latest documentation updates"` | diff --git a/WhatsNew.Cli/WhatsNew.Cli.csproj b/WhatsNew.Cli/WhatsNew.Cli.csproj deleted file mode 100644 index 841f6fb3..00000000 --- a/WhatsNew.Cli/WhatsNew.Cli.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - net9.0 - enable - enable - Exe - WhatsNew - true - true - dotnet-whatsnew - Linux - - - Scott Addie - A command-line tool to generate what's new pages for docs.microsoft.com content sets. - docs-logo-ms.png - dotnet-whatsnew - ./nuget - dotnet;docs;whatsnew - git - https://github.com/dotnet/docs-tools - - - 3.1.0.0 - 3.1.0.0 - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/WhatsNew.Cli/action.yml b/WhatsNew.Cli/action.yml deleted file mode 100644 index 00e90a31..00000000 --- a/WhatsNew.Cli/action.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: 'What''s new in docs' -description: 'Automatically generate a PR that adds a "what''s new in docs" file for the recent time period.' -author: Bill Wagner -# https://actions-cool.github.io/github-action-branding/ -branding: - icon: 'zap' - color: 'purple' -inputs: - owner: - description: 'The GitHub organization name.' - required: true - repo: - description: 'The GitHub repository name within the provided organization.' - required: true - branch: - description: 'The branch name within the provided repository.' - default: main - docset: - description: 'The product name within the provided repository.' - startdate: - description: 'A range start date in a valid format.' - enddate: - description: 'A range end date in a valid format.' - savedir: - description: 'A directory path to which the generated Markdown file should be written. Used when a new file is generated each month.' - required: true - savefile: - description: 'A file path to which the generated Markdown file should be written. Used when a single file is modified each month.' - reporoot: - description: 'The path to the repository root folder.' -runs: - using: docker - image: ../whatsnew.Dockerfile - args: - - '--owner' - - ${{ inputs.owner }} - - '--repo' - - ${{ inputs.repo }} - - '--branch' - - ${{ inputs.branch }} - - '--docset' - - ${{ inputs.docset }} - - '--startdate' - - ${{ inputs.startdate }} - - '--enddate' - - ${{ inputs.enddate }} - - '--savedir' - - ${{ inputs.savedir }} - - '--savefile' - - ${{ inputs.savefile }} - - '--reporoot' - - ${{ inputs.reporoot }} diff --git a/WhatsNew.Cli/nuget/images/docs-logo-ms.png b/WhatsNew.Cli/nuget/images/docs-logo-ms.png deleted file mode 100644 index 2d6843d9914de4b29e27321710f249f2bcf1dff4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3841 zcmb7H`8U)J7yphK3}YF@*q$+iB4o=F8OEBe$<|o13^6>)Qpk`PG$t__MUrgMX6%xR zL@H!ycw_6x3V;II7ITl z0p&Q%w(jglhX4w5IAa2ocS|oF5{SR?Sz`dGN`visa~^V@P)pY^0Dy`78zA7}BXIx_ zxNl`<>_qbXp34({#9XrV_6L)fPxr0(OIdvD?rrj=|y<@nUvvhSreNIyIJZ6@?_WRN@ z17{Mkx3janxEzdk&p$IGzL-ec_bcGB03Lcn3MBq-kc2E|KYUP}!HRMhOQBQ;aNcg7 zy&^2yyYbRD!yGOdY=sJ9Yjk4! z4C`~dylhFVh%c+%TEgj4wL{)ag_mU$y2=hU^{Z)t@OA#`P!P%UtZ5i~4v|V!u1>~U zl9%4-)jXxeL$-F-SNWbpl@8dUZ?R~^D!99uSYvL|s8+hI`bVcn(iVI&`g6+7wnm9s z3nL#lV-HiQMmvVIp3~5{Kf7jsFFXxID=8c)|o(|uV%WL6$%~;6^ zl+xPAp3%19%59FBS@nzI2YZ5U8(lcb`q2!(_Wb0THyc8VNnT*T221RG9Vg_qO{R9s zQ&p}YJA85TD1ZN=t$ptxU!m8U)A@U!FW1V|^A{{K`(_z8fC_;5-t5JqTt`Lxr-68` zKpN=DI@uY3K2?l3>6!DXs3f6_QQq&$a8sGD)Eg(Bf=r9+C!~)|RoEX~WRSU-n;~9_ zuW)FGsUUD(+|TC6{pU<&i}^!yx4U0?qveOj|1tW)=y$6y)?e$cs1aUMd*{Y!w0iIY zO7ByfDZa}I> zjWI)vT)U<#O)*oKt8V-gMh679wjv|kRw68oG%kA)ead0cG3sw=a3_Bmi69cdDV-22 zl=_Xn2Mgn!7_Qpn_9Xxn+>11PeNCpK`&~nwko`Eq16IAbF<(GIgvhTY*~Xq_Md#F3t%AUs|KNU%7tlXZ-J>V_^kp!_cr&BpJ3b#4PsK^W zb$1!Bi;pu+D=sy3wF7@Q1k4rK`o6R{jl^CldnNQazy$OYoHNeK*&W2)cQ3S+o?-(j zMcRS^+_yftB`&db65AC5o!b_N`A5Jxe`OB;2yZ?`w!X+w!F|g-!DdqQH}0&!n~m~p zgUP;aoMwFML**^4?)eagBk^N$Z_dl;@$Y#x^sg)Z7#@i-&?mBzg@qJ$$MO6#oQ*y2 zmcik&{wz#z6aBoT*T4ZF(i(IXYn-W#=eQ2~Z#ZUzz;5l^O^sMgLTa0DUP+Lw2PW5I zFPh-4Wk8ux&pEOtpUZ?!{_=Pu*M}yJ0>GxFuyU81+`tHTB@8ZdvFa7dj7Jc2o&;;&sx$qzK%cpHM#|0dBE;bmX&tmU<;(Hg8oX zjDz1QDC~w4YK8-0Y=ALd*dvuLq+P5iLUPx-&WofM(`ixR=ELV37*3Tilo-#KJ4cL9 z!o%deC&!e&C8L0P;IhunZgZV+5&^_Vw4s$qNZ6a=*ZP|7V&LSjv&4l)!bd$J2mt|{ z`V-|{(N|0WE$3)Gv#yYp3w)pz_}o_ZX)lW0vLvbRC-g&Ang?_(#6#aFlvoKc0rwXr zrWSqbm$8#gWl`{gD%Z%h5NC&DhL+IEC!x)KJS`W?`M0*e(6Y}}oYB!atVXj|^-d~q z!GPGH+K52UJb9KZ=%tI3Bt+4W^(#U2!f_E9c?gLu=$O@+FBBHkw6K%*zWR!S-wb#M z%Xh1138g8rByT|I9V)y4Lu{n=nS_$$6G8@$b<_A<$>c3PGI8x?4?E8K2X4NGxMMcF2nCom0y&~uLjUy{deOsms-NGY8C=Y>e zVf#Qt;Q;2%C@wB8EnQvgDWcJ6&v5x+BP{}E{@srfCW`S4Ow87Lrm{0GSO5I^vXVyo zI|6mEAEN*W$^*!$Wh_GsR1xgJTFHdZKj%BfgLTtdr$bv}>p}So7rqIm`}UWd-`?I< z%H-um9|>Q-tZq)g3+Ue5&uNl(_HXOznrFpGHZe0Kb8+pto!@R68uX!yT2pQ4HfT`X z%@U?iBr$TOr!}q;?CbE-fJ3(!LQg_t*9`Xt8XB$tq!2H*&Z)RnXIMva)mU*0@d<0b zxjOq<*YR#~JT8nqt>u4**BkV5mCZ9-ldR0hTevVK7NBW0gghZ^L*?sO8a}N&_i*pWq*qW>q^aMwxgCe!Ht5&bu!!9bEdRQO-42ki zCaFa-vluAo9yCOW@ygE(Oa_X$T9U}}o?E|L)g~i1;l7a)x%eAf%)%8>1gaPMNmDGx zzfQ8jbFg4F-BBjy!Ai*W`nDInjGDZ0ClBh4B`zLNbhzV>VRkR3J8d zZgWHhFODElcf+?w#y+j>$876xlXIzN(7QIDI6SjF)BBI9+{enNWF0eq#Re&cy8J~Q zO}Yt$9@{N{SCncHgVZ$^OXGV4X8{r`DkN`+B`<%hWq+kR84<*a1UB8&~oKIv@qY=c|c40w1-Z}-$H&fi8q1K z3HMV=6NMUe>MK|MzMK9U_@66m?#-L7EsCgM?^$@c#eya5Tb`r$z(rp00`p7K+7~7M zt8HbM+MKnAz~Fmi0GXz6{U;39`H%6Cl(zu2%odvKx)J>@xK82FB_o9o4+d%Xu!q44 ztErrJmgE5R447=EBUJ9wRNk(QKZ;Q4bWI*-)D}?@}@F(Oqs#w`?@kf8e{( zRWZM5P(9$NATW6_T~ZPqQ3<#uzSvFTJsgG}yA(f&PXO$uqBkcW0(3nNIP# zH@4=hYe!#5+Y%6j@723m2tzSh?1rf*X=h?a0k)3g_C`$Y{f6b|VUC&sIcp-;^2j)7 ziabSY{i#Pu6L+j)HhWIWN#Ix|ta2?;G|d(%DvK?B3G!EU8pG@zRpJ&I;`V9(f+zO7 zs)%KEB)_7$Q4pyPs?(wCcp*weC`^;G5b`LGIqa{b$Y^+FBuNvJ7!a> zv?}$N>k{T>aqtNUZ$r$ik_=Z2Ov6MG)( zx=7~yuDjDC%jqd-r@PWo!RWq^9~cnXv%5a{;<-bvp4XKp)s2N#)CxAJ0SQN~+aH_~ zyR=o%7=Jp>8|2;mBiz_OZ7gNNTwUAr!=dQdlhBjj#mwhErs-PiZnxhSN5 zhke4WXd+-h>I7GjXwp?;O72^o!ew)l_)f%%+5S71Y!v0QNHm=ZD-foG^_x%Onr4k{ z7ZQD>acvt$sr=lyKQ)xqgrmJ2K>h9e8_VgCOq>4Fk@wW)0f|nhPRf_s5or|9ABrVl zh(7YuS6Jf--95>SgW|BeGiR3FH6261p{Msb_&LZT1X@WQ{|U~PQD~iY_<$kSra1sk lMvMR7Ki&TXnXg|TfVvoq|{0D~Q1KR)q diff --git a/WhatsNew.Infrastructure.Tests/Models/PageGeneratorInputTests.cs b/WhatsNew.Infrastructure.Tests/Models/PageGeneratorInputTests.cs deleted file mode 100644 index 3d583372..00000000 --- a/WhatsNew.Infrastructure.Tests/Models/PageGeneratorInputTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -using WhatsNew.Infrastructure.Models; -using Xunit; - -namespace WhatsNew.Infrastructure.Tests.Models; - -public class PageGeneratorInputTests -{ - [Theory] - [InlineData("", "")] -#pragma warning disable xUnit1012 // Null should only be used for nullable parameters - [InlineData(null, "")] - [InlineData("", null)] - [InlineData("2020-05-01", null)] - [InlineData(null, "2020-05-31")] -#pragma warning restore xUnit1012 // Null should only be used for nullable parameters - [InlineData("2020-05-01", "")] - [InlineData("", "2020-05-31")] - public void Empty_Or_Null_Date_Throws_ArgumentException(string startDate, string endDate) - { - var exception = Assert.Throws(() => - new PageGeneratorInput - { - Owner = "dotnet", - Repository = "AspNetCore.Docs", - DateStart = startDate, - DateEnd = endDate, - }); - Assert.StartsWith("The parameter cannot be null, empty, or whitespace.", exception.Message); - } - - [Theory] - [InlineData("")] - [InlineData(" ")] -#pragma warning disable xUnit1012 // Null should only be used for nullable parameters - [InlineData(null)] -#pragma warning restore xUnit1012 // Null should only be used for nullable parameters - public void Empty_Or_Null_Owner_Name_Throws_ArgumentException(string ownerName) - { - var exception = Assert.Throws(() => - new PageGeneratorInput - { - Owner = ownerName, - DateStart = "2020-05-01", - DateEnd = "2020-05-31", - }); - Assert.Equal($"The parameter 'Owner' cannot be null, empty, or whitespace.", exception.Message); - } - - [Theory] - [InlineData("")] - [InlineData(" ")] -#pragma warning disable xUnit1012 // Null should only be used for nullable parameters - [InlineData(null)] -#pragma warning restore xUnit1012 // Null should only be used for nullable parameters - public void Empty_Or_Null_Repo_Name_Throws_ArgumentException(string repoName) - { - var exception = Assert.Throws(() => - new PageGeneratorInput - { - Owner = "dotnet", - Repository = repoName, - DateStart = "2020-05-01", - DateEnd = "2020-05-31", - }); - Assert.Equal($"The parameter 'Repository' cannot be null, empty, or whitespace.", exception.Message); - } - - [Fact] - public void Using_Specific_Dates_Has_MonthYear_Null() - { - var input = new PageGeneratorInput - { - Owner = "dotnet", - Repository = "docs", - DateStart = "2020-05-01", - DateEnd = "2020-05-31", - }; - Assert.Null(input.MonthYear); - } -} diff --git a/WhatsNew.Infrastructure.Tests/Services/ConfigurationServiceTests.cs b/WhatsNew.Infrastructure.Tests/Services/ConfigurationServiceTests.cs deleted file mode 100644 index 6b5bff8d..00000000 --- a/WhatsNew.Infrastructure.Tests/Services/ConfigurationServiceTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -using WhatsNew.Infrastructure.Models; -using WhatsNew.Infrastructure.Services; -using Xunit; - -namespace WhatsNew.Infrastructure.Tests.Services; - -public class ConfigurationServiceTests -{ - private readonly ConfigurationService _service; - - public ConfigurationServiceTests() - { - _service = new(); - } - - [Fact(Skip = "Flaky in Az Pipelines build")] - public async Task Valid_Org_And_Repo_With_Invalid_DocSet_Throws_FileNotFoundException() - { - var input = new PageGeneratorInput - { - Owner = "dotnet", - Repository = "docs", - Branch = "main", - DocSet = "cognitive-services", - DateStart = "2020-05-01", - DateEnd = "2020-05-31", - }; - - var exception = await Assert.ThrowsAsync( - () => _service.GetConfiguration(input)); - Assert.StartsWith( - $@"Configuration file '.whatsnew/.{input.DocSet}.json' not found.", - exception.Message); - } - - [Theory(Skip = "Flaky in Az Pipelines build")] - [InlineData("")] - [InlineData(" ")] -#pragma warning disable xUnit1012 // Null should only be used for nullable parameters - [InlineData(null)] -#pragma warning restore xUnit1012 // Null should only be used for nullable parameters - public async Task Empty_GitHub_Personal_Access_Token_Throws_InvalidOperationException( - string gitHubKey) - { - var input = new PageGeneratorInput - { - Owner = "dotnet", - Repository = "AspNetCore.Docs", - Branch = "main", - DateStart = "2020-05-01", - DateEnd = "2020-05-31", - }; - - Environment.SetEnvironmentVariable("GitHubKey", gitHubKey); - - var exception = await Assert.ThrowsAsync( - () => _service.GetConfiguration(input)); - Assert.Equal( - "Store your GitHub personal access token in the 'GitHubKey' environment variable.", - exception.Message); - } -} diff --git a/WhatsNew.Infrastructure.Tests/Services/SchemaValidationServiceTests.cs b/WhatsNew.Infrastructure.Tests/Services/SchemaValidationServiceTests.cs deleted file mode 100644 index 87f5ea13..00000000 --- a/WhatsNew.Infrastructure.Tests/Services/SchemaValidationServiceTests.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using WhatsNew.Infrastructure.Services; -using Xunit; -using STJ = System.Text.Json; - -namespace WhatsNew.Infrastructure.Tests.Services; - -public class SchemaValidationServiceTests -{ - private readonly string _configDirectory; - private readonly SchemaValidationService _service; - - public SchemaValidationServiceTests() - { - _configDirectory = Path.Combine(Directory.GetCurrentDirectory(), "assets"); - _service = new(); - } - - [Fact] - public async Task Empty_DocSet_Product_Name_Throws_JsonException() - { - var configFilePath = Path.Combine( - _configDirectory, "MicrosoftDocs", "azure-devops-docs-pr.json"); - JToken configFile = await GetConfigFileAsync(configFilePath); - - var exception = Assert.Throws( - () => _service.ValidateConfiguration(configFile)); - Assert.StartsWith( - $"JSON schema validation failed for config file. String '' is less than minimum length of 1.", - exception.Message); - } - - [Fact] - public async Task Empty_Root_Directory_Throws_JsonException() - { - var configFilePath = Path.Combine( - _configDirectory, "dotnet", "AspNetCore.Docs.json"); - JToken configFile = await GetConfigFileAsync(configFilePath); - - var exception = Assert.Throws( - () => _service.ValidateConfiguration(configFile)); - Assert.StartsWith( - "JSON schema validation failed for config file. String '' is less than minimum length of 1.", - exception.Message); - } - - [Fact] - public async Task Missing_Relative_Link_Prefix_Property_Throws_JsonException() - { - var configFilePath = Path.Combine( - _configDirectory, "dotnet", "docs.json"); - JToken configFile = await GetConfigFileAsync(configFilePath); - - var exception = Assert.Throws( - () => _service.ValidateConfiguration(configFile)); - Assert.StartsWith( - "JSON schema validation failed for config file. JSON is valid against no schemas from 'oneOf'. Path 'docLinkSettings',", - exception.Message); - } - - [Fact] - public async Task Empty_Areas_Array_Throws_JsonException() - { - var configFilePath = Path.Combine( - _configDirectory, "MicrosoftDocs", "cpp-docs-pr.json"); - JToken configFile = await GetConfigFileAsync(configFilePath); - - var exception = Assert.Throws( - () => _service.ValidateConfiguration(configFile)); - Assert.StartsWith( - "JSON schema validation failed for config file. Array item count 0 is less than minimum count of 1. Path 'areas',", - exception.Message); - } - - private async Task GetConfigFileAsync(string filePath) - { - using StreamReader file = File.OpenText(filePath); - using var reader = new JsonTextReader(file); - var configFile = await JToken.ReadFromAsync(reader); - - return configFile; - } -} diff --git a/WhatsNew.Infrastructure.Tests/WhatsNew.Infrastructure.Tests.csproj b/WhatsNew.Infrastructure.Tests/WhatsNew.Infrastructure.Tests.csproj deleted file mode 100644 index c7ea26b1..00000000 --- a/WhatsNew.Infrastructure.Tests/WhatsNew.Infrastructure.Tests.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - net9.0 - enable - enable - false - 2.1.0.0 - 2.1.0.0 - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - PreserveNewest - - - \ No newline at end of file diff --git a/WhatsNew.Infrastructure.Tests/assets/MicrosoftDocs/azure-devops-docs-pr.json b/WhatsNew.Infrastructure.Tests/assets/MicrosoftDocs/azure-devops-docs-pr.json deleted file mode 100644 index 2c027cb7..00000000 --- a/WhatsNew.Infrastructure.Tests/assets/MicrosoftDocs/azure-devops-docs-pr.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "docSetProductName": "", - "rootDirectory": "docs/", - "docLinkSettings": { - "linkFormat": "relative", - "relativeLinkPrefix": "/azure/devops/" - }, - "inclusionCriteria": { - "minAdditionsToFile": 62 - }, - "areas": [ - { - "names": [ "artifacts" ], - "heading": "Artifacts" - } - ] -} diff --git a/WhatsNew.Infrastructure.Tests/assets/MicrosoftDocs/cpp-docs-pr.json b/WhatsNew.Infrastructure.Tests/assets/MicrosoftDocs/cpp-docs-pr.json deleted file mode 100644 index 42e20e84..00000000 --- a/WhatsNew.Infrastructure.Tests/assets/MicrosoftDocs/cpp-docs-pr.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "docSetProductName": "C++, C, and Assembler", - "rootDirectory": "docs/", - "docLinkSettings": { - "linkFormat": "relative", - "relativeLinkPrefix": "/cpp/" - }, - "inclusionCriteria": { - "minAdditionsToFile": 10, - "omitPullRequestTitles": true - }, - "areas": [] -} diff --git a/WhatsNew.Infrastructure.Tests/assets/dotnet/AspNetCore.Docs.json b/WhatsNew.Infrastructure.Tests/assets/dotnet/AspNetCore.Docs.json deleted file mode 100644 index 796c44a9..00000000 --- a/WhatsNew.Infrastructure.Tests/assets/dotnet/AspNetCore.Docs.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "docSetProductName": "ASP.NET Core", - "rootDirectory": "", - "docLinkSettings": { - "linkFormat": "xref" - }, - "areas": [ - { - "names": [ "azure" ], - "heading": "Azure" - } - ] -} diff --git a/WhatsNew.Infrastructure.Tests/assets/dotnet/docs.json b/WhatsNew.Infrastructure.Tests/assets/dotnet/docs.json deleted file mode 100644 index 11ca74d2..00000000 --- a/WhatsNew.Infrastructure.Tests/assets/dotnet/docs.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "docSetProductName": ".NET", - "rootDirectory": "docs/", - "docLinkSettings": { - "linkFormat": "relative" - }, - "inclusionCriteria": { - }, - "areas": [ - { - "names": [ "architecture" ], - "heading": "Architecture guides" - } - ] -} diff --git a/WhatsNew.Infrastructure.Tests/xunit.runner.json b/WhatsNew.Infrastructure.Tests/xunit.runner.json deleted file mode 100644 index 9cd95ee9..00000000 --- a/WhatsNew.Infrastructure.Tests/xunit.runner.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "methodDisplayOptions": "replaceUnderscoreWithSpace" -} diff --git a/WhatsNew.Infrastructure/Configuration/reposettings.schema.json b/WhatsNew.Infrastructure/Configuration/reposettings.schema.json deleted file mode 100644 index e16fcf01..00000000 --- a/WhatsNew.Infrastructure/Configuration/reposettings.schema.json +++ /dev/null @@ -1,221 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "description": "JSON schema for .whatsnew.json and .whatsnew.{DOCSET-NAME}.json configuration files", - "required": [ - "areas", - "docLinkSettings", - "docSetProductName", - "rootDirectory" - ], - - "properties": { - "areas": { - "type": "array", - "description": "A list of directories to include within the repository.", - "items": { "$ref": "#/definitions/area" }, - "minItems": 1, - "uniqueItems": true - }, - - "docLinkSettings": { - "$ref": "#/definitions/docLinkSetting" - }, - - "docSetProductName": { - "type": "string", - "description": "The name of the product supported by this docset.", - "minLength": 1 - }, - - "inclusionCriteria": { - "$ref": "#/definitions/inclusionCriteria" - }, - - "navigationOptions": { - "$ref": "#/definitions/navigationOption" - }, - - "rootDirectory": { - "type": "string", - "description": "The GitHub repository's root directory path containing the docs.", - "minLength": 1 - } - }, - - "definitions": { - "area": { - "type": "object", - "description": "Key-value pairs of repository directory names and their associated headings.", - "required": [ - "names", - "heading" - ], - - "properties": { - "names": { - "type": "array", - "description": "The directory name(s) as it/they appear(s) in the page URL.", - "minItems": 1, - "uniqueItems": true - }, - - "heading": { - "type": "string", - "description": "The friendly heading text for the directory or directories provided in `names`.", - "minLength": 1 - } - }, - - "additionalProperties": false - }, - - "docLinkSetting": { - "type": "object", - "description": "Settings to control the construction of links to docs in the generated Markdown.", - "required": [ - "linkFormat" - ], - - "properties": { - "linkFormat": { - "type": "string", - "description": "The Markdown format to use when creating links to docs.", - "enum": [ - "relative", - "siteRelative", - "xref" - ] - }, - - "relativeLinkPrefix": { - "type": "string", - "description": "The path that prefixes the doc link.", - "minLength": 1 - } - }, - - "oneOf": [ - { - "$comment": "If the `linkFormat` property is set to `relative`, the `relativeLinkPrefix` property is required.", - "properties": { - "linkFormat": { "const": "relative" } - }, - "required": [ - "relativeLinkPrefix" - ] - }, - { - "$comment": "If the `linkFormat` property is set to `siteRelative`, the `relativeLinkPrefix` property is required.", - "properties": { - "linkFormat": { "const": "siteRelative" } - }, - "required": [ - "relativeLinkPrefix" - ] - }, - { - "$comment": "If the `linkFormat` property is set to `xref`, the `relativeLinkPrefix` property isn't allowed.", - "properties": { - "linkFormat": { "const": "xref" } - } - } - ], - - "additionalProperties": false - }, - - "inclusionCriteria": { - "type": "object", - "description": "Settings to control the inclusion and exclusion of PRs and community contributors.", - - "properties": { - "labels": { - "type": "array", - "description": "A list of GitHub label filters to apply. The label filters will be converted to a space-delimited string.", - "minItems": 1, - "uniqueItems": true, - "examples": [ - [ "label:cognitive-services/svc", "label:speech-service/subsvc", "-label:do-not-merge" ] - ] - }, - - "maxFilesChanged": { - "type": "integer", - "description": "The maximum number of changed files that a PR can contain before being ignored.", - "default": 75 - }, - - "minAdditionsToFile": { - "type": "integer", - "description": "The minimum number of lines changed that a PR file must contain before being included.", - "default": 75 - }, - - "omitPullRequestTitles": { - "type": "boolean", - "description": "A flag indicating whether to display pull request titles." - }, - - "pullRequestTitlesToIgnore": { - "type": "array", - "description": "A list of regular expressions matching PR titles to ignore.", - "minItems": 1, - "uniqueItems": true, - "examples": [ - [ "^[Uu]pdate ", "^Prerender" ] - ] - } - - }, - - "additionalProperties": false - }, - - "navigationOption": { - "type": "object", - "description": "This object defines the properties needed to update the TOC.YML and Index.YML files.", - "required": [ - "maximumNumberOfArticles", - "tocParentNode", - "repoTocFolder", - "indexParentNode", - "repoIndexFolder" - ], - - "properties": { - "maximumNumberOfArticles": { - "type": "integer", - "description": "Maximum number of articles live in the TOC.", - "maximum": 6, - "minimum": 1 - }, - - "tocParentNode": { - "type": "string", - "description": "The name of the parent node in the TOC.", - "minLength": 1 - }, - - "repoTocFolder": { - "type": "string", - "description": "Path from the root of the repository to the toc to modify.", - "minLength": 1 - }, - - "indexParentNode": { - "type": "string", - "description": "The name of the parent node in the TOC.", - "minLength": 1 - }, - - "repoIndexFolder": { - "type": "string", - "description": "The name of the parent node in the TOC.", - "minLength": 1 - } - }, - - "additionalProperties": false - } - } -} diff --git a/WhatsNew.Infrastructure/Constants.cs b/WhatsNew.Infrastructure/Constants.cs deleted file mode 100644 index 098c6bdf..00000000 --- a/WhatsNew.Infrastructure/Constants.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace WhatsNew.Infrastructure; - -/// -/// Constants used throughout the what's new solution's projects. -/// -public static class Constants -{ - /// - /// The directory name in the WhatsNew.Infrastructure project which stores the - /// JSON configuration files for each docset. - /// - public const string ConfigurationDirectory = "Configuration"; - - /// - /// The name of the default directory to which the generated Markdown file is - /// written. - /// - public const string MarkdownFileDirectoryName = "whatsnew"; - - /// - /// The suffix used to indicate a private GitHub repository. - /// - public const string PrivateRepoNameSuffix = "-pr"; -} diff --git a/WhatsNew.Infrastructure/Models/DocLinkSettings.cs b/WhatsNew.Infrastructure/Models/DocLinkSettings.cs deleted file mode 100644 index a833d6b2..00000000 --- a/WhatsNew.Infrastructure/Models/DocLinkSettings.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace WhatsNew.Infrastructure.Models; - -/// -/// Encapsulates the settings that control the construction of doc -/// links in the generated Markdown file. -/// -public class DocLinkSettings -{ - /// - /// The link format to use for links to docs appearing in the - /// generated Markdown file. - /// - public LinkFormat LinkFormat { get; init; } - - /// - /// The path that prefixes the doc link. - /// - /// - /// This property is required when - /// is . - /// - /// /dotnet/ - public string? RelativeLinkPrefix { get; init; } -} diff --git a/WhatsNew.Infrastructure/Models/InclusionCriteria.cs b/WhatsNew.Infrastructure/Models/InclusionCriteria.cs deleted file mode 100644 index bc55ac3c..00000000 --- a/WhatsNew.Infrastructure/Models/InclusionCriteria.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace WhatsNew.Infrastructure.Models; - -/// -/// Configuration settings that control which content is included in -/// and excluded from the generated Markdown file. -/// -public class InclusionCriteria -{ - /// - /// A collection of internal, Microsoft-owned organization names - /// whose members should be excluded from the community contributors - /// list. - /// - public List AdditionalMicrosoftOrgs { get; init; } = new(); - - /// - /// A flag indicating whether to display PR titles. - /// - public bool OmitPullRequestTitles { get; init; } - - /// - /// A collection of regular expressions matching PR titles to ignore. - /// PRs with titles matching the regex(es) will be excluded from the - /// generated Markdown file. - /// - public List PullRequestTitlesToIgnore { get; init; } = new(); - - /// - /// A collection of label filters to be applied. - /// - /// The label filters will be converted to a space-delimited string. - public List Labels { get; init; } = new(); - - /// - /// The maximum number of changed files that a PR can contain - /// before being ignored. - /// - /// The default value is 75. - public int MaxFilesChanged { get; init; } = 75; - - /// - /// The minimum number of lines changed that a PR file must - /// contain before being included. - /// - /// The default value is 75. - public int MinAdditionsToFile { get; init; } = 75; -} diff --git a/WhatsNew.Infrastructure/Models/Landing.cs b/WhatsNew.Infrastructure/Models/Landing.cs deleted file mode 100644 index 03b21bc8..00000000 --- a/WhatsNew.Infrastructure/Models/Landing.cs +++ /dev/null @@ -1,52 +0,0 @@ -using YamlDotNet.Serialization; - -namespace WhatsNew.Infrastructure.Models; - -public class Landing -{ - [YamlMember(Alias = "title", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string Title { get; set; } = default!; - [YamlMember(Alias = "summary", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string Summary { get; set; } = default!; - [YamlMember(Alias = "metadata", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public MetaDataBlock Metadata { get; set; } = default!; - - [YamlMember(Alias = "landingContent", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public List LandingContent { get; set; } = default!; -} - -public class MetaDataBlock -{ - [YamlMember(Alias = "title", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string Title { get; set; } = default!; - [YamlMember(Alias = "description", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string Description { get; set; } = default!; - [YamlMember(Alias = "ms.date", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string MsDate { get; set; } = default!; - [YamlMember(Alias = "ms.topic", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string MsTopic { get; set; } = default!; -} - -public class Tile -{ - [YamlMember(Alias = "title", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string Title { get; set; } = default!; - [YamlMember(Alias = "linkLists", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public List LinkLists { get; set; } = default!; -} - -public class LinkSection -{ - [YamlMember(Alias = "linkListType", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string LinkListType { get; set; } = default!; - [YamlMember(Alias = "links", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public List
Links { get; set; } = default!; -} - -public class Article -{ - [YamlMember(Alias = "text", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string Text { get; set; } = default!; - [YamlMember(Alias = "url", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string Url { get; set; } = default!; -} diff --git a/WhatsNew.Infrastructure/Models/NavigationDetails.cs b/WhatsNew.Infrastructure/Models/NavigationDetails.cs deleted file mode 100644 index 2d2ab3c5..00000000 --- a/WhatsNew.Infrastructure/Models/NavigationDetails.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace WhatsNew.Infrastructure.Models; - -/// -/// This class defines the properties needed to update the TOC.YML and Index.YML files. -/// -/// -/// These properties are used when you run the tool to create a PR for -/// the what's new documents. -/// -public class NavigationDetails -{ - /// - /// Maximum number of articles live in the TOC. - /// - public int MaximumNumberOfArticles { get; set; } - - /// - /// The name of the parent node in the TOC. - /// - public string TocParentNode { get; set; } = default!; - - /// - /// Path from the root of the repository to the toc to modify - /// - public string RepoTocFolder { get; set; } = default!; - - /// - /// The name of the parent node in the TOC. - /// - public string IndexParentNode { get; set; } = default!; - - /// - /// Path from the root of the repository to the toc to modify - /// - public string RepoIndexFolder { get; set; } = default!; -} diff --git a/WhatsNew.Infrastructure/Models/PageGeneratorInput.cs b/WhatsNew.Infrastructure/Models/PageGeneratorInput.cs deleted file mode 100644 index 26696179..00000000 --- a/WhatsNew.Infrastructure/Models/PageGeneratorInput.cs +++ /dev/null @@ -1,125 +0,0 @@ -namespace WhatsNew.Infrastructure.Models; - -/// -/// An encapsulation of the arguments and options accepted by the command-line tool. -/// -public class PageGeneratorInput -{ - private string _dateEnd = null!; - private string _dateStart = null!; - private string _owner = null!; - private string _repository = null!; - - /// - /// The owner/organization of the GitHub repository to be processed. - /// - /// dotnet - public string Owner - { - get => _owner; - set - { - if (string.IsNullOrWhiteSpace(value)) - throw new ArgumentException($"The parameter '{nameof(Owner)}' cannot be null, empty, or whitespace."); - - _owner = value.Trim(); - } - } - /// - /// The name of the GitHub repository within the organization specified - /// in . - /// - /// AspNetCore.Docs - public string Repository - { - get => _repository; - set - { - if (string.IsNullOrWhiteSpace(value)) - throw new ArgumentException($"The parameter '{nameof(Repository)}' cannot be null, empty, or whitespace."); - - _repository = value.Trim(); - } - } - - /// - /// The name of the branch within the repository specified in - /// . - /// - /// main - public string? Branch { get; set; } - - /// - /// The docset directory name. This setting is used when the - /// and aren't enough to select the appropriate repo config - /// file. This setting is useful for the MicrosoftDocs/azure-docs-pr repo. - /// - /// cognitive-services - public string? DocSet { get; init; } - - /// - /// The directory path to which the generated Markdown file should be written. - /// - /// C:\whatsnew - public string? SaveDir { get; init; } - - /// - /// The directory path to the root of the repository. - /// - /// ../dotnet/docs/ - public string RepoRoot { get; init; } = "./"; - - /// - /// An absolute path to a local JSON configuration file. - /// - /// C:\config\.whatsnew.json - public string? LocalConfig { get; init; } - - /// - /// The range end date in a valid format. - /// - /// - /// Examples of valid formats include yyyy-MM-dd and MM/dd/yyyy. - /// - public string DateEnd - { - get => _dateEnd; - set - { - if (string.IsNullOrWhiteSpace(value)) - throw new ArgumentException( - $"The parameter cannot be null, empty, or whitespace.", nameof(DateEnd)); - - _dateEnd = value.Trim(); - } - } - - /// - /// The range start date in a valid format. - /// - /// - /// Examples of valid formats include yyyy-MM-dd and MM/dd/yyyy. - /// - public string DateStart - { - get => _dateStart; - set - { - if (string.IsNullOrWhiteSpace(value)) - throw new ArgumentException( - $"The parameter cannot be null, empty, or whitespace.", nameof(DateStart)); - - _dateStart = value.Trim(); - } - } - - /// - /// Default date range when none is provided on the command line. - /// - /// - /// If the custom command line doesn't add any date information, this - /// string contains "Month, YYYY". Otherwise it is null. In the case - /// where it is null, the dates are formatted from the start and end dates. - /// - public string? MonthYear { get; init; } -} diff --git a/WhatsNew.Infrastructure/Models/RepositoryArea.cs b/WhatsNew.Infrastructure/Models/RepositoryArea.cs deleted file mode 100644 index e0c9e8e0..00000000 --- a/WhatsNew.Infrastructure/Models/RepositoryArea.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace WhatsNew.Infrastructure.Models; - -/// -/// Represents a name-value pair, where represents the -/// directory name(s) within the repo and represents the -/// heading text corresponding to that directory. -/// -public class RepositoryArea -{ - /// - /// A collection of directory names in the GitHub repository. - /// - public IEnumerable Names { get; init; } = null!; - - /// - /// The heading text for the area/directory. - /// - public string Heading { get; init; } = null!; -} diff --git a/WhatsNew.Infrastructure/Models/RepositoryDetail.cs b/WhatsNew.Infrastructure/Models/RepositoryDetail.cs deleted file mode 100644 index a83a16ea..00000000 --- a/WhatsNew.Infrastructure/Models/RepositoryDetail.cs +++ /dev/null @@ -1,89 +0,0 @@ -using static WhatsNew.Infrastructure.Constants; - -namespace WhatsNew.Infrastructure.Models; - -/// -/// Encapsulates necessary details for the docset for which a "What's New" -/// Markdown file is to be generated. -/// -public class RepositoryDetail -{ - /// - /// The name of the branch to process. - /// - public string Branch { get; set; } = null!; - - /// - /// The product name supported by the docset. - /// - /// ASP.NET Core - public string DocSetProductName { get; init; } = null!; - - /// - /// The GitHub repository name. - /// - /// AspNetCore.Docs - public string Name { get; set; } = null!; - - /// - /// A flag indicating whether the repository is private. - /// - public bool IsPrivateRepo => Name.EndsWith(PrivateRepoNameSuffix); - - /// - /// The GitHub organization name. - /// - /// dotnet - public string Owner { get; set; } = null!; - - /// - /// The GitHub repository's root directory path containing the docs. - /// - /// articles/ - public string RootDirectory { get; init; } = null!; - - /// - /// Settings to control the construction of doc links in the generated - /// Markdown file. - /// - public DocLinkSettings DocLinkSettings { get; init; } = new(); - - /// - /// The repository directories of interest. - /// - /// - /// Each directory will have a heading printed in the generated - /// Markdown file. - /// - public IEnumerable Areas { get; init; } = null!; - - /// - /// Configuration settings that control which content is included in - /// and excluded from the generated Markdown file. - /// - public InclusionCriteria InclusionCriteria { get; init; } = new(); - - /// - /// Optional object that defines config for updating the TOC and Index YAML files. - /// - public NavigationDetails? NavigationOptions { get; set; } -} - -/// -/// An enumeration of link formats supported in the generated Markdown file. -/// -public enum LinkFormat -{ - /// - /// A link in the format `[DOC-TITLE-HERE](../index.md)`. - /// - Relative, - /// - /// A link in the format `[DOC-TITLE-HERE](/site/index)`. - /// - SiteRelative, - /// - /// A link in the format `<xref:index>`. - /// - Xref, -} diff --git a/WhatsNew.Infrastructure/Models/Toc.cs b/WhatsNew.Infrastructure/Models/Toc.cs deleted file mode 100644 index a4df9c60..00000000 --- a/WhatsNew.Infrastructure/Models/Toc.cs +++ /dev/null @@ -1,39 +0,0 @@ -using YamlDotNet.Serialization; - -namespace WhatsNew.Infrastructure.Models; - -public class Toc -{ - [YamlMember(Alias = "items", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public List Items { get; set; } = new List(); -} - -public class TocNode -{ - [YamlMember(Alias ="name",DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string Name { get; set; } = default!; - [YamlMember(Alias = "href", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string? Href { get; set; } - [YamlMember(Alias = "expanded", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public bool? Expanded { get; set; } - [YamlMember(Alias = "displayName", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string? DisplayName { get; set; } - [YamlMember(Alias = "items", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public List? Items { get; set; } - - // generally not used: - - [YamlMember(Alias = "homepage", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string? Homepage { get; set; } - [YamlMember(Alias = "preserveContext", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public bool? PreserveContext { get; set; } - [YamlMember(Alias = "tocHref", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string? TocHRef { get; set; } - [YamlMember(Alias = "topicHref", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string? TopicHref { get; set; } - [YamlMember(Alias = "topicUid", DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string? TopicUid { get; set; } - [YamlMember(Alias = "type", ApplyNamingConventions = false, DefaultValuesHandling = DefaultValuesHandling.OmitNull)] - public string? NodeType { get; set; } - -} diff --git a/WhatsNew.Infrastructure/Models/WhatsNewConfiguration.cs b/WhatsNew.Infrastructure/Models/WhatsNewConfiguration.cs deleted file mode 100644 index b9e23fcd..00000000 --- a/WhatsNew.Infrastructure/Models/WhatsNewConfiguration.cs +++ /dev/null @@ -1,70 +0,0 @@ -using DotNetDocs.Tools.GitHubCommunications; -using DotNetDocs.Tools.Utility; -using Microsoft.DotnetOrg.Ospo; -using System.Text; - -namespace WhatsNew.Infrastructure.Models; - -/// -/// Encapsulates all the configuration details necessary to generate -/// a "What's New" Markdown file. -/// -public class WhatsNewConfiguration -{ - /// - /// Details from the JSON configuration file. - /// - public RepositoryDetail Repository { get; init; } = null!; - - - /// - /// The start and end dates used for the search. - /// - public DateRange DateRange { get; init; } - - /// - /// The displayed date range. - /// - public string RangeTitle { get; init; } = null!; - - /// - /// The name to be used for the generated Markdown file. - /// - public string MarkdownFileName { get; init; } = null!; - - /// - /// The directory path to which the generated Markdown file should be written. - /// - public string? SaveDir { get; set; } - - /// - /// The GitHub client object used for querying. - /// - public IGitHubClient GitHubClient { get; init; } = null!; - - /// - /// The path to the root of the repo. - /// - /// - /// This might eventually replace the saveDir option. - /// - public string PathToRepoRoot { get; set; } = null!; - - /// - /// This is the client that makes queries to the Microsoft OSPO office API. - /// - public OspoClient? OspoClient { get; set; } - - public string? LogConfigSettings() - { - StringBuilder output = new (); - output.AppendLine($"Repository: {Repository.Owner}/{Repository.Name}"); - output.AppendLine($"Branch: {Repository.Branch}"); - output.AppendLine($"DocSetProductName: {Repository.DocSetProductName}"); - output.AppendLine($"RootDirectory: {Repository.RootDirectory}"); - output.AppendLine($"DateRange: {DateRange.StartDate:D} - {DateRange.EndDate:D}"); - output.AppendLine($"MarkdownFileName: {MarkdownFileName}"); - output.AppendLine($"PathToRepoRoot: {PathToRepoRoot}"); - return output.ToString(); - } -} diff --git a/WhatsNew.Infrastructure/Services/ConfigurationService.cs b/WhatsNew.Infrastructure/Services/ConfigurationService.cs deleted file mode 100644 index f4d4e1f2..00000000 --- a/WhatsNew.Infrastructure/Services/ConfigurationService.cs +++ /dev/null @@ -1,103 +0,0 @@ -using DotNetDocs.Tools.GitHubCommunications; -using DotNetDocs.Tools.Utility; -using Microsoft.Extensions.Configuration; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using WhatsNew.Infrastructure.Models; -using DotNetDocs.Tools.GraphQLQueries; -using DotNet.DocsTools.GitHubObjects; -using Microsoft.DotnetOrg.Ospo; - -namespace WhatsNew.Infrastructure.Services; - -/// -/// The class responsible for constructing the configuration details -/// needed to generate "What's New" pages. -/// -public class ConfigurationService -{ - public async Task GetConfiguration(PageGeneratorInput input) - { - var config = new ConfigurationBuilder() - .AddEnvironmentVariables() - .Build(); - - var key = config["GitHubKey"]; - if (string.IsNullOrWhiteSpace(key)) - throw new InvalidOperationException("Store your GitHub personal access token in the 'GitHubKey' environment variable."); - - var dateRange = new DateRange(input.DateStart, input.DateEnd); - string configFileName, configFileContents, markdownFileName; - - if (string.IsNullOrWhiteSpace(input.DocSet)) - { - configFileName = ".whatsnew.json"; - } - else - { - configFileName = $".whatsnew/.{input.DocSet.Trim()}.json"; - } - - var client = IGitHubClient.CreateGitHubClient(key); - - var token = config["AZURE_ACCESS_TOKEN"]; - - OspoClient? ospoClient = token is not null - ? new OspoClient(token, true) : null; - - if (ospoClient is null) - { - Console.WriteLine("Warning: Microsoft FTEs won't be filtered from the contributor list."); - } - - if (string.IsNullOrWhiteSpace(input.Branch)) - { - var query = new ScalarQuery(client); - var result = await query.PerformQuery(new DefaultBranchVariables(input.Owner, input.Repository)); - - // There should never be a case where the default branch is null, but just in case... - if (result is null) throw new InvalidOperationException("Could not find default branch"); - input.Branch = result.DefaultBranchName; - } - - JToken configToken; - - var pathToConfig = string.IsNullOrWhiteSpace(input.LocalConfig) - ? Path.Combine(input.RepoRoot, configFileName) - : input.LocalConfig; - - configFileContents = await File.ReadAllTextAsync(pathToConfig); - configToken = JToken.Parse(configFileContents); - - var validationService = new SchemaValidationService(); - validationService.ValidateConfiguration(configToken); - - var repo = JsonConvert.DeserializeObject(configFileContents) ?? throw new InvalidOperationException("Config object not found"); - repo.Branch = input.Branch!; - repo.Owner = input.Owner; - repo.Name = input.Repository; - - int mod = repo.NavigationOptions?.MaximumNumberOfArticles ?? 3; - string fNameCycle = $"mod{dateRange.StartDate.Month % mod}"; - if (string.IsNullOrWhiteSpace(input.DocSet)) - { - markdownFileName = $"{input.Owner}-{input.Repository}-{fNameCycle}.md"; - } - else - { - markdownFileName = $"{input.Owner}-{input.Repository}-{input.DocSet.Trim()}-{fNameCycle}.md"; - } - - return new WhatsNewConfiguration - { - DateRange = dateRange, - RangeTitle = input.MonthYear ?? $"{input.DateStart} - {input.DateEnd}", - GitHubClient = client, - OspoClient = ospoClient, - MarkdownFileName = markdownFileName, - Repository = repo, - SaveDir = input.SaveDir?.Trim(), - PathToRepoRoot = input.RepoRoot, - }; - } -} diff --git a/WhatsNew.Infrastructure/Services/IndexUpdateService.cs b/WhatsNew.Infrastructure/Services/IndexUpdateService.cs deleted file mode 100644 index f2f8db6f..00000000 --- a/WhatsNew.Infrastructure/Services/IndexUpdateService.cs +++ /dev/null @@ -1,65 +0,0 @@ -using WhatsNew.Infrastructure.Models; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - -namespace WhatsNew.Infrastructure.Services; - -// Assumptions: -// 2. Node title is "Find .NET updates" **invalid** -// 1. Title is "Month YYYY" **valid** -public class IndexUpdateService -{ - private readonly WhatsNewConfiguration _configuration; - - public IndexUpdateService(WhatsNewConfiguration config) => _configuration = config; - - public async Task UpdateWhatsNewLandingPage() - { - if (_configuration.Repository.NavigationOptions?.RepoIndexFolder is null) - { - Console.WriteLine("No index folder specified in the configuration. Skipping index update."); - return; - } - - // Update Index.YML: - var indexFile = Path.Combine(Path.Combine(_configuration.PathToRepoRoot, _configuration.Repository.NavigationOptions.RepoIndexFolder), "index.yml"); - using var indexReader = new StreamReader(indexFile); - var indexDeserializer = new DeserializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); - - var index = indexDeserializer.Deserialize(indexReader); - indexReader.Close(); - - var section = index.LandingContent.Single(s => s.Title == _configuration.Repository.NavigationOptions.IndexParentNode); - - var tile = section.LinkLists.First(); - var articleText = _configuration.DateRange.StartDate.ToString("MMMM yyyy"); - if (!tile.Links.Any(item => item.Text.Equals(articleText))) - { - var newArticle = new Article - { - Text = articleText, - Url = _configuration.MarkdownFileName, - }; - tile.Links.Insert(0, newArticle); - index.Metadata.MsDate = DateTime.Now.Date.ToString("MM/dd/yyyy"); - } - - while (tile.Links.Count > _configuration.Repository.NavigationOptions.MaximumNumberOfArticles) - { - tile.Links.RemoveAt(tile.Links.Count-1); - index.Metadata.MsDate = DateTime.Now.Date.ToString("MM/dd/yyyy"); - } - - // 3. Write index file - var indexSerializer = new SerializerBuilder().Build(); - var indexYaml = indexSerializer.Serialize(index); - using var writer = new StreamWriter(indexFile); - await writer.WriteLineAsync("### YamlMime:Landing"); - await writer.WriteLineAsync(); - await writer.WriteAsync(indexYaml); - Console.WriteLine($"Update the index landing page \"{indexFile}\""); - - } -} diff --git a/WhatsNew.Infrastructure/Services/PageGenerationService.cs b/WhatsNew.Infrastructure/Services/PageGenerationService.cs deleted file mode 100644 index d3930829..00000000 --- a/WhatsNew.Infrastructure/Services/PageGenerationService.cs +++ /dev/null @@ -1,500 +0,0 @@ -using DotNet.DocsTools.GitHubObjects; -using DotNetDocs.Tools.GitHubCommunications; -using DotNetDocs.Tools.GitHubObjects; -using DotNetDocs.Tools.GraphQLQueries; -using DotNetDocs.Tools.RESTQueries; -using DotNetDocs.Tools.Utility; -using System.Text; -using System.Text.RegularExpressions; -using WhatsNew.Infrastructure.Models; -using static WhatsNew.Infrastructure.Constants; - -namespace WhatsNew.Infrastructure.Services; - -public record PrDetail(string Source, bool NewContent, string PrTitle, int PrNumber); - -public record WhatsNewEntry(string Heading, List Changes); - -/// -/// The class responsible for generating monthly "What's New" pages. -/// -public class PageGenerationService -{ - /* - File extensions to consider for inclusion in the generated Markdown file. - While processing PRs, only files with these extensions will be analyzed. - */ - private readonly List _fileExtensions = new() - { - ".md", - ".yml", - }; - private readonly List<(string login, string name)> _contributors = new(); - private readonly Dictionary _majorChanges = new(); - private readonly WhatsNewConfiguration _configuration = null!; - - public PageGenerationService(WhatsNewConfiguration configuration) => - _configuration = configuration; - - /// - /// Generates the "What's New" Markdown file for the docset identified by - /// and . - /// - public async Task WriteMarkdownFile(string? existingMarkdownFile= null) - { - var totalPRs = await ProcessPullRequests(); - - if (totalPRs == 0) - { - var color = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("No PRs found."); - Console.WriteLine("This is likely a problem with one of:"); - Console.WriteLine("\t- the date range"); - Console.WriteLine("\t- the required label"); - Console.WriteLine("\t- GitHub permissions"); - Console.WriteLine("Exiting."); - Console.ForegroundColor = color; - return; - } - - if (string.IsNullOrWhiteSpace(existingMarkdownFile) || !File.Exists(existingMarkdownFile)) - { - var filePath = string.IsNullOrWhiteSpace(existingMarkdownFile) ? GetMarkdownFilePath() : existingMarkdownFile; - await using TextWriter stream = new StreamWriter(filePath); - - await GenerateHeader(stream); - await WriteNewDocInformation(stream, false); - await WriteContributorInformation(stream); - - Console.WriteLine($"Created the file \"{filePath}\""); - } - else - { - // Read the file. - var lines = await File.ReadAllLinesAsync(existingMarkdownFile); - int sectionsWritten = 0; - await using TextWriter stream = new StreamWriter(existingMarkdownFile); - // This might be easier without pattern matching. - bool dontTrim = false; - foreach (var line in lines) - { - if (line.StartsWith("ms.date")) - { - await stream.WriteLineAsync($"ms.date: {DateTime.Now:MM/dd/yyyy}"); - continue; - } - if ((sectionsWritten == 0) && (line.StartsWith("## "))) - { - dontTrim = await WriteNewSection(stream, line); - sectionsWritten++; - } - if (line.StartsWith("## ")) - { - sectionsWritten++; - } - if ((sectionsWritten <= (_configuration.Repository.NavigationOptions?.MaximumNumberOfArticles ?? 3)) - || dontTrim) - { - await stream.WriteLineAsync(line); - } - } - - async Task WriteNewSection(TextWriter stream, string line) - { - string header = $"## {DateTime.Now.AddMonths(-1):MMMM yyyy}"; - await stream.WriteLineAsync(header); - await stream.WriteLineAsync(); - await WriteNewDocInformation(stream, true); - await WriteContributorInformation(stream); - await stream.WriteLineAsync(); - return header == line; - } - } - } - - private string GetMarkdownFilePath() - { - _configuration.SaveDir ??= GetWhatsNewDirectory(); - Directory.CreateDirectory(_configuration.SaveDir); - - var filePath = Path.Combine(_configuration.SaveDir, _configuration.MarkdownFileName); - return filePath; - - static string GetWhatsNewDirectory() - { - // Inspired by the following user secrets configuration provider code: - // https://github.com/dotnet/runtime/blob/7f7791f5ae3938c643e6c76b514a46b095c1730a/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/src/PathHelper.cs#L46-L51 - var rootDir = Environment.GetEnvironmentVariable("APPDATA") - ?? Environment.GetEnvironmentVariable("HOME") - ?? Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) - ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - - if (string.IsNullOrEmpty(rootDir)) - throw new InvalidOperationException( - "Could not determine an appropriate location for storing the generated Markdown file. Use the --savedir option to specify a directory where the file should be stored."); - - var whatsNewDir = Path.Combine(rootDir, "whatsnew"); - return whatsNewDir; - } - } - - private async Task GenerateHeader(TextWriter stream) - { - var docSetProductName = _configuration.Repository.DocSetProductName; - - var sb = new StringBuilder(); - sb.AppendLine("---"); - sb.AppendLine($"title: \"{docSetProductName} docs: What's new for {_configuration.RangeTitle}\""); - sb.AppendLine($"description: \"What's new in the {docSetProductName} docs for {_configuration.RangeTitle}.\""); - sb.AppendLine($"ms.custom: {DateTime.Now.AddMonths(-1):MMMM-yyyy}"); - sb.AppendLine($"ms.date: {DateTime.Now:MM/dd/yyyy}"); - sb.AppendLine("---"); - sb.AppendLine(); - sb.AppendLine($"# {docSetProductName} docs: What's new for {_configuration.RangeTitle}"); - sb.AppendLine(); - sb.AppendLine($"Welcome to what's new in the {docSetProductName} docs for {_configuration.RangeTitle}. This article lists some of the major changes to docs during this period."); - sb.AppendLine(); - - await stream.WriteAsync(sb.ToString()); - } - - private async Task WriteNewDocInformation(TextWriter stream, bool singleFile) - { - var repo = _configuration.Repository; - - var allDocs = from change in _majorChanges - from area in repo.Areas - where change.Value.Heading == area.Heading - group change by area.Heading into headingGroup - select headingGroup; - - var rootDirectoryHeading = (from area in repo.Areas - where area.Names.FirstOrDefault() == "." - select area.Heading).FirstOrDefault(); - - foreach(var area in repo.Areas) - { - // Don't write anything for blank areas. - if (area is null) - continue; - var docArea = allDocs.FirstOrDefault(da => da.Key == area.Heading); - if (docArea is null) - { - Console.WriteLine($"No changes found for {area.Heading}"); - continue; - } else - { - Console.WriteLine($"Writing changes for {area.Heading}"); - } - var isRootDirectoryArea = area.Heading == rootDirectoryHeading; - - await stream.WriteLineAsync($"{(singleFile ? "###" : "##")} {area.Heading}"); - await stream.WriteLineAsync(); - - writeDocNodes(true); - writeDocNodes(false); - - void writeDocNodes(bool isNew) - { - var prQuery = docArea.Where(da => da.Value.Changes.Any(pr => pr.NewContent == isNew)); - - if (prQuery.Any()) - { - var header = isNew ? "New articles" : "Updated articles"; - stream.WriteLine(singleFile ? $"**{header}**" : $"### {header}"); - stream.WriteLine(); - - List<(string title, string line)> sectionItems = new(); - - foreach (var doc in prQuery) - { - // Potential problem: Why only look at the first source? - List docPullRequests = doc.Value.Changes; - // Check all PRs to get a title. - // If a single PR title fails, write a warning to the console. - // If all PRs fail to retrieve the title, put a warning in the output. - var value = docPullRequests.First(); - string? docLink = default; - string? docTitle = default; - string prs = ""; - foreach (var pr in docPullRequests) - { - // First title wins (it's the newest), but keep checking to issue warnings. - try - { - docTitle ??= getDocTitle(pr.Source); - docLink ??= getDocLink(pr.Source, doc.Key, isRootDirectoryArea); - if (docLink == null) - { - Console.WriteLine($"Title not found for {repo.DocLinkSettings.RelativeLinkPrefix}{doc.Key.Replace("./", string.Empty)} in PR #{pr.PrNumber}"); - prs += $"#{pr.PrNumber}"; - } - } - catch (IOException) - { - Console.WriteLine("PR includes what's new file. Ignoring"); - } - } - if (docLink == null) - { - // root directory: - docLink = isRootDirectoryArea ? - $"[ZZZ - Title not found in: {prs}]({doc.Key.Replace("./", string.Empty)})" : - $"[ZZZ - Title not found in: {prs}]({repo.DocLinkSettings.RelativeLinkPrefix}{doc.Key.Replace("./", string.Empty)})"; - } - - if (!string.IsNullOrEmpty(docLink) && !string.IsNullOrEmpty(docTitle)) - { - string docListing = $"- {docLink}"; - - if (isNew || repo.InclusionCriteria.OmitPullRequestTitles) - { - sectionItems.Add((docTitle, docListing)); - } - else - { - for (int prIndex = 0; prIndex < docPullRequests.Count; prIndex++) - { - if (docPullRequests.Count > 1) - docListing += Environment.NewLine; - - var (_, _, PrTitle, PrNumber) = docPullRequests[prIndex]; - int prTitleLeftPadding = docPullRequests.Count > 1 ? 2 : 1; - var trimmedPrTitle = $"- {PrTitle.Trim()}"; - int paddedTitleLength = prTitleLeftPadding + trimmedPrTitle.Length; - docListing += trimmedPrTitle.PadLeft(paddedTitleLength); - } - sectionItems.Add((docTitle, docListing)); - } - } - } - foreach(var item in sectionItems.OrderBy(item => item.title)) - { - stream.WriteLine(item.line); - } - stream.WriteLine(); - } - } - - string? getDocLink(string source, string docUrl, bool isRootDirectoryArea) - { - var path = Path.Combine(_configuration.PathToRepoRoot, source); - if (!File.Exists(path)) - return null; - - string metadataValue = (repo.DocLinkSettings.LinkFormat == LinkFormat.Xref) - ? RawContentFromLocalFile.RetrieveUidFromFile(path) - : RawContentFromLocalFile.RetrieveTitleFromFile(path); - - string? docLink = (repo.DocLinkSettings.LinkFormat, string.IsNullOrEmpty(metadataValue), isRootDirectoryArea) switch - { - (_, true, _) => null, // Matches an invalid enum value. Potential bug, but tested at app load (during JSON schema validation). - (LinkFormat.Relative, false, true) => $"[{metadataValue.Replace("\"", string.Empty)}]({docUrl.Replace("./", string.Empty)})", - (LinkFormat.Xref, false, _) => $"", - (_, false, _) => $"[{metadataValue.Replace("\"", string.Empty)}]({repo.DocLinkSettings.RelativeLinkPrefix}{docUrl.Replace("./", string.Empty)})", - }; - - if (repo.DocLinkSettings.LinkFormat == LinkFormat.SiteRelative) - _fileExtensions.ForEach(extension => docLink = docLink?.Replace(extension, string.Empty)); - - return docLink; - } - - string getDocTitle(string source) - { - var path = Path.Combine(_configuration.PathToRepoRoot, source); - - return File.Exists(path) - ? RawContentFromLocalFile.RetrieveTitleFromFile(path) - : "ZZZ - Title Not Found"; - } - } - } - - private async Task WriteContributorInformation(TextWriter stream) - { - Console.WriteLine("Writing contributors"); - var allContributors = from c in _contributors - orderby c.login - group c by (c.login, c.name) into stats - select (User: stats.Key, Count: stats.Count()); - - if (allContributors.Any()) - { - var sb = new StringBuilder(); - sb.AppendLine("## Community contributors"); - sb.AppendLine(); - sb.AppendLine(@$"The following people contributed to the {_configuration.Repository.DocSetProductName} docs during this period. Thank you! Learn how to contribute by following the links under ""Get involved"" in the [what's new landing page](index.yml)."); - sb.AppendLine(); - - foreach (var (user, count) in allContributors.OrderByDescending(c => c.Count)) - { - // Format a merged pull requests badge, for example: - // https://img.shields.io/badge/Merged%20Pull%20Requests-7-green - var altText = $"{count} pull requests."; - var mergedPullRequestsBadge = $"![{altText}](https://img.shields.io/badge/Merged%20Pull%20Requests-{count}-green)"; - - sb.AppendLine($"- [{user.login}](https://github.com/{user.login}){(user.name != null ? " - " + user.name : "")} {mergedPullRequestsBadge}"); - } - - await stream.WriteAsync(sb.ToString()); - } - } - - private async Task ProcessPullRequests() - { - var repo = _configuration.Repository; - var client = _configuration.GitHubClient; - var ospoClient = _configuration.OspoClient; - - var totalPRs = await processPRs(); - - // If processing a private repo, fetch the PRs & community contributors from - // the accompanying public repo. Merge private results with public results. - if (repo.IsPrivateRepo) - { - repo.Name = repo.Name.Replace(PrivateRepoNameSuffix, string.Empty); - totalPRs += await processPRs(); - } - return totalPRs; - - async Task processPRs() - { - Console.ForegroundColor = ConsoleColor.DarkMagenta; - Console.WriteLine($"== {repo.Owner}/{repo.Name} ({repo.Branch}) ==", Console.ForegroundColor); - Console.ForegroundColor = ConsoleColor.Gray; - - var excludedContributors = new List(); - var query = new EnumerationQuery(client); - var queryParms = new WhatsNewVariables(repo.Owner, repo.Name, repo.Branch, repo.InclusionCriteria.Labels, _configuration.DateRange); - var authorLoginFTECache = new Dictionary(); - - var totalPRs = 0; - await foreach (var item in query.PerformQuery(queryParms)) - { - totalPRs++; - var prNumber = item.Number; - Console.WriteLine($"Processing PR {prNumber}"); - - if (item.Author?.Login is not null) - { - if (!authorLoginFTECache.TryGetValue(item.Author!.Login!, out var isFTE)) - { - isFTE = await item.Author.IsMicrosoftFTE(ospoClient); - authorLoginFTECache[item.Author.Login] = isFTE; - } - - if (isFTE == false) - _contributors.Add((login: item.Author.Login, name: item.Author.Name)); - else if (isFTE == true) - // If a user account was deleted, it's replaced with the "ghost" account. - // For example, https://github.com/MicrosoftDocs/visualstudio-docs/pull/5837. - excludedContributors.Add(!string.IsNullOrEmpty(item.Author.Login) ? item.Author.Login : "ghost"); - } - await ProcessSinglePullRequest(client, prNumber, item.Title, item.ChangedFiles, repo); - } - - Console.WriteLine(); - var distinctExcludedContributors = excludedContributors.Distinct().OrderBy(c => c).ToList(); - var excludedContributorsCount = distinctExcludedContributors.Count; - Console.WriteLine($"Excluded {excludedContributorsCount} {(excludedContributorsCount == 1 ? "contributor" : "contributors")}"); - for (int index = 1; index <= excludedContributorsCount; index++) - { - Console.WriteLine($"{index}. {distinctExcludedContributors[index - 1]}"); - } - Console.WriteLine(); - return totalPRs; - } - } - - private async Task ProcessSinglePullRequest( - IGitHubClient client, int prNumber, string prTitle, int changedFiles, RepositoryDetail repo) - { - // Before sending the request to GitHub, check rules to minimize work and network requests. - - // 1. PRs with a number of files exceeding the value of `MaxFilesChanged` can be ignored. - if (changedFiles > repo.InclusionCriteria.MaxFilesChanged) - { - Console.WriteLine($"Ignoring PR {prNumber}: {prTitle}"); - return; - } - - // 2. PRs whose titles match the provided regex(es) can be ignored. - foreach (var pattern in repo.InclusionCriteria.PullRequestTitlesToIgnore) - { - if (Regex.IsMatch(prTitle, pattern)) - { - Console.WriteLine($"Ignoring PR {prNumber}: {prTitle}"); - return; - } - } - - var request = new PullRequestFilesRequest(client, repo.Owner, repo.Name, prNumber); - if (await request.PerformQueryAsync()) - { - foreach (var prFile in request.Files) - { - var path = prFile.Filename.ToLower(); - var extension = Path.GetExtension(path); - var baseFileName = Path.GetFileNameWithoutExtension(path); - - // The file must begin with the path defined in the config file's `rootDirectory` property. - var notInclude = path.StartsWith(repo.RootDirectory) && - !path.Contains("/includes/") && - baseFileName != "license" && - baseFileName != "readme" && - baseFileName != "toc"; - - // Build the conditions to examine - var useFile = _fileExtensions.Contains(extension); - useFile &= notInclude; - - var significant = prFile.Status switch - { - PullRequestFilesRequest.FileStatus.Added => true, - PullRequestFilesRequest.FileStatus.Modified => - prFile.Additions >= repo.InclusionCriteria.MinAdditionsToFile, - _ => false, - }; - - if (useFile && significant) - { - PrDetail prDetail = new (prFile.Filename, - prFile.Status is PullRequestFilesRequest.FileStatus.Added, - prTitle, - prNumber); - var link = path.Replace(repo.RootDirectory, string.Empty); - - // If the link doesn't contain a slash, it must reside in the root directory. - // Prepend the string with a "./" to indicate so. - if (!link.Contains('/')) - link = $"./{link}"; - - var heading = (from area in repo.Areas - from areaName in area.Names - where link.StartsWith(areaName) - select area.Heading).FirstOrDefault(); - - if (heading is not null) - { - if (!_majorChanges.ContainsKey(link)) - { - _majorChanges[link] = new(heading, - new List() { prDetail }); - } - else - { - _majorChanges[link].Changes.Add(prDetail); - } - } - } - } - } - else - { - Console.WriteLine($"Ignoring {prNumber}: {prTitle} due to error {request.ErrorMessage}"); - return; - } - } -} diff --git a/WhatsNew.Infrastructure/Services/SchemaValidationService.cs b/WhatsNew.Infrastructure/Services/SchemaValidationService.cs deleted file mode 100644 index 134d24f3..00000000 --- a/WhatsNew.Infrastructure/Services/SchemaValidationService.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Microsoft.Extensions.FileProviders; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Schema; -using System.Reflection; -using static WhatsNew.Infrastructure.Constants; -using STJ = System.Text.Json; - -namespace WhatsNew.Infrastructure.Services; - -/// -/// The class responsible for validating JSON configuration files against -/// the associated JSON schema. -/// -public class SchemaValidationService -{ - public void ValidateConfiguration(JToken configToken) - { - var schema = GetSchemaFileJSchema(); - bool isValid = configToken.IsValid(schema, out IList errors); - - if (!isValid) - { - var delimitedErrorDetails = string.Join(", ", errors); - throw new STJ.JsonException( - $"JSON schema validation failed for config file. {delimitedErrorDetails}"); - } - - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("Config file passed schema validation"); - Console.ForegroundColor = ConsoleColor.Gray; - Console.WriteLine(); - } - - public async Task GetSchemaFileContents() - { - var fileInfo = GetSchemaFileInfo(); - - using var stream = fileInfo.CreateReadStream(); - using var reader = new StreamReader(stream); - var fileContents = await reader.ReadToEndAsync(); - - return fileContents; - } - - private JSchema GetSchemaFileJSchema() - { - var fileInfo = GetSchemaFileInfo(); - - using var fileStream = fileInfo.CreateReadStream(); - using var streamReader = new StreamReader(fileStream); - using var textReader = new JsonTextReader(streamReader); - var schema = JSchema.Load(textReader); - - return schema; - } - - private IFileInfo GetSchemaFileInfo() - { - var filePath = Path.Combine(ConfigurationDirectory, "reposettings.schema.json"); - var fileProvider = new ManifestEmbeddedFileProvider(Assembly.GetExecutingAssembly()); - var fileInfo = fileProvider.GetFileInfo(filePath); - - if (!fileInfo.Exists) - throw new FileNotFoundException($"The schema file {filePath} doesn't exist."); - - return fileInfo; - } -} diff --git a/WhatsNew.Infrastructure/Services/TocUpdateService.cs b/WhatsNew.Infrastructure/Services/TocUpdateService.cs deleted file mode 100644 index 88e7a09f..00000000 --- a/WhatsNew.Infrastructure/Services/TocUpdateService.cs +++ /dev/null @@ -1,75 +0,0 @@ -using WhatsNew.Infrastructure.Models; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; - -namespace WhatsNew.Infrastructure.Services; - -// Assumptions: -// 1. Toc node name is "Month YYYY" **valid** -// Later enhancement: The code assumes the parent node is a child of the root node. -// This may need to be updated to have the parent node be anywhere in the tree. -public class TocUpdateService -{ - private readonly WhatsNewConfiguration _configuration; - - public TocUpdateService(WhatsNewConfiguration config) => _configuration = config; - - public async Task UpdateWhatsNewToc() - { - if (_configuration.Repository.NavigationOptions?.RepoTocFolder is null) - { - Console.WriteLine("No TOC folder specified in the configuration. Skipping TOC update."); - return; - } - - // Update TOC.YML: - // 1. Read Toc: - var tocFile = Path.Combine(Path.Combine(_configuration.PathToRepoRoot, _configuration.Repository.NavigationOptions.RepoTocFolder), "toc.yml"); - using var reader = new StreamReader(tocFile); - var deserializer = new DeserializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); - - var toc = deserializer.Deserialize(reader); - reader.Close(); - TocNode? SearchChildren(TocNode item, string target) - { - TocNode? result = null; - if (item.Items is not null) - { - foreach (var child in item.Items) - { - result ??= SearchChildren(child, target); - } - } - result ??= item.Name == target ? item : null; - return result; - } - TocNode? newArticles = default; - foreach(var node in toc.Items) - newArticles ??= SearchChildren(node, _configuration.Repository.NavigationOptions.TocParentNode); - - // 2. Add new article, if it doesn't already exist - string articleName = _configuration.DateRange.StartDate.ToString("MMMM yyyy"); - if (!newArticles?.Items?.Any(item =>item.Name.Equals(articleName)) ?? false) - { - var newNode = new TocNode - { - Name = articleName, - Href = _configuration.MarkdownFileName, - }; - newArticles?.Items?.Insert(0, newNode); - } - - // 3 remove outdated article. - while (newArticles?.Items?.Count > _configuration.Repository.NavigationOptions.MaximumNumberOfArticles) - { - newArticles?.Items.RemoveAt(newArticles.Items.Count - 1); - } - - var serializer = new SerializerBuilder().Build(); - var yaml = serializer.Serialize(toc); - await File.WriteAllTextAsync(tocFile, yaml); - Console.WriteLine($"Updated the table of contents \"{tocFile}\""); - } -} diff --git a/WhatsNew.Infrastructure/WhatsNew.Infrastructure.csproj b/WhatsNew.Infrastructure/WhatsNew.Infrastructure.csproj deleted file mode 100644 index 24d168a1..00000000 --- a/WhatsNew.Infrastructure/WhatsNew.Infrastructure.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - net9.0 - enable - enable - true - 3.1.0.0 - 3.1.0.0 - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs-tools.sln b/docs-tools.sln index 29165643..a1725eef 100644 --- a/docs-tools.sln +++ b/docs-tools.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.4.33205.214 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 d18.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CleanRepo", "cleanrepo\CleanRepo.csproj", "{2BBD8090-3B66-4B57-807E-3A5BC41CC5A9}" EndProject @@ -32,12 +32,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IssueCloser", "IssueCloser\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RepoMan", "RepoMan\RepoMan.csproj", "{009B6BA3-57A5-4CED-8922-96E469A5D247}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhatsNew.Cli", "WhatsNew.Cli\WhatsNew.Cli.csproj", "{D8BFE1AD-8E06-4E7D-A401-C1A08BC7B3D3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhatsNew.Infrastructure", "WhatsNew.Infrastructure\WhatsNew.Infrastructure.csproj", "{32475754-07EC-4D83-9094-90907BF715B4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhatsNew.Infrastructure.Tests", "WhatsNew.Infrastructure.Tests\WhatsNew.Infrastructure.Tests.csproj", "{1FE286F4-8231-4658-BB6A-E63AF22DDBB4}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetDocsTools.Tests", "DotnetDocsToolsTests\DotnetDocsTools.Tests.csproj", "{DCA6CCA3-423E-43A8-8D41-A54837E63F48}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AB0D9824-FE64-4EC3-8F0B-950B68D135C4}" @@ -47,6 +41,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .github\workflows\build-docs-verifier.yml = .github\workflows\build-docs-verifier.yml CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml + Directory.Packages.props = Directory.Packages.props .github\workflows\dogfood.yml = .github\workflows\dogfood.yml .github\workflows\dotnet-build-validation.yml = .github\workflows\dotnet-build-validation.yml LICENSE = LICENSE @@ -59,7 +54,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution sequester.Dockerfile = sequester.Dockerfile ThirdPartyNotices.md = ThirdPartyNotices.md whatsnew.Dockerfile = whatsnew.Dockerfile - Directory.Packages.props = Directory.Packages.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snippets5000", "snippets5000\Snippets5000\Snippets5000.csproj", "{713E0DB5-FF4C-415A-A3CC-42FCDC5BB307}" @@ -134,18 +128,6 @@ Global {009B6BA3-57A5-4CED-8922-96E469A5D247}.Debug|Any CPU.Build.0 = Debug|Any CPU {009B6BA3-57A5-4CED-8922-96E469A5D247}.Release|Any CPU.ActiveCfg = Release|Any CPU {009B6BA3-57A5-4CED-8922-96E469A5D247}.Release|Any CPU.Build.0 = Release|Any CPU - {D8BFE1AD-8E06-4E7D-A401-C1A08BC7B3D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D8BFE1AD-8E06-4E7D-A401-C1A08BC7B3D3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D8BFE1AD-8E06-4E7D-A401-C1A08BC7B3D3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D8BFE1AD-8E06-4E7D-A401-C1A08BC7B3D3}.Release|Any CPU.Build.0 = Release|Any CPU - {32475754-07EC-4D83-9094-90907BF715B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32475754-07EC-4D83-9094-90907BF715B4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32475754-07EC-4D83-9094-90907BF715B4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32475754-07EC-4D83-9094-90907BF715B4}.Release|Any CPU.Build.0 = Release|Any CPU - {1FE286F4-8231-4658-BB6A-E63AF22DDBB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1FE286F4-8231-4658-BB6A-E63AF22DDBB4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1FE286F4-8231-4658-BB6A-E63AF22DDBB4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1FE286F4-8231-4658-BB6A-E63AF22DDBB4}.Release|Any CPU.Build.0 = Release|Any CPU {DCA6CCA3-423E-43A8-8D41-A54837E63F48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DCA6CCA3-423E-43A8-8D41-A54837E63F48}.Debug|Any CPU.Build.0 = Debug|Any CPU {DCA6CCA3-423E-43A8-8D41-A54837E63F48}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/whatsnew.Dockerfile b/whatsnew.Dockerfile deleted file mode 100644 index 1d172af1..00000000 --- a/whatsnew.Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM mcr.microsoft.com/dotnet/sdk:10.0 as build-env -# Copy everything and publish the release (publish implicitly restores and builds) -WORKDIR /app -COPY . ./ -RUN dotnet publish "WhatsNew.Cli/WhatsNew.Cli.csproj" -c Release -o out --no-self-contained - -# Relayer the .NET SDK, anew with the build output -FROM mcr.microsoft.com/dotnet/sdk:10.0 -COPY --from=build-env /app/out . -ENTRYPOINT [ "dotnet", "/WhatsNew.dll" ] \ No newline at end of file From e4034f37be3439035be7793344e32a17cd757c82 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 17 Dec 2025 09:48:33 -0500 Subject: [PATCH 2/3] Update solution to .NET 10 Update all projects, and all docker files to use .NET 10. --- .github/workflows/build-docs-verifier.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/dotnet-build-validation.yml | 8 ++++---- Directory.Packages.props | 6 +++--- DotNet.DocsTools/DotNet.DocsTools.csproj | 6 +++--- DotnetDocsToolsTests/DotnetDocsTools.Tests.csproj | 2 +- IssueCloser/IssueCloser.csproj | 6 +++--- RepoMan/RepoMan.csproj | 8 ++++---- RepoManChecker/RepoManCheckerCLI.csproj | 2 +- XmlDocConflictResolver/XmlDocConflictResolver.csproj | 2 +- .../src/dependabot-bot/dependabot-bot.csproj | 2 +- actions/docs-verifier/Dockerfile | 4 ++-- .../docs-verifier/src/ActionRunner/ActionRunner.csproj | 2 +- .../BuildVerifier.IO.Abstractions.csproj | 2 +- actions/docs-verifier/src/GitHub/GitHub.csproj | 2 +- .../MarkdownLinksVerifier/MarkdownLinksVerifier.csproj | 2 +- .../src/RedirectionVerifier/RedirectionVerifier.csproj | 2 +- .../tests/GitHub.UnitTests/GitHub.UnitTests.csproj | 2 +- .../MarkdownLinksVerifier.UnitTests.csproj | 2 +- actions/sequester/ImportIssues/ImportIssues.csproj | 6 +++--- .../Quest2GitHub.Tests/Quest2GitHub.Tests.csproj | 2 +- actions/sequester/Quest2GitHub/Quest2GitHub.csproj | 6 +++--- cleanrepo/CleanRepo.csproj | 2 +- snippets5000/Snippets5000/Snippets5000.csproj | 2 +- 24 files changed, 41 insertions(+), 41 deletions(-) diff --git a/.github/workflows/build-docs-verifier.yml b/.github/workflows/build-docs-verifier.yml index d3fe115d..be32f1f7 100644 --- a/.github/workflows/build-docs-verifier.yml +++ b/.github/workflows/build-docs-verifier.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@main with: - dotnet-version: '9.0.x' + dotnet-version: '10.0.x' - name: Try get cached dependencies uses: actions/cache@main with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8cee93d2..8d42cbba 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,7 +41,7 @@ jobs: if: ${{ matrix.language == 'csharp' }} uses: actions/setup-dotnet@main with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Install dependencies if: ${{ matrix.language == 'csharp' }} diff --git a/.github/workflows/dotnet-build-validation.yml b/.github/workflows/dotnet-build-validation.yml index c240e482..af0088a1 100644 --- a/.github/workflows/dotnet-build-validation.yml +++ b/.github/workflows/dotnet-build-validation.yml @@ -32,10 +32,10 @@ jobs: env: REASON: ${{ github.event.inputs.reason }} - - name: Setup .NET 9.0 + - name: Setup .NET 10.0 uses: actions/setup-dotnet@main with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Restore dependencies for .NET docs tools run: | @@ -53,10 +53,10 @@ jobs: steps: - uses: actions/checkout@main - - name: Setup .NET 9.0 + - name: Setup .NET 10.0 uses: actions/setup-dotnet@main with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Run tests run: | diff --git a/Directory.Packages.props b/Directory.Packages.props index 4edc72c3..5b037ff2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,7 +17,7 @@ - + @@ -29,8 +29,8 @@ - - + + diff --git a/DotNet.DocsTools/DotNet.DocsTools.csproj b/DotNet.DocsTools/DotNet.DocsTools.csproj index e7a6b861..e72f35f5 100644 --- a/DotNet.DocsTools/DotNet.DocsTools.csproj +++ b/DotNet.DocsTools/DotNet.DocsTools.csproj @@ -1,10 +1,10 @@  - net9.0 + net10.0 enable enable - 2.2.0.0 - 2.2.0.0 + 2.3.0.0 + 2.3.0.0 diff --git a/DotnetDocsToolsTests/DotnetDocsTools.Tests.csproj b/DotnetDocsToolsTests/DotnetDocsTools.Tests.csproj index 7373cf58..8517076f 100644 --- a/DotnetDocsToolsTests/DotnetDocsTools.Tests.csproj +++ b/DotnetDocsToolsTests/DotnetDocsTools.Tests.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 enable enable false diff --git a/IssueCloser/IssueCloser.csproj b/IssueCloser/IssueCloser.csproj index 869d9543..50d66b92 100644 --- a/IssueCloser/IssueCloser.csproj +++ b/IssueCloser/IssueCloser.csproj @@ -1,11 +1,11 @@  Exe - net9.0 + net10.0 enable enable - 2.0.0.0 - 2.0.0.0 + 2.1.0.0 + 2.1.0.0 diff --git a/RepoMan/RepoMan.csproj b/RepoMan/RepoMan.csproj index 22608b98..eb748db5 100644 --- a/RepoMan/RepoMan.csproj +++ b/RepoMan/RepoMan.csproj @@ -1,15 +1,15 @@ - net9.0 + net10.0 v4 Exe enable enable - 4.1.1.0 - 4.1.1 - 4.1.1.0 + 4.2.1.0 + 4.2.1 + 4.2.1.0 diff --git a/RepoManChecker/RepoManCheckerCLI.csproj b/RepoManChecker/RepoManCheckerCLI.csproj index 9a508110..c84973fa 100644 --- a/RepoManChecker/RepoManCheckerCLI.csproj +++ b/RepoManChecker/RepoManCheckerCLI.csproj @@ -1,7 +1,7 @@ exe - net9.0 + net10.0 enable enable RepoMan diff --git a/XmlDocConflictResolver/XmlDocConflictResolver.csproj b/XmlDocConflictResolver/XmlDocConflictResolver.csproj index db0d12e4..da86c307 100644 --- a/XmlDocConflictResolver/XmlDocConflictResolver.csproj +++ b/XmlDocConflictResolver/XmlDocConflictResolver.csproj @@ -1,7 +1,7 @@ Exe - net9.0 + net10.0 enable enable diff --git a/actions/dependabot-bot/src/dependabot-bot/dependabot-bot.csproj b/actions/dependabot-bot/src/dependabot-bot/dependabot-bot.csproj index bc67a8a4..50d60b67 100644 --- a/actions/dependabot-bot/src/dependabot-bot/dependabot-bot.csproj +++ b/actions/dependabot-bot/src/dependabot-bot/dependabot-bot.csproj @@ -1,7 +1,7 @@ Exe - net9.0 + net10.0 enable enable A .NET tool for generating dependabot config files for .NET. diff --git a/actions/docs-verifier/Dockerfile b/actions/docs-verifier/Dockerfile index de809a77..04bd41fc 100644 --- a/actions/docs-verifier/Dockerfile +++ b/actions/docs-verifier/Dockerfile @@ -1,10 +1,10 @@ -FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /app COPY . ./ RUN dotnet publish ./src/ActionRunner/ActionRunner.csproj -c Release -o out --no-self-contained # Build the runtime image -FROM mcr.microsoft.com/dotnet/runtime:9.0 +FROM mcr.microsoft.com/dotnet/runtime:10.0 COPY --from=build /app/out . ENTRYPOINT [ "dotnet", "/ActionRunner.dll" ] diff --git a/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj b/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj index a440b20f..551ad3db 100644 --- a/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj +++ b/actions/docs-verifier/src/ActionRunner/ActionRunner.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 enable enable diff --git a/actions/docs-verifier/src/BuildVerifier.IO.Abstractions/BuildVerifier.IO.Abstractions.csproj b/actions/docs-verifier/src/BuildVerifier.IO.Abstractions/BuildVerifier.IO.Abstractions.csproj index b7b7355d..46bf87d7 100644 --- a/actions/docs-verifier/src/BuildVerifier.IO.Abstractions/BuildVerifier.IO.Abstractions.csproj +++ b/actions/docs-verifier/src/BuildVerifier.IO.Abstractions/BuildVerifier.IO.Abstractions.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable diff --git a/actions/docs-verifier/src/GitHub/GitHub.csproj b/actions/docs-verifier/src/GitHub/GitHub.csproj index fb0e3a80..3db27c0c 100644 --- a/actions/docs-verifier/src/GitHub/GitHub.csproj +++ b/actions/docs-verifier/src/GitHub/GitHub.csproj @@ -1,6 +1,6 @@  - net9.0 + net10.0 enable enable diff --git a/actions/docs-verifier/src/MarkdownLinksVerifier/MarkdownLinksVerifier.csproj b/actions/docs-verifier/src/MarkdownLinksVerifier/MarkdownLinksVerifier.csproj index 87b4229c..2db70848 100644 --- a/actions/docs-verifier/src/MarkdownLinksVerifier/MarkdownLinksVerifier.csproj +++ b/actions/docs-verifier/src/MarkdownLinksVerifier/MarkdownLinksVerifier.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 enable enable diff --git a/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj b/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj index b0c62756..778d1380 100644 --- a/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj +++ b/actions/docs-verifier/src/RedirectionVerifier/RedirectionVerifier.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 enable enable diff --git a/actions/docs-verifier/tests/GitHub.UnitTests/GitHub.UnitTests.csproj b/actions/docs-verifier/tests/GitHub.UnitTests/GitHub.UnitTests.csproj index 5bf689e5..029d49eb 100644 --- a/actions/docs-verifier/tests/GitHub.UnitTests/GitHub.UnitTests.csproj +++ b/actions/docs-verifier/tests/GitHub.UnitTests/GitHub.UnitTests.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 enable enable false diff --git a/actions/docs-verifier/tests/MarkdownLinksVerifier/MarkdownLinksVerifier.UnitTests.csproj b/actions/docs-verifier/tests/MarkdownLinksVerifier/MarkdownLinksVerifier.UnitTests.csproj index 55734be4..ddbb2c79 100644 --- a/actions/docs-verifier/tests/MarkdownLinksVerifier/MarkdownLinksVerifier.UnitTests.csproj +++ b/actions/docs-verifier/tests/MarkdownLinksVerifier/MarkdownLinksVerifier.UnitTests.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 enable enable false diff --git a/actions/sequester/ImportIssues/ImportIssues.csproj b/actions/sequester/ImportIssues/ImportIssues.csproj index 7d222633..ead02c11 100644 --- a/actions/sequester/ImportIssues/ImportIssues.csproj +++ b/actions/sequester/ImportIssues/ImportIssues.csproj @@ -1,12 +1,12 @@ Exe - net9.0 + net10.0 enable enable Linux - 2.2.0.0 - 2.2.0.0 + 2.3.0.0 + 2.3.0.0 diff --git a/actions/sequester/Quest2GitHub.Tests/Quest2GitHub.Tests.csproj b/actions/sequester/Quest2GitHub.Tests/Quest2GitHub.Tests.csproj index e045a4f1..c178980d 100644 --- a/actions/sequester/Quest2GitHub.Tests/Quest2GitHub.Tests.csproj +++ b/actions/sequester/Quest2GitHub.Tests/Quest2GitHub.Tests.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 enable enable false diff --git a/actions/sequester/Quest2GitHub/Quest2GitHub.csproj b/actions/sequester/Quest2GitHub/Quest2GitHub.csproj index fc95a57e..16eb7ba5 100644 --- a/actions/sequester/Quest2GitHub/Quest2GitHub.csproj +++ b/actions/sequester/Quest2GitHub/Quest2GitHub.csproj @@ -1,10 +1,10 @@ - net9.0 + net10.0 enable enable - 2.1.0.0 - 2.1.0.0 + 2.2.0.0 + 2.2.0.0 diff --git a/cleanrepo/CleanRepo.csproj b/cleanrepo/CleanRepo.csproj index 5583ed49..1114b564 100644 --- a/cleanrepo/CleanRepo.csproj +++ b/cleanrepo/CleanRepo.csproj @@ -1,7 +1,7 @@ Exe - net9.0 + net10.0 enable enable Linux diff --git a/snippets5000/Snippets5000/Snippets5000.csproj b/snippets5000/Snippets5000/Snippets5000.csproj index 07aa1ada..d7a8a9a1 100644 --- a/snippets5000/Snippets5000/Snippets5000.csproj +++ b/snippets5000/Snippets5000/Snippets5000.csproj @@ -4,7 +4,7 @@ net10.0 enable enable - 4.4.0.0 + 4.5.0.0 Snippets 5000 Andy De George Microsoft From 748db4ae30409401d683ca1d5562a3c3d465f734 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 17 Dec 2025 09:59:02 -0500 Subject: [PATCH 3/3] Remove IEvangelist He's all aspire all the time. --- .github/CODEOWNERS | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 82a46e22..ad02d5c6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,21 +1,21 @@ * @dotnet/docs -actions/dependabot-bot/ @IEvangelist -actions/docs-verifier/ @BillWagner @IEvangelist +actions/dependabot-bot/ @dotnet/docs +actions/docs-verifier/ @BillWagner actions/dotnet-version-updater/ @IEvangelist actions/sequester/ @BillWagner actions/status-checker/ @gewarren cleanrepo/ @gewarren -DotNet.DocsTools/ @IEvangelist @BillWagner -DotNet.DocsToolsTests/ @IEvangelist @BillWagner +DotNet.DocsTools/ @BillWagner +DotNet.DocsToolsTests/ @BillWagner GitHub.QuerySandbox/ @BillWagner GitHub.RepositoryExplorer/ @BillWagner GitHub.RepositoryExplorer.Client/ @BillWagner GitHub.RepositoryExplorer.Functions/ @BillWagner GitHub.RepositoryExplorer.Models/ @BillWagner GitHub.RepositoryExplorer.Services/ @BillWagner -GitHub.RepositoryExplorer.WebApi/ @BillWagner +GitHub.RepositoryExplorer.WebApi/ @BillWagner IssueCloser/ @BillWagner PackageIndexer/ @gewarren RepoMan/ @adegeo @@ -26,4 +26,3 @@ WhatsNew.Cli/ @BillWagner WhatsNew.Infrastructure/ @BillWagner WhatsNew.Infrastructure.Tests/ @BillWagner XmlDocConflictResolver/ @gewarren -