diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index e88baa6a05..a136aa68c8 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -20,6 +20,8 @@ jobs: - name: Checkout code uses: actions/checkout@v6 + with: + fetch-depth: 0 # GitVersion.MsBuild needs full history - name: Setup .NET Core uses: actions/setup-dotnet@v5 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ff3f13d3e0..55277f0aa6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -32,9 +32,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 + fetch-depth: 0 # GitVersion.MsBuild needs full history # If this run was triggered by a pull request event, then checkout # the head of the pull request instead of the merge commit. diff --git a/.github/workflows/finalize-release.yml b/.github/workflows/finalize-release.yml new file mode 100644 index 0000000000..1ae78d9b74 --- /dev/null +++ b/.github/workflows/finalize-release.yml @@ -0,0 +1,120 @@ +name: Finalize Release + +# Runs automatically when a release PR is merged into main. +# Creates the git tag, GitHub Release, and opens a back-merge PR to develop. + +on: + pull_request: + types: [closed] + branches: [main] + +jobs: + finalize-release: + name: Tag, Release, and Back-merge + # Only run when a release PR is actually merged (not just closed) + if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/') + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout main + uses: actions/checkout@v6 + with: + ref: main + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Extract release info from branch name + id: release + run: | + BRANCH="${{ github.event.pull_request.head.ref }}" + # Branch name is release/vX.Y.Z or release/vX.Y.Z-beta + TAG="${BRANCH#release/}" + SEMVER="${TAG#v}" + + # Determine if this is a pre-release + if echo "$SEMVER" | grep -q '-'; then + PRERELEASE="true" + else + PRERELEASE="false" + fi + + echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "semver=${SEMVER}" >> $GITHUB_OUTPUT + echo "prerelease=${PRERELEASE}" >> $GITHUB_OUTPUT + + - name: Check if tag already exists + run: | + if git rev-parse "${{ steps.release.outputs.tag }}" >/dev/null 2>&1; then + echo "::error::Tag ${{ steps.release.outputs.tag }} already exists. Skipping." + exit 1 + fi + + - name: Create annotated tag + run: | + TAG="${{ steps.release.outputs.tag }}" + git tag -a "$TAG" -m "Release $TAG" + git push origin "$TAG" + echo "Created and pushed tag: $TAG" + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.release.outputs.tag }} + SEMVER: ${{ steps.release.outputs.semver }} + PRERELEASE: ${{ steps.release.outputs.prerelease }} + run: | + PRERELEASE_FLAG="" + if [ "$PRERELEASE" = "true" ]; then + PRERELEASE_FLAG="--prerelease" + fi + + gh release create "$TAG" \ + --title "$TAG" \ + --generate-notes \ + $PRERELEASE_FLAG + + - name: Delete release branch + run: | + BRANCH="${{ github.event.pull_request.head.ref }}" + git push origin --delete "$BRANCH" || true + echo "Deleted branch: $BRANCH" + + - name: Create back-merge PR (main → develop) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.release.outputs.tag }} + run: | + BACKMERGE_BRANCH="backmerge/${TAG}" + + # Create a branch from main for the back-merge + git checkout -b "$BACKMERGE_BRANCH" main + git push origin "$BACKMERGE_BRANCH" + + # Create the PR + gh pr create \ + --base develop \ + --head "$BACKMERGE_BRANCH" \ + --title "Back-merge ${TAG} from main into develop" \ + --body "Automatic back-merge after release ${TAG}. Merge this to keep \`develop\` in sync with \`main\`." + + - name: Summary + run: | + echo "## Release Finalized :rocket:" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Tag:** ${{ steps.release.outputs.tag }}" >> $GITHUB_STEP_SUMMARY + echo "- **Version:** ${{ steps.release.outputs.semver }}" >> $GITHUB_STEP_SUMMARY + echo "- **Pre-release:** ${{ steps.release.outputs.prerelease }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### What happened" >> $GITHUB_STEP_SUMMARY + echo "1. ✅ Tag \`${{ steps.release.outputs.tag }}\` created" >> $GITHUB_STEP_SUMMARY + echo "2. ✅ GitHub Release created with auto-generated notes" >> $GITHUB_STEP_SUMMARY + echo "3. ✅ Publish workflow triggered (NuGet)" >> $GITHUB_STEP_SUMMARY + echo "4. ✅ Back-merge PR opened to develop" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 49848aee8a..3514c44a3f 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + with: + fetch-depth: 0 # GitVersion.MsBuild needs full history - name: Setup .NET Core uses: actions/setup-dotnet@v5 diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 0000000000..3adcdca274 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,184 @@ +name: Prepare Release + +# Creates a release PR from develop → main. +# Maintainers trigger this from Actions → Prepare Release → Run workflow. +# After reviewing, merge the PR — the "Finalize Release" workflow handles the rest. + +on: + workflow_dispatch: + inputs: + release_type: + description: 'Release type (controls the pre-release label on main)' + required: true + type: choice + options: + - beta + - rc + - stable + default: 'beta' + version_override: + description: 'Version override (optional, e.g., 2.0.0). Leave blank to let GitVersion calculate it.' + required: false + type: string + +jobs: + prepare-release: + name: Create Release PR + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout develop + uses: actions/checkout@v6 + with: + ref: develop + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v4.5.0 + with: + versionSpec: '6.x' + + - name: Determine Version + uses: gittools/actions/gitversion/execute@v4.5.0 + with: + useConfigFile: true + id: gitversion + + - name: Compute release version + id: version + run: | + if [ -n "${{ github.event.inputs.version_override }}" ]; then + VERSION="${{ github.event.inputs.version_override }}" + else + VERSION="${{ steps.gitversion.outputs.MajorMinorPatch }}" + fi + + RELEASE_TYPE="${{ github.event.inputs.release_type }}" + + if [ "$RELEASE_TYPE" = "stable" ]; then + SEMVER="${VERSION}" + TAG="v${VERSION}" + else + SEMVER="${VERSION}-${RELEASE_TYPE}" + TAG="v${VERSION}-${RELEASE_TYPE}" + fi + + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "semver=${SEMVER}" >> $GITHUB_OUTPUT + echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "release_type=${RELEASE_TYPE}" >> $GITHUB_OUTPUT + + - name: Check if tag already exists + run: | + if git rev-parse "${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then + echo "::error::Tag ${{ steps.version.outputs.tag }} already exists." + echo "::error::Choose a different version or delete the existing tag first." + exit 1 + fi + + - name: Create release branch + run: | + BRANCH="release/${{ steps.version.outputs.tag }}" + git checkout -b "$BRANCH" + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + id: branch + + - name: Update GitVersion.yml label for release type + run: | + RELEASE_TYPE="${{ steps.version.outputs.release_type }}" + + if [ "$RELEASE_TYPE" = "stable" ]; then + NEW_LABEL=" label: ''" + else + NEW_LABEL=" label: ${RELEASE_TYPE}" + fi + + # Update the label line that follows the main branch regex. + # The main section has 'regex: ^main$' followed by a comment then 'label: ...' + # Use sed to replace the label line after the main regex match. + sed -i "/regex: \^main/,/^ [a-z]/{s/^ label: .*/ label: ${RELEASE_TYPE}/}" GitVersion.yml + + # For stable, label must be empty string + if [ "$RELEASE_TYPE" = "stable" ]; then + sed -i "/regex: \^main/,/^ [a-z]/{s/^ label: .*/ label: ''/}" GitVersion.yml + fi + + echo "Updated GitVersion.yml main label to: '${RELEASE_TYPE}'" + grep -A 5 "^ main:" GitVersion.yml + + - name: Commit label change + run: | + if git diff --quiet GitVersion.yml; then + echo "No label change needed" + else + git add GitVersion.yml + git commit -m "Set release label to '${{ steps.version.outputs.release_type }}' for ${{ steps.version.outputs.tag }}" + fi + + - name: Push release branch + run: | + BRANCH="release/${{ steps.version.outputs.tag }}" + git push origin "$BRANCH" + + - name: Create Pull Request + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SEMVER: ${{ steps.version.outputs.semver }} + TAG: ${{ steps.version.outputs.tag }} + RELEASE_TYPE: ${{ steps.version.outputs.release_type }} + run: | + BRANCH="release/${TAG}" + + if [ "$RELEASE_TYPE" = "stable" ]; then + PRERELEASE_NOTE="" + else + PRERELEASE_NOTE="This is a **${RELEASE_TYPE}** pre-release." + fi + + cat > /tmp/pr_body.md << EOF + ## Release ${TAG} + + ${PRERELEASE_NOTE} + + **Version:** \`${SEMVER}\` + **NuGet Package:** \`Terminal.Gui ${SEMVER}\` + + ### What happens when this PR is merged + + 1. ✅ The **Finalize Release** workflow will automatically create tag \`${TAG}\` + 2. ✅ The **Publish** workflow will build and push to [NuGet.org](https://www.nuget.org/packages/Terminal.Gui) + 3. ✅ A **GitHub Release** will be created with auto-generated notes + 4. ✅ A **back-merge PR** from \`main\` → \`develop\` will be opened + + ### Checklist + + - [ ] CI passes on this PR + - [ ] Version looks correct: \`${SEMVER}\` + - [ ] Release notes reviewed (will be auto-generated on merge) + EOF + + gh pr create \ + --base main \ + --head "$BRANCH" \ + --title "Release ${TAG}" \ + --body-file /tmp/pr_body.md + + - name: Summary + run: | + echo "## Release PR Created :rocket:" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Version:** ${{ steps.version.outputs.semver }}" >> $GITHUB_STEP_SUMMARY + echo "- **Tag:** ${{ steps.version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY + echo "- **Type:** ${{ steps.version.outputs.release_type }}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch:** release/${{ steps.version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Review and merge the PR to trigger the release." >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7f7e5abd17..186c66b5a1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,6 +19,9 @@ jobs: with: fetch-depth: 0 # fetch-depth is needed for GitVersion https://github.com/GitTools/actions/blob/main/docs/cloning.md + # GitVersion.MsBuild (in Terminal.Gui.csproj) automatically computes the version + # from git history + GitVersion.yml during build. We still run the CLI here to + # capture the SemVer for use in subsequent steps (push, template dispatch). - name: Install GitVersion uses: gittools/actions/gitversion/setup@v4.5.0 with: @@ -28,7 +31,6 @@ jobs: uses: gittools/actions/gitversion/execute@v4.5.0 with: useConfigFile: true - updateAssemblyInfo: true id: gitversion # step id used as reference for output values - name: Setup dotnet @@ -41,7 +43,7 @@ jobs: run: dotnet build Terminal.Gui/Terminal.Gui.csproj --no-incremental --nologo --force --configuration Release - name: Pack Release Terminal.Gui ${{ steps.gitversion.outputs.SemVer }} for Nuget - run: dotnet pack Terminal.Gui/Terminal.Gui.csproj -c Release --include-symbols -p:Version='${{ steps.gitversion.outputs.SemVer }}' + run: dotnet pack Terminal.Gui/Terminal.Gui.csproj -c Release --include-symbols --no-build # - name: Test to generate Code Coverage Report # run: | diff --git a/.github/workflows/stress-tests.yml b/.github/workflows/stress-tests.yml index 0660308e30..481741c6fd 100644 --- a/.github/workflows/stress-tests.yml +++ b/.github/workflows/stress-tests.yml @@ -22,6 +22,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + with: + fetch-depth: 0 # GitVersion.MsBuild needs full history - name: Setup .NET Core uses: actions/setup-dotnet@v5 @@ -65,6 +67,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + with: + fetch-depth: 0 # GitVersion.MsBuild needs full history - name: Setup .NET Core uses: actions/setup-dotnet@v5 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index b466926f52..a73ca887de 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -26,6 +26,8 @@ jobs: - name: Checkout code uses: actions/checkout@v6 + with: + fetch-depth: 0 # GitVersion.MsBuild needs full history - name: Setup .NET Core uses: actions/setup-dotnet@v5 @@ -84,6 +86,8 @@ jobs: - name: Checkout code uses: actions/checkout@v6 + with: + fetch-depth: 0 # GitVersion.MsBuild needs full history - name: Setup .NET Core uses: actions/setup-dotnet@v5 diff --git a/Directory.Packages.props b/Directory.Packages.props index 6718b0de22..43bd0c219f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -47,6 +47,7 @@ + diff --git a/Examples/AI/AI.csproj b/Examples/AI/AI.csproj index 751ecad327..6361cd36df 100644 --- a/Examples/AI/AI.csproj +++ b/Examples/AI/AI.csproj @@ -1,10 +1,6 @@ Exe - 2.0 - 2.0 - 2.0 - 2.0 enable latest diff --git a/Examples/Example/Example.csproj b/Examples/Example/Example.csproj index a734c759e3..ed4cca28a1 100644 --- a/Examples/Example/Example.csproj +++ b/Examples/Example/Example.csproj @@ -1,13 +1,6 @@  Exe - - - - 2.0 - 2.0 - 2.0 - 2.0 enable latest diff --git a/Examples/InlineCLI/InlineCLI.csproj b/Examples/InlineCLI/InlineCLI.csproj index d5931053e5..963226eeae 100644 --- a/Examples/InlineCLI/InlineCLI.csproj +++ b/Examples/InlineCLI/InlineCLI.csproj @@ -1,13 +1,6 @@ Exe - - - - 2.0 - 2.0 - 2.0 - 2.0 enable latest diff --git a/Examples/InlineColorPicker/InlineColorPicker.csproj b/Examples/InlineColorPicker/InlineColorPicker.csproj index 38c47ab3fc..46d92f7faf 100644 --- a/Examples/InlineColorPicker/InlineColorPicker.csproj +++ b/Examples/InlineColorPicker/InlineColorPicker.csproj @@ -1,13 +1,6 @@ Exe - - - - 2.0 - 2.0 - 2.0 - 2.0 enable latest diff --git a/Examples/InlineSelect/InlineSelect.csproj b/Examples/InlineSelect/InlineSelect.csproj index 38c47ab3fc..46d92f7faf 100644 --- a/Examples/InlineSelect/InlineSelect.csproj +++ b/Examples/InlineSelect/InlineSelect.csproj @@ -1,13 +1,6 @@ Exe - - - - 2.0 - 2.0 - 2.0 - 2.0 enable latest diff --git a/Examples/README.md b/Examples/README.md new file mode 100644 index 0000000000..0f6aa383ff --- /dev/null +++ b/Examples/README.md @@ -0,0 +1,3 @@ +# Terminal.Gui Examples + +This directory contains example applications demonstrating Terminal.Gui. Each example has its own README with usage details and documentation. diff --git a/Examples/ReactiveExample/ReactiveExample.csproj b/Examples/ReactiveExample/ReactiveExample.csproj index f639a03d79..e3052b9b61 100644 --- a/Examples/ReactiveExample/ReactiveExample.csproj +++ b/Examples/ReactiveExample/ReactiveExample.csproj @@ -1,13 +1,6 @@  Exe - - - - 2.0 - 2.0 - 2.0 - 2.0 diff --git a/Examples/ScenarioRunner/ScenarioRunner.csproj b/Examples/ScenarioRunner/ScenarioRunner.csproj index 1030734925..b9f3a1dfef 100644 --- a/Examples/ScenarioRunner/ScenarioRunner.csproj +++ b/Examples/ScenarioRunner/ScenarioRunner.csproj @@ -1,13 +1,6 @@ Exe - - - - 2.0 - 2.0 - 2.0 - 2.0 TRACE diff --git a/Examples/UICatalog/README.md b/Examples/UICatalog/README.md index 042bc8a945..007733bd8c 100644 --- a/Examples/UICatalog/README.md +++ b/Examples/UICatalog/README.md @@ -1,92 +1,77 @@ # Terminal.Gui UI Catalog -UI Catalog is a comprehensive sample library for Terminal.Gui. It attempts to satisfy the following goals: +UI Catalog is a comprehensive sample library for Terminal.Gui. It provides: -1. Be an easy-to-use showcase for Terminal.Gui concepts and features. -2. Provide sample code that illustrates how to properly implement -said concepts & features. -3. Make it easy for contributors to add additional samples in a structured way. +1. An easy-to-use, interactive, showcase for Terminal.Gui concepts and features. +2. Sample code that illustrates how to properly implement said concepts & features. +3. A structured way for contributors to add additional samples. -![screenshot](screenshot.png) - -## Motivation - -The original `demo.cs` sample app for Terminal.Gui is neither good to showcase, nor does it explain different concepts. In addition, because it is built on a single source file, it has proven to cause friction when multiple contributors are simultaneously working on different aspects of Terminal.Gui. -See [Issue #368](https://github.com/giu-cs/Terminal.Gui/issues/368) for more background. +![uicatalog](../../docfx/images/uicatalog.gif) ## How To Use -Build and run UI Catalog by typing `dotnet run` from the `UI Catalog` folder or by using the `Terminal.Gui` Visual Studio solution. - -`Program.cs` is the main **UI Catalog** app and provides a UI for selecting and running **Scenarios**. Each **Scenario* is implemented as a class derived from `Scenario` and `Program.cs` uses reflection to dynamically build the UI. - -**Scenarios** are tagged with categories using the `[ScenarioCategory]` attribute. The left pane of the main screen lists the categories. Clicking on a category shows all the scenarios in that category. - -**Scenarios** can be run either from the **UICatalog.exe** app UI or by being specified on the command line: - ``` -UICatalog.exe +dotnet run --project ./Examples/UICatalog/UICatalog.csproj ``` -e.g. +or ``` -UICatalog.exe Buttons +dotnet build +./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe ``` -Hitting ENTER on a selected Scenario or double-clicking on a Scenario runs that scenario as though it were a stand-alone Terminal.Gui app. - -When a **Scenario** is run, it runs as though it were a standalone `Terminal.Gui` app. However, scaffolding is provided (in the `Scenario` base class) that (optionally) takes care of `Terminal.Gui` initialization. +## Implementing a Scenario -## Contributing by Adding Scenarios +**Scenarios** are tagged with categories using the `[ScenarioCategory]` attribute. The left pane of the main screen lists the categories. Clicking on a category shows all the scenarios in that category. -To add a new **Scenario** simply: +To add a new **Scenario**: -1. Create a new `.cs` file in the `Scenarios` directory that derives from `Scenario`. -2. Add a `[ScenarioMetaData]` attribute to the class specifying the scenario's name and description. -3. Add one or more `[ScenarioCategory]` attributes to the class specifying which categories the sceanrio belongs to. If you don't specify a category the sceanrio will show up in "All". +1. Create a new `.cs` file in `./Examples/UICatalog/Scenarios` that derives from `Scenario`. +2. Add a `[ScenarioMetaData]` attribute specifying the scenario's name and description. +3. Add one or more `[ScenarioCategory]` attributes specifying which categories the scenario belongs to. If you don't specify a category, the scenario will show up in "All". 4. Implement the `Setup` override which will be called when a user selects the scenario to run. 5. Optionally, implement the `Init` and/or `Run` overrides to provide a custom implementation. -The sample below is provided in the `.\UICatalog\Scenarios` directory as a generic sample that can be copied and re-named: - -```csharp - -namespace UICatalog { - [ScenarioMetadata (Name: "Generic", Description: "Generic sample - A template for creating new Scenarios")] - [ScenarioCategory ("Controls")] - class MyScenario : Scenario { - public override void Setup () - { - // Put your scenario code here, e.g. - Win.Add (new Button () { -Text = "Press me!", - X = Pos.Center (), - Y = Pos.Center (), - Clicked = () => MessageBox.Query (20, 7, "Hi", "Neat?", Strings.btnNo, Strings.btnYes) - }); - } - } -} -``` +See `./Examples/UICatalog/Scenarios/Generic.cs` for a starting point. + +### Contribution Guidelines -`Scenario` provides `Win`, a `Window` object that provides a canvas for the Scenario to operate. +- Provide a terse, descriptive `Name` for `Scenarios`. Keep them short. +- Provide a clear `Description`. +- Comment `Scenario` code to describe to others why it's a useful `Scenario`. +- Annotate `Scenarios` with `[ScenarioCategory]` attributes. Minimize the number of new categories created. -The default `Window` shows the Scenario name and supports exiting the Scenario through the `Esc` key. -![screenshot](generic_screenshot.png) +## Command Line Arguments -To build a more advanced scenario, where control of the `Runnable` and `Window` is needed (e.g. for scenarios using `MenuBar` or `StatusBar`), simply use `Application.Top` per normal Terminal.Gui programming, as seen in the `Notepad` scenario. +Usage: `UICatalog [] [options]` -For complete control, the `Init` and `Run` overrides can be implemented. The `base.Init` creates `Win`. The `base.Run` simply calls `Application.Run(Application.Top)`. +| Option | Description | +|--------|-------------| +| `` | The name of the Scenario to run. If not provided, the interactive UI will be shown. | +| `-dl`, `--debug-log-level` | The log level (`Trace`, `Debug`, `Information`, `Warning`, `Error`, `Critical`, `None`). Default: `Warning`. | +| `-b`, `--benchmark` | Enables benchmarking. If a Scenario is specified, just that Scenario will be benchmarked. | +| `-t`, `--timeout ` | Max time in milliseconds per benchmark. Default: `2500`. | +| `-f`, `--file ` | File to save benchmark results to. If omitted, results are displayed in a `TableView`. | +| `-d`, `--driver ` | The `IDriver` to use (`ansi`, `dotnet`, `windows`). | +| `-dcm`, `--disable-cm` | Disables Configuration Management. Only `ConfigLocations.HardCoded` settings will be loaded. | +| `-16`, `--force-16-colors` | Forces 16-color mode instead of TrueColor. | +| `--help` | Show help (renders this README as formatted markdown). | +| `--version` | Show version information. | -## Contribution Guidelines +### Examples -- Provide a terse, descriptive `Name` for `Scenarios`. Keep them short. -- Provide a clear `Description`. -- Comment `Scenario` code to describe to others why it's a useful `Scenario`. -- Annotate `Scenarios` with `[ScenarioCategory]` attributes. Minimize the number of new categories created. -- Use the `Bug Repo` Category for `Scenarios` that reproduce bugs. - - Include the Github Issue # in the Description. - - Once the bug has been fixed in `develop` submit another PR to remove the `Scenario` (or modify it to provide a good regression test/sample). -- Tag bugs or suggestions for `UI Catalog` as [`Terminal.Gui` Github Issues](https://github.com/gui-cs/Terminal.Gui/issues) with "UICatalog: ". +```bash +# Show formatted help +./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe --help + +# Run a specific scenario +./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe "All Views Tester" + +# Benchmark all scenarios +./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe --benchmark + +# Run with a specific driver and debug logging +./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe -d ansi -dl Debug +``` \ No newline at end of file diff --git a/Examples/UICatalog/UICatalog.cs b/Examples/UICatalog/UICatalog.cs index 1945190ddf..b7ea221c03 100644 --- a/Examples/UICatalog/UICatalog.cs +++ b/Examples/UICatalog/UICatalog.cs @@ -12,6 +12,7 @@ global using Terminal.Gui.Resources; using System.CommandLine; using System.CommandLine.Help; +using System.CommandLine.Invocation; using System.CommandLine.Parsing; using System.Diagnostics; using System.Globalization; @@ -22,6 +23,7 @@ using Serilog; using Serilog.Core; using Serilog.Events; +using TextMateSharp.Grammars; using ILogger = Microsoft.Extensions.Logging.ILogger; #nullable enable @@ -175,7 +177,7 @@ private static int Main (string [] args) DontEnableConfigurationManagement = context.GetRequiredValue (disableConfigManagement), Benchmark = context.GetRequiredValue (benchmarkFlag), BenchmarkTimeout = context.GetRequiredValue (benchmarkTimeout), - ResultsFile = context.GetRequiredValue (resultsFile) ?? string.Empty, + ResultsFile = context.GetRequiredValue (resultsFile), DebugLogLevel = context.GetRequiredValue (debugLogLevel), // Only set Force16Colors if explicitly specified on command line @@ -186,31 +188,52 @@ private static int Main (string [] args) Options = options; }); - var helpShown = false; + // Capture terminal dimensions before any driver changes them + int terminalWidth; + int terminalHeight; - ParseResult parseResult = rootCommand.Parse (args); - - // Check if the analysis results indicate that help should be displayed - if (parseResult.Errors.Count == 0 && parseResult.Action is HelpAction) + try { - helpShown = true; + terminalWidth = Console.WindowWidth; + terminalHeight = Console.WindowHeight; } - - parseResult.Invoke (); - - if (helpShown) + catch (IOException) { - return 0; + // Console dimensions unavailable (e.g. output is piped) + terminalWidth = 120; + terminalHeight = 40; } + // Override --help to render the embedded README.md as formatted markdown + HelpOption helpOption = rootCommand.Options.OfType ().First (); + helpOption.Action = new MarkdownHelpAction (() => RenderMarkdown (ReadEmbeddedReadme (), terminalWidth, terminalHeight)); + + // Override --version to show the Terminal.Gui library version + VersionOption versionOption = rootCommand.Options.OfType ().First (); + versionOption.Action = new VersionAction (() => Console.WriteLine (GetLibraryVersion ())); + + ParseResult parseResult = rootCommand.Parse (args); + + // If there are parse errors, show README then print diagnostics underneath if (parseResult.Errors.Count > 0) { + RenderMarkdown (ReadEmbeddedReadme (), terminalWidth, terminalHeight); + foreach (ParseError error in parseResult.Errors) { Console.Error.WriteLine (error.Message); } - return 1; // Non-zero exit code for error + return 1; + } + + parseResult.Invoke (); + + // If our SetAction callback wasn't invoked (--help, --version, etc.), + // Options won't have been initialized — return early. + if (Options.DebugLogLevel is null) + { + return 0; } Scenario.BenchmarkTimeout = Options.BenchmarkTimeout; @@ -324,4 +347,104 @@ private static void UICatalogMain (UICatalogCommandLineOptions options) runner.RunInteractive (!Options.DontEnableConfigurationManagement); } + + /// + /// Renders markdown content to the terminal using the ANSI driver and exits. + /// + private static void RenderMarkdown (string markdown, int width, int height) + { + // Prevent the ANSI driver from trying to read/write real terminal size or capabilities + Environment.SetEnvironmentVariable ("DisableRealDriverIO", "1"); + IApplication app = Application.Create (); + app.Init (DriverRegistry.Names.ANSI); + + app.Driver?.SetScreenSize (width, height); + + Markdown markdownView = new () + { + App = app, + UseThemeBackground = true, + ShowCopyButtons = false, + Width = Dim.Fill (), + Height = Dim.Fill (), + SyntaxHighlighter = new TextMateSyntaxHighlighter (ThemeName.Dracula), + Text = markdown + }; + + // Layout to get natural content height + markdownView.SetRelativeLayout (app.Screen.Size); + markdownView.Layout (); + + // Resize to the full content height but keep the terminal width + int contentHeight = markdownView.GetContentHeight (); + app.Driver?.SetScreenSize (width, contentHeight); + markdownView.SetRelativeLayout (app.Screen.Size); + + markdownView.Frame = app.Screen with { X = 0, Y = 0 }; + markdownView.Layout (); + + app.Driver?.ClearContents (); + markdownView.Draw (); + Console.WriteLine (app.Driver?.ToAnsi ()); + } + + /// + /// Gets the Terminal.Gui library version from its assembly metadata. + /// + public static string GetLibraryVersion () + { + Assembly libAssembly = typeof (View).Assembly; + + string? informationalVersion = libAssembly.GetCustomAttribute ()?.InformationalVersion; + + if (informationalVersion is { }) + { + // Strip build metadata (everything after '+') for a terse SemVer + int plusIndex = informationalVersion.IndexOf ('+'); + + return plusIndex >= 0 ? informationalVersion [..plusIndex] : informationalVersion; + } + + return libAssembly.GetName ().Version?.ToString () ?? "unknown"; + } + + /// + /// Reads the embedded README.md resource from the assembly. + /// + private static string ReadEmbeddedReadme () + { + Assembly assembly = Assembly.GetExecutingAssembly (); + string resourceName = assembly.GetManifestResourceNames ().First (n => n.EndsWith ("README.md", StringComparison.Ordinal)); + + using Stream stream = assembly.GetManifestResourceStream (resourceName)!; + using StreamReader reader = new (stream); + + return reader.ReadToEnd (); + } +} + +/// +/// Custom help action that renders the embedded README.md as formatted markdown. +/// +internal sealed class MarkdownHelpAction (Action renderHelp) : SynchronousCommandLineAction +{ + public override int Invoke (ParseResult parseResult) + { + renderHelp (); + + return 0; + } +} + +/// +/// Custom version action that displays the Terminal.Gui library version. +/// +internal sealed class VersionAction (Action printVersion) : SynchronousCommandLineAction +{ + public override int Invoke (ParseResult parseResult) + { + printVersion (); + + return 0; + } } diff --git a/Examples/UICatalog/UICatalog.csproj b/Examples/UICatalog/UICatalog.csproj index 88e916f9cf..1dc8d9f303 100644 --- a/Examples/UICatalog/UICatalog.csproj +++ b/Examples/UICatalog/UICatalog.csproj @@ -2,13 +2,7 @@ UICatalog.UICatalog Exe - - - - 2.0 - 2.0 - 2.0 - 2.0 + Linux @@ -22,6 +16,7 @@ + diff --git a/Examples/UICatalog/UICatalogRunnable.cs b/Examples/UICatalog/UICatalogRunnable.cs index aa57f3599f..4dc1b1137b 100644 --- a/Examples/UICatalog/UICatalogRunnable.cs +++ b/Examples/UICatalog/UICatalogRunnable.cs @@ -913,7 +913,7 @@ private void ShowAboutDialog () { Width = Dim.Auto (), Height = Dim.Auto (), - Text = "v2 - Beta", + Text = $"Terminal.Gui {UICatalog.GetLibraryVersion ()}", X = Pos.Center (), Y = Pos.Bottom (logo) + 1 }; diff --git a/Examples/mdv/mdv.csproj b/Examples/mdv/mdv.csproj index f465e91566..98e6fca9b7 100644 --- a/Examples/mdv/mdv.csproj +++ b/Examples/mdv/mdv.csproj @@ -1,13 +1,6 @@ Exe - - - - 2.0 - 2.0 - 2.0 - 2.0 enable latest diff --git a/GitVersion.yml b/GitVersion.yml index ddfe73b07c..f10138884e 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -70,20 +70,28 @@ branches: # source-branches: ['v1_develop'] # Pull Request Branches - # Configures versioning for PRs (e.g., 2.0.0-pr.feature-123.1) + # Configures versioning for PRs (e.g., 2.0.0-PullRequest5009.1) pull-request: - # Matches typical PR branch names - regex: ^(pull|pull\-requests|pr)[/-] - # Uses 'pr' prefix with branch name in the label (e.g., pr.feature-123) - label: pr.{BranchName} - # Inherits increment strategy from source branch + regex: "^(pull|pull\\-requests|pr)[\\/-](?\\d+)" + label: "PullRequest{Number}" increment: Inherit source-branches: - develop - main - # High weight ensures PR versions sort after regular pre-releases + - feature pre-release-weight: 30000 + # Feature / Fix / Topic Branches + # Matches branches like feature/*, fix/*, issue/*, etc. + # Inherits version from develop and uses the branch name as pre-release label. + # e.g., if develop is at 2.2.0, fix/foobar produces 2.2.0-fix-foobar.1 + feature: + regex: "^(feature|fix|issue|dependabot|copilot)[\\/-](?.+)" + label: "{BranchName}" + increment: Inherit + source-branches: + - develop + # Ignore specific commits if needed (currently empty) ignore: sha: [] \ No newline at end of file diff --git a/Terminal.Gui/README.md b/Terminal.Gui/README.md index fbc18bafd8..abbbfffb41 100644 --- a/Terminal.Gui/README.md +++ b/Terminal.Gui/README.md @@ -1,126 +1,161 @@ -# Terminal.Gui Project +# Terminal.Gui Library — Maintainer Guide -**Terminal.Gui** is a cross-platform UI toolkit for creating console-based graphical user interfaces in .NET. This repository contains all files required to build the **Terminal.Gui** library and NuGet package, enabling developers to create rich terminal applications with ease. +This directory contains the core **Terminal.Gui** library source code. This README documents how to maintain and release the library. For contribution guidelines, see [CONTRIBUTING.md](../CONTRIBUTING.md). For building apps with Terminal.Gui, see [the documentation](https://gui-cs.github.io/Terminal.Gui). -## Project Overview +## Versioning -**Terminal.Gui** provides a comprehensive framework for building interactive console applications with support for keyboard and mouse input, customizable views, and a robust event system. It is designed to work across Windows, macOS, and Linux, leveraging platform-specific console capabilities where available. +Versions are computed automatically by [GitVersion 6.x](https://gitversion.net) using the [GitFlow](https://gitversion.net/docs/learn/branching-strategies/gitflow/) branching strategy. Configuration is in [`GitVersion.yml`](../GitVersion.yml). -## Project Folder Structure +The [GitVersion.MsBuild](https://www.nuget.org/packages/GitVersion.MsBuild) NuGet package is included in `Terminal.Gui.csproj` and automatically sets `Version`, `AssemblyVersion`, `FileVersion`, and `InformationalVersion` from git history at build time. No manual version management is needed. -This directory contains the core **Terminal.Gui** library source code. For a detailed repository structure, see [CONTRIBUTING.md - Repository Structure](../CONTRIBUTING.md#repository-structure). +### How Versions Are Computed -## Getting Started +| Branch | Example Version | Increment | Notes | +|--------|----------------|-----------|-------| +| `main` (pre-release) | `2.0.0-beta.3` | Patch | Label set in `GitVersion.yml` (`label: beta`) | +| `main` (stable) | `2.0.0` | Patch | Set `label: ''` for stable release | +| `develop` | `2.1.0-develop.42` | Minor | Always carries `-develop` pre-release label | +| `feature/*`, `fix/*`, etc. | `2.1.0-my-feature.1` | Inherit | Inherits from `develop`; branch name becomes label | +| `pull-request/*` | `2.0.0-pr.123.1` | Inherit | PR number in label | -For instructions on how to start using **Terminal.Gui**, refer to the [Getting Started Guide](https://gui-cs.github.io/Terminal.Gui/docs/getting-started.html) in our documentation. +### Checking Versions Locally -## Documentation +```powershell +# Install the CLI tool (one-time) +dotnet tool install --global GitVersion.Tool -Comprehensive documentation is available at [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui). +# Show what version would be computed for the current branch +dotnet-gitversion +``` -For information on generating and updating the API documentation locally, refer to the [DocFX README](../docfx/README.md). +The version is also embedded in every build. For example, `UICatalog --version` displays the terse SemVer (build metadata after `+` is stripped). -## Versioning +### Pre-Release Label Progression on `main` -Version information for Terminal.Gui is managed by [gitversion](https://gitversion.net). To install `gitversion`: +To change the pre-release stage, edit the `label` field under `main` in `GitVersion.yml`: -```powershell -dotnet tool install --global GitVersion.Tool -dotnet-gitversion -``` +| Stage | `label` value | Example Output | +|-------|--------------|----------------| +| Beta | `beta` | `2.0.0-beta.1` | +| Release Candidate | `rc` | `2.0.0-rc.1` | +| Stable Release | `''` (empty) | `2.0.0` | -The project version (used in the NuGet package and `Terminal.Gui.dll`) is determined from the latest `git tag`. The format of version numbers is `major.minor.patch.build.height` and follows [Semantic Versioning](https://semver.org/) rules. +## Publishing a Release -To define a new version, tag a commit using `git tag`: +Releases follow [Semantic Versioning](https://semver.org/): **MAJOR** for breaking changes, **MINOR** for new features, **PATCH** for bug fixes. -```powershell -git tag v2.1.0-beta.1 -a -m "Release v2.1.0 Beta 1" -dotnet-gitversion /updateprojectfiles -dotnet build -c Release -``` +### Automated Release Workflow (Preferred) -**DO NOT COMMIT AFTER USING `/updateprojectfiles`!** Doing so will update the `.csproj` files in your branch with version info, which we do not want. +Two workflows handle the release lifecycle: -## Publishing a Release of Terminal.Gui +**Step 1 — [Prepare Release](../.github/workflows/prepare-release.yml)** (manual trigger): +1. Go to **Actions → Prepare Release → Run workflow**. +2. Pick the release type (`beta`, `rc`, `stable`) and optionally override the version. +3. The workflow creates a `release/vX.Y.Z` branch from `develop`, updates the `GitVersion.yml` label, and opens a PR into `main`. +4. Review the PR — CI runs, branch protections apply. -To release a new version, follow these steps based on [Semantic Versioning](https://semver.org/) rules: +**Step 2 — [Finalize Release](../.github/workflows/finalize-release.yml)** (automatic on PR merge): +When the release PR is merged into `main`: +1. Creates an annotated tag (`vX.Y.Z` or `vX.Y.Z-beta`) +2. Creates a GitHub Release with auto-generated notes +3. [Publish](../.github/workflows/publish.yml) fires automatically → NuGet package published +4. Opens a back-merge PR (`main` → `develop`) to keep branches in sync -- **MAJOR** version for incompatible API changes. -- **MINOR** version for backwards-compatible functionality additions. -- **PATCH** version for backwards-compatible bug fixes. +### Manual Release Steps -### Steps for Release: +If you need to release manually: -1. **Verify the `develop` branch is ready for release**: - - Ensure all changes are committed and pushed to the `develop` branch. - - Ensure your local `develop` branch is up-to-date with `upstream/develop`. +1. **Ensure `develop` is ready**: all changes committed, CI passing. -2. **Create a pull request for the release in the `develop` branch**: - - Title the PR as "Release vX.Y.Z". - ```powershell - git checkout develop - git pull upstream develop - git checkout -b vX_Y_Z - git add . - git commit -m "Release vX.Y.Z" - git push - ``` - - Go to the link printed by `git push` and fill out the Pull Request. +2. **Merge `develop` into `main`**: + ```powershell + git checkout main + git pull upstream main + git checkout develop + git pull upstream develop + git checkout main + git merge develop + # Fix any merge conflicts + ``` -3. **On github.com, verify the build action worked on your fork, then merge the PR**. +3. **Update the pre-release label** in `GitVersion.yml` if changing stages (e.g., `beta` → `rc` → `''`). -4. **Pull the merged `develop` from `upstream`**: - ```powershell - git checkout develop - git pull upstream develop - ``` +4. **Tag the release on `main`**: + ```powershell + git tag vX.Y.Z -a -m "Release vX.Y.Z" + ``` -5. **Merge `develop` into `main`**: - ```powershell - git checkout main - git pull upstream main - git merge develop - ``` - - Fix any merge errors. +5. **Push atomically**: + ```powershell + git push --atomic upstream main vX.Y.Z + ``` -6. **Create a new annotated tag for the release on `main`**: - ```powershell - git tag vX.Y.Z -a -m "Release vX.Y.Z" - ``` +6. **Monitor CI**: the [Publish workflow](https://github.com/gui-cs/Terminal.Gui/actions) builds and pushes to [NuGet](https://www.nuget.org/packages/Terminal.Gui). It also triggers an update to [Terminal.Gui.templates](https://github.com/gui-cs/Terminal.Gui.templates) for stable releases. -7. **Push the new tag to `main` on `upstream`**: - ```powershell - git push --atomic upstream main vX.Y.Z - ``` +7. **Create a GitHub Release** at [Releases](https://github.com/gui-cs/Terminal.Gui/releases) with auto-generated release notes. -8. **Monitor Github Actions to ensure the NuGet publishing worked**: - - Check [GitHub Actions](https://github.com/gui-cs/Terminal.Gui/actions). +8. **Merge `main` back into `develop`**: + ```powershell + git checkout develop + git pull upstream develop + git merge main + git push upstream develop + ``` -9. **Check NuGet to see the new package version (wait a few minutes)**: - - Visit [NuGet Package](https://www.nuget.org/packages/Terminal.Gui). +## CI/CD Workflows -10. **Add a new Release in Github**: - - Go to [GitHub Releases](https://github.com/gui-cs/Terminal.Gui/releases) and generate release notes with the list of PRs since the last release. +All workflows are in [`.github/workflows/`](../.github/workflows/): -11. **Update the `develop` branch with the new version**: - ```powershell - git checkout develop - git pull upstream develop - git merge main - git push upstream develop - ``` +| Workflow | Trigger | Purpose | +|----------|---------|---------| +| **[prepare-release.yml](../.github/workflows/prepare-release.yml)** | Manual dispatch | Creates a release PR from `develop` → `main` with label updates | +| **[finalize-release.yml](../.github/workflows/finalize-release.yml)** | Release PR merged to `main` | Creates tag, GitHub Release, back-merge PR to `develop` | +| **[publish.yml](../.github/workflows/publish.yml)** | Push to `main` or `develop`, version tags | Builds Release config, packs, and publishes to NuGet.org | +| **[release.yml](../.github/workflows/release.yml)** | Manual dispatch | **(Legacy)** Direct tag-and-release on `main`; superseded by prepare/finalize | +| **[build-validation.yml](../.github/workflows/build-validation.yml)** | Push/PR to `main` or `develop` | Builds all configurations to validate compilation | +| **[unit-tests.yml](../.github/workflows/unit-tests.yml)** | Push/PR to `main` or `develop` | Runs all unit tests | +| **[api-docs.yml](../.github/workflows/api-docs.yml)** | Push to `develop` | Builds and deploys API docs to GitHub Pages | +| **[codeql-analysis.yml](../.github/workflows/codeql-analysis.yml)** | Push/PR to `main` or `develop` | CodeQL security analysis | +| **[integration-tests.yml](../.github/workflows/integration-tests.yml)** | Push/PR to `main` or `develop` | Integration tests | +| **[stress-tests.yml](../.github/workflows/stress-tests.yml)** | Push/PR to `main` or `develop` | Stress tests | -## NuGet +## V1 Legacy Branches -The official NuGet package for Terminal.Gui is available at [https://www.nuget.org/packages/Terminal.Gui](https://www.nuget.org/packages/Terminal.Gui). When a new version tag is defined and merged into `main`, a NuGet package is automatically generated by a GitHub Action. Pre-release versions (e.g., `2.0.0-beta.5`) are tagged as pre-release on NuGet. +Terminal.Gui V1 (latest: `v1.19.0`) is maintained on separate branches: -## Contributing +| Branch | Purpose | +|--------|---------| +| `v1_release` | V1 stable releases (equivalent of `main` for V1) | +| `v1_develop` | V1 development (equivalent of `develop` for V1) | -We welcome contributions from the community. For complete contribution guidelines, including: -- Build and test instructions -- Coding conventions and style rules -- Testing requirements and patterns -- Pull request guidelines -- CI/CD workflows +V1 follows the same GitFlow model as V2 but is in maintenance-only mode. The V1 NuGet package is `Terminal.Gui` 1.x. V1 API docs are published from `v1_release` to a separate GitHub Pages path. + +These branches are **not** configured in `GitVersion.yml` (the config was removed to avoid interference with V2 versioning). V1 releases are tagged manually (e.g., `v1.19.0`). + +## NuGet Package + +- **Package**: [nuget.org/packages/Terminal.Gui](https://www.nuget.org/packages/Terminal.Gui) +- **Auto-published** on every push to `main` or `develop` (pre-release versions from `develop`, release versions from `main`) +- Pre-release versions (e.g., `2.0.0-beta.5`) are marked as pre-release on NuGet + +### Local Package Development + +When building in `Release` configuration, the `.csproj` automatically: +1. Copies `.nupkg` and `.snupkg` to `../local_packages/` +2. Pushes the package to the local NuGet cache + +Use the `local_packages` folder as a local NuGet source to test packages before publishing: +```powershell +dotnet build Terminal.Gui/Terminal.Gui.csproj -c Release +# Package is now in local_packages/ and your global NuGet cache +``` + +## Documentation + +- **Live docs**: [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui) +- **DocFX source**: [`docfx/`](../docfx/) — see [`docfx/README.md`](../docfx/README.md) for local generation +- **API docs** are auto-deployed to GitHub Pages on every push to `develop` + +## Contributing -Please refer to [CONTRIBUTING.md](../CONTRIBUTING.md) in the repository root. +See [CONTRIBUTING.md](../CONTRIBUTING.md) for build/test instructions, coding conventions, and PR guidelines. diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index a91b4c6d6f..893d3143e0 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -1,13 +1,8 @@  - - - + - - 2.0.0 - @@ -45,7 +40,6 @@ true - @@ -76,6 +70,9 @@ + + + @@ -209,6 +206,6 @@ - + diff --git a/Tests/UnitTests.Legacy/UnitTests.Legacy.csproj b/Tests/UnitTests.Legacy/UnitTests.Legacy.csproj index 98cce1be6b..9eb7fa9818 100644 --- a/Tests/UnitTests.Legacy/UnitTests.Legacy.csproj +++ b/Tests/UnitTests.Legacy/UnitTests.Legacy.csproj @@ -1,13 +1,4 @@  - - - - - 2.0 - 2.0 - 2.0 - 2.0 - UnitTests.Legacy Exe diff --git a/Tests/UnitTests.NonParallelizable/UnitTests.NonParallelizable.csproj b/Tests/UnitTests.NonParallelizable/UnitTests.NonParallelizable.csproj index 76f1c2b6bf..b414931854 100644 --- a/Tests/UnitTests.NonParallelizable/UnitTests.NonParallelizable.csproj +++ b/Tests/UnitTests.NonParallelizable/UnitTests.NonParallelizable.csproj @@ -1,13 +1,4 @@  - - - - - 2.0 - 2.0 - 2.0 - 2.0 - UnitTests.NonParallelizable Exe diff --git a/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj b/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj index f590d74bd6..7da1c0c2dd 100644 --- a/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj +++ b/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj @@ -1,13 +1,4 @@  - - - - - 2.0 - 2.0 - 2.0 - 2.0 - Exe true diff --git a/docfx/images/uicatalog.gif b/docfx/images/uicatalog.gif new file mode 100644 index 0000000000..c91292922d Binary files /dev/null and b/docfx/images/uicatalog.gif differ