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.

97 lines
3.3 KiB

  1. # -*- coding: utf8 -*-
  2. # Copyright (c) 2019 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 nr.databind.core import Field, ObjectMapper, Struct
  22. from shore.util.version import Version
  23. from typing import Optional
  24. import os
  25. import yaml
  26. class ChangelogEntry(Struct):
  27. type = Field(str)
  28. component = Field(str)
  29. flags = Field([str], default=list)
  30. description = Field(str)
  31. class Changelog:
  32. def __init__(self, filename: str, version: Optional[Version], mapper: ObjectMapper) -> None:
  33. self.filename = filename
  34. self.version = version
  35. self.mapper = mapper
  36. self.entries = []
  37. def exists(self) -> bool:
  38. return os.path.isfile(self.filename)
  39. def load(self) -> None:
  40. with open(self.filename) as fp:
  41. data = yaml.safe_load(fp)
  42. self.entries = self.mapper.deserialize(data, [ChangelogEntry], filename=self.filename)
  43. def save(self, create_directory: bool = False) -> None:
  44. if create_directory:
  45. os.makedirs(os.path.dirname(self.filename), exist_ok=True)
  46. data = self.mapper.serialize(self.entries, [ChangelogEntry])
  47. with open(self.filename, 'w') as fp:
  48. yaml.safe_dump(data, fp)
  49. def add_entry(self, entry: ChangelogEntry) -> None:
  50. self.entries.append(entry)
  51. class ChangelogManager:
  52. TYPES = frozenset(['fix', 'improvement', 'docs', 'change', 'refactor', 'feature'])
  53. def __init__(self, directory: str, mapper: ObjectMapper) -> None:
  54. self.directory = directory
  55. self.mapper = mapper
  56. self._cache = {}
  57. def _get(self, name: str, version: Optional[str]) -> Changelog:
  58. key = (name, str(version))
  59. if key in self._cache:
  60. return self._cache[key]
  61. changelog = Changelog(os.path.join(self.directory, name), version, self.mapper)
  62. if os.path.isfile(changelog.filename):
  63. changelog.load()
  64. self._cache[key] = changelog
  65. return changelog
  66. @property
  67. def unreleased(self) -> Changelog:
  68. return self._get('_unreleased.yml', None)
  69. def version(self, version: Version) -> Changelog:
  70. return self._get(str(version) + '.yml', version)
  71. def release(self, version: Version) -> Changelog:
  72. """
  73. Renames the unreleased changelog to the file name for the specified *version*.
  74. """
  75. unreleased = self.unreleased
  76. os.rename(unreleased.filename, self.version(version).filename)
  77. self._cache.clear()
  78. return self.version(version)