Skip to content

Commit cc303a7

Browse files
authored
Merge pull request #5891: Add pants-plugins/release to handle wheel metadata generation
2 parents eca0b2d + ffe174b commit cc303a7

File tree

9 files changed

+559
-2
lines changed

9 files changed

+559
-2
lines changed

CHANGELOG.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Added
1515
working on StackStorm, improve our security posture, and improve CI reliability thanks in part
1616
to pants' use of PEX lockfiles. This is not a user-facing addition.
1717
#5778 #5789 #5817 #5795 #5830 #5833 #5834 #5841 #5840 #5838 #5842 #5837 #5849 #5850
18-
#5846 #5853 #5848 #5847 #5858 #5857 #5860 #5868 #5871 #5864 #5874 #5884 #5893
18+
#5846 #5853 #5848 #5847 #5858 #5857 #5860 #5868 #5871 #5864 #5874 #5884 #5893 #5891
1919
Contributed by @cognifloyd
2020

2121
* Added a joint index to solve the problem of slow mongo queries for scheduled executions. #5805

pants-plugins/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ These StackStorm-specific plugins might be useful in other StackStorm-related re
1616

1717
These StackStorm-specific plugins are probably only useful for the st2 repo.
1818
- `api_spec`
19+
- `release`
1920
- `sample_conf`
2021
- `schemas`
2122

@@ -52,6 +53,14 @@ If it is not checked out, then some of the tests will fail.
5253
If it is not checked out, `pack_metadata_in_git_submodule` handles providing
5354
a helpful, instructive error message as early as possible.
5455

56+
### `release` plugin
57+
58+
This plugin implements the [`SetupKwargs`](https://www.pantsbuild.org/docs/plugins-setup-py)
59+
plugin hook that pants uses when it auto-generates a `setup.py` file whenever
60+
it builds a `python_distribution()` (ie a wheel or an sdist). This makes it
61+
easy to centralize all of the common bits of metadata that need to go in all
62+
the wheels (like `author="StackStorm"` or our `project_urls`).
63+
5564
### `sample_conf` plugin
5665

5766
This plugin wires up pants to make sure `conf/st2.conf.sample` gets

pants-plugins/release/BUILD

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
python_sources()
2+
3+
python_tests(
4+
name="tests",
5+
)

pants-plugins/release/__init__.py

Whitespace-only changes.

pants-plugins/release/register.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2023 The StackStorm Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Please see https://www.pantsbuild.org/docs/plugins-setup-py
17+
"""
18+
19+
from release.rules import rules as release_rules
20+
21+
22+
def rules():
23+
return release_rules()

pants-plugins/release/rules.py

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Copyright 2023 The StackStorm Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Please see https://www.pantsbuild.org/docs/plugins-setup-py
17+
Based in part on Apache 2.0 licensed code from:
18+
https://github.com/pantsbuild/pants/blob/master/pants-plugins/internal_plugins/releases/register.py
19+
"""
20+
21+
from __future__ import annotations
22+
23+
import re
24+
25+
from pants.backend.python.goals.setup_py import SetupKwargs, SetupKwargsRequest
26+
from pants.engine.fs import DigestContents, GlobMatchErrorBehavior, PathGlobs
27+
from pants.engine.target import Target
28+
from pants.engine.rules import collect_rules, Get, MultiGet, rule, UnionRule
29+
from pants.util.frozendict import FrozenDict
30+
31+
32+
REQUIRED_KWARGS = (
33+
"description",
34+
# TODO: source the version from one place for the whole repo.
35+
"version_file", # version extracted from this
36+
)
37+
PROJECT_METADATA = dict(
38+
author="StackStorm",
39+
author_email="info@stackstorm.com",
40+
url="https://stackstorm.com",
41+
license="Apache License, Version 2.0",
42+
# dynamically added:
43+
# - version (from version_file)
44+
# - long_description (from README.rst if present)
45+
# - long_description_content_type (text/x-rst)
46+
)
47+
PROJECT_URLS = {
48+
# TODO: use more standard slugs for these
49+
"Pack Exchange": "https://exchange.stackstorm.org",
50+
"Repository": "https://github.com/StackStorm/st2",
51+
"Documentation": "https://docs.stackstorm.com",
52+
"Community": "https://stackstorm.com/community-signup",
53+
"Questions": "https://github.com/StackStorm/st2/discussions",
54+
"Donate": "https://funding.communitybridge.org/projects/stackstorm",
55+
"News/Blog": "https://stackstorm.com/blog",
56+
"Security": "https://docs.stackstorm.com/latest/security.html",
57+
"Bug Reports": "https://github.com/StackStorm/st2/issues",
58+
}
59+
META_CLASSIFIERS = (
60+
"Development Status :: 5 - Production/Stable",
61+
"Intended Audience :: Information Technology",
62+
"Intended Audience :: Developers",
63+
"Intended Audience :: System Administrators",
64+
"License :: OSI Approved :: Apache Software License",
65+
)
66+
LINUX_CLASSIFIER = "Operating System :: POSIX :: Linux"
67+
68+
69+
def python_classifiers(*versions: str) -> list[str]:
70+
classifiers = [
71+
"Programming Language :: Python",
72+
]
73+
for version in versions:
74+
classifiers.append(f"Programming Language :: Python :: {version}")
75+
return classifiers
76+
77+
78+
class StackStormSetupKwargsRequest(SetupKwargsRequest):
79+
@classmethod
80+
def is_applicable(cls, _: Target) -> bool:
81+
return True
82+
# if we need to separate runner wheels vs component wheels,
83+
# we could have different Requests for each type:
84+
# return target.address.spec.startswith("contrib/runners/")
85+
# return target.address.spec.startswith("st2")
86+
87+
88+
@rule
89+
async def setup_kwargs_plugin(request: StackStormSetupKwargsRequest) -> SetupKwargs:
90+
kwargs = request.explicit_kwargs.copy()
91+
92+
for required in REQUIRED_KWARGS:
93+
if required not in kwargs:
94+
raise ValueError(
95+
f"Missing a `{required}` kwarg in the `provides` field for {request.target.address}."
96+
)
97+
98+
version_file = kwargs.pop("version_file")
99+
100+
version_digest_contents, readme_digest_contents = await MultiGet(
101+
Get(
102+
DigestContents,
103+
PathGlobs(
104+
[f"{request.target.address.spec_path}/{version_file}"],
105+
description_of_origin=f"StackStorm version file: {version_file}",
106+
glob_match_error_behavior=GlobMatchErrorBehavior.error,
107+
),
108+
),
109+
Get(
110+
DigestContents,
111+
PathGlobs(
112+
[f"{request.target.address.spec_path}/README.rst"],
113+
glob_match_error_behavior=GlobMatchErrorBehavior.ignore,
114+
),
115+
),
116+
)
117+
118+
version_file_contents = version_digest_contents[0].content.decode()
119+
version_match = re.search(
120+
r"^__version__ = ['\"]([^'\"]*)['\"]", version_file_contents, re.M
121+
)
122+
if not version_match:
123+
raise ValueError(
124+
f"Could not find the __version__ in {request.target.address.spec_path}/{version_file}\n{version_file_contents}"
125+
)
126+
127+
# Hardcode certain kwargs and validate that they weren't already set.
128+
hardcoded_kwargs = PROJECT_METADATA.copy()
129+
hardcoded_kwargs["project_urls"] = FrozenDict(PROJECT_URLS)
130+
hardcoded_kwargs["version"] = version_match.group(1)
131+
132+
long_description = (
133+
readme_digest_contents[0].content.decode() if readme_digest_contents else ""
134+
)
135+
if long_description:
136+
hardcoded_kwargs["long_description_content_type"] = "text/x-rst"
137+
hardcoded_kwargs["long_description"] = long_description
138+
139+
conflicting_hardcoded_kwargs = set(kwargs.keys()).intersection(
140+
hardcoded_kwargs.keys()
141+
)
142+
if conflicting_hardcoded_kwargs:
143+
raise ValueError(
144+
f"These kwargs should not be set in the `provides` field for {request.target.address} "
145+
"because pants-plugins/release automatically sets them: "
146+
f"{sorted(conflicting_hardcoded_kwargs)}"
147+
)
148+
kwargs.update(hardcoded_kwargs)
149+
150+
# Add classifiers. We preserve any that were already set.
151+
kwargs["classifiers"] = (
152+
*META_CLASSIFIERS,
153+
LINUX_CLASSIFIER,
154+
# TODO: add these dynamically based on interpreter constraints
155+
*python_classifiers("3", "3.6", "3.8"),
156+
*kwargs.get("classifiers", []),
157+
)
158+
159+
return SetupKwargs(kwargs, address=request.target.address)
160+
161+
162+
def rules():
163+
return [
164+
*collect_rules(),
165+
UnionRule(SetupKwargsRequest, StackStormSetupKwargsRequest),
166+
]

0 commit comments

Comments
 (0)