From f3eaaafd3c17068d049d96c0f043b2c947de5f91 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Sun, 11 Feb 2024 08:31:43 +0100 Subject: [PATCH] WIP: Adding support for git subtree --- helmule/examples/giantswarm/charts/zot.yaml | 21 ++++---- helmule/examples/giantswarm/helmule.yaml | 4 +- .../examples/giantswarm/patches/git/zot.patch | 11 ++-- helmule/src/main.rs | 4 +- helmule/src/mirror/git.rs | 1 + helmule/src/patches.rs | 52 +++++++++++++++---- lib/src/git.rs | 13 +++-- lib/src/helm/git_repository.rs | 26 ++++++++-- 8 files changed, 95 insertions(+), 37 deletions(-) diff --git a/helmule/examples/giantswarm/charts/zot.yaml b/helmule/examples/giantswarm/charts/zot.yaml index 3b17f9b..1f069d7 100644 --- a/helmule/examples/giantswarm/charts/zot.yaml +++ b/helmule/examples/giantswarm/charts/zot.yaml @@ -4,21 +4,22 @@ name: zot repository: zot-git extensions: - - name: Add VPA - source_dir: ../extensions/vpa - target_dir: templates/gs-vpa - - name: Add values for CI - source_dir: ../extensions/ci-values - target_dir: ci + #- name: Add VPA + # source_dir: ../extensions/vpa + # target_dir: templates/gs-vpa + #- name: Add values for CI + # source_dir: ../extensions/ci-values + # target_dir: ci variables: target_repo: zot-app patches: - - name: team annotation - - name: set home - - name: set engine - - name: yamlfmt + #- name: team annotation + #- name: set home + #- name: set engine + #- name: yamlfmt - name: Git patch git: path: ../patches/git/zot.patch + ref: d43aaea7bc0e21edb5cc391d4eb47866a5a94aba mirrors: - apps-git diff --git a/helmule/examples/giantswarm/helmule.yaml b/helmule/examples/giantswarm/helmule.yaml index 3bd26c2..f6f330d 100644 --- a/helmule/examples/giantswarm/helmule.yaml +++ b/helmule/examples/giantswarm/helmule.yaml @@ -8,6 +8,7 @@ include: path: ./charts/gitops-server.yaml - kind: Charts path: ./charts/external-secrets-operator.yaml + patches: - name: yamlfmt custom_command: @@ -40,6 +41,7 @@ repositories: url: https://github.com/project-zot/helm-charts.git git_ref: zot-0.1.42 path: charts + subtree: true - name: weave helm: url: https://helm.gitops.weave.works @@ -50,7 +52,7 @@ mirrors: - name: apps-git git: url: git@git.badhouseplants.net:allanger/{{ variables.target_repo }}.git - git_dir: app-{{ name }}-git + git_dir: app-{{ name }} branch: upgrade-{{ name }}-to-{{ version }} path: helm/{{ name }} commit: |- diff --git a/helmule/examples/giantswarm/patches/git/zot.patch b/helmule/examples/giantswarm/patches/git/zot.patch index 59969bb..7646096 100644 --- a/helmule/examples/giantswarm/patches/git/zot.patch +++ b/helmule/examples/giantswarm/patches/git/zot.patch @@ -109,13 +109,10 @@ index ac7f0f0..9730e9c 100644 replicaCount: 1 image: - repository: ghcr.io/project-zot/zot-linux-amd64 -- pullPolicy: IfNotPresent -- # Overrides the image tag whose default is the chart appVersion. -- tag: "v2.0.0" -+ registry: gsoci.azurecr.io -+ image: dummy/zot-linux -+ pullPolicy: Always -+ tag: "" ++ repository: ghcr.io/project-zot/zot-linux-amd64-bla + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "v2.0.0" serviceAccount: # Specifies whether a service account should be created create: true diff --git a/helmule/src/main.rs b/helmule/src/main.rs index 0f9f147..eac9720 100644 --- a/helmule/src/main.rs +++ b/helmule/src/main.rs @@ -47,7 +47,7 @@ struct Args { } fn exec(args: Args) -> Result<(), Box> { - let prerequisites = vec![args.helm_bin, args.git_bin, args.yq_bin]; + let prerequisites = vec![args.helm_bin, args.git_bin.clone(), args.yq_bin]; check_prerequisites(prerequisites)?; let workdir_path = helmzoo_lib::workdir::setup_workdir(args.workdir)?; let mut config: Config = read_config(args.config.clone())?; @@ -72,7 +72,7 @@ fn exec(args: Args) -> Result<(), Box> { patches .into_iter() .try_for_each(|patch| -> Result<(), Box> { - patch.apply(chart_path.clone(), config.patches.clone()) + patch.apply(chart_path.clone(), config.patches.clone(), workdir_path.clone(), args.git_bin.clone()) })? } config diff --git a/helmule/src/mirror/git.rs b/helmule/src/mirror/git.rs index a4b1433..ebab519 100644 --- a/helmule/src/mirror/git.rs +++ b/helmule/src/mirror/git.rs @@ -46,6 +46,7 @@ impl Target for GitMirror { Some(dir) => template::render(dir.clone(), &chart_local)?, None => general_purpose::STANDARD_NO_PAD.encode(self.url.clone()), }; + let git_instance = Git { url: template::render(self.url.clone(), &chart_local)?, repo_path: repo_path.clone(), diff --git a/helmule/src/patches.rs b/helmule/src/patches.rs index 251fc55..67f9ff3 100644 --- a/helmule/src/patches.rs +++ b/helmule/src/patches.rs @@ -1,12 +1,11 @@ use std::{ fs::{self, read_dir, remove_dir_all, File, OpenOptions}, io::Write, - path::{Path, PathBuf}, + path::{Path, PathBuf}, process::exit, }; use helmzoo_lib::{ - cli::{cli_exec, cli_exec_from_dir}, - output::message_info, + cli::{cli_exec, cli_exec_from_dir}, git::{CheckoutOptions, Git, GitOptions}, output::message_info, workdir }; use serde::{Deserialize, Serialize}; @@ -26,6 +25,12 @@ pub(crate) struct RegexpPatch { #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub(crate) struct GitPatch { path: String, + #[serde(alias = "ref")] + git_ref: Option, + #[serde(skip)] + workdir: String, + #[serde(skip)] + git_bin: String } #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] @@ -62,18 +67,20 @@ impl Patch { &self, chart_local_path: String, global_patches: Option>, + workdir: String, + git_bin: String, ) -> Result<(), Box> { let patch_action: Box; if self.is_ref() { let patch_ref = self.get_ref(global_patches)?; - patch_action = Box::from(patch_action_from_definition(patch_ref)?); + patch_action = Box::from(patch_action_from_definition(patch_ref, workdir.clone(), git_bin.clone())?); } else { - patch_action = Box::from(patch_action_from_definition(self.clone())?); + patch_action = Box::from(patch_action_from_definition(self.clone(), workdir.clone(), git_bin.clone())?); } patch_action.apply(chart_local_path) } pub(crate) fn get_path(&self) -> String { - match patch_action_from_definition(self.clone()) { + match patch_action_from_definition(self.clone(), String::new(), String::new()) { Ok(patch) => patch.get_path(), Err(_) => "".to_string(), } @@ -235,12 +242,34 @@ impl PatchInterface for RegexpPatch { impl PatchInterface for GitPatch { fn apply(&self, chart_local_path: String) -> Result<(), Box> { - if !is_git_repo(chart_local_path.clone()) { + let git_instance = Git { + url: chart_local_path.clone(), + repo_path: chart_local_path.clone(), + }; + let path = format!("{}", chart_local_path); + let is_git_repo = is_git_repo(path.clone()); + if !is_git_repo { init_git_repo(chart_local_path.clone())?; }; + let cmd = "git rev-parse --abbrev-ref HEAD".to_string(); + let current_ref = cli_exec_from_dir(cmd, path)?; + let git_opts = GitOptions::new(self.git_bin.clone(), None); + if let Some(git_ref) = self.git_ref.clone() { + let checkout_options = CheckoutOptions { + create: false, + git_ref: git_ref.clone(), + }; + git_instance.checkout(git_opts, checkout_options)?; + } let cmd = format!("git -C {} apply {}", chart_local_path, self.path); cli_exec(cmd)?; - remove_dir_all(chart_local_path + "/.git")?; + + if let Some(git_ref) = self.git_ref.clone() { + exit(1); + } + if !is_git_repo{ + remove_dir_all(chart_local_path + "/.git")?; + } Ok(()) } @@ -273,6 +302,8 @@ impl PatchInterface for CustomCommandPatch { fn patch_action_from_definition( patch: Patch, + workdir: String, + git_bin: String, ) -> Result, Box> { if let Some(regexp) = patch.regexp { Ok(Box::new(RegexpPatch { path: regexp.path })) @@ -285,6 +316,9 @@ fn patch_action_from_definition( None => git.path.clone(), } }, + git_ref: git.git_ref.clone(), + workdir: workdir.clone(), + git_bin: git_bin.clone(), })); } else if let Some(custom_command) = patch.custom_command { return Ok(Box::new(CustomCommandPatch { @@ -301,7 +335,7 @@ fn patch_action_from_definition( } fn is_git_repo(path: String) -> bool { - let dot_git_path = path + ".git"; + let dot_git_path = path + "/.git"; Path::new(dot_git_path.as_str()).exists() } diff --git a/lib/src/git.rs b/lib/src/git.rs index 598f9d2..bc59b1b 100644 --- a/lib/src/git.rs +++ b/lib/src/git.rs @@ -1,8 +1,8 @@ -use std::error::Error; +use std::{error::Error, fmt::format}; use serde::{Deserialize, Serialize}; -use crate::cli::{cli_exec, cli_exec_from_dir}; +use crate::cli::{cli_exec, cli_exec_from_dir, is_path_relative}; #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] pub struct GitOptions { @@ -46,6 +46,10 @@ pub struct Git { impl Git { pub fn new(url: String, repo_path: String) -> Self { + let repo_path = match is_path_relative(repo_path.clone()) { + true => format!("./{}", repo_path), + false => todo!(), + }; Self { url, repo_path } } @@ -90,8 +94,9 @@ impl Git { let _ = self.exec(cmd, git_opts.workdir.clone())?; } let cmd = format!( - "{} diff --staged --quiet || {} -C {} commit -m \"{}\"", - git_opts.bin, git_opts.bin, self.repo_path, opts.message + "{} -C {} diff --staged --quiet || {} -C {} commit -m \"{}\"", + git_opts.bin, self.repo_path, + git_opts.bin, self.repo_path, opts.message ); match self.exec(cmd, git_opts.workdir.clone()) { Ok(_) => Ok(()), diff --git a/lib/src/helm/git_repository.rs b/lib/src/helm/git_repository.rs index 66637ad..f5708b2 100644 --- a/lib/src/helm/git_repository.rs +++ b/lib/src/helm/git_repository.rs @@ -4,6 +4,7 @@ use crate::git::GitOptions; use crate::{cli::cli_exec, helm::repository::Version}; use std::error::Error; use std::fs::{self, rename}; +use std::process::exit; use base64::{engine::general_purpose, Engine}; use serde::Deserialize; @@ -19,6 +20,7 @@ pub struct GitRepo { #[serde(alias = "ref")] pub git_ref: String, pub path: String, + pub subtree: bool, #[serde(default = "default_git_bin")] pub(crate) git_bin: String, } @@ -40,7 +42,7 @@ impl RepositoryImpl for GitRepo { git_ref: self.git_ref.clone(), }; - git_instance.checkout(git_opts, checkout_opts)?; + git_instance.checkout(git_opts.clone(), checkout_opts)?; let old_dir_name = format!( "{}/{}/{}/{}", @@ -56,13 +58,29 @@ impl RepositoryImpl for GitRepo { match serde_yaml::from_str::(&helm_stdout) { Ok(res) => { new_dir_name = format!("{}/{}-{}", workdir_path, chart.name.clone(), res.version); - rename(old_dir_name, new_dir_name.clone())?; + match self.subtree { + true => { + let cmd = format!("git subtree split --prefix {}/{} -b helmule-subtree-split", self.path, chart.name.clone()); + cli_exec_from_dir(cmd, format!("{}/{}", workdir_path.clone(), repo_name))?; + let checkout_opts = CheckoutOptions{ + create: false, + git_ref: "helmule-subtree-split".to_string(), + }; + git_instance.checkout(git_opts.clone(), checkout_opts)?; + let old_dir_name = format!("{}/{}", workdir_path.clone(), repo_name); + rename(old_dir_name, new_dir_name.clone())?; + }, + false => { + rename(old_dir_name, new_dir_name.clone())?; + // Cleaning up + fs::remove_dir_all(format!("{}/{}", workdir_path, repo_name))?; + } + + }; } Err(err) => return Err(Box::from(err)), }; - // Cleaning up - fs::remove_dir_all(format!("{}/{}", workdir_path, repo_name))?; // Get the version let cmd = "helm show chart . | yq '.version'".to_string(); -- 2.45.2