Merge pull request #224 from Xoconoch/dev
Guard docker-compose file image
This commit is contained in:
109
.github/scripts/ensure_compose_image.py
vendored
Executable file
109
.github/scripts/ensure_compose_image.py
vendored
Executable file
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except Exception:
|
||||||
|
sys.stderr.write("PyYAML is required to run this check.\n")
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
EXPECTED_IMAGE = "cooldockerizer93/spotizerr"
|
||||||
|
|
||||||
|
|
||||||
|
def load_compose(path: Path):
|
||||||
|
with path.open("r", encoding="utf-8") as f:
|
||||||
|
return yaml.safe_load(f) or {}
|
||||||
|
|
||||||
|
|
||||||
|
def save_compose(path: Path, data) -> None:
|
||||||
|
with path.open("w", encoding="utf-8") as f:
|
||||||
|
yaml.safe_dump(data, f, sort_keys=False)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_image_unversioned(data) -> Tuple[bool, str, str]:
|
||||||
|
"""
|
||||||
|
Returns (changed, old_image, new_image)
|
||||||
|
"""
|
||||||
|
services = (data or {}).get("services", {})
|
||||||
|
svc = services.get("spotizerr", {})
|
||||||
|
image = svc.get("image")
|
||||||
|
if image == EXPECTED_IMAGE:
|
||||||
|
return False, image, image
|
||||||
|
|
||||||
|
# Normalize to expected image if it has a tag/digest or is different
|
||||||
|
svc["image"] = EXPECTED_IMAGE
|
||||||
|
services["spotizerr"] = svc
|
||||||
|
data["services"] = services
|
||||||
|
return True, image, EXPECTED_IMAGE
|
||||||
|
|
||||||
|
|
||||||
|
def git(*args: str) -> subprocess.CompletedProcess:
|
||||||
|
return subprocess.run(["git", *args], check=False, text=True, capture_output=True)
|
||||||
|
|
||||||
|
|
||||||
|
def autocommit(file_path: str) -> None:
|
||||||
|
# Configure git identity if missing
|
||||||
|
git("config", "user.name").stdout
|
||||||
|
if git("config", "user.name").stdout.strip() == "":
|
||||||
|
git("config", "user.name", "github-actions[bot]")
|
||||||
|
if git("config", "user.email").stdout.strip() == "":
|
||||||
|
git("config", "user.email", "github-actions[bot]@users.noreply.github.com")
|
||||||
|
# Stage and commit
|
||||||
|
git("add", file_path)
|
||||||
|
status = git("status", "--porcelain").stdout.strip()
|
||||||
|
if status:
|
||||||
|
msg = "chore: normalize docker-compose image to cooldockerizer93/spotizerr"
|
||||||
|
commit_res = git("commit", "-m", msg)
|
||||||
|
if commit_res.returncode != 0:
|
||||||
|
sys.stderr.write(f"Git commit failed: {commit_res.stderr}\n")
|
||||||
|
sys.exit(1)
|
||||||
|
push_res = git("push")
|
||||||
|
if push_res.returncode != 0:
|
||||||
|
sys.stderr.write(f"Git push failed: {push_res.stderr}\n")
|
||||||
|
sys.exit(1)
|
||||||
|
print("Pushed normalization commit")
|
||||||
|
else:
|
||||||
|
print("No changes to commit")
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str]) -> int:
|
||||||
|
# Usage: ensure_compose_image.py [docker-compose.yaml] [--autocommit]
|
||||||
|
compose_path = (
|
||||||
|
Path(argv[1])
|
||||||
|
if len(argv) > 1 and not argv[1].startswith("-")
|
||||||
|
else Path("docker-compose.yaml")
|
||||||
|
)
|
||||||
|
do_autocommit = any(arg == "--autocommit" for arg in argv[1:])
|
||||||
|
|
||||||
|
if not compose_path.exists():
|
||||||
|
sys.stderr.write(f"File not found: {compose_path}\n")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = load_compose(compose_path)
|
||||||
|
except Exception as e:
|
||||||
|
sys.stderr.write(f"Failed to parse YAML from {compose_path}: {e}\n")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
changed, old_image, new_image = ensure_image_unversioned(data)
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
save_compose(compose_path, data)
|
||||||
|
sys.stderr.write(
|
||||||
|
f"Normalized services.spotizerr.image from '{old_image}' to '{new_image}'\n"
|
||||||
|
)
|
||||||
|
# For pre-commit: exit non-zero to force user to re-stage
|
||||||
|
if do_autocommit:
|
||||||
|
autocommit(str(compose_path))
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(f"OK: docker-compose image is '{EXPECTED_IMAGE}'")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv))
|
||||||
46
.github/workflows/compose-image-guard.yml
vendored
Normal file
46
.github/workflows/compose-image-guard.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
name: Compose Image Guard
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ dev, main, master ]
|
||||||
|
paths:
|
||||||
|
- 'docker-compose.yaml'
|
||||||
|
- '.github/workflows/compose-image-guard.yml'
|
||||||
|
- '.github/scripts/ensure_compose_image.py'
|
||||||
|
pull_request:
|
||||||
|
branches: [ dev, main, master ]
|
||||||
|
paths:
|
||||||
|
- 'docker-compose.yaml'
|
||||||
|
- '.github/workflows/compose-image-guard.yml'
|
||||||
|
- '.github/scripts/ensure_compose_image.py'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-compose-image:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install pyyaml
|
||||||
|
|
||||||
|
# On pushes to this repo: normalize and push
|
||||||
|
- name: Validate and normalize (auto-commit on push)
|
||||||
|
if: github.event_name == 'push' && github.repository == 'Xoconoch/spotizerr'
|
||||||
|
run: python .github/scripts/ensure_compose_image.py docker-compose.yaml --autocommit
|
||||||
|
|
||||||
|
# On PRs (including forks): validate only, no push
|
||||||
|
- name: Validate (no auto-commit on PR)
|
||||||
|
if: github.event_name != 'push' || github.repository != 'Xoconoch/spotizerr'
|
||||||
|
run: python .github/scripts/ensure_compose_image.py docker-compose.yaml
|
||||||
@@ -52,4 +52,4 @@ repos:
|
|||||||
args: [--no-strict-optional, --ignore-missing-imports]
|
args: [--no-strict-optional, --ignore-missing-imports]
|
||||||
exclude: ^spotizerr-ui/
|
exclude: ^spotizerr-ui/
|
||||||
# NOTE: you might need to add some deps here:
|
# NOTE: you might need to add some deps here:
|
||||||
additional_dependencies: [waitress==3.0.2, types-waitress, types-requests]
|
additional_dependencies: [waitress==3.0.2, types-waitress, types-requests, types-PyYAML]
|
||||||
|
|||||||
@@ -1,32 +1,28 @@
|
|||||||
name: spotizerr
|
name: spotizerr
|
||||||
|
|
||||||
services:
|
services:
|
||||||
spotizerr:
|
spotizerr:
|
||||||
image: cooldockerizer93/spotizerr:beta
|
image: cooldockerizer93/spotizerr
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
- ./downloads:/app/downloads # <-- Change this for your music library dir
|
- ./downloads:/app/downloads
|
||||||
- ./logs:/app/logs # <-- Volume for persistent logs
|
- ./logs:/app/logs
|
||||||
ports:
|
ports:
|
||||||
- 7171:7171
|
- 7171:7171
|
||||||
container_name: spotizerr-app
|
container_name: spotizerr-app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:alpine
|
image: redis:alpine
|
||||||
container_name: spotizerr-redis
|
container_name: spotizerr-redis
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
volumes:
|
volumes:
|
||||||
- redis-data:/data
|
- redis-data:/data
|
||||||
command: sh -c 'redis-server --requirepass "$REDIS_PASSWORD" --appendonly yes'
|
command: sh -c 'redis-server --requirepass "$REDIS_PASSWORD" --appendonly yes'
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
redis-data:
|
redis-data:
|
||||||
driver: local
|
driver: local
|
||||||
|
|||||||
Reference in New Issue
Block a user