From e39ad161e0c52db06aaa827d08a28d2530bdb04d Mon Sep 17 00:00:00 2001 From: John Smith Date: Thu, 12 Nov 2026 11:14:37 +0000 Subject: [PATCH 1/3] build: target net10.0 and net11.0 only .NET 8 and 9 left support on 10 Nov 2026. The tool and tests now target net10.0;net11.0, and CI installs only the 10.0.x and 11.0.x SDKs. Docs and agent instructions name .NET 10 and 11. A machine with only .NET 8 or 9 can no longer install new releases. Release this as a minor bump (1.3.0). RollForward Major is unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014Mjr3bTLWVmY8sc2j5bcqH --- .github/copilot-instructions.md | 6 +++--- .github/workflows/publish.yml | 6 ++---- AGENTS.md | 8 ++++---- CHANGELOG.md | 8 ++++++++ README.md | 6 +++--- src/Ytx/Ytx.csproj | 2 +- tests/Ytx.Tests/Ytx.Tests.csproj | 2 +- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 62914b7..c06e087 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -5,7 +5,7 @@ This repo is a small .NET global tool (PackageId: `solrevdev.ytx`) that extracts - Entry points: - `src/Ytx/Program.cs` — single-file console app; contains the main flow: input parsing (args or JSON stdin), YoutubeExplode calls, caption selection, transcript normalization, and JSON output. - - `src/Ytx/Ytx.csproj` — packaging details (PackAsTool, ToolCommandName=`ytx`), TargetFrameworks (`net8.0;net9.0`), and Version (auto-bumped by CI). + - `src/Ytx/Ytx.csproj` — packaging details (PackAsTool, ToolCommandName=`ytx`), TargetFrameworks (`net10.0;net11.0`), and Version (auto-bumped by CI). - What to change and where: - Feature or bugfix touching runtime behavior: edit `Program.cs` and add small, well-scoped tests under `tests/` when applicable. @@ -14,7 +14,7 @@ This repo is a small .NET global tool (PackageId: `solrevdev.ytx`) that extracts - Build / run / package (explicit commands example): - Restore: `dotnet restore src/Ytx` - Build: `dotnet build src/Ytx -c Release` - - Run (manual test): `dotnet run --project src/Ytx --framework net8.0 ""` + - Run (manual test): `dotnet run --project src/Ytx --framework net10.0 ""` - Pack: `dotnet pack src/Ytx -c Release` (packages to `./nupkg` per csproj) - Local tool install for verification: `dotnet tool install -g solrevdev.ytx --add-source ./nupkg` @@ -35,7 +35,7 @@ This repo is a small .NET global tool (PackageId: `solrevdev.ytx`) that extracts - External dependencies & compatibility: - Uses `YoutubeExplode` (pinned to 6.5.4). Avoid upgrading without testing caption extraction end-to-end. - - Targets .NET 8 and 9; keep `Nullable` and `ImplicitUsings` enabled to match project style. + - Targets .NET 10 and 11; keep `Nullable` and `ImplicitUsings` enabled to match project style. - Small fixes that are preferred: - Add unit tests for any non-trivial parsing/formatting changes. diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 671d1e0..fc02273 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -50,9 +50,8 @@ jobs: uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0 with: dotnet-version: | - 8.0.x - 9.0.x 10.0.x + 11.0.x - name: Restore run: | @@ -84,9 +83,8 @@ jobs: uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0 with: dotnet-version: | - 8.0.x - 9.0.x 10.0.x + 11.0.x - name: Determine and bump version id: bump diff --git a/AGENTS.md b/AGENTS.md index 9c1d9e3..6e60ace 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ This file provides guidance to coding agents such as Codex and Claude Code when ## Project Overview -This is `ytx`, a .NET Global Tool that extracts YouTube video metadata and transcripts as structured JSON. It's packaged as `solrevdev.ytx` on NuGet and targets .NET 8.0, 9.0, and 10.0. +This is `ytx`, a .NET Global Tool that extracts YouTube video metadata and transcripts as structured JSON. It's packaged as `solrevdev.ytx` on NuGet and targets .NET 10.0 and 11.0. ## Core Architecture @@ -27,7 +27,7 @@ This is `ytx`, a .NET Global Tool that extracts YouTube video metadata and trans # Build and test locally dotnet restore src/Ytx dotnet build src/Ytx -c Release -dotnet run --project src/Ytx --framework net8.0 "YOUTUBE_URL" +dotnet run --project src/Ytx --framework net10.0 "YOUTUBE_URL" # Package for local testing dotnet pack src/Ytx -c Release @@ -40,7 +40,7 @@ dotnet tool install -g solrevdev.ytx --add-source ./nupkg ## CI/CD Integration **GitHub Actions Workflow** (`.github/workflows/publish.yml`): -- Pull requests that change the project, tests or workflow run read-only restore, build, test (net8.0, net9.0, net10.0) and pack validation +- Pull requests that change the project, tests or workflow run read-only restore, build, test (net10.0, net11.0) and pack validation - Pushes to `master` automatically use a patch bump; manual dispatch can select patch, minor, or major - Builds and packs before publishing the exact package to NuGet with `--skip-duplicate` - Only after NuGet publication succeeds, atomically pushes the version commit and tag @@ -66,7 +66,7 @@ dotnet tool install -g solrevdev.ytx --add-source ./nupkg ## Key Dependencies - `YoutubeExplode` 6.6.2 - Core YouTube data extraction -- .NET 8.0/9.0/10.0 target frameworks with nullable reference types enabled +- .NET 10.0/11.0 target frameworks with nullable reference types enabled - System.Text.Json for serialization - System.Text.RegularExpressions for caption text normalization diff --git a/CHANGELOG.md b/CHANGELOG.md index c00acc9..46e5a20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ All notable changes to ytx are listed here. The format follows ## [Unreleased] +### Breaking + +- ytx now targets .NET 10 and 11 only. The net8.0 and net9.0 builds are gone because .NET 8 and 9 left support on 10 November 2026. A machine with only .NET 8 or 9 can no longer install new releases. It needs the .NET 10 or 11 runtime. Older ytx versions stay on NuGet for those machines. + +### Changed + +- Tests and CI run on .NET 10 and 11. + ### Fixed - The tool rolls forward to a newer .NET major, so it keeps running when only a later runtime, such as .NET 11, is installed. diff --git a/README.md b/README.md index dc9a8fb..e83cd82 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ A .NET global tool that extracts YouTube video metadata and transcripts as JSON. - 🌍 **Captions-aware** — Prefers human-written captions in your language, reports which track it used - 📝 **Markdown transcript** — Human-readable format with timestamped links, split into sections by chapter - 📑 **Chapters** — Read from the video description, using YouTube's own rules -- 🚀 **Cross-platform** — Works on macOS, Windows, Linux (.NET 8/9/10) +- 🚀 **Cross-platform** — Works on macOS, Windows, Linux (.NET 10/11) - ⚡ **Fast & lightweight** — No dependencies beyond .NET and YoutubeExplode ## Quick Start @@ -172,7 +172,7 @@ dotnet restore src/Ytx # Build dotnet build src/Ytx -c Release -# Run the unit tests (on .NET 8, 9 and 10; add -f net8.0 to use one runtime) +# Run the unit tests (on .NET 10 and 11; add -f net10.0 to use one runtime) dotnet test tests/Ytx.Tests -c Release # Test locally (choose an installed target framework) @@ -248,7 +248,7 @@ The GitHub Actions workflow uses NuGet Trusted Publishing and does not need a st ## Dependencies - [YoutubeExplode](https://github.com/Tyrrrz/YoutubeExplode) — YouTube video data extraction -- .NET 8.0+ SDK (for development) +- .NET 10 and 11 SDKs (for development; tests run on both) ## License diff --git a/src/Ytx/Ytx.csproj b/src/Ytx/Ytx.csproj index 2684dd3..7c53eda 100644 --- a/src/Ytx/Ytx.csproj +++ b/src/Ytx/Ytx.csproj @@ -1,7 +1,7 @@ Exe - net8.0;net9.0;net10.0 + net10.0;net11.0 Major enable diff --git a/tests/Ytx.Tests/Ytx.Tests.csproj b/tests/Ytx.Tests/Ytx.Tests.csproj index d6ffa8f..696e48c 100644 --- a/tests/Ytx.Tests/Ytx.Tests.csproj +++ b/tests/Ytx.Tests/Ytx.Tests.csproj @@ -1,6 +1,6 @@ - net8.0;net9.0;net10.0 + net10.0;net11.0 enable enable false From e074add853473c915bba94ee80fcef1a535775f2 Mon Sep 17 00:00:00 2001 From: John Smith Date: Thu, 12 Nov 2026 11:24:04 +0000 Subject: [PATCH 2/3] build: build net11.0 only when SDK 11 is present .NET 10 is the LTS baseline. global.json asks for SDK 10.0.100 and rolls forward to the latest installed major, so a machine with 10 and 11 builds with 11. The tool and tests target net10.0 always and add net11.0 when NETCoreSdkVersion is 11 or later, so SDK 10 alone still builds. CI installs both SDKs, so the package ships both targets. publish.yml now also runs when global.json changes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014Mjr3bTLWVmY8sc2j5bcqH --- .github/copilot-instructions.md | 5 +++-- .github/workflows/publish.yml | 2 ++ AGENTS.md | 5 +++-- README.md | 13 +++++++++++-- global.json | 1 + src/Ytx/Ytx.csproj | 4 +++- tests/Ytx.Tests/Ytx.Tests.csproj | 4 +++- 7 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 global.json diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c06e087..66488d7 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -5,13 +5,14 @@ This repo is a small .NET global tool (PackageId: `solrevdev.ytx`) that extracts - Entry points: - `src/Ytx/Program.cs` — single-file console app; contains the main flow: input parsing (args or JSON stdin), YoutubeExplode calls, caption selection, transcript normalization, and JSON output. - - `src/Ytx/Ytx.csproj` — packaging details (PackAsTool, ToolCommandName=`ytx`), TargetFrameworks (`net10.0;net11.0`), and Version (auto-bumped by CI). + - `src/Ytx/Ytx.csproj` — packaging details (PackAsTool, ToolCommandName=`ytx`), TargetFrameworks (`net10.0`, plus `net11.0` when the SDK is 11 or later), and Version (auto-bumped by CI). - What to change and where: - Feature or bugfix touching runtime behavior: edit `Program.cs` and add small, well-scoped tests under `tests/` when applicable. - Packaging/version bumps: update `` in `src/Ytx/Ytx.csproj` if needed locally — CI auto-increments on `master` pushes. - Build / run / package (explicit commands example): + - SDK: `global.json` asks for 10.0.100 and rolls forward to the latest major. SDK 11 builds both targets; SDK 10 alone builds net10.0 only. CI installs both SDKs, so releases ship both. - Restore: `dotnet restore src/Ytx` - Build: `dotnet build src/Ytx -c Release` - Run (manual test): `dotnet run --project src/Ytx --framework net10.0 ""` @@ -35,7 +36,7 @@ This repo is a small .NET global tool (PackageId: `solrevdev.ytx`) that extracts - External dependencies & compatibility: - Uses `YoutubeExplode` (pinned to 6.5.4). Avoid upgrading without testing caption extraction end-to-end. - - Targets .NET 10 and 11; keep `Nullable` and `ImplicitUsings` enabled to match project style. + - Targets .NET 10 (LTS baseline) and 11; keep `Nullable` and `ImplicitUsings` enabled to match project style. - Small fixes that are preferred: - Add unit tests for any non-trivial parsing/formatting changes. diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fc02273..03f02d1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,11 +17,13 @@ on: paths: - 'src/Ytx/**' - 'tests/Ytx.Tests/**' + - 'global.json' - '.github/workflows/publish.yml' push: branches: [master] paths: - 'src/Ytx/**' + - 'global.json' - '.github/workflows/publish.yml' permissions: diff --git a/AGENTS.md b/AGENTS.md index 6e60ace..13bb357 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,8 @@ This is `ytx`, a .NET Global Tool that extracts YouTube video metadata and trans ## Development Commands ```bash -# Build and test locally +# Build and test locally. global.json rolls forward to the latest SDK major. +# SDK 11 builds net10.0 and net11.0; SDK 10 alone builds net10.0 only. dotnet restore src/Ytx dotnet build src/Ytx -c Release dotnet run --project src/Ytx --framework net10.0 "YOUTUBE_URL" @@ -66,7 +67,7 @@ dotnet tool install -g solrevdev.ytx --add-source ./nupkg ## Key Dependencies - `YoutubeExplode` 6.6.2 - Core YouTube data extraction -- .NET 10.0/11.0 target frameworks with nullable reference types enabled +- .NET 10.0/11.0 target frameworks with nullable reference types enabled. net10.0 (LTS) is the baseline; net11.0 is added only when the SDK is 11 or later (a `NETCoreSdkVersion` condition in both csproj files). `global.json` pins SDK 10.0.100 with `rollForward: latestMajor`. CI installs SDKs 10 and 11 so the package ships both targets - System.Text.Json for serialization - System.Text.RegularExpressions for caption text normalization diff --git a/README.md b/README.md index e83cd82..d521aab 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,14 @@ When `captionStatus` is not `ok`, the caption fields are `null` and both transcr ## Development +### SDK policy + +.NET 10 is the LTS and the baseline. `global.json` asks for SDK 10.0.100 and rolls forward to the latest installed major, so a machine with SDKs 10 and 11 builds with 11. + +- With SDK 11 (alone or with 10), the project builds and tests `net10.0` and `net11.0`. +- With SDK 10 only, it builds and tests `net10.0` only. That is fine for local work, but release packages must come from SDK 11. +- CI installs SDKs 10 and 11, so the published package holds both targets. + ### Local Build & Test ```bash @@ -172,7 +180,8 @@ dotnet restore src/Ytx # Build dotnet build src/Ytx -c Release -# Run the unit tests (on .NET 10 and 11; add -f net10.0 to use one runtime) +# Run the unit tests (net10.0 and net11.0 with SDK 11, net10.0 only with SDK 10; +# add -f net10.0 to use one runtime) dotnet test tests/Ytx.Tests -c Release # Test locally (choose an installed target framework) @@ -248,7 +257,7 @@ The GitHub Actions workflow uses NuGet Trusted Publishing and does not need a st ## Dependencies - [YoutubeExplode](https://github.com/Tyrrrz/YoutubeExplode) — YouTube video data extraction -- .NET 10 and 11 SDKs (for development; tests run on both) +- .NET 10 SDK or later for development. SDK 11 builds both targets; see [SDK policy](#sdk-policy). ## License diff --git a/global.json b/global.json new file mode 100644 index 0000000..a49afd6 --- /dev/null +++ b/global.json @@ -0,0 +1 @@ +{ "sdk": { "version": "10.0.100", "rollForward": "latestMajor", "allowPrerelease": false } } diff --git a/src/Ytx/Ytx.csproj b/src/Ytx/Ytx.csproj index 7c53eda..56004f5 100644 --- a/src/Ytx/Ytx.csproj +++ b/src/Ytx/Ytx.csproj @@ -1,7 +1,9 @@ Exe - net10.0;net11.0 + + net10.0 + net10.0;net11.0 Major enable diff --git a/tests/Ytx.Tests/Ytx.Tests.csproj b/tests/Ytx.Tests/Ytx.Tests.csproj index 696e48c..bfd3d55 100644 --- a/tests/Ytx.Tests/Ytx.Tests.csproj +++ b/tests/Ytx.Tests/Ytx.Tests.csproj @@ -1,6 +1,8 @@ - net10.0;net11.0 + + net10.0 + net10.0;net11.0 enable enable false From bbe5fddb7b8c85a1e68538167f420a96fac130f1 Mon Sep 17 00:00:00 2001 From: John Smith Date: Thu, 12 Nov 2026 11:33:11 +0000 Subject: [PATCH 3/3] ci: fail the publish if the package lacks a target The package targets depend on the SDK that built it. A "Check package targets" step after each Pack fails the run unless the nupkg holds tools/net10.0 and tools/net11.0. It matches bbx's step and also insists on exactly one package, since unzip -l reads a second path as a member name. global.json leaves the path filters again, so a build-setting change alone does not publish. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014Mjr3bTLWVmY8sc2j5bcqH --- .github/workflows/publish.yml | 38 +++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 03f02d1..6e58c83 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,13 +17,11 @@ on: paths: - 'src/Ytx/**' - 'tests/Ytx.Tests/**' - - 'global.json' - '.github/workflows/publish.yml' push: branches: [master] paths: - 'src/Ytx/**' - - 'global.json' - '.github/workflows/publish.yml' permissions: @@ -69,6 +67,24 @@ jobs: - name: Pack run: dotnet pack "$PROJECT_DIR" --configuration Release --no-build + - name: Check package targets + shell: bash + run: | + set -euo pipefail + # The targets depend on the SDK that built them; fail rather than + # ship a package missing one. + PACKAGES=("$NUPKG_DIR"/solrevdev.ytx.*.nupkg) + # unzip -l reads a second path as a member name, so insist on one package. + if [[ ${#PACKAGES[@]} -ne 1 || ! -f "${PACKAGES[0]}" ]]; then + echo "Expected one package in $NUPKG_DIR, found: ${PACKAGES[*]}" >&2 + exit 1 + fi + LISTING=$(unzip -l "${PACKAGES[0]}") + for tfm in net10.0 net11.0; do + grep -q "tools/$tfm/any/Ytx.dll" <<<"$LISTING" \ + || { echo "Package is missing tools/$tfm" >&2; exit 1; } + done + publish: if: github.event_name != 'pull_request' permissions: @@ -166,6 +182,24 @@ jobs: - name: Pack run: dotnet pack "$PROJECT_DIR" --configuration Release --no-build + - name: Check package targets + shell: bash + run: | + set -euo pipefail + # The targets depend on the SDK that built them; fail rather than + # ship a package missing one. + PACKAGES=("$NUPKG_DIR"/solrevdev.ytx.*.nupkg) + # unzip -l reads a second path as a member name, so insist on one package. + if [[ ${#PACKAGES[@]} -ne 1 || ! -f "${PACKAGES[0]}" ]]; then + echo "Expected one package in $NUPKG_DIR, found: ${PACKAGES[*]}" >&2 + exit 1 + fi + LISTING=$(unzip -l "${PACKAGES[0]}") + for tfm in net10.0 net11.0; do + grep -q "tools/$tfm/any/Ytx.dll" <<<"$LISTING" \ + || { echo "Package is missing tools/$tfm" >&2; exit 1; } + done + - name: Verify package id: package shell: bash