def main(destination, **kw): develop = os.environ['BATOU_DEVELOP'] if develop: output.annotate( 'Initializing with a development copy of batou will cause your ' 'project to have a reference outside its repository. ' 'Use at your own risk. ') develop = os.path.abspath(develop) print(( 'Bootstrapping new batou project in {}. This can take a while.'.format( os.path.abspath(destination)))) if os.path.exists(destination): print(('{} exists already. Not copying template structure.'.format( destination))) os.chdir(destination) else: source = os.path.dirname(__file__) + '/init-template' shutil.copytree(source, destination) os.chdir(destination) cmd('hg -y init .') update_bootstrap(os.environ['BATOU_VERSION'], develop) # Need to clean up to avoid inheriting info that we're bootstrapped # already. for key in list(os.environ): if key.startswith('BATOU_'): del os.environ[key] cmd('./batou --help')
def test_has_changes_counts_untracked_files_as_changes(root, repos_path): clone = batou.lib.git.Clone(repos_path, target="clone", branch="master") root.component += clone root.component.deploy() assert not clone.has_changes() cmd("touch {}/clone/bar".format(root.workdir)) assert clone.has_changes()
def test_cmd_quotes_spacey_args(popen): popen().communicate.return_value = (b'', b'') popen().returncode = 0 cmd(['cat', 'foo', 'bar bz baz']) assert popen.call_args[0] == ("cat foo 'bar bz baz'", ) cmd(['cat', 'foo', "bar 'bz baz"]) assert popen.call_args[0] == (r"cat foo 'bar \'bz baz'", )
def test_cmd_quotes_spacey_args(popen): popen().communicate.return_value = (b"", b"") popen().returncode = 0 cmd(["cat", "foo", "bar bz baz"]) assert popen.call_args[0] == ("cat foo 'bar bz baz'",) cmd(["cat", "foo", "bar 'bz baz"]) assert popen.call_args[0] == (r"cat foo 'bar \'bz baz'",)
def _prepare_ssh(self, host): container = host.name # XXX application / user-specific files # https://unix.stackexchange.com/questions/312988/understanding-home-configuration-file-locations-config-and-local-sha KNOWN_HOSTS_FILE = os.path.expanduser('~/.batou/known_hosts') if not os.path.exists(KNOWN_HOSTS_FILE): prefix_dir = os.path.dirname(KNOWN_HOSTS_FILE) if not os.path.exists(prefix_dir): os.makedirs(prefix_dir) with open(KNOWN_HOSTS_FILE, 'w'): pass if self.rebuild: cmd(['ssh-keygen', '-R', container, '-f', KNOWN_HOSTS_FILE]) ssh_config = [] # We need to provide a version of the key that doesn't trigger # OpenSSHs "wrong permission" check. packaged_insecure_key = os.path.join(os.path.dirname(__file__), 'insecure-private.key') local_insecure_key = os.path.abspath('insecure-private.key') with open(local_insecure_key, 'w') as f_target: with open(packaged_insecure_key) as f_packaged: f_target.write(f_packaged.read()) os.chmod(local_insecure_key, 0o600) ssh_config.append(""" Host {container} {aliases} HostName {container} ProxyJump {target_host} User developer IdentityFile {insecure_private_key} StrictHostKeyChecking no UserKnownHostsFile {known_hosts} """.format(container=container, aliases=' '.join(host._aliases), target_host=self.target_host, known_hosts=KNOWN_HOSTS_FILE, insecure_private_key=local_insecure_key)) # More generic includes need to go last because parameters are # set by a match-first and things like User settings for * may # otherwise collide with our very specific settings. See # `man 5 ssh_config`. if os.path.exists(os.path.expanduser('~/.ssh/config')): ssh_config.append('Host *\n Include ~/.ssh/config') if os.path.exists('ssh_config'): ssh_config.append('Host *\n Include {}'.format( os.path.abspath('ssh_config'))) # Place this in the deployment base directory persistently and # keep updating it. This helps users to also interact with containers # by running `ssh -F ssh_config_dev mycontainer` self.ssh_config_file = os.path.abspath('ssh_config_{}'.format( host.environment.name)) with open(self.ssh_config_file, 'w') as f: f.write('\n'.join(ssh_config))
def test_changes_lost_on_update_without_incoming(root, repos_path): root.component += batou.lib.git.Clone(repos_path, target="clone", branch="master") root.component.deploy() cmd("cd {dir}/clone; echo foobar >foo".format(dir=root.workdir)) root.component.deploy() assert not open(root.component.map("clone/foo")).read()
def test_remote_deployment_initializable(sample_service): cmd("hg init") with open(".hg/hgrc", "w") as f: f.write("[paths]\ndefault=https://example.com") env = Environment("test-with-env-config") env.load() env.configure() Deployment(env, platform="", jobs=1, timeout=30, dirty=False)
def test_has_changes_counts_changes_to_tracked_files(root, repos_path): clone = batou.lib.git.Clone(repos_path, target='clone', branch='master') root.component += clone root.component.deploy() assert not clone.has_changes() cmd('touch {}/clone/bar'.format(root.workdir)) cmd('cd {}/clone; git add bar'.format(root.workdir)) assert clone.has_changes()
def test_remote_deployment_initializable(sample_service): cmd('hg init') with open('.hg/hgrc', 'w') as f: f.write('[paths]\ndefault=https://example.com') env = Environment('test-with-env-config') env.load() env.configure() Deployment(env, platform='', jobs=1, timeout=30, dirty=False)
def _repos_path(root, name): repos_path = os.path.join(root.environment.workdir_base, name) cmd("mkdir {dir}; cd {dir}; git init;" "git config user.name Jenkins;" "git config user.email [email protected];" "touch foo; git add .;" 'git commit -am "foo"'.format(dir=repos_path)) return repos_path
def test_changes_lost_on_update_without_incoming(root, repos_path): root.component += batou.lib.mercurial.Clone(repos_path, target='clone', branch='default') root.component.deploy() cmd('cd {dir}/clone; echo foobar >foo'.format(dir=root.workdir)) root.component.deploy() assert not open(root.component.map('clone/foo')).read()
def test_untracked_files_are_removed_on_update(root, repos_path): root.component += batou.lib.mercurial.Clone(repos_path, target='clone', branch='default') root.component.deploy() cmd('cd {dir}/clone; mkdir bar; echo foobar >bar/baz'.format( dir=root.workdir)) root.component.deploy() assert not os.path.exists(root.component.map('clone/bar/baz'))
def test_clean_clone_updates_on_incoming_changes(root, repos_path): root.component += batou.lib.mercurial.Clone(repos_path, target='clone', branch='default') root.component.deploy() cmd('cd {dir}; touch bar; hg addremove; hg ci -m "commit"'.format( dir=repos_path)) root.component.deploy() assert os.path.isfile(root.component.map('clone/bar'))
def test_has_changes_counts_untracked_files_as_changes(root, repos_path): clone = batou.lib.mercurial.Clone(repos_path, target='clone', branch='default') root.component += clone root.component.deploy() assert not clone.has_changes() cmd('touch {}/clone/bar'.format(root.workdir)) assert clone.has_changes()
def test_clean_clone_updates_on_incoming_changes(root, repos_path): root.component += batou.lib.git.Clone(repos_path, target="clone", branch="master") root.component.deploy() cmd('cd {dir}; touch bar; git add .; git commit -m "commit"'.format( dir=repos_path)) root.component.deploy() assert os.path.isfile(root.component.map("clone/bar"))
def test_untracked_files_are_removed_on_update(root, repos_path): root.component += batou.lib.git.Clone(repos_path, target="clone", branch="master") root.component.deploy() cmd("cd {dir}/clone; mkdir bar; echo foobar >bar/baz".format( dir=root.workdir)) root.component.deploy() assert not os.path.exists(root.component.map("clone/bar/baz"))
def test_has_changes_counts_changes_to_tracked_files(root, repos_path): clone = batou.lib.mercurial.Clone(repos_path, target="clone", branch="default") root.component += clone root.component.deploy() assert not clone.has_changes() cmd("touch {}/clone/bar".format(root.workdir)) cmd("cd {}/clone; hg add bar".format(root.workdir)) assert clone.has_changes()
def test_setting_branch_updates_on_incoming_changes(root, repos_path): root.component += batou.lib.git.Clone(repos_path, target="clone", branch="master") root.component.deploy() cmd('cd {dir}; touch bar; git add .; git commit -m "commit"'.format( dir=repos_path)) root.component.deploy() assert os.path.isfile( os.path.join(root.environment.workdir_base, "mycomponent/clone/bar"))
def test_setting_branch_updates_on_incoming_changes(root, repos_path): root.component += batou.lib.mercurial.Clone(repos_path, target='clone', branch='default') root.component.deploy() cmd('cd {dir}; touch bar; hg addremove; hg ci -m "commit"'.format( dir=repos_path)) root.component.deploy() assert os.path.isfile( os.path.join(root.environment.workdir_base, 'mycomponent/clone/bar'))
def test_branch_does_switch_branch(root, repos_path): cmd('cd {dir}; hg branch bar; hg ci -m "commit branch"'.format( dir=repos_path)) root.component += batou.lib.mercurial.Clone(repos_path, target='clone', branch='bar') root.component.deploy() stdout, stderr = cmd( 'cd {workdir}/clone; hg branch'.format(workdir=root.workdir)) assert 'bar' == stdout.strip()
def test_changes_lost_on_update_with_incoming(root, repos_path): root.component += batou.lib.git.Clone(repos_path, target="clone", branch="master") root.component.deploy() cmd('cd {dir}; touch bar; git add .; git commit -m "commit"'.format( dir=repos_path)) cmd("cd {dir}/clone; echo foobar >foo".format(dir=root.workdir)) root.component.deploy() assert os.path.exists(root.component.map("clone/bar")) assert not open(root.component.map("clone/foo")).read()
def test_runs_svn_to_clone_repository(root): repos_path = os.path.join(root.environment.workdir_base, "repos") cmd("svnadmin create " + repos_path) cmd("svn checkout file://{dir} upstream; cd upstream;" 'touch foo; svn add foo; svn commit -m "bar"'.format(dir=repos_path)) root.component += batou.lib.svn.Checkout( "file://" + repos_path, target="clone", revision="head") root.component.deploy() assert os.path.isfile( os.path.join(root.environment.workdir_base, "mycomponent/clone/foo")) root.component.deploy() # trigger verify
def test_changes_lost_on_update_with_incoming(root, repos_path): root.component += batou.lib.mercurial.Clone(repos_path, target='clone', branch='default') root.component.deploy() cmd('cd {dir}; touch bar; hg addremove; hg ci -m "commit"'.format( dir=repos_path)) cmd('cd {dir}/clone; echo foobar >foo'.format(dir=root.workdir)) root.component.deploy() assert os.path.exists(root.component.map('clone/bar')) assert not open(root.component.map('clone/foo')).read()
def test_branch_does_switch_branch(root, repos_path): cmd("cd {dir}; touch bar; git add .; git checkout -b bar;" 'git commit -m "commit branch"'.format(dir=repos_path)) root.component += batou.lib.git.Clone(repos_path, target="clone", branch="bar") root.component.deploy() stdout, stderr = cmd( "cd {workdir}/clone; git rev-parse --abbrev-ref HEAD".format( workdir=root.workdir)) assert "bar" == stdout.strip()
def test_runs_svn_to_clone_repository(root): repos_path = os.path.join(root.environment.workdir_base, 'repos') cmd('svnadmin create ' + repos_path) cmd('svn checkout file://{dir} upstream; cd upstream;' 'touch foo; svn add foo; svn commit -m "bar"'.format(dir=repos_path)) root.component += batou.lib.svn.Checkout( 'file://' + repos_path, target='clone', revision='head') root.component.deploy() assert os.path.isfile( os.path.join(root.environment.workdir_base, 'mycomponent/clone/foo')) root.component.deploy() # trigger verify
def test_clean_clone_vcs_update_false_leaves_changes_intact(root, repos_path): root.component += batou.lib.git.Clone(repos_path, target="clone", branch="master", vcs_update=False) root.component.deploy() cmd("cd {dir}; echo foobar >foo; touch bar; git add .; " 'git commit -m "commit"'.format(dir=repos_path)) cmd("cd {dir}/clone; echo asdf >foo".format(dir=root.workdir)) root.component.deploy() assert "asdf\n" == open(root.component.map("clone/foo")).read() assert not os.path.exists(root.component.map("clone/bar"))
def test_clean_clone_vcs_update_false_leaves_changes_intact(root, repos_path): root.component += batou.lib.mercurial.Clone(repos_path, target='clone', branch='default', vcs_update=False) root.component.deploy() cmd('cd {dir}; echo foobar >foo; touch bar; hg addremove; ' 'hg ci -m "commit"'.format(dir=repos_path)) cmd('cd {dir}/clone; echo asdf >foo'.format(dir=root.workdir)) root.component.deploy() assert 'asdf\n' == open(root.component.map('clone/foo')).read() assert not os.path.exists(root.component.map('clone/bar'))
def test_setting_revision_updates_on_incoming_changes(root, repos_path): cmd('cd {dir}; touch bar; git add .; git commit -m "commit2"'.format( dir=repos_path)) commit1, _ = cmd("cd {dir}; git rev-parse HEAD^".format(dir=repos_path)) root.component += batou.lib.git.Clone(repos_path, target="clone", revision=commit1) root.component.deploy() cmd('cd {dir}; touch qux; git add .; git commit -m "commit3"'.format( dir=repos_path)) root.component.deploy() # Our main assertion: Nothing breaks here assert not os.path.isfile( os.path.join(root.environment.workdir_base, "mycomponent/clone/qux"))
def test_example_async_sync_deployment(): os.chdir('examples/sync_async') out, _ = cmd('./batou -d deploy default') print(out) assert "Number of jobs: 1" in out out, _ = cmd('./batou -d deploy -j 2 default') print(out) assert "Number of jobs: 2" in out out, _ = cmd('./batou -d deploy async') print(out) assert "Number of jobs: 2" in out
def test_changed_remote_is_updated(root, repos_path, repos_path2): git = batou.lib.git.Clone(repos_path, target="clone", branch="master") root.component += git # Fresh, unrelated repo cmd("cd {dir}; echo baz >bar; git add .;" 'git commit -m "commit"'.format(dir=repos_path2)) root.component.deploy() assert not os.path.exists(root.component.map("clone/bar")) git.url = repos_path2 root.component.deploy() assert os.path.exists(root.component.map("clone/bar"))