Allow modules to be categorized, and also sort them when generating the documentation.

This commit is contained in:
Michael DeHaan
2013-04-28 15:03:45 -04:00
parent f46bdb6343
commit 391fb98ee2
87 changed files with 118 additions and 67 deletions

174
library/source_control/bzr Normal file
View File

@@ -0,0 +1,174 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, André Paramés <git@andreparames.com>
# Based on the Git module by 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/>.
DOCUMENTATION = u'''
---
module: bzr
author: André Paramés
version_added: "1.1"
short_description: Deploy software (or files) from bzr branches
description:
- Manage I(bzr) branches to deploy files or software.
options:
name:
required: true
aliases: [ 'parent' ]
description:
- SSH or HTTP protocol address of the parent branch.
dest:
required: true
description:
- Absolute path of where the branch should be cloned to.
version:
required: false
default: "head"
description:
- What version of the branch to clone. This can be the
bzr revno or revid.
force:
required: false
default: "yes"
choices: [ yes, no ]
description:
- If C(yes), any modified files in the working
tree will be discarded.
examples:
- code: "bzr name=bzr+ssh://foosball.example.org/path/to/branch dest=/srv/checkout version=22"
description: Example bzr checkout from Ansible Playbooks
'''
import re
import tempfile
def get_version(dest):
''' samples the version of the bzr branch'''
os.chdir(dest)
cmd = "bzr revno"
revno = os.popen(cmd).read().strip()
return revno
def clone(module, parent, dest, version):
''' makes a new bzr branch if it does not already exist '''
dest_dirname = os.path.dirname(dest)
try:
os.makedirs(dest_dirname)
except:
pass
os.chdir(dest_dirname)
if version.lower() != 'head':
cmd = "bzr branch -r %s %s %s" % (version, parent, dest)
else:
cmd = "bzr branch %s %s" % (parent, dest)
return module.run_command(cmd, check_rc=True)
def has_local_mods(dest):
os.chdir(dest)
cmd = "bzr status -S"
lines = os.popen(cmd).read().splitlines()
lines = filter(lambda c: not re.search('^\\?\\?.*$', c), lines)
return len(lines) > 0
def reset(module,dest,force):
'''
Resets the index and working tree to head.
Discards any changes to tracked files in the working
tree since that commit.
'''
os.chdir(dest)
if not force and has_local_mods(dest):
module.fail_json(msg="Local modifications exist in branch (force=no).")
return module.run_command("bzr revert", check_rc=True)
def fetch(module, dest, version):
''' updates branch from remote sources '''
os.chdir(dest)
if version.lower() != 'head':
(rc, out, err) = module.run_command("bzr pull -r %s" % version)
else:
(rc, out, err) = module.run_command("bzr pull")
if rc != 0:
module.fail_json(msg="Failed to pull")
return (rc, out, err)
def switch_version(module, dest, version):
''' once pulled, switch to a particular revno or revid'''
os.chdir(dest)
cmd = ''
if version.lower() != 'head':
cmd = "bzr revert -r %s" % version
else:
cmd = "bzr revert"
return module.run_command(cmd, check_rc=True)
# ===========================================
def main():
module = AnsibleModule(
argument_spec = dict(
dest=dict(required=True),
name=dict(required=True, aliases=['parent']),
version=dict(default='head'),
force=dict(default='yes', type='bool')
)
)
dest = os.path.abspath(os.path.expanduser(module.params['dest']))
parent = module.params['name']
version = module.params['version']
force = module.params['force']
bzrconfig = os.path.join(dest, '.bzr', 'branch', 'branch.conf')
rc, out, err, status = (0, None, None, None)
# if there is no bzr configuration, do a branch operation
# else pull and switch the version
before = None
local_mods = False
if not os.path.exists(bzrconfig):
(rc, out, err) = clone(module, parent, dest, version)
else:
# else do a pull
local_mods = has_local_mods(dest)
before = get_version(dest)
(rc, out, err) = reset(module, dest, force)
if rc != 0:
module.fail_json(msg=err)
(rc, out, err) = fetch(module, dest, version)
if rc != 0:
module.fail_json(msg=err)
# switch to version specified regardless of whether
# we cloned or pulled
(rc, out, err) = switch_version(module, dest, version)
# determine if we changed anything
after = get_version(dest)
changed = False
if before != after or local_mods:
changed = True
module.exit_json(changed=changed, before=before, after=after)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

338
library/source_control/git Normal file
View File

@@ -0,0 +1,338 @@
#!/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/>.
DOCUMENTATION = '''
---
module: git
author: Michael DeHaan
version_added: "0.0.1"
short_description: Deploy software (or files) from git checkouts
description:
- Manage I(git) checkouts of repositories to deploy files or software.
options:
repo:
required: true
aliases: [ name ]
description:
- git, SSH, or HTTP protocol address of the git repository.
dest:
required: true
description:
- Absolute path of where the repository should be checked out to.
version:
required: false
default: "HEAD"
description:
- What version of the repository to check out. This can be the
git I(SHA), the literal string C(HEAD), a branch name, or a tag name.
remote:
required: false
default: "origin"
description:
- Name of the remote.
force:
required: false
default: "yes"
choices: [ "yes", "no" ]
version_added: "0.7"
description:
- If C(yes), any modified files in the working
repository will be discarded. Prior to 0.7, this was always
'yes' and could not be disabled.
depth:
required: false
default: null
version_added: "1.2"
description:
- Create a shallow clone with a history truncated to the specified
number or revisions. The minimum possible value is C(1), otherwise
ignored.
examples:
- code: "git: repo=git://foosball.example.org/path/to/repo.git dest=/srv/checkout version=release-0.22"
description: Example git checkout from Ansible Playbooks
- code: "git: repo=ssh://git@github.com/mylogin/hello.git dest=/home/mylogin/hello"
description: Example read-write git checkout from github
'''
import re
import tempfile
def get_version(dest):
''' samples the version of the git repo '''
os.chdir(dest)
cmd = "git show --abbrev-commit"
sha = os.popen(cmd).read().split("\n")
sha = sha[0].split()[1]
return sha
def clone(module, repo, dest, remote, depth):
''' makes a new git repo if it does not already exist '''
dest_dirname = os.path.dirname(dest)
try:
os.makedirs(dest_dirname)
except:
pass
os.chdir(dest_dirname)
cmd = [ module.get_bin_path('git', True), 'clone', '-o', remote ]
if depth:
cmd.extend([ '--depth', str(depth) ])
cmd.extend([ repo, dest ])
return module.run_command(cmd, check_rc=True)
def has_local_mods(dest):
os.chdir(dest)
cmd = "git status -s"
lines = os.popen(cmd).read().splitlines()
lines = filter(lambda c: not re.search('^\\?\\?.*$', c), lines)
return len(lines) > 0
def reset(module, dest, force):
'''
Resets the index and working tree to HEAD.
Discards any changes to tracked files in working
tree since that commit.
'''
os.chdir(dest)
if not force and has_local_mods(dest):
module.fail_json(msg="Local modifications exist in repository (force=no).")
return module.run_command("git reset --hard HEAD", check_rc=True)
def get_remote_head(module, dest, version, remote):
cmd = ''
os.chdir(dest)
if version == 'HEAD':
version = get_head_branch(module, dest, remote)
if is_remote_branch(module, dest, remote, version):
cmd = 'git ls-remote %s -h refs/heads/%s' % (remote, version)
elif is_remote_tag(module, dest, remote, version):
cmd = 'git ls-remote %s -t refs/tags/%s' % (remote, version)
else:
# appears to be a sha1. return as-is since it appears
# cannot check for a specific sha1 on remote
return version
(rc, out, err) = module.run_command(cmd, check_rc=True)
if len(out) < 1:
module.fail_json(msg="Could not determine remote revision for %s" % version)
rev = out.split()[0]
return rev
def is_remote_tag(module, dest, remote, version):
os.chdir(dest)
cmd = 'git ls-remote %s -t refs/tags/%s' % (remote, version)
(rc, out, err) = module.run_command(cmd)
if version in out:
return True
else:
return False
def get_branches(module, dest):
os.chdir(dest)
branches = []
(rc, out, err) = module.run_command("git branch -a")
if rc != 0:
module.fail_json(msg="Could not determine branch data - received %s" % out)
for line in out.split('\n'):
branches.append(line.strip())
return branches
def is_remote_branch(module, dest, remote, branch):
branches = get_branches(module, dest)
rbranch = 'remotes/%s/%s' % (remote, branch)
if rbranch in branches:
return True
else:
return False
def is_local_branch(module, dest, branch):
branches = get_branches(module, dest)
lbranch = '%s' % branch
if lbranch in branches:
return True
elif '* %s' % branch in branches:
return True
else:
return False
def is_current_branch(module, dest, branch):
branches = get_branches(module, dest)
for b in branches:
if b.startswith('* '):
cur_branch = b
if branch == cur_branch or '* %s' % branch == cur_branch:
return True
else:
return True
def is_not_a_branch(module, dest):
branches = get_branches(module, dest)
for b in branches:
if b.startswith('* ') and 'no branch' in b:
return True
return False
def get_head_branch(module, dest, remote):
'''
Determine what branch HEAD is associated with. This is partly
taken from lib/ansible/utils/__init__.py. It finds the correct
path to .git/HEAD and reads from that file the branch that HEAD is
associated with. In the case of a detached HEAD, this will look
up the branch in .git/refs/remotes/<remote>/HEAD.
'''
repo_path = os.path.join(dest, '.git')
# Check if the .git is a file. If it is a file, it means that we are in a submodule structure.
if os.path.isfile(repo_path):
try:
gitdir = yaml.safe_load(open(repo_path)).get('gitdir')
# There is a posibility the .git file to have an absolute path.
if os.path.isabs(gitdir):
repo_path = gitdir
else:
repo_path = os.path.join(repo_path.split('.git')[0], gitdir)
except (IOError, AttributeError):
return ''
# Read .git/HEAD for the name of the branch.
# If we're in a detached HEAD state, look up the branch associated with
# the remote HEAD in .git/refs/remotes/<remote>/HEAD
f = open(os.path.join(repo_path, "HEAD"))
if is_not_a_branch(module, dest):
f.close()
f = open(os.path.join(repo_path, 'refs', 'remotes', remote, 'HEAD'))
branch = f.readline().split('/')[-1].rstrip("\n")
f.close()
return branch
def fetch(module, repo, dest, version, remote):
''' updates repo from remote sources '''
os.chdir(dest)
(rc, out1, err1) = module.run_command("git fetch %s" % remote)
if rc != 0:
module.fail_json(msg="Failed to download remote objects and refs")
(rc, out2, err2) = module.run_command("git fetch --tags %s" % remote)
if rc != 0:
module.fail_json(msg="Failed to download remote objects and refs")
return (rc, out1 + out2, err1 + err2)
def switch_version(module, dest, remote, version):
''' once pulled, switch to a particular SHA, tag, or branch '''
os.chdir(dest)
cmd = ''
if version != 'HEAD':
if is_remote_branch(module, dest, remote, version):
if not is_local_branch(module, dest, version):
cmd = "git checkout --track -b %s %s/%s" % (version, remote, version)
else:
(rc, out, err) = module.run_command("git checkout --force %s" % version)
if rc != 0:
module.fail_json(msg="Failed to checkout branch %s" % version)
cmd = "git reset --hard %s/%s" % (remote, version)
else:
cmd = "git checkout --force %s" % version
else:
branch = get_head_branch(module, dest, remote)
(rc, out, err) = module.run_command("git checkout --force %s" % branch)
if rc != 0:
module.fail_json(msg="Failed to checkout branch %s" % branch)
cmd = "git reset --hard %s" % remote
return module.run_command(cmd, check_rc=True)
# ===========================================
def main():
module = AnsibleModule(
argument_spec = dict(
dest=dict(required=True),
repo=dict(required=True, aliases=['name']),
version=dict(default='HEAD'),
remote=dict(default='origin'),
force=dict(default='yes', type='bool'),
depth=dict(default=None, type='int'),
),
supports_check_mode=True
)
dest = os.path.abspath(os.path.expanduser(module.params['dest']))
repo = module.params['repo']
version = module.params['version']
remote = module.params['remote']
force = module.params['force']
depth = module.params['depth']
gitconfig = os.path.join(dest, '.git', 'config')
rc, out, err, status = (0, None, None, None)
# if there is no git configuration, do a clone operation
# else pull and switch the version
before = None
local_mods = False
if not os.path.exists(gitconfig):
if module.check_mode:
module.exit_json(changed=True)
(rc, out, err) = clone(module, repo, dest, remote, depth)
else:
# else do a pull
local_mods = has_local_mods(dest)
before = get_version(dest)
# if force, do a reset
if local_mods and module.check_mode:
module.exit_json(changed=True, msg='Local modifications exist')
(rc, out, err) = reset(module,dest,force)
if rc != 0:
module.fail_json(msg=err)
# check or get changes from remote
remote_head = get_remote_head(module, dest, version, remote)
if module.check_mode:
changed = False
if remote_head == version:
# get_remote_head returned version as-is
# were given a sha1 object, see if it is present
(rc, out, err) = module.run_command("git show %s" % version)
if version in out:
changed = False
else:
changed = True
else:
remote_head = remote_head[0:7]
if before != remote_head:
changed = True
else:
changed = False
module.exit_json(changed=changed, before=before, after=remote_head)
(rc, out, err) = fetch(module, repo, dest, version, remote)
if rc != 0:
module.fail_json(msg=err)
# switch to version specified regardless of whether
# we cloned or pulled
(rc, out, err) = switch_version(module, dest, remote, version)
# determine if we changed anything
after = get_version(dest)
changed = False
if before != after or local_mods:
changed = True
module.exit_json(changed=changed, before=before, after=after)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

252
library/source_control/hg Normal file
View File

@@ -0,0 +1,252 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
# (c) 2013, Yeukhon Wong <yeukhon@acm.org>
#
# This module was originally inspired by Brad Olson's ansible-module-mercurial
# <https://github.com/bradobro/ansible-module-mercurial>. This module tends
# to follow the git module implementation.
#
# 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/>.
import os
import shutil
import ConfigParser
from subprocess import Popen, PIPE
DOCUMENTATION = '''
---
module: hg
short_description: Manages Mercurial (hg) repositories.
description:
- Manages Mercurial (hg) repositories. Supports SSH, HTTP/S and local address.
version_added: "1.0"
author: Yeukhon Wong
options:
repo:
description:
- The repository address.
required: true
default: null
dest:
description:
- Absolute path of where the repository should be cloned to.
required: true
default: null
revision:
description:
- Equivalent C(-r) option in hg command which could be the changeset, revision number,
branch name or even tag.
required: false
default: "default"
force:
description:
- Discards uncommited changes. Runs C(hg update -C).
required: false
default: "yes"
choices: [ "yes", "no" ]
purge:
description:
- Delets untracked files. Runs C(hg purge). Note this requires C(purge) extension to
be enabled if C(purge=yes). This module will modify hgrc file on behalf of the user
and undo the changes before exiting the task.
required: false
default: "no"
choices: [ "yes", "no" ]
notes:
- If the task seems to be hanging, first verify remote host is in C(known_hosts).
SSH will prompt user to authorize the first contact with a remote host. One solution is to add
C(StrictHostKeyChecking no) in C(.ssh/config) which will accept and authorize the connection
on behalf of the user. However, if you run as a different user such as setting sudo to True),
for example, root will not look at the user .ssh/config setting.
examples:
- code: "hg: repo=https://bitbucket.org/user/repo1 dest=/home/user/repo1 revision=stable purge=yes"
description: Ensure the current working copy is inside the stable branch and deletes untracked files if any.
requirements: [ ]
'''
def _set_hgrc(hgrc, vals):
parser = ConfigParser.SafeConfigParser()
parser.read(hgrc)
# val is a list of triple-tuple of the form [(section, option, value),...]
for each in vals:
(section, option, value) = each
if not parser.has_section(section):
parser.add_section(section)
parser.set(section, option, value)
f = open(hgrc, 'w')
parser.write(f)
f.close()
def _undo_hgrc(hgrc, vals):
parser = ConfigParser.SafeConfigParser()
parser.read(hgrc)
for each in vals:
(section, option, value) = each
if parser.has_section(section):
parser.remove_option(section, option)
f = open(hgrc, 'w')
parser.write(f)
f.close()
def _hg_command(module, args_list):
(rc, out, err) = module.run_command(['hg'] + args_list)
return (rc, out, err)
def _hg_list_untracked(module, dest):
return _hg_command(module, ['purge', '-R', dest, '--print'])
def get_revision(module, dest):
"""
hg id -b -i -t returns a string in the format:
"<changeset>[+] <branch_name> <tag>"
This format lists the state of the current working copy,
and indicates whether there are uncommitted changes by the
plus sign. Otherwise, the sign is omitted.
Read the full description via hg id --help
"""
(rc, out, err) = _hg_command(module, ['id', '-b', '-i', '-t', '-R', dest])
if rc != 0:
module.fail_json(msg=err)
else:
return out.strip('\n')
def has_local_mods(module, dest):
now = get_revision(module, dest)
if '+' in now:
return True
else:
return False
def hg_discard(module, dest):
before = has_local_mods(module, dest)
if not before:
return False
(rc, out, err) = _hg_command(module, ['update', '-C', '-R', dest])
if rc != 0:
module.fail_json(msg=err)
after = has_local_mods(module, dest)
if before != after and not after: # no more local modification
return True
def hg_purge(module, dest):
hgrc = os.path.join(dest, '.hg/hgrc')
purge_option = [('extensions', 'purge', '')]
_set_hgrc(hgrc, purge_option) # enable purge extension
# before purge, find out if there are any untracked files
(rc1, out1, err1) = _hg_list_untracked(module, dest)
if rc1 != 0:
module.fail_json(msg=err)
# there are some untrackd files
if out1 != '':
(rc2, out2, err2) = _hg_command(module, ['purge', '-R', dest])
if rc2 == 0:
_undo_hgrc(hgrc, purge_option)
else:
module.fail_json(msg=err)
return True
else:
return False
def hg_cleanup(module, dest, force, purge):
discarded = False
purged = False
if force:
discarded = hg_discard(module, dest)
if purge:
purged = hg_purge(module, dest)
if discarded or purged:
return True
else:
return False
def hg_pull(module, dest, revision, repo):
return _hg_command(module, ['pull', '-r', revision, '-R', dest, repo])
def hg_update(module, dest, revision):
return _hg_command(module, ['update', '-R', dest])
def hg_clone(module, repo, dest, revision):
return _hg_command(module, ['clone', repo, dest, '-r', revision])
def switch_version(module, dest, revision):
return _hg_command(module, ['update', '-r', revision, '-R', dest])
# ===========================================
def main():
module = AnsibleModule(
argument_spec = dict(
repo = dict(required=True),
dest = dict(required=True),
revision = dict(default="default"),
force = dict(default='yes', type='bool'),
purge = dict(default='no', type='bool')
),
)
repo = module.params['repo']
dest = module.params['dest']
revision = module.params['revision']
force = module.params['force']
purge = module.params['purge']
hgrc = os.path.join(dest, '.hg/hgrc')
# initial states
before = ''
changed = False
cleaned = False
# If there is no hgrc file, then assume repo is absent
# and perform clone. Otherwise, perform pull and update.
if not os.path.exists(hgrc):
(rc, out, err) = hg_clone(module, repo, dest, revision)
if rc != 0:
module.fail_json(msg=err)
else:
# get the current state before doing pulling
before = get_revision(module, dest)
# can perform force and purge
cleaned = hg_cleanup(module, dest, force, purge)
(rc, out, err) = hg_pull(module, dest, revision, repo)
if rc != 0:
module.fail_json(msg=err)
(rc, out, err) = hg_update(module, dest, revision)
if rc != 0:
module.fail_json(msg=err)
switch_version(module, dest, revision)
after = get_revision(module, dest)
if before != after or cleaned:
changed = True
module.exit_json(before=before, after=after, changed=changed, cleaned=cleaned)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

View File

@@ -0,0 +1,190 @@
#!/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/>.
DOCUMENTATION = '''
---
module: subversion
short_description: Deploys a subversion repository.
description:
- Deploy given repository URL / revision to dest.
version_added: "0.7"
author: Dane Summers, njharman@gmail.com
notes:
- Requres I(svn) to be installed on the client.
requirements: []
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
revision:
description:
- Specific revision to checkout.
required: false
default: HEAD
force:
description:
- If C(yes), modified files will be discarded. If C(no), module will fail if it encounters modified files.
required: false
default: "yes"
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:
- code: "subversion: repo=svn+ssh://an.example.org/path/to/repo dest=/src/checkout"
description: Checkout subversion repository to specified folder.
'''
import re
class Subversion(object):
def __init__(self, module, dest, repo, revision, username, password):
self.module = module
self.dest = dest
self.repo = repo
self.revision = revision
self.username = username
self.password = password
def _exec(self, args):
bits = ["svn --non-interactive --trust-server-cert --no-auth-cache", ]
if self.username:
bits.append("--username '%s'" % self.username)
if self.password:
bits.append("--password '%s'" % self.password)
bits.append(args)
rc, out, err = self.module.run_command(' '.join(bits), check_rc=True)
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
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
# ===========================================
def main():
module = AnsibleModule(
argument_spec=dict(
dest=dict(required=True),
repo=dict(required=True, aliases=['name', 'repository']),
revision=dict(default='HEAD', aliases=['rev']),
force=dict(default='yes', type='bool'),
username=dict(required=False),
password=dict(required=False),
),
supports_check_mode=True
)
dest = os.path.expanduser(module.params['dest'])
repo = module.params['repo']
revision = module.params['revision']
force = module.params['force']
username = module.params['username']
password = module.params['password']
svn = Subversion(module, dest, repo, revision, username, password)
if not os.path.exists(dest):
before = None
local_mods = False
if module.check_mode:
module.exit_json(changed=True)
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.
if module.check_mode:
check, before, after = svn.needs_update()
module.exit_json(changed=check, before=before, after=after)
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()
else:
module.fail_json(msg="ERROR: %s folder already exists, but its not a subversion repository." % (dest, ))
after = svn.get_revision()
changed = before != after or local_mods
module.exit_json(changed=changed, before=before, after=after)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()