-
Notifications
You must be signed in to change notification settings - Fork 68
feat(abc): src abc variables command #1293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+477
−1
Merged
Changes from 6 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
491acd4
src abc command
bahrmichael 858595c
remove unnecessary test
bahrmichael abe6baf
self review
bahrmichael 179c121
comment
bahrmichael 1c11dcd
semantics update
bahrmichael 54879fb
workflow instance id naming
bahrmichael ef3c689
Apply suggestions from code review
bahrmichael adae048
delete support multiple vars
bahrmichael 238cf6f
wording updates
bahrmichael 3bcf818
remove unnecessary tests
bahrmichael daae1c2
move workflow instance id back
bahrmichael a282361
test cleanup
bahrmichael 1a6476d
cleanup
bahrmichael b8ada6a
Merge branch 'main' into mb/src-abc-command
bahrmichael 08e26ec
Merge branch 'main' into mb/src-abc-command
bahrmichael 38992fa
migrate to urfave/cli
bahrmichael e2bd681
cleanup
bahrmichael e63b5f4
migrate abc command to use urfave/cli (#1297)
burmudar 819935a
review updates
bahrmichael 2259cc6
review updates
bahrmichael 8217e61
disable slices on abc variables commands
bahrmichael b88384a
Merge branch 'main' into mb/src-abc-command
bahrmichael 48e304e
post merge + lint
bahrmichael a791fe5
small changes
bahrmichael e48cd27
ci fix; deterministic ordering
bahrmichael File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,56 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "flag" | ||
| "fmt" | ||
|
|
||
| "github.com/sourcegraph/src-cli/internal/cmderrors" | ||
| ) | ||
|
|
||
| var abcCommands commander | ||
|
|
||
| func init() { | ||
| usage := `'src abc' is a tool that manages agentic batch changes on a Sourcegraph instance. | ||
|
|
||
| Usage: | ||
|
|
||
| src abc <workflow-instance-id> command [command options] | ||
|
|
||
| The commands are: | ||
|
|
||
| variables manage workflow instance variables | ||
|
|
||
| Use "src abc <workflow-instance-id> [command] -h" for more information about a command. | ||
| ` | ||
|
|
||
| flagSet := flag.NewFlagSet("abc", flag.ExitOnError) | ||
| usageFunc := func() { | ||
| fmt.Println(usage) | ||
| } | ||
| flagSet.Usage = usageFunc | ||
| handler := func(args []string) error { | ||
| if err := flagSet.Parse(args); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if flagSet.NArg() == 0 || flagSet.Arg(0) == "help" { | ||
| flagSet.SetOutput(flag.CommandLine.Output()) | ||
| flagSet.Usage() | ||
| return nil | ||
| } | ||
|
|
||
| if flagSet.NArg() < 2 { | ||
| return cmderrors.Usage("must provide a workflow instance ID and subcommand") | ||
| } | ||
|
|
||
| instanceID := flagSet.Arg(0) | ||
| abcCommands.runWithPrefixArgs("src abc <workflow-instance-id>", []string{instanceID}, flagSet.Args()[1:]) | ||
| return nil | ||
| } | ||
|
|
||
| commands = append(commands, &command{ | ||
| flagSet: flagSet, | ||
| handler: handler, | ||
| usageFunc: usageFunc, | ||
| }) | ||
| } | ||
This file contains hidden or 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,57 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "flag" | ||
| "fmt" | ||
|
|
||
| "github.com/sourcegraph/src-cli/internal/cmderrors" | ||
| ) | ||
|
|
||
| var abcVariablesCommands commander | ||
|
|
||
| func init() { | ||
| usage := `'src abc <workflow-instance-id> variables' is a tool that manages workflow instance variables on a Sourcegraph instance. | ||
|
bahrmichael marked this conversation as resolved.
Outdated
|
||
|
|
||
| Usage: | ||
|
|
||
| src abc <workflow-instance-id> variables command [command options] | ||
|
|
||
| The commands are: | ||
|
|
||
| set set workflow instance variables | ||
| delete delete a workflow instance variable | ||
|
bahrmichael marked this conversation as resolved.
Outdated
|
||
|
|
||
| Use "src abc <workflow-instance-id> variables [command] -h" for more information about a command. | ||
| ` | ||
|
|
||
| flagSet := flag.NewFlagSet("variables", flag.ExitOnError) | ||
| usageFunc := func() { | ||
| fmt.Println(usage) | ||
| } | ||
| flagSet.Usage = usageFunc | ||
| handler := func(args []string) error { | ||
| if len(args) == 0 { | ||
| return cmderrors.Usage("must provide a workflow instance ID") | ||
| } | ||
|
|
||
| instanceID := args[0] | ||
| if err := flagSet.Parse(args[1:]); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if flagSet.NArg() == 0 || flagSet.Arg(0) == "help" { | ||
| flagSet.SetOutput(flag.CommandLine.Output()) | ||
| flagSet.Usage() | ||
| return nil | ||
| } | ||
|
|
||
| abcVariablesCommands.runWithPrefixArgs("src abc <workflow-instance-id> variables", []string{instanceID}, flagSet.Args()) | ||
| return nil | ||
| } | ||
|
|
||
| abcCommands = append(abcCommands, &command{ | ||
| flagSet: flagSet, | ||
| handler: handler, | ||
| usageFunc: usageFunc, | ||
| }) | ||
| } | ||
This file contains hidden or 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,64 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "flag" | ||
| "fmt" | ||
|
|
||
| "github.com/sourcegraph/src-cli/internal/api" | ||
| "github.com/sourcegraph/src-cli/internal/cmderrors" | ||
| ) | ||
|
|
||
| func init() { | ||
| usage := ` | ||
| Examples: | ||
|
|
||
| Delete a variable from a workflow instance: | ||
|
|
||
| $ src abc QWdlbnRpY1dvcmtmbG93SW5zdGFuY2U6MQ== variables delete approval | ||
| ` | ||
|
|
||
| flagSet := flag.NewFlagSet("delete", flag.ExitOnError) | ||
| usageFunc := func() { | ||
| fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src abc <workflow-instance-id> variables %s':\n", flagSet.Name()) | ||
| flagSet.PrintDefaults() | ||
| fmt.Println(usage) | ||
| } | ||
| apiFlags := api.NewFlags(flagSet) | ||
|
|
||
| handler := func(args []string) error { | ||
| if len(args) == 0 { | ||
| return cmderrors.Usage("must provide a workflow instance ID") | ||
| } | ||
|
|
||
| instanceID := args[0] | ||
| if err := flagSet.Parse(args[1:]); err != nil { | ||
| return err | ||
| } | ||
| if flagSet.NArg() != 1 { | ||
| return cmderrors.Usage("must provide exactly one variable name") | ||
| } | ||
|
|
||
| key := flagSet.Arg(0) | ||
| client := cfg.apiClient(apiFlags, flagSet.Output()) | ||
| if err := updateABCWorkflowInstanceVariables(context.Background(), client, instanceID, []map[string]string{{ | ||
| "key": key, | ||
| "value": "null", | ||
| }}); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if apiFlags.GetCurl() { | ||
| return nil | ||
| } | ||
|
|
||
| fmt.Printf("Removed variable %q from workflow instance %q.\n", key, instanceID) | ||
| return nil | ||
|
bahrmichael marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| abcVariablesCommands = append(abcVariablesCommands, &command{ | ||
| flagSet: flagSet, | ||
| handler: handler, | ||
| usageFunc: usageFunc, | ||
| }) | ||
| } | ||
This file contains hidden or 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,186 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "encoding/json" | ||
| "flag" | ||
| "fmt" | ||
| "strings" | ||
|
|
||
| "github.com/sourcegraph/src-cli/internal/api" | ||
| "github.com/sourcegraph/src-cli/internal/cmderrors" | ||
| ) | ||
|
|
||
| const updateABCWorkflowInstanceVariablesMutation = `mutation UpdateAgenticWorkflowInstanceVariables( | ||
| $instanceID: ID!, | ||
| $variables: [AgenticWorkflowInstanceVariableInput!]!, | ||
| ) { | ||
| updateAgenticWorkflowInstanceVariables(instanceID: $instanceID, variables: $variables) { | ||
| id | ||
| } | ||
| }` | ||
|
|
||
| func init() { | ||
| usage := ` | ||
| Examples: | ||
|
|
||
| Set a string variable on a workflow instance: | ||
|
|
||
| $ src abc QWdlbnRpY1dvcmtmbG93SW5zdGFuY2U6MQ== variables set prompt="tighten the review criteria" | ||
|
|
||
| Set multiple variables in one request: | ||
|
|
||
| $ src abc QWdlbnRpY1dvcmtmbG93SW5zdGFuY2U6MQ== variables set --var prompt="tighten the review criteria" --var checkpoints='[1,2,3]' | ||
|
|
||
| Set a structured JSON value: | ||
|
|
||
| $ src abc QWdlbnRpY1dvcmtmbG93SW5zdGFuY2U6MQ== variables set checkpoints='[1,2,3]' | ||
|
|
||
| Values are interpreted as JSON literals when valid. Otherwise they are sent as plain strings. | ||
| ` | ||
|
|
||
| flagSet := flag.NewFlagSet("set", flag.ExitOnError) | ||
| var variableArgs abcVariableArgs | ||
| flagSet.Var(&variableArgs, "var", "Variable assignment in <name>=<value> form. Repeat to set multiple variables.") | ||
| usageFunc := func() { | ||
| fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src abc <workflow-instance-id> variables %s':\n", flagSet.Name()) | ||
| flagSet.PrintDefaults() | ||
| fmt.Println(usage) | ||
| } | ||
| apiFlags := api.NewFlags(flagSet) | ||
|
|
||
| handler := func(args []string) error { | ||
| if len(args) == 0 { | ||
| return cmderrors.Usage("must provide a workflow instance ID") | ||
| } | ||
|
|
||
| instanceID := args[0] | ||
| variableArgs = nil | ||
| if err := flagSet.Parse(args[1:]); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| variables, err := parseABCVariables(flagSet.Args(), variableArgs) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| graphqlVariables := make([]map[string]string, 0, len(variables)) | ||
| for _, variable := range variables { | ||
| graphqlVariables = append(graphqlVariables, map[string]string{ | ||
| "key": variable.Key, | ||
| "value": variable.Value, | ||
| }) | ||
| } | ||
|
|
||
| client := cfg.apiClient(apiFlags, flagSet.Output()) | ||
| if err := updateABCWorkflowInstanceVariables(context.Background(), client, instanceID, graphqlVariables); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if apiFlags.GetCurl() { | ||
| return nil | ||
| } | ||
|
|
||
| if len(variables) == 1 { | ||
| fmt.Printf("Set variable %q on workflow instance %q.\n", variables[0].Key, instanceID) | ||
| return nil | ||
| } | ||
|
|
||
| fmt.Printf("Updated %d variables on workflow instance %q.\n", len(variables), instanceID) | ||
| return nil | ||
| } | ||
|
|
||
| abcVariablesCommands = append(abcVariablesCommands, &command{ | ||
| flagSet: flagSet, | ||
| handler: handler, | ||
| usageFunc: usageFunc, | ||
| }) | ||
| } | ||
|
|
||
| type abcVariableArgs []string | ||
|
|
||
| func (a *abcVariableArgs) String() string { | ||
| return strings.Join(*a, ",") | ||
| } | ||
|
|
||
| func (a *abcVariableArgs) Set(value string) error { | ||
| *a = append(*a, value) | ||
| return nil | ||
|
bahrmichael marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| type abcVariable struct { | ||
| Key string | ||
| Value string | ||
| } | ||
|
|
||
| func parseABCVariables(positional []string, flagged abcVariableArgs) ([]abcVariable, error) { | ||
| rawVariables := append([]string{}, positional...) | ||
| rawVariables = append(rawVariables, flagged...) | ||
| if len(rawVariables) == 0 { | ||
| return nil, cmderrors.Usage("must provide at least one variable assignment") | ||
| } | ||
|
|
||
| variables := make([]abcVariable, 0, len(rawVariables)) | ||
|
bahrmichael marked this conversation as resolved.
Outdated
|
||
| for _, rawVariable := range rawVariables { | ||
| variable, err := parseABCVariable(rawVariable) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| variables = append(variables, variable) | ||
| } | ||
|
|
||
| return variables, nil | ||
| } | ||
|
|
||
| func parseABCVariable(raw string) (abcVariable, error) { | ||
| name, rawValue, ok := strings.Cut(raw, "=") | ||
| if !ok || name == "" { | ||
| return abcVariable{}, cmderrors.Usagef("invalid variable assignment %q: must be in <name>=<value> form", raw) | ||
| } | ||
|
|
||
| value, remove, err := marshalABCVariableValue(rawValue) | ||
| if err != nil { | ||
| return abcVariable{}, err | ||
| } | ||
| if remove { | ||
| return abcVariable{}, cmderrors.Usagef("invalid variable assignment %q: use 'src abc <workflow-instance-id> variables delete %s' to remove a variable", raw, name) | ||
| } | ||
|
|
||
| return abcVariable{Key: name, Value: value}, nil | ||
| } | ||
|
|
||
| func updateABCWorkflowInstanceVariables(ctx context.Context, client api.Client, instanceID string, variables []map[string]string) error { | ||
| var result struct { | ||
| UpdateAgenticWorkflowInstanceVariables struct { | ||
| ID string `json:"id"` | ||
| } `json:"updateAgenticWorkflowInstanceVariables"` | ||
| } | ||
| if ok, err := client.NewRequest(updateABCWorkflowInstanceVariablesMutation, map[string]any{ | ||
| "instanceID": instanceID, | ||
| "variables": variables, | ||
| }).Do(ctx, &result); err != nil || !ok { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func marshalABCVariableValue(raw string) (value string, remove bool, err error) { | ||
| // Try to compact valid JSON literals first so numbers, arrays, and objects are sent unchanged. | ||
| // A bare null is detected separately so the CLI can require the explicit delete command. | ||
| // If compacting doesn't work for the given value, fall back to string encoding. | ||
| var compact bytes.Buffer | ||
| if err := json.Compact(&compact, []byte(raw)); err == nil { | ||
| value := compact.String() | ||
| return value, value == "null", nil | ||
| } | ||
|
|
||
| encoded, err := json.Marshal(raw) | ||
| if err != nil { | ||
| return "", false, err | ||
| } | ||
|
|
||
| return string(encoded), false, nil | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.