diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index bd1e54a..c900f19 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -86,14 +86,6 @@ jobs: path: dist/ retention-days: 1 - - name: Upload Playwright report - if: failure() - uses: actions/upload-artifact@v3 - with: - name: playwright-report - path: playwright-report/ - retention-days: 7 - - name: Mark test complete id: mark_test if: success() @@ -122,6 +114,12 @@ jobs: name: admin-ui-dist path: dist + - name: Prune admin-ui-dist artifacts + env: + GITEA_TOKEN: ${{ secrets.REGISTRY_PASSWORD }} + REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + run: bash scripts/prune-gitea-artifacts.sh EventHub/EventHubFrontAdmin admin-ui-dist 0 + - name: Wait for Docker daemon run: bash scripts/ensure-docker-daemon.sh 240 @@ -273,14 +271,6 @@ jobs: E2E_IFT_BASE_URL: ${{ secrets.E2E_IFT_BASE_URL }} run: npm run test:e2e:ift - - name: Upload Playwright report - if: failure() - uses: actions/upload-artifact@v3 - with: - name: playwright-ift-report - path: playwright-report/ - retention-days: 7 - deploy-stage: needs: e2e-ift if: | @@ -395,14 +385,6 @@ jobs: E2E_STAGE_BASE_URL: ${{ secrets.E2E_STAGE_BASE_URL }} run: npm run test:e2e:stage - - name: Upload Playwright report - if: failure() - uses: actions/upload-artifact@v3 - with: - name: playwright-stage-report - path: playwright-report/ - retention-days: 7 - ci-done: needs: [test, push-image] if: | diff --git a/playwright.config.ts b/playwright.config.ts index cf6cda7..5b2f9d9 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -26,7 +26,7 @@ export default defineConfig({ forbidOnly: !!process.env.CI, retries: process.env.CI ? 1 : 0, workers: 1, - reporter: process.env.CI ? [['list'], ['html', { open: 'never' }]] : 'list', + reporter: 'list', timeout: 90_000, expect: { timeout: 10_000 }, use: { diff --git a/scripts/prune-ci-reports.sh b/scripts/prune-ci-reports.sh new file mode 100644 index 0000000..167c294 --- /dev/null +++ b/scripts/prune-ci-reports.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Удаляет каталоги отчётов старше CI_REPORTS_KEEP_DAYS (default 14). +# Usage: prune-ci-reports.sh [/var/eventhub/ci-reports] +set -euo pipefail + +ROOT="${1:-${CI_REPORTS_DIR:-/var/eventhub/ci-reports}}" +KEEP_DAYS="${CI_REPORTS_KEEP_DAYS:-14}" + +if [[ ! -d "${ROOT}" ]]; then + echo "ci-reports: ${ROOT} отсутствует — пропуск" + exit 0 +fi + +echo "ci-reports prune: root=${ROOT} keep_days=${KEEP_DAYS}" +find "${ROOT}" -mindepth 2 -maxdepth 2 -type d -mtime "+${KEEP_DAYS}" -print -exec rm -rf {} + 2>/dev/null || true +echo "ci-reports prune: done" diff --git a/scripts/prune-gitea-artifacts.sh b/scripts/prune-gitea-artifacts.sh new file mode 100644 index 0000000..e09cc59 --- /dev/null +++ b/scripts/prune-gitea-artifacts.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Delete Gitea Actions artifacts by exact name (keep newest N). +# Usage: prune-gitea-artifacts.sh [keep_last] +# Env: GITEA_BASE_URL, GITEA_TOKEN (or REGISTRY_PASSWORD) +set -euo pipefail + +REPO="${1:?owner/repo e.g. EventHub/EventHubFront}" +NAME_FILTER="${2:?artifact name}" +KEEP_LAST="${3:-0}" +BASE="${GITEA_BASE_URL:-https://git.sabilin.com}" +TOKEN="${GITEA_TOKEN:-${REGISTRY_PASSWORD:-}}" + +if [[ -z "${TOKEN}" ]]; then + echo "prune-gitea-artifacts: no token — skip" >&2 + exit 0 +fi + +python3 - "${BASE}" "${TOKEN}" "${REPO}" "${NAME_FILTER}" "${KEEP_LAST}" <<'PY' +import json, os, sys, urllib.request, urllib.error + +base, token, repo, name_filter, keep_last = sys.argv[1:6] +keep_last = int(keep_last) +headers = {"Authorization": f"token {token}", "Accept": "application/json"} + +def get(url): + req = urllib.request.Request(url, headers=headers) + with urllib.request.urlopen(req, timeout=60) as resp: + return json.load(resp) + +def delete(url): + req = urllib.request.Request(url, headers=headers, method="DELETE") + try: + with urllib.request.urlopen(req, timeout=60) as resp: + return resp.status + except urllib.error.HTTPError as e: + return e.code + +arts = [] +page = 1 +while page <= 40: + url = f"{base.rstrip('/')}/api/v1/repos/{repo}/actions/artifacts?limit=50&page={page}" + try: + data = get(url) + except Exception as exc: + print(f"prune-gitea-artifacts: list failed: {exc}", file=sys.stderr) + sys.exit(0) + batch = data if isinstance(data, list) else (data.get("artifacts") or data.get("data") or []) + if not batch: + break + for a in batch: + n = str(a.get("name") or "") + if n == name_filter or n.startswith(name_filter): + arts.append(a) + if len(batch) < 50: + break + page += 1 + +# Prefer newest first by id desc +arts.sort(key=lambda a: int(a.get("id") or 0), reverse=True) +to_delete = arts[keep_last:] +print(f"prune-gitea-artifacts: {repo} name={name_filter} matched={len(arts)} delete={len(to_delete)} keep={keep_last}") +for a in to_delete: + aid = a.get("id") + code = delete(f"{base.rstrip('/')}/api/v1/repos/{repo}/actions/artifacts/{aid}") + print(f" delete id={aid} name={a.get('name')} http={code}") +PY diff --git a/scripts/publish-ci-report.sh b/scripts/publish-ci-report.sh new file mode 100644 index 0000000..4039213 --- /dev/null +++ b/scripts/publish-ci-report.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# Publish HTML CI report tree to local runner host and print HTTPS URL. +# Usage: publish-ci-report.sh +# repo: EventHubBack | EventHubFront | … +# run_id: usually GITHUB_RUN_NUMBER or sha +# kind: eunit | ct | allure | e2e-ift | e2e-stage | … +# src_dir: directory with HTML to copy +set -euo pipefail + +REPO="${1:?repo}" +RUN_ID="${2:?run_id}" +KIND="${3:?kind}" +SRC="${4:?src_dir}" + +REPORTS_DIR="${CI_REPORTS_DIR:-/var/eventhub/ci-reports}" +STAND="${RUNNER_STAND:-}" + +detect_stand() { + if [[ -n "${CI_REPORTS_BASE_URL:-}" ]]; then + echo "" + return 0 + fi + if [[ -n "${STAND}" ]]; then + echo "${STAND}" + return 0 + fi + if getent hosts ci-reports.ift.eventhub.local >/dev/null 2>&1 \ + || getent ahostsv4 ci-reports.ift.eventhub.local >/dev/null 2>&1; then + # Prefer ift if this host resolves ift reports (IFT runner DNS) + if [[ -f /opt/eventhub-ift/.env ]] || [[ -d /opt/eventhub-ift ]]; then + echo "ift" + return 0 + fi + fi + if [[ -d /opt/eventhub-ift ]]; then + echo "ift" + return 0 + fi + echo "dev" +} + +if [[ -n "${CI_REPORTS_BASE_URL:-}" ]]; then + BASE_URL="${CI_REPORTS_BASE_URL%/}" +else + STAND="$(detect_stand)" + BASE_URL="https://ci-reports.${STAND}.eventhub.local" +fi + +if [[ ! -d "${SRC}" ]]; then + echo "publish-ci-report: src missing: ${SRC}" >&2 + exit 1 +fi + +DEST="${REPORTS_DIR}/${REPO}/${RUN_ID}/${KIND}" +mkdir -p "${REPORTS_DIR}/${REPO}/${RUN_ID}" +rm -rf "${DEST}" +mkdir -p "${DEST}" +cp -a "${SRC}/." "${DEST}/" + +# Convenience: CT puts index under ct_run.*; expose DEST/index.html +if [[ ! -f "${DEST}/index.html" ]]; then + CT_INDEX="$(find "${DEST}" -type f -name index.html 2>/dev/null | head -n 1 || true)" + if [[ -n "${CT_INDEX}" ]]; then + REL="${CT_INDEX#"${DEST}/"}" + cat > "${DEST}/index.html" < + + + +CT report + +

Open Common Test report

+ +EOF + fi +fi + +# Refresh run index +INDEX="${REPORTS_DIR}/${REPO}/${RUN_ID}/index.html" +{ + echo "" + echo "${REPO} #${RUN_ID}" + echo "" + echo "" + echo "

${REPO} — run ${RUN_ID}

" + echo "
    " + for d in "${REPORTS_DIR}/${REPO}/${RUN_ID}"/*/; do + [[ -d "${d}" ]] || continue + name="$(basename "${d}")" + if [[ -f "${d}index.html" ]]; then + echo "
  • ${name}
  • " + else + echo "
  • ${name}/
  • " + fi + done + echo "
" +} > "${INDEX}" + +URL="${BASE_URL}/${REPO}/${RUN_ID}/${KIND}/" +INDEX_URL="${BASE_URL}/${REPO}/${RUN_ID}/" +echo "Report: ${URL}" +echo "Report index: ${INDEX_URL}" +if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then + { + echo "### Test report" + echo "- [${KIND}](${URL})" + echo "- [index](${INDEX_URL})" + } >> "${GITHUB_STEP_SUMMARY}" +fi