refactor: update wheel and APK handling in build scripts by replacing glob patterns with array expansions and find commands.

This commit is contained in:
Ivan 2026-07-24 06:19:27 -05:00
parent be4fc384af
commit f46e70249c
No known key found for this signature in database
32 changed files with 64 additions and 59 deletions

Binary file not shown.

View file

@ -23,7 +23,7 @@ fi
for abi in ${ABI_LIST//,/ }; do
tag="$(abi_to_tag "${abi}")"
lib_wheel="$(ls "${VENDOR_DIR}"/chaquopy_libcodec2-*-android_*_"${tag}".whl 2>/dev/null | tail -n 1 || true)"
lib_wheel="$(find "${VENDOR_DIR}" -maxdepth 1 -type f -name "chaquopy_libcodec2-*-android_*_${tag}.whl" 2>/dev/null | sort | tail -n 1 || true)"
if [[ -z "${lib_wheel}" ]]; then
echo "No chaquopy_libcodec2 wheel for ${abi} under ${VENDOR_DIR}" >&2
exit 1

View file

@ -320,14 +320,14 @@ VENV_DIR="${PYPIDIR}/.venv-local"
rm -rf "${VENV_DIR}"
"${PYTHON_BIN}" -m venv "${VENV_DIR}"
# Some Python images only provide python3/python3.X in venv bin, while the
# script below invokes `${VENV_DIR}/bin/python`.
# script below invokes the venv bin/python entrypoint.
if [[ ! -e "${VENV_DIR}/bin/python" && -e "${VENV_DIR}/bin/python3" ]]; then
ln -sf python3 "${VENV_DIR}/bin/python"
fi
"${VENV_DIR}/bin/pip" install --upgrade pip
"${VENV_DIR}/bin/pip" install -r "${PYPIDIR}/requirements.txt"
"${VENV_DIR}/bin/pip" install "numpy==${NUMPY_VERSION}"
# Chaquopy build-wheel.py shells out to `wheel pack`, so ensure the venv scripts are first on PATH.
# Chaquopy build-wheel.py shells out to wheel pack, so ensure the venv scripts are first on PATH.
export PATH="${VENV_DIR}/bin:${PATH}"
if ! command -v wheel >/dev/null 2>&1; then
echo "Missing required wheel CLI in virtualenv at ${VENV_DIR}" >&2
@ -677,8 +677,12 @@ done
popd >/dev/null
mkdir -p "${OUT_DIR}"
cp -f "${PYPIDIR}/dist/chaquopy-libcodec2"/chaquopy_libcodec2-"${LIBCODEC2_VERSION}"-*.whl "${OUT_DIR}/"
cp -f "${PYPIDIR}/dist/pycodec2"/pycodec2-"${PYCODEC2_VERSION}"-*.whl "${OUT_DIR}/"
shopt -s nullglob
_codec2_wheels=("${PYPIDIR}/dist/chaquopy-libcodec2/chaquopy_libcodec2-${LIBCODEC2_VERSION}-"*.whl)
_pycodec2_wheels=("${PYPIDIR}/dist/pycodec2/pycodec2-${PYCODEC2_VERSION}-"*.whl)
shopt -u nullglob
cp -f "${_codec2_wheels[@]}" "${OUT_DIR}/"
cp -f "${_pycodec2_wheels[@]}" "${OUT_DIR}/"
echo "Bundling libcodec2.so into pycodec2 wheels (Android dlopen)"
"${VENV_DIR}/bin/python" "${ROOT_DIR}/scripts/repack-android-pycodec2-wheels.py" --vendor-dir "${OUT_DIR}"
@ -1080,12 +1084,14 @@ PY
--abi "${abi}" \
"${RECIPE_DST}"
WHEEL_GLOB="${PYPIDIR}/dist/${PACKAGE_NAME}"/*android_"${API_LEVEL}"_"${abi_tag}".whl
if ! ls ${WHEEL_GLOB} >/dev/null 2>&1; then
shopt -s nullglob
wheels=("${PYPIDIR}/dist/${PACKAGE_NAME}/"*android_"${API_LEVEL}_${abi_tag}".whl)
shopt -u nullglob
if [[ ${#wheels[@]} -eq 0 ]]; then
echo "Missing wheel output for ${PACKAGE_NAME} ${PACKAGE_VERSION} ${abi}" >&2
exit 1
fi
for built_wheel in ${WHEEL_GLOB}; do
for built_wheel in "${wheels[@]}"; do
cp -f "${built_wheel}" "${OUT_DIR}/"
fix_wheel_libpython_needed "${OUT_DIR}/$(basename "${built_wheel}")" "libpython${PYTHON_MINOR}.so"
done
@ -1116,4 +1122,4 @@ fi
echo "Done."
echo "Built wheels in: ${OUT_DIR}"
ls -1 "${OUT_DIR}" | sort
find "${OUT_DIR}" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort

View file

@ -17,7 +17,7 @@ cd "$ROOT"
# @electron/universal merges x64 and arm64 app bundles and requires every non-binary
# file present in both trees to have identical bytes. Per-arch backend-manifest.json
# contents always differ, so skip embedding it here; electron/main.js treats a missing
# contents always differ, so skip embedding it here. electron/main.js treats a missing
# manifest as "skip integrity check" (see verifyBackendIntegrity).
export MESHCHATX_SKIP_BACKEND_MANIFEST=1

View file

@ -8,7 +8,7 @@ copies any missing *.whl files from that directory into each identity's
repository-server/bundled folder (no network required).
The PyPI/sdist wheel intentionally omits this tree (see MANIFEST.in and
tool.setuptools.exclude-package-data); use this script for desktop or
tool.setuptools.exclude-package-data). Use this script for desktop or
Android builds, or refresh bundled wheels when online. If
dist/reticulum_meshchatx-*.whl exists at the project root, it is copied into
the bundled directory after PyPI downloads so the shipped wheel matches this

View file

@ -68,7 +68,7 @@ fail_with_logs() {
probe_status_ok() {
# Probe from inside the emulator (server binds 127.0.0.1 on-device).
# Prefer toybox wget (API 30+ images); fall back to python if present.
# Prefer toybox wget (API 30+ images). Fall back to python if present.
local body=""
if adb shell "command -v wget >/dev/null 2>&1" >/dev/null 2>&1; then
body="$(adb shell "wget -qO- --no-check-certificate https://127.0.0.1:8000${STATUS_PATH}" 2>/dev/null | tr -d '\r' || true)"

View file

@ -1,8 +1,8 @@
#!/bin/sh
# Create SLSA v1 cosign bundle attestations next to each release binary under DIR.
# Requires: cosign on PATH; COSIGN_KEY_PATH to cosign private key PEM; COSIGN_PASSWORD
# Requires: cosign on PATH, COSIGN_KEY_PATH to cosign private key PEM, and COSIGN_PASSWORD
# if the key is encrypted. Run from repository root so scripts/ci/slsa-predicate.py resolves.
# Cosign v3+: uses cosign-signing-config-no-rekor.json (no Rekor upload); verify with
# Cosign v3+: uses cosign-signing-config-no-rekor.json (no Rekor upload). Verify with
# cosign verify-blob-attestation --insecure-ignore-tlog=true when using the repo public key.
#
# Usage: attest-release-assets.sh <directory>
@ -10,7 +10,7 @@ set -eu
DIR="${1:?directory}"
KEY="${COSIGN_KEY_PATH:?set COSIGN_KEY_PATH}"
SCI_DIR="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
SCI_DIR="$(CDPATH='' cd -- "$(dirname "$0")" && pwd)"
SIGNCFG="${SCI_DIR}/cosign-signing-config-no-rekor.json"
if [ ! -f "$KEY" ]; then

View file

@ -1,6 +1,6 @@
#!/bin/sh
# Ensure /usr/local/bin (go-task, etc.) is on PATH without shadowing actions/setup-node.
# ARM64 GitHub-hosted images may ship Node 20 in /usr/local/bin; prepending it breaks pnpm 11.
# ARM64 GitHub-hosted images may ship Node 20 in /usr/local/bin. Prepending it breaks pnpm 11.
case ":${PATH}:" in
*:/usr/local/bin:*) ;;
*) PATH="${PATH}:/usr/local/bin" ;;

View file

@ -4,8 +4,8 @@
# Usage: docker-tags.sh <image_name> [output_file]
# Environment: GITEA_REF / GITHUB_REF, GITEA_REF_NAME / GITHUB_REF_NAME, TAG_SUFFIX
#
# The output file contains one `-t registry/image:tag` per line,
# suitable for passing directly to `docker buildx build`.
# The output file contains one -t registry/image:tag per line,
# suitable for passing directly to docker buildx build.
set -eu
IMAGE="$1"
@ -14,11 +14,10 @@ SUFFIX="${TAG_SUFFIX:-}"
: > "$OUTPUT"
_suffix_tag() {
local tag="$1"
if [ -n "$SUFFIX" ]; then
printf '%s' "${tag}${SUFFIX}"
printf '%s' "${1}${SUFFIX}"
else
printf '%s' "$tag"
printf '%s' "$1"
fi
}

View file

@ -3,5 +3,6 @@
# Usage: sh scripts/ci/exec-priv.sh apt-get update
set -eu
# shellcheck source=priv.sh disable=SC1091
. "$(dirname "$0")/priv.sh"
run_priv "$@"

View file

@ -2,7 +2,7 @@
# APT packages needed for Linux Electron packaging (AppImage, deb, rpm) on Debian/Ubuntu or in Dockerfile.build (root).
set -euo pipefail
# shellcheck source=scripts/ci/priv.sh
# shellcheck source=priv.sh disable=SC1091
. "$(dirname "$0")/priv.sh"
_HOST_ARCH="$(uname -m)"
@ -11,8 +11,8 @@ if [ "$_HOST_ARCH" = "x86_64" ]; then
fi
run_priv apt-get update -y
_PKGS="patchelf libopusfile0 espeak-ng zip rpm elfutils fakeroot file"
_PKGS=(patchelf libopusfile0 espeak-ng zip rpm elfutils fakeroot file)
if [ "$_HOST_ARCH" = "x86_64" ]; then
_PKGS="$_PKGS libc6:i386 libstdc++6:i386"
_PKGS+=("libc6:i386" "libstdc++6:i386")
fi
run_priv apt-get install -y --no-install-recommends $_PKGS
run_priv apt-get install -y --no-install-recommends "${_PKGS[@]}"

View file

@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Build macOS universal (x64 + arm64) DMG via electron-builder. Unsigned CI build; signing is disabled.
# Build macOS universal (x64 + arm64) DMG via electron-builder. Unsigned CI build. Signing is disabled.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"

View file

@ -48,7 +48,6 @@ skip_noise() {
library.zip | library.zip.cosign.bundle) return 0 ;;
*__library.zip | *__library.zip.cosign.bundle) return 0 ;;
*.so.yml | *.so.yml.cosign.bundle) return 0 ;;
*__*.so.yml | *__*.so.yml.cosign.bundle) return 0 ;;
esac
return 1
}
@ -118,7 +117,7 @@ mapfile -t files < <(find "$STAGE" -type f)
for f in "${files[@]}"; do
b=$(basename "$f")
hash=$(sha256sum "$f" | awk '{print $1}')
printf '| %s | `%s` |\n' "$b" "$hash"
printf "| %s | \`%s\` |\n" "$b" "$hash"
done
echo
echo "## Verification"

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Ensure an x86_64 (Rosetta) Homebrew exists at /usr/local/bin/brew.
# GitHub-hosted Apple Silicon runners ship /opt/homebrew only; cx_Freeze universal
# GitHub-hosted Apple Silicon runners ship /opt/homebrew only. cx_Freeze universal
# x64 slices need x86_64 libraries from /usr/local (see codec2, libyaml steps).
set -euo pipefail

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Install locked Python deps for the darwin-x64 cx_Freeze slice on Apple Silicon CI.
# The arm64 slice uses uv sync into .venv; this script mirrors that with .venv-x64 so
# The arm64 slice uses uv sync into .venv. This script mirrors that with .venv-x64 so
# NumPy/LXST native wheels match the lockfile instead of unpinned pip -e . resolution.
set -euo pipefail
@ -66,7 +66,7 @@ if [[ -z "$_NUMPY_VERSION" || -z "$_PYCODEC2_VERSION" ]]; then
exit 1
fi
# Host is arm64; without --python-platform uv still resolves macOS wheels for aarch64.
# Host is arm64. Without --python-platform uv still resolves macOS wheels for aarch64.
# pycodec2 has no cp314 macOS x86_64 wheel, so uv would build it from sdist and pull
# numpy into an isolated cross build (meson: "Can not run test applications").
uv sync --frozen --group dev \
@ -90,7 +90,7 @@ uv pip install --python "$_PY" \
# native extension is installed as x86_64-only, so the arm64-loaded interpreter
# fails to dlopen it ("incompatible architecture"). Building the wheel ourselves
# lets us force x86_64 on the one interpreter invocation that runs native code
# (via `arch -x86_64`), then hand uv a finished wheel to install, which is a
# (via arch -x86_64), then hand uv a finished wheel to install, which is a
# plain file copy where uv's own architecture no longer matters.
_lock_sdist_url() {
awk -v pkg="$1" '
@ -128,8 +128,8 @@ uv pip install --python "$_PY" \
--python-platform x86_64-apple-darwin \
"$_pycodec2_wheel"
# Cython/wheel are build-time-only tools for the pycodec2 sdist compile above;
# the finished wheel's .so no longer needs them at runtime. uv.lock does not
# Cython/wheel are build-time-only tools for the pycodec2 sdist compile above.
# The finished wheel's .so no longer needs them at runtime. uv.lock does not
# pin either, so leaving them installed would make this venv's site-packages
# diverge from .venv's (arm64, which never builds pycodec2 from source and
# never needs them) -- cx_Freeze's module finder bundles whatever is actually

View file

@ -2,7 +2,7 @@
# Point this clone at .githooks/ (tracked git hooks).
set -eu
ROOT="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)"
ROOT="$(CDPATH='' cd -- "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
git config core.hooksPath .githooks

View file

@ -2,7 +2,7 @@
# Normalize the installed pycodec2 package's bundled libcodec2 into a fixed,
# arch-independent relative layout:
#
# pycodec2/pycodec2*.so (extension module; @loader_path/libcodec2.dylib)
# pycodec2/pycodec2*.so (extension module, @loader_path/libcodec2.dylib)
# pycodec2/libcodec2.dylib (single canonical name, no .dylibs/ subfolder)
#
# Published macOS wheels bundle libcodec2 under pycodec2/.dylibs/libcodec2.<ver>.dylib

View file

@ -3,6 +3,7 @@
# Usage: setup-cosign.sh [version]
set -eu
# shellcheck source=priv.sh disable=SC1091
. "$(dirname "$0")/priv.sh"
COSIGN_VERSION="${1:-3.0.6}"

View file

@ -4,6 +4,7 @@
# Usage: setup-task.sh [version]
set -eu
# shellcheck source=priv.sh disable=SC1091
. "$(dirname "$0")/priv.sh"
TASK_VERSION="${1:-3.49.1}"

View file

@ -12,7 +12,7 @@
# RNS_ID_PATH=~/.local/share/reticulum-go/reticulum-go-release.rid sh scripts/ci/sign-tree-rsm.sh
set -eu
ROOT="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)"
ROOT="$(CDPATH='' cd -- "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
ID_PATH="${RNS_ID_PATH:-}"

View file

@ -18,7 +18,7 @@
# tree-manifest.sh verify-tracked [inv] also fail if tracked files are missing from inv
set -eu
ROOT="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)"
ROOT="$(CDPATH='' cd -- "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
MANIFEST_HEADER="# meshchatx tree manifest v1"

View file

@ -4,7 +4,7 @@
set -eu
# MeshChatX runtime Python deps come from uv.lock (and pip-audit). Vendored trees may
# ship upstream poetry.lock files for standalone development only; skip them here.
# ship upstream poetry.lock files for standalone development only. Skip them here.
exec trivy fs --exit-code 1 --severity HIGH,CRITICAL \
--skip-dirs .pnpm-store,.venv,temp-tests \
--skip-files vendor/lxmfy/poetry.lock,vendor/lxmfy/docs/poetry.lock \

View file

@ -2,7 +2,7 @@
# Smoke tests for verify-package-contents.sh (no full freeze required).
set -euo pipefail
ROOT="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)"
ROOT="$(CDPATH='' cd -- "$(dirname "$0")/../.." && pwd)"
SCRIPT="$ROOT/scripts/ci/verify-package-contents.sh"
tmp="$(mktemp -d "${TMPDIR:-/tmp}/pkg-bloat-test.XXXXXX")"
trap 'rm -rf "$tmp"' EXIT INT

View file

@ -15,7 +15,7 @@
# PACKAGE_BLOAT_MAX_HITS stop after N hits (default 40)
set -euo pipefail
ROOT="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)"
ROOT="$(CDPATH='' cd -- "$(dirname "$0")/../.." && pwd)"
MODE="${1:-}"
TARGET="${2:-}"
MAX_HITS="${PACKAGE_BLOAT_MAX_HITS:-40}"
@ -113,7 +113,7 @@ scan_directory_tree() {
deny_re="$2"
# Process substitution keeps scan_path_list in this shell (hits accumulate).
scan_path_list "$deny_re" < <(
CDPATH= cd -- "$root" || exit 1
CDPATH='' cd -- "$root" || exit 1
find . -print 2>/dev/null | sed 's|^\./||'
)
}
@ -149,7 +149,7 @@ scan_dir() {
scan_wheel() {
whl="${TARGET:-}"
if [ -z "$whl" ]; then
whl="$(ls -1 "$ROOT"/python-dist/*.whl 2>/dev/null | head -n 1 || true)"
whl="$(find "$ROOT/python-dist" -maxdepth 1 -type f -name '*.whl' -print 2>/dev/null | head -n 1 || true)"
fi
[ -n "$whl" ] && [ -f "$whl" ] || {
echo "verify-package-contents.sh: wheel not found" >&2
@ -193,7 +193,7 @@ scan_appimage() {
echo "verify-package-contents.sh: extracting AppImage $ai"
chmod +x "$ai" || true
(
CDPATH= cd -- "$tmp"
CDPATH='' cd -- "$tmp"
"$ai" --appimage-extract >/dev/null
)
scan_directory_tree "$tmp/squashfs-root" "$FROZEN_DENY_RE"

View file

@ -15,7 +15,7 @@
# sh scripts/ci/verify-tree-rsm.sh
set -eu
ROOT="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)"
ROOT="$(CDPATH='' cd -- "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
SIGNER="${RNS_REQUIRED_SIGNER:-e46112d44649266d71fe2193e00a4710}"

View file

@ -13,7 +13,7 @@
# RNS_TREE_VERIFY_STRICT if 1, fail the process on verify errors
set -eu
ROOT="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)"
ROOT="$(CDPATH='' cd -- "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
STRICT="${RNS_TREE_VERIFY_STRICT:-0}"

View file

@ -3,13 +3,13 @@
"""Alpine/musl Docker: copy cffi-built filter shared library to LXST.filterlib name.
LXST ships glibc-tagged filterlib*.so wheels; musl ignores them and cffi
LXST ships glibc-tagged filterlib*.so wheels. On musl, those are ignored and cffi
verify() drops the musl artifact under LXST/__pycache__/_cffi__*.so.
Without this step, a fresh process cannot resolve LXST.filterlib for
ffi.dlopen() and would try to compile again at runtime (no gcc).
The cffi artifact is a plain shared library (loaded via dlopen), not a Python
extension module (no PyInit_filterlib); do not import LXST.filterlib.
extension module (no PyInit_filterlib). Do not import LXST.filterlib.
"""
from __future__ import annotations

View file

@ -1,5 +1,5 @@
#!/usr/bin/env bash
# flatpak-bundler installs runtimes with flatpak --user and a bare ref; that only works
# flatpak-bundler installs runtimes with flatpak --user and a bare ref. That only works
# once the Flathub remote exists (see @malept/flatpak-bundler ensureRef).
set -euo pipefail

View file

@ -5,7 +5,7 @@ set -euo pipefail
# MESHCHATX_RELEASES_RSS Release feed (default: .../MeshChatX/releases.rss)
# MESHCHATX_REPO_BASE Repo root for synthesized wheel URLs if RSS has no
# .whl link in descriptions (default: derived from RSS URL)
# Cosign: Sigstore attestation verify needs the real cosign binary; this script can
# Cosign: Sigstore attestation verify needs the real cosign binary. This script can
# download a checksum-verified release from GitHub to /tmp if none is on PATH.
# MESHCHATX_COSIGN_VERSION (default: 3.0.6)
# MESHCHATX_COSIGN_PUB_URL (default: raw cosign.pub from master in this repo)

View file

@ -29,7 +29,7 @@ echo "Downloading Windows Python and Git..."
wget -q "$PYTHON_URL"
wget -q "$GIT_URL"
chmod +x *.exe
chmod +x ./*.exe
echo "Initializing Wine prefix in $WINEPREFIX..."
wine_wrap wine wineboot --init

View file

@ -70,7 +70,7 @@ if [[ -z "${ANDROID_HOME}" ]]; then
exit 1
fi
BT_DIR="$(ls -d "${ANDROID_HOME}"/build-tools/* 2>/dev/null | sort -V | tail -n 1)"
BT_DIR="$(find "${ANDROID_HOME}/build-tools" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort -V | tail -n 1)"
if [[ -z "${BT_DIR}" ]]; then
echo "No Android build-tools found under ${ANDROID_HOME}/build-tools." >&2
exit 1
@ -81,9 +81,7 @@ if [[ ! -x "${BT_DIR}/zipalign" || ! -x "${BT_DIR}/apksigner" ]]; then
fi
APK_GLOB="${APK_GLOB:-android/app/build/outputs/apk/release/*-unsigned.apk}"
shopt -s nullglob
APKS=( ${APK_GLOB} )
shopt -u nullglob
mapfile -t APKS < <(compgen -G "${APK_GLOB}" || true)
if [[ ${#APKS[@]} -eq 0 ]]; then
echo "No unsigned APKs matched: ${APK_GLOB}" >&2
exit 1

View file

@ -5,8 +5,8 @@
# 1. Every file must exist in BOTH trees (no unique-to-one-arch files).
# 2. Every non-Mach-O file must be byte-identical across trees.
#
# Python bytecode (.pyc inside library.zip) is architecture-independent;
# only timestamps and zip metadata cause SHA differences.
# Python bytecode (.pyc inside library.zip) is architecture-independent.
# Only timestamps and zip metadata cause SHA differences.
set -euo pipefail
@ -82,14 +82,14 @@ while IFS= read -r -d '' rel; do
unified=$((unified + 1))
continue
fi
# cx_Freeze bundles pure-Python modules into lib/library.zip; native
# cx_Freeze bundles pure-Python modules into lib/library.zip. Native
# extensions are always written to the filesystem separately (they
# can't be dlopen'd from inside a zip), so this archive's *contents*
# are pure CPython bytecode just like loose .pyc files. Its raw bytes
# almost always differ across two independent builds (each entry's
# own PEP 552 header + the zip's own per-entry timestamps), so only
# trust a blind copy once we've confirmed both slices bundled the
# same set of modules; a differing member list would mean the two
# same set of modules. A differing member list would mean the two
# Python environments actually resolved different dependencies.
if [[ "$(basename "$rel")" == "library.zip" ]]; then
# Exclude *.dist-info provenance files that record *how* a package