FixKeployFolderPermissions attempts to fix permission issues on the keploy folder by changing ownership to the current user using sudo chown. It shows which files have issues and provides a timeout for the sudo password prompt. If timeout is reached, it continues without fixing (with a warning).
(ctx context.Context, logger *zap.Logger, keployPath string, permErrors []PermissionError)
| 145 | // It shows which files have issues and provides a timeout for the sudo password prompt. |
| 146 | // If timeout is reached, it continues without fixing (with a warning). |
| 147 | func FixKeployFolderPermissions(ctx context.Context, logger *zap.Logger, keployPath string, permErrors []PermissionError) error { |
| 148 | // Get current user |
| 149 | currentUser, err := user.Current() |
| 150 | if err != nil { |
| 151 | return fmt.Errorf("failed to get current user: %w", err) |
| 152 | } |
| 153 | |
| 154 | // Log the files with permission issues |
| 155 | fmt.Println() |
| 156 | logger.Info("The keploy folder contains files not accessible by current user (likely from running an older version with sudo).") |
| 157 | |
| 158 | // Show the problematic files (up to 5, then "and X more...") |
| 159 | if len(permErrors) > 0 { |
| 160 | logger.Info("Files with permission issues:") |
| 161 | maxShow := 5 |
| 162 | for i, pe := range permErrors { |
| 163 | if i >= maxShow { |
| 164 | logger.Info(fmt.Sprintf(" ... and %d more", len(permErrors)-maxShow)) |
| 165 | break |
| 166 | } |
| 167 | logger.Info(fmt.Sprintf(" - %s (owner UID: %d)", pe.Path, pe.OwnerUID)) |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | fmt.Println() |
| 172 | logger.Info("To fix this, we need to change ownership of the keploy folder to your user.") |
| 173 | fmt.Println() |
| 174 | |
| 175 | // First, cache sudo credentials using sudo -v (this prompts for password) |
| 176 | cmd := exec.CommandContext(ctx, "sudo", "-v") |
| 177 | cmd.Stdin = os.Stdin |
| 178 | cmd.Stdout = os.Stdout |
| 179 | cmd.Stderr = os.Stderr |
| 180 | if err := cmd.Run(); err != nil { |
| 181 | // Check if context was cancelled (user pressed Ctrl+C) |
| 182 | if ctx.Err() != nil { |
| 183 | return ctx.Err() |
| 184 | } |
| 185 | // Check if the process was killed by a signal (e.g., SIGINT from Ctrl+C) |
| 186 | var exitErr *exec.ExitError |
| 187 | if errors.As(err, &exitErr) { |
| 188 | // Exit code -1 or signal termination indicates interrupt |
| 189 | if exitErr.ExitCode() == -1 || strings.Contains(err.Error(), "signal") { |
| 190 | return context.Canceled |
| 191 | } |
| 192 | } |
| 193 | logger.Info(fmt.Sprintf("Failed to authenticate sudo: %v", err)) |
| 194 | logger.Info(fmt.Sprintf("To fix manually, run: sudo chown -R %s %s", currentUser.Username, keployPath)) |
| 195 | return fmt.Errorf("sudo authentication failed") |
| 196 | } |
| 197 | |
| 198 | // Now run chown with cached credentials (should not prompt again) |
| 199 | logger.Info(fmt.Sprintf("Running: sudo chown -R %s %s", currentUser.Username, keployPath)) |
| 200 | if err := runSudoChownNonInteractive(ctx, currentUser.Username, true, keployPath); err != nil { |
| 201 | if ctx.Err() != nil { |
| 202 | return ctx.Err() |
| 203 | } |
| 204 | logger.Info(fmt.Sprintf("Failed to fix permissions: %v", err)) |
no test coverage detected