From ebb079215bb8adf1ba2245653df274a8cecf4217 Mon Sep 17 00:00:00 2001 From: wangqiyang Date: Mon, 10 Aug 2026 22:49:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=89=E5=AE=89=E8=A3=85=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=20Firefox=20=E7=A6=BB=E7=BA=BF=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/m9zCtrlTty/firefox_install.go | 118 +++++++++++++++++++++--- apps/m9zCtrlTty/firefox_install_test.go | 23 +++++ 2 files changed, 126 insertions(+), 15 deletions(-) diff --git a/apps/m9zCtrlTty/firefox_install.go b/apps/m9zCtrlTty/firefox_install.go index 22de4f6..1cb1b49 100644 --- a/apps/m9zCtrlTty/firefox_install.go +++ b/apps/m9zCtrlTty/firefox_install.go @@ -46,6 +46,12 @@ var forbiddenFirefoxDebPackages = map[string]struct{}{ "util-linux": {}, } +type firefoxBundleInstallPlan struct { + TotalPackageCount int + HasFirefoxPackage bool + BaseSystemPackages []string +} + func isFirefoxUsable() bool { cmd := exec.Command("firefox", "--version") out, err := cmd.CombinedOutput() @@ -82,10 +88,12 @@ func isExecutableFile(path string) bool { func installFirefox() error { bundlePath := firefoxBundlePath() - if err := validateFirefoxBundle(bundlePath); err != nil { + plan, err := validateFirefoxBundle(bundlePath) + if err != nil { kioskErrorf("firefox install refused bundle=%s err=%v", bundlePath, err) return err } + baseSystemPackages := strings.Join(plan.BaseSystemPackages, " ") var p struct { DebPkgPath string `json:"DebPkgPath"` @@ -107,7 +115,7 @@ echo "skip automatic dpkg purge to avoid touching base system packages" echo "" echo "=== clean previous offline bundle files ===" -rm -rf /opt/firefox-deb /opt/firefox-deb.tar.gz 2>/dev/null +rm -rf /opt/firefox-deb /opt/firefox-installable /opt/firefox-deb.tar.gz 2>/dev/null echo "" echo "=== start Firefox install ===" @@ -119,20 +127,84 @@ echo "2. extract offline bundle..." cd /opt && tar -xzvf firefox-deb.tar.gz || { echo "error: extract offline bundle failed"; exit 1; } echo "" -echo "3. install Firefox deb packages..." -cd /opt/firefox-deb && sudo dpkg -i *.deb || { echo "error: install Firefox deb packages failed"; exit 1; } +echo "3. filter Firefox deb packages..." +base_system_packages=%s +mkdir -p /opt/firefox-installable +selected_count=0 +skipped_installed_count=0 +selected_missing_base_count=0 +selected_regular_count=0 +firefox_selected=0 +select_deb() { + cp -v "$deb" /opt/firefox-installable/ + selected_count=$((selected_count + 1)) +} +for deb in /opt/firefox-deb/*.deb; do + [ -e "$deb" ] || continue + file=$(basename "$deb") + pkg=${file%%_*} + pkg=${pkg%%.deb} + + status=$(dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null || true) + installed=0 + if [ "$status" = "install ok installed" ]; then + installed=1 + fi + + if [ "$pkg" = "firefox" ]; then + select_deb + firefox_selected=1 + continue + fi + + case " $base_system_packages " in + *" $pkg "*) + if [ "$installed" -eq 1 ]; then + echo "skip installed base system package: $file" + skipped_installed_count=$((skipped_installed_count + 1)) + continue + fi + echo "select missing base system package: $file" + select_deb + selected_missing_base_count=$((selected_missing_base_count + 1)) + continue + ;; + esac + + if [ "$installed" -eq 1 ]; then + echo "skip installed package: $file" + skipped_installed_count=$((skipped_installed_count + 1)) + continue + fi + + select_deb + selected_regular_count=$((selected_regular_count + 1)) +done +echo "selected packages: $selected_count, selected missing base: $selected_missing_base_count, selected regular: $selected_regular_count, skipped installed: $skipped_installed_count" +if [ "$selected_count" -eq 0 ]; then + echo "error: no packages selected for install" + exit 1 +fi +if [ "$firefox_selected" -ne 1 ]; then + echo "error: Firefox deb package was not selected" + exit 1 +fi echo "" -echo "4. verify Firefox..." +echo "4. install selected Firefox packages..." +cd /opt/firefox-installable && sudo dpkg -i ./*.deb || { echo "error: install selected Firefox deb packages failed"; exit 1; } +echo "" + +echo "5. verify Firefox..." firefox --version || { echo "error: Firefox verification failed"; exit 1; } echo "" echo "=== Firefox install complete ===" -`, shellQuote(bundlePath))) +`, shellQuote(bundlePath), shellQuote(baseSystemPackages))) command.Stdout = os.Stdout command.Stderr = os.Stderr - err := command.Run() + err = command.Run() if err != nil { kioskErrorf("firefox install command failed err=%v", err) return err @@ -173,22 +245,38 @@ func firefoxBundlePath() string { return filepath.Join(system.GetPathOfProgram(), g.Config().Firefox.DebPkgPath, g.Config().Firefox.DebPkgName) } -func validateFirefoxBundle(bundlePath string) error { +func validateFirefoxBundle(bundlePath string) (firefoxBundleInstallPlan, error) { packages, err := firefoxBundlePackageNames(bundlePath) if err != nil { - return fmt.Errorf("read firefox bundle packages: %w", err) + return firefoxBundleInstallPlan{}, fmt.Errorf("read firefox bundle packages: %w", err) } if len(packages) == 0 { - return fmt.Errorf("firefox bundle has no deb packages") + return firefoxBundleInstallPlan{}, fmt.Errorf("firefox bundle has no deb packages") } - blocked := unsafeFirefoxDebPackages(packages) - if len(blocked) > 0 { - return fmt.Errorf("firefox bundle contains blocked base system packages: %s", strings.Join(blocked, ", ")) + plan := firefoxBundleInstallPlanFromPackages(packages) + if !plan.HasFirefoxPackage { + return firefoxBundleInstallPlan{}, fmt.Errorf("firefox bundle has no firefox deb package") } - kioskInfof("firefox bundle validation passed bundle=%s packages=%d", bundlePath, len(packages)) - return nil + if len(plan.BaseSystemPackages) > 0 { + kioskWarnf("firefox bundle contains base system packages; installed ones will be skipped and missing ones may be installed packages=%s", strings.Join(plan.BaseSystemPackages, ", ")) + } + kioskInfof("firefox bundle validation passed bundle=%s totalPackages=%d baseSystemPackages=%d hasFirefox=%v", bundlePath, plan.TotalPackageCount, len(plan.BaseSystemPackages), plan.HasFirefoxPackage) + return plan, nil +} + +func firefoxBundleInstallPlanFromPackages(packages []string) firefoxBundleInstallPlan { + plan := firefoxBundleInstallPlan{ + TotalPackageCount: len(packages), + BaseSystemPackages: unsafeFirefoxDebPackages(packages), + } + for _, name := range packages { + if name == "firefox" { + plan.HasFirefoxPackage = true + } + } + return plan } func firefoxBundlePackageNames(bundlePath string) ([]string, error) { diff --git a/apps/m9zCtrlTty/firefox_install_test.go b/apps/m9zCtrlTty/firefox_install_test.go index adcb2d7..20afa89 100644 --- a/apps/m9zCtrlTty/firefox_install_test.go +++ b/apps/m9zCtrlTty/firefox_install_test.go @@ -35,6 +35,29 @@ func TestUnsafeFirefoxDebPackages(t *testing.T) { } } +func TestFirefoxBundleInstallPlanDetectsBasePackages(t *testing.T) { + packages := []string{ + "firefox", + "libgtk-3-0", + "dpkg", + "coreutils", + "libc6", + "tar", + } + + plan := firefoxBundleInstallPlanFromPackages(packages) + wantBlocked := []string{"coreutils", "dpkg", "libc6", "tar"} + if !plan.HasFirefoxPackage { + t.Fatal("plan should detect firefox package") + } + if plan.TotalPackageCount != len(packages) { + t.Fatalf("TotalPackageCount = %d, want %d", plan.TotalPackageCount, len(packages)) + } + if !stringSlicesEqual(plan.BaseSystemPackages, wantBlocked) { + t.Fatalf("BaseSystemPackages = %v, want %v", plan.BaseSystemPackages, wantBlocked) + } +} + func stringSlicesEqual(left []string, right []string) bool { if len(left) != len(right) { return false