├── README.md └── log_vcs.py /README.md: -------------------------------------------------------------------------------- 1 | # log_vcs - create (git) branch for every ansible run 2 | 3 | This callback plugin creates a VCS branch every time you run Ansible. If you ever need to go back to a certain state or environment, check out that branch and be sure nothing has changed. 4 | 5 | This is useful when you have multiple environments or multiple people deploying and continually develop your Ansible. When you often deploy to test / acceptance and less often to production, you can checkout the last branch that deployed to production if a hotfix or other maintenance is required, without having to search back in your commits and logs. I would recommend to develop infrastructure features in feature branches and have the master branch always deployable to production. However, reality learns that that is not always the case and this is a nice automatic way to have a fallback. 6 | 7 | ## Requirements and installation 8 | 9 | The plugin requires 'GitPython'. On Ubuntu this can be installed with the package manager: 10 | 11 | apt-get install python-git python3-git 12 | 13 | The plugin requires the Ansible folder to be a git repository. If you have a seperate 'roles' directory, that is not included in this plugin. 14 | 15 | More infor can be [found here](https://raymii.org/s/software/log_vcs_-_Ansible_Callback_plugin_that_creates_git_branches_for_every_ansible_run.html) 16 | 17 | To install the plugin, create a folder in your Ansible folder: 18 | 19 | mkdir -p plugins/callbacks 20 | 21 | Place the file in there and edit your `ansible.cfg` file: 22 | 23 | [defaults] 24 | callback_whitelist = log_vcs 25 | callback_plugins = plugins/callbacks 26 | 27 | ## Environments 28 | 29 | If you have multiple environments (multiple inventories) then every inventory needs a `group_var` (in `group_vars/all.yml`) named `environment`. The plugin uses this in the branch name. In my case it can be `dev`, `tst`, `acc`, `prd` or `mgmt`. It is not required, if it is not found the plugin will substitute it with `env`. 30 | 31 | ## Branch name format 32 | 33 | The branch name format is: 34 | 35 | auto-$year$month$dayT$hour$minute-$env-$branch-$username-$playbook-filename 36 | 37 | For example: 38 | 39 | auto-20180710T100719-env-master-remy-nginx-vps-for-raymii.org.yml 40 | 41 | or: 42 | 43 | auto-20180709T161235-tst-refactor-for-odoo-remy-odoo.yml 44 | auto-20180710T091419-prd-refactor-for-odoo-remy-ping.yml 45 | 46 | 47 | ## Auto-commit or cleanup? 48 | 49 | There is no auto-commit or auto-push to a git server. In my use-case deployment is always done from a management machine, otherwise you have to extend the plugin to do auto-commit and push. I decided in my case it would not be useful. 50 | 51 | Auto-cleanup is also not implemented. We have bash for that: 52 | 53 | git branch | grep 'auto-' | xargs -L 1 -I % git branch -d % -------------------------------------------------------------------------------- /log_vcs.py: -------------------------------------------------------------------------------- 1 | # (C) 2018 Remy van Elst, https://raymii.org 2 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 | 4 | from __future__ import (absolute_import, division, print_function) 5 | __metaclass__ = type 6 | 7 | DOCUMENTATION = ''' 8 | callback: log_vcs 9 | type: notification 10 | short_description: create VCS entry for playbook run 11 | version_added: historical 12 | description: 13 | - This plugin creates a VCS entry for playbook runs. 14 | - "For example, a git branch for every execution." 15 | - "This way the exact state can be 're-played'. " 16 | - "Usefull when working with multiple environments (DTAP) and multiple people," 17 | - "where when you are developing your Ansible, you sometimes need to go back" 18 | - "to a previous point in time and re-run the exact state." 19 | - "This pluging creates a git-branch for every run, then goes back to the active branch." 20 | - "TODO: Implement other VCS systems then git." 21 | requirements: 22 | - Ansible folder must be git repository 23 | - Gitpython 24 | ''' 25 | 26 | import os 27 | import pwd 28 | import sys 29 | import time 30 | from collections import MutableMapping 31 | from ansible.plugins.callback import CallbackBase 32 | 33 | # TODO: Implement SVN 34 | try: 35 | from git import Repo 36 | except ImportError as e: 37 | print("Please install gitpython.") 38 | raise e 39 | 40 | class CallbackModule(CallbackBase): 41 | """ 42 | Creates VCS entry for every execution. 43 | Git only for now, git branch 44 | """ 45 | CALLBACK_VERSION = 2.0 46 | CALLBACK_TYPE = 'version_history' 47 | CALLBACK_NAME = 'log_vcs' 48 | CALLBACK_NEEDS_WHITELIST = True 49 | TIME_FORMAT = "%Y%m%dT%H%M%S" 50 | 51 | def __init__(self): 52 | super(CallbackModule, self).__init__() 53 | self.play = None 54 | if not os.path.exists(os.getcwd() + "/.git/"): 55 | raise Exception(os.getcwd() + " is not a git repository.") 56 | sys.exit(1) 57 | 58 | def create_git_branch(self,env_name="env", playbook_filename="playbook.yml"): 59 | repo = Repo(os.getcwd()) 60 | active_branch = str(repo.active_branch) 61 | current_datetime = str(time.strftime(self.TIME_FORMAT, time.localtime())) 62 | current_user = str(pwd.getpwuid(os.geteuid()).pw_name) 63 | BRANCH_NAME = ("auto-" + current_datetime + "-" + str(env_name) + 64 | "-" + active_branch + "-" + current_user + "-" + 65 | str(playbook_filename)) 66 | repo.git.checkout('-b', BRANCH_NAME) 67 | self._display.display("Created branch " + BRANCH_NAME) 68 | repo.git.checkout(active_branch) 69 | 70 | def playbook_on_play_start(self, play): 71 | self.v2_playbook_on_play_start(play) 72 | 73 | def v2_playbook_on_play_start(self, play): 74 | self.play = play 75 | # every environment has an env var named "environment" 76 | # with for example "tst", "acc", "prd" 77 | playbook_path = list(self.play._loader._FILE_CACHE)[0] 78 | playbook_filename = os.path.basename(playbook_path) 79 | first_hostname=str(list(self.play._variable_manager._hostvars)[0]) 80 | try: 81 | env_name=self.play._variable_manager._hostvars[first_hostname]["environment"] 82 | except Exception as e: 83 | env_name="env" 84 | self.create_git_branch(env_name=env_name, playbook_filename=playbook_filename) 85 | --------------------------------------------------------------------------------