Skip to content

Commit 5e852fd

Browse files
authored
chore: improve build info extraction for Python packages (#1230)
Improve the extraction and handling of build information for Python packages for build spec generation. This update refines how metadata is gathered and fixes the default command for pure Python packages. Signed-off-by: behnazh-w <[email protected]>
1 parent b8d9476 commit 5e852fd

File tree

10 files changed

+276
-146
lines changed

10 files changed

+276
-146
lines changed

src/macaron/build_spec_generator/build_command_patcher.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,45 @@
2626

2727
PatchValueType = GradleOptionPatchValueType | MavenOptionPatchValueType
2828

29+
CLI_COMMAND_PATCHES: dict[
30+
PatchCommandBuildTool,
31+
Mapping[str, PatchValueType | None],
32+
] = {
33+
PatchCommandBuildTool.MAVEN: {
34+
"goals": ["clean", "package"],
35+
"--batch-mode": False,
36+
"--quiet": False,
37+
"--no-transfer-progress": False,
38+
# Example pkg:maven/io.liftwizard/[email protected]
39+
# https://github.com/liftwizard/liftwizard/blob/
40+
# 4ea841ffc9335b22a28a7a19f9156e8ba5820027/.github/workflows/build-and-test.yml#L23
41+
"--threads": None,
42+
# For cases such as
43+
# pkg:maven/org.apache.isis.valuetypes/[email protected]
44+
"--version": False,
45+
"--define": {
46+
# pkg:maven/org.owasp/[email protected]
47+
# To remove "-Dgpg.passphrase=$MACARON_UNKNOWN"
48+
"gpg.passphrase": None,
49+
"skipTests": "true",
50+
"maven.test.skip": "true",
51+
"maven.site.skip": "true",
52+
"rat.skip": "true",
53+
"maven.javadoc.skip": "true",
54+
},
55+
},
56+
PatchCommandBuildTool.GRADLE: {
57+
"tasks": ["clean", "assemble"],
58+
"--console": "plain",
59+
"--exclude-task": ["test"],
60+
"--project-prop": {
61+
"skip.signing": "",
62+
"skipSigning": "",
63+
"gnupg.skip": "",
64+
},
65+
},
66+
}
67+
2968

3069
def _patch_commands(
3170
cmds_sequence: Sequence[list[str]],

src/macaron/build_spec_generator/build_spec_generator.py

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@
66
import json
77
import logging
88
import os
9-
from collections.abc import Mapping
109
from enum import Enum
1110

1211
from packageurl import PackageURL
1312
from sqlalchemy import create_engine
1413
from sqlalchemy.orm import Session
1514

16-
from macaron.build_spec_generator.build_command_patcher import PatchCommandBuildTool, PatchValueType
1715
from macaron.build_spec_generator.common_spec.core import gen_generic_build_spec
1816
from macaron.build_spec_generator.reproducible_central.reproducible_central import gen_reproducible_central_build_spec
1917
from macaron.console import access_handler
@@ -31,46 +29,6 @@ class BuildSpecFormat(str, Enum):
3129
DEFAULT = "default-buildspec"
3230

3331

34-
CLI_COMMAND_PATCHES: dict[
35-
PatchCommandBuildTool,
36-
Mapping[str, PatchValueType | None],
37-
] = {
38-
PatchCommandBuildTool.MAVEN: {
39-
"goals": ["clean", "package"],
40-
"--batch-mode": False,
41-
"--quiet": False,
42-
"--no-transfer-progress": False,
43-
# Example pkg:maven/io.liftwizard/[email protected]
44-
# https://github.com/liftwizard/liftwizard/blob/
45-
# 4ea841ffc9335b22a28a7a19f9156e8ba5820027/.github/workflows/build-and-test.yml#L23
46-
"--threads": None,
47-
# For cases such as
48-
# pkg:maven/org.apache.isis.valuetypes/[email protected]
49-
"--version": False,
50-
"--define": {
51-
# pkg:maven/org.owasp/[email protected]
52-
# To remove "-Dgpg.passphrase=$MACARON_UNKNOWN"
53-
"gpg.passphrase": None,
54-
"skipTests": "true",
55-
"maven.test.skip": "true",
56-
"maven.site.skip": "true",
57-
"rat.skip": "true",
58-
"maven.javadoc.skip": "true",
59-
},
60-
},
61-
PatchCommandBuildTool.GRADLE: {
62-
"tasks": ["clean", "assemble"],
63-
"--console": "plain",
64-
"--exclude-task": ["test"],
65-
"--project-prop": {
66-
"skip.signing": "",
67-
"skipSigning": "",
68-
"gnupg.skip": "",
69-
},
70-
},
71-
}
72-
73-
7432
def gen_build_spec_for_purl(
7533
purl: PackageURL,
7634
database_path: str,
@@ -113,7 +71,7 @@ def gen_build_spec_for_purl(
11371

11472
with Session(db_engine) as session, session.begin():
11573
try:
116-
build_spec = gen_generic_build_spec(purl=purl, session=session, patches=CLI_COMMAND_PATCHES)
74+
build_spec = gen_generic_build_spec(purl=purl, session=session)
11775
except GenerateBuildSpecError as error:
11876
logger.error("Error while generating the build spec: %s.", error)
11977
return os.EX_DATAERR
@@ -128,7 +86,7 @@ def gen_build_spec_for_purl(
12886
# Default build spec.
12987
case BuildSpecFormat.DEFAULT:
13088
try:
131-
build_spec_content = json.dumps(build_spec)
89+
build_spec_content = json.dumps(build_spec, indent=4)
13290
except ValueError as error:
13391
logger.error("Error while serializing the build spec: %s.", error)
13492
return os.EX_DATAERR

src/macaron/build_spec_generator/common_spec/base_spec.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,26 @@ def resolve_fields(self, purl: PackageURL) -> None:
9292
This method should be implemented by subclasses to handle
9393
logic specific to a given package ecosystem, such as Maven or PyPI.
9494
"""
95+
96+
@abstractmethod
97+
def get_default_build_command(
98+
self,
99+
build_tool_name: str,
100+
) -> list[str]:
101+
"""Return a default build command for the build tool.
102+
103+
Parameters
104+
----------
105+
build_tool_name: str
106+
The build tool to get the default build command.
107+
108+
Returns
109+
-------
110+
list[str]
111+
The build command as a list[str].
112+
113+
Raises
114+
------
115+
GenerateBuildSpecError
116+
If there is no default build command available for the specified build tool.
117+
"""

src/macaron/build_spec_generator/common_spec/core.py

Lines changed: 3 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66
import logging
77
import pprint
88
import shlex
9-
from collections.abc import Mapping, Sequence
9+
from collections.abc import Sequence
1010
from enum import Enum
1111
from importlib import metadata as importlib_metadata
12-
from pprint import pformat
1312

1413
import sqlalchemy.orm
1514
from packageurl import PackageURL
1615

17-
from macaron.build_spec_generator.build_command_patcher import PatchCommandBuildTool, PatchValueType, patch_commands
1816
from macaron.build_spec_generator.common_spec.base_spec import BaseBuildSpecDict
1917
from macaron.build_spec_generator.common_spec.maven_spec import MavenBuildSpec
2018
from macaron.build_spec_generator.common_spec.pypi_spec import PyPIBuildSpec
@@ -119,45 +117,6 @@ def compose_shell_commands(cmds_sequence: list[list[str]]) -> str:
119117
return result
120118

121119

122-
def get_default_build_command(
123-
build_tool_name: MacaronBuildToolName,
124-
) -> list[str] | None:
125-
"""Return a default build command for the build tool.
126-
127-
Parameters
128-
----------
129-
build_tool_name: MacaronBuildToolName
130-
The type of build tool to get the default build command.
131-
132-
Returns
133-
-------
134-
list[str] | None
135-
The build command as a list[str] or None if we cannot get one for this tool.
136-
"""
137-
default_build_command = None
138-
139-
match build_tool_name:
140-
case MacaronBuildToolName.MAVEN:
141-
default_build_command = "mvn clean package".split()
142-
case MacaronBuildToolName.GRADLE:
143-
default_build_command = "./gradlew clean assemble publishToMavenLocal".split()
144-
case MacaronBuildToolName.PIP:
145-
default_build_command = "python -m build".split()
146-
case MacaronBuildToolName.POETRY:
147-
default_build_command = "poetry build".split()
148-
case _:
149-
pass
150-
151-
if not default_build_command:
152-
logger.critical(
153-
"There is no default build command available for the build tool %s.",
154-
build_tool_name,
155-
)
156-
return None
157-
158-
return default_build_command
159-
160-
161120
def get_macaron_build_tool_name(
162121
build_tool_facts: Sequence[BuildToolFacts], target_language: str
163122
) -> MacaronBuildToolName | None:
@@ -322,10 +281,6 @@ def get_language_version(
322281
def gen_generic_build_spec(
323282
purl: PackageURL,
324283
session: sqlalchemy.orm.Session,
325-
patches: Mapping[
326-
PatchCommandBuildTool,
327-
Mapping[str, PatchValueType | None],
328-
],
329284
) -> BaseBuildSpecDict:
330285
"""
331286
Generate and return the Buildspec file.
@@ -336,9 +291,6 @@ def gen_generic_build_spec(
336291
The PackageURL to generate build spec for.
337292
session : sqlalchemy.orm.Session
338293
The SQLAlchemy Session opened for the database to extract build information.
339-
patches : Mapping[PatchCommandBuildTool, Mapping[str, PatchValueType | None]]
340-
The patches to apply to the build commands in ``build_info`` before being populated in
341-
the output Buildspec.
342294
343295
Returns
344296
-------
@@ -360,12 +312,6 @@ def gen_generic_build_spec(
360312
f"PURL type '{purl.type}' is not supported. Supported: {[e.name.lower() for e in ECOSYSTEMS]}"
361313
)
362314

363-
logger.debug(
364-
"Generating build spec for %s with command patches:\n%s",
365-
purl,
366-
pformat(patches),
367-
)
368-
369315
target_language = LANGUAGES[purl.type.upper()].value
370316
group = purl.namespace
371317
artifact = purl.name
@@ -414,22 +360,7 @@ def gen_generic_build_spec(
414360
build_command_info or "Cannot find any.",
415361
)
416362

417-
selected_build_command = (
418-
build_command_info.command
419-
if build_command_info
420-
else get_default_build_command(
421-
build_tool_name,
422-
)
423-
)
424-
if not selected_build_command:
425-
raise GenerateBuildSpecError(f"Failed to get a build command for {purl}.")
426-
427-
patched_build_commands = patch_commands(
428-
cmds_sequence=[selected_build_command],
429-
patches=patches,
430-
)
431-
if not patched_build_commands:
432-
raise GenerateBuildSpecError(f"Failed to patch command sequences {selected_build_command}.")
363+
selected_build_command = build_command_info.command if build_command_info else []
433364

434365
lang_version = get_language_version(build_command_info) if build_command_info else ""
435366

@@ -447,7 +378,7 @@ def gen_generic_build_spec(
447378
"purl": str(purl),
448379
"language": target_language,
449380
"build_tool": build_tool_name.value,
450-
"build_commands": patched_build_commands,
381+
"build_commands": [selected_build_command],
451382
}
452383
)
453384
ECOSYSTEMS[purl.type.upper()].value(base_build_spec_dict).resolve_fields(purl)

src/macaron/build_spec_generator/common_spec/maven_spec.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
from packageurl import PackageURL
1010

11+
from macaron.build_spec_generator.build_command_patcher import CLI_COMMAND_PATCHES, patch_commands
1112
from macaron.build_spec_generator.common_spec.base_spec import BaseBuildSpec, BaseBuildSpecDict
1213
from macaron.build_spec_generator.common_spec.jdk_finder import find_jdk_version_from_central_maven_repo
1314
from macaron.build_spec_generator.common_spec.jdk_version_normalizer import normalize_jdk_version
15+
from macaron.errors import GenerateBuildSpecError
1416

1517
logger: logging.Logger = logging.getLogger(__name__)
1618

@@ -29,6 +31,46 @@ def __init__(self, data: BaseBuildSpecDict):
2931
"""
3032
self.data = data
3133

34+
def get_default_build_command(
35+
self,
36+
build_tool_name: str,
37+
) -> list[str]:
38+
"""Return a default build command for the build tool.
39+
40+
Parameters
41+
----------
42+
build_tool_name: str
43+
The build tool to get the default build command.
44+
45+
Returns
46+
-------
47+
list[str]
48+
The build command as a list[str].
49+
50+
Raises
51+
------
52+
GenerateBuildSpecError
53+
If there is no default build command available for the specified build tool.
54+
"""
55+
default_build_command = None
56+
57+
match build_tool_name:
58+
case "maven":
59+
default_build_command = "mvn clean package".split()
60+
case "gradle":
61+
default_build_command = "./gradlew clean assemble publishToMavenLocal".split()
62+
case _:
63+
pass
64+
65+
if not default_build_command:
66+
logger.critical(
67+
"There is no default build command available for the build tool %s.",
68+
build_tool_name,
69+
)
70+
raise GenerateBuildSpecError("Unable to find a default build command.")
71+
72+
return default_build_command
73+
3274
def resolve_fields(self, purl: PackageURL) -> None:
3375
"""
3476
Resolve Maven-specific fields in the build specification.
@@ -69,3 +111,17 @@ def resolve_fields(self, purl: PackageURL) -> None:
69111
return
70112

71113
self.data["language_version"] = [major_jdk_version]
114+
115+
# Resolve and patch build commands.
116+
selected_build_commands = self.data["build_commands"] or [
117+
self.get_default_build_command(self.data["build_tool"])
118+
]
119+
120+
patched_build_commands = patch_commands(
121+
cmds_sequence=selected_build_commands,
122+
patches=CLI_COMMAND_PATCHES,
123+
)
124+
if not patched_build_commands:
125+
raise GenerateBuildSpecError(f"Failed to patch command sequences {selected_build_commands}.")
126+
127+
self.data["build_commands"] = patched_build_commands

0 commit comments

Comments
 (0)