diff --git a/.github/workflows/build-linux-packages.yml b/.github/workflows/build-linux-packages.yml index b67afc1..543b8dd 100644 --- a/.github/workflows/build-linux-packages.yml +++ b/.github/workflows/build-linux-packages.yml @@ -1,4 +1,4 @@ -# Linux packaging build test: Flatpak (Snap job commented out for now). +# Linux packaging build test: Flatpak. # # Pinned first-party actions (bump tag and SHA together when upgrading): # actions/checkout@v6.0.1 8e8c483db84b4bee98b60c0593521ed34d9990e8 @@ -139,87 +139,3 @@ jobs: out/make/**/*.flatpak if-no-files-found: warn retention-days: 7 - - # snap: - # name: Snap (electron-forge) - # needs: frontend - # runs-on: ubuntu-latest - # timeout-minutes: 90 - # permissions: - # contents: read - # env: - # FRONTEND_ARTIFACT_NAME: ${{ needs.frontend.outputs.artifact_name }} - # MESHCHATX_FRONTEND_PREBUILT: "1" - # SNAPCRAFT_BUILD_INFO: "1" - # steps: - # - name: Checkout - # uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 - # - # - name: Install snapcraft - # run: | - # set -euo pipefail - # sudo snap install snapcraft --classic - # snapcraft --version - # - # - name: Prepare snaps for Snapcraft (destructive mode) - # run: | - # set -euo pipefail - # sudo snap wait system seed.loaded 2>/dev/null || true - # for snap_name in core22 gnome-42-2204; do - # if ! snap list "$snap_name" >/dev/null 2>&1; then - # sudo snap install "$snap_name" - # fi - # snap list "$snap_name" - # done - # - # - name: Set up Python - # uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 - # with: - # python-version: ${{ env.PYTHON_VERSION }} - # - # - name: Install Poetry (PyPI pin) - # env: - # POETRY_VERSION: ${{ env.POETRY_VERSION }} - # run: bash scripts/ci/github-install-poetry.sh - # - # - name: Set up Node - # uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f - # with: - # node-version: ${{ env.NODE_VERSION }} - # - # - name: Enable pnpm (corepack) - # run: corepack enable && corepack prepare "pnpm@${PNPM_VERSION}" --activate - # - # - name: Install dependencies - # run: bash scripts/ci/github-install-deps.sh - # - # - name: Download frontend artifact - # uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 - # with: - # name: ${{ env.FRONTEND_ARTIFACT_NAME }} - # path: meshchatx/public - # - # - name: Verify frontend artifact contents - # run: | - # set -euo pipefail - # test -f meshchatx/public/index.html - # test -d meshchatx/public/assets - # test -d meshchatx/public/reticulum-docs-bundled/current - # - # - name: Build snap package - # run: bash scripts/ci/github-build-linux-snap.sh - # - # - name: List snap output - # run: | - # set -euo pipefail - # ls -la out/make 2>/dev/null || true - # find out/make -maxdepth 5 -type f -name "*.snap" -print || true - # - # - name: Upload snap artifact - # uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 - # with: - # name: meshchatx-linux-snap-${{ github.ref_name }}-${{ github.run_id }} - # path: | - # out/make/**/*.snap - # if-no-files-found: warn - # retention-days: 7 diff --git a/scripts/ci/github-build-linux-snap.sh b/scripts/ci/github-build-linux-snap.sh deleted file mode 100755 index ce87b5e..0000000 --- a/scripts/ci/github-build-linux-snap.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -# Build a Snap via electron-forge's @electron-forge/maker-snap. - -set -euo pipefail - -ROOT="$(cd "$(dirname "$0")/../.." && pwd)" -cd "$ROOT" - -if [[ ! -f "meshchatx/public/index.html" ]]; then - echo "meshchatx/public/index.html is missing; download the prebuilt frontend artifact first." >&2 - exit 1 -fi - -export PLATFORM=linux - -pnpm run electron-postinstall -pnpm run version:sync -pnpm run build-backend - -export DEBUG="${DEBUG:+$DEBUG,}electron-installer-snap:snapcraft" - -FORGE_MAKE_SNAP=1 node scripts/electron-forge-local-tmp.js make --targets @electron-forge/maker-snap diff --git a/scripts/ci/setup-docker.sh b/scripts/ci/setup-docker.sh deleted file mode 100755 index 97b93e2..0000000 --- a/scripts/ci/setup-docker.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# Set up QEMU and Docker Buildx for multi-platform builds. -# Optionally log in to a container registry. -# -# Usage: setup-docker.sh [registry] [username] [password] -# or set REGISTRY, REGISTRY_USERNAME, REGISTRY_PASSWORD env vars. -set -eu - -. "$(dirname "$0")/priv.sh" - -REGISTRY="${1:-${REGISTRY:-}}" -USERNAME="${2:-${REGISTRY_USERNAME:-}}" -PASSWORD="${3:-${REGISTRY_PASSWORD:-}}" - -echo "Registering QEMU binfmt handlers" -run_priv apt-get update -qq -run_priv apt-get install -y -qq qemu-user-static binfmt-support - -echo "Creating Docker Buildx builder" -docker buildx create --name multiarch --driver docker-container --use -docker buildx inspect --bootstrap - -if [ -n "$REGISTRY" ] && [ -n "$USERNAME" ] && [ -n "$PASSWORD" ]; then - echo "Logging in to ${REGISTRY}" - echo "$PASSWORD" | docker login "$REGISTRY" -u "$USERNAME" --password-stdin -fi diff --git a/scripts/ci/setup-java.sh b/scripts/ci/setup-java.sh deleted file mode 100755 index 4cc6bcd..0000000 --- a/scripts/ci/setup-java.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh -# Install Adoptium Temurin JDK with SHA256 verification. -# Uses the Adoptium assets API to obtain the checksum and download URL -# directly from the source, avoiding opaque redirects. -# Source: https://adoptium.net (Eclipse Foundation) -# Usage: setup-java.sh [major_version] -set -eu - -. "$(dirname "$0")/priv.sh" - -JAVA_VERSION="${1:-17}" - -ARCH="$(uname -m)" -case "$ARCH" in - x86_64) ARCH="x64" ;; - aarch64) ARCH="aarch64" ;; - *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; -esac - -echo "Installing Temurin JDK ${JAVA_VERSION} (${ARCH})" - -ASSETS_API="https://api.adoptium.net/v3/assets/latest/${JAVA_VERSION}/hotspot?architecture=${ARCH}&image_type=jdk&os=linux&vendor=eclipse" -API_JSON="$(curl -fsSL "$ASSETS_API")" - -DOWNLOAD_URL="$(echo "$API_JSON" | sed -n 's/.*"link" *: *"\([^"]*\)".*/\1/p' | head -1)" -CHECKSUM_URL="$(echo "$API_JSON" | sed -n 's/.*"checksum_link" *: *"\([^"]*\)".*/\1/p' | head -1)" -EXPECTED="$(echo "$API_JSON" | sed -n 's/.*"checksum" *: *"\([0-9a-f]*\)".*/\1/p' | head -1)" - -if [ -z "$DOWNLOAD_URL" ] || [ -z "$EXPECTED" ]; then - echo "Failed to resolve Temurin JDK ${JAVA_VERSION} for ${ARCH}" >&2 - exit 1 -fi - -curl -fsSL "$DOWNLOAD_URL" -o /tmp/jdk.tar.gz - -ACTUAL="$(sha256sum /tmp/jdk.tar.gz | cut -d' ' -f1)" -if [ "$EXPECTED" != "$ACTUAL" ]; then - echo "SHA256 verification failed for JDK ${JAVA_VERSION}" >&2 - echo " expected: ${EXPECTED}" >&2 - echo " got: ${ACTUAL}" >&2 - rm -f /tmp/jdk.tar.gz - exit 1 -fi -echo "SHA256 verified: ${ACTUAL}" - -if [ -n "$CHECKSUM_URL" ]; then - EXPECTED_LINK="$(curl -fsSL "$CHECKSUM_URL" | cut -d' ' -f1)" - if [ "$EXPECTED_LINK" != "$ACTUAL" ]; then - echo "Cross-check against checksum_link also failed" >&2 - rm -f /tmp/jdk.tar.gz - exit 1 - fi - echo "Cross-verified against checksum_link" -fi - -run_priv mkdir -p /opt/java -run_priv tar -xzf /tmp/jdk.tar.gz -C /opt/java --strip-components=1 -rm -f /tmp/jdk.tar.gz - -CI_ENV="${GITEA_ENV:-${GITHUB_ENV:-/dev/null}}" -CI_PATH="${GITEA_PATH:-${GITHUB_PATH:-/dev/null}}" -echo "JAVA_HOME=/opt/java" >> "$CI_ENV" -echo "/opt/java/bin" >> "$CI_PATH" - -export JAVA_HOME=/opt/java -export PATH="/opt/java/bin:$PATH" -java -version diff --git a/scripts/ci/setup-node.sh b/scripts/ci/setup-node.sh deleted file mode 100755 index a3775ee..0000000 --- a/scripts/ci/setup-node.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh -# Install Node.js from the official binary distribution with SHA256 verification. -# Source: https://nodejs.org -# Usage: setup-node.sh [major_version] -set -eu - -. "$(dirname "$0")/priv.sh" - -NODE_MAJOR="${1:-24}" - -ARCH="$(uname -m)" -case "$ARCH" in - x86_64) ARCH="x64" ;; - aarch64) ARCH="arm64" ;; - armv7l) ARCH="armv7l" ;; - *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; -esac - -DIST_URL="https://nodejs.org/dist/latest-v${NODE_MAJOR}.x" - -curl -fsSL "${DIST_URL}/SHASUMS256.txt" -o /tmp/node-shasums.txt - -VERSION="$(grep -o "node-v[0-9.]*-linux-${ARCH}" /tmp/node-shasums.txt \ - | head -1 \ - | sed "s/-linux-${ARCH}//" \ - | sed 's/node-//')" - -if [ -z "$VERSION" ]; then - echo "Failed to resolve Node.js v${NODE_MAJOR} for ${ARCH}" >&2 - exit 1 -fi - -TARBALL="node-${VERSION}-linux-${ARCH}.tar.xz" -echo "Installing Node.js ${VERSION} (${ARCH})" -curl -fsSL "${DIST_URL}/${TARBALL}" -o /tmp/node.tar.xz - -EXPECTED="$(grep " ${TARBALL}\$" /tmp/node-shasums.txt | cut -d' ' -f1)" -ACTUAL="$(sha256sum /tmp/node.tar.xz | cut -d' ' -f1)" -if [ -z "$EXPECTED" ] || [ "$EXPECTED" != "$ACTUAL" ]; then - echo "SHA256 verification failed for ${TARBALL}" >&2 - echo " expected: ${EXPECTED}" >&2 - echo " got: ${ACTUAL}" >&2 - rm -f /tmp/node.tar.xz /tmp/node-shasums.txt - exit 1 -fi -echo "SHA256 verified: ${ACTUAL}" - -run_priv tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 -rm -f /tmp/node.tar.xz /tmp/node-shasums.txt - -export PATH="/usr/local/bin:$PATH" - -# Act / Gitea runners often ship an older Node in /usr/bin; later steps start a new shell. -if [ -n "${GITHUB_ENV:-}" ]; then - echo "PATH=/usr/local/bin:$PATH" >> "$GITHUB_ENV" -fi -if [ -n "${GITEA_ENV:-}" ]; then - echo "PATH=/usr/local/bin:$PATH" >> "$GITEA_ENV" -fi -if [ -n "${GITHUB_PATH:-}" ]; then - echo "/usr/local/bin" >> "$GITHUB_PATH" -fi -if [ -n "${GITEA_PATH:-}" ]; then - echo "/usr/local/bin" >> "$GITEA_PATH" -fi - -node --version -npm --version diff --git a/scripts/ci/setup-pnpm.sh b/scripts/ci/setup-pnpm.sh deleted file mode 100755 index 6f149ae..0000000 --- a/scripts/ci/setup-pnpm.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -# Activate pnpm via corepack. -# Usage: setup-pnpm.sh [version] -set -eu - -export PATH="/usr/local/bin:$PATH" - -PNPM_VERSION="${1:-10.32.1}" - -corepack enable -corepack prepare "pnpm@${PNPM_VERSION}" --activate - -pnpm config set verify-store-integrity true - -pnpm --version diff --git a/scripts/ci/setup-python.sh b/scripts/ci/setup-python.sh deleted file mode 100755 index 63bdf04..0000000 --- a/scripts/ci/setup-python.sh +++ /dev/null @@ -1,166 +0,0 @@ -#!/bin/sh -# Build and install Python from official python.org source. -# Verifies: OpenPGP (.asc) when present, otherwise Sigstore (.sigstore) with cosign. -# https://www.python.org/download/sigstore/ PEP 761 (3.14+): PGP removed; Sigstore only. -# -# Usage: setup-python.sh [version] -# version: exact (3.14.x) or minor (3.14, resolved to latest patch). -set -eu - -. "$(dirname "$0")/priv.sh" - -# Sigstore identity and OIDC issuer per release (see python.org/download/sigstore). -sigstore_identity_for() { - _v="$1" - case "$_v" in - 3.7.*) echo "nad@python.org|https://github.com/login/oauth" ;; - 3.8.*|3.9.*) echo "lukasz@langa.pl|https://github.com/login/oauth" ;; - 3.10.*|3.11.*) echo "pablogsal@python.org|https://accounts.google.com" ;; - 3.12.*|3.13.*) echo "thomas@python.org|https://accounts.google.com" ;; - 3.14.*|3.15.*) echo "hugo@python.org|https://github.com/login/oauth" ;; - 3.16.*|3.17.*) echo "savannah@python.org|https://github.com/login/oauth" ;; - 3.*.*) echo "savannah@python.org|https://github.com/login/oauth" ;; - *) echo "" ;; - esac -} - -download_cosign() { - COSIGN_VERSION="${COSIGN_VERSION:-2.4.1}" - COSIGN_ARCH="linux-amd64" - case "$(uname -m)" in - aarch64|arm64) COSIGN_ARCH="linux-arm64" ;; - x86_64|amd64) COSIGN_ARCH="linux-amd64" ;; - x86_64-gnu) COSIGN_ARCH="linux-amd64" ;; - *) - echo "unsupported uname -m for cosign: $(uname -m)" >&2 - exit 1 - ;; - esac - COSIGN_BIN="/tmp/cosign-${COSIGN_VERSION}-${COSIGN_ARCH}" - if [ ! -x "$COSIGN_BIN" ]; then - curl -fsSL "https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-${COSIGN_ARCH}" \ - -o "$COSIGN_BIN" - chmod +x "$COSIGN_BIN" - fi - echo "$COSIGN_BIN" -} - -PY_INPUT="${1:-3.14}" - -CURRENT="$(python3 --version 2>/dev/null | sed 's/Python //')" || true - -case "$PY_INPUT" in - *.*.*) - PY_VERSION="$PY_INPUT" - ;; - *) - echo "Resolving latest Python ${PY_INPUT}.x from python.org" - PY_VERSION="$(curl -fsSL "https://www.python.org/ftp/python/" \ - | grep -o "href=\"${PY_INPUT}\.[0-9]*/" \ - | sed 's/href="//;s/\///' \ - | sort -t. -k3 -n \ - | tail -1)" - if [ -z "$PY_VERSION" ]; then - echo "Failed to resolve Python ${PY_INPUT}.x" >&2 - exit 1 - fi - echo "Resolved to ${PY_VERSION}" - ;; -esac - -if [ "$CURRENT" = "$PY_VERSION" ]; then - echo "Python ${PY_VERSION} already installed" - python3 --version - exit 0 -fi - -echo "Building Python ${PY_VERSION} from source (python.org)" - -run_priv apt-get update -qq -run_priv apt-get install -y -qq \ - build-essential gnupg curl \ - libssl-dev zlib1g-dev libbz2-dev libreadline-dev \ - libsqlite3-dev libffi-dev liblzma-dev libncurses-dev > /dev/null 2>&1 - -TARBALL="Python-${PY_VERSION}.tar.xz" -SRC_URL="https://www.python.org/ftp/python/${PY_VERSION}/${TARBALL}" -SIG_URL="${SRC_URL}.asc" - -curl -fsSL "$SRC_URL" -o "/tmp/${TARBALL}" - -SIG_HTTP="$(curl -sS -o "/tmp/${TARBALL}.asc" -w "%{http_code}" "$SIG_URL" || true)" -if [ "$SIG_HTTP" != "200" ]; then - rm -f "/tmp/${TARBALL}.asc" -fi - -GPG_KEYS="" -case "$PY_VERSION" in - 3.13.*|3.14.*) - GPG_KEYS="A035C8C19219BA821ECEA86B64E628F8D684696D 7169605F62C751356D054A26A821E680E5FA6305" - ;; - 3.11.*|3.12.*) - GPG_KEYS="A035C8C19219BA821ECEA86B64E628F8D684696D 7169605F62C751356D054A26A821E680E5FA6305" - ;; - 3.9.*|3.10.*) - GPG_KEYS="E3FF2839C048B25C084DEBE9B26995E310250568" - ;; - *) - echo "No known GPG key for Python ${PY_VERSION}; skipping OpenPGP signature check" >&2 - ;; -esac - -GPG_VERIFIED=0 -if [ "$SIG_HTTP" = "200" ] && [ -n "$GPG_KEYS" ]; then - export GNUPGHOME="$(mktemp -d)" - for key in $GPG_KEYS; do - gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" 2>/dev/null || \ - gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" 2>/dev/null || true - done - gpg --batch --verify "/tmp/${TARBALL}.asc" "/tmp/${TARBALL}" - rm -rf "$GNUPGHOME" - unset GNUPGHOME - echo "OpenPGP signature verified" - GPG_VERIFIED=1 -fi - -if [ "$GPG_VERIFIED" != "1" ]; then - SIGSTORE_URL="${SRC_URL}.sigstore" - SIGSTORE_HTTP="$(curl -sS -o "/tmp/${TARBALL}.sigstore" -w "%{http_code}" "$SIGSTORE_URL" || true)" - if [ "$SIGSTORE_HTTP" != "200" ]; then - rm -f "/tmp/${TARBALL}.sigstore" - echo "No OpenPGP signature (HTTP ${SIG_HTTP}) and no Sigstore bundle at ${SIGSTORE_URL} (HTTP ${SIGSTORE_HTTP})" >&2 - exit 1 - fi - II="$(sigstore_identity_for "$PY_VERSION")" - if [ -z "$II" ]; then - echo "No Sigstore identity mapping for Python ${PY_VERSION}" >&2 - exit 1 - fi - SIG_IDENTITY="$(echo "$II" | cut -d'|' -f1)" - SIG_ISSUER="$(echo "$II" | cut -d'|' -f2)" - COSIGN_BIN="$(download_cosign)" - "$COSIGN_BIN" verify-blob --new-bundle-format \ - --certificate-oidc-issuer "$SIG_ISSUER" \ - --certificate-identity "$SIG_IDENTITY" \ - --bundle "/tmp/${TARBALL}.sigstore" \ - "/tmp/${TARBALL}" - echo "Sigstore signature verified" -fi - -cd /tmp -tar -xJf "${TARBALL}" -cd "Python-${PY_VERSION}" - -BUILD_LOG="/tmp/python-build.log" -./configure --prefix=/usr/local --with-ensurepip=install > "$BUILD_LOG" 2>&1 -make -j"$(nproc)" >> "$BUILD_LOG" 2>&1 -run_priv make install >> "$BUILD_LOG" 2>&1 - -run_priv ln -sf /usr/local/bin/python3 /usr/local/bin/python -run_priv ln -sf /usr/local/bin/pip3 /usr/local/bin/pip - -cd / -rm -rf "/tmp/${TARBALL}" "/tmp/${TARBALL}.asc" "/tmp/${TARBALL}.sigstore" "/tmp/Python-${PY_VERSION}" "$BUILD_LOG" - -python3 --version -pip3 --version diff --git a/scripts/ci/setup-rekor-cli.sh b/scripts/ci/setup-rekor-cli.sh deleted file mode 100755 index 6551eb6..0000000 --- a/scripts/ci/setup-rekor-cli.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# Install rekor-cli from GitHub releases with SHA256 verification. -# Usage: setup-rekor-cli.sh [version] -set -eu - -. "$(dirname "$0")/priv.sh" - -REKOR_VERSION="${1:-1.5.1}" - -ARCH="$(uname -m)" -case "$ARCH" in - x86_64) - BINARY="rekor-cli-linux-amd64" - EXPECTED_SHA256="0b4964af85477892c37039fb80793b151864970d19838873eaa1a777ca2fb813" - ;; - aarch64) - BINARY="rekor-cli-linux-arm64" - EXPECTED_SHA256="6417ea36bea9239125ec21e73c5d9b5e7e837b580cfdfea1e47e04bb02235534" - ;; - *) - echo "Unsupported architecture: $ARCH" >&2 - exit 1 - ;; -esac - -BASE_URL="https://github.com/sigstore/rekor/releases/download/v${REKOR_VERSION}" -curl -fsSL "${BASE_URL}/${BINARY}" -o /tmp/rekor-cli - -ACTUAL="$(sha256sum /tmp/rekor-cli | awk '{print $1}')" -if [ "$EXPECTED_SHA256" != "$ACTUAL" ]; then - echo "SHA256 verification failed for ${BINARY}" >&2 - rm -f /tmp/rekor-cli - exit 1 -fi - -run_priv install -m 0755 /tmp/rekor-cli /usr/local/bin/rekor-cli -rm -f /tmp/rekor-cli -rekor-cli version