π GitHub Action that validates npm package version increments in pull requests to ensure proper semantic versioning
This action prevents developers from forgetting to bump package.json version before merging PRs that contain code changes, which would cause publishing issues later.
- π― Smart file detection - Only runs when JavaScript/TypeScript/package files are modified
- π§ Intelligent dependency checking - Distinguishes between actual dependency changes vs metadata-only changes in package.json and package-lock.json
- π§ Configurable devDependencies handling - Choose whether devDependency changes should trigger version bumps
- π Semantic versioning validation - Ensures new version is higher than previous release
- π·οΈ Git tag comparison - Compares against the latest git tag
- π Shallow clone compatible - Automatically fetches tags, works with default checkout
- π First release support - Gracefully handles repositories with no previous tags
- π JavaScript action - Fast execution with Node.js runtime
- π Clear messaging - Provides detailed success/error messages with emojis
- βοΈ Configurable - Supports custom package.json paths, tag prefixes, and dependency policies
- Node.js project with
package.json
- Git tags following semantic versioning (e.g.,
v1.0.0
,v2.1.3
) - Used in pull request workflows
Add this step to your workflow file (e.g., .github/workflows/ci.yml
):
name: CI
on:
pull_request:
branches: [main]
jobs:
version-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: joshjohanning/npm-version-check-action@v1
- uses: joshjohanning/npm-version-check-action@v1
with:
package-path: 'packages/core/package.json' # Custom package.json path
tag-prefix: 'v' # Tag prefix (default: 'v')
skip-files-check: 'false' # Always run, don't check files
include-dev-dependencies: 'true' # Require version bump for devDependencies
name: CI
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Check version increment
uses: joshjohanning/npm-version-check-action@v1
with:
package-path: 'package.json'
tag-prefix: 'v'
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
Input | Description | Required | Default |
---|---|---|---|
package-path |
Path to package.json file (relative to repository root) | No | package.json |
tag-prefix |
Prefix for version tags (e.g., "v" for v1.0.0) | No | v |
skip-files-check |
Skip checking if JS/package files changed (always run version check) | No | false |
include-dev-dependencies |
Whether devDependency changes should trigger version bump requirement | No | false |
Output | Description |
---|---|
version-changed |
Whether the version was changed (true /false ) |
current-version |
Current version from package.json |
previous-version |
Previous version from latest git tag |
- name: Check version
id: version-check
uses: joshjohanning/npm-version-check-action@v1
- name: Print version info
run: |
echo "Version changed: ${{ steps.version-check.outputs.version-changed }}"
echo "Current version: ${{ steps.version-check.outputs.current-version }}"
echo "Previous version: ${{ steps.version-check.outputs.previous-version }}"
- Smart File Change Detection: Analyzes which files were modified in the PR
- JavaScript/TypeScript files trigger version checks
- Package files (
package.json
,package-lock.json
) undergo intelligent dependency analysis
- Intelligent Dependency Analysis: For package files, distinguishes between:
- Functional changes: Actual dependency additions, updates, or removals that affect functionality
- Metadata changes: Version bumps, description updates, scripts changes, or devDependency changes that don't affect runtime
- Version Extraction: Reads the current version from
package.json
- Tag Comparison: Fetches the latest git tag and compares versions
- Semantic Validation: Ensures the new version is higher than the previous release
- Clear Feedback: Provides success or error messages with actionable hints
The action intelligently handles different types of file changes:
.js
- JavaScript files.ts
- TypeScript files.jsx
- React JavaScript files.tsx
- React TypeScript files
package.json
- Only triggers version check for dependency changes, not metadata- β
Triggers check: Changes to
dependencies
,peerDependencies
,optionalDependencies
,bundleDependencies
- β
Triggers check (configurable): Changes to
devDependencies
wheninclude-dev-dependencies: true
- β Skips check: Changes to
version
,description
,scripts
,author
, etc.
- β
Triggers check: Changes to
package-lock.json
- Smart handling based on devDependencies configuration- β Always triggers check: Production dependency changes (new packages, version updates, integrity changes)
- π Configurable behavior: When only devDependencies changed in package.json:
- β Skips check if
include-dev-dependencies: false
(default) - package-lock.json changes are ignored - β
Triggers check if
include-dev-dependencies: true
- package-lock.json changes are analyzed
- β Skips check if
- β Skips check: Pure metadata changes (version bumps, format updates)
When include-dev-dependencies: false
(default) and only devDependencies change in package.json:
- The action completely skips package-lock.json analysis
- This prevents false positives where massive lock file changes from dev dependency updates incorrectly trigger version bump requirements
- Much simpler and more reliable than trying to filter dev dependencies from complex lock file structures
This intelligent approach prevents unnecessary version bumps when only non-functional changes are made.
Previous | Current | Result |
---|---|---|
1.0.0 |
1.0.1 |
β Valid (patch) |
1.0.0 |
1.1.0 |
β Valid (minor) |
1.0.0 |
2.0.0 |
β Valid (major) |
1.0.0 |
1.0.0 |
β Same version |
1.1.0 |
1.0.5 |
β Lower version |
For monorepos with multiple packages:
- uses: joshjohanning/npm-version-check-action@v1
with:
package-path: 'packages/frontend/package.json'
- uses: joshjohanning/npm-version-check-action@v1
with:
package-path: 'packages/backend/package.json'
If your tags don't use the v
prefix:
- uses: joshjohanning/npm-version-check-action@v1
with:
tag-prefix: 'release-' # For tags like 'release-1.0.0'
To always validate version regardless of changed files:
- uses: joshjohanning/npm-version-check-action@v1
with:
skip-files-check: 'true'
By default, devDependencies
changes don't trigger version bump requirements since they typically don't affect production functionality. The action uses smart logic to handle this configuration:
- uses: joshjohanning/npm-version-check-action@v1
with:
include-dev-dependencies: 'false' # Default - devDeps don't require version bump
What happens with this setting:
- π― package.json: Only production dependencies (
dependencies
,peerDependencies
, etc.) trigger version checks - π« package-lock.json: When only devDependencies changed in package.json, lock file changes are completely ignored
- β Result: No false positives from massive lock file changes due to dev dependency updates
For libraries where build tools/devDependencies can affect the published package:
- uses: joshjohanning/npm-version-check-action@v1
with:
include-dev-dependencies: 'true' # devDeps changes require version bump
What happens with this setting:
- β package.json: Both production AND development dependencies trigger version checks
- β package-lock.json: All dependency changes are analyzed, including dev dependency effects
- Library packages: Where build tools, bundlers, or transpilers can affect the final output
- Strict versioning policies: Teams that want every dependency change tracked
- CI/CD sensitive packages: Where test runners or build scripts changes impact deliverables
This is normal for the first release. The action will pass and allow the PR to proceed.
Ensure your package.json
has a valid version
field:
{
"name": "my-package",
"version": "1.0.0"
}
The action automatically fetches git tags to work with shallow clones. If this warning appears, it means there was an issue fetching tags, but the action will continue with limited functionality. This is rare and usually indicates network or permission issues.
If you made changes to devDependencies
and expected a version bump requirement:
- Check the default behavior: By default,
devDependencies
changes don't require version bumps - Configure if needed: Set
include-dev-dependencies: 'true'
to require version bumps for devDependency changes - Review smart detection: The action distinguishes between functional dependency changes and metadata-only changes
This is the expected behavior with the default configuration! π
- β
Working as designed: When
include-dev-dependencies: false
(default), massive package-lock.json changes from dev dependency updates are intentionally ignored - π« No false positives: The action completely skips package-lock.json analysis when only devDependencies changed
- π― Smart logic: This prevents the "I'm only updating devDependencies :(" problem
- βοΈ Configurable: Set
include-dev-dependencies: true
if you want dev dependency changes to require version bumps
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
If you have any questions or run into issues, please open an issue on GitHub.