DEPRECATED -- Rewritten and moved to https://github.com/NiklasRosenstein/shut/. 🌊 Shore is a distribution and release management tool for pure Python packages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
4.2 KiB

  1. # -*- coding: utf8 -*-
  2. # Copyright (c) 2020 Niklas Rosenstein
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a copy
  5. # of this software and associated documentation files (the "Software"), to
  6. # deal in the Software without restriction, including without limitation the
  7. # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. # sell copies of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be included in
  12. # all copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. # IN THE SOFTWARE.
  21. from shore.util.version import Version
  22. from . import v1, v2, v3
  23. from nr.databind.core import ObjectMapper, SkipDefaults
  24. from nr.databind.json import JsonModule
  25. from typing import Iterable, Optional
  26. import datetime
  27. import os
  28. import yaml
  29. mapper = ObjectMapper(JsonModule())
  30. supported_changelog_types = (
  31. v3.Changelog,
  32. v2.Changelog,
  33. v1.Changelog,
  34. )
  35. class Changelog:
  36. """
  37. Represents a changelog on disk.
  38. """
  39. def __init__(self, filename: str, version: Optional[Version]) -> None:
  40. self.filename = filename
  41. self.version = version
  42. self.data = v3.Changelog(changes=[])
  43. @property
  44. def entries(self):
  45. return self.data.changes
  46. def exists(self) -> bool:
  47. " Returns #True if the changelog file exists. "
  48. return os.path.isfile(self.filename)
  49. def load(self) -> None:
  50. " Loads the data from the file of this changelog. "
  51. with open(self.filename) as fp:
  52. raw_data = yaml.safe_load(fp)
  53. data = mapper.deserialize(raw_data, supported_changelog_types, filename=self.filename)
  54. if not isinstance(data, v3.Changelog):
  55. data = v3.Changelog.migrate(data)
  56. self.data = data
  57. def save(self, create_directory: bool = False) -> None:
  58. " Saves the changelog. It will always save the changelog in the newest supported format. "
  59. if create_directory:
  60. os.makedirs(os.path.dirname(self.filename), exist_ok=True)
  61. data = mapper.serialize(self.data, v3.Changelog)
  62. with open(self.filename, 'w') as fp:
  63. yaml.safe_dump(data, fp, sort_keys=False)
  64. def set_release_date(self, date: datetime.date) -> None:
  65. self.data.release_date = date
  66. def add_entry(self, entry) -> None:
  67. assert isinstance(entry, v3.Changelog.Entry), type(entry)
  68. self.data.changes.append(entry)
  69. class ChangelogManager:
  70. def __init__(self, directory: str) -> None:
  71. self.directory = directory
  72. self._cache = {}
  73. def _get(self, name: str, version: Optional[str]) -> Changelog:
  74. key = (name, str(version))
  75. if key in self._cache:
  76. return self._cache[key]
  77. changelog = Changelog(os.path.join(self.directory, name), version)
  78. if os.path.isfile(changelog.filename):
  79. changelog.load()
  80. self._cache[key] = changelog
  81. return changelog
  82. @property
  83. def unreleased(self) -> Changelog:
  84. return self._get('_unreleased.yml', None)
  85. def version(self, version: Version) -> Changelog:
  86. return self._get(str(version) + '.yml', version)
  87. def release(self, version: Version) -> Changelog:
  88. """
  89. Renames the unreleased changelog to the file name for the specified *version*.
  90. """
  91. unreleased = self.unreleased
  92. unreleased.data.release_date = datetime.date.today()
  93. unreleased.save()
  94. os.rename(unreleased.filename, self.version(version).filename)
  95. self._cache.clear()
  96. return self.version(version)
  97. def all(self) -> Iterable[Changelog]:
  98. """
  99. Yields all changelogs.
  100. """
  101. for name in os.listdir(self.directory):
  102. if not name.endswith('.yml'):
  103. continue
  104. if name == '_unreleased.yml':
  105. yield self.unreleased
  106. else:
  107. version = Version(name[:-4])
  108. yield self.version(version)