Skip to content

Commit 9228f24

Browse files
committed
refactor(java): enhance postprocess keep logic, resource handling and style compliance
- Extract newKeepSet and path.Clean normalization for robust file preservation - Refactor ExtractSnippets with extractSnippetsFromFile helper to ensure defer f.Close() - Migrate os.IsNotExist to errors.Is(err, fs.ErrNotExist) across java package - Standardize log/slog logging and correct unexported acronym casing - Replace manual test assertions with cmp.Diff and remove non-thread-safe t.Chdir
1 parent 3ecc331 commit 9228f24

9 files changed

Lines changed: 134 additions & 93 deletions

File tree

internal/librarian/java/clean.go

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ package java
1717
import (
1818
"bufio"
1919
"errors"
20-
"fmt"
2120
"io"
21+
"log/slog"
2222
"io/fs"
2323
"os"
2424
"path"
@@ -56,11 +56,8 @@ var (
5656
// It targets patterns like proto-*, grpc-*, and the main GAPIC module.
5757
func Clean(library *config.Library) error {
5858
patterns := cleanPatterns(library)
59-
fmt.Printf("Clean patterns for %s: %v\n", library.Output, patterns)
60-
keepSet := make(map[string]bool)
61-
for _, k := range library.Keep {
62-
keepSet[strings.TrimSuffix(filepath.ToSlash(k), "/")] = true
63-
}
59+
slog.Debug("Clean patterns", "output", library.Output, "patterns", patterns)
60+
keepSet := newKeepSet(library.Keep)
6461
for pattern, useMarker := range patterns {
6562
matches, err := filepath.Glob(filepath.Join(library.Output, pattern))
6663
if err != nil {
@@ -145,8 +142,7 @@ func cleanPath(targetPath, root string, keepSet map[string]bool, useMarker bool)
145142
return nil
146143
}
147144
}
148-
fmt.Println("DELETING:", path)
149-
fmt.Println("DELETING:", path)
145+
slog.Info("Deleting path", "path", path)
150146
return os.Remove(path)
151147
})
152148
if err != nil && !errors.Is(err, fs.ErrNotExist) {
@@ -173,15 +169,28 @@ func isDirNotEmpty(err error) bool {
173169
return errors.Is(err, syscall.ENOTEMPTY) || errors.Is(err, syscall.EEXIST)
174170
}
175171

176-
// shouldPreserve returns true if the given slash-separated path should be preserved
177-
// based on the keepSet or standard preservation patterns.
178-
// It also checks if any ancestor directory is in the keepSet.
179-
func shouldPreserve(p string, keepSet map[string]bool) bool {
180-
if keepSet[p] || isDefaultPreserved(p) {
172+
// newKeepSet normalizes a list of keep paths using [path.Clean] and returns
173+
// a map for fast lookup.
174+
func newKeepSet(keep []string) map[string]bool {
175+
keepSet := make(map[string]bool)
176+
for _, k := range keep {
177+
normalized := path.Clean(filepath.ToSlash(k))
178+
if normalized == "." {
179+
continue
180+
}
181+
keepSet[strings.TrimSuffix(normalized, "/")] = true
182+
}
183+
return keepSet
184+
}
185+
186+
// isKept returns true if the path is explicitly kept by the user.
187+
func isKept(p string, keepSet map[string]bool) bool {
188+
cleanP := path.Clean(p)
189+
if keepSet[cleanP] {
181190
return true
182191
}
183192

184-
dir := path.Dir(p)
193+
dir := path.Dir(cleanP)
185194
for dir != "." && dir != "/" && dir != "" {
186195
if keepSet[dir] {
187196
return true
@@ -191,6 +200,13 @@ func shouldPreserve(p string, keepSet map[string]bool) bool {
191200
return false
192201
}
193202

203+
// shouldPreserve returns true if the given slash-separated path should be preserved
204+
// based on the keepSet or standard preservation patterns.
205+
// It also checks if any ancestor directory is in the keepSet.
206+
func shouldPreserve(p string, keepSet map[string]bool) bool {
207+
return isKept(p, keepSet) || isDefaultPreserved(p)
208+
}
209+
194210
func isDefaultPreserved(path string) bool {
195211
return itTestRegexp.MatchString(path) || versionRegexp.MatchString(path)
196212
}

internal/librarian/java/generate_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package java
1717
import (
1818
"context"
1919
"errors"
20+
"io/fs"
2021
"os"
2122
"path/filepath"
2223
"strings"
@@ -1113,7 +1114,7 @@ func TestGenerateAPI_Gating(t *testing.T) {
11131114
if gotProtoDir {
11141115
resNameFile := filepath.Join(stagingProtoPath, "com", "google", "cloud", "secretmanager", "v1", "SecretName.java")
11151116
_, errRes := os.Stat(resNameFile)
1116-
gotResNameFiles := !os.IsNotExist(errRes)
1117+
gotResNameFiles := !errors.Is(errRes, fs.ErrNotExist)
11171118
if gotResNameFiles != test.wantResNameFiles {
11181119
t.Errorf("gotResNameFiles = %v, want %v (file: %s)", gotResNameFiles, test.wantResNameFiles, resNameFile)
11191120
}
@@ -1125,7 +1126,7 @@ func TestGenerateAPI_Gating(t *testing.T) {
11251126
func assertDirExists(t *testing.T, path string, want bool, desc string) bool {
11261127
t.Helper()
11271128
_, err := os.Stat(path)
1128-
got := !os.IsNotExist(err)
1129+
got := !errors.Is(err, fs.ErrNotExist)
11291130
if got != want {
11301131
t.Errorf("expected %s existence to be %v, got %v (path: %s)", desc, want, got, path)
11311132
}

internal/librarian/java/postprocess.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,7 @@ func postProcessAPI(ctx context.Context, params postProcessParams) error {
144144
coords := params.coords()
145145

146146
if params.useGoPostprocessor {
147-
keepSet := make(map[string]bool)
148-
for _, k := range params.library.Keep {
149-
keepSet[strings.TrimSuffix(filepath.ToSlash(k), "/")] = true
150-
}
147+
keepSet := newKeepSet(params.library.Keep)
151148
if err := restructureModules(params, params.outDir, keepSet, params.outDir); err != nil {
152149
return fmt.Errorf("failed to restructure direct to outDir: %w", err)
153150
}
@@ -495,14 +492,10 @@ func copyProtos(protos []protoFileToCopy, destDir string) error {
495492
// then matched against the library's Keep configuration.
496493
func removeKeptFilesFromStaging(library *config.Library, outDir string) error {
497494
stagingDir := stagingDir(outDir)
498-
if _, err := os.Stat(stagingDir); os.IsNotExist(err) {
495+
if _, err := os.Stat(stagingDir); errors.Is(err, fs.ErrNotExist) {
499496
return nil
500497
}
501-
keepSet := make(map[string]bool)
502-
for _, keep := range library.Keep {
503-
normalized := strings.TrimSuffix(filepath.ToSlash(keep), "/")
504-
keepSet[normalized] = true
505-
}
498+
keepSet := newKeepSet(library.Keep)
506499
return filepath.WalkDir(stagingDir, func(path string, d os.DirEntry, err error) error {
507500
if err != nil {
508501
return err

internal/librarian/java/postprocess_new.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,15 @@ import (
3030
// It applies post-processing operations configured in librarian.yaml, and renders the README.md directly
3131
// on the generated files in their final destinations.
3232
func postProcessLibraryNew(ctx context.Context, p libraryPostProcessParams) error {
33+
keepSet := newKeepSet(p.library.Keep)
34+
3335
// 1. Load postprocess configuration and apply operations
3436
if p.library.Postprocess != nil {
3537
if err := postprocessing.Validate(p.library.Postprocess); err != nil {
3638
return fmt.Errorf("invalid postprocess config: %w", err)
3739
}
3840
cfg := p.library.Postprocess
3941

40-
keepSet := make(map[string]bool)
41-
for _, k := range p.library.Keep {
42-
keepSet[strings.TrimSuffix(filepath.ToSlash(k), "/")] = true
43-
}
44-
4542
// 1. Apply Copies
4643
for _, c := range cfg.CopyFile {
4744
if shouldPreserve(filepath.ToSlash(c.Dst), keepSet) {
@@ -138,7 +135,7 @@ func postProcessLibraryNew(ctx context.Context, p libraryPostProcessParams) erro
138135
return fmt.Errorf("failed to find BOM version: %w", err)
139136
}
140137

141-
if err := RenderREADME(p.outDir, p.metadata, bomVersion, libraryVersion); err != nil {
138+
if err := RenderREADME(p.outDir, p.metadata, bomVersion, libraryVersion, keepSet); err != nil {
142139
return fmt.Errorf("failed to render README: %w", err)
143140
}
144141

@@ -161,8 +158,11 @@ func applyToFiles(outDir string, pathPattern string, keepSet map[string]bool, ac
161158
var replacedAny bool
162159
var lastTextNotFoundErr error
163160
for _, file := range files {
164-
relPath, _ := filepath.Rel(outDir, file)
165-
if shouldPreserve(filepath.ToSlash(relPath), keepSet) {
161+
relPath, err := filepath.Rel(outDir, file)
162+
if err != nil {
163+
return fmt.Errorf("failed to get relative path for %s: %w", file, err)
164+
}
165+
if isKept(filepath.ToSlash(relPath), keepSet) {
166166
continue
167167
}
168168
if err := action(file); err != nil {

internal/librarian/java/postprocess_new_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525

2626
func TestPostProcessLibraryNew(t *testing.T) {
2727
tmpDir := t.TempDir()
28-
t.Chdir(tmpDir)
2928

3029
// Setup structure directly in outDir
3130
destDir := filepath.Join(tmpDir, "my-module", "src", "main", "java")

internal/librarian/java/postprocess_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,7 +1172,6 @@ func TestPostProcessLibrary_Branching(t *testing.T) {
11721172

11731173
t.Run("UseGoPostprocessor true, no yaml, success", func(t *testing.T) {
11741174
outDir := t.TempDir()
1175-
t.Chdir(outDir)
11761175

11771176
if err := os.MkdirAll(filepath.Join(outDir, "owl-bot-staging"), 0755); err != nil {
11781177
t.Fatal(err)
@@ -1221,7 +1220,6 @@ func TestPostProcessLibrary_Branching(t *testing.T) {
12211220

12221221
t.Run("UseGoPostprocessor true, with postprocess config in library", func(t *testing.T) {
12231222
outDir := t.TempDir()
1224-
t.Chdir(outDir)
12251223

12261224
if err := os.MkdirAll(filepath.Join(outDir, "owl-bot-staging"), 0755); err != nil {
12271225
t.Fatal(err)

internal/librarian/java/readme.go

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func ExtractSnippets(dir string) (map[string]string, error) {
146146

147147
err := filepath.WalkDir(samplesDir, func(path string, d os.DirEntry, err error) error {
148148
if err != nil {
149-
if errors.Is(err, fs.ErrNotExist) || os.IsNotExist(err) {
149+
if errors.Is(err, fs.ErrNotExist) {
150150
return nil
151151
}
152152
return err
@@ -181,44 +181,9 @@ func ExtractSnippets(dir string) (map[string]string, error) {
181181
snippetLines := make(map[string][]string)
182182

183183
for _, file := range files {
184-
f, err := os.Open(file)
185-
if err != nil {
186-
continue
187-
}
188-
189-
openSnippets := make(map[string]bool)
190-
excluding := false
191-
scanner := bufio.NewScanner(f)
192-
scanner.Buffer(make([]byte, 64*1024), 10*1024*1024)
193-
194-
for scanner.Scan() {
195-
line := scanner.Text()
196-
openMatch := openSnippetRegex.FindStringSubmatch(line)
197-
closeMatch := closeSnippetRegex.FindStringSubmatch(line)
198-
199-
if len(openMatch) > 1 && !excluding {
200-
name := openMatch[1]
201-
openSnippets[name] = true
202-
if _, exists := snippetLines[name]; !exists {
203-
snippetLines[name] = []string{}
204-
}
205-
} else if len(closeMatch) > 1 && !excluding {
206-
delete(openSnippets, closeMatch[1])
207-
} else if openExcludeRegex.MatchString(line) {
208-
excluding = true
209-
} else if closeExcludeRegex.MatchString(line) {
210-
excluding = false
211-
} else if !excluding {
212-
for s := range openSnippets {
213-
snippetLines[s] = append(snippetLines[s], line)
214-
}
215-
}
216-
}
217-
if err := scanner.Err(); err != nil {
218-
f.Close()
184+
if err := extractSnippetsFromFile(file, snippetLines); err != nil {
219185
return nil, err
220186
}
221-
f.Close()
222187
}
223188

224189
if len(snippetLines) == 0 {
@@ -267,3 +232,45 @@ func trimLeadingWhitespace(lines []string) string {
267232
}
268233
return sb.String()
269234
}
235+
236+
// extractSnippetsFromFile parses a single file to extract tagged code snippets.
237+
func extractSnippetsFromFile(file string, snippetLines map[string][]string) error {
238+
f, err := os.Open(file)
239+
if err != nil {
240+
return fmt.Errorf("failed to open file %s: %w", file, err)
241+
}
242+
defer f.Close()
243+
244+
openSnippets := make(map[string]bool)
245+
excluding := false
246+
scanner := bufio.NewScanner(f)
247+
scanner.Buffer(make([]byte, 64*1024), 10*1024*1024)
248+
249+
for scanner.Scan() {
250+
line := scanner.Text()
251+
openMatch := openSnippetRegex.FindStringSubmatch(line)
252+
closeMatch := closeSnippetRegex.FindStringSubmatch(line)
253+
254+
if len(openMatch) > 1 && !excluding {
255+
name := openMatch[1]
256+
openSnippets[name] = true
257+
if _, exists := snippetLines[name]; !exists {
258+
snippetLines[name] = []string{}
259+
}
260+
} else if len(closeMatch) > 1 && !excluding {
261+
delete(openSnippets, closeMatch[1])
262+
} else if openExcludeRegex.MatchString(line) {
263+
excluding = true
264+
} else if closeExcludeRegex.MatchString(line) {
265+
excluding = false
266+
} else if !excluding {
267+
for s := range openSnippets {
268+
snippetLines[s] = append(snippetLines[s], line)
269+
}
270+
}
271+
}
272+
if err := scanner.Err(); err != nil {
273+
return fmt.Errorf("failed scanning file %s: %w", file, err)
274+
}
275+
return nil
276+
}

internal/librarian/java/template_render.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"embed"
1919
"errors"
2020
"fmt"
21+
"io/fs"
2122
"os"
2223
"path/filepath"
2324
"strings"
@@ -28,16 +29,20 @@ import (
2829
)
2930

3031
//go:embed template/README.md.go.tmpl
31-
var defaultTemplateFS embed.FS
32+
var defaultTemplateFs embed.FS
3233

3334
// RenderREADME renders the README.md file using the template and metadata.
3435
// dir is the directory containing where README.md will be written.
35-
func RenderREADME(dir string, metadata *repoMetadata, bomVersion, libraryVersion string) error {
36+
func RenderREADME(dir string, metadata *repoMetadata, bomVersion, libraryVersion string, keepSet map[string]bool) error {
37+
outputPath := filepath.Join(dir, "README.md")
38+
if isKept("README.md", keepSet) {
39+
return nil
40+
}
41+
3642
partialsPath := filepath.Join(dir, ".readme-partials.yaml")
37-
if _, err := os.Stat(partialsPath); os.IsNotExist(err) {
43+
if _, err := os.Stat(partialsPath); errors.Is(err, fs.ErrNotExist) {
3844
partialsPath = filepath.Join(dir, ".readme-partials.yml")
3945
}
40-
outputPath := filepath.Join(dir, "README.md")
4146

4247
// Read partials if exist
4348
var partials map[string]interface{}
@@ -62,13 +67,13 @@ func RenderREADME(dir string, metadata *repoMetadata, bomVersion, libraryVersion
6267
// Prepare data for template
6368
distName := metadata.DistributionName
6469
distParts := strings.Split(distName, ":")
65-
groupID := ""
66-
artifactID := ""
70+
groupId := ""
71+
artifactId := ""
6772
if len(distParts) > 0 {
68-
groupID = distParts[0]
73+
groupId = distParts[0]
6974
}
7075
if len(distParts) > 1 {
71-
artifactID = distParts[1]
76+
artifactId = distParts[1]
7277
}
7378

7479
repoName := metadata.Repo
@@ -84,7 +89,6 @@ func RenderREADME(dir string, metadata *repoMetadata, bomVersion, libraryVersion
8489
if minJavaVersion == 0 {
8590
minJavaVersion = 8 // Default to Java 8
8691
}
87-
fmt.Println("DEBUG minJavaVersion:", minJavaVersion)
8892

8993
samples, err := ExtractSamples(dir)
9094
if err != nil {
@@ -121,8 +125,8 @@ func RenderREADME(dir string, metadata *repoMetadata, bomVersion, libraryVersion
121125
LibraryVersion string
122126
}{
123127
Metadata: templateMetadata,
124-
GroupID: groupID,
125-
ArtifactID: artifactID,
128+
GroupID: groupId,
129+
ArtifactID: artifactId,
126130
Version: version,
127131
RepoShort: repoShort,
128132
MigratedSplitRepo: false,
@@ -135,9 +139,9 @@ func RenderREADME(dir string, metadata *repoMetadata, bomVersion, libraryVersion
135139
templatePath := filepath.Join(dir, "template", "README.md.go.tmpl")
136140
tmplBytes, err := os.ReadFile(templatePath)
137141
if err != nil {
138-
if errors.Is(err, os.ErrNotExist) {
142+
if errors.Is(err, fs.ErrNotExist) {
139143
// Fallback to embedded default template
140-
tmplBytes, err = defaultTemplateFS.ReadFile("template/README.md.go.tmpl")
144+
tmplBytes, err = defaultTemplateFs.ReadFile("template/README.md.go.tmpl")
141145
}
142146
if err != nil {
143147
return fmt.Errorf("failed to read template: %w", err)

0 commit comments

Comments
 (0)