how to workflow test - #622
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces an automated tooling suite under docs/howto/_tooling/ to generate, capture, render, and validate the per-release user guide for AudioMuse-AI, utilizing Playwright for browser automation and a throwaway Docker Compose stack for CI. The feedback focuses on improving the robustness of these scripts: resolving a Playwright routing error when handling failed fetches, extending the metadata masking regex to support double quotes, adding a verification step to fail early if login fails during screenshot capture, and refining the slugify function in the validator to more accurately match GitHub's heading-anchor algorithm.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def make_route_handler(mock_all=False): | ||
| def handle(route): | ||
| url = route.request.url | ||
| if "stream" in url: | ||
| return route.continue_() | ||
| if mock_all: | ||
| data = build_mock(url, route.request.method) | ||
| if data is not None: | ||
| try: | ||
| return route.fulfill(status=200, content_type="application/json", | ||
| body=json.dumps(data, ensure_ascii=False)) | ||
| except Exception: | ||
| pass | ||
| try: | ||
| resp = route.fetch() | ||
| ct = (resp.headers or {}).get("content-type", "") | ||
| if "application/json" not in ct: | ||
| return route.fulfill(response=resp) | ||
| body = json.dumps(mask(resp.json()), ensure_ascii=False) | ||
| return route.fulfill(response=resp, body=body, content_type="application/json") | ||
| except Exception: | ||
| try: | ||
| return route.continue_() | ||
| except Exception: | ||
| return | ||
| return handle |
There was a problem hiding this comment.
If route.fetch() succeeds but subsequent processing (like resp.json() or mask()) throws an exception, calling route.continue_() will fail because Playwright does not allow continuing a route after it has already been fetched. This will raise another exception and leave the request hung indefinitely. Fulfill the route with the original response instead if route.fetch() succeeded.
def make_route_handler(mock_all=False):
def handle(route):
url = route.request.url
if 'stream' in url:
return route.continue_()
if mock_all:
data = build_mock(url, route.request.method)
if data is not None:
try:
return route.fulfill(status=200, content_type='application/json',
body=json.dumps(data, ensure_ascii=False))
except Exception:
pass
resp = None
try:
resp = route.fetch()
ct = (resp.headers or {}).get('content-type', '')
if 'application/json' not in ct:
return route.fulfill(response=resp)
body = json.dumps(mask(resp.json()), ensure_ascii=False)
return route.fulfill(response=resp, body=body, content_type='application/json')
except Exception:
if resp is not None:
try:
return route.fulfill(response=resp)
except Exception:
return
else:
try:
return route.continue_()
except Exception:
return
return handle| _EMBED_RE = re.compile(r"(title|artist|author|album)\s*=\s*'([^']*)'", re.IGNORECASE) | ||
|
|
||
|
|
||
| def _scrub_string(s): | ||
| if not isinstance(s, str) or "=" not in s: | ||
| return s | ||
|
|
||
| def repl(m): | ||
| field = m.group(1).lower() | ||
| cat = "title" if field == "title" else ("album" if field == "album" else "artist") | ||
| return "%s='%s'" % (m.group(1), _placeholder(cat, m.group(2))) | ||
|
|
||
| return _EMBED_RE.sub(repl, s) |
There was a problem hiding this comment.
The current regular expression only matches single-quoted values (e.g., title='...'). If the API response or configuration uses double quotes, the metadata masking will be bypassed. Update the regex to support both single and double quotes to ensure robust masking.
| _EMBED_RE = re.compile(r"(title|artist|author|album)\s*=\s*'([^']*)'", re.IGNORECASE) | |
| def _scrub_string(s): | |
| if not isinstance(s, str) or "=" not in s: | |
| return s | |
| def repl(m): | |
| field = m.group(1).lower() | |
| cat = "title" if field == "title" else ("album" if field == "album" else "artist") | |
| return "%s='%s'" % (m.group(1), _placeholder(cat, m.group(2))) | |
| return _EMBED_RE.sub(repl, s) | |
| _EMBED_RE = re.compile(r"(title|artist|author|album)\s*=\s*(['\"])(.*?)\2", re.IGNORECASE) | |
| def _scrub_string(s): | |
| if not isinstance(s, str) or "=" not in s: | |
| return s | |
| def repl(m): | |
| field = m.group(1).lower() | |
| quote = m.group(2) | |
| cat = 'title' if field == 'title' else ('album' if field == 'album' else 'artist') | |
| return '%s=%s%s%s' % (m.group(1), quote, _placeholder(cat, m.group(3)), quote) | |
| return _EMBED_RE.sub(repl, s) |
| page.fill("#login-user", user) | ||
| page.fill("#login-password", password) | ||
| page.click("#login-form button[type=submit]") | ||
| page.wait_for_load_state("networkidle", timeout=40000) | ||
| print("logged in ->", page.url) |
There was a problem hiding this comment.
If the login fails (e.g., due to incorrect credentials), the script will continue capturing other pages, which will all redirect to the login page or show unauthorized errors. This results in broken screenshots. Add a check to verify that the login was successful and raise an error early if it failed.
| page.fill("#login-user", user) | |
| page.fill("#login-password", password) | |
| page.click("#login-form button[type=submit]") | |
| page.wait_for_load_state("networkidle", timeout=40000) | |
| print("logged in ->", page.url) | |
| page.fill('#login-user', user) | |
| page.fill('#login-password', password) | |
| page.click('#login-form button[type=submit]') | |
| page.wait_for_load_state('networkidle', timeout=40000) | |
| if '/login' in page.url: | |
| raise RuntimeError('Login failed: still on login page. Please check your credentials.') | |
| print('logged in ->', page.url) |
| def slugify(heading, seen): | ||
| """Approximate GitHub's heading-anchor algorithm (github-slugger).""" | ||
| s = heading.strip().lower() | ||
| s = _PUNCT.sub("", s) # drop punctuation/symbols (keep word chars, space, hyphen) | ||
| s = s.replace(" ", "-") | ||
| base = s | ||
| n = seen.get(base, 0) | ||
| seen[base] = n + 1 | ||
| return base if n == 0 else "%s-%d" % (base, n) |
There was a problem hiding this comment.
GitHub's heading-anchor algorithm (github-slugger) collapses consecutive spaces/hyphens into a single hyphen and strips leading/trailing hyphens. The current implementation only replaces spaces with hyphens, which can lead to false positives (e.g., reporting broken links) for headings with multiple spaces or consecutive punctuation. Update slugify to match GitHub's behavior more accurately.
| def slugify(heading, seen): | |
| """Approximate GitHub's heading-anchor algorithm (github-slugger).""" | |
| s = heading.strip().lower() | |
| s = _PUNCT.sub("", s) # drop punctuation/symbols (keep word chars, space, hyphen) | |
| s = s.replace(" ", "-") | |
| base = s | |
| n = seen.get(base, 0) | |
| seen[base] = n + 1 | |
| return base if n == 0 else "%s-%d" % (base, n) | |
| def slugify(heading, seen): | |
| """Approximate GitHub's heading-anchor algorithm (github-slugger).""" | |
| s = heading.strip().lower() | |
| s = _PUNCT.sub('', s) | |
| s = re.sub(r'\s+', '-', s) | |
| s = re.sub(r'-+', '-', s) | |
| s = s.strip('-') | |
| base = s | |
| n = seen.get(base, 0) | |
| seen[base] = n + 1 | |
| return base if n == 0 else '%s-%d' % (base, n) |
|



This PR introduce and How to Workflow
PR test builds:
ghcr.io/neptunehub/audiomuse-ai:pr-622ghcr.io/neptunehub/audiomuse-ai:pr-622-nvidiaghcr.io/neptunehub/audiomuse-ai:pr-622-noavx2