Skip to content

Commit 67311e2

Browse files
authored
feat: Dashboard redesign with modern UI and health check improvements (#84)
* feat: Implement alternate dashboard design with modern mode - Added DesignModeContext and useDesignMode hook for managing design modes. - Created a test suite for DesignModeContext with localStorage and URL parameter handling. - Developed modern-theme.css for the new design system with a teal/cyan palette. - Implemented Modern mode components and layout structure. - Enabled design mode switching via URL parameters and localStorage persistence. - Conducted extensive testing to ensure functionality and accessibility compliance. - Documented design specifications and tasks for the new dashboard design. * feat: Enhance health map logic for modern components and improve test coverage for design mode changes * feat: add ModernServiceStatusCard component for service status display - Implemented ModernServiceStatusCard to provide a compact summary of service statuses including error, warning, running, and stopped counts. - Introduced StatusCount component for displaying individual status counts with appropriate icons and animations. - Integrated loading state handling within the card. feat: create ServiceOperationsContext for managing service operations - Added ServiceOperationsContext to handle service lifecycle operations (start, stop, restart) and track their states. - Implemented bulk operation capabilities for managing multiple services simultaneously. - Provided utility functions for querying operation states and available actions based on service status. test: add comprehensive tests for useServiceOperations hook - Developed unit tests for useServiceOperations to validate operation state management, action availability, and API interactions. - Mocked fetch calls to simulate API responses for service operations. test: implement log-utils tests for log processing functions - Created tests for log utility functions to ensure correct handling of ANSI to HTML conversion, log level detection, and service color assignment. test: establish custom render utility for testing with context providers - Introduced custom render function to wrap components with necessary context providers for testing purposes. * Refactor azd app to eliminate file-based state storage and use gRPC services for configuration - Removed .azure/services.json and .azure/ports.json, transitioning to in-memory state management. - Implemented azdconfig package for gRPC UserConfig service interactions. - Updated port management to utilize azdconfig for service port assignments. - Refactored logs_config to store user preferences via azdconfig instead of file storage. - Added /api/ping endpoint to dashboard for health checks. - Modified azd app commands (info, stop) to query live dashboard API instead of reading from files. - Ensured all existing functionality is preserved and all tests pass. * feat: Implement health check improvements for portless services - Added a design system document for the azd-app marketing website, detailing color tokens, typography, spacing, breakpoints, animations, shadows, border radius, z-index, focus states, motion preferences, dark mode implementation, and accessibility guidelines. - Defined functional requirements for health checks on portless services, ensuring services without explicit ports do not get assigned ports and utilize process-based health checks. - Updated `NeedsPort()` logic to return false for services without ports in `azure.yaml`. - Enhanced health check tests to reflect new behavior and ensure accurate health status reporting. - Implemented integration tests for portless services to verify correct health check functionality and dashboard display. * Refactor code structure for improved readability and maintainability * feat: Prevent regression of running services to starting during health check grace period * fix: share in-memory config client across port managers for test consistency When gRPC is not available (e.g., during tests), all port manager instances now share the same in-memory config client. This ensures port assignments saved by one port manager are visible to others within the same process. Previously, each port manager created its own InMemoryClient, causing port assignments to be invisible across instances. This broke tests that: 1. Create a dashboard server (which uses port manager) 2. Stop the server and clear the servers map 3. Query the port manager for persisted assignments The fix introduces a shared in-memory client (sharedInMemoryClient) that is lazily initialized once and reused by all port managers when gRPC is unavailable. ClearCacheForTesting() now also resets this shared client for proper test isolation. * fix: eliminate flaky test by using TryLock instead of goroutine-based lock The TestExecuteOperation_ConcurrentPrevention test was flaky because the previous goroutine-based lock acquisition approach had race conditions: - A timed-out operation's goroutine could acquire and release the lock after the timeout, interfering with subsequent operations - The channel-based signaling had edge cases with the default select case Replaced with a simpler TryLock loop approach that: - Polls TryLock() every 10ms until timeout - Has no lingering goroutines after timeout - Is deterministic and race-free Also added a small delay in the test after the first operation completes to ensure any cleanup from timed-out operations settles. * fix: resolve symlinks in portmanager path normalization for macOS compatibility On macOS, temp directories use symlinks (e.g., /var -> /private/var). The dashboard server resolves symlinks via filepath.EvalSymlinks, but the portmanager did not. This caused path mismatches when caching port managers, resulting in test failures on macOS where the test and server would get different port manager instances. Added filepath.EvalSymlinks to portmanager's path normalization to match the dashboard server's behavior and ensure consistent caching across different path representations. * fix: update portmanager cache tests to use normalized paths The cache tests were checking for the original tempDir path, but with symlink resolution now in GetPortManager, the cache key may be different (e.g., /private/var vs /var on macOS). Updated tests to normalize paths the same way GetPortManager does before checking the cache.
1 parent ec82392 commit 67311e2

File tree

198 files changed

+13710
-28888
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

198 files changed

+13710
-28888
lines changed

cli/build.ps1

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,82 @@ $EXTENSION_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
1010
# Change to the script directory
1111
Set-Location -Path $EXTENSION_DIR
1212

13-
# Check if .go files have changed by comparing timestamps
14-
# Only kill CLI processes if Go files need rebuilding
15-
$shouldKillProcesses = $false
16-
$goFiles = Get-ChildItem -Path "src" -Recurse -Filter "*.go" -ErrorAction SilentlyContinue
17-
18-
if ($goFiles) {
19-
# Check if any binary exists to compare against
20-
$existingBinaries = Get-ChildItem -Path "bin" -Filter "*.exe" -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike "*.old" }
21-
if ($existingBinaries) {
22-
$newestBinary = $existingBinaries | Sort-Object LastWriteTime -Descending | Select-Object -First 1
23-
$newestGoFile = $goFiles | Sort-Object LastWriteTime -Descending | Select-Object -First 1
24-
if ($newestGoFile.LastWriteTime -gt $newestBinary.LastWriteTime) {
25-
$shouldKillProcesses = $true
26-
}
27-
} else {
28-
# No binary exists, will need to build (and kill any stale processes)
29-
$shouldKillProcesses = $true
30-
}
31-
}
32-
33-
if ($shouldKillProcesses) {
34-
# Kill any running app processes to allow rebuilding
35-
# This is necessary on Windows where the binary cannot be overwritten while in use
36-
Write-Host "Go files changed - stopping any running app processes..." -ForegroundColor Yellow
13+
# Helper function to kill extension processes
14+
# This is necessary on Windows where binaries cannot be overwritten while in use
15+
function Stop-ExtensionProcesses {
3716
$binaryName = "app"
3817
$extensionId = "jongio.azd.app"
3918
$extensionBinaryPrefix = $extensionId -replace '\.', '-'
4019

41-
# Kill processes silently (ignore errors if not running)
20+
# Kill processes by name silently (ignore errors if not running)
4221
taskkill /F /IM "$binaryName.exe" 2>$null | Out-Null
4322
foreach ($arch in @("windows-amd64", "windows-arm64")) {
4423
$procName = "$extensionBinaryPrefix-$arch.exe"
4524
taskkill /F /IM $procName 2>$null | Out-Null
4625
}
26+
27+
# Also kill any processes running from the installed extension directory
28+
# This catches processes that azd x watch/install started
29+
$installedExtensionDir = Join-Path $env:USERPROFILE ".azd\extensions\$extensionId"
30+
if (Test-Path $installedExtensionDir) {
31+
Get-Process | Where-Object {
32+
$_.Path -and $_.Path.StartsWith($installedExtensionDir)
33+
} | ForEach-Object {
34+
Write-Host " Stopping process: $($_.Name) (PID: $($_.Id))" -ForegroundColor Gray
35+
Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
36+
}
37+
}
38+
39+
# Give processes time to fully terminate and release file handles
40+
Start-Sleep -Milliseconds 500
41+
}
42+
43+
# Check if we need to rebuild the Go binary
44+
# This happens when: Go files changed OR embedded dashboard dist changed
45+
$needsGoBuild = $false
46+
$existingBinaries = Get-ChildItem -Path "bin" -Filter "*.exe" -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike "*.old" }
47+
48+
if (-not $existingBinaries) {
49+
# No binary exists, definitely need to build
50+
$needsGoBuild = $true
51+
Write-Host "No existing binary found, will build" -ForegroundColor Yellow
52+
} else {
53+
$newestBinary = $existingBinaries | Sort-Object LastWriteTime -Descending | Select-Object -First 1
54+
$binaryTime = $newestBinary.LastWriteTime
55+
56+
# Check Go source files
57+
$goFiles = Get-ChildItem -Path "src" -Recurse -Filter "*.go" -ErrorAction SilentlyContinue
58+
if ($goFiles) {
59+
$newestGoFile = $goFiles | Sort-Object LastWriteTime -Descending | Select-Object -First 1
60+
if ($newestGoFile.LastWriteTime -gt $binaryTime) {
61+
$needsGoBuild = $true
62+
Write-Host "Go source files changed, will rebuild" -ForegroundColor Yellow
63+
}
64+
}
65+
66+
# Check embedded dashboard dist (it's compiled into the binary)
67+
$dashboardDistPath = "src\internal\dashboard\dist"
68+
if (Test-Path $dashboardDistPath) {
69+
$distFiles = Get-ChildItem -Path $dashboardDistPath -Recurse -File -ErrorAction SilentlyContinue
70+
if ($distFiles) {
71+
$newestDistFile = $distFiles | Sort-Object LastWriteTime -Descending | Select-Object -First 1
72+
if ($newestDistFile.LastWriteTime -gt $binaryTime) {
73+
$needsGoBuild = $true
74+
Write-Host "Embedded dashboard dist changed, will rebuild" -ForegroundColor Yellow
75+
}
76+
}
77+
}
78+
}
79+
80+
# Only kill extension processes if we're actually going to rebuild the binary
81+
# This prevents unnecessary restarts when dashboard source changes (vite watcher handles that separately)
82+
if ($needsGoBuild) {
83+
Write-Host "Stopping extension processes before rebuild..." -ForegroundColor Yellow
84+
Stop-ExtensionProcesses
85+
} else {
86+
# Nothing to rebuild - exit early to prevent azd x watch from trying to install
87+
Write-Host " ✓ Binary up to date, skipping build" -ForegroundColor Green
88+
exit 0
4789
}
4890

4991
Write-Host "Building App Extension..." -ForegroundColor Cyan

cli/build.sh

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,44 +10,74 @@ EXTENSION_DIR="$(cd "$(dirname "$0")" && pwd)"
1010
# Change to the script directory
1111
cd "$EXTENSION_DIR" || exit
1212

13-
# Check if .go files have changed by comparing timestamps
14-
# Only kill CLI processes if Go files need rebuilding
15-
SHOULD_KILL_PROCESSES=false
16-
17-
if [ -d "src" ]; then
18-
# Find newest .go file
19-
NEWEST_GO_FILE=$(find src -name "*.go" -type f -exec stat -c %Y {} \; 2>/dev/null | sort -n | tail -1 || \
20-
find src -name "*.go" -type f -exec stat -f %m {} \; 2>/dev/null | sort -n | tail -1)
13+
# Helper function to kill extension processes
14+
# This prevents "file in use" errors when azd x watch copies to ~/.azd/extensions/
15+
stop_extension_processes() {
16+
BINARY_NAME="app"
17+
EXTENSION_ID_FOR_KILL="jongio.azd.app"
18+
EXTENSION_BINARY_PREFIX="${EXTENSION_ID_FOR_KILL//./-}"
19+
20+
# Kill processes by name silently (ignore errors if not running)
21+
pkill -f "$BINARY_NAME" 2>/dev/null || true
22+
pkill -f "$EXTENSION_BINARY_PREFIX" 2>/dev/null || true
2123

22-
if [ -d "bin" ]; then
23-
# Find newest binary (excluding .old files)
24-
NEWEST_BINARY=$(find bin -type f ! -name "*.old" -exec stat -c %Y {} \; 2>/dev/null | sort -n | tail -1 || \
25-
find bin -type f ! -name "*.old" -exec stat -f %m {} \; 2>/dev/null | sort -n | tail -1)
24+
# Also kill any processes running from the installed extension directory
25+
INSTALLED_EXT_DIR="$HOME/.azd/extensions/$EXTENSION_ID_FOR_KILL"
26+
if [ -d "$INSTALLED_EXT_DIR" ]; then
27+
pkill -f "$INSTALLED_EXT_DIR" 2>/dev/null || true
28+
fi
29+
30+
# Give processes time to fully terminate and release file handles
31+
sleep 0.5
32+
}
33+
34+
# Check if we need to rebuild the Go binary
35+
# This happens when: Go files changed OR embedded dashboard dist changed
36+
NEEDS_GO_BUILD=false
37+
38+
if [ -d "bin" ]; then
39+
# Find newest binary (excluding .old files)
40+
NEWEST_BINARY_TIME=$(find bin -type f ! -name "*.old" -exec stat -c %Y {} \; 2>/dev/null | sort -n | tail -1 || \
41+
find bin -type f ! -name "*.old" -exec stat -f %m {} \; 2>/dev/null | sort -n | tail -1)
42+
43+
if [ -z "$NEWEST_BINARY_TIME" ]; then
44+
NEEDS_GO_BUILD=true
45+
echo "No existing binary found, will build"
46+
else
47+
# Check Go source files
48+
if [ -d "src" ]; then
49+
NEWEST_GO_TIME=$(find src -name "*.go" -type f -exec stat -c %Y {} \; 2>/dev/null | sort -n | tail -1 || \
50+
find src -name "*.go" -type f -exec stat -f %m {} \; 2>/dev/null | sort -n | tail -1)
51+
if [ -n "$NEWEST_GO_TIME" ] && [ "$NEWEST_GO_TIME" -gt "$NEWEST_BINARY_TIME" ]; then
52+
NEEDS_GO_BUILD=true
53+
echo "Go source files changed, will rebuild"
54+
fi
55+
fi
2656

27-
if [ -n "$NEWEST_GO_FILE" ] && [ -n "$NEWEST_BINARY" ]; then
28-
if [ "$NEWEST_GO_FILE" -gt "$NEWEST_BINARY" ]; then
29-
SHOULD_KILL_PROCESSES=true
57+
# Check embedded dashboard dist (it's compiled into the binary)
58+
DASHBOARD_DIST_PATH="src/internal/dashboard/dist"
59+
if [ -d "$DASHBOARD_DIST_PATH" ]; then
60+
NEWEST_DIST_TIME=$(find "$DASHBOARD_DIST_PATH" -type f -exec stat -c %Y {} \; 2>/dev/null | sort -n | tail -1 || \
61+
find "$DASHBOARD_DIST_PATH" -type f -exec stat -f %m {} \; 2>/dev/null | sort -n | tail -1)
62+
if [ -n "$NEWEST_DIST_TIME" ] && [ "$NEWEST_DIST_TIME" -gt "$NEWEST_BINARY_TIME" ]; then
63+
NEEDS_GO_BUILD=true
64+
echo "Embedded dashboard dist changed, will rebuild"
3065
fi
31-
elif [ -n "$NEWEST_GO_FILE" ]; then
32-
# No binary exists, will need to build
33-
SHOULD_KILL_PROCESSES=true
3466
fi
35-
else
36-
# No bin directory, will need to build
37-
SHOULD_KILL_PROCESSES=true
3867
fi
68+
else
69+
NEEDS_GO_BUILD=true
70+
echo "No bin directory found, will build"
3971
fi
4072

41-
if [ "$SHOULD_KILL_PROCESSES" = true ]; then
42-
# Kill any running app processes to allow rebuilding
43-
echo "Go files changed - stopping any running app processes..."
44-
BINARY_NAME="app"
45-
EXTENSION_ID_FOR_KILL="jongio.azd.app"
46-
EXTENSION_BINARY_PREFIX="${EXTENSION_ID_FOR_KILL//./-}"
47-
48-
# Kill processes silently (ignore errors if not running)
49-
pkill -f "$BINARY_NAME" 2>/dev/null || true
50-
pkill -f "$EXTENSION_BINARY_PREFIX" 2>/dev/null || true
73+
# Only kill extension processes if we're actually going to rebuild the binary
74+
if [ "$NEEDS_GO_BUILD" = true ]; then
75+
echo "Stopping extension processes before rebuild..."
76+
stop_extension_processes
77+
else
78+
# Nothing to rebuild - exit early to prevent azd x watch from trying to install
79+
echo " ✓ Binary up to date, skipping build"
80+
exit 0
5181
fi
5282

5383
echo "Building App Extension..."

0 commit comments

Comments
 (0)