Skip to content

Commit

Permalink
Add support for 'changed files' and 'watch-pattern' annotation (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
dag-andersen committed Oct 22, 2024
1 parent 9c51e4a commit b7c9b42
Show file tree
Hide file tree
Showing 15 changed files with 471 additions and 122 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/generate-diff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
- "examples/**"

jobs:
build:
render-diff:
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/mkdocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ on:
branches:
- master
- main
paths:
- "docs/**"

permissions:
contents: write

jobs:
deploy:
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
pull_request:
branches:
- "main"
paths:
- "src/**"
- Cargo.toml

jobs:
build-amd64:
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "argocd-diff-preview"
version = "0.0.19"
version = "0.0.20"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand All @@ -10,7 +10,7 @@ tokio = {version="1.40.0",features = ["full"]}
base64 = "0.22.1"
serde = "1.0.210"
serde_yaml = "0.9.33"
serde_json = "1.0.128"
serde_json = "1.0.131"
walkdir = "2.5.0"
schemars = "0.8.21"
structopt = { version = "0.3" }
Expand Down
22 changes: 22 additions & 0 deletions examples/helm/applications/label-selectors/my-app-labels.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app-labels
namespace: argocd
labels:
team: my-team
spec:
project: default
destination:
name: in-cluster
namespace: default
sources:
- repoURL: https://github.com/dag-andersen/argocd-diff-preview
ref: local-files
- path: examples/helm/charts/myApp
repoURL: https://github.com/dag-andersen/argocd-diff-preview
helm:
valueFiles:
- $local-files/examples/helm/values/values.yaml
valuesObject:
replicaCount: 5
22 changes: 22 additions & 0 deletions examples/helm/applications/watch-pattern/broken-regex.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app-watch-pattern-broken-regex
namespace: argocd
annotations:
argocd-diff-preview/watch-pattern: '\'
spec:
project: default
destination:
name: in-cluster
namespace: default
sources:
- repoURL: https://github.com/dag-andersen/argocd-diff-preview
ref: local-files
- path: examples/helm/charts/myApp
repoURL: https://github.com/dag-andersen/argocd-diff-preview
helm:
valueFiles:
- $local-files/examples/helm/values/values.yaml
valuesObject:
replicaCount: 5
22 changes: 22 additions & 0 deletions examples/helm/applications/watch-pattern/valid-regex.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app-watch-pattern-valid-regex
namespace: argocd
annotations:
argocd-diff-preview/watch-pattern: "examples/helm/values/.*"
spec:
project: default
destination:
name: in-cluster
namespace: default
sources:
- repoURL: https://github.com/dag-andersen/argocd-diff-preview
ref: local-files
- path: examples/helm/charts/myApp
repoURL: https://github.com/dag-andersen/argocd-diff-preview
helm:
valueFiles:
- $local-files/examples/helm/values/values.yaml
valuesObject:
replicaCount: 5
18 changes: 14 additions & 4 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ pull-repostory:
cd base-branch && gh repo clone $(github_org)/$(gitops_repo) -- --depth=1 --branch "$(base_branch)" && cp -r $(gitops_repo)/. . && rm -rf .git && echo "*" > .gitignore && rm -rf $(gitops_repo) && cd -
cd target-branch && gh repo clone $(github_org)/$(gitops_repo) -- --depth=1 --branch "$(target_branch)" && cp -r $(gitops_repo)/. . && rm -rf .git && echo "*" > .gitignore && rm -rf $(gitops_repo) && cd -

local-test-cargo: pull-repostory
cargo run -- -b "$(base_branch)" -t "$(target_branch)" --repo $(github_org)/$(gitops_repo) -r "$(regex)" --debug --diff-ignore "$(diff-ignore)" --timeout $(timeout) -l "$(selector)"
run-with-cargo: pull-repostory
cargo run -- -b "$(base_branch)" \
-t "$(target_branch)" \
--repo $(github_org)/$(gitops_repo) \
--debug \
-r "$(regex)" \
--diff-ignore "$(diff_ignore)" \
--timeout $(timeout) \
-l "$(selector)" \
--files-changed="$(files_changed)"

local-test-docker: pull-repostory
run-with-docker: pull-repostory
docker build . -f $(docker_file) -t image
docker run \
--network=host \
Expand All @@ -27,6 +35,8 @@ local-test-docker: pull-repostory
-e TARGET_BRANCH=$(target_branch) \
-e REPO=$(github_org)/$(gitops_repo) \
-e FILE_REGEX="$(regex)" \
-e DIFF_IGNORE="$(diff-ignore)" \
-e DIFF_IGNORE="$(diff_ignore)" \
-e TIMEOUT=$(timeout) \
-e SELECTOR="$(selector)" \
-e FILES_CHANGED="$(files_changed)" \
image
6 changes: 1 addition & 5 deletions src/argocd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ pub async fn install_argo_cd(options: ArgoCDOptions<'_>) -> Result<(), Box<dyn E
}
}

info!("🦑 Installing Argo CD Helm Chart");

let helm_install_command = format!(
"helm install argocd argo/argo-cd -n argocd {} {} {}",
values.unwrap_or_default(),
Expand Down Expand Up @@ -121,10 +119,8 @@ pub async fn install_argo_cd(options: ArgoCDOptions<'_>) -> Result<(), Box<dyn E
let password_decoded = BASE64_STANDARD
.decode(password_encoded)
.expect("failed to decode password");
let password =
String::from_utf8(password_decoded).expect("failed to convert password to string");

password
String::from_utf8(password_decoded).expect("failed to convert password to string")
};

// sleep for 5 seconds
Expand Down
4 changes: 3 additions & 1 deletion src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub async fn get_resources(
timeout: u64,
output_folder: &str,
) -> Result<(), Box<dyn Error>> {
info!("🌚 Getting resources from {}", branch_type);
info!("🌚 Getting resources from {}-branch", branch_type);

let app_file = apps_file(branch_type);

Expand Down Expand Up @@ -111,10 +111,12 @@ pub async fn get_resources(
Some(msg)
if TIMEOUT_MESSAGES.iter().any(|e| msg.contains(e)) =>
{
debug!("Application: {} timed out with error: {}", name, msg);
list_of_timed_out_apps.push(name.to_string().clone());
other_errors.push((name.to_string(), msg.to_string()));
}
Some(msg) => {
debug!("Application: {} failed with error: {}", name, msg);
other_errors.push((name.to_string(), msg.to_string()));
}
_ => (),
Expand Down
108 changes: 76 additions & 32 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::utils::{check_if_folder_exists, create_folder_if_not_exists, run_command};
use log::{debug, error, info};
use parsing::{applications_to_string, GetApplicationOptions};
use regex::Regex;
use std::fs;
use std::path::PathBuf;
Expand All @@ -14,6 +15,7 @@ mod diff;
mod extract;
mod kind;
mod minikube;
mod no_apps_found;
mod parsing;
mod utils;

Expand Down Expand Up @@ -75,9 +77,14 @@ struct Opt {
/// Max diff message character count. Default: 65536 (GitHub comment limit)
#[structopt(long, env)]
max_diff_length: Option<usize>,

/// Label selector to filter on, supports '=', '==', and '!='. (e.g. -l key1=value1,key2=value2).
#[structopt(long, short = "l", env)]
selector: Option<String>,

/// List of files changed between the two branches. Input must be a comma or space separated string. When provided, only Applications watching these files will be rendered.
#[structopt(long, env)]
files_changed: Option<String>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -174,6 +181,19 @@ async fn main() -> Result<(), Box<dyn Error>> {
.as_deref()
.filter(|f| !f.trim().is_empty());
let max_diff_length = opt.max_diff_length;
let files_changed: Option<Vec<String>> = opt
.files_changed
.map(|f| f.trim().to_string())
.filter(|f| !f.is_empty())
.map(|f| {
(if f.contains(',') {
f.split(',')
} else {
f.split(' ')
})
.map(|s| s.trim().to_string())
.collect()
});

// select local cluster tool
let tool = match opt.local_cluster_tool {
Expand Down Expand Up @@ -216,9 +236,12 @@ async fn main() -> Result<(), Box<dyn Error>> {
if let Some(a) = max_diff_length {
info!("✨ - max-diff-length: {}", a);
}
if let Some(a) = files_changed.clone() {
info!("✨ - files-changed: {:?}", a);
}

// label selectors can be fined in the following format: key1==value1,key2=value2,key3!=value3
let selector = opt.selector.map(|s| {
let selector = opt.selector.filter(|s| !s.trim().is_empty()).map(|s| {
let labels: Vec<Selector> = s
.split(",")
.filter(|l| !l.trim().is_empty())
Expand Down Expand Up @@ -291,16 +314,39 @@ async fn main() -> Result<(), Box<dyn Error>> {

let cluster_name = CLUSTER_NAME;

// remove .git from repo
let repo = repo.trim_end_matches(".git");
let (base_apps, target_apps) = parsing::get_applications_for_both_branches(
GetApplicationOptions {
directory: BASE_BRANCH_FOLDER,
branch: &base_branch_name,
},
GetApplicationOptions {
directory: TARGET_BRANCH_FOLDER,
branch: &target_branch_name,
},
&file_regex,
&selector,
&files_changed,
repo,
)
.await?;

let found_base_apps = !base_apps.is_empty();
let found_target_apps = !target_apps.is_empty();

if !found_base_apps && !found_target_apps {
info!("👀 Nothing to compare");
info!("👀 If this doesn't seem right, try running the tool with '--debug' to get more details about what is happening");
no_apps_found::write_message(output_folder, &selector, &files_changed).await?;
info!("🎉 Done in {} seconds", start.elapsed().as_secs());
return Ok(());
}

match tool {
ClusterTool::Kind => kind::create_cluster(&cluster_name).await?,
ClusterTool::Minikube => minikube::create_cluster().await?,
}

argocd::install_argo_cd(argocd::ArgoCDOptions {
version: argocd_version,
debug: opt.debug,
})
.await?;

create_folder_if_not_exists(secrets_folder);
match apply_folder(secrets_folder) {
Expand All @@ -312,36 +358,34 @@ async fn main() -> Result<(), Box<dyn Error>> {
}
}

// remove .git from repo
let repo = repo.trim_end_matches(".git");
let base_apps = parsing::get_applications_as_string(
BASE_BRANCH_FOLDER,
&base_branch_name,
&file_regex,
&selector,
repo,
)
.await?;
let target_apps = parsing::get_applications_as_string(
TARGET_BRANCH_FOLDER,
&target_branch_name,
&file_regex,
&selector,
repo,
)
argocd::install_argo_cd(argocd::ArgoCDOptions {
version: argocd_version,
debug: opt.debug,
})
.await?;

fs::write(apps_file(&Branch::Base), base_apps)?;
fs::write(apps_file(&Branch::Target), &target_apps)?;
fs::write(apps_file(&Branch::Base), applications_to_string(base_apps))?;
fs::write(
apps_file(&Branch::Target),
applications_to_string(target_apps),
)?;

// Cleanup
// Cleanup output folder
clean_output_folder(output_folder);

extract::get_resources(&Branch::Base, timeout, output_folder).await?;
extract::delete_applications().await;
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
extract::get_resources(&Branch::Target, timeout, output_folder).await?;
// Extract resources from Argo CD
if found_base_apps {
extract::get_resources(&Branch::Base, timeout, output_folder).await?;
if found_target_apps {
extract::delete_applications().await;
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
}
}
if found_target_apps {
extract::get_resources(&Branch::Target, timeout, output_folder).await?;
}

// Delete cluster
match tool {
ClusterTool::Kind => kind::delete_cluster(&cluster_name),
ClusterTool::Minikube => minikube::delete_cluster(),
Expand Down Expand Up @@ -378,7 +422,7 @@ fn apply_manifest(file_name: &str) -> Result<Output, Output> {
.arg("-f")
.arg(file_name)
.output()
.expect(format!("failed to apply manifest: {}", file_name).as_str());
.unwrap_or_else(|_| panic!("failed to apply manifest: {}", file_name));
match output.status.success() {
true => Ok(output),
false => Err(output),
Expand Down
Loading

0 comments on commit b7c9b42

Please sign in to comment.