composer / semver

Semantic versioning utilities with the addition of version constraints parsing and checking.
MIT License
3.15k stars 76 forks source link

semantic version constraint #62

Closed freshp closed 6 years ago

freshp commented 6 years ago

Hi,

I am a little bit confused about the version constraint. In different other packages (https://docs.npmjs.com/misc/semver) and tools (https://github.com/jubianchi/semver-check) the previous constraints work in special ways.

Example from the known behavoir:

~1.2.3 => >=1.2.3 <1.3.0 ~1.2 => >=1.2.0 <1.3.0 ~0.2.3 => >=0.2.3 <0.3.0 ~0.2 => >=0.2.0 <0.3.0

^1.2.3 => >=1.2.3 <2.0.0 ^1.2 => >=1.2.0 <2.0.0 ^0.2.3 => >=0.2.3 <0.3.0 ^0.2 => >=0.2.0 <0.3.0

Example with composer:

~1.2.3 => >=1.2.3 <1.3.0 ~1.2 => >=1.2.0 <2.0.0 ~0.2.3 => >=0.2.3 <0.3.0 ~0.2 => >=0.2.0 <1.0.0

^1.2.3 => >=1.2.3 <2.0.0 ^1.2 => >=1.2.0 <2.0.0 ^0.2.3 => >=0.2.3 <0.3.0 ^0.2 => >=0.2.0 <0.3.0

* difference

To check it, I was adding the following test cases to the satisfiedByProvider in the SemverTest.php

/**
 * @return array
 */
public function satisfiedByProvider()
{
    return array(
        array(
            '~1.0',
            array('1.0', '1.2', '1.9999.9999', '2.0', '2.1', '0.9999.9999'),
            array('1.0', '1.2', '1.9999.9999'),
        ),
        array(
            '>1.0 <3.0 || >=4.0',
            array('1.0', '1.1', '2.9999.9999', '3.0', '3.1', '3.9999.9999', '4.0', '4.1'),
            array('1.1', '2.9999.9999', '4.0', '4.1'),
        ),
        array(
            '^0.2.0',
            array('0.1.1', '0.1.9999', '0.2.0', '0.2.1', '0.3.0'),
            array('0.2.0', '0.2.1'),
        ),
        array(
            '~1.2.3',
            array('1.2.2', '1.2.3', '1.2.4', '1.3.0', '1.4.0', '2.0.0'),
            array('1.2.3', '1.2.4'),
        ),
        array(
            '~1.2',
            array('1.2.2', '1.2.3', '1.2.4', '1.3.0', '1.4.0', '2.0.0'),
            array('1.2.2', '1.2.3', '1.2.4', '1.3.0', '1.4.0'),
        ),
        array(
            '~0.2.3',
            array('0.2.2', '0.2.3', '0.2.4', '0.3.0', '0.4.0', '1.0.0'),
            array('0.2.3', '0.2.4'),
        ),
        array(
            '~0.2',
            array('0.2.2', '0.2.3', '0.2.4', '0.3.0', '0.4.0', '1.0.0'),
            array('0.2.2', '0.2.3', '0.2.4', '0.3.0', '0.4.0'),
        ),
        array(
            '^1.2.3',
            array('1.2.2', '1.2.3', '1.2.4', '1.3.0', '1.4.0', '2.0.0'),
            array('1.2.3', '1.2.4', '1.3.0', '1.4.0'),
        ),
        array(
            '^1.2',
            array('1.2.2', '1.2.3', '1.2.4', '1.3.0', '1.4.0', '2.0.0'),
            array('1.2.2', '1.2.3', '1.2.4', '1.3.0', '1.4.0'),
        ),
        array(
            '^0.2.3',
            array('0.2.2', '0.2.3', '0.2.4', '0.3.0', '0.4.0', '1.0.0'),
            array('0.2.3', '0.2.4'),
        ),
        array(
            '^0.2',
            array('0.2.2', '0.2.3', '0.2.4', '0.3.0', '0.4.0', '1.0.0'),
            array('0.2.2', '0.2.3', '0.2.4'),
        ),
    );
}

At the end I dont really know, if its a bug, but its a difference, which confuses me. Is it a conscious decision? If it is that, why?

Many thanks in advance for your help.

stof commented 6 years ago

The ~ operator in composer does not come from the npm operator but from the Ruby bundler ~> one (it works exactly the same than this operator). The initial version of composer was much more inspired by bundler than by npm (which was much less widespread at that time). Having the ^ semver operator behaving the same than in npm is something which was added later. And changing the behavior of the existing operator is impossible (it would be a BC break for the whole ecosystem, which is not acceptable).

The semver spec (https://semver.org/) does not define version constraints, only versions. So these operators are defined by each package manager, and npm and composer made different choices for ~.

Btw, the ~1.2.0 constraint means >=1.2.0 <1.3.0 in Composer (and also in npm, as ~ means "increment the minor version" in npm)

freshp commented 6 years ago

I am fine with that, but I was searching for a reasons why this happens.

The result for me would be, that I should use the complete (~major.minor.patch) version for my requirements. So I do not have any changes at all.

Thanks for the explanation!

stof commented 6 years ago

@freshp using exact-match requirements is not a good idea, as it means that you will have to update your constraints each time to get even security fixes and bug fixes rather than just running composer update. Locking dependencies to have the same versions for your whole team and for the prod is the what the lock file is for (and anywya, your dependencies are probably not using exact matches for their own deps, which would be a pain)

freshp commented 6 years ago

For sure. I forgot to mention the tilde ~major.minor.patch for example ~1.2.3

In my projects I try to use it with a lock file and the explained way, to get all patches with an update. In packages I try to use for example ^1.2 to get all minor changes without any BC break and I do not commit a lock file.