Skip to content

Commit 46df1e5

Browse files
committed
Updates
Tested up to version in readme and plugin file, and removal of assertion for preconnect hint with no crossorigin attribute.
1 parent 1583e49 commit 46df1e5

6 files changed

Lines changed: 267 additions & 3 deletions

File tree

Binary file not shown.
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
#!/usr/bin/env python3
2+
"""Check WordPress "Tested up to" metadata against the latest release."""
3+
4+
from __future__ import annotations
5+
6+
import json
7+
import os
8+
import re
9+
import subprocess
10+
import sys
11+
import urllib.request
12+
from pathlib import Path
13+
14+
15+
WORDPRESS_VERSION_CHECK_URL = os.environ.get(
16+
"WORDPRESS_VERSION_CHECK_URL",
17+
"https://api.wordpress.org/core/version-check/1.7/",
18+
)
19+
WORDPRESS_LATEST_VERSION = os.environ.get("WORDPRESS_LATEST_VERSION")
20+
SCAN_EXTENSIONS = {".php", ".md", ".txt"}
21+
DEFAULT_EXCLUDED_DIRS = {
22+
".git",
23+
"build",
24+
"coverage",
25+
"dist",
26+
"node_modules",
27+
"plugin-check-build",
28+
"vendor",
29+
}
30+
TESTED_UP_TO_PATTERN = re.compile(
31+
r"\btested\s+up\s+to\s*:\s*([0-9]+(?:\.[0-9]+){1,2})\b",
32+
re.IGNORECASE,
33+
)
34+
TESTED_UP_TO_LABEL_PATTERN = re.compile(r"\btested\s+up\s+to\s*:", re.IGNORECASE)
35+
VERSION_PATTERN = re.compile(r"^[0-9]+(?:\.[0-9]+){1,2}$")
36+
37+
38+
def main() -> int:
39+
latest_version = get_latest_wordpress_major_minor()
40+
excluded_dirs = get_excluded_dirs()
41+
findings = find_tested_up_to_entries(excluded_dirs)
42+
43+
if not findings:
44+
message = "No Tested up to metadata was found in tracked PHP, Markdown, or text files."
45+
print_github_error(message)
46+
write_summary(latest_version, [], [message])
47+
return 1
48+
49+
failures = []
50+
for finding in findings:
51+
if finding["version"] is None:
52+
failures.append(
53+
f"{finding['path']}:{finding['line']}: Could not parse Tested up to version."
54+
)
55+
print_github_error(
56+
"Could not parse Tested up to version.",
57+
finding["path"],
58+
finding["line"],
59+
)
60+
continue
61+
62+
tested_version = normalize_major_minor(finding["version"])
63+
if tested_version != latest_version:
64+
message = (
65+
f"Tested up to is {finding['version']}; expected {latest_version} "
66+
"for the latest WordPress release."
67+
)
68+
failures.append(f"{finding['path']}:{finding['line']}: {message}")
69+
print_github_error(message, finding["path"], finding["line"])
70+
71+
write_summary(latest_version, findings, failures)
72+
73+
if failures:
74+
entry_label = "entry" if len(failures) == 1 else "entries"
75+
print(f"Found {len(failures)} stale or invalid Tested up to {entry_label}.")
76+
for failure in failures:
77+
print(f"- {failure}")
78+
return 1
79+
80+
print(f"All Tested up to entries match WordPress {latest_version}.")
81+
return 0
82+
83+
84+
def get_latest_wordpress_major_minor() -> str:
85+
if WORDPRESS_LATEST_VERSION:
86+
return normalize_major_minor(WORDPRESS_LATEST_VERSION)
87+
88+
request = urllib.request.Request(
89+
WORDPRESS_VERSION_CHECK_URL,
90+
headers={"User-Agent": "wordpress-tested-up-to-check/1.0"},
91+
)
92+
93+
with urllib.request.urlopen(request, timeout=20) as response:
94+
payload = json.load(response)
95+
96+
versions = []
97+
for offer in payload.get("offers", []):
98+
version = offer.get("current") or offer.get("version")
99+
if isinstance(version, str) and VERSION_PATTERN.match(version):
100+
versions.append(version)
101+
102+
if not versions:
103+
raise RuntimeError("Could not determine the latest WordPress version from the version-check API.")
104+
105+
return normalize_major_minor(max(versions, key=version_sort_key))
106+
107+
108+
def normalize_major_minor(version: str) -> str:
109+
parts = version.split(".")
110+
111+
if len(parts) < 2 or not all(part.isdigit() for part in parts):
112+
raise ValueError(f"Invalid WordPress version: {version}")
113+
114+
return ".".join(parts[:2])
115+
116+
117+
def version_sort_key(version: str) -> tuple[int, ...]:
118+
return tuple(int(part) for part in version.split("."))
119+
120+
121+
def get_excluded_dirs() -> set[str]:
122+
configured = os.environ.get("WORDPRESS_TESTED_UP_TO_EXCLUDE_DIRS", "")
123+
extra_dirs = {item.strip() for item in configured.split(",") if item.strip()}
124+
125+
return DEFAULT_EXCLUDED_DIRS | extra_dirs
126+
127+
128+
def find_tested_up_to_entries(
129+
excluded_dirs: set[str],
130+
) -> list[dict[str, str | int | None]]:
131+
findings = []
132+
133+
for path in get_tracked_files():
134+
if not should_scan(path, excluded_dirs):
135+
continue
136+
137+
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
138+
139+
for line_number, line in enumerate(lines, 1):
140+
if not TESTED_UP_TO_LABEL_PATTERN.search(line):
141+
continue
142+
143+
match = TESTED_UP_TO_PATTERN.search(line)
144+
findings.append(
145+
{
146+
"path": path.as_posix(),
147+
"line": line_number,
148+
"version": match.group(1) if match else None,
149+
}
150+
)
151+
152+
return findings
153+
154+
155+
def get_tracked_files() -> list[Path]:
156+
result = subprocess.run(
157+
["git", "ls-files", "*.php", "*.md", "*.txt"],
158+
check=True,
159+
stdout=subprocess.PIPE,
160+
stderr=subprocess.PIPE,
161+
text=True,
162+
)
163+
164+
return [Path(line) for line in result.stdout.splitlines() if line.strip()]
165+
166+
167+
def should_scan(path: Path, excluded_dirs: set[str]) -> bool:
168+
if path.suffix.lower() not in SCAN_EXTENSIONS:
169+
return False
170+
171+
return not any(part in excluded_dirs for part in path.parts)
172+
173+
174+
def write_summary(
175+
latest_version: str,
176+
findings: list[dict[str, str | int | None]],
177+
failures: list[str],
178+
) -> None:
179+
summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
180+
if not summary_path:
181+
return
182+
183+
lines = [
184+
"# WordPress Tested Up To Check",
185+
"",
186+
f"Latest WordPress major.minor release: `{latest_version}`",
187+
f"Metadata entries checked: `{len(findings)}`",
188+
"",
189+
]
190+
191+
if failures:
192+
lines.append("## Failures")
193+
lines.extend(f"- {failure}" for failure in failures)
194+
else:
195+
lines.append("All Tested up to entries are current.")
196+
197+
with open(summary_path, "a", encoding="utf-8") as summary:
198+
summary.write("\n".join(lines))
199+
summary.write("\n")
200+
201+
202+
def print_github_error(message: str, path: str | None = None, line: int | None = None) -> None:
203+
if path is None:
204+
print(f"::error::{escape_github_command_data(message)}")
205+
return
206+
207+
properties = f"file={escape_github_command_property(path)}"
208+
if line is not None:
209+
properties += f",line={line}"
210+
211+
print(f"::error {properties}::{escape_github_command_data(message)}")
212+
213+
214+
def escape_github_command_property(value: str) -> str:
215+
return (
216+
value.replace("%", "%25")
217+
.replace("\r", "%0D")
218+
.replace("\n", "%0A")
219+
.replace(":", "%3A")
220+
.replace(",", "%2C")
221+
)
222+
223+
224+
def escape_github_command_data(value: str) -> str:
225+
return value.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")
226+
227+
228+
if __name__ == "__main__":
229+
try:
230+
sys.exit(main())
231+
except Exception as exc:
232+
print_github_error(str(exc))
233+
sys.exit(1)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# This workflow checks that WordPress "Tested up to" metadata stays current.
2+
# It scans tracked PHP, Markdown, and text files without assuming repository-specific
3+
# metadata filenames, then compares each value to the latest WordPress major.minor release.
4+
5+
name: WordPress Tested Up To
6+
7+
on:
8+
push:
9+
branches: [ main ]
10+
pull_request:
11+
schedule:
12+
- cron: '23 12 * * *'
13+
workflow_dispatch:
14+
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
permissions:
20+
contents: read
21+
22+
jobs:
23+
tested-up-to:
24+
name: Verify Tested Up To Metadata
25+
runs-on: ubuntu-latest
26+
27+
steps:
28+
- name: Checkout code
29+
uses: actions/checkout@v6
30+
31+
- name: Check WordPress Tested Up To metadata
32+
run: python3 .github/scripts/check-wordpress-tested-up-to.py

enginescript-site-optimizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* Text Domain: enginescript-site-optimizer
1111
* Requires at least: 6.8
1212
* Requires PHP: 8.2
13-
* Tested up to: 6.9
13+
* Tested up to: 7.0
1414
* Security: Follows OWASP security guidelines and WordPress best practices
1515
*
1616
* @package EngineScript_Site_Optimizer

readme.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Contributors: enginescript
33
Tags: optimization, performance, cleanup
44
Requires at least: 6.8
5-
Tested up to: 6.9
5+
Tested up to: 7.0
66
Stable tag: 2.0.0
77
Requires PHP: 8.2
88
License: GPLv3 or later

tests/unit/DomainValidationTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ public function test_resource_hints_use_wordpress_filter_contract(): void {
195195
$cdn_hint = $this->findResourceHintByHref( $preconnect_hints, 'https://cdn.example.com' );
196196

197197
$this->assertContains( array( 'href' => 'https://fonts.gstatic.com', 'crossorigin' => 'anonymous' ), $preconnect_hints );
198-
$this->assertContains( array( 'href' => 'https://cdn.example.com' ), $preconnect_hints );
199198
$this->assertIsArray( $cdn_hint );
200199
$this->assertArrayNotHasKey( 'crossorigin', $cdn_hint );
201200
$this->assertContains( 'https://static.example.com', $dns_prefetch_urls );

0 commit comments

Comments
 (0)