Skip to content

Commit

Permalink
feat: add directory feature to yambler
Browse files Browse the repository at this point in the history
  • Loading branch information
chaaz committed Sep 18, 2020
1 parent 4b0584a commit 4524cff
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 43 deletions.
29 changes: 19 additions & 10 deletions scripts/yamble-repo-pre-push.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,28 @@
# see https://github.com/chaaz/versio-actions/tree/main/yambler.

repo=`git rev-parse --show-toplevel`
ghdir="$repo/.github"
tmp_dir=`mktemp -d -t yamble-pre-push`

for f in $repo/.github/workflows-src/*.* ; do
s=`basename $f`
yambler \
-i "$f" \
-o "$tmp_dir/$s" \
-s $repo/.github/snippets/*.*
if ! diff "$repo/.github/workflows/$s" "$tmp_dir/$s" >/dev/null 2>&1 ; then
echo >&2 "Workflow $s is out-of-date"
rm -rf $tmp_dir
exit 1
yambler -i "$ghdir/workflows-src" -o "$tmp_dir" -s "$ghdir/snippets"

bads=''
for f in $tmp_dir/*.* ; do
b=`basename "$f"`
if ! diff "$f" "$ghdir/workflows/$b" >/dev/null 2>&1 ; then
if [ "$bads" == '' ] ; then
bads="$b"
else
bads="$bads, $b"
fi
fi
done

rm -rf $tmp_dir

if [ "$bads" != '' ] ; then
echo >&2 "Out of date workflows: $bads"
echo >&2 "Use the yambler or yamble-repo script:"
echo >&2 " https://github.com/chaaz/versio-actions/tree/main/yambler"
exit 1
fi
13 changes: 6 additions & 7 deletions scripts/yamble-repo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@
# workflows.

repo=`git rev-parse --show-toplevel`
ghdir="$repo/.github"

rm -f $repo/.github/workflows/*.*
for f in $repo/.github/workflows-src/*.* ; do
yambler \
-i "$f" \
-o "$repo/.github/workflows/`basename $f`" \
-s $repo/.github/snippets/*.*
done
mkdir -p "$ghdir/workflows-src"
mkdir -p "$ghdir/workflows"
mkdir -p "$ghdir/snippets"

yambler -i "$ghdir/workflows-src" -o "$ghdir/workflows" -s "$ghdir/snippets"
13 changes: 13 additions & 0 deletions yambler/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Changelog

## Version 0.1.1

### Added

- The ability for the yambler to operate on an entire directory of input
files.

### Changed

- Updated scripts to use the new directory feature.
- Documented new directory feature.
18 changes: 14 additions & 4 deletions yambler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@ and understand them.

Run like this:
```
$ yambler -i <input file> -o <output file> -s <snippet files ...>
yambler -i <input file> -o <output file> -s <snippet files ...>
```
Or like this:
```
yambler -i <input dir> -o <output dir> -s <snippet dir>
```

This replaces _placeholder strings_ in the input file(s) with YAML
objects defined in the snippet files, writing the resultant document to
the output file(s).

This replaces _placeholder strings_ in the input file with YAML objects
defined in the snippet files, writing the resultant document to the
output file.
The `--snippet` (`-s`) argument can be a list of files, or a single
directory with a bunch of YAML files in it. Likewise, the `--input`
(`-i`) argument can either be a single file, or a directory that
contains a bunch of YAML input files. If the input is a directory, the
output (`--output` / `-o`) must also be a directory.

## Getting Started

Expand Down
116 changes: 94 additions & 22 deletions yambler/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,103 @@ use crate::errors::Result;
use clap::{crate_version, App, AppSettings, Arg};
use error_chain::bail;
use std::collections::HashMap;
use std::path::Path;
use std::path::{Path, PathBuf};
use yaml_rust::{Yaml, YamlEmitter, YamlLoader};

fn run(input_file: &str, output_file: &str, snips: &[&str]) -> Result<()> {
let inputs: Vec<Yaml> = YamlLoader::load_from_str(&std::fs::read_to_string(input_file)?)?;
let inputs = read_inputs(input_file, output_file)?;
let snips = read_snips(snips)?;
convert_and_write(inputs, &snips)
}

fn convert_and_write(inputs: Vec<(PathBuf, PathBuf, Vec<Yaml>)>, snips: &HashMap<String, Yaml>) -> Result<()> {
for (input_file, output_file, inputs) in inputs {
let mut trail = Vec::new();
let outputs = inputs.into_iter().map(|input| replace(&mut trail, input, snips)).collect::<Result<Vec<_>>>()?;

let input_file_name = input_file.file_name().map(|n| n.to_string_lossy()).unwrap_or_else(|| "??".into());
let mut out_str = format!("---\n# DO NOT EDIT\n# Created from template \"{}\".\n", input_file_name);
let header_len = out_str.len();
let mut emitter = YamlEmitter::new(&mut out_str);
for output in &outputs {
emitter.dump(output)?;
}
if !outputs.is_empty() {
out_str.replace_range(header_len .. header_len + 4, "");
}
std::fs::write(output_file, &out_str)?;
}

let snips = snips
.iter()
Ok(())
}

/// Read the input file and output file, and build a list of entries to work on.
///
/// Each entry has the form (input_file_path, output_file_path, list_of_yamls_in_input_file). If the input file
/// is a single file, the output file must also be a single file, and the list of entries has a length of one.
/// If the input file is a directory, the output file must also be a directory, and the list of entries
/// corresponds to each YAML file in the input directory.
fn read_inputs(input_file: &str, output_file: &str) -> Result<Vec<(PathBuf, PathBuf, Vec<Yaml>)>> {
let input_file = Path::new(input_file);
let output_file = Path::new(output_file);

if input_file.is_dir() {
if !output_file.is_dir() {
bail!("If input is a directory, output must also be a directory.");
}
input_file
.read_dir()?
.filter(|entry| {
entry.as_ref().map(|entry| {
let n = entry.file_name().to_string_lossy().to_string();
n.ends_with(".yml") || n.ends_with(".yaml")
}).unwrap_or(false)
})
.map(|entry| {
let entry = entry?;
let output = output_file.join(entry.file_name());
Ok((entry.path(), output, YamlLoader::load_from_str(&std::fs::read_to_string(entry.path())?)?))
})
.collect::<Result<Vec<_>>>()
} else {
if output_file.is_dir() {
bail!("If input is a file, output must also be a file.");
}
Ok(vec![(
input_file.to_path_buf(),
output_file.to_path_buf(),
YamlLoader::load_from_str(&std::fs::read_to_string(input_file)?)?
)])
}
}

/// Read the snippet files into a hash map.
///
/// The input list of files can be multiple YAML files, or can be a single directory, in which case all YAML
/// files in the directory are counted. The resulting hashmap are the keyed YAML snippets which are found in all
/// files.
fn read_snips(snips: &[&str]) -> Result<HashMap<String, Yaml>> {
let snips = snips.iter().map(|s| Path::new(s)).collect::<Vec<_>>();
let snips: Vec<_> = if snips.len() == 1 && snips[0].is_dir() {
snips[0]
.read_dir()?
.filter_map(|entry| {
entry.ok().and_then(|entry| {
let n = entry.file_name().to_string_lossy().to_string();
if n.ends_with(".yml") || n.ends_with(".yaml") {
Some(entry.path())
} else {
None
}
})
})
.collect()
} else {
snips.into_iter().map(|s| s.to_path_buf()).collect()
};

snips
.into_iter()
.map(|snip| Ok(YamlLoader::load_from_str(&std::fs::read_to_string(snip)?)?))
.collect::<Result<Vec<_>>>()?
.into_iter()
Expand All @@ -28,24 +117,7 @@ fn run(input_file: &str, output_file: &str, snips: &[&str]) -> Result<()> {
let val = hash.remove(&Yaml::String("value".into())).ok_or_else(|| bad!("Yaml snip doesn't have a value."))?;
Result::Ok((key, val))
})
.collect::<Result<_>>()?;

let mut trail = Vec::new();
let outputs = inputs.into_iter().map(|input| replace(&mut trail, input, &snips)).collect::<Result<Vec<_>>>()?;

let input_file_name = Path::new(input_file).file_name().map(|n| n.to_string_lossy()).unwrap_or_else(|| "??".into());
let mut out_str = format!("---\n# DO NOT EDIT\n# Created from template \"{}\".\n", input_file_name);
let header_len = out_str.len();
let mut emitter = YamlEmitter::new(&mut out_str);
for output in &outputs {
emitter.dump(output)?;
}
if !outputs.is_empty() {
out_str.replace_range(header_len .. header_len + 4, "");
}
std::fs::write(output_file, &out_str)?;

Ok(())
.collect::<Result<_>>()
}

fn replace(trail: &mut Vec<String>, input: Yaml, snips: &HashMap<String, Yaml>) -> Result<Yaml> {
Expand Down

0 comments on commit 4524cff

Please sign in to comment.