test_install_vcs_git.py 19.3 KB
Newer Older
1 2
from typing import Optional

3
import pytest
4

5
from pip._internal.utils.urls import path_to_url
6
from tests.lib import pyversion  # noqa: F401
P
Pradyun S. Gedam 已提交
7
from tests.lib import (
8
    PipTestEnvironment,
9 10 11
    _change_test_package_version,
    _create_test_package,
    _test_path_to_file_url,
P
Pradyun S. Gedam 已提交
12
)
13
from tests.lib.git_submodule_helpers import (
14 15
    _change_test_package_submodule,
    _create_test_package_with_submodule,
16 17
    _pull_in_submodule_changes_to_module,
)
18
from tests.lib.local_repos import local_checkout
19
from tests.lib.path import Path
20

21

22
def _get_editable_repo_dir(script: PipTestEnvironment, package_name: str) -> Path:
C
Chris Jerdonek 已提交
23 24 25
    """
    Return the repository directory for an editable install.
    """
26
    return script.venv_path / "src" / package_name
C
Chris Jerdonek 已提交
27 28


29
def _get_editable_branch(script: PipTestEnvironment, package_name: str) -> str:
30 31 32
    """
    Return the current branch of an editable install.
    """
C
Chris Jerdonek 已提交
33
    repo_dir = _get_editable_repo_dir(script, package_name)
34
    result = script.run("git", "rev-parse", "--abbrev-ref", "HEAD", cwd=repo_dir)
C
Chris Jerdonek 已提交
35 36
    return result.stdout.strip()

37

38 39 40
def _get_branch_remote(
    script: PipTestEnvironment, package_name: str, branch: str
) -> str:
41
    """ """
C
Chris Jerdonek 已提交
42
    repo_dir = _get_editable_repo_dir(script, package_name)
43
    result = script.run("git", "config", f"branch.{branch}.remote", cwd=repo_dir)
44 45 46
    return result.stdout.strip()


47 48 49 50 51 52 53
def _github_checkout(
    url_path: str,
    temp_dir: Path,
    rev: Optional[str] = None,
    egg: Optional[str] = None,
    scheme: Optional[str] = None,
) -> str:
54
    """
55
    Call local_checkout() with a GitHub URL, and return the resulting URL.
56 57

    Args:
58 59
      url_path: the string used to create the package URL by filling in the
        format string "git+{scheme}://github.com/{url_path}".
60 61 62
      temp_dir: the pytest tmpdir value.
      egg: an optional project name to append to the URL as the egg fragment,
        prior to returning.
63
      scheme: the scheme without the "git+" prefix. Defaults to "https".
64
    """
65
    if scheme is None:
66 67
        scheme = "https"
    url = f"git+{scheme}://github.com/{url_path}"
68
    local_url = local_checkout(url, temp_dir)
69
    if rev is not None:
70
        local_url += f"@{rev}"
71
    if egg is not None:
72
        local_url += f"#egg={egg}"
73

74
    return local_url
75 76


77 78 79
def _make_version_pkg_url(
    path: Path, rev: Optional[str] = None, name: str = "version_pkg"
) -> str:
80 81 82 83 84 85 86 87
    """
    Return a "git+file://" URL to the version_pkg test package.

    Args:
      path: a tests.lib.path.Path object pointing to a Git repository
        containing the version_pkg package.
      rev: an optional revision to install like a branch name, tag, or SHA.
    """
C
Chris Jerdonek 已提交
88
    file_url = _test_path_to_file_url(path)
89 90
    url_rev = "" if rev is None else f"@{rev}"
    url = f"git+{file_url}{url_rev}#egg={name}"
91 92 93 94

    return url


95 96 97 98 99 100
def _install_version_pkg_only(
    script: PipTestEnvironment,
    path: Path,
    rev: Optional[str] = None,
    expect_stderr: bool = False,
) -> None:
101
    """
102 103
    Install the version_pkg package in editable mode (without returning
    the version).
104 105 106 107

    Args:
      path: a tests.lib.path.Path object pointing to a Git repository
        containing the package.
108
      rev: an optional revision to install like a branch name or tag.
109
    """
110
    version_pkg_url = _make_version_pkg_url(path, rev=rev)
111
    script.pip("install", "-e", version_pkg_url, expect_stderr=expect_stderr)
112 113


114 115 116 117 118 119
def _install_version_pkg(
    script: PipTestEnvironment,
    path: Path,
    rev: Optional[str] = None,
    expect_stderr: bool = False,
) -> str:
120 121 122 123 124 125 126 127 128 129
    """
    Install the version_pkg package in editable mode, and return the version
    installed.

    Args:
      path: a tests.lib.path.Path object pointing to a Git repository
        containing the package.
      rev: an optional revision to install like a branch name or tag.
    """
    _install_version_pkg_only(
130 131 132 133
        script,
        path,
        rev=rev,
        expect_stderr=expect_stderr,
134
    )
135
    result = script.run("version_pkg")
136 137 138 139 140
    version = result.stdout.strip()

    return version


141
def test_git_install_again_after_changes(script: PipTestEnvironment) -> None:
142 143 144 145 146 147 148 149 150 151
    """
    Test installing a repository a second time without specifying a revision,
    and after updates to the remote repository.

    This test also checks that no warning message like the following gets
    logged on the update: "Did not find branch or tag ..., assuming ref or
    revision."
    """
    version_pkg_path = _create_test_package(script)
    version = _install_version_pkg(script, version_pkg_path)
152
    assert version == "0.1"
153 154 155

    _change_test_package_version(script, version_pkg_path)
    version = _install_version_pkg(script, version_pkg_path)
156
    assert version == "some different version"
157 158


159 160 161
def test_git_install_branch_again_after_branch_changes(
    script: PipTestEnvironment,
) -> None:
162 163 164 165 166
    """
    Test installing a branch again after the branch is updated in the remote
    repository.
    """
    version_pkg_path = _create_test_package(script)
167 168
    version = _install_version_pkg(script, version_pkg_path, rev="master")
    assert version == "0.1"
169 170

    _change_test_package_version(script, version_pkg_path)
171 172
    version = _install_version_pkg(script, version_pkg_path, rev="master")
    assert version == "some different version"
173 174


175
@pytest.mark.network
176 177 178
def test_install_editable_from_git_with_https(
    script: PipTestEnvironment, tmpdir: Path
) -> None:
J
Jannis Leidel 已提交
179 180 181
    """
    Test cloning from Git with https.
    """
182 183 184 185
    url_path = "pypa/pip-test-package.git"
    local_url = _github_checkout(url_path, tmpdir, egg="pip-test-package")
    result = script.pip("install", "-e", local_url)
    result.assert_installed("pip-test-package", with_files=[".git"])
J
Jannis Leidel 已提交
186

H
Hugo Lopes Tavares 已提交
187

188
@pytest.mark.network
189
@pytest.mark.usefixtures("with_wheel")
190
def test_install_noneditable_git(script: PipTestEnvironment) -> None:
191 192 193 194
    """
    Test installing from a non-editable git URL with a given tag.
    """
    result = script.pip(
195 196 197
        "install",
        "git+https://github.com/pypa/pip-test-package.git"
        "@0.1.1#egg=pip-test-package",
M
Marcus Smith 已提交
198
    )
199 200
    dist_info_folder = script.site_packages / "pip_test_package-0.1.1.dist-info"
    result.assert_installed("piptestpackage", without_egg_link=True, editable=False)
201
    result.did_create(dist_info_folder)
202 203


204
def test_git_with_sha1_revisions(script: PipTestEnvironment) -> None:
H
Hugo Lopes Tavares 已提交
205 206 207
    """
    Git backend should be able to install from SHA1 revisions
    """
D
Donald Stufft 已提交
208 209
    version_pkg_path = _create_test_package(script)
    _change_test_package_version(script, version_pkg_path)
210
    sha1 = script.run(
211 212 213
        "git",
        "rev-parse",
        "HEAD~1",
214 215
        cwd=version_pkg_path,
    ).stdout.strip()
216
    version = _install_version_pkg(script, version_pkg_path, rev=sha1)
217
    assert "0.1" == version
218 219


220
def test_git_with_short_sha1_revisions(script: PipTestEnvironment) -> None:
221 222 223 224 225 226
    """
    Git backend should be able to install from SHA1 revisions
    """
    version_pkg_path = _create_test_package(script)
    _change_test_package_version(script, version_pkg_path)
    sha1 = script.run(
227 228 229
        "git",
        "rev-parse",
        "HEAD~1",
230 231
        cwd=version_pkg_path,
    ).stdout.strip()[:7]
232
    version = _install_version_pkg(script, version_pkg_path, rev=sha1)
233
    assert "0.1" == version
H
Hugo Lopes Tavares 已提交
234

235

236
def test_git_with_branch_name_as_revision(script: PipTestEnvironment) -> None:
237 238 239
    """
    Git backend should be able to install from branch names
    """
D
Donald Stufft 已提交
240
    version_pkg_path = _create_test_package(script)
241 242
    branch = "test_branch"
    script.run("git", "checkout", "-b", branch, cwd=version_pkg_path)
D
Donald Stufft 已提交
243
    _change_test_package_version(script, version_pkg_path)
244
    version = _install_version_pkg(script, version_pkg_path, rev=branch)
245
    assert "some different version" == version
246

247

248
def test_git_with_tag_name_as_revision(script: PipTestEnvironment) -> None:
249 250 251
    """
    Git backend should be able to install from tag names
    """
D
Donald Stufft 已提交
252
    version_pkg_path = _create_test_package(script)
253
    script.run("git", "tag", "test_tag", cwd=version_pkg_path)
D
Donald Stufft 已提交
254
    _change_test_package_version(script, version_pkg_path)
255 256
    version = _install_version_pkg(script, version_pkg_path, rev="test_tag")
    assert "0.1" == version
257

258

259
def _add_ref(script: PipTestEnvironment, path: Path, ref: str) -> None:
260
    """
C
Chris Jerdonek 已提交
261
    Add a new ref to a repository at the given path.
262
    """
263
    script.run("git", "update-ref", ref, "HEAD", cwd=path)
C
Chris Jerdonek 已提交
264 265


266
def test_git_install_ref(script: PipTestEnvironment) -> None:
C
Chris Jerdonek 已提交
267 268 269
    """
    The Git backend should be able to install a ref with the first install.
    """
270
    version_pkg_path = _create_test_package(script)
271
    _add_ref(script, version_pkg_path, "refs/foo/bar")
272
    _change_test_package_version(script, version_pkg_path)
C
Chris Jerdonek 已提交
273

274
    version = _install_version_pkg(
275 276 277
        script,
        version_pkg_path,
        rev="refs/foo/bar",
278
    )
279
    assert "0.1" == version
C
Chris Jerdonek 已提交
280 281


282
def test_git_install_then_install_ref(script: PipTestEnvironment) -> None:
C
Chris Jerdonek 已提交
283 284 285 286
    """
    The Git backend should be able to install a ref after a package has
    already been installed.
    """
287
    version_pkg_path = _create_test_package(script)
288
    _add_ref(script, version_pkg_path, "refs/foo/bar")
289
    _change_test_package_version(script, version_pkg_path)
C
Chris Jerdonek 已提交
290

291
    version = _install_version_pkg(script, version_pkg_path)
292
    assert "some different version" == version
C
Chris Jerdonek 已提交
293 294

    # Now install the ref.
295
    version = _install_version_pkg(
296 297 298
        script,
        version_pkg_path,
        rev="refs/foo/bar",
299
    )
300
    assert "0.1" == version
301 302


303
@pytest.mark.network
304 305 306 307 308 309 310 311 312 313 314 315 316 317
@pytest.mark.parametrize(
    "rev, expected_sha",
    [
        # Clone the default branch
        ("", "5547fa909e83df8bd743d3978d6667497983a4b7"),
        # Clone a specific tag
        ("@0.1.1", "7d654e66c8fa7149c165ddeffa5b56bc06619458"),
        # Clone a specific commit
        (
            "@65cf0a5bdd906ecf48a0ac241c17d656d2071d56",
            "65cf0a5bdd906ecf48a0ac241c17d656d2071d56",
        ),
    ],
)
318 319 320
def test_install_git_logs_commit_sha(
    script: PipTestEnvironment, rev: str, expected_sha: str, tmpdir: Path
) -> None:
321 322 323 324 325 326 327 328 329 330 331
    """
    Test installing from a git repository logs a commit SHA.
    """
    url_path = "pypa/pip-test-package.git"
    base_local_url = _github_checkout(url_path, tmpdir)
    local_url = f"{base_local_url}{rev}#egg=pip-test-package"
    result = script.pip("install", local_url)
    # `[4:]` removes a 'git+' prefix
    assert f"Resolved {base_local_url[4:]} to commit {expected_sha}" in result.stdout


332
@pytest.mark.network
333
def test_git_with_tag_name_and_update(script: PipTestEnvironment, tmpdir: Path) -> None:
334 335 336
    """
    Test cloning a git repository and updating to a different version.
    """
337
    url_path = "pypa/pip-test-package.git"
338 339
    base_local_url = _github_checkout(url_path, tmpdir)

340 341 342
    local_url = f"{base_local_url}#egg=pip-test-package"
    result = script.pip("install", "-e", local_url)
    result.assert_installed("pip-test-package", with_files=[".git"])
343

344
    new_local_url = f"{base_local_url}@0.1.2#egg=pip-test-package"
345
    result = script.pip(
346 347 348 349
        "install",
        "--global-option=--version",
        "-e",
        new_local_url,
350
        allow_stderr_warning=True,
351
    )
352
    assert "0.1.2" in result.stdout
353

354

355
@pytest.mark.network
356 357 358
def test_git_branch_should_not_be_changed(
    script: PipTestEnvironment, tmpdir: Path
) -> None:
359 360 361 362
    """
    Editable installations should not change branch
    related to issue #32 and #161
    """
363 364 365 366 367
    url_path = "pypa/pip-test-package.git"
    local_url = _github_checkout(url_path, tmpdir, egg="pip-test-package")
    script.pip("install", "-e", local_url)
    branch = _get_editable_branch(script, "pip-test-package")
    assert "master" == branch
368

369

370
@pytest.mark.network
371 372 373
def test_git_with_non_editable_unpacking(
    script: PipTestEnvironment, tmpdir: Path
) -> None:
374 375 376
    """
    Test cloning a git repository from a non-editable URL with a given tag.
    """
377
    url_path = "pypa/pip-test-package.git"
378
    local_url = _github_checkout(
379 380 381 382
        url_path,
        tmpdir,
        rev="0.1.2",
        egg="pip-test-package",
383
    )
384 385 386 387 388 389
    result = script.pip(
        "install",
        "--global-option=--version",
        local_url,
        allow_stderr_warning=True,
    )
390
    assert "0.1.2" in result.stdout
391

392

393
@pytest.mark.network
394 395 396
def test_git_with_editable_where_egg_contains_dev_string(
    script: PipTestEnvironment, tmpdir: Path
) -> None:
397
    """
398 399
    Test cloning a git repository from an editable url which contains "dev"
    string
400
    """
401
    url_path = "dcramer/django-devserver.git"
402
    local_url = _github_checkout(
403 404 405 406
        url_path,
        tmpdir,
        egg="django-devserver",
        scheme="git",
407
    )
408 409
    result = script.pip("install", "-e", local_url)
    result.assert_installed("django-devserver", with_files=[".git"])
410

411

412
@pytest.mark.network
413 414 415
def test_git_with_non_editable_where_egg_contains_dev_string(
    script: PipTestEnvironment, tmpdir: Path
) -> None:
416
    """
417 418
    Test cloning a git repository from a non-editable url which contains "dev"
    string
419
    """
420
    url_path = "dcramer/django-devserver.git"
421
    local_url = _github_checkout(
422 423 424 425
        url_path,
        tmpdir,
        egg="django-devserver",
        scheme="git",
426
    )
427 428
    result = script.pip("install", local_url)
    devserver_folder = script.site_packages / "devserver"
429
    result.did_create(devserver_folder)
430 431


432
def test_git_with_ambiguous_revs(script: PipTestEnvironment) -> None:
433 434 435
    """
    Test git with two "names" (tag/branch) pointing to the same commit
    """
D
Donald Stufft 已提交
436
    version_pkg_path = _create_test_package(script)
437 438 439 440
    version_pkg_url = _make_version_pkg_url(version_pkg_path, rev="0.1")
    script.run("git", "tag", "0.1", cwd=version_pkg_path)
    result = script.pip("install", "-e", version_pkg_url)
    assert "Could not find a tag or branch" not in result.stdout
441 442
    # it is 'version-pkg' instead of 'version_pkg' because
    # egg-link name is version-pkg.egg-link because it is a single .py module
443
    result.assert_installed("version-pkg", with_files=[".git"])
R
test  
Rory McCann 已提交
444

445

446
def test_editable__no_revision(script: PipTestEnvironment) -> None:
C
Chris Jerdonek 已提交
447 448 449 450 451 452
    """
    Test a basic install in editable mode specifying no revision.
    """
    version_pkg_path = _create_test_package(script)
    _install_version_pkg_only(script, version_pkg_path)

453 454
    branch = _get_editable_branch(script, "version-pkg")
    assert branch == "master"
C
Chris Jerdonek 已提交
455

456 457
    remote = _get_branch_remote(script, "version-pkg", "master")
    assert remote == "origin"
C
Chris Jerdonek 已提交
458 459


460
def test_editable__branch_with_sha_same_as_default(script: PipTestEnvironment) -> None:
C
Chris Jerdonek 已提交
461 462
    """
    Test installing in editable mode a branch whose sha matches the sha
463
    of the default branch, but is different from the default branch.
C
Chris Jerdonek 已提交
464 465 466
    """
    version_pkg_path = _create_test_package(script)
    # Create a second branch with the same SHA.
467 468
    script.run("git", "branch", "develop", cwd=version_pkg_path)
    _install_version_pkg_only(script, version_pkg_path, rev="develop")
C
Chris Jerdonek 已提交
469

470 471
    branch = _get_editable_branch(script, "version-pkg")
    assert branch == "develop"
C
Chris Jerdonek 已提交
472

473 474
    remote = _get_branch_remote(script, "version-pkg", "develop")
    assert remote == "origin"
C
Chris Jerdonek 已提交
475 476


477 478 479
def test_editable__branch_with_sha_different_from_default(
    script: PipTestEnvironment,
) -> None:
C
Chris Jerdonek 已提交
480 481 482 483 484 485
    """
    Test installing in editable mode a branch whose sha is different from
    the sha of the default branch.
    """
    version_pkg_path = _create_test_package(script)
    # Create a second branch.
486
    script.run("git", "branch", "develop", cwd=version_pkg_path)
C
Chris Jerdonek 已提交
487 488 489
    # Add another commit to the master branch to give it a different sha.
    _change_test_package_version(script, version_pkg_path)

490 491
    version = _install_version_pkg(script, version_pkg_path, rev="develop")
    assert version == "0.1"
C
Chris Jerdonek 已提交
492

493 494
    branch = _get_editable_branch(script, "version-pkg")
    assert branch == "develop"
C
Chris Jerdonek 已提交
495

496 497
    remote = _get_branch_remote(script, "version-pkg", "develop")
    assert remote == "origin"
C
Chris Jerdonek 已提交
498 499


500
def test_editable__non_master_default_branch(script: PipTestEnvironment) -> None:
501 502 503 504 505 506 507
    """
    Test the branch you get after an editable install from a remote repo
    with a non-master default branch.
    """
    version_pkg_path = _create_test_package(script)
    # Change the default branch of the remote repo to a name that is
    # alphabetically after "master".
508
    script.run("git", "checkout", "-b", "release", cwd=version_pkg_path)
509
    _install_version_pkg_only(script, version_pkg_path)
C
Chris Jerdonek 已提交
510

511 512
    branch = _get_editable_branch(script, "version-pkg")
    assert branch == "release"
513 514


515 516 517
def test_reinstalling_works_with_editable_non_master_branch(
    script: PipTestEnvironment,
) -> None:
518 519 520 521 522 523 524
    """
    Reinstalling an editable installation should not assume that the "master"
    branch exists. See https://github.com/pypa/pip/issues/4448.
    """
    version_pkg_path = _create_test_package(script)

    # Switch the default branch to something other than 'master'
525
    script.run("git", "branch", "-m", "foobar", cwd=version_pkg_path)
526

527
    version = _install_version_pkg(script, version_pkg_path)
528
    assert "0.1" == version
529 530

    _change_test_package_version(script, version_pkg_path)
531
    version = _install_version_pkg(script, version_pkg_path)
532
    assert "some different version" == version
533 534 535 536


# TODO(pnasrat) fix all helpers to do right things with paths on windows.
@pytest.mark.skipif("sys.platform == 'win32'")
537
def test_check_submodule_addition(script: PipTestEnvironment) -> None:
538 539 540
    """
    Submodules are pulled in on install and updated on upgrade.
    """
541 542
    module_path, submodule_path = _create_test_package_with_submodule(
        script, rel_path="testpkg/static"
543
    )
544 545

    install_result = script.pip(
546
        "install", "-e", "git+" + path_to_url(module_path) + "#egg=version_pkg"
547
    )
548
    install_result.did_create(script.venv / "src/version-pkg/testpkg/static/testfile")
549 550

    _change_test_package_submodule(script, submodule_path)
551
    _pull_in_submodule_changes_to_module(
552 553 554
        script,
        module_path,
        rel_path="testpkg/static",
555
    )
556 557 558

    # expect error because git may write to stderr
    update_result = script.pip(
559 560 561 562
        "install",
        "-e",
        "git+" + path_to_url(module_path) + "#egg=version_pkg",
        "--upgrade",
563 564
    )

565
    update_result.did_create(script.venv / "src/version-pkg/testpkg/static/testfile2")
566 567


568
@pytest.mark.usefixtures("with_wheel")
569
def test_install_git_branch_not_cached(script: PipTestEnvironment) -> None:
570 571 572 573 574 575 576
    """
    Installing git urls with a branch revision does not cause wheel caching.
    """
    PKG = "gitbranchnotcached"
    repo_dir = _create_test_package(script, name=PKG)
    url = _make_version_pkg_url(repo_dir, rev="master", name=PKG)
    result = script.pip("install", url, "--only-binary=:all:")
577
    assert f"Successfully built {PKG}" in result.stdout, result.stdout
578 579 580
    script.pip("uninstall", "-y", PKG)
    # build occurs on the second install too because it is not cached
    result = script.pip("install", url)
581
    assert f"Successfully built {PKG}" in result.stdout, result.stdout
582 583


584
@pytest.mark.usefixtures("with_wheel")
585
def test_install_git_sha_cached(script: PipTestEnvironment) -> None:
586 587 588 589 590
    """
    Installing git urls with a sha revision does cause wheel caching.
    """
    PKG = "gitshacached"
    repo_dir = _create_test_package(script, name=PKG)
591
    commit = script.run("git", "rev-parse", "HEAD", cwd=repo_dir).stdout.strip()
592 593
    url = _make_version_pkg_url(repo_dir, rev=commit, name=PKG)
    result = script.pip("install", url)
594
    assert f"Successfully built {PKG}" in result.stdout, result.stdout
595 596 597
    script.pip("uninstall", "-y", PKG)
    # build does not occur on the second install because it is cached
    result = script.pip("install", url)
598
    assert f"Successfully built {PKG}" not in result.stdout, result.stdout