8000 Pytest plugin: VCS Configuration for `hg commit` and `git commit` by tony · Pull Request #476 · vcs-python/libvcs · GitHub
[go: up one dir, main page]

Skip to content

Pytest plugin: VCS Configuration for hg commit and git commit #476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,25 @@ $ pip install --user --upgrade --pre libvcs

<!-- Maintainers, insert changes / features for the next release here -->

### New features

#### pytest plugin: Authorship fixtures (#476)

- New, customizable session-scoped fixtures for default committer on Mercurial and Git:
- Name: {func}`libvcs.pytest_plugin.vcs_name`
- Email: {func}`libvcs.pytest_plugin.vcs_email`
- User (e.g. _`user <email@tld>`_): {func}`libvcs.pytest_plugin.vcs_user`
- For git only: {func}`libvcs.pytest_plugin.git_commit_envvars`

#### pytest plugins: Default repos use authorship fixtures (#476)

New repos will automatically apply these session-scoped fixtures.

## libvcs 0.32.3 (2024-10-13)

### Bug fixes

- Pytest fixtures `hg_remote_repo_single_commit_post_init()` and `git_remote_repo_single_commit_post_init()` now support passing `env` for VCS configuration.
- Pytest fixtures `hg_remote_repo_single_commit_post_init()` and `git_remote_repo_single_commit_post_init()` now support passing `env` for VCS configuration.

Both functions accept `hgconfig` and `gitconfig` fixtures, now applied in the `hg_repo` and `git_repo` fixtures.

Expand Down
39 changes: 37 additions & 2 deletions docs/pytest-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ This pytest plugin works by providing {ref}`pytest fixtures <pytest:fixtures-api

## Recommended Fixtures

When the plugin is enabled and `pytest` is run, these fixtures are automatically used:
When the plugin is enabled and `pytest` is run, these overridable fixtures are automatically used:

- Create temporary test directories for:
- `/home/` ({func}`home_path`)
Expand All @@ -50,6 +50,11 @@ When the plugin is enabled and `pytest` is run, these fixtures are automatically
- Set default VCS configurations:
- Use {func}`hgconfig` for [`HGRCPATH`] via {func}`set_hgconfig`
- Use {func}`gitconfig` for [`GIT_CONFIG`] via {func}`set_gitconfig`
- Set default commit names and emails:
- Name: {func}`vcs_name`
- Email: {func}`vcs_email`
- User (e.g. _`user <email@tld>`_): {func}`vcs_user`
- For git only: {func}`git_commit_envvars`

These ensure that repositories can be cloned and created without unnecessary warnings.

Expand All @@ -74,10 +79,19 @@ def setup(set_home: None):
pass
```

### Setting a Default VCS Configuration
### VCS Configuration

#### Git

You can override the default author used in {func}`git_remote_repo` and other
fixtures via {func}`vcs_name`, {func}`vcs_email`, and {func}`vcs_user`:

```
@pytest.fixture(scope="session")
def vcs_name() -> str:
return "My custom name"
```

Use the {func}`set_gitconfig` fixture with `autouse=True`:

```python
Expand All @@ -88,6 +102,27 @@ def setup(set_gitconfig: None):
pass
```

Sometimes, `set_getconfig` via `GIT_CONFIG` doesn't apply as expected. For those
cases, you can use {func}`git_commit_envvars`:

```python
import pytest

@pytest.fixture
def my_git_repo(
create_git_remote_repo: CreateRepoPytestFixtureFn,
gitconfig: pathlib.Path,
git_commit_envvars: "_ENV",
) -> pathlib.Path:
"""Copy the session-scoped Git repository to a temporary directory."""
repo_path = create_git_remote_repo()
git_remote_repo_single_commit_post_init(
remote_repo_path=repo_path,
env=git_commit_envvars,
)
return repo_path
```

#### Mercurial

Use the {func}`set_hgconfig` fixture with `autouse=True`:
Expand Down
92 changes: 75 additions & 17 deletions src/libvcs/pytest_plugin.py
F438
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,43 @@ def __init__(self, attempts: int, *args: object) -> None:
)


DEFAULT_VCS_NAME = "Test user"
DEFAULT_VCS_EMAIL = "test@example.com"


@pytest.fixture(scope="session")
def vcs_name() -> str:
"""Return default VCS name."""
return DEFAULT_VCS_NAME


@pytest.fixture(scope="session")
def vcs_email() -> str:
"""Return default VCS email."""
return DEFAULT_VCS_EMAIL


@pytest.fixture(scope="session")
def vcs_user(vcs_name: str, vcs_email: str) -> str:
"""Return default VCS user."""
return f"{vcs_name} <{vcs_email}>"


@pytest.fixture(scope="session")
def git_commit_envvars(vcs_name: str, vcs_email: str) -> "_ENV":
"""Return environment variables for `git commit`.

For some reason, `GIT_CONFIG` via {func}`set_gitconfig` doesn't work for `git
commit`.
"""
return {
"GIT_AUTHOR_NAME": vcs_name,
"GIT_AUTHOR_EMAIL": vcs_email,
"GIT_COMMITTER_NAME": vcs_name,
"GIT_COMMITTER_EMAIL": vcs_email,
}


class RandomStrSequence:
"""Create a random string sequence."""

Expand Down Expand Up @@ -110,13 +147,12 @@ def set_home(
monkeypatch.setenv("HOME", str(user_path))


vcs_email = "libvcs@git-pull.com"


@pytest.fixture(scope="session")
@skip_if_git_missing
def gitconfig(
user_path: pathlib.Path,
vcs_email: str,
vcs_name: str,
) -> pathlib.Path:
"""Return git configuration, pytest fixture."""
gitconfig = user_path / ".gitconfig"
Expand All @@ -129,7 +165,7 @@ def gitconfig(
f"""
[user]
email = {vcs_email}
name = {getpass.getuser()}
name = {vcs_name}
[color]
diff = auto
""",
Expand All @@ -155,14 +191,15 @@ def set_gitconfig(
@skip_if_hg_missing
def hgconfig(
user_path: pathlib.Path,
vcs_user: str,
) -> pathlib.Path:
"""Return Mercurial configuration."""
hgrc = user_path / ".hgrc"
hgrc.write_text(
textwrap.dedent(
f"""
[ui]
username = libvcs tests <libvcs@git-pull.com>
username = {vcs_user}
merge = internal:merge

[trusted]
Expand Down Expand Up @@ -237,7 +274,11 @@ def unique_repo_name(remote_repos_path: pathlib.Path, max_retries: int = 15) ->
class CreateRepoPostInitFn(Protocol):
"""Typing for VCS repo creation callback."""

def __call__(self, remote_repo_path: pathlib.Path) -> None:
def __call__(
self,
remote_repo_path: pathlib.Path,
env: "_ENV | None" = None,
) -> None:
"""Ran after creating a repo from pytest fixture."""
...

Expand All @@ -263,6 +304,7 @@ def _create_git_remote_repo(
remote_repo_path: pathlib.Path,
remote_repo_post_init: Optional[CreateRepoPostInitFn] = None,
init_cmd_args: InitCmdArgs = DEFAULT_GIT_REMOTE_REPO_CMD_ARGS,
env: "_ENV | None" = None,
) -> pathlib.Path:
if init_cmd_args is None:
init_cmd_args = []
Expand All @@ -272,7 +314,7 @@ def _create_git_remote_repo(
)

if remote_repo_post_init is not None and callable(remote_repo_post_init):
remote_repo_post_init(remote_repo_path=remote_repo_path)
remote_repo_post_init(remote_repo_path=remote_repo_path, env=env)

return remote_repo_path

Expand Down Expand Up @@ -402,26 +444,29 @@ def git_remote_repo_single_commit_post_init(
run(
["touch", testfile_filename],
cwd=remote_repo_path,
env={"GITCONFIG": str(gitconfig)},
env=env,
)
run(["git", "add", testfile_filename], cwd=remote_repo_path, env=env)
run(
["git", "commit", "-m", "test file for dummyrepo"],
cwd=remote_repo_path,
env=env,
)
run(["git", "add", testfile_filename], cwd=remote_repo_path)
run(["git", "commit", "-m", "test file for dummyrepo"], cwd=remote_repo_path)


@pytest.fixture(scope="session")
@skip_if_git_missing
def git_remote_repo(
create_git_remote_repo: CreateRepoPytestFixtureFn,
gitconfig: pathlib.Path,
git_commit_envvars: "_ENV",
) -> pathlib.Path:
"""Copy the session-scoped Git repository to a temporary directory."""
# TODO: Cache the effect of of this in a session-based repo
repo_path = create_git_remote_repo()
git_remote_repo_single_commit_post_init(
remote_repo_path=repo_path,
env={
"GITCONFIG": str(gitconfig),
},
env=git_commit_envvars,
)
return repo_path

Expand Down Expand Up @@ -596,6 +641,7 @@ def empty_hg_repo(
def create_hg_remote_repo(
remote_repos_path: pathlib.Path,
empty_hg_repo: pathlib.Path,
hgconfig: pathlib.Path,
) -> CreateRepoPytestFixtureFn:
"""Pre-made hg repo, bare, used as a file:// remote to checkout and commit to."""

Expand All @@ -612,7 +658,10 @@ def fn(
shutil.copytree(empty_hg_repo, remote_repo_path)

if remote_repo_post_init is not None and callable(remote_repo_post_init):
remote_repo_post_init(remote_repo_path=remote_repo_path)
remote_repo_post_init(
remote_repo_path=remote_repo_path,
env={"HGRCPATH": str(hgconfig)},
)

assert empty_hg_repo.exists()

Expand All @@ -633,7 +682,8 @@ def hg_remote_repo(
"""Pre-made, file-based repo for push and pull."""
repo_path = create_hg_remote_repo()
hg_remote_repo_single_commit_post_init(
remote_repo_path=repo_path, env={"HGRCPATH": str(hgconfig)}
remote_repo_path=repo_path,
env={"HGRCPATH": str(hgconfig)},
)
return repo_path

Expand Down Expand Up @@ -731,6 +781,8 @@ def add_doctest_fixtures(
doctest_namespace: dict[str, Any],
tmp_path: pathlib.Path,
set_home: pathlib.Path,
git_commit_envvars: "_ENV",
hgconfig: pathlib.Path,
create_git_remote_repo: CreateRepoPytestFixtureFn,
create_svn_remote_repo: CreateRepoPytestFixtureFn,
create_hg_remote_repo: CreateRepoPytestFixtureFn,
Expand All @@ -745,7 +797,10 @@ def add_doctest_fixtures(
if shutil.which("git"):
doctest_namespace["create_git_remote_repo"] = functools.partial(
create_git_remote_repo,
remote_repo_post_init=git_remote_repo_single_commit_post_init,
remote_repo_post_init=functools.partial(
git_remote_repo_single_commit_post_init,
env=git_commit_envvars,
),
init_cmd_args=None,
)
doctest_namespace["create_git_remote_repo_bare"] = create_git_remote_repo
Expand All @@ -760,5 +815,8 @@ def add_doctest_fixtures(
doctest_namespace["create_hg_remote_repo_bare"] = create_hg_remote_repo
doctest_namespace["create_hg_remote_repo"] = functools.partial(
create_hg_remote_repo,
remote_repo_post_init=hg_remote_repo_single_commit_post_init,
remote_repo_post_init=functools.partial(
hg_remote_repo_single_commit_post_init,
env={"HGRCPATH": str(hgconfig)},
),
)
Loading
0