Skip to content

Commit 09c6019

Browse files
author
internet-dot
committed
fix: improve CI resilience and remove invalid plugin entry
- Add retry logic with exponential backoff to generate_plugins_json.py - Increase timeout from 45s to 60s for network requests - Add better error messages for debugging - Remove Toprank from README (uses .claude-plugin not .codex-plugin)
1 parent de65dc0 commit 09c6019

2 files changed

Lines changed: 26 additions & 10 deletions

File tree

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ Third-party plugins built by the community. [PRs welcome](#contributing)!
119119
- [Synta MCP](https://github.com/Synta-ai/n8n-mcp-codex-plugin-synta) - Build, edit, validate, and self-heal n8n workflows with Synta MCP tools and Codex-ready workflow guidance.
120120
- [Task Scheduler](https://github.com/6Delta9/task-scheduler-codex-plugin) - OpenAI Codex plugin and local MCP server for turning task lists into realistic schedules with blocked dates, capacity overrides, overflow tracking, and markdown planning output.
121121
- [TokRepo Search](https://github.com/henu-wang/tokrepo-codex-plugin) - Search and install AI assets from TokRepo with a bundled skill and MCP server for Codex.
122-
- [Toprank](https://github.com/nowork-studio/toprank) - Open-source MIT Claude Code plugin with 9 SEO and Google Ads skills that connects Google Search Console, PageSpeed Insights, and the Google Ads API, and can ship fixes such as meta tag rewrites, JSON-LD schema generation, keyword bid adjustments, and CMS content pushes.
123122
- [Upwork Autopilot](https://github.com/klajdikkolaj/upwork-autopilot) - Controlled Upwork job search, qualification, and proposal submission sessions through a dedicated Chrome profile.
124123
- [Yandex Direct](https://github.com/nebelov/yandex-direct-for-all) - GitHub-ready Codex plugin bundle for Yandex Direct, Wordstat, Metrika, and Roistat.
125124

scripts/generate_plugins_json.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
OUTPUT = Path(__file__).parent.parent / "plugins.json"
2626
MARKETPLACE_OUTPUT = Path(__file__).parent.parent / ".agents" / "plugins" / "marketplace.json"
2727
PLUGINS_ROOT = Path(__file__).parent.parent / "plugins"
28-
REQUEST_TIMEOUT_SECONDS = 45
28+
REQUEST_TIMEOUT_SECONDS = 60
29+
MAX_RETRIES = 3
2930
USER_AGENT = "awesome-codex-plugins-generator"
3031
OPTIONAL_PLUGIN_FILES = (
3132
"README.md",
@@ -103,12 +104,21 @@ def parse_plugins(readme_path: Path) -> list[dict[str, str]]:
103104

104105

105106
def fetch_repo_archive(owner: str, repo: str) -> zipfile.ZipFile:
106-
request = urllib.request.Request(
107-
f"https://github.com/{owner}/{repo}/archive/HEAD.zip",
108-
headers={"User-Agent": USER_AGENT},
109-
)
110-
with urllib.request.urlopen(request, timeout=REQUEST_TIMEOUT_SECONDS) as response:
111-
return zipfile.ZipFile(io.BytesIO(response.read()))
107+
last_error = None
108+
for attempt in range(MAX_RETRIES):
109+
try:
110+
request = urllib.request.Request(
111+
f"https://github.com/{owner}/{repo}/archive/HEAD.zip",
112+
headers={"User-Agent": USER_AGENT},
113+
)
114+
with urllib.request.urlopen(request, timeout=REQUEST_TIMEOUT_SECONDS) as response:
115+
return zipfile.ZipFile(io.BytesIO(response.read()))
116+
except Exception as e:
117+
last_error = e
118+
if attempt < MAX_RETRIES - 1:
119+
import time
120+
time.sleep(2 ** attempt) # exponential backoff
121+
raise last_error
112122

113123

114124
def resolve_plugin_root(names: set[str]) -> PurePosixPath:
@@ -175,9 +185,16 @@ def collect_selected_paths(
175185

176186

177187
def mirror_plugin_bundle(plugin: dict[str, str]) -> tuple[dict[str, object], str]:
178-
archive = fetch_repo_archive(plugin["owner"], plugin["repo"])
188+
owner_repo = f"{plugin['owner']}/{plugin['repo']}"
189+
try:
190+
archive = fetch_repo_archive(plugin["owner"], plugin["repo"])
191+
except Exception as e:
192+
raise ValueError(f"Failed to fetch {owner_repo}: {e}") from e
179193
names = {name for name in archive.namelist() if not name.endswith("/")}
180-
plugin_root = resolve_plugin_root(names)
194+
try:
195+
plugin_root = resolve_plugin_root(names)
196+
except ValueError:
197+
raise ValueError(f"Archive for {owner_repo} does not contain .codex-plugin/plugin.json") from None
181198
manifest = load_manifest(archive, plugin_root)
182199
selected_paths = collect_selected_paths(manifest, names, plugin_root)
183200

0 commit comments

Comments
 (0)