Skip to content

Conversation

@dogancanbakir
Copy link
Member

@dogancanbakir dogancanbakir commented Jan 6, 2026

Summary

  • Add --tags/-t flag to start command for starting multiple templates by tag
  • Add --tags/-t flag to stop command for stopping multiple templates by tag
  • Add GetByTags() helper function for retrieving templates matching given tags

Usage

# Start all templates with sqli tag
vt start --tags sqli

# Start all templates with sqli OR xss tags
vt start --tags sqli,xss

# Stop all templates with web tag
vt stop -t web

Features

  • OR logic - Multiple comma-separated tags match templates with ANY of the tags
  • Case-insensitive - --tags SQLI matches templates with "sqli" tag
  • Substring matching - --tags owa matches templates with "owasp" tag
  • Mutually exclusive - --id and --tags cannot be used together
  • Error resilience - Continues on failure, reports failed templates at end

Test plan

  • All existing tests pass
  • New tests for GetByTags() function
  • New tests for templateMatchesTags() helper
  • Linter passes

Closes #101

Summary by CodeRabbit

  • New Features
    • Templates can now be started or stopped by tags in addition to ID. Use --id to target a specific template or --tags to batch-operate on multiple templates matching the provided tags. These options are mutually exclusive, and at least one is required.

✏️ Tip: You can customize this high-level summary in your review settings.

Add the ability to start and stop multiple templates by tag filter.

Changes:
- Add GetByTags() function to retrieve templates matching given tags
- Add --tags/-t flag to start command for starting multiple templates
- Add --tags/-t flag to stop command for stopping multiple templates
- Tags support case-insensitive and substring matching
- --id and --tags are mutually exclusive
- Continue on failure and report failed templates at end

Usage:
  vt start --tags sqli,xss    # Start all templates with sqli OR xss tags
  vt stop --tags web          # Stop all templates with web tag

Closes #101
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

Walkthrough

This PR adds tag-based template selection to the start and stop commands, complementing the existing ID-based approach. It introduces a new GetByTags function in the template package to retrieve templates matching provided tags, with comprehensive test coverage for both the CLI commands and template matching logic.

Changes

Cohort / File(s) Summary
CLI command updates
internal/cli/start.go, internal/cli/stop.go
Added --tags flag to enable starting/stopping multiple templates by tag. Enforced mutual exclusivity with --id flag and required at least one. Refactored to conditionally fetch templates by ID or tags, iterate over multiple templates, collect failures, and log per-template outcomes. Introduced startTemplate helper in start.go to encapsulate single-template logic.
Template package
pkg/template/template.go
Added public GetByTags function to retrieve templates matching any provided tags with case-insensitive and substring matching support. Added unexported templateMatchesTags helper for tag comparison logic. Includes validation for non-empty tags and error handling when no matches found.
Test coverage
pkg/template/template_test.go
Added TestGetByTags covering single/multiple tags, case-insensitive and substring matching, no-match error cases, and edge cases. Added TestTemplateMatchesTags validating tag comparison behavior across various matching scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • ahsentekd
  • recepgunes1

Poem

🐰 Hop hop, tags now align,
Templates multiply, no more just one line,
From ID to tags, flexibility blooms,
Starting and stopping—--tags—fills the rooms! 🌱

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a --tags flag to start and stop commands, which aligns with the primary focus of the pull request.
Linked Issues check ✅ Passed The pull request addresses the core requirement of #101 by implementing --tags flag for start and stop commands with OR logic, case-insensitive and substring matching, and mutual exclusion with --id.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the --tags feature: template tag matching logic, start/stop command modifications, and comprehensive test coverage for new functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/run-templates-by-tags

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (4)
pkg/template/template.go (1)

223-238: Minor: Redundant exact-match check.

The strings.EqualFold check on line 231 is redundant because the subsequent strings.Contains(strings.ToLower(...), strings.ToLower(...)) on line 232 already covers exact matches. However, the explicit check may improve readability by showing intent, so this is purely optional.

🔎 Optional simplification
 func templateMatchesTags(tmpl *Template, filterTags []string) bool {
 	for _, filterTag := range filterTags {
 		filterTag = strings.TrimSpace(filterTag)
 		if filterTag == "" {
 			continue
 		}
 		for _, templateTag := range tmpl.Info.Tags {
-			if strings.EqualFold(templateTag, filterTag) ||
-				strings.Contains(strings.ToLower(templateTag), strings.ToLower(filterTag)) {
+			if strings.Contains(strings.ToLower(templateTag), strings.ToLower(filterTag)) {
 				return true
 			}
 		}
 	}
 	return false
 }
internal/cli/stop.go (1)

66-83: Consider exiting with non-zero status on partial failures.

When some templates fail to stop, the command logs a warning but exits successfully. For CI/scripting scenarios, a non-zero exit code when len(failed) > 0 would better signal partial failure.

🔎 Proposed change
 			if len(failed) > 0 {
 				log.Warn().Msgf("failed to stop %d templates: %s", len(failed), strings.Join(failed, ", "))
+				log.Fatal().Msgf("completed with %d failures", len(failed))
 			}
 			if stopped > 0 {
 				log.Info().Msgf("successfully stopped %d templates", stopped)
 			}

Alternatively, use os.Exit(1) after logging to allow the success message to print first.

internal/cli/start.go (2)

79-81: Consider exiting with non-zero status on partial failures.

Same concern as in stop.go: when some templates fail to start, the command logs a warning but exits with status 0. This may cause issues in CI/automation scenarios.


99-114: Inconsistent post-install log message format.

In startTemplate (line 107), the log says "Post-installation instructions:" without the template ID, while in the tags-based loop (line 70), it includes the template ID: "Post-installation instructions for %s:". Consider aligning these for consistency.

🔎 Proposed fix
 func (c *CLI) startTemplate(provider interface{ Start(*tmpl.Template) error }, template *tmpl.Template, providerName string) {
 	err := provider.Start(template)
 	if err != nil {
 		log.Fatal().Msgf("%v", err)
 	}

 	if len(template.PostInstall) > 0 {
-		log.Info().Msg("Post-installation instructions:")
+		log.Info().Msgf("Post-installation instructions for %s:", template.ID)
 		for _, instruction := range template.PostInstall {
 			fmt.Printf("  %s\n", instruction)
 		}
 	}

 	log.Info().Msgf("%s template is running on %s", template.ID, providerName)
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db6ccbd and 3dd2eaf.

📒 Files selected for processing (4)
  • internal/cli/start.go
  • internal/cli/stop.go
  • pkg/template/template.go
  • pkg/template/template_test.go
🧰 Additional context used
🧬 Code graph analysis (2)
internal/cli/start.go (3)
pkg/provider/registry/registry.go (1)
  • GetProvider (19-22)
pkg/template/template.go (4)
  • GetByID (192-198)
  • GetByTags (203-221)
  • Info (27-39)
  • Template (17-24)
internal/cli/cli.go (1)
  • CLI (29-32)
internal/cli/stop.go (2)
pkg/provider/registry/registry.go (1)
  • GetProvider (19-22)
pkg/template/template.go (3)
  • GetByID (192-198)
  • Info (27-39)
  • GetByTags (203-221)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (7)
pkg/template/template.go (1)

199-221: LGTM! Well-structured tag-based retrieval.

The implementation correctly:

  • Validates for empty tags input
  • Creates copies to avoid pointer aliasing issues when iterating over map values
  • Returns a clear error when no templates match
pkg/template/template_test.go (2)

34-101: Comprehensive test coverage for GetByTags.

The test cases cover all documented behaviors:

  • Single and multiple tag matching (OR logic)
  • Case-insensitive matching
  • Substring matching
  • Error cases (no matches, empty tags)
  • Whitespace handling

103-128: Good unit tests for templateMatchesTags.

The helper function is well tested with edge cases including empty and whitespace-only filter tags.

internal/cli/stop.go (2)

28-38: LGTM! Clear mutual exclusivity validation.

The validation correctly enforces that exactly one of --id or --tags must be provided.


95-96: LGTM! Flag definition is consistent with start command.

internal/cli/start.go (2)

28-38: LGTM! Consistent with stop command validation.


100-100: The current inline interface is the better approach. The function only uses the Start method, and the existing Provider interface in pkg/provider/provider.go includes four methods (Name(), Start(), Stop(), Status()). Using the full Provider interface would violate the Interface Segregation Principle by coupling the function to methods it doesn't use. The inline interface is cleaner and makes testing/mocking easier.

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.

run templates with tags

2 participants