Menyimpan artefak pengujian (seperti laporan HTML dari Playwright, Jest, atau Allure) langsung di CI platform sering kali memperlambat alur kerja code review. Reviewer harus mengunduh berkas ZIP, mengekstraknya secara lokal, lalu membuka berkas HTML melalui peramban. Mengotomasi publikasi artefak pengujian ke edge static hosting seperti Cloudflare Pages mempercepat siklus validasi pull request (PR) secara signifikan.

Arsitektur Alur Kerja

Laporan pengujian dieksekusi di runner CI (misalnya GitHub Actions), dikompilasi menjadi bundel aset statis (HTML/CSS/JS), disanitasi dari data sensitif, kemudian diunggah ke Cloudflare Pages menggunakan CLI resmi (Wrangler). Setiap branch atau pull request mendapatkan URL pratinjau independen yang otomatis dikomentari kembali ke pull request terkait.

1. Sanitasi Artefak dan Log Pengujian

Laporan pengujian sering kali secara tidak sengaja mencakup header otorisasi, URL database, atau token API yang tercetak saat pengujian gagal. Sanitasi artefak statis sebelum diunggah merupakan tahapan wajib untuk mencegah kebocoran kredensial internal.

# Hapus file environment dan ganti string sensitif di direktori report
find ./playwright-report -type f -name "*.html" -o -name "*.json" | while read file; do
  sed -i 's/Bearer [A-Za-z0-9_.-]*/Bearer [REDACTED]/g' "$file"
  sed -i 's/password=[^&]*/password=[REDACTED]/g' "$file"
done
rm -rf ./playwright-report/trace/*.env

2. Pipeline Deployment ke Cloudflare Pages

Gunakan runner GitHub Actions untuk mengeksekusi test suite dan mempublikasikan direktori output via Wrangler CLI. Nilai flag --branch diarahkan ke nomor PR atau nama branch untuk menjaga isolasi preview deployment.

name: Test & Publish Edge Report

on:
  pull_request:
    branches: [main]

jobs:
  test-and-deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install Dependencies
        run: npm ci

      - name: Run Playwright Tests
        run: npx playwright test
        continue-on-error: true

      - name: Sanitize Test Artifacts
        run: |
          find ./playwright-report -type f \( -name "*.html" -o -name "*.json" \) -exec sed -i 's/Bearer [A-Za-z0-9_.-]*/Bearer [REDACTED]/g' {} +

      - name: Deploy Report to Cloudflare Pages
        id: deploy
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CF_PAGES_API_TOKEN }}
          CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
        run: |
          DEPLOYMENT_OUTPUT=$(npx wrangler pages deploy ./playwright-report \
            --project-name="ci-test-reports" \
            --branch="pr-${{ github.event.pull_request.number }}")
          
          DEPLOY_URL=$(echo "$DEPLOYMENT_OUTPUT" | grep -o 'https://[^ ]*\.pages\.dev' | head -n 1)
          echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT

      - name: Comment PR with Report URL
        uses: actions/github-script@v7
        with:
          script: |
            const reportUrl = '${{ steps.deploy.outputs.url }}';
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `### 📊 Test Report Tersedia\n\nReview hasil eksekusi pengujian: [Akses Edge Report](${reportUrl})`
            });

3. Proteksi Akses Internal

Laporan pengujian tidak boleh terbuka untuk publik luas. Terdapat dua pendekatan standar untuk membatasi akses pada Cloudflare Pages:

  • Cloudflare Access (Zero Trust): Terapkan policy pada domain *.ci-test-reports.pages.dev. Batasi akses hanya untuk pengguna yang memiliki domain email perusahaan (misal @perusahaan.com) melalui integrasi SSO (Google Workspace, Okta, dsb).
  • Cloudflare Worker Basic Auth: Buat file konfigurasi _middleware.js di direktori root report sebelum deploy jika Cloudflare Zero Trust tidak tersedia.
// playwright-report/_middleware.js
export async function onRequest(context) {
  const authHeader = context.request.headers.get("Authorization");
  if (!authHeader) {
    return new Response("Unauthorized", {
      status: 401,
      headers: { "WWW-Authenticate": 'Basic realm="Secure CI Report"' },
    });
  }

  const [scheme, encoded] = authHeader.split(" ");
  if (scheme !== "Basic" || !encoded) {
    return new Response("Bad Request", { status: 400 });
  }

  const decoded = atob(encoded);
  const [user, pass] = decoded.split(":");

  if (user === context.env.REPORT_USER && pass === context.env.REPORT_PASS) {
    return context.next();
  }

  return new Response("Forbidden", { status: 403 });
}

4. Pengelolaan Retensi dan Lifecycle Artefak

Membuka deployment baru pada setiap commit dapat menyebabkan akumulasi artefak statis di edge storage. Gunakan strategi pembersihan berkala melalui Cloudflare API untuk menghapus deployment branch PR yang telah berstatus closed atau merged.

# Script rotasi pembersihan deployment lama (Node.js)
import fetch from 'node-fetch';

const ACCOUNT_ID = process.env.CF_ACCOUNT_ID;
const PROJECT_NAME = 'ci-test-reports';
const API_TOKEN = process.env.CF_PAGES_API_TOKEN;

async function pruneDeployments() {
  const res = await fetch(`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/pages/projects/${PROJECT_NAME}/deployments`, {
    headers: { Authorization: `Bearer ${API_TOKEN}` }
  });
  const data = await res.json();
  const deployments = data.result;

  // Batasi hanya menyimpan 50 deployment terakhir
  const toDelete = deployments.slice(50);
  for (const dep of toDelete) {
    await fetch(`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/pages/projects/${PROJECT_NAME}/deployments/${dep.id}`, {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${API_TOKEN}` }
    });
    console.log(`Deleted stale deployment: ${dep.id}`);
  }
}

pruneDeployments();

Catatan: Jalankan script pembersihan secara terjadwal (cron) mingguan di CI atau trigger otomatis via webhook saat status pull request berubah menjadi closed.