Skip to content

Commit

Permalink
docs: improve documentation and publish info
Browse files Browse the repository at this point in the history
  • Loading branch information
chaaz committed Sep 17, 2020
1 parent 98691c5 commit d47f1db
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 47 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Versio Actions

Some simple GitHub actions usable with
[Versio](https://github.com/chaaz/versio)
[Versio](https://github.com/chaaz/versio). We also host the
[Yambler](./yambler) here, which is helpful to reduce bloat and repeated
configuration when you're constructing your GitHub Actions.
8 changes: 6 additions & 2 deletions yambler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ name = "yambler"
version = "0.1.0"
authors = ["Charlie Ozinga <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
description = "Yambler is a tool to stitch reusable yaml snippets together."
homepage = "https://github.com/chaaz/versio-actions/tree/changes/yambler/"
repository = "https://github.com/chaaz/versio-actions/"
license-file = "LICENSE.txt"
keywords = ["yaml", "actions", "development", "configuration"]
categories = ["command-line-utilities", "config"]

[dependencies]
serde_yaml = "0.8"
Expand Down
11 changes: 11 additions & 0 deletions yambler/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
This is Versio, written by Charles Ozinga <[email protected]>

This code is made available "AS IS" without warranties of any kind. Your
use of this software code is at your own risk and you waive any claim
against Charles Ozinga with respect to your use of this software code.

Copyright (c) 2020 Charles Ozinga, all rights reserved

Permission is granted to make and distribute verbatim copies of this
document provided that the copyright notice and this permission notice
are preserved on all copies.
129 changes: 85 additions & 44 deletions yambler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,50 @@ This replaces _placeholder strings_ in the input file with YAML objects
defined in the snippet files, writing the resultant document to the
output file.

## Getting Started

Download the Yambler binary for your platform from our
[Releases](https://github.com/chaaz/versio-actions/releases) pages, and
start yambling some yamls.

## For GitHub Actions

As of this writing, it's difficult to reuse common logic in the various
workflows that you build for GitHub Actions. You're either stuck
publishing custom actions (which themselves are limited in what they can
reuse), hacking some shell scripts together, or doing some big
copy-and-paste and hoping you remember where everything is when you need
change things.
copy-and-paste and hoping you remember where all the copies are when you
need to make a change.

The Yambler was written to deal with this problem: it's not an ideal
solution (more powerful composite actions and/or respecting YAML
aliases/anchors would probably be better), but this lets you at least
keep your workflows relatively DRY.
solution (GitHub is working on more elegant solutions), but this lets
you at least keep your workflows relatively
[DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).

### Example

I currently have `.github/workflows-src` directory in my repos, which is
where I keep the "source" workflow templates. Such a template might look
something like this:
The Yambler is used in the CI/CD pipelines of the
[Versio](https://github.com/chaaz/versio) release manager, another handy
developer tool. My `.github` directory there looks something like this:

```
.github
├─ workflows-src
│  ├─ pr.yml
│  └─ release.yml
├─ snippets
│  ├─ check-versio.yml
│  ├─ common-env.yml
│  ├─ job-premerge-checks.yml
│  └─ <other snippet files ...>
└─ workflows
├─ pr.yml
└─ release.yml
```

I don't touch anything in `workflows` directly: everything there is
script-generated. Instead, `workflows-src` is where I do my top-level
editing. `.github/workflows-src/pr.yml` looks something like this:

```yaml
---
Expand All @@ -44,37 +71,48 @@ jobs:
premerge-checks: SNIPPET_job-premerge-checks
```
I then keep my snippets, one per file, in their own directory, e.g.
`.github/snippets/job-create-matrixes.yml`. Just before I push my repo,
I generate the actual workflows with a script:
I then keep my snippets, one per file, in `.github/snippets`. Here's
`common-env.yml`:

```yaml
key: common-env
value:
RUSTFLAGS: '-D warnings'
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_USER: ${{ github.actor }}
```

Just before I push my repo, I generate the actual workflows by calling
`yambler` once for each `workflow-src` file:

```bash
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
yambler \
-i .github/workflows-src/pr.yml \
-o .github/workflows/pr.yml \
-s .github/snippets/*.yml
```

This script is available in this repository as `scripts/yamble-repo.sh`
Of course, there's a script that automatically does this for each file,
available in this repository as `scripts/yamble-repo.sh`
[here](../scripts/yamble-repo.sh). Feel free to use it directly, or
modify it to taste. There's also a similar script named
modify it to taste; it's pretty straightforward. There's also a
companion script named
[yamble-repo-pre-push.sh](../scripts/yamble-repo-pre-push.sh), which you
can copy to a file named `.git/hooks/pre-push` in your local repo to
ensure that your workflows are up-to-date before pushing.
ensure that your workflows are up-to-date when you push.

Due the nature of how GitHub Actions work, the generated workflows do
need to be committed and pushed: while it would be theoretically
possible to generate and execute these workflows automatically (possibly
in its own static workflow), I find it easier just to run this manually
before I push, or whenever I change the workflow sources.
Due the nature of how GitHub Actions work, the generated workflows must
be committed and pushed: you can't `.gitignore` them (like you'd
normally want to do to generated files) or you defeat their whole
purpose. While it would be theoretically possible to generate and
execute these workflows automatically (possibly in its own static
workflow), I find it easier just to run this manually before I push, or
whenever I change the workflow sources.

## Operation

This is how the application works: First, all documents of the input
file are read, and everywhere a placeholder string of the form
This is how the Yambler works: First, all documents of the input file
are read, and wherever a placeholder string of the form
"SNIPPET\_&lt;snippet name&gt;" is encountered, it is replaced by the
YAML snippet value defined in the snippet files. This process happens
recursively, so snippets can contain other snippets, etc. Infinite loops
Expand All @@ -90,33 +128,36 @@ could be replaced by simple quotes, etc.
Using Yambler is roughly analogous to using a macro language such as
C/C++ macros, VBA, or ML/1; with many of the same benefits and pitfalls.
There is no parameter passing or templating: snippets are inserted
verbatim into the text, so keep that in mind as you use them.
verbatim into the text, so keep that in mind as you use them. On the
other hand, this makes it very easy to judge what your final output is
going to be.

### Snippets

A snippet file can have multiple documents, and each is considered a
snippet: it's expected that each document is a hash with at least the
two keys "key" and "value". The "key" must be a string that defines
snippet key (which is identified in the placeholder string); the "value"
is the YAML value itself, which can have any YAML type.
A snippet file can have multiple documents, and each is considered its
own snippet: each snippet document must be a hash with at least the two
keys "key" and "value". (You can have other keys: they're just ignored.)
The "key" must be a string that defines the snippet key (which is
identified in the placeholder string); the "value" is the YAML value
itself, which can have any YAML type.

The names of the snippet files is largely irrelevant, but it's good
The names of the snippet files are largely irrelevant, but it's good
practice to have at least some association between the file name and the
snippets contained within, so that it's easy to find a particular
snippet when you need to quickly find it.
snippets contained within, so that it's easy to quickly find a
particular snippet.

If multiple snippets are defined with the same key, the behavior is
undefined, although what probably happens is that the last defined
snippet is the one that "wins" that key.
snippet is the one that "wins" that key. Don't do this!

### Splicing

An exception to the above operation is the _splice rule_: if the
placeholder string is in an array, and the snippet that is replacing it
is _also_ an array, then the snippet array is spliced in directly,
rather than replacing the single element. This makes it easy to place a
snippet directly inside a array, or to concatenate multiple snippets to
form a longer list.
One exception to the replacement described above is the _splice rule_:
if the placeholder string is a direct array element, and the replacement
snippet is _also_ an array, then the snippet array is spliced in
directly, rather than replacing the single element. This makes it easy
to place a snippet directly inside a array, or to concatenate multiple
snippets to form a longer list.

## Examples

Expand Down

0 comments on commit d47f1db

Please sign in to comment.