autopub 0.2.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {autopub-0.2.2 → autopub-0.4.0}/PKG-INFO +12 -12
- {autopub-0.2.2 → autopub-0.4.0}/README.md +3 -3
- autopub-0.4.0/autopub/base.py +160 -0
- autopub-0.4.0/autopub/build_release.py +14 -0
- {autopub-0.2.2 → autopub-0.4.0}/autopub/check_release.py +6 -2
- {autopub-0.2.2 → autopub-0.4.0}/autopub/create_github_release.py +5 -5
- {autopub-0.2.2 → autopub-0.4.0}/autopub/prepare_release.py +19 -14
- autopub-0.4.0/autopub/publish_release.py +17 -0
- autopub-0.4.0/autopub/vendor/__init__.py +0 -0
- autopub-0.4.0/autopub/vendor/github_release.py +1164 -0
- {autopub-0.2.2 → autopub-0.4.0}/pyproject.toml +6 -4
- autopub-0.2.2/autopub/build_release.py +0 -15
- autopub-0.2.2/autopub/publish_release.py +0 -22
- autopub-0.2.2/setup.py +0 -38
- {autopub-0.2.2 → autopub-0.4.0}/LICENSE +0 -0
- {autopub-0.2.2 → autopub-0.4.0}/autopub/__init__.py +0 -0
- {autopub-0.2.2 → autopub-0.4.0}/autopub/autopub.py +0 -0
- /autopub-0.2.2/autopub/base.py → /autopub-0.4.0/autopub/base copy.py +0 -0
- {autopub-0.2.2 → autopub-0.4.0}/autopub/commit_release.py +0 -0
- {autopub-0.2.2 → autopub-0.4.0}/autopub/deploy_release.py +0 -0
- {autopub-0.2.2 → autopub-0.4.0}/autopub/github_contributor.py +0 -0
- {autopub-0.2.2 → autopub-0.4.0}/autopub/prepare_release copy.py +0 -0
@@ -1,31 +1,32 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: autopub
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
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.10.0,<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
|
-
Classifier: Programming Language :: Python :: 3.8
|
20
|
-
Classifier: Programming Language :: Python :: 3.9
|
21
17
|
Classifier: Programming Language :: Python :: 3.10
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
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: httpx (==0.16.1) ; extra == "github"
|
28
28
|
Requires-Dist: tomlkit (>=0.5,<2.0)
|
29
|
+
Requires-Dist: twine (>=4.0.2,<5.0.0)
|
29
30
|
Project-URL: Issue Tracker, https://github.com/autopub/autopub/issues
|
30
31
|
Project-URL: Repository, https://github.com/autopub/autopub
|
31
32
|
Description-Content-Type: text/markdown
|
@@ -38,7 +39,7 @@ AutoPub enables project maintainers to release new package versions to PyPI by m
|
|
38
39
|
|
39
40
|
## Environment
|
40
41
|
|
41
|
-
AutoPub is intended for use with continuous integration (CI) systems such as [GitHub Actions][], [CircleCI][], or [Travis CI][]. Projects used with AutoPub
|
42
|
+
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
43
|
|
43
44
|
## Configuration
|
44
45
|
|
@@ -82,6 +83,5 @@ For systems such as Travis CI in which only one deployment step is permitted, th
|
|
82
83
|
[GitHub Actions]: https://github.com/features/actions
|
83
84
|
[CircleCI]: https://circleci.com
|
84
85
|
[Travis CI]: https://travis-ci.org
|
85
|
-
[
|
86
|
-
[
|
87
|
-
|
86
|
+
[build]: https://pypa-build.readthedocs.io
|
87
|
+
[Twine]: https://twine.readthedocs.io/
|
@@ -6,7 +6,7 @@ AutoPub enables project maintainers to release new package versions to PyPI by m
|
|
6
6
|
|
7
7
|
## Environment
|
8
8
|
|
9
|
-
AutoPub is intended for use with continuous integration (CI) systems such as [GitHub Actions][], [CircleCI][], or [Travis CI][]. Projects used with AutoPub
|
9
|
+
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.
|
10
10
|
|
11
11
|
## Configuration
|
12
12
|
|
@@ -50,5 +50,5 @@ For systems such as Travis CI in which only one deployment step is permitted, th
|
|
50
50
|
[GitHub Actions]: https://github.com/features/actions
|
51
51
|
[CircleCI]: https://circleci.com
|
52
52
|
[Travis CI]: https://travis-ci.org
|
53
|
-
[
|
54
|
-
[
|
53
|
+
[build]: https://pypa-build.readthedocs.io
|
54
|
+
[Twine]: https://twine.readthedocs.io/
|
@@ -0,0 +1,160 @@
|
|
1
|
+
import os
|
2
|
+
import re
|
3
|
+
import subprocess
|
4
|
+
import sys
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
from tomlkit import parse
|
8
|
+
|
9
|
+
|
10
|
+
def dict_get(_dict, keys, default=None):
|
11
|
+
"""Query nested dictionary with list of keys, returning None if not found."""
|
12
|
+
for key in keys:
|
13
|
+
if isinstance(_dict, dict):
|
14
|
+
_dict = _dict.get(key, default)
|
15
|
+
else:
|
16
|
+
return default
|
17
|
+
return _dict
|
18
|
+
|
19
|
+
|
20
|
+
# Determine CI/CD environment
|
21
|
+
|
22
|
+
if os.environ.get("CIRCLECI"):
|
23
|
+
CI_SYSTEM = "circleci"
|
24
|
+
CIRCLE_PROJECT_USERNAME = os.environ.get("CIRCLE_PROJECT_USERNAME")
|
25
|
+
CIRCLE_PROJECT_REPONAME = os.environ.get("CIRCLE_PROJECT_REPONAME")
|
26
|
+
REPO_SLUG = f"{CIRCLE_PROJECT_USERNAME}/{CIRCLE_PROJECT_REPONAME}"
|
27
|
+
elif os.environ.get("GITHUB_ACTIONS") == "true":
|
28
|
+
CI_SYSTEM = "github"
|
29
|
+
REPO_SLUG = os.environ.get("GITHUB_REPOSITORY")
|
30
|
+
elif os.environ.get("TRAVIS"):
|
31
|
+
CI_SYSTEM = "travis"
|
32
|
+
REPO_SLUG = os.environ.get("TRAVIS_REPO_SLUG")
|
33
|
+
else:
|
34
|
+
CI_SYSTEM = os.environ.get("CI_SYSTEM", None)
|
35
|
+
REPO_SLUG = os.environ.get("REPO_SLUG", None)
|
36
|
+
|
37
|
+
# Project root and file name configuration
|
38
|
+
|
39
|
+
PROJECT_ROOT = os.environ.get("PROJECT_ROOT")
|
40
|
+
PYPROJECT_FILE_NAME = os.environ.get("PYPROJECT_FILE_NAME", "pyproject.toml")
|
41
|
+
|
42
|
+
if PROJECT_ROOT:
|
43
|
+
ROOT = Path(PROJECT_ROOT)
|
44
|
+
else:
|
45
|
+
ROOT = Path(
|
46
|
+
subprocess.check_output(["git", "rev-parse", "--show-toplevel"])
|
47
|
+
.decode("ascii")
|
48
|
+
.strip()
|
49
|
+
)
|
50
|
+
|
51
|
+
PYPROJECT_FILE = ROOT / PYPROJECT_FILE_NAME
|
52
|
+
|
53
|
+
# Retrieve configuration from pyproject file
|
54
|
+
|
55
|
+
if os.path.exists(PYPROJECT_FILE):
|
56
|
+
config = parse(open(PYPROJECT_FILE).read())
|
57
|
+
else:
|
58
|
+
print(f"Could not find pyproject file at: {PYPROJECT_FILE}")
|
59
|
+
sys.exit(1)
|
60
|
+
|
61
|
+
PROJECT_NAME = dict_get(config, ["tool", "autopub", "project-name"])
|
62
|
+
if not PROJECT_NAME:
|
63
|
+
PROJECT_NAME = dict_get(config, ["tool", "poetry", "name"])
|
64
|
+
if not PROJECT_NAME:
|
65
|
+
PROJECT_NAME = dict_get(config, ["project", "name"])
|
66
|
+
if not PROJECT_NAME:
|
67
|
+
print(
|
68
|
+
"Could not determine project name. Under the pyproject file's "
|
69
|
+
'[tool.autopub] header, add:\nproject-name = "YourProjectName"'
|
70
|
+
)
|
71
|
+
sys.exit(1)
|
72
|
+
|
73
|
+
RELEASE_FILE_NAME = dict_get(
|
74
|
+
config, ["tool", "autopub", "release-file"], default="RELEASE.md"
|
75
|
+
)
|
76
|
+
RELEASE_FILE = ROOT / RELEASE_FILE_NAME
|
77
|
+
|
78
|
+
CHANGELOG_FILE_NAME = dict_get(
|
79
|
+
config, ["tool", "autopub", "changelog-file"], default="CHANGELOG.md"
|
80
|
+
)
|
81
|
+
CHANGELOG_FILE = ROOT / CHANGELOG_FILE_NAME
|
82
|
+
|
83
|
+
CHANGELOG_HEADER = dict_get(
|
84
|
+
config, ["tool", "autopub", "changelog-header"], default="========="
|
85
|
+
)
|
86
|
+
|
87
|
+
VERSION_HEADER = dict_get(config, ["tool", "autopub", "version-header"], default="-")
|
88
|
+
VERSION_STRINGS = dict_get(config, ["tool", "autopub", "version-strings"], default=[])
|
89
|
+
|
90
|
+
TAG_PREFIX = dict_get(config, ["tool", "autopub", "tag-prefix"], default="")
|
91
|
+
|
92
|
+
PYPI_URL = dict_get(config, ["tool", "autopub", "pypi-url"])
|
93
|
+
|
94
|
+
# Git configuration
|
95
|
+
|
96
|
+
GIT_USERNAME = dict_get(config, ["tool", "autopub", "git-username"])
|
97
|
+
GIT_EMAIL = dict_get(config, ["tool", "autopub", "git-email"])
|
98
|
+
|
99
|
+
# GitHub
|
100
|
+
|
101
|
+
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", None)
|
102
|
+
APPEND_GITHUB_CONTRIBUTOR = dict_get(
|
103
|
+
config, ["tool", "autopub", "append-github-contributor"], False
|
104
|
+
)
|
105
|
+
|
106
|
+
|
107
|
+
def run_process(popenargs, encoding="utf-8", env=None):
|
108
|
+
if env is not None:
|
109
|
+
env = {**os.environ, **env}
|
110
|
+
try:
|
111
|
+
return subprocess.check_output(popenargs, encoding=encoding, env=env).strip()
|
112
|
+
except subprocess.CalledProcessError as e:
|
113
|
+
print(e.output, file=sys.stderr)
|
114
|
+
sys.exit(1)
|
115
|
+
|
116
|
+
|
117
|
+
def git(popenargs):
|
118
|
+
# Do not decode ASCII for commit messages so emoji are preserved
|
119
|
+
return subprocess.check_output(["git", *popenargs])
|
120
|
+
|
121
|
+
|
122
|
+
def check_exit_code(popenargs):
|
123
|
+
return subprocess.call(popenargs, shell=True)
|
124
|
+
|
125
|
+
|
126
|
+
def get_project_version():
|
127
|
+
# Backwards compat: Try poetry first and then fall "back" to standards
|
128
|
+
version = dict_get(config, ["tool", "poetry", "version"])
|
129
|
+
if version is None:
|
130
|
+
return dict_get(config, ["project", "version"])
|
131
|
+
else:
|
132
|
+
return version
|
133
|
+
|
134
|
+
|
135
|
+
def get_release_info():
|
136
|
+
RELEASE_TYPE_REGEX = re.compile(r"^[Rr]elease [Tt]ype: (major|minor|patch)$")
|
137
|
+
|
138
|
+
with open(RELEASE_FILE, "r") as f:
|
139
|
+
line = f.readline()
|
140
|
+
match = RELEASE_TYPE_REGEX.match(line)
|
141
|
+
|
142
|
+
if not match:
|
143
|
+
print(
|
144
|
+
"The RELEASE file should start with 'Release type' and "
|
145
|
+
"specify one of the following values: major, minor, or patch."
|
146
|
+
)
|
147
|
+
sys.exit(1)
|
148
|
+
|
149
|
+
type_ = match.group(1)
|
150
|
+
changelog = "".join([l for l in f.readlines()]).strip()
|
151
|
+
|
152
|
+
return type_, changelog
|
153
|
+
|
154
|
+
|
155
|
+
def configure_git():
|
156
|
+
if not GIT_USERNAME or not GIT_EMAIL:
|
157
|
+
print("git-username and git-email must be defined in the pyproject file")
|
158
|
+
sys.exit(1)
|
159
|
+
git(["config", "user.name", GIT_USERNAME])
|
160
|
+
git(["config", "user.email", GIT_EMAIL])
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import os
|
2
|
+
import sys
|
3
|
+
|
4
|
+
sys.path.append(os.path.dirname(__file__)) # noqa
|
5
|
+
|
6
|
+
from base import git, run_process
|
7
|
+
|
8
|
+
|
9
|
+
def build_release():
|
10
|
+
env = None
|
11
|
+
if "SOURCE_DATE_EPOCH" not in os.environ:
|
12
|
+
ctime = git(["log", "-1", "--pretty=%ct"]).decode().strip()
|
13
|
+
env = {"SOURCE_DATE_EPOCH": ctime}
|
14
|
+
run_process([sys.executable, "-m", "build"], env=env)
|
@@ -7,9 +7,13 @@ from base import CI_SYSTEM, RELEASE_FILE, run_process
|
|
7
7
|
|
8
8
|
|
9
9
|
def check_release():
|
10
|
-
|
10
|
+
needs_release = os.path.exists(RELEASE_FILE)
|
11
|
+
if not needs_release:
|
11
12
|
print("Not releasing a new version because there is no RELEASE file.")
|
12
13
|
if CI_SYSTEM == "circleci":
|
13
14
|
run_process(["circleci", "step", "halt"])
|
14
|
-
elif CI_SYSTEM == "
|
15
|
+
elif CI_SYSTEM == "travis":
|
15
16
|
sys.exit(1)
|
17
|
+
if CI_SYSTEM == "github":
|
18
|
+
with open(os.path.expandvars("$GITHUB_OUTPUT"), "a") as f:
|
19
|
+
f.write("autopub_release={}\n".format("true" if needs_release else "false"))
|
@@ -5,20 +5,20 @@ import time
|
|
5
5
|
sys.path.append(os.path.dirname(__file__)) # noqa
|
6
6
|
|
7
7
|
from base import (
|
8
|
-
run_process,
|
9
|
-
check_exit_code,
|
10
|
-
get_project_version,
|
11
|
-
configure_git,
|
12
8
|
PROJECT_NAME,
|
13
9
|
REPO_SLUG,
|
14
10
|
TAG_PREFIX,
|
11
|
+
check_exit_code,
|
12
|
+
configure_git,
|
13
|
+
get_project_version,
|
15
14
|
get_release_info,
|
15
|
+
run_process,
|
16
16
|
)
|
17
17
|
|
18
18
|
|
19
19
|
def create_github_release():
|
20
20
|
try:
|
21
|
-
from github_release import
|
21
|
+
from .vendor.github_release import gh_asset_upload, gh_release_create
|
22
22
|
except ModuleNotFoundError:
|
23
23
|
print("Cannot create GitHub release due to missing dependency: github_release")
|
24
24
|
sys.exit(1)
|
@@ -6,17 +6,20 @@ sys.path.append(os.path.dirname(__file__)) # noqa
|
|
6
6
|
|
7
7
|
from datetime import datetime
|
8
8
|
|
9
|
+
import tomlkit
|
9
10
|
from base import (
|
10
|
-
configure_git,
|
11
|
-
get_release_info,
|
12
|
-
run_process,
|
13
11
|
CHANGELOG_FILE,
|
14
12
|
CHANGELOG_HEADER,
|
13
|
+
PYPROJECT_FILE,
|
15
14
|
ROOT,
|
16
15
|
VERSION_HEADER,
|
17
16
|
VERSION_STRINGS,
|
17
|
+
configure_git,
|
18
|
+
dict_get,
|
19
|
+
get_project_version,
|
20
|
+
get_release_info,
|
18
21
|
)
|
19
|
-
|
22
|
+
from dunamai import Version
|
20
23
|
from github_contributor import append_github_contributor
|
21
24
|
|
22
25
|
|
@@ -38,20 +41,22 @@ def update_version_strings(file_path, new_version):
|
|
38
41
|
def prepare_release():
|
39
42
|
configure_git()
|
40
43
|
|
41
|
-
POETRY_DUMP_VERSION_OUTPUT = re.compile(
|
42
|
-
r"Bumping version from \d+\.\d+\.\d+ to (?P<version>\d+\.\d+\.\d+)"
|
43
|
-
)
|
44
|
-
|
45
44
|
type_, release_changelog = get_release_info()
|
46
45
|
|
47
|
-
|
48
|
-
|
46
|
+
version = Version(get_project_version())
|
47
|
+
new_version = version.bump({"major": 0, "minor": 1, "patch": 2}[type_]).serialize()
|
48
|
+
|
49
|
+
with open(PYPROJECT_FILE, "r") as f:
|
50
|
+
config = tomlkit.load(f)
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
poetry = dict_get(config, ["tool", "poetry", "version"])
|
53
|
+
if poetry:
|
54
|
+
config["tool"]["poetry"]["version"] = new_version
|
55
|
+
else:
|
56
|
+
config["project"]["version"] = new_version
|
53
57
|
|
54
|
-
|
58
|
+
with open(PYPROJECT_FILE, "w") as f:
|
59
|
+
config = tomlkit.dump(config, f)
|
55
60
|
|
56
61
|
if VERSION_STRINGS:
|
57
62
|
for version_file in VERSION_STRINGS:
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import glob
|
2
|
+
import os
|
3
|
+
import sys
|
4
|
+
|
5
|
+
sys.path.append(os.path.dirname(__file__)) # noqa
|
6
|
+
|
7
|
+
from base import PYPI_URL, run_process
|
8
|
+
|
9
|
+
|
10
|
+
def publish_release():
|
11
|
+
env = None
|
12
|
+
if PYPI_URL:
|
13
|
+
env = {"TWINE_REPOSITORY_URL": PYPI_URL}
|
14
|
+
dists = glob.glob("dist/*")
|
15
|
+
run_process(
|
16
|
+
[sys.executable, "-m", "twine", "upload", "--non-interactive", *dists], env=env
|
17
|
+
)
|
File without changes
|