diff --git a/.env.example b/.env.example index 1aaa923..7a5efe9 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ # Application APP_NAME=WebDrop Bridge -APP_VERSION=0.9.0 +APP_VERSION=0.9.1 # Web App WEBAPP_URL=file:///./webapp/index.html diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e29cab5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [0.9.1] - 2026-04-15 + +### Changed +- Improved release publishing so release descriptions can be generated from the changelog. +- Updated the release workflow to use a clearer, user-facing summary for update information. +- Added "in App" Branding Management instead of using separated Brand Builds. + +### Fixed +- Removed the generic placeholder release description from published releases. +- Added a reliable fallback message when no detailed notes are available. +- instead of using "Profiles" and "Configurations" use setups for more clarity. diff --git a/build/scripts/create_release.ps1 b/build/scripts/create_release.ps1 index 187c9aa..1cb5940 100644 --- a/build/scripts/create_release.ps1 +++ b/build/scripts/create_release.ps1 @@ -34,6 +34,18 @@ function Get-CurrentVersion { return (& $pythonExe -c "from pathlib import Path; import sys; sys.path.insert(0, str(Path(r'$projectRoot/build/scripts').resolve())); from version_utils import get_current_version; print(get_current_version())").Trim() } +function Get-ReleaseNotes { + param([string]$Version) + + $notes = & $pythonExe -c "from pathlib import Path; import sys; sys.path.insert(0, str(Path(r'$projectRoot/build/scripts').resolve())); from version_utils import get_release_notes; print(get_release_notes('$Version'))" + + if ($LASTEXITCODE -ne 0) { + return "## WebDrop Bridge v$Version`n`nThis release package contains the latest improvements, fixes, and installer updates for this version." + } + + return ($notes | Out-String).Trim() +} + function Get-LocalReleaseData { $arguments = @($brandHelper, "local-release-data", "--platform", "windows", "--version", $Version) if ($Brands) { @@ -127,10 +139,11 @@ $headers = @{ $releaseLookupUrl = "$ForgejoUrl/api/v1/repos/$Repo/releases/tags/v$Version" $releaseUrl = "$ForgejoUrl/api/v1/repos/$Repo/releases" +$releaseBody = Get-ReleaseNotes -Version $Version $releaseData = @{ tag_name = "v$Version" name = "WebDropBridge v$Version" - body = "Shared branded release for WebDrop Bridge v$Version" + body = $releaseBody draft = $false prerelease = $false } | ConvertTo-Json diff --git a/build/scripts/create_release.sh b/build/scripts/create_release.sh index 2ce39f8..218de20 100644 --- a/build/scripts/create_release.sh +++ b/build/scripts/create_release.sh @@ -40,6 +40,10 @@ if [ -z "$VERSION" ]; then VERSION="$(python3 -c "from pathlib import Path; import sys; sys.path.insert(0, str(Path(r'$PROJECT_ROOT/build/scripts').resolve())); from version_utils import get_current_version; print(get_current_version())")" fi +get_release_notes() { + python3 -c "from pathlib import Path; import sys; sys.path.insert(0, str(Path(r'$PROJECT_ROOT/build/scripts').resolve())); from version_utils import get_release_notes; print(get_release_notes('$VERSION'))" +} + LOCAL_ARGS=("$BRAND_HELPER" "local-release-data" "--platform" "macos" "--version" "$VERSION") if [ ${#BRANDS[@]} -gt 0 ]; then LOCAL_ARGS+=("--brands" "${BRANDS[@]}") @@ -186,15 +190,19 @@ else fi if [ -z "$RELEASE_ID" ]; then - RELEASE_DATA=$(cat < str: init_file = project_root / "src" / "webdrop_bridge" / "__init__.py" if not init_file.exists(): - raise FileNotFoundError( - f"Cannot find __init__.py at {init_file}" - ) + raise FileNotFoundError(f"Cannot find __init__.py at {init_file}") content = init_file.read_text(encoding="utf-8") match = re.search(r'__version__\s*=\s*["\']([^"\']+)["\']', content) if not match: raise ValueError( - f"Could not find __version__ in {init_file}. " - "Expected: __version__ = \"X.Y.Z\"" + f"Could not find __version__ in {init_file}. " 'Expected: __version__ = "X.Y.Z"' ) return match.group(1) + + +def extract_release_notes(changelog_content: str, version: str) -> str | None: + """Extract the notes for a specific version from changelog content. + + Args: + changelog_content: Full text of CHANGELOG.md + version: Version to extract, e.g. "0.9.1" + + Returns: + The section content for that version, or None if not found. + """ + version_header = re.compile( + rf"^##\s*\[?{re.escape(version)}\]?(?:\s*-\s*.+)?\s*$", + re.MULTILINE, + ) + match = version_header.search(changelog_content) + if not match: + return None + + section_start = match.end() + next_header = re.search(r"^##\s+", changelog_content[section_start:], re.MULTILINE) + section_end = section_start + next_header.start() if next_header else len(changelog_content) + section = changelog_content[section_start:section_end].strip() + return section or None + + +def get_release_notes(version: str, project_root: Path | None = None) -> str: + """Build a readable release body for publishing. + + Prefers the matching version section from CHANGELOG.md. If no changelog + entry exists yet, falls back to a generic but user-facing description. + + Args: + version: Release version string. + project_root: Optional project root override for testing. + + Returns: + Release notes text suitable for Forgejo/GitHub release bodies. + """ + root = project_root or get_project_root() + changelog_file = root / "CHANGELOG.md" + + if changelog_file.exists(): + content = changelog_file.read_text(encoding="utf-8") + notes = extract_release_notes(content, version) + if notes: + return f"## WebDrop Bridge v{version}\n\n{notes}" + + return ( + f"## WebDrop Bridge v{version}\n\n" + "This release package contains the latest improvements, fixes, " + "and installer updates for this version." + ) diff --git a/src/webdrop_bridge/__init__.py b/src/webdrop_bridge/__init__.py index 0178dab..afd4e03 100644 --- a/src/webdrop_bridge/__init__.py +++ b/src/webdrop_bridge/__init__.py @@ -1,6 +1,6 @@ """WebDrop Bridge - Qt-based desktop application for intelligent drag-and-drop file handling.""" -__version__ = "0.9.0" +__version__ = "0.9.1" __author__ = "WebDrop Team" __license__ = "MIT" diff --git a/tests/unit/test_version_utils.py b/tests/unit/test_version_utils.py new file mode 100644 index 0000000..cedf2fc --- /dev/null +++ b/tests/unit/test_version_utils.py @@ -0,0 +1,46 @@ +"""Tests for build script version utilities.""" + +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "build" / "scripts")) + +from version_utils import get_release_notes + + +class TestReleaseNotes: + """Test release note extraction for published releases.""" + + def test_get_release_notes_from_changelog(self, tmp_path): + """Extract only the selected version section from the changelog.""" + changelog = tmp_path / "CHANGELOG.md" + changelog.write_text( + """## [0.9.1] - 2026-04-15 + +### Added +- Better update text +- New installer checks + +### Fixed +- Upload retries + +## [0.9.0] - 2026-04-01 + +### Added +- Older changes +""", + encoding="utf-8", + ) + + notes = get_release_notes("0.9.1", project_root=tmp_path) + + assert "Better update text" in notes + assert "New installer checks" in notes + assert "Older changes" not in notes + + def test_get_release_notes_uses_fallback_when_missing(self, tmp_path): + """Return a readable fallback when no changelog entry exists.""" + notes = get_release_notes("0.9.1", project_root=tmp_path) + + assert "WebDrop Bridge v0.9.1" in notes + assert "release package" in notes.lower()