Skip to content

Commit 7bdc959

Browse files
author
Antonio Salinas
committed
Refactored git syth to detect 'Affects:' pattern in commit logs
1 parent 8ebba3f commit 7bdc959

File tree

5 files changed

+541
-871
lines changed

5 files changed

+541
-871
lines changed

internal/app/azldev/core/sources/sourceprep.go

Lines changed: 85 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,14 @@ func (p *sourcePreparerImpl) PrepareSources(
125125
return p.applyOverlaysToSources(ctx, component, outputDir)
126126
}
127127

128-
// applyOverlaysToSources writes the macros file and then applies overlays using either the
129-
// synthetic history path or the standard postProcessSources path.
128+
// applyOverlaysToSources writes the macros file and then applies all overlays and
129+
// records synthetic git history.
130130
func (p *sourcePreparerImpl) applyOverlaysToSources(
131131
ctx context.Context, component components.Component, outputDir string,
132132
) error {
133133
// Emit computed macros to a macros file in the output directory.
134134
// If the build configuration produces no macros, no file is written and
135-
// macrosFileName will be empty, signaling postProcessSources to skip
136-
// injecting the macro load directive and Source9999 tag.
135+
// macrosFileName will be empty.
137136
var macrosFileName string
138137

139138
macrosFilePath, err := p.writeMacrosFile(component, outputDir)
@@ -146,23 +145,22 @@ func (p *sourcePreparerImpl) applyOverlaysToSources(
146145
macrosFileName = filepath.Base(macrosFilePath)
147146
}
148147

149-
// Apply user-defined overlays as attributed synthetic git commits, then apply
150-
// system overlays separately. If synthetic history cannot be generated (e.g. no
151-
// git repo available), applyOverlaysWithHistory falls back to the standard path.
148+
// Apply all overlays and record synthetic git history.
152149
err = p.applyOverlaysWithHistory(ctx, component, outputDir, macrosFileName)
153150
if err != nil {
154-
return fmt.Errorf("failed to post-process sources for component %q:\n%w", component.GetName(), err)
151+
return fmt.Errorf("failed to apply overlays for component %#q:\n%w", component.GetName(), err)
155152
}
156153

157154
return nil
158155
}
159156

160-
// applyOverlaysWithHistory applies user-defined overlays as attributed synthetic git
161-
// commits in the upstream repository, then applies system overlays (macros, check-skip,
162-
// header) outside of the synthetic history so they don't pollute blame attribution.
157+
// applyOverlaysWithHistory applies all overlays (user-defined and system-generated) to the
158+
// component sources, then attempts to record the changes as synthetic git history.
163159
//
164-
// When the sources directory does not contain a git repository (e.g. local or unspecified
165-
// spec sources), the function falls back to applying overlays sequentially without history.
160+
// Overlay application is fully decoupled from git history generation: overlays are always
161+
// applied first, then the changes are optionally committed. If the sources directory does
162+
// not contain a git repository (e.g. local or unspecified spec sources), overlays are still
163+
// applied but no synthetic history is created.
166164
func (p *sourcePreparerImpl) applyOverlaysWithHistory(
167165
_ context.Context, component components.Component, sourcesDirPath, macrosFileName string,
168166
) error {
@@ -175,55 +173,97 @@ func (p *sourcePreparerImpl) applyOverlaysWithHistory(
175173
return err
176174
}
177175

178-
config := component.GetConfig()
179-
if len(config.Overlays) == 0 {
180-
slog.Debug("No overlays defined; skipping synthetic history generation",
181-
"component", component.GetName())
176+
// Collect all overlays in application order. This ensures every change is
177+
// captured in the synthetic history, including build configuration changes.
178+
allOverlays := p.collectOverlays(component, macrosFileName)
182179

183-
return p.applySystemOverlays(component, sourcesDirPath, absSpecPath, macrosFileName)
180+
if len(allOverlays) == 0 {
181+
return nil
184182
}
185183

186-
// Try the synthetic history path. If it succeeds, apply system overlays and return.
187-
if err := p.trySyntheticHistory(component, config, sourcesDirPath, absSpecPath); err == nil {
188-
return p.applySystemOverlays(component, sourcesDirPath, absSpecPath, macrosFileName)
184+
// Apply all overlays to the working tree.
185+
if err := p.applyOverlayList(allOverlays, sourcesDirPath, absSpecPath); err != nil {
186+
return fmt.Errorf("failed to apply overlays for component %#q:\n%w", component.GetName(), err)
189187
}
190188

191-
// Synthetic history was not possible; apply user overlays sequentially.
192-
if err := p.applyOverlayList(config.Overlays, sourcesDirPath, absSpecPath); err != nil {
193-
return fmt.Errorf("failed to apply overlays for component %#q:\n%w", component.GetName(), err)
189+
// Record the changes as synthetic git history. This is required for rpmautospec
190+
// release numbering and delta builds, so failure here is fatal.
191+
if err := p.trySyntheticHistory(component, sourcesDirPath); err != nil {
192+
return fmt.Errorf("failed to generate synthetic history for component %#q:\n%w",
193+
component.GetName(), err)
194194
}
195195

196-
return p.applySystemOverlays(component, sourcesDirPath, absSpecPath, macrosFileName)
196+
return nil
197197
}
198198

199-
// trySyntheticHistory attempts to apply user-defined overlays as synthetic git commits.
200-
// Returns nil on success, or a non-nil error if the synthetic history path is not available
201-
// (e.g. no .git directory, no resolvable blame groups). The caller should fall back to
202-
// sequential overlay application when this returns an error.
199+
// collectOverlays gathers all overlays for a component into a single ordered slice:
200+
// component overlays first, followed by macros-load, check-skip, and file-header overlays.
201+
func (p *sourcePreparerImpl) collectOverlays(
202+
component components.Component, macrosFileName string,
203+
) []projectconfig.ComponentOverlay {
204+
config := component.GetConfig()
205+
206+
var allOverlays []projectconfig.ComponentOverlay
207+
208+
allOverlays = append(allOverlays, config.Overlays...)
209+
210+
if macrosFileName != "" {
211+
macroOverlays, err := synthesizeMacroLoadOverlays(macrosFileName)
212+
if err != nil {
213+
slog.Error("Failed to compute macros load overlays",
214+
"component", component.GetName(), "error", err)
215+
216+
panic(fmt.Sprintf("failed to compute macros load overlays for component %q: %v", component.GetName(), err))
217+
}
218+
219+
allOverlays = append(allOverlays, macroOverlays...)
220+
}
221+
222+
allOverlays = append(allOverlays, synthesizeCheckSkipOverlays(config.Build.Check)...)
223+
allOverlays = append(allOverlays, generateFileHeaderOverlay()...)
224+
225+
return allOverlays
226+
}
227+
228+
// trySyntheticHistory attempts to create synthetic git commits on top of the upstream
229+
// repository. All file changes must already be present in the working tree before calling
230+
// this function — it only handles staging and committing.
231+
//
232+
// Returns nil when there is no .git directory (legitimate for local/unspecified specs).
233+
// Returns a non-nil error if a .git directory exists but history generation fails.
203234
func (p *sourcePreparerImpl) trySyntheticHistory(
204235
component components.Component,
205-
config *projectconfig.ComponentConfig,
206-
sourcesDirPath, absSpecPath string,
236+
sourcesDirPath string,
207237
) error {
208238
// Check for an upstream git repository in the sources directory. Local and
209-
// unspecified spec sources won't have a .git directory.
239+
// unspecified spec sources won't have a .git directory — that's expected.
210240
gitDirPath := filepath.Join(sourcesDirPath, ".git")
211-
if _, err := p.fs.Stat(gitDirPath); err != nil {
212-
slog.Debug("No .git directory in sources; falling back to standard overlay path",
213-
"component", component.GetName(),
214-
"sourcesDirPath", sourcesDirPath)
215241

216-
return fmt.Errorf("no .git directory in sources dir %#q:\n%w", sourcesDirPath, err)
242+
hasGitDir, err := fileutils.Exists(p.fs, gitDirPath)
243+
if err != nil {
244+
return fmt.Errorf("failed to check for .git directory at %#q:\n%w", gitDirPath, err)
245+
}
246+
247+
if !hasGitDir {
248+
slog.Debug("No .git directory in sources; skipping synthetic history",
249+
"component", component.GetName())
250+
251+
return nil
217252
}
218253

219-
// Resolve the project repository and blame the config file to produce overlay groups.
220-
groups, err := buildOverlayGroups(p.fs, config, component.GetName())
254+
config := component.GetConfig()
255+
256+
// Build commit metadata from Affects commits and dirty state.
257+
commits, err := buildSyntheticCommits(config, component.GetName())
221258
if err != nil {
222-
return fmt.Errorf("failed to build overlay groups:\n%w", err)
259+
return fmt.Errorf("failed to build synthetic commits:\n%w", err)
223260
}
224261

225-
if len(groups) == 0 {
226-
return errors.New("no overlay groups resolved")
262+
if len(commits) == 0 {
263+
slog.Debug("No synthetic commits to create; skipping history generation",
264+
"component", component.GetName())
265+
266+
return nil
227267
}
228268

229269
// Open the upstream git repository where synthetic commits will be recorded.
@@ -232,41 +272,13 @@ func (p *sourcePreparerImpl) trySyntheticHistory(
232272
return fmt.Errorf("failed to open upstream repository at %#q:\n%w", sourcesDirPath, err)
233273
}
234274

235-
// Build the overlay-application callback using the shared helper.
236-
applyFn := func(overlays []projectconfig.ComponentOverlay) error {
237-
return p.applyOverlayList(overlays, sourcesDirPath, absSpecPath)
238-
}
239-
240-
if err := CommitSyntheticHistory(sourcesRepo, groups, applyFn); err != nil {
275+
if err := CommitSyntheticHistory(sourcesRepo, commits); err != nil {
241276
return fmt.Errorf("failed to commit synthetic history:\n%w", err)
242277
}
243278

244279
return nil
245280
}
246281

247-
// applySystemOverlays applies the macros-load, check-skip, and file-header overlays that
248-
// are synthesized at build time rather than defined in the project TOML config.
249-
func (p *sourcePreparerImpl) applySystemOverlays(
250-
component components.Component, sourcesDirPath, absSpecPath, macrosFileName string,
251-
) error {
252-
// Collect all system overlays in application order: macros, check-skip, file header.
253-
var systemOverlays []projectconfig.ComponentOverlay
254-
255-
if macrosFileName != "" {
256-
macroOverlays, macroErr := synthesizeMacroLoadOverlays(macrosFileName)
257-
if macroErr != nil {
258-
return fmt.Errorf("failed to compute macros load overlays:\n%w", macroErr)
259-
}
260-
261-
systemOverlays = append(systemOverlays, macroOverlays...)
262-
}
263-
264-
systemOverlays = append(systemOverlays, synthesizeCheckSkipOverlays(component.GetConfig().Build.Check)...)
265-
systemOverlays = append(systemOverlays, generateFileHeaderOverlay()...)
266-
267-
return p.applyOverlayList(systemOverlays, sourcesDirPath, absSpecPath)
268-
}
269-
270282
// DiffSources implements the [SourcePreparer] interface.
271283
// It fetches the component's sources once, copies them to a second directory, applies overlays
272284
// to the copy, then diffs the two trees. This avoids fetching the sources twice.
@@ -306,19 +318,8 @@ func (p *sourcePreparerImpl) DiffSources(
306318
}
307319

308320
// Apply overlays in-place to the copied directory only.
309-
var macrosFileName string
310-
311-
macrosFilePath, err := p.writeMacrosFile(component, overlaidDir)
312-
if err != nil {
313-
return nil, fmt.Errorf("failed to write macros file for component %#q:\n%w", component.GetName(), err)
314-
}
315-
316-
if macrosFilePath != "" {
317-
macrosFileName = filepath.Base(macrosFilePath)
318-
}
319-
320-
if err := p.postProcessSources(component, overlaidDir, macrosFileName); err != nil {
321-
return nil, fmt.Errorf("failed to post-process sources for component %#q:\n%w", component.GetName(), err)
321+
if err := p.applyOverlaysToSources(ctx, component, overlaidDir); err != nil {
322+
return nil, fmt.Errorf("failed to apply overlays for component %#q:\n%w", component.GetName(), err)
322323
}
323324

324325
// Diff the original tree against the overlaid tree.

0 commit comments

Comments
 (0)