Skip to content
2 changes: 2 additions & 0 deletions .github/requirements-audit.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests>=2.31.0
beautifulsoup4>=4.12.0
135 changes: 135 additions & 0 deletions .github/workflows/seo-audit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
name: Landing Page Audit Pipeline

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
workflow_dispatch:
Comment thread
coderabbitai[bot] marked this conversation as resolved.

jobs:
seo-and-crawl-audit:
runs-on: ubuntu-latest
Comment thread
coderabbitai[bot] marked this conversation as resolved.
steps:
- name: Checkout Code
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false

- name: Set up Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'

# Decide which URL to audit: local build for PRs, production for push/manual
- name: Set audit URL
id: set-url
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
echo "AUDIT_URL=http://localhost:3000" >> $GITHUB_ENV
else
echo "AUDIT_URL=https://social-share-button.aossie.org/" >> $GITHUB_ENV
fi

# Build and serve the landing page locally so PR audits test the PR's own code
- name: Build and serve landing page (PR only)
if: github.event_name == 'pull_request'
run: |
cd landing-page
npm ci
npm run build
npx serve -s out -l 3000 &
sleep 5

# 1. SCAN FOR BROKEN LINKS & ASSETS
# Docker-based actions cannot reach localhost on the runner.
# linkchecker runs directly on the runner, so it works for both
# localhost (PR builds) and the production URL (push/manual).
- name: Install Link Checker
run: pip install linkchecker

- name: Audit Broken Links and Images
run: linkchecker "${{ env.AUDIT_URL }}" --check-extern --no-warnings

# 2. RUN LIGHTHOUSE SEO + PERFORMANCE + ACCESSIBILITY AUDIT (no API key needed)
- name: Run Lighthouse Audit
uses: treosh/lighthouse-ci-action@0d716cfc9995b090bb89fe8f531fc3c133ec89ef # v11
with:
urls: |
${{ env.AUDIT_URL }}
configPath: ./.lighthouserc.json
uploadArtifacts: true
temporaryPublicStorage: true
Comment on lines +55 to +62

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟑 Minor | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden --glob '!**/.git/**' \
  'treosh/lighthouse-ci-action|lighthouserc|configPath|assertions|budgetPath' .

Repository: AOSSIE-Org/SocialShareButton

Length of output: 249


🌐 Web query:

treosh lighthouse-ci-action v11 default assertions configPath uploadArtifacts CI fail workflow

πŸ’‘ Result:

To configure treosh/lighthouse-ci-action v11 (or later) to perform CI assertions and manage artifacts, you should use the configPath and uploadArtifacts inputs in your workflow [1][2][3]. When you define assertions in a lighthouserc file (referenced by configPath), the action will automatically evaluate these assertions, and the CI workflow will fail if any assertion set to "error" is not met [1][4][5]. Key configuration details: 1. configPath: Use this input to specify the path to your lighthouserc file (e.g.,./lighthouserc.json) [4][5]. This file provides full control over LHCI assertions [4]. Example lighthouserc.json: { "ci": { "assert": { "assertions": { "categories:performance": ["error", { "minScore": 0.6 }] } } } } [4] 2. uploadArtifacts: Set this to true to save the Lighthouse reports as GitHub Actions artifacts [1][4]. This is helpful for debugging or persistent storage of results [4]. Example workflow step: - name: Audit URLs uses: treosh/lighthouse-ci-action@v11 with: urls: 'https://example.com/' configPath: './lighthouserc.json' uploadArtifacts: true [4][5] Troubleshooting workflow failures: If your workflow fails unexpectedly, check if you are using includePassedAssertions: true in your lighthouserc. In some older versions of this action, the presence of passed assertions in the results could cause the action to incorrectly report a failure [6]. Additionally, ensure that your assertions are correctly categorized as "error" if you intend for them to break the build; "warn" will typically report the issue without failing the workflow [4].

Citations:


Fail CI on Lighthouse score thresholds.

This step uploads artifacts but does not provide configPath, assertions, or score thresholds, and the repo has no checked-in Lighthouse CI config. Add a checked-in Lighthouse CI configuration with error-level assertions for accessibility, best practices, SEO, and performance, then pass it with configPath so low scores fail the workflow.

🧰 Tools
πŸͺ› zizmor (1.29.0)

[error] 25-25: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/seo-audit.yml around lines 24 - 30, Update the β€œRun
Lighthouse Audit” step to reference a checked-in Lighthouse CI configuration
through configPath, and add that configuration with error-level score assertions
for accessibility, best practices, SEO, and performance. Use the repository’s
existing workflow/config conventions and preserve the current audited URL and
artifact uploads.


# 3. ADVANCED VERIFICATIONS (Canonical, Schema, Robots.txt)
- name: Set up Python for Advanced Custom Scans
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.10'

- name: Install Advanced Audit Dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r .github/requirements-audit.txt

- name: Run Advanced Structural Validation
env:
TARGET_URL: ${{ env.AUDIT_URL }}
run: |
python - <<EOF
import os
import json
import requests
from bs4 import BeautifulSoup

url = os.environ["TARGET_URL"]
response = requests.get(url, headers={"User-Agent": "Googlebot"}, timeout=(10, 30))

# Test 1: Server Status and Speed
print(f"HTTP Status: {response.status_code}")
assert response.status_code == 200, "Landing page is down!"
ttfb = response.elapsed.total_seconds()
ttfb_budget = 2.5 # TTFB must be under 2.5 seconds
print(f"Time to First Byte (TTFB): {ttfb}s (Budget: {ttfb_budget}s)")
assert ttfb <= ttfb_budget, f"Performance Error: TTFB of {ttfb}s exceeds budget of {ttfb_budget}s"

soup = BeautifulSoup(response.text, 'html.parser')

# Test 2: Canonical Tag Check
canonicals = soup.find_all('link', rel='canonical')
assert len(canonicals) > 0, "CRITICAL: Missing rel='canonical' tag!"
assert len(canonicals) == 1, f"CRITICAL: Duplicate canonical tags found ({len(canonicals)}). Expected exactly 1!"

canonical_href = canonicals[0].get('href', '').strip()
print(f"Canonical URL found: {canonical_href if canonical_href else 'Empty'}")

assert canonical_href, "CRITICAL: The rel='canonical' tag has an empty or missing 'href' attribute!"

normalized_href = canonical_href.rstrip('/')
normalized_url = "https://social-share-button.aossie.org"
assert normalized_href == normalized_url, f"CRITICAL: Canonical URL '{canonical_href}' does not match the expected base URL '{normalized_url}/'!"

# Test 3: Heading Hierarchy Check
h1s = soup.find_all('h1')
print(f"Found {len(h1s)} H1 tags.")
assert len(h1s) == 1, "SEO Error: Landing page must have exactly ONE <h1> tag."

# Test 4: Schema Markup Verification (no extruct – uses built-in json)
script_tags = soup.find_all('script', type='application/ld+json')
json_ld = []
for index, tag in enumerate(script_tags, start=1):
raw = tag.get_text(strip=True)
assert raw, f"CRITICAL: JSON-LD block {index} is empty."
try:
data = json.loads(raw)
json_ld.append(data)
except json.JSONDecodeError as error:
raise AssertionError(
f"CRITICAL: JSON-LD block {index} is invalid: {error}"
) from error

print(f"Structured Data: Found {len(json_ld)} JSON-LD blocks.")
assert len(json_ld) > 0, "Warning: No Structured Schema found on page."

print("βœ… All advanced landing page validations passed successfully!")
EOF
15 changes: 15 additions & 0 deletions .lighthouserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"ci": {
"assert": {
"assertions": {
"categories:performance": ["error", { "minScore": 0.8 }],
"categories:accessibility": ["error", { "minScore": 0.9 }],
"categories:seo": ["error", { "minScore": 0.9 }],
"categories:best-practices": ["error", { "minScore": 0.9 }]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}
Loading