Dependency Update Checker for Composer and npm in Bash
AI generated
$_
#!/
Bash · Composer · npm · Real World
Dependency Update Checker for Composer and npm
catching outdated and insecure packages automatically

A dependency update checker regularly collects the state of outdated packages across multiple repositories and surfaces security advisories before they show up unpleasantly in an audit. This article shows how such a dependency update checker evaluates composer outdated and npm outdated with jq, classifies version jumps, and automatically ships the results as a report.

18 min read composer outdated · npm outdated · jq · Slack webhook Bash 4.x · 5.x · Composer 2.x · npm 10.x

1. Why a dependency update checker pays off

Dependencies age faster in almost every project than teams actively track. Without a fixed process, the realization that a package has not received a security update for months is often left to chance, for instance when an external pentest finds the gap. A dependency update checker closes this gap by regularly and automatically checking which packages are outdated and how large the backlog really is.

For projects using both PHP with Composer and JavaScript with npm, as is common for Magento or Hyvä based shops, a unified dependency update checker covers both ecosystems at once. Instead of maintaining two separate tools, a Bash script combines the output of composer outdated and npm outdated into a single, consistent report.

The real value of a dependency update checker only emerges through automation: a weekly run that ships the results via Slack or email makes outdated dependencies visible without anyone having to actively remember to trigger the check manually.

2. composer outdated and npm outdated as a data source

Both Composer and npm ship built in commands that list outdated packages: composer outdated --format=json and npm outdated --json. A dependency update checker uses this structured JSON output instead of parsing the human readable table format, which is considerably more robust against format changes between versions.

It is important to handle both commands' return value behavior correctly: npm outdated intentionally returns a non zero exit code as soon as outdated packages are found, which would immediately end the script under set -e. A clean dependency update checker therefore explicitly catches this specific exit code instead of being caught off guard by set -e.


#!/usr/bin/env bash
# collect-outdated.sh — gather raw outdated package data from both ecosystems
set -euo pipefail

collect_composer_outdated() {
  local project_dir="$1"
  (cd "$project_dir" && composer outdated --direct --format=json 2>/dev/null) || true
}

collect_npm_outdated() {
  local project_dir="$1"
  # npm outdated exits 1 when packages are outdated — this is expected, not a failure
  (cd "$project_dir" && npm outdated --json 2>/dev/null) || true
}

readonly PROJECT_DIR="${1:?Usage: collect-outdated.sh <project-dir>}"

collect_composer_outdated "$PROJECT_DIR" > /tmp/composer-outdated.json
collect_npm_outdated "$PROJECT_DIR" > /tmp/npm-outdated.json

echo "[OK] Raw outdated data collected for ${PROJECT_DIR}" >&2

3. Reading structured JSON output with jq

The raw JSON output of both tools has different structures: Composer returns an object with an installed array, npm returns a flat object with the package name as the key. A dependency update checker normalizes both formats with jq into one common, unified line format containing package name, current version, and available version.

This normalization is the decisive step that later makes every further evaluation, from version classification to report generation, independent of the original ecosystem. From this point on, the dependency update checker no longer needs to know whether a package came from Composer or npm.


#!/usr/bin/env bash
# normalize.sh — unify composer and npm outdated output into one line format
set -euo pipefail

normalize_composer() {
  jq -r '.installed[]? | [.name, .version, .latest, "composer"] | @tsv' \
    /tmp/composer-outdated.json
}

normalize_npm() {
  jq -r 'to_entries[]? | [.key, .value.current, .value.latest, "npm"] | @tsv' \
    /tmp/npm-outdated.json
}

{
  normalize_composer
  normalize_npm
} > /tmp/normalized-outdated.tsv

echo "[OK] Normalized $(wc -l < /tmp/normalized-outdated.tsv) outdated packages" >&2

4. Classifying version jumps

Not every outdated package is equally urgent. A dependency update checker should classify the version jump following semantic versioning: a patch update is usually low risk, a minor update should be checked soon, a major update often requires manual adjustments and its own test run. This classification helps teams focus on the changes that truly matter first, instead of losing track amid twenty patch updates.

The calculation works by having the dependency update checker compare the first digit of the current and available versions. If it differs, it is a major update. If the first digit stays the same but the second differs, it is a minor update. Everything else counts as a patch update.


#!/usr/bin/env bash
# classify.sh — categorize version jumps as patch, minor, or major
set -euo pipefail

classify_bump() {
  local current="$1" latest="$2"
  local cur_major cur_minor lat_major lat_minor

  IFS='.' read -r cur_major cur_minor _ <<< "${current#v}"
  IFS='.' read -r lat_major lat_minor _ <<< "${latest#v}"

  if [[ "$cur_major" != "$lat_major" ]]; then
    echo "major"
  elif [[ "$cur_minor" != "$lat_minor" ]]; then
    echo "minor"
  else
    echo "patch"
  fi
}

while IFS=$'\t' read -r name current latest source; do
  bump="$(classify_bump "$current" "$latest")"
  printf '%s\t%s\t%s\t%s\t%s\n' "$name" "$current" "$latest" "$source" "$bump"
done < /tmp/normalized-outdated.tsv > /tmp/classified-outdated.tsv

5. Factoring in known security advisories

Beyond plain version status, a complete dependency update checker also pulls information about known security advisories. composer audit --format=json and npm audit --json each return a list of affected packages with severity. This information should be clearly highlighted in the report, regardless of whether the affected package was otherwise classified as only slightly outdated.

An important difference from plain version checking: a package can be security critical even though the version jump itself is only a patch update. A good dependency update checker therefore treats security advisories as their own, prioritized category, which always appears first in the report regardless of the bump classification.


#!/usr/bin/env bash
# audit.sh — pull known security advisories from both ecosystems
set -euo pipefail

audit_composer() {
  local project_dir="$1"
  (cd "$project_dir" && composer audit --format=json 2>/dev/null) || true
}

audit_npm() {
  local project_dir="$1"
  (cd "$project_dir" && npm audit --json 2>/dev/null) || true
}

readonly PROJECT_DIR="${1:?Usage: audit.sh <project-dir>}"

echo "=== Composer security advisories ==="
audit_composer "$PROJECT_DIR" | jq -r '.advisories // {} | to_entries[]? | .key'

echo "=== npm security advisories ==="
audit_npm "$PROJECT_DIR" | jq -r '.vulnerabilities // {} | to_entries[]? | "\(.key): \(.value.severity)"'

6. Generating a readable report

Raw TSV lines are unsuitable for daily use. A dependency update checker should turn the classified data into a readable Markdown document, organized with security advisories first, then major, minor, and finally patch updates. This order reflects actual urgency and spares readers from having to pick out the relevant lines from a long list themselves.

The Markdown output of a dependency update checker can be posted directly as a comment on a GitLab or GitHub issue, which integrates the report immediately into the existing team workflow without needing to run an additional dashboard.


#!/usr/bin/env bash
# generate-report.sh — turn classified data into a readable markdown report
set -euo pipefail

{
  echo "# Dependency Update Report — $(date +%F)"
  echo
  for bump in major minor patch; do
    echo "## ${bump^} updates"
    echo
    echo "| Package | Current | Latest | Source |"
    echo "|---|---|---|---|"
    awk -F'\t' -v b="$bump" '$5 == b { printf "| %s | %s | %s | %s |\n", $1, $2, $3, $4 }' \
      /tmp/classified-outdated.tsv
    echo
  done
} > dependency-report.md

echo "[OK] Report written to dependency-report.md" >&2

7. Notification via Slack webhook

A report that only sits as a file on disk is rarely looked at actively. A dependency update checker only gains real value through active notification, for example via a Slack incoming webhook that posts a short summary with the number of security advisories and major updates directly into the team channel.

A simple curl call with a JSON payload is enough for the message itself. It is important to keep the message compact and only link to the full report, instead of squeezing the entire table into the chat, which tends to hurt overview rather than help it.


#!/usr/bin/env bash
# notify-slack.sh — post a compact summary to a Slack channel
set -euo pipefail

readonly WEBHOOK_URL="${SLACK_WEBHOOK_URL:?Set SLACK_WEBHOOK_URL}"

security_count="$(grep -c "CVE" dependency-report.md || true)"
major_count="$(awk -F'\t' '$5 == "major"' /tmp/classified-outdated.tsv | wc -l)"

payload=$(jq -n \
  --arg sec "$security_count" \
  --arg maj "$major_count" \
  '{ text: ("Dependency check: \($sec) security advisories, \($maj) major updates pending. See dependency-report.md for details.") }')

curl -sf -X POST -H 'Content-Type: application/json' -d "$payload" "$WEBHOOK_URL" > /dev/null
echo "[OK] Slack notification sent"

8. Automation as a cron job and CI job

For the dependency update checker to actually run regularly, it belongs either in a cron job on the deployment server or as a scheduled job in the CI pipeline. A weekly run, for instance every Monday morning, gives the team enough lead time to react to critical updates before the next release window.

Important for the dependency update checker in a CI context: the job should never fail the build just because outdated packages were found, unless it is a critical security advisory. The task is informational, not blocking, otherwise the team quickly loses patience with constantly red pipelines over uncritical patch updates.

9. Dependency update checker compared to alternatives

There are SaaS solutions like Dependabot or Renovate that automate similar tasks and even solve them with automatic pull requests. A self built dependency update checker in Bash, by contrast, offers full control over format, channel, and criteria, without depending on an external service or its rate limits.

Approach Automatic PRs Control over format Suited for
Bash dependency update checker No, reporting only Full Internal reports, Slack integration
Dependabot Yes, automatic Limited configurability GitHub native projects
Renovate Yes, automatic Extensively configurable Large monorepos, many ecosystems
Manual review No No structure Very small projects

For teams who want to decide themselves when and how an update gets rolled out, a dependency update checker without automatic PRs is often the more deliberate choice, especially in sensitive production environments like Magento shops with many custom extensions.

Mironsoft

Shell automation, dependency management, and security tooling

A dependency update checker for your projects?

We build a tailored dependency update checker for Composer and npm, with security auditing, classification, and automatic Slack notification.

Script development

A custom dependency update checker for your stack

Security auditing

Detect known security advisories in Composer and npm packages

Reporting

Weekly reports with Slack or email integration

10. Summary

A dependency update checker in Bash combines the built in commands composer outdated and npm outdated into one unified, prioritized overview of outdated packages. Classification by semantic versioning and factoring in security audits ensure the truly urgent cases do not get lost in a long list.

The decisive lever lies in automation: a dependency update checker that runs weekly and actively ships results via Slack prevents outdated dependencies from going unnoticed for months. For teams with multiple PHP and JavaScript projects, this significantly reduces manual review effort without handing control over the update timing to an external SaaS solution.

Dependency update checker in Bash, the essentials at a glance

Data source

composer outdated --format=json and npm outdated --json as a structured foundation.

Normalization

jq unifies both formats into one shared line structure, independent of the ecosystem.

Prioritization

Security advisories first, then major, minor, and patch, each classified by semantic versioning.

Notification

A compact Slack message linking to the full markdown report, weekly via cron or CI.

11. FAQ: Dependency update checker for Composer and npm

1What is a dependency update checker?
A script that detects, classifies, and summarizes outdated and insecure dependencies.
2Why JSON instead of table format?
JSON stays structurally more stable across versions than the human readable output.
3Why does npm outdated report an error?
Intentional behavior for outdated packages found, must be explicitly caught in the script.
4How are both formats merged?
Via jq, normalizing both JSON structures into a shared line format.
5How are version jumps classified?
By semantic versioning into major, minor, and patch based on the differing version digit.
6How do security advisories factor in?
Via composer audit and npm audit, always prioritized ahead of plain version classification.
7Should the CI build fail?
Only for critical advisories, otherwise the check should stay purely informational.
8How does the team learn the result?
Via a compact Slack message with a link to the full report.
9How often should it run?
Weekly, to give enough lead time before the next release window.
10Better than Dependabot?
Not fundamentally, but with full control over format and notification without an external service.