Skip to content
Open
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
37 changes: 37 additions & 0 deletions .github/workflows/test-install.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Test Installation

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
test-install:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10"]

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install with pip
run: |
pip install .
docker-pretty-ps --version

- name: Install with uv
uses: astral-sh/setup-uv@v4

- name: Test uv tool install
run: |
uv tool install .
echo "$HOME/.local/bin" >> $GITHUB_PATH
docker-pretty-ps --version
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,28 @@ Total running: 5
Containers in search: 2
```
# Install
### Over pip
### Using uv (Recommended)
```bash
uv tool install docker-pretty-ps
```

### Using pip
```bash
pip install docker-pretty-ps
```

### Build and install
### Manual Install
```bash
git clone https://github.com/politeauthority/docker-pretty-ps.git
cd docker-pretty-ps
python3 setup.py build
python3 setup.py install

pip install .
```
Then you should be able to run the command ```docker-pretty-ps``` any where on your system.

Then you should be able to run the command `docker-pretty-ps` anywhere on your system.

> [!TIP]
> User installs typically place binaries in `~/.local/bin`. Ensure this is in your `PATH`.


# Other Example Usages
### Example Slim Output --slim, (-s) Mode
Expand Down
50 changes: 22 additions & 28 deletions dockerprettyps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
__title__ = """
_ _ _ _
__| |___ __| |_____ _ _ ___ _ __ _ _ ___| |_| |_ _ _ ___ _ __ ___
/ _` / _ \/ _| / / -_) '_| |___| | '_ \ '_/ -_) _| _| || | |___| | '_ (_-<
\__,_\___/\__|_\_\___|_| | .__/_| \___|\__|\__|\_, | | .__/__/
/ _` / _ \\/ _| / / -_) '_| |___| | '_ \\ '_/ -_) _| _| || | |___| | '_ (_-<
\\__,_\\___/\\__|_\\_\\___|_| | .__/_| \\___|\\__|\\__|\\_, | | .__/__/
|_| |__/ |_|
"""

Expand Down Expand Up @@ -183,7 +183,7 @@ def get_raw_containers():
:returns: The raw information from the `docker ps` command.
:rtype: str
"""
cmds = ["docker", "ps", "-a"]
cmds = ["docker", "ps", "-a", "--format", "{{json .}}"]
out = subprocess.Popen(
cmds,
stdout=subprocess.PIPE,
Expand All @@ -206,34 +206,28 @@ def clean_output(output):
:returns: Cleaned, usable output from docker-ps
:rtype: list
"""
lines = output.split("\n")
lines = output.strip().split("\n")
containers = []
for line in lines[1:]:
line_split = line.split(" ")
revised_line_split = []
if len(line_split) == 1:
for line in lines:
if not line.strip():
continue
for piece in line_split:
if piece and piece.strip() != "":
revised_line_split.append(piece)
try:
data = json.loads(line)
except json.JSONDecodeError:
continue

container = {
"container_id": revised_line_split[0].strip(),
"image": revised_line_split[1].strip(),
"command": revised_line_split[2].strip().replace('"', ""),
"created": revised_line_split[3].strip(),
"created_date": _parse_ps_date(revised_line_split[4].strip()),
"status": revised_line_split[4].strip(),
"status_date": _parse_ps_date(revised_line_split[4].strip()),
"running": _clean_status(revised_line_split[4])
"container_id": data.get("ID", "").strip(),
"image": data.get("Image", "").strip(),
"command": data.get("Command", "").strip().replace('"', ""),
"created": data.get("RunningFor", "").strip(),
"created_date": _parse_ps_date(data.get("RunningFor", "").strip()),
"status": data.get("Status", "").strip(),
"status_date": _parse_ps_date(data.get("Status", "").strip()),
"running": _clean_status(data.get("Status", "")),
"ports": _parse_ports(data.get("Ports", "")),
"name": data.get("Names", "").strip()
}

# Not all containers will have ports
if len(revised_line_split) == 6:
container["ports"] = []
container["name"] = revised_line_split[5].strip()
else:
container["ports"] = _parse_ports(revised_line_split[5])
container["name"] = revised_line_split[6].strip()
containers.append(container)

containers = get_container_colors(containers)
Expand Down Expand Up @@ -282,7 +276,7 @@ def _parse_ps_date(val):
cleaned = cleaned[:cleaned.find('(healthy')].strip()

elif '(health' in cleaned:
cleaned = cleaned[:cleaned.find(')') + 1].strip()
cleaned = cleaned[:cleaned.find('(health')].strip()

elif '(unhealthy' in cleaned:
cleaned = cleaned[:cleaned.find('(')].strip()
Expand Down
4 changes: 4 additions & 0 deletions dockerprettyps/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import run_cli

if __name__ == "__main__":
run_cli()
8 changes: 3 additions & 5 deletions dockerprettyps/bin/docker-pretty-ps
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#!/usr/bin/env python

import dockerprettyps

dockerprettyps.run_cli()
#!/usr/bin/env python3
from dockerprettyps import run_cli
run_cli()
7 changes: 7 additions & 0 deletions pr_body.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- Fixes #21 (hardcoded python3.7 shebang)
- Fixes brittle parsing logic (now uses `docker ps --format json`) which fixes #21 and cases where commands contain newlines/spaces
- Likely fixes/avoids #37 (pkg_resources launcher deprecation warning)
- Fixes `SyntaxWarning` in Python 3.12+ by escaping ASCII art backslashes
- Adds `python -m dockerprettyps` fallback
- Adds CI coverage for pip and uv installs
- Updates README to recommend modern install methods
6 changes: 5 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
url="https://github.com/politeauthority/docker-pretty-ps",
download_url="https://github.com/politeauthority/docker-pretty-ps/archive/v0.0.1-alpha.zip",
packages=setuptools.find_packages(),
scripts=['dockerprettyps/bin/docker-pretty-ps'],
entry_points={
"console_scripts": [
"docker-pretty-ps = dockerprettyps:run_cli",
],
},
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
Expand Down
33 changes: 16 additions & 17 deletions tests/data/raw_docker_ps.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a3ef7ad2c5a diginc/pi-hole:latest "/s6-init" 5 weeks ago Up 5 days (healthy) 0.0.0.0:53->53/udp, 0.0.0.0:53->53/tcp, 0.0.0.0:67->67/udp, 443/tcp, 0.0.0.0:5021->80/tcp, 0.0.0.0:8082->8080/tcp pihole_pihole_1
2e8e372b6d20 puckel/docker-airflow "/entrypoint.sh airf…" 5 weeks ago Restarting (1) 11 seconds ago airflow_airflow_web_1
135a0d0e2ca9 owncloud/server:latest "/usr/bin/entrypoint…" 5 weeks ago Up 5 days (healthy) 0.0.0.0:5010->8080/tcp owncloud_owncloud_1
4e477defb6ca webhippie/mariadb:latest "/usr/bin/entrypoint…" 5 weeks ago Up 5 days (healthy) 3306/tcp owncloud_db_1
87934825540d webhippie/redis:latest "/usr/bin/entrypoint…" 5 weeks ago Up 5 days (healthy) 6379/tcp owncloud_redis_1
a70bd525c078 9c3aa826ace7 "/entrypoint.sh work…" 6 weeks ago Up About a minute 5555/tcp, 8080/tcp, 0.0.0.0:8793->8793/tcp booj-etl_airflowworker_1
034fde646ed1 c0ee826a6aec "/entrypoint.sh sche…" 6 weeks ago Up 2 minutes 5555/tcp, 8080/tcp, 8793/tcp booj-etl_airflowscheduler_1
0f8c1c2ba751 booj-etl_my_db "docker-entrypoint.s…" 6 weeks ago Up 5 days 0.0.0.0:3306->3306/tcp booj-etl_my_db_1
563356667e54 d6c0da8ff386 "/entrypoint.sh webs…" 6 weeks ago Up 2 minutes (unhealthy) 5555/tcp, 8793/tcp, 0.0.0.0:8080->8080/tcp booj-etl_airflowwebserver_1
d9b9e83dd105 postgres:9.6-alpine "docker-entrypoint.s…" 6 weeks ago Up 5 days 5432/tcp booj-etl_postgres_1
547b6849f253 puckel/docker-airflow:1.9.0-4 "/entrypoint.sh flow…" 7 weeks ago Up 38 seconds 8080/tcp, 0.0.0.0:5555->5555/tcp, 8793/tcp booj-etl_airflowflower_1
1650086ac606 rabbitmq:alpine "docker-entrypoint.s…" 7 weeks ago Up 5 days 4369/tcp, 0.0.0.0:5672->5672/tcp, 5671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp booj-etl_rabbitmq_1
d52427bfedab tradetrack_scrape "tail -f /dev/null" 2 months ago Up 5 days tradetrack_scrape_1
6ed0de0c6a6e tradetrack_dev_scrape "tail -f /dev/null" 2 months ago Up 5 days tradetrack_dev_scrape_1
5f7ab3814051 tradetrack_dev_web "gunicorn -b 0.0.0.0…" 2 months ago Up 5 days 80/tcp, 0.0.0.0:5012->5000/tcp, 0.0.0.0:5011->5010/tcp tradetrack_dev_web_1
fa55881999c7 postgres:alpine "docker-entrypoint.s…" 2 months ago Up 5 days 0.0.0.0:5432->5432/tcp some-postgres
{"ID": "2a3ef7ad2c5a", "Image": "diginc/pi-hole:latest", "Command": "\"/s6-init\"", "RunningFor": "5 weeks ago", "Status": "Up 5 days (healthy)", "Ports": "0.0.0.0:53->53/udp, 0.0.0.0:53->53/tcp, 0.0.0.0:67->67/udp, 443/tcp, 0.0.0.0:5021->80/tcp, 0.0.0.0:8082->8080/tcp", "Names": "pihole_pihole_1"}
{"ID": "2e8e372b6d20", "Image": "puckel/docker-airflow", "Command": "\"/entrypoint.sh airf\u2026\"", "RunningFor": "5 weeks ago", "Status": "Restarting (1) 11 seconds ago", "Ports": "", "Names": "airflow_airflow_web_1"}
{"ID": "135a0d0e2ca9", "Image": "owncloud/server:latest", "Command": "\"/usr/bin/entrypoint\u2026\"", "RunningFor": "5 weeks ago", "Status": "Up 5 days (healthy)", "Ports": "0.0.0.0:5010->8080/tcp", "Names": "owncloud_owncloud_1"}
{"ID": "4e477defb6ca", "Image": "webhippie/mariadb:latest", "Command": "\"/usr/bin/entrypoint\u2026\"", "RunningFor": "5 weeks ago", "Status": "Up 5 days (healthy)", "Ports": "3306/tcp", "Names": "owncloud_db_1"}
{"ID": "87934825540d", "Image": "webhippie/redis:latest", "Command": "\"/usr/bin/entrypoint\u2026\"", "RunningFor": "5 weeks ago", "Status": "Up 5 days (healthy)", "Ports": "6379/tcp", "Names": "owncloud_redis_1"}
{"ID": "a70bd525c078", "Image": "9c3aa826ace7", "Command": "\"/entrypoint.sh work\u2026\"", "RunningFor": "6 weeks ago", "Status": "Up About a minute", "Ports": "5555/tcp, 8080/tcp, 0.0.0.0:8793->8793/tcp", "Names": "booj-etl_airflowworker_1"}
{"ID": "034fde646ed1", "Image": "c0ee826a6aec", "Command": "\"/entrypoint.sh sche\u2026\"", "RunningFor": "6 weeks ago", "Status": "Up 2 minutes", "Ports": "5555/tcp, 8080/tcp, 8793/tcp", "Names": "booj-etl_airflowscheduler_1"}
{"ID": "0f8c1c2ba751", "Image": "booj-etl_my_db", "Command": "\"docker-entrypoint.s\u2026\"", "RunningFor": "6 weeks ago", "Status": "Up 5 days", "Ports": "0.0.0.0:3306->3306/tcp", "Names": "booj-etl_my_db_1"}
{"ID": "563356667e54", "Image": "d6c0da8ff386", "Command": "\"/entrypoint.sh webs\u2026\"", "RunningFor": "6 weeks ago", "Status": "Up 2 minutes (unhealthy)", "Ports": "5555/tcp, 8793/tcp, 0.0.0.0:8080->8080/tcp", "Names": "booj-etl_airflowwebserver_1"}
{"ID": "d9b9e83dd105", "Image": "postgres:9.6-alpine", "Command": "\"docker-entrypoint.s\u2026\"", "RunningFor": "6 weeks ago", "Status": "Up 5 days", "Ports": "5432/tcp", "Names": "booj-etl_postgres_1"}
{"ID": "547b6849f253", "Image": "puckel/docker-airflow:1.9.0-4", "Command": "\"/entrypoint.sh flow\u2026\"", "RunningFor": "7 weeks ago", "Status": "Up 38 seconds", "Ports": "8080/tcp, 0.0.0.0:5555->5555/tcp, 8793/tcp", "Names": "booj-etl_airflowflower_1"}
{"ID": "1650086ac606", "Image": "rabbitmq:alpine", "Command": "\"docker-entrypoint.s\u2026\"", "RunningFor": "7 weeks ago", "Status": "Up 5 days", "Ports": "4369/tcp, 0.0.0.0:5672->5672/tcp, 5671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp", "Names": "booj-etl_rabbitmq_1"}
{"ID": "d52427bfedab", "Image": "tradetrack_scrape", "Command": "\"tail -f /dev/null\"", "RunningFor": "2 months ago", "Status": "Up 5 days", "Ports": "", "Names": "tradetrack_scrape_1"}
{"ID": "6ed0de0c6a6e", "Image": "tradetrack_dev_scrape", "Command": "\"tail -f /dev/null\"", "RunningFor": "2 months ago", "Status": "Up 5 days", "Ports": "", "Names": "tradetrack_dev_scrape_1"}
{"ID": "5f7ab3814051", "Image": "tradetrack_dev_web", "Command": "\"gunicorn -b 0.0.0.0\u2026\"", "RunningFor": "2 months ago", "Status": "Up 5 days", "Ports": "80/tcp, 0.0.0.0:5012->5000/tcp, 0.0.0.0:5011->5010/tcp", "Names": "tradetrack_dev_web_1"}
{"ID": "fa55881999c7", "Image": "postgres:alpine", "Command": "\"docker-entrypoint.s\u2026\"", "RunningFor": "2 months ago", "Status": "Up 5 days", "Ports": "0.0.0.0:5432->5432/tcp", "Names": "some-postgres"}