Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[0.7.x] Fix riscv64 runtime breakage #106

Merged
merged 4 commits into from
Mar 25, 2024
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
run_id: ${{ github.run_id }}
release_mirror_url: ${{ steps.run_dist.outputs.release_mirror_url }}
container:
image: ghcr.io/ruyisdk/ruyi-python-dist:20240311
image: ghcr.io/ruyisdk/ruyi-python-dist:20240325
options: --user root # https://github.com/actions/checkout/issues/1014
credentials:
username: ${{ github.actor }}
Expand All @@ -50,7 +50,7 @@ jobs:
- name: Cache deps and Nuitka output
uses: actions/cache@v4
with:
key: ${{ runner.os }}-tgt-${{ matrix.arch }}-r2
key: ${{ runner.os }}-tgt-${{ matrix.arch }}-r3
path: |
/ccache
/poetry-cache
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ruyi"
version = "0.7.0-beta.20240323"
version = "0.7.0-beta.20240325"
description = "Package manager for RuyiSDK"
keywords = ["ruyi", "ruyisdk"]
# license = { file = "LICENSE-Apache.txt" }
Expand Down
14 changes: 11 additions & 3 deletions ruyi/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@
import ruyi
from ruyi import log

# this must happen before pygit2 is imported
from ruyi.utils import ssl_patch
if hasattr(ruyi, "__compiled__") and ruyi.__compiled__.standalone:
# If we're running from a bundle, our bundled libssl may remember a
# different path for loading certificates than appropriate for the
# current system, in which case the pygit2 import will fail. To avoid
# this we have to patch ssl.get_default_verify_paths with additional
# logic.
#
# this must happen before pygit2 is imported
from ruyi.utils import ssl_patch

del ssl_patch

del ssl_patch
from ruyi.cli import init_debug_status, main
from ruyi.cli.nuitka import get_nuitka_self_exe

Expand Down
94 changes: 75 additions & 19 deletions ruyi/utils/ssl_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import ssl
import sys
from typing import NamedTuple

from .. import log

Expand All @@ -12,29 +13,38 @@
def get_system_ssl_default_verify_paths() -> ssl.DefaultVerifyPaths:
global _cached_paths

if _cached_paths is None:
_cached_paths = _get_system_ssl_default_verify_paths()
return _cached_paths


def _get_system_ssl_default_verify_paths() -> ssl.DefaultVerifyPaths:
orig_paths = _orig_get_default_verify_paths()
if sys.platform != "linux":
return orig_paths

if _cached_paths is not None:
return _cached_paths
result: ssl.DefaultVerifyPaths | None = None

# imitate the stdlib flow but with overridden data source
try:
parts = _query_linux_system_ssl_default_cert_paths()
if parts is None:
log.W("failed to probe system libcrypto")
else:
result = to_ssl_paths(parts)
except Exception as e:
log.D(f"cannot get system libssl default cert paths: {e}")
return orig_paths
log.D(f"cannot get system libcrypto default cert paths: {e}")

cafile = os.environ.get(parts[0], parts[1])
capath = os.environ.get(parts[2], parts[3])
if result is None:
log.D("falling back to probing hard-coded paths")
result = probe_fallback_verify_paths()

# must do "else None" like the stdlib, despite the type annotation being just "str"
result = ssl.DefaultVerifyPaths(
cafile if os.path.isfile(cafile) else None, # type: ignore[arg-type]
capath if os.path.isdir(capath) else None, # type: ignore[arg-type]
*parts,
)
if result is None:
# cannot proceed without certificates info (pygit2 initialization is
# bound to fail anyway)
log.F("cannot find the system libcrypto and/or TLS certificates")
log.I("TLS certificates and library are required for Ruyi to function")
raise SystemExit(1)

if result != orig_paths:
log.D(
Expand All @@ -43,10 +53,26 @@ def get_system_ssl_default_verify_paths() -> ssl.DefaultVerifyPaths:
log.D(f"bundled: {orig_paths}")
log.D(f" system: {result}")

_cached_paths = result
return result


def to_ssl_paths(parts: tuple[str, str, str, str]) -> ssl.DefaultVerifyPaths | None:
cafile = os.environ.get(parts[0], parts[1])
capath = os.environ.get(parts[2], parts[3])

is_cafile_present = os.path.isfile(cafile)
is_capath_present = os.path.isdir(capath)
if not is_cafile_present and not is_capath_present:
return None

# must do "else None" like the stdlib, despite the type annotation being just "str"
return ssl.DefaultVerifyPaths(
cafile if is_cafile_present else None, # type: ignore[arg-type]
capath if is_capath_present else None, # type: ignore[arg-type]
*parts,
)


def _decode_fsdefault_or_none(val: int | None) -> str:
if val is None:
return ""
Expand All @@ -60,7 +86,7 @@ def _decode_fsdefault_or_none(val: int | None) -> str:

def _query_linux_system_ssl_default_cert_paths(
soname: str | None = None,
) -> tuple[str, str, str, str]:
) -> tuple[str, str, str, str] | None:
if soname is None:
# check libcrypto instead of libssl, because if the system libssl is
# newer than the bundled one, the system libssl will depend on the
Expand All @@ -73,11 +99,7 @@ def _query_linux_system_ssl_default_cert_paths(
log.D(f"soname {soname} not working: {e}")
continue

# cannot proceed without certificates info (pygit2 initialization is
# bound to fail anyway)
log.F("cannot find the system libcrypto")
log.I("TLS certificates and library are required for Ruyi to function")
raise SystemExit(1)
return None

# dlopen-ing the bare soname will get us the system library
lib = ctypes.CDLL(soname)
Expand All @@ -102,4 +124,38 @@ def _query_linux_system_ssl_default_cert_paths(
return result


class WellKnownCALocation(NamedTuple):
cafile: str
capath: str


WELL_KNOWN_CA_LOCATIONS: list[WellKnownCALocation] = [
# Most others
WellKnownCALocation("/etc/ssl/cert.pem", "/etc/ssl/certs"),
# Debian-based distros
WellKnownCALocation("/usr/lib/ssl/cert.pem", "/usr/lib/ssl/certs"),
# RPM-based distros
WellKnownCALocation("/etc/pki/tls/cert.pem", "/etc/pki/tls/certs"),
]


def probe_fallback_verify_paths() -> ssl.DefaultVerifyPaths | None:
for loc in WELL_KNOWN_CA_LOCATIONS:
is_file_present = os.path.isfile(loc.cafile)
is_dir_present = os.path.isdir(loc.capath)
if not is_file_present and not is_dir_present:
continue

return ssl.DefaultVerifyPaths(
loc.cafile if is_file_present else None, # type: ignore[arg-type]
loc.capath if is_dir_present else None, # type: ignore[arg-type]
"SSL_CERT_FILE",
loc.cafile,
"SSL_CERT_DIR",
loc.capath,
)

return None


ssl.get_default_verify_paths = get_system_ssl_default_verify_paths
2 changes: 1 addition & 1 deletion scripts/_image_tag_base.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ image_tag_base() {

case "$arch" in
""|amd64|arm64|riscv64)
echo "ghcr.io/ruyisdk/ruyi-python-dist:20240311"
echo "ghcr.io/ruyisdk/ruyi-python-dist:20240325"
;;
*)
echo "error: unsupported arch $arch; supported are: amd64, arm64, riscv64" >&2
Expand Down
2 changes: 1 addition & 1 deletion scripts/dist-image/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
FROM --platform=linux/amd64 ubuntu:20.04 as build-amd64
FROM --platform=linux/arm64 ubuntu:20.04 as build-arm64
FROM --platform=linux/riscv64 debian:sid-20231030 as build-riscv64
FROM --platform=linux/riscv64 riscv64/ubuntu:22.04 as build-riscv64

FROM build-$TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion scripts/dist-image/prepare-distro.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ amd64|arm64)
exec ./prepare-distro.ubuntu2004.sh
;;
riscv64)
exec ./prepare-distro.debian.sh
exec ./prepare-distro.ubuntu2204-riscv64.sh
;;
esac

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true

# HTTPS needs ca-certificates to work
sed -i 's@http://deb\.debian\.org/@http://mirrors.huaweicloud.com/@g' /etc/apt/sources.list.d/debian.sources
# sed -i 's@http://archive\.ubuntu\.com/@http://mirrors.huaweicloud.com/@g' /etc/apt/sources.list

# Non-interactive configuration of tzdata
debconf-set-selections <<EOF
Expand Down