-
Notifications
You must be signed in to change notification settings - Fork 70
feat: Add search-jobs CLI commands for managing search jobs #1145
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
Merged
Changes from 8 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
ee7a504
feat: Add search-jobs CLI commands for managing search jobs
trly 4f57cbc
feat: implement search-jobs logs command
trly 45339c2
feat: Add search jobs results retrieval command
trly bcf88c4
cleanup trailing whitespace
trly f46b05a
remove tests as they are not functionally testing production code
trly dae3f65
Fix linter issues. Add support for job id numbers (#1151)
peterguy bdbe4b8
feat: Add search jobs restart command
trly 50165f4
(lint) removed unused testutil
trly 38248af
refactor: Redesign search-jobs command structure with builder pattern
trly 6c66488
refactor: make search-jobs command builder methods private
trly 049e554
(fix) do not display command success output when using -get-curl
trly b87df9b
(fix) clean up inconsistencies in usage text
trly 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
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
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,146 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "encoding/base64" | ||
| "flag" | ||
| "fmt" | ||
| "regexp" | ||
| "strconv" | ||
|
|
||
| "github.com/sourcegraph/src-cli/internal/cmderrors" | ||
| ) | ||
|
|
||
| // searchJobFragment is a GraphQL fragment that defines the fields to be queried | ||
| // for a SearchJob. It includes the job's ID, query, state, creator information, | ||
| // timestamps, URLs, and repository statistics. | ||
| const SearchJobFragment = ` | ||
| fragment SearchJobFields on SearchJob { | ||
| id | ||
| query | ||
| state | ||
| creator { | ||
| username | ||
| } | ||
| createdAt | ||
| startedAt | ||
| finishedAt | ||
| URL | ||
| logURL | ||
| repoStats { | ||
| total | ||
| completed | ||
| failed | ||
| inProgress | ||
| } | ||
| }` | ||
|
|
||
| var searchJobsCommands commander | ||
|
|
||
| // init registers the 'src search-jobs' command with the CLI. It provides subcommands | ||
| // for managing search jobs, including creating, listing, getting, canceling and deleting | ||
| // jobs. The command uses a flagset for parsing options and displays usage information | ||
| // when help is requested. | ||
| func init() { | ||
| usage := `'src search-jobs' is a tool that manages search jobs on a Sourcegraph instance. | ||
|
|
||
| Usage: | ||
|
|
||
| src search-jobs command [command options] | ||
|
|
||
| The commands are: | ||
|
|
||
| cancel cancels a search job by ID | ||
| create creates a search job | ||
| delete deletes a search job by ID | ||
| get gets a search job by ID | ||
| restart restarts a search job by ID | ||
| list lists search jobs | ||
| logs outputs the logs for a search job by ID | ||
| results outputs the results for a search job by ID | ||
|
|
||
| Use "src search-jobs [command] -h" for more information about a command. | ||
| ` | ||
|
|
||
| flagSet := flag.NewFlagSet("search-jobs", flag.ExitOnError) | ||
| handler := func(args []string) error { | ||
| searchJobsCommands.run(flagSet, "src search-jobs", usage, args) | ||
| return nil | ||
| } | ||
|
|
||
| commands = append(commands, &command{ | ||
| flagSet: flagSet, | ||
| aliases: []string{"search-job"}, | ||
| handler: handler, | ||
| usageFunc: func() { | ||
| fmt.Println(usage) | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| // SearchJob represents a search job with its metadata, including the search query, | ||
| // execution state, creator information, timestamps, URLs, and repository statistics. | ||
| type SearchJob struct { | ||
| ID string | ||
| Query string | ||
| State string | ||
| Creator struct { | ||
| Username string | ||
| } | ||
| CreatedAt string | ||
| StartedAt string | ||
| FinishedAt string | ||
| URL string | ||
| LogURL string | ||
| RepoStats struct { | ||
| Total int | ||
| Completed int | ||
| Failed int | ||
| InProgress int | ||
| } | ||
| } | ||
|
|
||
| type SearchJobID struct { | ||
| number uint64 | ||
| } | ||
|
|
||
| func ParseSearchJobID(input string) (*SearchJobID, error) { | ||
trly marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // accept either: | ||
| // - the numeric job id (non-negative integer) | ||
| // - the plain text SearchJob:<integer> form of the id | ||
| // - the base64-encoded "SearchJob:<integer>" string | ||
trly marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| if input == "" { | ||
| return nil, cmderrors.Usage("must provide a search job ID") | ||
| } | ||
|
|
||
| // Try to decode if it's base64 first | ||
| if decoded, err := base64.StdEncoding.DecodeString(input); err == nil { | ||
| input = string(decoded) | ||
| } | ||
|
|
||
| // Match either "SearchJob:<integer>" or "<integer>" | ||
| re := regexp.MustCompile(`^(?:SearchJob:)?(\d+)$`) | ||
| matches := re.FindStringSubmatch(input) | ||
| if matches == nil { | ||
| return nil, fmt.Errorf("invalid ID format: must be a non-negative integer, 'SearchJob:<integer>', or that string base64-encoded") | ||
| } | ||
|
|
||
| number, err := strconv.ParseUint(matches[1], 10, 64) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("invalid ID format: must be a 64-bit non-negative integer") | ||
| } | ||
|
|
||
| return &SearchJobID{number: number}, nil | ||
| } | ||
|
|
||
| func (id *SearchJobID) String() string { | ||
| return fmt.Sprintf("SearchJob:%d", id.Number()) | ||
| } | ||
|
|
||
| func (id *SearchJobID) Canonical() string { | ||
trly marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return base64.StdEncoding.EncodeToString([]byte(id.String())) | ||
| } | ||
|
|
||
| func (id *SearchJobID) Number() uint64 { | ||
| return id.number | ||
| } | ||
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,79 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "flag" | ||
| "fmt" | ||
|
|
||
| "github.com/sourcegraph/src-cli/internal/api" | ||
| ) | ||
|
|
||
| const CancelSearchJobMutation = `mutation CancelSearchJob($id: ID!) { | ||
| cancelSearchJob(id: $id) { | ||
| alwaysNil | ||
| } | ||
| }` | ||
|
|
||
| // init registers the 'cancel' subcommand for search jobs, which allows users to cancel | ||
| // a running search job by its ID. It sets up the command's flag parsing, usage information, | ||
| // and handles the GraphQL mutation to cancel the specified search job. | ||
| func init() { | ||
| usage := ` | ||
| Examples: | ||
|
|
||
| Cancel a search job: | ||
|
|
||
| $ src search-jobs cancel -id 999 | ||
| ` | ||
| flagSet := flag.NewFlagSet("cancel", flag.ExitOnError) | ||
| usageFunc := func() { | ||
| fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src search-jobs %s':\n", flagSet.Name()) | ||
| flagSet.PrintDefaults() | ||
| fmt.Println(usage) | ||
| } | ||
|
|
||
| var ( | ||
| idFlag = flagSet.String("id", "", "ID of the search job to cancel") | ||
| apiFlags = api.NewFlags(flagSet) | ||
| ) | ||
|
|
||
| handler := func(args []string) error { | ||
| if err := flagSet.Parse(args); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| client := api.NewClient(api.ClientOpts{ | ||
| Endpoint: cfg.Endpoint, | ||
| AccessToken: cfg.AccessToken, | ||
| Out: flagSet.Output(), | ||
| Flags: apiFlags, | ||
| }) | ||
|
|
||
| jobID, err := ParseSearchJobID(*idFlag) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| query := CancelSearchJobMutation | ||
|
|
||
| var result struct { | ||
| CancelSearchJob struct { | ||
| AlwaysNil bool | ||
| } | ||
| } | ||
|
|
||
| if ok, err := client.NewRequest(query, map[string]interface{}{ | ||
| "id": api.NullString(jobID.Canonical()), | ||
| }).Do(context.Background(), &result); err != nil || !ok { | ||
| return err | ||
| } | ||
| fmt.Fprintf(flagSet.Output(), "Search job %s canceled successfully\n", *idFlag) | ||
| return nil | ||
| } | ||
|
|
||
| searchJobsCommands = append(searchJobsCommands, &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,103 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "flag" | ||
| "fmt" | ||
|
|
||
| "github.com/sourcegraph/src-cli/internal/api" | ||
| "github.com/sourcegraph/src-cli/internal/cmderrors" | ||
| ) | ||
|
|
||
| // ValidateSearchJobQuery defines the GraphQL query for validating search jobs | ||
| const ValidateSearchJobQuery = `query ValidateSearchJob($query: String!) { | ||
| validateSearchJob(query: $query) { | ||
| alwaysNil | ||
| } | ||
| }` | ||
|
|
||
| // CreateSearchJobQuery defines the GraphQL mutation for creating search jobs | ||
| const CreateSearchJobQuery = `mutation CreateSearchJob($query: String!) { | ||
| createSearchJob(query: $query) { | ||
| ...SearchJobFields | ||
| } | ||
| }` + SearchJobFragment | ||
|
|
||
| // init registers the "search-jobs create" subcommand. It allows users to create a search job | ||
| // with a specified query, validates the query before creation, and outputs the result in a | ||
| // customizable format. The command requires a search query and supports custom output formatting | ||
| // using Go templates. | ||
| func init() { | ||
| usage := ` | ||
| Examples: | ||
|
|
||
| Create a search job: | ||
|
|
||
| $ src search-jobs create -query "repo:^github\.com/sourcegraph/sourcegraph$ sort:indexed-desc" | ||
| ` | ||
|
|
||
| flagSet := flag.NewFlagSet("create", flag.ExitOnError) | ||
| usageFunc := func() { | ||
| fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src search-jobs %s':\n", flagSet.Name()) | ||
| flagSet.PrintDefaults() | ||
| fmt.Println(usage) | ||
| } | ||
|
|
||
| var ( | ||
| queryFlag = flagSet.String("query", "", "Search query") | ||
| formatFlag = flagSet.String("f", "{{searchJobIDNumber .ID}}: {{.Creator.Username}} {{.State}} ({{.Query}})", `Format for the output, using the syntax of Go package text/template. (e.g. "{{.ID}}: {{.Creator.Username}} ({{.Query}})" or "{{.|json}}")`) | ||
| apiFlags = api.NewFlags(flagSet) | ||
| ) | ||
|
|
||
| handler := func(args []string) error { | ||
| if err := flagSet.Parse(args); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| client := api.NewClient(api.ClientOpts{ | ||
| Endpoint: cfg.Endpoint, | ||
| AccessToken: cfg.AccessToken, | ||
| Out: flagSet.Output(), | ||
| Flags: apiFlags, | ||
| }) | ||
|
|
||
| tmpl, err := parseTemplate(*formatFlag) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if *queryFlag == "" { | ||
| return cmderrors.Usage("must provide a query") | ||
| } | ||
|
|
||
| var validateResult struct { | ||
| ValidateSearchJob interface{} `json:"validateSearchJob"` | ||
| } | ||
|
|
||
| if ok, err := client.NewRequest(ValidateSearchJobQuery, map[string]interface{}{ | ||
| "query": *queryFlag, | ||
| }).Do(context.Background(), &validateResult); err != nil || !ok { | ||
| return err | ||
| } | ||
|
|
||
| query := CreateSearchJobQuery | ||
|
|
||
| var result struct { | ||
| CreateSearchJob *SearchJob `json:"createSearchJob"` | ||
| } | ||
|
|
||
| if ok, err := client.NewRequest(query, map[string]interface{}{ | ||
| "query": *queryFlag, | ||
| }).Do(context.Background(), &result); !ok { | ||
| return err | ||
| } | ||
|
|
||
| return execTemplate(tmpl, result.CreateSearchJob) | ||
| } | ||
|
|
||
| searchJobsCommands = append(searchJobsCommands, &command{ | ||
| flagSet: flagSet, | ||
| handler: handler, | ||
| usageFunc: usageFunc, | ||
| }) | ||
| } |
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.