Skip to content

Commit b6e5cb8

Browse files
feature: add config reset command for Codacy CLI
- Introduced a new `config reset` command to reset Codacy configuration to default or repository-specific settings. - Implemented logic to handle API token validation and ensure required flags are provided when using remote configurations. - Added YAML parsing for CLI mode configuration from `.codacy/cli-config.yaml`. - Updated error handling and user feedback for better clarity during configuration resets.
1 parent 9fd81be commit b6e5cb8

File tree

3 files changed

+138
-22
lines changed

3 files changed

+138
-22
lines changed

cmd/config.go

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,85 @@ import (
1111
"codacy/cli-v2/utils"
1212

1313
"github.com/spf13/cobra"
14+
// Added import for YAML parsing
1415
)
1516

16-
// configUpdateInitFlags holds the flags for the config update command, similar to init command flags.
17-
var configUpdateInitFlags domain.InitFlags
17+
// configResetInitFlags holds the flags for the config reset command.
18+
var configResetInitFlags domain.InitFlags
1819

1920
var configCmd = &cobra.Command{
2021
Use: "config",
2122
Short: "Manage Codacy configuration",
2223
}
2324

24-
var configUpdateCmd = &cobra.Command{
25-
Use: "update",
26-
Short: "Update Codacy configuration, or initialize if it doesn't exist",
27-
Long: "Updates the Codacy configuration files. If .codacy/codacy.yaml does not exist, it runs the initialization process.",
25+
// cliConfigYaml defines the structure for parsing .codacy/cli-config.yaml
26+
type cliConfigYaml struct {
27+
Mode string `yaml:"mode"`
28+
}
29+
30+
var configResetCmd = &cobra.Command{
31+
Use: "reset",
32+
Short: "Reset Codacy configuration to default or repository-specific settings",
33+
Long: "Resets the Codacy configuration files and tool-specific configurations. " +
34+
"This command will overwrite an existing configuration with local default configurations " +
35+
"if no API token is provided (and current mode is not 'remote'). If an API token is provided, it will fetch and apply " +
36+
"repository-specific configurations from the Codacy API, effectively resetting to those.",
2837
Run: func(cmd *cobra.Command, args []string) {
38+
// Get current CLI mode from config
39+
currentCliMode, err := config.Config.GetCliMode()
40+
if err != nil {
41+
// Log the error for debugging purposes
42+
log.Printf("Warning: Could not determine CLI mode from cli-config.yaml: %v. Defaulting to 'local' mode.", err)
43+
// Show a user-facing warning on stdout
44+
fmt.Println("⚠️ Warning: Could not read or parse .codacy/cli-config.yaml. Defaulting to 'local' CLI mode.")
45+
fmt.Println(" You might want to run 'codacy-cli init' or 'codacy-cli config reset --api-token ...' to correctly set up your configuration.")
46+
fmt.Println()
47+
currentCliMode = "local" // Default to local as per existing logic
48+
}
49+
50+
apiTokenFlagProvided := len(configResetInitFlags.ApiToken) > 0
51+
52+
// If current mode is 'remote', prevent resetting to local without explicit API token for a remote reset.
53+
if currentCliMode == "remote" && !apiTokenFlagProvided {
54+
fmt.Println("Error: Your Codacy CLI is currently configured in 'remote' (cloud) mode.")
55+
fmt.Println("To reset your configuration using remote settings, you must provide the --api-token, --provider, --organization, and --repository flags.")
56+
fmt.Println("Running 'config reset' without these flags is not permitted while configured for 'remote' mode.")
57+
fmt.Println("This prevents an accidental switch to a local default configuration.")
58+
fmt.Println()
59+
if errHelp := cmd.Help(); errHelp != nil {
60+
log.Printf("Warning: Failed to display command help: %v\n", errHelp)
61+
}
62+
os.Exit(1)
63+
}
64+
65+
// Validate flags: if API token is provided, other related flags must also be provided.
66+
if apiTokenFlagProvided {
67+
if configResetInitFlags.Provider == "" || configResetInitFlags.Organization == "" || configResetInitFlags.Repository == "" {
68+
fmt.Println("Error: When using --api-token, you must also provide --provider, --organization, and --repository flags.")
69+
fmt.Println("Please provide all required flags and try again.")
70+
fmt.Println()
71+
if errHelp := cmd.Help(); errHelp != nil {
72+
log.Fatalf("Failed to display command help: %v", errHelp)
73+
}
74+
os.Exit(1)
75+
}
76+
}
77+
2978
codacyConfigFile := config.Config.ProjectConfigFile()
3079
// Check if the main configuration file exists
3180
if _, err := os.Stat(codacyConfigFile); os.IsNotExist(err) {
3281
fmt.Println("Configuration file (.codacy/codacy.yaml) not found, running initialization logic...")
33-
runConfigUpdateLogic(cmd, args, configUpdateInitFlags)
82+
runConfigResetLogic(cmd, args, configResetInitFlags)
3483
} else {
35-
fmt.Println("Updating existing Codacy configuration...")
36-
runConfigUpdateLogic(cmd, args, configUpdateInitFlags)
84+
fmt.Println("Resetting existing Codacy configuration...")
85+
runConfigResetLogic(cmd, args, configResetInitFlags)
3786
}
3887
},
3988
}
4089

41-
// runConfigUpdateLogic contains the core logic for updating or initializing the configuration.
90+
// runConfigResetLogic contains the core logic for resetting or initializing the configuration.
4291
// It mirrors the behavior of the original init command but uses shared functions from the configsetup package.
43-
func runConfigUpdateLogic(cmd *cobra.Command, args []string, flags domain.InitFlags) {
92+
func runConfigResetLogic(cmd *cobra.Command, args []string, flags domain.InitFlags) {
4493
// Create local .codacy directory first
4594
if err := config.Config.CreateLocalCodacyDir(); err != nil {
4695
log.Fatalf("Failed to create local codacy directory: %v", err)
@@ -57,7 +106,7 @@ func runConfigUpdateLogic(cmd *cobra.Command, args []string, flags domain.InitFl
57106

58107
if cliLocalMode {
59108
fmt.Println()
60-
fmt.Println("ℹ️ No API token was specified. Proceeding with local default configurations.")
109+
fmt.Println("ℹ️ Resetting to local default configurations.")
61110
noTools := []domain.Tool{} // Empty slice for tools as we are in local mode without specific toolset from API initially
62111
if err := configsetup.CreateConfigurationFiles(noTools, cliLocalMode); err != nil {
63112
log.Fatalf("Failed to create base configuration files: %v", err)
@@ -72,7 +121,7 @@ func runConfigUpdateLogic(cmd *cobra.Command, args []string, flags domain.InitFl
72121
}
73122
} else {
74123
// API token provided, fetch configuration from Codacy
75-
fmt.Println("API token specified. Fetching repository-specific configurations from Codacy...")
124+
fmt.Println("API token specified. Fetching and applying repository-specific configurations from Codacy...")
76125
if err := configsetup.BuildRepositoryConfigurationFiles(flags); err != nil {
77126
log.Fatalf("Failed to build repository-specific configuration files: %v", err)
78127
}
@@ -84,7 +133,7 @@ func runConfigUpdateLogic(cmd *cobra.Command, args []string, flags domain.InitFl
84133
}
85134

86135
fmt.Println()
87-
fmt.Println("✅ Successfully initialized/updated Codacy configuration!")
136+
fmt.Println("✅ Successfully reset Codacy configuration!")
88137
fmt.Println()
89138
fmt.Println("🔧 Next steps:")
90139
fmt.Println(" 1. Run 'codacy-cli install' to install all dependencies based on the new/updated configuration.")
@@ -93,14 +142,14 @@ func runConfigUpdateLogic(cmd *cobra.Command, args []string, flags domain.InitFl
93142
}
94143

95144
func init() {
96-
// Define flags for the config update command. These are the same flags used by the init command.
97-
configUpdateCmd.Flags().StringVar(&configUpdateInitFlags.ApiToken, "api-token", "", "Optional Codacy API token. If defined, configurations will be fetched from Codacy.")
98-
configUpdateCmd.Flags().StringVar(&configUpdateInitFlags.Provider, "provider", "", "Provider (e.g., gh, bb, gl) to fetch configurations from Codacy. Required when api-token is provided.")
99-
configUpdateCmd.Flags().StringVar(&configUpdateInitFlags.Organization, "organization", "", "Remote organization name to fetch configurations from Codacy. Required when api-token is provided.")
100-
configUpdateCmd.Flags().StringVar(&configUpdateInitFlags.Repository, "repository", "", "Remote repository name to fetch configurations from Codacy. Required when api-token is provided.")
101-
102-
// Add the update subcommand to the config command
103-
configCmd.AddCommand(configUpdateCmd)
145+
// Define flags for the config reset command. These are the same flags used by the init command.
146+
configResetCmd.Flags().StringVar(&configResetInitFlags.ApiToken, "api-token", "", "Optional Codacy API token. If defined, configurations will be fetched from Codacy.")
147+
configResetCmd.Flags().StringVar(&configResetInitFlags.Provider, "provider", "", "Provider (e.g., gh, bb, gl) to fetch configurations from Codacy. Required when api-token is provided.")
148+
configResetCmd.Flags().StringVar(&configResetInitFlags.Organization, "organization", "", "Remote organization name to fetch configurations from Codacy. Required when api-token is provided.")
149+
configResetCmd.Flags().StringVar(&configResetInitFlags.Repository, "repository", "", "Remote repository name to fetch configurations from Codacy. Required when api-token is provided.")
150+
151+
// Add the reset subcommand to the config command
152+
configCmd.AddCommand(configResetCmd)
104153
// Add the config command to the root command
105154
rootCmd.AddCommand(configCmd)
106155
}

config/config.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@ import (
88

99
"codacy/cli-v2/plugins"
1010
"codacy/cli-v2/utils"
11+
12+
"gopkg.in/yaml.v3" // Added import for YAML parsing
1113
)
1214

15+
// CliConfigYaml defines the structure for parsing .codacy/cli-config.yaml
16+
type CliConfigYaml struct {
17+
Mode string `yaml:"mode"`
18+
}
19+
1320
type ConfigType struct {
1421
repositoryDirectory string
1522

@@ -231,3 +238,35 @@ func (c *ConfigType) IsToolInstalled(name string, tool *plugins.ToolInfo) bool {
231238

232239
// Global singleton config-file
233240
var Config = ConfigType{}
241+
242+
// GetCliMode reads and parses the .codacy/cli-config.yaml file to determine the CLI's operational mode.
243+
// It returns "local" by default and an error if the file doesn't exist or an error occurs during parsing.
244+
func (c *ConfigType) GetCliMode() (string, error) {
245+
cliConfigFilePath := c.CliConfigFile()
246+
currentCliMode := "local" // Default to local
247+
248+
content, readErr := os.ReadFile(cliConfigFilePath)
249+
if readErr != nil {
250+
if os.IsNotExist(readErr) {
251+
// File does not exist. Return default mode and the error so the caller can warn.
252+
return currentCliMode, readErr
253+
}
254+
// Some other error occurred during reading the file.
255+
return currentCliMode, fmt.Errorf("failed to read %s: %w", cliConfigFilePath, readErr)
256+
}
257+
258+
// If ReadFile was successful, the file exists. Now parse it.
259+
var parsedCliConfig CliConfigYaml
260+
if yamlErr := yaml.Unmarshal(content, &parsedCliConfig); yamlErr != nil {
261+
return currentCliMode, fmt.Errorf("failed to parse %s: %w", cliConfigFilePath, yamlErr)
262+
}
263+
264+
if parsedCliConfig.Mode == "remote" || parsedCliConfig.Mode == "local" {
265+
currentCliMode = parsedCliConfig.Mode
266+
} else {
267+
// Invalid mode value in the config file.
268+
return "local", fmt.Errorf("invalid mode value \"%s\" in %s", parsedCliConfig.Mode, cliConfigFilePath)
269+
}
270+
271+
return currentCliMode, nil
272+
}

docs/config-reset-scenarios.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Codacy CLI `config reset` Command Scenarios
2+
3+
This document outlines the possible scenarios for the `codacy-cli config reset` command, considering the current CLI mode (as defined in `.codacy/cli-config.yaml`) and the flags provided during the command execution.
4+
5+
| Current CLI Mode (`.codacy/cli-config.yaml`) | `config reset` Flags Provided | Behavior |
6+
| :------------------------------------------- | :------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7+
| **Local** | No flags | Resets configuration to local default settings. Creates/overwrites `.codacy/codacy.yaml` and `tools-configs/` with defaults. `cli-config.yaml` is set to `mode: local`. |
8+
| **Local** | `--api-token <token>`<br>(missing provider/org/repo) | **Error:** Command exits. Message indicates that `--provider`, `--organization`, and `--repository` are required when `--api-token` is used. |
9+
| **Local** | `--api-token <token>`<br>`--provider <p>`<br>`--organization <o>`<br>`--repository <r>` | Fetches repository-specific configurations from Codacy API. Creates/overwrites `.codacy/codacy.yaml` and `tools-configs/` based on API response. `cli-config.yaml` is set to `mode: remote`. |
10+
| **Remote** | No flags | **Error:** Command exits. Message indicates that the CLI is in 'remote' mode, and to reset, API flags (`--api-token`, `--provider`, etc.) must be provided. This prevents accidental reset to local defaults. |
11+
| **Remote** | `--api-token <token>`<br>(missing provider/org/repo) | **Error:** Command exits. Message indicates that `--provider`, `--organization`, and `--repository` are required when `--api-token` is used. |
12+
| **Remote** | `--api-token <token>`<br>`--provider <p>`<br>`--organization <o>`<br>`--repository <r>` | Fetches repository-specific configurations from Codacy API. Creates/overwrites `.codacy/codacy.yaml` and `tools-configs/` based on API response. `cli-config.yaml` remains/is set to `mode: remote`. |
13+
| File missing or unparseable (`.codacy/cli-config.yaml`) | No flags | (Defaults to Local mode) Resets configuration to local default settings. Creates/overwrites `.codacy/codacy.yaml` and `tools-configs/` with defaults. `cli-config.yaml` is set to `mode: local`. **User-facing warning printed to console and logged regarding `cli-config.yaml` issue.** |
14+
| File missing or unparseable (`.codacy/cli-config.yaml`) | `--api-token <token>`<br>(missing provider/org/repo) | (Defaults to Local mode) **Error:** Command exits. Message indicates that `--provider`, `--organization`, and `--repository` are required when `--api-token` is used. **User-facing warning printed to console and logged regarding `cli-config.yaml` issue.** |
15+
| File missing or unparseable (`.codacy/cli-config.yaml`) | `--api-token <token>`<br>`--provider <p>`<br>`--organization <o>`<br>`--repository <r>` | (Defaults to Local mode) Fetches repository-specific configurations from Codacy API. Creates/overwrites `.codacy/codacy.yaml` and `tools-configs/` based on API response. `cli-config.yaml` is set to `mode: remote`. **User-facing warning printed to console and logged regarding `cli-config.yaml` issue.** |
16+
17+
## Key Points
18+
19+
* The `runConfigResetLogic` function determines whether to use local defaults or fetch from the API based purely on the presence of the `ApiToken` flag at the time of its execution.
20+
* The `cliLocalMode` variable within `runConfigResetLogic` (which influences `CreateConfigurationFiles` and `CliConfigFileTemplate`) is set based on `len(flags.ApiToken) == 0`.
21+
* If an API token is provided to `config reset`, the resulting `.codacy/cli-config.yaml` will always be set to `mode: remote`.
22+
* If no token is provided, it will be set to `mode: local`.
23+
* The validation logic in the `configResetCmd.Run` function occurs *before* `runConfigResetLogic` is called. This validation is responsible for:
24+
* Informing the user with a console warning if `.codacy/cli-config.yaml` is missing or unparseable, then defaulting to 'local' mode for subsequent checks.
25+
* Ensuring that if the (potentially defaulted) current CLI mode is "remote", an API token *must* be supplied to proceed with the reset.
26+
* Ensuring that if an API token *is* supplied, then `--provider`, `--organization`, and `--repository` flags must also be supplied.
27+
28+
This table should cover the main operational flows and error conditions for the `config reset` command based on the implemented logic.

0 commit comments

Comments
 (0)