def test_merge_downstream(wst, capsys): config.merge.branches = '1.0.x 2.0.x 3.0.x master' with temp_git_repo(): run('git commit --allow-empty -m dummy-commit') run('git branch 3.0.x') run('git checkout -b 2.0.x') run('git commit --allow-empty -m new-commit') wst('merge --downstream') branches = run('git branch', return_output=True) changes = run('git log --oneline', return_output=True) out, _ = capsys.readouterr() assert out == """\ Merging 2.0.x into 3.0.x Pushing 3.0.x Merging 3.0.x into master Pushing master """ assert '* master' in branches assert 'new-commit' in changes
def test_script_sanity(script): try: run([script, '-h'], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print(e.output) assert e.returncode == 0
def remove_branch(branch, raises=False, remote=False, force=False): """ Removes branch """ run(['git', 'branch', '-D' if force else '-d', branch], raises=raises) if remote: silent_run(['git', 'push', default_remote(), '--delete', branch], raises=raises)
def local_commit(msg=None, amend=False, empty=False): cmd = ['git', 'commit'] if amend: cmd.append('--amend') if empty: cmd.append('--allow-empty') if msg: cmd.extend(['-m', msg]) run(cmd)
def prompt_with_editor(instruction): """ Prompt user with instruction in $EDITOR and return the response """ with tempfile.NamedTemporaryFile(suffix='.tmp') as fh: fh.write('\n\n# ' + '\n# '.join(instruction.split('\n'))) fh.flush() editor = os.environ.get('EDITOR', 'vim') run([editor, fh.name]) return '\n'.join([l for l in open(fh.name).read().split('\n') if not l.startswith('#')]).strip()
def temp_git_repo(name=None): with temp_dir() as tmpdir: if name: os.makedirs(name) os.chdir(name) run('git init') yield (tmpdir / name) if name else tmpdir
def install_editable_dependencies(self, tox, env, editable_products): name = product_name(tox.path) editable_products = expand_product_groups(editable_products) dependencies_output = self.show_installed_dependencies(tox, env, return_output=True) if not dependencies_output: log.debug('%s is not installed or there is no dependencies - skipping editable mode changes', name) return try: product_dependencies_list = json.loads(dependencies_output) except Exception as e: log.debug('Failed to get installed dependencies - skipping editable mode changes: %s', e) return product_dependencies = {} for dep, _, path in product_dependencies_list: product_dependencies[dep] = path available_products = [os.path.basename(r) for r in product_repos()] libs = [d for d in editable_products if d in available_products and d in product_dependencies and tox.envdir(env) in product_dependencies[d]] already_editable = [d for d in editable_products if d in product_dependencies and tox.envdir(env) not in product_dependencies[d]] for lib in already_editable: click.echo('{} is already installed in editable mode.'.format(lib)) not_dependent = [d for d in editable_products if d not in product_dependencies] for lib in not_dependent: log.debug('%s is not currently installed (not a dependency) and will be ignored.', lib) not_available = [d for d in editable_products if d not in not_dependent and d not in available_products] for lib in not_available: click.echo('{} is a dependency but not checked out in workspace, and so can not be installed in editable mode.'.format(lib)) pip = tox.bindir(env, 'pip') for lib in libs: if not self.silent or self.debug: click.echo('{}: Installing {} in editable mode'.format(env, lib)) with log_exception('An error occurred when installing %s in editable mode' % lib): run([pip, 'uninstall', lib, '-y'], raises=False, silent=not self.debug) lib_path = product_path(lib) if os.path.exists(os.path.join(lib_path, lib, 'setup.py')): lib_path = os.path.join(lib_path, lib) run([pip, 'install', '--editable', lib_path], silent=not self.debug)
def submit(self): token = self.ui.userTokenLineEdit.text().strip() server_id = self.ui.serverIDLineEdit.text().strip() channel_checkbox = self.ui.checkBox.isChecked() if token == "" or server_id == "": message.create( QMessageBox.Warning, "Warning", "Token or Server ID is empty.", ).exec() return opts = [config.dscli_bin, "config", "-t", token, "-i", server_id] if channel_checkbox: opts.append("-d") result = process.run(opts) if result.returncode == 0: message.create(QMessageBox.Information, "Success", "Settings saved successfully.").exec() self.close() return else: message.create(QMessageBox.Warning, "Error", "Settings saved unsuccessfully.").exec() return
def refresh_list_impl(progress_callback): result = process.run([config.dscli_bin, "ls", "-l"]) if result.returncode != 0: raise RuntimeError return result.stdout.decode()
def test_merge_branch_skip_none(wst, capsys): """ Test to check if merge_branch works when three commits are created, with no commits skipped. Fourth commit used so that all 3 commits on 3.0.x create merge commits. """ config.merge.branches = '1.0.x 2.0.x 3.0.x master' with temp_git_repo(): # Dummy commit run('git commit --allow-empty -m dummy-commit') run('git checkout -b 3.0.x') commit1_sha = make_commit("commit1") commit2_sha = make_commit("commit2") commit3_sha = make_commit("commit3") run('git checkout master') make_commit("commit4") wst('merge 3.0.x --skip-commits \'skip\' \'space separated example\'') changes = run('git log --oneline', return_output=True) assert f'Merge commit \'{commit1_sha[0:7]}\'' in changes assert f'Merge commit \'{commit2_sha[0:7]}\'' in changes assert f'Merge commit \'{commit3_sha[0:7]}\'' in changes assert 'commit1' in changes assert 'commit2' in changes assert 'commit3' in changes temp_git_dir_path = os.getcwd() assert os.path.isfile(os.path.join(temp_git_dir_path, "commit1.xml")) assert os.path.isfile(os.path.join(temp_git_dir_path, "commit2.xml")) assert os.path.isfile(os.path.join(temp_git_dir_path, "commit3.xml")) out, _ = capsys.readouterr() assert out.split('\n')[0] == 'Merging 3.0.x into master'
def test_merge_branch_skip_all(wst, capsys): """ Test to check if merge_branch works with three commits created on 3.0.x, with the all commits skipped. """ config.merge.branches = '1.0.x 2.0.x 3.0.x master' with temp_git_repo(): # Dummy commit run('git commit --allow-empty -m dummy-commit') run('git checkout -b 3.0.x') skipped1_sha = make_commit("commit1", skip=True) skipped2_sha = make_commit("commit2", skip=True) skipped3_sha = make_commit("commit3", skip=True) run('git checkout master') wst('merge 3.0.x --skip-commits \'skip\' \'space separated example\'') changes = run('git log --oneline', return_output=True) # Change should have been in the log entry assert f'Merge commit {skipped1_sha[0:7]} into master (using strategy ours)' in changes assert f'Merge commit {skipped2_sha[0:7]} into master (using strategy ours)' in changes assert f'Merge commit {skipped3_sha[0:7]} into master (using strategy ours)' in changes assert '[skip]' in changes assert '[skip]' in changes assert '[skip]' in changes temp_git_dir_path = os.getcwd() assert not os.path.isfile( os.path.join(temp_git_dir_path, "commit1.xml")) assert not os.path.isfile( os.path.join(temp_git_dir_path, "commit2.xml")) assert not os.path.isfile( os.path.join(temp_git_dir_path, "commit3.xml")) out, _ = capsys.readouterr() assert out.split('\n')[0] == 'Merging 3.0.x into master'
def ssh(ssh_args): """ Run ssh using the given args. Ansible hostname should be first|last arg. """ if not ssh_args: click.echo('Hostname is required') sys.exit(1) # Find index of hostname ssh_args = list(ssh_args) host_index = None for i, arg in enumerate(ssh_args): if '@' in arg: host_index = i break if host_index is None: if ssh_args[0].startswith('-'): host_index = -1 else: host_index = 0 # Split out user/hostname if '@' in ssh_args[host_index]: user, host = arg.split('@') else: user, host = None, ssh_args[host_index] # Translate matching hostname and replace hosts = sorted(_hosts_matching(host), key=lambda h: h.name) if hosts: if len(hosts) > 1: click.echo('Found multiple matches and will use first one: ' + ', '.join(h.name for h in hosts)) host = hosts[0].vars.get('ansible_host', hosts[0].name) ssh_args[host_index] = (user + '@' + host) if user else host # ssh to host try: run(['ssh'] + ssh_args) except Exception as e: sys.exit(1)
def diff_repo(path=None, branch=None, context=None, return_output=False, name_only=False, color=False): cmd = ['git', 'diff'] if name_only: cmd.append('--name-only') if color: cmd.append('--color') if branch: cmd.append(branch) if context: cmd.append(context) return run(cmd, cwd=path, return_output=return_output)
def test_commit(wst): with temp_dir(): with pytest.raises(SystemExit): wst('commit') config.merge.branches = '1.0.x 2.0.x 3.0.x master' with temp_git_repo(): test_cleanrun with pytest.raises(SystemExit): wst('commit "no files to commit"') with temp_git_repo(): run('git checkout -b 3.0.x') with open('new_file_commit1', 'w') as fp: fp.write('New World') run('git add -A') wst('commit "Add new file"') changes = run('git log --oneline', return_output=True) expected_commit_message = "Add new file" assert expected_commit_message in changes # test commit with branch with open('new_file_commit2', 'w') as fp: fp.write('Hello World') wst('commit "New Master file" --branch master') changes = run('git log --oneline', return_output=True) expected_commit_message = "New Master file" assert expected_commit_message in changes
def test_merge_branch(wst, capsys): config.merge.branches = '1.0.x 2.0.x 3.0.x master' with temp_git_repo(): run('git commit --allow-empty -m dummy-commit') run('git checkout -b 3.0.x') run('git commit --allow-empty -m new-commit') run('git checkout master') wst('merge 3.0.x') changes = run('git log --oneline', return_output=True) assert 'new-commit' in changes out, _ = capsys.readouterr() assert out.split('\n')[0] == 'Merging 3.0.x into master'
def commit_logs(limit=None, repo=None, diff=False, show_revision=None, extra_args=None, to_pager=False): if show_revision: diff = True if not limit: limit = 1 cmd = ['git', 'log', '--decorate'] if show_revision: cmd.extend(['-U', show_revision]) if limit: cmd.append('-%d' % limit) if diff: cmd.append('-c') if extra_args: cmd.extend(extra_args) return run(cmd, return_output=not to_pager, shell=to_pager, cwd=repo)
def make_commit(name, skip=False): """ Function to create a temporary file in current directory, and commit it using name as the commit message unless skip is True (in which case the commit message will have [skip] appended to the front of the commit name. Returns the SHA1 of the commit created. :param skip: [Optional] Append "[skip]" to the front of the commit message, for use in --skip-commits """ run('touch {}.xml'.format(name)) run('git add -A') msg = name if skip: msg = "[skip] {}".format(msg) run(['git', 'commit', '-m', msg]) sha = run('git rev-parse --verify HEAD', return_output=True).strip() return sha
def setup(): extension = ".exe" if platform.system() == "Windows" else "" artifact_name = \ f"dscli" \ f"_{config.dscli_version}" \ f"_{platform.system().lower()}" \ f"_{'amd64' if platform.architecture()[0] == '64bit' else '386'}" \ f".{'zip' if platform.system() == 'Windows' else 'tar.gz'}" response = requests.get(f"https://api.github.com/repos/darenliang/dscli/" f"releases/tags/v{config.dscli_version}") release_info = response.json() if path.exists(f"{bin_folder}/dscli{extension}"): result = process.run([f"{bin_folder}/dscli{extension}", "-v"]) version = result.stdout.decode()[len("dscli version "):] if version == config.dscli_version: return if not path.exists(bin_folder): os.makedirs(bin_folder) for asset in release_info["assets"]: if asset["name"] == artifact_name: urllib.request.urlretrieve(asset["browser_download_url"], f"{bin_folder}/{artifact_name}") break else: raise RuntimeError(f"Artifact not found: {artifact_name}") if platform.system() == "Windows": with zipfile.ZipFile(f"{bin_folder}/{artifact_name}", "r") as file: file.extractall(bin_folder) else: with tarfile.open(f"{bin_folder}/{artifact_name}", "r:gz") as file: file.extractall(bin_folder) artifact_path = pathlib.Path(f"{bin_folder}/{artifact_name}") artifact_path.unlink()
def rename_file(self): row_index = self.ui.tableView.currentIndex().row() if row_index == -1: return filename = \ self.file_model.view[row_index][0] text, ok = QInputDialog.getText(self, f"Rename {filename}", "New filename:", QLineEdit.Normal, filename) if not ok or text == "" or text == filename: return result = process.run([config.dscli_bin, "mv", filename, text]) if result.returncode != 0: message.create(QMessageBox.Warning, "Warning", f"Failed to rename {filename} to {text}.").exec() return self.refresh_list()
def delete_file(self): row_index = self.ui.tableView.currentIndex().row() if row_index == -1: return filename = \ self.file_model.view[row_index][0] reply = QMessageBox.question(self, f"Delete {filename}", f"Are you sure?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return result = process.run([config.dscli_bin, "rm", filename]) if result.returncode != 0: message.create(QMessageBox.Warning, "Warning", f"Failed to delete {filename}.").exec() return self.refresh_list()
def test_run(capsys): with in_temp_dir(): assert run('echo hello > hello.txt; echo world >> hello.txt', shell=True) out = run('ls', return_output=True) assert out == 'hello.txt\n' out = run(['cat', 'hello.txt'], return_output=True) assert out == 'hello\nworld\n' with pytest.raises(RunError): run('blah') assert not run('blah', raises=False) assert silent_run('ls -l') out, _ = capsys.readouterr() assert out == ''
def test_status(wst, capsys): with temp_git_repo(): wst('status') out, _ = capsys.readouterr() assert out == '# Branches: \n' run('git commit --allow-empty -m Dummy') wst('status') out, _ = capsys.readouterr() assert out == '# Branches: master\n' run('git checkout -b feature') wst('status') out, _ = capsys.readouterr() assert out == '# Branches: feature master\n' run('git checkout HEAD^0') wst('status') out, _ = capsys.readouterr() assert re.fullmatch('# Branches: \w+\* feature master\n', out)
def install_editable_dependencies(self, tox, env, editable_products): name = product_name(tox.path) editable_products = expand_product_groups(editable_products) dependencies_output = self.show_installed_dependencies( tox, env, return_output=True) if not dependencies_output: log.debug( '%s is not installed or there is no dependencies - skipping editable mode changes', name) return try: product_dependencies_list = json.loads(dependencies_output) except Exception as e: log.debug( 'Failed to get installed dependencies - skipping editable mode changes: %s', e) return product_dependencies = {} for dep, _, path in product_dependencies_list: product_dependencies[dep] = path available_products = [os.path.basename(r) for r in product_repos()] libs = [ d for d in editable_products if d in available_products and d in product_dependencies and tox.envdir(env) in product_dependencies[d] ] already_editable = [ d for d in editable_products if d in product_dependencies and tox.envdir(env) not in product_dependencies[d] ] for lib in already_editable: click.echo('{} is already installed in editable mode.'.format(lib)) not_dependent = [ d for d in editable_products if d not in product_dependencies ] for lib in not_dependent: log.debug( '%s is not currently installed (not a dependency) and will be ignored.', lib) not_available = [ d for d in editable_products if d not in not_dependent and d not in available_products ] for lib in not_available: click.echo( '{} is a dependency but not checked out in workspace, and so can not be installed in editable mode.' .format(lib)) pip = tox.bindir(env, 'pip') for lib in libs: if not self.silent or self.debug: click.echo('{}: Installing {} in editable mode'.format( env, lib)) with log_exception( 'An error occurred when installing %s in editable mode' % lib): run([pip, 'uninstall', lib, '-y'], raises=False, silent=not self.debug) lib_path = product_path(lib) if os.path.exists(os.path.join(lib_path, lib, 'setup.py')): lib_path = os.path.join(lib_path, lib) run([pip, 'install', '--editable', lib_path], silent=not self.debug)
def stat_repo(path=None, return_output=False, with_color=False): if with_color: cmd = 'git -c color.status=always status' else: cmd = 'git status' return run(cmd, cwd=path, return_output=return_output)
def diff_branch(right_branch, left_branch='master', path=None): cmd = 'git log %s..%s' % (left_branch, right_branch) return run(cmd, cwd=path, return_output=True)
def show_installed_dependencies(self, tox, env, return_output=False, filter_name=None): script_template = """ import json import os import sys try: from pip._internal.utils.misc import get_installed_distributions # pip < 10 except Exception: from pip import get_installed_distributions # Required params to run this script package = '%s' json_output = %s env = '%s' filter_name = '%s' cwd = os.getcwd() workspace_dir = os.path.dirname(cwd) try: libs = [(p.key, p.version, p.location) for p in get_installed_distributions()] except Exception as e: print(e) sys.exit(1) output = [] if not json_output: print(env + ':') def strip_cwd(dir): if dir.startswith(cwd + '/'): dir = dir[len(cwd):].lstrip('/') elif dir.startswith(workspace_dir): dir = os.path.join('..', dir[len(workspace_dir):].lstrip('/')) return dir for lib, version, location in sorted(libs): if filter_name and filter_name not in lib: continue if json_output: output.append((lib, version, location)) else: output.append(' %%-25s %%-10s %%s' %% (lib, version, strip_cwd(location))) if json_output: print(json.dumps(output)) else: print('\\n'.join(output)) """ name = product_name(tox.path) filter_name = isinstance(filter_name, str) and filter_name or '' script = script_template % (name, return_output, env, filter_name) python = tox.bindir(env, 'python') if not os.path.exists(python): log.error('Test environment %s is not installed. Please run without -d / --show-dependencies to install it first.', env) sys.exit(1) return run([python, '-c', script], return_output=return_output, raises=False)
def install(build_dir): args = ['install'] return process.run('make', args, build_dir)
def test_publish(wst, monkeypatch): silent_run_mock = Mock() config_mock = Mock() monkeypatch.setattr('workspace.commands.publish.LocalConfig', config_mock) monkeypatch.setattr('workspace.commands.publish.silent_run', silent_run_mock) monkeypatch.setattr('workspace.commands.publish.run', silent_run_mock) config_mock().get.side_effect = ['repo', 'user', 'pass'] * 10 with temp_git_repo() as cwd: wst('setup --product') # Patch release run('git commit --allow-empty -m change1') run('git commit --allow-empty -m change2') wst('publish') changes = open('docs/CHANGELOG.rst').read() assert changes == """\ Version 0.0.1 ================================================================================ * change2 * change1 """ setup_py = open('setup.py').read().split('\n') assert setup_py[5] == " version='0.0.2'," python = Path('~/.virtualenvs').expanduser() / Path( cwd).name / 'bin' / 'python' repo_path = '/private' + str(cwd) assert silent_run_mock.call_args_list == [ call('rm -rf dist/*', cwd=repo_path, shell=True), call(f'{python} setup.py sdist bdist_wheel', cwd=repo_path), call('twine upload -r "pypi" -u "user" -p "pass" dist/*', cwd=repo_path, shell=True, silent=2) ] # No changes with pytest.raises(SystemExit): wst('publish') # Minor release run('git commit --allow-empty -m feature1') wst('publish --minor') assert 'Bump minor version' in commit_logs() changes = open('docs/CHANGELOG.rst').read() print(changes) assert changes == """\ Version 0.1.0 ================================================================================ * feature1 Version 0.0.1 ================================================================================ * change2 * change1 """ setup_py = open('setup.py').read().split('\n') assert setup_py[5] == " version='0.1.1'," # Major release run('git commit --allow-empty -m feature2') wst('publish --major') assert 'Bump major version' in commit_logs() changes = open('docs/CHANGELOG.rst').read() print(changes) assert changes == """\ Version 1.0.0 ================================================================================ * feature2 Version 0.1.0 ================================================================================ * feature1 Version 0.0.1 ================================================================================ * change2 * change1 """ setup_py = open('setup.py').read().split('\n') assert setup_py[5] == " version='1.0.1'," # Already published patch version will bump before publish run('git commit --allow-empty -m "Publish version 1.0.1"', shell=True) run('git commit --allow-empty -m bugfix1') wst('publish') changes = open('docs/CHANGELOG.rst').read() assert changes == """\ Version 1.0.2 ================================================================================ * bugfix1 Version 1.0.0 -------------------------------------------------------------------------------- * feature2 Version 0.1.0 ================================================================================ * feature1 Version 0.0.1 ================================================================================ * change2 * change1 """ setup_py = open('setup.py').read().split('\n') assert setup_py[5] == " version='1.0.3',"
def temp_remote_git_repo(): with temp_dir() as tmpdir: run('git clone https://github.com/maxzheng/remoteconfig.git') repo_dir = tmpdir / 'remoteconfig' os.chdir(repo_dir) yield repo_dir
def test_merge_branch_with_whitelist(wst, capsys): config.merge.branches = '1.0.x 2.0.x 3.0.x master' with temp_git_repo(): # Dummy commit run('git commit --allow-empty -m dummy-commit') run('git checkout -b 3.0.x') run('touch temp.xml') run('git add -A') # Commit 1 run('git commit -m [skip]') # commit 2 run('touch temp2.xml') run('git add -A') run('git commit -m commit2') run('git checkout master') wst('merge 3.0.x --skip-commits \'skip\' \'space separated example\'') changes = run('git log --oneline', return_output=True) # Change should have been in the log entry assert 'Merge branch \'3.0.x\'' in changes assert '[skip]' in changes assert 'commit2' in changes out, _ = capsys.readouterr() assert out.split('\n')[0] == 'Merging 3.0.x into master'
def test_publish(wst, monkeypatch): silent_run_mock = Mock() config_mock = Mock() monkeypatch.setattr('workspace.commands.publish.LocalConfig', config_mock) monkeypatch.setattr('workspace.commands.publish.silent_run', silent_run_mock) monkeypatch.setattr('workspace.commands.publish.run', silent_run_mock) config_mock().get.side_effect = ['repo', 'user', 'pass'] * 10 with temp_git_repo() as cwd: wst('setup --product') # Patch release run('git commit --allow-empty -m change1') run('git commit --allow-empty -m change2') wst('publish') changes = open('docs/CHANGELOG.rst').read() assert changes == """\ Version 0.0.1 ================================================================================ * change2 * change1 """ setup_py = open('setup.py').read().split('\n') assert setup_py[5] == " version='0.0.2'," python = Path('~/.virtualenvs').expanduser() / Path(cwd).name / 'bin' / 'python' assert silent_run_mock.call_args_list == [ call('rm -rf dist/*', cwd=str(cwd), shell=True), call(f'{python} setup.py sdist bdist_wheel', cwd=str(cwd)), call('twine upload -r "pypi" -u "user" -p "pass" dist/*', cwd=str(cwd), shell=True, silent=2)] # No changes with pytest.raises(SystemExit): wst('publish') # Minor release run('git commit --allow-empty -m feature1') wst('publish --minor') assert 'Bump minor version' in commit_logs() changes = open('docs/CHANGELOG.rst').read() print(changes) assert changes == """\ Version 0.1.0 ================================================================================ * feature1 Version 0.0.1 ================================================================================ * change2 * change1 """ setup_py = open('setup.py').read().split('\n') assert setup_py[5] == " version='0.1.1'," # Major release run('git commit --allow-empty -m feature2') wst('publish --major') assert 'Bump major version' in commit_logs() changes = open('docs/CHANGELOG.rst').read() print(changes) assert changes == """\ Version 1.0.0 ================================================================================ * feature2 Version 0.1.0 ================================================================================ * feature1 Version 0.0.1 ================================================================================ * change2 * change1 """ setup_py = open('setup.py').read().split('\n') assert setup_py[5] == " version='1.0.1'," # Already published patch version will bump before publish run('git commit --allow-empty -m "Publish version 1.0.1"', shell=True) run('git commit --allow-empty -m bugfix1') wst('publish') changes = open('docs/CHANGELOG.rst').read() assert changes == """\ Version 1.0.2 ================================================================================ * bugfix1 Version 1.0.0 -------------------------------------------------------------------------------- * feature2 Version 0.1.0 ================================================================================ * feature1 Version 0.0.1 ================================================================================ * change2 * change1 """ setup_py = open('setup.py').read().split('\n') assert setup_py[5] == " version='1.0.3',"
def run(source, dest, install_prefix): args = [source, '-DCMAKE_INSTALL_PREFIX={}'.format(install_prefix)] return process.run('cmake', args, dest)
def hard_reset(to_commit): run(['git', 'reset', '--hard', to_commit])
def run(self): if self.minor and self.major: log.error('--minor and --major are mutually exclusive, please use only one.') return repo_check() pypirc = LocalConfig('~/.pypirc') repository = pypirc.get(self.repo, 'repository') username = pypirc.get(self.repo, 'username') password = pypirc.get(self.repo, 'password') repo_title = 'PyPI' if self.repo == 'pypi' else self.repo.title() if not repository: log.error('Please add repository / username to [%s] section in ~/.pypirc', self.repo) sys.exit(1) if not username: username = getpass.getuser('{} Username: '******'{} Password: '******'update') published_version, changes = self.changes_since_last_publish() if not changes: click.echo('There are no changes since last publish') sys.exit(0) silent_run('rm -rf dist/*', shell=True, cwd=repo_path()) if self.major or self.minor: new_version, setup_file = self.bump_version(major=self.major, minor=self.minor) major_minor = 'major' if self.major else 'minor' self.commander.run('commit', msg=f'Bump {major_minor} version', files=[setup_file], push=2, skip_style_check=True) else: current_version, setup_file = self.get_version() # Previously, we publish the current version in setup.py, so need to bump it first before # we can publish a new version. if published_version == current_version: new_version, setup_file = self.bump_version() else: new_version = current_version changelog_file = self.update_changelog(new_version, changes, self.minor or self.major) tox = ToxIni() envs = [e for e in tox.envlist if e != 'style'] if envs: env = envs[0] if len(envs) > 1: log.debug('Found multiple default envs in tox.ini, will use first one to build: %s', env) else: click.echo('Odd, there are no default envs in tox.ini, so we can not build.') sys.exit(1) envdir = tox.envdir(env) python = os.path.join(envdir, 'bin', 'python') click.echo('Building source/built distribution') silent_run(f'{python} setup.py sdist bdist_wheel', cwd=repo_path()) click.echo('Uploading to ' + repo_title) try: run('twine upload -r "{repo}" -u "{username}" -p "{password}" dist/*'.format( repo=self.repo, username=username, password=password), shell=True, cwd=repo_path(), silent=2) except Exception: sys.exit(1) self.bump_version() self.commander.run('commit', msg=PUBLISH_VERSION_PREFIX + new_version, push=2, files=[setup_file, changelog_file], skip_style_check=True)
def test_cleanrun(wst): config.clean.remove_products_older_than_days = 30 with temp_dir(): repos = ['repo', 'old_repo', 'old_repo_dirty'] run('touch file; mkdir ' + ' '.join(repos), shell=True) for repo in repos: run('cd {}; git init; git commit --allow-empty -m "Initial commit"'.format(repo), shell=True) if repo.startswith('old'): run('touch -t 200001181205.09 ' + repo) run('cd old_repo_dirty; touch new_file', shell=True) run('ls -l') wst('clean') assert os.listdir() == ['old_repo_dirty', 'repo', 'file'] with temp_git_repo(): run('touch hello.py hello.pyc') wst('clean') assert os.listdir() == ['.git', 'hello.py']
def run(self): if self.test_dependents: name = product_name() # Convert None to list for tuple([]) if not self.env_or_file: self.env_or_file = [] if not self.extra_args: self.extra_args = [] test_args = ( ('env_or_file', tuple(self.env_or_file)), ('return_output', True), ('num_processes', self.num_processes), ('silent', True), ('debug', self.debug), ('extra_args', tuple(self.extra_args)) ) test_repos = [repo_path()] test_repos.extend(r for r in repos(workspace_path()) if self.product_depends_on(r, name) and r not in test_repos) test_args = [(r, test_args, self.__class__) for r in test_repos] def test_done(result): name, output = result success, summary = self.summarize(output) if success: click.echo('{}: {}'.format(name, summary)) else: temp_output_file = os.path.join(tempfile.gettempdir(), 'test-%s.out' % name) with open(temp_output_file, 'w') as fp: fp.write(output) temp_output_file = 'See ' + temp_output_file log.error('%s: %s', name, '\n\t'.join([summary, temp_output_file])) def show_remaining(completed, all_args): completed_repos = set(product_name(r) for r, _, _ in completed) all_repos = set(product_name(r) for r, _, _ in all_args) remaining_repos = sorted(list(all_repos - completed_repos)) if len(remaining_repos): repo = remaining_repos.pop() more = '& %d more' % len(remaining_repos) if remaining_repos else '' return '%s %s' % (repo, more) else: return 'None' repo_results = parallel_call(test_repo, test_args, callback=test_done, show_progress=show_remaining, progress_title='Remaining') for result in list(repo_results.values()): if isinstance(result, tuple): _, result = result success, _ = self.summarize(result) if not (success or self.return_output): sys.exit(1) return dict(list(repo_results.values())) if not self.repo: self.repo = project_path() # Strip out venv bin path to python to avoid issues with it being removed when running tox if 'VIRTUAL_ENV' in os.environ: venv_bin = os.environ['VIRTUAL_ENV'] os.environ['PATH'] = os.pathsep.join([p for p in os.environ['PATH'].split(os.pathsep) if os.path.exists(p) and not p.startswith(venv_bin)]) envs = [] files = [] if self.env_or_file: for ef in self.env_or_file: if os.path.exists(ef): files.append(os.path.abspath(ef)) else: envs.append(ef) pytest_args = '' if self.match_test or self.num_processes is not None or files or self.extra_args: pytest_args = [] if self.match_test: pytest_args.append('-k ' + self.match_test) if self.num_processes is None: # Skip parallel for targeted test run / works better with pdb self.num_processes = 0 if self.num_processes is not None: pytest_args.append('-n ' + str(self.num_processes)) if self.extra_args: pytest_args.extend(self.extra_args) if files: pytest_args.extend(files) pytest_args = ' '.join(pytest_args) os.environ['PYTESTARGS'] = pytest_args tox = ToxIni(self.repo, self.tox_ini) if not envs: envs = tox.envlist # Prefer 'test' over 'cover' when there are pytest args as cover is likely to fail and distract from # test results. And also remove style as user is focused on fixing a test, and style for the whole project # isn't interesting yet. if pytest_args: if 'cover' in envs: python = tox.get(tox.envsection('cover'), 'basepython') version = ''.join(python.strip('python').split('.')) if python else '36' envs[envs.index('cover')] = 'py' + version if 'style' in envs: envs.remove('style') env_commands = {} if self.install_only and not self.redevelop: self.redevelop = 1 if self.show_dependencies: if 'style' in envs: envs.remove('style') for env in envs: self.show_installed_dependencies(tox, env, filter_name=self.show_dependencies) elif self.install_editable: if 'style' in envs: envs.remove('style') for env in envs: if len(envs) > 1: print(env + ':') self.install_editable_dependencies(tox, env, editable_products=self.install_editable) elif self.redevelop: if self.tox_cmd: cmd = self.tox_cmd else: cmd = ['tox', '-c', tox.tox_ini] if envs: cmd.extend(['-e', ','.join(envs)]) if self.redevelop > 1: cmd.append('-r') if self.install_only: cmd.append('--notest') output = run(cmd, cwd=self.repo, raises=not self.return_output, silent=self.silent, return_output=self.return_output) if not output: if self.return_output: return False else: sys.exit(1) for env in envs: env_commands[env] = ' '.join(cmd) # Touch envdir envdir = tox.envdir(env) if os.path.exists(envdir): os.utime(envdir, None) # Strip entry version self._strip_version_from_entry_scripts(tox, env) if self.return_output: return output else: for env in envs: envdir = tox.envdir(env) def requirements_updated(): req_mtime = 0 requirements_files = ['requirements.txt', 'pinned.txt', 'tox.ini'] for req_file in requirements_files: req_path = os.path.join(self.repo, req_file) if os.path.exists(req_path): req_mtime = max(req_mtime, os.stat(req_path).st_mtime) return req_mtime > os.stat(envdir).st_mtime if not os.path.exists(envdir) or requirements_updated(): env_commands.update( self.commander.run('test', env_or_file=[env], repo=self.repo, redevelop=True, tox_cmd=self.tox_cmd, tox_ini=self.tox_ini, tox_commands=self.tox_commands, match_test=self.match_test, num_processes=self.num_processes, silent=self.silent, debug=self.debug, extra_args=self.extra_args)) continue commands = self.tox_commands.get(env) or tox.commands(env) env_commands[env] = '\n'.join(commands) for command in commands: full_command = os.path.join(envdir, 'bin', command) command_path = full_command.split()[0] if os.path.exists(command_path): if 'pytest' in full_command or 'py.test' in full_command: if 'PYTESTARGS' in full_command: full_command = full_command.replace('{env:PYTESTARGS:}', pytest_args) else: full_command += ' ' + pytest_args activate = '. ' + os.path.join(envdir, 'bin', 'activate') output = run(activate + '; ' + full_command, shell=True, cwd=self.repo, raises=False, silent=self.silent, return_output=self.return_output) if not output: if self.return_output: return False else: sys.exit(1) if not self.silent and (len(envs) > 1 or env == 'style'): click.secho(f'{env}: OK', fg='green') if self.return_output: return output else: log.error('%s does not exist', command_path) if self.return_output: return False else: sys.exit(1) return env_commands