feat(platform): detect distro, WSL, and architecture
This commit is contained in:
63
internal/platform/platform.go
Normal file
63
internal/platform/platform.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Package platform detects the host OS/distro/WSL status.
|
||||||
|
package platform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Distro is a narrow enum of distros we care about.
|
||||||
|
type Distro string
|
||||||
|
|
||||||
|
const (
|
||||||
|
DistroArch Distro = "arch"
|
||||||
|
DistroUnknown Distro = "unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Platform holds detected host information.
|
||||||
|
type Platform struct {
|
||||||
|
Distro Distro
|
||||||
|
IsWSL bool
|
||||||
|
Arch string // e.g. "amd64", "arm64"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect reads the live system. Returns DistroUnknown rather than an error
|
||||||
|
// when /etc/os-release is missing or unrecognized; callers decide what to do.
|
||||||
|
func Detect() (Platform, error) {
|
||||||
|
f, err := os.Open("/etc/os-release")
|
||||||
|
distro := DistroUnknown
|
||||||
|
if err == nil {
|
||||||
|
distro = parseDistro(f)
|
||||||
|
_ = f.Close()
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
return Platform{}, fmt.Errorf("open /etc/os-release: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wsl := false
|
||||||
|
if data, err := os.ReadFile("/proc/sys/kernel/osrelease"); err == nil {
|
||||||
|
lower := strings.ToLower(string(data))
|
||||||
|
wsl = strings.Contains(lower, "microsoft") || strings.Contains(lower, "wsl")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Platform{Distro: distro, IsWSL: wsl, Arch: runtime.GOARCH}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDistro(r io.Reader) Distro {
|
||||||
|
sc := bufio.NewScanner(r)
|
||||||
|
for sc.Scan() {
|
||||||
|
line := sc.Text()
|
||||||
|
if !strings.HasPrefix(line, "ID=") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
id := strings.Trim(strings.TrimPrefix(line, "ID="), `"`)
|
||||||
|
if id == "arch" {
|
||||||
|
return DistroArch
|
||||||
|
}
|
||||||
|
return DistroUnknown
|
||||||
|
}
|
||||||
|
return DistroUnknown
|
||||||
|
}
|
||||||
45
internal/platform/platform_test.go
Normal file
45
internal/platform/platform_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package platform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseDistroArch(t *testing.T) {
|
||||||
|
osRelease := `NAME="Arch Linux"
|
||||||
|
ID=arch
|
||||||
|
ID_LIKE=archlinux
|
||||||
|
`
|
||||||
|
if got := parseDistro(strings.NewReader(osRelease)); got != DistroArch {
|
||||||
|
t.Fatalf("got %v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseDistroUbuntu(t *testing.T) {
|
||||||
|
osRelease := `NAME="Ubuntu"
|
||||||
|
ID=ubuntu
|
||||||
|
`
|
||||||
|
if got := parseDistro(strings.NewReader(osRelease)); got != DistroUnknown {
|
||||||
|
t.Fatalf("got %v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseDistroQuoted(t *testing.T) {
|
||||||
|
osRelease := `ID="arch"
|
||||||
|
`
|
||||||
|
if got := parseDistro(strings.NewReader(osRelease)); got != DistroArch {
|
||||||
|
t.Fatalf("got %v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDetectReturnsArch(t *testing.T) {
|
||||||
|
p, err := Detect()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if p.Arch == "" {
|
||||||
|
t.Fatal("arch is empty")
|
||||||
|
}
|
||||||
|
// We don't assert Distro here because the test host may not be Arch.
|
||||||
|
// The parse-level tests above cover that logic.
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user