-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #207 from open-sauced/feat/offboarding-command
feat: implement `pizza offboard` command
- Loading branch information
Showing
4 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package offboard | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"slices" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/open-sauced/pizza-cli/v2/pkg/config" | ||
"github.com/open-sauced/pizza-cli/v2/pkg/constants" | ||
"github.com/open-sauced/pizza-cli/v2/pkg/utils" | ||
) | ||
|
||
type Options struct { | ||
offboardingUsers []string | ||
|
||
// config file path | ||
configPath string | ||
|
||
// repository path | ||
path string | ||
|
||
// from global config | ||
ttyDisabled bool | ||
|
||
// telemetry for capturing CLI events via PostHog | ||
telemetry *utils.PosthogCliClient | ||
} | ||
|
||
const offboardLongDesc string = `CAUTION: Experimental Command. Removes users from the \".sauced.yaml\" config and \"CODEOWNERS\" files. | ||
Requires the users' name OR email.` | ||
|
||
func NewConfigCommand() *cobra.Command { | ||
opts := &Options{} | ||
cmd := &cobra.Command{ | ||
Use: "offboard <username/email> [flags]", | ||
Short: "CAUTION: Experimental Command. Removes users from the \".sauced.yaml\" config and \"CODEOWNERS\" files.", | ||
Long: offboardLongDesc, | ||
Args: func(_ *cobra.Command, args []string) error { | ||
if len(args) == 0 { | ||
return errors.New("you must provide at least one argument: the offboarding user's email/username") | ||
} | ||
|
||
opts.offboardingUsers = args | ||
|
||
return nil | ||
}, | ||
RunE: func(cmd *cobra.Command, _ []string) error { | ||
opts.ttyDisabled, _ = cmd.Flags().GetBool("tty-disable") | ||
opts.configPath, _ = cmd.Flags().GetString("config") | ||
disableTelem, _ := cmd.Flags().GetBool(constants.FlagNameTelemetry) | ||
|
||
opts.telemetry = utils.NewPosthogCliClient(!disableTelem) | ||
|
||
opts.path, _ = cmd.Flags().GetString("path") | ||
err := run(opts) | ||
_ = opts.telemetry.Done() | ||
|
||
return err | ||
}, | ||
} | ||
|
||
cmd.PersistentFlags().StringP("path", "p", "", "the path to the repository (required)") | ||
if err := cmd.MarkPersistentFlagRequired("path"); err != nil { | ||
fmt.Printf("error MarkPersistentFlagRequired: %v", err) | ||
} | ||
return cmd | ||
} | ||
|
||
func run(opts *Options) error { | ||
var spec *config.Spec | ||
var err error | ||
if len(opts.configPath) != 0 { | ||
spec, _, err = config.LoadConfig(opts.configPath) | ||
} else { | ||
var configPath string | ||
if strings.Compare(string(opts.path[len(opts.path)-1]), "/") == 0 { | ||
configPath = opts.path + ".sauced.yaml" | ||
} else { | ||
configPath = opts.path + "/.sauced.yaml" | ||
} | ||
spec, _, err = config.LoadConfig(configPath) | ||
} | ||
|
||
if err != nil { | ||
_ = opts.telemetry.CaptureFailedOffboard() | ||
return fmt.Errorf("error loading config: %v", err) | ||
} | ||
|
||
var offboardingNames []string | ||
attributions := spec.Attributions | ||
for _, user := range opts.offboardingUsers { | ||
added := false | ||
|
||
// deletes if the user is a name (key) | ||
delete(attributions, user) | ||
|
||
// delete if the user is an email (value) | ||
for k, v := range attributions { | ||
if slices.Contains(v, user) { | ||
offboardingNames = append(offboardingNames, k) | ||
delete(attributions, k) | ||
added = true | ||
} | ||
} | ||
|
||
if !added { | ||
offboardingNames = append(offboardingNames, user) | ||
} | ||
} | ||
|
||
if len(opts.configPath) != 0 { | ||
err = generateConfigFile(opts.configPath, attributions) | ||
} else { | ||
var configPath string | ||
if strings.Compare(string(opts.path[len(opts.path)-1]), "/") == 0 { | ||
configPath = opts.path + ".sauced.yaml" | ||
} else { | ||
configPath = opts.path + "/.sauced.yaml" | ||
} | ||
err = generateConfigFile(configPath, attributions) | ||
} | ||
|
||
if err != nil { | ||
_ = opts.telemetry.CaptureFailedOffboard() | ||
return fmt.Errorf("error generating config file: %v", err) | ||
} | ||
|
||
err = generateOwnersFile(opts.path, offboardingNames) | ||
if err != nil { | ||
_ = opts.telemetry.CaptureFailedOffboard() | ||
return fmt.Errorf("error generating owners file: %v", err) | ||
} | ||
|
||
_ = opts.telemetry.CaptureOffboard() | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package offboard | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/open-sauced/pizza-cli/v2/pkg/config" | ||
"github.com/open-sauced/pizza-cli/v2/pkg/utils" | ||
) | ||
|
||
func generateConfigFile(outputPath string, attributionMap map[string][]string) error { | ||
file, err := os.Create(outputPath) | ||
if err != nil { | ||
return fmt.Errorf("error creating %s file: %w", outputPath, err) | ||
} | ||
defer file.Close() | ||
|
||
var config config.Spec | ||
config.Attributions = attributionMap | ||
|
||
// for pretty print test | ||
yaml, err := utils.OutputYAML(config) | ||
|
||
if err != nil { | ||
return fmt.Errorf("failed to turn into YAML: %w", err) | ||
} | ||
|
||
_, err = file.WriteString(yaml) | ||
|
||
if err != nil { | ||
return fmt.Errorf("failed to turn into YAML: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func generateOwnersFile(path string, offboardingUsers []string) error { | ||
outputType := "/CODEOWNERS" | ||
var owners []byte | ||
var err error | ||
|
||
var ownersPath string | ||
|
||
if _, err = os.Stat(filepath.Join(path, "/CODEOWNERS")); !errors.Is(err, os.ErrNotExist) { | ||
outputType = "CODEOWNERS" | ||
ownersPath = filepath.Join(path, "/CODEOWNERS") | ||
owners, err = os.ReadFile(ownersPath) | ||
} else if _, err = os.Stat(filepath.Join(path, "OWNERS")); !errors.Is(err, os.ErrNotExist) { | ||
outputType = "OWNERS" | ||
ownersPath = filepath.Join(path, "/OWNERS") | ||
owners, err = os.ReadFile(ownersPath) | ||
} | ||
|
||
if err != nil { | ||
fmt.Printf("will create a new %s file in the path %s", outputType, path) | ||
} | ||
|
||
lines := strings.Split(string(owners), "\n") | ||
var newLines []string | ||
for _, line := range lines { | ||
newLine := line | ||
for _, name := range offboardingUsers { | ||
result, _, found := strings.Cut(newLine, "@"+name) | ||
if found { | ||
newLine = result | ||
} | ||
} | ||
newLines = append(newLines, newLine) | ||
} | ||
|
||
output := strings.Join(newLines, "\n") | ||
file, err := os.Create(ownersPath) | ||
if err != nil { | ||
return fmt.Errorf("error creating %s file: %w", outputType, err) | ||
} | ||
defer file.Close() | ||
|
||
_, err = file.WriteString(output) | ||
if err != nil { | ||
return fmt.Errorf("failed writing file %s: %w", path+outputType, err) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters