Skip to content

Commit 3b2a2b6

Browse files
committed
fix after changes to prev. PR
1 parent 2190492 commit 3b2a2b6

File tree

2 files changed

+24
-59
lines changed

2 files changed

+24
-59
lines changed

internal/app/azldev/cmds/component/render.go

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func RenderComponents(env *azldev.Env, options *RenderOptions) ([]*RenderResult,
162162
results := make([]*RenderResult, len(componentList))
163163

164164
// ── Phase 1: Parallel source preparation ──
165-
prepared := parallelPrepare(env, componentList, stagingDir, results)
165+
prepared := parallelPrepare(env, componentList, stagingDir, options.OutputDir, options.Force, results)
166166

167167
// ── Phase 2: Batch mock processing ──
168168
mockResultMap := batchMockProcess(env, mockProcessor, stagingDir, prepared)
@@ -255,6 +255,8 @@ func parallelPrepare(
255255
env *azldev.Env,
256256
comps []components.Component,
257257
stagingDir string,
258+
outputDir string,
259+
allowOverwrite bool,
258260
results []*RenderResult,
259261
) []*preparedComponent {
260262
progressEvent := env.StartEvent("Preparing component sources", "count", len(comps))
@@ -274,7 +276,7 @@ func parallelPrepare(
274276
go func(idx int, comp components.Component) {
275277
defer waitGroup.Done()
276278

277-
resultsChan <- prepWithSemaphore(workerEnv, semaphore, idx, comp, stagingDir)
279+
resultsChan <- prepWithSemaphore(workerEnv, semaphore, idx, comp, stagingDir, outputDir, allowOverwrite)
278280
}(compIdx, comp)
279281
}
280282

@@ -308,25 +310,20 @@ func prepWithSemaphore(
308310
index int,
309311
comp components.Component,
310312
stagingDir string,
313+
outputDir string,
314+
allowOverwrite bool,
311315
) prepResult {
312-
// Validate component name before any filesystem work.
313-
if err := validateComponentName(comp.GetName()); err != nil {
314-
return prepResult{index: index, result: &RenderResult{
315-
Component: comp.GetName(),
316-
OutputDir: "(invalid)",
317-
Status: renderStatusError,
318-
Error: err.Error(),
319-
}}
320-
}
316+
componentName := comp.GetName()
317+
compOutputDir := filepath.Join(outputDir, componentName)
321318

322319
// Context-aware semaphore acquisition.
323320
select {
324321
case semaphore <- struct{}{}:
325322
defer func() { <-semaphore }()
326323
case <-env.Done():
327324
return prepResult{index: index, result: &RenderResult{
328-
Component: comp.GetName(),
329-
OutputDir: comp.GetName(),
325+
Component: componentName,
326+
OutputDir: compOutputDir,
330327
Status: renderStatusCancelled,
331328
Error: "context cancelled",
332329
}}
@@ -335,11 +332,21 @@ func prepWithSemaphore(
335332
prep, err := prepareComponentSources(env, comp, stagingDir)
336333
if err != nil {
337334
slog.Error("Failed to prepare component sources",
338-
"component", comp.GetName(), "error", err)
335+
"component", componentName, "error", err)
336+
337+
// Write error marker so the failure is visible in git diff.
338+
if allowOverwrite {
339+
if removeErr := env.FS().RemoveAll(compOutputDir); removeErr != nil {
340+
slog.Debug("Failed to clean output before writing error marker",
341+
"path", compOutputDir, "error", removeErr)
342+
}
343+
}
344+
345+
writeRenderErrorMarker(env.FS(), compOutputDir)
339346

340347
return prepResult{index: index, result: &RenderResult{
341-
Component: comp.GetName(),
342-
OutputDir: comp.GetName(),
348+
Component: componentName,
349+
OutputDir: compOutputDir,
343350
Status: renderStatusError,
344351
Error: err.Error(),
345352
}}
@@ -388,7 +395,7 @@ func prepareComponentSources(
388395
// WithSkipLookaside avoids expensive tarball downloads — only spec +
389396
// sidecar files are needed for rendering.
390397
preparerOpts := []sources.PreparerOption{
391-
sources.WithGitRepo(),
398+
sources.WithGitRepo(env.Config().Project.DefaultAuthorEmail),
392399
sources.WithSkipLookaside(),
393400
}
394401

@@ -839,19 +846,6 @@ func validateOutputDir(outputDir string) error {
839846
return nil
840847
}
841848

842-
// validateComponentName rejects component names that could cause path traversal
843-
// when used as directory names in filepath.Join.
844-
func validateComponentName(name string) error {
845-
if name == "" || name == "." ||
846-
strings.Contains(name, "/") || strings.Contains(name, "\\") ||
847-
strings.Contains(name, "..") || strings.ContainsRune(name, 0) {
848-
return fmt.Errorf(
849-
"component name %#q is invalid or contains path separators/traversal sequences", name)
850-
}
851-
852-
return nil
853-
}
854-
855849
// createMockProcessor creates a [sources.MockProcessor] using the project's
856850
// mock config. Returns nil if the mock config is not available (e.g., no project
857851
// config loaded, or no mock config path configured).

internal/app/azldev/cmds/component/render_internal_test.go

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -173,35 +173,6 @@ func TestValidateOutputDir(t *testing.T) {
173173
}
174174
}
175175

176-
func TestValidateComponentName(t *testing.T) {
177-
tests := []struct {
178-
name string
179-
wantErr bool
180-
}{
181-
{"curl", false},
182-
{"my-package", false},
183-
{"my_package.123", false},
184-
{"../escape", true},
185-
{"foo/bar", true},
186-
{"foo\\bar", true},
187-
{"..evil", true},
188-
{"", true},
189-
{".", true},
190-
{"has\x00null", true},
191-
}
192-
193-
for _, tt := range tests {
194-
t.Run(tt.name, func(t *testing.T) {
195-
err := validateComponentName(tt.name)
196-
if tt.wantErr {
197-
assert.Error(t, err)
198-
} else {
199-
assert.NoError(t, err)
200-
}
201-
})
202-
}
203-
}
204-
205176
func TestRemoveUnreferencedFiles(t *testing.T) {
206177
t.Run("keeps spec and referenced files, removes others", func(t *testing.T) {
207178
testFS := afero.NewMemMapFs()

0 commit comments

Comments
 (0)