Skip to content

Commit c57f59d

Browse files
authored
Packaging: Add pex_binary BUILD metadata for building st2 venv (#6307)
2 parents bf22cf1 + 8a81662 commit c57f59d

File tree

4 files changed

+199
-1
lines changed

4 files changed

+199
-1
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Added
7878
working on StackStorm, improve our security posture, and improve CI reliability thanks in part
7979
to pants' use of PEX lockfiles. This is not a user-facing addition.
8080
#6118 #6141 #6133 #6120 #6181 #6183 #6200 #6237 #6229 #6240 #6241 #6244 #6251 #6253
81-
#6254 #6258 #6259 #6260 #6269 #6275 #6279 #6278 #6282 #6283 #6273 #6287 #6306
81+
#6254 #6258 #6259 #6260 #6269 #6275 #6279 #6278 #6282 #6283 #6273 #6287 #6306 #6307
8282
Contributed by @cognifloyd
8383
* Build of ST2 EL9 packages #6153
8484
Contributed by @amanda11

packaging/BUILD.venv

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# rules on what packaging can depend on
2+
__dependencies_rules__(
3+
(
4+
# All python sources in this directory
5+
"<python_*>[/*]",
6+
(
7+
# may depend on 3rd party dependencies,
8+
"//reqs#*",
9+
# and on anything in this diretory,
10+
"/**",
11+
# but nothing else (eg not st2common, st2*, runners, ...).
12+
"!*",
13+
),
14+
),
15+
# other targets (not python in this directory) may depend on anything.
16+
("*", "*"),
17+
)
18+
19+
python_sources()
20+
21+
# We use st2-py*.pex to quickly build a venv (like /opt/stackstorm/st2)
22+
# that includes all requirements and our wheels.
23+
24+
25+
def _pex_py3(minor: str, constraint: str = ""):
26+
if not constraint:
27+
constraint = f"CPython==3.{minor}.*"
28+
return parametrize(
29+
f"py3{minor}",
30+
output_path=f"${{spec_path_normalized}}/st2-py3{minor}.pex",
31+
interpreter_constraints=[constraint],
32+
)
33+
34+
35+
pex_binary(
36+
name="st2.pex",
37+
dependencies=[
38+
# this should depend on all python_distribution targets
39+
"//st2actions",
40+
"//st2api",
41+
"//st2auth",
42+
"//st2client",
43+
"//st2common",
44+
"//st2reactor",
45+
"//st2stream",
46+
"//st2tests",
47+
"//contrib/runners/action_chain_runner",
48+
"//contrib/runners/announcement_runner",
49+
"//contrib/runners/http_runner",
50+
"//contrib/runners/inquirer_runner",
51+
"//contrib/runners/local_runner",
52+
"//contrib/runners/noop_runner",
53+
"//contrib/runners/orquesta_runner",
54+
"//contrib/runners/python_runner",
55+
"//contrib/runners/remote_runner",
56+
"//contrib/runners/winrm_runner",
57+
],
58+
executable="build_st2_venv.py", # included by dependency inferrence
59+
execution_mode="venv",
60+
layout="zipapp", # zipapp creates a single file, loose and packed create directories
61+
sh_boot=True, # faster startup time (only relevant for unpacking the pex)
62+
include_tools=True, # include pex.tools to populate a venv from the pex
63+
# TODO: To improve docker layer caching, we could break this into 2 pexes
64+
# one w/ include_requirements=False and the other w/ include_requirements=True.
65+
include_requirements=True, # include third party requirements
66+
include_sources=False, # already includes our wheels, skipping wheel-owned sources
67+
venv_hermetic_scripts=False, # do not add -sE to script shebangs
68+
# 1 parametrize group per python minor version in [DEFAULT].st2_interpreter_constraints in pants.toml
69+
**_pex_py3("8", constraint="CPython>=3.8.1,<3.9"),
70+
**_pex_py3("9"),
71+
**_pex_py3("10"),
72+
**_pex_py3("11"),
73+
)

packaging/build_st2_venv.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Copyright 2025 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+
# NOTE: In this script, all 3rd party deps are available thanks to pex.
16+
# Do not import any st2 code to avoid polluting the pex with extra files
17+
# (Pants uses dependency inference to add sources beyond our wheels).
18+
19+
import os
20+
import sys
21+
import subprocess
22+
23+
from pathlib import Path
24+
from typing import List, Optional
25+
26+
from oslo_config import cfg
27+
28+
29+
def get_pex_path() -> str:
30+
return os.environ.get("PEX", sys.argv[0])
31+
32+
33+
def get_st2_base_path(args: Optional[List[str]] = None) -> Path:
34+
st2_config_path = (
35+
os.environ.get("ST2_CONFIG_PATH", os.environ.get("ST2_CONF"))
36+
or "/etc/st2/st2.conf"
37+
)
38+
39+
cfg.CONF.register_opts(
40+
[cfg.StrOpt("base_path", default="/opt/stackstorm")], group="system"
41+
)
42+
43+
try:
44+
cfg.CONF(args=args, default_config_files=[st2_config_path], use_env=False)
45+
except cfg.ConfigFilesNotFoundError:
46+
pass
47+
48+
st2_base_path = os.environ.get(
49+
"ST2_SYSTEM__BASE_PATH", cfg.CONF["system"]["base_path"]
50+
)
51+
return Path(st2_base_path)
52+
53+
54+
def unpack_venv(st2_venv_path: Path) -> int:
55+
if st2_venv_path.exists():
56+
print(f"WARNING: This will overwrite {st2_venv_path}", file=sys.stderr)
57+
58+
env = {"PEX_TOOLS": "1"}
59+
cmd = [
60+
get_pex_path(),
61+
"venv",
62+
"--force", # remove and replace the venv if it exists
63+
"--non-hermetic-scripts", # do not add -sE to python shebang
64+
# st2-packages has a note about python symlinks breaking pack install.
65+
# uncomment this if that proves to still be an issue.
66+
# "--copies", # pack install follows python symlinks to find bin dir
67+
"--system-site-packages",
68+
"--compile", # pre-compile all pyc files
69+
"--prompt=st2",
70+
str(st2_venv_path),
71+
]
72+
pretty_cmd = "".join(k + "=" + v + " " for k, v in env.items()) + " ".join(cmd)
73+
print(f"Now running: {pretty_cmd}", file=sys.stderr)
74+
75+
result = subprocess.call(cmd, env=env)
76+
77+
if result == 0:
78+
print(f"Successfully unpacked venv to {st2_venv_path}", file=sys.stderr)
79+
else:
80+
print(
81+
f"Encountered an error unpacking venv to {st2_venv_path}", file=sys.stderr
82+
)
83+
84+
return result
85+
86+
87+
def tidy_venv(st2_venv_path: Path) -> None:
88+
"""Clean up and remove this script from the venv.
89+
90+
Unfortunately, the way pants uses pex, this script ends up in the venv.
91+
"""
92+
for path in (st2_venv_path / "lib").glob("python*"):
93+
script_path = path / "site-packages" / "packaging" / "build_st2_venv.py"
94+
if script_path.exists():
95+
script_path.unlink()
96+
97+
script_path = path / "site-packages" / "__pex_executable__.py"
98+
if script_path.exists():
99+
script_path.unlink()
100+
101+
# and remove the reference to this script
102+
main_path = st2_venv_path / "__main__.py"
103+
main_path.write_text(main_path.read_text().replace("__pex_executable__", ""))
104+
105+
106+
def main() -> int:
107+
st2_base_path = get_st2_base_path(sys.argv[1:])
108+
st2_venv_path = st2_base_path / "st2"
109+
110+
if not os.access(st2_base_path, os.W_OK):
111+
print(
112+
f"ERROR: venv parent directory is not writable: {st2_base_path}",
113+
file=sys.stderr,
114+
)
115+
return 1
116+
117+
venv_result = unpack_venv(st2_venv_path)
118+
tidy_venv(st2_venv_path)
119+
120+
return venv_result
121+
122+
123+
if __name__ == "__main__":
124+
sys.exit(main())

pants.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ root_patterns = [
106106
# DEFAULT has values that we can reuse/interpolate below
107107
[DEFAULT]
108108
# This is the range of python versions that we support.
109+
# On update, make sure to also update parametrizations in packaging/BUILD*.
109110
st2_interpreter_constraints = "CPython>=3.8.1,<3.12"
110111

111112
# This should match the pants interpreter_constraints:

0 commit comments

Comments
 (0)