Metadata-Version: 2.4
Name: compose2pod
Version: 0.7.0
Summary: Convert a Docker Compose file into a script that runs its services as a single Podman pod
Keywords: podman,docker-compose,docker,containers,cli,python
Author: Artur Shiriev
Author-email: Artur Shiriev <me@shiriev.ru>
License-Expression: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
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 :: Free Threading :: 2 - Beta
Classifier: Typing :: Typed
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Testing
Requires-Dist: pyyaml>=6 ; python_full_version < '3.12' and extra == 'yaml'
Requires-Dist: pyyaml>=6.0.1 ; python_full_version == '3.12.*' and extra == 'yaml'
Requires-Dist: pyyaml>=6.0.2 ; python_full_version == '3.13.*' and extra == 'yaml'
Requires-Dist: pyyaml>=6.0.3 ; python_full_version >= '3.14' and extra == 'yaml'
Requires-Python: >=3.11, <4
Project-URL: Homepage, https://modern-python.org
Project-URL: Repository, https://github.com/modern-python/compose2pod
Project-URL: Issues, https://github.com/modern-python/compose2pod/issues
Project-URL: Changelog, https://github.com/modern-python/compose2pod/releases
Provides-Extra: yaml
Description-Content-Type: text/markdown

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)"  srcset="https://raw.githubusercontent.com/modern-python/.github/main/brand/projects/compose2pod/lockup-dark.svg">
    <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/modern-python/.github/main/brand/projects/compose2pod/lockup-light.svg">
    <img alt="compose2pod" src="https://raw.githubusercontent.com/modern-python/.github/main/brand/projects/compose2pod/lockup.png" width="420">
  </picture>
</p>

[![PyPI version](https://img.shields.io/pypi/v/compose2pod.svg)](https://pypi.org/project/compose2pod/)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/compose2pod.svg)](https://pypi.org/project/compose2pod/)
[![Downloads](https://static.pepy.tech/badge/compose2pod/month)](https://pepy.tech/projects/compose2pod)
[![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen.svg)](https://github.com/modern-python/compose2pod/actions/workflows/ci.yml)
[![CI](https://github.com/modern-python/compose2pod/actions/workflows/ci.yml/badge.svg)](https://github.com/modern-python/compose2pod/actions/workflows/ci.yml)
[![License](https://img.shields.io/github/license/modern-python/compose2pod.svg)](https://github.com/modern-python/compose2pod/blob/main/LICENSE)
[![GitHub stars](https://img.shields.io/github/stars/modern-python/compose2pod)](https://github.com/modern-python/compose2pod/stargazers)
[![Context7](https://img.shields.io/badge/Context7-docs-blue)](https://context7.com/modern-python/compose2pod)
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![ty](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty)

Convert a Docker Compose file into a POSIX `sh` script that runs its services as a **single Podman pod**.

Built for CI and test environments where you can't use `docker compose` or `podman kube play`:

- No bridge networking or netavark. Unprivileged CI containers often have a read-only `/proc/sys`, so netavark fails to create bridge networks. A single pod shares one network namespace with no bridge: services talk over `127.0.0.1`, and names resolve via a generated `/etc/hosts` the script owns.
- No systemd. Podman healthchecks are normally scheduled by systemd timers. compose2pod gates startup by polling `podman healthcheck run` directly, so `depends_on: service_healthy` works without systemd.
- No heavy runtime. The core is stdlib-only, with no dependencies and no compiled wheels, so it installs and runs in minimal Python images.

## Requirements

Podman 4.9 or newer. compose2pod accepts only forms that every Podman from 4.9 to 6.1 can
run, so a script it generates runs on any of them
([ADR-0006](https://github.com/modern-python/compose2pod/blob/main/docs/adr/0006-docker-rejection-parity.md)).

compose2pod's generated scripts own `/etc/hosts`: they write it to a temp
file and bind-mount it read-only into every container under `--no-hosts`, so
pod-internal name resolution works on any Podman version. `host.containers.internal` /
`host.docker.internal` are not provided; add an explicit `extra_hosts`
entry if you need them.

## Install

```bash
pip install compose2pod            # core: reads compose as JSON
pip install 'compose2pod[yaml]'    # optional: read YAML directly (adds PyYAML)
```

## Usage

```bash
# YAML directly (needs the 'compose2pod[yaml]' extra)
compose2pod docker-compose.yml --target app --image myimage:ci > run.sh

# Or stay dependency-free by piping JSON (e.g. via yq)
yq -o=json '.' docker-compose.yml | compose2pod --target app --image myimage:ci > run.sh

sh ./run.sh
```

Options:

- `--target`: the service to run in the foreground (required).
- `--image`: the CI image that replaces every service with a `build` section (required).
- `--command`: a shell command overriding the target service's command.
- `--project-dir`: the host path that relative volume and `env_file` sources resolve against (default `.`).
- `--pod-name`: the name of the Podman pod, also used as the prefix of every container name (default `test-pod`).
- `--format`: the input format, one of `auto`, `json`, `yaml` (default `auto`, which tries JSON, then YAML).
- `--artifact SRC:DST`: a file to `podman cp` out of the target container after it exits (repeatable).
- `--allow-exit-code`: a target exit code treated as success in addition to 0 (repeatable).

## Supported compose subset

compose2pod refuses every document `docker compose config` refuses. A test
harness runs `docker compose config` and compose2pod over the same files to
check this. So a file that compiles is a file Docker would run; where compose2pod
still refuses a form Docker accepts, it is because Podman cannot express it;
each case is documented in [`docs/adr/`](https://github.com/modern-python/compose2pod/tree/main/docs/adr/).

Within that boundary it covers most of what real compose files use:

- Services: `image`/`build`, `command`/`entrypoint`, `environment` and
  `env_file` (string and long-form `{path, required, format}`), `volumes`
  (short-form and long-form `--mount`, including the `bind` and `tmpfs`
  option maps), `tmpfs`, `healthcheck`, `depends_on` (all conditions), `links`
  (read as a dependency plus a hostname alias, as Docker reads it), network
  `aliases`, `hostname`/`container_name`.
- Confinement and metadata: `user`, `working_dir`, `read_only`, `init`,
  `privileged`, `cap_add`/`cap_drop`, `security_opt`, `devices`, `group_add`,
  `platform`, `labels`, `annotations`, `pull_policy` (the quoted-boolean and
  YAML-1.1 spellings Docker accepts, too).
- Resources: the legacy keys (`mem_limit`, `cpus`, `pids_limit`,
  `ulimits`, …) and the modern `deploy.resources` block.
- Pod-wide: `dns`/`dns_search`/`dns_opt`, `sysctls`, `extra_hosts`.
- Composition: same-file `extends`, `secrets`/`configs`.

Accepted and ignored with a warning, since they mean nothing inside one shared pod:
`ports`, `expose`, `restart`, `stdin_open`, `tty`, `stop_signal`,
`stop_grace_period`, and `profiles` (every service runs regardless of profile).

Compose extension fields (any `x-`-prefixed key) and YAML anchors are accepted
as-is, so a top-level `x-*` anchor block for shared config is accepted.
`${VAR}`-style variable interpolation is left live in the generated script,
resolved by its shell against the environment present when the script runs (no
`.env` file support). The boundary rulings, which forms are refused and why,
are recorded in [`docs/adr/`](https://github.com/modern-python/compose2pod/tree/main/docs/adr/).

## Status

Beta. Part of the [modern-python](https://github.com/modern-python) family. MIT licensed.
