#!/usr/bin/env bash set -euo pipefail PROJECT_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" PACKAGE_SOURCE_ROOT="${PROJECT_ROOT}/src" GAMESCOPE_PROVENANCE_ROOT="${PROJECT_ROOT}/native/gamescope" SOURCE_VERSION="$(sed -n 's/^__version__ = "\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)"$/\1/p' "${PACKAGE_SOURCE_ROOT}/ptlinux/__init__.py")" VERSION="${PTLINUX_VERSION:-$SOURCE_VERSION}" if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] then printf 'Invalid release version: %s (expected X.Y.Z)\n' "$VERSION" >&2 exit 2 fi if [[ "$VERSION" != "$SOURCE_VERSION" ]] then printf 'Release version %s does not match source version %s.\n' "$VERSION" "$SOURCE_VERSION" >&2 exit 2 fi RELEASE_MODE="${PTLINUX_RELEASE_MODE:-development}" if [[ "$RELEASE_MODE" != "development" && "$RELEASE_MODE" != "production" ]] then printf 'Invalid release mode: %s (expected development or production)\n' "$RELEASE_MODE" >&2 exit 2 fi VERSION_SLUG="${VERSION//./-}" ARCHIVE_NAME="pristontale-eu-linux-installer-v${VERSION_SLUG}" OUTPUT_DIRECTORY="${PTLINUX_OUTPUT_DIRECTORY:-${PROJECT_ROOT}/.artifacts/releases/aio}" PYTHON_BIN="${PTLINUX_PYTHON:-python3}" if [[ "$RELEASE_MODE" == "production" && -z "${PTLINUX_SIGNING_KEY:-}" ]] then printf '%s\n' "PTLINUX_SIGNING_KEY is required for a production release." >&2 exit 2 fi if [[ -n "${PTLINUX_SIGNING_KEY:-}" ]] then PTLINUX_SIGNING_KEY="$( PYTHONPATH="${PROJECT_ROOT}/src:${PROJECT_ROOT}${PYTHONPATH:+:${PYTHONPATH}}" \ "$PYTHON_BIN" -c \ 'import sys from pathlib import Path from scripts.release_lib import ReleaseToolError, _safe_signing_key_path try: print(_safe_signing_key_path(Path(sys.argv[1]), output_directory=Path(sys.argv[2]))) except ReleaseToolError as exc: print(f"Invalid release signing key: {exc}", file=sys.stderr) raise SystemExit(2)' \ "$PTLINUX_SIGNING_KEY" "$OUTPUT_DIRECTORY" )" export PTLINUX_SIGNING_KEY fi STAGING_DIRECTORY="$(mktemp -d /tmp/ptlinux-release.XXXXXX)" RELEASE_ROOT="${STAGING_DIRECTORY}/${ARCHIVE_NAME}" cleanup() { rm -r -- "$STAGING_DIRECTORY" } trap cleanup EXIT install -d -m 0755 "$RELEASE_ROOT" install -d -m 0755 \ "$RELEASE_ROOT/ptlinux" \ "$RELEASE_ROOT/ptlinux/native" \ "$RELEASE_ROOT/ptdiag" \ "$RELEASE_ROOT/packaging" \ "$RELEASE_ROOT/release" \ "$RELEASE_ROOT/scripts" shopt -s globstar nullglob for SOURCE_MODULE in "${PACKAGE_SOURCE_ROOT}/ptlinux/"**/*.py do RELATIVE_MODULE="${SOURCE_MODULE#"${PACKAGE_SOURCE_ROOT}/ptlinux/"}" install -D -m 0644 \ "$SOURCE_MODULE" \ "${RELEASE_ROOT}/ptlinux/${RELATIVE_MODULE}" done shopt -u globstar nullglob install -m 0644 \ "${PACKAGE_SOURCE_ROOT}/ptlinux/launcher-update-public.pem" \ "$RELEASE_ROOT/ptlinux/launcher-update-public.pem" install -m 0644 \ "${PACKAGE_SOURCE_ROOT}/ptlinux/native/pt-identity-probe.exe" \ "$RELEASE_ROOT/ptlinux/native/pt-identity-probe.exe" install -m 0644 \ "${PACKAGE_SOURCE_ROOT}/ptlinux/native/ptr-bcrypt-proxy.dll" \ "$RELEASE_ROOT/ptlinux/native/ptr-bcrypt-proxy.dll" install -m 0755 \ "${PACKAGE_SOURCE_ROOT}/ptlinux/native/gamescope-3.16.20-ubuntu26.04-amd64" \ "$RELEASE_ROOT/ptlinux/native/gamescope-3.16.20-ubuntu26.04-amd64" install -m 0644 \ "${GAMESCOPE_PROVENANCE_ROOT}/gamescope-compat.README" \ "${GAMESCOPE_PROVENANCE_ROOT}/gamescope-compat.copyright" \ "${GAMESCOPE_PROVENANCE_ROOT}/patches/gamescope-lazy-output-images.patch" \ "${GAMESCOPE_PROVENANCE_ROOT}/patches/gamescope-wayland-live-resize.patch" \ "${GAMESCOPE_PROVENANCE_ROOT}/patches/gamescope-wayland-letterbox-backing.patch" \ "${GAMESCOPE_PROVENANCE_ROOT}/patches/gamescope-wayland-libdecor-input-scope.patch" \ "${GAMESCOPE_PROVENANCE_ROOT}/patches/gamescope-keep-nested-windows-visible.patch" \ "$RELEASE_ROOT/ptlinux/native/" install -m 0644 "${PACKAGE_SOURCE_ROOT}/ptdiag/__init__.py" "$RELEASE_ROOT/ptdiag/__init__.py" install -m 0644 "${PACKAGE_SOURCE_ROOT}/ptdiag/assbin.py" "$RELEASE_ROOT/ptdiag/assbin.py" install -m 0644 "${PACKAGE_SOURCE_ROOT}/ptdiag/logs.py" "$RELEASE_ROOT/ptdiag/logs.py" install -m 0644 \ "${PROJECT_ROOT}/packaging/eu.pristontale.PTLinux.desktop" \ "${PROJECT_ROOT}/packaging/eu.pristontale.PTLinux.png" \ "${PROJECT_ROOT}/packaging/install-dependencies.sh" \ "$RELEASE_ROOT/packaging/" install -m 0755 "${PROJECT_ROOT}/packaging/pristontale-eu" "$RELEASE_ROOT/packaging/pristontale-eu" install -m 0644 \ "${PROJECT_ROOT}/release/config.json" \ "$RELEASE_ROOT/release/config.json" install -m 0644 \ "${PROJECT_ROOT}/scripts/__init__.py" \ "${PROJECT_ROOT}/scripts/repository_paths.py" \ "${PROJECT_ROOT}/scripts/release_lib.py" \ "$RELEASE_ROOT/scripts/" install -m 0755 "${PROJECT_ROOT}/install.sh" "$RELEASE_ROOT/install.sh" install -m 0644 "${PROJECT_ROOT}/RELEASE_README.md" "$RELEASE_ROOT/README.md" install -d "$OUTPUT_DIRECTORY" tar \ --sort=name \ --owner=0 \ --group=0 \ --numeric-owner \ --mtime="@${SOURCE_DATE_EPOCH:-0}" \ -C "$STAGING_DIRECTORY" \ -czf "${OUTPUT_DIRECTORY}/${ARCHIVE_NAME}.tar.gz" \ "$ARCHIVE_NAME" chmod 0644 "${OUTPUT_DIRECTORY}/${ARCHIVE_NAME}.tar.gz" ( cd "$OUTPUT_DIRECTORY" sha256sum "${ARCHIVE_NAME}.tar.gz" > "${ARCHIVE_NAME}.tar.gz.sha256" ) chmod 0644 "${OUTPUT_DIRECTORY}/${ARCHIVE_NAME}.tar.gz.sha256" if ! command -v zip >/dev/null 2>&1 then printf 'Missing release dependency: zip\n' >&2 exit 1 fi ZIP_TIMESTAMP_EPOCH="${SOURCE_DATE_EPOCH:-315532800}" if (( ZIP_TIMESTAMP_EPOCH < 315532800 )) then # ZIP timestamps cannot represent dates before 1980-01-01. ZIP_TIMESTAMP_EPOCH=315532800 fi find "$RELEASE_ROOT" -exec touch -h -d "@${ZIP_TIMESTAMP_EPOCH}" {} + ( cd "$STAGING_DIRECTORY" LC_ALL=C find "$ARCHIVE_NAME" -print \ | LC_ALL=C sort \ | zip -X -q "${OUTPUT_DIRECTORY}/${ARCHIVE_NAME}.zip" -@ ) chmod 0644 "${OUTPUT_DIRECTORY}/${ARCHIVE_NAME}.zip" ( cd "$OUTPUT_DIRECTORY" sha256sum "${ARCHIVE_NAME}.zip" > "${ARCHIVE_NAME}.zip.sha256" ) chmod 0644 "${OUTPUT_DIRECTORY}/${ARCHIVE_NAME}.zip.sha256" PUBLIC_KEY_ARTIFACT="${OUTPUT_DIRECTORY}/launcher-update-public.pem" install -m 0644 \ "${PACKAGE_SOURCE_ROOT}/ptlinux/launcher-update-public.pem" \ "$PUBLIC_KEY_ARTIFACT" for installer_archive in \ "${OUTPUT_DIRECTORY}/${ARCHIVE_NAME}.tar.gz" \ "${OUTPUT_DIRECTORY}/${ARCHIVE_NAME}.zip" do if [[ -n "${PTLINUX_SIGNING_KEY:-}" ]] then "$PYTHON_BIN" "${PROJECT_ROOT}/tools/sign_release_artifact.py" \ --signing-key "$PTLINUX_SIGNING_KEY" \ --public-key "${PACKAGE_SOURCE_ROOT}/ptlinux/launcher-update-public.pem" \ "$installer_archive" else rm -f -- "${installer_archive}.sig" fi done UPDATE_COMMAND=( "$PYTHON_BIN" "${PROJECT_ROOT}/tools/build_launcher_update.py" --version "$VERSION" --output-directory "$OUTPUT_DIRECTORY" --repository-url "${PTLINUX_REPOSITORY_URL:-https://git.m4ko.net/m4ko/pristontale-eu-linux}" ) if [[ -n "${SOURCE_DATE_EPOCH:-}" ]] then UPDATE_COMMAND+=(--source-date-epoch "$SOURCE_DATE_EPOCH") fi if [[ -n "${PTLINUX_SIGNING_KEY:-}" ]] then UPDATE_COMMAND+=(--signing-key "$PTLINUX_SIGNING_KEY") fi if [[ "$RELEASE_MODE" == "production" ]] then UPDATE_COMMAND+=(--require-signature) fi "${UPDATE_COMMAND[@]}" printf '%s\n' "${OUTPUT_DIRECTORY}/${ARCHIVE_NAME}.tar.gz"