mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Subverion module improvements
- Added username, password arguments. - Documented existing revision argument. - Corrected documentation/docstrings; removed git references, use svn nomenclature, etc. - Refactored duplicate code, redundant shell calls, filter abuse, inconsistent formating, etc. - Shell quoting so it doesn't break for one guy who has spaces in pathnames. - svn called with '--non-interactive' and '--no-auth-cache'.
This commit is contained in:
parent
959a461080
commit
2e6999dab9
1 changed files with 105 additions and 106 deletions
|
@ -23,8 +23,12 @@ DOCUMENTATION = '''
|
||||||
module: subversion
|
module: subversion
|
||||||
short_description: Deploys a subversion repository.
|
short_description: Deploys a subversion repository.
|
||||||
description:
|
description:
|
||||||
- This module is really simple, it checks out from the given branch of a repo or at a particular tag.
|
- Deploy given repository URL / revision to dest.
|
||||||
version_added: "0.7"
|
version_added: "0.7"
|
||||||
|
author: Dane Summers, njharman@gmail.com
|
||||||
|
notes:
|
||||||
|
- Requres I(svn) to be installed on the client.
|
||||||
|
requirements: []
|
||||||
options:
|
options:
|
||||||
repo:
|
repo:
|
||||||
description:
|
description:
|
||||||
|
@ -36,141 +40,136 @@ options:
|
||||||
- Absolute path where the repository should be deployed.
|
- Absolute path where the repository should be deployed.
|
||||||
required: true
|
required: true
|
||||||
default: null
|
default: null
|
||||||
|
revision:
|
||||||
|
description:
|
||||||
|
- Specific revision to checkout.
|
||||||
|
required: false
|
||||||
|
default: HEAD
|
||||||
force:
|
force:
|
||||||
description:
|
description:
|
||||||
- If C(yes), any modified files in the working repository will be discarded. If C(no), this module will fail if it encounters modified files.
|
- If C(yes), modified files will be discarded. If C(no), module will fail if it encounters modified files.
|
||||||
required: false
|
required: false
|
||||||
default: yes
|
default: yes
|
||||||
choices: [ "yes", "no" ]
|
choices: [ "yes", "no" ]
|
||||||
|
username:
|
||||||
|
description:
|
||||||
|
- --username parameter passed to svn.
|
||||||
|
required: false
|
||||||
|
default: null
|
||||||
|
password:
|
||||||
|
description:
|
||||||
|
- --password parameter passed to svn.
|
||||||
|
required: false
|
||||||
|
default: null
|
||||||
examples:
|
examples:
|
||||||
- code: "subversion: repo=svn+ssh://an.example.org/path/to/repo dest=/src/checkout"
|
- code: "subversion: repo=svn+ssh://an.example.org/path/to/repo dest=/src/checkout"
|
||||||
description: Export subversion repository in a specified folder
|
description: Checkout subversion repository to specified folder.
|
||||||
notes:
|
|
||||||
- Requires I(subversion) and I(grep) on the client.
|
|
||||||
requirements: [ ]
|
|
||||||
author: Dane Summers
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
def get_version(dest):
|
|
||||||
''' samples the version of the git repo '''
|
|
||||||
os.chdir(dest)
|
|
||||||
cmd = "svn info"
|
|
||||||
revision = filter(lambda l: re.search('Revision',l) != None,os.popen(cmd).read().splitlines())
|
|
||||||
url = filter(lambda l: re.search('^URL',l) != None,os.popen(cmd).read().splitlines())
|
|
||||||
return [revision[0],url[0]]
|
|
||||||
|
|
||||||
def checkout(repo, dest):
|
class Subversion(object):
|
||||||
''' makes a new svn repo if it does not already exist '''
|
def __init__(self, fail_callback, dest, repo, revision, username, password):
|
||||||
cmd = "svn co %s %s" % (repo, dest)
|
self.fail_callback = fail_callback
|
||||||
cmd = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
self.dest = dest
|
||||||
(out, err) = cmd.communicate()
|
self.repo = repo
|
||||||
rc = cmd.returncode
|
self.revision = revision
|
||||||
return (rc, out, err)
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
|
||||||
def switch(repo, dest):
|
def _exec(self, args):
|
||||||
''' makes a new svn repo if it does not already exist '''
|
bits = ["svn --non-interactive --no-auth-cache", ]
|
||||||
cmd = "svn sw %s %s" % (repo, dest)
|
if self.username:
|
||||||
cmd = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
bits.append("--username '%s'" % self.username)
|
||||||
(out, err) = cmd.communicate()
|
if self.password:
|
||||||
rc = cmd.returncode
|
bits.append("--password '%s'" % self.password)
|
||||||
return (rc, out, err)
|
bits.append(args)
|
||||||
|
cmd = subprocess.Popen(' '.join(bits), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
out, err = cmd.communicate()
|
||||||
|
rc = cmd.returncode
|
||||||
|
if rc != 0:
|
||||||
|
self.fail_callback(msg=err)
|
||||||
|
return out.splitlines()
|
||||||
|
|
||||||
def has_local_mods(dest):
|
def checkout(self):
|
||||||
os.chdir(dest)
|
'''Creates new svn working directory if it does not already exist.'''
|
||||||
cmd = "svn status"
|
self._exec("checkout -r %s '%s' '%s'" % (self.revision, self.repo, self.dest))
|
||||||
lines = os.popen(cmd).read().splitlines()
|
|
||||||
filtered = filter(lambda c: re.search('^\\?.*$',c) == None,lines)
|
|
||||||
return len(filtered) > 0
|
|
||||||
|
|
||||||
def reset(dest,force):
|
def switch(self):
|
||||||
'''
|
'''Change working directory's repo.'''
|
||||||
Reset the repo:
|
# switch to ensure we are pointing at correct repo.
|
||||||
force: if true, then remove any local modifications. Else, fail if there are local modifications
|
self._exec("switch '%s' '%s'" % (self.repo, self.dest))
|
||||||
'''
|
|
||||||
if has_local_mods(dest):
|
def update(self):
|
||||||
if force:
|
'''Update existing svn working directory.'''
|
||||||
cmd = "svn revert -R ."
|
self._exec("update -r %s '%s'" % (self.revision, self.dest))
|
||||||
cmd = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
(out, err) = cmd.communicate()
|
def revert(self):
|
||||||
rc = cmd.returncode
|
'''Revert svn working directory.'''
|
||||||
return (rc, out, err)
|
self._exec("revert -R '%s'" % self.dest)
|
||||||
else:
|
|
||||||
return (-1,"ERROR: modified files exist in the repository.","")
|
def get_revision(self):
|
||||||
return (0,"","")
|
'''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
|
||||||
|
|
||||||
def update(module, dest, version):
|
|
||||||
''' update an existing svn repo '''
|
|
||||||
os.chdir(dest)
|
|
||||||
cmd = ''
|
|
||||||
if version != 'HEAD':
|
|
||||||
cmd = "svn up -r %s" % version
|
|
||||||
else:
|
|
||||||
cmd = "svn up"
|
|
||||||
cmd = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
(out, err) = cmd.communicate()
|
|
||||||
rc = cmd.returncode
|
|
||||||
return (rc, out, err)
|
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec = dict(
|
argument_spec=dict(
|
||||||
dest=dict(required=True),
|
dest=dict(required=True),
|
||||||
repo=dict(required=True, aliases=['name']),
|
repo=dict(required=True, aliases=['name', 'repository']),
|
||||||
revision=dict(default='HEAD'),
|
revision=dict(default='HEAD', aliases=['rev']),
|
||||||
force=dict(default='yes', choices=['yes', 'no'], aliases=['force'])
|
force=dict(default='yes', choices=['yes', 'no']),
|
||||||
|
username=dict(required=False),
|
||||||
|
password=dict(required=False),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
dest = os.path.expanduser(module.params['dest'])
|
dest = os.path.expanduser(module.params['dest'])
|
||||||
repo = module.params['repo']
|
repo = module.params['repo']
|
||||||
revision = module.params['revision']
|
revision = module.params['revision']
|
||||||
force = module.boolean(module.params['force'])
|
force = module.boolean(module.params['force'])
|
||||||
|
username = module.params['username']
|
||||||
|
password = module.params['password']
|
||||||
|
|
||||||
rc, out, err, status = (0, None, None, None)
|
svn = Subversion(module.fail_json, dest, repo, revision, username, password)
|
||||||
|
|
||||||
# if there is no .svn folder, do a checkout
|
if not os.path.exists(dest):
|
||||||
# else update.
|
before = None
|
||||||
before = None
|
local_mods = False
|
||||||
local_mods = False
|
svn.checkout()
|
||||||
if not os.path.exists("%s/.svn" % (dest)):
|
elif os.path.exists("%s/.svn" % (dest, )):
|
||||||
if os.path.exists(dest):
|
# Order matters. Need to get local mods before switch to avoid false
|
||||||
module.fail_json(msg="%s folder already exists, but its not a subversion repository." % (dest))
|
# positives. Need to switch before revert to ensure we are reverting to
|
||||||
else:
|
# correct repo.
|
||||||
(rc, out, err) = checkout(repo, dest)
|
before = svn.get_revision()
|
||||||
if rc != 0:
|
local_mods = svn.has_local_mods()
|
||||||
module.fail_json(msg=err)
|
svn.switch()
|
||||||
|
if local_mods:
|
||||||
|
if force:
|
||||||
|
svn.revert()
|
||||||
|
else:
|
||||||
|
module.fail_json(msg="ERROR: modified files exist in the repository.")
|
||||||
|
svn.update()
|
||||||
else:
|
else:
|
||||||
local_mods = has_local_mods(dest)
|
module.fail_json(msg="ERROR: %s folder already exists, but its not a subversion repository." % (dest, ))
|
||||||
# else do an update
|
|
||||||
before = get_version(dest)
|
|
||||||
(rc, out, err) = reset(dest,force)
|
|
||||||
if rc != 0:
|
|
||||||
module.fail_json(msg=err)
|
|
||||||
(rc, out, err) = switch(repo, dest)
|
|
||||||
if rc != 0:
|
|
||||||
module.fail_json(msg=err)
|
|
||||||
|
|
||||||
# handle errors from switch or pull
|
|
||||||
if err.find('ERROR') != -1:
|
|
||||||
module.fail_json(msg=err)
|
|
||||||
|
|
||||||
# switch to version specified regardless of whether
|
|
||||||
# we cloned or pulled
|
|
||||||
(rc, out, err) = update(module, dest, revision)
|
|
||||||
if rc != 0:
|
|
||||||
module.fail_json(msg=err)
|
|
||||||
|
|
||||||
# determine if we changed anything
|
|
||||||
after = get_version(dest)
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
if before != after or local_mods:
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
|
after = svn.get_revision()
|
||||||
|
changed = before != after or local_mods
|
||||||
module.exit_json(changed=changed, before=before, after=after)
|
module.exit_json(changed=changed, before=before, after=after)
|
||||||
|
|
||||||
# include magic from lib/ansible/module_common.py
|
# include magic from lib/ansible/module_common.py
|
||||||
|
|
Loading…
Reference in a new issue