Skip to content

Commit

Permalink
support target and empty output (#30)
Browse files Browse the repository at this point in the history
* support target

* local mode
  • Loading branch information
hirosassa authored Nov 26, 2022
1 parent b8c5e5b commit 675ad0c
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 17 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,20 @@ skaffold render -p dev | kubectl diff -f - 2> /dev/null | | ksnotify --notifier
```

The concrete example of GitLab CI configuration is shown in [example](https://github.com/hirosassa/ksnotify/tree/main/example).


## For developers

To run `ksnotify` locally, use local option for debug.
For local mode, `ksnotify` just renders contents on stdout.

```console
skaffold render -p dev | kubectl diff -f - 2> /dev/null | ~/Dev/ksnotify/ksnotify --ci local --notifier gitlab --suppress-skaffold

> ## Plan result
> [CI link]( )
>
> * updated
> blah
> blah
```
9 changes: 9 additions & 0 deletions src/ci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ use strum_macros::EnumString;

#[derive(Debug, PartialEq, Eq, Clone, Copy, EnumString)]
pub enum CIKind {
/// ksnotify is running on GitLab CI.
#[strum(serialize = "gitlab")]
GitLab,

/// ksnotify is running on Local PC (for debug).
#[strum(serialize = "local")]
Local,
}

#[derive(Clone, Debug)]
Expand All @@ -32,6 +37,10 @@ impl CI {
merge_request,
})
}
CIKind::Local => Ok(Self {
job_url: "".to_string(),
merge_request: MergeRequest { number: 1 },
}),
}
}

Expand Down
31 changes: 24 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ pub struct Cli {
#[arg(long)]
pub notifier: Option<String>,

/// Whether if suppress diffs comes from Skaffold labels
/// Target component name to distinguish for each environments or product.
#[arg(long)]
pub target: Option<String>,

/// Whether if suppress diffs comes from Skaffold labels.
#[arg(long)]
pub suppress_skaffold: bool,

Expand Down Expand Up @@ -56,6 +60,14 @@ fn run() -> Result<()> {
let config =
config::Config::new(&cli).with_context(|| format!("failed to load config: {:?}", cli))?;
info!("config: {:?}", config);

// Local PC (for debug)
if config.ci == ci::CIKind::Local {
let content = process(config, None, cli.target)?;
println!("{}", content);
return Ok(());
}

let ci =
ci::CI::new(config.ci).with_context(|| format!("failed to create CI: {:?}", config.ci))?;
let notifier_kind = config.notifier;
Expand All @@ -65,14 +77,19 @@ fn run() -> Result<()> {
}
.with_context(|| format!("failed to create notifier: {:?}", ci))?;

let content = process(config, Some(ci.job_url().to_string()), cli.target)?;
notifier
.notify(content)
.with_context(|| "failed to notify".to_string())?;
Ok(())
}

fn process(config: config::Config, url: Option<String>, target: Option<String>) -> Result<String> {
let mut body = String::new();
io::stdin().read_to_string(&mut body)?;
let parser = parser::DiffParser::new(config.suppress_skaffold)?;
let result = parser.parse(&body)?;
let template = template::Template::new(result.kind_result, ci.job_url().to_string());

notifier
.notify(template.render()?)
.with_context(|| "failed to notify".to_string())?;
Ok(())
let link = url.unwrap_or_default();
let template = template::Template::new(result.kind_result, link, target);
template.render()
}
117 changes: 107 additions & 10 deletions src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,24 @@ use serde::Serialize;

#[derive(Serialize)]
pub struct Template {
title: String,
target: Option<String>,
changed_kinds: String,
details: String,
link: String,
is_no_changes: bool,
}

impl Template {
const DEFAULT_BUILD_TITLE: &'static str = "## Plan result";

const DEFAULT_BUILD_TEMPLATE: &'static str = "{{ title }}
const DEFAULT_BUILD_TITLE_TEMPLATE: &'static str =
"## Plan result{{#if target}} ({{target}}){{/if}}";
const DEFAULT_BUILD_BODY_TEMPLATE: &'static str = "
[CI link]( {{ link }} )
{{#if is_no_changes}}
```
No changes. Kubernetes configurations are up-to-date.
```
{{else}}
* updated
{{ changed_kinds }}
Expand All @@ -28,23 +33,28 @@ impl Template {
{{{ details }}}
</details>
{{/if}}
";

pub fn new(results: HashMap<String, String>, link: String) -> Self {
pub fn new(results: HashMap<String, String>, link: String, target: Option<String>) -> Self {
let changed_kinds = Self::generate_changed_kinds_markdown(&results);
let details = Self::generate_details_markdown(&results);
let is_no_changes = results.is_empty();
Self {
title: Self::DEFAULT_BUILD_TITLE.to_string(),
target,
changed_kinds,
details,
link,
is_no_changes,
}
}

pub fn render(&self) -> Result<String> {
let reg = Handlebars::new();
let j = serde_json::to_value(self).unwrap();
Ok(reg.render_template(Self::DEFAULT_BUILD_TEMPLATE, &j)?)
let title = reg.render_template(Self::DEFAULT_BUILD_TITLE_TEMPLATE, &j)?;
let body = reg.render_template(Self::DEFAULT_BUILD_BODY_TEMPLATE, &j)?;
Ok(format!("{}{}", title, body))
}

fn generate_changed_kinds_markdown(results: &HashMap<String, String>) -> String {
Expand All @@ -57,7 +67,7 @@ impl Template {
let details: Vec<String> = kinds
.iter()
.map(|k| {
let title = format!("## {}", k);
let title = format!("### {}", k);
let body = format!("```diff\n{}\n```", results[k]);
format!("{}\n{}", title, body)
})
Expand Down Expand Up @@ -94,7 +104,94 @@ mod tests {
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
let actual = Template::generate_details_markdown(&data);
let expected = "## test1\n```diff\nABC\n```\n## test2\n```diff\nDEF\n```".to_string();
let expected = "### test1\n```diff\nABC\n```\n### test2\n```diff\nDEF\n```".to_string();
assert_eq!(actual, expected);
}

#[test]
fn test_new_with_no_changes() {
let results: HashMap<String, String> = HashMap::new();
let link = "http://example.com".to_string();
let target = Some("sample app".to_string());
let t = Template::new(results, link, target);
assert_eq!(t.is_no_changes, true);
}

#[test]
fn test_new_with_some_changes() {
let mut results: HashMap<String, String> = HashMap::new();
results.insert("sample".to_string(), "change content".to_string());
let link = "http://example.com".to_string();
let target = Some("sample app".to_string());
let t = Template::new(results, link, target);
assert_eq!(t.is_no_changes, false);
}

#[test]
fn test_render_title_with_target() {
let reg = Handlebars::new();
let mut data = HashMap::new();
data.insert("target".to_string(), "sample".to_string());
let actual = reg
.render_template(Template::DEFAULT_BUILD_TITLE_TEMPLATE, &data)
.unwrap();
let expected = "## Plan result (sample)".to_string();
assert_eq!(actual, expected);
}

#[test]
fn test_render_title_without_target() {
let reg = Handlebars::new();
let data = HashMap::<String, String>::new();
let actual = reg
.render_template(Template::DEFAULT_BUILD_TITLE_TEMPLATE, &data)
.unwrap();
let expected = "## Plan result".to_string();
assert_eq!(actual, expected);
}

#[test]
fn test_render_body_with_no_changes() {
let reg = Handlebars::new();
let mut data = HashMap::new();
data.insert("is_no_changes".to_string(), "true".to_string());
data.insert("link".to_string(), "http://example.com".to_string());
let actual = reg
.render_template(Template::DEFAULT_BUILD_BODY_TEMPLATE, &data)
.unwrap();
let expected = "
[CI link]( http://example.com )
```
No changes. Kubernetes configurations are up-to-date.
```
"
.to_string();
assert_eq!(actual, expected);
}

#[test]
fn test_render_body_with_changes() {
let reg = Handlebars::new();
let mut data = HashMap::new();
data.insert("link".to_string(), "http://example.com".to_string());
data.insert("link".to_string(), "http://example.com".to_string());
let actual = reg
.render_template(Template::DEFAULT_BUILD_BODY_TEMPLATE, &data)
.unwrap();
let expected = "
[CI link]( http://example.com )
* updated
<details><summary>Details (Click me)</summary>
</details>
"
.to_string();
assert_eq!(actual, expected);
}
}

0 comments on commit 675ad0c

Please sign in to comment.