2012-08-15 13:04:17 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
|
|
|
#
|
|
|
|
# This file is part of Ansible
|
|
|
|
#
|
|
|
|
# Ansible is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# Ansible is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2012-09-29 14:28:55 +00:00
|
|
|
DOCUMENTATION = '''
|
|
|
|
---
|
|
|
|
module: subversion
|
|
|
|
short_description: Deploys a subversion repository.
|
|
|
|
description:
|
2012-12-11 02:34:26 +00:00
|
|
|
- Deploy given repository URL / revision to dest.
|
2012-09-29 14:28:55 +00:00
|
|
|
version_added: "0.7"
|
2012-12-11 02:34:26 +00:00
|
|
|
author: Dane Summers, njharman@gmail.com
|
|
|
|
notes:
|
|
|
|
- Requres I(svn) to be installed on the client.
|
|
|
|
requirements: []
|
2012-09-29 14:28:55 +00:00
|
|
|
options:
|
|
|
|
repo:
|
|
|
|
description:
|
|
|
|
- The subversion URL to the repository.
|
|
|
|
required: true
|
|
|
|
default: null
|
|
|
|
dest:
|
|
|
|
description:
|
|
|
|
- Absolute path where the repository should be deployed.
|
|
|
|
required: true
|
|
|
|
default: null
|
2012-12-11 02:34:26 +00:00
|
|
|
revision:
|
|
|
|
description:
|
|
|
|
- Specific revision to checkout.
|
|
|
|
required: false
|
|
|
|
default: HEAD
|
2012-09-29 14:28:55 +00:00
|
|
|
force:
|
|
|
|
description:
|
2012-12-11 02:34:26 +00:00
|
|
|
- If C(yes), modified files will be discarded. If C(no), module will fail if it encounters modified files.
|
2012-09-29 14:28:55 +00:00
|
|
|
required: false
|
2013-03-12 12:18:12 +00:00
|
|
|
default: "yes"
|
2012-09-29 14:28:55 +00:00
|
|
|
choices: [ "yes", "no" ]
|
2012-12-11 02:34:26 +00:00
|
|
|
username:
|
|
|
|
description:
|
|
|
|
- --username parameter passed to svn.
|
|
|
|
required: false
|
|
|
|
default: null
|
|
|
|
password:
|
|
|
|
description:
|
|
|
|
- --password parameter passed to svn.
|
|
|
|
required: false
|
|
|
|
default: null
|
2012-09-29 14:28:55 +00:00
|
|
|
examples:
|
2012-10-23 13:14:01 +00:00
|
|
|
- code: "subversion: repo=svn+ssh://an.example.org/path/to/repo dest=/src/checkout"
|
2012-12-11 02:34:26 +00:00
|
|
|
description: Checkout subversion repository to specified folder.
|
2012-09-29 14:28:55 +00:00
|
|
|
'''
|
2012-08-15 13:04:17 +00:00
|
|
|
|
|
|
|
import re
|
|
|
|
|
2012-12-11 02:34:26 +00:00
|
|
|
|
|
|
|
class Subversion(object):
|
2012-12-12 22:55:30 +00:00
|
|
|
def __init__(self, module, dest, repo, revision, username, password):
|
|
|
|
self.module = module
|
2012-12-11 02:34:26 +00:00
|
|
|
self.dest = dest
|
|
|
|
self.repo = repo
|
|
|
|
self.revision = revision
|
|
|
|
self.username = username
|
|
|
|
self.password = password
|
|
|
|
|
|
|
|
def _exec(self, args):
|
2013-02-27 01:23:12 +00:00
|
|
|
bits = ["svn --non-interactive --trust-server-cert --no-auth-cache", ]
|
2012-12-11 02:34:26 +00:00
|
|
|
if self.username:
|
|
|
|
bits.append("--username '%s'" % self.username)
|
|
|
|
if self.password:
|
|
|
|
bits.append("--password '%s'" % self.password)
|
|
|
|
bits.append(args)
|
2013-01-13 17:12:40 +00:00
|
|
|
rc, out, err = self.module.run_command(' '.join(bits), check_rc=True)
|
2012-12-11 02:34:26 +00:00
|
|
|
return out.splitlines()
|
|
|
|
|
|
|
|
def checkout(self):
|
|
|
|
'''Creates new svn working directory if it does not already exist.'''
|
|
|
|
self._exec("checkout -r %s '%s' '%s'" % (self.revision, self.repo, self.dest))
|
|
|
|
|
|
|
|
def switch(self):
|
|
|
|
'''Change working directory's repo.'''
|
|
|
|
# switch to ensure we are pointing at correct repo.
|
|
|
|
self._exec("switch '%s' '%s'" % (self.repo, self.dest))
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
'''Update existing svn working directory.'''
|
|
|
|
self._exec("update -r %s '%s'" % (self.revision, self.dest))
|
|
|
|
|
|
|
|
def revert(self):
|
|
|
|
'''Revert svn working directory.'''
|
|
|
|
self._exec("revert -R '%s'" % self.dest)
|
|
|
|
|
|
|
|
def get_revision(self):
|
|
|
|
'''Revision and URL of subversion working directory.'''
|
|
|
|
text = '\n'.join(self._exec("info '%s'" % self.dest))
|
|
|
|
rev = re.search(r'^Revision:.*$', text, re.MULTILINE).group(0)
|
|
|
|
url = re.search(r'^URL:.*$', text, re.MULTILINE).group(0)
|
|
|
|
return rev, url
|
|
|
|
|
|
|
|
def has_local_mods(self):
|
|
|
|
'''True if revisioned files have been added or modified. Unrevisioned files are ignored.'''
|
|
|
|
lines = self._exec("status '%s'" % self.dest)
|
|
|
|
# Match only revisioned files, i.e. ignore status '?'.
|
|
|
|
regex = re.compile(r'^[^?]')
|
|
|
|
# Has local mods if more than 0 modifed revisioned files.
|
|
|
|
return len(filter(regex.match, lines)) > 0
|
|
|
|
|
2013-03-06 18:23:35 +00:00
|
|
|
def needs_update(self):
|
|
|
|
curr, url = self.get_revision()
|
|
|
|
out2 = '\n'.join(self._exec("info -r HEAD '%s'" % self.dest))
|
|
|
|
head = re.search(r'^Revision:.*$', out2, re.MULTILINE).group(0)
|
|
|
|
rev1 = int(curr.split(':')[1].strip())
|
|
|
|
rev2 = int(head.split(':')[1].strip())
|
|
|
|
change = False
|
|
|
|
if rev1 < rev2:
|
|
|
|
change = True
|
|
|
|
return change, curr, head
|
|
|
|
|
2012-08-15 13:04:17 +00:00
|
|
|
|
|
|
|
# ===========================================
|
|
|
|
|
|
|
|
def main():
|
|
|
|
module = AnsibleModule(
|
2012-12-11 02:34:26 +00:00
|
|
|
argument_spec=dict(
|
2012-08-15 13:04:17 +00:00
|
|
|
dest=dict(required=True),
|
2012-12-11 02:34:26 +00:00
|
|
|
repo=dict(required=True, aliases=['name', 'repository']),
|
|
|
|
revision=dict(default='HEAD', aliases=['rev']),
|
2013-02-23 21:56:45 +00:00
|
|
|
force=dict(default='yes', type='bool'),
|
2012-12-11 02:34:26 +00:00
|
|
|
username=dict(required=False),
|
|
|
|
password=dict(required=False),
|
2013-02-27 20:23:35 +00:00
|
|
|
),
|
|
|
|
supports_check_mode=True
|
2012-08-15 13:04:17 +00:00
|
|
|
)
|
|
|
|
|
2012-12-11 02:34:26 +00:00
|
|
|
dest = os.path.expanduser(module.params['dest'])
|
|
|
|
repo = module.params['repo']
|
2012-08-15 13:04:17 +00:00
|
|
|
revision = module.params['revision']
|
2013-02-23 18:59:52 +00:00
|
|
|
force = module.params['force']
|
2012-12-11 02:34:26 +00:00
|
|
|
username = module.params['username']
|
|
|
|
password = module.params['password']
|
|
|
|
|
2012-12-12 22:55:30 +00:00
|
|
|
svn = Subversion(module, dest, repo, revision, username, password)
|
2012-12-11 02:34:26 +00:00
|
|
|
|
|
|
|
if not os.path.exists(dest):
|
|
|
|
before = None
|
|
|
|
local_mods = False
|
2013-02-27 20:23:35 +00:00
|
|
|
if module.check_mode:
|
|
|
|
module.exit_json(changed=True)
|
2012-12-11 02:34:26 +00:00
|
|
|
svn.checkout()
|
|
|
|
elif os.path.exists("%s/.svn" % (dest, )):
|
|
|
|
# Order matters. Need to get local mods before switch to avoid false
|
|
|
|
# positives. Need to switch before revert to ensure we are reverting to
|
|
|
|
# correct repo.
|
2013-02-27 20:23:35 +00:00
|
|
|
if module.check_mode:
|
2013-03-06 18:23:35 +00:00
|
|
|
check, before, after = svn.needs_update()
|
|
|
|
module.exit_json(changed=check, before=before, after=after)
|
2012-12-11 02:34:26 +00:00
|
|
|
before = svn.get_revision()
|
|
|
|
local_mods = svn.has_local_mods()
|
|
|
|
svn.switch()
|
|
|
|
if local_mods:
|
|
|
|
if force:
|
|
|
|
svn.revert()
|
|
|
|
else:
|
|
|
|
module.fail_json(msg="ERROR: modified files exist in the repository.")
|
|
|
|
svn.update()
|
2012-08-15 13:04:17 +00:00
|
|
|
else:
|
2012-12-11 02:34:26 +00:00
|
|
|
module.fail_json(msg="ERROR: %s folder already exists, but its not a subversion repository." % (dest, ))
|
2012-08-15 13:04:17 +00:00
|
|
|
|
2012-12-11 02:34:26 +00:00
|
|
|
after = svn.get_revision()
|
|
|
|
changed = before != after or local_mods
|
2012-08-23 03:06:03 +00:00
|
|
|
module.exit_json(changed=changed, before=before, after=after)
|
2012-08-15 13:04:17 +00:00
|
|
|
|
|
|
|
# include magic from lib/ansible/module_common.py
|
|
|
|
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
|
|
|
|
main()
|