Skip to content
Draft
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
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ jobs:
run: pip install --upgrade hatch

- name: Run static analysis
run: |
# hatch fmt --check
echo linter errors will be fixed in a separate PR
run: hatch fmt --check

- name: Run tests
run: hatch test --python ${{ matrix.python-version }} --cover --randomize --parallel --retries 2 --retry-delay 1
82 changes: 82 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,85 @@ exclude_lines = [
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]

[tool.ruff]
line-length = 120
target-version = "py311"

[tool.ruff.lint]
ignore = [
# Style rules that are too opinionated
"TID252", # Relative imports are valid and preferred in Python packages
"T201", # Print statements are valid for CLI tools and scripts
"T203", # pprint is also valid for debugging
"FA100", # Future annotations not required for Python 3.9+
"FA102", # Future annotations not required for Python 3.9+
"EM101", # Exception message string formatting - too strict
"EM102", # Exception message f-string formatting - too strict
"EM103", # Exception message .format() - too strict
"TRY003", # Long messages outside exception class - too strict
"TRY300", # Consider moving statement to else block - too strict
"TRY301", # Abstract raise to an inner function - too strict
"TRY400", # Use logging.exception instead of logging.error - too strict
"TRY401", # Redundant exception object in logging.exception - too strict
"TRY004", # Prefer TypeError exception for invalid type - too strict
"G004", # Logging format string - too strict
"LOG015", # Using root logger - too strict
"FBT001", # Boolean positional argument - too strict
"FBT002", # Boolean default argument - too strict
"FBT003", # Boolean positional value - too strict
"BLE001", # Blind exception catch - sometimes necessary
"PLR2004", # Magic value used in comparison - too strict for simple scripts
"S607", # Starting process with partial executable path - OK for git/docker/etc
"S108", # Probable insecure usage of temp file - too strict
"INP001", # Missing __init__.py in namespace package - not needed for all dirs
"B023", # Function definition does not bind loop variable - too strict
"B008", # Function call in argument defaults - too strict
"B006", # Mutable argument default - will be addressed separately
"PLW0602", # Global variable not assigned - too strict
"PLW0603", # Global variable being updated - too strict
"PLW2901", # Outer variable overwritten by inner loop - too strict
"PLW1508", # Invalid type for environment variable default - too strict
"PLC0415", # Import outside top-level - sometimes necessary
"RUF005", # Collection literal concatenation - too strict
"RUF015", # Prefer next() over [0] indexing - too strict
"RUF059", # Unused unpacked assignment - too strict
"SLF001", # Private member access - sometimes necessary
"ARG001", # Unused function argument - sometimes required for API compatibility
"ARG002", # Unused method argument - sometimes required for API compatibility
"ARG005", # Unused lambda argument - sometimes required
"RET503", # Missing explicit return - too strict
"RET504", # Unnecessary assignment before return - too strict
"RET505", # Unnecessary else after return - too strict
"RET506", # Unnecessary else after raise - too strict
"SIM102", # Collapsible if statements - sometimes less readable
"SIM115", # Context manager for opening files - too strict
"SIM210", # Use ternary operator - sometimes less readable
"E741", # Ambiguous variable name - sometimes needed for math/physics
"E721", # Type comparison instead of isinstance - sometimes intentional
"E722", # Bare except - sometimes necessary
"UP006", # Use modern type annotations - too strict for compatibility
"UP035", # Import from collections.abc - too strict for compatibility
"N801", # Class name should use CapWords - sometimes intentional
"N802", # Function name should be lowercase - sometimes required by API
"N806", # Variable in function should be lowercase - sometimes intentional
"N818", # Exception name should end with 'Error' - sometimes 'Exception' is fine
"A001", # Variable shadowing built-in - sometimes acceptable
"A002", # Argument shadowing built-in - sometimes acceptable
"A004", # Import shadowing built-in - sometimes acceptable
"B904", # Raise from within except - will be addressed in future
"C405", # Set literal instead of set() call - too strict
"ASYNC109", # Async function with timeout parameter - sometimes valid pattern
"C416", # Unnecessary list comprehension - too strict
"PYI041", # Type stub redundant numeric union - too strict
"PERF401", # List comprehension instead of loop - too strict
]

[tool.ruff.lint.per-file-ignores]
# release_tools are standalone scripts, allow more flexibility
"release_tools/*.py" = ["T201", "S607", "PLR2004", "S108", "INP001"]
# Tests may need more flexibility
"tests/*.py" = ["ARG001", "PLR2004", "S101"]
# jsonrpyc is a forked library, be more lenient
"src/seclab_taskflow_agent/mcp_servers/codeql/jsonrpyc/*.py" = ["B904", "N802", "A002", "ARG002"]

9 changes: 7 additions & 2 deletions release_tools/copy_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@

import os
import shutil
import sys
import subprocess
import sys


def read_file_list(list_path):
"""
Reads a file containing file paths, ignoring empty lines and lines starting with '#'.
Returns a list of relative file paths.
"""
with open(list_path, "r") as f:
with open(list_path) as f:
lines = [line.strip() for line in f]
return [line for line in lines if line and not line.startswith("#")]


def copy_files(file_list, dest_dir):
"""
Copy files listed in file_list to dest_dir, preserving their relative paths.
Expand All @@ -29,6 +31,7 @@ def copy_files(file_list, dest_dir):
shutil.copy2(abs_src, abs_dest)
print(f"Copied {abs_src} -> {abs_dest}")


def ensure_git_repo(dest_dir):
"""
Initializes a git repository in dest_dir if it's not already a git repo.
Expand Down Expand Up @@ -56,6 +59,7 @@ def ensure_git_repo(dest_dir):
print(f"Failed to ensure 'main' branch in {dest_dir}: {e}")
sys.exit(1)


def git_add_files(file_list, dest_dir):
"""
Runs 'git add' on each file in file_list within dest_dir.
Expand All @@ -72,6 +76,7 @@ def git_add_files(file_list, dest_dir):
finally:
os.chdir(cwd)


if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python copy_files.py <file_list.txt> <dest_dir>")
Expand Down
19 changes: 10 additions & 9 deletions release_tools/publish_docker.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
# SPDX-FileCopyrightText: 2025 GitHub
# SPDX-License-Identifier: MIT

import os
import shutil
import subprocess
import sys


def get_image_digest(image_name, tag):
result = subprocess.run(
["docker", "buildx", "imagetools", "inspect", f"{image_name}:{tag}"],
stdout=subprocess.PIPE, check=True, text=True
stdout=subprocess.PIPE,
check=True,
text=True,
)
for line in result.stdout.splitlines():
if line.strip().startswith("Digest:"):
return line.strip().split(":", 1)[1].strip()
return None


def build_and_push_image(dest_dir, image_name, tag):
# Build
subprocess.run([
"docker", "buildx", "build", "--platform", "linux/amd64", "-t", f"{image_name}:{tag}", dest_dir
], check=True)
subprocess.run(
["docker", "buildx", "build", "--platform", "linux/amd64", "-t", f"{image_name}:{tag}", dest_dir], check=True
)
# Push
subprocess.run([
"docker", "push", f"{image_name}:{tag}"
], check=True)
subprocess.run(["docker", "push", f"{image_name}:{tag}"], check=True)
print(f"Pushed {image_name}:{tag}")
digest = get_image_digest(image_name, tag)
print(f"Image digest: {digest}")
with open("/tmp/digest.txt", "w") as f:
f.write(digest)


if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python build_and_publish_docker.py <ghcr_username/repo> <tag>")
Expand Down
Loading