autopub 0.2.2__py3-none-any.whl → 1.0.0a1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- autopub/__init__.py +182 -0
- autopub/cli/__init__.py +121 -0
- autopub/cli/plugins.py +37 -0
- autopub/exceptions.py +47 -0
- autopub/plugins/__init__.py +40 -0
- autopub/plugins/bump_version.py +41 -0
- autopub/plugins/pdm.py +18 -0
- autopub/plugins/poetry.py +16 -0
- autopub/plugins/update_changelog.py +50 -0
- autopub/types.py +11 -0
- {autopub-0.2.2.dist-info → autopub-1.0.0a1.dist-info}/METADATA +15 -9
- autopub-1.0.0a1.dist-info/RECORD +15 -0
- {autopub-0.2.2.dist-info → autopub-1.0.0a1.dist-info}/WHEEL +1 -1
- autopub-1.0.0a1.dist-info/entry_points.txt +3 -0
- autopub/autopub.py +0 -83
- autopub/base.py +0 -159
- autopub/build_release.py +0 -15
- autopub/check_release.py +0 -15
- autopub/commit_release.py +0 -33
- autopub/create_github_release.py +0 -66
- autopub/deploy_release.py +0 -18
- autopub/github_contributor.py +0 -83
- autopub/prepare_release copy.py +0 -119
- autopub/prepare_release.py +0 -93
- autopub/publish_release.py +0 -22
- autopub-0.2.2.dist-info/RECORD +0 -17
- autopub-0.2.2.dist-info/entry_points.txt +0 -3
- {autopub-0.2.2.dist-info → autopub-1.0.0a1.dist-info}/LICENSE +0 -0
autopub/__init__.py
CHANGED
@@ -0,0 +1,182 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import hashlib
|
4
|
+
import json
|
5
|
+
from collections.abc import Iterable
|
6
|
+
from pathlib import Path
|
7
|
+
|
8
|
+
import frontmatter
|
9
|
+
|
10
|
+
from autopub.exceptions import (
|
11
|
+
ArtifactHashMismatch,
|
12
|
+
ArtifactNotFound,
|
13
|
+
AutopubException,
|
14
|
+
NoPackageManagerPluginFound,
|
15
|
+
ReleaseFileEmpty,
|
16
|
+
ReleaseFileNotFound,
|
17
|
+
ReleaseNotesEmpty,
|
18
|
+
ReleaseTypeInvalid,
|
19
|
+
ReleaseTypeMissing,
|
20
|
+
)
|
21
|
+
from autopub.plugins import AutopubPackageManagerPlugin, AutopubPlugin
|
22
|
+
from autopub.types import ReleaseInfo
|
23
|
+
|
24
|
+
|
25
|
+
class Autopub:
|
26
|
+
RELEASE_FILE_PATH = "RELEASE.md"
|
27
|
+
|
28
|
+
def __init__(self, plugins: Iterable[type[AutopubPlugin]] = ()) -> None:
|
29
|
+
self.plugins = [plugin_class() for plugin_class in plugins]
|
30
|
+
|
31
|
+
@property
|
32
|
+
def release_file(self) -> Path:
|
33
|
+
return Path.cwd() / self.RELEASE_FILE_PATH
|
34
|
+
|
35
|
+
@property
|
36
|
+
def release_notes(self) -> str:
|
37
|
+
return self.release_file.read_text()
|
38
|
+
|
39
|
+
@property
|
40
|
+
def release_file_hash(self) -> str:
|
41
|
+
return hashlib.sha256(self.release_notes.encode("utf-8")).hexdigest()
|
42
|
+
|
43
|
+
@property
|
44
|
+
def release_data_file(self) -> Path:
|
45
|
+
return Path(".autopub") / "release_data.json"
|
46
|
+
|
47
|
+
# TODO: typed dict
|
48
|
+
@property
|
49
|
+
def release_data(self) -> ReleaseInfo:
|
50
|
+
if not self.release_data_file.exists():
|
51
|
+
raise ArtifactNotFound()
|
52
|
+
|
53
|
+
release_data = json.loads(self.release_data_file.read_text())
|
54
|
+
|
55
|
+
if release_data["hash"] != self.release_file_hash:
|
56
|
+
raise ArtifactHashMismatch()
|
57
|
+
|
58
|
+
return ReleaseInfo(
|
59
|
+
release_type=release_data["release_type"],
|
60
|
+
release_notes=release_data["release_notes"],
|
61
|
+
additional_info=release_data["plugin_data"],
|
62
|
+
)
|
63
|
+
|
64
|
+
def check(self) -> ReleaseInfo:
|
65
|
+
release_file = Path(self.RELEASE_FILE_PATH)
|
66
|
+
|
67
|
+
if not release_file.exists():
|
68
|
+
raise ReleaseFileNotFound()
|
69
|
+
|
70
|
+
try:
|
71
|
+
release_info = self._validate_release_notes(self.release_notes)
|
72
|
+
except AutopubException as e:
|
73
|
+
for plugin in self.plugins:
|
74
|
+
plugin.on_release_notes_invalid(e)
|
75
|
+
raise
|
76
|
+
|
77
|
+
for plugin in self.plugins:
|
78
|
+
plugin.on_release_notes_valid(release_info)
|
79
|
+
|
80
|
+
self._write_artifact(release_info)
|
81
|
+
|
82
|
+
return release_info
|
83
|
+
|
84
|
+
def build(self) -> None:
|
85
|
+
if not any(
|
86
|
+
isinstance(plugin, AutopubPackageManagerPlugin) for plugin in self.plugins
|
87
|
+
):
|
88
|
+
raise NoPackageManagerPluginFound()
|
89
|
+
|
90
|
+
for plugin in self.plugins:
|
91
|
+
if isinstance(plugin, AutopubPackageManagerPlugin):
|
92
|
+
plugin.build()
|
93
|
+
|
94
|
+
def prepare(self) -> None:
|
95
|
+
for plugin in self.plugins:
|
96
|
+
plugin.prepare(self.release_data)
|
97
|
+
|
98
|
+
def publish(self, repository: str | None = None) -> None:
|
99
|
+
# TODO: shall we put this in a function, to make it
|
100
|
+
# clear that we are triggering the logic to check the release file?
|
101
|
+
self.release_data
|
102
|
+
|
103
|
+
for plugin in self.plugins:
|
104
|
+
if isinstance(plugin, AutopubPackageManagerPlugin):
|
105
|
+
plugin.publish(repository=repository)
|
106
|
+
|
107
|
+
def _write_artifact(self, release_info: ReleaseInfo) -> None:
|
108
|
+
data = {
|
109
|
+
"hash": self.release_file_hash,
|
110
|
+
"release_type": release_info.release_type,
|
111
|
+
"release_notes": release_info.release_notes,
|
112
|
+
"plugin_data": {
|
113
|
+
key: value
|
114
|
+
for plugin in self.plugins
|
115
|
+
for key, value in plugin.data.items()
|
116
|
+
},
|
117
|
+
}
|
118
|
+
|
119
|
+
self.release_data_file.parent.mkdir(exist_ok=True)
|
120
|
+
self.release_data_file.write_text(json.dumps(data))
|
121
|
+
|
122
|
+
def _deprecated_load(self, release_notes: str) -> ReleaseInfo:
|
123
|
+
# supports loading of old release notes format, which is
|
124
|
+
# deprecated and will be removed in a future release
|
125
|
+
# and looks like this:
|
126
|
+
# Release type: patch
|
127
|
+
# release notes here.
|
128
|
+
|
129
|
+
try:
|
130
|
+
release_info, release_notes = release_notes.split("\n", 1)
|
131
|
+
except ValueError as e:
|
132
|
+
raise ReleaseTypeMissing() from e
|
133
|
+
|
134
|
+
release_info = release_info.lower()
|
135
|
+
|
136
|
+
if not release_info.startswith("release type:"):
|
137
|
+
raise ReleaseTypeMissing()
|
138
|
+
|
139
|
+
release_type = release_info.split(":", 1)[1].strip().lower()
|
140
|
+
|
141
|
+
return ReleaseInfo(
|
142
|
+
release_type=release_type, release_notes=release_notes.strip()
|
143
|
+
)
|
144
|
+
|
145
|
+
def _load_from_frontmatter(self, release_notes: str) -> ReleaseInfo:
|
146
|
+
# supports loading of new release notes format, which looks like this:
|
147
|
+
# ---
|
148
|
+
# release type: patch
|
149
|
+
# ---
|
150
|
+
# release notes here.
|
151
|
+
|
152
|
+
post = frontmatter.loads(release_notes)
|
153
|
+
|
154
|
+
data: dict[str, str] = post.to_dict()
|
155
|
+
|
156
|
+
release_type = data.pop("release type").lower()
|
157
|
+
|
158
|
+
if release_type not in ("major", "minor", "patch"):
|
159
|
+
raise ReleaseTypeInvalid(release_type)
|
160
|
+
|
161
|
+
if post.content.strip() == "":
|
162
|
+
raise ReleaseNotesEmpty()
|
163
|
+
|
164
|
+
return ReleaseInfo(
|
165
|
+
release_type=release_type,
|
166
|
+
release_notes=post.content,
|
167
|
+
additional_info=data,
|
168
|
+
)
|
169
|
+
|
170
|
+
def _validate_release_notes(self, release_notes: str) -> ReleaseInfo:
|
171
|
+
if not release_notes:
|
172
|
+
raise ReleaseFileEmpty()
|
173
|
+
|
174
|
+
try:
|
175
|
+
release_info = self._load_from_frontmatter(release_notes)
|
176
|
+
except KeyError:
|
177
|
+
release_info = self._deprecated_load(release_notes)
|
178
|
+
|
179
|
+
for plugin in self.plugins:
|
180
|
+
plugin.validate_release_notes(release_info)
|
181
|
+
|
182
|
+
return release_info
|
autopub/cli/__init__.py
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
from typing import Optional, TypedDict
|
2
|
+
|
3
|
+
import rich
|
4
|
+
import typer
|
5
|
+
from rich.console import Group
|
6
|
+
from rich.markdown import Markdown
|
7
|
+
from rich.padding import Padding
|
8
|
+
from rich.panel import Panel
|
9
|
+
from typing_extensions import Annotated
|
10
|
+
|
11
|
+
from autopub import Autopub
|
12
|
+
from autopub.cli.plugins import find_plugins
|
13
|
+
from autopub.exceptions import AutopubException
|
14
|
+
|
15
|
+
app = typer.Typer()
|
16
|
+
|
17
|
+
|
18
|
+
class State(TypedDict):
|
19
|
+
plugins: list[str]
|
20
|
+
|
21
|
+
|
22
|
+
state: State = {"plugins": []}
|
23
|
+
|
24
|
+
|
25
|
+
@app.command()
|
26
|
+
def check():
|
27
|
+
"""This commands checks if the current PR has a valid release file."""
|
28
|
+
|
29
|
+
autopub = Autopub(plugins=find_plugins(state["plugins"]))
|
30
|
+
|
31
|
+
try:
|
32
|
+
release_info = autopub.check()
|
33
|
+
except AutopubException as e:
|
34
|
+
rich.print(Panel.fit(f"[red]{e.message}"))
|
35
|
+
|
36
|
+
raise typer.Exit(1) from e
|
37
|
+
else:
|
38
|
+
rich.print(
|
39
|
+
Padding(
|
40
|
+
Group(
|
41
|
+
(
|
42
|
+
"[bold on bright_magenta] Release type: [/] "
|
43
|
+
f"[yellow italic underline]{release_info.release_type}[/]\n"
|
44
|
+
),
|
45
|
+
"[bold on bright_magenta] Release notes: [/]\n",
|
46
|
+
Markdown(release_info.release_notes),
|
47
|
+
"\n---\n\n[green bold]Release file is valid![/] 🚀",
|
48
|
+
),
|
49
|
+
(1, 1),
|
50
|
+
)
|
51
|
+
)
|
52
|
+
|
53
|
+
|
54
|
+
@app.command()
|
55
|
+
def build():
|
56
|
+
autopub = Autopub(plugins=find_plugins(state["plugins"]))
|
57
|
+
|
58
|
+
try:
|
59
|
+
autopub.build()
|
60
|
+
except AutopubException as e:
|
61
|
+
rich.print(Panel.fit(f"[red]{e.message}"))
|
62
|
+
|
63
|
+
raise typer.Exit(1) from e
|
64
|
+
else:
|
65
|
+
rich.print(Panel.fit("[green]Build succeeded"))
|
66
|
+
|
67
|
+
|
68
|
+
@app.command()
|
69
|
+
def prepare():
|
70
|
+
autopub = Autopub(plugins=find_plugins(state["plugins"]))
|
71
|
+
|
72
|
+
try:
|
73
|
+
autopub.prepare()
|
74
|
+
except AutopubException as e:
|
75
|
+
rich.print(Panel.fit(f"[red]{e.message}"))
|
76
|
+
|
77
|
+
raise typer.Exit(1) from e
|
78
|
+
else:
|
79
|
+
rich.print(Panel.fit("[green]Preparation succeeded"))
|
80
|
+
|
81
|
+
|
82
|
+
@app.command()
|
83
|
+
def publish(
|
84
|
+
repository: Annotated[
|
85
|
+
Optional[str],
|
86
|
+
typer.Option("--repository", "-r", help="Repository to publish to"),
|
87
|
+
] = None,
|
88
|
+
):
|
89
|
+
autopub = Autopub(plugins=find_plugins(state["plugins"]))
|
90
|
+
|
91
|
+
try:
|
92
|
+
autopub.publish(repository=repository)
|
93
|
+
except AutopubException as e:
|
94
|
+
rich.print(Panel.fit(f"[red]{e.message}"))
|
95
|
+
|
96
|
+
raise typer.Exit(1) from e
|
97
|
+
else:
|
98
|
+
rich.print(Panel.fit("[green]Publishing succeeded"))
|
99
|
+
|
100
|
+
|
101
|
+
@app.callback(invoke_without_command=True)
|
102
|
+
def main(
|
103
|
+
plugins: list[str] = typer.Option(
|
104
|
+
[],
|
105
|
+
"--plugin",
|
106
|
+
"-p",
|
107
|
+
help="List of plugins to use",
|
108
|
+
),
|
109
|
+
should_show_version: Annotated[
|
110
|
+
Optional[bool], typer.Option("--version", is_eager=True)
|
111
|
+
] = None,
|
112
|
+
):
|
113
|
+
state["plugins"] = plugins
|
114
|
+
state["plugins"].extend(["bump_version"])
|
115
|
+
|
116
|
+
if should_show_version:
|
117
|
+
from importlib.metadata import version
|
118
|
+
|
119
|
+
print(version("autopub"))
|
120
|
+
|
121
|
+
raise typer.Exit()
|
autopub/cli/plugins.py
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
from importlib import import_module
|
2
|
+
|
3
|
+
from autopub.plugins import AutopubPlugin
|
4
|
+
|
5
|
+
|
6
|
+
def _find_plugin(module: object) -> type[AutopubPlugin] | None:
|
7
|
+
for obj in module.__dict__.values():
|
8
|
+
if (
|
9
|
+
isinstance(obj, type)
|
10
|
+
and issubclass(obj, AutopubPlugin)
|
11
|
+
and obj is not AutopubPlugin
|
12
|
+
):
|
13
|
+
return obj
|
14
|
+
|
15
|
+
return None
|
16
|
+
|
17
|
+
|
18
|
+
def find_plugins(names: list[str]) -> list[type[AutopubPlugin]]:
|
19
|
+
plugins: list[type] = []
|
20
|
+
|
21
|
+
for plugin_name in names:
|
22
|
+
try:
|
23
|
+
# TODO: find plugins outside the autopub namespace
|
24
|
+
plugin_module = import_module(f"autopub.plugins.{plugin_name}")
|
25
|
+
|
26
|
+
plugin_class = _find_plugin(plugin_module)
|
27
|
+
|
28
|
+
if plugin_class is None:
|
29
|
+
print(f"Could not find plugin {plugin_name}")
|
30
|
+
# TODO: raise
|
31
|
+
continue
|
32
|
+
|
33
|
+
plugins.append(plugin_class)
|
34
|
+
except ImportError as e:
|
35
|
+
print(f"Error importing plugin {plugin_name}: {e}")
|
36
|
+
|
37
|
+
return plugins
|
autopub/exceptions.py
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
class AutopubException(Exception):
|
2
|
+
message: str
|
3
|
+
|
4
|
+
def __init__(self) -> None:
|
5
|
+
super().__init__(self.message)
|
6
|
+
|
7
|
+
|
8
|
+
class ReleaseFileNotFound(AutopubException):
|
9
|
+
message = "Release file not found"
|
10
|
+
|
11
|
+
|
12
|
+
class ReleaseFileEmpty(AutopubException):
|
13
|
+
message = "Release file is empty"
|
14
|
+
|
15
|
+
|
16
|
+
class ReleaseNotesEmpty(AutopubException):
|
17
|
+
message = "Release notes are empty"
|
18
|
+
|
19
|
+
|
20
|
+
class ReleaseTypeMissing(AutopubException):
|
21
|
+
message: str = "Release note is missing release type"
|
22
|
+
|
23
|
+
|
24
|
+
class ReleaseTypeInvalid(AutopubException):
|
25
|
+
def __init__(self, release_type: str):
|
26
|
+
self.message = f"Release type {release_type} is invalid"
|
27
|
+
super().__init__()
|
28
|
+
|
29
|
+
|
30
|
+
class NoPackageManagerPluginFound(AutopubException):
|
31
|
+
message = "No package manager plugin found"
|
32
|
+
|
33
|
+
|
34
|
+
class ArtifactNotFound(AutopubException):
|
35
|
+
message = "Artifact not found, did you run `autopub check`?"
|
36
|
+
|
37
|
+
|
38
|
+
class ArtifactHashMismatch(AutopubException):
|
39
|
+
message = "Artifact hash mismatch, did you run `autopub check`?"
|
40
|
+
|
41
|
+
|
42
|
+
class CommandFailed(AutopubException):
|
43
|
+
def __init__(self, command: list[str], returncode: int) -> None:
|
44
|
+
self.message = (
|
45
|
+
f"Command {' '.join(command)} failed with return code {returncode}"
|
46
|
+
)
|
47
|
+
super().__init__()
|
@@ -0,0 +1,40 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import subprocess
|
4
|
+
from typing import Any, Protocol, runtime_checkable
|
5
|
+
|
6
|
+
from autopub.exceptions import AutopubException, CommandFailed
|
7
|
+
from autopub.types import ReleaseInfo
|
8
|
+
|
9
|
+
|
10
|
+
class AutopubPlugin:
|
11
|
+
data: dict[str, object] = {}
|
12
|
+
|
13
|
+
def run_command(self, command: list[str]) -> None:
|
14
|
+
try:
|
15
|
+
subprocess.run(command, check=True)
|
16
|
+
except subprocess.CalledProcessError as e:
|
17
|
+
raise CommandFailed(command=command, returncode=e.returncode) from e
|
18
|
+
|
19
|
+
def prepare(self, release_info: ReleaseInfo) -> None: # pragma: no cover
|
20
|
+
...
|
21
|
+
|
22
|
+
def validate_release_notes(self, release_info: ReleaseInfo): # pragma: no cover
|
23
|
+
...
|
24
|
+
|
25
|
+
def on_release_notes_valid(self, release_info: ReleaseInfo): # pragma: no cover
|
26
|
+
...
|
27
|
+
|
28
|
+
def on_release_notes_invalid(self, exception: AutopubException): # pragma: no cover
|
29
|
+
...
|
30
|
+
|
31
|
+
|
32
|
+
@runtime_checkable
|
33
|
+
class AutopubPackageManagerPlugin(Protocol):
|
34
|
+
def build(self) -> None: # pragma: no cover
|
35
|
+
...
|
36
|
+
|
37
|
+
def publish(
|
38
|
+
self, repository: str | None = None, **kwargs: Any
|
39
|
+
) -> None: # pragma: no cover
|
40
|
+
...
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import pathlib
|
4
|
+
|
5
|
+
import tomlkit
|
6
|
+
from dunamai import Version
|
7
|
+
|
8
|
+
from autopub.plugins import AutopubPlugin
|
9
|
+
from autopub.types import ReleaseInfo
|
10
|
+
|
11
|
+
|
12
|
+
class BumpVersionPlugin(AutopubPlugin):
|
13
|
+
@property
|
14
|
+
def pyproject_config(self) -> tomlkit.TOMLDocument:
|
15
|
+
content = pathlib.Path("pyproject.toml").read_text()
|
16
|
+
|
17
|
+
return tomlkit.parse(content)
|
18
|
+
|
19
|
+
def _get_version(self, config: tomlkit.TOMLDocument) -> str:
|
20
|
+
try:
|
21
|
+
return config["tool"]["poetry"]["version"] # type: ignore
|
22
|
+
except KeyError:
|
23
|
+
return config["project"]["version"] # type: ignore
|
24
|
+
|
25
|
+
def _update_version(self, config: tomlkit.TOMLDocument, new_version: str) -> None:
|
26
|
+
try:
|
27
|
+
config["tool"]["poetry"]["version"] = new_version # type: ignore
|
28
|
+
except KeyError:
|
29
|
+
config["project"]["version"] = new_version # type: ignore
|
30
|
+
|
31
|
+
def prepare(self, release_info: ReleaseInfo) -> None:
|
32
|
+
config = self.pyproject_config
|
33
|
+
|
34
|
+
version = Version(self._get_version(config))
|
35
|
+
|
36
|
+
bump_type = {"major": 0, "minor": 1, "patch": 2}[release_info.release_type]
|
37
|
+
new_version = version.bump(bump_type).serialize()
|
38
|
+
|
39
|
+
self._update_version(config, new_version)
|
40
|
+
|
41
|
+
pathlib.Path("pyproject.toml").write_text(tomlkit.dumps(config)) # type: ignore
|
autopub/plugins/pdm.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
from autopub.plugins import AutopubPackageManagerPlugin, AutopubPlugin
|
6
|
+
|
7
|
+
|
8
|
+
class PDMPlugin(AutopubPlugin, AutopubPackageManagerPlugin):
|
9
|
+
def build(self) -> None:
|
10
|
+
self.run_command(["pdm", "build"])
|
11
|
+
|
12
|
+
def publish(self, repository: str | None = None, **kwargs: Any) -> None:
|
13
|
+
additional_args: list[str] = []
|
14
|
+
|
15
|
+
if repository:
|
16
|
+
additional_args += ["--repository", repository]
|
17
|
+
|
18
|
+
self.run_command(["pdm", "publish", *additional_args])
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from autopub.plugins import AutopubPackageManagerPlugin, AutopubPlugin
|
4
|
+
|
5
|
+
|
6
|
+
class PoetryPlugin(AutopubPlugin, AutopubPackageManagerPlugin):
|
7
|
+
def build(self) -> None:
|
8
|
+
self.run_command(["poetry", "build"])
|
9
|
+
|
10
|
+
def publish(self, repository: str | None = None, **kwargs: str) -> None:
|
11
|
+
additional_args: list[str] = []
|
12
|
+
|
13
|
+
if repository:
|
14
|
+
additional_args += ["--repository", repository]
|
15
|
+
|
16
|
+
self.run_command(["poetry", "publish", *additional_args])
|
@@ -0,0 +1,50 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from datetime import date
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
from autopub.plugins import AutopubPlugin
|
7
|
+
from autopub.types import ReleaseInfo
|
8
|
+
|
9
|
+
# TODO: from config
|
10
|
+
CHANGELOG_HEADER = "========="
|
11
|
+
VERSION_HEADER = "-"
|
12
|
+
|
13
|
+
|
14
|
+
class UpdateChangelogPlugin(AutopubPlugin):
|
15
|
+
@property
|
16
|
+
def changelog_file(self) -> Path:
|
17
|
+
return Path("CHANGELOG.md")
|
18
|
+
|
19
|
+
def post_prepare(self, release_info: ReleaseInfo) -> None:
|
20
|
+
assert release_info.version is not None
|
21
|
+
|
22
|
+
if not self.changelog_file.exists():
|
23
|
+
self.changelog_file.write_text(f"CHANGELOG\n{CHANGELOG_HEADER}\n\n")
|
24
|
+
|
25
|
+
current_date = date.today().strftime("%Y-%m-%d")
|
26
|
+
|
27
|
+
old_changelog_data = ""
|
28
|
+
header = ""
|
29
|
+
|
30
|
+
lines = self.changelog_file.read_text().splitlines()
|
31
|
+
|
32
|
+
for index, line in enumerate(lines):
|
33
|
+
if CHANGELOG_HEADER != line.strip():
|
34
|
+
continue
|
35
|
+
|
36
|
+
old_changelog_data = lines[index + 1 :]
|
37
|
+
header = lines[: index + 1]
|
38
|
+
break
|
39
|
+
|
40
|
+
with self.changelog_file.open("w") as f:
|
41
|
+
f.write("\n".join(header))
|
42
|
+
f.write("\n")
|
43
|
+
|
44
|
+
new_version_header = f"{release_info.version} - {current_date}"
|
45
|
+
|
46
|
+
f.write(f"\n{new_version_header}\n")
|
47
|
+
f.write(f"{VERSION_HEADER * len(new_version_header)}\n\n")
|
48
|
+
f.write(release_info.release_notes)
|
49
|
+
f.write("\n")
|
50
|
+
f.write("\n".join(old_changelog_data))
|
autopub/types.py
ADDED
@@ -1,31 +1,37 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: autopub
|
3
|
-
Version: 0.
|
3
|
+
Version: 1.0.0a1
|
4
4
|
Summary: Automatic package release upon pull request merge
|
5
5
|
Home-page: https://github.com/autopub/autopub
|
6
6
|
License: AGPL-3.0
|
7
7
|
Keywords: automatic,packaging,publish,release,version
|
8
8
|
Author: Justin Mayer
|
9
9
|
Author-email: entroP@gmail.com
|
10
|
-
Requires-Python: >=3.
|
10
|
+
Requires-Python: >=3.8,<4.0
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
12
12
|
Classifier: Environment :: Console
|
13
13
|
Classifier: Intended Audience :: Developers
|
14
14
|
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
15
15
|
Classifier: Operating System :: OS Independent
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
17
|
-
Classifier: Programming Language :: Python :: 3.6
|
18
|
-
Classifier: Programming Language :: Python :: 3.7
|
19
17
|
Classifier: Programming Language :: Python :: 3.8
|
20
18
|
Classifier: Programming Language :: Python :: 3.9
|
21
19
|
Classifier: Programming Language :: Python :: 3.10
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
22
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
23
22
|
Classifier: Topic :: System :: Software Distribution
|
24
23
|
Classifier: Topic :: System :: Systems Administration
|
25
24
|
Provides-Extra: github
|
26
|
-
Requires-Dist:
|
27
|
-
Requires-Dist:
|
25
|
+
Requires-Dist: build (>=0.10.0,<0.11.0)
|
26
|
+
Requires-Dist: dunamai (>=1.17.0,<2.0.0)
|
27
|
+
Requires-Dist: githubrelease (>=1.5.9,<2.0.0) ; extra == "github"
|
28
|
+
Requires-Dist: httpx (==0.16.1) ; extra == "github"
|
29
|
+
Requires-Dist: python-frontmatter (>=1.0.0,<2.0.0)
|
30
|
+
Requires-Dist: rich (>=12.5.1,<13.0.0)
|
31
|
+
Requires-Dist: time-machine (>=2.13.0,<3.0.0)
|
28
32
|
Requires-Dist: tomlkit (>=0.5,<2.0)
|
33
|
+
Requires-Dist: twine (>=4.0.2,<5.0.0)
|
34
|
+
Requires-Dist: typer (>=0.9.0,<0.10.0)
|
29
35
|
Project-URL: Issue Tracker, https://github.com/autopub/autopub/issues
|
30
36
|
Project-URL: Repository, https://github.com/autopub/autopub
|
31
37
|
Description-Content-Type: text/markdown
|
@@ -38,7 +44,7 @@ AutoPub enables project maintainers to release new package versions to PyPI by m
|
|
38
44
|
|
39
45
|
## Environment
|
40
46
|
|
41
|
-
AutoPub is intended for use with continuous integration (CI) systems such as [GitHub Actions][], [CircleCI][], or [Travis CI][]. Projects used with AutoPub
|
47
|
+
AutoPub is intended for use with continuous integration (CI) systems such as [GitHub Actions][], [CircleCI][], or [Travis CI][]. Projects used with AutoPub are built via [build][] and published via [Twine][]. Contributions that add support for other CI and build systems are welcome.
|
42
48
|
|
43
49
|
## Configuration
|
44
50
|
|
@@ -82,6 +88,6 @@ For systems such as Travis CI in which only one deployment step is permitted, th
|
|
82
88
|
[GitHub Actions]: https://github.com/features/actions
|
83
89
|
[CircleCI]: https://circleci.com
|
84
90
|
[Travis CI]: https://travis-ci.org
|
85
|
-
[
|
86
|
-
[
|
91
|
+
[build]: https://pypa-build.readthedocs.io
|
92
|
+
[Twine]: https://twine.readthedocs.io/
|
87
93
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
autopub/__init__.py,sha256=zcNp8aJE_jo-D5xvDuWNZ-ZK6c67NqbTaKtwfGJWtZs,5576
|
2
|
+
autopub/cli/__init__.py,sha256=XG1QVlKPv7svwFOcuosBzXr9AIy9xKDTuOPfI14dtBs,2980
|
3
|
+
autopub/cli/plugins.py,sha256=x80BsIpY81o6rv3MJTH2oTAZkqnAYp3MkjUtpPfxGfI,1021
|
4
|
+
autopub/exceptions.py,sha256=JYn8sIYWCedhtO3XfftZ0M5M_rAPxiGQ4MGbWUaTYwE,1243
|
5
|
+
autopub/plugins/__init__.py,sha256=HtnCcxvZqTUC79vs5aVpNHenRCNuTPpz1gAu2OYkCzg,1153
|
6
|
+
autopub/plugins/bump_version.py,sha256=p-_q80pJDCUy-fKhp-9_9HWTWC2pKe2lFJnimuHHH7w,1338
|
7
|
+
autopub/plugins/pdm.py,sha256=Pczye06fKg8_HMJDkEfMXQyvao9rZ7sqzTHFd6lLEpU,532
|
8
|
+
autopub/plugins/poetry.py,sha256=d2LvW9RI7ZB3reBOXbcp1mqWmzQ06Uyg_T-MxTvlSBg,517
|
9
|
+
autopub/plugins/update_changelog.py,sha256=MmDBGFs6v8zPn3NCfUumpF8YODhg5x2AdjyTzgq5bYQ,1456
|
10
|
+
autopub/types.py,sha256=FcRH4l27nrkQFUQrbAKxNzPPJfzg_0DExZYsCu9Jdzk,249
|
11
|
+
autopub-1.0.0a1.dist-info/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
12
|
+
autopub-1.0.0a1.dist-info/METADATA,sha256=hJImNEz5eaUoQoG1Ziu44EuCP5Wwjo-s9NBUiFWLiMk,3757
|
13
|
+
autopub-1.0.0a1.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
14
|
+
autopub-1.0.0a1.dist-info/entry_points.txt,sha256=oeTav5NgCxif6mcZ_HeVGgGv5LzS4DwdI01nr4bO1IM,43
|
15
|
+
autopub-1.0.0a1.dist-info/RECORD,,
|