diff --git a/.github/workflows/rustfs-heal-test.yml b/.github/workflows/rustfs-heal-test.yml index 4e6b4e190..a86deb332 100644 --- a/.github/workflows/rustfs-heal-test.yml +++ b/.github/workflows/rustfs-heal-test.yml @@ -280,65 +280,51 @@ jobs: fi rm -f "${B64_FILE}" - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.test.outcome == 'failure' || steps.test.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Replaces the old per-run failure filing. One entry point that: + # - dedups by signal: failing cases are matched against open backlog + # issues by label (category + case ID); covered cases become a + # comment on the existing issue, only uncovered cases file a new one + # - labels new issues (functional-test, category, case IDs, env) + # - closes fixed issues after a fully green run + # - never files or closes on cancelled runs + if: ${{ always() && steps.evidence.outcome == 'success' }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} - EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} SUITE: 'heal' SUITE_LABEL: 'Heal' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Attempt: ${GITHUB_RUN_ATTEMPT}" - echo "- Workflow Commit: ${GITHUB_SHA}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + PACKAGE_URL='${{ inputs.package_url }}' + PACKAGE_SOURCE="" + if [ -n "${PACKAGE_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_URL}" + else + PACKAGE_SOURCE="${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.test.outcome }}" \ + --report-file "${REPORT_FILE}" \ + --log "${LOG_FILE}" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: Upload test logs if: ${{ always() && steps.evidence.outcome == 'success' }} diff --git a/.github/workflows/rustfs-kms-test.yml b/.github/workflows/rustfs-kms-test.yml index 9fec24333..e74f9ddaf 100644 --- a/.github/workflows/rustfs-kms-test.yml +++ b/.github/workflows/rustfs-kms-test.yml @@ -230,65 +230,55 @@ jobs: fi rm -f "${B64_FILE}" - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.test.outcome == 'failure' || steps.test.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Replaces the old per-run failure filing. One entry point that: + # - dedups by signal: failing cases are matched against open backlog + # issues by label (category + case ID); covered cases become a + # comment on the existing issue, only uncovered cases file a new one + # - labels new issues (functional-test, category, case IDs, env) + # - closes fixed issues after a fully green run + # - never files or closes on cancelled runs + if: ${{ always() && steps.evidence.outcome == 'success' }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} - EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} SUITE: 'kms' SUITE_LABEL: 'KMS' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Attempt: ${GITHUB_RUN_ATTEMPT}" - echo "- Workflow Commit: ${GITHUB_SHA}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + PACKAGE_URL='${{ inputs.package_url }}' + RUSTFS_VERSION='${{ inputs.rustfs_version }}' + PACKAGE_SOURCE="" + if [ -n "${PACKAGE_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_URL}" + elif [ -n "${RUSTFS_VERSION}" ]; then + PACKAGE_SOURCE="version ${RUSTFS_VERSION}" + else + PACKAGE_SOURCE="${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.test.outcome }}" \ + --report "${FUNCTIONAL_ARTIFACTS_DIR}/cases.md" \ + --report-file "${REPORT_FILE}" \ + --log "${LOG_FILE}" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: Upload report and logs if: ${{ always() && steps.evidence.outcome == 'success' }} diff --git a/.github/workflows/rustfs-performance-test.yml b/.github/workflows/rustfs-performance-test.yml index 699b583cf..c10690344 100644 --- a/.github/workflows/rustfs-performance-test.yml +++ b/.github/workflows/rustfs-performance-test.yml @@ -237,65 +237,48 @@ jobs: echo "created ${REPORT_PATH} in rustfs/dashboard" fi - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.benchmark.outcome == 'failure' || steps.benchmark.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Signal-based lifecycle: dedups against open issues by label, + # labels new issues (functional-test, category, case IDs, env), + # closes fixed issues after a fully green run. Never acts on + # cancelled runs. Logic lives in auto-testing/scripts/. + if: ${{ always() && steps.evidence.outcome == 'success' }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} - EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} SUITE: 'performance' SUITE_LABEL: 'Performance' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Attempt: ${GITHUB_RUN_ATTEMPT}" - echo "- Workflow Commit: ${GITHUB_SHA}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + PACKAGE_URL='${{ inputs.package_url }}' + PACKAGE_SOURCE="" + if [ -n "${PACKAGE_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_URL}" + else + PACKAGE_SOURCE="${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.benchmark.outcome }}" \ + --report-file "${REPORT_FILE}" \ + --log "${LOG_FILE}" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: Upload test logs & results if: ${{ always() && steps.evidence.outcome == 'success' }} diff --git a/.github/workflows/rustfs-pool-expand-test.yml b/.github/workflows/rustfs-pool-expand-test.yml index c700df7c8..e320402fa 100644 --- a/.github/workflows/rustfs-pool-expand-test.yml +++ b/.github/workflows/rustfs-pool-expand-test.yml @@ -556,64 +556,54 @@ jobs: fi rm -f "${B64_FILE}" - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.pool_test.outcome == 'failure' || steps.pool_test.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Replaces the old per-run failure filing. One entry point that: + # - dedups by signal: failing cases are matched against open backlog + # issues by label (category + case ID); covered cases become a + # comment on the existing issue, only uncovered cases file a new one + # - labels new issues (functional-test, category, case IDs, env) + # - closes fixed issues after a fully green run + # - never files or closes on cancelled runs + if: ${{ always() }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} SUITE: 'pool' SUITE_LABEL: 'Pool expansion' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - REPORT_FILE: '${{ env.POOL_ARTIFACT_DIR }}/pool-report.md' - LOG_FILE: '${{ env.POOL_ARTIFACT_DIR }}/pool-test.log' run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + PACKAGE_URL='${{ inputs.package_url }}' + RUSTFS_VERSION='${{ inputs.rustfs_version }}' + PACKAGE_SOURCE="" + if [ -n "${PACKAGE_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_URL}" + elif [ -n "${RUSTFS_VERSION}" ]; then + PACKAGE_SOURCE="version ${RUSTFS_VERSION}" + else + PACKAGE_SOURCE="${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.pool_test.outcome }}" \ + --report-file "${POOL_ARTIFACT_DIR}/pool-report.md" \ + --log "${POOL_ARTIFACT_DIR}/pool-test.log" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: Upload test logs if: always() diff --git a/.github/workflows/rustfs-replication-test.yml b/.github/workflows/rustfs-replication-test.yml index 969bf60bc..5e8ca4454 100644 --- a/.github/workflows/rustfs-replication-test.yml +++ b/.github/workflows/rustfs-replication-test.yml @@ -238,65 +238,55 @@ jobs: fi rm -f "${B64_FILE}" - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.test.outcome == 'failure' || steps.test.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Replaces the old per-run failure filing. One entry point that: + # - dedups by signal: failing cases are matched against open backlog + # issues by label (category + case ID); covered cases become a + # comment on the existing issue, only uncovered cases file a new one + # - labels new issues (functional-test, category, case IDs, env) + # - closes fixed issues after a fully green run + # - never files or closes on cancelled runs + if: ${{ always() && steps.evidence.outcome == 'success' }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} - EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} SUITE: 'replication' SUITE_LABEL: 'Replication (bucket + site)' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Attempt: ${GITHUB_RUN_ATTEMPT}" - echo "- Workflow Commit: ${GITHUB_SHA}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + PACKAGE_URL='${{ inputs.package_url }}' + RUSTFS_VERSION='${{ inputs.rustfs_version }}' + PACKAGE_SOURCE="" + if [ -n "${PACKAGE_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_URL}" + elif [ -n "${RUSTFS_VERSION}" ]; then + PACKAGE_SOURCE="version ${RUSTFS_VERSION}" + else + PACKAGE_SOURCE="${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.test.outcome }}" \ + --report "${FUNCTIONAL_ARTIFACTS_DIR}/cases.md" \ + --report-file "${REPORT_FILE}" \ + --log "${LOG_FILE}" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: Upload report and logs if: ${{ always() && steps.evidence.outcome == 'success' }} diff --git a/.github/workflows/rustfs-s3-compat-test.yml b/.github/workflows/rustfs-s3-compat-test.yml index 6aad45711..7bdc16de4 100644 --- a/.github/workflows/rustfs-s3-compat-test.yml +++ b/.github/workflows/rustfs-s3-compat-test.yml @@ -207,65 +207,55 @@ jobs: fi rm -f "${B64_FILE}" - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.test.outcome == 'failure' || steps.test.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Replaces the old per-run failure filing. One entry point that: + # - dedups by signal: failing cases are matched against open backlog + # issues by label (category + case ID); covered cases become a + # comment on the existing issue, only uncovered cases file a new one + # - labels new issues (functional-test, category, case IDs, env) + # - closes fixed issues after a fully green run + # - never files or closes on cancelled runs + if: ${{ always() && steps.evidence.outcome == 'success' }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} - EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} SUITE: 's3' SUITE_LABEL: 'S3 compatibility' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Attempt: ${GITHUB_RUN_ATTEMPT}" - echo "- Workflow Commit: ${GITHUB_SHA}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + PACKAGE_URL='${{ inputs.package_url }}' + RUSTFS_VERSION='${{ inputs.rustfs_version }}' + PACKAGE_SOURCE="" + if [ -n "${PACKAGE_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_URL}" + elif [ -n "${RUSTFS_VERSION}" ]; then + PACKAGE_SOURCE="version ${RUSTFS_VERSION}" + else + PACKAGE_SOURCE="${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.test.outcome }}" \ + --report "${FUNCTIONAL_ARTIFACTS_DIR}/cases.md" \ + --report-file "${REPORT_FILE}" \ + --log "${LOG_FILE}" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: Upload report and logs if: ${{ always() && steps.evidence.outcome == 'success' }} diff --git a/.github/workflows/rustfs-security-test.yml b/.github/workflows/rustfs-security-test.yml index 6653705d3..b8926b7a1 100644 --- a/.github/workflows/rustfs-security-test.yml +++ b/.github/workflows/rustfs-security-test.yml @@ -239,65 +239,54 @@ jobs: fi rm -f "${B64_FILE}" - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.test.outcome == 'failure' || steps.test.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Replaces the old per-run failure filing. One entry point that: + # - dedups by signal: failing cases are matched against open backlog + # issues by label (category + case ID); covered cases become a + # comment on the existing issue, only uncovered cases file a new one + # - labels new issues (functional-test, category, case IDs, env) + # - closes fixed issues after a fully green run + # - never files or closes on cancelled runs + if: ${{ always() && steps.evidence.outcome == 'success' }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} SUITE: 'security' SUITE_LABEL: 'Security' - EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - REPORT_FILE: ${{ env.SECURITY_ARTIFACTS_DIR }}/report.md - LOG_FILE: '' run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + PACKAGE_URL='${{ inputs.package_url }}' + RUSTFS_VERSION='${{ inputs.rustfs_version }}' + PACKAGE_SOURCE="" + if [ -n "${PACKAGE_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_URL}" + elif [ -n "${RUSTFS_VERSION}" ]; then + PACKAGE_SOURCE="version ${RUSTFS_VERSION}" + else + PACKAGE_SOURCE="${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.test.outcome }}" \ + --report-file "${SECURITY_ARTIFACTS_DIR}/report.md" \ + --log "${SECURITY_ARTIFACTS_DIR}/suite.log" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: Upload report and logs if: ${{ always() && steps.evidence.outcome == 'success' }} diff --git a/.github/workflows/rustfs-storage-test.yml b/.github/workflows/rustfs-storage-test.yml index 88ff53fe1..3651fe486 100644 --- a/.github/workflows/rustfs-storage-test.yml +++ b/.github/workflows/rustfs-storage-test.yml @@ -222,65 +222,55 @@ jobs: fi rm -f "${B64_FILE}" - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.test.outcome == 'failure' || steps.test.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Replaces the old per-run failure filing. One entry point that: + # - dedups by signal: failing cases are matched against open backlog + # issues by label (category + case ID); covered cases become a + # comment on the existing issue, only uncovered cases file a new one + # - labels new issues (functional-test, category, case IDs, env) + # - closes fixed issues after a fully green run + # - never files or closes on cancelled runs + if: ${{ always() && steps.evidence.outcome == 'success' }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} - EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} SUITE: 'storage' SUITE_LABEL: 'Storage engine' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Attempt: ${GITHUB_RUN_ATTEMPT}" - echo "- Workflow Commit: ${GITHUB_SHA}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + PACKAGE_URL='${{ inputs.package_url }}' + RUSTFS_VERSION='${{ inputs.rustfs_version }}' + PACKAGE_SOURCE="" + if [ -n "${PACKAGE_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_URL}" + elif [ -n "${RUSTFS_VERSION}" ]; then + PACKAGE_SOURCE="version ${RUSTFS_VERSION}" + else + PACKAGE_SOURCE="${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.test.outcome }}" \ + --report "${FUNCTIONAL_ARTIFACTS_DIR}/cases.md" \ + --report-file "${REPORT_FILE}" \ + --log "${LOG_FILE}" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: Upload report and logs if: ${{ always() && steps.evidence.outcome == 'success' }} diff --git a/.github/workflows/rustfs-tier-test.yml b/.github/workflows/rustfs-tier-test.yml index 0bbdd3fc7..4314331fa 100644 --- a/.github/workflows/rustfs-tier-test.yml +++ b/.github/workflows/rustfs-tier-test.yml @@ -494,75 +494,55 @@ jobs: fi [ "${failed}" -eq 0 ] - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.test.outcome == 'failure' || steps.test.outcome == 'cancelled' || steps.evidence_verify.outcome == 'failure' || steps.evidence_verify.outcome == 'cancelled' || steps.gate.outcome == 'failure' || steps.gate.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Replaces the old per-run failure filing. One entry point that: + # - dedups by signal: failing cases are matched against open backlog + # issues by label (category + case ID); covered cases become a + # comment on the existing issue, only uncovered cases file a new one + # - labels new issues (functional-test, category, case IDs, env) + # - closes fixed issues after a fully green run + # - never files or closes on cancelled runs + if: ${{ always() && steps.evidence.outcome == 'success' }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} SUITE: 'tier' SUITE_LABEL: 'Tier' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - EVIDENCE_DIR: ${{ env.TIER_ARTIFACTS_DIR }} - EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} - VERIFY_OUTCOME: ${{ steps.evidence_verify.outcome }} - GATE_OUTCOME: ${{ steps.gate.outcome }} - REPORT_FILE: ${{ env.TIER_ARTIFACTS_DIR }}/rustfs-tier-report.md - LOG_FILE: ${{ env.TIER_ARTIFACTS_DIR }}/rustfs-tier.log run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "- Evidence initialization: ${EVIDENCE_OUTCOME}" - echo "- Evidence verification: ${VERIFY_OUTCOME}" - echo "- Final gate: ${GATE_OUTCOME}" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ "${EVIDENCE_OUTCOME}" != "success" ]; then - echo "(the run evidence directory was rejected; its contents were not read)" - elif [ ! -d "${EVIDENCE_DIR}" ] || [ -L "${EVIDENCE_DIR}" ]; then - echo "(the run evidence directory is missing or unsafe; its contents were not read)" - elif [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + PACKAGE_URL='${{ inputs.package_url }}' + RUSTFS_VERSION='${{ inputs.rustfs_version }}' + PACKAGE_SOURCE="" + if [ -n "${PACKAGE_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_URL}" + elif [ -n "${RUSTFS_VERSION}" ]; then + PACKAGE_SOURCE="version ${RUSTFS_VERSION}" + else + PACKAGE_SOURCE="${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.test.outcome }}" \ + --report "${TIER_ARTIFACTS_DIR}/rustfs-tier-cases.md" \ + --report-file "${TIER_ARTIFACTS_DIR}/rustfs-tier-report.md" \ + --log "${TIER_ARTIFACTS_DIR}/rustfs-tier.log" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: "Continue functional chain (next: Storage engine)" # Only chain-triggered runs forward to the next suite; standalone diff --git a/.github/workflows/rustfs-upgrade-test.yml b/.github/workflows/rustfs-upgrade-test.yml index 5a91793a0..bad5c1119 100644 --- a/.github/workflows/rustfs-upgrade-test.yml +++ b/.github/workflows/rustfs-upgrade-test.yml @@ -306,65 +306,62 @@ jobs: fi rm -f "${B64_FILE}" - - name: File failure issue in rustfs/backlog - if: ${{ always() && (failure() || steps.test.outcome == 'failure' || steps.test.outcome == 'cancelled') }} + - name: Manage backlog issues (dedup / label / auto-close) + # Replaces the old per-run failure filing. One entry point that: + # - dedups by signal: failing cases are matched against open backlog + # issues by label (category + case ID); covered cases become a + # comment on the existing issue, only uncovered cases file a new one + # - labels new issues (functional-test, category, case IDs, env) + # - closes fixed issues after a fully green run + # - never files or closes on cancelled runs + if: ${{ always() && steps.evidence.outcome == 'success' }} continue-on-error: true env: GH_TOKEN: ${{ secrets.PF_TESTING_GH_TOKEN }} - EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} SUITE: 'upgrade' SUITE_LABEL: 'Upgrade compatibility' RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then - echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue" + echo "PF_TESTING_GH_TOKEN is not configured; skipping backlog issue management" exit 0 fi - TITLE="[functional][${SUITE}] ${SUITE_LABEL} suite failed (run ${GITHUB_RUN_ID})" - EXISTING="$(gh issue list -R rustfs/backlog --state all \ - --search "in:title \"run ${GITHUB_RUN_ID}\"" \ - --json number --jq '.[].number' || true)" - if [ -n "${EXISTING}" ]; then - echo "backlog issue already exists for run ${GITHUB_RUN_ID}; skipping" + if [ ! -f auto-testing/scripts/issue_manager.py ]; then + echo "issue_manager.py not found in auto-testing checkout; skipping" exit 0 fi - redact() { - sed -E \ - -e 's/(RUSTFS_(ACCESS_KEY|SECRET_KEY)[=: ]+)[^[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/(Authorization:).*/\1 [REDACTED]/Ig' \ - -e 's/(X-Amz-Signature=)[^&[:space:]]+/\1[REDACTED]/Ig' \ - -e 's/^.*(password|secret|token)[=: ].*/[REDACTED SENSITIVE LINE]/Ig' - } - BODY_FILE="$(mktemp)" - { - echo "The **${SUITE_LABEL}** functional suite failed." - echo "" - echo "- Suite: \`${SUITE}\`" - echo "- Run: ${RUN_URL}" - echo "- Attempt: ${GITHUB_RUN_ATTEMPT}" - echo "- Workflow Commit: ${GITHUB_SHA}" - echo "- Trigger: ${GITHUB_EVENT_NAME}" - echo "- Date: $(date -u +%Y-%m-%d)" - echo "" - echo "## Report (errors and symptoms)" - echo "" - if [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${REPORT_FILE}" ]; then - redact < "${REPORT_FILE}" - elif [ "${EVIDENCE_OUTCOME}" = "success" ] && [ -s "${LOG_FILE:-}" ]; then - echo "(report file missing; log tail below)" - echo "" - tail -n 200 "${LOG_FILE}" | redact - else - echo "(no report or log file was produced)" - fi - } | head -c 55000 > "${BODY_FILE}" - gh label create functional-test -R rustfs/backlog --color d73a4a 2>/dev/null || true - if ! gh issue create -R rustfs/backlog --title "${TITLE}" \ - --body-file "${BODY_FILE}" --label functional-test; then - gh issue create -R rustfs/backlog --title "${TITLE}" --body-file "${BODY_FILE}" + FROM_URL='${{ inputs.from_url }}' + FROM_VERSION='${{ inputs.from_version }}' + TO_URL='${{ inputs.to_url }}' + TO_VERSION='${{ inputs.to_version }}' + PACKAGE_SOURCE="" + if [ -n "${TO_URL}" ]; then + PACKAGE_SOURCE="to ${TO_URL}" + elif [ -n "${TO_VERSION}" ]; then + PACKAGE_SOURCE="to version ${TO_VERSION}" + else + PACKAGE_SOURCE="to ${RUSTFS_NIGHTLY_PACKAGE_URL}" fi - echo "filed backlog issue for suite ${SUITE}" + if [ -n "${FROM_URL}" ]; then + PACKAGE_SOURCE="${PACKAGE_SOURCE}; from ${FROM_URL}" + elif [ -n "${FROM_VERSION}" ]; then + PACKAGE_SOURCE="${PACKAGE_SOURCE}; from version ${FROM_VERSION}" + fi + python3 auto-testing/scripts/issue_manager.py handle \ + --repo rustfs/backlog \ + --suite "${SUITE}" --category "${SUITE}" --suite-label "${SUITE_LABEL}" \ + --outcome "${{ steps.test.outcome }}" \ + --report "${FUNCTIONAL_ARTIFACTS_DIR}/cases.md" \ + --report-file "${REPORT_FILE}" \ + --log "${LOG_FILE}" \ + --run-url "${RUN_URL}" \ + --run-id "${GITHUB_RUN_ID}" \ + --attempt "${GITHUB_RUN_ATTEMPT}" \ + --commit "${GITHUB_SHA}" \ + --trigger "${{ github.event_name }}" \ + --package-source "${PACKAGE_SOURCE}" \ + --date "$(date -u +%Y-%m-%d)" - name: Upload report and logs if: ${{ always() && steps.evidence.outcome == 'success' }} diff --git a/scripts/test_security_workflow.py b/scripts/test_security_workflow.py index 7f3acce46..340033605 100644 --- a/scripts/test_security_workflow.py +++ b/scripts/test_security_workflow.py @@ -140,9 +140,9 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase): self.assertNotIn(" continue-on-error: true", self.steps[name]) self.assertIn(" if: ${{ always() && steps.evidence.outcome == 'success' }}", self.steps["Generate report"]) self.assertNotIn("/tmp/rustfs-security", self.source) - for name in ("Upload functional report to dashboard", "File failure issue in rustfs/backlog"): - report = next(line for line in self.steps[name] if line.strip().startswith("REPORT_FILE:")) - self.assertIn("${{ env.SECURITY_ARTIFACTS_DIR }}/report.md", report) + for name in ("Upload functional report to dashboard", "Manage backlog issues (dedup / label / auto-close)"): + expected = "${{ env.SECURITY_ARTIFACTS_DIR }}/report.md" if name.startswith("Upload") else '--report-file "${SECURITY_ARTIFACTS_DIR}/report.md"' + self.assertIn(expected, "\n".join(self.steps[name])) for name in ("Upload functional report to dashboard", "Upload report and logs"): self.assertIn(" if: ${{ always() && steps.evidence.outcome == 'success' }}", self.steps[name]) artifact_settings = yaml_block(self.steps["Upload report and logs"], "with", 8) @@ -268,10 +268,12 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase): gh.chmod(0o755) body = self.directory / "issue-body.md" self.env.update(PATH=f"{fake_bin}{os.pathsep}{os.environ['PATH']}", CAPTURE_BODY=str(body)) - result = self.run_step("File failure issue in rustfs/backlog") + result = self.run_step("Manage backlog issues (dedup / label / auto-close)") self.assertEqual(result.returncode, 0, result.stderr) - self.assertNotIn("OLD RUN REPORT", body.read_text()) - self.assertIn("https://github.com/rustfs/rustfs/actions/runs/314159", body.read_text()) + # The manager lives in the private auto-testing checkout; without it + # the step must skip without publishing anything. + self.assertIn("issue_manager.py not found", result.stdout + result.stderr) + self.assertFalse(body.exists()) def test_all_ten_suites_hold_the_shared_lock_for_manual_and_chain_runs(self) -> None: for suite in ("upgrade", "s3-compat", "kms", "tier", "storage", "heal", "pool-expand", "security", "replication", "performance"): @@ -587,11 +589,10 @@ class FunctionalEvidenceTests(WorkflowSteps, unittest.TestCase): initialized = self.run_step("Initialize functional evidence") self.assertNotEqual(initialized.returncode, 0) self.assertFalse(Path(self.env["GITHUB_ENV"]).exists()) - issue = self.run_step("File failure issue in rustfs/backlog") - self.assertEqual(issue.returncode, 0, issue.stderr) - body = Path(self.env["CAPTURE_BODY"]).read_text() - self.assertNotIn("OLD RUN EVIDENCE", body) - self.assertIn("no report or log file was produced", body) + manager = self.run_step("Manage backlog issues (dedup / label / auto-close)") + self.assertEqual(manager.returncode, 0, manager.stderr) + self.assertIn("issue_manager.py not found", manager.stdout + manager.stderr) + self.assertFalse(Path(self.env["CAPTURE_BODY"]).exists()) self.assertEqual((existing / "report.md").read_text(), "OLD RUN EVIDENCE") def test_reports_use_only_current_complete_suite_evidence(self):