From 7a38c6976fca34665665688b9d4188a8d8f88c15 Mon Sep 17 00:00:00 2001 From: Buba Date: Tue, 26 Aug 2025 19:46:49 +0000 Subject: [PATCH 1/6] Replace Flake8 with Ruff, move long-running pre-commit checks to end --- .flake8 | 9 ----- .pre-commit-config.yaml | 90 ++++++++++++++++++++--------------------- .vscode/extensions.json | 2 +- CLAUDE.md | 8 +--- Taskfile.yaml | 7 +++- ruff.toml | 43 ++++++++++++++++++++ 6 files changed, 96 insertions(+), 63 deletions(-) delete mode 100644 .flake8 create mode 100644 ruff.toml diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 45761ee6..00000000 --- a/.flake8 +++ /dev/null @@ -1,9 +0,0 @@ -# Options: https://flake8.pycqa.org/en/latest/user/options.html - -[flake8] - -# Line too long (82 > 79 characters) (E501) -# Multiple statements on one line (colon) (E701) -extend-ignore = E501,E701 - -exclude = .git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d083a7fb..402d661a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,19 +24,6 @@ repos: args: ["--allow-missing-credentials"] - id: detect-private-key - # Find security vulnerabilities, compliance issues, and infrastructure misconfigurations early in the development cycle of your infrastructure-as-code with KICS by Checkmarx. - # KICS stands for Keeping Infrastructure as Code Secure, it is open source and is a must-have for any cloud native project. - # https://docs.kics.io/latest/commands/ - # Ignoring errors: https://docs.kics.io/latest/running-kics/#using_commands_on_scanned_files_as_comments - - repo: https://github.com/Checkmarx/kics - rev: v2.1.13 - hooks: - - id: kics-scan - # TODO solve the high severity issues first - args: ["--exclude-severities", "info,low,medium"] - pass_filenames: false - always_run: true - # Gitleaks is a SAST tool for detecting and preventing hardcoded secrets like passwords, api keys, and tokens in git repos. # Gitleaks is an easy-to-use, all-in-one solution for detecting secrets, past or present, in your code. - repo: https://github.com/gitleaks/gitleaks @@ -44,6 +31,39 @@ repos: hooks: - id: gitleaks + # ShellCheck, a static analysis tool for shell scripts - https://www.shellcheck.net/ + # Ignoring errors: https://www.shellcheck.net/wiki/Ignore, `.shellcheckrc` + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.10.0.1 + hooks: + - id: shellcheck + + # Ruff is an extremely fast Python linter and code formatter, written in Rust. + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.12.10 + hooks: + # Linter - https://docs.astral.sh/ruff/linter/ + - id: ruff-check + args: ["--fix"] + # TODO Enable after pendling MRs are merged + # Formatter - https://docs.astral.sh/ruff/formatter/ + # - id: ruff-format + + # Dockerfile linter, validate inline bash, written in Haskell + - repo: https://github.com/hadolint/hadolint + rev: v2.13.0-beta + hooks: + - id: hadolint-docker + exclude: "\\.dockerignore$" + + # Run renovate-config-validator + - repo: https://github.com/renovatebot/pre-commit-hooks + rev: 41.1.4 + hooks: + - id: renovate-config-validator + # TODO Old nodeenv (v0.13.4) does not support "lts" - remove after Ubuntu upgrade + language_version: "22.17.1" + # pre-commit git hooks to take care of Terraform configurations - repo: https://github.com/antonbabenko/pre-commit-terraform rev: v1.100.0 @@ -68,41 +88,21 @@ repos: hooks: - id: ansible-lint files: ^ansible/ + always_run: false entry: > env ANSIBLE_ROLES_PATH=~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:ansible/roles env ANSIBLE_COLLECTIONS_PATH=/usr/lib/python3/dist-packages:/usr/share/ansible/collections:/etc/ansible/collections:/opt/pipx/venvs/ansible-core/lib/python3.12/site-packages/ansible_collections:~/.ansible/collections:collections python3 -m ansiblelint --force-color - # ShellCheck, a static analysis tool for shell scripts - https://www.shellcheck.net/ - # Ignoring errors: https://www.shellcheck.net/wiki/Ignore, `.shellcheckrc` - - repo: https://github.com/shellcheck-py/shellcheck-py - rev: v0.10.0.1 - hooks: - - id: shellcheck - - # flake8 is a python tool that glues together pycodestyle, pyflakes, mccabe, and third-party plugins to check the style and quality of some python code. - # Third-party scripts are excluded. - - repo: https://github.com/PyCQA/flake8 - rev: 7.3.0 - hooks: - - id: flake8 - exclude: | - (?x)^( - scripts/git-filter-repo.py| - scripts/test-colors.py - )$ - - # Dockerfile linter, validate inline bash, written in Haskell - - repo: https://github.com/hadolint/hadolint - rev: v2.13.0-beta - hooks: - - id: hadolint-docker - exclude: "\\.dockerignore$" - - # Run renovate-config-validator - - repo: https://github.com/renovatebot/pre-commit-hooks - rev: 41.1.4 + # Find security vulnerabilities, compliance issues, and infrastructure misconfigurations early in the development cycle of your infrastructure-as-code with KICS by Checkmarx. + # KICS stands for Keeping Infrastructure as Code Secure, it is open source and is a must-have for any cloud native project. + # https://docs.kics.io/latest/commands/ + # Ignoring errors: https://docs.kics.io/latest/running-kics/#using_commands_on_scanned_files_as_comments + - repo: https://github.com/Checkmarx/kics + rev: v2.1.13 hooks: - - id: renovate-config-validator - # TODO Old nodeenv (v0.13.4) does not support "lts" - remove after Ubuntu upgrade - language_version: "22.17.1" + - id: kics-scan + # TODO solve the high severity issues first + args: ["--exclude-severities", "info,low,medium"] + pass_filenames: false + always_run: false diff --git a/.vscode/extensions.json b/.vscode/extensions.json index bee993d9..d3f98dd7 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -8,7 +8,7 @@ "mhutchie.git-graph", "hashicorp.terraform", "ms-vscode.makefile-tools", - "ms-python.flake8", + "charliermarsh.ruff", "ban.spellright" ] } diff --git a/CLAUDE.md b/CLAUDE.md index 03b3b554..9ff7cc4f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,18 +19,12 @@ task --list-all # Run all linting and build tasks task build -# Run linting tools (via pre-commit) +# Run linting tools on all files (via pre-commit) task lint # Open TaskUI (terminal UI for executing tasks) task ui -# Update pre-commit hooks -task pre-commit-update - -# Run renovatebot locally -task renovate - # Clear generated content and cache data task clean ``` diff --git a/Taskfile.yaml b/Taskfile.yaml index 3a96f155..338c8361 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -35,10 +35,15 @@ tasks: # See https://pre-commit.com for more information lint: - desc: Run linting tools (via pre-commit) + desc: Run linting tools on all files (via pre-commit) cmds: - pre-commit run --all-files + lint-stashed: + desc: Run linting tools on stashed files (via pre-commit) + cmds: + - pre-commit run + git-rebase-fixup: desc: Interactively rebase current branch to origin/main (stash, rebase, pop) aliases: [rebase] diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..75a9b630 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,43 @@ +# https://docs.astral.sh/ruff/settings/ + +target-version = "py313" +line-length = 120 + +[lint] +select = [ + "E", # pycodestyle + "F", # Pyflakes + "PL", # Pylint + "UP", # pyupgrade + "A", # flake8-builtins + "B", # flake8-bugbear + "SIM", # flake8-simplify + "C4", # flake8-comprehensions + "PT", # flake8-pytest-style + "N", # pep8-naming + "FAST", # FastAPI + "I", # isort +] + +ignore = [ + "E501", # Line too long + "PLR2004", # Magic value used in comparison +] + +# Exclude third-party scripts +exclude = [ + "scripts/git-filter-repo.py", + "scripts/test-colors.py", +] + +[lint.isort] +known-first-party = [ + "src", + "tools", +] + +[format] +quote-style = "double" +indent-style = "space" +docstring-code-format = true +skip-magic-trailing-comma = true From afb509397862b05cd9b74e5e4b98a6a7899dbeec Mon Sep 17 00:00:00 2001 From: Buba Date: Wed, 27 Aug 2025 17:17:44 +0000 Subject: [PATCH 2/6] Move MCP tools to subfolder --- scripts/task-mcp/server.py | 2 +- scripts/task-mcp/{ => tools}/find_app_icon.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename scripts/task-mcp/{ => tools}/find_app_icon.py (100%) diff --git a/scripts/task-mcp/server.py b/scripts/task-mcp/server.py index 306c5535..b3a04880 100755 --- a/scripts/task-mcp/server.py +++ b/scripts/task-mcp/server.py @@ -14,7 +14,7 @@ from fastmcp.tools import Tool from starlette.requests import Request from starlette.responses import PlainTextResponse -from find_app_icon import AppIconFinder +from tools.find_app_icon import AppIconFinder # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') diff --git a/scripts/task-mcp/find_app_icon.py b/scripts/task-mcp/tools/find_app_icon.py similarity index 100% rename from scripts/task-mcp/find_app_icon.py rename to scripts/task-mcp/tools/find_app_icon.py From d18629841549977be8a55811913341b5ecfbf56a Mon Sep 17 00:00:00 2001 From: Buba Date: Wed, 27 Aug 2025 22:05:06 +0200 Subject: [PATCH 3/6] Enable ruff - isort --- docker/labctl.py | 9 +++++---- docs/web/update-docs.py | 9 +++++---- scripts/create-example-env.py | 1 - scripts/git-reorder-fixup.py | 2 +- scripts/github-extract-links.py | 2 +- scripts/github-star-repo.py | 3 ++- scripts/proxy-request-log.py | 2 +- scripts/task-mcp/server.py | 4 +++- scripts/task-mcp/tools/find_app_icon.py | 7 ++++--- 9 files changed, 22 insertions(+), 17 deletions(-) diff --git a/docker/labctl.py b/docker/labctl.py index 4665af83..900eb3b1 100755 --- a/docker/labctl.py +++ b/docker/labctl.py @@ -4,16 +4,17 @@ Uses YAML configuration to manage Docker services. """ -import os -import sys import argparse -import socket -import yaml import logging +import os +import socket import subprocess +import sys from pathlib import Path from typing import Dict, Optional +import yaml + # Configure logging logging.basicConfig(level=logging.INFO, format="%(message)s") logger = logging.getLogger(__name__) diff --git a/docs/web/update-docs.py b/docs/web/update-docs.py index 7544f33a..31cc684d 100755 --- a/docs/web/update-docs.py +++ b/docs/web/update-docs.py @@ -1,15 +1,16 @@ #!/usr/bin/env python3 +import argparse +import logging import os +import re import shutil import subprocess import sys -import yaml -import argparse -import re -import logging from pathlib import Path +import yaml + class DocsProcessor: """Class for processing documentation files and managing links.""" diff --git a/scripts/create-example-env.py b/scripts/create-example-env.py index 5778510f..c058e467 100755 --- a/scripts/create-example-env.py +++ b/scripts/create-example-env.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import sys - SENSITIVE_VARS_TO_MASK = [ 'KEY', 'USERNAME', diff --git a/scripts/git-reorder-fixup.py b/scripts/git-reorder-fixup.py index c9d6e159..d9f1cc00 100755 --- a/scripts/git-reorder-fixup.py +++ b/scripts/git-reorder-fixup.py @@ -6,8 +6,8 @@ # alias rebase-fixup="GIT_SEQUENCE_EDITOR=~/repos/infra/scripts/git-reorder-fixup.py git rebase -i" # git config --global sequence.editor "code --wait" -import sys import subprocess +import sys def reorder_commits(file_path: str) -> None: diff --git a/scripts/github-extract-links.py b/scripts/github-extract-links.py index 36e07d2f..d91bbf03 100755 --- a/scripts/github-extract-links.py +++ b/scripts/github-extract-links.py @@ -3,7 +3,7 @@ import os import re import sys -from typing import Set, List +from typing import List, Set def extract_github_links(directory: str) -> List[str]: diff --git a/scripts/github-star-repo.py b/scripts/github-star-repo.py index 619bf66b..8ae22053 100755 --- a/scripts/github-star-repo.py +++ b/scripts/github-star-repo.py @@ -5,8 +5,9 @@ # `./github-extract-links.py ~/repos/infra | ./github-star-repo.py` import os +from urllib.parse import unquote, urlparse + import requests -from urllib.parse import urlparse, unquote def star_github_repo(repo_url: str) -> None: diff --git a/scripts/proxy-request-log.py b/scripts/proxy-request-log.py index 8e6dd9f8..6b37af5b 100755 --- a/scripts/proxy-request-log.py +++ b/scripts/proxy-request-log.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 +import http.client import http.server import socketserver -import http.client from urllib.parse import urlparse, urlunparse diff --git a/scripts/task-mcp/server.py b/scripts/task-mcp/server.py index b3a04880..64b1485c 100755 --- a/scripts/task-mcp/server.py +++ b/scripts/task-mcp/server.py @@ -5,15 +5,17 @@ """ import logging +import os import re import subprocess import sys -import os from typing import Callable, Dict, List + from fastmcp import FastMCP from fastmcp.tools import Tool from starlette.requests import Request from starlette.responses import PlainTextResponse + from tools.find_app_icon import AppIconFinder # Configure logging diff --git a/scripts/task-mcp/tools/find_app_icon.py b/scripts/task-mcp/tools/find_app_icon.py index 0f480cf3..83fc632e 100644 --- a/scripts/task-mcp/tools/find_app_icon.py +++ b/scripts/task-mcp/tools/find_app_icon.py @@ -1,9 +1,10 @@ -import requests -import sys import re -from bs4 import BeautifulSoup +import sys from urllib.parse import urljoin +import requests +from bs4 import BeautifulSoup + class AppIconFinder: """ From 49d210f4486644866fd9606642906929fa49794e Mon Sep 17 00:00:00 2001 From: Buba Date: Wed, 27 Aug 2025 22:10:05 +0200 Subject: [PATCH 4/6] Enable ruff - pyupgrade --- docker/labctl.py | 11 +++++------ docs/web/update-docs.py | 10 +++++----- scripts/create-example-env.py | 2 +- scripts/git-reorder-fixup.py | 2 +- scripts/github-extract-links.py | 11 +++++------ scripts/task-mcp/server.py | 6 +++--- 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/docker/labctl.py b/docker/labctl.py index 900eb3b1..3f553e91 100755 --- a/docker/labctl.py +++ b/docker/labctl.py @@ -11,7 +11,6 @@ import subprocess import sys from pathlib import Path -from typing import Dict, Optional import yaml @@ -20,7 +19,7 @@ logger = logging.getLogger(__name__) # Global variables -host_config_dir: Optional[Path] = None +host_config_dir: Path | None = None docker_stacks_dir: Path = Path(__file__).parent.absolute() @@ -62,7 +61,7 @@ def get_compose_file(stack_dir: str, service_name: str) -> str: def has_build_directive(compose_file: str) -> bool: """Check if the service uses a build directive.""" - with open(compose_file, "r") as f: + with open(compose_file) as f: yaml_content = yaml.safe_load(f) if yaml_content and 'services' in yaml_content: for service_config in yaml_content['services'].values(): @@ -134,10 +133,10 @@ def docker_command(stack_dir: str, service_name: str, action: str) -> None: docker(["compose", "-f", compose_file, *env_file_args, "up", "--detach", "--force-recreate"]) -def load_services_config(config_file: str) -> Dict: +def load_services_config(config_file: str) -> dict: """Load services configuration from YAML file.""" try: - with open(config_file, 'r') as file: + with open(config_file) as file: config = yaml.safe_load(file) return config except Exception as e: @@ -145,7 +144,7 @@ def load_services_config(config_file: str) -> Dict: sys.exit(1) -def process_services(config: Dict, state_override: Optional[str] = None) -> None: +def process_services(config: dict, state_override: str | None = None) -> None: """Process services based on the configuration.""" if not config or 'services' not in config: logger.error("Error: Invalid configuration format. 'services' key not found.") diff --git a/docs/web/update-docs.py b/docs/web/update-docs.py index 31cc684d..c1d37a6f 100755 --- a/docs/web/update-docs.py +++ b/docs/web/update-docs.py @@ -46,7 +46,7 @@ def load_config(self): """Load markdown locations from YAML file.""" yaml_path = self.repository_path / "docs" / "web" / "update-docs-config.yaml" try: - with open(yaml_path, "r") as yaml_file: + with open(yaml_path) as yaml_file: data = yaml.safe_load(yaml_file) return data.get("locations", []) except (FileNotFoundError, yaml.YAMLError) as e: @@ -199,7 +199,7 @@ def delete_directory_content(self, content_path): def copy_markdown_file(self, source_file_path, target_file_path, weight=0): """Copy and process a markdown file, adding frontmatter and fixing links.""" - with open(source_file_path, "r") as readme_file: + with open(source_file_path) as readme_file: content = readme_file.read() lines = content.splitlines(True) # Keep line endings @@ -267,7 +267,7 @@ def get_icon_url(self, icon): def get_compose_metadata(self, file_path): """Extract metadata from a docker-compose file.""" try: - with open(file_path, "r") as stream: + with open(file_path) as stream: compose_dict = yaml.safe_load(stream) if compose_dict is None: return {} @@ -309,7 +309,7 @@ def process_docker_compose_file(self, source_dir, target_dir, root, file): metadata = self.get_compose_metadata(source_file_path) metadata.setdefault("name", source_file_path.stem.capitalize()) - with open(source_file_path, "r") as compose_file: + with open(source_file_path) as compose_file: lines = compose_file.readlines() yaml_started = False @@ -371,7 +371,7 @@ def get_git_root(): ["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE, check=True, - universal_newlines=True, + text=True, ).stdout.strip() diff --git a/scripts/create-example-env.py b/scripts/create-example-env.py index c058e467..ecfa1f40 100755 --- a/scripts/create-example-env.py +++ b/scripts/create-example-env.py @@ -28,7 +28,7 @@ def contains_any_substring(string: str, substrings: list[str]) -> bool: def mask_sensitive_variables(input_file: str) -> str: - with open(input_file, 'r') as f: + with open(input_file) as f: lines = f.readlines() output_lines = [] diff --git a/scripts/git-reorder-fixup.py b/scripts/git-reorder-fixup.py index d9f1cc00..0d5807b2 100755 --- a/scripts/git-reorder-fixup.py +++ b/scripts/git-reorder-fixup.py @@ -13,7 +13,7 @@ def reorder_commits(file_path: str) -> None: GIT_SEQUENCE_EDITOR = 'nano' - with open(file_path, 'r') as file: + with open(file_path) as file: lines = file.readlines() reordered_lines = [] diff --git a/scripts/github-extract-links.py b/scripts/github-extract-links.py index d91bbf03..ca449ca8 100755 --- a/scripts/github-extract-links.py +++ b/scripts/github-extract-links.py @@ -3,16 +3,15 @@ import os import re import sys -from typing import List, Set -def extract_github_links(directory: str) -> List[str]: - github_links: Set[str] = set() +def extract_github_links(directory: str) -> list[str]: + github_links: set[str] = set() for root, dirs, files in os.walk(directory): for file in files: if file.endswith(".md") or file.endswith(".yml") or file.endswith(".yaml") or file.endswith(".sh") or file.startswith("Dockerfile"): file_path = os.path.join(root, file) - with open(file_path, "r") as f: + with open(file_path) as f: content = f.read() # Usernames for user accounts on GitHub can only contain alphanumeric characters and dashes ( - ). links = re.findall(r"https://github.com/([\w.\-\_]+/[\w.\-\_]+)", content) @@ -21,8 +20,8 @@ def extract_github_links(directory: str) -> List[str]: return list(github_links) -def trim_git_ending(links: List[str]) -> List[str]: - trimmed_links: List[str] = [] +def trim_git_ending(links: list[str]) -> list[str]: + trimmed_links: list[str] = [] for link in links: if link.endswith(".git"): trimmed_links.append(link[:-4]) diff --git a/scripts/task-mcp/server.py b/scripts/task-mcp/server.py index 64b1485c..305c6adf 100755 --- a/scripts/task-mcp/server.py +++ b/scripts/task-mcp/server.py @@ -9,7 +9,7 @@ import re import subprocess import sys -from typing import Callable, Dict, List +from collections.abc import Callable from fastmcp import FastMCP from fastmcp.tools import Tool @@ -36,7 +36,7 @@ def get_git_root() -> str: ["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE, check=True, - universal_newlines=True, + text=True, ) except FileNotFoundError: raise RuntimeError("Git executable not found. Please install Git and ensure it is on your PATH.") from None @@ -45,7 +45,7 @@ def get_git_root() -> str: return result.stdout.strip() -def get_task_list() -> List[Dict[str, str]]: +def get_task_list() -> list[dict[str, str]]: """ Get the list of available tasks by running 'task --list-all'. From 30f0ee79a5a5fc0bec1f6d20b4440355e1809fa4 Mon Sep 17 00:00:00 2001 From: Buba Date: Wed, 27 Aug 2025 22:16:31 +0200 Subject: [PATCH 5/6] Enable ruff - flake8-bugbear and flake8-simplify --- docs/web/update-docs.py | 11 ++--------- scripts/github-extract-links.py | 7 ++----- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/docs/web/update-docs.py b/docs/web/update-docs.py index c1d37a6f..d637d2be 100755 --- a/docs/web/update-docs.py +++ b/docs/web/update-docs.py @@ -383,15 +383,8 @@ def get_git_root(): args = parser.parse_args() # Get repository and output paths - if args.repository_path: - repository_path = Path(args.repository_path) - else: - repository_path = Path(get_git_root()) - - if args.output_content_path: - output_content_path = Path(args.output_content_path) - else: - output_content_path = repository_path / "docs" / "web" / "src" / "content" + repository_path = Path(args.repository_path) if args.repository_path else Path(get_git_root()) + output_content_path = Path(args.output_content_path) if args.output_content_path else repository_path / "docs" / "web" / "src" / "content" # Create and run the docs processor processor = DocsProcessor(repository_path, output_content_path, args.verbose) diff --git a/scripts/github-extract-links.py b/scripts/github-extract-links.py index ca449ca8..1ec3e2b7 100755 --- a/scripts/github-extract-links.py +++ b/scripts/github-extract-links.py @@ -7,7 +7,7 @@ def extract_github_links(directory: str) -> list[str]: github_links: set[str] = set() - for root, dirs, files in os.walk(directory): + for root, _dirs, files in os.walk(directory): for file in files: if file.endswith(".md") or file.endswith(".yml") or file.endswith(".yaml") or file.endswith(".sh") or file.startswith("Dockerfile"): file_path = os.path.join(root, file) @@ -31,10 +31,7 @@ def trim_git_ending(links: list[str]) -> list[str]: def main(): - if len(sys.argv) > 1: - directory = sys.argv[1] - else: - directory = os.getcwd() + directory = sys.argv[1] if len(sys.argv) > 1 else os.getcwd() links = extract_github_links(directory) for link in links: From afcddc7ca0c44ea9918ac60ede164da3555dd177 Mon Sep 17 00:00:00 2001 From: Buba Date: Wed, 27 Aug 2025 22:25:03 +0200 Subject: [PATCH 6/6] Enable ruff - flake8-comprehensions and Pylint, manual fixes --- docker/labctl.py | 49 +++++++++++++++--------------------- scripts/git-reorder-fixup.py | 22 ++++++++-------- scripts/proxy-request-log.py | 2 +- 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/docker/labctl.py b/docker/labctl.py index 3f553e91..e48dd176 100755 --- a/docker/labctl.py +++ b/docker/labctl.py @@ -19,7 +19,6 @@ logger = logging.getLogger(__name__) # Global variables -host_config_dir: Path | None = None docker_stacks_dir: Path = Path(__file__).parent.absolute() @@ -32,12 +31,11 @@ def create_network_if_missing(network_name: str) -> None: docker(["network", "create", "--driver", "bridge", network_name]) -def create_localhost_link() -> None: +def create_localhost_link(docker_config_dir: Path) -> None: """Create 'localhost' symlink in the parent directory.""" - parent_dir = Path(host_config_dir).parent hostname = socket.gethostname() - localhost_link = parent_dir / "localhost" - hostname_dir = parent_dir / hostname + localhost_link = docker_config_dir / "localhost" + hostname_dir = docker_config_dir / hostname if hostname_dir.exists() and hostname_dir.is_dir(): # Create or update the localhost symlink @@ -54,12 +52,12 @@ def create_localhost_link() -> None: logger.error(f"Error creating localhost symlink: {e}") -def get_compose_file(stack_dir: str, service_name: str) -> str: +def get_compose_file(stack_dir: Path, service_name: str) -> Path: """Get the yaml file path for a service.""" - return str(Path(stack_dir) / f"{service_name}.yaml") + return stack_dir / f"{service_name}.yaml" -def has_build_directive(compose_file: str) -> bool: +def has_build_directive(compose_file: Path) -> bool: """Check if the service uses a build directive.""" with open(compose_file) as f: yaml_content = yaml.safe_load(f) @@ -70,7 +68,7 @@ def has_build_directive(compose_file: str) -> bool: return False -def get_env_file_args(service_name: str) -> list[str]: +def get_env_file_args(host_config_dir: Path, service_name: str) -> list[str]: """Get environment file arguments for Docker Compose with normalized paths.""" env_paths = [ host_config_dir.parent / ".env", # Common .env file in config/docker @@ -81,9 +79,9 @@ def get_env_file_args(service_name: str) -> list[str]: args = [] for file in env_paths: - norm_file = os.path.normpath(file) - if os.path.isfile(norm_file): - args.extend(["--env-file", norm_file]) + absolute_path = file.resolve() + if absolute_path.is_file(): + args.extend(["--env-file", str(absolute_path)]) return args @@ -91,16 +89,16 @@ def docker(cmd: list[str], env=None, stdin=None, stdout=None, stderr=None) -> No subprocess.run(["docker"] + cmd, env=env, stdin=stdin, stdout=stdout, stderr=stderr, check=True) -def docker_command(stack_dir: str, service_name: str, action: str) -> None: +def docker_command(host_config_dir: Path, stack_dir: Path, service_name: str, action: str) -> None: """Execute Docker Compose command for a service.""" print() # empty line for separation compose_file = get_compose_file(stack_dir, service_name) - if not os.path.exists(compose_file): + if not compose_file.exists(): logger.error(f"Compose file not found: {compose_file}") return - env_file_args = get_env_file_args(service_name) + env_file_args = get_env_file_args(host_config_dir, service_name) # Handle pull operations if action in ["update", "pull"]: @@ -144,7 +142,7 @@ def load_services_config(config_file: str) -> dict: sys.exit(1) -def process_services(config: dict, state_override: str | None = None) -> None: +def process_services(host_config_dir: Path, config: dict, state_override: str | None = None) -> None: """Process services based on the configuration.""" if not config or 'services' not in config: logger.error("Error: Invalid configuration format. 'services' key not found.") @@ -174,21 +172,18 @@ def process_services(config: dict, state_override: str | None = None) -> None: logger.warning(f"Unknown state '{state}' for service {category}/{name}") continue - stack_dir = os.path.join(docker_stacks_dir, category) - docker_command(stack_dir, name, state) + docker_command(host_config_dir, docker_stacks_dir / category, name, state) def get_host_config_dir() -> Path: hostname = socket.gethostname().lower() script_dir = Path(__file__).parent.absolute() - config_dir = script_dir.parent / "config" / "docker" - return config_dir / hostname + docker_config_dir = script_dir.parent / "config" / "docker" + return docker_config_dir / hostname def cmd_config_apply(args) -> None: """Apply configuration to Docker services.""" - global host_config_dir - if args.config: config_file = args.config host_config_dir = Path(config_file).parent @@ -199,11 +194,11 @@ def cmd_config_apply(args) -> None: logger.info("Init...") config = load_services_config(config_file) - create_localhost_link() + create_localhost_link(host_config_dir.parent) create_network_if_missing("proxy") # Process services with optional mode override - process_services(config, args.mode) + process_services(host_config_dir, config, args.mode) # Cleanup old images if enabled in config and in update mode if args.mode == 'update': @@ -214,9 +209,6 @@ def cmd_config_apply(args) -> None: def cmd_service(args) -> None: """Manage individual Docker services.""" - global host_config_dir - host_config_dir = get_host_config_dir() - if not args.name: logger.error("Service name is required") sys.exit(1) @@ -232,8 +224,7 @@ def cmd_service(args) -> None: # Everything before the last part is the category path category_path = '/'.join(name_parts[:-1]) - stack_dir = os.path.join(docker_stacks_dir, category_path) - docker_command(stack_dir, service_name, args.operation) + docker_command(get_host_config_dir(), docker_stacks_dir / category_path, service_name, args.operation) def main() -> None: diff --git a/scripts/git-reorder-fixup.py b/scripts/git-reorder-fixup.py index 0d5807b2..b8f5fe79 100755 --- a/scripts/git-reorder-fixup.py +++ b/scripts/git-reorder-fixup.py @@ -6,12 +6,14 @@ # alias rebase-fixup="GIT_SEQUENCE_EDITOR=~/repos/infra/scripts/git-reorder-fixup.py git rebase -i" # git config --global sequence.editor "code --wait" +import os +import shlex import subprocess import sys def reorder_commits(file_path: str) -> None: - GIT_SEQUENCE_EDITOR = 'nano' + git_sequence_editor = os.environ.get('VISUAL') or os.environ.get('EDITOR') or 'nano' with open(file_path) as file: lines = file.readlines() @@ -22,28 +24,28 @@ def reorder_commits(file_path: str) -> None: for line in lines: if line.startswith('#'): reordered_lines.append(line) + elif '[FIXUP]' in line or '[F]' in line: + fixup_lines.append(line) else: - if '[FIXUP]' in line or '[F]' in line: - fixup_lines.append(line) - else: - reordered_lines.append(line) + reordered_lines.append(line) for fixup_line in fixup_lines: - original_commit_message = get_original_commit_message(fixup_line) + line_to_add = fixup_line + original_commit_message = get_original_commit_message(line_to_add) original_commit_found = False for i, reordered_line in enumerate(reordered_lines): if original_commit_message in reordered_line: - fixup_line = fixup_line.replace('pick', 'fixup', 1) - reordered_lines.insert(i + 1, fixup_line) + line_to_add = line_to_add.replace('pick', 'fixup', 1) + reordered_lines.insert(i + 1, line_to_add) original_commit_found = True break if not original_commit_found: - reordered_lines.insert(0, fixup_line) + reordered_lines.insert(0, line_to_add) with open(file_path, 'w') as file: file.writelines(reordered_lines) - res = subprocess.call([GIT_SEQUENCE_EDITOR, file_path]) + res = subprocess.call(shlex.split(git_sequence_editor) + [file_path]) sys.exit(res) diff --git a/scripts/proxy-request-log.py b/scripts/proxy-request-log.py index 6b37af5b..ada2e189 100755 --- a/scripts/proxy-request-log.py +++ b/scripts/proxy-request-log.py @@ -33,7 +33,7 @@ def proxy_request(self): post_data = self.rfile.read(content_length) if content_length > 0 else None # Create headers for the target request - headers = {key: value for key, value in self.headers.items()} + headers = dict(self.headers.items()) print("\nHeaders:") print(headers)