Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 4 additions & 20 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,33 +48,17 @@ The repository uses multiple GitHub Actions workflows. What runs and when:
3. Runs IntegrationTests with diagnostic output
4. Uploads logs per-OS

### 4) Create Release (`.github/workflows/release.yml`)

- **Triggers**: `workflow_dispatch` (manual trigger from GitHub Actions UI)
- **Inputs**:
- `release_type`: Choose from `beta`, `rc`, or `stable`
- `version_override`: (Optional) Specify exact version number, otherwise GitVersion calculates it
- **Process**:
1. Checks out `main` branch
2. Determines version using GitVersion or override
3. Creates annotated git tag (e.g., `v2.0.1-rc.1` or `v2.1.0`)
4. Creates release commit with message
5. Pushes tag and commit to repository
6. Creates GitHub Release (marked as pre-release if not stable)
7. Automatically triggers publish workflow (see below)
- **Purpose**: Automates the release process to prevent manual errors

### 5) Publish to NuGet (`.github/workflows/publish.yml`)
### 4) Publish to NuGet (`.github/workflows/publish.yml`)

- **Triggers**: push to `develop` and tags `v*` (ignores `**.md`)
- Uses GitVersion to compute SemVer, builds Release, packs Terminal.Gui and Terminal.Gui.Interop.Spectre with symbols, and pushes both to NuGet.org using `NUGET_API_KEY`
- **Automatically triggered** by the Create Release workflow when a new tag is pushed
- **Automatically triggered** when a `v*` tag is pushed — typically by the **Finalize Release** workflow after a release PR (from **Prepare Release**) is merged into `main`
- **Additional actions on tag push** (`v*`):
- Triggers Terminal.Gui.templates repository update via repository_dispatch (requires `TEMPLATE_REPO_TOKEN` secret)

### 6) Build and publish API docs (`.github/workflows/api-docs.yml`)
### 5) Build and publish API docs (`.github/workflows/api-docs.yml`)

- **Triggers**: push to `main`
- **Triggers**: push to `develop`, and `workflow_dispatch`
- Builds DocFX site on Windows and deploys to GitHub Pages


Expand Down
144 changes: 0 additions & 144 deletions .github/workflows/release.yml

This file was deleted.

22 changes: 10 additions & 12 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,23 +336,21 @@ foreach (Rune rune in text.EnumerateRunes ())

### Automated Release Workflow

Releases are now automated using GitHub Actions to prevent manual errors. To create a release:
Releases are automated using GitHub Actions to prevent manual errors. The flow is PR-based: **Prepare Release** opens a release PR from `develop` → `main`, and merging it runs **Finalize Release**. To create a release:

1. **Navigate to Actions tab** in the GitHub repository
2. **Select "Create Release" workflow** from the left sidebar
2. **Select "Prepare Release" workflow** from the left sidebar
3. **Click "Run workflow"** button
4. **Configure release parameters:**
- **Branch:** Ensure `main` is selected
- **Release type:** Choose from `prealpha`, `alpha`, `beta`, `rc`, or `stable`
- **Release type:** Choose from `beta`, `rc`, or `stable`
- **Version override:** (Optional) Specify exact version (e.g., `2.0.0`), otherwise GitVersion calculates it automatically
5. **Click "Run workflow"** to start the automated release process

The workflow will:
- Create an annotated git tag (e.g., `v2.0.0-prealpha` or `v2.0.0`)
- Create a release commit on `main`
- Push the tag and commit to the repository
- Create a GitHub Release
- Automatically trigger the publish workflow to push the package to NuGet.org
5. **Click "Run workflow"** to create the release PR

Prepare Release creates a `release/<tag>` branch — where `<tag>` is the full version tag, e.g. `release/v2.0.0` for a stable release or `release/v2.0.0-beta.1` for a pre-release — with the GitVersion label updated for the release type, and opens a PR into `main`. After CI passes, **review and merge that PR**. Merging triggers **Finalize Release**, which:
- Creates an annotated git tag (e.g., `v2.0.0-beta.1` or `v2.0.0`)
- Creates a GitHub Release with auto-generated notes
- Opens a back-merge PR from `main` → `develop`
- Triggers the publish workflow (via the `v*` tag) to push the package to NuGet.org

## What NOT to Do

Expand Down
2 changes: 1 addition & 1 deletion GitVersion.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ branches:
# Matches the main branch
regex: ^main$
# Uses an empty label for stable releases
label: beta
label: ''
# Increments patch version (x.y.z+1) on commits
increment: Patch
# Specifies develop as the source branch
Expand Down
27 changes: 14 additions & 13 deletions Terminal.Gui/Configuration/Settings/TuiConfigurationBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -168,7 +167,7 @@ public void ApplyToStaticFacades ()

[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Settings POCOs are simple types preserved by DynamicDependency in ConfigPropertyHostTypes.")]
[UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Settings POCOs are simple types; no generic instantiation needed at runtime.")]
private static void BindSection<T> (IConfiguration config, string sectionName, Action<T> apply) where T : new ()
private static void BindSection<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T> (IConfiguration config, string sectionName, Action<T> apply) where T : new ()
{
T settings = new ();
IConfigurationSection section = config.GetSection (sectionName);
Expand Down Expand Up @@ -206,10 +205,10 @@ private static void BindThemeScalar (IConfiguration config)

/// <summary>
/// Binds flat dotted keys (e.g. <c>Driver.Force16Colors</c>) from the configuration root to the
/// corresponding properties on the settings POCO.
/// corresponding properties on the settings POCO. <typeparamref name="T"/>'s public properties are
/// preserved for trimming via the <see cref="DynamicallyAccessedMembersAttribute"/> on the type parameter.
/// </summary>
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Settings POCOs are simple types preserved by DynamicDependency in ConfigPropertyHostTypes.")]
private static void BindFlatDottedKeys<T> (IConfiguration config, string sectionName, T settings)
private static void BindFlatDottedKeys<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T> (IConfiguration config, string sectionName, T settings)
{
string prefix = sectionName + ".";

Expand Down Expand Up @@ -244,8 +243,12 @@ private static void BindFlatDottedKeys<T> (IConfiguration config, string section
}

/// <summary>
/// Converts a configuration string value to the target property type, using a fast path for the
/// common scalar types and falling back to a <see cref="TypeConverter"/>.
/// Converts a configuration string value to the target property type. Only the scalar types used by the
/// settings POCOs are supported, so this path is trim/AOT-safe — it deliberately avoids
/// <c>TypeDescriptor.GetConverter</c> (which is <see cref="RequiresUnreferencedCodeAttribute"/> /
/// <see cref="RequiresDynamicCodeAttribute"/> and breaks NativeAOT/trimmed consumers). New non-scalar
/// settings property types must be added here explicitly. Unsupported types return <see langword="null"/>
/// and are skipped by <see cref="BindFlatDottedKeys{T}"/>.
/// </summary>
private static object? ConvertValue (string value, Type targetType)
{
Expand All @@ -269,16 +272,14 @@ private static void BindFlatDottedKeys<T> (IConfiguration config, string section
return value.Length > 0 ? new Rune (value [0]) : new Rune ('+');
}

if (targetType.IsEnum)
if (targetType == typeof (Key))
{
return Enum.Parse (targetType, value);
return Key.TryParse (value, out Key key) ? key : null;
}

TypeConverter converter = TypeDescriptor.GetConverter (targetType);

if (converter.CanConvertFrom (typeof (string)))
if (targetType.IsEnum)
{
return converter.ConvertFromInvariantString (value);
return Enum.Parse (targetType, value);
}

return null;
Expand Down
9 changes: 4 additions & 5 deletions Terminal.Gui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Two workflows handle the release lifecycle:
**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`.
3. The workflow creates a `release/<tag>` branch from `develop` — where `<tag>` is the full version tag, e.g. `release/v2.0.0` or `release/v2.0.0-beta.1` — updates the `GitVersion.yml` label, and opens a PR into `main`.
4. Review the PR — CI runs, branch protections apply.

**Step 2 — [Finalize Release](../.github/workflows/finalize-release.yml)** (automatic on PR merge):
Expand Down Expand Up @@ -110,8 +110,7 @@ All workflows are in [`.github/workflows/`](../.github/workflows/):
|----------|---------|---------|
| **[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 |
| **[publish.yml](../.github/workflows/publish.yml)** | Push to `develop`, or `v*` tags | Builds Release config, packs, and publishes to NuGet.org |
| **[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 |
Expand All @@ -135,7 +134,7 @@ These branches are **not** configured in `GitVersion.yml` (the config was remove
## 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`, stable versions from `main`)
- **Auto-published** on every push to `develop` (pre-release versions) and on `v*` tag pushes (stable/pre-release versions tagged on `main`)
- Pre-release versions (e.g., `2.1.0-develop.42`) are marked as pre-release on NuGet

### Local Package Development
Expand All @@ -154,7 +153,7 @@ dotnet build Terminal.Gui/Terminal.Gui.csproj -c Release

- **Live docs**: [tui-cs.github.io/Terminal.Gui](https://tui-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 `main`
- **API docs** are auto-deployed to GitHub Pages on every push to `develop`

## Contributing

Expand Down
Loading
Loading