Merge branch 'source_control-executable' of https://github.com/skyl/ansible into skyl-source_control-executable

This commit is contained in:
James Cammarata 2013-09-30 21:57:47 -05:00
commit c8dcdc7b7a
4 changed files with 222 additions and 153 deletions

View file

@ -50,6 +50,13 @@ options:
description: description:
- If C(yes), any modified files in the working - If C(yes), any modified files in the working
tree will be discarded. tree will be discarded.
executable:
required: false
default: null
version_added: "1.4"
description:
- Path to bzr executable to use. If not supplied,
the normal mechanism for resolving binary paths will be used.
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -58,67 +65,79 @@ EXAMPLES = '''
''' '''
import re 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): class Bzr(object):
''' makes a new bzr branch if it does not already exist ''' def __init__(self, module, parent, dest, version, bzr_path):
dest_dirname = os.path.dirname(dest) self.module = module
try: self.parent = parent
os.makedirs(dest_dirname) self.dest = dest
except: self.version = version
pass self.bzr_path = bzr_path
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): def _command(self, args_list, **kwargs):
os.chdir(dest) (rc, out, err) = self.module.run_command(
cmd = "bzr status -S" [self.bzr_path] + args_list, **kwargs)
lines = os.popen(cmd).read().splitlines() return (rc, out, err)
lines = filter(lambda c: not re.search('^\\?\\?.*$', c), lines)
return len(lines) > 0
def reset(module,dest,force): def get_version(self):
''' '''samples the version of the bzr branch'''
Resets the index and working tree to head. os.chdir(self.dest)
Discards any changes to tracked files in the working cmd = "%s revno" % self.bzr_path
tree since that commit. revno = os.popen(cmd).read().strip()
''' return revno
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): def clone(self):
''' updates branch from remote sources ''' '''makes a new bzr branch if it does not already exist'''
os.chdir(dest) dest_dirname = os.path.dirname(self.dest)
if version.lower() != 'head': try:
(rc, out, err) = module.run_command("bzr pull -r %s" % version) os.makedirs(dest_dirname)
else: except:
(rc, out, err) = module.run_command("bzr pull") pass
if rc != 0: os.chdir(dest_dirname)
module.fail_json(msg="Failed to pull") if self.version.lower() != 'head':
return (rc, out, err) args_list = ["branch", "-r", self.version, self.parent, self.dest]
else:
args_list = ["branch", self.parent, self.dest]
return self._command(args_list, check_rc=True)
def switch_version(module, dest, version): def has_local_mods(self):
''' once pulled, switch to a particular revno or revid''' os.chdir(self.dest)
os.chdir(dest) cmd = "%s status -S" % self.bzr_path
cmd = '' lines = os.popen(cmd).read().splitlines()
if version.lower() != 'head': lines = filter(lambda c: not re.search('^\\?\\?.*$', c), lines)
cmd = "bzr revert -r %s" % version return len(lines) > 0
else:
cmd = "bzr revert" def reset(self, force):
return module.run_command(cmd, check_rc=True) '''
Resets the index and working tree to head.
Discards any changes to tracked files in the working
tree since that commit.
'''
os.chdir(self.dest)
if not force and self.has_local_mods():
self.module.fail_json(msg="Local modifications exist in branch (force=no).")
return self._command(["revert"], check_rc=True)
def fetch(self):
'''updates branch from remote sources'''
os.chdir(self.dest)
if self.version.lower() != 'head':
(rc, out, err) = self._command(["pull", "-r", self.version])
else:
(rc, out, err) = self._command(["pull"])
if rc != 0:
self.module.fail_json(msg="Failed to pull")
return (rc, out, err)
def switch_version(self):
'''once pulled, switch to a particular revno or revid'''
os.chdir(self.dest)
if self.version.lower() != 'head':
args_list = ["revert", "-r", self.version]
else:
args_list = ["revert"]
return self._command(args_list, check_rc=True)
# =========================================== # ===========================================
@ -128,7 +147,8 @@ def main():
dest=dict(required=True), dest=dict(required=True),
name=dict(required=True, aliases=['parent']), name=dict(required=True, aliases=['parent']),
version=dict(default='head'), version=dict(default='head'),
force=dict(default='yes', type='bool') force=dict(default='yes', type='bool'),
executable=dict(default=None),
) )
) )
@ -136,34 +156,38 @@ def main():
parent = module.params['name'] parent = module.params['name']
version = module.params['version'] version = module.params['version']
force = module.params['force'] force = module.params['force']
bzr_path = module.params['executable'] or module.get_bin_path('bzr', True)
bzrconfig = os.path.join(dest, '.bzr', 'branch', 'branch.conf') bzrconfig = os.path.join(dest, '.bzr', 'branch', 'branch.conf')
rc, out, err, status = (0, None, None, None) rc, out, err, status = (0, None, None, None)
bzr = Bzr(module, parent, dest, version, bzr_path)
# if there is no bzr configuration, do a branch operation # if there is no bzr configuration, do a branch operation
# else pull and switch the version # else pull and switch the version
before = None before = None
local_mods = False local_mods = False
if not os.path.exists(bzrconfig): if not os.path.exists(bzrconfig):
(rc, out, err) = clone(module, parent, dest, version) (rc, out, err) = bzr.clone()
else: else:
# else do a pull # else do a pull
local_mods = has_local_mods(dest) local_mods = bzr.has_local_mods()
before = get_version(dest) before = bzr.get_version()
(rc, out, err) = reset(module, dest, force) (rc, out, err) = bzr.reset(force)
if rc != 0: if rc != 0:
module.fail_json(msg=err) module.fail_json(msg=err)
(rc, out, err) = fetch(module, dest, version) (rc, out, err) = bzr.fetch()
if rc != 0: if rc != 0:
module.fail_json(msg=err) module.fail_json(msg=err)
# switch to version specified regardless of whether # switch to version specified regardless of whether
# we cloned or pulled # we cloned or pulled
(rc, out, err) = switch_version(module, dest, version) (rc, out, err) = bzr.switch_version()
# determine if we changed anything # determine if we changed anything
after = get_version(dest) after = bzr.get_version()
changed = False changed = False
if before != after or local_mods: if before != after or local_mods:

View file

@ -73,6 +73,13 @@ options:
- If C(yes), repository will be updated using the supplied - If C(yes), repository will be updated using the supplied
remote. Otherwise the repo will be left untouched. remote. Otherwise the repo will be left untouched.
Prior to 1.2, this was always 'yes' and could not be disabled. Prior to 1.2, this was always 'yes' and could not be disabled.
executable:
required: false
default: null
version_added: "1.4"
description:
- Path to git executable to use. If not supplied,
the normal mechanism for resolving binary paths will be used.
notes: notes:
- If the task seems to be hanging, first verify remote host is in C(known_hosts). - 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 SSH will prompt user to authorize the first contact with a remote host. One solution is to add
@ -304,6 +311,7 @@ def main():
force=dict(default='yes', type='bool'), force=dict(default='yes', type='bool'),
depth=dict(default=None, type='int'), depth=dict(default=None, type='int'),
update=dict(default='yes', type='bool'), update=dict(default='yes', type='bool'),
executable=dict(default=None),
), ),
supports_check_mode=True supports_check_mode=True
) )
@ -315,8 +323,8 @@ def main():
force = module.params['force'] force = module.params['force']
depth = module.params['depth'] depth = module.params['depth']
update = module.params['update'] update = module.params['update']
git_path = module.params['executable'] or module.get_bin_path('git', True)
git_path = module.get_bin_path('git', True)
gitconfig = os.path.join(dest, '.git', 'config') gitconfig = os.path.join(dest, '.git', 'config')
rc, out, err, status = (0, None, None, None) rc, out, err, status = (0, None, None, None)

View file

@ -22,10 +22,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import os
import shutil
import ConfigParser import ConfigParser
from subprocess import Popen, PIPE
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
@ -68,6 +65,13 @@ options:
required: false required: false
default: "no" default: "no"
choices: [ "yes", "no" ] choices: [ "yes", "no" ]
executable:
required: false
default: null
version_added: "1.4"
description:
- Path to hg executable to use. If not supplied,
the normal mechanism for resolving binary paths will be used.
notes: notes:
- If the task seems to be hanging, first verify remote host is in C(known_hosts). - 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 SSH will prompt user to authorize the first contact with a remote host. One solution is to add
@ -98,6 +102,7 @@ def _set_hgrc(hgrc, vals):
parser.write(f) parser.write(f)
f.close() f.close()
def _undo_hgrc(hgrc, vals): def _undo_hgrc(hgrc, vals):
parser = ConfigParser.SafeConfigParser() parser = ConfigParser.SafeConfigParser()
parser.read(hgrc) parser.read(hgrc)
@ -111,94 +116,105 @@ def _undo_hgrc(hgrc, vals):
parser.write(f) parser.write(f)
f.close() 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): class Hg(object):
return _hg_command(module, ['purge', '-R', dest, '--print'])
def get_revision(module, dest): def __init__(self, module, dest, repo, revision, hg_path):
""" self.module = module
hg id -b -i -t returns a string in the format: self.dest = dest
"<changeset>[+] <branch_name> <tag>" self.repo = repo
This format lists the state of the current working copy, self.revision = revision
and indicates whether there are uncommitted changes by the self.hg_path = self.hg_path
plus sign. Otherwise, the sign is omitted.
Read the full description via hg id --help def _command(self, args_list):
""" (rc, out, err) = self.module.run_command([self.hg_path] + args_list)
(rc, out, err) = _hg_command(module, ['id', '-b', '-i', '-t', '-R', dest]) return (rc, out, err)
if rc != 0:
module.fail_json(msg=err)
else:
return out.strip('\n')
def has_local_mods(module, dest): def _list_untracked(self):
now = get_revision(module, dest) return self._command(['purge', '-R', self.dest, '--print'])
if '+' in now:
return True
else:
return False
def hg_discard(module, dest): def get_revision(self):
before = has_local_mods(module, dest) """
if not before: hg id -b -i -t returns a string in the format:
return False "<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.
(rc, out, err) = _hg_command(module, ['update', '-C', '-R', dest]) Read the full description via hg id --help
if rc != 0: """
module.fail_json(msg=err) (rc, out, err) = self._command(['id', '-b', '-i', '-t', '-R', self.dest])
if rc != 0:
after = has_local_mods(module, dest) self.module.fail_json(msg=err)
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: else:
module.fail_json(msg=err) return out.strip('\n')
return True
else:
return False
def hg_cleanup(module, dest, force, purge): def has_local_mods(self):
discarded = False now = self.get_revision()
purged = False if '+' in now:
return True
else:
return False
if force: def discard(self):
discarded = hg_discard(module, dest) before = self.has_local_mods()
if purge: if not before:
purged = hg_purge(module, dest) return False
if discarded or purged:
return True
else:
return False
def hg_pull(module, dest, revision, repo): (rc, out, err) = self._command(['update', '-C', '-R', self.dest])
return _hg_command(module, ['pull', '-r', revision, '-R', dest, repo]) if rc != 0:
self.module.fail_json(msg=err)
def hg_update(module, dest, revision): after = self.has_local_mods()
return _hg_command(module, ['update', '-R', dest]) if before != after and not after: # no more local modification
return True
def hg_clone(module, repo, dest, revision): def purge(self):
return _hg_command(module, ['clone', repo, dest, '-r', revision]) hgrc = os.path.join(self.dest, '.hg/hgrc')
purge_option = [('extensions', 'purge', '')]
_set_hgrc(hgrc, purge_option) # enable purge extension
def switch_version(module, dest, revision): # before purge, find out if there are any untracked files
return _hg_command(module, ['update', '-r', revision, '-R', dest]) (rc1, out1, err1) = self._list_untracked()
if rc1 != 0:
self.module.fail_json(msg=err1)
# there are some untrackd files
if out1 != '':
(rc2, out2, err2) = self._command(['purge', '-R', self.dest])
if rc2 == 0:
_undo_hgrc(hgrc, purge_option)
else:
self.module.fail_json(msg=err2)
return True
else:
return False
def cleanup(self, force, purge):
discarded = False
purged = False
if force:
discarded = self.discard()
if purge:
purged = self.purge()
if discarded or purged:
return True
else:
return False
def pull(self):
return self._command(
['pull', '-r', self.revision, '-R', self.dest, self.repo])
def update(self):
return self._command(['update', '-R', self.dest])
def clone(self):
return self._command(['clone', self.repo, self.dest, '-r', self.revision])
def switch_version(self):
return self._command(['update', '-r', self.revision, '-R', self.dest])
# =========================================== # ===========================================
@ -209,7 +225,8 @@ def main():
dest = dict(required=True), dest = dict(required=True),
revision = dict(default="default", aliases=['version']), revision = dict(default="default", aliases=['version']),
force = dict(default='yes', type='bool'), force = dict(default='yes', type='bool'),
purge = dict(default='no', type='bool') purge = dict(default='no', type='bool'),
executable = dict(default=None),
), ),
) )
repo = module.params['repo'] repo = module.params['repo']
@ -217,6 +234,7 @@ def main():
revision = module.params['revision'] revision = module.params['revision']
force = module.params['force'] force = module.params['force']
purge = module.params['purge'] purge = module.params['purge']
hg_path = module.parames['executable'] or module.get_bin_path('hg', True)
hgrc = os.path.join(dest, '.hg/hgrc') hgrc = os.path.join(dest, '.hg/hgrc')
# initial states # initial states
@ -224,29 +242,31 @@ def main():
changed = False changed = False
cleaned = False cleaned = False
hg = Hg(module, dest, repo, revision, hg_path)
# If there is no hgrc file, then assume repo is absent # If there is no hgrc file, then assume repo is absent
# and perform clone. Otherwise, perform pull and update. # and perform clone. Otherwise, perform pull and update.
if not os.path.exists(hgrc): if not os.path.exists(hgrc):
(rc, out, err) = hg_clone(module, repo, dest, revision) (rc, out, err) = hg.clone()
if rc != 0: if rc != 0:
module.fail_json(msg=err) module.fail_json(msg=err)
else: else:
# get the current state before doing pulling # get the current state before doing pulling
before = get_revision(module, dest) before = hg.get_revision()
# can perform force and purge # can perform force and purge
cleaned = hg_cleanup(module, dest, force, purge) cleaned = hg.cleanup(force, purge)
(rc, out, err) = hg_pull(module, dest, revision, repo) (rc, out, err) = hg.pull()
if rc != 0: if rc != 0:
module.fail_json(msg=err) module.fail_json(msg=err)
(rc, out, err) = hg_update(module, dest, revision) (rc, out, err) = hg.update()
if rc != 0: if rc != 0:
module.fail_json(msg=err) module.fail_json(msg=err)
switch_version(module, dest, revision) hg.switch_version()
after = get_revision(module, dest) after = hg.get_revision()
if before != after or cleaned: if before != after or cleaned:
changed = True changed = True
module.exit_json(before=before, after=after, changed=changed, cleaned=cleaned) module.exit_json(before=before, after=after, changed=changed, cleaned=cleaned)

View file

@ -63,6 +63,13 @@ options:
- --password parameter passed to svn. - --password parameter passed to svn.
required: false required: false
default: null default: null
executable:
required: false
default: null
version_added: "1.4"
description:
- Path to svn executable to use. If not supplied,
the normal mechanism for resolving binary paths will be used.
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -71,19 +78,27 @@ EXAMPLES = '''
''' '''
import re import re
import tempfile
class Subversion(object): class Subversion(object):
def __init__(self, module, dest, repo, revision, username, password): def __init__(
self, module, dest, repo, revision, username, password, svn_path):
self.module = module self.module = module
self.dest = dest self.dest = dest
self.repo = repo self.repo = repo
self.revision = revision self.revision = revision
self.username = username self.username = username
self.password = password self.password = password
self.svn_path = svn_path
def _exec(self, args): def _exec(self, args):
bits = ["svn --non-interactive --trust-server-cert --no-auth-cache", ] bits = [
self.svn_path,
'--non-interactive',
'--trust-server-cert',
'--no-auth-cache',
]
if self.username: if self.username:
bits.append("--username '%s'" % self.username) bits.append("--username '%s'" % self.username)
if self.password: if self.password:
@ -147,6 +162,7 @@ def main():
force=dict(default='yes', type='bool'), force=dict(default='yes', type='bool'),
username=dict(required=False), username=dict(required=False),
password=dict(required=False), password=dict(required=False),
executable=dict(default=None),
), ),
supports_check_mode=True supports_check_mode=True
) )
@ -157,8 +173,9 @@ def main():
force = module.params['force'] force = module.params['force']
username = module.params['username'] username = module.params['username']
password = module.params['password'] password = module.params['password']
svn_path = module.params['executable'] or module.get_bin_path('svn', True)
svn = Subversion(module, dest, repo, revision, username, password) svn = Subversion(module, dest, repo, revision, username, password, svn_path)
if not os.path.exists(dest): if not os.path.exists(dest):
before = None before = None