diff --git a/.github/workflows/lint_changed_files.yml b/.github/workflows/lint_changed_files.yml index c80a5fe1d9b0..78fb807bcff9 100644 --- a/.github/workflows/lint_changed_files.yml +++ b/.github/workflows/lint_changed_files.yml @@ -171,6 +171,12 @@ jobs: run: | . "$GITHUB_WORKSPACE/.github/workflows/scripts/common/lint_package_json_files" "${{ steps.changed-files.outputs.files }}" + # Lint package.json metadata: + - name: 'Lint package.json metadata' + if: success() || failure() + run: | + make lint-pkgs-metadata-files FILES="${{ steps.changed-files.outputs.files }}" + # Lint REPL help files... - name: 'Lint REPL help files' if: success() || failure() diff --git a/.github/workflows/scripts/common/lint_package_json_files b/.github/workflows/scripts/common/lint_package_json_files index f3644dcbbe74..4fc4c35c2c99 100755 --- a/.github/workflows/scripts/common/lint_package_json_files +++ b/.github/workflows/scripts/common/lint_package_json_files @@ -16,7 +16,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Script to lint package.json files and check/update metadata fields. +# Script to lint package.json files. # # Usage: lint_package_json_files file1 [file2 file3 ...] # @@ -32,61 +32,14 @@ root=$(git rev-parse --show-toplevel) # Define the path to a utility for linting package.json files: lint_package_json="${root}/lib/node_modules/@stdlib/_tools/lint/pkg-json/bin/cli" -# Define the path to the package name validation tool: -validate_package_names="${root}/lib/node_modules/@stdlib/_tools/lint/pkg-json-names/bin/cli" - -# Define paths to utilities for updating package.json metadata fields: -update_package_json_directories="${root}/lib/node_modules/@stdlib/_tools/package-json/scripts/update_directories" -update_package_json_gypfile="${root}/lib/node_modules/@stdlib/_tools/package-json/scripts/update_gypfile" - # Files to process: files_to_process="$*" -# Initialize needs_changes flag: -needs_changes=0 - # Lint package.json files: files=$(echo "${files_to_process}" | tr ' ' '\n' | awk -F/ '$NF=="package.json"' | tr '\n' ' ' | sed 's/ $//') if [ -n "${files}" ]; then echo "Linting package.json files..." printf '%s' "${files}" | "${lint_package_json}" --split=" " - - echo "Validating package names..." - if ! printf '%s' "${files}" | "${validate_package_names}" --split=" "; then - echo "ERROR: Package name validation failed" - needs_changes=1 - fi else echo "No package.json files to lint." fi - -# Check if metadata fields need to be updated in package.json files of affected packages: -dirs=$(echo "${files_to_process}" | tr ' ' '\n' | \ - xargs dirname | \ - sed -E 's/\/(benchmark|bin|data|docs|etc|examples|include|lib|scripts|src|test)(\/.*)?$//' | \ - sort -u) - -echo "Checking package.json files in directories: ${dirs}" -for dir in ${dirs}; do - echo "Checking package.json in ${dir}..." - package_json="${dir}/package.json" - if [ ! -f "${package_json}" ]; then - continue - fi - original_content=$(cat "${package_json}") - - "${update_package_json_directories}" "${dir}" - "${update_package_json_gypfile}" "${dir}" - - new_content=$(cat "${package_json}") - if [ "$original_content" != "$new_content" ]; then - echo "ERROR: package.json in ${dir} needs updates to directories and/or gypfile fields" - git --no-pager diff "${package_json}" - needs_changes=1 - fi -done - -# Exit with failure if any needed changes were detected: -if [ $needs_changes -eq 1 ]; then - exit 1 -fi diff --git a/lib/node_modules/@stdlib/_tools/package-json/scripts/validate_package_json_files b/lib/node_modules/@stdlib/_tools/package-json/scripts/validate_package_json_files new file mode 100755 index 000000000000..ad4aeabee5d4 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/package-json/scripts/validate_package_json_files @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# +# @license Apache-2.0 +# +# Copyright (c) 2026 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 validate package.json metadata fields. +# +# Usage: validate_package_json_files file1 [file2 file3 ...] +# +# Arguments: +# +# file1 File path. +# file2 File path. +# file3 File path. + +# Determine root directory: +root=$(git rev-parse --show-toplevel) + +# Define the path to the package name validation tool: +validate_package_names="${root}/lib/node_modules/@stdlib/_tools/lint/pkg-json-names/bin/cli" + +# Define paths to utilities for updating package.json metadata fields: +update_package_json_directories="${root}/lib/node_modules/@stdlib/_tools/package-json/scripts/update_directories" +update_package_json_gypfile="${root}/lib/node_modules/@stdlib/_tools/package-json/scripts/update_gypfile" + +# Files to process: +files_to_process="$*" + +# Initialize needs_changes flag: +needs_changes=0 + +# Validate package.json package names: +files=$(echo "${files_to_process}" | tr ' ' '\n' | awk -F/ '$NF=="package.json"' | tr '\n' ' ' | sed 's/ $//') +if [ -n "${files}" ]; then + echo "Validating package names..." + if ! printf '%s' "${files}" | "${validate_package_names}" --split=" "; then + echo "ERROR: Package name validation failed" + needs_changes=1 + fi +else + echo "No package.json files to validate." +fi + +# Check if metadata fields need to be updated in package.json files of affected packages: +dirs=$(echo "${files_to_process}" | tr ' ' '\n' | \ + xargs dirname | \ + sed -E 's/\/(benchmark|bin|data|docs|etc|examples|include|lib|scripts|src|test)(\/[^@]*)?$//' | \ + sort -u) + +echo "Checking package.json files in directories: ${dirs}" +for dir in ${dirs}; do + echo "Checking package.json in ${dir}..." + package_json="${dir}/package.json" + if [ ! -f "${package_json}" ]; then + continue + fi + original_content=$(cat "${package_json}") + + "${update_package_json_directories}" "${dir}" + "${update_package_json_gypfile}" "${dir}" + + new_content=$(cat "${package_json}") + if [ "$original_content" != "$new_content" ]; then + echo "ERROR: package.json in ${dir} needs updates to directories and/or gypfile fields" + git --no-pager diff "${package_json}" + needs_changes=1 + fi +done + +# Exit with failure if any needed changes were detected: +if [ $needs_changes -eq 1 ]; then + exit 1 +fi diff --git a/tools/git/hooks/pre-commit b/tools/git/hooks/pre-commit index dbee4c6e1076..dde54882907c 100644 --- a/tools/git/hooks/pre-commit +++ b/tools/git/hooks/pre-commit @@ -254,6 +254,21 @@ run_lint() { else task_status 'skipped' fi + # Lint package.json metadata... + add_task 'lint_pkgs_metadata' + if [[ -z "${skip_package_json}" ]]; then + files=$(echo "${changed_files}" | tr '\n' ' ') + make FILES="${files}" lint-pkgs-metadata-files > /dev/null >&2 + if [[ "$?" -ne 0 ]]; then + task_status 'failed' + echo '' >&2 + echo 'package.json metadata out of date.' >&2 + return 1 + fi + task_status 'passed' + else + task_status 'skipped' + fi # Lint REPL help files... add_task 'lint_repl_help' if [[ -z "${skip_repl_help}" ]]; then diff --git a/tools/make/lib/lint/pkgs/Makefile b/tools/make/lib/lint/pkgs/Makefile index 91e0e427c6b8..32f85e375758 100644 --- a/tools/make/lib/lint/pkgs/Makefile +++ b/tools/make/lib/lint/pkgs/Makefile @@ -16,6 +16,12 @@ # limitations under the License. #/ +# VARIABLES # + +# Define the path for script validating `package.json` metadata: +VALIDATE_PKG_JSON ?= $(TOOLS_PKGS_DIR)/package-json/scripts/validate_package_json_files + + # RULES # # TODO: support linting filenames, package.json, REPL help, JavaScript CLI files (see pre-commit hook), license headers @@ -44,3 +50,17 @@ lint-pkgs: lint-editorconfig $(QUIET) $(MAKE) -f $(this_file) lint-typescript TYPESCRIPT_DECLARATIONS_FILTER=$(PACKAGES_FILTER) TYPESCRIPT_DECLARATIONS_TESTS_FILTER=$(PACKAGES_FILTER) .PHONY: lint-pkgs + +#/ +# Lints `package.json` metadata associated with a list of files. +# +# @param {string} FILES - list of file paths +# +# @example +# make lint-pkgs-metadata-files FILES='/foo/lib/index.js /bar/package.json' +#/ +lint-pkgs-metadata-files: $(NODE_MODULES) + $(QUIET) echo 'Linting package.json metadata...' + $(QUIET) $(VALIDATE_PKG_JSON) $(FILES) + +.PHONY: lint-pkgs-metadata-files