Skip to content

chore(deps)(deps): bump bullmq from 5.79.3 to 5.80.2 in /apps/api in the bullmq group #430

chore(deps)(deps): bump bullmq from 5.79.3 to 5.80.2 in /apps/api in the bullmq group

chore(deps)(deps): bump bullmq from 5.79.3 to 5.80.2 in /apps/api in the bullmq group #430

name: docs-linkcheck
# Builds the BoringStack docs site and runs lychee against the built output.
# Internal broken links fail the job; external 4xx/5xx are warnings only
# (rate-limited APIs and intermittent third-party 503s are not the docs'
# problem to solve at PR-review time).
on:
push:
branches: [main]
paths:
- "apps/docs/src/**"
- "apps/docs/scripts/**"
- "apps/docs/astro.config.mjs"
- "apps/docs/package.json"
- "apps/docs/bun.lock"
# Build/install/scan config: wrangler (Cloudflare asset + build settings),
# bunfig (install behavior), osv-scanner (vuln allowlist). A config-only
# push must still rebuild + linkcheck so a broken config can't land
# unverified.
- "apps/docs/wrangler.jsonc"
- "apps/docs/bunfig.toml"
- "apps/docs/osv-scanner.toml"
- ".github/workflows/apps-docs-linkcheck.yml"
# Catalog inputs: the generated docs data is derived from sibling-app
# scripts/README.md, package.json scripts, and lint-meta export-catalog.
# Changes here can stale the committed catalogs, so they must trigger the
# freshness check even when no apps/docs file changed.
- "apps/api/scripts/**"
- "apps/api/package.json"
- "apps/ui/scripts/**"
- "apps/ui/package.json"
# PR trigger has NO `paths:` filter — the `linkcheck` check is in
# branch protection's required-checks list, so the workflow must run
# on every PR or branch protection will wait forever for a status
# that never comes ("Expected — Waiting for status to be reported").
# The expensive build + lychee steps are gated on the internal
# paths-filter below, so PRs that don't touch docs report green
# without doing any real work.
pull_request: {}
workflow_dispatch:
permissions:
contents: read
concurrency:
group: apps-docs-linkcheck-docs-${{ github.ref }}
cancel-in-progress: true
jobs:
linkcheck:
defaults:
run:
working-directory: apps/docs
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Detect docs changes
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
docs:
- 'apps/docs/src/**'
- 'apps/docs/scripts/**'
- 'apps/docs/astro.config.mjs'
- 'apps/docs/package.json'
- 'apps/docs/bun.lock'
- 'apps/docs/wrangler.jsonc'
- 'apps/docs/bunfig.toml'
- 'apps/docs/osv-scanner.toml'
- '.github/workflows/apps-docs-linkcheck.yml'
catalog:
- 'apps/api/scripts/**'
- 'apps/api/package.json'
- 'apps/ui/scripts/**'
- 'apps/ui/package.json'
- name: No-op notice for unaffected PRs
if: steps.filter.outputs.docs != 'true' && steps.filter.outputs.catalog != 'true'
run: echo "No docs or catalog-input files changed in this PR — skipped."
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
with:
bun-version: 1.3.14
- name: Cache bun install
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('apps/docs/bun.lock', 'apps/ui/bun.lock', 'apps/api/bun.lock') }}
restore-keys: |
bun-${{ runner.os }}-
- name: Install docs deps
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
run: bun install --frozen-lockfile
- name: Install apps/ui deps for lint-meta export
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
working-directory: apps/ui
run: bun install --frozen-lockfile
- name: Install apps/api deps for lint-meta export
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
working-directory: apps/api
run: bun install --frozen-lockfile
- name: Verify generated docs data
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
env:
BORINGSTACK_UI_DIR: ${{ github.workspace }}/apps/ui
BORINGSTACK_API_DIR: ${{ github.workspace }}/apps/api
run: bun run check:docs-data
# The full site build + lychee link check stay gated on docs changes
# only — a catalog-input change needs the freshness check above, not a
# multi-minute rebuild of the whole site.
- name: Build docs site
if: steps.filter.outputs.docs == 'true'
env:
BORINGSTACK_UI_DIR: ${{ github.workspace }}/apps/ui
BORINGSTACK_API_DIR: ${{ github.workspace }}/apps/api
run: bun run build:ci
- name: Internal link check (fail on broken)
if: steps.filter.outputs.docs == 'true'
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
with:
args: >-
--no-progress
--offline
--root-dir ${{ github.workspace }}/apps/docs/dist
'${{ github.workspace }}/apps/docs/dist/**/*.html'
fail: true
- name: External link check (warning only)
if: steps.filter.outputs.docs == 'true'
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
continue-on-error: true
with:
# This step is warning-only, so it must never block the runner. Two
# changes keep it fast on a shared CI IP that github.com throttles:
#
# --max-retries 0 A throttled link (HTTP 429 + a giant
# Retry-After) becomes an immediate warning
# instead of a per-retry backoff loop
# ("unexpectedly big rate limit backoff … capping
# to 1m") that serialised into a multi-minute hang.
#
# exclude self-repo Starlight renders an "Edit this page" link
# (github.com/.../edit/main/<page>) on EVERY doc
# page — ~one github.com request per page. Plus
# the blob/tree source links. None are API-eligible,
# so the token can't help them and they dominated
# the throttling. They are auto-generated /
# self-referential and verifying our own repo from
# CI adds nothing a public repo + the internal link
# check don't already cover. Excluding the whole
# boringstack-xyz org covers edit + blob + tree +
# org/repo-root in one pattern.
#
# Third-party github.com links (ripgrep, gitleaks, …) are still
# checked — the GITHUB_TOKEN below routes those parseable owner/repo
# URLs through api.github.com (5000 req/h), where they don't throttle.
args: >-
--no-progress
--max-retries 0
--root-dir ${{ github.workspace }}/apps/docs/dist
--exclude-path './llms-full.txt'
--exclude-path './llms-small.txt'
--exclude '^https?://(localhost|127\.0\.0\.1|.*\.localhost)'
--exclude 'github\.com/boringstack-xyz'
'${{ github.workspace }}/apps/docs/dist/**/*.html'
fail: false
env:
# Routes the remaining (third-party) github.com links through the
# GitHub API (5000 req/h) instead of anonymous HTTP (60 req/h), so
# they don't throttle. Needs only the default contents:read.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}