diff --git a/internal/sidecar/assets/sidecar-all.jar b/internal/sidecar/assets/sidecar-all.jar new file mode 100644 index 0000000..dac0c12 --- /dev/null +++ b/internal/sidecar/assets/sidecar-all.jar @@ -0,0 +1 @@ +uatu-sidecar-placeholder \ No newline at end of file diff --git a/internal/sidecar/embed.go b/internal/sidecar/embed.go new file mode 100644 index 0000000..48c730f --- /dev/null +++ b/internal/sidecar/embed.go @@ -0,0 +1,64 @@ +package sidecar + +import ( + "crypto/sha256" + "encoding/hex" + _ "embed" + "errors" + "fmt" + "os" + "path/filepath" +) + +//go:embed assets/sidecar-all.jar +var embeddedJAR []byte + +// PlaceholderMagic is the byte sequence written to the placeholder JAR that +// ships in the repository. A binary that still carries the placeholder is +// not runnable against a real sidecar — `make sidecar && make uatu` must +// have been invoked to embed the real fat JAR. +const PlaceholderMagic = "uatu-sidecar-placeholder" + +// IsPlaceholder reports whether the embedded JAR is the development +// placeholder rather than a real sidecar fat JAR. +func IsPlaceholder() bool { + return string(embeddedJAR) == PlaceholderMagic +} + +// EmbeddedSize returns the size in bytes of the embedded JAR. +func EmbeddedSize() int { return len(embeddedJAR) } + +// EmbeddedSHA256 returns the hex-encoded SHA-256 of the embedded JAR. +func EmbeddedSHA256() string { + sum := sha256.Sum256(embeddedJAR) + return hex.EncodeToString(sum[:]) +} + +// Extract writes the embedded JAR to a deterministic path inside dir, +// alongside a .sha256 file. If the destination already exists with a +// matching checksum, no rewrite happens. Returns the JAR path. +func Extract(dir string) (string, error) { + if len(embeddedJAR) == 0 { + return "", errors.New("sidecar: embedded JAR is empty") + } + if err := os.MkdirAll(dir, 0o755); err != nil { + return "", fmt.Errorf("mkdir %s: %w", dir, err) + } + jarPath := filepath.Join(dir, "uatu-sidecar.jar") + checksumPath := jarPath + ".sha256" + checksum := EmbeddedSHA256() + + if existing, err := os.ReadFile(checksumPath); err == nil && string(existing) == checksum { + if _, err := os.Stat(jarPath); err == nil { + return jarPath, nil + } + } + + if err := os.WriteFile(jarPath, embeddedJAR, 0o644); err != nil { + return "", fmt.Errorf("write %s: %w", jarPath, err) + } + if err := os.WriteFile(checksumPath, []byte(checksum), 0o644); err != nil { + return "", fmt.Errorf("write checksum: %w", err) + } + return jarPath, nil +} diff --git a/internal/sidecar/embed_test.go b/internal/sidecar/embed_test.go new file mode 100644 index 0000000..8609290 --- /dev/null +++ b/internal/sidecar/embed_test.go @@ -0,0 +1,95 @@ +package sidecar + +import ( + "crypto/sha256" + "encoding/hex" + "os" + "path/filepath" + "testing" +) + +func TestEmbeddedNonZero(t *testing.T) { + if EmbeddedSize() == 0 { + t.Errorf("expected embedded JAR to be non-empty") + } +} + +func TestExtract_WritesJARAndChecksum(t *testing.T) { + directory := t.TempDir() + path, err := Extract(directory) + if err != nil { + t.Fatal(err) + } + if path != filepath.Join(directory, "uatu-sidecar.jar") { + t.Errorf("unexpected path: %s", path) + } + body, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if EmbeddedSize() != len(body) { + t.Errorf("size mismatch: embedded=%d, written=%d", EmbeddedSize(), len(body)) + } + sum := sha256.Sum256(body) + if hex.EncodeToString(sum[:]) != EmbeddedSHA256() { + t.Errorf("hash mismatch") + } + checksum, err := os.ReadFile(path + ".sha256") + if err != nil { + t.Fatal(err) + } + if string(checksum) != EmbeddedSHA256() { + t.Errorf("checksum file content wrong: %q", checksum) + } +} + +func TestExtract_ReusesIdenticalFile(t *testing.T) { + directory := t.TempDir() + path, err := Extract(directory) + if err != nil { + t.Fatal(err) + } + originalStat, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + originalModTime := originalStat.ModTime() + + // Second extract should be a no-op (no rewrite). + if _, err := Extract(directory); err != nil { + t.Fatal(err) + } + secondStat, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if !secondStat.ModTime().Equal(originalModTime) { + t.Errorf("second extract should not have rewritten the file") + } +} + +func TestExtract_RewritesIfChecksumMissing(t *testing.T) { + directory := t.TempDir() + path, err := Extract(directory) + if err != nil { + t.Fatal(err) + } + if err := os.Remove(path + ".sha256"); err != nil { + t.Fatal(err) + } + if _, err := Extract(directory); err != nil { + t.Fatal(err) + } + if _, err := os.Stat(path + ".sha256"); err != nil { + t.Errorf("checksum should have been written: %v", err) + } +} + +func TestIsPlaceholder_FlagsDevBuilds(t *testing.T) { + // The repo ships with a placeholder so fresh clones build without + // needing `make sidecar` first. The flag lets `uatu test` warn loudly + // before trying to drive a real device. + if !IsPlaceholder() { + t.Logf("running against a real sidecar JAR (size=%d)", EmbeddedSize()) + } +}