autopub 0.3.0__py3-none-any.whl → 0.4.0__py3-none-any.whl

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/base copy.py ADDED
@@ -0,0 +1,159 @@
1
+ import os
2
+ import re
3
+ import subprocess
4
+
5
+ from pathlib import Path
6
+ from tomlkit import parse
7
+
8
+
9
+ def dict_get(_dict, keys, default=None):
10
+ """Query nested dictionary with list of keys, returning None if not found."""
11
+ for key in keys:
12
+ if isinstance(_dict, dict):
13
+ _dict = _dict.get(key, default)
14
+ else:
15
+ return default
16
+ return _dict
17
+
18
+
19
+ # Determine CI/CD environment
20
+
21
+ if os.environ.get("CIRCLECI"):
22
+ CI_SYSTEM = "circleci"
23
+ CIRCLE_PROJECT_USERNAME = os.environ.get("CIRCLE_PROJECT_USERNAME")
24
+ CIRCLE_PROJECT_REPONAME = os.environ.get("CIRCLE_PROJECT_REPONAME")
25
+ REPO_SLUG = f"{CIRCLE_PROJECT_USERNAME}/{CIRCLE_PROJECT_REPONAME}"
26
+ elif os.environ.get("GITHUB_WORKFLOW"):
27
+ CI_SYSTEM = "github"
28
+ REPO_SLUG = os.environ.get("GITHUB_REPOSITORY")
29
+ elif os.environ.get("TRAVIS"):
30
+ CI_SYSTEM = "travis"
31
+ REPO_SLUG = os.environ.get("TRAVIS_REPO_SLUG")
32
+ else:
33
+ CI_SYSTEM = os.environ.get("CI_SYSTEM", None)
34
+ REPO_SLUG = os.environ.get("REPO_SLUG", None)
35
+
36
+ # Project root and file name configuration
37
+
38
+ PROJECT_ROOT = os.environ.get("PROJECT_ROOT")
39
+ PYPROJECT_FILE_NAME = os.environ.get("PYPROJECT_FILE_NAME", "pyproject.toml")
40
+
41
+ if PROJECT_ROOT:
42
+ ROOT = Path(PROJECT_ROOT)
43
+ else:
44
+ ROOT = Path(
45
+ subprocess.check_output(["git", "rev-parse", "--show-toplevel"])
46
+ .decode("ascii")
47
+ .strip()
48
+ )
49
+
50
+ PYPROJECT_FILE = ROOT / PYPROJECT_FILE_NAME
51
+
52
+ # Retrieve configuration from pyproject file
53
+
54
+ if os.path.exists(PYPROJECT_FILE):
55
+ config = parse(open(PYPROJECT_FILE).read())
56
+ else:
57
+ raise SystemExit(f"Could not find pyproject file at: {PYPROJECT_FILE}")
58
+
59
+ PROJECT_NAME = dict_get(config, ["tool", "autopub", "project-name"])
60
+ if not PROJECT_NAME:
61
+ PROJECT_NAME = dict_get(config, ["tool", "poetry", "name"])
62
+ if not PROJECT_NAME:
63
+ raise SystemExit(
64
+ "Could not determine project name. Under the pyproject file's "
65
+ '[tool.autopub] header, add:\nproject-name = "YourProjectName"'
66
+ )
67
+
68
+ RELEASE_FILE_NAME = dict_get(
69
+ config, ["tool", "autopub", "release-file"], default="RELEASE.md"
70
+ )
71
+ RELEASE_FILE = ROOT / RELEASE_FILE_NAME
72
+
73
+ CHANGELOG_FILE_NAME = dict_get(
74
+ config, ["tool", "autopub", "changelog-file"], default="CHANGELOG.md"
75
+ )
76
+ CHANGELOG_FILE = ROOT / CHANGELOG_FILE_NAME
77
+
78
+ CHANGELOG_HEADER = dict_get(
79
+ config, ["tool", "autopub", "changelog-header"], default="========="
80
+ )
81
+
82
+ VERSION_HEADER = dict_get(config, ["tool", "autopub", "version-header"], default="-")
83
+ VERSION_STRINGS = dict_get(config, ["tool", "autopub", "version-strings"], default=[])
84
+
85
+ TAG_PREFIX = dict_get(config, ["tool", "autopub", "tag-prefix"], default="")
86
+
87
+ PYPI_URL = dict_get(config, ["tool", "autopub", "pypi-url"])
88
+
89
+ BUILD_SYSTEM = dict_get(config, ["tool", "autopub", "build-system"])
90
+ if not BUILD_SYSTEM:
91
+ build_requires = dict_get(config, ["build-system", "requires"])
92
+ if "poetry" in build_requires:
93
+ BUILD_SYSTEM = "poetry"
94
+ elif "setuptools" in build_requires:
95
+ BUILD_SYSTEM = "setuptools"
96
+
97
+ # Git configuration
98
+
99
+ GIT_USERNAME = dict_get(config, ["tool", "autopub", "git-username"])
100
+ GIT_EMAIL = dict_get(config, ["tool", "autopub", "git-email"])
101
+
102
+ # GitHub
103
+
104
+ GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", None)
105
+ APPEND_GITHUB_CONTRIBUTOR = dict_get(
106
+ config, ["tool", "autopub", "append-github-contributor"], False
107
+ )
108
+
109
+
110
+ def run_process(popenargs, encoding="utf-8"):
111
+ return subprocess.check_output(popenargs).decode(encoding).strip()
112
+
113
+
114
+ def git(popenargs):
115
+ # Do not decode ASCII for commit messages so emoji are preserved
116
+ return subprocess.check_output(["git", *popenargs])
117
+
118
+
119
+ def check_exit_code(popenargs):
120
+ return subprocess.call(popenargs, shell=True)
121
+
122
+
123
+ def get_project_version():
124
+ VERSION_REGEX = re.compile(r"^version\s*=\s*\"(?P<version>\d+\.\d+\.\d+)\"$")
125
+
126
+ with open(PYPROJECT_FILE) as f:
127
+ for line in f:
128
+ match = VERSION_REGEX.match(line)
129
+
130
+ if match:
131
+ return match.group("version")
132
+
133
+ return None
134
+
135
+
136
+ def get_release_info():
137
+ RELEASE_TYPE_REGEX = re.compile(r"^[Rr]elease [Tt]ype: (major|minor|patch)$")
138
+
139
+ with open(RELEASE_FILE, "r") as f:
140
+ line = f.readline()
141
+ match = RELEASE_TYPE_REGEX.match(line)
142
+
143
+ if not match:
144
+ raise SystemExit(
145
+ "The RELEASE file should start with 'Release type' and "
146
+ "specify one of the following values: major, minor, or patch."
147
+ )
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
+ raise SystemExit("git-username and git-email must be defined in the pyproject file")
158
+ git(["config", "user.name", GIT_USERNAME])
159
+ git(["config", "user.email", GIT_EMAIL])
@@ -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 gh_release_create, gh_asset_upload
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)
@@ -0,0 +1,119 @@
1
+ import os
2
+ import re
3
+ import sys
4
+
5
+ sys.path.append(os.path.dirname(__file__)) # noqa
6
+
7
+ from datetime import datetime
8
+
9
+ from base import (
10
+ configure_git,
11
+ get_release_info,
12
+ run_process,
13
+ CHANGELOG_FILE,
14
+ CHANGELOG_HEADER,
15
+ ROOT,
16
+ VERSION_HEADER,
17
+ VERSION_STRINGS,
18
+ )
19
+
20
+
21
+ def find_existing_changelog_files():
22
+ changelogs = []
23
+ changelog_paths = [ROOT, ROOT / "docs"]
24
+ changelog_names = ["CHANGELOG", "CHANGES", "HISTORY"]
25
+ changelog_extensions = ["", ".md", ".markdown", ".mdown", ".mkd", ".rst", ".txt"]
26
+ for path in changelog_paths:
27
+ for name in changelog_names:
28
+ for ext in changelog_extensions:
29
+ changelog = path / f"{name}{ext}"
30
+ if changelog.is_file():
31
+ changelogs.append(changelog)
32
+ if len(changelogs) > 0:
33
+ print(f"Specified changelog file not found: {CHANGELOG_FILE}")
34
+ print("Maybe set 'changelog-file' setting to discovered existing file:\n")
35
+ for changelog in changelogs:
36
+ print(f"{changelog}\n")
37
+ sys.exit(1)
38
+
39
+
40
+ def validate_release():
41
+ if not CHANGELOG_FILE.is_file():
42
+ find_existing_changelog_files()
43
+
44
+ if not os.environ.get("PYPI_PASSWORD"):
45
+ print("PYPI_PASSWORD environment variable is not set.")
46
+ sys.exit(1)
47
+
48
+
49
+ def update_version_strings(file_path, new_version):
50
+ version_regex = re.compile(r"(^_*?version_*?\s*=\s*['\"])(\d+\.\d+\.\d+)", re.M)
51
+ with open(file_path, "r+") as f:
52
+ content = f.read()
53
+ f.seek(0)
54
+ f.write(
55
+ re.sub(
56
+ version_regex,
57
+ lambda match: "{}{}".format(match.group(1), new_version),
58
+ content,
59
+ )
60
+ )
61
+ f.truncate()
62
+
63
+
64
+ def prepare_release():
65
+ configure_git()
66
+ validate_release()
67
+
68
+ POETRY_DUMP_VERSION_OUTPUT = re.compile(
69
+ r"Bumping version from \d+\.\d+\.\d+ to (?P<version>\d+\.\d+\.\d+)"
70
+ )
71
+
72
+ release_type, release_changelog = get_release_info()
73
+
74
+ output = run_process(["poetry", "version", release_type])
75
+ version_match = POETRY_DUMP_VERSION_OUTPUT.match(output)
76
+
77
+ if not version_match:
78
+ print("Unable to bump the project version using Poetry")
79
+ sys.exit(1)
80
+
81
+ new_version = version_match.group("version")
82
+
83
+ if VERSION_STRINGS:
84
+ for version_file in VERSION_STRINGS:
85
+ file_path = ROOT / version_file
86
+ update_version_strings(file_path, new_version)
87
+
88
+ current_date = datetime.utcnow().strftime("%Y-%m-%d")
89
+
90
+ old_changelog_data = ""
91
+ header = ""
92
+
93
+ if not CHANGELOG_FILE.is_file():
94
+ with open(CHANGELOG_FILE, "a+") as f:
95
+ f.write(f"CHANGELOG\n{CHANGELOG_HEADER}\n\n")
96
+
97
+ with open(CHANGELOG_FILE, "r") as f:
98
+ lines = f.readlines()
99
+
100
+ for index, line in enumerate(lines):
101
+ if CHANGELOG_HEADER != line.strip():
102
+ continue
103
+
104
+ old_changelog_data = lines[index + 1 :]
105
+ header = lines[: index + 1]
106
+ break
107
+
108
+ with open(CHANGELOG_FILE, "w") as f:
109
+ f.write("".join(header))
110
+
111
+ new_version_header = f"{new_version} - {current_date}"
112
+
113
+ f.write(f"\n{new_version_header}\n")
114
+ f.write(f"{VERSION_HEADER * len(new_version_header)}\n\n")
115
+
116
+ f.write(release_changelog)
117
+ f.write("\n")
118
+
119
+ f.write("".join(old_changelog_data))
File without changes