Commit 59110c91 authored by Maciej Delmanowski's avatar Maciej Delmanowski

Merge branch 'drybjed-add-yadm'

parents da0274d4 ce65dd7a
Pipeline #59778280 failed with stages
in 146 minutes and 39 seconds
......@@ -369,7 +369,7 @@ stages:
JANE_LOG_PATTERN: '\[debops\.docker_gen\]'
'docker_registry role':
<<: *test_role_2nd_deps
<<: *test_role_3rd_deps
variables:
JANE_TEST_PLAY: '${DEBOPS_PLAYBOOKS}/service/docker_registry.yml'
JANE_INVENTORY_GROUPS: 'debops_service_docker_registry'
......@@ -706,7 +706,7 @@ stages:
variables:
JANE_TEST_PLAY: '${DEBOPS_PLAYBOOKS}/service/ldap.yml'
JANE_INVENTORY_GROUPS: 'debops_service_ldap'
JANE_INVENTORY_HOSTVARS: 'ldap_enabled=true'
JANE_INVENTORY_HOSTVARS: 'ldap__enabled=true'
JANE_DIFF_PATTERN: '.*/debops.ldap/.*'
JANE_LOG_PATTERN: '\[debops\.ldap\]'
......@@ -928,7 +928,7 @@ stages:
variables:
JANE_TEST_PLAY: '${DEBOPS_PLAYBOOKS}/service/nslcd.yml'
JANE_INVENTORY_GROUPS: 'debops_service_nslcd'
JANE_INVENTORY_HOSTVARS: 'ldap_enabled=true'
JANE_INVENTORY_HOSTVARS: 'ldap__enabled=true'
JANE_DIFF_PATTERN: '.*/debops.nslcd/.*'
JANE_LOG_PATTERN: '\[debops\.nslcd\]'
......@@ -1350,7 +1350,7 @@ stages:
JANE_LOG_PATTERN: '\[debops\.stunnel\]'
'sudo role':
<<: *test_role_no_deps
<<: *test_role_2nd_deps
variables:
JANE_TEST_PLAY: '${DEBOPS_PLAYBOOKS}/service/sudo.yml'
JANE_INVENTORY_GROUPS: 'debops_service_sudo'
......@@ -1358,7 +1358,7 @@ stages:
JANE_LOG_PATTERN: '\[debops\.sudo\]'
'system_groups role':
<<: *test_role_1st_deps
<<: *test_role_3rd_deps
variables:
JANE_TEST_PLAY: '${DEBOPS_PLAYBOOKS}/service/system_groups.yml'
JANE_INVENTORY_GROUPS: 'debops_service_system_groups'
......@@ -1460,3 +1460,15 @@ stages:
JANE_TEST_PLAY: '${DEBOPS_PLAYBOOKS}/service/users.yml'
JANE_DIFF_PATTERN: '.*/debops.users/.*'
JANE_LOG_PATTERN: '\[debops\.users\]'
# --- y --- [[[2
'yadm role':
<<: *test_role_1st_deps
variables:
JANE_TEST_PLAY: '${DEBOPS_PLAYBOOKS}/service/yadm.yml'
JANE_INVENTORY_GROUPS: 'debops_service_yadm'
JANE_INVENTORY_HOSTVARS: 'yadm__dotfiles_enabled=true'
JANE_DIFF_PATTERN: '.*/debops.yadm/.*'
JANE_LOG_PATTERN: '\[debops\.yadm\]'
......@@ -39,6 +39,12 @@ Added
the :file:`/etc/security/` directory. The role is designed to allow other
Ansible roles to easily manage their own PAM access rules.
- :ref:`debops.yadm` role installs the `Yet Another Dotfiles Manager`__
script and ensures that additional shells are available. It can also mirror
dotfiles locally.
.. __: https://yadm.io/
- [debops.nginx] The role will automatically generate configuration which
redirects short hostnames or subdomains to their FQDN equivalents. This
allows HTTP clients to reach websites by specifying their short names via DNS
......
......@@ -60,6 +60,7 @@
tags: [ 'role::apt_preferences', 'skip::apt_preferences' ]
apt_preferences__dependent_list:
- '{{ sshd__apt_preferences__dependent_list }}'
- '{{ yadm__apt_preferences__dependent_list }}'
- role: debops.apt_proxy
tags: [ 'role::apt_proxy', 'skip::apt_proxy' ]
......@@ -107,6 +108,9 @@
- role: debops.nslcd
tags: [ 'role::nslcd', 'skip::nslcd' ]
- role: debops.yadm
tags: [ 'role::yadm', 'skip::yadm' ]
- role: debops.sudo
tags: [ 'role::sudo', 'skip::sudo', 'role::system_groups' ]
......
......@@ -70,6 +70,7 @@
apt_preferences__dependent_list:
- '{{ sshd__apt_preferences__dependent_list }}'
- '{{ apt_install__apt_preferences__dependent_list }}'
- '{{ yadm__apt_preferences__dependent_list }}'
- '{{ rsyslog__apt_preferences__dependent_list }}'
- role: debops.apt_proxy
......@@ -114,6 +115,9 @@
- role: debops.apt_install
tags: [ 'role::apt_install', 'skip::apt_install' ]
- role: debops.yadm
tags: [ 'role::yadm', 'skip::yadm' ]
- role: debops.sudo
tags: [ 'role::sudo', 'skip::sudo' ]
......
---
- name: Configure yadm, Yet Another Dotfiles Manager
hosts: [ 'debops_all_hosts', 'debops_service_yadm' ]
become: True
environment: '{{ inventory__environment | d({})
| combine(inventory__group_environment | d({}))
| combine(inventory__host_environment | d({})) }}'
roles:
- role: debops.apt_preferences
tags: [ 'role::apt_preferences', 'skip::apt_preferences' ]
apt_preferences__dependent_list:
- '{{ yadm__apt_preferences__dependent_list }}'
- role: debops.yadm
tags: [ 'role::yadm', 'skip::yadm' ]
debops.yadm - Manage yadm, Yet Another Dotfiles Manager
Copyright (C) 2019 Maciej Delmanowski <drybjed@gmail.com>
Copyright (C) 2019 DebOps Project https://debops.org/
This Ansible role is part of DebOps.
DebOps is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
DebOps is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DebOps. If not, see https://www.gnu.org/licenses/.
---
# .. vim: foldmarker=[[[,]]]:foldmethod=marker
# debops.yadm default variables
# =============================
# .. contents:: Sections
# :local:
#
# .. include:: ../../../includes/global.rst
# General configuration [[[
# -------------------------
# .. envvar:: yadm__enabled [[[
#
# Enable or disable support for Yet Another Dotfiles Manager.
yadm__enabled: True
# ]]]
# .. envvar:: yadm__dotfiles_enabled [[[
#
# Enable or disable download of the custom dotfiles to a local centralized
# directory. The dotfiles will be cloned using the ``root`` account, but no
# code will be executed at this point. See below for the configuration of the
# custom dotfile lists.
yadm__dotfiles_enabled: False
# ]]]
# .. envvar:: yadm__dotfiles_root [[[
#
# Absolute path to the directory where the role will clone dotfile
# repositories. The repositories will be cloned in subdirectories based on the
# URL path. Users can then install the dotfiles from a local mirror of the
# repository instead of directly from the Internet.
yadm__dotfiles_root: '{{ (ansible_local.root.src
if (ansible_local|d() and ansible_local.root|d() and
ansible_local.root.src|d())
else "/usr/local/src") + "/yadm" }}'
# ]]]
# .. envvar:: yadm__dotfiles_host [[[
#
# The host of the default dotfiles defined in the Ansible local facts.
yadm__dotfiles_host: 'github.com'
# ]]]
# .. envvar:: yadm__dotfiles_owner [[[
#
# The owner of the default dotfiles defined in the Ansible local facts; usually
# a GitHub account which owns a ``dotfiles`` repository.
# The current owner is temporary, until DebOps will have its own set of default
# dotfiles available.
yadm__dotfiles_owner: 'drybjed'
# ]]]
# .. envvar:: yadm__dotfiles_path [[[
#
# Absolute path of the default dotfiles to use, exposed in Ansible local facts.
# If custom dotfiles are not enabled, the fact will be empty.
# The role clones the dotfiles as a bare repository with the ``.git`` extension
# in the directory name; be sure to include it in the inventory definition.
yadm__dotfiles_path: '{{ (yadm__dotfiles_root + "/" + yadm__dotfiles_host + "/"
+ yadm__dotfiles_owner + "/dotfiles.git")
if yadm__dotfiles_enabled|bool
else "" }}'
# ]]]
# ]]]
# APT packages [[[
# ----------------
# .. envvar:: yadm__base_packages [[[
#
# List of the default APT packages installed with yadm. Including shells here
# ensures that users with login shells other than :command:`/bin/bash` will be
# able to login to the host regardless of their dotfiles configuration.
yadm__base_packages:
- 'git'
- 'less'
- 'ncurses-term'
- 'tmux'
- 'zsh'
- '{{ [] if yadm__upstream_enabled|bool else "yadm" }}'
# ]]]
# .. envvar:: yadm__packages [[[
#
# List of additional APT packages to install on all hosts in the Ansible
# inventory.
yadm__packages: []
# ]]]
# .. envvar:: yadm__group_packages [[[
#
# List of additional APT packages to install on hosts in a specific Ansible
# inventory group.
yadm__group_packages: []
# ]]]
# .. envvar:: yadm__host_packages [[[
#
# List of additional APT packages to install on specific hosts in the Ansible
# inventory.
yadm__host_packages: []
# ]]]
# ]]]
# Upstream yadm installation [[[
# ------------------------------
# .. envvar:: yadm__upstream_enabled [[[
#
# Enable or disable installation of the :command:`yadm` script from upstream
# :command:`git` repository.
yadm__upstream_enabled: '{{ True
if (ansible_distribution_release in
[ "wheezy", "jessie", "precise", "trusty" ])
else False }}'
# ]]]
# .. envvar:: yadm__upstream_gpg_id [[[
#
# The GPG fingerprint of the key used to sign the :command:`yadm` releases.
yadm__upstream_gpg_id: '31B9 62F7 CC57 473B 0329 CB9A 6CBE 24C2 FD8C F76E'
# ]]]
# .. envvar:: yadm__upstream_repo [[[
#
# The URL of the yadm upstream :command:`git` repository.
yadm__upstream_repo: 'https://github.com/TheLocehiliosan/yadm'
# ]]]
# .. envvar:: yadm__upstream_dest [[[
#
# Absolute path to the directory where the yadm upstream :command:`git`
# repository will be cloned into.
yadm__upstream_dest: '{{ (ansible_local.root.src
if (ansible_local|d() and ansible_local.root|d() and
ansible_local.root.src|d())
else "/usr/local/src") + "/yadm/"
+ yadm__upstream_repo.split("://")[1] }}'
# ]]]
# .. envvar:: yadm__upstream_version [[[
#
# The version of the upstream :command:`git` repository to check out.
yadm__upstream_version: '1.12.0'
# ]]]
# .. envvar:: yadm__upstream_link [[[
#
# The aboslute path of the symlink created to expose the :command:`yadm` binary
# in the ``$PATH`` environment.
yadm__upstream_link: '/usr/local/bin/yadm'
# ]]]
# ]]]
# Custom dotfiles [[[
# -------------------
# These variables define custom dotfile repositories to clone to the host, so
# that they are available locally to users. See :ref:`yadm__ref_dotfiles` for
# more details.
# .. envvar:: yadm__default_dotfiles [[[
#
# List of the custom dotfile repositories defined by the role.
yadm__default_dotfiles:
- name: 'drybjed'
gpg: '2706 7A91 D620 EE91 D503 09D9 2DCC F53E 9BC7 4BEC'
git: 'https://github.com/drybjed/dotfiles'
# ]]]
# .. envvar:: yadm__dotfiles [[[
#
# List of the custom dotfile repositories defined on all hosts in the Ansible
# inventory.
yadm__dotfiles: []
# ]]]
# .. envvar:: yadm__group_dotfiles [[[
#
# List of the custom dotfile repositories defined on hosts in a specific
# Ansible inventory group.
yadm__group_dotfiles: []
# ]]]
# .. envvar:: yadm__host_dotfiles [[[
#
# List of the custom dotfile repositories defined on specific hosts in the
# Ansible inventory.
yadm__host_dotfiles: []
# ]]]
# .. envvar:: yadm__combined_dotfiles [[[
#
# Variable which combines custom dotfile lists and is used in the role tasks.
yadm__combined_dotfiles: '{{ yadm__default_dotfiles
+ yadm__dotfiles
+ yadm__group_dotfiles
+ yadm__host_dotfiles }}'
# ]]]
# ]]]
# Configuration for other Ansible roles [[[
# -----------------------------------------
# .. envvar:: yadm__apt_preferences__dependent_list [[[
#
# Configuration for the :ref:`debops.apt_preferences` Ansible role.
yadm__apt_preferences__dependent_list:
- package: 'yadm'
backports: [ 'stretch' ]
reason: 'Version parity with Debian Buster'
by_role: 'debops.yadm'
# ]]]
# ]]]
---
dependencies:
- role: debops.ansible_plugins
galaxy_info:
role_name: 'yadm'
author: 'Maciej Delmanowski'
description: 'Install yadm, Yet Another Dotfiles Manager'
company: 'DebOps'
license: 'GNU General Public License v3'
min_ansible_version: '2.7'
platforms:
- name: Ubuntu
versions:
- precise
- trusty
- name: GenericLinux
versions:
- all
- name: Debian
versions:
- all
galaxy_tags:
- system
- shell
- bash
- zsh
- dotfiles
- yadm
# Role: debops.yadm
# Package: yadm
# Version: 1.12.0
version=3
opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/$1\.tar\.gz/ \
https://github.com/TheLocehiliosan/yadm/tags .*/v?(\d\S*)\.tar\.gz
---
- name: Install required APT packages
package:
name: '{{ q("flattened", yadm__base_packages
+ yadm__packages
+ yadm__group_packages
+ yadm__host_packages) }}'
state: 'present'
register: yadm__register_packages
until: yadm__register_packages is succeeded
when: yadm__enabled|bool
- name: Install yadm from upstream
include_tasks: 'upstream_yadm.yml'
when: yadm__enabled|bool and yadm__upstream_enabled|bool
- name: Ensure that the /root/.gnupg directory exists
file:
path: '~/.gnupg'
state: 'directory'
mode: '0700'
when: yadm__enabled|bool and yadm__dotfiles_enabled|bool
- name: Download custom dotfile repositories
include_tasks: 'manage_dotfiles.yml'
loop: '{{ q("flattened", yadm__combined_dotfiles) | parse_kv_items }}'
loop_control:
loop_var: 'dotfile'
label: '{{ dotfile.name }}'
when: yadm__enabled|bool and yadm__dotfiles_enabled|bool and
dotfile.name|d()
- name: Make sure that Ansible local facts directory exists
file:
path: '/etc/ansible/facts.d'
state: 'directory'
mode: '0755'
- name: Save yadm local facts
template:
src: 'etc/ansible/facts.d/yadm.fact.j2'
dest: '/etc/ansible/facts.d/yadm.fact'
mode: '0755'
register: yadm__register_facts
- name: Update Ansible facts if they were modified
action: setup
when: yadm__register_facts is changed
---
- name: 'Import {{ dotfile.name }} GPG keys to root keyring'
apt_key:
keyring: '~/.gnupg/pubring.gpg'
id: '{{ (item_gpg.id | d(item_gpg)) | replace(" ","") }}'
keyserver: '{{ ansible_local.core.keyserver
if (ansible_local|d() and ansible_local.core|d() and
ansible_local.core.keyserver|d())
else "hkp://pool.sks-keyservers.net" }}'
state: '{{ item_gpg.state | d(dotfile.state | d("present")) }}'
loop: '{{ q("flattened", dotfile.gpg) }}'
loop_control:
loop_var: 'item_gpg'
register: yadm__register_dotfiles_gpg_ids
until: yadm__register_dotfiles_gpg_ids is succeeded
when: dotfile.gpg|d() and dotfile.state|d('present') not in [ 'ignore' ]
- name: 'Clone the {{ dotfile.name }} dotfiles'
git:
repo: '{{ item_git.repo | d(item_git) }}'
dest: '{{ yadm__dotfiles_root + "/"
+ (item_git.repo | d(item_git)).split("://")[1] | regex_replace("\.git$","")
+ ".git" }}'
version: '{{ item_git.version | d("master") }}'
verify_commit: '{{ True if dotfile.gpg|d() else omit }}'
bare: True
loop: '{{ q("flattened", dotfile.git) }}'
loop_control:
loop_var: 'item_git'
when: dotfile.git|d() and dotfile.state|d('present') not in [ 'absent', 'ignore' ]
- name: 'Remove the {{ dotfile.name }} dotfiles mirror if requested'
file:
dest: '{{ (yadm__dotfiles_root + "/"
+ (item_git.repo | d(item_git)).split("://")[1] | regex_replace("\.git$","")
+ ".git") | dirname }}'
state: 'absent'
loop: '{{ q("flattened", dotfile.git) }}'
loop_control:
loop_var: 'item_git'
when: dotfile.git|d() and dotfile.state|d('present') == 'absent'
---
- name: Ensure that the /root/.gnupg directory exists
file:
path: '~/.gnupg'
state: 'directory'
mode: '0700'
- name: Import yadm upstream GPG key to root keyring
apt_key:
keyring: '~/.gnupg/pubring.gpg'
id: '{{ yadm__upstream_gpg_id | replace(" ","") }}'
keyserver: '{{ ansible_local.core.keyserver
if (ansible_local|d() and ansible_local.core|d() and
ansible_local.core.keyserver|d())
else "hkp://pool.sks-keyservers.net" }}'
state: 'present'
register: yadm__register_upstream_gpg_id
until: yadm__register_upstream_gpg_id is succeeded
- name: Clone the yadm upstream repository
git:
repo: '{{ yadm__upstream_repo }}'
dest: '{{ yadm__upstream_dest }}'
version: '{{ yadm__upstream_version }}'
verify_commit: True
- name: Symlink the upstream yadm binary in $PATH
file:
dest: '{{ yadm__upstream_link }}'
src: '{{ yadm__upstream_dest + "/yadm" }}'
state: 'link'
#!{{ ansible_python['executable'] }}
# {{ ansible_managed }}
from __future__ import print_function
from json import dumps, loads
import os
def cmd_exists(cmd):
return any(
os.access(os.path.join(path, cmd), os.X_OK)
for path in os.environ["PATH"].split(os.pathsep)
)
output = loads('''{{ {"installed": False,
"dotfiles": yadm__dotfiles_path}
| to_nice_json }}''')
if not os.path.exists(output['dotfiles']):
output['dotfiles'] = ''
output['installed'] = cmd_exists('yadm')
print(dumps(output, sort_keys=True, indent=4))
......@@ -290,6 +290,7 @@ System configuration
- :ref:`debops.sysnews`
- :ref:`debops.system_groups`
- :ref:`debops.users`
- :ref:`debops.yadm`
- ``debops.console``
- ``debops.gitusers``
- ``debops.sftpusers``
......
Default variable details
========================
Some of ``debops.yadm`` default variables have more extensive configuration
than simple strings or lists, here you can find documentation and examples for
them.
.. contents::
:local:
:depth: 1
.. _yadm__ref_dotfiles:
yadm__dotfiles
--------------
The ``yadm__*_dotfiles`` variables define a list of custom dotfile
:command:`git` repositories to clone to the host, so that they are available
locally. The dotfiles will be cloned to the directory defined by the
:envvar:`yadm__dotfiles_root` variable, with subdirectories corresponding to
the repository host (for example ``github.com``), repository owner (for example
``drybjed``), and finally the ``dotfiles.git`` directory, which contains a bare
:command:`git` repository itself.
The repositories are cloned and/or updated using the ``root`` account, with
optional GPG signature verification. The role does not execute any code
contained in the repositories at this stage. Users are then able to use the
:command:`yadm` script to install or update their desired dotfiles from the
local mirror instead of the remote repository. This can be done either
manually, or via other Ansible roles.
Because :command:`yadm` uses the ``$HOME`` directory directly as the
:command:`git` work directory, it's best to avoid putting non-dotfile files
like ``README.md`` and similar in the ``master`` branch of the repository
(:command:`yadm` will check it out by default). If you want to present a README
file in the dotfiles repository, you can put it on a separate :command:`git`
branch and set it as default branch in the GitHub repository settings.
See `yadm dotfile repository examples`__ for an example repositories compatible
with :command:`yadm` script.
.. __: https://yadm.io/docs/examples
Examples
~~~~~~~~
Clone dotfiles without any GPG signature verification:
.. code-block:: yaml
yadm__dotfiles:
- name: 'user'
git:
- repo: 'https://github.com/user/dotfiles'
Disable the default ``drybjed`` dotfiles from being cloned automatically and
remove them if they are present:
.. code-block:: yaml
yadm__dotfiles:
- name: 'drybjed'
state: 'absent'
Syntax
~~~~~~
The variables are YAML lists, each list entry is a YAML dictionary that uses
specific parameters:
``name``
Required. A name of a given dotfile entry, not used otherwise. Entries with
the same ``name`` parameter are merged together, this can be used to modify
existing entries later on.
``state``
Optional. If not specified or ``present``, a given dotfile repository will be
cloned or updated by the role. If ``absent``, a given repository and GPG keys
will be removed from the host, or will not be imported and cloned. If
``ignore``, a given configuration entry will be ignored during evaluation by
the role.
``gpg``
Optional. A string containing a GPG key fingerprint used to sign the commits
and/or tags in the dotfile repository; you can also specify multiple GPG
fingerprints as a YAML list. Spaces in the fingerprint will be automatically
removed. An alternative format is a YAML dictionary for each list element,
with specific parameters:
``id``
The GPG key fingerprint.
``state``
Optional, either ``present`` (import the GPG key) or ``absent`` (remove the
GPG key from the keyring).
The specified GPG keys will be added to the ``root`` GPG keyring in the
:file:`~/.gnupg/pubring.gpg` file and subsequently used to verify commits in
cloned or updated :command:`git` repositories.
``git``
Optional. A string containing an URL to the :command:`git` repository with
dotfiles; you can also specify multiple URLs as a YAML list. Only public
repositories accessible via ``https://`` make sense - the role does not
support cloning private repositories using a password, or repositories
accessible over SSH connection. An alternative format is a YAML dictionary
for each list element, with specific parameters:
``repo``
The URL of the repository.
``version``
The :command:`git` branch/tag to checkout - not useful because the role
will clone bare :command:`git` repositories without checking them out.
Getting started
===============
.. contents::
:local:
Default dotfiles
----------------
The role does not clone any dotfile :command:`git` repositories defined in the
:ref:`yadm__ref_dotfiles` variables by default. To enable this you should set
in the inventory:
.. code-block:: yaml
yadm__dotfiles_enabled: True
Without this, users still are able to use :command:`yadm` to install their own
preferred dotfiles, and role ensures that commonly used CLI shells are present
so that users are able to login if they use, for example, :command:`/bin/zsh`
as a shell defined in the LDAP directory.
The role exposes the ``ansible_local.yadm.dotfiles`` Ansible local fact, which
defines an absolute path to a default dotfiles repository mirrored locally.
Other Ansible roles can use it to install a default set of dotfiles using
:command:`yadm` on the users' account. If installation of dotfiles is disabled,
or the specified repository is not present, the variable will be empty.
Example inventory
-----------------