├── .ansible-lint ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .moleculew ├── ansible_lint_version ├── ansible_version ├── flake8_version ├── molecule_version ├── python_version ├── testinfra_version └── yamllint_version ├── .yamllint ├── LICENSE ├── README.md ├── defaults └── main.yml ├── handlers └── main.yml ├── meta └── main.yml ├── molecule ├── debian-max │ ├── INSTALL.rst │ └── molecule.yml ├── debian-min │ ├── INSTALL.rst │ └── molecule.yml ├── default │ ├── INSTALL.rst │ ├── converge.yml │ ├── molecule.yml │ └── tests │ │ ├── conftest.py │ │ └── test_role.py └── ubuntu-min │ ├── INSTALL.rst │ └── molecule.yml ├── moleculew ├── tasks └── main.yml ├── templates └── keyboards.j2 └── vars └── main.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | skip_list: 2 | - '106' # Role name {} does not match \^[a-z][a-z0-9_]+$\ pattern' 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Defaults for all editor files 7 | [*] 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 4 11 | trim_trailing_whitespace = true 12 | 13 | # Files with a smaller indent 14 | [*.yml] 15 | indent_size = 2 16 | 17 | # Jinja2 template files 18 | [*.j2] 19 | end_of_line = lf 20 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | .gitignore text 7 | .gitattributes text 8 | *.xml text 9 | *.yml text 10 | *.yaml text 11 | *.json text 12 | *.txt text 13 | *.md text 14 | *.htm text 15 | *.html text 16 | *.xhtml text 17 | *.js text 18 | *.ts text 19 | *.css text 20 | *.less text 21 | *.scss text 22 | *.cfg text 23 | LICENSE text 24 | 25 | # Declare files that will always have CRLF line endings on checkout. 26 | *.bat text eol=crlf 27 | *.cmd text eol=crlf 28 | 29 | # Declare files that will always have LF line endings on checkout. 30 | *.sh text eol=lf 31 | *.service eol=lf 32 | *.conf eol=lf 33 | *.desktop eol=lf 34 | *.j2 eol=lf 35 | 36 | # Denote all files that are truly binary and should not be modified. 37 | *.png binary 38 | *.jpg binary 39 | *.jpeg binary 40 | *.gif binary 41 | *.eot binary 42 | *.tff binary 43 | *.woff binary 44 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: {} 8 | 9 | env: 10 | MOLECULEW_USE_SYSTEM: 'true' 11 | PY_COLORS: '1' 12 | ANSIBLE_FORCE_COLOR: '1' 13 | 14 | jobs: 15 | test: 16 | name: Molecule test 17 | runs-on: ubuntu-20.04 18 | 19 | strategy: 20 | matrix: 21 | ansible-version: 22 | - '2.10.7' # max-ansible-test-version 23 | molecule-scenario: 24 | - debian-min 25 | - debian-max 26 | - default 27 | include: 28 | - ansible-version: '2.9.26' # min-ansible-test-version 29 | molecule-scenario: ubuntu-min 30 | 31 | env: 32 | MOLECULEW_ANSIBLE: ${{ matrix.ansible-version }} 33 | 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v2 37 | 38 | - name: Set up Python 39 | uses: actions/setup-python@v2 40 | with: 41 | python-version: '3.8' 42 | 43 | - name: Cache Molecule 44 | id: molecule-cache 45 | uses: actions/cache@v2 46 | with: 47 | path: ~/.moleculew 48 | key: Ansible-${{ matrix.ansible-version }}-${{ hashFiles('.moleculew/**') }} 49 | 50 | - name: Install dependencies 51 | if: steps.molecule-cache.outputs.cache-hit != 'true' 52 | run: ./moleculew wrapper-install 53 | 54 | - name: Dependency versions 55 | run: ./moleculew wrapper-versions 56 | 57 | - name: Login to Docker Hub 58 | if: '!github.event.pull_request || github.event.pull_request.head.repo.full_name == github.repository' 59 | uses: docker/login-action@v1 60 | with: 61 | username: ${{ secrets.DOCKERHUB_USERNAME }} 62 | password: ${{ secrets.DOCKERHUB_TOKEN }} 63 | 64 | - name: Molecule test 65 | run: ./moleculew test --scenario-name=${{ matrix.molecule-scenario }} 66 | 67 | - name: Compact cache 68 | if: steps.molecule-cache.outputs.cache-hit != 'true' 69 | run: find ~/.moleculew -name '__pycache__' -exec rm -rf {} + 70 | 71 | test-all: 72 | if: ${{ always() }} 73 | name: Test (matrix) 74 | runs-on: ubuntu-20.04 75 | needs: test 76 | steps: 77 | - name: Check test matrix status 78 | run: "[[ '${{ needs.test.result }}' == 'success' ]] || exit 1" 79 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | release: 10 | name: Release 11 | uses: gantsign/workflows/.github/workflows/ansible-galaxy-import.yml@v1 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/windows,linux,osx,vim,emacs,intellij,eclipse,visualstudiocode,ansible 2 | 3 | ### Windows ### 4 | # Windows image file caches 5 | Thumbs.db 6 | ehthumbs.db 7 | 8 | # Folder config file 9 | Desktop.ini 10 | 11 | # Recycle Bin used on file shares 12 | $RECYCLE.BIN/ 13 | 14 | # Windows Installer files 15 | *.cab 16 | *.msi 17 | *.msm 18 | *.msp 19 | 20 | # Windows shortcuts 21 | *.lnk 22 | 23 | 24 | ### Linux ### 25 | *~ 26 | 27 | # temporary files which can be created if a process still has a handle open of a deleted file 28 | .fuse_hidden* 29 | 30 | # KDE directory preferences 31 | .directory 32 | 33 | # Linux trash folder which might appear on any partition or disk 34 | .Trash-* 35 | 36 | 37 | ### OSX ### 38 | *.DS_Store 39 | .AppleDouble 40 | .LSOverride 41 | 42 | # Icon must end with two \r 43 | Icon 44 | 45 | 46 | # Thumbnails 47 | ._* 48 | 49 | # Files that might appear in the root of a volume 50 | .DocumentRevisions-V100 51 | .fseventsd 52 | .Spotlight-V100 53 | .TemporaryItems 54 | .Trashes 55 | .VolumeIcon.icns 56 | .com.apple.timemachine.donotpresent 57 | 58 | # Directories potentially created on remote AFP share 59 | .AppleDB 60 | .AppleDesktop 61 | Network Trash Folder 62 | Temporary Items 63 | .apdisk 64 | 65 | 66 | ### Vim ### 67 | # swap 68 | [._]*.s[a-w][a-z] 69 | [._]s[a-w][a-z] 70 | # session 71 | Session.vim 72 | # temporary 73 | .netrwhist 74 | *~ 75 | # auto-generated tag files 76 | tags 77 | 78 | 79 | ### Emacs ### 80 | # -*- mode: gitignore; -*- 81 | *~ 82 | \#*\# 83 | /.emacs.desktop 84 | /.emacs.desktop.lock 85 | *.elc 86 | auto-save-list 87 | tramp 88 | .\#* 89 | 90 | # Org-mode 91 | .org-id-locations 92 | *_archive 93 | 94 | # flymake-mode 95 | *_flymake.* 96 | 97 | # eshell files 98 | /eshell/history 99 | /eshell/lastdir 100 | 101 | # elpa packages 102 | /elpa/ 103 | 104 | # reftex files 105 | *.rel 106 | 107 | # AUCTeX auto folder 108 | /auto/ 109 | 110 | # cask packages 111 | .cask/ 112 | dist/ 113 | 114 | # Flycheck 115 | flycheck_*.el 116 | 117 | # server auth directory 118 | /server/ 119 | 120 | # projectiles files 121 | .projectile 122 | 123 | ### Intellij ### 124 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 125 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 126 | 127 | # User-specific stuff: 128 | .idea/workspace.xml 129 | .idea/tasks.xml 130 | .idea/dictionaries 131 | .idea/vcs.xml 132 | .idea/jsLibraryMappings.xml 133 | 134 | # Sensitive or high-churn files: 135 | .idea/dataSources.ids 136 | .idea/dataSources.xml 137 | .idea/dataSources.local.xml 138 | .idea/sqlDataSources.xml 139 | .idea/dynamic.xml 140 | .idea/uiDesigner.xml 141 | 142 | # Gradle: 143 | .idea/gradle.xml 144 | .idea/libraries 145 | 146 | # Mongo Explorer plugin: 147 | .idea/mongoSettings.xml 148 | 149 | ## File-based project format: 150 | *.iws 151 | 152 | ## Plugin-specific files: 153 | 154 | # IntelliJ 155 | /out/ 156 | 157 | # mpeltonen/sbt-idea plugin 158 | .idea_modules/ 159 | 160 | # JIRA plugin 161 | atlassian-ide-plugin.xml 162 | 163 | # Crashlytics plugin (for Android Studio and IntelliJ) 164 | com_crashlytics_export_strings.xml 165 | crashlytics.properties 166 | crashlytics-build.properties 167 | fabric.properties 168 | 169 | ### Intellij Patch ### 170 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 171 | 172 | # *.iml 173 | # modules.xml 174 | # .idea/misc.xml 175 | # *.ipr 176 | 177 | 178 | ### Eclipse ### 179 | 180 | .metadata 181 | bin/ 182 | tmp/ 183 | *.tmp 184 | *.bak 185 | *.swp 186 | *~.nib 187 | local.properties 188 | .settings/ 189 | .loadpath 190 | .recommenders 191 | 192 | # Eclipse Core 193 | .project 194 | 195 | # External tool builders 196 | .externalToolBuilders/ 197 | 198 | # Locally stored "Eclipse launch configurations" 199 | *.launch 200 | 201 | # PyDev specific (Python IDE for Eclipse) 202 | *.pydevproject 203 | 204 | # CDT-specific (C/C++ Development Tooling) 205 | .cproject 206 | 207 | # JDT-specific (Eclipse Java Development Tools) 208 | .classpath 209 | 210 | # Java annotation processor (APT) 211 | .factorypath 212 | 213 | # PDT-specific (PHP Development Tools) 214 | .buildpath 215 | 216 | # sbteclipse plugin 217 | .target 218 | 219 | # Tern plugin 220 | .tern-project 221 | 222 | # TeXlipse plugin 223 | .texlipse 224 | 225 | # STS (Spring Tool Suite) 226 | .springBeans 227 | 228 | # Code Recommenders 229 | .recommenders/ 230 | 231 | 232 | ### VisualStudioCode ### 233 | .vscode/* 234 | !.vscode/settings.json 235 | !.vscode/tasks.json 236 | !.vscode/launch.json 237 | 238 | 239 | ### Ansible ### 240 | *.retry 241 | 242 | #################### 243 | ### Custom rules ### 244 | #################### 245 | 246 | ### Molecule ### 247 | 248 | __pycache__ 249 | .cache 250 | .molecule 251 | -------------------------------------------------------------------------------- /.moleculew/ansible_lint_version: -------------------------------------------------------------------------------- 1 | 5.4.0 2 | -------------------------------------------------------------------------------- /.moleculew/ansible_version: -------------------------------------------------------------------------------- 1 | 2.10.7 2 | -------------------------------------------------------------------------------- /.moleculew/flake8_version: -------------------------------------------------------------------------------- 1 | 4.0.1 2 | -------------------------------------------------------------------------------- /.moleculew/molecule_version: -------------------------------------------------------------------------------- 1 | 3.1.5 2 | -------------------------------------------------------------------------------- /.moleculew/python_version: -------------------------------------------------------------------------------- 1 | 3.8.10 2 | -------------------------------------------------------------------------------- /.moleculew/testinfra_version: -------------------------------------------------------------------------------- 1 | 5.3.1 2 | -------------------------------------------------------------------------------- /.moleculew/yamllint_version: -------------------------------------------------------------------------------- 1 | 1.26.3 2 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | # Based on ansible-lint config 3 | extends: default 4 | 5 | rules: 6 | braces: 7 | max-spaces-inside: 1 8 | level: error 9 | brackets: 10 | max-spaces-inside: 1 11 | level: error 12 | colons: 13 | max-spaces-after: -1 14 | level: error 15 | commas: 16 | max-spaces-after: -1 17 | level: error 18 | comments: disable 19 | comments-indentation: disable 20 | document-start: disable 21 | empty-lines: 22 | max: 3 23 | level: error 24 | hyphens: 25 | level: error 26 | indentation: disable 27 | key-duplicates: enable 28 | line-length: disable 29 | new-line-at-end-of-file: disable 30 | new-lines: 31 | type: unix 32 | trailing-spaces: disable 33 | truthy: disable 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 GantSign Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Ansible Role: Keyboard 2 | ====================== 3 | 4 | [![Tests](https://github.com/gantsign/ansible-role-keyboard/workflows/Tests/badge.svg)](https://github.com/gantsign/ansible-role-keyboard/actions?query=workflow%3ATests) 5 | [![Ansible Galaxy](https://img.shields.io/badge/ansible--galaxy-gantsign.keyboard-blue.svg)](https://galaxy.ansible.com/gantsign/keyboard) 6 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/gantsign/ansible-role-keyboard/master/LICENSE) 7 | 8 | Role to configure the keyboard layout etc. 9 | 10 | Requirements 11 | ------------ 12 | 13 | * Ansible >= 2.9 14 | 15 | * Linux Distribution 16 | 17 | * Debian Family 18 | 19 | * Debian 20 | 21 | * Stretch (9) 22 | * Buster (10) 23 | * Bullseye (11) 24 | 25 | * Ubuntu 26 | 27 | * Bionic (18.04) 28 | * Focal (20.04) 29 | 30 | Role Variables 31 | -------------- 32 | 33 | The following variables will change the behavior of this role (default values 34 | are shown below): 35 | 36 | ```yaml 37 | # The XKB keyboard model name. 38 | keyboard_model: pc104 39 | 40 | # The XKB keyboard layout name. 41 | keyboard_layout: us 42 | 43 | # The XKB keyboard variant components. 44 | keyboard_variant: '' 45 | 46 | # The XKB keyboard option components. 47 | keyboard_options: '' 48 | 49 | # The behavior of and keys. 50 | keyboard_backspace: guess 51 | ``` 52 | 53 | See `man keyboard` for configuration options. 54 | 55 | Example Playbook 56 | ---------------- 57 | 58 | ```yaml 59 | - hosts: servers 60 | roles: 61 | - role: gantsign.keyboard 62 | # European keyboard model (pc105 is widely used outside Europe as well) 63 | keyboard_model: pc105 64 | # Layout for Great Britain / United Kingdom 65 | keyboard_layout: gb 66 | ``` 67 | 68 | More Roles From GantSign 69 | ------------------------ 70 | 71 | You can find more roles from GantSign on 72 | [Ansible Galaxy](https://galaxy.ansible.com/gantsign). 73 | 74 | Development & Testing 75 | --------------------- 76 | 77 | This project uses [Molecule](http://molecule.readthedocs.io/) to aid in the 78 | development and testing; the role is unit tested using 79 | [Testinfra](http://testinfra.readthedocs.io/) and 80 | [pytest](http://docs.pytest.org/). 81 | 82 | To develop or test you'll need to have installed the following: 83 | 84 | * Linux (e.g. [Ubuntu](http://www.ubuntu.com/)) 85 | * [Docker](https://www.docker.com/) 86 | * [Python](https://www.python.org/) (including python-pip) 87 | * [Ansible](https://www.ansible.com/) 88 | * [Molecule](http://molecule.readthedocs.io/) 89 | 90 | Because the above can be tricky to install, this project includes 91 | [Molecule Wrapper](https://github.com/gantsign/molecule-wrapper). Molecule 92 | Wrapper is a shell script that installs Molecule and it's dependencies (apart 93 | from Linux) and then executes Molecule with the command you pass it. 94 | 95 | To test this role using Molecule Wrapper run the following command from the 96 | project root: 97 | 98 | ```bash 99 | ./moleculew test 100 | ``` 101 | 102 | Note: some of the dependencies need `sudo` permission to install. 103 | 104 | License 105 | ------- 106 | 107 | MIT 108 | 109 | Author Information 110 | ------------------ 111 | 112 | John Freeman 113 | 114 | GantSign Ltd. 115 | Company No. 06109112 (registered in England) 116 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # The XKB keyboard model name. 3 | keyboard_model: pc104 4 | 5 | # The XKB keyboard layout name. 6 | keyboard_layout: us 7 | 8 | # The XKB keyboard variant components. 9 | keyboard_variant: '' 10 | 11 | # The XKB keyboard option components. 12 | keyboard_options: '' 13 | 14 | # The behavior of and keys. 15 | keyboard_backspace: guess 16 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: dpkg-reconfigure keyboard-configuration 3 | become: yes 4 | command: /usr/sbin/dpkg-reconfigure -f noninteractive keyboard-configuration 5 | when: ansible_os_family == 'Debian' 6 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: John Freeman 4 | description: Role for configuring keyboard layout etc. 5 | company: GantSign Ltd. 6 | license: MIT 7 | min_ansible_version: 2.9 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - bionic 12 | - focal 13 | - name: Debian 14 | versions: 15 | - buster 16 | - stretch 17 | - bullseye 18 | galaxy_tags: 19 | - keyboard 20 | - ubuntu 21 | - system 22 | dependencies: [] 23 | -------------------------------------------------------------------------------- /molecule/debian-max/INSTALL.rst: -------------------------------------------------------------------------------- 1 | ******* 2 | Docker driver installation guide 3 | ******* 4 | 5 | Requirements 6 | ============ 7 | 8 | * Docker Engine 9 | 10 | Install 11 | ======= 12 | 13 | Please refer to the `Virtual environment`_ documentation for installation best 14 | practices. If not using a virtual environment, please consider passing the 15 | widely recommended `'--user' flag`_ when invoking ``pip``. 16 | 17 | .. _Virtual environment: https://virtualenv.pypa.io/en/latest/ 18 | .. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site 19 | 20 | .. code-block:: bash 21 | 22 | $ python3 -m pip install 'molecule[docker]' 23 | -------------------------------------------------------------------------------- /molecule/debian-max/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | 5 | driver: 6 | name: docker 7 | 8 | lint: | 9 | set -e 10 | yamllint . 11 | ansible-lint 12 | flake8 13 | 14 | platforms: 15 | - name: ansible-role-keyboard-debian-max 16 | image: debian:11 17 | 18 | provisioner: 19 | name: ansible 20 | playbooks: 21 | converge: ../default/converge.yml 22 | 23 | verifier: 24 | name: testinfra 25 | directory: ../default/tests 26 | -------------------------------------------------------------------------------- /molecule/debian-min/INSTALL.rst: -------------------------------------------------------------------------------- 1 | ******* 2 | Docker driver installation guide 3 | ******* 4 | 5 | Requirements 6 | ============ 7 | 8 | * Docker Engine 9 | 10 | Install 11 | ======= 12 | 13 | Please refer to the `Virtual environment`_ documentation for installation best 14 | practices. If not using a virtual environment, please consider passing the 15 | widely recommended `'--user' flag`_ when invoking ``pip``. 16 | 17 | .. _Virtual environment: https://virtualenv.pypa.io/en/latest/ 18 | .. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site 19 | 20 | .. code-block:: bash 21 | 22 | $ python3 -m pip install 'molecule[docker]' 23 | -------------------------------------------------------------------------------- /molecule/debian-min/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | 5 | driver: 6 | name: docker 7 | 8 | lint: | 9 | set -e 10 | yamllint . 11 | ansible-lint 12 | flake8 13 | 14 | platforms: 15 | - name: ansible-role-keyboard-debian-min 16 | image: debian:9 17 | 18 | provisioner: 19 | name: ansible 20 | playbooks: 21 | converge: ../default/converge.yml 22 | 23 | verifier: 24 | name: testinfra 25 | directory: ../default/tests 26 | -------------------------------------------------------------------------------- /molecule/default/INSTALL.rst: -------------------------------------------------------------------------------- 1 | ******* 2 | Docker driver installation guide 3 | ******* 4 | 5 | Requirements 6 | ============ 7 | 8 | * Docker Engine 9 | 10 | Install 11 | ======= 12 | 13 | Please refer to the `Virtual environment`_ documentation for installation best 14 | practices. If not using a virtual environment, please consider passing the 15 | widely recommended `'--user' flag`_ when invoking ``pip``. 16 | 17 | .. _Virtual environment: https://virtualenv.pypa.io/en/latest/ 18 | .. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site 19 | 20 | .. code-block:: bash 21 | 22 | $ python3 -m pip install 'molecule[docker]' 23 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | 5 | pre_tasks: 6 | - name: update apt cache 7 | apt: 8 | update_cache: yes 9 | changed_when: no 10 | 11 | roles: 12 | - role: ansible-role-keyboard 13 | keyboard_model: pc105 14 | keyboard_layout: brai 15 | keyboard_variant: right_hand 16 | keyboard_options: 'lv3:alt_switch,compose:rctrl' 17 | keyboard_backspace: guess 18 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | 5 | driver: 6 | name: docker 7 | 8 | lint: | 9 | set -e 10 | yamllint . 11 | ansible-lint 12 | flake8 13 | 14 | platforms: 15 | - name: ansible-role-keyboard-ubuntu-max 16 | image: ubuntu:20.04 17 | 18 | provisioner: 19 | name: ansible 20 | 21 | verifier: 22 | name: testinfra 23 | -------------------------------------------------------------------------------- /molecule/default/tests/conftest.py: -------------------------------------------------------------------------------- 1 | """PyTest Fixtures.""" 2 | from __future__ import absolute_import 3 | 4 | import os 5 | 6 | import pytest 7 | 8 | 9 | def pytest_runtest_setup(item): 10 | """Run tests only when under molecule with testinfra installed.""" 11 | try: 12 | import testinfra 13 | except ImportError: 14 | pytest.skip("Test requires testinfra", allow_module_level=True) 15 | if "MOLECULE_INVENTORY_FILE" in os.environ: 16 | pytest.testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 17 | os.environ["MOLECULE_INVENTORY_FILE"] 18 | ).get_hosts("all") 19 | else: 20 | pytest.skip( 21 | "Test should run only from inside molecule.", 22 | allow_module_level=True) 23 | -------------------------------------------------------------------------------- /molecule/default/tests/test_role.py: -------------------------------------------------------------------------------- 1 | def test_keyboard_file(host): 2 | kb = host.file('/etc/default/keyboard') 3 | 4 | assert kb.exists 5 | assert kb.is_file 6 | assert kb.user == 'root' 7 | assert kb.group == 'root' 8 | assert oct(kb.mode) == '0o644' 9 | 10 | assert kb.contains('XKBMODEL="pc105"') 11 | assert kb.contains('XKBLAYOUT="brai"') 12 | assert kb.contains('XKBVARIANT="right_hand"') 13 | assert kb.contains('XKBOPTIONS="lv3:alt_switch,compose:rctrl"') 14 | assert kb.contains('BACKSPACE="guess"') 15 | -------------------------------------------------------------------------------- /molecule/ubuntu-min/INSTALL.rst: -------------------------------------------------------------------------------- 1 | ******* 2 | Docker driver installation guide 3 | ******* 4 | 5 | Requirements 6 | ============ 7 | 8 | * Docker Engine 9 | 10 | Install 11 | ======= 12 | 13 | Please refer to the `Virtual environment`_ documentation for installation best 14 | practices. If not using a virtual environment, please consider passing the 15 | widely recommended `'--user' flag`_ when invoking ``pip``. 16 | 17 | .. _Virtual environment: https://virtualenv.pypa.io/en/latest/ 18 | .. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site 19 | 20 | .. code-block:: bash 21 | 22 | $ python3 -m pip install 'molecule[docker]' 23 | -------------------------------------------------------------------------------- /molecule/ubuntu-min/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | 5 | driver: 6 | name: docker 7 | 8 | lint: | 9 | set -e 10 | yamllint . 11 | ansible-lint 12 | flake8 13 | 14 | platforms: 15 | - name: ansible-role-keyboard-ubuntu-min 16 | image: ubuntu:18.04 17 | 18 | provisioner: 19 | name: ansible 20 | playbooks: 21 | converge: ../default/converge.yml 22 | 23 | verifier: 24 | name: testinfra 25 | directory: ../default/tests 26 | -------------------------------------------------------------------------------- /moleculew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (c) 2018 GantSign Ltd. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | 26 | # Molecule Wrapper the wrapper script for Molecule 27 | # https://github.com/gantsign/molecule-wrapper 28 | 29 | set -e 30 | 31 | WRAPPER_VERSION=1.2.0 32 | 33 | VERSION_DIR='.moleculew' 34 | PYTHON_VERSION_FILE="$VERSION_DIR/python_version" 35 | ANSIBLE_VERSION_FILE="$VERSION_DIR/ansible_version" 36 | MOLECULE_VERSION_FILE="$VERSION_DIR/molecule_version" 37 | YAMLLINT_VERSION_FILE="$VERSION_DIR/yamllint_version" 38 | ANSIBLE_LINT_VERSION_FILE="$VERSION_DIR/ansible_lint_version" 39 | FLAKE8_VERSION_FILE="$VERSION_DIR/flake8_version" 40 | TESTINFRA_VERSION_FILE="$VERSION_DIR/testinfra_version" 41 | 42 | BUILD_DEPENDENCIES_INSTALLLED=false 43 | PYENV_INSTALLED=false 44 | 45 | ANSIBLE_VERSION='' 46 | MOLECULE_VERSION='' 47 | PYTHON_VERSION='' 48 | YAMLLINT_VERSION='' 49 | ANSIBLE_LINT_VERSION='' 50 | FLAKE8_VERSION='' 51 | TESTINFRA_VERSION='' 52 | USE_SYSTEM_DEPENDENCIES=false 53 | 54 | PRE_ARGS=() 55 | MOLECULE_CMD='' 56 | POST_ARGS=() 57 | 58 | export PATH="$HOME/.pyenv/bin:$HOME/.local/bin:$PATH" 59 | 60 | hr() { 61 | for ((i = 1; i <= 80; i++)); do 62 | printf '*' 63 | done 64 | echo '' 65 | } 66 | 67 | banner() { 68 | hr 69 | echo "$1" 70 | hr 71 | } 72 | 73 | run_as_root() { 74 | if [[ $EUID -eq 0 ]]; then 75 | "$@" 76 | elif [ -x "$(command -v sudo)" ]; then 77 | sudo "$@" 78 | else 79 | echo "Error: sudo is not installed" >&2 80 | exit 1 81 | fi 82 | } 83 | 84 | build_dependencies_present() { 85 | if [[ $BUILD_DEPENDENCIES_INSTALLLED == true ]]; then 86 | return 87 | fi 88 | if [[ $USE_SYSTEM_DEPENDENCIES == true ]]; then 89 | return 90 | fi 91 | # https://github.com/pyenv/pyenv/wiki/common-build-problems 92 | if [[ -x "$(command -v apt-get)" ]]; then 93 | banner 'Installing build dependencies' 94 | 95 | run_as_root apt-get update 96 | run_as_root apt-get install --assume-yes \ 97 | make build-essential libssl-dev zlib1g-dev libbz2-dev \ 98 | libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev \ 99 | libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev \ 100 | git jq 101 | echo '' 102 | elif [[ -x "$(command -v dnf)" ]]; then 103 | banner 'Installing build dependencies' 104 | 105 | run_as_root dnf install \ 106 | zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel \ 107 | openssl-devel xz xz-devel libffi-devel \ 108 | git curl jq 109 | echo '' 110 | elif [[ -x "$(command -v yum)" ]]; then 111 | banner 'Installing build dependencies' 112 | 113 | run_as_root yum install \ 114 | zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel \ 115 | openssl-devel xz xz-devel libffi-devel \ 116 | git curl jq 117 | echo '' 118 | elif [[ -x "$(command -v zypper)" ]]; then 119 | banner 'Installing build dependencies' 120 | 121 | run_as_root zypper install \ 122 | zlib-devel bzip2 libbz2-devel readline-devel sqlite3 sqlite3-devel \ 123 | libopenssl-devel xz xz-devel \ 124 | git curl jq 125 | echo '' 126 | fi 127 | BUILD_DEPENDENCIES_INSTALLLED=true 128 | } 129 | 130 | pyenv_present() { 131 | if [[ $PYENV_INSTALLED == true ]]; then 132 | return 133 | fi 134 | if [[ $USE_SYSTEM_DEPENDENCIES == true ]]; then 135 | return 136 | fi 137 | if [[ -x "$(command -v pyenv)" ]]; then 138 | PYENV_INSTALLED=true 139 | return 140 | fi 141 | 142 | build_dependencies_present 143 | 144 | banner "Installing pyenv for user $USER" 145 | bash <(curl --location https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer) 146 | echo '' 147 | PYENV_INSTALLED=true 148 | } 149 | 150 | query_latest_python_version() { 151 | pyenv_present 152 | 153 | PYTHON_VERSION="$(~/.pyenv/plugins/python-build/bin/python-build --definitions | grep --color=never '^3\.' | grep --invert-match '\-dev$' | tail -1)" 154 | } 155 | 156 | query_latest_package_version() { 157 | if [[ ! -x "$(command -v curl)" ]]; then 158 | build_dependencies_present 159 | fi 160 | if [[ ! -x "$(command -v jq)" ]]; then 161 | build_dependencies_present 162 | fi 163 | if [[ ! -x "$(command -v curl)" ]]; then 164 | echo 'Error: curl is not installed.' >&2 165 | exit 1 166 | fi 167 | if [[ ! -x "$(command -v jq)" ]]; then 168 | echo 'Error: jq is not installed.' >&2 169 | exit 1 170 | fi 171 | 172 | local version 173 | # shellcheck disable=SC2034 174 | version=$(curl --fail --silent --show-error --location "https://pypi.org/pypi/$2/json" | jq --raw-output '.info.version') 175 | 176 | eval "$1=\"\$version\"" 177 | } 178 | 179 | docker_present() { 180 | if [[ -x "$(command -v docker)" ]]; then 181 | return 182 | fi 183 | if [[ $USE_SYSTEM_DEPENDENCIES == true ]]; then 184 | echo 'Error: Docker is not installed.' >&2 185 | exit 1 186 | fi 187 | 188 | build_dependencies_present 189 | 190 | banner 'Installing Docker' 191 | sh <(curl --fail --silent --show-error --location https://get.docker.com) 192 | run_as_root usermod --append --groups docker "$USER" 193 | banner "User '$USER' has been added to the 'docker' group. Logout/restart and log back in for changes to take effect." 194 | exit 195 | } 196 | 197 | python_present() { 198 | if [[ $PYTHON_VERSION == system ]]; then 199 | if [[ ! -x "$(command -v python3)" ]] && 200 | [[ ! -x "$(command -v python)" ]]; then 201 | echo 'Error: python is not installed.' >&2 202 | exit 1 203 | fi 204 | if [[ ! -x "$(command -v pip3)" ]] && 205 | [[ ! -x "$(command -v pip)" ]]; then 206 | echo 'Error: pip is not installed.' >&2 207 | exit 1 208 | fi 209 | PYTHON_EXE="$(command -v python3 || command -v python)" 210 | else 211 | if [[ ! -x "$(command -v git)" ]]; then 212 | echo 'Error: git is not installed.' >&2 213 | exit 1 214 | fi 215 | 216 | pyenv_present 217 | 218 | export PYENV_VERSION="$PYTHON_VERSION" 219 | if [[ ! -d "$HOME/.pyenv/versions/$PYTHON_VERSION" ]]; then 220 | build_dependencies_present 221 | 222 | banner "Making Python version $PYTHON_VERSION available using pyenv" 223 | pyenv install "$PYTHON_VERSION" 224 | echo '' 225 | fi 226 | eval "$(pyenv init -)" 227 | PYTHON_EXE="$(pyenv which python)" 228 | fi 229 | } 230 | 231 | virtualenv_presant() { 232 | if [[ ! -x "$(command -v virtualenv)" ]]; then 233 | banner "Installing virtualenv for user $USER" 234 | "$PYTHON_EXE" -m pip install --user virtualenv 235 | echo '' 236 | fi 237 | } 238 | 239 | install_rich() { 240 | # Workaround breaking changes in rich 11 by installing version 10 241 | local RICH_VERSION='10.16.2' 242 | banner "Installing Rich $RICH_VERSION into virtualenv $VIRTUAL_ENV" 243 | pip install "rich==$RICH_VERSION" 244 | echo '' 245 | } 246 | 247 | install_ansible() { 248 | banner "Installing Ansible $ANSIBLE_VERSION into virtualenv $VIRTUAL_ENV" 249 | pip install "ansible==$ANSIBLE_VERSION" 250 | echo '' 251 | } 252 | 253 | install_molecule() { 254 | banner "Installing Molecule $MOLECULE_VERSION into virtualenv $VIRTUAL_ENV" 255 | 256 | pip install "molecule[docker]==$MOLECULE_VERSION" 257 | echo '' 258 | } 259 | 260 | install_yamllint() { 261 | banner "Installing YamlLint $YAMLLINT_VERSION into virtualenv $VIRTUAL_ENV" 262 | 263 | pip install "yamllint==$YAMLLINT_VERSION" 264 | echo '' 265 | } 266 | 267 | install_ansible_lint() { 268 | banner "Installing Anssible Lint $ANSIBLE_LINT_VERSION into virtualenv $VIRTUAL_ENV" 269 | 270 | pip install "ansible-lint==$ANSIBLE_LINT_VERSION" 271 | echo '' 272 | } 273 | 274 | install_flake8() { 275 | banner "Installing Flake8 $FLAKE8_VERSION into virtualenv $VIRTUAL_ENV" 276 | 277 | pip install "flake8==$FLAKE8_VERSION" 278 | echo '' 279 | } 280 | 281 | install_testinfra() { 282 | banner "Installing Testinfra $TESTINFRA_VERSION into virtualenv $VIRTUAL_ENV" 283 | 284 | pip install "testinfra==$TESTINFRA_VERSION" 285 | echo '' 286 | } 287 | 288 | wrapper_clean() { 289 | local MOLECULE_WRAPPER_HOME="$HOME/.moleculew" 290 | read -r -p "Delete ${MOLECULE_WRAPPER_HOME} (y/n)? " yn 291 | case $yn in 292 | [Yy]|YES|yes|Yes) 293 | rm -rf "$MOLECULE_WRAPPER_HOME"; 294 | exit 295 | ;; 296 | *) 297 | exit 298 | ;; 299 | esac 300 | } 301 | 302 | wrapper_upgrade() { 303 | curl --fail --silent --show-error --location --output moleculew.new \ 304 | 'https://raw.githubusercontent.com/gantsign/molecule-wrapper/master/moleculew' \ 305 | && chmod 'u+x' moleculew.new \ 306 | && mv --force moleculew.new moleculew 307 | 308 | local NEW_VERSION 309 | NEW_VERSION="$(./moleculew wrapper-version)" 310 | if [ "$WRAPPER_VERSION" != "$NEW_VERSION" ]; then 311 | echo "Upgraded wrapper from version $WRAPPER_VERSION to $NEW_VERSION" 312 | else 313 | echo "You are already using the latest version" 314 | fi 315 | exit 316 | } 317 | 318 | wrapper_version() { 319 | echo "$WRAPPER_VERSION" 320 | exit 321 | } 322 | 323 | print_versions() { 324 | echo "Python: $PYTHON_VERSION" 325 | echo "Ansible: $ANSIBLE_VERSION" 326 | echo "Molecule: $MOLECULE_VERSION" 327 | echo "YamlLint: $YAMLLINT_VERSION" 328 | echo "Ansible Lint: $ANSIBLE_LINT_VERSION" 329 | echo "Flake8: $FLAKE8_VERSION" 330 | echo "Testinfra: $TESTINFRA_VERSION" 331 | } 332 | 333 | wrapper_versions() { 334 | detemine_versions 335 | 336 | print_versions 337 | exit 338 | } 339 | 340 | wrapper_freeze() { 341 | detemine_versions 342 | 343 | banner 'Freezing versions' 344 | 345 | mkdir -p "$VERSION_DIR" 346 | 347 | echo "$PYTHON_VERSION" > "$PYTHON_VERSION_FILE" 348 | echo "$ANSIBLE_VERSION" > "$ANSIBLE_VERSION_FILE" 349 | echo "$MOLECULE_VERSION" > "$MOLECULE_VERSION_FILE" 350 | echo "$YAMLLINT_VERSION" > "$YAMLLINT_VERSION_FILE" 351 | echo "$ANSIBLE_LINT_VERSION" > "$ANSIBLE_LINT_VERSION_FILE" 352 | echo "$FLAKE8_VERSION" > "$FLAKE8_VERSION_FILE" 353 | echo "$TESTINFRA_VERSION" > "$TESTINFRA_VERSION_FILE" 354 | 355 | print_versions 356 | 357 | exit 358 | } 359 | 360 | wrapper_unfreeze() { 361 | banner 'Un-freezing versions' 362 | 363 | if [[ -f "$PYTHON_VERSION_FILE" ]]; then 364 | rm --verbose "$PYTHON_VERSION_FILE" 365 | fi 366 | if [[ -f "$ANSIBLE_VERSION_FILE" ]]; then 367 | rm --verbose "$ANSIBLE_VERSION_FILE" 368 | fi 369 | if [[ -f "$MOLECULE_VERSION_FILE" ]]; then 370 | rm --verbose "$MOLECULE_VERSION_FILE" 371 | fi 372 | if [[ -f "$YAMLLINT_VERSION_FILE" ]]; then 373 | rm --verbose "$YAMLLINT_VERSION_FILE" 374 | fi 375 | if [[ -f "$ANSIBLE_LINT_VERSION_FILE" ]]; then 376 | rm --verbose "$ANSIBLE_LINT_VERSION_FILE" 377 | fi 378 | if [[ -f "$FLAKE8_VERSION_FILE" ]]; then 379 | rm --verbose "$FLAKE8_VERSION_FILE" 380 | fi 381 | if [[ -f "$TESTINFRA_VERSION_FILE" ]]; then 382 | rm --verbose "$TESTINFRA_VERSION_FILE" 383 | fi 384 | 385 | exit 386 | } 387 | 388 | wrapper_upgrade_versions() { 389 | detemine_versions 390 | 391 | banner 'Upgrading versions' 392 | 393 | local CURRENT_PYTHON_VERSION="$PYTHON_VERSION" 394 | local CURRENT_ANSIBLE_VERSION="$ANSIBLE_VERSION" 395 | local CURRENT_MOLECULE_VERSION="$MOLECULE_VERSION" 396 | local CURRENT_YAMLLINT_VERSION="$YAMLLINT_VERSION" 397 | local CURRENT_ANSIBLE_LINT_VERSION="$ANSIBLE_LINT_VERSION" 398 | local CURRENT_FLAKE8_VERSION="$FLAKE8_VERSION" 399 | local CURRENT_TESTINFRA_VERSION="$TESTINFRA_VERSION" 400 | 401 | query_latest_python_version 402 | query_latest_package_version ANSIBLE_VERSION ansible 403 | query_latest_package_version MOLECULE_VERSION molecule 404 | query_latest_package_version YAMLLINT_VERSION yamllint 405 | query_latest_package_version ANSIBLE_LINT_VERSION ansible-lint 406 | query_latest_package_version FLAKE8_VERSION flake8 407 | query_latest_package_version TESTINFRA_VERSION testinfra 408 | echo '' 409 | 410 | echo 'New versions:' 411 | if [[ "$CURRENT_PYTHON_VERSION" == "$PYTHON_VERSION" ]]; then 412 | echo "Python: $CURRENT_PYTHON_VERSION (no change)" 413 | else 414 | echo "Python: $CURRENT_PYTHON_VERSION -> $PYTHON_VERSION" 415 | fi 416 | 417 | if [[ "$CURRENT_ANSIBLE_VERSION" == "$ANSIBLE_VERSION" ]]; then 418 | echo "Ansible: $CURRENT_ANSIBLE_VERSION (no change)" 419 | else 420 | echo "Ansible: $CURRENT_ANSIBLE_VERSION -> $ANSIBLE_VERSION" 421 | fi 422 | 423 | if [[ "$CURRENT_MOLECULE_VERSION" == "$MOLECULE_VERSION" ]]; then 424 | echo "Molecule: $CURRENT_MOLECULE_VERSION (no change)" 425 | else 426 | echo "Molecule: $CURRENT_MOLECULE_VERSION -> $MOLECULE_VERSION" 427 | fi 428 | 429 | if [[ "$CURRENT_YAMLLINT_VERSION" == "$YAMLLINT_VERSION" ]]; then 430 | echo "YamlLint: $CURRENT_YAMLLINT_VERSION (no change)" 431 | else 432 | echo "YamlLint: $CURRENT_YAMLLINT_VERSION -> $YAMLLINT_VERSION" 433 | fi 434 | 435 | if [[ "$CURRENT_ANSIBLE_LINT_VERSION" == "$ANSIBLE_LINT_VERSION" ]]; then 436 | echo "Ansible Lint: $CURRENT_ANSIBLE_LINT_VERSION (no change)" 437 | else 438 | echo "Ansible Lint: $CURRENT_ANSIBLE_LINT_VERSION -> $ANSIBLE_LINT_VERSION" 439 | fi 440 | 441 | if [[ "$CURRENT_FLAKE8_VERSION" == "$FLAKE8_VERSION" ]]; then 442 | echo "Flake8: $CURRENT_FLAKE8_VERSION (no change)" 443 | else 444 | echo "Flake8: $CURRENT_FLAKE8_VERSION -> $FLAKE8_VERSION" 445 | fi 446 | 447 | if [[ "$CURRENT_TESTINFRA_VERSION" == "$TESTINFRA_VERSION" ]]; then 448 | echo "Testinfra: $CURRENT_TESTINFRA_VERSION (no change)" 449 | else 450 | echo "Testinfra: $CURRENT_TESTINFRA_VERSION -> $TESTINFRA_VERSION" 451 | fi 452 | 453 | echo '' 454 | 455 | wrapper_freeze 456 | } 457 | 458 | wrapper_help() { 459 | activate_virtualenv 460 | 461 | molecule --help 462 | 463 | echo " 464 | Molecule Wrapper 465 | 466 | Additional options: 467 | --ansible VERSION Use the specified version of Ansible 468 | --molecule VERSION Use the specified version of Molecule 469 | --python VERSION Use the specified version of Python 470 | --yamllint VERSION Use the specified version of YamlLint 471 | --ansible-lint VERSION Use the specified version of Ansible Lint 472 | --flake8 VERSION Use the specified version of Flake8 473 | --testinfra VERSION Use the specified version of Testinfra 474 | --use-system-dependencies Use system dependencies 475 | 476 | Additional commands: 477 | wrapper-clean Removes all the wrapper virtual environments 478 | wrapper-freeze Freezes the dependency versions being used 479 | wrapper-unfreeze Un-freezes the dependency versions 480 | wrapper-upgrade Upgrades the Molecule Wrapper to the latest version 481 | wrapper-upgrade-versions Upgrades any frozen dependency versions 482 | wrapper-version Displays the current version of Molecule Wrapper 483 | " 484 | } 485 | 486 | query_package_versions() { 487 | local package_name="$1" 488 | local min_version="$2" 489 | 490 | if [[ ! -x "$(command -v curl)" ]]; then 491 | build_dependencies_present > /dev/null 492 | fi 493 | if [[ ! -x "$(command -v jq)" ]]; then 494 | build_dependencies_present > /dev/null 495 | fi 496 | if [[ ! -x "$(command -v curl)" ]]; then 497 | echo 'Error: curl is not installed.' >&2 498 | exit 1 499 | fi 500 | if [[ ! -x "$(command -v jq)" ]]; then 501 | echo 'Error: jq is not installed.' >&2 502 | exit 1 503 | fi 504 | if [[ ! -x "$(command -v sort)" ]]; then 505 | echo 'Error: sort is not installed.' >&2 506 | exit 1 507 | fi 508 | 509 | for i in $(curl --fail --silent --show-error \ 510 | --location "https://pypi.org/pypi/$package_name/json" \ 511 | | jq --raw-output ".releases | keys | .[], \"$min_version.\"" \ 512 | | grep --invert-match '[a-zA-Z]' \ 513 | | sort --version-sort --reverse) ; do 514 | if [[ "$i" == "$min_version." ]]; then 515 | break 516 | fi 517 | echo "$i" 518 | done 519 | } 520 | 521 | wrapper_options_ansible() { 522 | echo 'latest' 523 | query_package_versions 'ansible' '2.8' 524 | } 525 | 526 | wrapper_options_molecule() { 527 | echo 'latest' 528 | query_package_versions 'molecule' '3.1.5' 529 | } 530 | 531 | wrapper_options_python() { 532 | if [[ ! -x "$(command -v sort)" ]]; then 533 | echo 'Error: sort is not installed.' >&2 534 | exit 1 535 | fi 536 | 537 | pyenv_present > /dev/null 538 | 539 | local min_version='3.6' 540 | 541 | echo 'latest' 542 | 543 | for i in $( (echo "$min_version." && \ 544 | ~/.pyenv/plugins/python-build/bin/python-build --definitions) \ 545 | | grep --color=never '^[0-9]' \ 546 | | grep --invert-match '\-dev$' \ 547 | | sort --version-sort --reverse) ; do 548 | if [[ "$i" == "$min_version." ]]; then 549 | break 550 | fi 551 | echo "$i" 552 | done 553 | } 554 | 555 | wrapper_options_yamllint() { 556 | echo 'latest' 557 | query_package_versions 'yamllint' '1.26.3' 558 | } 559 | 560 | wrapper_options_ansible_lint() { 561 | echo 'latest' 562 | query_package_versions 'ansible_lint' '5.4.0' 563 | } 564 | 565 | wrapper_options_flake8() { 566 | echo 'latest' 567 | query_package_versions 'flake8' '4.0.1' 568 | } 569 | 570 | wrapper_options_testinfra() { 571 | echo 'latest' 572 | query_package_versions 'testinfra' '5.3.1' 573 | } 574 | 575 | wrapper_options_scenario() { 576 | ( 577 | cd molecule > /dev/null && 578 | for d in *; do 579 | if [ -d "$d" ]; then 580 | echo "$d" 581 | fi 582 | done 583 | ) 584 | } 585 | 586 | wrapper_virtualenv() { 587 | activate_virtualenv > /dev/null 588 | echo "$VIRTUAL_ENV" 589 | } 590 | 591 | parse_args() { 592 | set +e 593 | 594 | while [[ $# -gt 0 ]]; do 595 | key="$1" 596 | 597 | case $key in 598 | --python=*) 599 | PYTHON_VERSION="${1#*=}" 600 | shift 601 | ;; 602 | --python) 603 | shift 604 | PYTHON_VERSION="$1" 605 | shift 606 | ;; 607 | --ansible=*) 608 | ANSIBLE_VERSION="${1#*=}" 609 | shift 610 | ;; 611 | --ansible) 612 | shift 613 | ANSIBLE_VERSION="$1" 614 | shift 615 | ;; 616 | --molecule=*) 617 | MOLECULE_VERSION="${1#*=}" 618 | shift 619 | ;; 620 | --molecule) 621 | shift 622 | MOLECULE_VERSION="$1" 623 | shift 624 | ;; 625 | --yamllint=*) 626 | YAMLLINT_VERSION="${1#*=}" 627 | shift 628 | ;; 629 | --yamllint) 630 | shift 631 | YAMLLINT_VERSION="$1" 632 | shift 633 | ;; 634 | --ansible-lint=*) 635 | ANSIBLE_LINT_VERSION="${1#*=}" 636 | shift 637 | ;; 638 | --ansible-lint) 639 | shift 640 | ANSIBLE_LINT_VERSION="$1" 641 | shift 642 | ;; 643 | --flake8=*) 644 | FLAKE8_VERSION="${1#*=}" 645 | shift 646 | ;; 647 | --flake8) 648 | shift 649 | FLAKE8_VERSION="$1" 650 | shift 651 | ;; 652 | --testinfra) 653 | shift 654 | TESTINFRA_VERSION="$1" 655 | shift 656 | ;; 657 | --testinfra=*) 658 | TESTINFRA_VERSION="${1#*=}" 659 | shift 660 | ;; 661 | --use-system-dependencies) 662 | USE_SYSTEM_DEPENDENCIES=true 663 | shift 664 | ;; 665 | --help) 666 | MOLECULE_CMD='wrapper-help' 667 | break 668 | ;; 669 | wrapper-*) 670 | MOLECULE_CMD="$1" 671 | shift 672 | ;; 673 | check|converge|create|dependency|destroy|idempotence|init|lint|list|login|matrix|prepare|side-effect|syntax|test|verify) 674 | if [[ "$MOLECULE_CMD" != '' ]]; then 675 | shift 676 | else 677 | MOLECULE_CMD="$1" 678 | shift 679 | for arg in "$@"; do 680 | POST_ARGS+=("$arg") 681 | done 682 | break 683 | fi 684 | ;; 685 | *) 686 | PRE_ARGS+=("$1") 687 | shift 688 | ;; 689 | esac 690 | done 691 | set -e 692 | } 693 | 694 | detemine_versions() { 695 | if [[ $USE_SYSTEM_DEPENDENCIES == false ]]; then 696 | USE_SYSTEM_DEPENDENCIES="$MOLECULEW_USE_SYSTEM" 697 | fi 698 | if [[ $PYTHON_VERSION == '' ]]; then 699 | PYTHON_VERSION="$MOLECULEW_PYTHON" 700 | fi 701 | if [[ $ANSIBLE_VERSION == '' ]]; then 702 | ANSIBLE_VERSION="$MOLECULEW_ANSIBLE" 703 | fi 704 | if [[ $MOLECULE_VERSION == '' ]]; then 705 | MOLECULE_VERSION="$MOLECULEW_MOLECULE" 706 | fi 707 | if [[ $YAMLLINT_VERSION == '' ]]; then 708 | YAMLLINT_VERSION="$MOLECULEW_YAMLLINT" 709 | fi 710 | if [[ $ANSIBLE_LINT_VERSION == '' ]]; then 711 | ANSIBLE_LINT_VERSION="$MOLECULEW_ANSIBLE_LINT" 712 | fi 713 | if [[ $FLAKE8_VERSION == '' ]]; then 714 | FLAKE8_VERSION="$MOLECULEW_FLAKE8" 715 | fi 716 | if [[ $TESTINFRA_VERSION == '' ]]; then 717 | TESTINFRA_VERSION="$MOLECULEW_TESTINFRA" 718 | fi 719 | 720 | if [[ $USE_SYSTEM_DEPENDENCIES == true ]]; then 721 | if [[ $PYTHON_VERSION != '' ]]; then 722 | echo "Error: --python and --use-system-dependencies cannot be used together" >&2 723 | exit 1 724 | fi 725 | PYTHON_VERSION=system 726 | elif [[ $PYTHON_VERSION == '' ]] || [[ $PYTHON_VERSION == 'default' ]]; then 727 | if [[ -f $PYTHON_VERSION_FILE ]]; then 728 | PYTHON_VERSION=$(<"$PYTHON_VERSION_FILE") 729 | fi 730 | if [[ $PYTHON_VERSION == '' ]]; then 731 | query_latest_python_version 732 | fi 733 | elif [[ $PYTHON_VERSION == 'latest' ]]; then 734 | query_latest_python_version 735 | fi 736 | 737 | if [[ $ANSIBLE_VERSION == '' ]] || [[ $ANSIBLE_VERSION == 'default' ]]; then 738 | if [[ -f $ANSIBLE_VERSION_FILE ]]; then 739 | ANSIBLE_VERSION=$(<"$ANSIBLE_VERSION_FILE") 740 | fi 741 | if [[ $ANSIBLE_VERSION == '' ]]; then 742 | query_latest_package_version ANSIBLE_VERSION ansible 743 | fi 744 | elif [[ $ANSIBLE_VERSION == 'latest' ]]; then 745 | query_latest_package_version ANSIBLE_VERSION ansible 746 | fi 747 | 748 | if [[ $MOLECULE_VERSION == '' ]] || [[ $MOLECULE_VERSION == 'default' ]]; then 749 | if [[ -f $MOLECULE_VERSION_FILE ]]; then 750 | MOLECULE_VERSION=$(<$MOLECULE_VERSION_FILE) 751 | fi 752 | if [[ $MOLECULE_VERSION == '' ]]; then 753 | query_latest_package_version MOLECULE_VERSION molecule 754 | fi 755 | elif [[ $MOLECULE_VERSION == 'latest' ]]; then 756 | query_latest_package_version MOLECULE_VERSION molecule 757 | fi 758 | 759 | if [[ $YAMLLINT_VERSION == '' ]] || [[ $YAMLLINT_VERSION == 'default' ]]; then 760 | if [[ -f $YAMLLINT_VERSION_FILE ]]; then 761 | YAMLLINT_VERSION=$(<$YAMLLINT_VERSION_FILE) 762 | fi 763 | if [[ $YAMLLINT_VERSION == '' ]]; then 764 | query_latest_package_version YAMLLINT_VERSION yamllint 765 | fi 766 | elif [[ $YAMLLINT_VERSION == 'latest' ]]; then 767 | query_latest_package_version YAMLLINT_VERSION yamllint 768 | fi 769 | 770 | if [[ $ANSIBLE_LINT_VERSION == '' ]] || [[ $ANSIBLE_LINT_VERSION == 'default' ]]; then 771 | if [[ -f $ANSIBLE_LINT_VERSION_FILE ]]; then 772 | ANSIBLE_LINT_VERSION=$(<$ANSIBLE_LINT_VERSION_FILE) 773 | fi 774 | if [[ $ANSIBLE_LINT_VERSION == '' ]]; then 775 | query_latest_package_version ANSIBLE_LINT_VERSION ansible-lint 776 | fi 777 | elif [[ $ANSIBLE_LINT_VERSION == 'latest' ]]; then 778 | query_latest_package_version ANSIBLE_LINT_VERSION ansible-lint 779 | fi 780 | 781 | if [[ $FLAKE8_VERSION == '' ]] || [[ $FLAKE8_VERSION == 'default' ]]; then 782 | if [[ -f $FLAKE8_VERSION_FILE ]]; then 783 | FLAKE8_VERSION=$(<$FLAKE8_VERSION_FILE) 784 | fi 785 | if [[ $FLAKE8_VERSION == '' ]]; then 786 | query_latest_package_version FLAKE8_VERSION flake8 787 | fi 788 | elif [[ $FLAKE8_VERSION == 'latest' ]]; then 789 | query_latest_package_version FLAKE8_VERSION flake8 790 | fi 791 | 792 | if [[ $TESTINFRA_VERSION == '' ]] || [[ $TESTINFRA_VERSION == 'default' ]]; then 793 | if [[ -f $TESTINFRA_VERSION_FILE ]]; then 794 | TESTINFRA_VERSION=$(<$TESTINFRA_VERSION_FILE) 795 | fi 796 | if [[ $TESTINFRA_VERSION == '' ]]; then 797 | query_latest_package_version TESTINFRA_VERSION testinfra 798 | fi 799 | elif [[ $TESTINFRA_VERSION == 'latest' ]]; then 800 | query_latest_package_version TESTINFRA_VERSION testinfra 801 | fi 802 | } 803 | 804 | activate_virtualenv() { 805 | detemine_versions 806 | 807 | MOLECULE_WRAPPER_ENV="$HOME/.moleculew/ml-${MOLECULE_VERSION}_an-${ANSIBLE_VERSION}_py-${PYTHON_VERSION}_yl-${YAMLLINT_VERSION}_al-${ANSIBLE_LINT_VERSION}_f8-${FLAKE8_VERSION}_ti-${TESTINFRA_VERSION}" 808 | 809 | if [ ! -f "$MOLECULE_WRAPPER_ENV/bin/activate" ]; then 810 | 811 | build_dependencies_present 812 | 813 | docker_present 814 | 815 | python_present 816 | 817 | virtualenv_presant 818 | 819 | banner "Initializing virtualenv $MOLECULE_WRAPPER_ENV" 820 | virtualenv "--python=$PYTHON_EXE" "$MOLECULE_WRAPPER_ENV" 821 | # shellcheck disable=SC1090 822 | source "$MOLECULE_WRAPPER_ENV/bin/activate" 823 | echo '' 824 | 825 | # Workaround breaking changes in rich 11 by installing version 10 826 | install_rich 827 | 828 | install_ansible 829 | 830 | install_molecule 831 | 832 | install_yamllint 833 | 834 | install_ansible_lint 835 | 836 | install_flake8 837 | 838 | install_testinfra 839 | else 840 | # shellcheck disable=SC1090 841 | source "$MOLECULE_WRAPPER_ENV/bin/activate" 842 | fi 843 | } 844 | 845 | parse_args "$@" 846 | 847 | case $MOLECULE_CMD in 848 | wrapper-clean) 849 | wrapper_clean 850 | ;; 851 | wrapper-freeze) 852 | wrapper_freeze 853 | ;; 854 | wrapper-help) 855 | wrapper_help 856 | ;; 857 | wrapper-install) 858 | activate_virtualenv 859 | ;; 860 | wrapper-options-ansible) 861 | wrapper_options_ansible 862 | ;; 863 | wrapper-options-molecule) 864 | wrapper_options_molecule 865 | ;; 866 | wrapper-options-python) 867 | wrapper_options_python 868 | ;; 869 | wrapper-options-yamllint) 870 | wrapper_options_yamllint 871 | ;; 872 | wrapper-options-ansible-lint) 873 | wrapper_options_ansible_lint 874 | ;; 875 | wrapper-options-flake8) 876 | wrapper_options_flake8 877 | ;; 878 | wrapper-options-testinfra) 879 | wrapper_options_testinfra 880 | ;; 881 | wrapper-options-scenario) 882 | wrapper_options_scenario 883 | ;; 884 | wrapper-unfreeze) 885 | wrapper_unfreeze 886 | ;; 887 | wrapper-upgrade) 888 | wrapper_upgrade 889 | ;; 890 | wrapper-upgrade-versions) 891 | wrapper_upgrade_versions 892 | ;; 893 | wrapper-version) 894 | wrapper_version 895 | ;; 896 | wrapper-versions) 897 | wrapper_versions 898 | ;; 899 | wrapper-virtualenv) 900 | wrapper_virtualenv 901 | ;; 902 | wrapper-*) 903 | echo "Unsupported command: $1" >&2 904 | exit 1 905 | ;; 906 | *) 907 | activate_virtualenv 908 | 909 | # shellcheck disable=SC2086 910 | exec molecule "${PRE_ARGS[@]}" $MOLECULE_CMD "${POST_ARGS[@]}" 911 | ;; 912 | esac 913 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: install keyboard-configuration 3 | become: yes 4 | apt: 5 | name: 6 | - keyboard-configuration 7 | - console-setup 8 | state: present 9 | when: ansible_os_family == 'Debian' 10 | 11 | - name: write keyboard configration 12 | become: yes 13 | template: 14 | src: keyboards.j2 15 | dest: /etc/default/keyboard 16 | force: yes 17 | owner: root 18 | group: root 19 | mode: 'u=rw,go=r' 20 | notify: 21 | - dpkg-reconfigure keyboard-configuration 22 | -------------------------------------------------------------------------------- /templates/keyboards.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | XKBMODEL="{{ keyboard_model }}" 4 | XKBLAYOUT="{{ keyboard_layout }}" 5 | XKBVARIANT="{{ keyboard_variant }}" 6 | XKBOPTIONS="{{ keyboard_options }}" 7 | BACKSPACE="{{ keyboard_backspace }}" 8 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for keyboard 3 | --------------------------------------------------------------------------------