├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── moleculew ├── next-version.sh └── zsh ├── README.md └── moleculew.plugin.zsh /.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 | # YAML is fussy about indenting and charset 14 | [*.yml] 15 | indent_style = space 16 | indent_size = 2 17 | continuation_indent_size = unset 18 | charset = utf-8 19 | 20 | # Markdown is fussy about indenting 21 | [*.md] 22 | indent_style = space 23 | indent_size = 4 24 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | # Set the default behavior, in case people don't have core.autocrlf set. 3 | * text=auto 4 | 5 | # Explicitly declare text files you want to always be normalized and converted 6 | # to native line endings on checkout. 7 | LICENSE text 8 | .editorconfig text 9 | .gitattributes text 10 | .gitignore text 11 | .yamllint text 12 | *.md text 13 | 14 | # Declare files that will always have CRLF line endings on checkout. 15 | *.bat text eol=crlf 16 | *.cmd text eol=crlf 17 | 18 | # Declare files that will always have LF line endings on checkout. 19 | moleculew eol=lf 20 | moleculew-install eol=lf 21 | *.sh text eol=lf 22 | *.zsh text eol=lf 23 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: {} 8 | 9 | jobs: 10 | test: 11 | name: Shell lint 12 | runs-on: ubuntu-20.04 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | 18 | - name: Install shellcheck 19 | run: sudo apt-get install -y shellcheck 20 | 21 | - name: Lint moleculew 22 | run: shellcheck moleculew 23 | 24 | - name: Lint next-version.sh 25 | run: shellcheck next-version.sh 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/windows,linux,osx,vim,emacs,intellij,eclipse,visualstudiocode 2 | 3 | ### Eclipse ### 4 | 5 | .metadata 6 | bin/ 7 | tmp/ 8 | *.tmp 9 | *.bak 10 | *.swp 11 | *~.nib 12 | local.properties 13 | .settings/ 14 | .loadpath 15 | .recommenders 16 | 17 | # External tool builders 18 | .externalToolBuilders/ 19 | 20 | # Locally stored "Eclipse launch configurations" 21 | *.launch 22 | 23 | # PyDev specific (Python IDE for Eclipse) 24 | *.pydevproject 25 | 26 | # CDT-specific (C/C++ Development Tooling) 27 | .cproject 28 | 29 | # CDT- autotools 30 | .autotools 31 | 32 | # Java annotation processor (APT) 33 | .factorypath 34 | 35 | # PDT-specific (PHP Development Tools) 36 | .buildpath 37 | 38 | # sbteclipse plugin 39 | .target 40 | 41 | # Tern plugin 42 | .tern-project 43 | 44 | # TeXlipse plugin 45 | .texlipse 46 | 47 | # STS (Spring Tool Suite) 48 | .springBeans 49 | 50 | # Code Recommenders 51 | .recommenders/ 52 | 53 | # Annotation Processing 54 | .apt_generated/ 55 | 56 | # Scala IDE specific (Scala & Java development for Eclipse) 57 | .cache-main 58 | .scala_dependencies 59 | .worksheet 60 | 61 | ### Eclipse Patch ### 62 | # Eclipse Core 63 | .project 64 | 65 | # JDT-specific (Eclipse Java Development Tools) 66 | .classpath 67 | 68 | # Annotation Processing 69 | .apt_generated 70 | 71 | ### Emacs ### 72 | # -*- mode: gitignore; -*- 73 | *~ 74 | \#*\# 75 | /.emacs.desktop 76 | /.emacs.desktop.lock 77 | *.elc 78 | auto-save-list 79 | tramp 80 | .\#* 81 | 82 | # Org-mode 83 | .org-id-locations 84 | *_archive 85 | 86 | # flymake-mode 87 | *_flymake.* 88 | 89 | # eshell files 90 | /eshell/history 91 | /eshell/lastdir 92 | 93 | # elpa packages 94 | /elpa/ 95 | 96 | # reftex files 97 | *.rel 98 | 99 | # AUCTeX auto folder 100 | /auto/ 101 | 102 | # cask packages 103 | .cask/ 104 | dist/ 105 | 106 | # Flycheck 107 | flycheck_*.el 108 | 109 | # server auth directory 110 | /server/ 111 | 112 | # projectiles files 113 | .projectile 114 | 115 | # directory configuration 116 | .dir-locals.el 117 | 118 | ### Intellij ### 119 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 120 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 121 | 122 | # User-specific stuff 123 | .idea/**/workspace.xml 124 | .idea/**/tasks.xml 125 | .idea/**/usage.statistics.xml 126 | .idea/**/dictionaries 127 | .idea/**/shelf 128 | 129 | # Sensitive or high-churn files 130 | .idea/**/dataSources/ 131 | .idea/**/dataSources.ids 132 | .idea/**/dataSources.local.xml 133 | .idea/**/sqlDataSources.xml 134 | .idea/**/dynamic.xml 135 | .idea/**/uiDesigner.xml 136 | .idea/**/dbnavigator.xml 137 | 138 | # Gradle 139 | .idea/**/gradle.xml 140 | .idea/**/libraries 141 | 142 | # Gradle and Maven with auto-import 143 | # When using Gradle or Maven with auto-import, you should exclude module files, 144 | # since they will be recreated, and may cause churn. Uncomment if using 145 | # auto-import. 146 | # .idea/modules.xml 147 | # .idea/*.iml 148 | # .idea/modules 149 | 150 | # CMake 151 | cmake-build-*/ 152 | 153 | # Mongo Explorer plugin 154 | .idea/**/mongoSettings.xml 155 | 156 | # File-based project format 157 | *.iws 158 | 159 | # IntelliJ 160 | out/ 161 | 162 | # mpeltonen/sbt-idea plugin 163 | .idea_modules/ 164 | 165 | # JIRA plugin 166 | atlassian-ide-plugin.xml 167 | 168 | # Cursive Clojure plugin 169 | .idea/replstate.xml 170 | 171 | # Crashlytics plugin (for Android Studio and IntelliJ) 172 | com_crashlytics_export_strings.xml 173 | crashlytics.properties 174 | crashlytics-build.properties 175 | fabric.properties 176 | 177 | # Editor-based Rest Client 178 | .idea/httpRequests 179 | 180 | ### Intellij Patch ### 181 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 182 | 183 | # *.iml 184 | # modules.xml 185 | # .idea/misc.xml 186 | # *.ipr 187 | 188 | # Sonarlint plugin 189 | .idea/sonarlint 190 | 191 | ### Linux ### 192 | 193 | # temporary files which can be created if a process still has a handle open of a deleted file 194 | .fuse_hidden* 195 | 196 | # KDE directory preferences 197 | .directory 198 | 199 | # Linux trash folder which might appear on any partition or disk 200 | .Trash-* 201 | 202 | # .nfs files are created when an open file is removed but is still being accessed 203 | .nfs* 204 | 205 | ### OSX ### 206 | # General 207 | .DS_Store 208 | .AppleDouble 209 | .LSOverride 210 | 211 | # Icon must end with two \r 212 | Icon 213 | 214 | # Thumbnails 215 | ._* 216 | 217 | # Files that might appear in the root of a volume 218 | .DocumentRevisions-V100 219 | .fseventsd 220 | .Spotlight-V100 221 | .TemporaryItems 222 | .Trashes 223 | .VolumeIcon.icns 224 | .com.apple.timemachine.donotpresent 225 | 226 | # Directories potentially created on remote AFP share 227 | .AppleDB 228 | .AppleDesktop 229 | Network Trash Folder 230 | Temporary Items 231 | .apdisk 232 | 233 | ### Vim ### 234 | # Swap 235 | [._]*.s[a-v][a-z] 236 | [._]*.sw[a-p] 237 | [._]s[a-rt-v][a-z] 238 | [._]ss[a-gi-z] 239 | [._]sw[a-p] 240 | 241 | # Session 242 | Session.vim 243 | 244 | # Temporary 245 | .netrwhist 246 | # Auto-generated tag files 247 | tags 248 | # Persistent undo 249 | [._]*.un~ 250 | 251 | ### VisualStudioCode ### 252 | .vscode/* 253 | !.vscode/settings.json 254 | !.vscode/tasks.json 255 | !.vscode/launch.json 256 | !.vscode/extensions.json 257 | 258 | ### Windows ### 259 | # Windows thumbnail cache files 260 | Thumbs.db 261 | ehthumbs.db 262 | ehthumbs_vista.db 263 | 264 | # Dump file 265 | *.stackdump 266 | 267 | # Folder config file 268 | [Dd]esktop.ini 269 | 270 | # Recycle Bin used on file shares 271 | $RECYCLE.BIN/ 272 | 273 | # Windows Installer files 274 | *.cab 275 | *.msi 276 | *.msix 277 | *.msm 278 | *.msp 279 | 280 | # Windows shortcuts 281 | *.lnk 282 | 283 | 284 | # End of https://www.gitignore.io/api/windows,linux,osx,vim,emacs,intellij,eclipse,visualstudiocode 285 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 | # Molecule Wrapper 2 | 3 | [![Lint](https://github.com/gantsign/molecule-wrapper/actions/workflows/ci.yml/badge.svg)](https://github.com/gantsign/molecule-wrapper/actions/workflows/ci.yml) 4 | 5 | A wrapper script (in the spirit of the 6 | [Grade Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html)) 7 | to make installing/running 8 | [Molecule](https://molecule.readthedocs.io/en/latest/configuration.html) easier. 9 | 10 | Note: this script currently only works on Linux. 11 | 12 | ## Installation 13 | 14 | In the root of your Ansible role run the following to install the wrapper: 15 | 16 | ```bash 17 | wget 'https://raw.githubusercontent.com/gantsign/molecule-wrapper/master/moleculew' -O moleculew \ 18 | && chmod 'u+x' moleculew 19 | ``` 20 | 21 | ### Tab completion and alias for Zsh 22 | 23 | To enable tab-completion support and add an alias so you can run the wrapper 24 | using `molecule` follow [these instructions](zsh/README.md). 25 | 26 | ## Caution 27 | 28 | Molecule has a lot of dependencies, some of which you'll need root privileges to 29 | install. 30 | 31 | When you run the molecule wrapper for the first time it will attempt to install 32 | the following: 33 | 34 | * [Python build dependencies](https://github.com/pyenv/pyenv/wiki/common-build-problems) 35 | * [Python](https://www.python.org/) 36 | * [Docker](https://www.docker.com) (if not already installed) 37 | * [Git](https://git-scm.com) 38 | * [curl](https://curl.haxx.se) 39 | * [jq](https://stedolan.github.io/jq/) 40 | * [pyenv](https://github.com/pyenv/pyenv) (if not already installed) 41 | * [virtualenv](https://virtualenv.pypa.io/en/stable) 42 | * [Ansible](https://www.ansible.com) (inside the virtual environment) 43 | * [Molecule](https://molecule.readthedocs.io) (inside the virtual environment) 44 | 45 | If you don't have root privileges see 46 | [using system dependencies](#using-system-dependencies). 47 | 48 | ## Usage 49 | 50 | You can run any Molecule command directly using the wrapper e.g.: 51 | 52 | ``` 53 | ./moleculew test 54 | ``` 55 | 56 | By default Molecule Wrapper runs using the latest versions of Python, 57 | Ansible, Molecule, YamlLint, Ansible Lint, Flake8 and Testinfra. 58 | 59 | You can also specify particular versions by passing command line arguments or 60 | setting environment variables: 61 | 62 | ``` 63 | Additional options: 64 | --ansible VERSION Use the specified version of Ansible 65 | --molecule VERSION Use the specified version of Molecule 66 | --python VERSION Use the specified version of Python 67 | --yamllint VERSION Use the specified version of YamlLint 68 | --ansible-lint VERSION Use the specified version of Ansible Lint 69 | --flake8 VERSION Use the specified version of Flake8 70 | --testinfra VERSION Use the specified version of Testinfra 71 | --use-system-dependencies Use system dependencies 72 | 73 | Environment variables: 74 | MOLECULEW_ANSIBLE Use the specified version of Ansible 75 | MOLECULEW_MOLECULE Use the specified version of Molecule 76 | MOLECULEW_PYTHON Use the specified version of Python 77 | MOLECULEW_YAMLLINT Use the specified version of YamlLint 78 | MOLECULEW_ANSIBLE_LINT Use the specified version of Ansible Lint 79 | MOLECULEW_FLAKE8 Use the specified version of Flake8 80 | MOLECULEW_TESTINFA Use the specified version of Testinfra 81 | MOLECULEW_USE_SYSTEM Use system dependencies (true/false) 82 | ``` 83 | 84 | The version may be a valid version number, `latest` to use the latest 85 | available version, or `default` to use the frozen version (if set) or otherwise 86 | use the latest version (i.e. the same effect as not specifying the option). 87 | 88 | The above command line arguments take preference over environment variables. 89 | 90 | e.g. 91 | 92 | ``` 93 | ./moleculew --python 3.6.6 test 94 | ``` 95 | 96 | ### Freezing versions 97 | 98 | You can freeze the dependency versions by running the following command: 99 | 100 | ``` 101 | ./moleculew wrapper-freeze 102 | ``` 103 | 104 | You can also specify versions when freezing the dependencies: 105 | 106 | ``` 107 | ./moleculew --python 3.6.6 wrapper-freeze 108 | ``` 109 | 110 | Note: any version you specify as a command line argument when running Molecule 111 | will override the frozen version of that dependency. 112 | 113 | ### Using system dependencies 114 | 115 | If you don't have root privileges but you already have the necessary 116 | dependencies installed you can uses those dependencies by specifying 117 | `--use-system-dependencies`: 118 | 119 | e.g. 120 | 121 | ``` 122 | ./moleculew --use-system-dependencies test 123 | ``` 124 | 125 | Required dependencies: 126 | * [Python build dependencies](https://github.com/pyenv/pyenv/wiki/common-build-problems) (to build Molecule and its dependencies) 127 | * [Python](https://www.python.org/) including [pip](https://pypi.org/project/pip/) 128 | * [Docker](https://www.docker.com) 129 | * [Git](https://git-scm.com) 130 | * [curl](https://curl.haxx.se) 131 | * [jq](https://stedolan.github.io/jq/) 132 | 133 | The remaining dependencies don't require root privileges and will be installed 134 | if necessary. 135 | 136 | This can also be useful for CI builds where you want to save time by using 137 | existing binaries. 138 | 139 | ## Command line reference 140 | 141 | ### wrapper-versions 142 | 143 | Displays the current dependency versions being used: 144 | 145 | ``` 146 | Options: 147 | --ansible VERSION Use the specified version of Ansible 148 | --molecule VERSION Use the specified version of Molecule 149 | --python VERSION Use the specified version of Python 150 | --yamllint VERSION Use the specified version of YamlLint 151 | --ansible-lint VERSION Use the specified version of Ansible Lint 152 | --flake8 VERSION Use the specified version of Flake8 153 | --testinfra VERSION Use the specified version of Testinfra 154 | --use-system-dependencies Use the system version of Python 155 | 156 | Environment variables: 157 | MOLECULEW_ANSIBLE Use the specified version of Ansible 158 | MOLECULEW_MOLECULE Use the specified version of Molecule 159 | MOLECULEW_PYTHON Use the specified version of Python 160 | MOLECULEW_YAMLLINT Use the specified version of YamlLint 161 | MOLECULEW_ANSIBLE_LINT Use the specified version of Ansible Lint 162 | MOLECULEW_FLAKE8 Use the specified version of Flake8 163 | MOLECULEW_TESTINFA Use the specified version of Testinfra 164 | MOLECULEW_USE_SYSTEM Use system dependencies (true/false) 165 | ``` 166 | 167 | The version may be a valid version number, `latest` to use the latest 168 | available version, or `default` to use the frozen version (if set) or otherwise 169 | display the latest version (i.e. the same effect as not specifying the option). 170 | 171 | The above command line arguments take preference over environment variables. 172 | 173 | e.g. 174 | 175 | ```bash 176 | ./moleculew wrapper-versions 177 | ``` 178 | 179 | ### wrapper-freeze 180 | 181 | Freezes the dependency versions being used: 182 | 183 | ``` 184 | Options: 185 | --ansible VERSION Use the specified version of Ansible 186 | --molecule VERSION Use the specified version of Molecule 187 | --python VERSION Use the specified version of Python 188 | --yamllint VERSION Use the specified version of YamlLint 189 | --ansible-lint VERSION Use the specified version of Ansible Lint 190 | --flake8 VERSION Use the specified version of Flake8 191 | --testinfra VERSION Use the specified version of Testinfra 192 | --use-system-dependencies Use the system version of Python 193 | 194 | Environment variables: 195 | MOLECULEW_ANSIBLE Use the specified version of Ansible 196 | MOLECULEW_MOLECULE Use the specified version of Molecule 197 | MOLECULEW_PYTHON Use the specified version of Python 198 | MOLECULEW_YAMLLINT Use the specified version of YamlLint 199 | MOLECULEW_ANSIBLE_LINT Use the specified version of Ansible Lint 200 | MOLECULEW_FLAKE8 Use the specified version of Flake8 201 | MOLECULEW_TESTINFA Use the specified version of Testinfra 202 | MOLECULEW_USE_SYSTEM Use system dependencies (true/false) 203 | ``` 204 | 205 | The version may be a valid version number, `latest` to freeze to the latest 206 | available version, or `default` to keep the frozen version (if set) or otherwise 207 | freeze at the current latest version (i.e. the same effect as not specifying 208 | the option). 209 | 210 | The above command line arguments take preference over environment variables. 211 | 212 | e.g. 213 | 214 | ```bash 215 | ./moleculew wrapper-freeze 216 | ``` 217 | 218 | To upgrade one (or more) of the versions specify the options as follows: 219 | 220 | ```bash 221 | ./moleculew --ansible latest --molecule 2.16.0 wrapper-freeze 222 | ``` 223 | 224 | ### wrapper-unfreeze 225 | 226 | Un-freezes the dependency versions: 227 | 228 | e.g. 229 | 230 | ```bash 231 | ./moleculew wrapper-unfreeze 232 | ``` 233 | 234 | ### wrapper-upgrade-versions 235 | 236 | Upgrades any frozen dependency versions to the latest version and freezes all 237 | dependency versions: 238 | 239 | e.g. 240 | 241 | ```bash 242 | ./moleculew wrapper-upgrade-versions 243 | ``` 244 | 245 | ### wrapper-clean 246 | 247 | Removes all the virtual environments created by Molecule Wrapper. The virtual 248 | environments can use up quite a bit of storage over time so you should run this 249 | periodically e.g. every few months. 250 | 251 | e.g. 252 | 253 | ```bash 254 | ./moleculew wrapper-clean 255 | ``` 256 | 257 | ### wrapper-version 258 | 259 | Displays the current version of the wrapper script. 260 | 261 | e.g. 262 | 263 | ```bash 264 | ./moleculew wrapper-version 265 | ``` 266 | 267 | ### wrapper-upgrade 268 | 269 | Upgrades the Molecule Wrapper to the latest version. 270 | 271 | e.g. 272 | 273 | ```bash 274 | ./moleculew wrapper-upgrade 275 | ``` 276 | 277 | ### wrapper-install 278 | 279 | Installs Molecule (if necessary). You don't need to use this because the other 280 | commands will run the install if they need to. This is included for CI 281 | environments where you might want to separate the console output for the install 282 | from the Molecule output. 283 | 284 | ``` 285 | Options: 286 | --ansible VERSION Use the specified version of Ansible 287 | --molecule VERSION Use the specified version of Molecule 288 | --python VERSION Use the specified version of Python 289 | --yamllint VERSION Use the specified version of YamlLint 290 | --ansible-lint VERSION Use the specified version of Ansible Lint 291 | --flake8 VERSION Use the specified version of Flake8 292 | --testinfra VERSION Use the specified version of Testinfra 293 | --use-system-dependencies Use the system version of Python 294 | 295 | Environment variables: 296 | MOLECULEW_ANSIBLE Use the specified version of Ansible 297 | MOLECULEW_MOLECULE Use the specified version of Molecule 298 | MOLECULEW_PYTHON Use the specified version of Python 299 | MOLECULEW_YAMLLINT Use the specified version of YamlLint 300 | MOLECULEW_ANSIBLE_LINT Use the specified version of Ansible Lint 301 | MOLECULEW_FLAKE8 Use the specified version of Flake8 302 | MOLECULEW_TESTINFA Use the specified version of Testinfra 303 | MOLECULEW_USE_SYSTEM Use system dependencies (true/false) 304 | ``` 305 | 306 | The version may be a valid version number, `latest` to freeze to the latest 307 | available version, or `default` to keep the frozen version (if set) or otherwise 308 | freeze at the current latest version (i.e. the same effect as not specifying 309 | the option). 310 | 311 | The above command line arguments take preference over environment variables. 312 | 313 | e.g. 314 | 315 | ```bash 316 | ./moleculew wrapper-install 317 | ``` 318 | 319 | ### wrapper-virtualenv 320 | 321 | Displays the location of the Virtualenv environment. 322 | 323 | ``` 324 | Options: 325 | --ansible VERSION Use the specified version of Ansible 326 | --molecule VERSION Use the specified version of Molecule 327 | --python VERSION Use the specified version of Python 328 | --yamllint VERSION Use the specified version of YamlLint 329 | --ansible-lint VERSION Use the specified version of Ansible Lint 330 | --flake8 VERSION Use the specified version of Flake8 331 | --testinfra VERSION Use the specified version of Testinfra 332 | --use-system-dependencies Use the system version of Python 333 | 334 | Environment variables: 335 | MOLECULEW_ANSIBLE Use the specified version of Ansible 336 | MOLECULEW_MOLECULE Use the specified version of Molecule 337 | MOLECULEW_PYTHON Use the specified version of Python 338 | MOLECULEW_YAMLLINT Use the specified version of YamlLint 339 | MOLECULEW_ANSIBLE_LINT Use the specified version of Ansible Lint 340 | MOLECULEW_FLAKE8 Use the specified version of Flake8 341 | MOLECULEW_TESTINFA Use the specified version of Testinfra 342 | MOLECULEW_USE_SYSTEM Use system dependencies (true/false) 343 | ``` 344 | 345 | The version may be a valid version number, `latest` to freeze to the latest 346 | available version, or `default` to keep the frozen version (if set) or otherwise 347 | freeze at the current latest version (i.e. the same effect as not specifying 348 | the option). 349 | 350 | The above command line arguments take preference over environment variables. 351 | 352 | e.g. 353 | 354 | ```bash 355 | ./moleculew wrapper-virtualenv 356 | ``` 357 | 358 | ## License 359 | 360 | This software is licensed under the terms in the file named "LICENSE" in the 361 | root directory of this project. This project has dependencies that are under 362 | different licenses. 363 | 364 | ## Author Information 365 | 366 | John Freeman 367 | 368 | GantSign Ltd. 369 | Company No. 06109112 (registered in England) 370 | -------------------------------------------------------------------------------- /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.1-dev 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 | -------------------------------------------------------------------------------- /next-version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ ! -x "$(command -v git)" ]]; then 6 | echo 'Error: git is not installed.' >&2 7 | exit 1 8 | fi 9 | if [[ ! -x "$(command -v hub)" ]]; then 10 | echo 'Error: hub is not installed.' >&2 11 | exit 1 12 | fi 13 | if [[ ! -x "$(command -v shellcheck)" ]]; then 14 | echo 'Error: shellcheck is not installed.' >&2 15 | exit 1 16 | fi 17 | if [[ "$(git status --untracked-files --short)" != '' ]]; then 18 | echo 'Error: you have uncommited changes' >&2 19 | exit 1 20 | fi 21 | 22 | # Update develop 23 | git checkout develop 24 | git pull 25 | 26 | shellcheck moleculew 27 | shellcheck next-version.sh 28 | 29 | WRAPPER_VERSION="$(./moleculew wrapper-version)" 30 | RELEASE_VERSION="${WRAPPER_VERSION%%-dev*}" 31 | 32 | read -r -p "Release version [$RELEASE_VERSION]? " USER_RELEASE_VERSION 33 | if [[ "$USER_RELEASE_VERSION" != '' ]]; then 34 | RELEASE_VERSION="$USER_RELEASE_VERSION" 35 | fi 36 | 37 | PATCH_VERSION="${RELEASE_VERSION##*.}" 38 | NEXT_PATCH_VERSION="$((PATCH_VERSION + 1))" 39 | NEXT_VERSION="${RELEASE_VERSION%.*}.$NEXT_PATCH_VERSION-dev" 40 | 41 | set -x 42 | 43 | # Update master branch 44 | git fetch 45 | git checkout master && git reset --hard origin/master 46 | 47 | # Return to develop 48 | git checkout develop 49 | 50 | git checkout -b "$RELEASE_VERSION-release" 51 | 52 | sed --in-place \ 53 | "s/^WRAPPER_VERSION=.*/WRAPPER_VERSION=$RELEASE_VERSION/" moleculew 54 | 55 | git add --verbose moleculew 56 | 57 | git commit --message="\ 58 | Prepare release $RELEASE_VERSION 59 | " 60 | 61 | hub pull-request --push --base=master --message="\ 62 | Release $RELEASE_VERSION 63 | " 64 | 65 | git checkout -b "$NEXT_VERSION" 66 | 67 | sed --in-place \ 68 | "s/^WRAPPER_VERSION=.*/WRAPPER_VERSION=$NEXT_VERSION/" moleculew 69 | 70 | git add --verbose moleculew 71 | 72 | git commit --message="\ 73 | Prepare for next development iteration 74 | " 75 | 76 | hub pull-request --push --base=develop --message="\ 77 | Develop $NEXT_VERSION 78 | " 79 | -------------------------------------------------------------------------------- /zsh/README.md: -------------------------------------------------------------------------------- 1 | # Zsh Support 2 | 3 | Add tab completion for Molecule Wrapper and alias `./moleculew` so you can 4 | run it using `molecule`. 5 | 6 | # Installing with [Antigen](https://github.com/zsh-users/antigen) 7 | 8 | ```bash 9 | antigen bundle gantsign/molecule-wrapper zsh 10 | ``` 11 | 12 | # Installing with [Ansible Role](https://galaxy.ansible.com/gantsign/antigen) 13 | 14 | Add the following to your Ansible playbook: 15 | 16 | ```yaml 17 | - role: gantsign.antigen 18 | users: 19 | - username: example_username 20 | antigen_bundles: 21 | - name: moleculew 22 | url: gantsign/molecule-wrapper 23 | location: zsh 24 | ``` 25 | 26 | Note: replace `example_username` with the username to install this plugin for. 27 | -------------------------------------------------------------------------------- /zsh/moleculew.plugin.zsh: -------------------------------------------------------------------------------- 1 | __moleculew_commands() { 2 | local -a commands=( 3 | 'check:Use the provisioner to perform a Dry-Run...' 4 | 'converge:Use the provisioner to configure instances...' 5 | 'create:Use the provisioner to start the instances.' 6 | 'dependency:Manage the roles dependencies.' 7 | 'destroy:Use the provisioner to destroy the instances.' 8 | 'idempotence:Use the provisioner to configure the...' 9 | 'init:Initialize a new role or scenario.' 10 | 'lint:Lint the role.' 11 | 'list:Lists status of instances.' 12 | 'login:Log in to one instance.' 13 | 'matrix:List matrix of steps used to test instances.' 14 | 'prepare:Use the provisioner to prepare the instances...' 15 | 'side-effect:Use the provisioner to perform side-effects...' 16 | 'syntax:Use the provisioner to syntax check the role.' 17 | 'test:Test (lint, destroy, dependency, syntax,...' 18 | 'verify:Run automated tests against instances.' 19 | ) 20 | 21 | if [[ -f ./moleculew ]]; then 22 | commands+=( 23 | 'wrapper-clean:remove all the wrapper virtual environments.' 24 | 'wrapper-freeze:freeze the dependency versions.' 25 | 'wrapper-install:install Molecule.' 26 | 'wrapper-unfreeze:un-freeze the dependency versions.' 27 | 'wrapper-update:update Molecule Wrapper to the latest version.' 28 | 'wrapper-upgrade-versions:upgrade dependency versions and freeze them.' 29 | 'wrapper-version:display the current version of the wrapper script.' 30 | 'wrapper-versions:display the current dependency versions being used.' 31 | 'wrapper-virtualenv:display the location of the Virtualenv environment.' 32 | ) 33 | fi 34 | 35 | _describe -t moleculew-commands "moleculew command" commands 36 | } 37 | 38 | __moleculew_init_commands() { 39 | local init_cmds=( 40 | 'role:Initialize a new role for use with Molecule.' 41 | 'scenario:Initialize a new scenario for use with...' 42 | 'template:Initialize a new role from a Cookiecutter...' 43 | ) 44 | 45 | _describe -t init_cmds "init cmd" init_cmds 46 | } 47 | 48 | __moleculew_init_subcommand() { 49 | local dep_name_arg driver_name_arg provisioner_name_arg role_name_arg verifier_name 50 | integer ret=1 51 | 52 | dep_name_arg='--dependency-name[Name of dependency to initialize. (galaxy)]:dependency:()' 53 | driver_name_arg=({-d,--driver-name}'[Name of driver to initialize. (docker)]:driver:(azure delegated docker ec2 gce lxc lxd openstack vagrant)') 54 | provisioner_name_arg='--provisioner-name[Name of provisioner to initialize. (ansible)]:provisioner:()' 55 | role_name_arg=({-r,--role-name}'[Name of the role to create. \[required\]]:role:()') 56 | verifier_name='--verifier-name[Name of verifier to initialize. (testinfra)]:verifier:(goss inspec testinfra)' 57 | 58 | case "$words[1]" in 59 | 60 | (role) 61 | _arguments \ 62 | $dep_name_arg \ 63 | $driver_name_arg \ 64 | '--lint-name[Name of lint to initialize. (yamllint)]:lint:()' \ 65 | $provisioner_name_arg \ 66 | $role_name_arg \ 67 | $verifier_name \ 68 | $help_arg && ret=0 69 | ;; 70 | 71 | (scenario) 72 | _arguments \ 73 | $dep_name_arg \ 74 | $driver_name_arg \ 75 | '--lint-name[Name of lint to initialize. (ansible-lint)]:lint:()' \ 76 | $provisioner_name_arg \ 77 | $role_name_arg \ 78 | {-s,--scenario-name}'[Name of the scenario to create. (default) \[required\]]:role name:()' \ 79 | $verifier_name \ 80 | $help_arg && ret=0 81 | ;; 82 | 83 | (template) 84 | _arguments \ 85 | '--url[URL to the Cookiecutter templates repository. \[required\]]:url:()' \ 86 | '--no-input[Do not prompt for parameters and only use cookiecutter.json for content.]' \ 87 | {-r,--role-name}'Name of the role to create.' \ 88 | $help_arg && ret=0 89 | ;; 90 | 91 | (*) 92 | _message 'Unknown init sub command' && ret=1 93 | ;; 94 | esac 95 | 96 | return ret 97 | } 98 | 99 | __moleculew_scenario_options() { 100 | if [[ -f ./moleculew ]]; then 101 | compadd -V scenario -- $(./moleculew wrapper-options-scenario) 102 | fi 103 | } 104 | 105 | __moleculew_subcommand() { 106 | local scenario_arg help_arg driver_arg host_arg 107 | 108 | scenario_arg=("($I --all)"{-s,--scenario-name}'[Name of the scenario to target. (default)]:scenario_names:__moleculew_scenario_options') 109 | help_arg='(- :)--help[Display help and exit.]' 110 | driver_arg=("($I)"{-d,--driver-name}'[Name of driver to use. (docker)]:driver:(azure delegated docker ec2 gce lxc lxd openstack vagrant)') 111 | host_arg=("($I)"{-h,--host}'[Host to access.]:host:()') 112 | 113 | integer ret=1 114 | 115 | case "$words[1]" in 116 | (check|converge|dependency|idempotence|lint|matrix|syntax|verify) 117 | _arguments \ 118 | $help_arg \ 119 | $scenario_arg && ret=0 120 | ;; 121 | 122 | (create) 123 | _arguments \ 124 | $driver_arg \ 125 | $help_arg \ 126 | $scenario_arg && ret=0 127 | ;; 128 | 129 | (destroy) 130 | _arguments \ 131 | "($I -s --scenario-name)--all[Destroy all scenarios.]" \ 132 | $driver_arg \ 133 | $help_arg \ 134 | $scenario_arg && ret=0 135 | ;; 136 | 137 | (init) 138 | _arguments -C \ 139 | $help_arg \ 140 | '(-): :->init-command' \ 141 | '(-)*:: :->init-option-or-argument' && ret=0 142 | 143 | case $state in 144 | (init-command) 145 | __moleculew_init_commands && ret=0 146 | ;; 147 | (init-option-or-argument) 148 | curcontext=${curcontext%:*:*}:moleculew-init-$words[2]: 149 | __moleculew_init_subcommand && ret=0 150 | ;; 151 | esac 152 | ;; 153 | 154 | (login) 155 | _arguments \ 156 | $help_arg \ 157 | $host_arg \ 158 | $scenario_arg && ret=0 159 | ;; 160 | 161 | (list) 162 | _arguments \ 163 | "($I)"{-f,--format}'[Change output format. (simple)]:format:(simple plain yaml)' \ 164 | $help_arg \ 165 | $scenario_arg && ret=0 166 | ;; 167 | 168 | (prepare) 169 | _arguments \ 170 | "($I)--force[Enable force mode.]" \ 171 | $driver_arg \ 172 | $help_arg \ 173 | $scenario_arg && ret=0 174 | ;; 175 | 176 | (test) 177 | _arguments \ 178 | "($I -s --scenario-name)--all[Test all scenarios.]" \ 179 | "($I)--destroy[The destroy strategy used at the conclusion of a Molecule run (always).]:destroy:(always never)" \ 180 | $driver_arg \ 181 | $help_arg \ 182 | $scenario_arg && ret=0 183 | ;; 184 | 185 | (wrapper-freeze|wrapper-install|wrapper-upgrade|wrapper-versions|wrapper-virtualenv) 186 | _arguments $wrapper_args && ret=0 187 | ;; 188 | 189 | (*) 190 | _message 'Unknown sub command' && ret=1 191 | ;; 192 | esac 193 | 194 | return ret 195 | } 196 | 197 | __moleculew_ansible_options() { 198 | compadd -V version -- $(./moleculew wrapper-options-ansible) 199 | } 200 | 201 | __moleculew_molecule_options() { 202 | compadd -V version -- $(./moleculew wrapper-options-molecule) 203 | } 204 | 205 | __moleculew_python_options() { 206 | compadd -V version -- $(./moleculew wrapper-options-python) 207 | } 208 | 209 | __moleculew_yamllint_options() { 210 | compadd -V version -- $(./moleculew wrapper-options-yamllint) 211 | } 212 | 213 | __moleculew_ansible_lint_options() { 214 | compadd -V version -- $(./moleculew wrapper-options-ansible-lint) 215 | } 216 | 217 | __moleculew_flake8_options() { 218 | compadd -V version -- $(./moleculew wrapper-options-flake8) 219 | } 220 | 221 | __moleculew_testinfra_options() { 222 | compadd -V version -- $(./moleculew wrapper-options-testinfra) 223 | } 224 | 225 | _moleculew() { 226 | if [[ ! -f ./moleculew ]] && [[ ! $commands[molecule] ]]; then 227 | _message -r 'moleculew not found' && return 1 228 | fi 229 | 230 | local context state state_descr line 231 | typeset -A opt_args 232 | integer ret=1 233 | 234 | local I wrapper_args arguments 235 | 236 | I='--help --version' 237 | 238 | wrapper_args=( 239 | "($I)--ansible[The version of Ansible to use.]:ansible_versions:__moleculew_ansible_options" 240 | "($I)--molecule[The version of Molecule to use.]:molecule_versions:__moleculew_molecule_options" 241 | "($I)--yamllint[The version of YamlLint to use.]:yamllint_versions:__moleculew_yamllint_options" 242 | "($I)--ansible-lint[The version of Ansible Lint to use.]:ansible_lint_versions:__moleculew_ansible_lint_options" 243 | "($I)--flake8[The version of Flake8 to use.]:flake8_versions:__moleculew_flake8_options" 244 | "($I)--testinfra[The version of Testinfra to use.]:testinfra_versions:__moleculew_testinfra_options" 245 | "($I --use-system-dependencies)--python[The version of Python to use.]:python_versions:__moleculew_python_options" 246 | "($I --python)--use-system-dependencies[Use system dependencies.]" 247 | ) 248 | 249 | arguments=( 250 | "($I)"{-c,--base-config}'[Path to a base config. If provided Molecule will load this config first, and deep merge each scenarios molecule.yml on top. (/home/vagrant/.config/molecule/config.yml)]:base config:_files' 251 | "($I)--debug[Enable debug mode.]" 252 | "($I)"{-e,--env-file}'[The file to read variables from when rendering molecule.yml. (.env.yml)]:env file:_files' 253 | '(- :)--help[Display help for the Molecule Wrapper and exit.]' 254 | '(- :)--version[Show the version and exit.]' 255 | '(-): :->command' 256 | '(-)*:: :->option-or-argument' 257 | ) 258 | 259 | if [[ -f ./moleculew ]]; then 260 | arguments+=($wrapper_args) 261 | fi 262 | 263 | _arguments -C $arguments && ret=0 264 | 265 | 266 | case $state in 267 | (command) 268 | __moleculew_commands && ret=0 269 | ;; 270 | (option-or-argument) 271 | curcontext=${curcontext%:*:*}:moleculew-$words[1]: 272 | __moleculew_subcommand && ret=0 273 | ;; 274 | esac 275 | 276 | return ret 277 | } 278 | 279 | _molecule_or_moleculew() { 280 | if [[ -f ./moleculew ]] ; then 281 | ./moleculew "$@" 282 | else 283 | moleculew "$@" 284 | fi 285 | } 286 | 287 | compdef _moleculew _molecule_or_moleculew 288 | 289 | alias molecule=_molecule_or_moleculew 290 | --------------------------------------------------------------------------------