labops 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- labops-0.3.2/.release-please-manifest.json +3 -0
- {labops-0.3.0 → labops-0.3.2}/CHANGELOG.md +14 -0
- {labops-0.3.0 → labops-0.3.2}/PKG-INFO +2 -2
- {labops-0.3.0 → labops-0.3.2}/README.md +1 -1
- {labops-0.3.0 → labops-0.3.2}/labops_cli.py +2 -2
- {labops-0.3.0/models/inputConf → labops-0.3.2/models/input_conf}/hosts.py +1 -1
- {labops-0.3.0/models/inputConf → labops-0.3.2/models/input_conf}/settings.py +1 -1
- labops-0.3.0/models/inputConf/YamlRoot.py → labops-0.3.2/models/input_conf/yaml_root.py +2 -2
- {labops-0.3.0 → labops-0.3.2}/pyproject.toml +4 -1
- {labops-0.3.0 → labops-0.3.2}/src/cli/core.py +1 -1
- {labops-0.3.0 → labops-0.3.2}/src/cli/host.py +5 -5
- {labops-0.3.0 → labops-0.3.2}/src/cli/lxc.py +4 -4
- {labops-0.3.0 → labops-0.3.2}/src/cli/validate.py +2 -2
- {labops-0.3.0 → labops-0.3.2}/src/cli/vm.py +2 -2
- {labops-0.3.0 → labops-0.3.2}/src/host/find.py +2 -2
- {labops-0.3.0 → labops-0.3.2}/src/host/setup.py +6 -4
- {labops-0.3.0 → labops-0.3.2}/src/host/update.py +7 -5
- {labops-0.3.0 → labops-0.3.2}/src/lxc/find.py +2 -2
- {labops-0.3.0 → labops-0.3.2}/src/lxc/update.py +4 -4
- {labops-0.3.0 → labops-0.3.2}/src/utils/ansible_runner.py +7 -6
- {labops-0.3.0 → labops-0.3.2}/src/utils/yaml_validator.py +1 -1
- {labops-0.3.0 → labops-0.3.2}/src/vm/find.py +2 -2
- {labops-0.3.0 → labops-0.3.2}/uv.lock +1 -1
- labops-0.3.0/.release-please-manifest.json +0 -3
- {labops-0.3.0 → labops-0.3.2}/.devcontainer/devcontainer.json +0 -0
- {labops-0.3.0 → labops-0.3.2}/.github/dependabot.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/.github/workflows/publish.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/.github/workflows/release_please.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/.gitignore +0 -0
- {labops-0.3.0 → labops-0.3.2}/.pre-commit-config.yaml +0 -0
- {labops-0.3.0 → labops-0.3.2}/.python-version +0 -0
- {labops-0.3.0 → labops-0.3.2}/LICENSE +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/host/setup/alpine.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/host/setup/common.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/host/setup/debian.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/host/setup/redhat.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/host/setup.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/host/update/alpine.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/host/update/debian.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/host/update/redhat.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/host/update.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/lxc/update/alpine.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/lxc/update/debian.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/lxc/update/redhat.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/playbooks/lxc/update.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/ansible/requirements.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/img/Cover.png +0 -0
- {labops-0.3.0 → labops-0.3.2}/justfile +0 -0
- {labops-0.3.0/models/inputConf → labops-0.3.2/models/input_conf}/creds.py +0 -0
- {labops-0.3.0 → labops-0.3.2}/release-please-config.json +0 -0
- {labops-0.3.0 → labops-0.3.2}/src/host/__init__.py +0 -0
- {labops-0.3.0 → labops-0.3.2}/src/lxc/__init__.py +0 -0
- {labops-0.3.0 → labops-0.3.2}/src/utils/__init__.py +0 -0
- {labops-0.3.0 → labops-0.3.2}/src/vm/__init__.py +0 -0
- {labops-0.3.0 → labops-0.3.2}/test-samples/docker/homeassistant/docker-compose.yaml +0 -0
- {labops-0.3.0 → labops-0.3.2}/test-samples/docker/nebula-sync/docker-compose.yaml +0 -0
- {labops-0.3.0 → labops-0.3.2}/test-samples/docker/nginx/docker-compose.yaml +0 -0
- {labops-0.3.0 → labops-0.3.2}/test-samples/homelab-complete.yml +0 -0
- {labops-0.3.0 → labops-0.3.2}/test-samples/homelab-test.yml +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.2](https://github.com/FreezeManny/labops/compare/labops-v0.3.1...labops-v0.3.2) (2026-04-21)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* Added LXC to Readme ([abb1910](https://github.com/FreezeManny/labops/commit/abb1910e8d427d93125ee81c062debe906f60d43))
|
|
9
|
+
|
|
10
|
+
## [0.3.1](https://github.com/FreezeManny/labops/compare/labops-v0.3.0...labops-v0.3.1) (2026-04-21)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* ansible directory not in built package ([33bc327](https://github.com/FreezeManny/labops/commit/33bc32740934daed8827c9d1c30a7a1d7e38b4bc))
|
|
16
|
+
|
|
3
17
|
## [0.3.0](https://github.com/FreezeManny/labops/compare/labops-v0.2.1...labops-v0.3.0) (2026-04-21)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: labops
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: YAML Based homeLAB
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.14
|
|
@@ -21,10 +21,10 @@ A declarative, YAML-based homelab manager. `labops` is a CLI tool designed to si
|
|
|
21
21
|
### Current Capabilities
|
|
22
22
|
- **Declarative YAML Configuration**: Define your complete homelab environment comprehensively using simple YAML configuration files.
|
|
23
23
|
- **Host Management**: Automated setup, initialization, and system updates for a variety of host operating systems (Alpine, Debian, RedHat) powered by integrated Ansible playbooks.
|
|
24
|
+
- **Proxmox LXC**: Update Proxmox Linux Containers (LXC) natively (through Proxmox Root Host)
|
|
24
25
|
|
|
25
26
|
### Roadmap & Future Scope
|
|
26
27
|
- **Docker Stack Management**: Seamlessly deploy, spin up, and manage Docker Compose stacks across your nodes.
|
|
27
|
-
- **Proxmox LXC**: Update Proxmox Linux Containers (LXC) natively.
|
|
28
28
|
- **DNS Automation**: Automated updating of internal DNS records.
|
|
29
29
|
- **Reverse Proxy Orchestration**: Manage, update, and automate reverse proxy routes
|
|
30
30
|
|
|
@@ -7,10 +7,10 @@ A declarative, YAML-based homelab manager. `labops` is a CLI tool designed to si
|
|
|
7
7
|
### Current Capabilities
|
|
8
8
|
- **Declarative YAML Configuration**: Define your complete homelab environment comprehensively using simple YAML configuration files.
|
|
9
9
|
- **Host Management**: Automated setup, initialization, and system updates for a variety of host operating systems (Alpine, Debian, RedHat) powered by integrated Ansible playbooks.
|
|
10
|
+
- **Proxmox LXC**: Update Proxmox Linux Containers (LXC) natively (through Proxmox Root Host)
|
|
10
11
|
|
|
11
12
|
### Roadmap & Future Scope
|
|
12
13
|
- **Docker Stack Management**: Seamlessly deploy, spin up, and manage Docker Compose stacks across your nodes.
|
|
13
|
-
- **Proxmox LXC**: Update Proxmox Linux Containers (LXC) natively.
|
|
14
14
|
- **DNS Automation**: Automated updating of internal DNS records.
|
|
15
15
|
- **Reverse Proxy Orchestration**: Manage, update, and automate reverse proxy routes
|
|
16
16
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from models.
|
|
2
|
+
from models.input_conf.yaml_root import YamlRoot
|
|
3
3
|
import typer
|
|
4
4
|
from rich.console import Console
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ from src.cli.validate import app as validate_app
|
|
|
11
11
|
|
|
12
12
|
# ------------- APP -----------------
|
|
13
13
|
app = typer.Typer(
|
|
14
|
-
help="[bold cyan]LabOPS CLI[/bold cyan] — manage homelab hosts, VMs and more from a single YAML.",
|
|
14
|
+
help="[bold cyan]LabOPS CLI[/bold cyan] — manage homelab hosts, LXCs, VMs and more from a single YAML.",
|
|
15
15
|
rich_markup_mode="rich",
|
|
16
16
|
no_args_is_help=True,
|
|
17
17
|
)
|
|
@@ -2,7 +2,7 @@ from pydantic import BaseModel, model_validator, DirectoryPath
|
|
|
2
2
|
from typing import Optional, Dict, Any, Literal
|
|
3
3
|
from ipaddress import IPv4Address
|
|
4
4
|
|
|
5
|
-
from models.
|
|
5
|
+
from models.input_conf.creds import Creds
|
|
6
6
|
|
|
7
7
|
OSType = Literal["debian", "alpine", "redhat"]
|
|
8
8
|
HostType = Literal["bare-metal", "proxmox"]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from pydantic import BaseModel, model_validator
|
|
2
2
|
from typing import Optional, Dict
|
|
3
3
|
|
|
4
|
-
from models.
|
|
5
|
-
from models.
|
|
4
|
+
from models.input_conf.hosts import Host
|
|
5
|
+
from models.input_conf.settings import Settings
|
|
6
6
|
|
|
7
7
|
class YamlRoot(BaseModel):
|
|
8
8
|
settings: Settings
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "labops"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "YAML Based homeLAB"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.14"
|
|
@@ -20,6 +20,9 @@ build-backend = "hatchling.build"
|
|
|
20
20
|
[tool.hatch.build.targets.wheel]
|
|
21
21
|
packages = ["src", "models", "labops_cli.py"]
|
|
22
22
|
|
|
23
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
24
|
+
"ansible" = "ansible"
|
|
25
|
+
|
|
23
26
|
[project.scripts]
|
|
24
27
|
labops = "labops_cli:main"
|
|
25
28
|
|
|
@@ -5,7 +5,7 @@ import typer
|
|
|
5
5
|
import yaml
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
from src.utils.yaml_validator import validate_yaml
|
|
8
|
-
from models.
|
|
8
|
+
from models.input_conf.yaml_root import YamlRoot
|
|
9
9
|
|
|
10
10
|
# ─── Shared state ─────────────────────────────────────────────────────────────
|
|
11
11
|
|
|
@@ -2,9 +2,9 @@ from typing import Optional
|
|
|
2
2
|
import typer
|
|
3
3
|
from rich.table import Table
|
|
4
4
|
|
|
5
|
-
from src.cli.core import get_model, resolve_targets, console
|
|
6
|
-
from models.
|
|
7
|
-
from models.
|
|
5
|
+
from src.cli.core import get_model, resolve_targets, console, state
|
|
6
|
+
from models.input_conf.yaml_root import YamlRoot
|
|
7
|
+
from models.input_conf.hosts import Host
|
|
8
8
|
import src.host as host
|
|
9
9
|
|
|
10
10
|
app = typer.Typer(help="Manage bare-metal hosts.", no_args_is_help=True)
|
|
@@ -18,7 +18,7 @@ def host_setup(
|
|
|
18
18
|
model: YamlRoot = get_model()
|
|
19
19
|
hosts = resolve_targets(model, target, False,
|
|
20
20
|
host.find, host.findAll, label="host")
|
|
21
|
-
host.setup(hosts[0], model.settings.default_creds)
|
|
21
|
+
host.setup(hosts[0], model.settings.default_creds, dry_run=state.dry_run, verbose=state.verbose)
|
|
22
22
|
|
|
23
23
|
@app.command("update")
|
|
24
24
|
def host_update(
|
|
@@ -33,7 +33,7 @@ def host_update(
|
|
|
33
33
|
model: YamlRoot = get_model()
|
|
34
34
|
hosts: list[Host] = resolve_targets(model, target, all, host.find,
|
|
35
35
|
host.findAll, label="host")
|
|
36
|
-
host.update(hosts, model.settings.default_creds)
|
|
36
|
+
host.update(hosts, model.settings.default_creds, dry_run=state.dry_run, verbose=state.verbose)
|
|
37
37
|
|
|
38
38
|
@app.command("list")
|
|
39
39
|
def host_list() -> None:
|
|
@@ -3,9 +3,9 @@ from typing import Optional
|
|
|
3
3
|
import typer
|
|
4
4
|
from rich.table import Table
|
|
5
5
|
|
|
6
|
-
from src.cli.core import get_model, resolve_targets, console
|
|
7
|
-
from models.
|
|
8
|
-
from models.
|
|
6
|
+
from src.cli.core import get_model, resolve_targets, console, state
|
|
7
|
+
from models.input_conf.yaml_root import YamlRoot
|
|
8
|
+
from models.input_conf.hosts import LXC, Host
|
|
9
9
|
import src.lxc as lxc
|
|
10
10
|
|
|
11
11
|
app = typer.Typer(help="Manage Proxmox LXC containers from Config.", no_args_is_help=True)
|
|
@@ -50,4 +50,4 @@ def execute_update(
|
|
|
50
50
|
targets: list[tuple[Host, LXC]] = resolve_targets(model, target, all, lxc.find,lxc.findAll, label="LXC")
|
|
51
51
|
|
|
52
52
|
# Pass them directly to the Ansible builder
|
|
53
|
-
lxc.update(targets, model.settings.default_creds)
|
|
53
|
+
lxc.update(targets, model.settings.default_creds, dry_run=state.dry_run, verbose=state.verbose)
|
|
@@ -4,8 +4,8 @@ from pathlib import Path
|
|
|
4
4
|
from rich.table import Table
|
|
5
5
|
|
|
6
6
|
from src.cli.core import ConfigError, get_model, console
|
|
7
|
-
from models.
|
|
8
|
-
from models.
|
|
7
|
+
from models.input_conf.yaml_root import YamlRoot
|
|
8
|
+
from models.input_conf.hosts import Host
|
|
9
9
|
|
|
10
10
|
app = typer.Typer(help="Validate configuration.")
|
|
11
11
|
|
|
@@ -3,8 +3,8 @@ import typer
|
|
|
3
3
|
from rich.table import Table
|
|
4
4
|
|
|
5
5
|
from src.cli.core import get_model, resolve_targets, console
|
|
6
|
-
from models.
|
|
7
|
-
from models.
|
|
6
|
+
from models.input_conf.yaml_root import YamlRoot
|
|
7
|
+
from models.input_conf.hosts import Host
|
|
8
8
|
import src.vm as vm
|
|
9
9
|
|
|
10
10
|
app = typer.Typer(help="Manage virtual machines.", no_args_is_help=True)
|
|
@@ -2,11 +2,11 @@ from ansible_runner.runner import Runner
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
import getpass
|
|
4
4
|
|
|
5
|
-
from models.
|
|
6
|
-
from models.
|
|
5
|
+
from models.input_conf.hosts import Host
|
|
6
|
+
from models.input_conf.creds import Creds
|
|
7
7
|
from src.utils.ansible_runner import run_playbook
|
|
8
8
|
|
|
9
|
-
def setup(host: Host, default_creds: Creds) -> None:
|
|
9
|
+
def setup(host: Host, default_creds: Creds, dry_run: bool = False, verbose: bool = False) -> None:
|
|
10
10
|
creds: Creds = host.creds or default_creds
|
|
11
11
|
|
|
12
12
|
if not creds.ssh_key_path:
|
|
@@ -65,7 +65,9 @@ def setup(host: Host, default_creds: Creds) -> None:
|
|
|
65
65
|
r: Runner = run_playbook(
|
|
66
66
|
playbook=f"host/setup.yml",
|
|
67
67
|
inventory=inventory,
|
|
68
|
-
extravars=extravars
|
|
68
|
+
extravars=extravars,
|
|
69
|
+
dry_run=dry_run,
|
|
70
|
+
verbose=verbose
|
|
69
71
|
)
|
|
70
72
|
|
|
71
73
|
if r.rc == 0:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from ansible_runner.runner import Runner
|
|
2
|
-
from models.
|
|
3
|
-
from models.
|
|
4
|
-
from models.
|
|
2
|
+
from models.input_conf.hosts import Host
|
|
3
|
+
from models.input_conf.hosts import OSType
|
|
4
|
+
from models.input_conf.creds import Creds
|
|
5
5
|
from src.utils.ansible_runner import run_playbook
|
|
6
6
|
|
|
7
|
-
def update(hosts: list[Host], default_creds: Creds) -> None:
|
|
7
|
+
def update(hosts: list[Host], default_creds: Creds, dry_run: bool = False, verbose: bool = False) -> None:
|
|
8
8
|
# Group hosts by OS to run the correct playbooks
|
|
9
9
|
hosts_by_os: dict[OSType, list[Host]] = {}
|
|
10
10
|
for host in hosts:
|
|
@@ -43,7 +43,9 @@ def update(hosts: list[Host], default_creds: Creds) -> None:
|
|
|
43
43
|
|
|
44
44
|
r: Runner = run_playbook(
|
|
45
45
|
playbook="host/update.yml",
|
|
46
|
-
inventory=inventory
|
|
46
|
+
inventory=inventory,
|
|
47
|
+
dry_run=dry_run,
|
|
48
|
+
verbose=verbose
|
|
47
49
|
)
|
|
48
50
|
|
|
49
51
|
if r.rc == 0:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from src.utils.ansible_runner import run_playbook
|
|
2
|
-
from models.
|
|
3
|
-
from models.
|
|
2
|
+
from models.input_conf.yaml_root import YamlRoot
|
|
3
|
+
from models.input_conf.hosts import Host, LXC
|
|
4
4
|
|
|
5
5
|
def findAll(config: YamlRoot) -> list[tuple[Host, LXC]]:
|
|
6
6
|
"""Returns a list of all LXCs found inside Proxmox hosts in the Yaml config."""
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from src.utils.ansible_runner import run_playbook
|
|
2
|
-
from models.
|
|
3
|
-
from models.
|
|
2
|
+
from models.input_conf.hosts import Host, LXC
|
|
3
|
+
from models.input_conf.creds import Creds
|
|
4
4
|
|
|
5
|
-
def update(proxmox_lxc_pairs: list[tuple[Host, LXC]], default_creds: Creds) -> None:
|
|
5
|
+
def update(proxmox_lxc_pairs: list[tuple[Host, LXC]], default_creds: Creds, dry_run: bool = False, verbose: bool = False) -> None:
|
|
6
6
|
"""
|
|
7
7
|
Builds a dynamic ansible inventory from the Yaml config and proxies the
|
|
8
8
|
commands via community.proxmox.proxmox_pct_remote inside community OS groups.
|
|
@@ -39,4 +39,4 @@ def update(proxmox_lxc_pairs: list[tuple[Host, LXC]], default_creds: Creds) -> N
|
|
|
39
39
|
|
|
40
40
|
inventory["all"]["children"][group_name]["hosts"][lxc_obj.name] = host_vars
|
|
41
41
|
|
|
42
|
-
run_playbook(playbook="lxc/update.yml", inventory=inventory)
|
|
42
|
+
run_playbook(playbook="lxc/update.yml", inventory=inventory, dry_run=dry_run, verbose=verbose)
|
|
@@ -6,7 +6,7 @@ from typing import Optional
|
|
|
6
6
|
project_root: str = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
|
7
7
|
playbook_root: str = os.path.join(project_root, "ansible", "playbooks")
|
|
8
8
|
|
|
9
|
-
def run_playbook(playbook: str, inventory: dict, extravars: Optional[dict] = None) -> Runner:
|
|
9
|
+
def run_playbook(playbook: str, inventory: dict, extravars: Optional[dict] = None, dry_run: bool = False, verbose: bool = False) -> Runner:
|
|
10
10
|
"""
|
|
11
11
|
Wrapper for ansible_runner.run with standard application settings.
|
|
12
12
|
"""
|
|
@@ -25,14 +25,15 @@ def run_playbook(playbook: str, inventory: dict, extravars: Optional[dict] = Non
|
|
|
25
25
|
if extravars:
|
|
26
26
|
kwargs["extravars"] = extravars
|
|
27
27
|
|
|
28
|
-
from src.cli.core import state
|
|
29
28
|
cmdline = []
|
|
30
|
-
if
|
|
29
|
+
if dry_run:
|
|
31
30
|
cmdline.append("--check")
|
|
32
|
-
if
|
|
31
|
+
if verbose:
|
|
33
32
|
cmdline.append("-v")
|
|
34
33
|
|
|
35
34
|
if cmdline:
|
|
36
35
|
kwargs["cmdline"] = " ".join(cmdline)
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
|
|
37
|
+
runner = ansible_runner.run(**kwargs)
|
|
38
|
+
return runner
|
|
39
|
+
return runner
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|