Skip to content

Commit

Permalink
📦 Update release management
Browse files Browse the repository at this point in the history
- Significantly improved release management script!
- Error handling
- One line release
- Beautify
- Add update Makefile target to update HASS with latest code
  • Loading branch information
hyperb1iss committed Jul 28, 2024
1 parent f0e97aa commit 52cb7b5
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 62 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ repos:
hooks:
- id: pylint
args: [--rcfile=pyproject.toml]
additional_dependencies: [colorama]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.0
hooks:
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ check:
poetry run mypy custom_components
poetry run ruff check .

update:
@./scripts/release.py update-hass

clean:
find . -type d -name "__pycache__" -exec rm -rf {} +
find . -type f -name "*.pyc" -delete
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ packages = [{ include = "custom_components" }]
python = ">=3.12,<3.13"
homeassistant = ">=2024.6.3"
signalrgb = "0.9.6"
colorama = "^0.4.6"

[tool.poetry.group.dev.dependencies]
pytest = "^8.2"
pytest-cov = "^5.0"
pytest-asyncio = "^0.23.0"
pytest-homeassistant-custom-component = "^0.13"
black = "^23.3.0"
black = "^24.4"
isort = "^5.12.0"
mypy = "^1.11"
pylint = "^3.2"
Expand Down Expand Up @@ -58,7 +59,7 @@ exclude_lines = [

[tool.black]
line-length = 88
target-version = ['py312']
target-version = ['py311']
include = '\.pyi?$'
extend-exclude = '''
/(
Expand Down
224 changes: 164 additions & 60 deletions scripts/release.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,187 @@
#!/usr/bin/env python
#!/usr/bin/env python3

import argparse
import json
import os
import shutil
import json
import subprocess
import argparse
import sys
from collections import OrderedDict

BASE_PATH = "/home/bliss/dev"
PROJECT_LINK = "https://github.com/hyperb1iss/signalrgb-homeassistant"
ISSUE_TRACKER = f"{PROJECT_LINK}/issues"


def copy_integration(src_path, dest_path):
if os.path.exists(dest_path):
shutil.rmtree(dest_path)
shutil.copytree(src_path, dest_path)
print(f"Copied integration from {src_path} to {dest_path}")

import colorama
from colorama import Fore, Style

def update_manifest_version(manifest_path, new_version):
with open(manifest_path, "r") as f:
manifest = json.load(f)
# Initialize colorama for cross-platform colored output
colorama.init(autoreset=True)

# Update version and links
manifest["version"] = new_version
manifest["documentation"] = PROJECT_LINK
manifest["issue_tracker"] = ISSUE_TRACKER

# Create a new OrderedDict with the required order
ordered_manifest = OrderedDict()
ordered_manifest["domain"] = manifest["domain"]
ordered_manifest["name"] = manifest["name"]

# Add the rest of the items in alphabetical order
for key in sorted(manifest.keys()):
if key not in ["domain", "name"]:
ordered_manifest[key] = manifest[key]
# Constants
PROJECT_NAME = "SignalRGB Home Assistant Integration"
PROJECT_LINK = "https://github.com/hyperb1iss/signalrgb-homeassistant"
ISSUE_TRACKER = f"{PROJECT_LINK}/issues"
HASS_CONFIG_DIR = os.getenv(
"HASS_CONFIG_DIR", os.path.expanduser("~/dev/ha_core/config")
)
CUSTOM_COMPONENTS_DIR = os.path.join(HASS_CONFIG_DIR, "custom_components")


def print_banner(version: str) -> None:
"""Print a beautiful banner for the release script."""
banner = f"""
{Fore.MAGENTA}{'═' * 60}
{Fore.CYAN}🚀 {PROJECT_NAME} Release Manager {Fore.MAGENTA}
{Fore.YELLOW}Version: {version}{' ' * (49 - len(version))} {Fore.MAGENTA}
{'═' * 60}{Style.RESET_ALL}
"""
print(banner)


def print_message(message: str, color: str = Fore.GREEN) -> None:
"""Print a colored message."""
print(f"{color}{message}{Style.RESET_ALL}")


def check_tool_installed(tool_name: str) -> None:
"""Check if a tool is installed."""
if shutil.which(tool_name) is None:
print_message(
f"❌ {tool_name} is not installed. Please install it and try again.",
Fore.RED,
)
sys.exit(1)


def copy_integration(src_path: str, dest_path: str, verbose: bool = False) -> None:
"""Copy the integration files to the destination."""
try:
if os.path.exists(dest_path):
shutil.rmtree(dest_path)
if verbose:
print_message(
f"🗑️ Removed existing directory at {dest_path}", Fore.BLUE
)
shutil.copytree(src_path, dest_path)
print_message(f"✅ Copied integration from {src_path} to {dest_path}")
except OSError as e:
print_message(f"❌ Error copying integration: {e}", Fore.RED)
sys.exit(1)


def update_manifest(
manifest_path: str, new_version: str, verbose: bool = False
) -> None:
"""Update the manifest.json file with the new version and reorder entries."""
try:
with open(manifest_path, "r", encoding="utf-8") as f:
manifest = json.load(f)

manifest["version"] = new_version
manifest["documentation"] = PROJECT_LINK
manifest["issue_tracker"] = ISSUE_TRACKER

ordered_manifest = OrderedDict(
[
("domain", manifest["domain"]),
("name", manifest["name"]),
]
+ sorted(
[(k, v) for k, v in manifest.items() if k not in ["domain", "name"]],
key=lambda x: x[0],
)
)

with open(manifest_path, "w", encoding="utf-8") as f:
json.dump(ordered_manifest, f, indent=2)

print_message(
f"✅ Updated manifest version to {new_version} and reordered entries"
)
if verbose:
print_message(
f"📄 New manifest: {json.dumps(ordered_manifest, indent=2)}", Fore.BLUE
)
except (OSError, json.JSONDecodeError) as e:
print_message(f"❌ Error updating manifest: {e}", Fore.RED)
sys.exit(1)


def git_commit_and_tag(version: str, verbose: bool = False) -> None:
"""Commit changes and create a new tag."""
try:
commit_message = f"🚀 Release version {version}"
subprocess.run(["git", "add", "custom_components"], check=True)
subprocess.run(["git", "commit", "-m", commit_message], check=True)
subprocess.run(
["git", "tag", "-a", f"v{version}", "-m", f"Version {version}"], check=True
)
print_message(f"✅ Changes committed and tagged as v{version}")
if verbose:
print_message(f"🔖 Created tag: v{version}", Fore.BLUE)
except subprocess.CalledProcessError as e:
print_message(f"❌ Error committing/tagging: {e}", Fore.RED)
sys.exit(1)


def update_hass(src_path: str, verbose: bool = False) -> None:
"""Update Home Assistant integration."""
src_path = os.path.join(os.getcwd(), "custom_components", "signalrgb")
dest_path = os.path.join(CUSTOM_COMPONENTS_DIR, "signalrgb")

print_message(
f"🔄 Updating Home Assistant integration from {src_path} to {dest_path}",
Fore.BLUE,
)
copy_integration(src_path, dest_path, verbose)
print_message(
"⚠️ Remember to reload Home Assistant to apply the changes.", Fore.YELLOW
)

with open(manifest_path, "w") as f:
json.dump(ordered_manifest, f, indent=2)

print(f"Updated manifest version to {new_version} and reordered entries")
def do_release(src_path: str, version: str, verbose: bool = False) -> None:
"""Perform the release process."""
print_banner(version)
manifest_path = os.path.join(src_path, "manifest.json")

update_manifest(manifest_path, version, verbose)
git_commit_and_tag(version, verbose)

def git_commit_and_tag(version):
commit_message = f"Release version {version}"
subprocess.run(["git", "add", "custom_components"], check=True)
subprocess.run(["git", "commit", "-m", commit_message], check=True)
subprocess.run(
["git", "tag", "-a", f"v{version}", "-m", f"Version {version}"], check=True
print_message(f"\n🎉 Release [{version}] process completed successfully!", Fore.CYAN)
print_message(
"⚠️ Don't forget to push the changes and the new tag to GitHub.", Fore.YELLOW
)
print(f"Changes committed and tagged as v{version}")


def main():
def main() -> None:
"""Main function to handle argument parsing and command execution."""
parser = argparse.ArgumentParser(
description="Release management for SignalRGB Home Assistant integration"
description=f"Release management for {PROJECT_NAME}"
)
parser.add_argument("version", help="New version number for the release")
args = parser.parse_args()

# Paths
ha_src_path = f"{BASE_PATH}/ha_core/homeassistant/components/signalrgb"
custom_components_path = (
f"{BASE_PATH}/signalrgb-homeassistant/custom_components/signalrgb"
parser.add_argument(
"command", choices=["update-hass", "release"], help="Command to run"
)
parser.add_argument(
"version",
nargs="?",
help="Version number for the release (required for release command)",
)
parser.add_argument(
"-v", "--verbose", action="store_true", help="Enable verbose output"
)
manifest_path = os.path.join(custom_components_path, "manifest.json")

# Copy integration files
copy_integration(ha_src_path, custom_components_path)

# Update manifest version
update_manifest_version(manifest_path, args.version)

# Commit changes and create tag
git_commit_and_tag(args.version)
args = parser.parse_args()

print("Release process completed successfully!")
# Paths
src_path = os.path.join(os.getcwd(), "custom_components", "signalrgb")

# Check for necessary tools
check_tool_installed("git")

if args.command == "update-hass":
update_hass(src_path, args.verbose)
elif args.command == "release":
if not args.version:
print_message("❌ Version number is required for release command.", Fore.RED)
sys.exit(1)
do_release(src_path, args.version, args.verbose)


if __name__ == "__main__":
Expand Down

0 comments on commit 52cb7b5

Please sign in to comment.