Skip to content

Commit 2d3c363

Browse files
authored
Merge pull request #46 from codecrafters-io/arpan/cc-1915-allow-upgrading-buildpacks
Update buildpack command to fetch language version and improve error …
2 parents e1cc1a5 + 137e2bd commit 2d3c363

File tree

6 files changed

+62
-80
lines changed

6 files changed

+62
-80
lines changed

cmd/codecrafters/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ EXAMPLES
3030
COMMANDS
3131
test: Run tests without committing changes
3232
submit: Commit changes & submit to move to next step
33-
update-buildpack: Update buildpack configuration
33+
update-buildpack: Update language version
3434
help: Show usage instructions
3535
3636
VERSION

install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
set -eu
44

55
# allow overriding the version
6-
VERSION=${CODECRAFTERS_CLI_VERSION:-v35}
6+
VERSION=${CODECRAFTERS_CLI_VERSION:-v37}
77

88
PLATFORM=$(uname -s)
99
ARCH=$(uname -m)

internal/commands/update_buildpack.go

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,33 +60,20 @@ func UpdateBuildpackCommand(ctx context.Context) (err error) {
6060

6161
logger.Debug().Msgf("identified remote: %s, %s", codecraftersRemote.Name, codecraftersRemote.Url)
6262

63-
logger.Debug().Msg("reading and updating codecrafters.yml file")
64-
65-
codecraftersYmlPath := filepath.Join(repoDir, "codecrafters.yml")
63+
logger.Debug().Msg("fetching current buildpack from server")
6664

67-
content, err := os.ReadFile(codecraftersYmlPath)
68-
if err != nil {
69-
if os.IsNotExist(err) {
70-
return fmt.Errorf("codecrafters.yml file not found in repository root")
71-
}
72-
return fmt.Errorf("failed to read codecrafters.yml: %w", err)
73-
}
74-
75-
updatedContent := utils.ReplaceYAMLField(string(content), "language_pack", "buildpack")
65+
codecraftersClient := utils.NewCodecraftersClient(codecraftersRemote.CodecraftersServerURL())
7666

77-
err = os.WriteFile(codecraftersYmlPath, []byte(updatedContent), 0644)
67+
repositoryBuildpackResponse, err := codecraftersClient.FetchRepositoryBuildpack(codecraftersRemote.CodecraftersRepositoryId())
7868
if err != nil {
79-
return fmt.Errorf("failed to write updated codecrafters.yml: %w", err)
69+
logger.Debug().Err(err).Msg("failed to fetch repository buildpack")
70+
return fmt.Errorf("failed to fetch repository buildpack: %w", err)
8071
}
8172

82-
buildpackValue := utils.ExtractYAMLFieldValue(updatedContent, "buildpack")
83-
if buildpackValue == "" {
84-
return fmt.Errorf("buildpack value not found in codecrafters.yml")
85-
}
73+
currentBuildpackSlug := repositoryBuildpackResponse.Buildpack.Slug
8674

8775
logger.Debug().Msg("fetching latest buildpack from server")
8876

89-
codecraftersClient := utils.NewCodecraftersClient(codecraftersRemote.CodecraftersServerURL())
9077
buildpacksResponse, err := codecraftersClient.FetchBuildpacks(codecraftersRemote.CodecraftersRepositoryId())
9178
if err != nil {
9279
logger.Debug().Err(err).Msg("failed to fetch buildpacks")
@@ -101,14 +88,14 @@ func UpdateBuildpackCommand(ctx context.Context) (err error) {
10188
}
10289
}
10390

104-
logger.Debug().Msgf("current buildpack: %s, latest buildpack: %s", buildpackValue, latestBuildpack.Slug)
91+
logger.Debug().Msgf("current buildpack: %s, latest buildpack: %s", currentBuildpackSlug, latestBuildpack.Slug)
10592

106-
if buildpackValue == latestBuildpack.Slug {
107-
fmt.Printf("Buildpack is already up to date (%s)\n", buildpackValue)
93+
if currentBuildpackSlug == latestBuildpack.Slug {
94+
fmt.Printf("Buildpack is already up to date (%s)\n", currentBuildpackSlug)
10895
return nil
10996
}
11097

111-
fmt.Printf("Current buildpack: %s\n", buildpackValue)
98+
fmt.Printf("Current buildpack: %s\n", currentBuildpackSlug)
11299
fmt.Printf("Do you want to upgrade to %s? (Press any key to proceed, CTRL-C to cancel)\n", latestBuildpack.Slug)
113100

114101
reader := bufio.NewReader(os.Stdin)
@@ -128,13 +115,26 @@ func UpdateBuildpackCommand(ctx context.Context) (err error) {
128115
return fmt.Errorf("update failed: %s", updateResponse.ErrorMessage)
129116
}
130117

118+
logger.Debug().Msg("reading and updating codecrafters.yml file")
119+
120+
codecraftersYmlPath := filepath.Join(repoDir, "codecrafters.yml")
121+
122+
content, err := os.ReadFile(codecraftersYmlPath)
123+
if err != nil {
124+
if os.IsNotExist(err) {
125+
return fmt.Errorf("codecrafters.yml file not found in repository root")
126+
}
127+
return fmt.Errorf("failed to read codecrafters.yml: %w", err)
128+
}
129+
130+
updatedContent := utils.ReplaceYAMLField(string(content), "language_pack", "buildpack")
131131
updatedContent = utils.ReplaceYAMLFieldValue(updatedContent, "buildpack", updateResponse.Buildpack.Slug)
132132

133133
err = os.WriteFile(codecraftersYmlPath, []byte(updatedContent), 0644)
134134
if err != nil {
135135
return fmt.Errorf("failed to write updated codecrafters.yml: %w", err)
136136
}
137137

138-
fmt.Printf("Updated buildpack from %s to %s\n", buildpackValue, updateResponse.Buildpack.Slug)
138+
fmt.Printf("Updated buildpack from %s to %s\n", currentBuildpackSlug, updateResponse.Buildpack.Slug)
139139
return nil
140140
}

internal/utils/codecrafters_client.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ type UpdateBuildpackResponse struct {
7878
IsError bool `json:"is_error"`
7979
}
8080

81+
type FetchRepositoryBuildpackResponse struct {
82+
Buildpack BuildpackInfo `json:"buildpack"`
83+
ErrorMessage string `json:"error_message"`
84+
IsError bool `json:"is_error"`
85+
}
86+
8187
type FetchBuildStatusResponse struct {
8288
Status string `json:"status"`
8389

@@ -317,3 +323,33 @@ func (c CodecraftersClient) UpdateBuildpack(repositoryId string) (UpdateBuildpac
317323

318324
return updateBuildpackResponse, nil
319325
}
326+
327+
func (c CodecraftersClient) FetchRepositoryBuildpack(repositoryId string) (FetchRepositoryBuildpackResponse, error) {
328+
response, err := grequests.Get(fmt.Sprintf("%s/services/cli/fetch_repository_buildpack", c.ServerUrl), &grequests.RequestOptions{
329+
Params: map[string]string{
330+
"repository_id": repositoryId,
331+
},
332+
Headers: c.headers(),
333+
})
334+
335+
if err != nil {
336+
return FetchRepositoryBuildpackResponse{}, fmt.Errorf("failed to fetch repository buildpack from CodeCrafters: %s", err)
337+
}
338+
339+
if !response.Ok {
340+
return FetchRepositoryBuildpackResponse{}, fmt.Errorf("failed to fetch repository buildpack from CodeCrafters. status code: %d", response.StatusCode)
341+
}
342+
343+
fetchRepositoryBuildpackResponse := FetchRepositoryBuildpackResponse{}
344+
345+
err = json.Unmarshal(response.Bytes(), &fetchRepositoryBuildpackResponse)
346+
if err != nil {
347+
return FetchRepositoryBuildpackResponse{}, fmt.Errorf("failed to parse fetch repository buildpack response: %s", err)
348+
}
349+
350+
if fetchRepositoryBuildpackResponse.IsError {
351+
return fetchRepositoryBuildpackResponse, fmt.Errorf("%s", fetchRepositoryBuildpackResponse.ErrorMessage)
352+
}
353+
354+
return fetchRepositoryBuildpackResponse, nil
355+
}

internal/utils/yaml_utils.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,13 @@ package utils
22

33
import (
44
"regexp"
5-
"strings"
65
)
76

87
func ReplaceYAMLField(content, oldField, newField string) string {
98
re := regexp.MustCompile(regexp.QuoteMeta(oldField) + `\s*:`)
109
return re.ReplaceAllString(content, newField+":")
1110
}
1211

13-
func ExtractYAMLFieldValue(content, field string) string {
14-
re := regexp.MustCompile(regexp.QuoteMeta(field) + `\s*:\s*([^\n\r]+)`)
15-
if matches := re.FindStringSubmatch(content); len(matches) > 1 {
16-
value := strings.TrimSpace(matches[1])
17-
if (strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`)) ||
18-
(strings.HasPrefix(value, `'`) && strings.HasSuffix(value, `'`)) {
19-
return value[1 : len(value)-1]
20-
}
21-
return value
22-
}
23-
return ""
24-
}
25-
2612
func ReplaceYAMLFieldValue(content, field, newValue string) string {
2713
re := regexp.MustCompile(regexp.QuoteMeta(field) + `\s*:\s*([^\n\r]+)`)
2814
return re.ReplaceAllString(content, field+": "+newValue)

internal/utils/yaml_utils_test.go

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -64,46 +64,6 @@ buildpack: go-1.19
6464
assert.Equal(t, expected, result)
6565
}
6666

67-
func TestExtractYAMLFieldValue(t *testing.T) {
68-
content := `# Available versions: rust-1.88
69-
debug: false
70-
buildpack: rust-1.88
71-
`
72-
value := ExtractYAMLFieldValue(content, "buildpack")
73-
assert.Equal(t, "rust-1.88", value)
74-
75-
value = ExtractYAMLFieldValue(content, "nonexistent")
76-
assert.Equal(t, "", value)
77-
}
78-
79-
func TestExtractYAMLFieldValueWithDoubleQuotes(t *testing.T) {
80-
content := `# Configuration
81-
debug: "false"
82-
buildpack: "rust-1.88"
83-
`
84-
value := ExtractYAMLFieldValue(content, "buildpack")
85-
assert.Equal(t, "rust-1.88", value)
86-
}
87-
88-
func TestExtractYAMLFieldValueWithSingleQuotes(t *testing.T) {
89-
content := `# Project settings
90-
debug: 'false'
91-
buildpack: 'rust-1.88'
92-
`
93-
value := ExtractYAMLFieldValue(content, "buildpack")
94-
assert.Equal(t, "rust-1.88", value)
95-
}
96-
97-
func TestExtractYAMLFieldValueWithWeirdWhitespace(t *testing.T) {
98-
content := `# Messy formatting but comments preserved
99-
debug: false
100-
buildpack : rust-1.88
101-
# Another comment
102-
`
103-
value := ExtractYAMLFieldValue(content, "buildpack")
104-
assert.Equal(t, "rust-1.88", value)
105-
}
106-
10767
func TestReplaceYAMLFieldValue(t *testing.T) {
10868
content := `# Set this to true if you want debug logs.
10969
debug: false

0 commit comments

Comments
 (0)