mirror of
https://github.com/Cheviiot/Nivora.git
synced 2026-08-03 15:51:12 +00:00
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:
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
launcher="${repo_root}/claude-desktop/claude-alt"
|
||||
icon="${repo_root}/claude-desktop/claude-alt.png"
|
||||
primary_tray_icon="${repo_root}/claude-desktop/claude-tray-orange.png"
|
||||
alternate_tray_icon="${repo_root}/claude-desktop/claude-alt-tray-turquoise.png"
|
||||
temp_dir="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
find "$temp_dir" -mindepth 1 -delete
|
||||
rmdir "$temp_dir"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
mkdir -p "${temp_dir}/bin" "${temp_dir}/home"
|
||||
log="${temp_dir}/launch.log"
|
||||
|
||||
cat >"${temp_dir}/bin/claude-alt-bin" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
printf 'desktop=%s\n' "${CHROME_DESKTOP:-}" >"$NIVORA_TEST_LOG"
|
||||
printf 'arg=%s\n' "$@" >>"$NIVORA_TEST_LOG"
|
||||
EOF
|
||||
chmod 0755 "${temp_dir}/bin/claude-alt-bin"
|
||||
|
||||
export HOME="${temp_dir}/home"
|
||||
export CLAUDE_ALT_EXECUTABLE="${temp_dir}/bin/claude-alt-bin"
|
||||
export NIVORA_TEST_LOG="$log"
|
||||
|
||||
unset XDG_CONFIG_HOME CLAUDE_ALT_DATA_DIR CLAUDE_DESKTOP_ACCOUNT2_DIR
|
||||
"$launcher" 'claude://claude.ai/new'
|
||||
grep -Fxq 'desktop=com.anthropic.ClaudeAlt.desktop' "$log"
|
||||
grep -Fxq "arg=--user-data-dir=${HOME}/.config/ClaudeAlt" "$log"
|
||||
grep -Fxq 'arg=--class=com.anthropic.ClaudeAlt' "$log"
|
||||
grep -Fxq 'arg=claude://claude.ai/new' "$log"
|
||||
test -d "${HOME}/.config/ClaudeAlt"
|
||||
|
||||
legacy_config="${temp_dir}/legacy-config"
|
||||
mkdir -p "${legacy_config}/Claude-Account-2"
|
||||
export XDG_CONFIG_HOME="$legacy_config"
|
||||
"$launcher"
|
||||
grep -Fxq "arg=--user-data-dir=${legacy_config}/Claude-Account-2" "$log"
|
||||
|
||||
explicit_profile="${temp_dir}/explicit-profile"
|
||||
export CLAUDE_ALT_DATA_DIR="$explicit_profile"
|
||||
"$launcher"
|
||||
grep -Fxq "arg=--user-data-dir=${explicit_profile}" "$log"
|
||||
test -d "$explicit_profile"
|
||||
|
||||
python3 - "$icon" "$primary_tray_icon" "$alternate_tray_icon" <<'PY'
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
expected_sizes = {
|
||||
Path(sys.argv[1]): (512, 512),
|
||||
Path(sys.argv[2]): (64, 64),
|
||||
Path(sys.argv[3]): (64, 64),
|
||||
}
|
||||
|
||||
for path, expected_size in expected_sizes.items():
|
||||
data = path.read_bytes()
|
||||
if data[:8] != b"\x89PNG\r\n\x1a\n":
|
||||
raise SystemExit(f"{path.name} is not a PNG")
|
||||
|
||||
width, height, bit_depth, color_type = struct.unpack(">IIBB", data[16:26])
|
||||
if (width, height) != expected_size:
|
||||
raise SystemExit(
|
||||
f"{path.name} must be {expected_size[0]}x{expected_size[1]}, "
|
||||
f"got {width}x{height}"
|
||||
)
|
||||
if bit_depth != 8 or color_type not in (4, 6):
|
||||
raise SystemExit(f"{path.name} must be an 8-bit PNG with alpha")
|
||||
PY
|
||||
|
||||
echo 'OK: ClaudeAlt launcher'
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd "${script_dir}/.." && pwd)"
|
||||
recipe="${repo_root}/codex/Staplerfile"
|
||||
driver="${repo_root}/codex/patch-computer-use.js"
|
||||
|
||||
grep -Fq 'release=1' "$recipe"
|
||||
grep -Fq '15f3bd4fabc44bc0afba85b16d0121f0b4abd052' "$recipe"
|
||||
grep -Fq 'computer-use-linux/archive/refs/tags/v0.4.1.tar.gz' "$recipe"
|
||||
grep -Fq 'cargo build' "$recipe"
|
||||
grep -Fq 'CODEX_LINUX_ENABLE_COMPUTER_USE_UI = "1"' "$driver"
|
||||
grep -Fq 'applyLinuxComputerUseHostPlatformPatch' "$driver"
|
||||
grep -Fq 'applyLinuxComputerUseInstallFlowPatch' "$driver"
|
||||
grep -Fq 'codexLinuxNativeDesktopApps(' "$driver"
|
||||
grep -Fq 'codexLinuxRegisterComputerUseCursorHandler' "$driver"
|
||||
grep -Fq 'isHostCompatiblePlatform:' "$driver"
|
||||
grep -Fq 'computer-use@openai-bundled' "${repo_root}/docs/packages/codex.md"
|
||||
|
||||
python3 - "$repo_root" <<'PY'
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(sys.argv[1])
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"asar_tree", root / "codex" / "asar_tree.py"
|
||||
)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
work = Path(temporary)
|
||||
content = b"upstream Codex fixture\n"
|
||||
digest = hashlib.sha256(content).hexdigest()
|
||||
header = {
|
||||
"files": {
|
||||
"fixture.txt": {
|
||||
"size": len(content),
|
||||
"offset": "0",
|
||||
"integrity": {
|
||||
"algorithm": "SHA256",
|
||||
"hash": digest,
|
||||
"blockSize": 4194304,
|
||||
"blocks": [digest],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
encoded = json.dumps(header, separators=(",", ":")).encode()
|
||||
padded = module.align4(len(encoded))
|
||||
payload_size = 4 + padded
|
||||
archive = work / "fixture.asar"
|
||||
archive.write_bytes(
|
||||
struct.pack("<IIII", 4, 4 + payload_size, payload_size, len(encoded))
|
||||
+ encoded
|
||||
+ b"\0" * (padded - len(encoded))
|
||||
+ content
|
||||
)
|
||||
extracted = work / "tree"
|
||||
rebuilt = work / "rebuilt.asar"
|
||||
module.extract(archive, extracted)
|
||||
module.rebuild(archive, extracted, rebuilt)
|
||||
assert rebuilt.read_bytes() == archive.read_bytes()
|
||||
|
||||
print("OK: Codex Computer Use использует закреплённую upstream-реализацию")
|
||||
PY
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd "${script_dir}/.." && pwd)"
|
||||
launcher="${repo_root}/happ/happ-launcher"
|
||||
staplerfile="${repo_root}/happ/Staplerfile"
|
||||
tmp_dir="$(mktemp -d)"
|
||||
trap 'rm -r -- "$tmp_dir"' EXIT
|
||||
|
||||
cp "$launcher" "${tmp_dir}/launcher-env"
|
||||
sed -i 's|/opt/happ/bin/Happ|/usr/bin/env|' "${tmp_dir}/launcher-env"
|
||||
|
||||
default_env="$(env -u QT_QPA_PLATFORMTHEME "${tmp_dir}/launcher-env")"
|
||||
grep -qx 'QT_QPA_PLATFORMTHEME=xdgdesktopportal' <<<"$default_env"
|
||||
|
||||
custom_env="$(QT_QPA_PLATFORMTHEME=qt6ct "${tmp_dir}/launcher-env")"
|
||||
grep -qx 'QT_QPA_PLATFORMTHEME=qt6ct' <<<"$custom_env"
|
||||
|
||||
cp "$launcher" "${tmp_dir}/launcher-args"
|
||||
sed -i 's|/opt/happ/bin/Happ|/usr/bin/printf|' "${tmp_dir}/launcher-args"
|
||||
forwarded="$("${tmp_dir}/launcher-args" '%s:%s' first second)"
|
||||
[[ "$forwarded" == 'first:second' ]]
|
||||
|
||||
grep -Fq "sed -i 's|^Exec=/opt/happ/bin/Happ|Exec=/usr/bin/happ|'" "$staplerfile"
|
||||
|
||||
echo 'OK: launcher Happ использует системную тему через XDG Desktop Portal'
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
temp_dir="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
find "$temp_dir" -mindepth 1 -delete
|
||||
rmdir "$temp_dir"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
mkdir -p "${temp_dir}/bin"
|
||||
log="${temp_dir}/commands.log"
|
||||
|
||||
cat >"${temp_dir}/bin/stplr" <<'EOF'
|
||||
#!/bin/bash
|
||||
printf 'stplr' >>"$NIVORA_TEST_LOG"
|
||||
printf ' %s' "$@" >>"$NIVORA_TEST_LOG"
|
||||
printf '\n' >>"$NIVORA_TEST_LOG"
|
||||
EOF
|
||||
|
||||
cat >"${temp_dir}/bin/test-sudo" <<'EOF'
|
||||
#!/bin/bash
|
||||
printf 'sudo' >>"$NIVORA_TEST_LOG"
|
||||
printf ' %s' "$@" >>"$NIVORA_TEST_LOG"
|
||||
printf '\n' >>"$NIVORA_TEST_LOG"
|
||||
exec "$@"
|
||||
EOF
|
||||
|
||||
chmod 0755 "${temp_dir}/bin/"*
|
||||
for alias in sli slii sl; do
|
||||
ln -s "$repo_root/nivora-stplr/nivora-stplr" "${temp_dir}/bin/${alias}"
|
||||
done
|
||||
|
||||
export NIVORA_TEST_LOG="$log"
|
||||
export NIVORA_STPLR_SUDO=test-sudo
|
||||
export NIVORA_STPLR_QUIET=1
|
||||
export PATH="${temp_dir}/bin:${PATH}"
|
||||
|
||||
sli --repo nivora parsec --clean
|
||||
slii nivora/codex
|
||||
|
||||
cat >"${temp_dir}/expected" <<'EOF'
|
||||
sudo stplr install nivora/parsec --clean
|
||||
stplr install nivora/parsec --clean
|
||||
stplr info nivora/codex
|
||||
EOF
|
||||
|
||||
diff -u "${temp_dir}/expected" "$log"
|
||||
echo 'OK: Nivora helper'
|
||||
@@ -0,0 +1,57 @@
|
||||
import importlib.util
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
MODULE_PATH = Path(__file__).resolve().parents[1] / "tools/validate_repo.py"
|
||||
SPEC = importlib.util.spec_from_file_location("validate_repo", MODULE_PATH)
|
||||
assert SPEC and SPEC.loader
|
||||
VALIDATOR = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(VALIDATOR)
|
||||
|
||||
|
||||
class ParserTests(unittest.TestCase):
|
||||
def test_scalar_and_array(self):
|
||||
text = "name='demo'\narchitectures=('amd64' 'arm64')\n"
|
||||
self.assertEqual(VALIDATOR.scalar(text, "name"), "demo")
|
||||
self.assertEqual(
|
||||
VALIDATOR.array(text, "architectures"), ["amd64", "arm64"]
|
||||
)
|
||||
|
||||
def test_source_arrays_include_architectures(self):
|
||||
text = "sources=('one')\n\nsources_arm64=(\n 'two'\n)\n"
|
||||
self.assertEqual(
|
||||
VALIDATOR.source_arrays(text),
|
||||
{"sources": ["one"], "sources_arm64": ["two"]},
|
||||
)
|
||||
|
||||
def test_local_source_rejects_traversal(self):
|
||||
self.assertEqual(VALIDATOR.local_source_name("local:///LICENSE"), "LICENSE")
|
||||
self.assertEqual(VALIDATOR.local_source_name("local:///../secret"), "")
|
||||
self.assertIsNone(VALIDATOR.local_source_name("https://example.com/file"))
|
||||
|
||||
def test_markdown_targets(self):
|
||||
text = "[Doc](docs/guide.md) <img src='assets/icon.svg'>"
|
||||
self.assertEqual(
|
||||
VALIDATOR.markdown_targets(text), {"docs/guide.md", "assets/icon.svg"}
|
||||
)
|
||||
|
||||
|
||||
class LinkTests(unittest.TestCase):
|
||||
def test_missing_link_is_reported(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = Path(directory) / "README.md"
|
||||
path.write_text("[missing](no.md)\n", encoding="utf-8")
|
||||
old_root = VALIDATOR.ROOT
|
||||
VALIDATOR.ROOT = Path(directory)
|
||||
try:
|
||||
errors = []
|
||||
VALIDATOR.validate_links(path, errors)
|
||||
self.assertEqual(len(errors), 1)
|
||||
finally:
|
||||
VALIDATOR.ROOT = old_root
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
package_dir="${repo_root}/ventoy"
|
||||
launcher="${package_dir}/ventoy"
|
||||
desktop="${package_dir}/ventoy.desktop"
|
||||
recipe="${package_dir}/Staplerfile"
|
||||
temp_dir="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
find "$temp_dir" -mindepth 1 -delete
|
||||
rmdir "$temp_dir"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
case "$(uname -m)" in
|
||||
x86_64 | amd64)
|
||||
gui='VentoyGUI.x86_64'
|
||||
;;
|
||||
aarch64 | arm64)
|
||||
gui='VentoyGUI.aarch64'
|
||||
;;
|
||||
*)
|
||||
echo 'SKIP: архитектура не поддерживается тестом Ventoy'
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
cat >"${temp_dir}/${gui}" <<'EOF'
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
printf 'cwd=%s\n' "$PWD" >"$NIVORA_TEST_LOG"
|
||||
printf 'arg=%s\n' "$@" >>"$NIVORA_TEST_LOG"
|
||||
EOF
|
||||
chmod 0755 "${temp_dir}/${gui}"
|
||||
|
||||
export VENTOY_INSTALL_DIR="$temp_dir"
|
||||
export NIVORA_TEST_LOG="${temp_dir}/launch.log"
|
||||
"$launcher" --qt5
|
||||
|
||||
grep -Fxq "cwd=${temp_dir}" "$NIVORA_TEST_LOG"
|
||||
grep -Fxq 'arg=--qt5' "$NIVORA_TEST_LOG"
|
||||
grep -Fxq 'Name=Ventoy' "$desktop"
|
||||
grep -Fxq 'Exec=ventoy' "$desktop"
|
||||
grep -Fxq 'Icon=ventoy' "$desktop"
|
||||
grep -Fq "architectures=('amd64' 'arm64')" "$recipe"
|
||||
grep -Fq "VentoyGUI.\${candidate}" "$recipe"
|
||||
grep -Fq "case \"\$(uname -m)\" in" "$recipe"
|
||||
|
||||
echo 'OK: Ventoy launcher'
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd "${script_dir}/.." && pwd)"
|
||||
recipe="${repo_root}/yandex-browser-stable/Staplerfile"
|
||||
postinstall="${repo_root}/yandex-browser-stable/postinstall.sh"
|
||||
|
||||
grep -Fq 'https://repo.yandex.ru/yandex-browser/deb/' "$recipe"
|
||||
grep -Fq '/usr/bin/yandex-browser-stable' "$recipe"
|
||||
grep -Fq 'extracted/usr/share/applications' "$recipe"
|
||||
grep -Fq 'compatibility_desktop=' "$recipe"
|
||||
grep -Fq 'canonical_desktop=' "$recipe"
|
||||
grep -Fq "sed -i '/^NoDisplay=true\$/d'" "$recipe"
|
||||
grep -Fq "sed -i '/^\\[Desktop Entry\\]\$/a NoDisplay=true'" "$recipe"
|
||||
grep -Fq 'update_codecs' "$postinstall"
|
||||
|
||||
if grep -Eq '/etc/(cron|xdg/autostart)' "$recipe"; then
|
||||
echo 'Yandex Browser recipe must not package upstream auto-update hooks' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 'OK: Yandex Browser управляется Stapler без upstream cron-задач'
|
||||
Reference in New Issue
Block a user