]> arthur.barton.de Git - ansible-collection-boilerplate.git/commitdiff
Initial commit
authorAlexander Barton <alex@barton.de>
Wed, 19 Apr 2023 10:24:13 +0000 (12:24 +0200)
committerAlexander Barton <alex@barton.de>
Thu, 25 May 2023 12:29:01 +0000 (14:29 +0200)
This implements the core functionality, including the "ansible-boilerplate"
script, Ansible wrapper scripts "a", "ap" and "aps", the Makefile, and
boilerplate configuration files for Ansible, ansible-lint, yamllint, etc.

19 files changed:
.ansible-lint [new file with mode: 0644]
.editorconfig [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.vscode/settings.json [new file with mode: 0644]
.yamllint.yml [new file with mode: 0644]
CHANGELOG.md [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.boilerplate [new file with mode: 0644]
README.md [new file with mode: 0644]
ansible.cfg [new file with mode: 0644]
bin/a [new file with mode: 0755]
bin/ansible-boilerplate [new file with mode: 0755]
bin/ap [new file with mode: 0755]
bin/aps [new file with mode: 0755]
galaxy.yml [new file with mode: 0644]
meta/runtime.yml [new file with mode: 0644]
playbooks/ping.yml [new file with mode: 0644]
requirements.txt [new file with mode: 0644]

diff --git a/.ansible-lint b/.ansible-lint
new file mode 100644 (file)
index 0000000..627e059
--- /dev/null
@@ -0,0 +1,14 @@
+---
+# ansible-lint configuration.
+# See <https://ansible-lint.readthedocs.io/configuring/>.
+
+exclude_paths:
+  - .git/
+  - .venv/
+  - ansible_galaxy/ansible_collections/
+  - ansible_galaxy/ansible_roles/
+  - bin/
+  - requirements.yml
+
+skip_list:
+  - galaxy[version-incorrect]
diff --git a/.editorconfig b/.editorconfig
new file mode 100644 (file)
index 0000000..36f06c5
--- /dev/null
@@ -0,0 +1,33 @@
+# .editorconfig: Editor settings, see <https://editorconfig.org>.
+
+# top-most EditorConfig file
+root = true
+
+# Defaults for all file types:
+
+[*]
+# Use UTF-8 encoding.
+charset = utf-8
+# Unix-style newlines.
+end_of_line = lf
+# Files should end with a newline.
+insert_final_newline = true
+# Remove whitespace characters preceding newline characters.
+trim_trailing_whitespace = true
+
+# File type specific settings follow:
+
+[{bin/*,Makefile*,*.sh}]
+# Tab indentation (8 characters wide).
+indent_style = tab
+indent_size = 8
+
+[*.{json,md}]
+# 4 space indentation.
+indent_style = space
+indent_size = 4
+
+[*.{yaml,yml}]
+# 2 space indentation.
+indent_style = space
+indent_size = 2
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d76a635
--- /dev/null
@@ -0,0 +1,14 @@
+# Intentionally untracked files to ignore. See gitignore(5).
+
+.DS_Store
+*.bak
+*.new
+*~
+/.cache
+/.venv
+/ansible_galaxy/ansible_collections/
+/ansible_galaxy/ansible_roles/
+/bin/a
+/bin/ap
+/bin/aps
+/dist/
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644 (file)
index 0000000..efb96e6
--- /dev/null
@@ -0,0 +1,14 @@
+{
+    "ansible.ansible.path": ".venv/bin/ansible",
+    "ansible.python.interpreterPath": ".venv/bin/python",
+    "ansible.validation.lint.path": ".venv/bin/ansible-lint",
+    "files.associations": {
+        "**/ansible/**/*.yml": "ansible",
+        "**/group_vars/*": "ansible",
+        "**/host_vars/*": "ansible",
+        "**/playbooks/**/*.yml": "ansible",
+        "**/roles/**/*.yml": "ansible",
+        "Makefile*": "makefile"
+    },
+    "python.defaultInterpreterPath": ".venv/bin/python"
+}
diff --git a/.yamllint.yml b/.yamllint.yml
new file mode 100644 (file)
index 0000000..d61e11d
--- /dev/null
@@ -0,0 +1,16 @@
+---
+# yamllint configuration.
+# See <https://yamllint.readthedocs.io/en/stable/configuration.html>.
+
+extends: default
+
+rules:
+  line-length:
+    max: 100
+    level: warning
+
+ignore: |
+  .trunk/
+  .venv/
+  ansible_galaxy/ansible_collections/
+  ansible_galaxy/ansible_roles/
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..197ab10
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,18 @@
+Copyright 2023 Alexander Barton <alex@barton.de>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..df66180
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,38 @@
+#
+# Ansible Boilerplate Collection: Makefile for the project itself.
+#
+
+DIST_D = $(CURDIR)/dist
+
+default: all
+
+# Include the "real" boilerplate Makefile into this "wrapper" Makefile:
+include Makefile.boilerplate
+
+check: check-ansible
+
+install:
+
+clean:
+
+distclean: clean distclean-ansible
+#      Remove distribution directory
+       rm -fr "$(DIST_D)"
+
+maintainer-clean: distclean
+
+dist: all $(VENV_BIN)/ansible-galaxy
+#      Create distribution archive ...
+       mkdir -p "$(DIST_D)"
+       "$(VENV_BIN)"/ansible-galaxy collection build --force --output-path "$(DIST_D)"
+
+distcheck: dist
+#      Run tests on distribution archive ...
+       mkdir -p "$(DIST_D)/check"
+       tar -C "$(DIST_D)/check" -xzf "$(DIST_D)"/*.tar.gz
+       test -e "$(VENV_D)" && ln -fs "$(VENV_D)" "$(DIST_D)/check/.venv"
+       make -C "$(DIST_D)/check" check
+#      Clean up ...
+       rm -fr "$(DIST_D)/check"
+
+.PHONY: default all check install clean distclean maintainer-clean dist distcheck
diff --git a/Makefile.boilerplate b/Makefile.boilerplate
new file mode 100644 (file)
index 0000000..4c4c6d5
--- /dev/null
@@ -0,0 +1,67 @@
+#
+# Ansible Boilerplate Collection: Makefile for inclusion into projects using it.
+#
+
+PYTHON ?= python3
+SOURCE_ROOT ?= $(CURDIR)
+VENV_D = $(SOURCE_ROOT)/.venv
+VENV_BIN = $(VENV_D)/bin
+
+this_makefile_path:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
+this_dir:=$(shell cd $(dir $(this_makefile_path));pwd)
+
+check-ansible: $(VENV_BIN)/ansible-lint
+#      Check Ansible executable ...
+       "$(VENV_BIN)"/ansible --version
+#      Check YAML files ...
+       "$(VENV_BIN)"/yamllint .
+#      Run ansible-playbook syntax check, when a "site play" is found ...
+ifneq ("$(wildcard playbooks/site.yml)","")
+       "$(VENV_BIN)"/ansible-playbook --syntax-check playbooks/site.yml
+endif
+ifneq ("$(wildcard site.yml)","")
+       "$(VENV_BIN)"/ansible-playbook --syntax-check site.yml
+endif
+#      Run ansible-lint ...
+       "$(VENV_BIN)"/ansible-lint --offline --project-dir "$(SOURCE_ROOT)" --show-relpath
+
+distclean-ansible:
+#      Remove Python "virtual environment" ...
+       rm -fr "$(SOURCE_ROOT)"/.venv
+#      Remove Ansible Galaxy collections and roles
+       rm -fr "$(SOURCE_ROOT)"/ansible_galaxy/ansible_collections "$(SOURCE_ROOT)"/ansible_galaxy/ansible_roles
+#      Remove cache directory, used by the Ansible "facts cache" for example ...
+       rm -fr "$(SOURCE_ROOT)"/.cache
+#      Clean up symlinked commands in dependant projects ...
+       for cmd in "$(SOURCE_ROOT)"/bin/a "$(SOURCE_ROOT)"/bin/ap "$(SOURCE_ROOT)"/bin/aps; do \
+         test -h "$$cmd" && rm -f "$$cmd" || true; \
+        done
+
+$(VENV_BIN)/ansible $(VENV_BIN)/ansible-galaxy $(VENV_BIN)/ansible-lint venv: $(VENV_BIN)/pip
+#      Install/upgrade Python package manager
+       "$(VENV_BIN)"/pip install --upgrade pip wheel
+       touch "$(VENV_BIN)/pip" "$(VENV_BIN)/wheel"
+#      Install/upgrade Python dependencies ...
+       "$(VENV_BIN)"/pip install --upgrade --requirement $(this_dir)/requirements.txt
+       touch "$(VENV_BIN)/ansible" "$(VENV_BIN)/ansible-galaxy" "$(VENV_BIN)/ansible-lint"
+
+$(VENV_BIN)/pip:
+#      Create/upgrade Python "virtual environment"
+       "$(PYTHON)" -m venv "$(SOURCE_ROOT)"/.venv
+
+.PHONY: venv
+
+ifneq ($(patsubst %..,,$(lastword $(SOURCE_ROOT))),)
+# SOURCE_ROOT does not end in "..", so looks like this Makefile fragment is
+# included in the top-level Makefile. So add some proprietary targets to the
+# "common" toplevel targets:
+
+all: $(VENV_BIN)/ansible $(VENV_BIN)/ansible-galaxy $(VENV_BIN)/ansible-lint
+check: check-ansible
+distclean: distclean-ansible
+
+upgrade: $(SOURCE_ROOT)/bin/ansible-boilerplate
+       $(SOURCE_ROOT)/bin/ansible-boilerplate upgrade
+
+.PHONY: check distclean
+endif
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..12f35bd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,99 @@
+# Ansible Boilerplate Collection - alexbarton.boilerplate
+
+This Ansible collection provides boilerplate files, plays, roles, scripts etc.
+on which new projects that will use Ansible can be based on.
+
+## Installation
+
+### Prerequisites
+
+To install this Ansible collection, you need the `ansible-galaxy` command. If
+it is not provided by your operating system installation by default, best is
+to create a new Python "virtual environment" inside of your project directory
+and install Ansible into it, like this:
+
+```bash
+cd ~/src/my-ansible-project
+python3 -m venv .venv
+./.venv/bin/pip install -U pip setuptools
+./.venv/bin/pip install ansible
+```
+
+#### Location of the Ansible Boilerplate Collection codebase
+
+By default, the commands here in the README file as well as the scripts of the
+Ansible Boilerplate Collection assume that the code of this collection can be
+found on the Ansible Galaxy repository with the name "alexbarton.boilerplate".
+
+If you want to override this, you can set the `BOILERPLATE_COLLECTION_SRC`
+environment variable to:
+
+* a file name (including path) to a local archive file (e. g.
+  `/tmp/ansible/alexbarton-boilerplate-0.0.1.tar.gz`)
+* a path name to a local Git repository (e. g.
+  `/usr/local/src/ansible-collection-boilerplate.git`)
+* a URL to a remote Git repository (e. g.
+  `git+https://my.git.server/ansible-collection-boilerplate.git`)
+* the name of an Collection stored on the Ansible Galaxy (e. g.
+  `alexbarton.boilerplate`)
+
+Example:
+
+```bash
+export BOILERPLATE_COLLECTION_SRC=/tmp/ansible/alexbarton-boilerplate-0.0.1.tar.gz
+```
+
+### Installing the Ansible Boilerplate Collection
+
+The following command will install the Ansible Boilerplate Collection from the
+location given in the `BOILERPLATE_COLLECTION_SRC` environment variable (see
+above) or from the Ansible Galaxy repository with its default name of
+"alexbarton.boilerplate", when the environment variable is not set:
+
+```bash
+./.venv/bin/ansible-galaxy collection install -p ansible_galaxy \
+  "${BOILERPLATE_COLLECTION_SRC:-alexbarton.boilerplate}"
+```
+
+## Initializing a New Project
+
+Once the Ansible Boilerplate Collection is available in the local
+`ansible_galaxy/ansible_collections` sub-directory, you can use the
+`ansible-boilerplate` command to initialize a new project:
+
+```bash
+./ansible_galaxy/ansible_collections/alexbarton/boilerplate/bin/ansible-boilerplate init
+```
+
+This initializes an Ansible-based project with some template files, links some
+scripts into the local `./bin/` directory (which is created as needed) and
+copies the `ansible-boilerplate` command into the `./bin/` directory, too, to
+make it available even when the "alexbarton.boilerplate" Ansible Collection is
+not yet installed on the local machine (for example, after freshly checking
+out the project or running `make distclean`).
+
+It is a good idea to commit this initial state of your new project to your
+code repository. For Git, something like this:
+
+```bash
+git init
+git add .
+git commit -m "Initial commit"
+```
+
+## Initializing/Upgrading an Existing Project
+
+In an existing and already initialized project the `ansible-boilerplate`
+command is available in the `./bin/` directory. You can use it to initialize
+the project "from scratch" and to upgrade it like this:
+
+```bash
+./bin/ansible-boilerplate upgrade
+```
+
+Or, when you use a Python virtual environment and stick to using the Makefile
+system provided by this project, you can just use the `make` command itself:
+
+```bash
+make upgrade
+```
diff --git a/ansible.cfg b/ansible.cfg
new file mode 100644 (file)
index 0000000..96aa4d0
--- /dev/null
@@ -0,0 +1,30 @@
+# Ansible configuration.
+# See <https://github.com/ansible/ansible/blob/devel/examples/ansible.cfg>.
+
+[defaults]
+ansible_managed = Managed by Ansible, DO NOT MODIFY, changes will be overwritten!
+collections_paths = ansible_galaxy
+fact_caching = jsonfile
+fact_caching_connection = .cache
+fact_caching_timeout = 3600
+gathering = smart
+interpreter_python = auto_silent
+inventory = hosts.ini
+roles_path = ansible_galaxy/ansible_roles:roles
+stdout_callback = yaml
+
+[colors]
+changed = bright yellow
+error = bright red
+ok = bright green
+
+[diff]
+always = True
+
+[privilege_escalation]
+become = True
+become_ask_pass = True
+
+[ssh_connection]
+control_path = %(directory)s/%%h-%%p-%%r
+pipelining = True
diff --git a/bin/a b/bin/a
new file mode 100755 (executable)
index 0000000..cf1eab0
--- /dev/null
+++ b/bin/a
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Ansible Collection Boilerplate:
+# "a": ansible(1) wrapper script.
+#
+
+# Change working directory to the base directory ...
+cd "$(dirname "$0")/.." || exit 1
+
+# Prepare environment ...
+set -e
+pwd
+test -x "${PWD}/.venv/bin/ansible" || make "${PWD}/.venv/bin/ansible"
+
+# Options ...
+options=("$@")
+
+[[ -r .ansible-vault-secret ]] && options+=("--vault-password-file=.ansible-vault-secret")
+
+# Run ansible(1):
+set -x
+"${PWD}/.venv/bin/ansible" "${options[@]}"
diff --git a/bin/ansible-boilerplate b/bin/ansible-boilerplate
new file mode 100755 (executable)
index 0000000..3af1566
--- /dev/null
@@ -0,0 +1,290 @@
+#!/bin/sh -e
+#
+# Ansible Boilerplate Collection: Setup & Maintenance Script.
+#
+
+# Detect commands to use:
+PYTHON="${PYTHON:-$(command -v python3 2>/dev/null || echo "python3")}"
+PIP="${PIP:-$(command -v pip 2>/dev/null || echo "pip")}"
+ANSIBLE_GALAXY="${ANSIBLE_GALAXY:-$(command -v ansible-galaxy 2>/dev/null || echo "ansible-galaxy")}"
+
+BASE_D="ansible_galaxy/ansible_collections/alexbarton/boilerplate"
+
+#
+# Show usage information on stderr.
+#
+Usage() {
+       {
+               echo "$0 <command>"
+               echo
+               echo "  help       Show this help text and exit."
+               echo "  init       Initialize project and boilerplate code."
+               echo "  upgrade    Upgrade boilerplate code and dependencies. [Alias: update, up]"
+               echo "    --force  Force overwriting an existing role or collection."
+               echo
+       } >&2
+}
+
+#
+# Initialize a new project.
+#
+# Create some default files and call Upgrade() afterwards. This function does not
+# overwrite any already existing file.
+#
+Init() {
+       if [ $# -ne 0 ]; then
+               Usage
+               exit 1
+       fi
+       if [ -e Makefile.boilerplate ]; then
+               echo "This is the upstream project! Don't call \"init\" on it!" >&2
+               exit 1
+       fi
+       echo "Initialize project:"
+
+       for file in \
+               README.md \
+               LICENSE \
+       ; do
+               test -e "${file}" || touch "${file}"
+       done
+       mkdir -p .vscode playbooks roles
+       test -e "hosts.ini" || Init_HostsIni
+       test -e "Makefile" || Init_Makefile
+       test -e "requirements.yml" || Init_RequirementsYml
+
+       Upgrade --init
+}
+
+#
+# Create a Makefile template file.
+#
+Init_Makefile() {
+       echo "Creating \"Makefile\" ..."
+       cat >Makefile <<EOF
+#
+# Makefile
+#
+
+SOURCE_ROOT ?= \$(CURDIR)
+
+# Make sure that the Ansible Boilerplate Collection project is set up (its
+# files are "available"), and initialize it if not!
+_DUMMY := \$(shell test -e ansible_galaxy/ansible_collections/alexbarton/boilerplate/Makefile.boilerplate || bin/ansible-boilerplate upgrade >&2)
+
+default: all
+
+# Include the Ansible Boilerplate Collection Makefile fragment:
+include ansible_galaxy/ansible_collections/alexbarton/boilerplate/Makefile.boilerplate
+
+all:
+
+check:
+
+install:
+
+clean:
+
+distclean: clean
+
+maintainer-clean: distclean
+
+.PHONY: default all check install clean distclean maintainer-clean
+EOF
+}
+
+#
+# Create a hosts.ini template file.
+#
+Init_HostsIni() {
+       echo "Creating \"hosts.ini\" ..."
+       cat >hosts.ini <<EOF
+# Ansible hosts list
+
+[all_managed]
+localhost
+EOF
+}
+
+#
+# Create a requirements.yml template file.
+#
+Init_RequirementsYml() {
+       echo "Creating \"requirements.yml\" ..."
+       cat >requirements.yml <<EOF
+---
+# Ansible dependencies
+
+collections:
+  - ${BOILERPLATE_COLLECTION_SRC:-alexbarton.boilerplate}
+
+roles:
+  []
+EOF
+}
+
+#
+# Upgrade a project.
+#
+# - Initialize a Python virtual environment (when a ".venv" directory exists
+#   or the ansible-galaxy command is not found).
+# - Install Ansible when ansible-galaxy command is not found.
+# - Install "ansible-boilerplate" collection when not found.
+# - Update local "ansible-boilerplate" setup: copy script, create links, ...
+# - Upgrade template files.
+# - Install/upgrade Python dependencies (from requirements.txt file).
+# - Install/upgrade Ansible Galaxy dependencies (from requirements.yml file).
+#
+# --force: Passed to "ansible-galaxy install" command.
+# --init: Upgrade() is called by the Init() function.
+#
+Upgrade() {
+       unset do_force
+       while [ $# -gt 0 ]; do
+               case "$1" in
+                       "--force")
+                               do_force="--force"
+                               ;;
+                       "--init")
+                               is_init="--init"
+                               ;;
+                       *)
+                               Usage
+                               exit 1
+               esac
+               shift
+       done
+       [ -z "${is_init}" ] && echo "Upgrade project:"
+
+       # Check Python virtual environment
+       if [ -d .venv ] || ! command -v "${ANSIBLE_GALAXY}" >/dev/null; then
+               # Either an existing ".venv" folder was found or the
+               # ansible-galaxy(1) command was not found on the system, so
+               # let's use a Python virtual environment!
+               echo "Using a Python virtual environment."
+               PIP="./.venv/bin/pip"
+               ANSIBLE_GALAXY="./.venv/bin/ansible-galaxy"
+               if ! [ -x .venv/bin/pip ]; then
+                       echo "Initializing Python virtual environment ..."
+                       "${PYTHON}" -m venv .venv
+                       "${PIP}" install -U pip setuptools
+               fi
+       fi
+       for var in PYTHON PIP ANSIBLE_GALAXY; do
+               eval 'echo " - ${var} is \"$'"${var}"'\"."'
+       done
+
+       if ! [ -x "${ANSIBLE_GALAXY}" ]; then
+               echo "Installing Ansible ..."
+               "${PIP}" install -U ansible
+       fi
+
+       # Are we running in a dependent project? If so, perform specific upgrade tasks!
+       # shellcheck disable=SC2086
+       [ -e Makefile.boilerplate ] || Upgrade_Dependent ${is_init}
+
+       if [ -r requirements.txt ]; then
+               echo "Installing Python dependencies ..."
+               "${PIP}" install -U -r requirements.txt
+       fi
+
+       if [ -r requirements.yml ]; then
+               echo "Upgrading Ansible Galaxy dependencies ..."
+               # shellcheck disable=SC2248
+               "${ANSIBLE_GALAXY}" collection install -U -r requirements.yml ${do_force}
+               # shellcheck disable=SC2248
+               "${ANSIBLE_GALAXY}" role install -r requirements.yml ${do_force}
+       fi
+}
+
+#
+# Upgrade steps for dependent projects only.
+#
+# --init: Upgrade() is called by the Init() function.
+#
+Upgrade_Dependent() {
+       collection="${BOILERPLATE_COLLECTION_SRC:-alexbarton.boilerplate}"
+       echo "Installing/upgrading \"${collection}\" ..."
+       "${ANSIBLE_GALAXY}" collection install -U -p ansible_galaxy "${collection}"
+
+       echo "Copying \"boilerplate\" script into bin/ directory ..."
+       mkdir -p bin
+       cp -av "${BASE_D}/bin/ansible-boilerplate" "bin/ansible-boilerplate"
+
+       echo "Creating symbolic links to files inside of the Boilerplate Collection ..."
+       for file in \
+               bin/a \
+               bin/ap \
+               bin/aps \
+       ; do
+               # Create (new) symbolic links, when the target already is a symbolic link or
+               # does not yet exists. Don't overwrite existing regular files etc.!
+               test -L "${file}" && ln -fsv "../${BASE_D}/${file}" "${file}"
+               test -e "${file}" || ln -fsv "../${BASE_D}/${file}" "${file}"
+       done
+
+       echo "Upgrading template files from the Boilerplate Collection ..."
+       for file in \
+               .ansible-lint \
+               .editorconfig \
+               .gitignore \
+               .vscode/settings.json \
+               .yamllint.yml \
+               ansible.cfg \
+               requirements.txt \
+       ; do
+               # shellcheck disable=SC2086
+               Upgrade_Template "${file}" ${is_init}
+       done
+
+       # Verify that the Boilerplate Collection is available now!
+       "${ANSIBLE_GALAXY}" collection verify --offline alexbarton.boilerplate
+}
+
+#
+# Upgrade a template file.
+#
+# --init: Initialize a new project, therefore create the template file if it
+#         does not yet exist.
+#
+Upgrade_Template() {
+       # Does the target directory exist? Skip this template file if not!
+       [ -d "$(dirname "$1")" ] || return 0
+
+       # Return when the target file does not exist and not in "init mode":
+       [ ! -e "$1" ] && [ "$2" != "--init" ] && return 0
+
+       # Remove the target when it is a symbolic link.
+       [ -L "$1" ] && rm -v "$1"
+
+       # Do not override the target when it exists already!
+       if [ -e "$1" ]; then
+               # Target already exists. Is it different?
+               if ! cmp "$1" "${BASE_D}/$1"; then
+                       # Files are not the same! Install new version in parallel:
+                       install -b -m 0644 -p -v "${BASE_D}/$1" "$1.new"
+               fi
+       else
+               # Target does not yet exist:
+               install -b -m 0644 -p -v "${BASE_D}/$1" "$1"
+       fi
+}
+
+cmd="$1"
+[ $# -gt 0 ] && shift
+
+case "${cmd}" in
+       "init")
+               Init "$@"
+               ;;
+       "upgrade"|"update"|"up")
+               Upgrade "$@"
+               ;;
+       "help"|"--help")
+               Usage
+               ;;
+       *)
+               Usage
+               exit 1
+esac
+exit 0
diff --git a/bin/ap b/bin/ap
new file mode 100755 (executable)
index 0000000..c3bbdb4
--- /dev/null
+++ b/bin/ap
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Ansible Collection Boilerplate:
+# "ap": ansible-playbook(1) wrapper script.
+#
+
+# Change working directory to the base directory ...
+cd "$(dirname "$0")/.." || exit 1
+
+# Prepare environment ...
+set -e
+pwd
+test -x "${PWD}/.venv/bin/ansible-playbook" || make "${PWD}/.venv/bin/ansible"
+
+# Get playbook name:
+play="$1"
+shift
+
+# Search playbook ...
+unset playbook
+for dir in \
+       . \
+       playbooks{,/deploy,/site} \
+       ansible_galaxy/ansible_collections/*/*/playbooks \
+; do
+       playbook="${dir}/${play}.yml"
+       test -r "${playbook}" && break
+       unset playbook
+done
+[[ -n "${playbook}" ]] || playbook="${play}"
+
+# Options ...
+options=("$@")
+
+[[ -r .ansible-vault-secret ]] && options+=("--vault-password-file=.ansible-vault-secret")
+
+# Run ansible-playbook(1):
+set -x
+"${PWD}/.venv/bin/ansible-playbook" "${playbook}" "${options[@]}"
diff --git a/bin/aps b/bin/aps
new file mode 100755 (executable)
index 0000000..e866dbd
--- /dev/null
+++ b/bin/aps
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# Ansible Collection Boilerplate:
+# "aps": ansible-playbook(1) "site" wrapper script.
+#
+
+# Change working directory to the base directory ...
+cd "$(dirname "$0")/.." || exit 1
+./bin/ap site "$@"
diff --git a/galaxy.yml b/galaxy.yml
new file mode 100644 (file)
index 0000000..6db0bf4
--- /dev/null
@@ -0,0 +1,32 @@
+---
+namespace: alexbarton
+name: boilerplate
+# NOTE: Remove "galaxy[version-incorrect]" from the "skip_list" in
+# .ansible-lint once you bump the version to 1.0.0 (or higher)!
+version: 0.0.1
+
+authors:
+  - Alexander Barton <alex@barton.de>
+
+description: Collection of boilerplate files, plays, roles and scripts for projects using Ansible.
+readme: README.md
+
+license_file: LICENSE
+
+tags:
+  - boilerplate
+  - common
+  - tools
+
+dependencies:
+  "community.general": ">=6.1.0"
+
+homepage: https://github.com/alexbarton/ansible-collection-boilerplate
+documentation: https://github.com/alexbarton/ansible-collection-boilerplate
+issues: https://github.com/alexbarton/ansible-collection-boilerplate/issues
+repository: https://github.com/alexbarton/ansible-collection-boilerplate
+
+build_ignore:
+  - .DS_Store
+  - .trunk
+  - .venv
diff --git a/meta/runtime.yml b/meta/runtime.yml
new file mode 100644 (file)
index 0000000..74fa9e7
--- /dev/null
@@ -0,0 +1,3 @@
+---
+
+requires_ansible: '>=2.14.0'
diff --git a/playbooks/ping.yml b/playbooks/ping.yml
new file mode 100644 (file)
index 0000000..241346b
--- /dev/null
@@ -0,0 +1,15 @@
+---
+# Connect to a system and run the Ansible "ping" module on it.
+#
+# Example:
+# ./bin/ap ping -l example.host.name
+#
+
+- name: Ping system
+  any_errors_fatal: true
+  hosts: '{{target|default("all")}}'
+
+  tasks:
+
+    - name: Run the Ansible "ping" module
+      ansible.builtin.ping:
diff --git a/requirements.txt b/requirements.txt
new file mode 100644 (file)
index 0000000..70fe5a0
--- /dev/null
@@ -0,0 +1,6 @@
+# Python dependencies
+
+ansible>=7.4
+ansible-core>=2.14
+ansible-lint>=6.14
+yamllint>=1.30