Skip to content

Commit 01f235d

Browse files
author
Trong Nhan Mai
committed
feat: add reproducible central buildspec generation
Signed-off-by: Trong Nhan Mai <[email protected]>
1 parent 226ac79 commit 01f235d

File tree

6 files changed

+814
-0
lines changed

6 files changed

+814
-0
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Copyright (c) 2025 - 2025, Oracle and/or its affiliates. All rights reserved.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
3+
4+
"""This module contains the functions used for generating build specs from the Macaron database."""
5+
6+
import logging
7+
from collections.abc import Mapping
8+
from enum import Enum
9+
10+
from packageurl import PackageURL
11+
from sqlalchemy import create_engine
12+
from sqlalchemy.orm import Session
13+
14+
from macaron.build_spec_generator.build_command_patcher import PatchCommandBuildTool, PatchValueType
15+
from macaron.build_spec_generator.reproducible_central.reproducible_central import gen_reproducible_central_build_spec
16+
17+
logger: logging.Logger = logging.getLogger(__name__)
18+
19+
20+
class BuildSpecFormat(str, Enum):
21+
"""The build spec format that we supports."""
22+
23+
REPRODUCIBLE_CENTRAL = "rc-buildspec"
24+
25+
26+
CLI_COMMAND_PATCHES: dict[
27+
PatchCommandBuildTool,
28+
Mapping[str, PatchValueType | None],
29+
] = {
30+
PatchCommandBuildTool.MAVEN: {
31+
"goals": ["clean", "package"],
32+
"--batch-mode": False,
33+
"--quiet": False,
34+
"--no-transfer-progress": False,
35+
# Example pkg:maven/io.liftwizard/[email protected]
36+
# https://github.com/liftwizard/liftwizard/blob/
37+
# 4ea841ffc9335b22a28a7a19f9156e8ba5820027/.github/workflows/build-and-test.yml#L23
38+
"--threads": None,
39+
# For cases such as
40+
# pkg:maven/org.apache.isis.valuetypes/[email protected]
41+
"--version": False,
42+
"--define": {
43+
# pkg:maven/org.owasp/[email protected]
44+
# To remove "-Dgpg.passphrase=$MACARON_UNKNOWN"
45+
"gpg.passphrase": None,
46+
"skipTests": "true",
47+
"maven.test.skip": "true",
48+
"maven.site.skip": "true",
49+
"rat.skip": "true",
50+
"maven.javadoc.skip": "true",
51+
},
52+
},
53+
PatchCommandBuildTool.GRADLE: {
54+
"tasks": ["clean", "assemble"],
55+
"--console": "plain",
56+
"--exclude-task": ["test"],
57+
"--project-prop": {
58+
"skip.signing": "",
59+
"skipSigning": "",
60+
"gnupg.skip": "",
61+
},
62+
},
63+
}
64+
65+
66+
def gen_build_spec_str(
67+
purl: PackageURL,
68+
database_path: str,
69+
build_spec_format: BuildSpecFormat,
70+
) -> str | None:
71+
"""Return the content of a build spec file from a given PURL.
72+
73+
Parameters
74+
----------
75+
purl: PackageURL
76+
The package URL to generate build spec for.
77+
database_path: str
78+
The path to the Macaron database.
79+
build_spec_format: BuildSpecFormat
80+
The format of the final build spec content.
81+
82+
Returns
83+
-------
84+
str | None
85+
The build spec content as a string, or None if there is an error.
86+
"""
87+
db_engine = create_engine(f"sqlite+pysqlite:///{database_path}", echo=False)
88+
89+
with Session(db_engine) as session, session.begin():
90+
match build_spec_format:
91+
case BuildSpecFormat.REPRODUCIBLE_CENTRAL:
92+
return gen_reproducible_central_build_spec(
93+
purl=purl,
94+
session=session,
95+
patches=CLI_COMMAND_PATCHES,
96+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copyright (c) 2025 - 2025, Oracle and/or its affiliates. All rights reserved.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Copyright (c) 2025 - 2025, Oracle and/or its affiliates. All rights reserved.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
3+
4+
"""This module contains the representation of information needed for Reproducible Central Buildspec generation."""
5+
6+
import logging
7+
from collections.abc import Sequence
8+
from dataclasses import dataclass
9+
10+
from packageurl import PackageURL
11+
from sqlalchemy.orm import Session
12+
13+
from macaron.build_spec_generator.macaron_db_extractor import (
14+
GenericBuildCommandInfo,
15+
lookup_any_build_command,
16+
lookup_build_tools_check,
17+
lookup_latest_component_id,
18+
lookup_repository,
19+
)
20+
from macaron.database.table_definitions import Repository
21+
from macaron.errors import QueryMacaronDatabaseError
22+
from macaron.slsa_analyzer.checks.build_tool_check import BuildToolFacts
23+
24+
logger: logging.Logger = logging.getLogger(__name__)
25+
26+
27+
@dataclass
28+
class RcInternalBuildInfo:
29+
"""An internal representation of the information obtained from the database for a PURL.
30+
31+
This is only used for generating the Reproducible Central build spec.
32+
"""
33+
34+
purl: PackageURL
35+
repository: Repository
36+
generic_build_command_facts: Sequence[GenericBuildCommandInfo] | None
37+
latest_component_id: int
38+
build_tool_facts: Sequence[BuildToolFacts]
39+
40+
41+
def get_rc_internal_build_info(
42+
purl: PackageURL,
43+
session: Session,
44+
) -> RcInternalBuildInfo | None:
45+
"""Return an ``RcInternalBuildInfo`` instance that captures the build related information for a PackageURL.
46+
47+
Parameters
48+
----------
49+
purl: PackageURL
50+
The PackageURL to extract information about.
51+
session: Session
52+
The SQLAlchemy Session for the Macaron database.
53+
54+
Returns
55+
-------
56+
RcInternalBuildInfo | None
57+
An instance of ``RcInternalBuildInfo`` or None if there was an error.
58+
"""
59+
try:
60+
latest_component_id = lookup_latest_component_id(
61+
purl=purl,
62+
session=session,
63+
)
64+
except QueryMacaronDatabaseError as lookup_component_error:
65+
logger.error(
66+
"Unexpected result from querying latest component id for %s. Error: %s",
67+
purl.to_string(),
68+
lookup_component_error,
69+
)
70+
return None
71+
if not latest_component_id:
72+
logger.error(
73+
"Cannot find an analysis result for PackageURL %s in the database. "
74+
+ "Please check if an analysis for it exists in the database.",
75+
purl.to_string(),
76+
)
77+
return None
78+
logger.debug("Latest component ID: %d", latest_component_id)
79+
80+
try:
81+
build_tool_facts = lookup_build_tools_check(
82+
component_id=latest_component_id,
83+
session=session,
84+
)
85+
except QueryMacaronDatabaseError as lookup_build_tools_error:
86+
logger.error(
87+
"Unexpected result from querying build tools for %s. Error: %s",
88+
purl.to_string(),
89+
lookup_build_tools_error,
90+
)
91+
return None
92+
if not build_tool_facts:
93+
logger.error(
94+
"Cannot find any build tool for PackageURL %s in the database.",
95+
purl.to_string(),
96+
)
97+
return None
98+
logger.debug("Build tools discovered from the %s table: %s", BuildToolFacts.__tablename__, build_tool_facts)
99+
100+
try:
101+
lookup_component_repository = lookup_repository(latest_component_id, session)
102+
except QueryMacaronDatabaseError as lookup_repository_error:
103+
logger.error(
104+
"Unexpected result from querying repository information for %s. Error: %s",
105+
purl.to_string(),
106+
lookup_repository_error,
107+
)
108+
return None
109+
if not lookup_component_repository:
110+
logger.error(
111+
"Cannot find any repository information for %s in the database.",
112+
purl.to_string(),
113+
)
114+
return None
115+
116+
try:
117+
lookup_build_facts = lookup_any_build_command(latest_component_id, session)
118+
except QueryMacaronDatabaseError as lookup_build_command_error:
119+
logger.error(
120+
"Unexpected result from querying all build command information for %s. Error: %s",
121+
purl.to_string(),
122+
lookup_build_command_error,
123+
)
124+
return None
125+
126+
return RcInternalBuildInfo(
127+
purl=purl,
128+
repository=lookup_component_repository,
129+
latest_component_id=latest_component_id,
130+
build_tool_facts=build_tool_facts,
131+
generic_build_command_facts=lookup_build_facts,
132+
)

0 commit comments

Comments
 (0)