#!/usr/bin/env python from argparse import ArgumentParser from glob import glob from os import path from subprocess import run from tempfile import mkdtemp, NamedTemporaryFile def clone_repository(repo, branch, target_path): run( ['git', 'clone', repo, '--depth', '1', '--branch', branch, target_path], check=True ) def render_helm_chart(chart_path, namespace, release_name, rendered_path): # Even if there is no Helm chart at the specified chart path, do not raise an error. # This accommodates cases where the entire chart is removed, or a new chart is added. # In such cases, the rendered file will simply be empty. if path.isdir(chart_path): run( ['helm', 'dependency', 'update', chart_path], check=True ) run( ['helm', 'template', '--namespace', namespace, release_name, chart_path], stdout=open(rendered_path, 'w'), check=True ) def changed_charts(source_path, target_path, subpath): changed_charts = [] # Convert to set for deduplication all_charts = set( glob(f"*", root_dir=f"{source_path}/{subpath}") + glob(f"*", root_dir=f"{target_path}/{subpath}") ) for chart in all_charts: source_chart_path = path.join(source_path, subpath, chart) target_chart_path = path.join(target_path, subpath, chart) if run(['diff', source_chart_path, target_chart_path], capture_output=True).returncode != 0: changed_charts.append(chart) return changed_charts def main(): parser = ArgumentParser(description='Compare Helm charts in a directory between two Git revisions.') parser.add_argument('--repository', required=True, help='Repository to clone') parser.add_argument('--source', required=True, help='Source branch (e.g. pull request branch)') parser.add_argument('--target', required=True, help='Target branch (e.g. master branch)') parser.add_argument('--subpath', required=True, help='Subpath containing the charts (e.g. system)') args = parser.parse_args() source_path = mkdtemp() target_path = mkdtemp() clone_repository(args.repository, args.source, source_path) clone_repository(args.repository, args.target, target_path) for chart in changed_charts(source_path, target_path, args.subpath): with NamedTemporaryFile(suffix='.yaml', mode='w+', delete=False) as f_source, NamedTemporaryFile(suffix='.yaml', mode='w+', delete=False) as f_target: render_helm_chart(f"{source_path}/{args.subpath}/{chart}", chart, chart, f_source.name) render_helm_chart(f"{target_path}/{args.subpath}/{chart}", chart, chart, f_target.name) diff_result = run( ['dyff', 'between', '--omit-header', '--use-go-patch-style', '--color=on', '--truecolor=off', f_target.name, f_source.name], capture_output=True, text=True, check=True ) print(diff_result.stdout) if __name__ == "__main__": main()