asf-tooling commented on issue #1067:
URL: 
https://github.com/apache/tooling-trusted-releases/issues/1067#issuecomment-4409837839

   <!-- gofannon-issue-triage-bot v2 -->
   
   **Automated triage** — analyzed at `main@2da7807a`
   
   **Type:** `new_feature`  •  **Classification:** `actionable`  •  
**Confidence:** `high`
   **Application domain(s):** `shared_infrastructure`, `automated_checks`
   
   ### Summary
   The issue requests parity between Python and npm dependency freshness 
enforcement. Currently, `scripts/check_when_dependencies_updated.py` enforces a 
30-day max age for Python deps via the `exclude-newer` timestamp in `uv.lock`, 
and a corresponding pre-commit hook exists. However, npm dependencies in 
`bootstrap/source/package.json` have no such automated freshness check or 
Dependabot coverage. The `bootstrap/context/bump.sh` script already prevents 
too-new packages (14-day cooldown) but has no maximum-age enforcement. The 
triage note 'look for pre-existing scanner' confirms there is none — only `npm 
audit` (vulnerability) and `npm audit signatures` (integrity) exist in 
`bump.sh`, not a staleness check.
   
   ### Where this lives in the code today
   
   #### `scripts/check_when_dependencies_updated.py` — `main` (lines 28-57)
   _currently does this_
   This is the pattern to mirror for npm freshness checking — parses a 
timestamp from the lock file and verifies it's within MAX_AGE_DAYS.
   
   ```python
   _MAX_AGE_DAYS: Final[int] = 30
   
   
   def main() -> None:
       lock_path = pathlib.Path("uv.lock")
       if not lock_path.exists():
           print("ERROR: uv.lock not found", file=sys.stderr)
           sys.exit(1)
   
       exclude_newer = _parse_exclude_newer(lock_path)
       if exclude_newer is None:
           print("ERROR: No exclude-newer timestamp in uv.lock", 
file=sys.stderr)
           print("Run: make update-deps", file=sys.stderr)
           sys.exit(1)
   
       timestamp = _parse_timestamp(exclude_newer)
       if timestamp is None:
           print(f"ERROR: Could not parse timestamp: {exclude_newer}", 
file=sys.stderr)
           sys.exit(1)
   
       now = datetime.datetime.now(datetime.UTC)
       age = now - timestamp
   
       if age > datetime.timedelta(days=_MAX_AGE_DAYS):
           print(f"ERROR: Dependencies are {age.days} days old (the limit is 
{_MAX_AGE_DAYS} days)", file=sys.stderr)
           print(f"Last updated: {exclude_newer}", file=sys.stderr)
           print("Run: make update-deps", file=sys.stderr)
           sys.exit(1)
   
       print(f"OK: Dependencies are {age.days} days old (the limit is 
{_MAX_AGE_DAYS} days)")
   ```
   
   ### Where new code would go
   - `scripts/check_npm_dependencies_updated.py` — new file
     New script mirroring check_when_dependencies_updated.py but for npm 
dependencies, using git log to determine last update time of package-lock.json.
   - `.github/dependabot.yml` — after docker ecosystem entry
     Add npm ecosystem entry for automated dependency update PRs.
   - `.pre-commit-config.yaml` — after check-when-dependencies-updated hook
     Add npm freshness check hook.
   
   ### Proposed approach
   The implementation follows the established pattern: (1) Add npm to 
Dependabot with the same weekly Tuesday schedule and cooldown. (2) Create 
`scripts/check_npm_dependencies_updated.py` that checks when 
`bootstrap/source/package-lock.json` was last updated. Since 
`package-lock.json` has no embedded timestamp like `uv.lock`'s `exclude-newer`, 
the best approach is to have `bump.sh` write a `.npm-exclude-newer` timestamp 
file alongside package-lock.json, which can then be parsed identically to the 
Python approach. This avoids fragile `mtime` checks and maintains the same 
pattern. (3) Add a pre-commit hook to run the new script. (4) Modify `bump.sh` 
to write the cutoff timestamp to the trackable file.
   
   The max age should be 60 days (as proposed in the issue) since npm 
dependencies here are frontend assets (Bootstrap CSS/JS) that change less 
frequently than Python runtime dependencies, and the 14-day cooldown in bump.sh 
means the effective window from publish-to-allowed is already constrained.
   
   ### Suggested patches
   
   #### `.github/dependabot.yml`
   Add npm ecosystem to Dependabot configuration with matching schedule and 
cooldown.
   
   ````diff
   --- a/.github/dependabot.yml
   +++ b/.github/dependabot.yml
   @@ -27,6 +27,14 @@ updates:
        cooldown:
          default-days: 7
   +  - package-ecosystem: "npm"
   +    directory: "/bootstrap/source"
   +    schedule:
   +      interval: "weekly"
   +      day: "tuesday"
   +    cooldown:
   +      default-days: 14
      # # TODO: Uncomment when pre-commit checks support cooldowns
      # - package-ecosystem: "pre-commit"
      #   directory: "/"
   ````
   
   #### `bootstrap/context/bump.sh`
   Write the cutoff timestamp to a file so the freshness check script can parse 
it.
   
   ````diff
   --- a/bootstrap/context/bump.sh
   +++ b/bootstrap/context/bump.sh
   @@ -35,6 +35,9 @@ echo "Using 14-day cooldown: --before=$CUTOFF"
    
    rm -rf node_modules package-lock.json
    
   +# Record the cutoff timestamp for freshness tracking
   +echo "$CUTOFF" > .npm-exclude-newer
   +
    npm install "bootstrap@$VERSION" --before="$CUTOFF"
    
    echo "Checking for known vulnerabilities..."
   ````
   
   #### `scripts/check_npm_dependencies_updated.py`
   New freshness check script for npm dependencies, mirroring the Python 
dependency check pattern.
   
   ````diff
   --- /dev/null
   +++ b/scripts/check_npm_dependencies_updated.py
   @@ -0,0 +1,67 @@
   +#!/usr/bin/env python3
   +
   +# 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.
   +
   +"""Check that npm dependencies are not stale (ASVS 15.2.1)."""
   +
   +import datetime
   +import pathlib
   +import sys
   +from typing import Final
   +
   +_MAX_AGE_DAYS: Final[int] = 60
   +_TIMESTAMP_FILE: Final[str] = "bootstrap/source/.npm-exclude-newer"
   +
   +
   +def main() -> None:
   +    timestamp_path = pathlib.Path(_TIMESTAMP_FILE)
   +    if not timestamp_path.exists():
   +        print(f"ERROR: {_TIMESTAMP_FILE} not found", file=sys.stderr)
   +        print("Run: make bump-bootstrap BOOTSTRAP_VERSION=X.Y.Z", 
file=sys.stderr)
   +        sys.exit(1)
   +
   +    timestamp_str = timestamp_path.read_text(encoding="utf-8").strip()
   +    timestamp = _parse_timestamp(timestamp_str)
   +    if timestamp is None:
   +        print(f"ERROR: Could not parse timestamp: {timestamp_str}", 
file=sys.stderr)
   +        sys.exit(1)
   +
   +    now = datetime.datetime.now(datetime.UTC)
   +    age = now - timestamp
   +
   +    if age > datetime.timedelta(days=_MAX_AGE_DAYS):
   +        print(
   +            f"ERROR: npm dependencies are {age.days} days old (the limit is 
{_MAX_AGE_DAYS} days)",
   +            file=sys.stderr,
   +        )
   +        print(f"Last updated: {timestamp_str}", file=sys.stderr)
   +        print("Run: make bump-bootstrap BOOTSTRAP_VERSION=X.Y.Z", 
file=sys.stderr)
   +        sys.exit(1)
   +
   +    print(f"OK: npm dependencies are {age.days} days old (the limit is 
{_MAX_AGE_DAYS} days)")
   +
   +
   +def _parse_timestamp(timestamp_str: str) -> datetime.datetime | None:
   +    try:
   +        return datetime.datetime.fromisoformat(timestamp_str.replace("Z", 
"+00:00"))
   +    except ValueError:
   +        return None
   +
   +
   +if __name__ == "__main__":
   +    main()
   ````
   
   #### `.pre-commit-config.yaml`
   Add pre-commit hook to enforce npm dependency freshness alongside the 
existing Python dependency check.
   
   ````diff
   --- a/.pre-commit-config.yaml
   +++ b/.pre-commit-config.yaml
   @@ -139,6 +139,13 @@ repos:
          pass_filenames: false
          always_run: true
    
   +    - id: check-npm-dependencies-updated
   +      name: check when npm dependencies were updated
   +      description: Verify that npm dependencies were updated within the 
configured timeframe, for ASVS 15.2.1
   +      entry: uv run --frozen python 
scripts/check_npm_dependencies_updated.py
   +      language: system
   +      pass_filenames: false
   +      always_run: true
   +
        - id: check-models-imports
          name: check models imports
          description: Ensure that the models only import from approved packages
   ````
   
   ### Open questions
   - Should the .npm-exclude-newer file be committed to git (so the pre-commit 
hook works after clone), or should the freshness check use `git log` to 
determine when package-lock.json was last modified?
   - Is 60 days the right max age for npm dependencies, given they are frontend 
assets (Bootstrap CSS/JS) that update less frequently than Python runtime deps?
   - Should package-lock.json be committed to the repo (it doesn't appear to 
exist currently — bump.sh deletes and regenerates it)?
   - The issue mentions a DEPENDENCIES.md documentation file — should this be a 
new top-level doc or added to existing documentation?
   - The Dependabot npm entry uses cooldown of 14 days to match bump.sh — 
should it be 7 days to match other ecosystems instead?
   
   ### Files examined
   - `.github/dependabot.yml`
   - `bootstrap/context/bump.sh`
   - `bootstrap/source/package.json`
   - `Makefile`
   - `scripts/check_when_dependencies_updated.py`
   - `.github/workflows/analyze.yml`
   - `.pre-commit-config.yaml`
   - `.github/workflows/build.yml`
   
   ### Related issues
   This issue appears related to: #1068.
   
   _Both address missing update timeframes and monitoring for external 
dependencies (Docker tools vs npm)_
   
   ---
   *Draft from a triage agent. A human reviewer should validate before merging 
any change. The agent did not run tests or verify diffs apply.*


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to