From a39eae392cc602f8fd9401b7a0085612b0e20985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Jan=C4=8Da=C5=99=C3=ADk?= Date: Mon, 29 Dec 2025 11:31:23 +0100 Subject: [PATCH 1/5] Fix cross-platform installs: use console_scripts entry point (pip/uv), remove hardcoded shebang launcher --- .github/workflows/test-install.yml | 38 +++++++++++++++++++++++++++++ README.md | 20 ++++++++++----- dockerprettyps/__main__.py | 4 +++ dockerprettyps/bin/docker-pretty-ps | 8 +++--- setup.py | 6 ++++- 5 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/test-install.yml create mode 100644 dockerprettyps/__main__.py diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml new file mode 100644 index 0000000..eb5d52a --- /dev/null +++ b/.github/workflows/test-install.yml @@ -0,0 +1,38 @@ +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 + with: + enable-cache: true + + - name: Test uv tool install + run: | + uv tool install . + docker-pretty-ps --version diff --git a/README.md b/README.md index e70b918..c614167 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/dockerprettyps/__main__.py b/dockerprettyps/__main__.py new file mode 100644 index 0000000..5b9e532 --- /dev/null +++ b/dockerprettyps/__main__.py @@ -0,0 +1,4 @@ +from . import run_cli + +if __name__ == "__main__": + run_cli() diff --git a/dockerprettyps/bin/docker-pretty-ps b/dockerprettyps/bin/docker-pretty-ps index 8dfb102..6475e0e 100755 --- a/dockerprettyps/bin/docker-pretty-ps +++ b/dockerprettyps/bin/docker-pretty-ps @@ -1,5 +1,3 @@ -#!/usr/bin/env python - -import dockerprettyps - -dockerprettyps.run_cli() +#!/usr/bin/env python3 +from dockerprettyps import run_cli +run_cli() diff --git a/setup.py b/setup.py index e2cd22a..578d3e3 100644 --- a/setup.py +++ b/setup.py @@ -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", From 2a1c480ec5093b8ee2c947a9b128980e8dd3b004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Jan=C4=8Da=C5=99=C3=ADk?= Date: Mon, 29 Dec 2025 11:43:20 +0100 Subject: [PATCH 2/5] Fix SyntaxWarning in Python 3.12 and CI/CD path issues --- .github/workflows/test-install.yml | 3 +-- dockerprettyps/__init__.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index eb5d52a..42ed8a6 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -29,10 +29,9 @@ jobs: - name: Install with uv uses: astral-sh/setup-uv@v4 - with: - enable-cache: true - name: Test uv tool install run: | uv tool install . + echo "$HOME/.local/bin" >> $GITHUB_PATH docker-pretty-ps --version diff --git a/dockerprettyps/__init__.py b/dockerprettyps/__init__.py index 8c8ffac..d2e41f5 100644 --- a/dockerprettyps/__init__.py +++ b/dockerprettyps/__init__.py @@ -37,7 +37,7 @@ from dockerprettyps import errors __version__ = "1.0.2" -__title__ = """ +__title__ = r""" _ _ _ _ __| |___ __| |_____ _ _ ___ _ __ _ _ ___| |_| |_ _ _ ___ _ __ ___ / _` / _ \/ _| / / -_) '_| |___| | '_ \ '_/ -_) _| _| || | |___| | '_ (_-< From df847cc2e51893140b001a6a23a19a6f50e37ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Jan=C4=8Da=C5=99=C3=ADk?= Date: Mon, 29 Dec 2025 11:53:24 +0100 Subject: [PATCH 3/5] Fix brittle parsing by using docker ps --format json (fixes issues with command newlines/spaces) --- dockerprettyps/__init__.py | 44 ++++++++++++++++-------------------- tests/data/raw_docker_ps.txt | 33 +++++++++++++-------------- 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/dockerprettyps/__init__.py b/dockerprettyps/__init__.py index d2e41f5..eb8dcfe 100644 --- a/dockerprettyps/__init__.py +++ b/dockerprettyps/__init__.py @@ -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, @@ -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) diff --git a/tests/data/raw_docker_ps.txt b/tests/data/raw_docker_ps.txt index 380f29d..9db673f 100644 --- a/tests/data/raw_docker_ps.txt +++ b/tests/data/raw_docker_ps.txt @@ -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"} From 299413f79a7e39dac712cfa4e6340d8af0ddfc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Jan=C4=8Da=C5=99=C3=ADk?= Date: Mon, 29 Dec 2025 11:53:58 +0100 Subject: [PATCH 4/5] Fix SyntaxWarning by escaping backslashes in ASCII art --- dockerprettyps/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dockerprettyps/__init__.py b/dockerprettyps/__init__.py index eb8dcfe..6920699 100644 --- a/dockerprettyps/__init__.py +++ b/dockerprettyps/__init__.py @@ -37,11 +37,11 @@ from dockerprettyps import errors __version__ = "1.0.2" -__title__ = r""" +__title__ = """ _ _ _ _ __| |___ __| |_____ _ _ ___ _ __ _ _ ___| |_| |_ _ _ ___ _ __ ___ - / _` / _ \/ _| / / -_) '_| |___| | '_ \ '_/ -_) _| _| || | |___| | '_ (_-< - \__,_\___/\__|_\_\___|_| | .__/_| \___|\__|\__|\_, | | .__/__/ + / _` / _ \\/ _| / / -_) '_| |___| | '_ \\ '_/ -_) _| _| || | |___| | '_ (_-< + \\__,_\\___/\\__|_\\_\\___|_| | .__/_| \\___|\\__|\\__|\\_, | | .__/__/ |_| |__/ |_| """ From 7d43204615972eab3ed142d6ccaf9063c7fcb1e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Jan=C4=8Da=C5=99=C3=ADk?= Date: Tue, 30 Dec 2025 22:20:30 +0100 Subject: [PATCH 5/5] Fix _parse_ps_date to handle (health: starting) status --- dockerprettyps/__init__.py | 2 +- pr_body.txt | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 pr_body.txt diff --git a/dockerprettyps/__init__.py b/dockerprettyps/__init__.py index 6920699..e78a273 100644 --- a/dockerprettyps/__init__.py +++ b/dockerprettyps/__init__.py @@ -276,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() diff --git a/pr_body.txt b/pr_body.txt new file mode 100644 index 0000000..876a013 --- /dev/null +++ b/pr_body.txt @@ -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