Skip to content

updated flake, added debug handler, and added example for debug handler#8

Merged
connerohnesorge merged 4 commits intomainfrom
debug-handler
May 18, 2025
Merged

updated flake, added debug handler, and added example for debug handler#8
connerohnesorge merged 4 commits intomainfrom
debug-handler

Conversation

@connerohnesorge
Copy link
Owner

@connerohnesorge connerohnesorge commented May 10, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a debug handler for inspecting Tailwind CSS classes (not intended for production use).
    • Added a simple example application with an HTTP server, graceful shutdown, and debug logging.
    • Provided a new HTML template with a styled layout, navigation bar, and footer using Tailwind CSS.
  • Chores

    • Added Go module definition for the example application.
    • Updated templating code to use a CDN for Tailwind CSS and removed a custom JavaScript script.

@coderabbitai
Copy link

coderabbitai bot commented May 10, 2025

Walkthrough

The changes introduce a new debug handler in the twerge package for Tailwind CSS class debugging, along with a new example application under examples/simple-debug. This example includes a Go module, a main server application with graceful shutdown and logging, and a templated HTML view using Tailwind CSS via CDN. The templating code reflects updates to error paths and resource inclusion.

Changes

File(s) Change Summary
debug.go Added DebugHandler type with thread-safe cache, implementing methods for debugging Tailwind CSS classes.
examples/simple-debug/go.mod Added Go module definition specifying Go version and dependency on github.com/a-h/templ.
examples/simple-debug/main.go Added example HTTP server application with structured logging, graceful shutdown, and route setup.
examples/simple-debug/views/view.templ Added new HTML template using Tailwind CSS via CDN and dynamic class application with twerge.It.
examples/simple-debug/views/view_templ.go Updated generated template code: Tailwind loaded via CDN, error paths updated, removed custom JS script tag.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HTTP Server
    participant Handler (templ)
    participant twerge.DebugHandler

    User->>HTTP Server: Sends HTTP request
    HTTP Server->>Handler (templ): Route handler invoked
    Handler (templ)->>twerge.DebugHandler: Call It() with class string
    twerge.DebugHandler-->>Handler (templ): Return original class string
    Handler (templ)-->>HTTP Server: Rendered HTML response
    HTTP Server-->>User: Sends HTML response
Loading
sequenceDiagram
    participant OS
    participant Main
    participant HTTP Server

    Main->>HTTP Server: Start server in goroutine
    OS-->>Main: Sends interrupt/termination signal
    Main->>HTTP Server: Initiate graceful shutdown with timeout
    HTTP Server-->>Main: Shutdown complete
    Main->>Main: Wait for goroutines, then exit
Loading

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24e13e1 and 9c70f18.

📒 Files selected for processing (1)
  • debug.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • debug.go
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (10)
examples/simple-debug/input.css (1)

6-7: Development tool identification.

The code is marked as generated by "twerge(devel)", indicating this is from a development version of the tool. For a stable example, consider removing the "(devel)" suffix once the tool reaches a stable release.

debug.go (3)

22-22: Remove the GoImpl comment directive.

The :GoImpl comment appears to be an IDE-specific directive that shouldn't be in the final code. This is likely a development aid that should be removed before committing.

-// :GoImpl d *DebugHandler twerge.Handler

27-28: Fix documentation reference from Generator to DebugHandler.

The comment incorrectly refers to a "Generator" when it should reference the "DebugHandler" for consistency.

-// Cache returns the cache of the [Generator].
+// Cache returns the cache of the [DebugHandler].

30-30: Fix documentation reference from Generator to DebugHandler.

The comment incorrectly refers to a "Generator" when it should reference the "DebugHandler" for consistency.

-// SetCache sets the cache of the [Generator].
+// SetCache sets the cache of the [DebugHandler].
examples/simple-debug/gen.go (1)

51-63: Refactor timing code to avoid duplication.

The timing code in runTailwind() duplicates logic already present in the main function. Consider extracting this to a helper function.

+func timed(name string, fn func() error) error {
+	start := time.Now()
+	defer func() {
+		elapsed := time.Since(start)
+		fmt.Printf("(%s) Done in %s.\n", name, elapsed)
+	}()
+	return fn()
+}

 func runTailwind() {
-	start := time.Now()
-	defer func() {
-		elapsed := time.Since(start)
-		fmt.Printf("(tailwind) Done in %s.\n", elapsed)
-	}()
 	cmd := exec.Command("tailwindcss", "-i", "input.css", "-o", "_static/dist/styles.css")
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
-	if err := cmd.Run(); err != nil {
-		panic(err)
-	}
+	return timed("tailwind", func() error {
+		return cmd.Run()
+	})
 }
examples/simple-debug/views/view.templ (2)

33-35: Improve placeholder content comment.

The "Content" placeholder text could be confusing to users. Consider making it clearer that this is where dynamic content should be inserted.

-				<!-- Page content will be inserted here -->
-				Content
+				<!-- 
+				Page content will be inserted here.
+				Replace this placeholder with dynamic content from your application.
+				-->
+				<div class={ twerge.It("p-4 bg-white rounded shadow") }>
+					<h2 class={ twerge.It("text-xl font-bold") }>Welcome to Stellar</h2>
+					<p class={ twerge.It("mt-2") }>This is a placeholder. Replace with your content.</p>
+				</div>

52-52: Update copyright year.

The copyright year is set to 1999, which seems outdated. Consider updating it to a more recent year or using dynamic generation.

-						&copy; 1999 stellar. All rights reserved.
+						&copy; 2023 stellar. All rights reserved.
examples/simple-debug/main.go (3)

43-45: Remove unused parameter.

The function signature includes an unused parameter _ func(string) string. Consider removing it if it's not needed.

-func Run(
-	ctx context.Context,
-	_ func(string) string,
-) error {
+func Run(ctx context.Context) error {

And update the call in main:

-	if err := Run(context.Background(), os.Getenv); err != nil {
+	if err := Run(context.Background()); err != nil {

36-39: Remove unnecessary error return in AddRoutes.

The AddRoutes function always returns nil, making the error return unnecessary. Either implement error handling or simplify the function.

-func AddRoutes(
-	mux *http.ServeMux,
-) error {
-	mux.Handle("/{$}", templ.Handler(views.View()))
-	return nil
+func AddRoutes(mux *http.ServeMux) {
+	mux.Handle("/", templ.Handler(views.View()))
+}

And update the call:

-	err := AddRoutes(mux)
-	if err != nil {
-		slog.Error("error adding routes", slog.String("error", err.Error()))
-		log.Fatal(err)
-	}
+	AddRoutes(mux)

64-64: Add comment explaining debug handler usage.

Line 64 sets the default handler to a debug handler without explanation. Add a comment indicating this is for demonstration purposes.

+	// Use the DebugHandler for this example. Not recommended for production use.
 	twerge.SetDefault(twerge.New(twerge.NewDebugHandler()))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 643bab3 and 24e13e1.

⛔ Files ignored due to path filters (2)
  • examples/simple-debug/go.sum is excluded by !**/*.sum
  • flake.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • debug.go (1 hunks)
  • examples/simple-debug/gen.go (1 hunks)
  • examples/simple-debug/go.mod (1 hunks)
  • examples/simple-debug/input.css (1 hunks)
  • examples/simple-debug/main.go (1 hunks)
  • examples/simple-debug/tailwind.config.js (1 hunks)
  • examples/simple-debug/views/view.templ (1 hunks)
  • examples/simple-debug/views/view_templ.go (24 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
debug.go (1)
twerge.go (2)
  • CacheValue (17-26)
  • It (55-57)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: checks
🔇 Additional comments (8)
examples/simple-debug/go.mod (1)

1-5: Module configuration looks good.

The module is properly configured with a clear path and dependency on the templ package. The Go version specified (1.24.1) is very recent, which is fine for an example but might limit compatibility with older Go installations.

examples/simple-debug/tailwind.config.js (1)

1-8: Minimal but sufficient Tailwind configuration.

The configuration is intentionally minimal, focusing only on the content source without any theme extensions or plugins, which is appropriate for a simple debug example.

examples/simple-debug/views/view_templ.go (4)

3-3: Version update to v0.3.865.

The templ version has been updated from v0.3.850 to v0.3.865, which is a good practice to keep dependencies current.


34-34: Using Tailwind CSS from CDN.

The template now uses Tailwind CSS from a CDN instead of a local stylesheet, which simplifies the example setup. This is appropriate for a debug example, though be aware that using CDN resources introduces external dependencies.


50-50: Updated error file path references.

All error return statements now correctly reference views/view.templ instead of the previous path, which improves error reporting in this example.

Also applies to: 72-72, 94-94, 116-116, 138-138, 160-160, 182-182, 204-204, 226-226, 248-248, 270-270, 292-292, 314-314, 336-336, 358-358, 380-380, 402-402, 424-424, 446-446, 468-468, 490-490, 512-512


518-518: Simplified footer HTML structure.

The footer's closing HTML has been simplified by removing the inclusion of a custom JavaScript script tag that was previously present. This keeps the example focused on the CSS debugging aspects.

examples/simple-debug/input.css (2)

1-4: Standard Tailwind CSS imports.

The file properly imports Tailwind's base, components, and utilities layers, which is the recommended setup.


5-98: Generated utility classes with clear structure.

The file contains generated utility classes (.tw-0 through .tw-17) with each class including a comment that shows the original Tailwind classes it was generated from. This approach:

  1. Makes the generation process transparent
  2. Provides a clear mapping between numeric IDs and their component classes
  3. Uses standard Tailwind @apply directive for composition

This structure supports the debug example's purpose of demonstrating Tailwind class handling.

@@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./classes/classes.html"],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify content path for Tailwind scanning.

The content path ./classes/classes.html seems very specific. Make sure this file exists in your project structure and contains all the Tailwind classes you need to be included in the generated CSS.


🏁 Script executed:

#!/bin/bash
# Check if the classes.html file exists at the expected location
if [ -f "./examples/simple-debug/classes/classes.html" ]; then
  echo "✅ File exists: ./examples/simple-debug/classes/classes.html"
  echo "File content:"
  cat "./examples/simple-debug/classes/classes.html"
else
  echo "❌ File not found: ./examples/simple-debug/classes/classes.html"
  # Check if the directory exists
  if [ -d "./examples/simple-debug/classes" ]; then
    echo "Directory exists, but file is missing"
    echo "Files in directory:"
    ls -la "./examples/simple-debug/classes"
  else
    echo "Directory does not exist"
  fi
fi

Length of output: 296


Incorrect content path: missing classes/classes.html in examples/simple-debug

The Tailwind config’s content entry on line 3 points to ./classes/classes.html, but that file (and directory) does not exist under examples/simple-debug. Update this path to match where your HTML files actually live. For example:

• If your HTML is directly under the example root:

content: ["./examples/simple-debug/*.html"],

• If it lives in a public folder:

content: ["./examples/simple-debug/public/**/*.html"],

Locations to update:

  • examples/simple-debug/tailwind.config.js:3
🤖 Prompt for AI Agents (early access)
In examples/simple-debug/tailwind.config.js at line 3, the content path
"./classes/classes.html" is incorrect because the file and directory do not
exist. Update the content array to point to the actual location of your HTML
files, such as "./examples/simple-debug/*.html" if the HTML files are in the
example root, or adjust the path accordingly if they are in a subfolder like
"public". This ensures Tailwind scans the correct files for class usage.

elapsed := time.Since(start)
fmt.Printf("(tailwind) Done in %s.\n", elapsed)
}()
cmd := exec.Command("tailwindcss", "-i", "input.css", "-o", "_static/dist/styles.css")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for missing Tailwind CLI.

The code assumes that the tailwindcss command is available in the PATH, but doesn't provide helpful error messages if it's not installed.

#!/bin/bash
# Check if tailwindcss is installed
which tailwindcss > /dev/null 2>&1
if [ $? -ne 0 ]; then
  echo "Error: tailwindcss not found in PATH"
  echo "Please install it with: npm install -g tailwindcss"
  exit 1
fi
🤖 Prompt for AI Agents (early access)
In examples/simple-debug/gen.go at line 57, the code runs the tailwindcss
command without checking if it is installed, which can cause unclear errors. Add
a check before executing the command to verify if the tailwindcss CLI is
available in the system PATH. If it is missing, return or log a clear error
message instructing the user to install tailwindcss, preventing the command from
running and failing silently.

"time"

"github.com/conneroisu/twerge"
"github.com/conneroisu/twerge/examples/simple/views"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update import path to match directory structure.

The import path references examples/simple/views but the directory path is examples/simple-debug/views, which may cause compilation errors.

-	"github.com/conneroisu/twerge/examples/simple/views"
+	"github.com/conneroisu/twerge/examples/simple-debug/views"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"github.com/conneroisu/twerge/examples/simple/views"
- "github.com/conneroisu/twerge/examples/simple/views"
+ "github.com/conneroisu/twerge/examples/simple-debug/views"
🤖 Prompt for AI Agents (early access)
In examples/simple-debug/gen.go at line 14, the import path incorrectly
references "examples/simple/views" instead of "examples/simple-debug/views".
Update the import statement to use the correct path
"github.com/conneroisu/twerge/examples/simple-debug/views" to match the
directory structure and avoid compilation errors.

<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>stellar</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Clarify CDN usage vs local CSS builds.

The template uses Tailwind CSS from a CDN, but the project also generates local CSS files via the build script. This inconsistency might lead to style differences between development and production.

Consider one of these approaches:

  1. Use the locally generated CSS file for consistency with the build process
  2. Add a comment explaining why the CDN is used instead of the local build
-			<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
+			<!-- For development purposes only - use local CSS in production -->
+			<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
+			<!-- Uncomment for production: <link rel="stylesheet" href="/_static/dist/styles.css"> -->

Run this script to check if the build process is generating CSS files correctly:


🏁 Script executed:

#!/bin/bash
# Check if the local CSS file exists and if CDN is still referenced
if [ -f "_static/dist/styles.css" ]; then
  echo "Local CSS file exists:"
  ls -la "_static/dist/styles.css"
  echo "File size: $(du -h _static/dist/styles.css | cut -f1)"
  
  # Check first few lines of the CSS file
  echo "CSS file preview:"
  head -n 5 "_static/dist/styles.css"
else
  echo "Local CSS file does not exist. Build process may be incomplete."
fi

Length of output: 173


Fix local CSS build or clarify CDN usage

The check shows that _static/dist/styles.css doesn’t exist, so the template always falls back to the CDN. Please address this by choosing one of the following:

• Implement or correct the build step so that _static/dist/styles.css is generated, and then load it in production:

-   <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
+   <!-- In production, load the locally built CSS -->
+   <link rel="stylesheet" href="/_static/dist/styles.css">

• If you intend to rely on the CDN in all environments, add a clarifying comment:

<!-- Tailwind CSS is loaded via CDN because no local build is configured -->
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
🤖 Prompt for AI Agents (early access)
In examples/simple-debug/views/view.templ at line 12, the template loads
Tailwind CSS from a CDN while the local CSS file _static/dist/styles.css does
not exist, causing inconsistency. Fix this by either implementing or correcting
the build process to generate the local CSS file and update the template to load
it instead of the CDN, or if relying on the CDN is intentional, add a comment
above the script tag explaining that Tailwind CSS is loaded via CDN because no
local build is configured.

func AddRoutes(
mux *http.ServeMux,
) error {
mux.Handle("/{$}", templ.Handler(views.View()))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix route pattern syntax.

The route pattern "/{$}" appears to be invalid. If you intend to match the root path, use "/" instead. If you need to match all paths, use "/" and handle sub-paths in your handler.

-	mux.Handle("/{$}", templ.Handler(views.View()))
+	mux.Handle("/", templ.Handler(views.View()))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mux.Handle("/{$}", templ.Handler(views.View()))
mux.Handle("/", templ.Handler(views.View()))
🤖 Prompt for AI Agents (early access)
In examples/simple-debug/main.go at line 37, the route pattern "/{$}" is
invalid. Replace it with "/" to correctly match the root path. If you want to
handle all sub-paths, keep the pattern as "/" and implement the necessary logic
inside the handler to manage sub-paths.

Comment on lines +113 to +164
select {
case err := <-serverErrors:
return fmt.Errorf("server error: %w", err)
case <-innerCtx.Done():
// Signal received, initiate graceful shutdown
slog.Info("shutdown signal received, shutting down server...")

// Create shutdown context with timeout
shutdownCtx, cancel := context.WithTimeout(
context.Background(), // Use a fresh context for shutdown
shutdownTimeout,
)
defer cancel()

// Attempt graceful shutdown
err := httpServer.Shutdown(shutdownCtx)
if err != nil {
slog.Error("error during server shutdown",
slog.String("error", err.Error()),
slog.Duration("timeout", shutdownTimeout),
)
}

// Wait for all goroutines to finish
slog.Info("waiting for server shutdown to complete")
wg.Wait()
slog.Info("server shutdown completed")
return nil
case <-ctx.Done():
// Parent context cancelled
slog.Info("parent context cancelled, shutting down...")

// Create shutdown context with timeout
shutdownCtx, cancel := context.WithTimeout(
context.Background(), // Use a fresh context for shutdown
shutdownTimeout,
)
defer cancel()

// Attempt graceful shutdown
err := httpServer.Shutdown(shutdownCtx)
if err != nil {
slog.Error("error during server shutdown",
slog.String("error", err.Error()),
slog.Duration("timeout", shutdownTimeout),
)
}

// Wait for all goroutines to finish
wg.Wait()
return nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor duplicate shutdown logic.

There's significant code duplication between the signal-based shutdown and context-based shutdown paths. Consider refactoring this to a separate function.

+// performGracefulShutdown attempts to gracefully shut down the HTTP server
+func performGracefulShutdown(httpServer *http.Server, wg *sync.WaitGroup) {
+	// Create shutdown context with timeout
+	shutdownCtx, cancel := context.WithTimeout(
+		context.Background(), // Use a fresh context for shutdown
+		shutdownTimeout,
+	)
+	defer cancel()
+
+	// Attempt graceful shutdown
+	err := httpServer.Shutdown(shutdownCtx)
+	if err != nil {
+		slog.Error("error during server shutdown",
+			slog.String("error", err.Error()),
+			slog.Duration("timeout", shutdownTimeout),
+		)
+	}
+
+	// Wait for all goroutines to finish
+	slog.Info("waiting for server shutdown to complete")
+	wg.Wait()
+	slog.Info("server shutdown completed")
+}

 	// Wait for either server error or shutdown signal
 	select {
 	case err := <-serverErrors:
 		return fmt.Errorf("server error: %w", err)
 	case <-innerCtx.Done():
 		// Signal received, initiate graceful shutdown
 		slog.Info("shutdown signal received, shutting down server...")
-
-		// Create shutdown context with timeout
-		shutdownCtx, cancel := context.WithTimeout(
-			context.Background(), // Use a fresh context for shutdown
-			shutdownTimeout,
-		)
-		defer cancel()
-
-		// Attempt graceful shutdown
-		err := httpServer.Shutdown(shutdownCtx)
-		if err != nil {
-			slog.Error("error during server shutdown",
-				slog.String("error", err.Error()),
-				slog.Duration("timeout", shutdownTimeout),
-			)
-		}
-
-		// Wait for all goroutines to finish
-		slog.Info("waiting for server shutdown to complete")
-		wg.Wait()
-		slog.Info("server shutdown completed")
+		performGracefulShutdown(httpServer, &wg)
 		return nil
 	case <-ctx.Done():
 		// Parent context cancelled
 		slog.Info("parent context cancelled, shutting down...")
-
-		// Create shutdown context with timeout
-		shutdownCtx, cancel := context.WithTimeout(
-			context.Background(), // Use a fresh context for shutdown
-			shutdownTimeout,
-		)
-		defer cancel()
-
-		// Attempt graceful shutdown
-		err := httpServer.Shutdown(shutdownCtx)
-		if err != nil {
-			slog.Error("error during server shutdown",
-				slog.String("error", err.Error()),
-				slog.Duration("timeout", shutdownTimeout),
-			)
-		}
-
-		// Wait for all goroutines to finish
-		wg.Wait()
+		performGracefulShutdown(httpServer, &wg)
 		return nil
 	}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents (early access)
In examples/simple-debug/main.go between lines 113 and 164, the shutdown logic
for both signal-based and context-based cases is duplicated. Refactor by
extracting the shutdown steps—creating a shutdown context with timeout, calling
httpServer.Shutdown, logging errors if any, and waiting for goroutines—into a
separate function. Then call this function from both select cases to eliminate
duplication and improve code clarity.

@connerohnesorge connerohnesorge merged commit 9920fd7 into main May 18, 2025
8 checks passed
@connerohnesorge connerohnesorge deleted the debug-handler branch May 18, 2025 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant