diff --git a/.gitea/workflows/quality-assurance-template.yml b/.gitea/workflows/quality-assurance-template.yml new file mode 100644 index 0000000..8996f0f --- /dev/null +++ b/.gitea/workflows/quality-assurance-template.yml @@ -0,0 +1,161 @@ +name: Reusable Nightly QA + +on: + workflow_call: + inputs: + TARGET_URL: + description: 'The URL to test (e.g., https://testing.klz-cables.com)' + required: true + type: string + PROJECT_NAME: + description: 'The internal project name for notifications' + required: true + type: string + secrets: + GOTIFY_URL: + required: true + GOTIFY_TOKEN: + required: true + GATEKEEPER_PASSWORD: + required: true + +jobs: + qa_suite: + name: 🛡️ Nightly QA Suite + runs-on: docker + container: + image: catthehacker/ubuntu:act-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 10 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: 🔐 Registry Auth + run: | + echo "@mintel:registry=https://git.infra.mintel.me/api/packages/mmintel/npm" > .npmrc + echo "//git.infra.mintel.me/api/packages/mmintel/npm/:_authToken=${{ secrets.MINTEL_PRIVATE_TOKEN || secrets.GITEA_PAT }}" >> .npmrc + - name: Install dependencies + run: | + pnpm store prune + pnpm install --no-frozen-lockfile + + - name: 📦 Cache APT Packages + uses: actions/cache@v4 + with: + path: /var/cache/apt/archives + key: apt-cache-${{ runner.os }}-${{ runner.arch }}-chromium + + - name: 💾 Cache Chromium + id: cache-chromium + uses: actions/cache@v4 + with: + path: /usr/bin/chromium + key: ${{ runner.os }}-chromium-native-${{ hashFiles('package.json') }} + + - name: 🔍 Install Chromium (Native & ARM64) + if: steps.cache-chromium.outputs.cache-hit != 'true' + run: | + rm -f /etc/apt/apt.conf.d/docker-clean + apt-get update + apt-get install -y gnupg wget ca-certificates + OS_ID=$(. /etc/os-release && echo $ID) + CODENAME=$(. /etc/os-release && echo $VERSION_CODENAME) + if [ "$OS_ID" = "debian" ]; then + apt-get install -y chromium + else + mkdir -p /etc/apt/keyrings + KEY_ID="82BB6851C64F6880" + wget -qO- "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x$KEY_ID" | gpg --dearmor > /etc/apt/keyrings/xtradeb.gpg + echo "deb [signed-by=/etc/apt/keyrings/xtradeb.gpg] http://ppa.launchpad.net/xtradeb/apps/ubuntu $CODENAME main" > /etc/apt/sources.list.d/xtradeb-ppa.list + printf "Package: *\nPin: release o=LP-PPA-xtradeb-apps\nPin-Priority: 1001\n" > /etc/apt/preferences.d/xtradeb + apt-get update + apt-get install -y --allow-downgrades chromium + fi + [ -f /usr/bin/chromium ] && ln -sf /usr/bin/chromium /usr/bin/google-chrome + [ -f /usr/bin/chromium ] && ln -sf /usr/bin/chromium /usr/bin/chromium-browser + + # ── Quality Gates ───────────────────────────────────────────────────────── + + - name: 🌐 Full Sitemap HTML Validation + if: always() + env: + NEXT_PUBLIC_BASE_URL: ${{ inputs.TARGET_URL }} + GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD }} + run: pnpm run check:html + + - name: 🌐 Dynamic Asset Presence & Error Scan + if: always() + env: + NEXT_PUBLIC_BASE_URL: ${{ inputs.TARGET_URL }} + GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD }} + run: pnpm run check:assets + + - name: ♿ Accessibility Scan (WCAG) + if: always() + continue-on-error: true + env: + NEXT_PUBLIC_BASE_URL: ${{ inputs.TARGET_URL }} + GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD }} + run: pnpm run check:wcag + + - name: 🔗 Markdown & HTML Link Check (Lychee) + if: always() + uses: lycheeverse/lychee-action@v2 + with: + args: --accept 200,204,429 --timeout 15 content/ app/ public/ + fail: true + + - name: 🎭 LHCI Desktop Audit + id: lhci_desktop + if: always() + env: + LHCI_URL: ${{ inputs.TARGET_URL }} + GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD }} + run: pnpm run pagespeed:test -- --collect.settings.preset=desktop + + - name: 📱 LHCI Mobile Audit + id: lhci_mobile + if: always() + env: + LHCI_URL: ${{ inputs.TARGET_URL }} + GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD }} + run: pnpm run pagespeed:test -- --collect.settings.preset=mobile + + notifications: + name: 🔔 Notify + needs: [qa_suite] + if: always() + runs-on: docker + container: + image: catthehacker/ubuntu:act-latest + steps: + - name: 🔔 Gotify + shell: bash + run: | + SUITE="${{ needs.qa_suite.result }}" + PROJECT="${{ inputs.PROJECT_NAME }}" + URL="${{ inputs.TARGET_URL }}" + + if [[ "$SUITE" != "success" ]]; then + PRIORITY=8 + EMOJI="⚠️" + STATUS_LINE="Nightly QA Failed! Action required." + else + PRIORITY=2 + EMOJI="✅" + STATUS_LINE="Nightly QA Passed perfectly." + fi + + TITLE="$EMOJI $PROJECT Nightly QA" + MESSAGE="$STATUS_LINE\n$URL\nPlease check Pipeline output for details." + + curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \ + -F "title=$TITLE" \ + -F "message=$MESSAGE" \ + -F "priority=$PRIORITY" || true