Skip to content

Commit d99fc72

Browse files
OliverBclaude
andcommitted
Run ruff across codebase, add lint CI, fix gitignore
- Fix pyproject.toml: remove build-system (not a package), fix dependencies placement, add SIM108/N812 to ignore list - Run ruff check --fix and ruff format across all 22 generators - Fix SIM103 (return condition directly) in 3 files - Fix SIM105 (contextlib.suppress) in anthropic_news_blog.py - Add .github/workflows/lint.yml: ruff check on PRs and pushes to main (only triggers on .py and pyproject.toml changes) - Add feeds/.gitkeep to preserve feeds directory - Fix stray G character in .gitignore line 2 - Update uv.lock Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c0782c2 commit d99fc72

36 files changed

Lines changed: 830 additions & 813 deletions

.github/workflows/lint.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Lint
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "**.py"
7+
- "pyproject.toml"
8+
push:
9+
branches: [main]
10+
paths:
11+
- "**.py"
12+
- "pyproject.toml"
13+
14+
jobs:
15+
lint:
16+
runs-on: ubuntu-latest
17+
timeout-minutes: 5
18+
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v6
22+
23+
- name: Set up Python
24+
uses: actions/setup-python@v6
25+
with:
26+
python-version: "3.11"
27+
28+
- name: Install uv
29+
uses: astral-sh/setup-uv@v8.0.0
30+
with:
31+
enable-cache: true
32+
33+
- name: Lint and format check
34+
run: |
35+
uv sync --group dev
36+
uv run ruff check .
37+
uv run ruff format --check .

.github/workflows/run_feeds.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ jobs:
3232
timeout-minutes: 20
3333
run: |
3434
set -e
35-
uv venv
36-
uv pip install -r requirements.txt
35+
uv sync
3736
uv run feed_generators/run_all_feeds.py --skip-selenium
3837
3938
- name: Commit and push feeds

.github/workflows/run_selenium_feeds.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ jobs:
3838
timeout-minutes: 45
3939
run: |
4040
set -e
41-
uv venv
42-
uv pip install -r requirements.txt
41+
uv sync
4342
uv run feed_generators/run_all_feeds.py --selenium-only
4443
4544
- name: Commit and push feeds

.github/workflows/test_feed.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ jobs:
2525
- name: Install dependencies and run test feed
2626
run: |
2727
set -e
28-
uv venv
29-
uv pip install -r requirements.txt
28+
uv sync
3029
uv run feed_generators/ollama_blog.py
3130
3231
- name: Upload test feed artifact

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# Byte-compiled / optimized / DLL files
2-
G
32
__pycache__/
43
*.py[cod]
54
*$py.class

AGENTS.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ make feeds_generate_all # Run all feed generators
3636
make feeds_<name> # Run specific feed (e.g., feeds_ollama, feeds_anthropic_news)
3737

3838
# Development
39-
make dev_format # Format code with black and isort
39+
make dev_lint # Check code with ruff
40+
make dev_lint_fix # Auto-fix and format with ruff
41+
make dev_format # Alias for dev_lint_fix
4042
make dev_test_feed # Run test feed generator
4143

4244
# Run single generator directly

feed_generators/anthropic_changelog_claude_code.py

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import pytz
55
from feedgen.feed import FeedGenerator
6+
67
from utils import fetch_page, save_rss_feed, setup_feed_links, setup_logging
78

89
logger = setup_logging()
@@ -17,7 +18,7 @@ def fetch_changelog_content(
1718
try:
1819
return fetch_page(url)
1920
except Exception as e:
20-
logger.error(f"Error fetching changelog content: {str(e)}")
21+
logger.error(f"Error fetching changelog content: {e!s}")
2122
raise
2223

2324

@@ -40,11 +41,7 @@ def parse_changelog_markdown(markdown_content, max_versions=50):
4041
if current_version and current_changes:
4142
version_anchor = current_version.replace(".", "")
4243
# Create HTML list for description
43-
description_html = (
44-
"<ul>"
45-
+ "".join(f"<li>{change}</li>" for change in current_changes)
46-
+ "</ul>"
47-
)
44+
description_html = "<ul>" + "".join(f"<li>{change}</li>" for change in current_changes) + "</ul>"
4845
item = {
4946
"title": f"v{current_version}",
5047
"link": f"https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#{version_anchor}",
@@ -63,13 +60,9 @@ def parse_changelog_markdown(markdown_content, max_versions=50):
6360
date_str = version_match.group(2)
6461
if date_str:
6562
try:
66-
current_date = datetime.strptime(
67-
date_str.strip(), "%Y-%m-%d"
68-
).replace(tzinfo=pytz.UTC)
63+
current_date = datetime.strptime(date_str.strip(), "%Y-%m-%d").replace(tzinfo=pytz.UTC)
6964
except ValueError:
70-
logger.warning(
71-
f"Could not parse date '{date_str}' for version {current_version}"
72-
)
65+
logger.warning(f"Could not parse date '{date_str}' for version {current_version}")
7366
current_changes = []
7467
continue
7568

@@ -82,11 +75,7 @@ def parse_changelog_markdown(markdown_content, max_versions=50):
8275
# Don't forget the last version (if we haven't hit the limit)
8376
if current_version and current_changes and len(items) < max_versions:
8477
version_anchor = current_version.replace(".", "")
85-
description_html = (
86-
"<ul>"
87-
+ "".join(f"<li>{change}</li>" for change in current_changes)
88-
+ "</ul>"
89-
)
78+
description_html = "<ul>" + "".join(f"<li>{change}</li>" for change in current_changes) + "</ul>"
9079
item = {
9180
"title": f"v{current_version}",
9281
"link": f"https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#{version_anchor}",
@@ -101,7 +90,7 @@ def parse_changelog_markdown(markdown_content, max_versions=50):
10190
return items
10291

10392
except Exception as e:
104-
logger.error(f"Error parsing markdown content: {str(e)}")
93+
logger.error(f"Error parsing markdown content: {e!s}")
10594
raise
10695

10796

@@ -132,7 +121,7 @@ def generate_rss_feed(items, feed_name=FEED_NAME):
132121
return fg
133122

134123
except Exception as e:
135-
logger.error(f"Error generating RSS feed: {str(e)}")
124+
logger.error(f"Error generating RSS feed: {e!s}")
136125
raise
137126

138127

@@ -152,7 +141,7 @@ def main(feed_name=FEED_NAME):
152141
return True
153142

154143
except Exception as e:
155-
logger.error(f"Failed to generate RSS feed: {str(e)}")
144+
logger.error(f"Failed to generate RSS feed: {e!s}")
156145
return False
157146

158147

feed_generators/anthropic_eng_blog.py

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import pytz
55
from bs4 import BeautifulSoup
66
from feedgen.feed import FeedGenerator
7-
from utils import (fetch_page, save_rss_feed, setup_feed_links, setup_logging,
8-
sort_posts_for_feed)
7+
8+
from utils import fetch_page, save_rss_feed, setup_feed_links, setup_logging, sort_posts_for_feed
99

1010
logger = setup_logging()
1111

@@ -18,7 +18,7 @@ def fetch_engineering_content(url=BLOG_URL):
1818
try:
1919
return fetch_page(url)
2020
except Exception as e:
21-
logger.error(f"Error fetching engineering content: {str(e)}")
21+
logger.error(f"Error fetching engineering content: {e!s}")
2222
raise
2323

2424

@@ -28,9 +28,7 @@ def validate_article(article):
2828
return False
2929
if not article.get("link") or not article["link"].startswith("http"):
3030
return False
31-
if not article.get("date"):
32-
return False
33-
return True
31+
return bool(article.get("date"))
3432

3533

3634
def parse_engineering_html(html_content):
@@ -42,18 +40,12 @@ def parse_engineering_html(html_content):
4240
# Find the Next.js script tag containing article data
4341
script_tag = None
4442
for script in soup.find_all("script"):
45-
if (
46-
script.string
47-
and "publishedOn" in script.string
48-
and "engineeringArticle" in script.string
49-
):
43+
if script.string and "publishedOn" in script.string and "engineeringArticle" in script.string:
5044
script_tag = script
5145
break
5246

5347
if not script_tag:
54-
logger.error(
55-
"Could not find Next.js data script containing article information"
56-
)
48+
logger.error("Could not find Next.js data script containing article information")
5749
return []
5850

5951
script_content = script_tag.string
@@ -82,26 +74,16 @@ def parse_engineering_html(html_content):
8274

8375
# Extract title and summary (they appear AFTER the slug in the data)
8476
# Use negative lookbehind to handle escaped quotes correctly
85-
title_match = re.search(
86-
r'\\"title\\":\\"(.*?)(?<!\\)\\"', search_section
87-
)
88-
title = (
89-
title_match.group(1)
90-
if title_match
91-
else slug.replace("-", " ").title()
92-
)
77+
title_match = re.search(r'\\"title\\":\\"(.*?)(?<!\\)\\"', search_section)
78+
title = title_match.group(1) if title_match else slug.replace("-", " ").title()
9379
# Unescape the title using re.sub to handle all escaped characters
9480
title = re.sub(r"\\(.)", r"\1", title) if title else title
9581

9682
# Extract summary/description
97-
summary_match = re.search(
98-
r'\\"summary\\":\\"(.*?)(?<!\\)\\"', search_section
99-
)
83+
summary_match = re.search(r'\\"summary\\":\\"(.*?)(?<!\\)\\"', search_section)
10084
description = summary_match.group(1) if summary_match else title
10185
# Unescape the description
102-
description = (
103-
re.sub(r"\\(.)", r"\1", description) if description else description
104-
)
86+
description = re.sub(r"\\(.)", r"\1", description) if description else description
10587

10688
# Parse the date
10789
date = datetime.strptime(published_date, "%Y-%m-%d")
@@ -120,14 +102,14 @@ def parse_engineering_html(html_content):
120102
logger.info(f"Found article: {title} ({published_date})")
121103

122104
except Exception as e:
123-
logger.warning(f"Error parsing article {slug}: {str(e)}")
105+
logger.warning(f"Error parsing article {slug}: {e!s}")
124106
continue
125107

126108
logger.info(f"Successfully parsed {len(articles)} articles from JSON data")
127109
return articles
128110

129111
except Exception as e:
130-
logger.error(f"Error parsing HTML content: {str(e)}")
112+
logger.error(f"Error parsing HTML content: {e!s}")
131113
raise
132114

133115

@@ -136,9 +118,7 @@ def generate_rss_feed(articles, feed_name=FEED_NAME):
136118
try:
137119
fg = FeedGenerator()
138120
fg.title("Anthropic Engineering Blog")
139-
fg.description(
140-
"Latest engineering articles and insights from Anthropic's engineering team"
141-
)
121+
fg.description("Latest engineering articles and insights from Anthropic's engineering team")
142122
setup_feed_links(fg, BLOG_URL, feed_name)
143123
fg.language("en")
144124

@@ -164,7 +144,7 @@ def generate_rss_feed(articles, feed_name=FEED_NAME):
164144
return fg
165145

166146
except Exception as e:
167-
logger.error(f"Error generating RSS feed: {str(e)}")
147+
logger.error(f"Error generating RSS feed: {e!s}")
168148
raise
169149

170150

@@ -191,7 +171,7 @@ def main(feed_name=FEED_NAME):
191171
return True
192172

193173
except Exception as e:
194-
logger.error(f"Failed to generate RSS feed: {str(e)}")
174+
logger.error(f"Failed to generate RSS feed: {e!s}")
195175
return False
196176

197177

0 commit comments

Comments
 (0)