Metadata-Version: 2.4
Name: systemd-pydantic
Version: 0.1.0
Summary: Pydantic models for systemd
Project-URL: Repository, https://github.com/airflow-laminar/systemd-pydantic
Project-URL: Homepage, https://github.com/airflow-laminar/systemd-pydantic
Author-email: the systemd-pydantic authors <t.paine154@gmail.com>
License: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.11
Requires-Dist: hydra-core
Requires-Dist: pydantic>=2
Requires-Dist: typer
Provides-Extra: develop
Requires-Dist: build; extra == 'develop'
Requires-Dist: bump-my-version; extra == 'develop'
Requires-Dist: check-dist; extra == 'develop'
Requires-Dist: codespell; extra == 'develop'
Requires-Dist: hatchling; extra == 'develop'
Requires-Dist: mdformat; extra == 'develop'
Requires-Dist: mdformat-tables>=1; extra == 'develop'
Requires-Dist: pytest; extra == 'develop'
Requires-Dist: pytest-cov; extra == 'develop'
Requires-Dist: ruff; extra == 'develop'
Requires-Dist: twine; extra == 'develop'
Requires-Dist: ty; extra == 'develop'
Requires-Dist: uv; extra == 'develop'
Requires-Dist: wheel; extra == 'develop'
Description-Content-Type: text/markdown

# systemd-pydantic

[Pydantic](https://docs.pydantic.dev/latest/) models for systemd service and timer units.

[![Build Status](https://github.com/airflow-laminar/systemd-pydantic/actions/workflows/build.yaml/badge.svg?branch=main&event=push)](https://github.com/airflow-laminar/systemd-pydantic/actions/workflows/build.yaml)
[![codecov](https://codecov.io/gh/airflow-laminar/systemd-pydantic/branch/main/graph/badge.svg)](https://codecov.io/gh/airflow-laminar/systemd-pydantic)
[![License](https://img.shields.io/github/license/airflow-laminar/systemd-pydantic)](https://github.com/airflow-laminar/systemd-pydantic)
[![PyPI](https://img.shields.io/pypi/v/systemd-pydantic.svg)](https://pypi.python.org/pypi/systemd-pydantic)

## Overview

`systemd-pydantic` provides typed, YAML-friendly models, lifecycle clients, and convenience commands for systemd. Its layers mirror [`supervisor-pydantic`](https://github.com/airflow-laminar/supervisor-pydantic):

- `ServiceConfiguration`: `[Service]` settings, analogous to `ProgramConfiguration`
- `ServiceUnitConfiguration` and `TimerUnitConfiguration`: individual unit files
- `SystemdConfiguration`: named service and timer collections, file persistence, Hydra loading, and basic lifecycle methods
- `SystemdConvenienceConfiguration`: defaults and persisted JSON used by external tools such as Airflow
- `SystemdClient`: typed `systemctl` operations and `UnitInfo` state
- `_systemd_convenience`: lifecycle CLI for local or SSH-driven orchestration

Timer support lives in this package because timers share systemd's common `[Unit]` and `[Install]` sections and normally activate a matching service unit.

## Configuration

```python
from systemd_pydantic import ServiceConfiguration, ServiceUnitConfiguration, SystemdConfiguration

config = SystemdConfiguration(
    service={
        "long-running-job": ServiceUnitConfiguration(
            unit={"description": "Long-running Airflow job"},
            service=ServiceConfiguration(
                type="exec",
                exec_start="/opt/jobs/run",
                restart="on-failure",
                restart_sec="5s",
                environment={"MODE": "production"},
            ),
        )
    },
    scope="user",
)

config.write()
```

Dictionary input works identically, making the configuration suitable for YAML and Hydra:

```yaml
# @package _global_

service:
  long-running-job:
    unit:
      description: Long-running Airflow job
    service:
      type: exec
      exec_start: /opt/jobs/run
      restart: on-failure
      restart_sec: 5s
      environment:
        MODE: production
scope: user
```

Timer values accept systemd time strings or Python `timedelta` values. Repeated calendar and monotonic triggers render as repeated directives, preserving systemd semantics.

`scope="system"` writes to `/etc/systemd/system` and calls `systemctl`. `scope="user"` writes to `~/.config/systemd/user` and calls `systemctl --user`. The executing user must have permission to write the selected unit directory and control its systemd manager.

## Lifecycle client

```python
from systemd_pydantic import SystemdClient

client = SystemdClient(config)
client.daemon_reload()
client.start_services()

for unit in client.get_all_service_info().values():
    print(unit.name, unit.active_state, unit.result)
```

`SystemdClient` also supports stopping, restarting, killing, enabling, and disabling units. A custom `CommandRunner` can be injected for tests or remote execution.

## Convenience CLI

`SystemdConvenienceConfiguration` persists its JSON representation alongside generated units. The `_systemd_convenience` CLI consumes that file and provides commands aligned with `supervisor-pydantic`:

```text
configure-systemd
start-services
check-services
restart-services
stop-services
unconfigure-systemd
```

Systemd itself is already running, so there are intentionally no `start-systemd` or `stop-systemd` daemon commands.

> [!NOTE]
> This library was generated using [copier](https://copier.readthedocs.io/en/stable/) from the [Base Python Project Template repository](https://github.com/python-project-templates/base).
