Files
OpenShell/openshell.spec
T
Polite_realism 36ee06f7b1 fix(rpm): build the Python SDK from older protoc and install every SDK module (#4439)
* fix(python): let proto stub generation skip .pyi for older protoc

Add a --no-pyi option to generate_python_proto.py for toolchains whose protoc predates --pyi_out, such as Fedora's grpcio-tools 1.48, and list python3-googleapis-common-protos and python3-httpx in the spec's Recommends and dist-info Requires-Dist, which the SDK needs to import. Default behavior is unchanged. Contributes to #3510.

Signed-off-by: politerealism <burdcat17@gmail.com>

* fix(rpm): install every SDK module and record the license accounting

The python3-openshell %install copied only __init__.py, sandbox.py and
_proto/, but the package imports .errors, .mutations and .providers and ships a
py.typed marker, so an installed RPM could not import openshell. Install the
missing files and add a %check that fails if any relative import in the
installed package has no matching installed module. Importing the package needs
grpcio, which the build environment lacks, so the check is static.

Set License to the minimal SPDX expression covering every crate linked into the
shipped binaries, keep the pure-Python subpackage at Apache-2.0, and note how
the non-crate content is covered.

Signed-off-by: politerealism <burdcat17@gmail.com>

---------

Signed-off-by: politerealism <burdcat17@gmail.com>
2026-10-14 18:52:32 +00:00

324 lines
14 KiB
RPMSpec

# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
%global crate openshell
%global openshell_version 0.0.37
%global openshell_cargo_version %{openshell_version}
# Python dist-info metadata intentionally follows the RPM Version. Dev build
# identity is represented by Release for RPM packages.
%global openshell_python_version %{openshell_version}
# Cargo/Rust builds with vendored deps do not produce debugsource listings
# in the format redhat-rpm-config expects (especially on EPEL).
%global debug_package %{nil}
Name: openshell
Version: %{openshell_version}
Release: 1.20260518180028805757.podman.toml.gateway.listener.11.g8c0cb7c8%{?dist}
Summary: Safe, sandboxed runtimes for autonomous AI agents
# OpenShell's own code is Apache-2.0. The binaries statically link third-party
# crates; this is the minimal SPDX expression covering every crate linked into
# openshell, openshell-gateway and openshell-prover (computed from `cargo tree`
# license metadata, treating "X OR MIT" as satisfied by MIT or Apache-2.0).
# The docs, man page sources, config templates and Python SDK sources carry
# Apache-2.0 SPDX headers or fall under the repository LICENSE. The unit file is
# generated from this spec. Re-check when Cargo.lock changes.
License: Apache-2.0 AND BSD-3-Clause AND CC0-1.0 AND CDLA-Permissive-2.0 AND ISC AND MIT AND Unicode-3.0 AND Zlib
URL: https://github.com/NVIDIA/OpenShell
Source0: openshell-%{openshell_version}.tar.gz
Source1: openshell-%{openshell_version}-vendor.tar.xz
ExclusiveArch: x86_64 aarch64
# Cargo metadata generation
BuildRequires: cargo
BuildRequires: cargo-rpm-macros >= 25
BuildRequires: systemd-rpm-macros
# Man page generation
BuildRequires: pandoc
# Python sub-package build dependencies
BuildRequires: python3-devel
# Runtime: container runtime for package-managed gateway sandboxes.
# The gateway auto-detects Podman when the package-managed service starts.
Recommends: podman
%description
OpenShell provides safe, sandboxed runtimes for autonomous AI agents.
It offers a CLI for managing gateway registrations, sandboxes, and providers with
policy-enforced egress routing, credential proxying, and privacy-aware
profile-backed model-provider access.
# --- Gateway sub-package ---
%package gateway
Summary: OpenShell gateway server with Podman sandbox driver
Requires: podman
Requires: openssl
Requires: %{name} = %{version}-%{release}
%description gateway
OpenShell gateway server providing the control-plane API for sandbox
lifecycle management. This package installs Podman-oriented defaults in
gateway TOML while leaving compute driver selection to gateway auto-detection
or explicit operator configuration.
# --- Standalone policy prover sub-package ---
%package prover
Summary: Standalone OpenShell policy boundary prover
%description prover
OpenShell policy prover for checking whether a local candidate policy stays
within an operator-supplied maximum without connecting to a gateway.
# --- Python SDK sub-package ---
%package -n python3-%{name}
Summary: OpenShell Python SDK for agent execution and management
# Pure Python; links none of the crates covered by the main License tag.
License: Apache-2.0
# Use Recommends instead of Requires because Fedora 43+ ships older
# versions of grpcio (1.48) and protobuf (3.19) than the SDK needs.
# Users on distros with older packages can install these via pip/uv.
Recommends: python3-cloudpickle >= 3.0
Recommends: python3-googleapis-common-protos >= 1.63
Recommends: python3-grpcio >= 1.60
Recommends: python3-httpx >= 0.27
Recommends: python3-protobuf >= 4.25
Recommends: %{name}
%description -n python3-%{name}
Python SDK for OpenShell providing programmatic access to sandbox
management, agent execution, and provider access via gRPC.
%prep
%autosetup -n %{name}-%{version}
# Extract vendored Cargo dependencies and configure offline build
tar xf %{SOURCE1}
%cargo_prep -v vendor
# Patch workspace version from placeholder to actual build identity.
sed -i 's/^version = "0.0.0"/version = "%{openshell_cargo_version}"/' Cargo.toml
grep -q 'version = "%{openshell_cargo_version}"' Cargo.toml || (echo "ERROR: Cargo.toml version patch failed" && exit 1)
%build
test -x "${OPENSHELL_PREBUILT_BINARIES_DIR}/openshell"
test -x "${OPENSHELL_PREBUILT_BINARIES_DIR}/openshell-gateway"
test -x "${OPENSHELL_PREBUILT_BINARIES_DIR}/openshell-prover"
# Generate vendored crate manifest and license metadata.
# cargo-vendor.txt is consumed by an RPM generator (from cargo-rpm-macros)
# to emit Provides: bundled(crate(...)) = version for every vendored dep.
%cargo_vendor_manifest
%{cargo_license_summary}
%{cargo_license} > LICENSE.dependencies
# Build man pages from markdown
pandoc -s -t man deploy/man/openshell.1.md -o openshell.1
pandoc -s -t man deploy/man/openshell-gateway.8.md -o openshell-gateway.8
%install
# --- CLI binary ---
install -Dpm 0755 "${OPENSHELL_PREBUILT_BINARIES_DIR}/%{name}" %{buildroot}%{_bindir}/%{name}
# --- Standalone policy prover ---
install -Dpm 0755 "${OPENSHELL_PREBUILT_BINARIES_DIR}/%{name}-prover" %{buildroot}%{_bindir}/%{name}-prover
# --- Gateway binary ---
install -Dpm 0755 "${OPENSHELL_PREBUILT_BINARIES_DIR}/%{name}-gateway" %{buildroot}%{_bindir}/%{name}-gateway
# --- Default gateway TOML config template ---
# Shipped as a read-only reference in %{_datadir}. The systemd unit seeds a
# user-level copy at ~/.config/openshell/gateway.toml on first start.
install -Dpm 0644 deploy/rpm/gateway.toml.default %{buildroot}%{_datadir}/%{name}-gateway/gateway.toml.default
install -Dpm 0644 deploy/rpm/gateway.toml.default.v1 %{buildroot}%{_datadir}/%{name}-gateway/gateway.toml.default.v1
install -Dpm 0755 deploy/rpm/migrate-gateway-config.sh %{buildroot}%{_libexecdir}/%{name}-gateway-migrate-config
# --- Gateway systemd user unit ---
# Installed to the systemd user unit directory so any user can run:
# systemctl --user enable --now openshell-gateway.service
install -d %{buildroot}%{_userunitdir}
cat > %{buildroot}%{_userunitdir}/%{name}-gateway.service << 'EOF'
[Unit]
Description=OpenShell Gateway (user)
Documentation=https://github.com/NVIDIA/OpenShell
After=podman.socket
Wants=podman.socket
[Service]
Type=exec
# On first start the unit seeds a default TOML config and generates PKI.
# Client certs are placed in ~/.config/openshell/gateways/openshell/mtls/ so
# the CLI discovers them automatically.
# See /usr/share/doc/openshell-gateway/ for details.
# Seed a default TOML config on first start. On upgrade, replace only the exact
# schema-v1 config previously seeded by this package; preserve edited files.
# %%E expands to $XDG_CONFIG_HOME (~/.config) in user units.
ExecStartPre=%{_libexecdir}/%{name}-gateway-migrate-config %%E/openshell/gateway.toml /usr/share/openshell-gateway/gateway.toml.default /usr/share/openshell-gateway/gateway.toml.default.v1
# Reject an invalid selected configuration before generating certificates or
# starting the gateway. The environment file below applies to every command.
ExecStartPre=/usr/bin/openshell-gateway config preflight
# Auto-generate PKI on first start if not present.
# The default local TLS dir uses %%h because %%S resolves differently across
# systemd user-manager versions. gateway.env may override this path.
Environment=OPENSHELL_LOCAL_TLS_DIR=%%h/.local/state/openshell/tls
ExecStartPre=/usr/bin/openshell-gateway generate-certs --output-dir ${OPENSHELL_LOCAL_TLS_DIR} --server-san host.openshell.internal
# gateway.env is honored for backward compatibility with pre-1415 installs.
# New installs use runtime defaults; create gateway.toml to override.
# See TROUBLESHOOTING.md for the env-to-TOML migration guide.
EnvironmentFile=-%%E/openshell/gateway.env
ExecStart=/usr/bin/openshell-gateway
StateDirectory=openshell
Restart=on-failure
RestartSec=5
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
PrivateTmp=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
[Install]
WantedBy=default.target
EOF
# --- Gateway documentation ---
install -d %{buildroot}%{_docdir}/%{name}-gateway
install -pm 0644 deploy/rpm/QUICKSTART.md %{buildroot}%{_docdir}/%{name}-gateway/QUICKSTART.md
install -pm 0644 deploy/rpm/CONFIGURATION.md %{buildroot}%{_docdir}/%{name}-gateway/CONFIGURATION.md
install -pm 0644 deploy/rpm/TROUBLESHOOTING.md %{buildroot}%{_docdir}/%{name}-gateway/TROUBLESHOOTING.md
# --- Man pages ---
install -Dpm 0644 openshell.1 %{buildroot}%{_mandir}/man1/openshell.1
install -Dpm 0644 openshell-gateway.8 %{buildroot}%{_mandir}/man8/openshell-gateway.8
# --- Python SDK ---
# Install Python SDK modules (test files are intentionally excluded)
install -d %{buildroot}%{python3_sitelib}/%{name}
install -d %{buildroot}%{python3_sitelib}/%{name}/_proto
install -pm 0644 python/%{name}/__init__.py %{buildroot}%{python3_sitelib}/%{name}/
install -pm 0644 python/%{name}/errors.py %{buildroot}%{python3_sitelib}/%{name}/
install -pm 0644 python/%{name}/mutations.py %{buildroot}%{python3_sitelib}/%{name}/
install -pm 0644 python/%{name}/providers.py %{buildroot}%{python3_sitelib}/%{name}/
install -pm 0644 python/%{name}/sandbox.py %{buildroot}%{python3_sitelib}/%{name}/
install -pm 0644 python/%{name}/py.typed %{buildroot}%{python3_sitelib}/%{name}/
install -pm 0644 python/%{name}/_proto/__init__.py %{buildroot}%{python3_sitelib}/%{name}/_proto/
install -pm 0644 python/%{name}/_proto/*.py %{buildroot}%{python3_sitelib}/%{name}/_proto/
# Create dist-info so importlib.metadata can resolve the package version
install -d %{buildroot}%{python3_sitelib}/%{name}-%{openshell_python_version}.dist-info
cat > %{buildroot}%{python3_sitelib}/%{name}-%{openshell_python_version}.dist-info/METADATA << EOF
Metadata-Version: 2.1
Name: %{name}
Version: %{openshell_python_version}
Summary: OpenShell Python SDK for agent execution and management
License: Apache-2.0
Requires-Python: >=3.12
Requires-Dist: cloudpickle>=3.0
Requires-Dist: googleapis-common-protos>=1.63
Requires-Dist: grpcio>=1.60
Requires-Dist: httpx>=0.27
Requires-Dist: protobuf>=4.25
EOF
# INSTALLER marker per PEP 376
echo "rpm" > %{buildroot}%{python3_sitelib}/%{name}-%{openshell_python_version}.dist-info/INSTALLER
# RECORD can be empty for RPM-managed installs
touch %{buildroot}%{python3_sitelib}/%{name}-%{openshell_python_version}.dist-info/RECORD
%check
# Smoke-test the CLI binary
%{buildroot}%{_bindir}/%{name} --version
# Smoke-test the standalone policy prover
%{buildroot}%{_bindir}/%{name}-prover --version
# Smoke-test the gateway binary
%{buildroot}%{_bindir}/%{name}-gateway --version
# Smoke-test the Python SDK version metadata via importlib.metadata.
# We query the dist-info directly rather than importing the package because
# the full import pulls in grpcio and other runtime deps not present in the
# build environment.
PYTHONPATH=%{buildroot}%{python3_sitelib} %{python3} -c "from importlib.metadata import version; v = version('openshell'); print(v); assert v == '%{openshell_python_version}', f'expected %{openshell_python_version}, got {v}'"
# Verify every module the installed SDK pulls in with a relative import was
# installed. Importing the package needs grpcio, which the build environment
# does not have, so check the imports statically.
%{python3} - %{buildroot}%{python3_sitelib}/%{name} <<'PY'
import ast, pathlib, sys
pkg = pathlib.Path(sys.argv[1])
missing = []
for source in pkg.glob("*.py"):
for node in ast.walk(ast.parse(source.read_text())):
if isinstance(node, ast.ImportFrom) and node.level == 1 and node.module:
base = pkg.joinpath(*node.module.split("."))
if not (base.with_suffix(".py").exists() or (base / "__init__.py").exists()):
missing.append(f"{source.name}: .{node.module}")
assert not missing, f"installed SDK imports modules that were not installed: {missing}"
PY
# Verify the RPM default TOML config template was installed.
# A missing template means first-start seeding silently falls back to the
# binary default of 127.0.0.1, which breaks Podman sandbox connectivity.
test -f %{buildroot}%{_datadir}/%{name}-gateway/gateway.toml.default
test -f %{buildroot}%{_datadir}/%{name}-gateway/gateway.toml.default.v1
test -x %{buildroot}%{_libexecdir}/%{name}-gateway-migrate-config
# Verify the systemd unit invokes exact-default migration before startup.
grep -q '%{name}-gateway-migrate-config' %{buildroot}%{_userunitdir}/%{name}-gateway.service
grep -q 'gateway.toml.default.v1' %{buildroot}%{_userunitdir}/%{name}-gateway.service
%post gateway
%systemd_user_post %{name}-gateway.service
%preun gateway
%systemd_user_preun %{name}-gateway.service
%postun gateway
%systemd_user_postun_with_restart %{name}-gateway.service
%files
%license LICENSE
%license LICENSE.dependencies
%license cargo-vendor.txt
%doc README.md
%{_bindir}/%{name}
%{_mandir}/man1/openshell.1*
%files prover
%license LICENSE
%license LICENSE.dependencies
%license cargo-vendor.txt
%{_bindir}/%{name}-prover
%files gateway
%license LICENSE
%license LICENSE.dependencies
%license cargo-vendor.txt
%doc %{_docdir}/%{name}-gateway/QUICKSTART.md
%doc %{_docdir}/%{name}-gateway/CONFIGURATION.md
%doc %{_docdir}/%{name}-gateway/TROUBLESHOOTING.md
%{_bindir}/%{name}-gateway
%{_userunitdir}/%{name}-gateway.service
%{_libexecdir}/%{name}-gateway-migrate-config
%{_datadir}/%{name}-gateway/gateway.toml.default
%{_datadir}/%{name}-gateway/gateway.toml.default.v1
%{_mandir}/man8/openshell-gateway.8*
%files -n python3-%{name}
%license LICENSE
%{python3_sitelib}/%{name}/
%{python3_sitelib}/%{name}-%{openshell_python_version}.dist-info/
%changelog
%autochangelog