diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index ad270ece..2a25b251 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -12,10 +12,6 @@ on: schedule: - cron: '0 0 * * MON' -defaults: - run: - shell: bash - jobs: stable: runs-on: ${{ matrix.os }} @@ -23,7 +19,21 @@ jobs: matrix: os: ['ubuntu-latest'] python-version: [3.6, 3.7, 3.8, 3.9, '3.10', 'pypy-3.7'] + shell: ['bash'] + include: + - os: 'windows-2019' + python-version: '3.10' + shell: 'bash' + - os: 'windows-2019' + python-version: '3.10' + shell: 'pwsh' + - os: 'windows-2019' + python-version: '3.10' + shell: 'cmd' + defaults: + run: + shell: ${{ matrix.shell }} steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/src/git/from_vcs.py b/src/git/from_vcs.py index 5dce8dc9..55b1de17 100644 --- a/src/git/from_vcs.py +++ b/src/git/from_vcs.py @@ -22,10 +22,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): version string, meaning we're inside a checked out source tree. """ GITS = ["git"] - TAG_PREFIX_REGEX = "*" if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - TAG_PREFIX_REGEX = r"\*" _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) @@ -34,12 +32,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): print("Directory %s not under git control" % root) raise NotThisMethod("'git rev-parse --git-dir' returned error") + MATCH_ARGS = ["--match", "%s*" % tag_prefix] if tag_prefix else [] + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", - "%s%s" % (tag_prefix, TAG_PREFIX_REGEX)], + "--always", "--long", *MATCH_ARGS], cwd=root) # --long was added in git-1.5.5 if describe_out is None: diff --git a/test/git/common.py b/test/git/common.py index 04a5e5db..0cddf66c 100644 --- a/test/git/common.py +++ b/test/git/common.py @@ -1,4 +1,6 @@ import os, sys +import stat +import shutil from subprocess_helper import run_command GITS = ["git"] @@ -41,3 +43,11 @@ def project_file(self, *path): def subpath(self, *path): return os.path.join(self.testdir, *path) + + def rmtree(self, path): + # rm -rf + # Found on https://stackoverflow.com/a/1889686 + def remove_readonly(func, path, excinfo): + os.chmod(path, stat.S_IWRITE) + func(path) + shutil.rmtree(path, onerror=remove_readonly) diff --git a/test/git/test_git.py b/test/git/test_git.py index f01b17ea..2e12e6a4 100644 --- a/test/git/test_git.py +++ b/test/git/test_git.py @@ -1,10 +1,12 @@ #! /usr/bin/python import os, sys +import posixpath import shutil import tarfile import unittest import tempfile +import re from pkg_resources import parse_version @@ -402,7 +404,10 @@ def test_project_in_subdir(self): # i.e. setup.py -- is not located in the root directory self.run_test("test/demoapp", False, "project") - def run_test(self, demoapp_dir, script_only, project_sub_dir): + def test_no_tag_prefix(self): + self.run_test("test/demoapp", False, ".", tag_prefix="") + + def run_test(self, demoapp_dir, script_only, project_sub_dir, tag_prefix=None): # The test dir should live under /tmp/ or /var/ or somewhere that # isn't the child of the versioneer repo's .git directory, since that # will confuse the tests that check what happens when there is no @@ -411,7 +416,7 @@ def run_test(self, demoapp_dir, script_only, project_sub_dir): self.testdir = tempfile.mkdtemp() if VERBOSE: print("testdir: %s" % (self.testdir,)) if os.path.exists(self.testdir): - shutil.rmtree(self.testdir) + self.rmtree(self.testdir) # Our tests run from a git repo that lives here. All self.git() # operations run from this directory unless overridden. @@ -431,6 +436,13 @@ def run_test(self, demoapp_dir, script_only, project_sub_dir): with open(setup_cfg_fn, "r") as f: setup_cfg = f.read() setup_cfg = setup_cfg.replace("@VCS@", "git") + + tag_prefix_regex = "tag_prefix = (.*)" + if tag_prefix is None: + tag_prefix = re.search(tag_prefix_regex, setup_cfg).group(1) + else: + setup_cfg = re.sub(tag_prefix_regex, f"tag_prefix = {tag_prefix}", setup_cfg) + with open(setup_cfg_fn, "w") as f: f.write(setup_cfg) shutil.copyfile("versioneer.py", self.project_file("versioneer.py")) @@ -447,10 +459,11 @@ def run_test(self, demoapp_dir, script_only, project_sub_dir): out = self.python("versioneer.py", "setup").splitlines() self.assertEqual(out[0], "creating src/demo/_version.py") + init = os.path.join("src/demo", "__init__.py") if script_only: - self.assertEqual(out[1], " src/demo/__init__.py doesn't exist, ok") + self.assertEqual(out[1], f" {init} doesn't exist, ok") else: - self.assertEqual(out[1], " appending to src/demo/__init__.py") + self.assertEqual(out[1], f" appending to {init}") self.assertEqual(out[2], " appending 'versioneer.py' to MANIFEST.in") self.assertEqual(out[3], " appending versionfile_source ('src/demo/_version.py') to MANIFEST.in") @@ -464,7 +477,7 @@ def remove_pyc(s): ] out = set(remove_pyc(self.git("status", "--porcelain").splitlines())) def pf(fn): - return os.path.normpath(os.path.join(self.project_sub_dir, fn)) + return posixpath.normpath(posixpath.join(self.project_sub_dir, fn)) expected = {"A %s" % pf(".gitattributes"), "M %s" % pf("MANIFEST.in"), "A %s" % pf("src/demo/_version.py"), @@ -483,9 +496,9 @@ def pf(fn): out = self.python("versioneer.py", "setup").splitlines() self.assertEqual(out[0], "creating src/demo/_version.py") if script_only: - self.assertEqual(out[1], " src/demo/__init__.py doesn't exist, ok") + self.assertEqual(out[1], f" {init} doesn't exist, ok") else: - self.assertEqual(out[1], " src/demo/__init__.py unmodified") + self.assertEqual(out[1], f" {init} unmodified") self.assertEqual(out[2], " 'versioneer.py' already in MANIFEST.in") self.assertEqual(out[3], " versionfile_source already in MANIFEST.in") out = set(remove_pyc(self.git("status", "--porcelain").splitlines())) @@ -525,7 +538,7 @@ def pf(fn): # S3: we commit that change, then make the first tag (1.0) self.git("add", self.project_file("setup.py")) self.git("commit", "-m", "dirty") - self.git("tag", "demo-1.0") + self.git("tag", f"{tag_prefix}1.0") # also add an unrelated tag, to test exclusion. git-describe appears # to return the highest lexicographically-sorted tag, so make sure # the unrelated one sorts earlier @@ -580,7 +593,7 @@ def pf(fn): def do_checks(self, state, exps): if os.path.exists(self.subpath("out")): - shutil.rmtree(self.subpath("out")) + self.rmtree(self.subpath("out")) # TA: project tree self.check_version(self.projdir, state, "TA", exps["TA"]) @@ -588,14 +601,14 @@ def do_checks(self, state, exps): target = self.subpath("out/demoapp-TB") shutil.copytree(self.projdir, target) if os.path.exists(os.path.join(target, ".git")): - shutil.rmtree(os.path.join(target, ".git")) + self.rmtree(os.path.join(target, ".git")) self.check_version(target, state, "TB", exps["TB"]) # TC: project tree in versionprefix-named parentdir target = self.subpath("out/demo-1.1") shutil.copytree(self.projdir, target) if os.path.exists(os.path.join(target, ".git")): - shutil.rmtree(os.path.join(target, ".git")) + self.rmtree(os.path.join(target, ".git")) self.check_version(target, state, "TC", ["1.1", None, False, None]) # XXX # TD: project subdir of an unpacked git-archive tarball @@ -611,7 +624,7 @@ def do_checks(self, state, exps): # TE: unpacked setup.py sdist tarball dist_path = os.path.join(self.projdir, "dist") if os.path.exists(dist_path): - shutil.rmtree(dist_path) + self.rmtree(dist_path) self.python("setup.py", "sdist", "--formats=tar") files = os.listdir(dist_path) self.assertTrue(len(files)==1, files) @@ -646,7 +659,7 @@ def check_version(self, workdir, state, tree, exps): # RB: setup.py build; rundemo --version if os.path.exists(os.path.join(workdir, "build")): - shutil.rmtree(os.path.join(workdir, "build")) + self.rmtree(os.path.join(workdir, "build")) self.python("setup.py", "build", "--build-lib=build/lib", "--build-scripts=build/lib", workdir=workdir) build_lib = os.path.join(workdir, "build", "lib") diff --git a/test/git/test_invocations.py b/test/git/test_invocations.py index a81206f9..c95623ab 100644 --- a/test/git/test_invocations.py +++ b/test/git/test_invocations.py @@ -1,4 +1,4 @@ -import os, sys, shutil, unittest, tempfile, tarfile, virtualenv, warnings +import os, sys, shutil, unittest, tempfile, tarfile, warnings from wheel.bdist_wheel import get_abi_tag, get_platform from packaging.tags import interpreter_name, interpreter_version @@ -39,7 +39,7 @@ def setUp(self): def make_venv(self, mode): if not os.path.exists(self.subpath("venvs")): os.mkdir(self.subpath("venvs")) - venv_dir = self.subpath("venvs/%s" % mode) + venv_dir = self.subpath(os.path.join("venvs", mode)) # python3 on OS-X uses a funky two-part executable and an environment # variable to communicate between them. If this variable is still set # by the time a virtualenv's 'pip' or 'python' is run, and if that @@ -53,27 +53,35 @@ def make_venv(self, mode): # switching to 'venv' on py3, but only py3.4 includes pip, and even # then it's an ancient version. os.environ.pop("__PYVENV_LAUNCHER__", None) - virtualenv.logger = virtualenv.Logger([]) # hush # virtualenv causes DeprecationWarning/ResourceWarning on py3 with warnings.catch_warnings(): warnings.simplefilter("ignore") - virtualenv.create_environment(venv_dir) + self.python('-m', 'virtualenv', venv_dir, workdir=self.testdir) self.run_in_venv(venv_dir, venv_dir, 'pip', 'install', '-U', 'pip', 'wheel', 'packaging') return venv_dir - def run_in_venv(self, venv, workdir, command, *args): - bins = {"python": os.path.join(venv, "bin", "python"), - "pip": os.path.join(venv, "bin", "pip"), - "rundemo": os.path.join(venv, "bin", "rundemo"), - } + def get_venv_bin(self, venv, command): + if sys.platform == "win32": + return os.path.join(venv, "Scripts", command) + + return os.path.join(venv, "bin", command) + + def run_in_venv(self, venv, workdir, command, *args, use_python=False): + bin_args = [self.get_venv_bin(venv, command)] + pybin = self.get_venv_bin(venv, "python") + if command == "pip": + bin_args = [pybin, "-m", "pip"] args = ["--isolated", "--no-cache-dir"] + list(args) - return self.command(bins[command], *args, workdir=workdir) + elif command == "rundemo" and use_python: + bin_args = [pybin] + bin_args + + return self.command(*bin_args, *args, workdir=workdir) - def check_in_venv(self, venv): - out = self.run_in_venv(venv, venv, "rundemo") + def check_in_venv(self, venv, use_python=False): + out = self.run_in_venv(venv, venv, "rundemo", use_python=use_python) v = dict(line.split(":", 1) for line in out.splitlines()) self.assertEqual(v["version"], "2.0") return v @@ -417,13 +425,13 @@ def test_install(self): repodir = self.make_distutils_repo() venv = self.make_venv("distutils-repo-install") self.run_in_venv(venv, repodir, "python", "setup.py", "install") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_install_subproject(self): projectdir = self.make_distutils_repo_subproject() venv = self.make_venv("distutils-repo-install-subproject") self.run_in_venv(venv, projectdir, "python", "setup.py", "install") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_wheel(self): self.make_distutils_wheel_with_pip() @@ -433,37 +441,37 @@ def test_pip_install(self): repodir = self.make_distutils_repo() venv = self.make_venv("distutils-repo-pip-install") self.run_in_venv(venv, repodir, "pip", "install", ".") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_install_subproject(self): projectdir = self.make_distutils_repo_subproject() venv = self.make_venv("distutils-repo-pip-install-subproject") self.run_in_venv(venv, projectdir, "pip", "install", ".") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_install_from_afar(self): repodir = self.make_distutils_repo() venv = self.make_venv("distutils-repo-pip-install-from-afar") self.run_in_venv(venv, venv, "pip", "install", repodir) - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_install_from_afar_subproject(self): projectdir = self.make_distutils_repo_subproject() venv = self.make_venv("distutils-repo-pip-install-from-afar-subproject") self.run_in_venv(venv, venv, "pip", "install", projectdir) - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_install_editable(self): repodir = self.make_distutils_repo() venv = self.make_venv("distutils-repo-pip-install-editable") self.run_in_venv(venv, repodir, "pip", "install", "--editable", ".") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_install_editable_subproject(self): projectdir = self.make_distutils_repo_subproject() venv = self.make_venv("distutils-repo-pip-install-editable-subproject") self.run_in_venv(venv, projectdir, "pip", "install", "--editable", ".") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) class SetuptoolsRepo(_Invocations, unittest.TestCase): def test_install(self): @@ -589,14 +597,14 @@ def test_pip_install(self): venv = self.make_venv("distutils-sdist-pip-install") self.run_in_venv(venv, venv, "pip", "install", sdist) - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_install_subproject(self): sdist = self.make_distutils_sdist_subproject() venv = self.make_venv("distutils-sdist-pip-install-subproject") self.run_in_venv(venv, venv, "pip", "install", sdist) - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) class SetuptoolsSdist(_Invocations, unittest.TestCase): def test_pip_install(self): @@ -645,13 +653,13 @@ def test_install(self): unpacked = self.make_distutils_unpacked() venv = self.make_venv("distutils-unpacked-install") self.run_in_venv(venv, unpacked, "python", "setup.py", "install") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_install_subproject(self): unpacked = self.make_distutils_subproject_unpacked() venv = self.make_venv("distutils-subproject-unpacked-install") self.run_in_venv(venv, unpacked, "python", "setup.py", "install") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_wheel(self): unpacked = self.make_distutils_unpacked() @@ -668,19 +676,19 @@ def test_pip_install(self): repodir = self.make_distutils_unpacked() venv = self.make_venv("distutils-unpacked-pip-install") self.run_in_venv(venv, repodir, "pip", "install", ".") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_install_subproject(self): unpacked = self.make_distutils_subproject_unpacked() venv = self.make_venv("distutils-subproject-unpacked-pip-install") self.run_in_venv(venv, unpacked, "pip", "install", ".") - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) def test_pip_install_from_afar(self): repodir = self.make_distutils_unpacked() venv = self.make_venv("distutils-unpacked-pip-install-from-afar") self.run_in_venv(venv, venv, "pip", "install", repodir) - self.check_in_venv(venv) + self.check_in_venv(venv, use_python=True) class SetuptoolsUnpacked(_Invocations, unittest.TestCase): def test_install(self): diff --git a/tox.ini b/tox.ini index 79826357..fb0191e5 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py36,py37,py38,py39,py310,pypy3 +envlist = py{36,37,38,39,310,py3}-{linux,windows} skip_missing_interpreters = True [flake8] @@ -19,13 +19,18 @@ python = 3.10: py310 pypy-3.7: pypy3 +[gh-actions:env] +PLATFORM = + ubuntu-latest: linux + windows-2019: windows + [testenv] deps = flake8 flake8-docstrings wheel>=0.35 setuptools>=50 - virtualenv<20 + virtualenv>=20 packaging>=20 pip>=20 !pypy3: mypy