Skip to content

Commit

Permalink
fix(provider): fix npm provider to update package-lock.json and npm-s…
Browse files Browse the repository at this point in the history
…hrinkwrap.json if they exist

closes #804
  • Loading branch information
davidlday authored and Lee-W committed Aug 26, 2023
1 parent a81d73c commit 6063338
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 144 deletions.
49 changes: 17 additions & 32 deletions commitizen/providers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

import json
import jsonpath_ng # type: ignore
import re
from abc import ABC, abstractmethod
from pathlib import Path
Expand Down Expand Up @@ -160,25 +159,14 @@ def set(self, document: dict[str, Any], version: str):


class NpmProvider(JsonProvider):
"""
npm package.json version management
"""

filename = "package.json"


class Npm2Provider(VersionProvider):
"""
npm package.json and package-lock.json version management
"""

indent: ClassVar[int] = 2
package_filename = "package.json"
package_expression = "$.version"
lock_filename = "package-lock.json"
lock_expressions = ["$.version", "$.packages.''.version"]
shrinkwrap_filename = "npm-shrinkwrap.json"
shrinkwrap_expressions = ["$.version", "$.packages.''.version"]

@property
def package_file(self) -> Path:
Expand All @@ -199,7 +187,7 @@ def get_version(self) -> str:
package_document = json.loads(self.package_file.read_text())
return self.get_package_version(package_document)

def set_version(self, version: str):
def set_version(self, version: str) -> None:
package_document = self.set_package_version(
json.loads(self.package_file.read_text()), version
)
Expand All @@ -222,29 +210,26 @@ def set_version(self, version: str):
)

def get_package_version(self, document: dict[str, Any]) -> str:
expr = jsonpath_ng.parse(self.package_expression)
versions = expr.find(document)
if len(versions) == 0:
raise ValueError("No version found")
elif len(versions) > 1:
raise ValueError("Multiple versions found")
return versions[0].value # type: ignore

def set_package_version(self, document: dict[str, Any], version: str):
expr = jsonpath_ng.parse(self.package_expression)
document = expr.update(document, version)
return document["version"] # type: ignore

def set_package_version(
self, document: dict[str, Any], version: str
) -> dict[str, Any]:
document["version"] = version
return document

def set_lock_version(self, document: dict[str, Any], version: str):
for expression in self.lock_expressions:
expr = jsonpath_ng.parse(expression)
document = expr.update(document, version)
def set_lock_version(
self, document: dict[str, Any], version: str
) -> dict[str, Any]:
document["version"] = version
document["packages"][""]["version"] = version
return document

def set_shrinkwrap_version(self, document: dict[str, Any], version: str):
for expression in self.shrinkwrap_expressions:
expr = jsonpath_ng.parse(expression)
document = expr.update(document, version)
def set_shrinkwrap_version(
self, document: dict[str, Any], version: str
) -> dict[str, Any]:
document["version"] = version
document["packages"][""]["version"] = version
return document


Expand Down
19 changes: 9 additions & 10 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,16 +293,15 @@ But you can use any `commitizen.provider` entrypoint as value for `version_provi

Commitizen provides some version providers for some well known formats:

| name | description |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `commitizen` | Default version provider: Fetch and set version in commitizen config. |
| `scm` | Fetch the version from git and does not need to set it back |
| `pep621` | Get and set version from `pyproject.toml` `project.version` field |
| `poetry` | Get and set version from `pyproject.toml` `tool.poetry.version` field |
| `cargo` | Get and set version from `Cargo.toml` `project.version` field |
| `npm` | Get and set version from `package.json` `project.version` field |
| `npm2` | Get and set version from `package.json` `$.version` field, `package-lock.json` `$.version,$.packages.''.version` fields if the file exists, and `npm-shrinkwrap.json` `$.version,$.packages.''.version` fields if the file exists |
| `composer` | Get and set version from `composer.json` `project.version` field |
| name | description |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `commitizen` | Default version provider: Fetch and set version in commitizen config. |
| `scm` | Fetch the version from git and does not need to set it back |
| `pep621` | Get and set version from `pyproject.toml` `project.version` field |
| `poetry` | Get and set version from `pyproject.toml` `tool.poetry.version` field |
| `cargo` | Get and set version from `Cargo.toml` `project.version` field |
| `npm` | Get and set version from `package.json` `version` field, `package-lock.json` `version,packages.''.version` fields if the file exists, and `npm-shrinkwrap.json` `version,packages.''.version` fields if the file exists |
| `composer` | Get and set version from `composer.json` `project.version` field |

!!! note
The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin.
Expand Down
30 changes: 1 addition & 29 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ typing-extensions = { version = "^4.0.1", python = "<3.8" }
charset-normalizer = ">=2.1.0,<4"
# Use the Python 3.11 and 3.12 compatible API: https://github.com/python/importlib_metadata#compatibility
importlib_metadata = { version = ">=4.13,<7"}
jsonpath-ng = "^1.5.3"

[tool.poetry.group.dev.dependencies]
ipython = "^7.34"
Expand Down Expand Up @@ -89,7 +88,6 @@ cargo = "commitizen.providers:CargoProvider"
commitizen = "commitizen.providers:CommitizenProvider"
composer = "commitizen.providers:ComposerProvider"
npm = "commitizen.providers:NpmProvider"
npm2 = "commitizen.providers:Npm2Provider"
pep621 = "commitizen.providers:Pep621Provider"
poetry = "commitizen.providers:PoetryProvider"
scm = "commitizen.providers:ScmProvider"
Expand Down
87 changes: 16 additions & 71 deletions tests/test_version_providers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from __future__ import annotations
import json

import os
from pathlib import Path
Expand All @@ -15,7 +14,6 @@
CommitizenProvider,
ComposerProvider,
NpmProvider,
Npm2Provider,
Pep621Provider,
PoetryProvider,
ScmProvider,
Expand Down Expand Up @@ -279,99 +277,46 @@ def test_scm_provider_default_without_commits_and_tags(config: BaseConfig):
}
"""

NPM_NO_VERSION = """\
{
"name": "whatever"
}
"""

NPM_MULTIPLE_VERSIONS = """\
{
"name": "whatever",
"version": "0.1.0",
"version": "0.2.0",
}
"""

NPM2_PROVIDER = [
(
NPM_PACKAGE_JSON,
NPM_PACKAGE_EXPECTED,
None,
None,
None,
None,
),
(
NPM_PACKAGE_JSON,
NPM_PACKAGE_EXPECTED,
NPM_LOCKFILE_JSON,
NPM_LOCKFILE_EXPECTED,
None,
None,
),
(
NPM_PACKAGE_JSON,
NPM_PACKAGE_EXPECTED,
None,
None,
NPM_LOCKFILE_JSON,
NPM_LOCKFILE_EXPECTED,
),
@pytest.mark.parametrize(
"pkg_lock_content,pkg_lock_expected,pkg_shrinkwrap_content,pkg_shrinkwrap_expected",
(
NPM_PACKAGE_JSON,
NPM_PACKAGE_EXPECTED,
NPM_LOCKFILE_JSON,
NPM_LOCKFILE_EXPECTED,
NPM_LOCKFILE_JSON,
NPM_LOCKFILE_EXPECTED,
(None, None, None, None),
(NPM_LOCKFILE_JSON, NPM_LOCKFILE_EXPECTED, None, None),
(None, None, NPM_LOCKFILE_JSON, NPM_LOCKFILE_EXPECTED),
(
NPM_LOCKFILE_JSON,
NPM_LOCKFILE_EXPECTED,
NPM_LOCKFILE_JSON,
NPM_LOCKFILE_EXPECTED,
),
),
]


@pytest.mark.parametrize(
"pkg_content,pkg_expected,pkg_lock_content,pkg_lock_expected,pkg_shrinkwrap_content,pkg_shrinkwrap_expected",
NPM2_PROVIDER,
)
def test_npm2_provider(
def test_npm_provider(
config: BaseConfig,
chdir: Path,
pkg_content: str,
pkg_expected: str,
pkg_lock_content: str,
pkg_lock_expected: str,
pkg_shrinkwrap_content: str,
pkg_shrinkwrap_expected: str,
):
pkg = chdir / "package.json"
pkg.write_text(dedent(pkg_content))
pkg.write_text(dedent(NPM_PACKAGE_JSON))
if pkg_lock_content:
pkg_lock = chdir / "package-lock.json"
pkg_lock.write_text(dedent(pkg_lock_content))
if pkg_shrinkwrap_content:
pkg_shrinkwrap = chdir / "npm-shrinkwrap.json"
pkg_shrinkwrap.write_text(dedent(pkg_shrinkwrap_content))
config.settings["version_provider"] = "npm2"
config.settings["version_provider"] = "npm"

provider = get_provider(config)
assert isinstance(provider, Npm2Provider)
assert isinstance(provider, NpmProvider)
assert provider.get_version() == "0.1.0"

provider.set_version("42.1")
assert pkg.read_text() == dedent(pkg_expected)
assert pkg.read_text() == dedent(NPM_PACKAGE_EXPECTED)
if pkg_lock_content:
assert pkg_lock.read_text() == dedent(pkg_lock_expected)
if pkg_shrinkwrap_content:
assert pkg_shrinkwrap.read_text() == dedent(pkg_shrinkwrap_expected)


def test_npm2_exceptions(
config: BaseConfig,
):
config.settings["version_provider"] = "npm2"
provider = get_provider(config)
assert isinstance(provider, Npm2Provider)
with pytest.raises(ValueError):
provider.get_package_version(json.loads(NPM_NO_VERSION))
with pytest.raises(ValueError):
provider.get_package_version(json.loads(NPM_MULTIPLE_VERSIONS))

0 comments on commit 6063338

Please sign in to comment.