Skip to content

Commit 6e5194d

Browse files
authored
Merge pull request #110 from Peefy/feat-groupby-different-crd-version-files
feat: groupby different kcl files according to the api version
2 parents 9e8f91d + 46992e9 commit 6e5194d

File tree

3 files changed

+186
-3
lines changed

3 files changed

+186
-3
lines changed

pkg/fs/util.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package fs
22

33
import (
44
"fmt"
5+
"io"
56
"os"
67
"path/filepath"
78
)
@@ -56,3 +57,31 @@ func FileExists(path string) bool {
5657
}
5758
return true
5859
}
60+
61+
// IsEmptyDir checks if a directory is empty.
62+
// It takes a string parameter `name` representing the directory path.
63+
// It returns a boolean value indicating whether the directory is empty or not,
64+
// and an error if any occurred during the process.
65+
//
66+
// Parameters:
67+
// - name: The path of the directory to check.
68+
//
69+
// Returns:
70+
// - bool: True if the directory is empty, false otherwise.
71+
// - error: An error if the directory cannot be read.
72+
//
73+
// Example usage:
74+
// empty, err := IsEmptyDir("/path/to/directory")
75+
func IsEmptyDir(name string) (bool, error) {
76+
f, err := os.Open(name)
77+
if err != nil {
78+
return false, err
79+
}
80+
defer f.Close()
81+
82+
_, err = f.Readdirnames(1)
83+
if err == io.EOF {
84+
return true, nil
85+
}
86+
return false, err
87+
}

pkg/import/crd/groupby.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copy from https://github.com/DavidChevallier/CRDtoKCL/blob/main/main.go
2+
package crd
3+
4+
import (
5+
"os"
6+
"path/filepath"
7+
"regexp"
8+
"strings"
9+
10+
"kcl-lang.io/cli/pkg/fs"
11+
"kcl-lang.io/kpm/pkg/opt"
12+
pkg "kcl-lang.io/kpm/pkg/package"
13+
)
14+
15+
// knownAPIVersions is a slice of strings that contains the known API versions.
16+
var knownAPIVersions = []string{
17+
"v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10",
18+
"v1alpha1", "v1alpha2", "v1alpha3", "v1alpha4", "v1alpha5",
19+
"v2alpha1", "v2alpha2", "v2alpha3", "v2alpha4", "v2alpha5",
20+
"v3alpha1", "v3alpha2", "v3alpha3", "v3alpha4", "v3alpha5",
21+
"v1beta1", "v1beta2", "v1beta3", "v1beta4", "v1beta5",
22+
"v2beta1", "v2beta2", "v2beta3", "v2beta4", "v2beta5",
23+
"v3beta1", "v3beta2", "v3beta3", "v3beta4", "v3beta5",
24+
}
25+
26+
const unknownVersion = "unknown"
27+
28+
// moveKclFiles moves files with the ".k" extension to a directory based on their API version.
29+
// It walks through the specified base directory and for each file with the ".k" extension,
30+
// it determines the API version based on the file name and moves the file to a subdirectory
31+
// named after the API version. If the API version cannot be determined, the file is moved
32+
// to a subdirectory named "unknown".
33+
//
34+
// Parameters:
35+
// - baseDir: The base directory to search for files.
36+
func GroupByKclFiles(baseDir string) error {
37+
entries, err := os.ReadDir(baseDir)
38+
if err != nil {
39+
return err
40+
}
41+
for _, entry := range entries {
42+
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".k") {
43+
continue
44+
}
45+
46+
apiVersion, err := extractAPIVersionFromName(entry.Name(), knownAPIVersions)
47+
if err != nil {
48+
apiVersion = unknownVersion
49+
}
50+
51+
newDir := filepath.Join(baseDir, apiVersion)
52+
os.MkdirAll(newDir, os.ModePerm)
53+
54+
oldPath := filepath.Join(baseDir, entry.Name())
55+
newPath := filepath.Join(newDir, entry.Name())
56+
57+
err = os.Rename(oldPath, newPath)
58+
if err != nil {
59+
return err
60+
}
61+
}
62+
kclPkg := pkg.NewKclPkg(&opt.InitOptions{
63+
Name: filepath.Base(baseDir),
64+
InitPath: baseDir,
65+
})
66+
err = kclPkg.ModFile.StoreModFile()
67+
if err != nil {
68+
return err
69+
}
70+
return removeEmptyDirs(baseDir)
71+
}
72+
73+
// removeEmptyDirs removes all empty directories within the specified directory.
74+
// It recursively walks through the directory and checks if each directory is empty.
75+
// If an empty directory is found, it is removed. The function stops when there are no more empty directories left.
76+
//
77+
// Parameters:
78+
// - dir: The directory path to start the search from.
79+
//
80+
// Example usage:
81+
// removeEmptyDirs("/path/to/directory")
82+
//
83+
// Note: This function does not handle errors related to directory traversal or removal.
84+
func removeEmptyDirs(dir string) error {
85+
for {
86+
var emptyDirs []string
87+
88+
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
89+
if err != nil {
90+
return err
91+
}
92+
if info.IsDir() {
93+
empty, err := fs.IsEmptyDir(path)
94+
if err != nil {
95+
return err
96+
}
97+
if empty {
98+
emptyDirs = append(emptyDirs, path)
99+
}
100+
}
101+
return nil
102+
})
103+
104+
if len(emptyDirs) == 0 {
105+
break
106+
}
107+
108+
for i := len(emptyDirs) - 1; i >= 0; i-- {
109+
err := os.Remove(emptyDirs[i])
110+
if err != nil {
111+
return err
112+
}
113+
}
114+
}
115+
return nil
116+
}
117+
118+
// extractAPIVersionFromName extracts the API version from the given name string.
119+
// It searches for a pattern in the name string and returns the extracted API version.
120+
// If the API version is found and is known (present in the knownAPIVersions slice),
121+
// it returns the API version string. Otherwise, it returns "unknown".
122+
//
123+
// Parameters:
124+
// - name: The name string to extract the API version from.
125+
// - knownAPIVersions: A slice of known API versions.
126+
//
127+
// Returns:
128+
// - string: The extracted API version or "unknown" if not found.
129+
// - error: An error if the extraction fails.
130+
//
131+
// Example usage:
132+
// version, err := extractAPIVersionFromName("example_v1alpha1", knownAPIVersions)
133+
func extractAPIVersionFromName(name string, knownAPIVersions []string) (string, error) {
134+
re := regexp.MustCompile(`_v([0-9a-zA-Z]+)`)
135+
matches := re.FindStringSubmatch(name)
136+
if len(matches) > 1 {
137+
apiVersion := "v" + matches[1]
138+
for _, v := range knownAPIVersions {
139+
if apiVersion == v {
140+
return apiVersion, nil
141+
}
142+
}
143+
}
144+
return unknownVersion, nil
145+
}

pkg/options/import.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"kcl-lang.io/cli/pkg/fs"
12+
"kcl-lang.io/cli/pkg/import/crd"
1213
"kcl-lang.io/kcl-go/pkg/logger"
1314
"kcl-lang.io/kcl-go/pkg/tools/gen"
1415
crdGen "kcl-lang.io/kcl-openapi/pkg/kube_resource/generator"
@@ -58,7 +59,7 @@ func (o *ImportOptions) Run() error {
5859
case Crd, OpenAPI:
5960
for _, p := range files {
6061
opts := new(generator.GenOpts)
61-
// cli opts to generator.GenOpts
62+
// Convert CLI options to generator.GenOpts
6263
opts.Spec = p
6364
if o.Output != "" {
6465
opts.Target = o.Output
@@ -67,12 +68,12 @@ func (o *ImportOptions) Run() error {
6768
}
6869
opts.ValidateSpec = !o.SkipValidation
6970
opts.ModelPackage = o.ModelPackage
70-
// set default configurations
71+
// Set default configurations
7172
if err := opts.EnsureDefaults(); err != nil {
7273
return err
7374
}
7475
var specs []string
75-
// when the spec is a crd, get openapi spec file from it
76+
// When the spec is a crd, get OpenAPI spec file from it
7677
if mode == Crd {
7778
specs, err = crdGen.GetSpecs(&crdGen.GenOpts{
7879
Spec: opts.Spec,
@@ -85,12 +86,20 @@ func (o *ImportOptions) Run() error {
8586
} else {
8687
specs = []string{opts.Spec}
8788
}
89+
// Generate specs to KCL files
8890
for _, spec := range specs {
8991
opts.Spec = spec
9092
if err := generator.Generate(opts); err != nil {
9193
logger.GetLogger().Error(err)
9294
}
9395
}
96+
// Group by the api version
97+
if mode == Crd {
98+
err := crd.GroupByKclFiles(opts.ModelPackage)
99+
if err != nil {
100+
return err
101+
}
102+
}
94103
}
95104
return nil
96105
default:

0 commit comments

Comments
 (0)