refactor: rename project uatu -> sanderling (#24)

* refactor: rename Go module path uatu -> sanderling

Module path github.com/priyanshujain/uatu -> github.com/priyanshujain/sanderling,
including all imports and the proto go_package option. Generated .pb.go files
rewritten in-place; safe to regenerate with protoc later.

* chore(proto): regenerate driverpb after module path rename

The previous sed-based module rename corrupted the embedded descriptor
byte lengths. buf generate rewrites them cleanly.

* refactor: rename CLI binary uatu -> sanderling

Updates Makefile target + UATU_BIN var, .goreleaser project/build IDs,
.gitignore comment, and all user-facing strings in the CLI help text,
error messages, and tests. Binary is now bin/sanderling.

* refactor(sdk): rename Kotlin package dev.uatu.sdk -> dev.sanderling.sdk

Moves sdk/android/src/{main,test}/kotlin/dev/uatu -> dev/sanderling and
rewrites package declarations, imports, and the Gradle namespace. Class
names (Uatu, UatuRuntime) are renamed in a follow-up commit.

* refactor(sidecar): rename Kotlin package dev.uatu.sidecar -> dev.sanderling.sidecar

Moves sidecar/src/{main,test}/kotlin/dev/uatu -> dev/sanderling and
rewrites package declarations, imports, and the application mainClass.

* refactor: rename Uatu API surface -> Sanderling

- Kotlin: Uatu -> Sanderling, UatuRuntime -> SanderlingRuntime (+ files).
- JS host binding: globalThis.__uatu__ -> __sanderling__ (Go verifier,
  spec-api, tests).
- TS interface: UatuRuntime -> SanderlingRuntime; internal tags
  __uatuFormula / __uatuActionGenerator -> __sanderling* variants.
- Go trace: UatuVersion field + uatu_version JSON tag renamed.
- Socket naming: uatu-agent / uatu-agent-reader -> sanderling-agent*.
- Sample app, docs, inline-JS test strings updated to match.

* refactor(examples): rename examples/folio/uatu -> examples/folio/sanderling

Renames the example spec directory; updates justfile paths + gitignore
entries accordingly. Package.json name/description and @uatu/spec
dependency are renamed in the npm + docs commits.

* chore(build): rename gradle property + rootProject.name uatu -> sanderling

- Renames the uatu.version gradle property and all its -P references in
  Makefile, build.gradle.kts files, and .github/workflows/release.yml.
- settings.gradle.kts rootProject.name = "sanderling".
- Renames .env.local.example header + release-cli workflow job name.

* refactor(proto): rename proto package uatu.driver.v1 -> sanderling.driver.v1

Updates the proto package and java_package, regenerates driver.pb.go +
driver_grpc.pb.go, rewrites Kotlin imports and the gRPC ServiceName
assertion in driver_test.go.

* refactor: rename npm package @uatu/spec -> @sanderling/spec

Renames package name in pkg/spec-api/package.json + lockfile, all
consumer imports (examples/folio spec, testdata, verifier tests), the
esbuild alias in cmd/sanderling/test_run.go, and related doc references.

* docs: rename uatu -> sanderling in README, docs, and URLs

- README + docs/{manual,development}/*: narrative + GitHub + Pages URLs.
- POM + npm package.json repo/homepage/bugs URLs.
- .gitignore + embed_stub + Makefile-comment references updated to
  'make sanderling'.
- Minor narrative comments in cmd/sanderling/test_run.go and
  internal/inspect/server.go.

* refactor: rename remaining internal uatu strings -> sanderling

- SANDERLING_TEST_PHONE/OTP env vars (cmd + bundler tests).
- sanderling-sidecar runtime tmp dir + extracted JAR filename.
- Inspect web UI: @sanderling/inspect-web package, title, theme
  localStorage key, RunList empty-state copy, uatu_version TS field.
- Sample app storage key sanderling.ledger.v1.
- Test data: sanderling_test AVD name + com.example.sanderling_test.
- Release docs tarball name template.
This commit is contained in:
pj authored and GitHub committed 2026-04-21 11:57:49 +07:00
1 parent 13bb2feb82
commit 8ccf95c1cf
111 files changed
+514 -514

No files matched your search

+3 -3
View File
@@ -5,7 +5,7 @@ import (
"os"
"path/filepath"
"github.com/priyanshujain/uatu/internal/bundler"
"github.com/priyanshujain/sanderling/internal/bundler"
)
func main() {
@@ -31,8 +31,8 @@ func main() {
result, err := bundler.Bundle(bundler.Options{
EntryFile: entryFile,
Aliases: map[string]string{
"@uatu/spec": specApiPath,
"@uatu/spec/defaults/properties": defaultPropertiesPath,
"@sanderling/spec": specApiPath,
"@sanderling/spec/defaults/properties": defaultPropertiesPath,
},
})
if err != nil {
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"os"
"github.com/priyanshujain/uatu/internal/hierarchy"
"github.com/priyanshujain/sanderling/internal/hierarchy"
)
func main() {
File renamed without changes.
@@ -31,18 +31,18 @@ func TestParseAdbDevices_Empty(t *testing.T) {
func TestParseAVDList_DropsInfoLines(t *testing.T) {
output := `INFO | Storing crashdata in: /tmp/x
Medium_Phone_API_36.0
uatu_test
sanderling_test
`
got := parseAVDList(output)
want := []string{"Medium_Phone_API_36.0", "uatu_test"}
want := []string{"Medium_Phone_API_36.0", "sanderling_test"}
if !reflect.DeepEqual(got, want) {
t.Fatalf("got %v, want %v", got, want)
}
}
func TestPickAVD_ExplicitName(t *testing.T) {
got, err := pickAVD("Pixel_7", []string{"Pixel_7", "uatu_test"})
got, err := pickAVD("Pixel_7", []string{"Pixel_7", "sanderling_test"})
if err != nil {
t.Fatal(err)
}
@@ -59,12 +59,12 @@ func TestPickAVD_ExplicitMissing(t *testing.T) {
}
func TestPickAVD_SingleAvailable(t *testing.T) {
got, err := pickAVD("", []string{"uatu_test"})
got, err := pickAVD("", []string{"sanderling_test"})
if err != nil {
t.Fatal(err)
}
if got != "uatu_test" {
t.Fatalf("got %q, want uatu_test", got)
if got != "sanderling_test" {
t.Fatalf("got %q, want sanderling_test", got)
}
}
@@ -11,7 +11,7 @@ import (
"strconv"
"time"
"github.com/priyanshujain/uatu/internal/sidecar"
"github.com/priyanshujain/sanderling/internal/sidecar"
)
type doctorCheck struct {
@@ -30,7 +30,7 @@ func defaultDoctorChecks() []doctorCheck {
func checkSidecarJAR(_ context.Context) error {
if sidecar.IsPlaceholder() {
return fmt.Errorf("placeholder JAR embedded; run `make sidecar && make uatu` to embed the real fat JAR")
return fmt.Errorf("placeholder JAR embedded; run `make sidecar && make sanderling` to embed the real fat JAR")
}
if sidecar.EmbeddedSize() == 0 {
return fmt.Errorf("embedded JAR is empty")
File renamed without changes.
@@ -14,7 +14,7 @@ import (
"strconv"
"time"
"github.com/priyanshujain/uatu/internal/inspect"
"github.com/priyanshujain/sanderling/internal/inspect"
)
type inspectOptions struct {
@@ -67,7 +67,7 @@ func runInspect(options inspectOptions, stdout io.Writer) error {
}
address := listener.Addr().(*net.TCPAddr)
browseURL := buildBrowseURL(address, deepLinkID)
fmt.Fprintf(stdout, "uatu inspect listening on %s (runs=%s)\n", browseURL, runsDirectory)
fmt.Fprintf(stdout, "sanderling inspect listening on %s (runs=%s)\n", browseURL, runsDirectory)
context, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -36,7 +36,7 @@ func TestParseInspectArgs_RejectsTooManyPositional(t *testing.T) {
func TestRun_HelpListsInspectCommand(t *testing.T) {
var stdout bytes.Buffer
if err := run([]string{"uatu"}, &stdout, io.Discard); err != nil {
if err := run([]string{"sanderling"}, &stdout, io.Discard); err != nil {
t.Fatal(err)
}
if !strings.Contains(stdout.String(), "inspect") {
+6 -6
View File
@@ -25,18 +25,18 @@ type testOptions struct {
output string
}
const topUsage = `uatu is a property-based UI fuzzer for mobile apps.
const topUsage = `sanderling is a property-based UI fuzzer for mobile apps.
Usage:
uatu <command> [flags]
sanderling <command> [flags]
Commands:
test Run a spec against an app for a fixed duration.
inspect Serve a local web UI for browsing runs/.
doctor Check that the host environment is ready to run uatu.
version Print the uatu version.
doctor Check that the host environment is ready to run sanderling.
version Print the sanderling version.
Run "uatu <command> -h" for command-specific flags.
Run "sanderling <command> -h" for command-specific flags.
`
func parseTestArgs(args []string, stderr io.Writer) (testOptions, error) {
@@ -100,7 +100,7 @@ func run(args []string, stdout, stderr io.Writer) error {
fmt.Fprintln(stdout, Version)
return nil
default:
return fmt.Errorf("unknown command: %q (try 'uatu help')", args[1])
return fmt.Errorf("unknown command: %q (try 'sanderling help')", args[1])
}
}
@@ -90,10 +90,10 @@ func TestParseTestArgs_RejectsNonAndroidPlatform(t *testing.T) {
func TestRun_HelpPrintsUsage(t *testing.T) {
var stdout bytes.Buffer
if err := run([]string{"uatu"}, &stdout, io.Discard); err != nil {
if err := run([]string{"sanderling"}, &stdout, io.Discard); err != nil {
t.Fatal(err)
}
if !strings.Contains(stdout.String(), "uatu <command>") {
if !strings.Contains(stdout.String(), "sanderling <command>") {
t.Errorf("usage missing, got: %q", stdout.String())
}
}
@@ -105,7 +105,7 @@ func TestRun_VersionPrintsVersion(t *testing.T) {
for _, arg := range []string{"version", "--version", "-v"} {
var stdout bytes.Buffer
if err := run([]string{"uatu", arg}, &stdout, io.Discard); err != nil {
if err := run([]string{"sanderling", arg}, &stdout, io.Discard); err != nil {
t.Fatalf("%s: %v", arg, err)
}
if strings.TrimSpace(stdout.String()) != "1.2.3-test" {
@@ -115,7 +115,7 @@ func TestRun_VersionPrintsVersion(t *testing.T) {
}
func TestRun_UnknownCommand(t *testing.T) {
err := run([]string{"uatu", "wat"}, io.Discard, io.Discard)
err := run([]string{"sanderling", "wat"}, io.Discard, io.Discard)
if err == nil || !strings.Contains(err.Error(), "unknown command") {
t.Fatalf("expected unknown-command error, got %v", err)
}
@@ -125,7 +125,7 @@ func TestRun_Doctor(t *testing.T) {
var stdout bytes.Buffer
// Doctor may pass or fail depending on host environment; we just want to
// confirm it runs and emits per-check lines.
_ = run([]string{"uatu", "doctor"}, &stdout, io.Discard)
_ = run([]string{"sanderling", "doctor"}, &stdout, io.Discard)
output := stdout.String()
if !strings.Contains(output, "OK") && !strings.Contains(output, "FAIL") {
t.Errorf("doctor output missing OK/FAIL lines: %q", output)
@@ -137,7 +137,7 @@ func TestRun_TestSubcommand_PipelineErrors(t *testing.T) {
// must surface a specific error rather than panicking — proves the flag
// wiring reaches the runner.
err := run([]string{
"uatu", "test",
"sanderling", "test",
"--spec", "definitely-missing-spec.ts",
"--bundle-id", "com.example",
"--avd", "definitely-missing-avd",
File renamed without changes.
@@ -12,17 +12,17 @@ import (
"strconv"
"time"
"github.com/priyanshujain/uatu/internal/agent"
"github.com/priyanshujain/uatu/internal/bundler"
"github.com/priyanshujain/uatu/internal/driver/maestro"
"github.com/priyanshujain/uatu/internal/runner"
"github.com/priyanshujain/uatu/internal/sidecar"
"github.com/priyanshujain/uatu/internal/trace"
"github.com/priyanshujain/uatu/internal/verifier"
"github.com/priyanshujain/sanderling/internal/agent"
"github.com/priyanshujain/sanderling/internal/bundler"
"github.com/priyanshujain/sanderling/internal/driver/maestro"
"github.com/priyanshujain/sanderling/internal/runner"
"github.com/priyanshujain/sanderling/internal/sidecar"
"github.com/priyanshujain/sanderling/internal/trace"
"github.com/priyanshujain/sanderling/internal/verifier"
)
const (
socketName = "uatu-agent"
socketName = "sanderling-agent"
sidecarStartupTimeout = 30 * time.Second
sdkAcceptTimeout = 60 * time.Second
)
@@ -35,17 +35,17 @@ func runTestPipeline(ctx context.Context, options testOptions, stdout io.Writer)
}
aliases := map[string]string{}
if specApiPath := resolveSpecAPIPath(options.spec); specApiPath != "" {
aliases["@uatu/spec"] = specApiPath
aliases["@sanderling/spec"] = specApiPath
// Also alias published subpath exports so specs importing from
// "@uatu/spec/defaults/properties" resolve to the in-tree source.
// "@sanderling/spec/defaults/properties" resolve to the in-tree source.
base := filepath.Dir(specApiPath)
aliases["@uatu/spec/defaults/properties"] = filepath.Join(base, "defaults/properties.ts")
aliases["@sanderling/spec/defaults/properties"] = filepath.Join(base, "defaults/properties.ts")
}
bundle, err := bundler.Bundle(bundler.Options{
EntryFile: options.spec,
Defines: map[string]string{
"UATU_TEST_PHONE": os.Getenv("UATU_TEST_PHONE"),
"UATU_TEST_OTP": os.Getenv("UATU_TEST_OTP"),
"SANDERLING_TEST_PHONE": os.Getenv("SANDERLING_TEST_PHONE"),
"SANDERLING_TEST_OTP": os.Getenv("SANDERLING_TEST_OTP"),
},
Aliases: aliases,
})
@@ -54,7 +54,7 @@ func runTestPipeline(ctx context.Context, options testOptions, stdout io.Writer)
}
fmt.Fprintf(stdout, "bundled spec: %d bytes (sha256=%s)\n", len(bundle.JavaScript), bundle.SHA256[:12])
sidecarDirectory := filepath.Join(os.TempDir(), "uatu-sidecar")
sidecarDirectory := filepath.Join(os.TempDir(), "sanderling-sidecar")
jarPath, err := sidecar.Extract(sidecarDirectory)
if err != nil {
return fmt.Errorf("extract sidecar: %w", err)
@@ -166,7 +166,7 @@ func runTestPipeline(ctx context.Context, options testOptions, stdout io.Writer)
Platform: options.platform,
BundleID: options.bundleID,
StartedAt: time.Now().UTC(),
UatuVersion: "0.0.1",
SanderlingVersion: "0.0.1",
}
if err := traceWriter.WriteMeta(meta); err != nil {
return fmt.Errorf("trace meta: %w", err)
@@ -212,8 +212,8 @@ func runTestPipeline(ctx context.Context, options testOptions, stdout io.Writer)
}
// resolveSpecAPIPath returns the path to pkg/spec-api/src/index.ts inside
// a uatu source checkout, searched upward from the spec file and the cwd.
// Returns "" when not found, in which case esbuild resolves @uatu/spec via
// a sanderling source checkout, searched upward from the spec file and the cwd.
// Returns "" when not found, in which case esbuild resolves @sanderling/spec via
// node_modules the way a downstream user's project would.
func resolveSpecAPIPath(specPath string) string {
candidates := []string{}
@@ -47,6 +47,6 @@ func TestResolveSpecAPIPath_ReturnsEmptyWhenMissing(t *testing.T) {
got := resolveSpecAPIPath(specPath)
if got != "" {
t.Fatalf("got %q, want empty (no uatu source tree reachable)", got)
t.Fatalf("got %q, want empty (no sanderling source tree reachable)", got)
}
}