-
Notifications
You must be signed in to change notification settings - Fork 6
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 #21 from codecrafters-io/add-submit-command
Refactor test command and add submission handler utility
- Loading branch information
Showing
5 changed files
with
249 additions
and
113 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
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,111 @@ | ||
package commands | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"os/exec" | ||
"strings" | ||
|
||
"github.com/codecrafters-io/cli/internal/utils" | ||
"github.com/getsentry/sentry-go" | ||
"github.com/rs/zerolog" | ||
) | ||
|
||
func SubmitCommand(ctx context.Context) (err error) { | ||
logger := zerolog.Ctx(ctx) | ||
|
||
logger.Debug().Msg("submit command starts") | ||
defer func() { | ||
logger.Debug().Err(err).Msg("submit command ends") | ||
}() | ||
|
||
defer func() { | ||
if p := recover(); p != nil { | ||
logger.Panic().Str("panic", fmt.Sprintf("%v", p)).Stack().Msg("panic") | ||
sentry.CurrentHub().Recover(p) | ||
|
||
panic(p) | ||
} | ||
|
||
if err == nil { | ||
return | ||
} | ||
|
||
var noRepo utils.NoCodecraftersRemoteFoundError | ||
if errors.Is(err, &noRepo) { | ||
// ignore | ||
return | ||
} | ||
|
||
sentry.CurrentHub().CaptureException(err) | ||
}() | ||
|
||
logger.Debug().Msg("computing repository directory") | ||
|
||
repoDir, err := utils.GetRepositoryDir() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
logger.Debug().Msgf("found repository directory: %s", repoDir) | ||
|
||
logger.Debug().Msg("identifying remotes") | ||
|
||
codecraftersRemote, err := utils.IdentifyGitRemote(repoDir) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
logger.Debug().Msgf("identified remote: %s, %s", codecraftersRemote.Name, codecraftersRemote.Url) | ||
|
||
currentBranchName, err := getCurrentBranch(repoDir) | ||
if err != nil { | ||
return fmt.Errorf("get current branch: %w", err) | ||
} | ||
|
||
defaultBranchName := "master" // TODO: Change when we allow customizing the defaultBranch | ||
|
||
if currentBranchName != defaultBranchName { | ||
return fmt.Errorf("You need to be on the `%s` branch to run this command.", defaultBranchName) | ||
} | ||
|
||
logger.Debug().Msgf("committing changes to %s", defaultBranchName) | ||
|
||
commitSha, err := commitChanges(repoDir, "codecrafters submit [skip ci]") | ||
if err != nil { | ||
return fmt.Errorf("commit changes: %w", err) | ||
} | ||
|
||
// Place this before the push so that it "feels" fast | ||
fmt.Printf("Submitting changes (commit: %s)...\n", commitSha[:7]) | ||
|
||
err = pushBranchToRemote(repoDir, codecraftersRemote.Name) | ||
if err != nil { | ||
return fmt.Errorf("push changes: %w", err) | ||
} | ||
|
||
logger.Debug().Msgf("pushed changes to remote branch %s", defaultBranchName) | ||
|
||
codecraftersClient := utils.NewCodecraftersClient(codecraftersRemote.CodecraftersServerURL()) | ||
|
||
logger.Debug().Msgf("creating submission for %s", commitSha) | ||
|
||
createSubmissionResponse, err := codecraftersClient.CreateSubmission(codecraftersRemote.CodecraftersRepositoryId(), commitSha) | ||
if err != nil { | ||
return fmt.Errorf("create submission: %w", err) | ||
} | ||
|
||
logger.Debug().Msgf("submission created: %v", createSubmissionResponse.Id) | ||
|
||
return utils.HandleSubmission(createSubmissionResponse, ctx, codecraftersClient) | ||
} | ||
|
||
func getCurrentBranch(repoDir string) (string, error) { | ||
outputBytes, err := exec.Command("git", "-C", repoDir, "rev-parse", "--abbrev-ref", "HEAD").CombinedOutput() | ||
if err != nil { | ||
return "", wrapError(err, outputBytes, "get current branch") | ||
} | ||
|
||
return strings.TrimSpace(string(outputBytes)), 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 |
---|---|---|
|
@@ -4,7 +4,6 @@ import ( | |
"context" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
|
@@ -13,8 +12,6 @@ import ( | |
"time" | ||
|
||
"github.com/codecrafters-io/cli/internal/utils" | ||
logstream_consumer "github.com/codecrafters-io/logstream/consumer" | ||
"github.com/fatih/color" | ||
"github.com/getsentry/sentry-go" | ||
cp "github.com/otiai10/copy" | ||
"github.com/rs/zerolog" | ||
|
@@ -115,99 +112,7 @@ func TestCommand(ctx context.Context) (err error) { | |
|
||
logger.Debug().Msgf("submission created: %v", createSubmissionResponse.Id) | ||
|
||
for _, message := range createSubmissionResponse.OnInitMessages { | ||
fmt.Println("") | ||
message.Print() | ||
} | ||
|
||
if createSubmissionResponse.BuildLogstreamURL != "" { | ||
logger.Debug().Msgf("streaming build logs from %s", createSubmissionResponse.BuildLogstreamURL) | ||
|
||
fmt.Println("") | ||
err = streamLogs(createSubmissionResponse.BuildLogstreamURL) | ||
if err != nil { | ||
return fmt.Errorf("stream build logs: %w", err) | ||
} | ||
|
||
logger.Debug().Msg("Finished streaming build logs") | ||
logger.Debug().Msg("fetching build") | ||
|
||
fetchBuildResponse, err := codecraftersClient.FetchBuild(createSubmissionResponse.BuildID) | ||
if err != nil { | ||
// TODO: Notify sentry | ||
red := color.New(color.FgRed).SprintFunc() | ||
fmt.Fprintln(os.Stderr, red(err.Error())) | ||
fmt.Fprintln(os.Stderr, "") | ||
fmt.Fprintln(os.Stderr, red("We couldn't fetch the results of your submission. Please try again?")) | ||
fmt.Fprintln(os.Stderr, red("Let us know at [email protected] if this error persists.")) | ||
return err | ||
} | ||
|
||
logger.Debug().Msgf("finished fetching build: %v", fetchBuildResponse) | ||
red := color.New(color.FgRed).SprintFunc() | ||
|
||
switch fetchBuildResponse.Status { | ||
case "failure": | ||
fmt.Fprintln(os.Stderr, red("")) | ||
fmt.Fprintln(os.Stderr, red("Looks like your codebase failed to build.")) | ||
fmt.Fprintln(os.Stderr, red("If you think this is a CodeCrafters error, please let us know at [email protected].")) | ||
fmt.Fprintln(os.Stderr, red("")) | ||
os.Exit(0) | ||
case "success": | ||
time.Sleep(1 * time.Second) // The delay in-between build and test logs is usually 5-10 seconds, so let's buy some time | ||
default: | ||
red := color.New(color.FgRed).SprintFunc() | ||
|
||
fmt.Fprintln(os.Stderr, red("We couldn't fetch the results of your build. Please try again?")) | ||
fmt.Fprintln(os.Stderr, red("Let us know at [email protected] if this error persists.")) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
fmt.Println("") | ||
fmt.Println("Running tests. Logs should appear shortly...") | ||
fmt.Println("") | ||
|
||
err = streamLogs(createSubmissionResponse.LogstreamURL) | ||
if err != nil { | ||
return fmt.Errorf("stream logs: %w", err) | ||
} | ||
|
||
logger.Debug().Msgf("fetching submission %s", createSubmissionResponse.Id) | ||
|
||
fetchSubmissionResponse, err := codecraftersClient.FetchSubmission(createSubmissionResponse.Id) | ||
if err != nil { | ||
// TODO: Notify sentry | ||
red := color.New(color.FgRed).SprintFunc() | ||
fmt.Fprintln(os.Stderr, red(err.Error())) | ||
fmt.Fprintln(os.Stderr, "") | ||
fmt.Fprintln(os.Stderr, red("We couldn't fetch the results of your submission. Please try again?")) | ||
fmt.Fprintln(os.Stderr, red("Let us know at [email protected] if this error persists.")) | ||
return err | ||
} | ||
|
||
logger.Debug().Msgf("finished fetching submission, status: %s", fetchSubmissionResponse.Status) | ||
|
||
switch fetchSubmissionResponse.Status { | ||
case "failure": | ||
for _, message := range createSubmissionResponse.OnFailureMessages { | ||
fmt.Println("") | ||
message.Print() | ||
} | ||
case "success": | ||
for _, message := range createSubmissionResponse.OnSuccessMessages { | ||
fmt.Println("") | ||
message.Print() | ||
} | ||
default: | ||
fmt.Println("") | ||
} | ||
|
||
if fetchSubmissionResponse.IsError { | ||
return fmt.Errorf("%s", fetchSubmissionResponse.ErrorMessage) | ||
} | ||
|
||
return nil | ||
return utils.HandleSubmission(createSubmissionResponse, ctx, codecraftersClient) | ||
} | ||
|
||
func copyRepositoryDirToTempDir(repoDir string) (string, error) { | ||
|
@@ -272,20 +177,6 @@ func pushBranchToRemote(tmpDir string, remoteName string) error { | |
return nil | ||
} | ||
|
||
func streamLogs(logstreamUrl string) error { | ||
consumer, err := logstream_consumer.NewConsumer(logstreamUrl, func(message string) {}) | ||
if err != nil { | ||
return fmt.Errorf("new log consumer: %w", err) | ||
} | ||
|
||
_, err = io.Copy(os.Stdout, consumer) | ||
if err != nil { | ||
return fmt.Errorf("stream data: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func wrapError(err error, output []byte, msg string) error { | ||
if _, ok := err.(*exec.ExitError); ok { | ||
return fmt.Errorf("add all files: %s", output) | ||
|
Oops, something went wrong.