mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-01 18:32:13 +00:00
Merge pull request #465 from jcastro/fix/figurine-release-architecture
fix: update Figurine to 2.1.1 and select matching Linux binaries
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
name: Figurine installation
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/test-figurine.yml'
|
||||||
|
- 'tests/test_figurine_installation.py'
|
||||||
|
- 'scripts/post_install/customizable_post_install.sh'
|
||||||
|
- 'AppImage/scripts/post_install_versions.py'
|
||||||
|
- 'AppImage/scripts/flask_proxmenux_routes.py'
|
||||||
|
push:
|
||||||
|
branches: [main, develop]
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/test-figurine.yml'
|
||||||
|
- 'tests/test_figurine_installation.py'
|
||||||
|
- 'scripts/post_install/customizable_post_install.sh'
|
||||||
|
- 'AppImage/scripts/post_install_versions.py'
|
||||||
|
- 'AppImage/scripts/flask_proxmenux_routes.py'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
installer-and-release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
- name: Check shell syntax and isolated installer regressions
|
||||||
|
run: |
|
||||||
|
bash -n scripts/post_install/customizable_post_install.sh
|
||||||
|
python3 -B -m unittest discover -s tests -p test_figurine_installation.py -v
|
||||||
|
- name: Install ARM user-mode emulation for release smoke tests
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y --no-install-recommends qemu-user
|
||||||
|
- name: Verify and run the pinned AMD64, ARM64 and ARMv7 release binaries
|
||||||
|
env:
|
||||||
|
FIGURINE_VERSION: '2.1.1'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
scratch="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$scratch"' EXIT
|
||||||
|
while read -r arch sha; do
|
||||||
|
archive="figurine_linux_${arch}_v${FIGURINE_VERSION}.tar.gz"
|
||||||
|
target="$scratch/$arch"
|
||||||
|
mkdir -p "$target"
|
||||||
|
curl --fail --location --retry 3 --output "$target/$archive" \
|
||||||
|
"https://github.com/arsham/figurine/releases/download/v${FIGURINE_VERSION}/$archive"
|
||||||
|
printf '%s %s\n' "$sha" "$target/$archive" | sha256sum --check
|
||||||
|
tar -xf "$target/$archive" -C "$target"
|
||||||
|
runner=()
|
||||||
|
case "$arch" in
|
||||||
|
arm64) runner=(qemu-aarch64) ;;
|
||||||
|
arm) runner=(qemu-arm) ;;
|
||||||
|
esac
|
||||||
|
"${runner[@]}" "$target/deploy/figurine" --version | tee "$target/version"
|
||||||
|
grep -F "figurine version v$FIGURINE_VERSION" "$target/version"
|
||||||
|
"${runner[@]}" "$target/deploy/figurine" -f "3d.flf" "ProxMenux-test" > "$target/banner"
|
||||||
|
test -s "$target/banner"
|
||||||
|
printf 'PASS: %s version and login banner\n' "$arch"
|
||||||
|
done <<'ASSETS'
|
||||||
|
amd64 f2b1c076942c94009d133df0f304a234a0994bf590b40f2927668324d578306a
|
||||||
|
arm64 611550ddb31a75437be3f8587bcdd029d0d890f4d820b7f494a2c6615fecba10
|
||||||
|
arm 429e66bded486d49f730c4a6559e728cd1e9a163a0f56a85867e2cbec010171d
|
||||||
|
ASSETS
|
||||||
@@ -35,7 +35,7 @@ TOOL_METADATA = {
|
|||||||
'system_utils': {'name': 'System Utilities', 'function': 'install_system_utils', 'version': '1.0'},
|
'system_utils': {'name': 'System Utilities', 'function': 'install_system_utils', 'version': '1.0'},
|
||||||
'network_optimization': {'name': 'Network Optimizations', 'function': 'apply_network_optimizations', 'version': '1.0'},
|
'network_optimization': {'name': 'Network Optimizations', 'function': 'apply_network_optimizations', 'version': '1.0'},
|
||||||
'bashrc_custom': {'name': 'Bashrc Customization', 'function': 'customize_bashrc', 'version': '1.0'},
|
'bashrc_custom': {'name': 'Bashrc Customization', 'function': 'customize_bashrc', 'version': '1.0'},
|
||||||
'figurine': {'name': 'Figurine', 'function': 'configure_figurine', 'version': '1.0'},
|
'figurine': {'name': 'Figurine', 'function': 'configure_figurine', 'version': '1.2'},
|
||||||
'fastfetch': {'name': 'Fastfetch', 'function': 'configure_fastfetch', 'version': '1.0'},
|
'fastfetch': {'name': 'Fastfetch', 'function': 'configure_fastfetch', 'version': '1.0'},
|
||||||
'log2ram': {'name': 'Log2ram (SSD Protection)', 'function': 'configure_log2ram', 'version': '1.0'},
|
'log2ram': {'name': 'Log2ram (SSD Protection)', 'function': 'configure_log2ram', 'version': '1.0'},
|
||||||
'zfs_autotrim': {'name': 'ZFS Autotrim', 'function': 'enable_zfs_autotrim', 'version': '1.0'},
|
'zfs_autotrim': {'name': 'ZFS Autotrim', 'function': 'enable_zfs_autotrim', 'version': '1.0'},
|
||||||
|
|||||||
+2
-1
@@ -8154,5 +8154,6 @@
|
|||||||
"⚠ Disk data will NOT be erased.": "⚠ Festplattendaten werden NICHT gelöscht.",
|
"⚠ Disk data will NOT be erased.": "⚠ Festplattendaten werden NICHT gelöscht.",
|
||||||
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Die Festplatte wird ausgehängt und aus /etc/fstab entfernt.",
|
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Die Festplatte wird ausgehängt und aus /etc/fstab entfernt.",
|
||||||
"⚠ The /etc/fstab entry will be removed.": "⚠ Der /etc/fstab-Eintrag wird entfernt.",
|
"⚠ The /etc/fstab entry will be removed.": "⚠ Der /etc/fstab-Eintrag wird entfernt.",
|
||||||
"⚠ The disk will be unmounted.": "⚠ Die Festplatte wird ausgehängt."
|
"⚠ The disk will be unmounted.": "⚠ Die Festplatte wird ausgehängt.",
|
||||||
|
"Unsupported architecture for Figurine:": "Nicht unterstützte Architektur für Figurine:"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -8126,5 +8126,6 @@
|
|||||||
"⚠ Disk data will NOT be erased.": "⚠ Los datos del disco NO se borrarán.",
|
"⚠ Disk data will NOT be erased.": "⚠ Los datos del disco NO se borrarán.",
|
||||||
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ El disco se desmontará y se eliminará de /etc/fstab.",
|
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ El disco se desmontará y se eliminará de /etc/fstab.",
|
||||||
"⚠ The /etc/fstab entry will be removed.": "⚠ Se eliminará la entrada /etc/fstab.",
|
"⚠ The /etc/fstab entry will be removed.": "⚠ Se eliminará la entrada /etc/fstab.",
|
||||||
"⚠ The disk will be unmounted.": "⚠ El disco se desmontará."
|
"⚠ The disk will be unmounted.": "⚠ El disco se desmontará.",
|
||||||
|
"Unsupported architecture for Figurine:": "Arquitectura no compatible con Figurine:"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -8153,5 +8153,6 @@
|
|||||||
"⚠ Disk data will NOT be erased.": "⚠ Les données du disque ne seront PAS effacées.",
|
"⚠ Disk data will NOT be erased.": "⚠ Les données du disque ne seront PAS effacées.",
|
||||||
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Le disque sera démonté et supprimé de /etc/fstab.",
|
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Le disque sera démonté et supprimé de /etc/fstab.",
|
||||||
"⚠ The /etc/fstab entry will be removed.": "⚠ L'entrée /etc/fstab sera supprimée.",
|
"⚠ The /etc/fstab entry will be removed.": "⚠ L'entrée /etc/fstab sera supprimée.",
|
||||||
"⚠ The disk will be unmounted.": "⚠ Le disque sera démonté."
|
"⚠ The disk will be unmounted.": "⚠ Le disque sera démonté.",
|
||||||
|
"Unsupported architecture for Figurine:": "Architecture non prise en charge pour Figurine :"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -8154,5 +8154,6 @@
|
|||||||
"⚠ Disk data will NOT be erased.": "⚠ I dati del disco NON verranno cancellati.",
|
"⚠ Disk data will NOT be erased.": "⚠ I dati del disco NON verranno cancellati.",
|
||||||
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Il disco verrà smontato e rimosso da /etc/fstab.",
|
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Il disco verrà smontato e rimosso da /etc/fstab.",
|
||||||
"⚠ The /etc/fstab entry will be removed.": "⚠ La voce /etc/fstab verrà rimossa.",
|
"⚠ The /etc/fstab entry will be removed.": "⚠ La voce /etc/fstab verrà rimossa.",
|
||||||
"⚠ The disk will be unmounted.": "⚠ Il disco verrà smontato."
|
"⚠ The disk will be unmounted.": "⚠ Il disco verrà smontato.",
|
||||||
|
"Unsupported architecture for Figurine:": "Architettura non supportata da Figurine:"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -8154,5 +8154,6 @@
|
|||||||
"⚠ Disk data will NOT be erased.": "⚠ Os dados do disco NÃO serão apagados.",
|
"⚠ Disk data will NOT be erased.": "⚠ Os dados do disco NÃO serão apagados.",
|
||||||
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ O disco será desmontado e removido de /etc/fstab.",
|
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ O disco será desmontado e removido de /etc/fstab.",
|
||||||
"⚠ The /etc/fstab entry will be removed.": "⚠ A entrada /etc/fstab será removida.",
|
"⚠ The /etc/fstab entry will be removed.": "⚠ A entrada /etc/fstab será removida.",
|
||||||
"⚠ The disk will be unmounted.": "⚠ O disco será desmontado."
|
"⚠ The disk will be unmounted.": "⚠ O disco será desmontado.",
|
||||||
|
"Unsupported architecture for Figurine:": "Arquitetura não suportada pelo Figurine:"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -8155,5 +8155,6 @@
|
|||||||
"⚠ Disk data will NOT be erased.": "⚠ Dáta na disku sa NEVYMAŽÚ.",
|
"⚠ Disk data will NOT be erased.": "⚠ Dáta na disku sa NEVYMAŽÚ.",
|
||||||
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disk sa odpojí a odstráni z /etc/fstab.",
|
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disk sa odpojí a odstráni z /etc/fstab.",
|
||||||
"⚠ The /etc/fstab entry will be removed.": "⚠ Záznam v /etc/fstab bude odstránený.",
|
"⚠ The /etc/fstab entry will be removed.": "⚠ Záznam v /etc/fstab bude odstránený.",
|
||||||
"⚠ The disk will be unmounted.": "⚠ Disk bude odpojený."
|
"⚠ The disk will be unmounted.": "⚠ Disk bude odpojený.",
|
||||||
|
"Unsupported architecture for Figurine:": "Nepodporovaná architektúra pre Figurine:"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -8154,5 +8154,6 @@
|
|||||||
"⚠ Disk data will NOT be erased.": "⚠ Diskdata kommer INTE att raderas.",
|
"⚠ Disk data will NOT be erased.": "⚠ Diskdata kommer INTE att raderas.",
|
||||||
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disken kommer att avmonteras och tas bort från /etc/fstab.",
|
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disken kommer att avmonteras och tas bort från /etc/fstab.",
|
||||||
"⚠ The /etc/fstab entry will be removed.": "⚠ /etc/fstab-posten kommer att tas bort.",
|
"⚠ The /etc/fstab entry will be removed.": "⚠ /etc/fstab-posten kommer att tas bort.",
|
||||||
"⚠ The disk will be unmounted.": "⚠ Disken kommer att avmonteras."
|
"⚠ The disk will be unmounted.": "⚠ Disken kommer att avmonteras.",
|
||||||
|
"Unsupported architecture for Figurine:": "Arkitekturen stöds inte av Figurine:"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3034,15 +3034,27 @@ register_tool "fastfetch" true "$FUNC_VERSION"
|
|||||||
|
|
||||||
|
|
||||||
configure_figurine() {
|
configure_figurine() {
|
||||||
local FUNC_VERSION="1.1"
|
local FUNC_VERSION="1.2"
|
||||||
pmx_journal_context "configure_figurine" "$FUNC_VERSION"
|
pmx_journal_context "configure_figurine" "$FUNC_VERSION"
|
||||||
# description: Install Figurine (ASCII-art hostname banner) and wire it into the SSH login flow.
|
# description: Install Figurine (ASCII-art hostname banner) and wire it into the SSH login flow.
|
||||||
msg_info2 "$(translate "Installing and configuring Figurine...")"
|
msg_info2 "$(translate "Installing and configuring Figurine...")"
|
||||||
# `FIGURINE_VERSION` env var allows pinning to a specific release;
|
# `FIGURINE_VERSION` env var allows pinning to a specific release;
|
||||||
# default tracks the last tested upstream tag. Audit Tier 6 —
|
# default tracks the last tested upstream tag. Audit Tier 6 —
|
||||||
# recursos remotos sin pinning de versión.
|
# recursos remotos sin pinning de versión.
|
||||||
local version="${FIGURINE_VERSION:-2.0.0}"
|
local version="${FIGURINE_VERSION:-2.1.1}"
|
||||||
local file="figurine_linux_amd64_v${version}.tar.gz"
|
# Prefer the Debian userspace ABI; the upstream 32-bit ARM build requires ARMv7.
|
||||||
|
local arch
|
||||||
|
arch="$(dpkg --print-architecture 2>/dev/null || uname -m)"
|
||||||
|
case "$arch" in
|
||||||
|
amd64|x86_64) arch="amd64" ;;
|
||||||
|
arm64|aarch64) arch="arm64" ;;
|
||||||
|
armhf|armv7l|armv8l) arch="arm" ;;
|
||||||
|
*)
|
||||||
|
msg_error "$(translate "Unsupported architecture for Figurine:") $arch"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
local file="figurine_linux_${arch}_v${version}.tar.gz"
|
||||||
local url="https://github.com/arsham/figurine/releases/download/v${version}/${file}"
|
local url="https://github.com/arsham/figurine/releases/download/v${version}/${file}"
|
||||||
local temp_dir; temp_dir=$(mktemp -d)
|
local temp_dir; temp_dir=$(mktemp -d)
|
||||||
local install_dir="/usr/local/bin"
|
local install_dir="/usr/local/bin"
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
"""Exercise the real Figurine installer with isolated files and no network/root access."""
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import importlib.util
|
||||||
|
import io
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
SCRIPT = ROOT / "scripts/post_install/customizable_post_install.sh"
|
||||||
|
FUNCTION = re.search(
|
||||||
|
r"^configure_figurine\(\) \{\n.*?^\}", SCRIPT.read_text(), re.M | re.S
|
||||||
|
).group(0)
|
||||||
|
VERSION = re.search(r'FIGURINE_VERSION:-([^}]+)', FUNCTION).group(1)
|
||||||
|
FUNCTION_VERSION = re.search(r'local FUNC_VERSION="([^"]+)"', FUNCTION).group(1)
|
||||||
|
|
||||||
|
|
||||||
|
class FigurineInstallation(unittest.TestCase):
|
||||||
|
def run_installer(self, arch="amd64", *, kernel="x86_64", dpkg_exit=0,
|
||||||
|
release=None, download_exit=0, package="valid", existing=True):
|
||||||
|
with tempfile.TemporaryDirectory(prefix="figurine-test-") as directory:
|
||||||
|
root = Path(directory)
|
||||||
|
binary_dir = root / "bin"
|
||||||
|
profile_dir = root / "profile.d"
|
||||||
|
binary_dir.mkdir()
|
||||||
|
profile_dir.mkdir()
|
||||||
|
(root / "tmp").mkdir()
|
||||||
|
binary = binary_dir / "figurine"
|
||||||
|
profile = profile_dir / "figurine.sh"
|
||||||
|
bashrc = root / "bashrc"
|
||||||
|
if existing:
|
||||||
|
binary.write_text("previous binary\n")
|
||||||
|
binary.chmod(0o755)
|
||||||
|
profile.write_text("previous profile\n")
|
||||||
|
bashrc.write_text("# previous bashrc\n")
|
||||||
|
archive = root / "fixture.tar.gz"
|
||||||
|
payload = b"#!/bin/sh\nprintf 'fixture Figurine\\n'\n"
|
||||||
|
if package == "corrupt":
|
||||||
|
archive.write_bytes(b"invalid tarball")
|
||||||
|
else:
|
||||||
|
with tarfile.open(archive, "w:gz") as tar:
|
||||||
|
name = "deploy/figurine" if package == "valid" else "README"
|
||||||
|
member = tarfile.TarInfo(name)
|
||||||
|
member.size = len(payload)
|
||||||
|
tar.addfile(member, io.BytesIO(payload))
|
||||||
|
|
||||||
|
# Relocate every installer filesystem boundary, including its login script.
|
||||||
|
function = (FUNCTION.replace("/usr/local/bin", str(binary_dir))
|
||||||
|
.replace("/etc/profile.d", str(profile_dir))
|
||||||
|
.replace("/root/.bashrc", str(bashrc)))
|
||||||
|
harness = r'''
|
||||||
|
translate() { printf '%s' "$1"; }
|
||||||
|
msg_info2() { :; }
|
||||||
|
msg_info() { :; }
|
||||||
|
msg_ok() { :; }
|
||||||
|
msg_success() { :; }
|
||||||
|
msg_error() { printf '%s\n' "$*" >&2; }
|
||||||
|
pmx_journal_context() { :; }
|
||||||
|
pmx_write_file() { cat > "$1"; }
|
||||||
|
pmx_append_file() { cat >> "$1"; }
|
||||||
|
pmx_edit_file() { :; }
|
||||||
|
chown() { :; }
|
||||||
|
dpkg() {
|
||||||
|
[[ "$TEST_DPKG_EXIT" == 0 ]] || return "$TEST_DPKG_EXIT"
|
||||||
|
printf '%s\n' "$TEST_ARCH"
|
||||||
|
}
|
||||||
|
uname() { printf '%s\n' "$TEST_KERNEL"; }
|
||||||
|
wget() {
|
||||||
|
[[ "$1" == -qO ]] || return 90
|
||||||
|
printf '%s\n' "$3" >> "$TEST_ROOT/downloads"
|
||||||
|
[[ "$TEST_DOWNLOAD_EXIT" == 0 ]] || return "$TEST_DOWNLOAD_EXIT"
|
||||||
|
cp "$TEST_ROOT/fixture.tar.gz" "$2"
|
||||||
|
}
|
||||||
|
register_tool() { printf '%s\n' "$*" > "$TEST_ROOT/registered"; }
|
||||||
|
'''
|
||||||
|
harness += function + "\nconfigure_figurine\nexit \"$?\"\n"
|
||||||
|
environment = {
|
||||||
|
"PATH": f"{binary_dir}:/usr/bin:/bin",
|
||||||
|
"HOME": str(root),
|
||||||
|
"TMPDIR": str(root / "tmp"),
|
||||||
|
"LC_ALL": "C",
|
||||||
|
"TEST_ROOT": str(root),
|
||||||
|
"TEST_ARCH": arch,
|
||||||
|
"TEST_DPKG_EXIT": str(dpkg_exit),
|
||||||
|
"TEST_KERNEL": kernel,
|
||||||
|
"TEST_DOWNLOAD_EXIT": str(download_exit),
|
||||||
|
}
|
||||||
|
if release is not None:
|
||||||
|
environment["FIGURINE_VERSION"] = release
|
||||||
|
result = subprocess.run(
|
||||||
|
["/bin/bash", "--noprofile", "--norc"], input=harness,
|
||||||
|
text=True, capture_output=True, env=environment, timeout=10,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"result": result,
|
||||||
|
"binary": binary.read_bytes(),
|
||||||
|
"mode": binary.stat().st_mode & 0o777,
|
||||||
|
"profile": profile.read_text(),
|
||||||
|
"bashrc": bashrc.read_text(),
|
||||||
|
"downloads": (root / "downloads").read_text() if (root / "downloads").exists() else "",
|
||||||
|
"registered": (root / "registered").read_text() if (root / "registered").exists() else "",
|
||||||
|
"payload": payload,
|
||||||
|
"binary_path": str(binary),
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_installs_release_matching_debian_userspace(self):
|
||||||
|
for arch, asset in (("amd64", "amd64"), ("arm64", "arm64"), ("armhf", "arm")):
|
||||||
|
with self.subTest(arch=arch):
|
||||||
|
state = self.run_installer(arch)
|
||||||
|
self.assertEqual(state["result"].returncode, 0, state["result"].stderr)
|
||||||
|
self.assertEqual(state["downloads"],
|
||||||
|
f"https://github.com/arsham/figurine/releases/download/v{VERSION}/"
|
||||||
|
f"figurine_linux_{asset}_v{VERSION}.tar.gz\n")
|
||||||
|
self.assertEqual(state["binary"], state["payload"])
|
||||||
|
self.assertEqual(state["mode"], 0o755)
|
||||||
|
self.assertIn(f'{state["binary_path"]} -f "3d.flf" $(hostname)', state["profile"])
|
||||||
|
self.assertEqual(state["registered"], f"figurine true {FUNCTION_VERSION}\n")
|
||||||
|
|
||||||
|
def test_falls_back_to_kernel_when_dpkg_is_unavailable(self):
|
||||||
|
for kernel, asset in (("x86_64", "amd64"), ("aarch64", "arm64"),
|
||||||
|
("armv7l", "arm"), ("armv8l", "arm")):
|
||||||
|
with self.subTest(kernel=kernel):
|
||||||
|
state = self.run_installer("", kernel=kernel, dpkg_exit=127)
|
||||||
|
self.assertEqual(state["result"].returncode, 0, state["result"].stderr)
|
||||||
|
self.assertIn(f"figurine_linux_{asset}_v{VERSION}.tar.gz", state["downloads"])
|
||||||
|
|
||||||
|
def test_userspace_takes_precedence_over_kernel(self):
|
||||||
|
state = self.run_installer("armhf", kernel="aarch64")
|
||||||
|
self.assertEqual(state["result"].returncode, 0, state["result"].stderr)
|
||||||
|
self.assertIn(f"figurine_linux_arm_v{VERSION}.tar.gz", state["downloads"])
|
||||||
|
|
||||||
|
def test_fresh_install_creates_binary_banner_and_aliases(self):
|
||||||
|
state = self.run_installer(existing=False)
|
||||||
|
self.assertEqual(state["result"].returncode, 0, state["result"].stderr)
|
||||||
|
self.assertEqual(state["binary"], state["payload"])
|
||||||
|
self.assertIn(' -f "3d.flf" $(hostname)', state["profile"])
|
||||||
|
self.assertIn("shopt -s expand_aliases", state["bashrc"])
|
||||||
|
self.assertIn("alias aptup='apt update && apt dist-upgrade'", state["bashrc"])
|
||||||
|
self.assertEqual(state["registered"], f"figurine true {FUNCTION_VERSION}\n")
|
||||||
|
|
||||||
|
def test_unsupported_architecture_fails_before_changes_or_download(self):
|
||||||
|
for arch in ("i386", "riscv64", "armel", "armv6l", "ppc64el", ""):
|
||||||
|
with self.subTest(arch=arch):
|
||||||
|
state = self.run_installer(arch)
|
||||||
|
self.assertNotEqual(state["result"].returncode, 0)
|
||||||
|
self.assertIn("Unsupported architecture for Figurine:", state["result"].stderr)
|
||||||
|
self.assertEqual(state["downloads"], "")
|
||||||
|
self.assert_preserved(state)
|
||||||
|
self.assertEqual(state["bashrc"], "# previous bashrc\n")
|
||||||
|
|
||||||
|
def assert_preserved(self, state):
|
||||||
|
self.assertEqual(state["binary"], b"previous binary\n")
|
||||||
|
self.assertEqual(state["profile"], "previous profile\n")
|
||||||
|
self.assertEqual(state["registered"], "")
|
||||||
|
|
||||||
|
def test_download_failure_preserves_previous_installation(self):
|
||||||
|
state = self.run_installer(download_exit=8)
|
||||||
|
self.assertNotEqual(state["result"].returncode, 0)
|
||||||
|
self.assertIn("Failed to download Figurine", state["result"].stderr)
|
||||||
|
self.assert_preserved(state)
|
||||||
|
|
||||||
|
def test_invalid_packages_preserve_previous_installation(self):
|
||||||
|
for package, error in (("corrupt", "Failed to extract package"),
|
||||||
|
("missing", "Binary not found in extracted content.")):
|
||||||
|
with self.subTest(package=package):
|
||||||
|
state = self.run_installer(package=package)
|
||||||
|
self.assertNotEqual(state["result"].returncode, 0)
|
||||||
|
self.assertIn(error, state["result"].stderr)
|
||||||
|
self.assert_preserved(state)
|
||||||
|
|
||||||
|
def test_release_override_is_preserved(self):
|
||||||
|
state = self.run_installer("arm64", release="2.1.0")
|
||||||
|
self.assertEqual(state["result"].returncode, 0, state["result"].stderr)
|
||||||
|
self.assertIn("/v2.1.0/figurine_linux_arm64_v2.1.0.tar.gz", state["downloads"])
|
||||||
|
|
||||||
|
def test_existing_installation_is_offered_the_update_once(self):
|
||||||
|
spec = importlib.util.spec_from_file_location(
|
||||||
|
"post_install_versions", ROOT / "AppImage/scripts/post_install_versions.py")
|
||||||
|
versions = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(versions)
|
||||||
|
metadata = versions.parse_post_install_script(SCRIPT)
|
||||||
|
installed = {"figurine": {"installed": True, "version": "1.1", "source": "custom"}}
|
||||||
|
updates = versions._detect_updates({}, metadata, installed)
|
||||||
|
self.assertEqual(len(updates), 1)
|
||||||
|
self.assertEqual(updates[0]["available_version"], FUNCTION_VERSION)
|
||||||
|
self.assertEqual(updates[0]["function"], "configure_figurine")
|
||||||
|
installed["figurine"]["version"] = FUNCTION_VERSION
|
||||||
|
self.assertEqual(versions._detect_updates({}, metadata, installed), [])
|
||||||
|
|
||||||
|
tree = ast.parse((ROOT / "AppImage/scripts/flask_proxmenux_routes.py").read_text())
|
||||||
|
fallback = next(ast.literal_eval(node.value) for node in tree.body
|
||||||
|
if isinstance(node, ast.Assign)
|
||||||
|
and any(isinstance(t, ast.Name) and t.id == "TOOL_METADATA"
|
||||||
|
for t in node.targets))
|
||||||
|
self.assertEqual(fallback["figurine"]["version"], FUNCTION_VERSION)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -312,25 +312,31 @@ EOF
|
|||||||
<p className="text-lg mb-2">{t("figurine.automates")}</p>
|
<p className="text-lg mb-2">{t("figurine.automates")}</p>
|
||||||
<CopyableCode
|
<CopyableCode
|
||||||
code={`
|
code={`
|
||||||
# Check for previous installation and remove if found
|
|
||||||
if command -v figurine &> /dev/null; then
|
|
||||||
rm -f "/usr/local/bin/figurine"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Download and install Figurine
|
# Download and install Figurine
|
||||||
version="2.0.0"
|
(
|
||||||
file="figurine_linux_amd64_v\${version}.tar.gz"
|
set -e
|
||||||
|
version="\${FIGURINE_VERSION:-2.1.1}"
|
||||||
|
arch="$(dpkg --print-architecture 2>/dev/null || uname -m)"
|
||||||
|
case "$arch" in
|
||||||
|
amd64|x86_64) arch="amd64" ;;
|
||||||
|
arm64|aarch64) arch="arm64" ;;
|
||||||
|
armhf|armv7l|armv8l) arch="arm" ;;
|
||||||
|
*) echo "Unsupported architecture for Figurine: $arch" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
file="figurine_linux_\${arch}_v\${version}.tar.gz"
|
||||||
url="https://github.com/arsham/figurine/releases/download/v\${version}/\${file}"
|
url="https://github.com/arsham/figurine/releases/download/v\${version}/\${file}"
|
||||||
wget -qO "/tmp/\${file}" "\${url}"
|
temp_dir="$(mktemp -d)"
|
||||||
tar -xf "/tmp/\${file}" -C "/tmp"
|
trap 'rm -rf "$temp_dir"' EXIT
|
||||||
mv "/tmp/deploy/figurine" "/usr/local/bin/figurine"
|
wget -qO "\${temp_dir}/\${file}" "\${url}"
|
||||||
chmod +x "/usr/local/bin/figurine"
|
tar -xf "\${temp_dir}/\${file}" -C "$temp_dir"
|
||||||
|
install -m 0755 "\${temp_dir}/deploy/figurine" "/usr/local/bin/figurine"
|
||||||
|
|
||||||
# Create welcome message script
|
# Create welcome message script
|
||||||
cat << 'EOF' > "/etc/profile.d/figurine.sh"
|
cat << 'EOF' > "/etc/profile.d/figurine.sh"
|
||||||
/usr/local/bin/figurine -f "3d.flf" $(hostname)
|
/usr/local/bin/figurine -f "3d.flf" $(hostname)
|
||||||
EOF
|
EOF
|
||||||
chmod +x "/etc/profile.d/figurine.sh"
|
chmod +x "/etc/profile.d/figurine.sh"
|
||||||
|
)
|
||||||
`}
|
`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -119,9 +119,9 @@
|
|||||||
"intro": "This option installs and configures Figurine, a tool that creates stylish ASCII text banners for your terminal, displaying your hostname in a visually appealing 3D format.",
|
"intro": "This option installs and configures Figurine, a tool that creates stylish ASCII text banners for your terminal, displaying your hostname in a visually appealing 3D format.",
|
||||||
"doesLabel": "What it does:",
|
"doesLabel": "What it does:",
|
||||||
"doesItems": [
|
"doesItems": [
|
||||||
"Downloads and installs Figurine v2.0.0 from GitHub",
|
"Downloads and installs Figurine v2.1.1 from GitHub for the host architecture (AMD64, ARM64 or ARMv7)",
|
||||||
"Creates a welcome message that displays your hostname in 3D ASCII art when you log in",
|
"Creates a welcome message that displays your hostname in 3D ASCII art when you log in",
|
||||||
"Automatically removes any previous Figurine installation if present",
|
"Updates an existing Figurine binary after download and extraction succeed",
|
||||||
"Sets up the welcome message to run automatically at login"
|
"Sets up the welcome message to run automatically at login"
|
||||||
],
|
],
|
||||||
"practicalLabel": "Practical Use:",
|
"practicalLabel": "Practical Use:",
|
||||||
|
|||||||
@@ -119,9 +119,9 @@
|
|||||||
"intro": "Esta opción instala y configura Figurine, una herramienta que crea banners de texto ASCII con estilo para tu terminal, mostrando tu hostname en un formato 3D atractivo visualmente.",
|
"intro": "Esta opción instala y configura Figurine, una herramienta que crea banners de texto ASCII con estilo para tu terminal, mostrando tu hostname en un formato 3D atractivo visualmente.",
|
||||||
"doesLabel": "Qué hace:",
|
"doesLabel": "Qué hace:",
|
||||||
"doesItems": [
|
"doesItems": [
|
||||||
"Descarga e instala Figurine v2.0.0 desde GitHub",
|
"Descarga e instala Figurine v2.1.1 desde GitHub para la arquitectura del host (AMD64, ARM64 o ARMv7)",
|
||||||
"Crea un mensaje de bienvenida que muestra tu hostname en ASCII art 3D al hacer login",
|
"Crea un mensaje de bienvenida que muestra tu hostname en ASCII art 3D al hacer login",
|
||||||
"Elimina automáticamente cualquier instalación previa de Figurine si la hay",
|
"Actualiza el binario existente de Figurine tras descargar y extraer el paquete correctamente",
|
||||||
"Configura el mensaje de bienvenida para que se ejecute automáticamente al hacer login"
|
"Configura el mensaje de bienvenida para que se ejecute automáticamente al hacer login"
|
||||||
],
|
],
|
||||||
"practicalLabel": "Uso práctico:",
|
"practicalLabel": "Uso práctico:",
|
||||||
|
|||||||
Reference in New Issue
Block a user