From 0f5a234cead7c45d4351d29b95659aef51e95c59 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sun, 2 Mar 2025 17:12:47 -0500 Subject: [PATCH 1/4] build: add error log variable to store errors in file --- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: na - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: na - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: na - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: na - task: lint_typescript_declarations status: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- type: pre_push_report description: Results of running various checks prior to pushing changes. report: - task: run_javascript_examples status: na - task: run_c_examples status: na - task: run_cpp_examples status: na - task: run_javascript_readme_examples status: na - task: run_c_benchmarks status: na - task: run_cpp_benchmarks status: na - task: run_fortran_benchmarks status: na - task: run_javascript_benchmarks status: na - task: run_julia_benchmarks status: na - task: run_python_benchmarks status: na - task: run_r_benchmarks status: na - task: run_javascript_tests status: na --- --- .github/workflows/lint_random_files.yml | 10 +++++----- tools/make/lib/lint/javascript/eslint.mk | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lint_random_files.yml b/.github/workflows/lint_random_files.yml index df52a3fb75a2..eca78ea723ec 100644 --- a/.github/workflows/lint_random_files.yml +++ b/.github/workflows/lint_random_files.yml @@ -341,31 +341,31 @@ jobs: done if [[ -n "${files}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" 2>&1 | tee -a "$ERR_FILE" + make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_ERROR_LOG="$ERR_FILE" fi # Lint JavaScript command-line interfaces... file=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '\.js$' | grep -E '/bin/cli$' | tr '\n' ' ') if [[ -n "${file}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${file}" 2>&1 | tee -a "$ERR_FILE" + make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${file}" ESLINT_ERROR_LOG="$ERR_FILE" fi # Lint JavaScript example files: files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '/examples/.*\.js$' | tr '\n' ' ') if [[ -n "${files}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_CONF="${eslint_examples_conf}" 2>&1 | tee -a "$ERR_FILE" + make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_CONF="${eslint_examples_conf}" ESLINT_ERROR_LOG="$ERR_FILE" fi # Lint JavaScript test files: files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '/test/.*\.js$' | tr '\n' ' ') if [[ -n "${files}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_CONF="${eslint_tests_conf}" 2>&1 | tee -a "$ERR_FILE" + make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_CONF="${eslint_tests_conf}" ESLINT_ERROR_LOG="$ERR_FILE" fi # Lint JavaScript benchmark files: files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '/benchmark/.*\.js$' | tr '\n' ' ') if [[ -n "${files}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_CONF="${eslint_benchmarks_conf}" 2>&1 | tee -a "$ERR_FILE" + make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_CONF="${eslint_benchmarks_conf}" ESLINT_ERROR_LOG="$ERR_FILE" fi # Create sub-issue for JavaScript lint failures: diff --git a/tools/make/lib/lint/javascript/eslint.mk b/tools/make/lib/lint/javascript/eslint.mk index 2d193bdc6421..356e0b619f61 100644 --- a/tools/make/lib/lint/javascript/eslint.mk +++ b/tools/make/lib/lint/javascript/eslint.mk @@ -44,6 +44,9 @@ ESLINT_CONF_BENCHMARKS ?= $(CONFIG_DIR)/eslint/.eslintrc.benchmarks.js # Define the path to the ESLint ignore file: ESLINT_IGNORE ?= $(ROOT_DIR)/.eslintignore +# Define optional path for storing lint failure reports: +ESLINT_ERROR_LOG ?= + # Define the command-line options to use when invoking the ESLint executable: ESLINT_FLAGS ?= \ --ignore-path $(ESLINT_IGNORE) \ @@ -251,6 +254,18 @@ ifeq ($(FAIL_FAST), true) echo "Linting file: $$file"; \ $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF) $$file || exit 1; \ done +else ifneq ($(ESLINT_ERROR_LOG),) + $(QUIET) status=0; \ + for file in $(FILES); do \ + echo ''; \ + echo "Linting file: $$file"; \ + if ! $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF) $$file; then \ + echo 'Linting failed.'; \ + $(ESLINT) $(ESLINT_FLAGS) --quiet --config $(ESLINT_CONF) $$file >> $(ESLINT_ERROR_LOG); \ + status=1; \ + fi; \ + done; \ + exit $$status; else $(QUIET) status=0; \ for file in $(FILES); do \ From e04d9a1b9550c14f3d909c6071301816b540a5b7 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 25 Apr 2025 15:42:09 -0400 Subject: [PATCH 2/4] chore: address PR feedback --- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: na - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: na - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: na - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: na - task: lint_typescript_declarations status: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .github/workflows/lint_random_files.yml | 44 +++++++++++++++++---- tools/make/lib/lint/javascript/eslint.mk | 50 ++++++++++-------------- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/.github/workflows/lint_random_files.yml b/.github/workflows/lint_random_files.yml index eca78ea723ec..e93e28382153 100644 --- a/.github/workflows/lint_random_files.yml +++ b/.github/workflows/lint_random_files.yml @@ -326,9 +326,30 @@ jobs: fi # Combined error file: - ERR_FILE="lint_javascript_errors.txt" + ERR_FILE="$GITHUB_WORKSPACE/lint_javascript_errors.txt" > "$ERR_FILE" # Initialize a clean file + # Initialize error flag: + error_occurred=0 + + run_make_lint() { + local files_to_lint="$1" + local make_args="$2" + local eslint_conf_arg="$3" + local error_log_file="$4" + + local make_cmd="make lint-javascript-files FIX=${FIX} FAST_FAIL=0 FILES=\"${files_to_lint}\" ${make_args} ${eslint_conf_arg}" + + echo "Running: ${make_cmd}" + if ! eval "${make_cmd}"; then + echo "Initial linting failed. Retrying with --quiet and logging errors..." + + # Run again with --quiet flag and log output: + eval "${make_cmd} ESLINT_FLAGS='--quiet'" >> "${error_log_file}" 2>&1 + error_occurred=1 + fi + } + # Lint JavaScript source files: files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '\.js$' | grep -v -e '/examples' -e '/test' -e '/benchmark' -e '^dist/' | tr '\n' ' ') @@ -341,31 +362,38 @@ jobs: done if [[ -n "${files}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_ERROR_LOG="$ERR_FILE" + run_make_lint "${files}" "" "" "${ERR_FILE}" fi - # Lint JavaScript command-line interfaces... + # Lint JavaScript command-line interfaces: file=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '\.js$' | grep -E '/bin/cli$' | tr '\n' ' ') if [[ -n "${file}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${file}" ESLINT_ERROR_LOG="$ERR_FILE" + run_make_lint "${file}" "" "" "${ERR_FILE}" fi # Lint JavaScript example files: files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '/examples/.*\.js$' | tr '\n' ' ') if [[ -n "${files}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_CONF="${eslint_examples_conf}" ESLINT_ERROR_LOG="$ERR_FILE" + run_make_lint "${files}" "" "ESLINT_CONF=\"${eslint_examples_conf}\"" "${ERR_FILE}" fi # Lint JavaScript test files: files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '/test/.*\.js$' | tr '\n' ' ') if [[ -n "${files}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_CONF="${eslint_tests_conf}" ESLINT_ERROR_LOG="$ERR_FILE" + run_make_lint "${files}" "" "ESLINT_CONF=\"${eslint_tests_conf}\"" "${ERR_FILE}" fi # Lint JavaScript benchmark files: files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '/benchmark/.*\.js$' | tr '\n' ' ') if [[ -n "${files}" ]]; then - make lint-javascript-files FIX="${FIX}" FAST_FAIL=0 FILES="${files}" ESLINT_CONF="${eslint_benchmarks_conf}" ESLINT_ERROR_LOG="$ERR_FILE" + run_make_lint "${files}" "" "ESLINT_CONF=\"${eslint_benchmarks_conf}\"" "${ERR_FILE}" + fi + + # Check if any errors occurred during linting: + if [[ "$error_occurred" -eq 1 ]]; then + echo "JavaScript linting errors occurred. See details below or in the artifact." + cat "$ERR_FILE" # Print errors to the workflow log + exit 1 fi # Create sub-issue for JavaScript lint failures: @@ -404,7 +432,7 @@ jobs: "5377" \ "Good First Issue" - rm "$BODY_FILE" + rm "$BODY_FILE" "$ERR_FILE" # Lint Python files: - name: 'Lint Python files' diff --git a/tools/make/lib/lint/javascript/eslint.mk b/tools/make/lib/lint/javascript/eslint.mk index 356e0b619f61..e148cbf346dd 100644 --- a/tools/make/lib/lint/javascript/eslint.mk +++ b/tools/make/lib/lint/javascript/eslint.mk @@ -44,27 +44,30 @@ ESLINT_CONF_BENCHMARKS ?= $(CONFIG_DIR)/eslint/.eslintrc.benchmarks.js # Define the path to the ESLint ignore file: ESLINT_IGNORE ?= $(ROOT_DIR)/.eslintignore -# Define optional path for storing lint failure reports: -ESLINT_ERROR_LOG ?= - # Define the command-line options to use when invoking the ESLint executable: -ESLINT_FLAGS ?= \ +eslint_flags := \ --ignore-path $(ESLINT_IGNORE) \ --report-unused-disable-directives +# Define user-supplied command-line options: +ESLINT_FLAGS ?= + ifeq ($(AUTOFIX),true) - ESLINT_FLAGS += --fix + eslint_flags += --fix endif FIX_TYPE ?= ifneq ($(FIX_TYPE),) - ESLINT_FLAGS += --fix-type $(FIX_TYPE) + eslint_flags += --fix-type $(FIX_TYPE) else ifeq ($(AUTOFIX),true) - ESLINT_FLAGS += --fix-type problem,layout,directive + eslint_flags += --fix-type problem,layout,directive endif endif +# Append user-supplied command-line options: +eslint_flags += ESLINT_FLAGS + # RULES # #/ @@ -91,14 +94,14 @@ ifeq ($(FAIL_FAST), true) $(QUIET) $(FIND_SOURCES_CMD) | grep '^[\/]\|^[a-zA-Z]:[/\]' | while read -r file; do \ echo ''; \ echo "Linting file: $$file"; \ - $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF) $$file || exit 1; \ + $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF) $$file || exit 1; \ done else $(QUIET) status=0; \ $(FIND_SOURCES_CMD) | grep '^[\/]\|^[a-zA-Z]:[/\]' | while read -r file; do \ echo ''; \ echo "Linting file: $$file"; \ - if ! $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF) $$file; then \ + if ! $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF) $$file; then \ echo 'Linting failed.'; \ status=1; \ fi; \ @@ -132,14 +135,14 @@ ifeq ($(FAIL_FAST), true) $(QUIET) $(FIND_TESTS_CMD) | grep '^[\/]\|^[a-zA-Z]:[/\]' | while read -r file; do \ echo ''; \ echo "Linting file: $$file"; \ - $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF_TESTS) $$file || exit 1; \ + $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF_TESTS) $$file || exit 1; \ done else $(QUIET) status=0; \ $(FIND_TESTS_CMD) | grep '^[\/]\|^[a-zA-Z]:[/\]' | while read -r file; do \ echo ''; \ echo "Linting file: $$file"; \ - if ! $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF_TESTS) $$file; then \ + if ! $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF_TESTS) $$file; then \ echo 'Linting failed.'; \ status=1; \ fi; \ @@ -173,14 +176,14 @@ ifeq ($(FAIL_FAST), true) $(QUIET) $(FIND_EXAMPLES_CMD) | grep '^[\/]\|^[a-zA-Z]:[/\]' | while read -r file; do \ echo ''; \ echo "Linting file: $$file"; \ - $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF_EXAMPLES) $$file || exit 1; \ + $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF_EXAMPLES) $$file || exit 1; \ done else $(QUIET) status=0; \ $(FIND_EXAMPLES_CMD) | grep '^[\/]\|^[a-zA-Z]:[/\]' | while read -r file; do \ echo ''; \ echo "Linting file: $$file"; \ - if ! $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF_EXAMPLES) $$file; then \ + if ! $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF_EXAMPLES) $$file; then \ echo 'Linting failed.'; \ status=1; \ fi; \ @@ -214,14 +217,14 @@ ifeq ($(FAIL_FAST), true) $(QUIET) $(FIND_BENCHMARKS_CMD) | grep '^[\/]\|^[a-zA-Z]:[/\]' | while read -r file; do \ echo ''; \ echo "Linting file: $$file"; \ - $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF_BENCHMARKS) $$file || exit 1; \ + $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF_BENCHMARKS) $$file || exit 1; \ done else $(QUIET) status=0; \ $(FIND_BENCHMARKS_CMD) | grep '^[\/]\|^[a-zA-Z]:[/\]' | while read -r file; do \ echo ''; \ echo "Linting file: $$file"; \ - if ! $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF_BENCHMARKS) $$file; then \ + if ! $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF_BENCHMARKS) $$file; then \ echo 'Linting failed.'; \ status=1; \ fi; \ @@ -252,26 +255,13 @@ ifeq ($(FAIL_FAST), true) $(QUIET) for file in $(FILES); do \ echo ''; \ echo "Linting file: $$file"; \ - $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF) $$file || exit 1; \ - done -else ifneq ($(ESLINT_ERROR_LOG),) - $(QUIET) status=0; \ - for file in $(FILES); do \ - echo ''; \ - echo "Linting file: $$file"; \ - if ! $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF) $$file; then \ - echo 'Linting failed.'; \ - $(ESLINT) $(ESLINT_FLAGS) --quiet --config $(ESLINT_CONF) $$file >> $(ESLINT_ERROR_LOG); \ - status=1; \ - fi; \ - done; \ - exit $$status; + $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF) $$file || exit 1; \ else $(QUIET) status=0; \ for file in $(FILES); do \ echo ''; \ echo "Linting file: $$file"; \ - if ! $(ESLINT) $(ESLINT_FLAGS) --config $(ESLINT_CONF) $$file; then \ + if ! $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF) $$file; then \ echo 'Linting failed.'; \ status=1; \ fi; \ From 5b36b38a80878b559c8a9001516eb18caad684c8 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Mon, 1 Sep 2025 00:20:13 -0500 Subject: [PATCH 3/4] fix: correct Makefile syntax and variable usage in eslint rules --- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: na - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: na - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: na - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: na - task: lint_typescript_declarations status: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- tools/make/lib/lint/javascript/eslint.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/make/lib/lint/javascript/eslint.mk b/tools/make/lib/lint/javascript/eslint.mk index e148cbf346dd..eda192fb5cb8 100644 --- a/tools/make/lib/lint/javascript/eslint.mk +++ b/tools/make/lib/lint/javascript/eslint.mk @@ -66,7 +66,7 @@ endif endif # Append user-supplied command-line options: -eslint_flags += ESLINT_FLAGS +eslint_flags += $(ESLINT_FLAGS) # RULES # @@ -256,6 +256,7 @@ ifeq ($(FAIL_FAST), true) echo ''; \ echo "Linting file: $$file"; \ $(ESLINT) $(eslint_flags) --config $(ESLINT_CONF) $$file || exit 1; \ + done else $(QUIET) status=0; \ for file in $(FILES); do \ From 6213840006135fa252c56f89649aae16dee30410 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Mon, 1 Sep 2025 12:09:24 -0500 Subject: [PATCH 4/4] build: move error log linting step to script file --- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: na - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: na - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: na - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: passed - task: lint_typescript_declarations status: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .github/workflows/lint_random_files.yml | 101 ++-------- .../scripts/lint_javascript_files_error_log | 174 ++++++++++++++++++ 2 files changed, 185 insertions(+), 90 deletions(-) create mode 100755 .github/workflows/scripts/lint_javascript_files_error_log diff --git a/.github/workflows/lint_random_files.yml b/.github/workflows/lint_random_files.yml index e93e28382153..7f4cdc825602 100644 --- a/.github/workflows/lint_random_files.yml +++ b/.github/workflows/lint_random_files.yml @@ -302,98 +302,19 @@ jobs: - name: 'Lint JavaScript files' id: lint-javascript if: ( github.event.inputs.javascript != 'false' ) && ( success() || failure() ) + env: + FIX: ${{ github.event.inputs.fix == 'true' && '1' || '0' }} + QUIET: '0' + ERR_FILE: ${{ github.workspace }}/lint_javascript_errors.txt run: | - # If any command in a pipeline fails, the entire pipeline should fail: - set -o pipefail - - # Determine root directory: - root=$(git rev-parse --show-toplevel) + # Get JavaScript files to lint: + files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '\.js$' | tr '\n' ' ') - # Define the path to ESLint configuration file for linting examples: - eslint_examples_conf="${root}/etc/eslint/.eslintrc.examples.js" - - # Define the path to ESLint configuration file for linting tests: - eslint_tests_conf="${root}/etc/eslint/.eslintrc.tests.js" - - # Define the path to ESLint configuration file for linting benchmarks: - eslint_benchmarks_conf="${root}/etc/eslint/.eslintrc.benchmarks.js" - - # Set `FIX` variable to `1` to enable automatic fixing of lint errors, `0` to disable: - if [ "${{ github.event.inputs.fix }}" == "true" ]; then - FIX=1 + # Run the lint script: + if [ -n "${files}" ]; then + "$GITHUB_WORKSPACE/.github/workflows/scripts/lint_javascript_files_error_log" ${files} else - FIX=0 - fi - - # Combined error file: - ERR_FILE="$GITHUB_WORKSPACE/lint_javascript_errors.txt" - > "$ERR_FILE" # Initialize a clean file - - # Initialize error flag: - error_occurred=0 - - run_make_lint() { - local files_to_lint="$1" - local make_args="$2" - local eslint_conf_arg="$3" - local error_log_file="$4" - - local make_cmd="make lint-javascript-files FIX=${FIX} FAST_FAIL=0 FILES=\"${files_to_lint}\" ${make_args} ${eslint_conf_arg}" - - echo "Running: ${make_cmd}" - if ! eval "${make_cmd}"; then - echo "Initial linting failed. Retrying with --quiet and logging errors..." - - # Run again with --quiet flag and log output: - eval "${make_cmd} ESLINT_FLAGS='--quiet'" >> "${error_log_file}" 2>&1 - error_occurred=1 - fi - } - - # Lint JavaScript source files: - files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '\.js$' | grep -v -e '/examples' -e '/test' -e '/benchmark' -e '^dist/' | tr '\n' ' ') - - # Build native addons if present: - packages=$(echo "${files}" | tr ' ' '\n' | sed 's/^lib\/node_modules\///g' | sed 's/\/lib\/.*//g' | sort | uniq) - for pkg in ${packages}; do - if [ -f "lib/node_modules/${pkg}/binding.gyp" ]; then - NODE_ADDONS_PATTERN="${pkg}" make install-node-addons - fi - done - - if [[ -n "${files}" ]]; then - run_make_lint "${files}" "" "" "${ERR_FILE}" - fi - - # Lint JavaScript command-line interfaces: - file=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '\.js$' | grep -E '/bin/cli$' | tr '\n' ' ') - if [[ -n "${file}" ]]; then - run_make_lint "${file}" "" "" "${ERR_FILE}" - fi - - # Lint JavaScript example files: - files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '/examples/.*\.js$' | tr '\n' ' ') - if [[ -n "${files}" ]]; then - run_make_lint "${files}" "" "ESLINT_CONF=\"${eslint_examples_conf}\"" "${ERR_FILE}" - fi - - # Lint JavaScript test files: - files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '/test/.*\.js$' | tr '\n' ' ') - if [[ -n "${files}" ]]; then - run_make_lint "${files}" "" "ESLINT_CONF=\"${eslint_tests_conf}\"" "${ERR_FILE}" - fi - - # Lint JavaScript benchmark files: - files=$(echo "${{ steps.random-files.outputs.files }}" | tr ',' '\n' | grep '/benchmark/.*\.js$' | tr '\n' ' ') - if [[ -n "${files}" ]]; then - run_make_lint "${files}" "" "ESLINT_CONF=\"${eslint_benchmarks_conf}\"" "${ERR_FILE}" - fi - - # Check if any errors occurred during linting: - if [[ "$error_occurred" -eq 1 ]]; then - echo "JavaScript linting errors occurred. See details below or in the artifact." - cat "$ERR_FILE" # Print errors to the workflow log - exit 1 + echo "No JavaScript files to lint." fi # Create sub-issue for JavaScript lint failures: @@ -432,7 +353,7 @@ jobs: "5377" \ "Good First Issue" - rm "$BODY_FILE" "$ERR_FILE" + rm "$BODY_FILE" # Lint Python files: - name: 'Lint Python files' diff --git a/.github/workflows/scripts/lint_javascript_files_error_log b/.github/workflows/scripts/lint_javascript_files_error_log new file mode 100755 index 000000000000..c7ce4c94a7e6 --- /dev/null +++ b/.github/workflows/scripts/lint_javascript_files_error_log @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +# +# @license Apache-2.0 +# +# Copyright (c) 2025 The Stdlib Authors. +# +# Licensed 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. + +# Script to lint JavaScript files with extended error handling and reporting. +# +# Usage: lint_javascript_files_extended file1 [file2 file3 ...] +# +# Arguments: +# +# file1 File path. +# file2 File path. +# file3 File path. +# +# Environment variables: +# +# FIX 0 or 1 indicating whether to automatically fix errors. +# QUIET 0 or 1 indicating whether to run in quiet mode (errors only). +# ERR_FILE Path to error output file (optional). +# + +# shellcheck disable=SC2086 + +# Ensure that the exit status of pipelines is non-zero in the event that at least one of the commands in a pipeline fails: +set -o pipefail + + +# VARIABLES # + +# Determine root directory: +root=$(git rev-parse --show-toplevel) + +# Flag indicating whether to automatically fix errors: +fix="${FIX:-0}" + +# Flag indicating whether to run in quiet mode: +quiet="${QUIET:-0}" + +# Error output file: +err_file="${ERR_FILE:-${TMPDIR:-/tmp}/lint_javascript_errors_$$.txt}" + +# Files to lint: +files_to_lint="$*" + +# Define the path to ESLint configuration file for linting examples: +eslint_examples_conf="${root}/etc/eslint/.eslintrc.examples.js" + +# Define the path to ESLint configuration file for linting tests: +eslint_tests_conf="${root}/etc/eslint/.eslintrc.tests.js" + +# Define the path to ESLint configuration file for linting benchmarks: +eslint_benchmarks_conf="${root}/etc/eslint/.eslintrc.benchmarks.js" + +# Initialize error flag: +error_occurred=0 + + +# FUNCTIONS # + +# Runs make lint command with error handling. +# +# $1 - files to lint +# $2 - additional make arguments +# $3 - ESLint configuration argument +run_make_lint() { + local files_to_lint="$1" + local make_args="$2" + local eslint_conf_arg="$3" + + local eslint_flags="" + if [ "${quiet}" -eq 1 ]; then + eslint_flags="ESLINT_FLAGS='--quiet'" + fi + + local make_cmd="make lint-javascript-files FIX=${fix} FAST_FAIL=0 FILES=\"${files_to_lint}\" ${make_args} ${eslint_conf_arg} ${eslint_flags}" + + echo "Running: ${make_cmd}" + if ! eval "${make_cmd}"; then + if [ "${quiet}" -eq 0 ]; then + echo "Initial linting failed. Retrying with --quiet and logging errors..." + eval "make lint-javascript-files FIX=${fix} FAST_FAIL=0 FILES=\"${files_to_lint}\" ${make_args} ${eslint_conf_arg} ESLINT_FLAGS='--quiet'" >> "${err_file}" 2>&1 + else + # Already running with --quiet, just log the error + echo "Linting failed for files." >> "${err_file}" + fi + error_occurred=1 + fi +} + + +# MAIN # + +# Initialize error file: +: > "${err_file}" + +# Build native addons if present: +packages=$(echo "${files_to_lint}" | tr ' ' '\n' | grep '^lib/node_modules/' | sed 's/^lib\/node_modules\///g' | sed 's/\/lib\/.*//g' | sort | uniq) +for pkg in ${packages}; do + if [ -f "lib/node_modules/${pkg}/binding.gyp" ]; then + echo "Building native addon for ${pkg}..." + NODE_ADDONS_PATTERN="${pkg}" make install-node-addons + fi +done + +# Lint JavaScript source files: +files=$(echo "${files_to_lint}" | tr ' ' '\n' | grep '\.js$' | grep -v -e '/examples' -e '/test' -e '/benchmark' -e '^dist/' | tr '\n' ' ' | sed 's/ $//') +if [ -n "${files}" ]; then + echo "Linting JavaScript source files..." + run_make_lint "${files}" "" "" +fi + +# Lint JavaScript command-line interfaces: +files=$(echo "${files_to_lint}" | tr ' ' '\n' | grep '\.js$' | grep -E '/bin/cli$' | tr '\n' ' ' | sed 's/ $//') +if [ -n "${files}" ]; then + echo "Linting JavaScript CLI files..." + run_make_lint "${files}" "" "" +fi + +# Lint JavaScript example files: +files=$(echo "${files_to_lint}" | tr ' ' '\n' | grep '/examples/.*\.js$' | tr '\n' ' ' | sed 's/ $//') +if [ -n "${files}" ]; then + echo "Linting JavaScript example files..." + run_make_lint "${files}" "" "ESLINT_CONF=\"${eslint_examples_conf}\"" +fi + +# Lint JavaScript test files: +files=$(echo "${files_to_lint}" | tr ' ' '\n' | grep '/test/.*\.js$' | tr '\n' ' ' | sed 's/ $//') +if [ -n "${files}" ]; then + echo "Linting JavaScript test files..." + run_make_lint "${files}" "" "ESLINT_CONF=\"${eslint_tests_conf}\"" +fi + +# Lint JavaScript benchmark files: +files=$(echo "${files_to_lint}" | tr ' ' '\n' | grep '/benchmark/.*\.js$' | tr '\n' ' ' | sed 's/ $//') +if [ -n "${files}" ]; then + echo "Linting JavaScript benchmark files..." + run_make_lint "${files}" "" "ESLINT_CONF=\"${eslint_benchmarks_conf}\"" +fi + +# Report results: +if [ "${error_occurred}" -eq 1 ]; then + echo "JavaScript linting errors occurred. See details below:" + cat "${err_file}" + + # Clean up temp file ONLY if it was auto-generated (ERR_FILE not provided): + if [ -z "${ERR_FILE}" ]; then + rm -f "${err_file}" + fi + + exit 1 +else + echo "JavaScript linting completed successfully." + + # Clean up temp file ONLY if it was auto-generated (ERR_FILE not provided): + if [ -z "${ERR_FILE}" ]; then + rm -f "${err_file}" + fi + + exit 0 +fi