diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c8a8c31..5d00b68 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,7 +63,7 @@ jobs: "prerelease=$($v.Contains('-').ToString().ToLower())" >> $env:GITHUB_OUTPUT "publish=$($publish.ToString().ToLower())" >> $env:GITHUB_OUTPUT - - name: Prepare release notes (English, with a link to the Portuguese changelog) + - name: Prepare release notes (English, a link to the Portuguese changelog, a summary per translated changelog) if: steps.version.outputs.publish == 'true' shell: pwsh env: @@ -79,10 +79,29 @@ jobs: return $notes } # Both changelogs must have the section; the Portuguese one is linked, not pasted. - $null = Get-Notes "CHANGELOG.md" + $pt = Get-Notes "CHANGELOG.md" $en = Get-Notes "CHANGELOG.en-US.md" - # English only, no language headings: ReleaseNotes.cs shows the first item to every - # language. A "(#NN)" at the end of an item becomes a link to that PR on GitHub. + # One hidden "" line per translated changelog (CHANGELOG.md is + # pt-BR, any CHANGELOG..md another language): the update notice shows it instead + # of the English first item (ReleaseNotes.cs). They go at the very end of the body, so + # clients up to 1.6.x, which show the first line they find, keep showing the English one. + function Get-FirstItem([string]$notes) { + $item = $notes -split "\r?\n" | Where-Object { $_ -match '^\s*[-*]\s+\S' } | Select-Object -First 1 + if (-not $item) { return $null } + return ($item -replace '^\s*[-*]\s+', '' -replace '-->', '').Trim() + } + $summaries = @() + $ptItem = Get-FirstItem $pt + if ($ptItem) { $summaries += "" } + foreach ($file in Get-ChildItem -Name -Filter "CHANGELOG.*.md" | Where-Object { $_ -ne "CHANGELOG.en-US.md" } | Sort-Object) { + $code = $file -replace '^CHANGELOG\.|\.md$', '' + # Optional: a translated changelog without this version's section is skipped, not fatal. + $notes = try { Get-Notes $file } catch { Write-Host "$file sem a seção $($env:VERSION); sem resumo em $code."; $null } + $item = if ($notes) { Get-FirstItem $notes } + if ($item) { $summaries += "" } + } + # English, no language headings: ReleaseNotes.cs shows its first item to every + # language without a summary. A "(#NN)" at the end of an item becomes a link to that PR on GitHub. $tag = "v$($env:VERSION)" $releaseNotes = @( $en @@ -96,7 +115,9 @@ jobs: "---" "" "🇧🇷 Notas em português: [CHANGELOG.md](https://github.com/lippdev/consolemode/blob/$tag/CHANGELOG.md)" - ) -join "`n" + ) + if ($summaries.Count -gt 0) { $releaseNotes += @("") + $summaries } + $releaseNotes = $releaseNotes -join "`n" Set-Content -LiteralPath "release-notes.md" -Value $releaseNotes -Encoding utf8 - name: Run tests diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index ad1bfdf..876a6d8 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -3,6 +3,8 @@ English (US) release notes, mirroring CHANGELOG.md (Brazilian Portuguese). Before publishing a version, add a `## [VERSION]` section to **both** files: the workflow publishes the section matching the tag from each one and fails if either is missing. ## [Unreleased] +### What's new +- Update notice in Portuguese: the new version's highlight shows in the app's language when there is a changelog in it (today, Portuguese); other languages still see it in English. Applies to updates published after this version. (#158) ## [1.6.2] ### What's new diff --git a/CHANGELOG.md b/CHANGELOG.md index 71c4042..0b87ab8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Notas em português do Brasil; a versão em inglês (Estados Unidos) fica em `CHANGELOG.en-US.md`. Antes de publicar uma versão, adicione uma seção `## [VERSÃO]` com o changelog daquela versão **nos dois arquivos**. O workflow publica as duas seções correspondentes à tag na mesma release e falha se faltar alguma. ## [Unreleased] +### Novidades +- Aviso de atualização em português: o destaque da nova versão aparece no idioma do app quando há um changelog nele (hoje, português); os outros idiomas continuam vendo o destaque em inglês. Vale a partir das atualizações publicadas depois desta versão. (#158) ## [1.6.2] ### Novidades diff --git a/src/ConsoleMode/Services/ReleaseNotes.cs b/src/ConsoleMode/Services/ReleaseNotes.cs index 5608351..f66ccc7 100644 --- a/src/ConsoleMode/Services/ReleaseNotes.cs +++ b/src/ConsoleMode/Services/ReleaseNotes.cs @@ -1,15 +1,24 @@ +using System.Text.RegularExpressions; + namespace ConsoleMode.Services; /// /// Reads the GitHub release body written by .github/workflows/release.yml. /// Since 1.5.0 the body has one section per language, each opened by a fixed /// "## …" heading; older releases are pt-BR only, with no language headings. +/// Since 1.7.0 the body ends with one hidden summary per translated changelog +/// (), which wins over the English notes. /// public static class ReleaseNotes { public const string PortugueseHeading = "## Português (Brasil)"; public const string EnglishHeading = "## English (US)"; + /// "<!-- summary pt-BR: first item -->": an HTML comment, so GitHub doesn't show it. + public const string SummaryPrefix = "", StringComparison.Ordinal)) continue; + var line = Clean(trimmed[prefix.Length..^3]); + if (line.Length > 0) return line; + } + return null; + } + + /// Plain text for the notice: no list marker, bold or link syntax, at most 140 characters. + private static string Clean(string text) + { + var line = MarkdownLink.Replace(text.Trim().TrimStart('-', '*', ' ').Replace("**", ""), "$1").Trim(); + return line.Length > 140 ? line[..140] + "…" : line; + } + private static bool IsHeading(string line, string heading) => string.Equals(line.Trim().TrimStart(''), heading, StringComparison.Ordinal); } diff --git a/tests/ConsoleMode.Tests/ReleaseNotesTests.cs b/tests/ConsoleMode.Tests/ReleaseNotesTests.cs index 4030ab0..9d156d0 100644 --- a/tests/ConsoleMode.Tests/ReleaseNotesTests.cs +++ b/tests/ConsoleMode.Tests/ReleaseNotesTests.cs @@ -80,6 +80,51 @@ public void FirstLine_EnglishOnlyBodyIsSharedByEveryLanguage() Assert.Equal("Session menu over the game. (#46)", ReleaseNotes.FirstLine(englishOnly, LocalizationService.SpanishSpain)); } + private const string WithSummaries = """ + ### What's new + - **Minimize to the system tray:** a new option in Settings. (#150) + + --- + + 🇧🇷 Notas em português: [CHANGELOG.md](https://example.com/CHANGELOG.md) + + + """; + + [Fact] + public void FirstLine_HiddenSummaryWinsForItsLanguage() + { + Assert.Equal("Minimizar para a bandeja: traduzida por u/Skinner99. (#150)", + ReleaseNotes.FirstLine(WithSummaries, LocalizationService.PortugueseBrazil)); + Assert.Equal("Minimizar para a bandeja: traduzida por u/Skinner99. (#150)", + ReleaseNotes.FirstLine(WithSummaries.Replace("\n", "\r\n"), LocalizationService.PortugueseBrazil)); + } + + [Fact] + public void FirstLine_LanguagesWithoutASummaryReadTheEnglishNotes() + { + Assert.Equal("Minimize to the system tray: a new option in Settings. (#150)", + ReleaseNotes.FirstLine(WithSummaries, LocalizationService.EnglishUnitedStates)); + Assert.Equal("Minimize to the system tray: a new option in Settings. (#150)", + ReleaseNotes.FirstLine(WithSummaries, LocalizationService.GermanGermany)); + } + + [Fact] + public void FirstLine_EmptyOrUnclosedSummaryFallsBackToTheNotes() + { + Assert.Equal("Item.", ReleaseNotes.FirstLine("- Item.\n", LocalizationService.PortugueseBrazil)); + Assert.Equal("Item.", ReleaseNotes.FirstLine("- Item.\n