Skip to content

Commit

Permalink
Implement CleanFiles command
Browse files Browse the repository at this point in the history
  • Loading branch information
tailhook committed Apr 4, 2018
1 parent c190397 commit aa25c3c
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 1 deletion.
42 changes: 42 additions & 0 deletions doc/render/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,45 @@ Options:
``commands``
List of commands to execute when condition is true. All the same commands
suported except the ``!Condition`` itself.


CleanFiles
----------

Cleans files by pattern, keeping only ones listed.

Example:

.. code-block: yaml
templates:
list: keep_list.txt.trm
commands:
- !CleanFiles
keep-list: "{{ list }}"
pattern: /etc/nginx/sites-available/(*).conf
Options:

``pattern``
Filename pattern to check. This supports basic **glob** syntax plus
any part of path can be *captured* like in regular expression. This means
that only parenthised part is matched against keep list, and only files
that match glob are removed.

Few pattern examples:

* ``/dir/(*).conf`` deletes ``*.conf`` files, ``keep-list`` contains
file names without extension
* ``/dir/(*.conf)``, same but ``keep-list`` contains filenames with
extension
* ``/dir/(**/*.conf)``, deletes ``*.conf`` recursively, where keep list
contains relative path (*without* ``./``)

``keep-list``
Filename of the file which lists **names** which should be kept. Each
line represents single name. The contents of each line matched against
thing captured in ``pattern`` (see above). No comments or escaping
is supported, empty lines are ignored.

66 changes: 66 additions & 0 deletions src/render/apply/clean_files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::collections::HashSet;
use std::io::{BufRead, BufReader};
use std::fs::{File, remove_file};

use capturing_glob::{glob_with, MatchOptions};
use quire::validate as V;

use apply::{Task, Error, Action};
use apply::expand::Variables;

#[derive(Deserialize, Debug, Clone)]
pub struct CleanFiles {
pattern: String,
keep_list: String,
}

impl CleanFiles {
pub fn config() -> V::Structure<'static> {
V::Structure::new()
.member("pattern", V::Scalar::new())
.member("keep_list", V::Scalar::new())
}
}

impl Action for CleanFiles {
fn execute(&self, task: &mut Task, variables: &Variables)
-> Result<(), Error>
{
let pattern = variables.expand(&self.pattern);
let keep_list = variables.expand(&self.keep_list);
task.log(format_args!(
"CleanFiles {{ pattern: {:?}, keep_list: {:?} }}\n",
&pattern, &keep_list));

let src = BufReader::new(File::open(&keep_list)
.map_err(|e| format_err!("can't open {:?}: {}", keep_list, e))?);
let keep_list = src.lines().collect::<Result<HashSet<_>, _>>()?;
let items = glob_with(&pattern, &MatchOptions {
case_sensitive: true,
require_literal_separator: true,
require_literal_leading_dot: true,
}).map_err(|_| format_err!(
"CleanFiles has invalid pattern: {:?}", pattern))?;

for entry in items {
let entry = entry.map_err(|e| {
format_err!("{:?}: {}",
e.path().to_path_buf(), e.error())
})?;
let name = entry.group(1)
.and_then(|x| x.to_str()).unwrap_or("");
if !keep_list.contains(name) {
let path = entry.path();
if task.dry_run {
task.log.log(format_args!("would remove {:?}\n", path));
} else {
task.log.log(format_args!("removing {:?}\n", path));
remove_file(path)?;
}
}
}

Ok(())
}

}
5 changes: 5 additions & 0 deletions src/render/apply/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod expand;
pub mod cmd;
pub mod condition;
pub mod copy;
pub mod clean_files;
pub mod peek_log;
pub mod root_command;
pub mod shell;
Expand All @@ -29,6 +30,7 @@ const COMMANDS: &'static [&'static str] = &[
"Copy",
"Condition",
"SplitText",
"CleanFiles",
"PeekLog",
];

Expand All @@ -39,6 +41,7 @@ pub enum CommandName {
Copy,
Condition,
SplitText,
CleanFiles,
PeekLog,
}

Expand Down Expand Up @@ -103,6 +106,7 @@ impl<'a> Visitor<'a> for NameVisitor {
"Copy" => Copy,
"Condition" => Condition,
"SplitText" => SplitText,
"CleanFiles" => CleanFiles,
"PeekLog" => PeekLog,
_ => return Err(E::custom("invalid command")),
};
Expand Down Expand Up @@ -136,6 +140,7 @@ impl<'a> Visitor<'a> for CommandVisitor {
Copy => decode::<copy::Copy, _>(v),
Condition => decode::<condition::Condition, _>(v),
SplitText => decode::<split_text::SplitText, _>(v),
CleanFiles => decode::<clean_files::CleanFiles, _>(v),
PeekLog => decode::<peek_log::PeekLog, _>(v),
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/render/apply/split_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl Action for SplitText {
let src_fn = variables.expand(&self.src);
let dest = variables.expand(&self.dest);
task.log(format_args!("SplitText {{ src: {:?}, dest: {:?} }}\n",
&self.src, &self.dest));
&src_fn, &dest));

if !task.dry_run {
let src = BufReader::new(File::open(&src_fn)
Expand Down
1 change: 1 addition & 0 deletions src/render/renderfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub fn command_validator<'x>(root: bool) -> V::Enum<'x> {
.option("Sh", apply::shell::Sh::config())
.option("Copy", apply::copy::Copy::config())
.option("SplitText", apply::split_text::SplitText::config())
.option("CleanFiles", apply::clean_files::CleanFiles::config())
.option("PeekLog", apply::peek_log::PeekLog::config());
if root {
val = val.option("Condition", apply::condition::Condition::config())
Expand Down

0 comments on commit aa25c3c

Please sign in to comment.