From bfaed623bb5b02b553a944bdb1604f1291dd4b5b Mon Sep 17 00:00:00 2001 From: strtgbb <146047128+strtgbb@users.noreply.github.com> Date: Mon, 9 Jun 2025 13:00:26 -0400 Subject: [PATCH 1/5] use report action from main branch --- .../actions/create_workflow_report/action.yml | 41 ++ .../ci_run_report.html.jinja | 269 ++++++++++ .../create_workflow_report.py | 478 +++++++++--------- .github/workflows/release_branches.yml | 23 +- 4 files changed, 540 insertions(+), 271 deletions(-) create mode 100644 .github/actions/create_workflow_report/action.yml create mode 100644 .github/actions/create_workflow_report/ci_run_report.html.jinja rename .github/{ => actions/create_workflow_report}/create_workflow_report.py (54%) diff --git a/.github/actions/create_workflow_report/action.yml b/.github/actions/create_workflow_report/action.yml new file mode 100644 index 000000000000..fde62d01e29d --- /dev/null +++ b/.github/actions/create_workflow_report/action.yml @@ -0,0 +1,41 @@ +name: Create and Upload Combined Report +description: Create and upload a combined CI report +inputs: + final: + description: "Control whether the report is final or a preview" + required: false + default: "false" +runs: + using: "composite" + steps: + - name: Create and upload workflow report + env: + PR_NUMBER: ${{ github.event.pull_request.number || 0 }} + COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + ACTIONS_RUN_URL: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }} + FINAL: ${{ inputs.final }} + shell: bash + run: | + pip install clickhouse-driver==0.2.8 numpy==1.26.4 pandas==2.0.3 jinja2==3.1.5 + + CMD="python3 .github/actions/create_workflow_report/create_workflow_report.py" + ARGS="--pr-number $PR_NUMBER --commit-sha $COMMIT_SHA --actions-run-url $ACTIONS_RUN_URL --known-fails tests/broken_tests.json --cves" + + set +e + if [[ "$FINAL" == "false" ]]; then + REPORT_LINK=$($CMD $ARGS --mark-preview) + else + REPORT_LINK=$($CMD $ARGS) + fi + + echo $REPORT_LINK + + if [[ "$FINAL" == "true" ]]; then + IS_VALID_URL=$(echo $REPORT_LINK | grep -E '^https?://') + if [[ -n $IS_VALID_URL ]]; then + echo "Workflow Run Report: [View Report]($REPORT_LINK)" >> $GITHUB_STEP_SUMMARY + else + echo "Error: $REPORT_LINK" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + fi diff --git a/.github/actions/create_workflow_report/ci_run_report.html.jinja b/.github/actions/create_workflow_report/ci_run_report.html.jinja new file mode 100644 index 000000000000..a92c1aa34e3a --- /dev/null +++ b/.github/actions/create_workflow_report/ci_run_report.html.jinja @@ -0,0 +1,269 @@ + + + +
+ + + + +| Pull Request | +{{ pr_info_html }} | +
|---|---|
| Workflow Run | +{{ workflow_id }} | +
| Commit | +{{ commit_sha }} | +
| Build Report | +Build Report | +
| Date | +{{ date }} | +
This is a preview. The workflow is not yet finished.
+ {% endif %} +Compared with base sha {{ base_sha }}
+ {{ new_fails_html }} + {%- endif %} + +
+ Fail reason conventions:
+ KNOWN - Accepted fail and fix is not planned
+ INVESTIGATE - We don't know why it fails
+ NEEDSFIX - Investigation done and a fix is needed to make it pass
+
| Pull Request | {pr_info_html} | -
|---|---|
| Workflow Run | {args.actions_run_url.split('/')[-1]} | -
| Commit | {args.commit_sha} | -
| Date | {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC | -
This is a preview. FinishCheck has not completed.
' if args.mark_preview else ""} -Not Checked
" if not args.cves else format_results_as_html_table(fail_results['docker_images_cves'])} - -Not Checked
" if not args.known_fails else format_results_as_html_table(fail_results['checks_known_fails'])} - -{script} - - -""" + # Define the context for rendering + context = { + "title": "ClickHouse® CI Workflow Run Report", + "github_repo": GITHUB_REPO, + "s3_bucket": S3_BUCKET, + "pr_info_html": pr_info_html, + "pr_number": args.pr_number, + "workflow_id": args.actions_run_url.split("/")[-1], + "commit_sha": args.commit_sha, + "base_sha": "" if args.pr_number == 0 else pr_info.get("base", {}).get("sha"), + "date": f"{datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC", + "is_preview": args.mark_preview, + "counts": { + "jobs_status": f"{sum(fail_results['job_statuses']['job_status'] != 'success')} fail/error", + "checks_errors": len(fail_results["checks_errors"]), + "checks_new_fails": len(fail_results["checks_fails"]), + "regression_new_fails": len(fail_results["regression_fails"]), + "cves": "N/A" if cves_not_checked else f"{high_cve_count} high/critical", + "checks_known_fails": ( + "N/A" + if not args.known_fails + else len(fail_results["checks_known_fails"]) + ), + "pr_new_fails": len(fail_results["pr_new_fails"]), + }, + "ci_jobs_status_html": format_results_as_html_table( + fail_results["job_statuses"] + ), + "checks_errors_html": format_results_as_html_table( + fail_results["checks_errors"] + ), + "checks_fails_html": format_results_as_html_table(fail_results["checks_fails"]), + "regression_fails_html": format_results_as_html_table( + fail_results["regression_fails"] + ), + "docker_images_cves_html": ( + "Not Checked
" + if cves_not_checked + else format_results_as_html_table(fail_results["docker_images_cves"]) + ), + "checks_known_fails_html": ( + "Not Checked
" + if not args.known_fails + else format_results_as_html_table(fail_results["checks_known_fails"]) + ), + "new_fails_html": format_results_as_html_table(fail_results["pr_new_fails"]), + } + + # Render the template with the context + rendered_html = template.render(context) + report_name = "ci_run_report.html" report_path = Path(report_name) - report_path.write_text(html_report, encoding="utf-8") + report_path.write_text(rendered_html, encoding="utf-8") if args.no_upload: print(f"Report saved to {report_path}") @@ -678,7 +650,7 @@ def main(): s3_client.put_object( Bucket=S3_BUCKET, Key=report_destination_key, - Body=html_report, + Body=rendered_html, ContentType="text/html; charset=utf-8", ) except NoCredentialsError: diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index 4fabdb872470..014412317cbe 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -569,25 +569,12 @@ jobs: run: | cd "$GITHUB_WORKSPACE/tests/ci" python3 finish_check.py - - name: Create and upload combined report + - name: Create and upload report + uses: ./.github/actions/create_workflow_report@b229c9e6f9d1e100ba32f271b6bba112d9894afb env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CHECKS_DATABASE_HOST: ${{ secrets.CHECKS_DATABASE_HOST }} CHECKS_DATABASE_USER: ${{ secrets.CLICKHOUSE_TEST_STAT_LOGIN }} CHECKS_DATABASE_PASSWORD: ${{ secrets.CLICKHOUSE_TEST_STAT_PASSWORD }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - PR_NUMBER: ${{ github.event.pull_request.number || 0 }} - ACTIONS_RUN_URL: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }} - shell: bash - run: | - pip install clickhouse-driver==0.2.8 numpy==1.26.4 pandas==2.0.3 - - REPORT_LINK=$(python3 .github/create_workflow_report.py --pr-number $PR_NUMBER --commit-sha $COMMIT_SHA --actions-run-url $ACTIONS_RUN_URL --known-fails tests/broken_tests.json --cves) - - IS_VALID_URL=$(echo $REPORT_LINK | grep -E '^https?://') - if [[ -n $IS_VALID_URL ]]; then - echo "Combined CI Report: [View Report]($REPORT_LINK)" >> $GITHUB_STEP_SUMMARY - else - echo "Error: $REPORT_LINK" >> $GITHUB_STEP_SUMMARY - exit 1 - fi + with: + final: true From 56b50aa8afc0b003a5fb4918b576c3189d37783c Mon Sep 17 00:00:00 2001 From: strtgbb <146047128+strtgbb@users.noreply.github.com> Date: Tue, 10 Jun 2025 12:16:57 -0400 Subject: [PATCH 2/5] Unfortunately, composite actions cannot be pinned to a ref --- .github/workflows/release_branches.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index 014412317cbe..953695ebc591 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -570,7 +570,7 @@ jobs: cd "$GITHUB_WORKSPACE/tests/ci" python3 finish_check.py - name: Create and upload report - uses: ./.github/actions/create_workflow_report@b229c9e6f9d1e100ba32f271b6bba112d9894afb + uses: ./.github/actions/create_workflow_report env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CHECKS_DATABASE_HOST: ${{ secrets.CHECKS_DATABASE_HOST }} From a1da223e34750aa3f03b5e638788eb3f24df483c Mon Sep 17 00:00:00 2001 From: strtgbb <146047128+strtgbb@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:06:12 -0400 Subject: [PATCH 3/5] support live updating report --- .github/workflows/release_branches.yml | 8 ++++++++ .github/workflows/reusable_test.yml | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index 953695ebc591..26d3705f2d52 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -60,6 +60,14 @@ jobs: - name: Re-create GH statuses for skipped jobs if any run: | python3 "$GITHUB_WORKSPACE/tests/ci/ci.py" --infile ${{ runner.temp }}/ci_run_data.json --update-gh-statuses + - name: Note report location to summary + env: + PR_NUMBER: ${{ github.event.pull_request.number || 0 }} + COMMIT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} + run: | + REPORT_LINK=https://s3.amazonaws.com/altinity-build-artifacts/$PR_NUMBER/$COMMIT_SHA/ci_run_report.html + echo "Workflow Run Report: [View Report]($REPORT_LINK)" >> $GITHUB_STEP_SUMMARY + BuildDockers: needs: [RunConfig] if: ${{ !failure() && !cancelled() }} diff --git a/.github/workflows/reusable_test.yml b/.github/workflows/reusable_test.yml index 38a6794f555f..82fb748b5706 100644 --- a/.github/workflows/reusable_test.yml +++ b/.github/workflows/reusable_test.yml @@ -159,6 +159,16 @@ jobs: if: ${{ !cancelled() }} run: | python3 "$GITHUB_WORKSPACE/tests/ci/ci.py" --infile ${{ toJson(inputs.data) }} --post --job-name '${{inputs.test_name}}' + - name: Update workflow report + if: ${{ !cancelled() }} + uses: ./.github/actions/create_workflow_report + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CHECKS_DATABASE_HOST: ${{ secrets.CHECKS_DATABASE_HOST }} + CHECKS_DATABASE_USER: ${{ secrets.CLICKHOUSE_TEST_STAT_LOGIN }} + CHECKS_DATABASE_PASSWORD: ${{ secrets.CLICKHOUSE_TEST_STAT_PASSWORD }} + with: + final: false - name: Mark as done if: ${{ !cancelled() }} run: | From 50205d8473550df4de16b3e06e395d2d0d13bf80 Mon Sep 17 00:00:00 2001 From: strtgbb <146047128+strtgbb@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:11:25 -0400 Subject: [PATCH 4/5] smarter pr number fetching for grype and report --- .github/workflows/grype_scan.yml | 26 +++++++++++++++++++++++--- .github/workflows/release_branches.yml | 4 +++- tests/ci/version_helper.py | 8 ++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/.github/workflows/grype_scan.yml b/.github/workflows/grype_scan.yml index 68c128e4e470..e68c3e63e283 100644 --- a/.github/workflows/grype_scan.yml +++ b/.github/workflows/grype_scan.yml @@ -15,6 +15,16 @@ on: description: 'Docker image. If no tag, it will be determined by version_helper.py' required: true type: string + version: + description: 'Version tag. If no version, it will be determined by version_helper.py' + required: false + type: string + default: "" + tag-suffix: + description: 'Tag suffix. To be appended the version from version_helper.py' + required: false + type: string + default: "" env: PYTHONUNBUFFERED: 1 AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -40,17 +50,25 @@ jobs: sudo apt-get install -y python3-pip python3-venv python3 -m venv venv source venv/bin/activate - pip install --upgrade requests chardet urllib3 + pip install --upgrade requests chardet urllib3 unidiff boto3 PyGithub pip install testflows==$TESTFLOWS_VERSION awscli==1.33.28 echo PATH=$PATH >>$GITHUB_ENV - name: Set image tag if not given if: ${{ !contains(inputs.docker_image, ':') }} id: set_version + env: + TAG_SUFFIX: ${{ inputs.tag-suffix }} + SPECIFIED_VERSION: ${{ inputs.version }} run: | python3 ./tests/ci/version_helper.py | tee /tmp/version_info source /tmp/version_info - echo "docker_image=${{ inputs.docker_image }}:${{ github.event.pull_request.number || 0 }}-$CLICKHOUSE_VERSION_STRING" >> $GITHUB_OUTPUT + if [ -z "$SPECIFIED_VERSION" ]; then + VERSION=$CLICKHOUSE_VERSION_STRING + else + VERSION=$SPECIFIED_VERSION + fi + echo "docker_image=${{ inputs.docker_image }}:$PR_NUMBER-$VERSION$TAG_SUFFIX" >> $GITHUB_OUTPUT echo "commit_sha=$CLICKHOUSE_VERSION_GITHASH" >> $GITHUB_OUTPUT - name: Run Grype Scan @@ -68,15 +86,17 @@ jobs: env: S3_BUCKET: "altinity-build-artifacts" COMMIT_SHA: ${{ steps.set_version.outputs.commit_sha || github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - PR_NUMBER: ${{ github.event.pull_request.number || 0 }} + PR_NUMBER: ${{ env.PR_NUMBER || github.event.pull_request.number || 0 }} DOCKER_IMAGE: ${{ steps.set_version.outputs.docker_image || inputs.docker_image }} run: | + echo "PR_NUMBER=$PR_NUMBER" ./.github/grype/transform_and_upload_results_s3.sh - name: Create step summary if: always() id: create_summary run: | + jq -r '"**Image**: \(.source.target.userInput)"' result.json >> $GITHUB_STEP_SUMMARY jq -r '.distro | "**Distro**: \(.name):\(.version)"' result.json >> $GITHUB_STEP_SUMMARY if jq -e '.matches | length == 0' result.json > /dev/null; then echo "No CVEs" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index 26d3705f2d52..2e4f27b303eb 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -195,7 +195,9 @@ jobs: uses: ./.github/workflows/grype_scan.yml secrets: inherit with: - docker_image: altinityinfra/clickhouse-${{ matrix.image }}:${{ github.event.pull_request.number || 0 }}-${{ fromJson(needs.RunConfig.outputs.data).version }}${{ matrix.suffix }} + docker_image: altinityinfra/clickhouse-${{ matrix.image }} + version: ${{ fromJson(needs.RunConfig.outputs.data).version }} + tag-suffix: ${{ matrix.suffix }} ############################################################################################ ##################################### BUILD REPORTER ####################################### ############################################################################################ diff --git a/tests/ci/version_helper.py b/tests/ci/version_helper.py index d1d867156df9..5007c1b2f4a8 100755 --- a/tests/ci/version_helper.py +++ b/tests/ci/version_helper.py @@ -4,6 +4,8 @@ from pathlib import Path from typing import Any, Dict, Iterable, List, Literal, Optional, Set, Tuple, Union +from pr_info import PRInfo # grype scan needs to know the PR number + from git_helper import TWEAK, Git, get_tags, git_runner, removeprefix, VersionType FILE_WITH_VERSION_PATH = "cmake/autogenerated_versions.txt" @@ -511,6 +513,12 @@ def main(): if args.update_part or args.update_cmake: update_cmake_version(version) + # grype scan needs to know the PR number + pr_info = PRInfo() + print(f"PR_NUMBER={pr_info.number}") + if args.export: + print(f"export PR_NUMBER") + for k, v in version.as_dict().items(): name = f"CLICKHOUSE_VERSION_{k.upper()}" print(f"{name}='{v}'") From 5b6e2b39cf5f8d3e840a381532154324b9ad2be4 Mon Sep 17 00:00:00 2001 From: strtgbb <146047128+strtgbb@users.noreply.github.com> Date: Wed, 11 Jun 2025 13:49:19 -0400 Subject: [PATCH 5/5] small fixes --- .../create_workflow_report/create_workflow_report.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/actions/create_workflow_report/create_workflow_report.py b/.github/actions/create_workflow_report/create_workflow_report.py index eca0d9dac7b6..f10b6b8179d6 100755 --- a/.github/actions/create_workflow_report/create_workflow_report.py +++ b/.github/actions/create_workflow_report/create_workflow_report.py @@ -447,7 +447,7 @@ def format_test_status(text: str) -> str: color = ( "red" if text.lower().startswith("fail") - else "orange" if text.lower() in ("error", "broken") else "green" + else "orange" if text.lower() in ("error", "broken", "pending") else "green" ) return f'{text}' @@ -537,9 +537,7 @@ def main(): # get_cves returns ... in the case where no Grype result files were found. # This might occur when run in preview mode. - cves_not_checked = not args.cves or ( - args.mark_preview and fail_results["docker_images_cves"] is ... - ) + cves_not_checked = not args.cves or fail_results["docker_images_cves"] is ... if args.known_fails: if not os.path.exists(args.known_fails): @@ -595,7 +593,7 @@ def main(): "date": f"{datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC", "is_preview": args.mark_preview, "counts": { - "jobs_status": f"{sum(fail_results['job_statuses']['job_status'] != 'success')} fail/error", + "jobs_status": f"{sum(fail_results['job_statuses']['job_status'] != 'success')} fail/error/pending", "checks_errors": len(fail_results["checks_errors"]), "checks_new_fails": len(fail_results["checks_fails"]), "regression_new_fails": len(fail_results["regression_fails"]),