Merge branch 'main' into claude-dispatcher-workflow

This commit is contained in:
Glenn Jocher
2026-07-25 19:31:07 +02:00
committed by GitHub
14 changed files with 122 additions and 345 deletions
+9 -14
View File
@@ -4,22 +4,17 @@ This file provides guidance to AI coding agents (Claude Code, etc.) when working
## Core Principles (CRITICAL)
Respecting these principles is critical for every PR.
**Less is more. The simplest solution is the best solution.** The action hierarchy for every change: **Delete > Replace > Add**.
**Less is more. The simplest solution is the best solution.**
1. **Solve at the owner**: Put behavior in the code path that owns or observes it. For fixes, never guard a symptom with a staleness check, initialization flag, skip-first-call branch, or `try/except` around broken logic; relocate the trigger and delete the wrong path. For features, extend the existing owner rather than creating a parallel abstraction.
2. **Search and reuse first**: Search the whole repository before creating anything new — a module, helper, utility, composite action, or workflow. Reuse or adapt what exists, consolidate in-scope duplication in the shared owner, and delete duplicate paths. Three similar lines beat a helper nobody else calls.
3. **Delete and modify existing code before creating new code**: Bugfixes are net-negative by default unless deletion and relocation are demonstrably impossible. A new file must first prove it cannot fit cleanly in an existing owner.
4. **Keep scope minimal**: Implement only the simplest complete solution. Avoid impossible-state handling, speculative flags, compatibility shims, policy scaffolding, and unrelated cleanup. Tests are out of scope by default — rely on existing coverage and focused validation; only an uncovered, high-risk regression path justifies minimal new test code.
5. **Ship zero-regression, production-ready changes**: Understand what you remove instead of retaining broken code as insurance. Remove unused imports, functions, types, files, and commented-out code; run relevant cleanup checks; and thoroughly debug and validate the changed owner. Do not break existing features or workflows unless the PR intentionally removes them with evidence.
The action hierarchy for every change: **Delete > Replace > Add**. The best code change is a deletion. The second best is modifying what exists. Adding new code is the last resort.
**Review gate:** for every addition, the reviewer decides whether deleting or changing existing code would have fixed the problem instead — if it would, that is a blocking finding. A missing or thin PR description is never itself a finding.
1. **Minimal**: The simplest solution that works. Do not over-engineer, over-abstract, or add code just in case. Three similar lines beat a premature abstraction. Avoid error handling for impossible states, feature flags, compatibility shims, or policy scaffolding unless they are truly required.
2. **Solve at the source**: Do not hack fixes. Solve problems at their root. If something is broken, fix or remove the broken thing. Never patch over a broken abstraction, add workarounds, or add synchronization code for state that should not be duplicated.
3. **Delete ruthlessly**: When replacing code, delete what it replaced. Remove unused imports, functions, types, files, and commented-out code. Git preserves history. Run the repo's relevant dead-code or cleanup check when available.
4. **Replace > Add**: Modify existing code over adding new code. Edit existing files, extend existing components or functions with minimal parameters, and reuse existing utilities. If creating a new file, first prove it cannot fit cleanly in an existing file.
5. **Check existing**: Search the entire repo before creating anything new. If a feature, component, helper, responder, workflow, or utility already solves a similar problem, reuse or adapt it and delete the duplicate path.
6. **Deduplicate**: Do not duplicate existing code when updating the repo. Consolidate or refactor duplicates you find when it is in scope and low risk.
7. **Zero Regression**: Do not break existing features or workflows unless the PR intentionally removes them with evidence.
8. **Production ready**: All changes must be thoroughly debugged, validated, and production ready.
**When fixing bugs, ask: "What can I delete?" before "What can I replace?" before "What should I add?"**
NEVER push to `main`. NEVER force push. Always start work in a new git worktree (`git worktree add`) on a feature branch and open a PR — never edit the primary checkout directly, it may hold in-flight work.
## PR Workflow
@@ -27,7 +22,7 @@ After opening a PR:
1. Wait for the automated PR review and auto-format commit from Ultralytics Actions (`format.yml`), then pull and address every finding.
2. Launch an independent adversarial review agent with cold context (just the PR diff and this file) to hunt for bugs, regressions, and Core Principles violations. Fix, push, and repeat with a fresh agent until one reports LGTM.
3. Never fight other commits: Ultralytics Actions pushes auto-format and header commits, and multiple users may work on the same PR. `git pull --rebase` before pushing; never force-push, reset, or revert commits you did not author.
3. Never fight other commits: Ultralytics Actions pushes auto-format and header commits, and multiple users may work on the same PR. `git pull --rebase` before pushing; never reset or revert commits you did not author.
4. After the PR merges, clean up: remove local worktrees and branches for it, then `git checkout main && git pull`.
## Commands
+11 -11
View File
@@ -113,17 +113,17 @@ runs:
env:
INPUTS_SPELLING: ${{ inputs.spelling }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_REF: ${{ github.ref }}
GITHUB_SHA: ${{ github.sha }}
run: |
echo "::group::Install Dependencies"
trap 'echo "::endgroup::"' EXIT
# Install from current git branch if testing in ultralytics/actions repo, otherwise from GitHub main branch
# Source tarballs install ~2x faster than git+ URLs, which clone the repository first. Test the current commit
# in the ultralytics/actions repo, otherwise install main.
if [ "$GITHUB_REPOSITORY" = "ultralytics/actions" ]; then
echo "Installing from git branch: $GITHUB_REF"
packages="git+https://github.com/ultralytics/actions@${GITHUB_REF#refs/heads/}"
echo "Installing from commit: $GITHUB_SHA"
packages="https://github.com/ultralytics/actions/archive/${GITHUB_SHA}.tar.gz"
else
# packages="ultralytics-actions"
packages="git+https://github.com/ultralytics/actions@main"
packages="https://github.com/ultralytics/actions/archive/refs/heads/main.tar.gz"
fi
if [ "$INPUTS_SPELLING" = "true" ]; then
@@ -216,18 +216,18 @@ runs:
run: |
echo "::group::Run Prettier"
trap 'echo "::endgroup::"' EXIT
npm install -g [email protected] prettier-plugin-sh
npm install -g [email protected]
curl -fsSL "https://github.com/mvdan/sh/releases/download/v3.13.1/shfmt_v3.13.1_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')" -o "$(npm prefix -g)/bin/shfmt"
chmod +x "$(npm prefix -g)/bin/shfmt"
ultralytics-actions-update-markdown-code-blocks
npx prettier --write --list-different --print-width 120 "**/*.{js,jsx,ts,tsx,css,less,scss,json,yml,yaml,html,vue,svelte}" '!**/*lock.{json,yaml,yml}' '!**/*.lock' '!**/model.json' '!**/*.min.js' '!**/*.min.css'
# Bash file format
if find . -name "*.sh" -type f | grep -q .; then
npx prettier --write --list-different --print-width 120 --plugin=$(npm root -g)/prettier-plugin-sh/lib/index.cjs "**/*.sh"
fi
# Handle Markdown separately
find . -name "*.md" -type f ! -path "*/docs/*" -exec npx prettier --write --list-different --print-width 120 {} +
if [ -d "./docs" ]; then
find ./docs -name "*.md" -type f ! -path "*/reference/*" -exec npx prettier --tab-width 4 --print-width 120 --write --list-different {} +
fi
# Bash last: shfmt exits non-zero when a script fails to parse, which would otherwise skip the formatting above
find . -name "*.sh" -type f -not -path "*/node_modules/*" -not -path "*/.git/*" -exec shfmt -i 2 -sr -bn -ci -l -w {} +
shell: bash
continue-on-error: true
+1 -1
View File
@@ -28,4 +28,4 @@
# ├── test_summarize_pr.py
# └── ...
__version__ = "0.2.37"
__version__ = "0.2.40"
+5 -4
View File
@@ -19,17 +19,18 @@ RUFF_CHECK = [
RUFF_FORMAT = ["ruff", "format", "--line-length=120", "."]
DOCSTRINGS = ["ultralytics-actions-format-python-docstrings", "."]
PRETTIER = """
npm install -g [email protected] prettier-plugin-sh
npm install -g [email protected]
curl -fsSL "https://github.com/mvdan/sh/releases/download/v3.13.1/shfmt_v3.13.1_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')" -o "$(npm prefix -g)/bin/shfmt"
chmod +x "$(npm prefix -g)/bin/shfmt"
ultralytics-actions-update-markdown-code-blocks
npx prettier --write --list-different --print-width 120 "**/*.{js,jsx,ts,tsx,css,less,scss,json,yml,yaml,html,vue,svelte}" '!**/*lock.{json,yaml,yml}' '!**/*.lock' '!**/model.json' '!**/*.min.js' '!**/*.min.css'
if find . -name "*.sh" -type f | grep -q .; then
npx prettier --write --list-different --print-width 120 --plugin=$(npm root -g)/prettier-plugin-sh/lib/index.cjs "**/*.sh"
fi
# Handle Markdown separately
find . -name "*.md" -type f ! -path "*/docs/*" -exec npx prettier --write --list-different --print-width 120 {} +
if [ -d "./docs" ]; then
find ./docs -name "*.md" -type f ! -path "*/reference/*" -exec npx prettier --tab-width 4 --print-width 120 --write --list-different {} +
fi
# Bash last: shfmt exits non-zero when a script fails to parse, which would otherwise skip the formatting above
find . -name "*.sh" -type f -not -path "*/node_modules/*" -not -path "*/.git/*" -exec shfmt -i 2 -sr -bn -ci -l -w {} +
"""
CODESPELL = [
"codespell",
+5 -11
View File
@@ -120,13 +120,9 @@ def update_file(file_path, prefix, block_start, block_end, base_header):
# Check for special first line
special_line_index = -1
first_line = lines[0].lstrip("\ufeff") if lines else ""
first_line = lines[0].lstrip("\ufeff").lower() if lines else ""
encoding_cookie = r"^[ \t\f]*#.*?coding[:=][ \t]*[-_.a-zA-Z0-9]+"
if lines and (
first_line.startswith(("#!", "<?xml"))
or first_line.lower().startswith("<!doctype")
or re.match(encoding_cookie, first_line)
):
if first_line.startswith(("#!", "<?xml", "<!doctype")) or re.match(encoding_cookie, first_line):
special_line_index = 0
prefix_lines.append(lines[0])
if first_line.startswith("#!") and len(lines) > 1 and re.match(encoding_cookie, lines[1]):
@@ -136,14 +132,12 @@ def update_file(file_path, prefix, block_start, block_end, base_header):
start_idx = special_line_index + 1 if special_line_index >= 0 else 0
end_idx = min(start_idx + 5, len(lines)) # Look in first few lines
# An existing header must be the first non-empty line and use the file's comment syntax.
candidate_index = next((i for i in range(start_idx, end_idx) if lines[i].strip()), -1)
candidate = lines[candidate_index].strip() if candidate_index >= 0 else ""
comment_start = (prefix or block_start or "# ").strip()
known_header = candidate.startswith(comment_start) and re.search(
r"AGPL-3\.0 License|CONFIDENTIAL: Unauthorized use|©\s*2014[–-]\d{4}\s+Ultralytics Inc\.|"
r"Ultralytics Inc\..*Copyright ©\s*2014[–-]\d{4}\s*-\s*CONFIDENTIAL\s*-",
candidate,
header = candidate[len(comment_start) :].strip() if candidate.startswith(comment_start) else ""
known_header = header.startswith(("Ultralytics 🚀 AGPL-3.0 License", "Ultralytics Inc. 🚀 Copyright")) or (
header.startswith("© 2014-") and " Ultralytics Inc." in header
)
header_index = candidate_index if candidate == formatted_header.strip() or known_header else -1
+8 -9
View File
@@ -96,26 +96,25 @@ def format_code_with_ruff(temp_dir):
print(f"ERROR running Python docstring formatter ❌ {e}")
def format_bash_with_prettier(temp_dir):
"""Formats bash script files in the specified directory using prettier."""
def format_bash_with_shfmt(temp_dir):
"""Formats bash script files in the specified directory using shfmt."""
if not next(Path(temp_dir).rglob("*.sh"), None):
return
try:
# Run prettier with explicit config path
# Flags reproduce prettier-plugin-sh output: 2-space indent, spaced redirects, binary ops and cases indented
result = subprocess.run(
"npx prettier --write --print-width=120 --plugin=$(npm root -g)/prettier-plugin-sh/lib/index.cjs ./**/*.sh",
shell=True, # must use shell=True to expand internal $(cmd)
["shfmt", "-i", "2", "-sr", "-bn", "-ci", "-w", str(temp_dir)],
capture_output=True,
text=True,
check=False,
)
if result.returncode != 0:
print(f"ERROR running prettier-plugin-sh ❌ {result.stderr}")
print(f"ERROR running shfmt ❌ {result.stderr}")
else:
print("Completed bash formatting ✅")
except Exception as e:
print(f"ERROR running prettier-plugin-sh ❌ {e}")
print(f"ERROR running shfmt ❌ {e}")
def process_markdown_string(
@@ -141,7 +140,7 @@ def process_markdown_string(
if python_files_exist:
format_code_with_ruff(temp_dir)
if bash_files_exist:
format_bash_with_prettier(temp_dir)
format_bash_with_shfmt(temp_dir)
update_markdown_file(temp_md, markdown_snapshot, temp_files)
formatted_markdown = temp_md.read_text(encoding="utf-8")
@@ -244,7 +243,7 @@ def main(root_dir=None, process_python=True, process_bash=True, verbose=False):
if process_python:
format_code_with_ruff(temp_dir) # Format Python files
if process_bash:
format_bash_with_prettier(temp_dir) # Format Bash files
format_bash_with_shfmt(temp_dir) # Format Bash files
# Update Markdown files with formatted code blocks
for markdown_file, markdown_content, temp_files in all_temp_files:
+41 -23
View File
@@ -182,9 +182,9 @@ REDIRECT_END_IGNORE_LIST = frozenset(
}
)
URL_PATTERN = re.compile(
r"\[([^]]+)]\(([^)]+)\)" # Matches Markdown links [text](url)
r"\[(?P<md_text>[^]]+)]\((?P<md_url>[^)]+)\)" # Matches Markdown links [text](url)
r"|"
r"(" # Start capturing group for plaintext URLs
r"(?P<plain_url>" # Start capturing group for plaintext URLs
r"(?:https?://)?" # Optional http:// or https://
r"(?:www\.)?" # Optional www.
r"(?:[\w.-]+)?" # Optional domain name and subdomains
@@ -292,8 +292,14 @@ def brave_search(query, api_key, count=5):
if len(query) > 400:
print(f"WARNING ⚠️ Brave search query length {len(query)} exceed limit of 400 characters, truncating.")
url = f"https://api.search.brave.com/res/v1/web/search?q={parse.quote(query.strip()[:400])}&count={count}"
response = requests.get(url, headers={"X-Subscription-Token": api_key, "Accept": "application/json"})
data = response.json() if response.status_code == 200 else {}
try:
response = requests.get(
url, headers={"X-Subscription-Token": api_key, "Accept": "application/json"}, timeout=10
)
data = response.json() if response.status_code == 200 else {}
except Exception as e: # a search outage must never block the caller from returning its text
print(f"WARNING ⚠️ Brave search failed: {e}")
return []
results = data.get("web", {}).get("results", []) if data else []
return [result.get("url") for result in results if result.get("url")]
@@ -351,10 +357,10 @@ def is_url(url, session=None, check=True, max_attempts=3, timeout=3, return_url=
def check_links_in_string(text, verbose=True, return_bad=False, replace=False):
"""Process text, find URLs, check for 404s, and handle replacements with redirects or Brave search."""
urls = []
for md_text, md_url, plain_url in URL_PATTERN.findall(text):
url = md_url or plain_url
for match in URL_PATTERN.finditer(text):
url = match["md_url"] or match["plain_url"]
if url and parse.urlparse(url).scheme:
urls.append((md_text, clean_url(url)))
urls.append((match["md_text"] or "", clean_url(url)))
with requests.Session() as session, ThreadPoolExecutor(max_workers=64) as executor:
session.headers.update(REQUESTS_HEADERS)
@@ -363,35 +369,47 @@ def check_links_in_string(text, verbose=True, return_bad=False, replace=False):
bad_urls = [url for (title, url), (valid, redirect) in zip(urls, results) if not valid]
if replace:
replacements = {}
modified_text = text
replacements, searched = {}, set()
# Process all URLs for replacements
brave_api_key = os.getenv("BRAVE_API_KEY")
for (title, url), (valid, redirect) in zip(urls, results):
# Handle invalid URLs with Brave search
if not valid and brave_api_key:
query = f"{(redirect or url)[:200]} {title[:199]}"
if search_urls := brave_search(query, brave_api_key, count=3):
best_url = next(
(alt_url for alt_url in search_urls if is_url(alt_url, session)),
search_urls[0],
)
if url != best_url:
# Handle invalid URLs with Brave search. Two queries, not two attempts: the dead URL biases the
# first toward the site root, so the second drops it and searches the link text on its domain.
if not valid:
if url in searched: # search once per URL, however many times it occurs
continue
searched.add(url)
for query in (
f"{(redirect or url)[:200]} {title[:199]}",
f"{title[:199]} {parse.urlparse(url).netloc}",
):
search_urls = brave_search(query, brave_api_key, count=3) or []
if best_url := next((u for u in search_urls if u != url and is_url(u, session)), None):
replacements[url] = best_url
modified_text = modified_text.replace(url, best_url)
break
# Handle redirects for valid URLs
elif valid and redirect and redirect != url:
elif redirect and redirect != url:
replacements[url] = redirect
modified_text = modified_text.replace(url, redirect)
if verbose and replacements:
print(
f"WARNING ⚠️ replaced {len(replacements)} links:\n"
+ "\n".join(f" {k}: {v}" for k, v in replacements.items())
)
if replacements:
return (True, bad_urls, modified_text) if return_bad else modified_text
def replace_link(match):
"""Swap a matched URL for its replacement, leaving the surrounding link syntax untouched."""
group = "md_url" if match["md_url"] else "plain_url"
raw_url = match[group]
if not (new_url := replacements.get(clean_url(raw_url))):
return match[0]
start, end = (i - match.start() for i in match.span(group))
suffix = raw_url[len(raw_url.rstrip(".,:;!?`\\")) :] # trailing punctuation clean_url() dropped
return f"{match[0][:start]}{new_url}{suffix}{match[0][end:]}"
text = URL_PATTERN.sub(replace_link, text)
bad_urls = [url for url in bad_urls if url not in replacements] # unfixable links stay reported
passing = not bad_urls
if verbose and not passing:
+6 -2
View File
@@ -2,7 +2,7 @@
# 🧹 Disk Space Cleanup Action
Cleans up disk space on Ubuntu GitHub Actions runners by removing unnecessary tool caches and swap space. Frees up ~19GB total space.
Cleans up disk space on Ubuntu GitHub Actions runners by removing unnecessary tool caches, Docker images, and swap space.
## 🚀 Usage
@@ -10,6 +10,9 @@ Cleans up disk space on Ubuntu GitHub Actions runners by removing unnecessary to
Add as early step in jobs requiring disk space:
> [!IMPORTANT]
> Run cleanup before pulling or building Docker images because it removes all images unused by containers.
```yaml
steps:
- uses: ultralytics/actions/cleanup-disk@main
@@ -57,7 +60,8 @@ steps:
## 🗑️ What Gets Cleaned
- `/opt/hostedtoolcache` - Tool cache (~15GB)
- `/swapfile` - Swap space (~4GB)
- Unused Docker images
- `/swapfile` or `/mnt/swapfile` - Swap space (~4GB)
## 💡 When to Use
+7 -4
View File
@@ -2,7 +2,7 @@
name: "Disk Space Cleanup"
author: "Ultralytics"
description: "Cleans up disk space by removing unnecessary tool caches and swap space"
description: "Cleans up disk space by removing unnecessary tool caches, Docker images, and swap space"
runs:
using: "composite"
steps:
@@ -13,21 +13,24 @@ runs:
df -h /
echo "::endgroup::"
echo "::group::Remove Tool Cache and Swap"
echo "::group::Remove Tool Cache, Docker Images, and Swap"
toolcache_pid=""
if [ -e /opt/hostedtoolcache ]; then
# Remove tool cache to free up ~15GB of space per https://github.com/ultralytics/ultralytics/pull/15848
rm -rf /opt/hostedtoolcache &
toolcache_pid=$!
fi
docker image prune --all --force &
docker_pid=$!
# Remove swap space to free up ~4GB
if [ -f /swapfile ]; then
if [ -f /swapfile ] || [ -f /mnt/swapfile ]; then
sudo swapoff -a
sudo rm -f /swapfile
sudo rm -f /swapfile /mnt/swapfile
fi
if [ -n "$toolcache_pid" ]; then
wait "$toolcache_pid" || true
fi
wait "$docker_pid" || true
echo "::endgroup::"
echo "::group::Disk Space After Cleanup"
+2 -2
View File
@@ -39,9 +39,9 @@ runs:
echo "::group::Install ultralytics-actions"
if [ "$GITHUB_REPOSITORY" = "ultralytics/actions" ]; then
echo "Installing from commit: $GITHUB_SHA"
packages="git+https://github.com/ultralytics/actions@${GITHUB_SHA}"
packages="https://github.com/ultralytics/actions/archive/${GITHUB_SHA}.tar.gz"
else
packages="git+https://github.com/ultralytics/actions@main"
packages="https://github.com/ultralytics/actions/archive/refs/heads/main.tar.gz"
fi
if [ "$(uname)" = "Darwin" ]; then
uv pip install --system --break-system-packages $packages
+2 -2
View File
@@ -55,9 +55,9 @@ runs:
echo "::group::Install ultralytics-actions"
if [ "$GITHUB_REPOSITORY" = "ultralytics/actions" ]; then
echo "Installing from commit: $GITHUB_SHA"
packages="git+https://github.com/ultralytics/actions@${GITHUB_SHA}"
packages="https://github.com/ultralytics/actions/archive/${GITHUB_SHA}.tar.gz"
else
packages="git+https://github.com/ultralytics/actions@main"
packages="https://github.com/ultralytics/actions/archive/refs/heads/main.tar.gz"
fi
if [ "$(uname)" = "Darwin" ]; then
uv pip install --system --break-system-packages $packages
-259
View File
@@ -1,259 +0,0 @@
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
"""Tests for the file headers update functionality."""
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import patch
from actions.update_file_headers import COMMENT_MAP, IGNORE_PATHS, main, update_file
def test_update_file_python():
"""Test updating Python file headers."""
with TemporaryDirectory() as tmp_dir:
# Create a test Python file
test_file = Path(tmp_dir) / "test.py"
test_file.write_text("print('Hello World')\n")
# Update file
result = update_file(test_file, "# ", None, None, "Ultralytics 🚀 Test Header")
# Check results
assert result is True
content = test_file.read_text()
assert content.startswith("# Ultralytics 🚀 Test Header\n\n")
assert "print('Hello World')" in content
def test_update_file_cpp():
"""Test updating C++ file headers."""
with TemporaryDirectory() as tmp_dir:
# Create a test C++ file
test_file = Path(tmp_dir) / "test.cpp"
test_file.write_text("#include <iostream>\n\nint main() {\n return 0;\n}\n")
# Update file
result = update_file(test_file, "// ", "/* ", " */", "Ultralytics 🚀 Test Header")
# Check results
assert result is True
content = test_file.read_text()
assert content.startswith("// Ultralytics 🚀 Test Header\n\n")
assert "#include <iostream>" in content
def test_update_file_with_existing_header():
"""Test updating file with existing header."""
with TemporaryDirectory() as tmp_dir:
# Create a test file with existing header
test_file = Path(tmp_dir) / "test.py"
test_file.write_text("# Ultralytics 🚀 AGPL-3.0 License\n\ndef main():\n pass\n")
# Update file
result = update_file(test_file, "# ", None, None, "Ultralytics 🚀 Test Header")
# Check results
assert result is True
content = test_file.read_text()
assert content.startswith("# Ultralytics 🚀 Test Header\n\n")
assert "def main():" in content
def test_update_file_with_shebang():
"""Test updating file with shebang."""
with TemporaryDirectory() as tmp_dir:
# Create a test file with shebang
test_file = Path(tmp_dir) / "test.py"
test_file.write_text("#!/usr/bin/env python3\n\nprint('Hello World')\n")
# Update file
result = update_file(test_file, "# ", None, None, "Ultralytics 🚀 Test Header")
# Check results
assert result is True
content = test_file.read_text()
assert content.startswith("#!/usr/bin/env python3\n# Ultralytics 🚀 Test Header\n\n")
assert "print('Hello World')" in content
def test_update_file_preserves_early_ultralytics_source():
"""Test that Ultralytics references in source code are not mistaken for headers."""
with TemporaryDirectory() as tmp_dir:
tmp_path = Path(tmp_dir)
docstring_file = tmp_path / "api.py"
docstring_file.write_text('"""Ultralytics Platform API client."""\n\nVALUE = 1\n')
import_file = tmp_path / "__init__.py"
import_file.write_text("from ultralytics import YOLO\n")
for test_file, spacing in ((docstring_file, ""), (import_file, "\n")):
original = test_file.read_text()
assert update_file(test_file, "# ", None, None, "Ultralytics 🚀 Test Header") is True
assert test_file.read_text() == f"# Ultralytics 🚀 Test Header\n{spacing}{original}"
def test_update_file_with_lowercase_doctype():
"""Test that Prettier's lowercase HTML doctype remains first."""
with TemporaryDirectory() as tmp_dir:
test_file = Path(tmp_dir) / "index.html"
test_file.write_text("<!doctype html>\n<html></html>\n")
assert update_file(test_file, None, "<!-- ", " -->", "Ultralytics 🚀 Test Header") is True
assert test_file.read_text().startswith("<!doctype html>\n<!-- Ultralytics 🚀 Test Header -->\n\n")
def test_update_file_with_bom_xml_declaration():
"""Test that a BOM-prefixed XML declaration remains first."""
with TemporaryDirectory() as tmp_dir:
test_file = Path(tmp_dir) / "layout.xml"
test_file.write_text('\ufeff<?xml version="1.0" encoding="utf-8"?>\n<root />\n')
assert update_file(test_file, None, "<!-- ", " -->", "Ultralytics 🚀 Test Header") is True
assert test_file.read_text().startswith(
'\ufeff<?xml version="1.0" encoding="utf-8"?>\n<!-- Ultralytics 🚀 Test Header -->\n\n'
)
def test_update_file_with_shebang_and_encoding_cookie():
"""Test that a Python encoding cookie remains on line two and the header stays idempotent."""
with TemporaryDirectory() as tmp_dir:
test_file = Path(tmp_dir) / "script.py"
test_file.write_text(
"#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# Ultralytics 🚀 AGPL-3.0 License\n\nprint('hello')\n"
)
assert update_file(test_file, "# ", None, None, "Ultralytics 🚀 Test Header") is True
assert test_file.read_text().startswith(
"#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# Ultralytics 🚀 Test Header\n\n"
)
assert update_file(test_file, "# ", None, None, "Ultralytics 🚀 Test Header") is False
def test_update_file_replaces_legacy_private_header():
"""Test that the previously emitted private header is replaced rather than duplicated."""
with TemporaryDirectory() as tmp_dir:
test_file = Path(tmp_dir) / "app.py"
test_file.write_text(
"# Ultralytics Inc. 🚀 Copyright © 2014-2025 - CONFIDENTIAL - "
"https://ultralytics.com - All Rights Reserved\n\n"
"print('hello')\n"
)
assert update_file(test_file, "# ", None, None, "© 2014-2026 Ultralytics Inc. CONFIDENTIAL") is True
assert test_file.read_text().startswith("# © 2014-2026 Ultralytics Inc. CONFIDENTIAL\n\n")
assert "Copyright" not in test_file.read_text()
def test_update_file_no_changes():
"""Test updating file with no changes needed."""
with TemporaryDirectory() as tmp_dir:
# Create a test file with correct header
test_file = Path(tmp_dir) / "test.py"
test_file.write_text("# Ultralytics 🚀 Test Header\n\nprint('Hello World')\n")
# Update file
result = update_file(test_file, "# ", None, None, "Ultralytics 🚀 Test Header")
# Check results
assert result is False # No changes made
def test_update_file_edge_cases():
"""Test header handling for empty, unreadable, and block-comment files."""
with TemporaryDirectory() as tmp_dir:
tmp_path = Path(tmp_dir)
empty_file = tmp_path / "empty.py"
empty_file.write_text("")
assert update_file(empty_file, "# ", None, None, "Ultralytics 🚀 Test Header") is False
assert update_file(tmp_path, "# ", None, None, "Ultralytics 🚀 Test Header") is False
css_file = tmp_path / "style.css"
css_file.write_text("body { color: black; }\n")
assert update_file(css_file, None, "/* ", " */", "Ultralytics 🚀 Test Header") is True
assert css_file.read_text().startswith("/* Ultralytics 🚀 Test Header */\n\n")
def test_comment_map_coverage():
"""Test that all supported file extensions have defined comment styles."""
# Check for a few key extensions
assert ".py" in COMMENT_MAP
assert ".cpp" in COMMENT_MAP
assert ".js" in COMMENT_MAP
assert ".html" in COMMENT_MAP
# Check comment style format
for ext, (prefix, block_start, block_end) in COMMENT_MAP.items():
assert isinstance(ext, str)
assert prefix is None or isinstance(prefix, str)
assert block_start is None or isinstance(block_start, str)
assert block_end is None or isinstance(block_end, str)
def test_ignore_paths():
"""Test that the ignore paths list exists and contains expected entries."""
assert isinstance(IGNORE_PATHS, set)
assert ".git" in IGNORE_PATHS
assert "__pycache__" in IGNORE_PATHS
def test_main_real_files():
"""Test main function on actual repository files."""
with patch("actions.update_file_headers.update_file", return_value=False) as mock_update, patch(
"actions.update_file_headers.Action"
) as mock_action:
mock_action.return_value.repository = "ultralytics/actions"
mock_action.return_value.is_repo_private.return_value = False
main()
assert mock_update.call_count > 0
def test_main_with_custom_header():
"""Test main function with custom header environment variable."""
with patch("actions.update_file_headers.update_file", return_value=False), patch(
"actions.update_file_headers.HEADER", "Custom Test Header"
), patch("actions.update_file_headers.Action") as mock_action:
mock_event = mock_action.return_value
mock_event.repository = "test/repo"
main()
mock_action.assert_called_once()
def test_main_private_and_skipped_repos():
"""Test main selects private headers and skips repos without a header source."""
with TemporaryDirectory() as tmp_dir:
tmp_path = Path(tmp_dir)
(tmp_path / "app.py").write_text("print('hello')\n")
with patch("actions.update_file_headers.Path.cwd", return_value=tmp_path), patch(
"actions.update_file_headers.update_file", return_value=False
) as mock_update, patch("actions.update_file_headers.Action") as mock_action:
mock_event = mock_action.return_value
mock_event.repository = "ultralytics/private"
mock_event.is_repo_private.return_value = True
main()
assert any("CONFIDENTIAL" in call.args[4] for call in mock_update.call_args_list)
with patch("actions.update_file_headers.update_file", return_value=False) as mock_update, patch(
"actions.update_file_headers.HEADER", None
), patch("actions.update_file_headers.Action") as mock_action:
mock_action.return_value.repository = "other/repo"
main()
mock_action.return_value.is_repo_private.assert_not_called()
mock_update.assert_not_called()
def test_main_updates_files_in_current_directory():
"""Test main updates supported files under the current directory."""
with TemporaryDirectory() as tmp_dir:
tmp_path = Path(tmp_dir)
test_file = tmp_path / "app.py"
test_file.write_text("print('hello')\n")
with patch("actions.update_file_headers.Path.cwd", return_value=tmp_path), patch(
"actions.update_file_headers.Action"
) as mock_action:
mock_event = mock_action.return_value
mock_event.repository = "ultralytics/actions"
mock_event.is_repo_private.return_value = False
main()
assert test_file.read_text().startswith("# Ultralytics 🚀 AGPL-3.0 License")
+3 -3
View File
@@ -6,7 +6,7 @@ from unittest.mock import mock_open, patch
from actions.update_markdown_code_blocks import (
add_indentation,
extract_code_blocks,
format_bash_with_prettier,
format_bash_with_shfmt,
generate_temp_filename,
main,
process_markdown_file,
@@ -110,11 +110,11 @@ def test():
def test_format_bash_skips_when_no_shell_files(tmp_path):
"""Test bash formatter skips Prettier when no shell snippets were extracted."""
"""Test bash formatter skips shfmt when no shell snippets were extracted."""
(tmp_path / "snippet.py").write_text("print('ok')", encoding="utf-8")
with patch("subprocess.run") as mock_run:
format_bash_with_prettier(tmp_path)
format_bash_with_shfmt(tmp_path)
mock_run.assert_not_called()
+22
View File
@@ -134,6 +134,28 @@ def test_urls_with_different_tlds(verbose):
assert mock_is_url.call_count == 5
def test_replace_keeps_unresolved_links(monkeypatch):
"""Replace links the search can fix, keep and report the ones it cannot, and never touch a longer neighbor."""
text = "[Broken](https://site.test/bad) and [Fixed](https://site.test/gone) and https://site.test/gone/deeper"
def fake_is_url(url, session=None, check=True, max_attempts=3, timeout=3, return_url=False, redirect=False):
valid = url in {"https://new.test", "https://site.test/gone/deeper"}
return (valid, url) if return_url else valid
monkeypatch.setenv("BRAVE_API_KEY", "test-key")
with patch("actions.utils.common_utils.is_url", side_effect=fake_is_url), patch(
"actions.utils.common_utils.brave_search",
side_effect=lambda query, *_, **__: [] if "Broken" in query else ["https://new.test"],
):
result = check_links_in_string(text, verbose=False, return_bad=True, replace=True)
assert result == (
False,
["https://site.test/bad"],
"[Broken](https://site.test/bad) and [Fixed](https://new.test) and https://site.test/gone/deeper",
)
def test_case_sensitivity(verbose):
"""Tests URL case sensitivity by verifying that URLs with different cases are correctly identified and handled."""
text = "Case test: HTTPS://err.com and https://err.com"