diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d50a80..3ee02a9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,8 @@ Common commands: make help make test make lint +make build +make serve ecommerce make web make audit-public go run src/main.go --help @@ -37,10 +39,13 @@ Useful fixture commands: ```bash make validate demo make scan ecommerce -make graph migration +make export-json-graph migration +make export-json-visualisation migration make serve ecommerce ``` +`make build` auto-refreshes the embedded web bundle when the frontend sources are newer than `src/internal/webui/dist/`. `make serve ` always rebuilds the embedded web bundle first, then rebuilds the binary, so local explorer testing always uses the latest app state. Use `make web` when you want an explicit frontend-only rebuild without building a binary. + ## Frontend changes The web explorer source lives under `src/internal/webui/frontend/`. diff --git a/Makefile b/Makefile index 6e32c74..deb7a04 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ FIXTURES := $(EXAMPLE_FIXTURES) playground FIXTURE ?= demo PRIMARY_GOAL := $(firstword $(MAKECMDGOALS)) SECOND_GOAL := $(word 2,$(MAKECMDGOALS)) -POSITIONAL_FIXTURE_COMMANDS := validate scan graph serve +POSITIONAL_FIXTURE_COMMANDS := validate scan export-json-graph export-json-visualisation serve ifneq ($(filter $(PRIMARY_GOAL),$(POSITIONAL_FIXTURE_COMMANDS)),) ifneq ($(filter $(SECOND_GOAL),$(FIXTURES)),) @@ -17,7 +17,7 @@ endif .PHONY: help fixtures build web install-dev-tools install-git-hooks init-hooks \ test-go test lint vet fmt cli-help \ testing-help testing-build testing-init playground-init audit-public \ - validate scan graph serve run + validate scan export-json-graph export-json-visualisation serve run help: ## Show grouped development, verification, and fixture commands @./scripts/help.sh @@ -25,10 +25,10 @@ help: ## Show grouped development, verification, and fixture commands fixtures: ## List discovered fixtures @for fixture in $(FIXTURES); do echo "$$fixture"; done -build: ## Build the local mapture binary into build/ +build: ## Build the latest local binary into build/, refreshing embedded web if stale @./scripts/build.sh -web: ## Rebuild the frontend bundle under src/internal/webui/dist/ +web: ## Rebuild only the frontend bundle under src/internal/webui/dist/ @go run ./scripts/build-web install-dev-tools: ## Install local Go dev tools into testing/tools/bin @@ -73,16 +73,19 @@ testing-init: ## Run init against testing/playground playground-init: ## Run init against the gitignored testing playground @$(MAKE) --no-print-directory testing-init -validate: ## Validate a fixture through testing/: make validate FIXTURE= +validate: ## Validate a fixture through testing/: make validate ecommerce or FIXTURE=ecommerce @./scripts/go.sh validate "$(FIXTURE)" scan: ## Scan a fixture and write testing/outputs/.scan.json; FIXTURE=all scans all examples @./scripts/go.sh scan "$(FIXTURE)" -graph: ## Export Mermaid for a fixture into testing/outputs/.mmd; FIXTURE=all graphs all examples - @./scripts/go.sh graph "$(FIXTURE)" +export-json-graph: ## Export JGF for a fixture into testing/outputs/.graph.json; FIXTURE=all exports all examples + @./scripts/go.sh export-json-graph "$(FIXTURE)" -serve: ## Rebuild testing/bin/mapture and run the local server against a fixture +export-json-visualisation: ## Export explorer JSON into testing/outputs/.visualisation.json; FIXTURE=all exports all examples + @./scripts/go.sh export-json-visualisation "$(FIXTURE)" + +serve: ## Always rebuild the embedded UI and binary, then run the local server against a fixture @./scripts/go.sh serve "$(FIXTURE)" run: ## Run any CLI command for a fixture: make run FIXTURE= CMD= @@ -90,7 +93,7 @@ run: ## Run any CLI command for a fixture: make run FIXTURE= CMD=` always rebuilds the embedded web bundle first, then rebuilds the binary, so the local explorer reflects the latest frontend and backend changes. Use `make web` only when you want a frontend-only rebuild. + Once installed, `mapture --help` and `mapture --version` show the current version, detected release channel, install source, and whether a newer build is available for that channel. ## Install diff --git a/scripts/build.sh b/scripts/build.sh index d25e6ba..2da84fd 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -4,5 +4,10 @@ set -euo pipefail source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh" ROOT_DIR="$(root_dir)" +OUTPUT="$ROOT_DIR/build/mapture" -build_binary "$ROOT_DIR/build/mapture" +mapture_print_section "Local Build" +ensure_embedded_web_bundle +build_binary "$OUTPUT" +print_binary_summary "local build" "$OUTPUT" +printf '%s\n' "$(mapture_success "build complete")" diff --git a/scripts/go.sh b/scripts/go.sh index c5fd083..47df5a9 100755 --- a/scripts/go.sh +++ b/scripts/go.sh @@ -66,6 +66,16 @@ fixture_output() { printf '%s/%s.%s\n' "$OUTPUTS_DIR" "$fixture" "$kind" } +ensure_testing_binary() { + local command="${1:-}" + if [[ "$command" == "serve" ]]; then + rebuild_embedded_web_bundle + else + ensure_embedded_web_bundle + fi + build_binary "$BIN" +} + sync_example_fixtures() { local requested="${1:-all}" local fixture source target @@ -90,16 +100,18 @@ run_for_all_examples() { shift || true case "$command" in - validate|scan|graph) + validate|scan|export-json-graph|export-json-visualisation) ;; *) printf '%s\n' "$(mapture_error "unsupported all-fixtures command: $command")" >&2 - printf '%s\n' "$(mapture_muted "supported commands: validate scan graph")" >&2 + printf '%s\n' "$(mapture_muted "supported commands: validate scan export-json-graph export-json-visualisation")" >&2 exit 1 ;; esac - local fixture + ensure_testing_binary "$command" + + local fixture target output while IFS= read -r fixture; do printf '%s\n' "$(mapture_strong "== $fixture ($command) ==")" case "$command" in @@ -107,10 +119,22 @@ run_for_all_examples() { "$BIN" validate "$(fixture_path "$fixture")" "$@" ;; scan) - run_scan "$fixture" + target="$(fixture_path "$fixture")" + output="$(fixture_output "$fixture" "scan.json")" + "$BIN" scan "$target" >"$output" + printf '%s\n' "$(mapture_success "wrote $(mapture_muted "$output")")" ;; - graph) - run_graph "$fixture" + export-json-graph) + target="$(fixture_path "$fixture")" + output="$(fixture_output "$fixture" "graph.json")" + "$BIN" export-json-graph "$target" -o "$output" + printf '%s\n' "$(mapture_success "wrote $(mapture_muted "$output")")" + ;; + export-json-visualisation) + target="$(fixture_path "$fixture")" + output="$(fixture_output "$fixture" "visualisation.json")" + "$BIN" export-json-visualisation "$target" -o "$output" + printf '%s\n' "$(mapture_success "wrote $(mapture_muted "$output")")" ;; esac done < <(example_fixture_names) @@ -145,17 +169,18 @@ serve_port() { show_help() { local fixture mapture_print_section "Repo Development Commands" - mapture_print_kv "./scripts/go.sh build" "Build the testing binary under $(mapture_muted "$BIN")" + mapture_print_kv "./scripts/go.sh build" "Build the latest local app into $(mapture_muted "$BIN")" mapture_print_kv "./scripts/go.sh init" "Run init against the testing playground" mapture_print_section "Repo Verification Commands" mapture_print_kv "./scripts/go.sh validate " "Validate one fixture or every example fixture" mapture_print_kv "./scripts/go.sh scan " "Write normalized scan output into $(mapture_muted "$OUTPUTS_DIR")" - mapture_print_kv "./scripts/go.sh graph " "Write Mermaid output into $(mapture_muted "$OUTPUTS_DIR")" + mapture_print_kv "./scripts/go.sh export-json-graph " "Write JGF output into $(mapture_muted "$OUTPUTS_DIR")" + mapture_print_kv "./scripts/go.sh export-json-visualisation " "Write explorer JSON into $(mapture_muted "$OUTPUTS_DIR")" mapture_print_section "Local Verification With Fixtures" mapture_print_kv "./scripts/go.sh fixtures" "List known fixtures" - mapture_print_kv "./scripts/go.sh serve " "Run the local explorer for a fixture" + mapture_print_kv "./scripts/go.sh serve " "Run the latest local explorer for a fixture" mapture_print_kv "./scripts/go.sh fixture " "Run any CLI command against a fixture path" mapture_print_section "Paths" @@ -176,7 +201,7 @@ run_validate() { if run_across_examples_if_needed validate "$fixture"; then return fi - build_binary "$BIN" + ensure_testing_binary validate exec "$BIN" validate "$(fixture_path "$fixture")" } @@ -185,7 +210,7 @@ run_scan() { if run_across_examples_if_needed scan "$fixture"; then return fi - build_binary "$BIN" + ensure_testing_binary scan local target output target="$(fixture_path "$fixture")" output="$(fixture_output "$fixture" "scan.json")" @@ -193,16 +218,29 @@ run_scan() { printf '%s\n' "$(mapture_success "wrote $(mapture_muted "$output")")" } -run_graph() { +run_export_json_graph() { local fixture="$1" - if run_across_examples_if_needed graph "$fixture"; then + if run_across_examples_if_needed export-json-graph "$fixture"; then return fi - build_binary "$BIN" + ensure_testing_binary export-json-graph local target output target="$(fixture_path "$fixture")" - output="$(fixture_output "$fixture" "mmd")" - "$BIN" graph "$target" -o "$output" + output="$(fixture_output "$fixture" "graph.json")" + "$BIN" export-json-graph "$target" -o "$output" + printf '%s\n' "$(mapture_success "wrote $(mapture_muted "$output")")" +} + +run_export_json_visualisation() { + local fixture="$1" + if run_across_examples_if_needed export-json-visualisation "$fixture"; then + return + fi + ensure_testing_binary export-json-visualisation + local target output + target="$(fixture_path "$fixture")" + output="$(fixture_output "$fixture" "visualisation.json")" + "$BIN" export-json-visualisation "$target" -o "$output" printf '%s\n' "$(mapture_success "wrote $(mapture_muted "$output")")" } @@ -212,10 +250,13 @@ run_serve() { printf '%s\n' "$(mapture_error 'serve does not support fixture "all"; choose one fixture')" >&2 exit 1 fi - build_binary "$BIN" + ensure_testing_binary serve local target addr target="$(fixture_path "$fixture")" addr="$(serve_port "$fixture")" + print_binary_summary "local testing binary" "$BIN" + printf ' %s %s\n' "$(mapture_accent "fixture")" "$(mapture_muted "$target")" + printf ' %s %s\n' "$(mapture_accent "serve")" "$(mapture_muted "http://$addr")" exec "$BIN" serve "$target" --addr "$addr" --open } @@ -236,11 +277,14 @@ case "$1" in fixture_path "${1:-demo}" ;; build) - build_binary "$BIN" - printf '%s\n' "$(mapture_success "built $(mapture_muted "$BIN")")" + mapture_print_section "Testing Build" + ensure_testing_binary serve + print_binary_summary "testing build" "$BIN" + printf '%s\n' "$(mapture_success "build complete")" ;; init) shift + ensure_testing_binary init exec "$BIN" init "${1:-$PLAYGROUND_DIR}" ;; validate) @@ -251,9 +295,13 @@ case "$1" in shift run_scan "${1:-demo}" ;; - graph) + export-json-graph) + shift + run_export_json_graph "${1:-demo}" + ;; + export-json-visualisation) shift - run_graph "${1:-demo}" + run_export_json_visualisation "${1:-demo}" ;; serve) shift @@ -269,10 +317,12 @@ case "$1" in fi shift shift || true + ensure_testing_binary "$command" exec "$BIN" "$command" "$(fixture_path "$fixture")" "$@" ;; run) shift + ensure_testing_binary "${1:-}" exec "$BIN" "$@" ;; demo|ecommerce|migration|playground) @@ -281,6 +331,7 @@ case "$1" in if [[ $# -eq 0 ]]; then set -- validate fi + ensure_testing_binary "${1:-validate}" exec "$BIN" "$1" "$(fixture_path "$fixture")" "${@:2}" ;; *) diff --git a/scripts/help.sh b/scripts/help.sh index 22459ee..9fd52a3 100755 --- a/scripts/help.sh +++ b/scripts/help.sh @@ -14,8 +14,8 @@ print_help() { mapture_print_kv "install-dev-tools" "Install local Go dev tools into testing/tools/bin" mapture_print_kv "install-git-hooks" "Configure git to use the repo-managed hooks" mapture_print_kv "init-hooks" "Configure git to use the repo-managed hooks" - mapture_print_kv "build" "Build the local mapture binary into build/" - mapture_print_kv "web" "Rebuild the frontend bundle under src/internal/webui/dist/" + mapture_print_kv "build" "Build the latest local mapture binary into build/" + mapture_print_kv "web" "Rebuild only the frontend bundle under src/internal/webui/dist/" mapture_print_section "Repo Verification Commands" mapture_print_kv "test-go" "Run Go tests through gotestsum" @@ -32,10 +32,11 @@ print_help() { mapture_print_kv "testing-build" "Build the current source into testing/bin/mapture" mapture_print_kv "testing-init" "Run init against testing/playground" mapture_print_kv "playground-init" "Run init against the gitignored testing playground" - mapture_print_kv "validate" "Validate a fixture: make validate FIXTURE=" + mapture_print_kv "validate" "Validate a fixture: make validate ecommerce or FIXTURE=ecommerce" mapture_print_kv "scan" "Scan a fixture: make scan FIXTURE=" - mapture_print_kv "graph" "Export Mermaid for a fixture: make graph FIXTURE=" - mapture_print_kv "serve" "Run the local server against a fixture: make serve FIXTURE=" + mapture_print_kv "export-json-graph" "Export JGF for a fixture: make export-json-graph FIXTURE=" + mapture_print_kv "export-json-visualisation" "Export explorer JSON: make export-json-visualisation FIXTURE=" + mapture_print_kv "serve" "Always rebuild UI+binary, then run: make serve ecommerce or FIXTURE=ecommerce" mapture_print_kv "run" "Run any CLI command for a fixture: make run FIXTURE= CMD=" mapture_print_section "Fixtures" @@ -46,9 +47,9 @@ print_help() { mapture_print_section "Fixture Aliases" while IFS= read -r fixture; do - printf ' validate.%s scan.%s graph.%s serve.%s\n' "$fixture" "$fixture" "$fixture" "$fixture" + printf ' validate.%s scan.%s export-json-graph.%s export-json-visualisation.%s serve.%s\n' "$fixture" "$fixture" "$fixture" "$fixture" "$fixture" done < <(discover_example_fixtures) - printf ' validate.playground scan.playground graph.playground serve.playground\n' + printf ' validate.playground scan.playground export-json-graph.playground export-json-visualisation.playground serve.playground\n' } print_help diff --git a/scripts/lib.sh b/scripts/lib.sh index 2de27fe..ff2eadf 100755 --- a/scripts/lib.sh +++ b/scripts/lib.sh @@ -53,6 +53,14 @@ root_dir() { cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd } +web_frontend_dir() { + printf '%s/src/internal/webui/frontend\n' "$(root_dir)" +} + +web_dist_dir() { + printf '%s/src/internal/webui/dist\n' "$(root_dir)" +} + testing_dir() { printf '%s/testing\n' "$(root_dir)" } @@ -126,6 +134,78 @@ build_binary() { "$repo/cmd/mapture" } +binary_version_line() { + local binary="$1" + "$binary" --version 2>/dev/null | sed -n '1p' +} + +print_binary_summary() { + local label="$1" + local binary="$2" + local version_line + + version_line="$(binary_version_line "$binary")" + + printf '%s\n' "$(mapture_success "$label ready")" + printf ' %s %s\n' "$(mapture_accent "binary")" "$(mapture_muted "$binary")" + if [[ -n "$version_line" ]]; then + printf ' %s %s\n' "$(mapture_accent "version")" "$(mapture_muted "$version_line")" + fi +} + +embedded_web_bundle_current() { + local frontend_dir dist_dir marker source + + frontend_dir="$(web_frontend_dir)" + dist_dir="$(web_dist_dir)" + marker="$dist_dir/app.js" + + [[ -f "$dist_dir/index.html" ]] || return 1 + [[ -f "$dist_dir/styles.css" ]] || return 1 + [[ -f "$marker" ]] || return 1 + + for source in \ + "$frontend_dir/index.html" \ + "$frontend_dir/package.json" \ + "$frontend_dir/package-lock.json" \ + "$frontend_dir/vite.config.ts"; do + [[ ! -e "$source" || "$source" -ot "$marker" ]] || return 1 + done + + if find "$frontend_dir/src" -type f -newer "$marker" | grep -q .; then + return 1 + fi + + return 0 +} + +ensure_embedded_web_bundle() { + local repo + repo="$(root_dir)" + + if embedded_web_bundle_current; then + printf '%s\n' "$(mapture_success "embedded web bundle is current")" + return 0 + fi + + printf '%s\n' "$(mapture_warning "embedded web bundle is stale; rebuilding")" + ( + cd "$repo" + go run ./scripts/build-web + ) +} + +rebuild_embedded_web_bundle() { + local repo + repo="$(root_dir)" + + printf '%s\n' "$(mapture_warning "rebuilding embedded web bundle")" + ( + cd "$repo" + go run ./scripts/build-web + ) +} + sha256_file() { local path="$1" diff --git a/scripts/test.sh b/scripts/test.sh index 6a86bd8..2f831bb 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -22,6 +22,7 @@ go run src/main.go scan examples/ecommerce >/dev/null go run src/main.go export-json-graph examples/demo >/dev/null graph_output="$(mktemp)" +visualisation_output="$(mktemp)" release_output_dir="$(mktemp -d)" formula_output="$(mktemp)" tap_output_dir="$(mktemp -d)" @@ -29,14 +30,35 @@ install_output_dir="$(mktemp -d)" help_plain_output="$(mktemp)" help_color_output="$(mktemp)" go_help_color_output="$(mktemp)" -trap 'rm -f "$graph_output" "$formula_output" "$help_plain_output" "$help_color_output" "$go_help_color_output"; rm -rf "$release_output_dir" "$tap_output_dir" "$install_output_dir"' EXIT +build_output="$(mktemp)" +testing_build_output="$(mktemp)" +stale_build_output="$(mktemp)" +fresh_build_output="$(mktemp)" +web_probe="$ROOT_DIR/src/internal/webui/frontend/src/.mapture-web-stale-probe" +trap 'rm -f "$graph_output" "$visualisation_output" "$formula_output" "$help_plain_output" "$help_color_output" "$go_help_color_output" "$build_output" "$testing_build_output" "$stale_build_output" "$fresh_build_output" "$web_probe"; rm -rf "$release_output_dir" "$tap_output_dir" "$install_output_dir"' EXIT go run src/main.go export-json-graph examples/ecommerce -o "$graph_output" test -s "$graph_output" +go run src/main.go export-json-visualisation examples/ecommerce -o "$visualisation_output" +test -s "$visualisation_output" mapture_print_section "Release helper checks" -./scripts/build.sh >/dev/null +./scripts/build.sh >"$build_output" +grep -q 'embedded web bundle' "$build_output" +grep -q 'build/mapture' "$build_output" +grep -q 'mapture.dev - 0.0.0-dev' "$build_output" build_version="$(./build/mapture --version)" [[ "$build_version" == *"0.0.0-dev"* ]] +./scripts/go.sh build >"$testing_build_output" +grep -q 'embedded web bundle' "$testing_build_output" +grep -q 'testing/bin/mapture' "$testing_build_output" +grep -q 'mapture.dev - 0.0.0-dev' "$testing_build_output" + +touch "$web_probe" +./scripts/build.sh >"$stale_build_output" +grep -q 'embedded web bundle is stale; rebuilding' "$stale_build_output" +rm -f "$web_probe" +./scripts/build.sh >"$fresh_build_output" +grep -q 'embedded web bundle is current' "$fresh_build_output" GOBIN="$install_output_dir" go install ./cmd/mapture test -x "$install_output_dir/mapture" @@ -80,6 +102,8 @@ MAPTURE_COLOR=always make help >"$help_color_output" grep -q $'\033' "$help_color_output" MAPTURE_COLOR=always ./scripts/go.sh help >"$go_help_color_output" grep -q $'\033' "$go_help_color_output" +! grep -q 'make graph' "$help_plain_output" +! grep -q './scripts/go.sh graph' "$go_help_color_output" expect_failure() { local path="$1"