Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# SLE 15 source-build CI

`workflows/build.yml` runs on openSUSE Leap 15.6 with the distribution's
OpenSSL 3 development package. It does not download or cache another OpenSSL.
Python 3.11 is a build tool only; the tests run the freshly built Python 3.6.

The jobs cover:

* A shared debug build with broad regression tests under Xvfb.
* A shared release build with SUSE compiler flags and PGO, followed by broad
regression tests and a separate OBS-style test pass.
* ABI comparison against `Doc/data/python3.6m.abi`.
* Generated-file and exported-symbol checks.

`configure-suse.sh` records the configuration shared by the jobs. The release
configuration follows the x86_64 `SUSE_SLE-15-SP6_Update` builds of Python 3.6.15.
The debug job deliberately retains assertions and debug allocation checks.
The ABI job uses `-O0 -g3` for ABI inspection and does not use PGO.

`check-build.py` checks the interpreter version, ABI, debug/shared configuration,
compiler flags, optional modules, SQLite extension loading, and dynamic library
resolution. Both the OpenSSL runtime and extension linkage must use OpenSSL 3.
Dependency installation rejects OpenSSL 1.1 packages rather than allowing an
accidental fallback. Generated files are regenerated with the built Python 3.6.

Each job uploads the package inventory, repository URLs, configure output,
build output, build checks, Python information, and test output as artifacts.
Shell steps use Bash with Actions' `-e -o pipefail`, so piping output to `tee`
does not hide a failing build or test command.

## Differences from OBS

Leap repositories track updates; the container tag does not pin every package.
The runner supplies an Ubuntu kernel, not the SLE kernel used by OBS.
The test worker count is explicitly four, including the OBS-style pass.
Regression tests run as the unprivileged `abuild` user, as in OBS, rather than
as container root. Compilation still runs as root inside the disposable container.

Leap's GDB requires the legacy system Python 3.6 and OpenSSL 1.1 packages.
It is not installed, so `test_gdb` skips. Restoring that coverage requires a GDB
build using Python 3.11. The unused `lcov` dependency is omitted for the same
reason.

The broad pass enables all test resources except `cpu`, uses Xvfb, and does not
copy OBS's suite exclusions. The additional release-only parity pass enables
only the `curses` resource, sets the OBS virtual-memory limit and 3000-second
timeout, and excludes `test_gdb`, `test_pydoc`, `test_capi`, and `test_uuid`.
Unlike OBS, the container does not disable external networking for that pass.
PGO training is inherited from CPython's Makefile and is not a regression gate;
the later regression passes are the gates.

## RPM building is deferred

These jobs test the checked-out source, not the RPM package. They do not check
RPM file lists, package splitting, scriptlets, or dependency generation.

A later RPM build/install smoke test could use a simplified spec with one
flavour and no patches, since the branch already contains the patches. That
would test packaging the PR's source, but would not validate the actual SUSE
package splitting or metadata. RPM building is not required for this CI rollout.
75 changes: 75 additions & 0 deletions .github/check-build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Fail CI if the interpreter does not match the intended SUSE configuration.

Run with the freshly built Python, not the container's build-tool Python.
"""
import importlib
import json
import os
import re
import ssl
import subprocess
import sys
import sysconfig


def require(condition, message):
if not condition:
raise SystemExit(message)


def main():
mode = sys.argv[1]
require(mode in ('debug', 'release', 'abi'), 'Unknown build mode')
keys = ('CONFIG_ARGS', 'CFLAGS', 'OPT', 'LDFLAGS', 'ABIFLAGS',
'Py_DEBUG', 'Py_ENABLE_SHARED', 'WANT_SIGFPE_HANDLER', 'WITH_PYMALLOC')
config = {key: sysconfig.get_config_var(key) for key in keys}
print(json.dumps(config, indent=2, sort_keys=True))
print('OpenSSL runtime:', ssl.OPENSSL_VERSION)
require(ssl.OPENSSL_VERSION_INFO[0] == 3, 'Expected OpenSSL 3 at runtime')
require(sys.version_info[:3] == (3, 6, 15), 'Expected Python 3.6.15')
require(bool(config['Py_DEBUG']) == (mode == 'debug'), 'Wrong debug mode')
require(config['Py_ENABLE_SHARED'] == 1, 'Shared libpython is required')
require(config['WANT_SIGFPE_HANDLER'] == 1, 'fpectl support is required')
require(sys.abiflags == ('dm' if mode == 'debug' else 'm'), 'Wrong ABI flags')
args = config['CONFIG_ARGS']
for option in ('--with-system-expat', '--with-system-ffi',
'--enable-loadable-sqlite-extensions', '--without-lto'):
require(option in args, 'Missing configure option: ' + option)
if mode == 'release':
require('--enable-optimizations' in args, 'Release build must use PGO')
require('-O2' in config['OPT'], 'Release build must use -O2')
for flag in ('-D_FORTIFY_SOURCE=2', '-fstack-protector-strong',
'-fstack-clash-protection', '-DOPENSSL_LOAD_CONF',
'-fwrapv', '-fno-semantic-interposition'):
require(flag in config['OPT'], 'Missing compiler flag: ' + flag)

modules = ('_ssl', '_hashlib', '_ctypes', 'pyexpat', '_sqlite3',
'_bz2', '_lzma', 'zlib', '_curses', '_curses_panel',
'_dbm', '_gdbm', '_tkinter', 'readline', 'nis')
linked = {}
for name in modules:
module = importlib.import_module(name)
path = os.path.realpath(module.__file__)
print(name, path)
linked[name] = subprocess.check_output(['ldd', path],
universal_newlines=True)
print(linked[name])
require('not found' not in linked[name], 'Unresolved library: ' + name)
require(not re.search(r'lib(?:ssl|crypto)\.so\.(?:1\.|10\b)', linked[name]),
'Legacy OpenSSL linkage: ' + name)
for name, soname in (('_ssl', 'libssl.so.3'),
('_hashlib', 'libcrypto.so.3'),
('pyexpat', 'libexpat.so.'),
('_ctypes', 'libffi.so.')):
require(soname in linked[name], '{} must link to {}'.format(name, soname))

import sqlite3
connection = sqlite3.connect(':memory:')
connection.enable_load_extension(True)
connection.enable_load_extension(False)
connection.close()
print('Build configuration checks passed')


if __name__ == '__main__':
main()
27 changes: 27 additions & 0 deletions .github/configure-suse.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/sh
# Match the x86_64 SLE 15 SP6 build flags recorded in the OBS logs.
set -eu

mode=${1:?usage: configure-suse.sh debug|release|abi}
common='-fmessage-length=0 -grecord-gcc-switches -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection'
case "$mode" in
release) optimization='-O2 -g'; set -- --enable-optimizations ;;
debug) optimization='-Og -g'; set -- --with-pydebug ;;
abi) optimization='-O0 -g3'; set -- ;;
*) echo "Unknown build mode: $mode" >&2; exit 2 ;;
esac
CFLAGS="$common $optimization"
OPT="$CFLAGS -DOPENSSL_LOAD_CONF -fwrapv -fno-semantic-interposition"
export CFLAGS OPT
export PYTHON_FOR_REGEN=python3.11

./configure \
--host=x86_64-suse-linux-gnu --build=x86_64-suse-linux-gnu \
--prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin \
--sysconfdir=/etc --datadir=/usr/share --includedir=/usr/include \
--libdir=/usr/lib64 --libexecdir=/usr/lib --localstatedir=/var \
--sharedstatedir=/var/lib --mandir=/usr/share/man --infodir=/usr/share/info \
--docdir=/usr/share/doc/packages/python \
--enable-ipv6 --enable-shared --with-fpectl --with-ensurepip=no \
--with-system-ffi --with-system-expat --without-lto \
--enable-loadable-sqlite-extensions "$@"
22 changes: 16 additions & 6 deletions .github/posix-deps-zypp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ zypper --non-interactive refresh
# packages. Allow the solver to downgrade dependencies rather than cancel.
# Install build tools explicitly: build patterns pull in a full base system,
# including packages that conflict with the container's busybox replacements.
# Python 3.6 needs OpenSSL 1.1 headers rather than Leap's default OpenSSL 3.
# GDB needs the UTF-32 converters supplied by glibc-locale-base.
# This branch targets the distribution's OpenSSL 3, including its patches.
# Use Python 3.11 for build tooling rather than the OpenSSL 1.1-based Python 3.6.
# Leap's gdb and lcov depend on the legacy Python 3.6/OpenSSL 1.1 stack.
# Do not install them; test_gdb will skip until a Python 3.11-based GDB is available.
zypper --non-interactive install --auto-agree-with-licenses \
--allow-downgrade --no-recommends \
autoconf automake gcc gcc-c++ make patch pkg-config python3 \
binutils diffutils findutils gdb glibc-locale-base gzip libtool perl tar which \
autoconf automake gcc gcc-c++ gcc-PIE make patch pkg-config python311 \
binutils diffutils findutils glibc-locale glibc-locale-base gzip libtool perl tar which \
shadow util-linux \
libabigail-tools xorg-x11-server-Xvfb xvfb-run \
cantarell-fonts google-droid-fonts google-inconsolata-fonts dejavu-fonts \
libexpat-devel \
Expand All @@ -24,8 +27,15 @@ zypper --non-interactive install --auto-agree-with-licenses \
ncurses-devel \
readline-devel \
sqlite3-devel \
libopenssl-1_1-devel \
libopenssl-3-devel \
gdbm-devel \
tk-devel \
libuuid-devel \
lcov
libnsl-devel libtirpc-devel

# Fail rather than silently testing against a legacy library or header package.
if rpm -qa --qf '%{NAME}\n' | grep -E '^(libopenssl1_1|libopenssl-1_1.*|openssl-1_1.*)$'; then
echo 'OpenSSL 1.1 packages must not be present in the CI image' >&2
exit 1
fi
pkg-config --atleast-version=3.0 openssl
Loading
Loading