fix: critical hooks bug - preserve all Claude Code settings
BREAKING BUG FIX: Hook installation was destroying existing Claude Code settings - Fix critical data loss in --install-hook and --uninstall-hook commands - Change ClaudeSettings to preserve ALL existing JSON settings via Data map - Add automatic timestamped backup creation before any settings modification - Implement surgical hook installation that adds to existing config structure - Implement surgical hook removal that only removes shush hooks - Prevent loss of user settings like includeCoAuthoredBy, themes, etc. - Add comprehensive error handling for malformed settings files This was a critical data loss bug that could destroy users' Claude Code configurations. All existing settings are now preserved during hook operations. Fixes: Users losing their Claude Code settings during hook installation Added: Automatic backups with timestamp (settings.json.backup.TIMESTAMP) Improved: Hook management now respects existing PostToolUse configurations
This commit is contained in:
@@ -17,6 +17,24 @@ Download the binary for your platform from the [releases page](https://github.co
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### 🤖 Claude Code Integration (Most Popular!)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install automatic comment cleanup after Claude modifies files
|
||||||
|
shush --install-hook # User-wide (all projects)
|
||||||
|
shush --install-hook -s project # Project-specific only
|
||||||
|
|
||||||
|
# Manage hooks
|
||||||
|
shush --hook-status # Check installation status
|
||||||
|
shush --list-hooks # Show all configured hooks
|
||||||
|
shush --uninstall-hook # Remove hooks
|
||||||
|
|
||||||
|
# Once installed, comments are automatically cleaned whenever Claude Code
|
||||||
|
# uses Write, Edit, or MultiEdit tools! Respects your .shush.toml config.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 📂 Basic File Operations
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Remove all comments from a file
|
# Remove all comments from a file
|
||||||
shush file.py
|
shush file.py
|
||||||
@@ -44,18 +62,23 @@ shush script.py --preserve-lines
|
|||||||
|
|
||||||
# Verbose output
|
# Verbose output
|
||||||
shush app.go --verbose
|
shush app.go --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔀 Git-Aware Processing
|
||||||
|
|
||||||
|
```bash
|
||||||
# Git-aware processing (only process changed lines)
|
# Git-aware processing (only process changed lines)
|
||||||
shush --staged # Clean comments from staged changes
|
shush --staged # Clean comments from staged changes
|
||||||
shush --unstaged # Clean comments from unstaged changes
|
shush --unstaged # Clean comments from unstaged changes
|
||||||
shush --changes-only # Clean comments from all changes
|
shush --changes-only # Clean comments from all changes
|
||||||
|
```
|
||||||
|
|
||||||
# Configuration and hooks management
|
### ⚙️ Configuration Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Configuration management
|
||||||
shush --create-config # Create .shush.toml configuration
|
shush --create-config # Create .shush.toml configuration
|
||||||
shush --config # Show current configuration
|
shush --config # Show current configuration
|
||||||
shush --install-hook # Install Claude Code hooks (user-wide)
|
|
||||||
shush --install-hook -s project # Install project-specific hooks
|
|
||||||
shush --hook-status # Check hook installation status
|
|
||||||
|
|
||||||
# Combine flags for complex operations
|
# Combine flags for complex operations
|
||||||
shush src/ --recursive --inline --dry-run --verbose
|
shush src/ --recursive --inline --dry-run --verbose
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/carlosarraes/shush/internal/types"
|
"github.com/carlosarraes/shush/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "0.2.2"
|
var version = "0.2.3"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var cliArgs types.CLI
|
var cliArgs types.CLI
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ func InstallHooks(scope string) error {
|
|||||||
return fmt.Errorf("failed to get settings path: %w", err)
|
return fmt.Errorf("failed to get settings path: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := hooks.CreateBackup(path); err != nil {
|
||||||
|
return fmt.Errorf("failed to create backup: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
settings, err := hooks.LoadSettings(path)
|
settings, err := hooks.LoadSettings(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to load settings: %w", err)
|
return fmt.Errorf("failed to load settings: %w", err)
|
||||||
@@ -109,6 +113,10 @@ func UninstallHooks(scope string) error {
|
|||||||
return fmt.Errorf("shush hook not found for %s scope at %s", scopeName, path)
|
return fmt.Errorf("shush hook not found for %s scope at %s", scopeName, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := hooks.CreateBackup(path); err != nil {
|
||||||
|
return fmt.Errorf("failed to create backup: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := exec.LookPath("jq"); err == nil {
|
if _, err := exec.LookPath("jq"); err == nil {
|
||||||
if err := uninstallWithJQ(path, scopeName); err == nil {
|
if err := uninstallWithJQ(path, scopeName); err == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -222,19 +230,56 @@ func listHooksForPath(path string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(settings.Hooks) == 0 {
|
hooksData, exists := settings.Data["hooks"]
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("no hooks")
|
||||||
|
}
|
||||||
|
|
||||||
|
hooksMap, ok := hooksData.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid hooks format")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hooksMap) == 0 {
|
||||||
return fmt.Errorf("no hooks")
|
return fmt.Errorf("no hooks")
|
||||||
}
|
}
|
||||||
|
|
||||||
hasShush := hooks.HasShushHook(settings)
|
hasShush := hooks.HasShushHook(settings)
|
||||||
|
|
||||||
for event, configs := range settings.Hooks {
|
for event, eventData := range hooksMap {
|
||||||
for _, config := range configs {
|
configs, ok := eventData.([]interface{})
|
||||||
for _, hook := range config.Hooks {
|
if !ok {
|
||||||
if hook.Command == "shush --changes-only" {
|
continue
|
||||||
fmt.Printf(" ✓ shush --changes-only (%s: %s)\n", event, config.Matcher)
|
}
|
||||||
|
|
||||||
|
for _, configData := range configs {
|
||||||
|
config, ok := configData.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher, _ := config["matcher"].(string)
|
||||||
|
hooksData, exists := config["hooks"]
|
||||||
|
if !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks, ok := hooksData.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hookData := range hooks {
|
||||||
|
hook, ok := hookData.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
command, _ := hook["command"].(string)
|
||||||
|
if command == "shush --changes-only" {
|
||||||
|
fmt.Printf(" ✓ shush --changes-only (%s: %s)\n", event, matcher)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(" ✓ %s (%s: %s)\n", hook.Command, event, config.Matcher)
|
fmt.Printf(" ✓ %s (%s: %s)\n", command, event, matcher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+217
-53
@@ -7,6 +7,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HookScope int
|
type HookScope int
|
||||||
@@ -17,7 +18,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ClaudeSettings struct {
|
type ClaudeSettings struct {
|
||||||
Hooks map[string][]EventConfig `json:"hooks,omitempty"`
|
Data map[string]interface{} `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventConfig struct {
|
type EventConfig struct {
|
||||||
@@ -58,28 +59,50 @@ func EnsureSettingsDirectory(settingsPath string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateBackup(settingsPath string) error {
|
||||||
|
if _, err := os.Stat(settingsPath); os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp := time.Now().Unix()
|
||||||
|
backupPath := fmt.Sprintf("%s.backup.%d", settingsPath, timestamp)
|
||||||
|
|
||||||
|
data, err := os.ReadFile(settingsPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read original settings for backup: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(backupPath, data, 0644); err != nil {
|
||||||
|
return fmt.Errorf("failed to create backup at %s: %w", backupPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("✓ Backup created: %s\n", backupPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func LoadSettings(path string) (*ClaudeSettings, error) {
|
func LoadSettings(path string) (*ClaudeSettings, error) {
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
|
||||||
return &ClaudeSettings{
|
return &ClaudeSettings{
|
||||||
Hooks: make(map[string][]EventConfig),
|
Data: make(map[string]interface{}),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to read settings file %s: %w", path, err)
|
return nil, fmt.Errorf("failed to read settings file %s: %w", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings ClaudeSettings
|
var allSettings map[string]interface{}
|
||||||
if err := json.Unmarshal(data, &settings); err != nil {
|
if err := json.Unmarshal(data, &allSettings); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse settings file %s: %w", path, err)
|
return nil, fmt.Errorf("failed to parse settings file %s: %w", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.Hooks == nil {
|
if allSettings == nil {
|
||||||
settings.Hooks = make(map[string][]EventConfig)
|
allSettings = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
return &settings, nil
|
return &ClaudeSettings{
|
||||||
|
Data: allSettings,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveSettings(path string, settings *ClaudeSettings) error {
|
func SaveSettings(path string, settings *ClaudeSettings) error {
|
||||||
@@ -87,7 +110,7 @@ func SaveSettings(path string, settings *ClaudeSettings) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.MarshalIndent(settings, "", " ")
|
data, err := json.MarshalIndent(settings.Data, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to marshal settings: %w", err)
|
return fmt.Errorf("failed to marshal settings: %w", err)
|
||||||
}
|
}
|
||||||
@@ -115,18 +138,55 @@ func CreateShushEventConfig() EventConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HasShushHook(settings *ClaudeSettings) bool {
|
func HasShushHook(settings *ClaudeSettings) bool {
|
||||||
if settings.Hooks == nil {
|
hooksData, exists := settings.Data["hooks"]
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
postToolUseConfigs, exists := settings.Hooks["PostToolUse"]
|
|
||||||
if !exists {
|
if !exists {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, config := range postToolUseConfigs {
|
hooksMap, ok := hooksData.(map[string]interface{})
|
||||||
for _, hook := range config.Hooks {
|
if !ok {
|
||||||
if strings.Contains(hook.Command, "shush --changes-only") {
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
postToolUseData, exists := hooksMap["PostToolUse"]
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
postToolUseConfigs, ok := postToolUseData.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, configData := range postToolUseConfigs {
|
||||||
|
config, ok := configData.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hooksData, exists := config["hooks"]
|
||||||
|
if !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks, ok := hooksData.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hookData := range hooks {
|
||||||
|
hook, ok := hookData.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
command, exists := hook["command"]
|
||||||
|
if !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
commandStr, ok := command.(string)
|
||||||
|
if ok && strings.Contains(commandStr, "shush --changes-only") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,71 +196,175 @@ func HasShushHook(settings *ClaudeSettings) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddShushHook(settings *ClaudeSettings) error {
|
func AddShushHook(settings *ClaudeSettings) error {
|
||||||
if settings.Hooks == nil {
|
|
||||||
settings.Hooks = make(map[string][]EventConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
if HasShushHook(settings) {
|
if HasShushHook(settings) {
|
||||||
return errors.New("shush hook already installed")
|
return errors.New("shush hook already installed")
|
||||||
}
|
}
|
||||||
|
|
||||||
postToolUseConfigs, exists := settings.Hooks["PostToolUse"]
|
if _, exists := settings.Data["hooks"]; !exists {
|
||||||
if !exists {
|
settings.Data["hooks"] = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
settings.Hooks["PostToolUse"] = []EventConfig{CreateShushEventConfig()}
|
hooksMap := settings.Data["hooks"].(map[string]interface{})
|
||||||
|
|
||||||
|
shushHook := map[string]interface{}{
|
||||||
|
"type": "command",
|
||||||
|
"command": "shush --changes-only",
|
||||||
|
"timeout": 30,
|
||||||
|
}
|
||||||
|
|
||||||
|
if postToolUseData, exists := hooksMap["PostToolUse"]; exists {
|
||||||
|
postToolUseConfigs, ok := postToolUseData.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid PostToolUse format in existing settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, configData := range postToolUseConfigs {
|
||||||
|
config, ok := configData.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher, exists := config["matcher"]
|
||||||
|
if !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
matcherStr, ok := matcher.(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if matcherStr == "Write|Edit|MultiEdit" {
|
||||||
|
hooksData, exists := config["hooks"]
|
||||||
|
if !exists {
|
||||||
|
config["hooks"] = []interface{}{shushHook}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks, ok := hooksData.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid hooks format in existing settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
config["hooks"] = append(hooks, shushHook)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig := map[string]interface{}{
|
||||||
|
"matcher": "Write|Edit|MultiEdit",
|
||||||
|
"hooks": []interface{}{shushHook},
|
||||||
|
}
|
||||||
|
|
||||||
|
hooksMap["PostToolUse"] = append(postToolUseConfigs, newConfig)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, config := range postToolUseConfigs {
|
newConfig := map[string]interface{}{
|
||||||
if config.Matcher == "Write|Edit|MultiEdit" || config.Matcher == "" {
|
"matcher": "Write|Edit|MultiEdit",
|
||||||
|
"hooks": []interface{}{shushHook},
|
||||||
postToolUseConfigs[i].Hooks = append(config.Hooks, CreateShushHookEntry())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.Hooks["PostToolUse"] = append(postToolUseConfigs, CreateShushEventConfig())
|
hooksMap["PostToolUse"] = []interface{}{newConfig}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveShushHook(settings *ClaudeSettings) error {
|
func RemoveShushHook(settings *ClaudeSettings) error {
|
||||||
if settings.Hooks == nil {
|
if !HasShushHook(settings) {
|
||||||
return errors.New("no hooks configuration found")
|
|
||||||
}
|
|
||||||
|
|
||||||
postToolUseConfigs, exists := settings.Hooks["PostToolUse"]
|
|
||||||
if !exists {
|
|
||||||
return errors.New("shush hook not found")
|
return errors.New("shush hook not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hooksData, exists := settings.Data["hooks"]
|
||||||
|
if !exists {
|
||||||
|
return errors.New("no hooks configuration found")
|
||||||
|
}
|
||||||
|
|
||||||
|
hooksMap, ok := hooksData.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid hooks format")
|
||||||
|
}
|
||||||
|
|
||||||
|
postToolUseData, exists := hooksMap["PostToolUse"]
|
||||||
|
if !exists {
|
||||||
|
return errors.New("no PostToolUse hooks found")
|
||||||
|
}
|
||||||
|
|
||||||
|
postToolUseConfigs, ok := postToolUseData.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid PostToolUse format")
|
||||||
|
}
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for i, config := range postToolUseConfigs {
|
var newConfigs []interface{}
|
||||||
newHooks := make([]HookEntry, 0, len(config.Hooks))
|
|
||||||
for _, hook := range config.Hooks {
|
for _, configData := range postToolUseConfigs {
|
||||||
if !strings.Contains(hook.Command, "shush --changes-only") {
|
config, ok := configData.(map[string]interface{})
|
||||||
newHooks = append(newHooks, hook)
|
if !ok {
|
||||||
} else {
|
newConfigs = append(newConfigs, configData)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hooksData, exists := config["hooks"]
|
||||||
|
if !exists {
|
||||||
|
newConfigs = append(newConfigs, configData)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks, ok := hooksData.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
newConfigs = append(newConfigs, configData)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var newHooks []interface{}
|
||||||
|
for _, hookData := range hooks {
|
||||||
|
hook, ok := hookData.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
newHooks = append(newHooks, hookData)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
command, exists := hook["command"]
|
||||||
|
if !exists {
|
||||||
|
newHooks = append(newHooks, hookData)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
commandStr, ok := command.(string)
|
||||||
|
if !ok {
|
||||||
|
newHooks = append(newHooks, hookData)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(commandStr, "shush --changes-only") {
|
||||||
found = true
|
found = true
|
||||||
|
} else {
|
||||||
|
newHooks = append(newHooks, hookData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
postToolUseConfigs[i].Hooks = newHooks
|
|
||||||
|
if len(newHooks) > 0 {
|
||||||
|
newConfig := make(map[string]interface{})
|
||||||
|
for k, v := range config {
|
||||||
|
newConfig[k] = v
|
||||||
|
}
|
||||||
|
newConfig["hooks"] = newHooks
|
||||||
|
newConfigs = append(newConfigs, newConfig)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
return errors.New("shush hook not found")
|
return errors.New("shush hook not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredConfigs := make([]EventConfig, 0, len(postToolUseConfigs))
|
if len(newConfigs) == 0 {
|
||||||
for _, config := range postToolUseConfigs {
|
delete(hooksMap, "PostToolUse")
|
||||||
if len(config.Hooks) > 0 {
|
} else {
|
||||||
filteredConfigs = append(filteredConfigs, config)
|
hooksMap["PostToolUse"] = newConfigs
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(filteredConfigs) == 0 {
|
if len(hooksMap) == 0 {
|
||||||
delete(settings.Hooks, "PostToolUse")
|
delete(settings.Data, "hooks")
|
||||||
} else {
|
|
||||||
settings.Hooks["PostToolUse"] = filteredConfigs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user