This is an automated email from the ASF dual-hosted git repository.
imbajin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hugegraph.git
The following commit(s) were added to refs/heads/master by this push:
new 0951d457c fix(ci): modernize codecov uploads (#3169)
0951d457c is described below
commit 0951d457ce80f1f807b34a0838bade05969f9c37
Author: contrueCT <[email protected]>
AuthorDate: Tue Aug 25 02:43:30 2026 +0800
fix(ci): modernize codecov uploads (#3169)
- accept quoted action refs and inline comments
- bind report paths to workflow job IDs
- allow harmless job declaration reordering
---------
Co-authored-by: imbajin <[email protected]>
---
.github/workflows/commons-ci.yml | 7 +-
.github/workflows/pd-store-ci.yml | 20 +-
.github/workflows/server-ci.yml | 7 +-
.../assembly/travis/test-codecov-upload-config.sh | 212 +++++++++++++++++++++
4 files changed, 237 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/commons-ci.yml b/.github/workflows/commons-ci.yml
index 5311ebeee..a03aaf09f 100644
--- a/.github/workflows/commons-ci.yml
+++ b/.github/workflows/commons-ci.yml
@@ -58,6 +58,9 @@ jobs:
mvn test -pl hugegraph-commons/hugegraph-rpc -Dtest=UnitTestSuite
-DskipCommonsTests=false
- name: Upload coverage to Codecov
- uses: codecov/[email protected]
+ uses: codecov/codecov-action@v5
with:
- file: target/jacoco.xml
+ token: ${{ secrets.CODECOV_TOKEN }}
+ files: hugegraph-commons/target/jacoco.xml
+ disable_search: true
+ fail_ci_if_error: false
diff --git a/.github/workflows/pd-store-ci.yml
b/.github/workflows/pd-store-ci.yml
index 2ad45bd64..d20f4ccb4 100644
--- a/.github/workflows/pd-store-ci.yml
+++ b/.github/workflows/pd-store-ci.yml
@@ -36,6 +36,9 @@ jobs:
- name: Run JaCoCo report validator tests
run:
hugegraph-server/hugegraph-dist/src/assembly/travis/test-check-jacoco-report.sh
+ - name: Run Codecov upload configuration tests
+ run:
hugegraph-server/hugegraph-dist/src/assembly/travis/test-codecov-upload-config.sh
+
- name: Use staged maven repo settings
run: |
cp $HOME/.m2/settings.xml /tmp/settings.xml || true
@@ -184,9 +187,12 @@ jobs:
hg-pd-grpc hg-pd-common hg-pd-client hg-pd-core hg-pd-service
hg-pd-dist
- name: Upload coverage to Codecov
- uses: codecov/[email protected]
+ uses: codecov/codecov-action@v5
with:
+ token: ${{ secrets.CODECOV_TOKEN }}
files: ${{ env.REPORT_FILE }}
+ disable_search: true
+ fail_ci_if_error: false
store:
needs: struct
@@ -316,9 +322,12 @@ jobs:
hg-store-grpc hg-store-common hg-store-client hg-store-rocksdb
- name: Upload coverage to Codecov
- uses: codecov/[email protected]
+ uses: codecov/codecov-action@v5
with:
+ token: ${{ secrets.CODECOV_TOKEN }}
files: ${{ env.REPORT_FILE }}
+ disable_search: true
+ fail_ci_if_error: false
hstore:
needs: struct
@@ -387,6 +396,9 @@ jobs:
$TRAVIS_DIR/run-tinkerpop-test.sh $BACKEND tinkerpop
- name: Upload coverage to Codecov
- uses: codecov/[email protected]
+ uses: codecov/codecov-action@v5
with:
- file: ${{ env.REPORT_DIR }}/*.xml
+ token: ${{ secrets.CODECOV_TOKEN }}
+ files: ${{ env.REPORT_DIR }}/*.xml
+ disable_search: true
+ fail_ci_if_error: false
diff --git a/.github/workflows/server-ci.yml b/.github/workflows/server-ci.yml
index 9c4e577d8..3c631fd38 100644
--- a/.github/workflows/server-ci.yml
+++ b/.github/workflows/server-ci.yml
@@ -216,11 +216,12 @@ jobs:
$TRAVIS_DIR/run-tinkerpop-test.sh $BACKEND tinkerpop
- name: Upload coverage to Codecov
- # TODO: update to v5 later
- uses: codecov/codecov-action@v3
+ uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
- file: ${{ env.REPORT_DIR }}/*.xml
+ files: ${{ env.REPORT_DIR }}/*.xml
+ disable_search: true
+ fail_ci_if_error: false
build-server-macos-rocksdb:
runs-on: ${{ matrix.os }}
diff --git
a/hugegraph-server/hugegraph-dist/src/assembly/travis/test-codecov-upload-config.sh
b/hugegraph-server/hugegraph-dist/src/assembly/travis/test-codecov-upload-config.sh
new file mode 100755
index 000000000..505b0cb36
--- /dev/null
+++
b/hugegraph-server/hugegraph-dist/src/assembly/travis/test-codecov-upload-config.sh
@@ -0,0 +1,212 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -euo pipefail
+
+SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+REPO_ROOT=$(cd "${SCRIPT_DIR}/../../../../.." && pwd)
+
+python3 - "${REPO_ROOT}" <<'PY'
+import pathlib
+import re
+import sys
+from collections import Counter
+
+repo_root = pathlib.Path(sys.argv[1])
+expected_files = {
+ ".github/workflows/commons-ci.yml": {
+ "build-commons": "hugegraph-commons/target/jacoco.xml",
+ },
+ ".github/workflows/pd-store-ci.yml": {
+ "pd": "${{ env.REPORT_FILE }}",
+ "store": "${{ env.REPORT_FILE }}",
+ "hstore": "${{ env.REPORT_DIR }}/*.xml",
+ },
+ ".github/workflows/server-ci.yml": {
+ "build-server": "${{ env.REPORT_DIR }}/*.xml",
+ },
+}
+action_pattern = re.compile(
+ r"^(?P<indent>\s*)(?P<dash>-\s+)?uses:\s*"
+ r"(?:(?P<quote>['\"])codecov/codecov-action@"
+ r"(?P<quoted>[^'\"\s]+)(?P=quote)\s*(?:#.*)?|"
+ r"codecov/codecov-action@(?P<plain>\S+?)(?:\s+#.*)?\s*)$"
+)
+job_pattern = re.compile(r"^ (?P<job>[a-zA-Z0-9_-]+):\s*(?:#.*)?$")
+version_pattern = re.compile(r"^v(?P<major>\d+)(?:[.-].*)?$")
+errors = []
+workflow_dir = repo_root / ".github/workflows"
+workflow_paths = sorted(
+ set(workflow_dir.glob("*.yml")) | set(workflow_dir.glob("*.yaml"))
+)
+checked_expected_workflows = set()
+
+
+def indentation(line):
+ return len(line) - len(line.lstrip())
+
+
+def find_uploads(lines):
+ uploads = []
+ current_job = None
+
+ for line_number, line in enumerate(lines, start=1):
+ job_match = job_pattern.match(line)
+ if job_match is not None:
+ current_job = job_match.group("job")
+ match = action_pattern.match(line)
+ if match is None:
+ continue
+
+ uses_indent = len(match.group("indent"))
+ if match.group("dash") is not None:
+ uses_indent += len(match.group("dash"))
+ step_indent = uses_indent - 2
+ block = []
+ for candidate in lines[line_number:]:
+ if candidate.strip() and indentation(candidate) <= step_indent:
+ break
+ block.append(candidate)
+ uploads.append(
+ (line_number, current_job,
+ match.group("quoted") or match.group("plain"), block, uses_indent)
+ )
+
+ return uploads
+
+
+def read_inputs(block, uses_indent):
+ inputs = {}
+ in_with_block = False
+ for candidate in block:
+ candidate_indent = indentation(candidate)
+ if candidate_indent == uses_indent and candidate.strip() == "with:":
+ in_with_block = True
+ continue
+ if (in_with_block and candidate.strip() and
+ candidate_indent <= uses_indent):
+ break
+ if not in_with_block or candidate_indent != uses_indent + 2:
+ continue
+ candidate_match = re.match(
+ r"^\s*(?P<key>[a-zA-Z_]+):\s*(?P<value>.*?)\s*$",
+ candidate,
+ )
+ if candidate_match is not None:
+ inputs[candidate_match.group("key")] =
candidate_match.group("value")
+ return inputs
+
+
+def parse_uploads(lines):
+ return [
+ (line_number, job, version, read_inputs(block, uses_indent))
+ for line_number, job, version, block, uses_indent in
find_uploads(lines)
+ ]
+
+
+def files_match(uploads, expected):
+ actual = Counter(
+ (job, inputs.get("files")) for _, job, _, inputs in uploads
+ )
+ return actual == Counter(expected.items())
+
+
+def check_mixed_upload_indentation():
+ lines = [
+ "jobs:",
+ " first:",
+ " steps:",
+ " - uses: codecov/codecov-action@v5",
+ " with:",
+ " token: ${{ secrets.CODECOV_TOKEN }}",
+ " files: first.xml",
+ " second:",
+ " steps:",
+ " - uses: codecov/codecov-action@v5",
+ " with:",
+ " token: ${{ secrets.CODECOV_TOKEN }}",
+ " files: second.xml",
+ ]
+ expected_inputs = [
+ {
+ "token": "${{ secrets.CODECOV_TOKEN }}",
+ "files": "first.xml",
+ },
+ {
+ "token": "${{ secrets.CODECOV_TOKEN }}",
+ "files": "second.xml",
+ },
+ ]
+ actual_inputs = [inputs for _, _, _, inputs in parse_uploads(lines)]
+ if actual_inputs != expected_inputs:
+ return ["Codecov uploads with mixed indentation were parsed
incorrectly"]
+ return []
+
+
+errors.extend(check_mixed_upload_indentation())
+
+
+for workflow_path in workflow_paths:
+ relative_path = str(workflow_path.relative_to(repo_root))
+ lines = workflow_path.read_text(encoding="utf-8").splitlines()
+ uploads = parse_uploads(lines)
+
+ expected_workflow_files = expected_files.get(relative_path)
+ if uploads and expected_workflow_files is None:
+ errors.append(
+ f"{relative_path}: unexpected Codecov upload workflow"
+ )
+ elif expected_workflow_files is not None:
+ checked_expected_workflows.add(relative_path)
+ if not files_match(uploads, expected_workflow_files):
+ errors.append(f"{relative_path}: unexpected Codecov files inputs")
+
+ for line_number, _, version, inputs in uploads:
+ version_match = version_pattern.match(version)
+ if version_match is None or int(version_match.group("major")) < 5:
+ errors.append(
+ f"{relative_path}:{line_number}: Codecov action {version} "
+ "uses the legacy uploader"
+ )
+
+ if inputs.get("token") != "${{ secrets.CODECOV_TOKEN }}":
+ errors.append(
+ f"{relative_path}:{line_number}: Codecov upload must pass "
+ "secrets.CODECOV_TOKEN for trusted runs"
+ )
+ if inputs.get("disable_search") != "true":
+ errors.append(
+ f"{relative_path}:{line_number}: Codecov upload must set "
+ "disable_search: true"
+ )
+ if inputs.get("fail_ci_if_error") != "false":
+ errors.append(
+ f"{relative_path}:{line_number}: Codecov upload must keep "
+ "fail_ci_if_error: false"
+ )
+
+for relative_path in expected_files.keys() - checked_expected_workflows:
+ errors.append(f"{relative_path}: expected workflow file is missing")
+
+if errors:
+ for error in errors:
+ print(f"ERROR: {error}", file=sys.stderr)
+ sys.exit(1)
+
+print("PASS: Codecov upload configuration contract")
+PY