diff --git a/CHANGELOG.md b/CHANGELOG.md index e58f2fd..9d5012c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,4 +93,6 @@ ## 1.0.0 - 2018-12-30 -First release +### Added + +- First release diff --git a/README.md b/README.md index ea7b6ba..86bcaf9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Nanonote is a minimalist note taking application. ![Screenshot](screenshot.png) -It automatically saves anything you type. Being minimalist means it has no synchronisation, does not support multiple documents, images or any advanced formatting (the only formatting is highlighting URLs). +It automatically saves anything you type. Being minimalist means it has no synchronisation, does not support multiple documents, images or any advanced formatting (the only formatting is highlighting URLs and Markdown-like headings). It is developed and tested on Linux but also works on macOS and Windows as well. diff --git a/changelog.py b/changelog.py new file mode 100644 index 0000000..e60e0ac --- /dev/null +++ b/changelog.py @@ -0,0 +1,72 @@ +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Iterable + + +@dataclass +class Release: + version: str + date: str + # type => [changes] + changes: dict[str, list[str]] = field(default_factory=dict) + + +@dataclass +class Changelog: + # version => release + releases: dict[str, Release] = field(default_factory=dict) + + @staticmethod + def from_path(changelog_path: Path) -> "Changelog": + with changelog_path.open() as f: + parser = Parser(f) + return parser.parse() + + +def _get_dict_last_added_item(dct: dict[Any, Any]) -> Any: + return list(dct.values())[-1] + + +class Parser: + def __init__(self, line_it: Iterable[str]): + self.changelog = Changelog() + self.line_it = line_it + + def parse(self) -> Changelog: + self._parser = self._parse_prolog + for line in self.line_it: + line = line.strip() + if line: + self._parser(line) + + return self.changelog + + def _parse_prolog(self, line: str) -> None: + if line.startswith("## "): + self._parse_release_title(line) + self._parser = self._parse_release_content + + def _parse_release_title(self, line: str) -> None: + version, date = line[3:].split(" - ", maxsplit=1) + release = Release(version=version, date=date) + self.changelog.releases[version] = release + + def _parse_release_content(self, line: str) -> None: + if line.startswith("## "): + self._parse_release_title(line) + return + + release = _get_dict_last_added_item(self.changelog.releases) + + if line.startswith("### "): + change_type = line[4:] + release.changes[change_type] = [] + else: + assert line.startswith("- "), line + current_changes = _get_dict_last_added_item(release.changes) + current_changes.append(line[2:]) + + +if __name__ == "__main__": + changelog = Changelog.from_path(Path("CHANGELOG.md")) + print(changelog) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e2b0708..ff7efe0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -100,6 +100,10 @@ if (UNIX AND NOT APPLE) DESTINATION share/applications RENAME ${INVERSE_ORGANIZATION_NAME}.${APP_NAME}.desktop ) + install(FILES linux/${APP_NAME}.metainfo.xml + DESTINATION share/metainfo + RENAME ${INVERSE_ORGANIZATION_NAME}.${APP_NAME}.metainfo.xml + ) endif() if (WIN32) diff --git a/src/linux/nanonote.metainfo.xml b/src/linux/nanonote.metainfo.xml new file mode 100644 index 0000000..729a15f --- /dev/null +++ b/src/linux/nanonote.metainfo.xml @@ -0,0 +1,180 @@ + + + + com.agateau.Nanonote + CC0-1.0 + Nanonote + + A minimalist note taking application. + + +

+ Nanonote is a minimalist note taking application. +

+

+ It automatically saves anything you type. Being minimalist means it has no synchronisation, does not support multiple documents, images or any advanced formatting (the only formatting is highlighting URLs and Markdown-like headings). + Pixel Wheels is a retro top-down race game for Linux, macOS, Windows and Android. +

+
+ + Utility + TextEditor + + https://agateau.com/projects/nanonote + https://github.com/agateau/nanonote/issues + https://agateau.com/support + com.agateau.nanonote.desktop + + + +

+ Added +

+
    +
  • Add support for Markdown-style tasks in lists (Daniel Laidig)
  • +
  • Add tips page (Aurelien Gateau)
  • +
+

+ Changed +

+
    +
  • Use Ctrl+G to open links and Ctrl+Enter for tasks (Daniel Laidig)
  • +
+

+ Fixed +

+
    +
  • Make sure standard actions like Copy or Paste are translated (Aurelien Gateau)
  • +
  • Show keyboard shortcuts in context menus on macOS (Daniel Laidig)
  • +
  • Do not change cursor to pointing-hand when not over a link (Aurelien Gateau)
  • +
+

+ Internals +

+
    +
  • CI: Bump Ubuntu to 20.04 and macOS to 11 (Aurelien Gateau)
  • +
  • CI: Install clang-format from muttleyxd/clang-tools-static-binaries (Aurelien Gateau)
  • +
  • Bump Qt to 5.15.2 on macOS and Windows (Aurelien Gateau)
  • +
  • Update singleaplication to 3.3.4 (Aurelien Gateau)
  • +
  • Update Catch2 to 3.3.0 (Aurelien Gateau)
  • +
+
+
+ + +

+ Changed +

+
    +
  • Update Spanish translation (Victorhck)
  • +
+

+ Fixed +

+
    +
  • Properly encode URL of the note path (Aurelien Gateau)
  • +
  • Fix untranslated text in About tab on Linux (Aurelien Gateau)
  • +
+
+
+ + +

+ Added +

+
    +
  • You can now search inside your notes with the new search bar (Pavol Oresky)
  • +
  • You can now move selected lines up and down with Alt+Shift+Up and Down (Aurelien Gateau)
  • +
  • macOS dmg (Aurelien Gateau)
  • +
  • Windows installer (Aurelien Gateau)
  • +
+

+ Changed +

+
    +
  • Reorganized context menu: added "Edit" and "View" submenus (Aurelien Gateau)
  • +
+
+
+ + +

+ Added +

+
    +
  • New German translation by Vinzenz Vietzke
  • +
  • Allow changing the font size using Ctrl + mouse wheel (Daniel Laidig)
  • +
  • Use the link color of the color theme instead of an hardcoded blue (Daniel Laidig)
  • +
  • Added a way to reset the font size to the default value (Daniel Laidig)
  • +
+

+ Fixed +

+
    +
  • Added explanation of how to open URLs to the welcome text (Robert Barat)
  • +
  • Allow '@' in URLs (Aurelien Gateau)
  • +
  • Use QSaveFile for safer saving (Aurelien Gateau)
  • +
+
+
+ + +

+ Added +

+
    +
  • Pressing tab now indents the whole line when the cursor is at the beginning of a list item (Daniel Laidig).
  • +
  • Pressing Enter on an empty list item now unindents, then removes the bullet (Aurelien Gateau).
  • +
  • Added French and Spanish translations (Aurelien Gateau, Victorhck).
  • +
+

+ Fixed +

+
    +
  • Improved url detection: '+', '%' and '~' are now allowed in the middle of urls (Aurelien Gateau).
  • +
  • Fixed wrong indentation behavior in upward selections (Aurelien Gateau).
  • +
+
+
+ + +

+ Added +

+
    +
  • Added unit-tests.
  • +
  • Added Travis integration.
  • +
  • Added rpm and deb packages generated using CPack.
  • +
+

+ Fixed +

+
    +
  • Fixed indentation and make it respect indentation columns.
  • +
  • Made it possible to indent/unindent selected lines with Tab/Shift+Tab.
  • +
  • Update welcome text to reflect current shortcuts.
  • +
+
+
+ + +

+ Added +

+
    +
  • First release
  • +
+
+
+
+ + nanonote + + GPL-3.0-or-later + + + https://github.com/agateau/nanonote/raw/1.3.91/screenshot.png + + + +
diff --git a/tasks.py b/tasks.py index 5bd8312..bc8a5ad 100644 --- a/tasks.py +++ b/tasks.py @@ -1,7 +1,9 @@ import os import re import shutil +import subprocess import sys +import xml.etree.ElementTree as ET from pathlib import Path from tempfile import NamedTemporaryFile @@ -9,9 +11,14 @@ from invoke import task, run +from changelog import Changelog, Release + ARTIFACTS_DIR = Path("artifacts") +CHANGELOG_MD = Path("CHANGELOG.md") +APPSTREAM_XML = Path("src/linux/nanonote.metainfo.xml") + MAIN_BRANCH = "master" @@ -64,6 +71,42 @@ def update_version(c): path.write_text(text) +@task +def update_appstream_releases(c): + """Regenerate the element of our appstream file from + CHANGELOG.md""" + changelog = Changelog.from_path(CHANGELOG_MD) + + releases_et = ET.Element("releases") + for release in changelog.releases.values(): + release_et = ET.SubElement(releases_et, "release", + { + "version": release.version, + "date": release.date + }) + description_et = ET.SubElement(release_et, "description") + for change_type, changes in release.changes.items(): + p_et = ET.SubElement(description_et, "p") + em_et = ET.SubElement(p_et, "em") + em_et.text = change_type + ul_et = ET.SubElement(description_et, "ul") + for change in changes: + li_et = ET.SubElement(ul_et, "li") + li_et.text = change + content = ET.tostring(releases_et, encoding="unicode") + + # Replace the element by hand to avoid loosing comments, if any + appstream_content = APPSTREAM_XML.read_text() + appstream_content, count = re.subn(".*", + content, + appstream_content, flags=re.DOTALL) + assert count == 1 + subprocess.run(["xmllint", "--format", "--output", APPSTREAM_XML, "-"], + check=True, + text=True, + input=appstream_content) + + @task def create_release_branch(c): version = get_version() @@ -98,6 +141,8 @@ def create_release_branch2(c): if not is_ok("Looks good?"): sys.exit(1) + update_appstream_releases(c) + @task def update_ts(c): @@ -145,23 +190,28 @@ def download_artifacts(c): erun(f"gh run download --dir {ARTIFACTS_DIR}", pty=True) -def prepare_release_notes(version_md: Path) -> str: +def prepare_release_notes(release: Release) -> str: """ - Take the content of $VERSION.md and return it ready to use as GitHub release notes: - - Remove header - - Turn all h3 into h2 + Take a Release instance and produce markdown suitable for GitHub release + notes """ - content = re.sub("^## .*", "", version_md.read_text()) - content = re.sub("^### ", "## ", content, flags=re.MULTILINE) - return content.strip() + "\n" + lines = [] + for change_type, changes in release.changes.items(): + lines.append(f"## {change_type}") + for change in changes: + lines.append(f"- {change}") + return "\n\n".join(lines) + "\n" @task(help={"pre": "This is a prerelease"}) def publish(c, pre=False): version = get_version() + changelog = Changelog.from_path(CHANGELOG_MD) + release = changelog.releases[version] + content = prepare_release_notes(release) + files_str = " ".join(str(x) for x in get_artifact_list()) with NamedTemporaryFile() as tmp_file: - content = prepare_release_notes(Path(".changes") / f"{version}.md") tmp_file.write(content.encode("utf-8")) tmp_file.flush() cmd = f"gh release create {version} -F{tmp_file.name} {files_str}"