Skip to content

Commit 4e002e3

Browse files
authored
feat: record dependency version and auto upgrade (#276)
* feat: record dependency version and auto upgrade * add dep version json file * fix version compare * pin deps * fix * remove cargo.lock * use `deps.json` * minor * minor
1 parent 5d8d653 commit 4e002e3

File tree

12 files changed

+220
-105
lines changed

12 files changed

+220
-105
lines changed

Diff for: config/config.go

+4
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ func (c *Config) StateFile() string {
170170
return filepath.Join(c.CacheDir(), constants.StateFilename)
171171
}
172172

173+
func (c *Config) DepVersionFile() string {
174+
return filepath.Join(c.CacheDir(), constants.DepVersionFilename)
175+
}
176+
173177
func (c *Config) QuestionCacheFile(ext string) string {
174178
return filepath.Join(c.CacheDir(), constants.QuestionCacheBaseName+ext)
175179
}

Diff for: constants/constants.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ const (
1111
ConfigFilename = "leetgo.yaml"
1212
QuestionCacheBaseName = "leetcode-questions"
1313
StateFilename = "state.json"
14+
DepVersionFilename = "deps.json"
1415
CodeBeginMarker = "@lc code=begin"
1516
CodeEndMarker = "@lc code=end"
1617
ProjectURL = "https://github.com/j178/leetgo"
17-
GoTestUtilsModPath = "github.com/j178/leetgo/testutils/go"
18-
RustTestUtilsCrate = "leetgo_rs"
19-
PythonTestUtilsMode = "leetgo_py"
2018
)

Diff for: lang/base.go

+6-8
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,14 @@ type Lang interface {
103103
ShortName() string
104104
// Slug returns the slug of the language. e.g. "cpp", "javascript", "python3"
105105
Slug() string
106+
// InitWorkspace initializes the language workspace for code running.
107+
InitWorkspace(dir string) error
106108
// Generate generates code files for the question.
107109
Generate(q *leetcode.QuestionData) (*GenerateResult, error)
108110
// GeneratePaths generates the paths of the code files for the question, without generating the real files.
109111
GeneratePaths(q *leetcode.QuestionData) (*GenerateResult, error)
110112
}
111113

112-
// NeedInitialization is an interface for languages that need to be initialized before generating code.
113-
type NeedInitialization interface {
114-
// HasInitialized returns whether the language workspace has been initialized.
115-
HasInitialized(dir string) (bool, error)
116-
// Initialize initializes the language workspace.
117-
Initialize(dir string) error
118-
}
119-
120114
// LocalTestable is an interface for languages that can run local test.
121115
type LocalTestable interface {
122116
// RunLocalTest runs local test for the question.
@@ -369,6 +363,10 @@ func (l baseLang) ShortName() string {
369363
return l.shortName
370364
}
371365

366+
func (l baseLang) InitWorkspace(_ string) error {
367+
return nil
368+
}
369+
372370
func (l baseLang) generateCodeContent(
373371
q *leetcode.QuestionData,
374372
blocks []config.Block,

Diff for: lang/cpp.go

+15-11
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/google/shlex"
99

1010
"github.com/j178/leetgo/config"
11-
"github.com/j178/leetgo/constants"
1211
"github.com/j178/leetgo/leetcode"
1312
cppUtils "github.com/j178/leetgo/testutils/cpp"
1413
"github.com/j178/leetgo/utils"
@@ -18,7 +17,11 @@ type cpp struct {
1817
baseLang
1918
}
2019

21-
func (c cpp) Initialize(outDir string) error {
20+
func (c cpp) InitWorkspace(outDir string) error {
21+
if should, err := c.shouldInit(outDir); err != nil || !should {
22+
return err
23+
}
24+
2225
headerPath := filepath.Join(outDir, cppUtils.HeaderName)
2326
err := utils.WriteFile(headerPath, cppUtils.HeaderContent)
2427
if err != nil {
@@ -29,28 +32,29 @@ func (c cpp) Initialize(outDir string) error {
2932
if err != nil {
3033
return err
3134
}
32-
return nil
35+
36+
err = UpdateDep(c)
37+
return err
3338
}
3439

35-
func (c cpp) HasInitialized(outDir string) (bool, error) {
40+
func (c cpp) shouldInit(outDir string) (bool, error) {
3641
headerPath := filepath.Join(outDir, cppUtils.HeaderName)
3742
if !utils.IsExist(headerPath) {
38-
return false, nil
43+
return true, nil
3944
}
4045
stdCxxPath := filepath.Join(outDir, "bits", "stdc++.h")
4146
if !utils.IsExist(stdCxxPath) {
42-
return false, nil
47+
return true, nil
4348
}
4449

45-
version, err := ReadVersion(headerPath)
50+
update, err := IsDepUpdateToDate(c)
4651
if err != nil {
4752
return false, err
4853
}
49-
currVersion := constants.Version
50-
if version != currVersion {
51-
return false, nil
54+
if !update {
55+
return true, nil
5256
}
53-
return true, nil
57+
return false, nil
5458
}
5559

5660
var cppTypes = map[string]string{

Diff for: lang/dep.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package lang
2+
3+
import (
4+
"errors"
5+
"os"
6+
7+
"github.com/goccy/go-json"
8+
9+
"github.com/j178/leetgo/config"
10+
)
11+
12+
// If client dependency needs to be updated, update this version number.
13+
var depVersions = map[string]int{
14+
cppGen.slug: 1,
15+
golangGen.slug: 1,
16+
python3Gen.slug: 1,
17+
rustGen.slug: 1,
18+
}
19+
20+
func readDepVersions() (map[string]int, error) {
21+
depVersionFile := config.Get().DepVersionFile()
22+
records := make(map[string]int)
23+
f, err := os.Open(depVersionFile)
24+
if errors.Is(err, os.ErrNotExist) {
25+
return records, nil
26+
}
27+
if err != nil {
28+
return nil, err
29+
}
30+
defer f.Close()
31+
err = json.NewDecoder(f).Decode(&records)
32+
if err != nil {
33+
return nil, err
34+
}
35+
return records, nil
36+
}
37+
38+
func IsDepUpdateToDate(lang Lang) (bool, error) {
39+
ver := depVersions[lang.Slug()]
40+
if ver == 0 {
41+
return true, nil
42+
}
43+
44+
records, err := readDepVersions()
45+
if err != nil {
46+
return false, err
47+
}
48+
old := records[lang.Slug()]
49+
if old == 0 || old != ver {
50+
return false, nil
51+
}
52+
53+
return true, nil
54+
}
55+
56+
func UpdateDep(lang Lang) error {
57+
ver := depVersions[lang.Slug()]
58+
if ver == 0 {
59+
return nil
60+
}
61+
62+
records, err := readDepVersions()
63+
if err != nil {
64+
return err
65+
}
66+
67+
records[lang.Slug()] = ver
68+
69+
depVersionFile := config.Get().DepVersionFile()
70+
f, err := os.Create(depVersionFile)
71+
if err != nil {
72+
return err
73+
}
74+
defer f.Close()
75+
enc := json.NewEncoder(f)
76+
enc.SetIndent("", " ")
77+
err = enc.Encode(records)
78+
if err != nil {
79+
return err
80+
}
81+
82+
return nil
83+
}

Diff for: lang/gen.go

+3-16
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,9 @@ func generate(q *leetcode.QuestionData) (Lang, *GenerateResult, error) {
7474
return nil, nil, err
7575
}
7676

77-
// Check and generate necessary library files.
78-
if t, ok := gen.(NeedInitialization); ok {
79-
ok, err := t.HasInitialized(outDir)
80-
if err == nil && !ok {
81-
err = t.Initialize(outDir)
82-
if err != nil {
83-
return nil, nil, err
84-
}
85-
}
86-
if err != nil {
87-
log.Error(
88-
"check initialization failed, skip initialization",
89-
"lang", gen.Slug(),
90-
"err", err,
91-
)
92-
}
77+
err = gen.InitWorkspace(outDir)
78+
if err != nil {
79+
return nil, nil, err
9380
}
9481

9582
// Generate files

Diff for: lang/go.go

+35-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package lang
22

33
import (
4-
"bytes"
54
"fmt"
65
"io"
76
"os"
@@ -12,11 +11,16 @@ import (
1211
"github.com/charmbracelet/log"
1312

1413
"github.com/j178/leetgo/config"
15-
"github.com/j178/leetgo/constants"
1614
"github.com/j178/leetgo/leetcode"
1715
"github.com/j178/leetgo/utils"
1816
)
1917

18+
const leetgoGo = "github.com/j178/leetgo/testutils/go"
19+
20+
var goDeps = []string{
21+
leetgoGo + "@v0.2.0",
22+
}
23+
2024
type golang struct {
2125
baseLang
2226
}
@@ -102,44 +106,56 @@ func addMod(code string, q *leetcode.QuestionData) string {
102106
return strings.Join(newLines, "\n")
103107
}
104108

105-
func (g golang) HasInitialized(outDir string) (bool, error) {
109+
func (g golang) shouldInit(outDir string) (bool, error) {
106110
if !utils.IsExist(filepath.Join(outDir, "go.mod")) {
107-
return false, nil
111+
return true, nil
108112
}
109-
cmd := exec.Command("go", "list", "-m", "-json", constants.GoTestUtilsModPath)
110-
cmd.Dir = outDir
111-
output, err := cmd.CombinedOutput()
113+
114+
update, err := IsDepUpdateToDate(g)
112115
if err != nil {
113-
if bytes.Contains(output, []byte("not a known dependency")) || bytes.Contains(
114-
output,
115-
[]byte("go.mod file not found"),
116-
) {
117-
return false, nil
118-
}
119-
return false, fmt.Errorf("go list failed: %w: %s", err, output)
116+
return false, err
117+
}
118+
if !update {
119+
return true, nil
120120
}
121-
return true, nil
121+
return false, nil
122122
}
123123

124-
func (g golang) Initialize(outDir string) error {
124+
func (g golang) InitWorkspace(outDir string) error {
125+
if should, err := g.shouldInit(outDir); err != nil || !should {
126+
return err
127+
}
128+
129+
err := utils.RemoveIfExist(filepath.Join(outDir, "go.mod"))
130+
if err != nil {
131+
return err
132+
}
133+
_ = utils.RemoveIfExist(filepath.Join(outDir, "go.sum"))
134+
125135
const modPath = "leetcode-solutions"
126136
var stderr strings.Builder
127137
cmd := exec.Command("go", "mod", "init", modPath)
128138
log.Info("go mod init", "cmd", cmd.String())
129139
cmd.Dir = outDir
130140
cmd.Stdout = os.Stdout
131141
cmd.Stderr = io.MultiWriter(os.Stderr, &stderr)
132-
err := cmd.Run()
142+
err = cmd.Run()
133143
if err != nil && !strings.Contains(stderr.String(), "go.mod already exists") {
134144
return err
135145
}
136146

137-
cmd = exec.Command("go", "get", constants.GoTestUtilsModPath)
147+
cmd = exec.Command("go", "get")
148+
cmd.Args = append(cmd.Args, goDeps...)
138149
log.Info("go get", "cmd", cmd.String())
139150
cmd.Dir = outDir
140151
cmd.Stdout = os.Stdout
141152
cmd.Stderr = os.Stderr
142153
err = cmd.Run()
154+
if err != nil {
155+
return err
156+
}
157+
158+
err = UpdateDep(g)
143159
return err
144160
}
145161

@@ -350,7 +366,7 @@ import (
350366
"os"
351367
352368
. "%s"
353-
)`, constants.GoTestUtilsModPath,
369+
)`, leetgoGo,
354370
)
355371
testContent, err := g.generateTestContent(q)
356372
if err != nil {

0 commit comments

Comments
 (0)