Files
FreeCrawl-SEO-Tool/docs/ci/github-actions-example.yml
T

105 lines
3.8 KiB
YAML

# FreeCrawl SEO — example GitHub Actions workflow
#
# Crawls your site with the headless CLI on every push and on a daily
# schedule, fails the build when broken links exceed a threshold, validates
# the sitemap, and uploads the crawl as a build artifact. Adapt TARGET_URL
# and the thresholds to your site.
#
# Save this as `.github/workflows/freecrawl-audit.yml` in your own repo.
# No FreeCrawl install is required on the runner — the workflow clones the
# source and builds the CLI (plus its workspace packages) from it.
#
# See docs/ci/README.md for the CLI exit-code contract, a zero-tolerance
# (no-script) gate variant, and how to add a regression-compare job.
name: FreeCrawl SEO Audit
on:
push:
branches: [main]
schedule:
- cron: '0 4 * * *' # 04:00 UTC daily
workflow_dispatch:
env:
TARGET_URL: https://example.com
MAX_URLS: '5000'
BROKEN_LINK_THRESHOLD: '10'
jobs:
audit:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
repository: kemalai/FreeCrawl-SEO-Tool
path: freecrawl
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: freecrawl/package-lock.json
- name: Install + build CLI
working-directory: freecrawl
run: |
# --ignore-scripts skips the Playwright browser download; the CLI's
# default crawl is undici-based and needs no Chromium. Drop the flag
# only if you enable JS rendering through a --config file.
npm ci --ignore-scripts
# Build the CLI and its workspace deps (shared-types → db → core) in
# dependency order via project references. The heavy Electron desktop
# build is intentionally skipped — `npm run build:cli` alone would
# NOT build the packages the CLI imports and the crawl would fail.
npx tsc -b apps/cli
- name: Crawl ${{ env.TARGET_URL }}
working-directory: freecrawl
run: |
# --json prints a machine-readable summary to stdout (captured for
# the gate); --out writes the full per-URL dump for the artifact.
# `|| true` hands pass/fail to the threshold gate below — the CLI
# itself exits 1 on ANY 4xx/5xx (see the zero-tolerance variant in
# docs/ci/README.md).
node apps/cli/dist/index.js "$TARGET_URL" \
--max "$MAX_URLS" \
--json \
--out "$GITHUB_WORKSPACE/crawl.json" \
> "$GITHUB_WORKSPACE/summary.json" || true
cat "$GITHUB_WORKSPACE/summary.json"
- name: Gate on broken links
run: |
# Count 4xx/5xx from the summary's byStatus map — robust against the
# full dump's column layout, and cheap (no multi-MB file parse).
BROKEN=$(node -e "
const s = require('$GITHUB_WORKSPACE/summary.json');
const by = (s.summary && s.summary.byStatus) || {};
let n = 0;
for (const [code, count] of Object.entries(by)) {
if (Number(code) >= 400) n += count;
}
console.log(n);
")
echo "Broken-URL count: $BROKEN (threshold: $BROKEN_LINK_THRESHOLD)"
if [ "$BROKEN" -gt "$BROKEN_LINK_THRESHOLD" ]; then
echo "::error::Broken-URL count $BROKEN exceeds threshold $BROKEN_LINK_THRESHOLD"
exit 1
fi
- name: Validate sitemap (non-blocking)
working-directory: freecrawl
continue-on-error: true
run: node apps/cli/dist/index.js validate-sitemap "$TARGET_URL/sitemap.xml"
- uses: actions/upload-artifact@v4
if: always()
with:
name: freecrawl-audit-${{ github.run_number }}
path: |
crawl.json
summary.json
retention-days: 30