Skip to content
Closed
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
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ jobs:

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

- name: Run tests
run: hatch test --python ${{ matrix.python-version }} --cover --randomize --parallel --retries 2 --retry-delay 1
75 changes: 72 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "seclab-taskflow-agent"
dynamic = ["version"]
description = "A taskflow agent for the SecLab project, enabling secure and automated workflow execution."
readme = "README.md"
requires-python = ">=3.9"
requires-python = ">=3.10"
license = "MIT"
keywords = []
authors = [
Expand All @@ -16,11 +16,10 @@ authors = [
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
Expand Down Expand Up @@ -145,3 +144,73 @@ exclude_lines = [
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]

[tool.ruff.lint]
ignore = [
"T201", # Allow print statements (used for CLI output)
"G004", # Allow f-strings in logging (more readable)
"S607", # Allow subprocess with partial paths (git commands are standard)
"E741", # Allow ambiguous variable names (single letters are common in loops)
"N818", # Allow exception names without Error suffix (existing convention)
"TRY003", # Allow long messages in exceptions (more readable)
"LOG015", # Allow simple logging without extra dict
"EM102", # Allow f-strings in exception messages
"EM101", # Allow string literals in exception messages
"TID252", # Allow relative imports (existing codebase pattern)
"FA102", # Don't require from __future__ import annotations
"FA100", # Don't require from __future__ import annotations
"FBT001", # Allow boolean positional args
"FBT002", # Allow boolean default args
"BLE001", # Allow catching Exception
"SLF001", # Allow accessing private members (needed for some integrations)
"ARG001", # Allow unused function arguments (may be required by interfaces)
"RET504", # Allow variable assignment before return
"PLR2004", # Allow magic values in comparisons
"TRY401", # Allow verbose logging in exception handlers
"TRY300", # Allow return in try block with else
"TRY301", # Allow raise in try block with else
"SIM115", # Allow context manager without assignment
"SIM210", # Allow if-else instead of ternary
"SIM102", # Allow nested if statements
"PERF102", # Allow dict.get() with default
"PERF401", # Allow manual list comprehension
"RUF059", # Allow unused unpacked variables
"RUF005", # Allow unpacking instead of concatenation
"RUF015", # Allow list() instead of []
"PLW2901", # Allow redefining loop variable
"PLW0602", # Allow global variable usage
"PLW0603", # Allow global statement usage
"PLW1508", # Allow environment variable usage without default
"UP006", # Allow old-style type hints
"UP035", # Allow old-style imports
"PLC0415", # Allow import outside top-level
"PT011", # Allow pytest.raises without match
"RET503", # Allow explicit return None
"B006", # Allow mutable default arguments (existing codebase pattern)
"B023", # Allow function definition without binding loop variable
"B904", # Allow raise without from in except clause
"A001", # Allow shadowing builtins (existing codebase pattern)
"A002", # Allow shadowing builtins in function arguments
"A004", # Allow shadowing builtins in imports
"B007", # Allow unused loop variable
"B008", # Allow function call in argument defaults
"E721", # Allow type() comparison instead of isinstance
"E722", # Allow bare except
"S108", # Allow probable insecure usage of temp file
"TRY004", # Allow type check in except clause
"TRY400", # Allow error() in except clause instead of exception()
"N801", # Allow class name not in CamelCase
"N802", # Allow function name not in snake_case
"N806", # Allow variable name not in snake_case
"F401", # Allow unused imports
"F811", # Allow redefinition of unused name
"F821", # Allow undefined name
"F841", # Allow unused local variable
"C405", # Allow unnecessary literals
"C416", # Allow unnecessary dict comprehension
]

[tool.ruff.lint.per-file-ignores]
"release_tools/*" = ["INP001"] # Release tools don't need __init__.py
"tests/*" = ["S101"] # Allow assert in tests
"src/seclab_taskflow_agent/mcp_servers/*" = ["INP001"] # MCP servers are dynamically loaded
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