From 305fecb41672d15189e62d0ddc31a5744fe6cc34 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sun, 19 Jul 2026 11:48:16 +0100 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20scaffold=20cubit=20skeleton=20(inge?= =?UTF-8?q?st=20=E2=86=92=20compare=20=E2=86=92=20report)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Walking-skeleton spine for the performance-regression tool: - internal/model: normalized, tool-agnostic Run/Measurement schema + Compare() - internal/criterion: ingest target/criterion/**/new/estimates.json (+ benchmark.json) - internal/report: advisory Markdown PR comment (adaptive ns/µs/ms, regression markers) - cmd/cubit: compare, record, version, and bulwark-mirrored self-update (releases/latest redirect → semver → SHA-256 verify → atomic swap; 24h nudge, never in CI) - testdata: captured criterion sample + baseline record Verified end-to-end: build, vet, test clean; `cubit compare` renders the baseline-vs-current table against the sample tree. Deferred: gitstore (cubit-state orphan branch), serve + React dashboard, goreleaser/action/install.sh, wardnet benches, unit tests. Claude-Session: https://claude.ai/code/session_016pTq6aVBJvTtXmwTgKw2Yh --- .gitignore | 12 + LICENSE | 21 ++ README.md | 52 +++- cmd/cubit/compare.go | 55 ++++ cmd/cubit/main.go | 24 ++ cmd/cubit/record.go | 35 +++ cmd/cubit/root.go | 22 ++ cmd/cubit/run.go | 65 ++++ cmd/cubit/update.go | 287 ++++++++++++++++++ cmd/cubit/version.go | 19 ++ go.mod | 15 + go.sum | 12 + internal/criterion/ingest.go | 158 ++++++++++ internal/model/model.go | 84 +++++ internal/report/report.go | 124 ++++++++ testdata/baseline.json | 24 ++ .../criterion/dns_cache/get/benchmark.json | 9 + .../dns_cache/get/new/estimates.json | 17 ++ .../dns_resolution/cache_miss/benchmark.json | 9 + .../cache_miss/new/estimates.json | 17 ++ 20 files changed, 1060 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 cmd/cubit/compare.go create mode 100644 cmd/cubit/main.go create mode 100644 cmd/cubit/record.go create mode 100644 cmd/cubit/root.go create mode 100644 cmd/cubit/run.go create mode 100644 cmd/cubit/update.go create mode 100644 cmd/cubit/version.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/criterion/ingest.go create mode 100644 internal/model/model.go create mode 100644 internal/report/report.go create mode 100644 testdata/baseline.json create mode 100644 testdata/criterion/dns_cache/get/benchmark.json create mode 100644 testdata/criterion/dns_cache/get/new/estimates.json create mode 100644 testdata/criterion/dns_resolution/cache_miss/benchmark.json create mode 100644 testdata/criterion/dns_resolution/cache_miss/new/estimates.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96aa1f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Build output +/cubit +/dist/ +*.exe + +# Go +*.test +*.out +coverage.txt + +# Editor / OS +.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d68577d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 wardnet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index b4df27a..60691b5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,52 @@ # cubit -Continuous benchmarking and performance-regression tracking — runs benchmarks, records baselines in a git branch, renders trend dashboards, and posts non-blocking baseline-vs-PR comparisons. + +Continuous benchmarking and performance-regression tracking. + +`cubit` runs your benchmarks, records baselines in a git branch, and reports how +a change moves performance — locally for a developer, or as a **non-blocking PR +comment** in CI. It is a *light gate*: the numbers and the trend dashboard inform +a human go/no-go, nothing hard-fails. + +Sibling to [bulwark](https://github.com/wardnet/bulwark) (code-quality scanning), +built for the [wardnet](https://github.com/wardnet) daemon's benchmarks. + +## How it works + +1. Your CI runs the benchmarks (today: Rust [criterion](https://github.com/bheisler/criterion.rs)). +2. `cubit compare` ingests the output, diffs it against the baseline recorded on + the `cubit-state` branch, and posts a Markdown table (baseline vs current, Δ%) + plus a link to a trend dashboard. +3. On the default branch, `cubit record` updates the baseline — keyed by commit + SHA — for the next PR to compare against. + +Baselines and history live in an orphan git branch (no database, no hosted +service). The dashboard renders from that branch as a CI artifact and via a local +`cubit serve`, so it works on private repos and repos whose GitHub Pages slot is +already taken. + +## Commands + +| Command | Purpose | +|---|---| +| `cubit compare` | Ingest a run, diff against a baseline, render the report | +| `cubit record` | Ingest a run and write it as a baseline record (SHA-keyed) | +| `cubit update` | Self-update to the latest release | +| `cubit version` | Print the version | + +## Install + +```sh +curl -fsSL https://github.com/wardnet/cubit/releases/latest/download/install.sh | sh +``` + +`cubit update` self-updates the CLI in place. + +## Status + +Early. The ingest → compare → report spine works; baseline persistence +(`cubit-state` branch), the release/action plumbing, and the React trend +dashboard are in progress. + +## License + +MIT — see [LICENSE](LICENSE). diff --git a/cmd/cubit/compare.go b/cmd/cubit/compare.go new file mode 100644 index 0000000..837dd74 --- /dev/null +++ b/cmd/cubit/compare.go @@ -0,0 +1,55 @@ +package main + +import ( + "github.com/spf13/cobra" + + "wardnet/cubit/internal/model" + "wardnet/cubit/internal/report" +) + +func newCompareCmd() *cobra.Command { + var ( + criterionDir string + baselinePath string + commit string + branch string + threshold float64 + dashboardURL string + out string + ) + cmd := &cobra.Command{ + Use: "compare", + Short: "Compare a benchmark run against a baseline and render a report", + Long: "Ingests a criterion output directory, diffs it against a baseline " + + "recorded by `cubit record`, and writes a Markdown report suitable for " + + "a PR comment. Advisory only — it never fails the build.", + Args: cobra.NoArgs, + SilenceUsage: true, + SilenceErrors: true, + RunE: func(cmd *cobra.Command, _ []string) error { + cur, err := buildRun(criterionDir, commit, branch, "") + if err != nil { + return err + } + var baseline model.Run + if baselinePath != "" { + baseline, err = loadRun(baselinePath) + if err != nil { + return err + } + } + deltas := model.Compare(cur, baseline, threshold) + md := report.Markdown(deltas, cur, threshold, dashboardURL) + return writeOut(out, []byte(md)) + }, + } + f := cmd.Flags() + f.StringVar(&criterionDir, "criterion-dir", "target/criterion", "criterion output directory to ingest") + f.StringVar(&baselinePath, "baseline", "", "baseline run JSON (from `cubit record`); omit to report the current run with no comparison") + f.StringVar(&commit, "commit", "", "commit SHA to label the current run with") + f.StringVar(&branch, "branch", "", "branch to label the current run with") + f.Float64Var(&threshold, "threshold", 15, "flag benchmarks slower than baseline by more than this percent") + f.StringVar(&dashboardURL, "dashboard-url", "", "trend-dashboard URL to link from the report") + f.StringVar(&out, "out", "-", "write the Markdown report here (\"-\" for stdout)") + return cmd +} diff --git a/cmd/cubit/main.go b/cmd/cubit/main.go new file mode 100644 index 0000000..badc060 --- /dev/null +++ b/cmd/cubit/main.go @@ -0,0 +1,24 @@ +// Command cubit is a continuous-benchmarking CLI: it ingests a benchmark +// runner's output, compares it against a recorded baseline, and reports the +// delta — locally for a developer, or as a non-blocking PR comment in CI. +package main + +import ( + "fmt" + "os" +) + +// version is overridden at release via -ldflags "-X main.version=". +// A "dev" build (built from source) disables self-update. +var version = "dev" + +func main() { + // A best-effort, once-a-day nudge when a newer release exists. Silent in + // CI, non-interactive, and from-source builds; never blocks the command. + maybeNudgeUpdate() + + if err := newRootCmd().Execute(); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + os.Exit(1) + } +} diff --git a/cmd/cubit/record.go b/cmd/cubit/record.go new file mode 100644 index 0000000..7538214 --- /dev/null +++ b/cmd/cubit/record.go @@ -0,0 +1,35 @@ +package main + +import "github.com/spf13/cobra" + +func newRecordCmd() *cobra.Command { + var ( + criterionDir string + commit string + branch string + out string + ) + cmd := &cobra.Command{ + Use: "record", + Short: "Ingest a benchmark run and write it as a baseline record", + Long: "Ingests a criterion output directory into a normalized run record, " + + "keyed by commit SHA. In CI this runs on the default branch to update " + + "the baseline the next PR compares against.", + Args: cobra.NoArgs, + SilenceUsage: true, + SilenceErrors: true, + RunE: func(cmd *cobra.Command, _ []string) error { + run, err := buildRun(criterionDir, commit, branch, "") + if err != nil { + return err + } + return writeJSON(out, run) + }, + } + f := cmd.Flags() + f.StringVar(&criterionDir, "criterion-dir", "target/criterion", "criterion output directory to ingest") + f.StringVar(&commit, "commit", "", "commit SHA this run was measured at") + f.StringVar(&branch, "branch", "", "branch this run was measured on") + f.StringVar(&out, "out", "-", "write the run record JSON here (\"-\" for stdout)") + return cmd +} diff --git a/cmd/cubit/root.go b/cmd/cubit/root.go new file mode 100644 index 0000000..1511806 --- /dev/null +++ b/cmd/cubit/root.go @@ -0,0 +1,22 @@ +package main + +import "github.com/spf13/cobra" + +func newRootCmd() *cobra.Command { + root := &cobra.Command{ + Use: "cubit", + Short: "Continuous benchmarking and performance-regression tracking", + Long: "cubit runs benchmarks, records baselines, and reports how a change " + + "moves performance — locally for a developer, or as a non-blocking PR " + + "comment in CI.", + SilenceUsage: true, + SilenceErrors: true, + } + root.AddCommand( + newCompareCmd(), + newRecordCmd(), + newUpdateCmd(), + newVersionCmd(), + ) + return root +} diff --git a/cmd/cubit/run.go b/cmd/cubit/run.go new file mode 100644 index 0000000..c4b7be9 --- /dev/null +++ b/cmd/cubit/run.go @@ -0,0 +1,65 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "time" + + "wardnet/cubit/internal/criterion" + "wardnet/cubit/internal/model" +) + +// buildRun ingests a criterion output directory into a normalized [model.Run] +// stamped with the given identity. An empty timestamp is filled with now (UTC, +// RFC3339) so records sort chronologically. +func buildRun(criterionDir, commit, branch, timestamp string) (model.Run, error) { + ms, err := criterion.Ingest(criterionDir) + if err != nil { + return model.Run{}, err + } + if timestamp == "" { + timestamp = time.Now().UTC().Format(time.RFC3339) + } + return model.Run{ + Schema: model.SchemaVersion, + Tool: "criterion", + Commit: commit, + Branch: branch, + Timestamp: timestamp, + Measurements: ms, + }, nil +} + +// loadRun reads a [model.Run] previously written by `cubit record`. +func loadRun(path string) (model.Run, error) { + var r model.Run + data, err := os.ReadFile(path) // #nosec G304 -- path is an explicit user-provided baseline file + if err != nil { + return r, fmt.Errorf("read baseline: %w", err) + } + if err := json.Unmarshal(data, &r); err != nil { + return r, fmt.Errorf("parse baseline %s: %w", path, err) + } + return r, nil +} + +// writeJSON marshals v as indented JSON to path, or to stdout when path is +// empty or "-". +func writeJSON(path string, v any) error { + data, err := json.MarshalIndent(v, "", " ") + if err != nil { + return err + } + data = append(data, '\n') + return writeOut(path, data) +} + +// writeOut writes data to path, or to stdout when path is empty or "-". +func writeOut(path string, data []byte) error { + if path == "" || path == "-" { + _, err := os.Stdout.Write(data) + return err + } + return os.WriteFile(path, data, 0o644) // #nosec G306 -- benchmark records/reports are not secrets +} diff --git a/cmd/cubit/update.go b/cmd/cubit/update.go new file mode 100644 index 0000000..2118c1d --- /dev/null +++ b/cmd/cubit/update.go @@ -0,0 +1,287 @@ +package main + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/spf13/cobra" + "golang.org/x/mod/semver" +) + +// releaseBaseURL is the GitHub project URL releases are downloaded from. +// It is a variable so tests can point it at a local HTTP server. +var releaseBaseURL = "https://github.com/wardnet/cubit" + +// updateCheckTTL bounds how often the background update nudge hits GitHub. +const updateCheckTTL = 24 * time.Hour + +// releaseAssetURL returns the download URL for a named asset of release ver. +func releaseAssetURL(ver, asset string) string { + return fmt.Sprintf("%s/releases/download/v%s/%s", releaseBaseURL, ver, asset) +} + +// canonicalVersion prefixes a bare version ("1.0.1") with the "v" that +// golang.org/x/mod/semver requires and reports whether the result is valid +// semver. Both the release tag (redirect target, "v" trimmed) and the injected +// build version are bare, so both flow through here. +func canonicalVersion(v string) (string, bool) { + sv := "v" + v + return sv, semver.IsValid(sv) +} + +// updateAvailable reports whether latest is a strictly newer release than +// current, so the nudge and `cubit update` only offer a real upgrade — never a +// sideways or downward move. Comparison is semantic, not lexical. An empty or +// unparseable latest OR current (a failed/never-run check, or a non-release +// build) is treated as "no update". +func updateAvailable(latest, current string) bool { + l, lok := canonicalVersion(latest) + c, cok := canonicalVersion(current) + if !lok || !cok { + return false + } + return semver.Compare(l, c) > 0 +} + +func newUpdateCmd() *cobra.Command { + return &cobra.Command{ + Use: "update", + Short: "Update cubit to the latest release", + Args: cobra.NoArgs, + SilenceUsage: true, + SilenceErrors: true, + RunE: func(cmd *cobra.Command, _ []string) error { + if version == "dev" { + return errors.New("this cubit was built from source — update via git, not 'cubit update'") + } + exe, err := os.Executable() + if err != nil { + return err + } + exe, err = filepath.EvalSymlinks(exe) + if err != nil { + return err + } + latest, err := latestReleaseVersion(cmd.Context(), http.DefaultClient) + if err != nil { + return fmt.Errorf("resolve latest release: %w", err) + } + // A non-release build version (not "dev", but not valid semver either) + // can't be ordered against the latest tag. Surface it explicitly on this + // explicit-action path rather than letting updateAvailable's silent + // false read as "already the latest release". + if _, ok := canonicalVersion(version); !ok { + return fmt.Errorf("running version %q is not a recognized release version — reinstall from a GitHub release to enable updates", version) + } + if !updateAvailable(latest, version) { + fmt.Fprintf(cmd.OutOrStdout(), "cubit v%s is already the latest release\n", version) + return nil + } + fmt.Fprintf(cmd.OutOrStdout(), "updating cubit v%s -> v%s...\n", version, latest) + if err := selfUpdate(cmd.Context(), latest, exe); err != nil { + return err + } + fmt.Fprintf(cmd.OutOrStdout(), "updated %s to v%s\n", exe, latest) + return nil + }, + } +} + +// latestReleaseVersion resolves the latest released version (without the "v" +// prefix) by reading the redirect target of the releases/latest URL, avoiding +// the rate-limited GitHub API. +func latestReleaseVersion(ctx context.Context, client *http.Client) (string, error) { + noRedirect := *client + noRedirect.CheckRedirect = func(*http.Request, []*http.Request) error { + return http.ErrUseLastResponse + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, releaseBaseURL+"/releases/latest", nil) + if err != nil { + return "", err + } + resp, err := noRedirect.Do(req) + if err != nil { + return "", err + } + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode < 300 || resp.StatusCode >= 400 { + return "", fmt.Errorf("expected a redirect from %s, got HTTP %d", req.URL, resp.StatusCode) + } + + // Location ends in .../releases/tag/v. + loc := resp.Header.Get("Location") + tag := loc[strings.LastIndex(loc, "/")+1:] + if !strings.HasPrefix(tag, "v") { + return "", fmt.Errorf("cannot parse release tag from redirect %q", loc) + } + return strings.TrimPrefix(tag, "v"), nil +} + +// selfUpdate downloads the raw cubit binary for the given version, verifies its +// SHA-256 against the release's checksums.txt, and atomically replaces the +// binary at exe. +func selfUpdate(ctx context.Context, ver, exe string) error { + asset := fmt.Sprintf("cubit_%s_%s_%s", ver, runtime.GOOS, runtime.GOARCH) + url := releaseAssetURL(ver, asset) + + // Stage in the same directory so the final rename is atomic. + tmp, err := os.CreateTemp(filepath.Dir(exe), ".cubit-update-*") + if err != nil { + return fmt.Errorf("stage update next to %s (is the directory writable?): %w", exe, err) + } + tmpPath := tmp.Name() + if err := tmp.Close(); err != nil { + return err + } + defer func() { _ = os.Remove(tmpPath) }() + + if err := downloadBinary(ctx, url, tmpPath); err != nil { + return err + } + if err := verifyChecksum(ctx, http.DefaultClient, ver, asset, tmpPath); err != nil { + return err + } + if err := os.Chmod(tmpPath, 0o755); err != nil { // #nosec G302 -- the replacement binary must be executable + return err + } + if err := os.Rename(tmpPath, exe); err != nil { + return fmt.Errorf("replace %s (is it writable?): %w", exe, err) + } + return nil +} + +// downloadBinary fetches url and writes it to dst. +func downloadBinary(ctx context.Context, url, dst string) error { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("HTTP %d fetching %s — asset not found for %s/%s", + resp.StatusCode, url, runtime.GOOS, runtime.GOARCH) + } + + f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o755) // #nosec G304 -- dst is our own staged temp path, not user input + if err != nil { + return err + } + _, copyErr := io.Copy(f, resp.Body) + closeErr := f.Close() + if copyErr != nil { + return copyErr + } + return closeErr +} + +// verifyChecksum fetches the release's checksums.txt and compares the named +// asset's SHA-256 against the file at path. +func verifyChecksum(ctx context.Context, client *http.Client, ver, asset, path string) error { + url := releaseAssetURL(ver, "checksums.txt") + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return err + } + resp, err := client.Do(req) + if err != nil { + return err + } + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("HTTP %d fetching %s", resp.StatusCode, url) + } + sums, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + var want string + for _, line := range strings.Split(string(sums), "\n") { + fields := strings.Fields(line) + if len(fields) == 2 && fields[1] == asset { + want = fields[0] + break + } + } + if want == "" { + return fmt.Errorf("no checksum for %s in %s", asset, url) + } + + f, err := os.Open(path) // #nosec G304 -- path is our own staged temp path, not user input + if err != nil { + return err + } + defer func() { _ = f.Close() }() + h := sha256.New() + if _, err := io.Copy(h, f); err != nil { + return err + } + if got := hex.EncodeToString(h.Sum(nil)); got != want { + return fmt.Errorf("checksum mismatch for %s: got %s, want %s", asset, got, want) + } + return nil +} + +// updateCheckState is the on-disk cache for the background update nudge. +type updateCheckState struct { + CheckedAt time.Time `json:"checked_at"` + Latest string `json:"latest"` +} + +// maybeNudgeUpdate prints a one-line notice on stderr when a newer release is +// known. It contacts GitHub at most once per updateCheckTTL, never in CI, +// non-interactive, or from-source ("dev") runs, and stays silent on any failure. +func maybeNudgeUpdate() { + if version == "dev" || os.Getenv("CI") != "" { + return + } + if fi, err := os.Stderr.Stat(); err != nil || fi.Mode()&os.ModeCharDevice == 0 { + return + } + cacheDir, err := os.UserCacheDir() + if err != nil { + return + } + path := filepath.Join(cacheDir, "cubit", "update-check.json") + + var st updateCheckState + if data, err := os.ReadFile(path); err == nil { // #nosec G304 -- path is built from os.UserCacheDir(), not user input + _ = json.Unmarshal(data, &st) + } + if time.Since(st.CheckedAt) >= updateCheckTTL { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + latest, err := latestReleaseVersion(ctx, http.DefaultClient) + // Record the attempt even when the check fails (offline, blocked) — + // otherwise every invocation past the TTL re-pays the network timeout. + st.CheckedAt = time.Now() + if err == nil { + st.Latest = latest + } + if data, err := json.Marshal(st); err == nil { + if err := os.MkdirAll(filepath.Dir(path), 0o750); err == nil { + _ = os.WriteFile(path, data, 0o600) + } + } + } + if updateAvailable(st.Latest, version) { + _, _ = fmt.Fprintf(os.Stderr, "\ncubit v%s is available (you have v%s) — run 'cubit update'\n", + st.Latest, version) + } +} diff --git a/cmd/cubit/version.go b/cmd/cubit/version.go new file mode 100644 index 0000000..988f9d4 --- /dev/null +++ b/cmd/cubit/version.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func newVersionCmd() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Print the cubit version", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + fmt.Fprintf(cmd.OutOrStdout(), "cubit %s\n", version) + return nil + }, + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4e48196 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module wardnet/cubit + +go 1.26.4 + +toolchain go1.26.5 + +require ( + github.com/spf13/cobra v1.10.2 + golang.org/x/mod v0.38.0 +) + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/pflag v1.0.9 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dcd844c --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/criterion/ingest.go b/internal/criterion/ingest.go new file mode 100644 index 0000000..f8f89c7 --- /dev/null +++ b/internal/criterion/ingest.go @@ -0,0 +1,158 @@ +// Package criterion ingests a criterion-rs output tree into Cubit's normalized +// [model.Measurement]s. +// +// criterion writes one directory per benchmark under target/criterion, each +// containing a new/estimates.json (the timing statistics of the most recent +// run) and a benchmark.json (the benchmark's identity + declared throughput). +// We read estimates.json for the numbers and benchmark.json for a stable ID, +// falling back to the directory path when the metadata file is absent. We take +// the *median* rather than the mean: it is the statistic least perturbed by the +// occasional slow sample on a noisy CI runner. +package criterion + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "sort" + "strings" + + "wardnet/cubit/internal/model" +) + +// estimates mirrors the subset of criterion's new/estimates.json we consume. +// All values are in nanoseconds. +type estimates struct { + Median struct { + PointEstimate float64 `json:"point_estimate"` + ConfidenceInterval struct { + LowerBound float64 `json:"lower_bound"` + UpperBound float64 `json:"upper_bound"` + } `json:"confidence_interval"` + } `json:"median"` +} + +// benchmarkMeta mirrors the subset of criterion's benchmark.json we consume. +type benchmarkMeta struct { + GroupID string `json:"group_id"` + FunctionID string `json:"function_id"` + ValueStr string `json:"value_str"` + Throughput []struct { + PerIteration uint64 `json:"per_iteration"` + } `json:"throughput"` +} + +// id builds a stable, human-readable benchmark id from the criterion metadata: +// "group/function/value", skipping the segments criterion left empty. +func (m benchmarkMeta) id() string { + parts := make([]string, 0, 3) + for _, p := range []string{m.GroupID, m.FunctionID, m.ValueStr} { + if p != "" { + parts = append(parts, p) + } + } + return strings.Join(parts, "/") +} + +func (m benchmarkMeta) throughput() float64 { + if len(m.Throughput) == 0 || m.Throughput[0].PerIteration == 0 { + return 0 + } + return float64(m.Throughput[0].PerIteration) +} + +// Ingest walks criterionDir (typically /criterion), returning one +// [model.Measurement] per benchmark, sorted by ID. It considers only the +// freshly-measured run — the ...//new/estimates.json files — ignoring +// criterion's retained "base" snapshots. +func Ingest(criterionDir string) ([]model.Measurement, error) { + info, err := os.Stat(criterionDir) + if err != nil { + return nil, fmt.Errorf("criterion directory: %w", err) + } + if !info.IsDir() { + return nil, fmt.Errorf("criterion directory %q is not a directory", criterionDir) + } + + var out []model.Measurement + walkErr := filepath.WalkDir(criterionDir, func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() || d.Name() != "estimates.json" { + return nil + } + // Only the current run: ...//new/estimates.json. + if filepath.Base(filepath.Dir(path)) != "new" { + return nil + } + benchDir := filepath.Dir(filepath.Dir(path)) + + est, err := readEstimates(path) + if err != nil { + return fmt.Errorf("%s: %w", path, err) + } + + id := deriveID(criterionDir, benchDir) + var thr float64 + if meta, err := readMeta(filepath.Join(benchDir, "benchmark.json")); err == nil { + if mid := meta.id(); mid != "" { + id = mid + } + thr = meta.throughput() + } + + out = append(out, model.Measurement{ + ID: id, + Unit: "ns", + Value: est.Median.PointEstimate, + Lower: est.Median.ConfidenceInterval.LowerBound, + Upper: est.Median.ConfidenceInterval.UpperBound, + Throughput: thr, + }) + return nil + }) + if walkErr != nil { + return nil, walkErr + } + if len(out) == 0 { + return nil, fmt.Errorf("no benchmarks found under %q (expected */new/estimates.json)", criterionDir) + } + sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID }) + return out, nil +} + +func readEstimates(path string) (estimates, error) { + var e estimates + data, err := os.ReadFile(path) // #nosec G304 -- path comes from our own WalkDir over criterionDir + if err != nil { + return e, err + } + if err := json.Unmarshal(data, &e); err != nil { + return e, err + } + return e, nil +} + +func readMeta(path string) (benchmarkMeta, error) { + var m benchmarkMeta + data, err := os.ReadFile(path) // #nosec G304 -- path is benchmark.json beside an ingested estimates.json + if err != nil { + return m, err + } + if err := json.Unmarshal(data, &m); err != nil { + return m, err + } + return m, nil +} + +// deriveID falls back to the benchmark's path relative to the criterion root +// when benchmark.json is missing, so ingestion still yields a usable ID. +func deriveID(root, benchDir string) string { + rel, err := filepath.Rel(root, benchDir) + if err != nil { + return filepath.Base(benchDir) + } + return filepath.ToSlash(rel) +} diff --git a/internal/model/model.go b/internal/model/model.go new file mode 100644 index 0000000..418c30d --- /dev/null +++ b/internal/model/model.go @@ -0,0 +1,84 @@ +// Package model defines Cubit's normalized, tool-agnostic benchmark schema. +// +// Every benchmark runner (criterion today, others later) is ingested into a +// [Run]: a set of [Measurement]s captured at one commit. Runs are what get +// persisted to the cubit-state branch and what the comparison + report layers +// operate on, so nothing downstream ever needs to know which runner produced +// the numbers. +package model + +import "sort" + +// SchemaVersion is bumped whenever the on-disk [Run] shape changes +// incompatibly, so the ingest/read layers can refuse or migrate old records. +const SchemaVersion = 1 + +// Measurement is a single benchmark's summary for one run. Value/Lower/Upper +// are expressed in Unit (nanoseconds for criterion latency benches); Lower and +// Upper are the confidence-interval bounds the runner reported. +type Measurement struct { + ID string `json:"id"` + Unit string `json:"unit"` + Value float64 `json:"value"` + Lower float64 `json:"lower"` + Upper float64 `json:"upper"` + Throughput float64 `json:"throughput,omitempty"` // elements/sec, when the bench declares it +} + +// Run is a full benchmark run captured at one commit. Timestamp is set by the +// caller (RFC3339) rather than at ingest, so replays and back-fills stay +// deterministic. +type Run struct { + Schema int `json:"schema"` + Tool string `json:"tool"` + Commit string `json:"commit"` + Branch string `json:"branch"` + Timestamp string `json:"timestamp"` + Measurements []Measurement `json:"measurements"` +} + +// Index returns the run's measurements keyed by benchmark ID. +func (r Run) Index() map[string]Measurement { + m := make(map[string]Measurement, len(r.Measurements)) + for _, x := range r.Measurements { + m[x.ID] = x + } + return m +} + +// Delta is a per-benchmark comparison of a current run against a baseline. +// PctChange is signed: positive means the current run is slower (a regression +// for latency units). +type Delta struct { + ID string + Unit string + Baseline float64 + Current float64 + PctChange float64 + HasBase bool // false when the baseline run had no matching benchmark (new bench) + Regressed bool // PctChange exceeded the report threshold +} + +// Compare diffs current against baseline, flagging any benchmark whose slowdown +// exceeds thresholdPct (a positive percentage). A benchmark absent from the +// baseline is reported with HasBase=false and never counts as a regression — +// it is new, not slower. The gate is advisory: Regressed is a hint for the +// human reviewer, not a build failure. +func Compare(current, baseline Run, thresholdPct float64) []Delta { + base := baseline.Index() + out := make([]Delta, 0, len(current.Measurements)) + for _, c := range current.Measurements { + d := Delta{ID: c.ID, Unit: c.Unit, Current: c.Value} + if b, ok := base[c.ID]; ok { + d.HasBase = true + d.Baseline = b.Value + if b.Value > 0 { + d.PctChange = (c.Value - b.Value) / b.Value * 100 + } + d.Regressed = d.PctChange > thresholdPct + } + out = append(out, d) + } + sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID }) + return out +} diff --git a/internal/report/report.go b/internal/report/report.go new file mode 100644 index 0000000..d81601c --- /dev/null +++ b/internal/report/report.go @@ -0,0 +1,124 @@ +// Package report renders a comparison between a current run and its baseline +// into the Markdown that Cubit posts as a sticky PR comment. +// +// The comment is deliberately advisory: it summarizes what moved and flags +// slowdowns past the threshold, but it never asserts pass/fail. The human +// reviewer, with the trend dashboard for context, makes the go/no-go call. +package report + +import ( + "fmt" + "strings" + + "wardnet/cubit/internal/model" +) + +// Markdown renders the PR comment body for the given deltas. thresholdPct is +// echoed into the header so a reader knows what "⚠️" means for this run. +func Markdown(deltas []model.Delta, cur model.Run, thresholdPct float64, dashboardURL string) string { + var b strings.Builder + + fmt.Fprintf(&b, "## 📐 cubit — performance\n\n") + if cur.Commit != "" { + fmt.Fprintf(&b, "Commit `%s`", shortSHA(cur.Commit)) + if cur.Branch != "" { + fmt.Fprintf(&b, " on `%s`", cur.Branch) + } + b.WriteString(" · ") + } + fmt.Fprintf(&b, "%d benchmarks · flagging Δ > %.0f%% slower\n\n", len(deltas), thresholdPct) + + regressed := countRegressed(deltas) + newCount := countNew(deltas) + switch { + case regressed > 0: + fmt.Fprintf(&b, "⚠️ **%d benchmark(s) slower than baseline by more than %.0f%%** — review before merge.\n\n", regressed, thresholdPct) + default: + b.WriteString("✅ No benchmark exceeded the regression threshold.\n\n") + } + + b.WriteString("| Benchmark | Baseline | Current | Δ | |\n") + b.WriteString("|---|--:|--:|--:|:-:|\n") + for _, d := range deltas { + b.WriteString(row(d)) + } + b.WriteString("\n") + + if newCount > 0 { + fmt.Fprintf(&b, "_%d benchmark(s) are new (no baseline to compare)._\n\n", newCount) + } + if dashboardURL != "" { + fmt.Fprintf(&b, "📈 [Trend dashboard](%s) — history and variance band.\n", dashboardURL) + } + + return b.String() +} + +func row(d model.Delta) string { + if !d.HasBase { + return fmt.Sprintf("| `%s` | — | %s | _new_ | 🆕 |\n", d.ID, formatNs(d.Current)) + } + return fmt.Sprintf("| `%s` | %s | %s | %s | %s |\n", + d.ID, formatNs(d.Baseline), formatNs(d.Current), formatPct(d.PctChange), marker(d)) +} + +func marker(d model.Delta) string { + switch { + case d.Regressed: + return "⚠️" + case d.PctChange < -5: + return "🚀" // meaningfully faster + default: + return "" + } +} + +func countRegressed(deltas []model.Delta) int { + n := 0 + for _, d := range deltas { + if d.Regressed { + n++ + } + } + return n +} + +func countNew(deltas []model.Delta) int { + n := 0 + for _, d := range deltas { + if !d.HasBase { + n++ + } + } + return n +} + +// formatNs renders a nanosecond value with an adaptive unit, so a table mixing +// sub-microsecond cache lookups and millisecond pipeline runs stays readable. +func formatNs(ns float64) string { + switch { + case ns >= 1e9: + return fmt.Sprintf("%.2f s", ns/1e9) + case ns >= 1e6: + return fmt.Sprintf("%.2f ms", ns/1e6) + case ns >= 1e3: + return fmt.Sprintf("%.2f µs", ns/1e3) + default: + return fmt.Sprintf("%.1f ns", ns) + } +} + +func formatPct(p float64) string { + sign := "+" + if p < 0 { + sign = "" + } + return fmt.Sprintf("%s%.1f%%", sign, p) +} + +func shortSHA(s string) string { + if len(s) > 8 { + return s[:8] + } + return s +} diff --git a/testdata/baseline.json b/testdata/baseline.json new file mode 100644 index 0000000..d589c16 --- /dev/null +++ b/testdata/baseline.json @@ -0,0 +1,24 @@ +{ + "schema": 1, + "tool": "criterion", + "commit": "0000000000000000000000000000000000000000", + "branch": "main", + "timestamp": "2026-07-18T00:00:00Z", + "measurements": [ + { + "id": "dns_cache/get", + "unit": "ns", + "value": 48.0, + "lower": 46.9, + "upper": 49.2 + }, + { + "id": "dns_resolution/cache_miss", + "unit": "ns", + "value": 1000000.0, + "lower": 985000.0, + "upper": 1015000.0, + "throughput": 800 + } + ] +} diff --git a/testdata/criterion/dns_cache/get/benchmark.json b/testdata/criterion/dns_cache/get/benchmark.json new file mode 100644 index 0000000..e306821 --- /dev/null +++ b/testdata/criterion/dns_cache/get/benchmark.json @@ -0,0 +1,9 @@ +{ + "group_id": "dns_cache", + "function_id": "get", + "value_str": null, + "throughput": [{ "per_iteration": 0, "unit": "Elements" }], + "full_id": "dns_cache/get", + "directory_name": "dns_cache/get", + "title": "dns_cache/get" +} diff --git a/testdata/criterion/dns_cache/get/new/estimates.json b/testdata/criterion/dns_cache/get/new/estimates.json new file mode 100644 index 0000000..18cf4f5 --- /dev/null +++ b/testdata/criterion/dns_cache/get/new/estimates.json @@ -0,0 +1,17 @@ +{ + "mean": { + "confidence_interval": { "confidence_level": 0.95, "lower_bound": 44.9, "upper_bound": 46.1 }, + "point_estimate": 45.4, + "standard_error": 0.31 + }, + "median": { + "confidence_interval": { "confidence_level": 0.95, "lower_bound": 44.1, "upper_bound": 46.8 }, + "point_estimate": 45.2, + "standard_error": 0.42 + }, + "std_dev": { + "confidence_interval": { "confidence_level": 0.95, "lower_bound": 1.8, "upper_bound": 3.1 }, + "point_estimate": 2.4, + "standard_error": 0.33 + } +} diff --git a/testdata/criterion/dns_resolution/cache_miss/benchmark.json b/testdata/criterion/dns_resolution/cache_miss/benchmark.json new file mode 100644 index 0000000..8db661a --- /dev/null +++ b/testdata/criterion/dns_resolution/cache_miss/benchmark.json @@ -0,0 +1,9 @@ +{ + "group_id": "dns_resolution", + "function_id": "cache_miss", + "value_str": null, + "throughput": [{ "per_iteration": 800, "unit": "Elements" }], + "full_id": "dns_resolution/cache_miss", + "directory_name": "dns_resolution/cache_miss", + "title": "dns_resolution/cache_miss" +} diff --git a/testdata/criterion/dns_resolution/cache_miss/new/estimates.json b/testdata/criterion/dns_resolution/cache_miss/new/estimates.json new file mode 100644 index 0000000..fb4d841 --- /dev/null +++ b/testdata/criterion/dns_resolution/cache_miss/new/estimates.json @@ -0,0 +1,17 @@ +{ + "mean": { + "confidence_interval": { "confidence_level": 0.95, "lower_bound": 1230000.0, "upper_bound": 1275000.0 }, + "point_estimate": 1252000.0, + "standard_error": 11200.0 + }, + "median": { + "confidence_interval": { "confidence_level": 0.95, "lower_bound": 1235000.0, "upper_bound": 1268000.0 }, + "point_estimate": 1250000.0, + "standard_error": 9800.0 + }, + "std_dev": { + "confidence_interval": { "confidence_level": 0.95, "lower_bound": 42000.0, "upper_bound": 71000.0 }, + "point_estimate": 55000.0, + "standard_error": 7300.0 + } +} From 83a42fd46ffb0c5806c10b623e166e9acae833ce Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sun, 19 Jul 2026 11:52:37 +0100 Subject: [PATCH 2/7] feat(gitstore): persist runs to the cubit-state orphan branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Baseline persistence + read-back for the real CI loop: - internal/gitstore: append runs to an orphan branch (default cubit-state) via git plumbing (hash-object → temp-index update-index → write-tree → commit-tree → compare-and-swap update-ref), never touching the working tree. Records at runs/.json with an index.json manifest; main-only, SHA-keyed. Read/Latest fall back to origin/ for the CI (fetched-not-checked-out) case. - record --persist: commit the run to the branch (CI default-branch step). - compare --baseline-ref: resolve the baseline from the branch by SHA, "latest", or "latest:", instead of a --baseline file. - integration test: append/read/latest, branch filtering, empty-commit rejection, and working-tree-untouched invariant. Verified: record --persist → compare --baseline-ref latest:main round-trips through the branch; working tree stays clean. Claude-Session: https://claude.ai/code/session_016pTq6aVBJvTtXmwTgKw2Yh --- cmd/cubit/compare.go | 58 +++++- cmd/cubit/record.go | 38 +++- internal/gitstore/gitstore.go | 300 +++++++++++++++++++++++++++++ internal/gitstore/gitstore_test.go | 115 +++++++++++ 4 files changed, 496 insertions(+), 15 deletions(-) create mode 100644 internal/gitstore/gitstore.go create mode 100644 internal/gitstore/gitstore_test.go diff --git a/cmd/cubit/compare.go b/cmd/cubit/compare.go index 837dd74..cfd55d0 100644 --- a/cmd/cubit/compare.go +++ b/cmd/cubit/compare.go @@ -1,8 +1,12 @@ package main import ( + "fmt" + "strings" + "github.com/spf13/cobra" + "wardnet/cubit/internal/gitstore" "wardnet/cubit/internal/model" "wardnet/cubit/internal/report" ) @@ -11,6 +15,9 @@ func newCompareCmd() *cobra.Command { var ( criterionDir string baselinePath string + baselineRef string + repo string + stateBranch string commit string branch string threshold float64 @@ -20,9 +27,10 @@ func newCompareCmd() *cobra.Command { cmd := &cobra.Command{ Use: "compare", Short: "Compare a benchmark run against a baseline and render a report", - Long: "Ingests a criterion output directory, diffs it against a baseline " + - "recorded by `cubit record`, and writes a Markdown report suitable for " + - "a PR comment. Advisory only — it never fails the build.", + Long: "Ingests a criterion output directory, diffs it against a baseline, and " + + "writes a Markdown report suitable for a PR comment. The baseline comes " + + "from --baseline (a file) or --baseline-ref (a record on the cubit-state " + + "branch). Advisory only — it never fails the build.", Args: cobra.NoArgs, SilenceUsage: true, SilenceErrors: true, @@ -31,12 +39,9 @@ func newCompareCmd() *cobra.Command { if err != nil { return err } - var baseline model.Run - if baselinePath != "" { - baseline, err = loadRun(baselinePath) - if err != nil { - return err - } + baseline, err := resolveBaseline(baselinePath, baselineRef, repo, stateBranch) + if err != nil { + return err } deltas := model.Compare(cur, baseline, threshold) md := report.Markdown(deltas, cur, threshold, dashboardURL) @@ -45,7 +50,10 @@ func newCompareCmd() *cobra.Command { } f := cmd.Flags() f.StringVar(&criterionDir, "criterion-dir", "target/criterion", "criterion output directory to ingest") - f.StringVar(&baselinePath, "baseline", "", "baseline run JSON (from `cubit record`); omit to report the current run with no comparison") + f.StringVar(&baselinePath, "baseline", "", "baseline run JSON file (takes precedence over --baseline-ref)") + f.StringVar(&baselineRef, "baseline-ref", "", "baseline from the cubit-state branch: a commit SHA, \"latest\", or \"latest:\"") + f.StringVar(&repo, "repo", ".", "git repository holding the cubit-state branch") + f.StringVar(&stateBranch, "state-branch", gitstore.DefaultBranch, "branch storing recorded runs") f.StringVar(&commit, "commit", "", "commit SHA to label the current run with") f.StringVar(&branch, "branch", "", "branch to label the current run with") f.Float64Var(&threshold, "threshold", 15, "flag benchmarks slower than baseline by more than this percent") @@ -53,3 +61,33 @@ func newCompareCmd() *cobra.Command { f.StringVar(&out, "out", "-", "write the Markdown report here (\"-\" for stdout)") return cmd } + +// resolveBaseline loads the baseline run from an explicit file, or from a +// reference on the cubit-state branch, or returns an empty run (everything +// reported as new) when neither is given or the reference is not found yet. +func resolveBaseline(path, ref, repo, stateBranch string) (model.Run, error) { + if path != "" { + return loadRun(path) + } + if ref == "" { + return model.Run{}, nil + } + st := gitstore.New(repo, stateBranch) + switch { + case ref == "latest": + run, _, err := st.Latest("") + return run, err + case strings.HasPrefix(ref, "latest:"): + run, _, err := st.Latest(strings.TrimPrefix(ref, "latest:")) + return run, err + default: + run, found, err := st.Read(ref) + if err != nil { + return model.Run{}, err + } + if !found { + return model.Run{}, fmt.Errorf("no recorded baseline for %q on %q", ref, stateBranch) + } + return run, nil + } +} diff --git a/cmd/cubit/record.go b/cmd/cubit/record.go index 7538214..89ca90f 100644 --- a/cmd/cubit/record.go +++ b/cmd/cubit/record.go @@ -1,6 +1,12 @@ package main -import "github.com/spf13/cobra" +import ( + "fmt" + + "github.com/spf13/cobra" + + "wardnet/cubit/internal/gitstore" +) func newRecordCmd() *cobra.Command { var ( @@ -8,13 +14,17 @@ func newRecordCmd() *cobra.Command { commit string branch string out string + persist bool + repo string + stateBranch string ) cmd := &cobra.Command{ Use: "record", Short: "Ingest a benchmark run and write it as a baseline record", Long: "Ingests a criterion output directory into a normalized run record, " + - "keyed by commit SHA. In CI this runs on the default branch to update " + - "the baseline the next PR compares against.", + "keyed by commit SHA. With --persist it commits the record to the " + + "cubit-state branch (via git plumbing, without a checkout) — the CI " + + "default-branch step that updates the baseline the next PR compares against.", Args: cobra.NoArgs, SilenceUsage: true, SilenceErrors: true, @@ -23,13 +33,31 @@ func newRecordCmd() *cobra.Command { if err != nil { return err } - return writeJSON(out, run) + if persist { + if err := gitstore.New(repo, stateBranch).Append(run); err != nil { + return err + } + fmt.Fprintf(cmd.ErrOrStderr(), "recorded %d benchmarks for %s on %s\n", + len(run.Measurements), run.Commit, stateBranch) + } + // Emit JSON to an explicit --out, or to stdout only when we are not + // purely persisting (so `record --persist` stays quiet on stdout). + if out != "" { + return writeJSON(out, run) + } + if !persist { + return writeJSON("-", run) + } + return nil }, } f := cmd.Flags() f.StringVar(&criterionDir, "criterion-dir", "target/criterion", "criterion output directory to ingest") f.StringVar(&commit, "commit", "", "commit SHA this run was measured at") f.StringVar(&branch, "branch", "", "branch this run was measured on") - f.StringVar(&out, "out", "-", "write the run record JSON here (\"-\" for stdout)") + f.StringVar(&out, "out", "", "also write the run record JSON here (\"-\" for stdout)") + f.BoolVar(&persist, "persist", false, "commit the record to the cubit-state branch") + f.StringVar(&repo, "repo", ".", "git repository holding the cubit-state branch") + f.StringVar(&stateBranch, "state-branch", gitstore.DefaultBranch, "branch to store the record on") return cmd } diff --git a/internal/gitstore/gitstore.go b/internal/gitstore/gitstore.go new file mode 100644 index 0000000..852595d --- /dev/null +++ b/internal/gitstore/gitstore.go @@ -0,0 +1,300 @@ +// Package gitstore persists benchmark runs to an orphan git branch (default +// "cubit-state") and reads them back, without ever checking that branch out. +// +// Records live at runs/.json, with an index.json manifest listing +// every recorded run for the dashboard and for resolving "the latest baseline". +// Only default-branch runs are persisted (main-only history), so the branch is +// a small, append-mostly ledger — a good fit for a git branch rather than a +// database. +// +// Writes use git plumbing (hash-object → update-index against a temp index → +// write-tree → commit-tree → update-ref with compare-and-swap), so they never +// touch the caller's working tree and are safe to run mid-build in CI. +package gitstore + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "os/exec" + "path" + "path/filepath" + "sort" + "strings" + + "wardnet/cubit/internal/model" +) + +// DefaultBranch is the orphan branch runs are stored on. +const DefaultBranch = "cubit-state" + +const ( + runsDir = "runs" + indexPath = "index.json" +) + +// Store reads and writes runs on an orphan branch within a git repository. +type Store struct { + RepoDir string // path to the git repository (worktree or bare) + Branch string // orphan branch name; empty means DefaultBranch +} + +// New returns a Store for repoDir. An empty branch defaults to DefaultBranch. +func New(repoDir, branch string) *Store { + if branch == "" { + branch = DefaultBranch + } + return &Store{RepoDir: repoDir, Branch: branch} +} + +// RunMeta is one entry in the index manifest. +type RunMeta struct { + Commit string `json:"commit"` + Branch string `json:"branch"` + Timestamp string `json:"timestamp"` +} + +// Index returns the manifest of recorded runs, sorted oldest-first by +// timestamp. It returns an empty slice (no error) when the branch or manifest +// does not exist yet. +func (s *Store) Index() ([]RunMeta, error) { + ref, ok, err := s.resolveRef() + if err != nil || !ok { + return nil, err + } + data, ok, err := s.showFile(ref, indexPath) + if err != nil || !ok { + return nil, err + } + var metas []RunMeta + if err := json.Unmarshal(data, &metas); err != nil { + return nil, fmt.Errorf("parse %s: %w", indexPath, err) + } + sort.SliceStable(metas, func(i, j int) bool { return metas[i].Timestamp < metas[j].Timestamp }) + return metas, nil +} + +// Read returns the run recorded for commit sha. The bool is false (with no +// error) when the branch or that record does not exist. +func (s *Store) Read(sha string) (model.Run, bool, error) { + ref, ok, err := s.resolveRef() + if err != nil || !ok { + return model.Run{}, false, err + } + data, ok, err := s.showFile(ref, runPath(sha)) + if err != nil || !ok { + return model.Run{}, false, err + } + var run model.Run + if err := json.Unmarshal(data, &run); err != nil { + return model.Run{}, false, fmt.Errorf("parse %s: %w", runPath(sha), err) + } + return run, true, nil +} + +// Latest returns the most recently recorded run (by index timestamp), matching +// only the given branch when branchFilter is non-empty. The bool is false (no +// error) when nothing has been recorded yet. +func (s *Store) Latest(branchFilter string) (model.Run, bool, error) { + metas, err := s.Index() + if err != nil { + return model.Run{}, false, err + } + for i := len(metas) - 1; i >= 0; i-- { + if branchFilter != "" && metas[i].Branch != branchFilter { + continue + } + return s.Read(metas[i].Commit) + } + return model.Run{}, false, nil +} + +// Append records run at runs/.json and updates index.json, committing +// to the orphan branch via plumbing. It refuses a run with no commit SHA (the +// records are SHA-keyed). Concurrent writers are guarded by a compare-and-swap +// on the branch ref: a racing update makes this fail rather than clobber. +func (s *Store) Append(run model.Run) error { + if strings.TrimSpace(run.Commit) == "" { + return fmt.Errorf("run has no commit SHA — records are keyed by commit") + } + + parent, hasParent, err := s.tip() + if err != nil { + return err + } + + // Stage into a throwaway index seeded from the parent tree, so existing + // records survive the commit. The index path must NOT pre-exist as an empty + // file — git rejects a zero-byte index — so we hand git a fresh path inside + // a temp dir and let it create the index itself. + tmpDir, err := os.MkdirTemp("", "cubit-index-") + if err != nil { + return err + } + defer func() { _ = os.RemoveAll(tmpDir) }() + env := append(os.Environ(), "GIT_INDEX_FILE="+filepath.Join(tmpDir, "index")) + + if hasParent { + if _, err := s.gitEnv(env, nil, "read-tree", parent+"^{tree}"); err != nil { + return err + } + } + + // Merge the new run into the manifest read from the parent tree. + metas, err := s.Index() + if err != nil { + return err + } + metas = upsertMeta(metas, RunMeta{Commit: run.Commit, Branch: run.Branch, Timestamp: run.Timestamp}) + + runJSON, err := marshal(run) + if err != nil { + return err + } + indexJSON, err := marshal(metas) + if err != nil { + return err + } + + if err := s.stage(env, runPath(run.Commit), runJSON); err != nil { + return err + } + if err := s.stage(env, indexPath, indexJSON); err != nil { + return err + } + + treeOut, err := s.gitEnv(env, nil, "write-tree") + if err != nil { + return err + } + tree := strings.TrimSpace(treeOut) + + msg := fmt.Sprintf("record %s (%s)", short(run.Commit), run.Branch) + commitArgs := []string{"commit-tree", tree, "-m", msg} + if hasParent { + commitArgs = append(commitArgs, "-p", parent) + } + commitOut, err := s.gitEnv(env, nil, commitArgs...) + if err != nil { + return err + } + commit := strings.TrimSpace(commitOut) + + // Compare-and-swap the branch: old value is the parent (or the empty tree's + // zero-arg form when the branch is being created). + ref := "refs/heads/" + s.Branch + if hasParent { + _, err = s.git(nil, "update-ref", ref, commit, parent) + } else { + _, err = s.git(nil, "update-ref", ref, commit, "") + } + return err +} + +// stage hashes content as a blob and adds it to the temp index at repoPath. +func (s *Store) stage(env []string, repoPath string, content []byte) error { + blobOut, err := s.gitEnv(env, content, "hash-object", "-w", "--stdin") + if err != nil { + return err + } + blob := strings.TrimSpace(blobOut) + _, err = s.gitEnv(env, nil, "update-index", "--add", "--cacheinfo", fmt.Sprintf("100644,%s,%s", blob, repoPath)) + return err +} + +// resolveRef returns a readable ref for the branch, preferring the local branch +// and falling back to origin/ (the CI case, where the branch is +// fetched but not checked out). ok is false when neither exists. +func (s *Store) resolveRef() (string, bool, error) { + for _, ref := range []string{"refs/heads/" + s.Branch, "refs/remotes/origin/" + s.Branch} { + out, err := s.git(nil, "rev-parse", "--verify", "--quiet", ref+"^{commit}") + if err == nil && strings.TrimSpace(out) != "" { + return ref, true, nil + } + } + return "", false, nil +} + +// tip returns the local branch's commit for compare-and-swap on write. It only +// considers the local branch (a CAS against a remote-tracking ref would be +// meaningless), so first-write-in-CI creates the branch locally then pushes. +func (s *Store) tip() (string, bool, error) { + out, err := s.git(nil, "rev-parse", "--verify", "--quiet", "refs/heads/"+s.Branch+"^{commit}") + if err != nil { + // rev-parse --quiet exits non-zero when the ref is absent; treat as "no tip". + return "", false, nil + } + sha := strings.TrimSpace(out) + if sha == "" { + return "", false, nil + } + return sha, true, nil +} + +// showFile returns the bytes of repoPath at ref. ok is false (no error) when +// the path does not exist at that ref. +func (s *Store) showFile(ref, repoPath string) ([]byte, bool, error) { + cmd := exec.Command("git", "-C", s.RepoDir, "show", ref+":"+repoPath) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + // A missing path is expected (empty branch / first run) — not an error. + if strings.Contains(stderr.String(), "does not exist") || + strings.Contains(stderr.String(), "exists on disk, but not in") || + strings.Contains(stderr.String(), "fatal: path") { + return nil, false, nil + } + return nil, false, fmt.Errorf("git show %s:%s: %s", ref, repoPath, strings.TrimSpace(stderr.String())) + } + return stdout.Bytes(), true, nil +} + +func (s *Store) git(stdin []byte, args ...string) (string, error) { + return s.gitEnv(nil, stdin, args...) +} + +func (s *Store) gitEnv(env []string, stdin []byte, args ...string) (string, error) { + cmd := exec.Command("git", append([]string{"-C", s.RepoDir}, args...)...) + if env != nil { + cmd.Env = env + } + if stdin != nil { + cmd.Stdin = bytes.NewReader(stdin) + } + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("git %s: %s", strings.Join(args, " "), strings.TrimSpace(stderr.String())) + } + return stdout.String(), nil +} + +func upsertMeta(metas []RunMeta, m RunMeta) []RunMeta { + for i := range metas { + if metas[i].Commit == m.Commit { + metas[i] = m + return metas + } + } + return append(metas, m) +} + +func marshal(v any) ([]byte, error) { + data, err := json.MarshalIndent(v, "", " ") + if err != nil { + return nil, err + } + return append(data, '\n'), nil +} + +func runPath(sha string) string { return path.Join(runsDir, sha+".json") } + +func short(sha string) string { + if len(sha) > 8 { + return sha[:8] + } + return sha +} diff --git a/internal/gitstore/gitstore_test.go b/internal/gitstore/gitstore_test.go new file mode 100644 index 0000000..6abd100 --- /dev/null +++ b/internal/gitstore/gitstore_test.go @@ -0,0 +1,115 @@ +package gitstore + +import ( + "os" + "os/exec" + "path/filepath" + "testing" + + "wardnet/cubit/internal/model" +) + +func TestAppendReadLatest(t *testing.T) { + repo := initRepo(t) + s := New(repo, "") + + // Empty store: no index, no latest, no record. + if metas, err := s.Index(); err != nil || len(metas) != 0 { + t.Fatalf("empty Index() = %v, %v; want empty, nil", metas, err) + } + if _, ok, err := s.Latest(""); err != nil || ok { + t.Fatalf("empty Latest() ok=%v err=%v; want false, nil", ok, err) + } + + run1 := model.Run{ + Schema: 1, Tool: "criterion", Commit: "aaaaaaaaaaaaaaaa", Branch: "main", + Timestamp: "2026-07-18T00:00:00Z", + Measurements: []model.Measurement{ + {ID: "dns_cache/get", Unit: "ns", Value: 45.2}, + }, + } + if err := s.Append(run1); err != nil { + t.Fatalf("Append(run1): %v", err) + } + + got, ok, err := s.Read("aaaaaaaaaaaaaaaa") + if err != nil || !ok { + t.Fatalf("Read(run1) ok=%v err=%v", ok, err) + } + if len(got.Measurements) != 1 || got.Measurements[0].Value != 45.2 { + t.Fatalf("Read(run1) = %+v; want the recorded measurement", got.Measurements) + } + + // A newer run on main supersedes as "latest"; branch filter is honored. + run2 := run1 + run2.Commit = "bbbbbbbbbbbbbbbb" + run2.Timestamp = "2026-07-19T00:00:00Z" + run2.Measurements = []model.Measurement{{ID: "dns_cache/get", Unit: "ns", Value: 44.0}} + if err := s.Append(run2); err != nil { + t.Fatalf("Append(run2): %v", err) + } + + metas, err := s.Index() + if err != nil || len(metas) != 2 { + t.Fatalf("Index() = %v (%d), %v; want 2 entries", metas, len(metas), err) + } + if metas[0].Commit != run1.Commit || metas[1].Commit != run2.Commit { + t.Fatalf("Index() not sorted oldest-first: %+v", metas) + } + + latest, ok, err := s.Latest("main") + if err != nil || !ok || latest.Commit != run2.Commit { + t.Fatalf("Latest(main) = %+v ok=%v err=%v; want run2", latest, ok, err) + } + if _, ok, _ := s.Latest("nonexistent-branch"); ok { + t.Fatalf("Latest(nonexistent-branch) should be not-found") + } + + // The orphan-branch writes must never touch the working tree. + if out := gitOut(t, repo, "status", "--porcelain"); out != "" { + t.Fatalf("working tree dirtied by Append: %q", out) + } + if _, err := os.Stat(filepath.Join(repo, "runs")); !os.IsNotExist(err) { + t.Fatalf("runs/ leaked into the working tree") + } +} + +func TestAppendRejectsEmptyCommit(t *testing.T) { + s := New(initRepo(t), "") + if err := s.Append(model.Run{Branch: "main"}); err == nil { + t.Fatal("Append with empty commit should fail") + } +} + +// initRepo creates a git repo with one commit on the default branch, so the +// orphan cubit-state branch has a repository to live in. +func initRepo(t *testing.T) string { + t.Helper() + dir := t.TempDir() + run := func(args ...string) { + t.Helper() + cmd := exec.Command("git", append([]string{"-C", dir}, args...)...) + cmd.Env = append(os.Environ(), + "GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@t", "GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@t") + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("git %v: %v\n%s", args, err, out) + } + } + run("init", "-q", "-b", "main") + if err := os.WriteFile(filepath.Join(dir, "README"), []byte("x\n"), 0o644); err != nil { + t.Fatal(err) + } + run("add", "-A") + run("commit", "-q", "-m", "init") + return dir +} + +func gitOut(t *testing.T, dir string, args ...string) string { + t.Helper() + cmd := exec.Command("git", append([]string{"-C", dir}, args...)...) + out, err := cmd.Output() + if err != nil { + t.Fatalf("git %v: %v", args, err) + } + return string(out) +} From ba5e4b964c0da9d2c3cad7faf2544b0fda29c042 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sun, 19 Jul 2026 12:34:38 +0100 Subject: [PATCH 3/7] feat(ci): release, self-update install, and composite action (bulwark-mirrored) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make cubit installable and wireable into any repo's CI, mirroring bulwark's plumbing — minus the hard-fail step, since cubit is a light gate. - scripts/install.sh: curl-pipe installer (redirect-resolve latest, arch detect, SHA-256 verify, atomic install to ~/.local/bin). - .goreleaser.yml: single cubit binary, linux+darwin × amd64+arm64, raw + archive assets, checksums, install.sh as an extra release file. - .github/workflows/release.yml: tag-triggered build/test → goreleaser. - .github/workflows/ci.yml: build, vet, test -race, gofmt on PR/push. - action.yml: composite consumer action. auto-mode — compare + sticky PR comment + report artifact on pull_request; record + push cubit-state on a default-branch push. Fetches the state branch, bases record on origin's tip (CAS-safe), and never fails the build. Validated: gofmt clean, build/vet/test pass, all YAML parses, install.sh sh -n clean. Claude-Session: https://claude.ai/code/session_016pTq6aVBJvTtXmwTgKw2Yh --- .github/workflows/ci.yml | 40 +++++++++ .github/workflows/release.yml | 49 +++++++++++ .goreleaser.yml | 76 +++++++++++++++++ action.yml | 148 ++++++++++++++++++++++++++++++++++ scripts/install.sh | 71 ++++++++++++++++ 5 files changed, 384 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .goreleaser.yml create mode 100644 action.yml create mode 100755 scripts/install.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ee1042b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +jobs: + build-test: + name: build, vet & test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + with: + go-version: "1.26.5" + cache: true + + - name: go build + run: go build ./... + + - name: go vet + run: go vet ./... + + - name: go test + run: go test -race ./... + + - name: gofmt + run: | + unformatted=$(gofmt -l .) + if [ -n "$unformatted" ]; then + echo "these files need gofmt:" >&2 + echo "$unformatted" >&2 + exit 1 + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a4ff696 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,49 @@ +name: Release + +on: + push: + tags: + - "v*.*.*" + +permissions: + contents: write + +jobs: + build-test: + name: build & test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + with: + go-version: "1.26.5" + cache: true + + - name: go build + run: go build ./... + + - name: go test + run: go test -race ./... + + goreleaser: + name: goreleaser + needs: build-test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + + - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + with: + go-version: "1.26.5" + cache: true + + - name: Run goreleaser + uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7 + with: + version: "~> v2" + args: release --clean --parallelism 1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..c067af3 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,76 @@ +# goreleaser configuration for cubit. +# +# Builds a single binary, `cubit`, from ./cmd/cubit. It runs both as a local +# developer tool (self-updates via `cubit update`) and inside CI via the +# composite action in this repo, so it ships for both linux and darwin. +version: 2 + +before: + hooks: + - go mod tidy + +builds: + - id: cubit + main: ./cmd/cubit + binary: cubit + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + ldflags: + - -s -w -X main.version={{ .Version }} + +# One archive so consumers get everything, plus a raw uncompressed binary for +# curl-and-run / CI usage that doesn't want an extraction step. +archives: + - id: cubit + ids: + - cubit + name_template: >- + cubit_{{ .Version }}_{{ .Os }}_{{ .Arch }} + + - id: raw + ids: + - cubit + formats: + - binary + name_template: >- + {{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }} + +checksum: + name_template: checksums.txt + +snapshot: + version_template: "{{ incpatch .Version }}-snapshot" + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + - "^chore:" + +release: + github: + owner: wardnet + name: cubit + extra_files: + - glob: scripts/install.sh + header: | + ## cubit + + Continuous benchmarking and performance-regression tracking — records + baselines in a git branch and posts non-blocking baseline-vs-PR comparisons. + + **Install / update** + + ```sh + curl -fsSL https://github.com/wardnet/cubit/releases/latest/download/install.sh | sh + ``` + + `cubit update` self-updates the CLI in place. diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..2957c8b --- /dev/null +++ b/action.yml @@ -0,0 +1,148 @@ +name: cubit +description: > + Run benchmarks through cubit — compare a PR against the recorded baseline and + post a non-blocking summary comment, or record a new baseline on the default + branch. Never fails the build; the comment + dashboard inform a human. + +inputs: + version: + description: cubit version to install (omit or "latest" for the latest release) + default: latest + criterion-dir: + description: criterion output directory to ingest (relative to the repo root) + default: target/criterion + bench-command: + description: > + optional shell command that produces the criterion output (e.g. + "cargo bench --bench dns_components"). Omit if an earlier step already ran + the benchmarks. + default: "" + state-branch: + description: orphan branch cubit stores baselines on + default: cubit-state + threshold: + description: flag benchmarks slower than baseline by more than this percent + default: "15" + dashboard-url: + description: trend-dashboard URL to link from the PR comment + default: "" + mode: + description: > + "auto" (default) compares on pull_request and records on a push to the + default branch. Force with "compare" or "record". + default: auto + github-token: + description: token used to post the PR comment and push the state branch + default: ${{ github.token }} + +runs: + using: composite + steps: + - name: Install cubit + shell: bash + env: + VERSION: ${{ inputs.version }} + run: | + # install.sh is fetched from the latest release; CUBIT_VERSION pins the + # binary it installs and install.sh verifies its SHA-256 before use. + if [[ "${VERSION}" != "latest" ]]; then + export CUBIT_VERSION="${VERSION}" + fi + export CUBIT_INSTALL_DIR="${RUNNER_TEMP}/cubit-bin" + curl -fsSL -o "${RUNNER_TEMP}/cubit-install.sh" https://github.com/wardnet/cubit/releases/latest/download/install.sh + sh "${RUNNER_TEMP}/cubit-install.sh" + echo "${CUBIT_INSTALL_DIR}" >> "$GITHUB_PATH" + + - name: Run benchmarks + if: inputs.bench-command != '' + shell: bash + env: + BENCH_COMMAND: ${{ inputs.bench-command }} + run: | + eval "$BENCH_COMMAND" + + - name: Fetch state branch + shell: bash + env: + STATE: ${{ inputs.state-branch }} + run: | + # Best-effort: the branch may not exist yet (first ever run). A present + # remote-tracking ref is what cubit reads baselines from, and what a + # record run bases its append on. + git fetch origin "+refs/heads/${STATE}:refs/remotes/origin/${STATE}" 2>/dev/null || true + + - name: cubit + id: cubit + shell: bash + env: + MODE: ${{ inputs.mode }} + DIR: ${{ inputs.criterion-dir }} + STATE: ${{ inputs.state-branch }} + THRESHOLD: ${{ inputs.threshold }} + DASH: ${{ inputs.dashboard-url }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + EVENT: ${{ github.event_name }} + PR_BASE: ${{ github.event.pull_request.base.ref }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} + run: | + set -euo pipefail + report="${RUNNER_TEMP}/cubit-report.md" + echo "report=${report}" >> "$GITHUB_OUTPUT" + + # Resolve auto mode from the triggering event. + mode="$MODE" + if [[ "$mode" == "auto" ]]; then + if [[ "$EVENT" == "pull_request" ]]; then + mode="compare" + elif [[ "$GITHUB_REF_NAME" == "$DEFAULT_BRANCH" ]]; then + mode="record" + else + echo "cubit: nothing to do (push to non-default branch $GITHUB_REF_NAME)" + echo "did-run=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + fi + echo "did-run=true" >> "$GITHUB_OUTPUT" + echo "mode=${mode}" >> "$GITHUB_OUTPUT" + + dash_arg=() + [[ -n "$DASH" ]] && dash_arg=(--dashboard-url "$DASH") + + if [[ "$mode" == "compare" ]]; then + # latest: yields an empty baseline (all "new") the first time, + # which is fine — cubit does not fail on a missing baseline. + cubit compare --repo . --criterion-dir "$DIR" \ + --baseline-ref "latest:${PR_BASE}" \ + --commit "$PR_HEAD_SHA" --branch "$PR_HEAD_REF" \ + --threshold "$THRESHOLD" "${dash_arg[@]}" \ + --out "$report" + else + # record: base the local state branch on origin's tip (if any) so the + # append's compare-and-swap lands on top of existing history. + if git rev-parse --verify --quiet "refs/remotes/origin/${STATE}" >/dev/null; then + git branch -f "$STATE" "refs/remotes/origin/${STATE}" + fi + git config user.name "cubit" + git config user.email "cubit@users.noreply.github.com" + cubit record --persist --repo . --criterion-dir "$DIR" \ + --commit "$GITHUB_SHA" --branch "$DEFAULT_BRANCH" --state-branch "$STATE" + git push origin "$STATE" + fi + + - name: Upload report artifact + if: always() && steps.cubit.outputs.mode == 'compare' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: cubit-report-${{ github.job }} + path: ${{ steps.cubit.outputs.report }} + if-no-files-found: ignore + retention-days: 14 + + - name: Post PR comment + if: github.event_name == 'pull_request' && steps.cubit.outputs.mode == 'compare' + uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 + with: + GITHUB_TOKEN: ${{ inputs.github-token }} + header: cubit + path: ${{ steps.cubit.outputs.report }} diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..06b2342 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# install.sh — download and install the cubit CLI from GitHub releases. +# +# Usage: +# curl -fsSL https://github.com/wardnet/cubit/releases/latest/download/install.sh | sh +# +# Environment: +# CUBIT_VERSION version to install, e.g. 1.2.0 or v1.2.0 (default: latest) +# CUBIT_INSTALL_DIR install directory (default: $HOME/.local/bin) +set -eu + +REPO_URL="https://github.com/wardnet/cubit" +INSTALL_DIR="${CUBIT_INSTALL_DIR:-$HOME/.local/bin}" +VERSION="${CUBIT_VERSION:-latest}" + +if [ "$VERSION" = "latest" ]; then + # The releases/latest URL redirects to .../releases/tag/v. + LOCATION=$(curl -fsSI -o /dev/null -w '%{redirect_url}' "$REPO_URL/releases/latest") + VERSION="${LOCATION##*/}" +fi +VERSION="${VERSION#v}" +if [ -z "$VERSION" ]; then + echo "error: could not resolve cubit version" >&2 + exit 1 +fi + +OS=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCH=$(uname -m) +case "$ARCH" in + x86_64 | amd64) ARCH=amd64 ;; + aarch64 | arm64) ARCH=arm64 ;; + *) + echo "error: unsupported architecture: $ARCH" >&2 + exit 1 + ;; +esac + +ASSET="cubit_${VERSION}_${OS}_${ARCH}" +# Stage inside the install dir so the final rename is atomic and never +# truncates a running binary (mv across filesystems degrades to copy+truncate). +mkdir -p "$INSTALL_DIR" +STAGE=$(mktemp -d "$INSTALL_DIR/.cubit-install.XXXXXX") +trap 'rm -rf "$STAGE"' EXIT + +echo "downloading cubit v${VERSION} (${OS}/${ARCH})..." +curl -fsSL -o "$STAGE/cubit" "$REPO_URL/releases/download/v${VERSION}/${ASSET}" +curl -fsSL -o "$STAGE/checksums.txt" "$REPO_URL/releases/download/v${VERSION}/checksums.txt" + +WANT=$(awk -v asset="$ASSET" '$2 == asset { print $1 }' "$STAGE/checksums.txt") +if [ -z "$WANT" ]; then + echo "error: no checksum for $ASSET in checksums.txt" >&2 + exit 1 +fi +if command -v sha256sum >/dev/null 2>&1; then + GOT=$(sha256sum "$STAGE/cubit" | awk '{ print $1 }') +else + GOT=$(shasum -a 256 "$STAGE/cubit" | awk '{ print $1 }') +fi +if [ "$GOT" != "$WANT" ]; then + echo "error: checksum mismatch for $ASSET: got $GOT, want $WANT" >&2 + exit 1 +fi + +chmod 0755 "$STAGE/cubit" +mv "$STAGE/cubit" "$INSTALL_DIR/cubit" +echo "installed cubit v${VERSION} to $INSTALL_DIR/cubit" + +case ":$PATH:" in + *":$INSTALL_DIR:"*) ;; + *) echo "note: $INSTALL_DIR is not on your PATH" ;; +esac From 4fe3e4a78b14aef5870e03627105504a5a8f007f Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sun, 19 Jul 2026 14:02:34 +0100 Subject: [PATCH 4/7] feat(dashboard): React/Vite trend dashboard + cubit serve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The visualization side of the light gate: - web/: Vite + React + TS app rendering per-benchmark trend charts (X = commit, Y = median) with a confidence-interval variance band, hand-rolled SVG (no chart-lib dependency). Built single-file via vite-plugin-singlefile so the page is standalone — no external requests, works over file:// and CI CSP. - internal/dashboard: embeds the built index.html, assembles per-benchmark series from the cubit-state runs, and injects the data as a window global (Build/Render/Serve). Vendored build at internal/dashboard/assets/index.html. - cubit serve: host the dashboard locally on --addr, or render a standalone HTML file with --out (the CI artifact form). - action.yml: render dashboard.html in compare mode and upload it alongside the report. Verified: yarn build → single 199 KB html; go build/vet/test clean; serve returns HTTP 200 with data injected; render produces a self-contained page from real DnsCache benchmark runs. Claude-Session: https://claude.ai/code/session_016pTq6aVBJvTtXmwTgKw2Yh --- .gitignore | 6 + action.yml | 10 +- cmd/cubit/root.go | 1 + cmd/cubit/serve.go | 52 + internal/dashboard/assets/index.html | 61 ++ internal/dashboard/dashboard.go | 146 +++ web/.yarnrc.yml | 1 + web/index.html | 12 + web/package.json | 21 + web/src/App.tsx | 44 + web/src/LineChart.tsx | 82 ++ web/src/data.ts | 34 + web/src/main.tsx | 10 + web/src/styles.css | 126 +++ web/src/types.ts | 23 + web/tsconfig.json | 17 + web/vite.config.ts | 15 + web/yarn.lock | 1430 ++++++++++++++++++++++++++ 18 files changed, 2090 insertions(+), 1 deletion(-) create mode 100644 cmd/cubit/serve.go create mode 100644 internal/dashboard/assets/index.html create mode 100644 internal/dashboard/dashboard.go create mode 100644 web/.yarnrc.yml create mode 100644 web/index.html create mode 100644 web/package.json create mode 100644 web/src/App.tsx create mode 100644 web/src/LineChart.tsx create mode 100644 web/src/data.ts create mode 100644 web/src/main.tsx create mode 100644 web/src/styles.css create mode 100644 web/src/types.ts create mode 100644 web/tsconfig.json create mode 100644 web/vite.config.ts create mode 100644 web/yarn.lock diff --git a/.gitignore b/.gitignore index 96aa1f5..77dc419 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,11 @@ *.out coverage.txt +# Web dashboard build (the built asset is vendored at +# internal/dashboard/assets/index.html; the raw dist/ and deps are not tracked) +/web/node_modules/ +/web/dist/ +/web/.yarn/ + # Editor / OS .DS_Store diff --git a/action.yml b/action.yml index 2957c8b..7151e1f 100644 --- a/action.yml +++ b/action.yml @@ -88,7 +88,9 @@ runs: run: | set -euo pipefail report="${RUNNER_TEMP}/cubit-report.md" + dashboard="${RUNNER_TEMP}/cubit-dashboard.html" echo "report=${report}" >> "$GITHUB_OUTPUT" + echo "dashboard=${dashboard}" >> "$GITHUB_OUTPUT" # Resolve auto mode from the triggering event. mode="$MODE" @@ -117,6 +119,10 @@ runs: --commit "$PR_HEAD_SHA" --branch "$PR_HEAD_REF" \ --threshold "$THRESHOLD" "${dash_arg[@]}" \ --out "$report" + # Render the standalone trend dashboard for the run artifact + # (best-effort — a missing state branch just yields an empty page). + cubit serve --repo . --state-branch "$STATE" --branch "$PR_BASE" \ + --out "$dashboard" || true else # record: base the local state branch on origin's tip (if any) so the # append's compare-and-swap lands on top of existing history. @@ -135,7 +141,9 @@ runs: uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: cubit-report-${{ github.job }} - path: ${{ steps.cubit.outputs.report }} + path: | + ${{ steps.cubit.outputs.report }} + ${{ steps.cubit.outputs.dashboard }} if-no-files-found: ignore retention-days: 14 diff --git a/cmd/cubit/root.go b/cmd/cubit/root.go index 1511806..8438130 100644 --- a/cmd/cubit/root.go +++ b/cmd/cubit/root.go @@ -15,6 +15,7 @@ func newRootCmd() *cobra.Command { root.AddCommand( newCompareCmd(), newRecordCmd(), + newServeCmd(), newUpdateCmd(), newVersionCmd(), ) diff --git a/cmd/cubit/serve.go b/cmd/cubit/serve.go new file mode 100644 index 0000000..53c35ea --- /dev/null +++ b/cmd/cubit/serve.go @@ -0,0 +1,52 @@ +package main + +import ( + "time" + + "github.com/spf13/cobra" + + "wardnet/cubit/internal/dashboard" + "wardnet/cubit/internal/gitstore" +) + +func newServeCmd() *cobra.Command { + var ( + repo string + stateBranch string + branch string + addr string + out string + ) + cmd := &cobra.Command{ + Use: "serve", + Short: "Serve the trend dashboard, or render it to a standalone HTML file", + Long: "Builds the per-benchmark trend dashboard from the cubit-state branch. " + + "With --out, writes a standalone HTML file (data inlined) and exits — the " + + "form CI uploads as an artifact. Otherwise serves it on --addr for local viewing.", + Args: cobra.NoArgs, + SilenceUsage: true, + SilenceErrors: true, + RunE: func(_ *cobra.Command, _ []string) error { + store := gitstore.New(repo, stateBranch) + if out != "" { + d, err := dashboard.Build(store, branch, time.Now()) + if err != nil { + return err + } + html, err := dashboard.Render(d) + if err != nil { + return err + } + return writeOut(out, html) + } + return dashboard.Serve(store, branch, addr) + }, + } + f := cmd.Flags() + f.StringVar(&repo, "repo", ".", "git repository holding the cubit-state branch") + f.StringVar(&stateBranch, "state-branch", gitstore.DefaultBranch, "branch storing recorded runs") + f.StringVar(&branch, "branch", "", "only chart runs recorded on this branch (empty = all)") + f.StringVar(&addr, "addr", "127.0.0.1:7777", "address to serve the dashboard on") + f.StringVar(&out, "out", "", "render a standalone HTML file here and exit (\"-\" for stdout)") + return cmd +} diff --git a/internal/dashboard/assets/index.html b/internal/dashboard/assets/index.html new file mode 100644 index 0000000..bea4df3 --- /dev/null +++ b/internal/dashboard/assets/index.html @@ -0,0 +1,61 @@ + + + + + + cubit — performance + + + + +
+ + diff --git a/internal/dashboard/dashboard.go b/internal/dashboard/dashboard.go new file mode 100644 index 0000000..2d936d3 --- /dev/null +++ b/internal/dashboard/dashboard.go @@ -0,0 +1,146 @@ +// Package dashboard turns recorded runs into the trend dashboard: a +// self-contained HTML page (JS + CSS + data inlined) that `cubit serve` hosts +// locally and CI uploads as an artifact. +// +// The React/Vite bundle is built from ../../web and embedded here as a single +// index.html; Build assembles the per-benchmark series from the cubit-state +// branch and Render injects it as a window global the page reads on load. +package dashboard + +import ( + "bytes" + _ "embed" + "encoding/json" + "fmt" + "net" + "net/http" + "sort" + "strings" + "time" + + "wardnet/cubit/internal/gitstore" +) + +//go:embed assets/index.html +var indexHTML []byte + +type point struct { + Commit string `json:"commit"` + ShortCommit string `json:"shortCommit"` + Timestamp string `json:"timestamp"` + Value float64 `json:"value"` + Lower float64 `json:"lower"` + Upper float64 `json:"upper"` +} + +type benchSeries struct { + ID string `json:"id"` + Unit string `json:"unit"` + Points []point `json:"points"` +} + +// Data is the payload the page renders. +type Data struct { + GeneratedAt string `json:"generatedAt"` + Branch string `json:"branch"` + Benchmarks []benchSeries `json:"benchmarks"` +} + +// Build assembles the dashboard payload from the store's recorded runs, +// optionally filtered to one branch, with each benchmark's points ordered +// oldest-first (the manifest order). +func Build(store *gitstore.Store, branch string, now time.Time) (Data, error) { + metas, err := store.Index() + if err != nil { + return Data{}, err + } + byID := map[string]*benchSeries{} + var ids []string + for _, m := range metas { + if branch != "" && m.Branch != branch { + continue + } + run, ok, err := store.Read(m.Commit) + if err != nil { + return Data{}, err + } + if !ok { + continue + } + for _, ms := range run.Measurements { + s, exists := byID[ms.ID] + if !exists { + s = &benchSeries{ID: ms.ID, Unit: ms.Unit} + byID[ms.ID] = s + ids = append(ids, ms.ID) + } + s.Points = append(s.Points, point{ + Commit: m.Commit, + ShortCommit: short(m.Commit), + Timestamp: m.Timestamp, + Value: ms.Value, + Lower: ms.Lower, + Upper: ms.Upper, + }) + } + } + sort.Strings(ids) + d := Data{GeneratedAt: now.UTC().Format(time.RFC3339), Branch: branch} + for _, id := range ids { + d.Benchmarks = append(d.Benchmarks, *byID[id]) + } + return d, nil +} + +// Render returns a standalone HTML page with data injected as a window global +// just before , so it is defined before the page's deferred module +// script runs. +func Render(d Data) ([]byte, error) { + raw, err := json.Marshal(d) + if err != nil { + return nil, err + } + // Guard against a "" sequence inside the JSON prematurely closing + // the injected tag. + safe := strings.ReplaceAll(string(raw), "window.__CUBIT_DATA__=" + safe + ";") + return bytes.Replace(indexHTML, []byte(""), inject, 1), nil +} + +// Serve builds the dashboard on every request (so it reflects the current +// branch state) and serves it at addr until the process is stopped. +func Serve(store *gitstore.Store, branch, addr string) error { + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + d, err := Build(store, branch, time.Now()) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + html, err := Render(d) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + _, _ = w.Write(html) + }) + + ln, err := net.Listen("tcp", addr) + if err != nil { + return err + } + fmt.Printf("cubit dashboard: http://%s (ctrl-c to stop)\n", ln.Addr()) + return http.Serve(ln, mux) // #nosec G114 -- local dev dashboard, no untrusted network exposure intended +} + +func short(sha string) string { + if len(sha) > 8 { + return sha[:8] + } + return sha +} diff --git a/web/.yarnrc.yml b/web/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/web/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..13fad88 --- /dev/null +++ b/web/index.html @@ -0,0 +1,12 @@ + + + + + + cubit — performance + + +
+ + + diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..77f2498 --- /dev/null +++ b/web/package.json @@ -0,0 +1,21 @@ +{ + "name": "cubit-dashboard", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.7.0", + "vite": "^6.0.0", + "vite-plugin-singlefile": "^2.1.0" + } +} diff --git a/web/src/App.tsx b/web/src/App.tsx new file mode 100644 index 0000000..5896d2c --- /dev/null +++ b/web/src/App.tsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from "react"; +import type { Dashboard } from "./types"; +import { loadData } from "./data"; +import { LineChart } from "./LineChart"; + +export function App() { + const [data, setData] = useState(undefined); + + useEffect(() => { + loadData().then(setData); + }, []); + + if (data === undefined) { + return
Loading…
; + } + if (data === null || data.benchmarks.length === 0) { + return ( +
+

cubit

+

+ No benchmark history yet. Record a run on the default branch to start + the trend. +

+
+ ); + } + + return ( +
+
+

cubit — performance

+

+ {data.benchmarks.length} benchmarks · branch {data.branch} · + generated {new Date(data.generatedAt).toISOString().replace("T", " ").slice(0, 16)} +

+
+
+ {data.benchmarks.map((b) => ( + + ))} +
+
+ ); +} diff --git a/web/src/LineChart.tsx b/web/src/LineChart.tsx new file mode 100644 index 0000000..7e38151 --- /dev/null +++ b/web/src/LineChart.tsx @@ -0,0 +1,82 @@ +import type { BenchSeries } from "./types"; +import { formatValue } from "./data"; + +const W = 720; +const H = 240; +const PAD = { top: 16, right: 16, bottom: 28, left: 64 }; + +// LineChart draws one benchmark's history: a median line with the +// confidence-interval band shaded behind it, so a reviewer can see whether a +// new point sits inside the run-to-run noise or steps out of it. +export function LineChart({ series }: { series: BenchSeries }) { + const pts = series.points; + const plotW = W - PAD.left - PAD.right; + const plotH = H - PAD.top - PAD.bottom; + + if (pts.length === 0) { + return
no data
; + } + + const lows = pts.map((p) => p.lower); + const highs = pts.map((p) => p.upper); + let yMin = Math.min(...lows); + let yMax = Math.max(...highs); + if (yMin === yMax) { + yMin *= 0.95; + yMax *= 1.05; + } + const span = yMax - yMin || 1; + + const x = (i: number) => + PAD.left + (pts.length === 1 ? plotW / 2 : (i / (pts.length - 1)) * plotW); + const y = (v: number) => PAD.top + plotH - ((v - yMin) / span) * plotH; + + const line = pts.map((p, i) => `${x(i)},${y(p.value)}`).join(" "); + const band = + pts.map((p, i) => `${x(i)},${y(p.upper)}`).join(" ") + + " " + + pts + .map((p, i) => `${x(pts.length - 1 - i)},${y(pts[pts.length - 1 - i].lower)}`) + .join(" "); + + const last = pts[pts.length - 1]; + + return ( +
+
+ {series.id} + {formatValue(last.value)} +
+ + {[0, 0.25, 0.5, 0.75, 1].map((f) => { + const gy = PAD.top + plotH * f; + const val = yMax - span * f; + return ( + + + + {formatValue(val)} + + + ); + })} + + + {pts.map((p, i) => ( + + + {p.shortCommit} · {formatValue(p.value)} ({new Date(p.timestamp).toISOString().slice(0, 10)}) + + + ))} + {pts.map((p, i) => + i === 0 || i === pts.length - 1 || pts.length <= 6 ? ( + + {p.shortCommit} + + ) : null, + )} + +
+ ); +} diff --git a/web/src/data.ts b/web/src/data.ts new file mode 100644 index 0000000..6e89da3 --- /dev/null +++ b/web/src/data.ts @@ -0,0 +1,34 @@ +import type { Dashboard } from "./types"; + +declare global { + interface Window { + __CUBIT_DATA__?: Dashboard | null; + } +} + +// loadData reads the dashboard payload. The Go side injects it as a global for +// the standalone/served page; a dev server (`yarn dev`) falls back to fetching +// ./data.json. +export async function loadData(): Promise { + if (window.__CUBIT_DATA__) { + return window.__CUBIT_DATA__; + } + try { + const res = await fetch("./data.json"); + if (res.ok) { + return (await res.json()) as Dashboard; + } + } catch { + /* no dev data available */ + } + return null; +} + +// formatValue renders a nanosecond metric with an adaptive unit, matching the +// PR-comment formatter so the dashboard and comment read the same. +export function formatValue(ns: number): string { + if (ns >= 1e9) return `${(ns / 1e9).toFixed(2)} s`; + if (ns >= 1e6) return `${(ns / 1e6).toFixed(2)} ms`; + if (ns >= 1e3) return `${(ns / 1e3).toFixed(2)} µs`; + return `${ns.toFixed(1)} ns`; +} diff --git a/web/src/main.tsx b/web/src/main.tsx new file mode 100644 index 0000000..9891129 --- /dev/null +++ b/web/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import { App } from "./App"; +import "./styles.css"; + +createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/web/src/styles.css b/web/src/styles.css new file mode 100644 index 0000000..d3b17d1 --- /dev/null +++ b/web/src/styles.css @@ -0,0 +1,126 @@ +:root { + --bg: #ffffff; + --fg: #1a1a1a; + --muted: #6b7280; + --grid: #e5e7eb; + --line: #2563eb; + --band: rgba(37, 99, 235, 0.15); + --card: #f9fafb; + --border: #e5e7eb; + color-scheme: light dark; +} + +@media (prefers-color-scheme: dark) { + :root { + --bg: #0d1117; + --fg: #e6edf3; + --muted: #8b949e; + --grid: #21262d; + --line: #58a6ff; + --band: rgba(88, 166, 255, 0.18); + --card: #161b22; + --border: #30363d; + } +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: var(--bg); + color: var(--fg); + font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +} + +.wrap { + max-width: 1200px; + margin: 0 auto; + padding: 32px 24px 64px; +} + +h1 { + font-size: 20px; + margin: 0 0 4px; +} + +.muted { + color: var(--muted); + margin: 0 0 24px; +} + +code { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 0.92em; +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); + gap: 20px; +} + +.chart { + margin: 0; + padding: 12px 12px 4px; + background: var(--card); + border: 1px solid var(--border); + border-radius: 10px; +} + +.chart figcaption { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 4px; +} + +.chart-id { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 13px; +} + +.chart-last { + font-variant-numeric: tabular-nums; + color: var(--muted); +} + +.chart svg { + width: 100%; + height: auto; + overflow: visible; +} + +.grid line.grid, +.chart .grid { + stroke: var(--grid); + stroke-width: 1; +} + +.band { + fill: var(--band); + stroke: none; +} + +.line { + stroke: var(--line); + stroke-width: 2; +} + +.dot { + fill: var(--line); +} + +.ytick, +.xtick { + fill: var(--muted); + font-size: 10px; + font-variant-numeric: tabular-nums; +} + +.chart-empty { + color: var(--muted); + padding: 24px; + text-align: center; +} diff --git a/web/src/types.ts b/web/src/types.ts new file mode 100644 index 0000000..2ae9a32 --- /dev/null +++ b/web/src/types.ts @@ -0,0 +1,23 @@ +// The dashboard data shape, produced by the Go side (internal/dashboard) from +// the runs recorded on the cubit-state branch. One BenchSeries per benchmark +// id; points are ordered oldest-first along the main branch. +export interface Point { + commit: string; + shortCommit: string; + timestamp: string; + value: number; + lower: number; + upper: number; +} + +export interface BenchSeries { + id: string; + unit: string; + points: Point[]; +} + +export interface Dashboard { + generatedAt: string; + branch: string; + benchmarks: BenchSeries[]; +} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..3f6dd8c --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "include": ["src"] +} diff --git a/web/vite.config.ts b/web/vite.config.ts new file mode 100644 index 0000000..70742c2 --- /dev/null +++ b/web/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import { viteSingleFile } from "vite-plugin-singlefile"; + +// viteSingleFile inlines JS + CSS into a single index.html, so the dashboard +// works as a standalone file — served locally by `cubit serve` or downloaded as +// a CI artifact and opened over file:// (no external requests, no CORS). +export default defineConfig({ + plugins: [react(), viteSingleFile()], + build: { + outDir: "dist", + target: "es2020", + assetsInlineLimit: 100_000_000, + }, +}); diff --git a/web/yarn.lock b/web/yarn.lock new file mode 100644 index 0000000..4fb02f5 --- /dev/null +++ b/web/yarn.lock @@ -0,0 +1,1430 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 10 + cacheKey: 10c0 + +"@babel/code-frame@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/code-frame@npm:7.29.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.29.7" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/169fc2080169a40c1760155eaaaf739bcb882df0bec76a83adbda5493645bc17270a3434b8848c494b1933e96fe1d147370001e3cda09a39f43ae30f08ef2069 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/compat-data@npm:7.29.7" + checksum: 10c0/47913f05e08a45a1c9df38c02b4b49e391005085b489432647a1abe112e5d9c75e3be8ea5972b7f6da4ec5d1339922ceb9ea02b8a25d4ed1cb8636e5261f344e + languageName: node + linkType: hard + +"@babel/core@npm:^7.28.0": + version: 7.29.7 + resolution: "@babel/core@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-compilation-targets": "npm:^7.29.7" + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helpers": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/remapping": "npm:^2.3.5" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/112fb09c24de7a1de64d1de2c31fe65c4e6af4cb2fb6e6d99ea5373e6fc51e75b88581c0efae4c4c68f119a02a988c7106e95011a41530a2fb8ed793c7eaa07b + languageName: node + linkType: hard + +"@babel/generator@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/generator@npm:7.29.7" + dependencies: + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/9bf72b01b5bd0ea5b1288a0e37dbd360bff2f2b1ce73342c0d40fb3db2ec3dc004ada5ffa925c5e12939a416eed59e600d562b8ecd938ce0d27dfd0eb6c6c2b7 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-compilation-targets@npm:7.29.7" + dependencies: + "@babel/compat-data": "npm:^7.29.7" + "@babel/helper-validator-option": "npm:^7.29.7" + browserslist: "npm:^4.24.0" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10c0/4c15fd4c69a0a7047799a28a88460c19cede0a0ee8af994ea169114986f4af48b92c7393a4a3fee0456c11a656eece3448a6ed06354453d6c27cccf17195453b + languageName: node + linkType: hard + +"@babel/helper-globals@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-globals@npm:7.29.7" + checksum: 10c0/f38417c40b1129a1b2b519ca961b9040c8827d1444fd74068702286b91b77089431dc76b6b9d5c1496e5da2a4f3ad329c6946e688ba3fa0d1d0b3d2b4f34f36a + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-imports@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/6adf60d97356027413342a092f818d9678c4f5caff716a33e3284b5ae14e47a9e88059d421dde4ee4894691260039a12602c0e7becadc175602194b40dfa345d + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-transforms@npm:7.29.7" + dependencies: + "@babel/helper-module-imports": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/ee5a2172c24a42be696836f4b0d947489c9729d8adf5821885cf77d1ad5333e3c447368e9a71f67df1099570490553dccf9f888ef0a92a48aa63cb086bd8c7e1 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-plugin-utils@npm:7.29.7" + checksum: 10c0/380477a06133274a2759f9355929cb60a95e8b8fee624a1ae1fa349e1d1645b89daca456f72833f6d1062bffa12ee4271c5bf0cc5a61c0166cdc24c7591e2408 + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-string-parser@npm:7.29.7" + checksum: 10c0/194bc0f1716e396d5ffde56ad6119745fb9557662c98611590e5e454906783a4ccb21ce93056b8eb69a4909044834e45d96e50ac695bbe9e3221648fe033c06c + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-identifier@npm:7.29.7" + checksum: 10c0/4795354e7ae0dcafa72de1cd04ec51252dc1498517170beaf019e03effc5b7bf13c6b21a3949a77e07b8125be7f106ed1131350d8ebd4566ae874094a726d62b + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-option@npm:7.29.7" + checksum: 10c0/d2a06c6d0ac40ba4a2f219fc2cab249c7a94bacdb2686273b7f9598571c908809b48468ff588915a346e6cc7296f60b581023d1d498b747fed06f779d335c2cc + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helpers@npm:7.29.7" + dependencies: + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/218e8d10953647c9f44775f5a022b227a182674853b5ea8631889deb7e1a3e4bc870388aaecf59bb8bd92a87f9a96220ed3f70a35bffec6bcf9169ecb67891ac + languageName: node + linkType: hard + +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/parser@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/65133038f80b54a714d6027cb77cee3f9a6b5c4c6842ce674301e13947cbcbfa8055e63acaf1b84c085d34226a14425b2c2b97b829e0e226d2e8f1299942a51d + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-self@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-jsx-self@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/288995f0fd0d61ab740a315fb56c8255eb87dd4a4ac2ac7d0fdd4ce173c3878200141e80da2db0e598c7b2a71e74e604afdbb4c8e14ae6e0527ce0b6294c03da + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-source@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-jsx-source@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/a121899631e6d99b9e1b276acf736dbb77948a31f8eeeae67b89c8a4ab0f05e51ba64544baa06c286a2b9944f227244e15aac464e2313d286d0511fe51e27975 + languageName: node + linkType: hard + +"@babel/template@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/template@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/8bb7f900dcab0e9e1c5ffbc33ca10e0d26b7b2e2ca804becb73ee771b9c4ed6e2908a4ae4a14c08560febb45d2b6b9a173955e42ad404d05f8b04840a14d9c58 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/traverse@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + debug: "npm:^4.3.1" + checksum: 10c0/e256a1fbdb956555b76f3c285b1e453f6bedec8b3afb61751d99d933efd11c7d79caf5ddf2493570058a9f7deaa1b48324380d7c1aa1443fd9508becbf56331a + languageName: node + linkType: hard + +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/types@npm:7.29.7" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/b6623994c69717fa27294f5fa46d59140338e2d86c6c1c13085c84ef7d53086ee357fbf4fe9abe3dd3da75734dc77c4c0df2f90fb29e667558bb3b3fb705e88f + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/aix-ppc64@npm:0.25.12" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm64@npm:0.25.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm@npm:0.25.12" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-x64@npm:0.25.12" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-arm64@npm:0.25.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-x64@npm:0.25.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-arm64@npm:0.25.12" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-x64@npm:0.25.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm64@npm:0.25.12" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm@npm:0.25.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ia32@npm:0.25.12" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-loong64@npm:0.25.12" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-mips64el@npm:0.25.12" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ppc64@npm:0.25.12" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-riscv64@npm:0.25.12" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-s390x@npm:0.25.12" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-x64@npm:0.25.12" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-arm64@npm:0.25.12" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-x64@npm:0.25.12" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-arm64@npm:0.25.12" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-x64@npm:0.25.12" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openharmony-arm64@npm:0.25.12" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/sunos-x64@npm:0.25.12" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-arm64@npm:0.25.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-ia32@npm:0.25.12" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-x64@npm:0.25.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: "npm:^7.0.4" + checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2 + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.13 + resolution: "@jridgewell/gen-mapping@npm:0.3.13" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/9a7d65fb13bd9aec1fbab74cda08496839b7e2ceb31f5ab922b323e94d7c481ce0fc4fd7e12e2610915ed8af51178bdc61e168e92a8c8b8303b030b03489b13b + languageName: node + linkType: hard + +"@jridgewell/remapping@npm:^2.3.5": + version: 2.3.5 + resolution: "@jridgewell/remapping@npm:2.3.5" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/3de494219ffeb2c5c38711d0d7bb128097edf91893090a2dbc8ee0b55d092bb7347b1fd0f478486c5eab010e855c73927b1666f2107516d472d24a73017d1194 + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": + version: 1.5.5 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" + checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.28": + version: 0.3.31 + resolution: "@jridgewell/trace-mapping@npm:0.3.31" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/4b30ec8cd56c5fd9a661f088230af01e0c1a3888d11ffb6b47639700f71225be21d1f7e168048d6d4f9449207b978a235c07c8f15c07705685d16dc06280e9d9 + languageName: node + linkType: hard + +"@rolldown/pluginutils@npm:1.0.0-beta.27": + version: 1.0.0-beta.27 + resolution: "@rolldown/pluginutils@npm:1.0.0-beta.27" + checksum: 10c0/9658f235b345201d4f6bfb1f32da9754ca164f892d1cb68154fe5f53c1df42bd675ecd409836dff46884a7847d6c00bdc38af870f7c81e05bba5c2645eb4ab9c + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.62.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-android-arm64@npm:4.62.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-darwin-arm64@npm:4.62.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-darwin-x64@npm:4.62.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.62.2" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-freebsd-x64@npm:4.62.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.62.2" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.62.2" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.62.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.62.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.62.2" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.62.2" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.62.2" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.62.2" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.62.2" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.62.2" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.62.2" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.62.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.62.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openbsd-x64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-openbsd-x64@npm:4.62.2" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.62.2" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.62.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.62.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.62.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.62.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@types/babel__core@npm:^7.20.5": + version: 7.20.5 + resolution: "@types/babel__core@npm:7.20.5" + dependencies: + "@babel/parser": "npm:^7.20.7" + "@babel/types": "npm:^7.20.7" + "@types/babel__generator": "npm:*" + "@types/babel__template": "npm:*" + "@types/babel__traverse": "npm:*" + checksum: 10c0/bdee3bb69951e833a4b811b8ee9356b69a61ed5b7a23e1a081ec9249769117fa83aaaf023bb06562a038eb5845155ff663e2d5c75dd95c1d5ccc91db012868ff + languageName: node + linkType: hard + +"@types/babel__generator@npm:*": + version: 7.27.0 + resolution: "@types/babel__generator@npm:7.27.0" + dependencies: + "@babel/types": "npm:^7.0.0" + checksum: 10c0/9f9e959a8792df208a9d048092fda7e1858bddc95c6314857a8211a99e20e6830bdeb572e3587ae8be5429e37f2a96fcf222a9f53ad232f5537764c9e13a2bbd + languageName: node + linkType: hard + +"@types/babel__template@npm:*": + version: 7.4.4 + resolution: "@types/babel__template@npm:7.4.4" + dependencies: + "@babel/parser": "npm:^7.1.0" + "@babel/types": "npm:^7.0.0" + checksum: 10c0/cc84f6c6ab1eab1427e90dd2b76ccee65ce940b778a9a67be2c8c39e1994e6f5bbc8efa309f6cea8dc6754994524cd4d2896558df76d92e7a1f46ecffee7112b + languageName: node + linkType: hard + +"@types/babel__traverse@npm:*": + version: 7.28.0 + resolution: "@types/babel__traverse@npm:7.28.0" + dependencies: + "@babel/types": "npm:^7.28.2" + checksum: 10c0/b52d7d4e8fc6a9018fe7361c4062c1c190f5778cf2466817cb9ed19d69fbbb54f9a85ffedeb748ed8062d2cf7d4cc088ee739848f47c57740de1c48cbf0d0994 + languageName: node + linkType: hard + +"@types/estree@npm:1.0.9": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 + languageName: node + linkType: hard + +"@vitejs/plugin-react@npm:^4.3.4": + version: 4.7.0 + resolution: "@vitejs/plugin-react@npm:4.7.0" + dependencies: + "@babel/core": "npm:^7.28.0" + "@babel/plugin-transform-react-jsx-self": "npm:^7.27.1" + "@babel/plugin-transform-react-jsx-source": "npm:^7.27.1" + "@rolldown/pluginutils": "npm:1.0.0-beta.27" + "@types/babel__core": "npm:^7.20.5" + react-refresh: "npm:^0.17.0" + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + checksum: 10c0/692f23960972879485d647713663ec299c478222c96567d60285acf7c7dc5c178e71abfe9d2eefddef1eeb01514dacbc2ed68aad84628debf9c7116134734253 + languageName: node + linkType: hard + +"abbrev@npm:^5.0.0": + version: 5.0.0 + resolution: "abbrev@npm:5.0.0" + checksum: 10c0/8e88f5c798ea4562d28c5a3e9ad69e3879890bc5d695d8f2dffb8609be4c890aacc8f80ef4553fdd2c6a62d70c2ce8bc57b38074e383beb7487bdafa9ed42ea5 + languageName: node + linkType: hard + +"baseline-browser-mapping@npm:^2.10.42": + version: 2.10.43 + resolution: "baseline-browser-mapping@npm:2.10.43" + bin: + baseline-browser-mapping: dist/cli.cjs + checksum: 10c0/08a9e585fe0b1672a0a41026579d41debbaff2c1ceffc9efe510b47713b9d78296846d7a86f0d74d8f64af0088fdebd715f5ea6cb88d6fe1d57f8fafa686b3b5 + languageName: node + linkType: hard + +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 + languageName: node + linkType: hard + +"browserslist@npm:^4.24.0": + version: 4.28.6 + resolution: "browserslist@npm:4.28.6" + dependencies: + baseline-browser-mapping: "npm:^2.10.42" + caniuse-lite: "npm:^1.0.30001803" + electron-to-chromium: "npm:^1.5.389" + node-releases: "npm:^2.0.51" + update-browserslist-db: "npm:^1.2.3" + bin: + browserslist: cli.js + checksum: 10c0/259e3c2e0e59daf1fab0889303d397b35505c1c910e503a329990c4f6c0e589c0959fcd2627487311e4a94d7b9e2a9b1fa02875592f5c98b8e9e03e0ccd1e3ea + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001803": + version: 1.0.30001806 + resolution: "caniuse-lite@npm:1.0.30001806" + checksum: 10c0/9442c8afff0968e9b2ce0104a7340c1ce4a5c09f469ccb9eef10d25712ea0afe542486e5318c697c70dd502f988cfc04eaa1c9438c92ac3bcf4d708a2a17bee1 + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10 + languageName: node + linkType: hard + +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b + languageName: node + linkType: hard + +"cubit-dashboard@workspace:.": + version: 0.0.0-use.local + resolution: "cubit-dashboard@workspace:." + dependencies: + "@vitejs/plugin-react": "npm:^4.3.4" + react: "npm:^19.0.0" + react-dom: "npm:^19.0.0" + typescript: "npm:^5.7.0" + vite: "npm:^6.0.0" + vite-plugin-singlefile: "npm:^2.1.0" + languageName: unknown + linkType: soft + +"debug@npm:^4.1.0, debug@npm:^4.3.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.5.389": + version: 1.5.393 + resolution: "electron-to-chromium@npm:1.5.393" + checksum: 10c0/d8ef05aa8511bafb6b60506683dddbb968829f966af2aec582f06c208dc3598d7c5fc68b69ea2303befde1177637c19e53065a0fb7f47ca09c4928436c5768e7 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"esbuild@npm:^0.25.0": + version: 0.25.12 + resolution: "esbuild@npm:0.25.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.12" + "@esbuild/android-arm": "npm:0.25.12" + "@esbuild/android-arm64": "npm:0.25.12" + "@esbuild/android-x64": "npm:0.25.12" + "@esbuild/darwin-arm64": "npm:0.25.12" + "@esbuild/darwin-x64": "npm:0.25.12" + "@esbuild/freebsd-arm64": "npm:0.25.12" + "@esbuild/freebsd-x64": "npm:0.25.12" + "@esbuild/linux-arm": "npm:0.25.12" + "@esbuild/linux-arm64": "npm:0.25.12" + "@esbuild/linux-ia32": "npm:0.25.12" + "@esbuild/linux-loong64": "npm:0.25.12" + "@esbuild/linux-mips64el": "npm:0.25.12" + "@esbuild/linux-ppc64": "npm:0.25.12" + "@esbuild/linux-riscv64": "npm:0.25.12" + "@esbuild/linux-s390x": "npm:0.25.12" + "@esbuild/linux-x64": "npm:0.25.12" + "@esbuild/netbsd-arm64": "npm:0.25.12" + "@esbuild/netbsd-x64": "npm:0.25.12" + "@esbuild/openbsd-arm64": "npm:0.25.12" + "@esbuild/openbsd-x64": "npm:0.25.12" + "@esbuild/openharmony-arm64": "npm:0.25.12" + "@esbuild/sunos-x64": "npm:0.25.12" + "@esbuild/win32-arm64": "npm:0.25.12" + "@esbuild/win32-ia32": "npm:0.25.12" + "@esbuild/win32-x64": "npm:0.25.12" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b + languageName: node + linkType: hard + +"escalade@npm:^3.2.0": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.3 + resolution: "exponential-backoff@npm:3.1.3" + checksum: 10c0/77e3ae682b7b1f4972f563c6dbcd2b0d54ac679e62d5d32f3e5085feba20483cf28bd505543f520e287a56d4d55a28d7874299941faf637e779a1aa5994d1267 + languageName: node + linkType: hard + +"fdir@npm:^6.4.4, fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: 10c0/782aba6cba65b1bb5af3b095d96249d20edbe8df32dbf4696fd49be2583faf676173bf4809386588828e4dd76a3354fcbeb577bab1c833ccd9fc4577f26103f8 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 + languageName: node + linkType: hard + +"isexe@npm:^4.0.0": + version: 4.0.0 + resolution: "isexe@npm:4.0.0" + checksum: 10c0/5884815115bceac452877659a9c7726382531592f43dc29e5d48b7c4100661aed54018cb90bd36cb2eaeba521092570769167acbb95c18d39afdccbcca06c5ce + languageName: node + linkType: hard + +"js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed + languageName: node + linkType: hard + +"jsesc@npm:^3.0.2": + version: 3.1.0 + resolution: "jsesc@npm:3.1.0" + bin: + jsesc: bin/jsesc + checksum: 10c0/531779df5ec94f47e462da26b4cbf05eb88a83d9f08aac2ba04206508fc598527a153d08bd462bae82fc78b3eaa1a908e1a4a79f886e9238641c4cdefaf118b1 + languageName: node + linkType: hard + +"json5@npm:^2.2.3": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 10c0/5a04eed94810fa55c5ea138b2f7a5c12b97c3750bc63d11e511dcecbfef758003861522a070c2272764ee0f4e3e323862f386945aeb5b85b87ee43f084ba586c + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: "npm:^3.0.2" + checksum: 10c0/89b2ef2ef45f543011e38737b8a8622a2f8998cddf0e5437174ef8f1f70a8b9d14a918ab3e232cb3ba343b7abddffa667f0b59075b2b80e6b4d63c3de6127482 + languageName: node + linkType: hard + +"micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 + languageName: node + linkType: hard + +"minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb + languageName: node + linkType: hard + +"minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec + languageName: node + linkType: hard + +"ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.16": + version: 3.3.16 + resolution: "nanoid@npm:3.3.16" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/bbf2dcffe22d2b62d16de2711752070b539c0f644c7916f823ad6521986b2078cbe524f2d6240f58c46e9141ea0c7b87a029e100c0f7f175228cacaf30e41bba + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 13.0.1 + resolution: "node-gyp@npm:13.0.1" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + graceful-fs: "npm:^4.2.6" + nopt: "npm:^10.0.0" + proc-log: "npm:^7.0.0" + semver: "npm:^7.3.5" + tar: "npm:^7.5.4" + tinyglobby: "npm:^0.2.12" + undici: "npm:^8.4.1" + which: "npm:^7.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/424077bc9e9bbe953a8e86db473ba818cbc6a121714008c977fd589e21e5f0c811fbf22faac730dc7182450b5e52df301811d01ae3373898658d999b7710f4e6 + languageName: node + linkType: hard + +"node-releases@npm:^2.0.51": + version: 2.0.51 + resolution: "node-releases@npm:2.0.51" + checksum: 10c0/6cf3fe1f9eabe02ce6de67e9db77b73edc9f5625003791e1f8f239f8e2a851450a5aeb1eff2cc43cfb26312e19b26bfe07626bf428479e6a76f56553fc6148da + languageName: node + linkType: hard + +"nopt@npm:^10.0.0": + version: 10.0.1 + resolution: "nopt@npm:10.0.1" + dependencies: + abbrev: "npm:^5.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/980d89257f9587f3e1f77877ddbf905d6aa3b738ec33e49a4fa1a059a0dd82eb28063982b150654a7ae9de386f2ead60e56172db7d37cf56de545f7392a2a26a + languageName: node + linkType: hard + +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"picomatch@npm:^2.3.1": + version: 2.3.2 + resolution: "picomatch@npm:2.3.2" + checksum: 10c0/a554d1709e59be97d1acb9eaedbbc700a5c03dbd4579807baed95100b00420bc729335440ef15004ae2378984e2487a7c1cebd743cfdb72b6fa9ab69223c0d61 + languageName: node + linkType: hard + +"picomatch@npm:^4.0.2, picomatch@npm:^4.0.4": + version: 4.0.5 + resolution: "picomatch@npm:4.0.5" + checksum: 10c0/947bc6b6e1ff1e6c5aaf95b107a0839d12802f4f7b867663f67d47accba939ca1cb582cf99dfc30438efa1c4648ac5990967e783e8929c36b03e8440704ef1bd + languageName: node + linkType: hard + +"postcss@npm:^8.5.3": + version: 8.5.20 + resolution: "postcss@npm:8.5.20" + dependencies: + nanoid: "npm:^3.3.16" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/9c7a0e32c77ac54c5613f974e6ccc97d4a70fdb3bf242617f3fb4d652e5c62a37c86c589e6e7a2d1e7120f80bc169cdf673482b88bd75020d2a44899fa569c13 + languageName: node + linkType: hard + +"proc-log@npm:^7.0.0": + version: 7.0.0 + resolution: "proc-log@npm:7.0.0" + checksum: 10c0/b89c2d862604f35fec795477b0c7e376feab3ba0d4f4d291c4e959567442697cf451ac557d0623c1cc38af45a78128b983410f397a10c5d3a67f76c33de4754b + languageName: node + linkType: hard + +"react-dom@npm:^19.0.0": + version: 19.2.7 + resolution: "react-dom@npm:19.2.7" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.7 + checksum: 10c0/970ff600f6e80d47d39e2f226f12f226173b3cba3382efc97c5f0cd663de9af38c7a4c11c213fb936094faeac83060d660247accaa96b752180d5b951b9cfecb + languageName: node + linkType: hard + +"react-refresh@npm:^0.17.0": + version: 0.17.0 + resolution: "react-refresh@npm:0.17.0" + checksum: 10c0/002cba940384c9930008c0bce26cac97a9d5682bc623112c2268ba0c155127d9c178a9a5cc2212d560088d60dfd503edd808669a25f9b377f316a32361d0b23c + languageName: node + linkType: hard + +"react@npm:^19.0.0": + version: 19.2.7 + resolution: "react@npm:19.2.7" + checksum: 10c0/0bd0e2f1bbd4ba97561c6597bf8a5fec05e6476fe61e165c1065598d16668efc6715205599c94d3ddd49d36cb0f21cbf1b9bcc18ee840b805ce222c3e8d558ac + languageName: node + linkType: hard + +"rollup@npm:^4.34.9": + version: 4.62.2 + resolution: "rollup@npm:4.62.2" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.62.2" + "@rollup/rollup-android-arm64": "npm:4.62.2" + "@rollup/rollup-darwin-arm64": "npm:4.62.2" + "@rollup/rollup-darwin-x64": "npm:4.62.2" + "@rollup/rollup-freebsd-arm64": "npm:4.62.2" + "@rollup/rollup-freebsd-x64": "npm:4.62.2" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.62.2" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.62.2" + "@rollup/rollup-linux-arm64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-arm64-musl": "npm:4.62.2" + "@rollup/rollup-linux-loong64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-loong64-musl": "npm:4.62.2" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-ppc64-musl": "npm:4.62.2" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-riscv64-musl": "npm:4.62.2" + "@rollup/rollup-linux-s390x-gnu": "npm:4.62.2" + "@rollup/rollup-linux-x64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-x64-musl": "npm:4.62.2" + "@rollup/rollup-openbsd-x64": "npm:4.62.2" + "@rollup/rollup-openharmony-arm64": "npm:4.62.2" + "@rollup/rollup-win32-arm64-msvc": "npm:4.62.2" + "@rollup/rollup-win32-ia32-msvc": "npm:4.62.2" + "@rollup/rollup-win32-x64-gnu": "npm:4.62.2" + "@rollup/rollup-win32-x64-msvc": "npm:4.62.2" + "@types/estree": "npm:1.0.9" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loong64-gnu": + optional: true + "@rollup/rollup-linux-loong64-musl": + optional: true + "@rollup/rollup-linux-ppc64-gnu": + optional: true + "@rollup/rollup-linux-ppc64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-openbsd-x64": + optional: true + "@rollup/rollup-openharmony-arm64": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/83ff5f4a1fea3fa05db2ef56beceee8c33d4a72b818e19c562f1e85c41076fe5b12aadc44048bb73e60b83336df82154b017fa6bf0186f3141643e6f215fbdcb + languageName: node + linkType: hard + +"scheduler@npm:^0.27.0": + version: 0.27.0 + resolution: "scheduler@npm:0.27.0" + checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 + languageName: node + linkType: hard + +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d + languageName: node + linkType: hard + +"semver@npm:^7.3.5": + version: 7.8.5 + resolution: "semver@npm:7.8.5" + bin: + semver: bin/semver.js + checksum: 10c0/b1f3127a5be8125a94f37188b361c212466c292c6910adce3ec106cff5dc211ccaedc4739c11bb70fda59d6fc1f040a9bca289f4e093451521a2372e5231fe0c + languageName: node + linkType: hard + +"source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + +"tar@npm:^7.5.4": + version: 7.5.20 + resolution: "tar@npm:7.5.20" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.1.0" + yallist: "npm:^5.0.0" + checksum: 10c0/4df4335c6d958b76adf1eaced55889dec3ca1c51f450658a074af80694bb0d9c154a8e93fdf4da617372d1575b121295379993961bbe4cd4b0867c0e5689846a + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 + languageName: node + linkType: hard + +"typescript@npm:^5.7.0": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^5.7.0#optional!builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 + languageName: node + linkType: hard + +"undici@npm:^8.4.1": + version: 8.7.0 + resolution: "undici@npm:8.7.0" + checksum: 10c0/b7e5ecb4de82fa4f905011a77544fe7ba4da06f27167ff99313a7ae2869f3cb233d676dcd085533bc69f36989bc40871f5ae6ea6e4c9b91db92616b9e91b579d + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.2.3": + version: 1.2.3 + resolution: "update-browserslist-db@npm:1.2.3" + dependencies: + escalade: "npm:^3.2.0" + picocolors: "npm:^1.1.1" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10c0/13a00355ea822388f68af57410ce3255941d5fb9b7c49342c4709a07c9f230bbef7f7499ae0ca7e0de532e79a82cc0c4edbd125f1a323a1845bf914efddf8bec + languageName: node + linkType: hard + +"vite-plugin-singlefile@npm:^2.1.0": + version: 2.3.3 + resolution: "vite-plugin-singlefile@npm:2.3.3" + dependencies: + micromatch: "npm:^4.0.8" + peerDependencies: + rollup: ^4.59.0 + vite: ^5.4.21 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10c0/9c2097e7843684706af1c4d18c8f77193c3089494e6fc15c508a7d079f8d85aabb21b8b25bf2af2088fec50a18270aa928a9ce3013adb270673bc8896d51dd80 + languageName: node + linkType: hard + +"vite@npm:^6.0.0": + version: 6.4.3 + resolution: "vite@npm:6.4.3" + dependencies: + esbuild: "npm:^0.25.0" + fdir: "npm:^6.4.4" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.2" + postcss: "npm:^8.5.3" + rollup: "npm:^4.34.9" + tinyglobby: "npm:^0.2.13" + peerDependencies: + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: ">=1.21.0" + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/23ce22e50d5c7a87321f04b155c5bc3cf915e34e6c40918975741ed5e8b0c257039a643f1bc1cd829ee814ad92a138704e82084b7ebe40b399a62d8effea630c + languageName: node + linkType: hard + +"which@npm:^7.0.0": + version: 7.0.0 + resolution: "which@npm:7.0.0" + dependencies: + isexe: "npm:^4.0.0" + bin: + node-which: bin/which.js + checksum: 10c0/ca0b54f198f78bbc4b7c02e34bda8d335cb352e0adb4cbca1c37b1a957af3a879a82c4c27ca6525bc942f548d8b64f816ef6528360af9f3de55ffb9b979b620d + languageName: node + linkType: hard + +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 10c0/c66a5c46bc89af1625476f7f0f2ec3653c1a1791d2f9407cfb4c2ba812a1e1c9941416d71ba9719876530e3340a99925f697142989371b72d93b9ee628afd8c1 + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 + languageName: node + linkType: hard From 4af3045a1fe1596929eca194b5fab1c58d99189f Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sun, 19 Jul 2026 14:06:50 +0100 Subject: [PATCH 5/7] feat(brand): add cubit logo to PR comment, dashboard, and README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - assets/cubit-logo.png: the cubit rod icon (background removed, transparent). - report: PR comment header uses the logo via an absolute raw URL pinned to main (resolves in the consuming repo's PR, like bulwark), replacing the emoji. - dashboard: logo in the header, imported through Vite so vite-plugin-singlefile inlines it as a data URI — the page stays fully self-contained. - README: logo in the heading. Claude-Session: https://claude.ai/code/session_016pTq6aVBJvTtXmwTgKw2Yh --- README.md | 2 ++ assets/cubit-logo.png | Bin 0 -> 73876 bytes internal/dashboard/assets/index.html | 26 +++++++++++++------------- internal/report/report.go | 6 +++++- web/src/App.tsx | 5 ++++- web/src/assets/cubit-logo.png | Bin 0 -> 73876 bytes web/src/styles.css | 8 ++++++++ web/src/vite-env.d.ts | 1 + 8 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 assets/cubit-logo.png create mode 100644 web/src/assets/cubit-logo.png create mode 100644 web/src/vite-env.d.ts diff --git a/README.md b/README.md index 60691b5..d976e4d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +cubit + # cubit Continuous benchmarking and performance-regression tracking. diff --git a/assets/cubit-logo.png b/assets/cubit-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a72540153977df8759eea401e3dd9ccbfd141e GIT binary patch literal 73876 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 literal 0 HcmV?d00001 diff --git a/internal/dashboard/assets/index.html b/internal/dashboard/assets/index.html index bea4df3..d50b178 100644 --- a/internal/dashboard/assets/index.html +++ b/internal/dashboard/assets/index.html @@ -4,7 +4,7 @@ cubit — performance - - +`+a.stack}}var Kn=Object.prototype.hasOwnProperty,Zn=C.unstable_scheduleCallback,qn=C.unstable_cancelCallback,Mv=C.unstable_shouldYield,Ov=C.unstable_requestPaint,kl=C.unstable_now,Nv=C.unstable_getCurrentPriorityLevel,Bi=C.unstable_ImmediatePriority,Di=C.unstable_UserBlockingPriority,Qe=C.unstable_NormalPriority,Yv=C.unstable_LowPriority,Ei=C.unstable_IdlePriority,Gv=C.log,jv=C.unstable_setDisableYieldValue,ba=null,Wl=null;function kt(l){if(typeof Gv=="function"&&jv(l),Wl&&typeof Wl.setStrictMode=="function")try{Wl.setStrictMode(ba,l)}catch{}}var _l=Math.clz32?Math.clz32:Uv,wv=Math.log,rv=Math.LN2;function Uv(l){return l>>>=0,l===0?32:31-(wv(l)/rv|0)|0}var Ce=256,He=262144,Te=4194304;function Cu(l){var t=l&42;if(t!==0)return t;switch(l&-l){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return l&261888;case 262144:case 524288:case 1048576:case 2097152:return l&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return l&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return l}}function Se(l,t,u){var a=l.pendingLanes;if(a===0)return 0;var e=0,n=l.suspendedLanes,f=l.pingedLanes;l=l.warmLanes;var c=a&134217727;return c!==0?(a=c&~n,a!==0?e=Cu(a):(f&=c,f!==0?e=Cu(f):u||(u=c&~l,u!==0&&(e=Cu(u))))):(c=a&~n,c!==0?e=Cu(c):f!==0?e=Cu(f):u||(u=a&~l,u!==0&&(e=Cu(u)))),e===0?0:t!==0&&t!==e&&(t&n)===0&&(n=e&-e,u=t&-t,n>=u||n===32&&(u&4194048)!==0)?t:e}function Ma(l,t){return(l.pendingLanes&~(l.suspendedLanes&~l.pingedLanes)&t)===0}function Rv(l,t){switch(l){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function zi(){var l=Te;return Te<<=1,(Te&62914560)===0&&(Te=4194304),l}function Vn(l){for(var t=[],u=0;31>u;u++)t.push(l);return t}function Oa(l,t){l.pendingLanes|=t,t!==268435456&&(l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0)}function xv(l,t,u,a,e,n){var f=l.pendingLanes;l.pendingLanes=u,l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0,l.expiredLanes&=u,l.entangledLanes&=u,l.errorRecoveryDisabledLanes&=u,l.shellSuspendCounter=0;var c=l.entanglements,i=l.expirationTimes,h=l.hiddenUpdates;for(u=f&~u;0"u")return null;try{return l.activeElement||l.body}catch{return l.body}}var Zv=/[\n"\\]/g;function it(l){return l.replace(Zv,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function _n(l,t,u,a,e,n,f,c){l.name="",f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"?l.type=f:l.removeAttribute("type"),t!=null?f==="number"?(t===0&&l.value===""||l.value!=t)&&(l.value=""+ct(t)):l.value!==""+ct(t)&&(l.value=""+ct(t)):f!=="submit"&&f!=="reset"||l.removeAttribute("value"),t!=null?$n(l,f,ct(t)):u!=null?$n(l,f,ct(u)):a!=null&&l.removeAttribute("value"),e==null&&n!=null&&(l.defaultChecked=!!n),e!=null&&(l.checked=e&&typeof e!="function"&&typeof e!="symbol"),c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"?l.name=""+ct(c):l.removeAttribute("name")}function wi(l,t,u,a,e,n,f,c){if(n!=null&&typeof n!="function"&&typeof n!="symbol"&&typeof n!="boolean"&&(l.type=n),t!=null||u!=null){if(!(n!=="submit"&&n!=="reset"||t!=null)){Wn(l);return}u=u!=null?""+ct(u):"",t=t!=null?""+ct(t):u,c||t===l.value||(l.value=t),l.defaultValue=t}a=a??e,a=typeof a!="function"&&typeof a!="symbol"&&!!a,l.checked=c?l.checked:!!a,l.defaultChecked=!!a,f!=null&&typeof f!="function"&&typeof f!="symbol"&&typeof f!="boolean"&&(l.name=f),Wn(l)}function $n(l,t,u){t==="number"&&Oe(l.ownerDocument)===l||l.defaultValue===""+u||(l.defaultValue=""+u)}function Fu(l,t,u,a){if(l=l.options,t){t={};for(var e=0;e"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),ef=!1;if(jt)try{var ja={};Object.defineProperty(ja,"passive",{get:function(){ef=!0}}),window.addEventListener("test",ja,ja),window.removeEventListener("test",ja,ja)}catch{ef=!1}var _t=null,nf=null,Ye=null;function Li(){if(Ye)return Ye;var l,t=nf,u=t.length,a,e="value"in _t?_t.value:_t.textContent,n=e.length;for(l=0;l=Ua),Fi=" ",Ii=!1;function Pi(l,t){switch(l){case"keyup":return Bd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ki(l){return l=l.detail,typeof l=="object"&&"data"in l?l.data:null}var Wu=!1;function Ed(l,t){switch(l){case"compositionend":return ki(t);case"keypress":return t.which!==32?null:(Ii=!0,Fi);case"textInput":return l=t.data,l===Fi&&Ii?null:l;default:return null}}function zd(l,t){if(Wu)return l==="compositionend"||!vf&&Pi(l,t)?(l=Li(),Ye=nf=_t=null,Wu=!1,l):null;switch(l){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:u,offset:t-l};l=a}l:{for(;u;){if(u.nextSibling){u=u.nextSibling;break l}u=u.parentNode}u=void 0}u=eA(u)}}function fA(l,t){return l&&t?l===t?!0:l&&l.nodeType===3?!1:t&&t.nodeType===3?fA(l,t.parentNode):"contains"in l?l.contains(t):l.compareDocumentPosition?!!(l.compareDocumentPosition(t)&16):!1:!1}function cA(l){l=l!=null&&l.ownerDocument!=null&&l.ownerDocument.defaultView!=null?l.ownerDocument.defaultView:window;for(var t=Oe(l.document);t instanceof l.HTMLIFrameElement;){try{var u=typeof t.contentWindow.location.href=="string"}catch{u=!1}if(u)l=t.contentWindow;else break;t=Oe(l.document)}return t}function sf(l){var t=l&&l.nodeName&&l.nodeName.toLowerCase();return t&&(t==="input"&&(l.type==="text"||l.type==="search"||l.type==="tel"||l.type==="url"||l.type==="password")||t==="textarea"||l.contentEditable==="true")}var Od=jt&&"documentMode"in document&&11>=document.documentMode,_u=null,yf=null,pa=null,mf=!1;function iA(l,t,u){var a=u.window===u?u.document:u.nodeType===9?u:u.ownerDocument;mf||_u==null||_u!==Oe(a)||(a=_u,"selectionStart"in a&&sf(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),pa&&Xa(pa,a)||(pa=a,a=Tn(yf,"onSelect"),0>=f,e-=f,Tt=1<<32-_l(t)+e|u<x?(q=M,M=null):q=M.sibling;var $=s(v,M,d[x],D);if($===null){M===null&&(M=q);break}l&&M&&$.alternate===null&&t(v,M),A=n($,A,x),_===null?N=$:_.sibling=$,_=$,M=q}if(x===d.length)return u(v,M),I&&rt(v,x),N;if(M===null){for(;xx?(q=M,M=null):q=M.sibling;var Eu=s(v,M,$.value,D);if(Eu===null){M===null&&(M=q);break}l&&M&&Eu.alternate===null&&t(v,M),A=n(Eu,A,x),_===null?N=Eu:_.sibling=Eu,_=Eu,M=q}if($.done)return u(v,M),I&&rt(v,x),N;if(M===null){for(;!$.done;x++,$=d.next())$=E(v,$.value,D),$!==null&&(A=n($,A,x),_===null?N=$:_.sibling=$,_=$);return I&&rt(v,x),N}for(M=a(M);!$.done;x++,$=d.next())$=m(M,v,x,$.value,D),$!==null&&(l&&$.alternate!==null&&M.delete($.key===null?x:$.key),A=n($,A,x),_===null?N=$:_.sibling=$,_=$);return l&&M.forEach(function(Ph){return t(v,Ph)}),I&&rt(v,x),N}function cl(v,A,d,D){if(typeof d=="object"&&d!==null&&d.type===k&&d.key===null&&(d=d.props.children),typeof d=="object"&&d!==null){switch(d.$$typeof){case Ul:l:{for(var N=d.key;A!==null;){if(A.key===N){if(N=d.type,N===k){if(A.tag===7){u(v,A.sibling),D=e(A,d.props.children),D.return=v,v=D;break l}}else if(A.elementType===N||typeof N=="object"&&N!==null&&N.$$typeof===pl&&wu(N)===A.type){u(v,A.sibling),D=e(A,d.props),Va(D,d),D.return=v,v=D;break l}u(v,A);break}else t(v,A);A=A.sibling}d.type===k?(D=Ou(d.props.children,v.mode,D,d.key),D.return=v,v=D):(D=Le(d.type,d.key,d.props,null,v.mode,D),Va(D,d),D.return=v,v=D)}return f(v);case p:l:{for(N=d.key;A!==null;){if(A.key===N)if(A.tag===4&&A.stateNode.containerInfo===d.containerInfo&&A.stateNode.implementation===d.implementation){u(v,A.sibling),D=e(A,d.children||[]),D.return=v,v=D;break l}else{u(v,A);break}else t(v,A);A=A.sibling}D=Cf(d,v.mode,D),D.return=v,v=D}return f(v);case pl:return d=wu(d),cl(v,A,d,D)}if(Bt(d))return S(v,A,d,D);if(Ll(d)){if(N=Ll(d),typeof N!="function")throw Error(y(150));return d=N.call(d),Y(v,A,d,D)}if(typeof d.then=="function")return cl(v,A,Ie(d),D);if(d.$$typeof===wl)return cl(v,A,Ze(v,d),D);Pe(v,d)}return typeof d=="string"&&d!==""||typeof d=="number"||typeof d=="bigint"?(d=""+d,A!==null&&A.tag===6?(u(v,A.sibling),D=e(A,d),D.return=v,v=D):(u(v,A),D=Qf(d,v.mode,D),D.return=v,v=D),f(v)):u(v,A)}return function(v,A,d,D){try{qa=0;var N=cl(v,A,d,D);return Aa=null,N}catch(M){if(M===ia||M===Ve)throw M;var _=lt(29,M,null,v.mode);return _.lanes=D,_.return=v,_}finally{}}}var Uu=YA(!0),GA=YA(!1),au=!1;function rf(l){l.updateQueue={baseState:l.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Uf(l,t){l=l.updateQueue,t.updateQueue===l&&(t.updateQueue={baseState:l.baseState,firstBaseUpdate:l.firstBaseUpdate,lastBaseUpdate:l.lastBaseUpdate,shared:l.shared,callbacks:null})}function eu(l){return{lane:l,tag:0,payload:null,callback:null,next:null}}function nu(l,t,u){var a=l.updateQueue;if(a===null)return null;if(a=a.shared,(ll&2)!==0){var e=a.pending;return e===null?t.next=t:(t.next=e.next,e.next=t),a.pending=t,t=pe(l),yA(l,null,u),t}return Xe(l,a,t,u),pe(l)}function Fa(l,t,u){if(t=t.updateQueue,t!==null&&(t=t.shared,(u&4194048)!==0)){var a=t.lanes;a&=l.pendingLanes,u|=a,t.lanes=u,Ci(l,u)}}function Rf(l,t){var u=l.updateQueue,a=l.alternate;if(a!==null&&(a=a.updateQueue,u===a)){var e=null,n=null;if(u=u.firstBaseUpdate,u!==null){do{var f={lane:u.lane,tag:u.tag,payload:u.payload,callback:null,next:null};n===null?e=n=f:n=n.next=f,u=u.next}while(u!==null);n===null?e=n=t:n=n.next=t}else e=n=t;u={baseState:a.baseState,firstBaseUpdate:e,lastBaseUpdate:n,shared:a.shared,callbacks:a.callbacks},l.updateQueue=u;return}l=u.lastBaseUpdate,l===null?u.firstBaseUpdate=t:l.next=t,u.lastBaseUpdate=t}var xf=!1;function Ia(){if(xf){var l=ca;if(l!==null)throw l}}function Pa(l,t,u,a){xf=!1;var e=l.updateQueue;au=!1;var n=e.firstBaseUpdate,f=e.lastBaseUpdate,c=e.shared.pending;if(c!==null){e.shared.pending=null;var i=c,h=i.next;i.next=null,f===null?n=h:f.next=h,f=i;var o=l.alternate;o!==null&&(o=o.updateQueue,c=o.lastBaseUpdate,c!==f&&(c===null?o.firstBaseUpdate=h:c.next=h,o.lastBaseUpdate=i))}if(n!==null){var E=e.baseState;f=0,o=h=i=null,c=n;do{var s=c.lane&-536870913,m=s!==c.lane;if(m?(Z&s)===s:(a&s)===s){s!==0&&s===fa&&(xf=!0),o!==null&&(o=o.next={lane:0,tag:c.tag,payload:c.payload,callback:null,next:null});l:{var S=l,Y=c;s=t;var cl=u;switch(Y.tag){case 1:if(S=Y.payload,typeof S=="function"){E=S.call(cl,E,s);break l}E=S;break l;case 3:S.flags=S.flags&-65537|128;case 0:if(S=Y.payload,s=typeof S=="function"?S.call(cl,E,s):S,s==null)break l;E=G({},E,s);break l;case 2:au=!0}}s=c.callback,s!==null&&(l.flags|=64,m&&(l.flags|=8192),m=e.callbacks,m===null?e.callbacks=[s]:m.push(s))}else m={lane:s,tag:c.tag,payload:c.payload,callback:c.callback,next:null},o===null?(h=o=m,i=E):o=o.next=m,f|=s;if(c=c.next,c===null){if(c=e.shared.pending,c===null)break;m=c,c=m.next,m.next=null,e.lastBaseUpdate=m,e.shared.pending=null}}while(!0);o===null&&(i=E),e.baseState=i,e.firstBaseUpdate=h,e.lastBaseUpdate=o,n===null&&(e.shared.lanes=0),gu|=f,l.lanes=f,l.memoizedState=E}}function jA(l,t){if(typeof l!="function")throw Error(y(191,l));l.call(t)}function wA(l,t){var u=l.callbacks;if(u!==null)for(l.callbacks=null,l=0;ln?n:8;var f=B.T,c={};B.T=c,ac(l,!1,t,u);try{var i=e(),h=B.S;if(h!==null&&h(c,i),i!==null&&typeof i=="object"&&typeof i.then=="function"){var o=xd(i,a);_a(l,t,o,nt(l))}else _a(l,t,a,nt(l))}catch(E){_a(l,t,{then:function(){},status:"rejected",reason:E},nt())}finally{H.p=n,f!==null&&c.types!==null&&(f.types=c.types),B.T=f}}function Zd(){}function tc(l,t,u,a){if(l.tag!==5)throw Error(y(476));var e=d0(l).queue;v0(l,e,t,j,u===null?Zd:function(){return h0(l),u(a)})}function d0(l){var t=l.memoizedState;if(t!==null)return t;t={memoizedState:j,baseState:j,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Xt,lastRenderedState:j},next:null};var u={};return t.next={memoizedState:u,baseState:u,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Xt,lastRenderedState:u},next:null},l.memoizedState=t,l=l.alternate,l!==null&&(l.memoizedState=t),t}function h0(l){var t=d0(l);t.next===null&&(t=l.alternate.memoizedState),_a(l,t.next.queue,{},nt())}function uc(){return Yl(ye)}function s0(){return El().memoizedState}function y0(){return El().memoizedState}function qd(l){for(var t=l.return;t!==null;){switch(t.tag){case 24:case 3:var u=nt();l=eu(u);var a=nu(t,l,u);a!==null&&(Il(a,t,u),Fa(a,t,u)),t={cache:Yf()},l.payload=t;return}t=t.return}}function Vd(l,t,u){var a=nt();u={lane:a,revertLane:0,gesture:null,action:u,hasEagerState:!1,eagerState:null,next:null},nn(l)?o0(t,u):(u=Ef(l,t,u,a),u!==null&&(Il(u,l,a),B0(u,t,a)))}function m0(l,t,u){var a=nt();_a(l,t,u,a)}function _a(l,t,u,a){var e={lane:a,revertLane:0,gesture:null,action:u,hasEagerState:!1,eagerState:null,next:null};if(nn(l))o0(t,e);else{var n=l.alternate;if(l.lanes===0&&(n===null||n.lanes===0)&&(n=t.lastRenderedReducer,n!==null))try{var f=t.lastRenderedState,c=n(f,u);if(e.hasEagerState=!0,e.eagerState=c,$l(c,f))return Xe(l,t,e,0),gl===null&&xe(),!1}catch{}finally{}if(u=Ef(l,t,e,a),u!==null)return Il(u,l,a),B0(u,t,a),!0}return!1}function ac(l,t,u,a){if(a={lane:2,revertLane:rc(),gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null},nn(l)){if(t)throw Error(y(479))}else t=Ef(l,u,a,2),t!==null&&Il(t,l,2)}function nn(l){var t=l.alternate;return l===U||t!==null&&t===U}function o0(l,t){va=_e=!0;var u=l.pending;u===null?t.next=t:(t.next=u.next,u.next=t),l.pending=t}function B0(l,t,u){if((u&4194048)!==0){var a=t.lanes;a&=l.pendingLanes,u|=a,t.lanes=u,Ci(l,u)}}var $a={readContext:Yl,use:tn,useCallback:ml,useContext:ml,useEffect:ml,useImperativeHandle:ml,useLayoutEffect:ml,useInsertionEffect:ml,useMemo:ml,useReducer:ml,useRef:ml,useState:ml,useDebugValue:ml,useDeferredValue:ml,useTransition:ml,useSyncExternalStore:ml,useId:ml,useHostTransitionStatus:ml,useFormState:ml,useActionState:ml,useOptimistic:ml,useMemoCache:ml,useCacheRefresh:ml};$a.useEffectEvent=ml;var D0={readContext:Yl,use:tn,useCallback:function(l,t){return xl().memoizedState=[l,t===void 0?null:t],l},useContext:Yl,useEffect:u0,useImperativeHandle:function(l,t,u){u=u!=null?u.concat([l]):null,an(4194308,4,f0.bind(null,t,l),u)},useLayoutEffect:function(l,t){return an(4194308,4,l,t)},useInsertionEffect:function(l,t){an(4,2,l,t)},useMemo:function(l,t){var u=xl();t=t===void 0?null:t;var a=l();if(Ru){kt(!0);try{l()}finally{kt(!1)}}return u.memoizedState=[a,t],a},useReducer:function(l,t,u){var a=xl();if(u!==void 0){var e=u(t);if(Ru){kt(!0);try{u(t)}finally{kt(!1)}}}else e=t;return a.memoizedState=a.baseState=e,l={pending:null,lanes:0,dispatch:null,lastRenderedReducer:l,lastRenderedState:e},a.queue=l,l=l.dispatch=Vd.bind(null,U,l),[a.memoizedState,l]},useRef:function(l){var t=xl();return l={current:l},t.memoizedState=l},useState:function(l){l=kf(l);var t=l.queue,u=m0.bind(null,U,t);return t.dispatch=u,[l.memoizedState,u]},useDebugValue:$f,useDeferredValue:function(l,t){var u=xl();return lc(u,l,t)},useTransition:function(){var l=kf(!1);return l=v0.bind(null,U,l.queue,!0,!1),xl().memoizedState=l,[!1,l]},useSyncExternalStore:function(l,t,u){var a=U,e=xl();if(I){if(u===void 0)throw Error(y(407));u=u()}else{if(u=t(),gl===null)throw Error(y(349));(Z&127)!==0||pA(a,t,u)}e.memoizedState=u;var n={value:u,getSnapshot:t};return e.queue=n,u0(JA.bind(null,a,n,l),[l]),a.flags|=2048,ha(9,{destroy:void 0},LA.bind(null,a,n,u,t),null),u},useId:function(){var l=xl(),t=gl.identifierPrefix;if(I){var u=St,a=Tt;u=(a&~(1<<32-_l(a)-1)).toString(32)+u,t="_"+t+"R_"+u,u=$e++,0<\/script>",n=n.removeChild(n.firstChild);break;case"select":n=typeof a.is=="string"?f.createElement("select",{is:a.is}):f.createElement("select"),a.multiple?n.multiple=!0:a.size&&(n.size=a.size);break;default:n=typeof a.is=="string"?f.createElement(e,{is:a.is}):f.createElement(e)}}n[Ol]=t,n[Jl]=a;l:for(f=t.child;f!==null;){if(f.tag===5||f.tag===6)n.appendChild(f.stateNode);else if(f.tag!==4&&f.tag!==27&&f.child!==null){f.child.return=f,f=f.child;continue}if(f===t)break l;for(;f.sibling===null;){if(f.return===null||f.return===t)break l;f=f.return}f.sibling.return=f.return,f=f.sibling}t.stateNode=n;l:switch(jl(n,e,a),e){case"button":case"input":case"select":case"textarea":a=!!a.autoFocus;break l;case"img":a=!0;break l;default:a=!1}a&&Lt(t)}}return sl(t),oc(t,t.type,l===null?null:l.memoizedProps,t.pendingProps,u),null;case 6:if(l&&t.stateNode!=null)l.memoizedProps!==a&&Lt(t);else{if(typeof a!="string"&&t.stateNode===null)throw Error(y(166));if(l=L.current,ea(t)){if(l=t.stateNode,u=t.memoizedProps,a=null,e=Nl,e!==null)switch(e.tag){case 27:case 5:a=e.memoizedProps}l[Ol]=t,l=!!(l.nodeValue===u||a!==null&&a.suppressHydrationWarning===!0||xg(l.nodeValue,u)),l||tu(t,!0)}else l=Sn(l).createTextNode(a),l[Ol]=t,t.stateNode=l}return sl(t),null;case 31:if(u=t.memoizedState,l===null||l.memoizedState!==null){if(a=ea(t),u!==null){if(l===null){if(!a)throw Error(y(318));if(l=t.memoizedState,l=l!==null?l.dehydrated:null,!l)throw Error(y(557));l[Ol]=t}else Nu(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;sl(t),l=!1}else u=bf(),l!==null&&l.memoizedState!==null&&(l.memoizedState.hydrationErrors=u),l=!0;if(!l)return t.flags&256?(ut(t),t):(ut(t),null);if((t.flags&128)!==0)throw Error(y(558))}return sl(t),null;case 13:if(a=t.memoizedState,l===null||l.memoizedState!==null&&l.memoizedState.dehydrated!==null){if(e=ea(t),a!==null&&a.dehydrated!==null){if(l===null){if(!e)throw Error(y(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(y(317));e[Ol]=t}else Nu(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;sl(t),e=!1}else e=bf(),l!==null&&l.memoizedState!==null&&(l.memoizedState.hydrationErrors=e),e=!0;if(!e)return t.flags&256?(ut(t),t):(ut(t),null)}return ut(t),(t.flags&128)!==0?(t.lanes=u,t):(u=a!==null,l=l!==null&&l.memoizedState!==null,u&&(a=t.child,e=null,a.alternate!==null&&a.alternate.memoizedState!==null&&a.alternate.memoizedState.cachePool!==null&&(e=a.alternate.memoizedState.cachePool.pool),n=null,a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(n=a.memoizedState.cachePool.pool),n!==e&&(a.flags|=2048)),u!==l&&u&&(t.child.flags|=8192),vn(t,t.updateQueue),sl(t),null);case 4:return Bl(),l===null&&Xc(t.stateNode.containerInfo),sl(t),null;case 10:return Rt(t.type),sl(t),null;case 19:if(z(Dl),a=t.memoizedState,a===null)return sl(t),null;if(e=(t.flags&128)!==0,n=a.rendering,n===null)if(e)te(a,!1);else{if(ol!==0||l!==null&&(l.flags&128)!==0)for(l=t.child;l!==null;){if(n=We(l),n!==null){for(t.flags|=128,te(a,!1),l=n.updateQueue,t.updateQueue=l,vn(t,l),t.subtreeFlags=0,l=u,u=t.child;u!==null;)mA(u,l),u=u.sibling;return T(Dl,Dl.current&1|2),I&&rt(t,a.treeForkCount),t.child}l=l.sibling}a.tail!==null&&kl()>mn&&(t.flags|=128,e=!0,te(a,!1),t.lanes=4194304)}else{if(!e)if(l=We(n),l!==null){if(t.flags|=128,e=!0,l=l.updateQueue,t.updateQueue=l,vn(t,l),te(a,!0),a.tail===null&&a.tailMode==="hidden"&&!n.alternate&&!I)return sl(t),null}else 2*kl()-a.renderingStartTime>mn&&u!==536870912&&(t.flags|=128,e=!0,te(a,!1),t.lanes=4194304);a.isBackwards?(n.sibling=t.child,t.child=n):(l=a.last,l!==null?l.sibling=n:t.child=n,a.last=n)}return a.tail!==null?(l=a.tail,a.rendering=l,a.tail=l.sibling,a.renderingStartTime=kl(),l.sibling=null,u=Dl.current,T(Dl,e?u&1|2:u&1),I&&rt(t,a.treeForkCount),l):(sl(t),null);case 22:case 23:return ut(t),pf(),a=t.memoizedState!==null,l!==null?l.memoizedState!==null!==a&&(t.flags|=8192):a&&(t.flags|=8192),a?(u&536870912)!==0&&(t.flags&128)===0&&(sl(t),t.subtreeFlags&6&&(t.flags|=8192)):sl(t),u=t.updateQueue,u!==null&&vn(t,u.retryQueue),u=null,l!==null&&l.memoizedState!==null&&l.memoizedState.cachePool!==null&&(u=l.memoizedState.cachePool.pool),a=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(a=t.memoizedState.cachePool.pool),a!==u&&(t.flags|=2048),l!==null&&z(ju),null;case 24:return u=null,l!==null&&(u=l.memoizedState.cache),t.memoizedState.cache!==u&&(t.flags|=2048),Rt(Ql),sl(t),null;case 25:return null;case 30:return null}throw Error(y(156,t.tag))}function Wd(l,t){switch(Tf(t),t.tag){case 1:return l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 3:return Rt(Ql),Bl(),l=t.flags,(l&65536)!==0&&(l&128)===0?(t.flags=l&-65537|128,t):null;case 26:case 27:case 5:return ze(t),null;case 31:if(t.memoizedState!==null){if(ut(t),t.alternate===null)throw Error(y(340));Nu()}return l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 13:if(ut(t),l=t.memoizedState,l!==null&&l.dehydrated!==null){if(t.alternate===null)throw Error(y(340));Nu()}return l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 19:return z(Dl),null;case 4:return Bl(),null;case 10:return Rt(t.type),null;case 22:case 23:return ut(t),pf(),l!==null&&z(ju),l=t.flags,l&65536?(t.flags=l&-65537|128,t):null;case 24:return Rt(Ql),null;case 25:return null;default:return null}}function K0(l,t){switch(Tf(t),t.tag){case 3:Rt(Ql),Bl();break;case 26:case 27:case 5:ze(t);break;case 4:Bl();break;case 31:t.memoizedState!==null&&ut(t);break;case 13:ut(t);break;case 19:z(Dl);break;case 10:Rt(t.type);break;case 22:case 23:ut(t),pf(),l!==null&&z(ju);break;case 24:Rt(Ql)}}function ue(l,t){try{var u=t.updateQueue,a=u!==null?u.lastEffect:null;if(a!==null){var e=a.next;u=e;do{if((u.tag&l)===l){a=void 0;var n=u.create,f=u.inst;a=n(),f.destroy=a}u=u.next}while(u!==e)}}catch(c){al(t,t.return,c)}}function iu(l,t,u){try{var a=t.updateQueue,e=a!==null?a.lastEffect:null;if(e!==null){var n=e.next;a=n;do{if((a.tag&l)===l){var f=a.inst,c=f.destroy;if(c!==void 0){f.destroy=void 0,e=t;var i=u,h=c;try{h()}catch(o){al(e,i,o)}}}a=a.next}while(a!==n)}}catch(o){al(t,t.return,o)}}function Z0(l){var t=l.updateQueue;if(t!==null){var u=l.stateNode;try{wA(t,u)}catch(a){al(l,l.return,a)}}}function q0(l,t,u){u.props=xu(l.type,l.memoizedProps),u.state=l.memoizedState;try{u.componentWillUnmount()}catch(a){al(l,t,a)}}function ae(l,t){try{var u=l.ref;if(u!==null){switch(l.tag){case 26:case 27:case 5:var a=l.stateNode;break;case 30:a=l.stateNode;break;default:a=l.stateNode}typeof u=="function"?l.refCleanup=u(a):u.current=a}}catch(e){al(l,t,e)}}function bt(l,t){var u=l.ref,a=l.refCleanup;if(u!==null)if(typeof a=="function")try{a()}catch(e){al(l,t,e)}finally{l.refCleanup=null,l=l.alternate,l!=null&&(l.refCleanup=null)}else if(typeof u=="function")try{u(null)}catch(e){al(l,t,e)}else u.current=null}function V0(l){var t=l.type,u=l.memoizedProps,a=l.stateNode;try{l:switch(t){case"button":case"input":case"select":case"textarea":u.autoFocus&&a.focus();break l;case"img":u.src?a.src=u.src:u.srcSet&&(a.srcset=u.srcSet)}}catch(e){al(l,l.return,e)}}function Bc(l,t,u){try{var a=l.stateNode;Dh(a,l.type,u,t),a[Jl]=t}catch(e){al(l,l.return,e)}}function F0(l){return l.tag===5||l.tag===3||l.tag===26||l.tag===27&&yu(l.type)||l.tag===4}function Dc(l){l:for(;;){for(;l.sibling===null;){if(l.return===null||F0(l.return))return null;l=l.return}for(l.sibling.return=l.return,l=l.sibling;l.tag!==5&&l.tag!==6&&l.tag!==18;){if(l.tag===27&&yu(l.type)||l.flags&2||l.child===null||l.tag===4)continue l;l.child.return=l,l=l.child}if(!(l.flags&2))return l.stateNode}}function Ec(l,t,u){var a=l.tag;if(a===5||a===6)l=l.stateNode,t?(u.nodeType===9?u.body:u.nodeName==="HTML"?u.ownerDocument.body:u).insertBefore(l,t):(t=u.nodeType===9?u.body:u.nodeName==="HTML"?u.ownerDocument.body:u,t.appendChild(l),u=u._reactRootContainer,u!=null||t.onclick!==null||(t.onclick=Gt));else if(a!==4&&(a===27&&yu(l.type)&&(u=l.stateNode,t=null),l=l.child,l!==null))for(Ec(l,t,u),l=l.sibling;l!==null;)Ec(l,t,u),l=l.sibling}function dn(l,t,u){var a=l.tag;if(a===5||a===6)l=l.stateNode,t?u.insertBefore(l,t):u.appendChild(l);else if(a!==4&&(a===27&&yu(l.type)&&(u=l.stateNode),l=l.child,l!==null))for(dn(l,t,u),l=l.sibling;l!==null;)dn(l,t,u),l=l.sibling}function I0(l){var t=l.stateNode,u=l.memoizedProps;try{for(var a=l.type,e=t.attributes;e.length;)t.removeAttributeNode(e[0]);jl(t,a,u),t[Ol]=l,t[Jl]=u}catch(n){al(l,l.return,n)}}var Jt=!1,Tl=!1,zc=!1,P0=typeof WeakSet=="function"?WeakSet:Set,Ml=null;function _d(l,t){if(l=l.containerInfo,Jc=jn,l=cA(l),sf(l)){if("selectionStart"in l)var u={start:l.selectionStart,end:l.selectionEnd};else l:{u=(u=l.ownerDocument)&&u.defaultView||window;var a=u.getSelection&&u.getSelection();if(a&&a.rangeCount!==0){u=a.anchorNode;var e=a.anchorOffset,n=a.focusNode;a=a.focusOffset;try{u.nodeType,n.nodeType}catch{u=null;break l}var f=0,c=-1,i=-1,h=0,o=0,E=l,s=null;t:for(;;){for(var m;E!==u||e!==0&&E.nodeType!==3||(c=f+e),E!==n||a!==0&&E.nodeType!==3||(i=f+a),E.nodeType===3&&(f+=E.nodeValue.length),(m=E.firstChild)!==null;)s=E,E=m;for(;;){if(E===l)break t;if(s===u&&++h===e&&(c=f),s===n&&++o===a&&(i=f),(m=E.nextSibling)!==null)break;E=s,s=E.parentNode}E=m}u=c===-1||i===-1?null:{start:c,end:i}}else u=null}u=u||{start:0,end:0}}else u=null;for(Kc={focusedElem:l,selectionRange:u},jn=!1,Ml=t;Ml!==null;)if(t=Ml,l=t.child,(t.subtreeFlags&1028)!==0&&l!==null)l.return=t,Ml=l;else for(;Ml!==null;){switch(t=Ml,n=t.alternate,l=t.flags,t.tag){case 0:if((l&4)!==0&&(l=t.updateQueue,l=l!==null?l.events:null,l!==null))for(u=0;u title"))),jl(n,a,u),n[Ol]=l,bl(n),a=n;break l;case"link":var f=tv("link","href",e).get(a+(u.href||""));if(f){for(var c=0;ccl&&(f=cl,cl=Y,Y=f);var v=nA(c,Y),A=nA(c,cl);if(v&&A&&(m.rangeCount!==1||m.anchorNode!==v.node||m.anchorOffset!==v.offset||m.focusNode!==A.node||m.focusOffset!==A.offset)){var d=E.createRange();d.setStart(v.node,v.offset),m.removeAllRanges(),Y>cl?(m.addRange(d),m.extend(A.node,A.offset)):(d.setEnd(A.node,A.offset),m.addRange(d))}}}}for(E=[],m=c;m=m.parentNode;)m.nodeType===1&&E.push({element:m,left:m.scrollLeft,top:m.scrollTop});for(typeof c.focus=="function"&&c.focus(),c=0;cu?32:u,B.T=null,u=Mc,Mc=null;var n=du,f=Ft;if(Sl=0,Ba=du=null,Ft=0,(ll&6)!==0)throw Error(y(331));var c=ll;if(ll|=4,fg(n.current),ag(n,n.current,f,u),ll=c,Ae(0,!1),Wl&&typeof Wl.onPostCommitFiberRoot=="function")try{Wl.onPostCommitFiberRoot(ba,n)}catch{}return!0}finally{H.p=e,B.T=a,Hg(l,t)}}function Sg(l,t,u){t=gt(u,t),t=cc(l.stateNode,t,2),l=nu(l,t,2),l!==null&&(Oa(l,2),Mt(l))}function al(l,t,u){if(l.tag===3)Sg(l,l,u);else for(;t!==null;){if(t.tag===3){Sg(t,l,u);break}else if(t.tag===1){var a=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof a.componentDidCatch=="function"&&(vu===null||!vu.has(a))){l=gt(u,l),u=b0(2),a=nu(t,u,2),a!==null&&(M0(u,a,t,l),Oa(a,2),Mt(a));break}}t=t.return}}function Gc(l,t,u){var a=l.pingCache;if(a===null){a=l.pingCache=new th;var e=new Set;a.set(t,e)}else e=a.get(t),e===void 0&&(e=new Set,a.set(t,e));e.has(u)||(Hc=!0,e.add(u),l=fh.bind(null,l,t,u),t.then(l,l))}function fh(l,t,u){var a=l.pingCache;a!==null&&a.delete(t),l.pingedLanes|=l.suspendedLanes&u,l.warmLanes&=~u,gl===l&&(Z&u)===u&&(ol===4||ol===3&&(Z&62914560)===Z&&300>kl()-yn?(ll&2)===0&&Da(l,0):Tc|=u,oa===Z&&(oa=0)),Mt(l)}function bg(l,t){t===0&&(t=zi()),l=Mu(l,t),l!==null&&(Oa(l,t),Mt(l))}function ch(l){var t=l.memoizedState,u=0;t!==null&&(u=t.retryLane),bg(l,u)}function ih(l,t){var u=0;switch(l.tag){case 31:case 13:var a=l.stateNode,e=l.memoizedState;e!==null&&(u=e.retryLane);break;case 19:a=l.stateNode;break;case 22:a=l.stateNode._retryCache;break;default:throw Error(y(314))}a!==null&&a.delete(t),bg(l,u)}function Ah(l,t){return Zn(l,t)}var Qn=null,za=null,jc=!1,Cn=!1,wc=!1,su=0;function Mt(l){l!==za&&l.next===null&&(za===null?Qn=za=l:za=za.next=l),Cn=!0,jc||(jc=!0,vh())}function Ae(l,t){if(!wc&&Cn){wc=!0;do for(var u=!1,a=Qn;a!==null;){if(l!==0){var e=a.pendingLanes;if(e===0)var n=0;else{var f=a.suspendedLanes,c=a.pingedLanes;n=(1<<31-_l(42|l)+1)-1,n&=e&~(f&~c),n=n&201326741?n&201326741|1:n?n|2:0}n!==0&&(u=!0,Yg(a,n))}else n=Z,n=Se(a,a===gl?n:0,a.cancelPendingCommit!==null||a.timeoutHandle!==-1),(n&3)===0||Ma(a,n)||(u=!0,Yg(a,n));a=a.next}while(u);wc=!1}}function gh(){Mg()}function Mg(){Cn=jc=!1;var l=0;su!==0&&zh()&&(l=su);for(var t=kl(),u=null,a=Qn;a!==null;){var e=a.next,n=Og(a,t);n===0?(a.next=null,u===null?Qn=e:u.next=e,e===null&&(za=u)):(u=a,(l!==0||(n&3)!==0)&&(Cn=!0)),a=e}Sl!==0&&Sl!==5||Ae(l),su!==0&&(su=0)}function Og(l,t){for(var u=l.suspendedLanes,a=l.pingedLanes,e=l.expirationTimes,n=l.pendingLanes&-62914561;0c)break;var o=i.transferSize,E=i.initiatorType;o&&Xg(E)&&(i=i.responseEnd,f+=o*(i"u"?null:document;function Wg(l,t,u){var a=Qa;if(a&&typeof t=="string"&&t){var e=it(t);e='link[rel="'+l+'"][href="'+e+'"]',typeof u=="string"&&(e+='[crossorigin="'+u+'"]'),kg.has(e)||(kg.add(e),l={rel:l,crossOrigin:u,href:t},a.querySelector(e)===null&&(t=a.createElement("link"),jl(t,"link",l),bl(t),a.head.appendChild(t)))}}function Nh(l){It.D(l),Wg("dns-prefetch",l,null)}function Yh(l,t){It.C(l,t),Wg("preconnect",l,t)}function Gh(l,t,u){It.L(l,t,u);var a=Qa;if(a&&l&&t){var e='link[rel="preload"][as="'+it(t)+'"]';t==="image"&&u&&u.imageSrcSet?(e+='[imagesrcset="'+it(u.imageSrcSet)+'"]',typeof u.imageSizes=="string"&&(e+='[imagesizes="'+it(u.imageSizes)+'"]')):e+='[href="'+it(l)+'"]';var n=e;switch(t){case"style":n=Ca(l);break;case"script":n=Ha(l)}mt.has(n)||(l=G({rel:"preload",href:t==="image"&&u&&u.imageSrcSet?void 0:l,as:t},u),mt.set(n,l),a.querySelector(e)!==null||t==="style"&&a.querySelector(he(n))||t==="script"&&a.querySelector(se(n))||(t=a.createElement("link"),jl(t,"link",l),bl(t),a.head.appendChild(t)))}}function jh(l,t){It.m(l,t);var u=Qa;if(u&&l){var a=t&&typeof t.as=="string"?t.as:"script",e='link[rel="modulepreload"][as="'+it(a)+'"][href="'+it(l)+'"]',n=e;switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":n=Ha(l)}if(!mt.has(n)&&(l=G({rel:"modulepreload",href:l},t),mt.set(n,l),u.querySelector(e)===null)){switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(u.querySelector(se(n)))return}a=u.createElement("link"),jl(a,"link",l),bl(a),u.head.appendChild(a)}}}function wh(l,t,u){It.S(l,t,u);var a=Qa;if(a&&l){var e=qu(a).hoistableStyles,n=Ca(l);t=t||"default";var f=e.get(n);if(!f){var c={loading:0,preload:null};if(f=a.querySelector(he(n)))c.loading=5;else{l=G({rel:"stylesheet",href:l,"data-precedence":t},u),(u=mt.get(n))&&kc(l,u);var i=f=a.createElement("link");bl(i),jl(i,"link",l),i._p=new Promise(function(h,o){i.onload=h,i.onerror=o}),i.addEventListener("load",function(){c.loading|=1}),i.addEventListener("error",function(){c.loading|=2}),c.loading|=4,Mn(f,t,a)}f={type:"stylesheet",instance:f,count:1,state:c},e.set(n,f)}}}function rh(l,t){It.X(l,t);var u=Qa;if(u&&l){var a=qu(u).hoistableScripts,e=Ha(l),n=a.get(e);n||(n=u.querySelector(se(e)),n||(l=G({src:l,async:!0},t),(t=mt.get(e))&&Wc(l,t),n=u.createElement("script"),bl(n),jl(n,"link",l),u.head.appendChild(n)),n={type:"script",instance:n,count:1,state:null},a.set(e,n))}}function Uh(l,t){It.M(l,t);var u=Qa;if(u&&l){var a=qu(u).hoistableScripts,e=Ha(l),n=a.get(e);n||(n=u.querySelector(se(e)),n||(l=G({src:l,async:!0,type:"module"},t),(t=mt.get(e))&&Wc(l,t),n=u.createElement("script"),bl(n),jl(n,"link",l),u.head.appendChild(n)),n={type:"script",instance:n,count:1,state:null},a.set(e,n))}}function _g(l,t,u,a){var e=(e=L.current)?bn(e):null;if(!e)throw Error(y(446));switch(l){case"meta":case"title":return null;case"style":return typeof u.precedence=="string"&&typeof u.href=="string"?(t=Ca(u.href),u=qu(e).hoistableStyles,a=u.get(t),a||(a={type:"style",instance:null,count:0,state:null},u.set(t,a)),a):{type:"void",instance:null,count:0,state:null};case"link":if(u.rel==="stylesheet"&&typeof u.href=="string"&&typeof u.precedence=="string"){l=Ca(u.href);var n=qu(e).hoistableStyles,f=n.get(l);if(f||(e=e.ownerDocument||e,f={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},n.set(l,f),(n=e.querySelector(he(l)))&&!n._p&&(f.instance=n,f.state.loading=5),mt.has(l)||(u={rel:"preload",as:"style",href:u.href,crossOrigin:u.crossOrigin,integrity:u.integrity,media:u.media,hrefLang:u.hrefLang,referrerPolicy:u.referrerPolicy},mt.set(l,u),n||Rh(e,l,u,f.state))),t&&a===null)throw Error(y(528,""));return f}if(t&&a!==null)throw Error(y(529,""));return null;case"script":return t=u.async,u=u.src,typeof u=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=Ha(u),u=qu(e).hoistableScripts,a=u.get(t),a||(a={type:"script",instance:null,count:0,state:null},u.set(t,a)),a):{type:"void",instance:null,count:0,state:null};default:throw Error(y(444,l))}}function Ca(l){return'href="'+it(l)+'"'}function he(l){return'link[rel="stylesheet"]['+l+"]"}function $g(l){return G({},l,{"data-precedence":l.precedence,precedence:null})}function Rh(l,t,u,a){l.querySelector('link[rel="preload"][as="style"]['+t+"]")?a.loading=1:(t=l.createElement("link"),a.preload=t,t.addEventListener("load",function(){return a.loading|=1}),t.addEventListener("error",function(){return a.loading|=2}),jl(t,"link",u),bl(t),l.head.appendChild(t))}function Ha(l){return'[src="'+it(l)+'"]'}function se(l){return"script[async]"+l}function lv(l,t,u){if(t.count++,t.instance===null)switch(t.type){case"style":var a=l.querySelector('style[data-href~="'+it(u.href)+'"]');if(a)return t.instance=a,bl(a),a;var e=G({},u,{"data-href":u.href,"data-precedence":u.precedence,href:null,precedence:null});return a=(l.ownerDocument||l).createElement("style"),bl(a),jl(a,"style",e),Mn(a,u.precedence,l),t.instance=a;case"stylesheet":e=Ca(u.href);var n=l.querySelector(he(e));if(n)return t.state.loading|=4,t.instance=n,bl(n),n;a=$g(u),(e=mt.get(e))&&kc(a,e),n=(l.ownerDocument||l).createElement("link"),bl(n);var f=n;return f._p=new Promise(function(c,i){f.onload=c,f.onerror=i}),jl(n,"link",a),t.state.loading|=4,Mn(n,u.precedence,l),t.instance=n;case"script":return n=Ha(u.src),(e=l.querySelector(se(n)))?(t.instance=e,bl(e),e):(a=u,(e=mt.get(n))&&(a=G({},u),Wc(a,e)),l=l.ownerDocument||l,e=l.createElement("script"),bl(e),jl(e,"link",a),l.head.appendChild(e),t.instance=e);case"void":return null;default:throw Error(y(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(a=t.instance,t.state.loading|=4,Mn(a,u.precedence,l));return t.instance}function Mn(l,t,u){for(var a=u.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),e=a.length?a[a.length-1]:null,n=e,f=0;f title"):null)}function xh(l,t,u){if(u===1||t.itemProp!=null)return!1;switch(l){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;switch(t.rel){case"stylesheet":return l=t.disabled,typeof t.precedence=="string"&&l==null;default:return!0}case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function av(l){return!(l.type==="stylesheet"&&(l.state.loading&3)===0)}function Xh(l,t,u,a){if(u.type==="stylesheet"&&(typeof a.media!="string"||matchMedia(a.media).matches!==!1)&&(u.state.loading&4)===0){if(u.instance===null){var e=Ca(a.href),n=t.querySelector(he(e));if(n){t=n._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(l.count++,l=Nn.bind(l),t.then(l,l)),u.state.loading|=4,u.instance=n,bl(n);return}n=t.ownerDocument||t,a=$g(a),(e=mt.get(e))&&kc(a,e),n=n.createElement("link"),bl(n);var f=n;f._p=new Promise(function(c,i){f.onload=c,f.onerror=i}),jl(n,"link",a),u.instance=n}l.stylesheets===null&&(l.stylesheets=new Map),l.stylesheets.set(u,t),(t=u.state.preload)&&(u.state.loading&3)===0&&(l.count++,u=Nn.bind(l),t.addEventListener("load",u),t.addEventListener("error",u))}}var _c=0;function ph(l,t){return l.stylesheets&&l.count===0&&Gn(l,l.stylesheets),0_c?50:800)+t);return l.unsuspend=u,function(){l.unsuspend=null,clearTimeout(a),clearTimeout(e)}}:null}function Nn(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Gn(this,this.stylesheets);else if(this.unsuspend){var l=this.unsuspend;this.unsuspend=null,l()}}}var Yn=null;function Gn(l,t){l.stylesheets=null,l.unsuspend!==null&&(l.count++,Yn=new Map,t.forEach(Lh,l),Yn=null,Nn.call(l))}function Lh(l,t){if(!(t.state.loading&4)){var u=Yn.get(l);if(u)var a=u.get(null);else{u=new Map,Yn.set(l,u);for(var e=l.querySelectorAll("link[data-precedence],style[data-precedence]"),n=0;n"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(C)}catch(R){console.error(R)}}return C(),ci.exports=as(),ci.exports}var ns=es();async function fs(){if(window.__CUBIT_DATA__)return window.__CUBIT_DATA__;try{const C=await fetch("./data.json");if(C.ok)return await C.json()}catch{}return null}function vi(C){return C>=1e9?`${(C/1e9).toFixed(2)} s`:C>=1e6?`${(C/1e6).toFixed(2)} ms`:C>=1e3?`${(C/1e3).toFixed(2)} µs`:`${C.toFixed(1)} ns`}const di=720,hi=240,Ot={top:16,right:16,bottom:28,left:64};function cs({series:C}){const R=C.points,P=di-Ot.left-Ot.right,y=hi-Ot.top-Ot.bottom;if(R.length===0)return il.jsx("div",{className:"chart-empty",children:"no data"});const X=R.map(p=>p.lower),W=R.map(p=>p.upper);let vl=Math.min(...X),zl=Math.max(...W);vl===zl&&(vl*=.95,zl*=1.05);const O=zl-vl||1,Q=p=>Ot.left+(R.length===1?P/2:p/(R.length-1)*P),V=p=>Ot.top+y-(p-vl)/O*y,G=R.map((p,k)=>`${Q(k)},${V(p.value)}`).join(" "),dl=R.map((p,k)=>`${Q(k)},${V(p.upper)}`).join(" ")+" "+R.map((p,k)=>`${Q(R.length-1-k)},${V(R[R.length-1-k].lower)}`).join(" "),Ul=R[R.length-1];return il.jsxs("figure",{className:"chart",children:[il.jsxs("figcaption",{children:[il.jsx("span",{className:"chart-id",children:C.id}),il.jsx("span",{className:"chart-last",children:vi(Ul.value)})]}),il.jsxs("svg",{viewBox:`0 0 ${di} ${hi}`,role:"img","aria-label":`${C.id} over time`,children:[[0,.25,.5,.75,1].map(p=>{const k=Ot.top+y*p,ot=zl-O*p;return il.jsxs("g",{children:[il.jsx("line",{className:"grid",x1:Ot.left,y1:k,x2:di-Ot.right,y2:k}),il.jsx("text",{className:"ytick",x:Ot.left-8,y:k+4,textAnchor:"end",children:vi(ot)})]},p)}),il.jsx("polygon",{className:"band",points:dl}),il.jsx("polyline",{className:"line",points:G,fill:"none"}),R.map((p,k)=>il.jsx("circle",{className:"dot",cx:Q(k),cy:V(p.value),r:3,children:il.jsxs("title",{children:[p.shortCommit," · ",vi(p.value)," (",new Date(p.timestamp).toISOString().slice(0,10),")"]})},p.commit+k)),R.map((p,k)=>k===0||k===R.length-1||R.length<=6?il.jsx("text",{className:"xtick",x:Q(k),y:hi-8,textAnchor:"middle",children:p.shortCommit},"x"+k):null)]})]})}const is="data:image/png;base64,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";function As(){const[C,R]=si.useState(void 0);return si.useEffect(()=>{fs().then(R)},[]),C===void 0?il.jsx("main",{className:"wrap",children:"Loading…"}):C===null||C.benchmarks.length===0?il.jsxs("main",{className:"wrap",children:[il.jsx("h1",{children:"cubit"}),il.jsx("p",{className:"muted",children:"No benchmark history yet. Record a run on the default branch to start the trend."})]}):il.jsxs("main",{className:"wrap",children:[il.jsxs("header",{children:[il.jsxs("h1",{children:[il.jsx("img",{className:"logo",src:is,alt:""})," cubit — performance"]}),il.jsxs("p",{className:"muted",children:[C.benchmarks.length," benchmarks · branch ",il.jsx("code",{children:C.branch})," · generated ",new Date(C.generatedAt).toISOString().replace("T"," ").slice(0,16)]})]}),il.jsx("div",{className:"grid",children:C.benchmarks.map(P=>il.jsx(cs,{series:P},P.id))})]})}ns.createRoot(document.getElementById("root")).render(il.jsx(si.StrictMode,{children:il.jsx(As,{})})); +
diff --git a/internal/report/report.go b/internal/report/report.go index d81601c..df2ba5d 100644 --- a/internal/report/report.go +++ b/internal/report/report.go @@ -18,7 +18,11 @@ import ( func Markdown(deltas []model.Delta, cur model.Run, thresholdPct float64, dashboardURL string) string { var b strings.Builder - fmt.Fprintf(&b, "## 📐 cubit — performance\n\n") + // The logo is an absolute raw URL, not a repo-relative path: this comment is + // posted into the *consuming* repo's PR, where "assets/..." would resolve + // against that repo and 404. Pinned to cubit's default branch (not a release + // tag) so the image keeps resolving for consumers on an older cubit version. + fmt.Fprintf(&b, "## \"\" cubit — performance\n\n") if cur.Commit != "" { fmt.Fprintf(&b, "Commit `%s`", shortSHA(cur.Commit)) if cur.Branch != "" { diff --git a/web/src/App.tsx b/web/src/App.tsx index 5896d2c..d1897ee 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import type { Dashboard } from "./types"; import { loadData } from "./data"; import { LineChart } from "./LineChart"; +import logo from "./assets/cubit-logo.png"; export function App() { const [data, setData] = useState(undefined); @@ -28,7 +29,9 @@ export function App() { return (
-

cubit — performance

+

+ cubit — performance +

{data.benchmarks.length} benchmarks · branch {data.branch} · generated {new Date(data.generatedAt).toISOString().replace("T", " ").slice(0, 16)} diff --git a/web/src/assets/cubit-logo.png b/web/src/assets/cubit-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a72540153977df8759eea401e3dd9ccbfd141e GIT binary patch literal 73876 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 literal 0 HcmV?d00001 diff --git a/web/src/styles.css b/web/src/styles.css index d3b17d1..c181cea 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -43,6 +43,14 @@ body { h1 { font-size: 20px; margin: 0 0 4px; + display: flex; + align-items: center; + gap: 8px; +} + +.logo { + height: 24px; + width: auto; } .muted { diff --git a/web/src/vite-env.d.ts b/web/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/web/src/vite-env.d.ts @@ -0,0 +1 @@ +/// From d1ebc330e2a9c1a7ee18b139d64f57d6f218d430 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sun, 19 Jul 2026 16:35:33 +0100 Subject: [PATCH 6/7] fix(gitstore,action): address code-review correctness findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gitstore: - Append: build the index AND read the manifest from a single consistent base (local branch, else origin/), and CAS the LOCAL ref. Previously tip() (local-only) and Index() (local-or-origin) could disagree: with only origin present, the tree was seeded empty while the manifest still listed every historical run, producing a divergent parentless orphan that dropped history and failed to push. Regression test added (TestAppendBuildsOnOriginTrackingRef). - Latest: fall back to the next-older matching run when an entry's blob is missing, instead of reporting "no baseline". - showFile/gitEnv: force LC_ALL=C so missing-path detection isn't defeated by localized git output. - Add History(): resolve the state ref once and read all runs, replacing the dashboard's O(N) Read-per-entry (each re-resolving the ref). dashboard: - Build uses gitstore.History (one ref resolution + one show per run). action.yml (light gate must never fail the build): - compare: on error, write a fallback comment and keep the job green (`if ! cubit compare ...`), instead of failing under `set -euo pipefail`. - record/push: best-effort — a failed record or rejected push logs to stderr and does not fail the workflow. Verified: gofmt/build/vet/test clean (incl. new regression test); real-data record → compare → dashboard loop unchanged. Claude-Session: https://claude.ai/code/session_016pTq6aVBJvTtXmwTgKw2Yh --- action.yml | 24 +++-- internal/dashboard/dashboard.go | 20 ++--- internal/gitstore/gitstore.go | 136 ++++++++++++++++++++++++----- internal/gitstore/gitstore_test.go | 60 +++++++++++++ 4 files changed, 199 insertions(+), 41 deletions(-) diff --git a/action.yml b/action.yml index 7151e1f..1f2f419 100644 --- a/action.yml +++ b/action.yml @@ -114,11 +114,17 @@ runs: if [[ "$mode" == "compare" ]]; then # latest: yields an empty baseline (all "new") the first time, # which is fine — cubit does not fail on a missing baseline. - cubit compare --repo . --criterion-dir "$DIR" \ + # + # This is a light gate: a compare error (e.g. no benchmark output was + # produced) must NOT fail the consuming workflow. `if ! ...` is exempt + # from `set -e`, so we fall back to a short note and keep the job green. + if ! cubit compare --repo . --criterion-dir "$DIR" \ --baseline-ref "latest:${PR_BASE}" \ --commit "$PR_HEAD_SHA" --branch "$PR_HEAD_REF" \ --threshold "$THRESHOLD" "${dash_arg[@]}" \ - --out "$report" + --out "$report"; then + printf '## cubit — performance\n\n⚠️ cubit could not produce a report — no benchmark output was found at `%s` (did the benchmark step run?).\n' "$DIR" > "$report" + fi # Render the standalone trend dashboard for the run artifact # (best-effort — a missing state branch just yields an empty page). cubit serve --repo . --state-branch "$STATE" --branch "$PR_BASE" \ @@ -131,9 +137,17 @@ runs: fi git config user.name "cubit" git config user.email "cubit@users.noreply.github.com" - cubit record --persist --repo . --criterion-dir "$DIR" \ - --commit "$GITHUB_SHA" --branch "$DEFAULT_BRANCH" --state-branch "$STATE" - git push origin "$STATE" + # Recording the baseline is best-effort: a failure to record or push + # (e.g. the state branch diverged, or a concurrent update won the CAS) + # must not fail the workflow — it just means the next PR compares + # against a slightly older baseline. Surface it on stderr instead. + if cubit record --persist --repo . --criterion-dir "$DIR" \ + --commit "$GITHUB_SHA" --branch "$DEFAULT_BRANCH" --state-branch "$STATE"; then + git push origin "$STATE" \ + || echo "cubit: push of ${STATE} failed (diverged or concurrent update?) — baseline not updated" >&2 + else + echo "cubit: recording the baseline failed — baseline not updated" >&2 + fi fi - name: Upload report artifact diff --git a/internal/dashboard/dashboard.go b/internal/dashboard/dashboard.go index 2d936d3..8eb0f7e 100644 --- a/internal/dashboard/dashboard.go +++ b/internal/dashboard/dashboard.go @@ -50,23 +50,13 @@ type Data struct { // optionally filtered to one branch, with each benchmark's points ordered // oldest-first (the manifest order). func Build(store *gitstore.Store, branch string, now time.Time) (Data, error) { - metas, err := store.Index() + runs, err := store.History(branch) if err != nil { return Data{}, err } byID := map[string]*benchSeries{} var ids []string - for _, m := range metas { - if branch != "" && m.Branch != branch { - continue - } - run, ok, err := store.Read(m.Commit) - if err != nil { - return Data{}, err - } - if !ok { - continue - } + for _, run := range runs { for _, ms := range run.Measurements { s, exists := byID[ms.ID] if !exists { @@ -75,9 +65,9 @@ func Build(store *gitstore.Store, branch string, now time.Time) (Data, error) { ids = append(ids, ms.ID) } s.Points = append(s.Points, point{ - Commit: m.Commit, - ShortCommit: short(m.Commit), - Timestamp: m.Timestamp, + Commit: run.Commit, + ShortCommit: short(run.Commit), + Timestamp: run.Timestamp, Value: ms.Value, Lower: ms.Lower, Upper: ms.Upper, diff --git a/internal/gitstore/gitstore.go b/internal/gitstore/gitstore.go index 852595d..247c863 100644 --- a/internal/gitstore/gitstore.go +++ b/internal/gitstore/gitstore.go @@ -105,7 +105,15 @@ func (s *Store) Latest(branchFilter string) (model.Run, bool, error) { if branchFilter != "" && metas[i].Branch != branchFilter { continue } - return s.Read(metas[i].Commit) + run, ok, err := s.Read(metas[i].Commit) + if err != nil { + return model.Run{}, false, err + } + if ok { + return run, true, nil + } + // The manifest lists this commit but its run blob is gone — fall back to + // the next-older matching run rather than reporting "no baseline". } return model.Run{}, false, nil } @@ -119,15 +127,23 @@ func (s *Store) Append(run model.Run) error { return fmt.Errorf("run has no commit SHA — records are keyed by commit") } - parent, hasParent, err := s.tip() + // The commit the new record builds on: the local branch if present, else the + // remote-tracking ref (the CI fetched-not-checked-out case). Seeding the + // index AND reading the manifest from this SAME base keeps the persisted tree + // and the manifest consistent, and setting the commit parent to it makes the + // new local branch a fast-forward descendant of origin — so the later push is + // not rejected. (Reading the manifest via a separately-resolved ref was the + // bug: with only origin present, the tree was seeded empty while the manifest + // still listed — and thus orphaned — every historical run.) + base, hasBase, err := s.baseCommit() if err != nil { return err } - // Stage into a throwaway index seeded from the parent tree, so existing - // records survive the commit. The index path must NOT pre-exist as an empty - // file — git rejects a zero-byte index — so we hand git a fresh path inside - // a temp dir and let it create the index itself. + // Stage into a throwaway index seeded from the base tree, so existing records + // survive the commit. The index path must NOT pre-exist as an empty file — + // git rejects a zero-byte index — so we hand git a fresh path inside a temp + // dir and let it create the index itself. tmpDir, err := os.MkdirTemp("", "cubit-index-") if err != nil { return err @@ -135,16 +151,23 @@ func (s *Store) Append(run model.Run) error { defer func() { _ = os.RemoveAll(tmpDir) }() env := append(os.Environ(), "GIT_INDEX_FILE="+filepath.Join(tmpDir, "index")) - if hasParent { - if _, err := s.gitEnv(env, nil, "read-tree", parent+"^{tree}"); err != nil { + if hasBase { + if _, err := s.gitEnv(env, nil, "read-tree", base+"^{tree}"); err != nil { return err } } - // Merge the new run into the manifest read from the parent tree. - metas, err := s.Index() - if err != nil { - return err + // Merge the new run into the manifest read from the exact base we seeded the + // tree from — never a separately-resolved ref — so history and tree agree. + var metas []RunMeta + if hasBase { + if data, ok, err := s.showFile(base, indexPath); err != nil { + return err + } else if ok { + if err := json.Unmarshal(data, &metas); err != nil { + return fmt.Errorf("parse %s at %s: %w", indexPath, short(base), err) + } + } } metas = upsertMeta(metas, RunMeta{Commit: run.Commit, Branch: run.Branch, Timestamp: run.Timestamp}) @@ -172,8 +195,8 @@ func (s *Store) Append(run model.Run) error { msg := fmt.Sprintf("record %s (%s)", short(run.Commit), run.Branch) commitArgs := []string{"commit-tree", tree, "-m", msg} - if hasParent { - commitArgs = append(commitArgs, "-p", parent) + if hasBase { + commitArgs = append(commitArgs, "-p", base) } commitOut, err := s.gitEnv(env, nil, commitArgs...) if err != nil { @@ -181,11 +204,18 @@ func (s *Store) Append(run model.Run) error { } commit := strings.TrimSpace(commitOut) - // Compare-and-swap the branch: old value is the parent (or the empty tree's - // zero-arg form when the branch is being created). + // Compare-and-swap the LOCAL branch. The old value is the local ref's current + // commit, or the empty string ("must not exist yet") when we are creating it + // — never the remote-tracking ref, which cannot be CAS'd. When base came from + // origin, the new commit's parent is origin's tip, so this create yields a + // clean fast-forward descendant. + localTip, hasLocal, err := s.tip() + if err != nil { + return err + } ref := "refs/heads/" + s.Branch - if hasParent { - _, err = s.git(nil, "update-ref", ref, commit, parent) + if hasLocal { + _, err = s.git(nil, "update-ref", ref, commit, localTip) } else { _, err = s.git(nil, "update-ref", ref, commit, "") } @@ -232,10 +262,69 @@ func (s *Store) tip() (string, bool, error) { return sha, true, nil } +// baseCommit resolves the commit a new record should build on: the local branch +// if it exists, else origin/ (the CI fetched-not-checked-out case). ok +// is false when neither exists (the branch is being created from scratch). +func (s *Store) baseCommit() (string, bool, error) { + for _, ref := range []string{"refs/heads/" + s.Branch, "refs/remotes/origin/" + s.Branch} { + out, err := s.git(nil, "rev-parse", "--verify", "--quiet", ref+"^{commit}") + if err == nil { + if sha := strings.TrimSpace(out); sha != "" { + return sha, true, nil + } + } + } + return "", false, nil +} + +// History returns every recorded run matching branchFilter (empty = all), +// oldest-first, resolving the state ref ONCE. Entries whose run blob is missing +// are skipped. It replaces one Read-per-entry (each of which re-resolved the +// ref) with a single resolution plus one git show per run. +func (s *Store) History(branchFilter string) ([]model.Run, error) { + ref, ok, err := s.resolveRef() + if err != nil || !ok { + return nil, err + } + data, ok, err := s.showFile(ref, indexPath) + if err != nil || !ok { + return nil, err + } + var metas []RunMeta + if err := json.Unmarshal(data, &metas); err != nil { + return nil, fmt.Errorf("parse %s: %w", indexPath, err) + } + sort.SliceStable(metas, func(i, j int) bool { return metas[i].Timestamp < metas[j].Timestamp }) + + var runs []model.Run + for _, m := range metas { + if branchFilter != "" && m.Branch != branchFilter { + continue + } + blob, ok, err := s.showFile(ref, runPath(m.Commit)) + if err != nil { + return nil, err + } + if !ok { + continue + } + var run model.Run + if err := json.Unmarshal(blob, &run); err != nil { + return nil, fmt.Errorf("parse %s: %w", runPath(m.Commit), err) + } + runs = append(runs, run) + } + return runs, nil +} + // showFile returns the bytes of repoPath at ref. ok is false (no error) when // the path does not exist at that ref. func (s *Store) showFile(ref, repoPath string) ([]byte, bool, error) { cmd := exec.Command("git", "-C", s.RepoDir, "show", ref+":"+repoPath) + // Force a stable C locale: the missing-path detection below matches git's + // English error text, which localized output (LANG/LC_ALL) would otherwise + // break — turning an expected "not found" into a hard error. + cmd.Env = append(os.Environ(), "LC_ALL=C", "LANG=C") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr @@ -257,9 +346,14 @@ func (s *Store) git(stdin []byte, args ...string) (string, error) { func (s *Store) gitEnv(env []string, stdin []byte, args ...string) (string, error) { cmd := exec.Command("git", append([]string{"-C", s.RepoDir}, args...)...) - if env != nil { - cmd.Env = env - } + base := env + if base == nil { + base = os.Environ() + } + // Force a stable C locale (see showFile) on every git invocation, including + // those carrying a GIT_INDEX_FILE. Later duplicate keys win, so this overrides + // any inherited LANG/LC_ALL. + cmd.Env = append(base, "LC_ALL=C", "LANG=C") if stdin != nil { cmd.Stdin = bytes.NewReader(stdin) } diff --git a/internal/gitstore/gitstore_test.go b/internal/gitstore/gitstore_test.go index 6abd100..0f9c972 100644 --- a/internal/gitstore/gitstore_test.go +++ b/internal/gitstore/gitstore_test.go @@ -4,6 +4,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "testing" "wardnet/cubit/internal/model" @@ -81,6 +82,51 @@ func TestAppendRejectsEmptyCommit(t *testing.T) { } } +// When only origin/ exists (the CI fetched-not-checked-out shape), +// Append must build on origin's tip: preserve historical run blobs and create a +// local branch that descends from origin (a fast-forward), not a divergent +// parentless orphan that drops history and fails to push. +func TestAppendBuildsOnOriginTrackingRef(t *testing.T) { + repo := initRepo(t) + s := New(repo, "") + + run1 := model.Run{ + Schema: 1, Tool: "criterion", Commit: "aaaaaaaaaaaaaaaa", Branch: "main", + Timestamp: "2026-07-18T00:00:00Z", + Measurements: []model.Measurement{{ID: "dns_cache/get", Unit: "ns", Value: 45.2}}, + } + if err := s.Append(run1); err != nil { + t.Fatalf("Append(run1): %v", err) + } + + // Relocate the branch to a remote-tracking ref only: origin has it, local + // doesn't. + originTip := strings.TrimSpace(gitOut(t, repo, "rev-parse", "refs/heads/cubit-state")) + mustGit(t, repo, "update-ref", "refs/remotes/origin/cubit-state", originTip) + mustGit(t, repo, "update-ref", "-d", "refs/heads/cubit-state") + + run2 := run1 + run2.Commit = "bbbbbbbbbbbbbbbb" + run2.Timestamp = "2026-07-19T00:00:00Z" + if err := s.Append(run2); err != nil { + t.Fatalf("Append(run2) on origin base: %v", err) + } + + // The historical run must survive (this is the regression: seed from origin's + // tree, not an empty index). + if _, ok, err := s.Read("aaaaaaaaaaaaaaaa"); err != nil || !ok { + t.Fatalf("historical run dropped after origin-based append: ok=%v err=%v", ok, err) + } + if metas, err := s.Index(); err != nil || len(metas) != 2 { + t.Fatalf("Index() = %d entries, %v; want 2", len(metas), err) + } + // The new local branch must descend from origin's tip, not be an orphan. + parent := strings.TrimSpace(gitOut(t, repo, "rev-parse", "refs/heads/cubit-state^")) + if parent != originTip { + t.Fatalf("new commit parent = %s; want origin tip %s (divergent orphan)", parent, originTip) + } +} + // initRepo creates a git repo with one commit on the default branch, so the // orphan cubit-state branch has a repository to live in. func initRepo(t *testing.T) string { @@ -96,6 +142,10 @@ func initRepo(t *testing.T) string { } } run("init", "-q", "-b", "main") + // Local identity so cubit's commit-tree works even on a runner with no + // global git config. + run("config", "user.email", "t@t") + run("config", "user.name", "t") if err := os.WriteFile(filepath.Join(dir, "README"), []byte("x\n"), 0o644); err != nil { t.Fatal(err) } @@ -104,6 +154,16 @@ func initRepo(t *testing.T) string { return dir } +func mustGit(t *testing.T, dir string, args ...string) { + t.Helper() + cmd := exec.Command("git", append([]string{"-C", dir}, args...)...) + cmd.Env = append(os.Environ(), + "GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@t", "GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@t") + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("git %v: %v\n%s", args, err, out) + } +} + func gitOut(t *testing.T, dir string, args ...string) string { t.Helper() cmd := exec.Command("git", append([]string{"-C", dir}, args...)...) From 3a26bea9bcd5c4353ff802080fdfc716f88700e3 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sun, 19 Jul 2026 17:21:56 +0100 Subject: [PATCH 7/7] ci: guard against dashboard bundle drift Add a CI job that rebuilds the dashboard from web/ and fails if the vendored bundle the Go binary embeds (internal/dashboard/assets/index.html) differs. The build is byte-reproducible (deps pinned via yarn.lock), so a mismatch means the source was changed without re-vendoring. Pin the toolchain via packageManager: yarn@4.17.1 so CI uses the same yarn as local. Fix on drift: yarn --cwd web build && cp web/dist/index.html internal/dashboard/assets/index.html Claude-Session: https://claude.ai/code/session_016pTq6aVBJvTtXmwTgKw2Yh --- .github/workflows/ci.yml | 38 ++++++++++++++++++++++++++++++++++++++ web/package.json | 1 + 2 files changed, 39 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee1042b..2d6d503 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,3 +38,41 @@ jobs: echo "$unformatted" >&2 exit 1 fi + + dashboard: + # Guard against drift between the web/ source and the vendored bundle that + # the Go binary embeds (internal/dashboard/assets/index.html). Rebuild the + # dashboard from source and fail if the committed bundle differs — the build + # is byte-reproducible (deps pinned via yarn.lock), so a mismatch means + # someone edited web/ without re-vendoring. + name: dashboard bundle up to date + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "26" + + - name: Enable corepack (pinned yarn from packageManager) + run: npm install -g corepack@latest && corepack enable + + - name: Install dashboard deps + working-directory: web + run: yarn install --immutable + + - name: Build dashboard + working-directory: web + run: yarn build + + - name: Verify vendored bundle matches source + run: | + cp web/dist/index.html internal/dashboard/assets/index.html + if ! git diff --quiet -- internal/dashboard/assets/index.html; then + echo "::error file=internal/dashboard/assets/index.html::vendored dashboard bundle is stale — rebuild and commit it" + echo "Fix locally with:" >&2 + echo " yarn --cwd web build && cp web/dist/index.html internal/dashboard/assets/index.html" >&2 + git --no-pager diff --stat -- internal/dashboard/assets/index.html + exit 1 + fi + echo "dashboard bundle matches source ✓" diff --git a/web/package.json b/web/package.json index 77f2498..d3be386 100644 --- a/web/package.json +++ b/web/package.json @@ -3,6 +3,7 @@ "private": true, "version": "0.0.0", "type": "module", + "packageManager": "yarn@4.17.1", "scripts": { "dev": "vite", "build": "vite build",