feat: create autonomous Nivora package repository

Maintain a validated cross-distribution package catalog.
Automate upstream updates, isolated builds, diagnostics, and direct publication.
Build the official GitHub Desktop sources for Linux with working OAuth.
This commit is contained in:
Александр
2026-07-25 00:49:32 +10:00
commit f405ae2bcf
168 changed files with 8483 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
set -euo pipefail
package_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${package_dir}/.." && pwd)"
exec "${repo_root}/tools/package_updates.sh" check "$(basename "$package_dir")"
+21
View File
@@ -0,0 +1,21 @@
Сведения о лицензии пакета Parsec
Лицензия: Custom.
Официальная страница загрузки: https://parsec.app/downloads
Источник Linux-пакета: https://builds.parsec.app/package/parsec-linux.deb
Нюансы пакета:
- Пакет перепаковывает официальный deb-артефакт Parsec для Linux.
- Parsec является проприетарным приложением Unity Technologies SF.
- Использование клиента, учетной записи, удаленного доступа и сетевых функций регулируется условиями Parsec/Unity.
- Пакет устанавливает клиентский бинарник `/usr/bin/parsecd` и runtime-данные `/usr/share/parsec`; учетная запись и удаленные хосты в пакет не входят.
- Команда `/usr/bin/parsecd` запускается через wrapper Nivora, который перед стартом Parsec включает `client_immersive` в пользовательском `~/.parsec/config.json` или, если JSON поврежден, в `~/.parsec/config.txt`. Это нужно, чтобы системные сочетания клавиш, включая переключение раскладки, уходили в удаленную систему, а не меняли локальную раскладку клиента.
- Wrapper учитывает новый формат Parsec 150-89+: если `config.json` является массивом, настройка записывается в объект второго элемента, без удаления существующих параметров. Если пользователь уже включил полный immersive mode через `client_immersive=1`, wrapper сохраняет это значение.
- Для обычного запуска без URL wrapper дополнительно передает `client_immersive=2` через command-line advanced setting. Запуски с `parsec://...` deep-link не переписываются, чтобы не ломать подключение по ссылке.
- Если нужно вернуть полностью upstream-поведение запуска, используйте `PARSEC_NIVORA_IMMERSIVE=0 parsecd` или измените значение `client_immersive` вручную в настройках Parsec.
- Для совместимости на ALT пакет явно требует `libjpeg8` и `libavcodec61`.
- Пакет помечен как несвободный через `nonfree=1`.
Справочные ссылки:
- https://parsec.app/terms
- https://unity.com/legal/privacy-policy
+81
View File
@@ -0,0 +1,81 @@
name='parsec'
version='150-104a'
release=3
summary='Low-latency remote desktop and game streaming client'
summary_ru='Клиент удаленного рабочего стола и игрового стриминга с низкой задержкой'
group='Networking/Remote access'
desc='Parsec is a proprietary low-latency remote desktop and game streaming client distributed as an official Linux deb package for Ubuntu 22.04 LTS Desktop.'
desc_ru='Parsec — проприетарный клиент удаленного рабочего стола и игрового стриминга с низкой задержкой, распространяемый официальным deb-пакетом для Ubuntu 22.04 LTS Desktop.'
homepage='https://parsec.app/downloads'
maintainer='Unity Technologies SF <https://parsec.app/>'
architectures=('amd64')
license=('Custom')
nonfree=1
nonfree_msg='Parsec является проприетарным приложением Unity Technologies SF. Использование клиента, учетной записи, удаленного доступа и сетевых функций регулируется условиями Parsec/Unity.'
nonfree_url='https://parsec.app/terms'
provides=()
replaces=('parsec')
conflicts=()
auto_reqprov_method="dirty"
auto_req=0
auto_prov=0
deps=()
deps_debian=('libc6' 'libgcc-s1 | libgcc1' 'libstdc++6' 'libudev1' 'libssl3 | libssl1.1' 'libx11-6' 'libxcursor1' 'libxi6' 'libsm6' 'libgl1' 'libasound2' 'libjpeg8 | libjpeg-turbo8' 'libpng16-16' 'libcurl4' 'libavcodec61 | libavcodec60 | libavcodec59 | libavcodec58 | libavcodec57' 'libxrandr2' 'libvulkan1')
deps_ubuntu=("${deps_debian[@]}")
deps_fedora=('glibc' 'libgcc' 'libstdc++' 'systemd-libs' 'openssl-libs' 'libX11' 'libXcursor' 'libXi' 'libSM' 'mesa-libGL' 'alsa-lib' 'libjpeg-turbo' 'libpng' 'libcurl' 'ffmpeg-libs' 'libXrandr' 'vulkan-loader')
deps_arch=('glibc' 'gcc-libs' 'systemd-libs' 'openssl' 'libx11' 'libxcursor' 'libxi' 'libsm' 'libglvnd' 'alsa-lib' 'libjpeg-turbo' 'libpng' 'curl' 'ffmpeg' 'libxrandr' 'vulkan-icd-loader')
deps_opensuse=('glibc' 'libgcc_s1' 'libstdc++6' 'libudev1' 'libopenssl3' 'libX11-6' 'libXcursor1' 'libXi6' 'libSM6' 'Mesa-libGL1' 'libasound2' 'libjpeg8' 'libpng16-16' 'libcurl4' 'libavcodec61' 'libXrandr2' 'libvulkan1')
deps_altlinux=('glibc' 'libgcc1' 'libstdc++6' 'libudev0' 'libssl3' 'libX11' 'libXcursor' 'libXi' 'libSM' 'libGL' 'libalsa' 'libjpeg8' 'libpng16' 'libcurl' 'libavcodec61' 'libXrandr' 'libvulkan1')
build_deps=('binutils')
sources=(
"https://builds.parsec.app/package/parsec-linux.deb?~archive=false&~name=parsec.deb"
"local:///parsecd-wrapper"
'local:///LICENSE'
)
checksums=(
'sha256:c8a4407713d72dcb1c59f082977e81e1b9e8ffd679d0666882cde4c8a0821dd2'
'sha256:2830871d7499ac00b70197ea11f2c132382059b4ad8718eb73bdcb28ad13a113'
'sha256:d8df7db0c861a3308e59beaabe617703e0e24a92d569044432e8b2c539f1fb6c'
)
scripts=(
['postinstall']='postinstall.sh'
['postremove']='postremove.sh'
)
package() {
cd "${srcdir}"
mkdir -p "${srcdir}/extracted"
ar x parsec.deb
tar -xf data.tar.* -C "${srcdir}/extracted"
install -Dm755 "${srcdir}/extracted/usr/bin/parsecd" "${pkgdir}/usr/lib/parsec/parsecd"
install -Dm755 parsecd-wrapper "${pkgdir}/usr/bin/parsecd"
install -Dm644 "${srcdir}/extracted/usr/share/applications/parsecd.desktop" "${pkgdir}/usr/share/applications/parsecd.desktop"
sed -i 's/^Categories=.*/Categories=Network;RemoteAccess;/' \
"${pkgdir}/usr/share/applications/parsecd.desktop"
install -Dm644 "${srcdir}/extracted/usr/share/icons/hicolor/256x256/apps/parsecd.png" "${pkgdir}/usr/share/icons/hicolor/256x256/apps/parsecd.png"
mkdir -p "${pkgdir}/usr/share/parsec"
cp -a "${srcdir}/extracted/usr/share/parsec/." "${pkgdir}/usr/share/parsec/"
install-license "${srcdir}/LICENSE" "parsec/LICENSE.nivora"
}
files() {
files-find-binary
files-find-desktop
files-find-license
files-find \
"/usr/lib/parsec/parsecd" \
"/usr/share/icons/hicolor/256x256/apps/parsecd.png" \
"/usr/share/parsec/**/*"
}
+109
View File
@@ -0,0 +1,109 @@
#!/bin/bash
set -euo pipefail
real_binary='/usr/lib/parsec/parsecd'
parsec_home="${PARSEC_CONFIG_HOME:-${HOME}/.parsec}"
config_txt="${parsec_home}/config.txt"
config_json="${parsec_home}/config.json"
json_notice='See https://parsec.app/config for documentation and example. JSON must be valid before saving or file be will be erased.'
immersive_enabled() {
case "${PARSEC_NIVORA_IMMERSIVE:-1}" in
0 | false | no | off) return 1 ;;
*) return 0 ;;
esac
}
set_config_txt_immersive() {
mkdir -p "${parsec_home}"
touch "${config_txt}"
if grep -Eq '^[[:space:]]*client_immersive[[:space:]]*=' "${config_txt}"; then
sed -i -E 's/^[[:space:]]*client_immersive[[:space:]]*=.*/client_immersive=2/' "${config_txt}"
else
printf '\nclient_immersive=2\n' >>"${config_txt}"
fi
}
set_config_json_immersive() {
command -v python3 >/dev/null 2>&1 || return 1
mkdir -p "${parsec_home}"
python3 - "${config_json}" "${json_notice}" <<'PY'
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
notice = sys.argv[2]
data = [notice, {}]
if path.exists() and path.stat().st_size:
try:
with path.open(encoding="utf-8") as fh:
data = json.load(fh)
except json.JSONDecodeError:
raise SystemExit(2)
if isinstance(data, dict):
settings = data
elif isinstance(data, list):
while len(data) < 2:
data.append({})
if not isinstance(data[0], str):
data[0] = notice
if not isinstance(data[1], dict):
data[1] = {}
settings = data[1]
else:
data = [notice, {}]
settings = data[1]
current = settings.get("client_immersive")
value = None
if isinstance(current, dict):
value = current.get("value")
if value in (1, 2, "1", "2"):
immersive_value = int(value)
else:
immersive_value = 2
settings["client_immersive"] = {"value": immersive_value}
tmp = path.with_suffix(path.suffix + ".tmp")
with tmp.open("w", encoding="utf-8") as fh:
json.dump(data, fh, ensure_ascii=False, indent=2, sort_keys=True)
fh.write("\n")
tmp.replace(path)
print(immersive_value)
PY
}
immersive_value=2
if immersive_enabled; then
if ! immersive_value="$(set_config_json_immersive)"; then
set_config_txt_immersive
immersive_value=2
fi
else
exec "${real_binary}" "$@"
fi
for arg in "$@"; do
if [[ "${arg}" == *client_immersive=* ]]; then
exec "${real_binary}" "$@"
fi
done
if (($# == 0)); then
exec "${real_binary}" "client_immersive=${immersive_value}"
fi
if [[ "$1" == *=* ]]; then
first_arg="client_immersive=${immersive_value}:${1}"
shift
exec "${real_binary}" "${first_arg}" "$@"
fi
exec "${real_binary}" "$@"
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
set -euo pipefail
optional_refresh() {
local label=$1
shift
if ! "$@"; then
echo "Предупреждение: не удалось обновить ${label}." >&2
fi
}
if command -v update-desktop-database >/dev/null 2>&1; then
optional_refresh "desktop-базу" update-desktop-database -q /usr/share/applications
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
optional_refresh "кэш иконок" gtk-update-icon-cache -f -q /usr/share/icons/hicolor
fi
if command -v kbuildsycoca6 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca6 --noincremental
elif command -v kbuildsycoca5 >/dev/null 2>&1; then
optional_refresh "кэш KDE" kbuildsycoca5 --noincremental
fi
+1
View File
@@ -0,0 +1 @@
include = "../stapler-repo.toml"