toolbox/src/cmd/export.go

144 lines
4.5 KiB
Go

package cmd
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/spf13/cobra"
)
var (
exportBin string
exportApp string
exportContainer string
)
var exportCmd = &cobra.Command{
Use: "export",
Short: "Export binaries or applications from a toolbox container",
RunE: runExport,
}
func init() {
exportCmd.Flags().StringVar(&exportBin, "bin", "", "Path or name of binary to export")
exportCmd.Flags().StringVar(&exportApp, "app", "", "Path or name of application to export")
exportCmd.Flags().StringVar(&exportContainer, "container", "", "Name of the toolbox container")
rootCmd.AddCommand(exportCmd)
}
func runExport(cmd *cobra.Command, args []string) error {
if exportBin == "" && exportApp == "" {
return errors.New("must specify either --bin or --app")
}
if exportContainer == "" {
return errors.New("must specify --container")
}
if exportBin != "" {
return exportBinary(exportBin, exportContainer)
} else if exportApp != "" {
return exportApplication(exportApp, exportContainer)
}
return nil
}
func exportBinary(binName, containerName string) error {
// Find the binary's full path inside the container
checkCmd := fmt.Sprintf("toolbox run -c %s which %s", containerName, binName)
out, err := exec.Command("sh", "-c", checkCmd).Output()
if err != nil || strings.TrimSpace(string(out)) == "" {
return fmt.Errorf("binary %s not found in container %s", binName, containerName)
}
binPath := strings.TrimSpace(string(out))
homeDir, err := os.UserHomeDir()
if err != nil {
return err
}
exportedBinPath := filepath.Join(homeDir, ".local", "bin", binName)
script := fmt.Sprintf(`#!/bin/sh
# toolbox_binary
# name: %s
exec toolbox run -c %s %s "$@"
`, containerName, containerName, binPath)
if err := os.WriteFile(exportedBinPath, []byte(script), 0755); err != nil {
return fmt.Errorf("failed to create wrapper: %v", err)
}
fmt.Printf("Successfully exported %s from container %s to %s\n", binName, containerName, exportedBinPath)
return nil
}
func exportApplication(appName, containerName string) error {
// Find the desktop file inside the container
findCmd := fmt.Sprintf("toolbox run -c %s sh -c 'find /usr/share/applications -name \"*%s*.desktop\" | head -1'", containerName, appName)
out, err := exec.Command("sh", "-c", findCmd).Output()
if err != nil || strings.TrimSpace(string(out)) == "" {
return fmt.Errorf("application %s not found in container %s", appName, containerName)
}
desktopFile := strings.TrimSpace(string(out))
// Read the desktop file content
catCmd := fmt.Sprintf("toolbox run -c %s cat %s", containerName, desktopFile)
content, err := exec.Command("sh", "-c", catCmd).Output()
if err != nil {
return fmt.Errorf("failed to read desktop file: %v", err)
}
lines := strings.Split(string(content), "\n")
var newLines []string
hasNameTranslations := false
for _, line := range lines {
if strings.HasPrefix(line, "Exec=") {
execCmd := line[5:]
line = fmt.Sprintf("Exec=toolbox run -c %s %s", containerName, execCmd)
} else if strings.HasPrefix(line, "Name=") {
line = fmt.Sprintf("Name=%s (on %s)", line[5:], containerName)
} else if strings.HasPrefix(line, "Name[") {
hasNameTranslations = true
} else if strings.HasPrefix(line, "GenericName=") {
line = fmt.Sprintf("GenericName=%s (on %s)", line[12:], containerName)
} else if strings.HasPrefix(line, "TryExec=") || line == "DBusActivatable=true" {
continue
}
newLines = append(newLines, line)
}
if hasNameTranslations {
for i, line := range newLines {
if strings.HasPrefix(line, "Name[") {
lang := line[5:strings.Index(line, "]")]
value := line[strings.Index(line, "=")+1:]
newLines[i] = fmt.Sprintf("Name[%s]=%s (on %s)", lang, value, containerName)
}
}
}
homeDir, err := os.UserHomeDir()
if err != nil {
return err
}
appsPath := filepath.Join(homeDir, ".local", "share", "applications")
exportedPath := filepath.Join(appsPath, filepath.Base(desktopFile))
exportedPath = strings.TrimSuffix(exportedPath, ".desktop") + "-" + containerName + ".desktop"
if err := os.MkdirAll(appsPath, 0755); err != nil {
return fmt.Errorf("failed to create applications directory: %v", err)
}
if err := os.WriteFile(exportedPath, []byte(strings.Join(newLines, "\n")), 0644); err != nil {
return fmt.Errorf("failed to create desktop file: %v", err)
}
// Update desktop database
exec.Command("update-desktop-database", appsPath).Run()
fmt.Printf("Successfully exported %s from container %s to %s\n", appName, containerName, exportedPath)
return nil
}