Files
rogee d7c883afc1
Docker image / Test (push) Successful in 2m12s
Docker image / Build and publish (push) Failing after 37s
HH-690: single-container Mihomo lifecycle and ExternalUI (#9)
2026-08-26 17:29:31 +08:00

84 lines
2.2 KiB
Go

package bootstrap
import (
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"syscall"
"time"
)
func validateSource(path, label string) error {
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("%s %q: %w", label, path, err)
}
if !info.Mode().IsRegular() {
return fmt.Errorf("%s %q is not a regular file", label, path)
}
if info.Size() == 0 {
return fmt.Errorf("%s %q is empty", label, path)
}
return nil
}
func validateMihomoConfig(ctx context.Context, binary, runtimeDir, configPath string) error {
command := exec.CommandContext(ctx, binary, "-t", "-d", runtimeDir, "-f", configPath)
command.Stdout = io.Discard
command.Stderr = io.Discard
if err := command.Run(); err != nil {
return errors.New("Mihomo validation failed")
}
return nil
}
func serviceCommand(ctx context.Context, binary, runtimeDir, configPath string) *exec.Cmd {
command := exec.CommandContext(ctx, binary, "-d", runtimeDir, "-f", configPath)
command.Stdout = os.Stdout
command.Stderr = os.Stderr
command.Cancel = func() error { return command.Process.Signal(syscall.SIGTERM) }
command.WaitDelay = 10 * time.Second
return command
}
func atomicWrite(path string, mode os.FileMode, write func(*os.File) error) (resultErr error) {
temp, err := os.CreateTemp(filepath.Dir(path), ".mohomo-docker-*")
if err != nil {
return fmt.Errorf("create temporary file for %q: %w", path, err)
}
tempPath := temp.Name()
closed := false
defer func() {
if !closed {
if closeErr := temp.Close(); resultErr == nil && closeErr != nil {
resultErr = fmt.Errorf("close temporary file for %q: %w", path, closeErr)
}
}
if resultErr != nil {
_ = os.Remove(tempPath)
}
}()
if err := temp.Chmod(mode); err != nil {
return fmt.Errorf("chmod temporary file for %q: %w", path, err)
}
if err := write(temp); err != nil {
return err
}
if err := temp.Sync(); err != nil {
return fmt.Errorf("sync temporary file for %q: %w", path, err)
}
if err := temp.Close(); err != nil {
return fmt.Errorf("close temporary file for %q: %w", path, err)
}
closed = true
if err := os.Rename(tempPath, path); err != nil {
return fmt.Errorf("replace %q atomically: %w", path, err)
}
return nil
}