Skip to content

Commit 0ecb7c2

Browse files
committed
Update to handle built dependencies #340
Signed-off-by: Chin Yeung Li <[email protected]>
1 parent dd9f0bf commit 0ecb7c2

File tree

1 file changed

+137
-21
lines changed

1 file changed

+137
-21
lines changed

etc/scripts/build_rpm_docker.py

Lines changed: 137 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616
1717
Once the RPM package is generated, you can install it using:
1818
19+
sudo rpm -i /path/to/<dejacode>.rpm
20+
OR
1921
sudo dnf install /path/to/<dejacode>.rpm
22+
OR
23+
sudo yum install /path/to/<dejacode>.rpm
2024
21-
(Replace the above path with the actual path to the generated RPM file.)
25+
Replace the above path with the actual path to the generated RPM file.
2226
"""
2327

2428
import os
@@ -38,6 +42,20 @@ def build_rpm_with_docker():
3842
pkg_name = project["name"]
3943
# Insert "python3-"" prefix that follows a common convention for Python RPMs
4044
rpm_name = f"python3-{pkg_name.lower()}"
45+
rpm_version = project["version"].replace("-dev", "~dev")
46+
47+
# Generate requirements for RPM - exclude packages installed from GitHub
48+
dependencies = project["dependencies"]
49+
50+
# Exclude packages that will be installed from GitHub URLs
51+
excluded_packages = {"django-rest-hooks", "django_notifications_patched"}
52+
53+
filtered_dependencies = [
54+
dep for dep in dependencies if not any(excluded in dep for excluded in excluded_packages)
55+
]
56+
57+
# Create a requirements.txt content for installation
58+
requirements_content = "\n".join(filtered_dependencies)
4159

4260
docker_cmd = [
4361
"docker",
@@ -51,8 +69,12 @@ def build_rpm_with_docker():
5169
"/bin/bash",
5270
"-c",
5371
f"""set -ex
54-
# Install All build dependencies
55-
dnf install -y rpm-build python3-devel python3-setuptools python3-wheel python3-build python3-toml
72+
# Install All build dependencies including development tools
73+
dnf install -y rpm-build python3-devel python3-setuptools python3-wheel \
74+
python3-build python3-pip python3-virtualenv curl gcc openldap-devel
75+
76+
# Clean up build directories to prevent recursive copying
77+
rm -rf build
5678
5779
# Build the wheel
5880
python3 -m build --wheel
@@ -65,58 +87,152 @@ def build_rpm_with_docker():
6587
fi
6688
WHEEL_FILENAME=$(basename "$WHEEL_FILE")
6789
68-
# Keep RPM version as is for sorting
69-
RPM_VERSION="{project["version"].replace("-dev", "~dev")}"
70-
7190
# Creates the standard directory structure required by rpmbuild
7291
mkdir -p dist/rpmbuild/{{BUILD,RPMS,SOURCES,SPECS,SRPMS}}
7392
mv "$WHEEL_FILE" dist/rpmbuild/SOURCES/
7493
94+
# Create requirements.txt in SOURCES
95+
cat > dist/rpmbuild/SOURCES/requirements.txt << 'REQ_EOF'
96+
{requirements_content}
97+
REQ_EOF
98+
7599
# Get the changelog date
76100
CHANGELOG_DATE=$(date '+%a %b %d %Y')
77101
78-
# Generate spec file with correct deps
102+
# Create source tarball with actual name
103+
tar czf dist/rpmbuild/SOURCES/{rpm_name}-{rpm_version}.tar.gz \\
104+
--transform "s,^,/{rpm_name}-{rpm_version}/," \\
105+
-C /workspace \\
106+
--exclude build --exclude=.git --exclude=dist --exclude=*.pyc --exclude=__pycache__ .
107+
108+
# Generate spec file with virtualenv approach - using actual values instead of variables
79109
cat > dist/rpmbuild/SPECS/{rpm_name}.spec << EOF
80110
Name: {rpm_name}
81-
Version: $RPM_VERSION
111+
Version: {rpm_version}
82112
Release: 1%{{?dist}}
83113
Summary: {
84114
project.get(
85115
"description",
86-
"Automate open source license complianceand ensure supply chain integrity",
116+
"Automate open source license compliance and ensure supply chain integrity",
87117
)
88118
}
89119
90120
License: {project.get("license", "AGPL-3.0-only")}
91121
URL: {
92122
project.get("urls", "").get("Homepage", "https://github.com/aboutcode-org/dejacode")
93123
}
94-
Source0: "$WHEEL_FILENAME"
124+
Source0: {rpm_name}-{rpm_version}.tar.gz
125+
Source1: requirements.txt
95126
96127
BuildArch: noarch
97-
BuildRequires: python3-devel python3-setuptools python3-wheel python3-build python3-toml
128+
BuildRequires: python3-devel python3-virtualenv gcc openldap-devel
129+
AutoReqProv: no
98130
99131
%description
100132
{
101133
project.get(
102134
"description",
103-
"Automate open source license compliance and ensuresupply chain integrity",
135+
"Automate open source license compliance and ensure supply chain integrity",
104136
)
105137
}
106138
107139
%prep
140+
%setup -q
141+
142+
%build
108143
109144
%install
110-
mkdir -p %{{buildroot}}%{{python3_sitelib}}
111-
# Use the actual filename for pip install, which %SOURCE0 resolves to
112-
pip install --no-deps --ignore-installed --root %{{buildroot}} --prefix %{{_prefix}} %{{SOURCE0}}
145+
# Create directories
146+
mkdir -p %{{buildroot}}/opt/%{{name}}/venv
147+
mkdir -p %{{buildroot}}/usr/bin
148+
149+
# Create virtual environment in a temporary location first
150+
mkdir -p /tmp/venv_build
151+
python3 -m venv /tmp/venv_build
152+
153+
# Upgrade pip in the temporary virtual environment
154+
/tmp/venv_build/bin/pip install --upgrade pip
155+
156+
# funcparserlib Patch/Install
157+
cd /tmp
158+
curl -L -o funcparserlib-0.3.6.tar.gz https://files.pythonhosted.org/packages/source/f/funcparserlib/funcparserlib-0.3.6.tar.gz
159+
tar -xzf funcparserlib-0.3.6.tar.gz
160+
cd funcparserlib-0.3.6
161+
162+
# rewrite setup.py to remove the "use_2to3" as this is not suported in Python3
163+
cat > setup.py << 'SETUP_EOF'
164+
from setuptools import setup
165+
166+
setup(
167+
name='funcparserlib',
168+
version='0.3.6',
169+
packages=['funcparserlib', 'funcparserlib.tests'],
170+
author='Andrey Vlasovskikh',
171+
author_email='[email protected]',
172+
description='Recursive descent parsing library based on functional '
173+
'combinators',
174+
license='MIT',
175+
url='http://code.google.com/p/funcparserlib/',
176+
)
177+
SETUP_EOF
178+
179+
# Install the patched funcparserlib
180+
/tmp/venv_build/bin/pip install .
181+
182+
# Clean up
183+
cd /tmp
184+
rm -rf funcparserlib-0.3.6 funcparserlib-0.3.6.tar.gz
185+
186+
# Install all other dependencies including the main package
187+
cd %{{_sourcedir}}
188+
/tmp/venv_build/bin/pip install -r requirements.txt
189+
190+
# Install non-PyPI dependencies
191+
/tmp/venv_build/bin/pip install \\
192+
https://github.com/aboutcode-org/django-rest-hooks/releases/download/1.6.1/django_rest_hooks-1.6.1-py2.py3-none-any.whl
193+
194+
/tmp/venv_build/bin/pip install \\
195+
https://github.com/dejacode/django-notifications-patched/archive/refs/tags/2.0.0.tar.gz
196+
197+
# Install the main package from the wheel
198+
/tmp/venv_build/bin/pip install --no-deps $WHEEL_FILENAME
199+
200+
# Copy the completed virtual environment to the final location
201+
cp -r /tmp/venv_build/* %{{buildroot}}/opt/%{{name}}/venv/
202+
203+
# Clean up temporary virtual environment
204+
rm -rf /tmp/venv_build
205+
206+
# Doing clean up to prevent the "Arch dependent binaries in noarch package" error.
207+
# Remove arch-dependent shared object files (*.so)
208+
find %{{buildroot}}/opt/%{{name}}/venv -name "*.so*" -type f -delete
209+
210+
# Remove any other potential binary files
211+
find %{{buildroot}}/opt/%{{name}}/venv -type f -exec file {{}} \\; | grep -i \
212+
"executable" | cut -d: -f1 | xargs -r rm -f
213+
214+
# Create wrapper script for dejacode command
215+
cat > %{{buildroot}}/usr/bin/dejacode << 'WRAPPER_EOF'
216+
#!/bin/sh
217+
export PYTHONPATH="/opt/{rpm_name}/venv/lib/python3.13/site-packages"
218+
exec /usr/bin/python3 -m dejacode "\\$@"
219+
WRAPPER_EOF
220+
chmod 755 %{{buildroot}}/usr/bin/dejacode
221+
222+
# Clean up any remaining build artifacts
223+
find %{{buildroot}}/opt/%{{name}}/venv -name "*.pyc" -delete
224+
find %{{buildroot}}/opt/%{{name}}/venv -name "__pycache__" -type d \
225+
-exec rm -rf {{}} + 2>/dev/null || true
226+
find %{{buildroot}}/opt/%{{name}}/venv -path "*/pip/_vendor/distlib/*.tmp" \
227+
-delete 2>/dev/null || true
113228
114229
%files
115-
%{{_bindir}}/dejacode
116-
%{{python3_sitelib}}/*
230+
%dir /opt/%{{name}}
231+
/opt/%{{name}}/venv
232+
/usr/bin/dejacode
117233
118234
%changelog
119-
* $CHANGELOG_DATE {project.get("authors", [{}])[0].get("name", "nexB Inc.")} - $RPM_VERSION-1
235+
* $CHANGELOG_DATE {project.get("authors", [{}])[0].get("name", "nexB Inc.")} - {rpm_version}-1
120236
- {
121237
project.get("urls", "").get(
122238
"Changelog", "https://github.com/aboutcode-org/dejacode/blob/main/CHANGELOG.rst"
@@ -125,16 +241,16 @@ def build_rpm_with_docker():
125241
EOF
126242
127243
# Build the RPM
128-
rpmbuild --define "_topdir /workspace/dist/rpmbuild" -bb dist/rpmbuild/SPECS/{rpm_name}.spec
244+
cd dist/rpmbuild && rpmbuild --define "_topdir $(pwd)" -bb SPECS/{rpm_name}.spec
129245
130246
# Fix permissions for Windows host
131-
chmod -R u+rwX dist/rpmbuild
247+
chmod -R u+rwX /workspace/dist/rpmbuild
132248
""",
133249
]
134250

135251
try:
136252
subprocess.run(docker_cmd, check=True, shell=False) # noqa: S603
137-
# Verify the existance of the .rpm
253+
# Verify the existence of the .rpm
138254
rpm_file = next(Path("dist/rpmbuild/RPMS/noarch").glob("*.rpm"), None)
139255
if rpm_file:
140256
print(f"\nSuccess! RPM built: {rpm_file}")

0 commit comments

Comments
 (0)