Files
truecharts/clustertool/pkg/charts/changelog/patch.go
T

156 lines
6.2 KiB
Go

package changelog
import (
"errors"
"fmt"
"strings"
"github.com/Masterminds/semver/v3"
"github.com/go-git/go-git/v5/plumbing/format/diff"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/rs/zerolog/log"
)
var errSkipPatch = errors.New("skip patch")
func getChangedFilePair(p diff.FilePatch) (string, oldNewPaths, error) {
old, new := p.Files()
if new == nil { // No new file, nothing to do
log.Debug().Msgf("Skipping file patch. Reason: New file is empty")
return "", oldNewPaths{}, errSkipPatch
}
// Get chart name and check if its an active chart
// if the new.Path() is a path outside of the charts folder,
// it will not be an active chart anyway and so we skip the diff
chartName := getChartName(new.Path())
if chartName == invalidName || !activeCharts.isActiveChart(chartName) {
log.Debug().Msgf("Skipping file patch. Reason: [%s] is not an active chart", new.Path())
return "", oldNewPaths{}, errSkipPatch
}
if _, err := getChartPath(new.Path()); err != nil {
log.Debug().Msgf("Skipping file patch. Reason: [%s] is not a valid chart path", new.Path())
return "", oldNewPaths{}, errSkipPatch
}
if old != nil { // If an old file exists in the patch
if _, err := getChartPath(old.Path()); err != nil {
log.Debug().Msgf("Skipping file patch. Reason: [%s] is not a valid chart path", old.Path())
return "", oldNewPaths{}, errSkipPatch
}
}
return chartName, oldNewPaths{new: new, old: old}, nil
}
func getOldAndNewVersion(c *object.Commit, par *object.Commit, paths oldNewPaths) (string, string, error) {
newChartPath, err := getChartPath(paths.new.Path())
if err != nil {
return "", "", fmt.Errorf("failed to get chart path from file path [%s]: %w", paths.new.Path(), err)
}
newChartVer, err := getChartVersion(c, newChartPath)
if err != nil {
return "", "", fmt.Errorf("failed to get chart data from path [%s]: %w", newChartPath, err)
}
oldChartVer := ""
if paths.old != nil { // If an old file exists in the patch
oldChartPath, err := getChartPath(paths.old.Path())
if err != nil {
return "", "", fmt.Errorf("failed to get chart path from file path [%s]: %w", paths.old.Path(), err)
}
// Note here we pass the parent commit, not the current commit
oldChartVer, err = getChartVersion(par, oldChartPath)
if err != nil {
return "", "", fmt.Errorf("failed to get chart data from path [%s]: %w", oldChartPath, err)
}
}
return oldChartVer, newChartVer, nil
}
func getChartsWithMultipleChangedFiles(p *object.Patch) (chartsWithChangedFiles, error) {
chartsWithMultipleFiles := make(chartsWithChangedFiles)
for _, p := range p.FilePatches() {
// Get chart name and the "new" file path
chartName, paths, err := getChangedFilePair(p)
if err != nil {
if errors.Is(err, errSkipPatch) {
continue
}
return chartsWithChangedFiles{}, fmt.Errorf("failed to get changed files: %w", err)
}
// if there is no new file, skip the filePatch
if paths.new.Path() == "" {
continue
}
// Add the file to the charts changed files
chartsWithMultipleFiles[chartName] = append(chartsWithMultipleFiles[chartName], paths)
}
return chartsWithMultipleFiles, nil
}
func getChartsWithSingleChangedFile(c chartsWithChangedFiles) chartsWithChangedFile {
chartsWithSingleFile := make(chartsWithChangedFile)
for chartName, filePaths := range c {
for _, paths := range filePaths {
_, ok := chartsWithSingleFile[chartName]
// If the chart hasn't been seen before,
// or the filePath is a Chart.yaml file
// we add the pair to the map
if !ok || strings.HasSuffix(paths.new.Path(), "Chart.yaml") {
chartsWithSingleFile[chartName] = paths
continue
}
}
}
return chartsWithSingleFile
}
func processChartsWithSingleChangedFile(c *object.Commit, par *object.Commit, chartsWithSingleFile chartsWithChangedFile) error {
// For each chart, get the old and new versions
for chartName, paths := range chartsWithSingleFile {
oldVer, newVer, err := getOldAndNewVersion(c, par, paths)
if err != nil {
return fmt.Errorf("failed to get old and new versions: %w", err)
}
// If the old version is empty, (chart addition)
// we add the new version to the changedData
if oldVer == "" {
changedData.mu.Lock()
changedData.AddOrUpdateChart(chartName, newVer, getChartTrain(paths.new.Path()), c)
changedData.mu.Unlock()
continue
}
oldSemVer, err := semver.NewVersion(oldVer)
if err != nil {
return fmt.Errorf("failed to parse old version ([%s]) for file [%s] in commit [%s]: %w", oldVer, paths.new.Path(), c.Hash.String(), err)
}
newSemVer, err := semver.NewVersion(newVer)
if err != nil {
return fmt.Errorf("failed to parse new version ([%s]) for file [%s] in commit [%s]: %w", newVer, paths.new.Path(), c.Hash.String(), err)
}
// if new version is greater than the old version, we add the new version to the changedData
if newSemVer.GreaterThan(oldSemVer) {
changedData.mu.Lock()
changedData.AddOrUpdateChart(chartName, newVer, getChartTrain(paths.new.Path()), c)
changedData.mu.Unlock()
continue
}
// Otherwise, we add the new version to the stagingData
// It is probably less or equal to the old version,
// in either case the chart changes is unreleased.
// so it should go to the "next" version, we do that at the end
// although if its less, it will be hard to actually get which is the "next" version
// but we can't really do anything about it, so just put it on the immediate next version
stagingData.mu.Lock()
stagingData.AddOrUpdateChart(chartName, newVer, getChartTrain(paths.new.Path()), c)
stagingData.mu.Unlock()
}
return nil
}