Also poss a side-effect of `box install` not havin...
# box-products
a
Also poss a side-effect of
box install
not having an equiv of a
composer.lock
/
package-lock.json
file but
box install
changed the version constraint on testbox I had said to use (
"^4.2.1+400"
) to... um... a version constraint based on the version it actually installed:
"^4.5.0+5"
. Now I get that version
4.5.0+5
matches the constraint I specified, but looks to me like
box install
doesn't quite understand the differences between constraints and versions. It should not be updating my constraint.
(again... it's not a prob for what I'm doing, but it poss ought to be looked at)
d
Just did a quick test and works as I'd expect it to:
Copy code
$ box install testbox@^4.2.1+400
 √ | Installing package [forgebox:testbox@^4.2.1+400]
   | √ | Installing package [forgebox:cbstreams@^1.5.0]
   | √ | Installing package [forgebox:mockdatacfc@^3.3.0+22]

$ cat box.json 
{
    "dependencies":{
        "testbox":"^4.2.1+400"
    },
    "installPaths":{
        "testbox":"testbox/"
    }
}

$ box install
 √ | Installing ALL dependencies
   | √ | Installing package [forgebox:testbox@^4.2.1+400]

$ cat box.json 
{
    "dependencies":{
        "testbox":"^4.2.1+400"
    },
    "installPaths":{
        "testbox":"testbox/"
    }
}

$ box install testbox
 √ | Installing package [forgebox:testbox]

$ cat box.json 
{
    "dependencies":{
        "testbox":"^4.5.0+5"
    },
    "installPaths":{
        "testbox":"testbox/"
    }
}
(It has only bumped the version in box.json when I have explicitly asked to install the latest testbox). Do you see the same @Adam Cameron? ^^ I just ran that in an empty dir for quick test.
a
when I have explicitly asked to install the latest testbox
That is not what you did though:
Copy code
box install testbox
That does not say anything about versions one way or the other. Here are my tests. First with `box install`:
Copy code
root@37d6a8a217c4:/var/www# ls
Application.cfc  LICENSE  README.md  box.json  cfml  docker  test  var
root@37d6a8a217c4:/var/www# cat box.json
{
    "dependencies":{
    },
    "devDependencies":{
        "testbox":"^4.2.1+400"
    },
    "installPaths":{
        "testbox":"testbox/"
    },
    "testbox":{
        "runner":"<http://localhost:8888/test/runTests.cfm?reportFormat=testbox.system.reports.TextReporter>"
    }
}
root@37d6a8a217c4:/var/www# box install
 √ | Installing ALL dependencies
   | √ | Installing package [forgebox:testbox@^4.2.1+400]
   |   | √ | Installing package [forgebox:cbstreams@^1.5.0]
   |   | √ | Installing package [forgebox:mockdatacfc@^3.3.0+22]
root@37d6a8a217c4:/var/www# cat box.json
{
    "dependencies":{
    },
    "devDependencies":{
        "testbox":"^4.2.1+400"
    },
    "installPaths":{
        "testbox":"testbox/"
    },
    "testbox":{
        "runner":"<http://localhost:8888/test/runTests.cfm?reportFormat=testbox.system.reports.TextReporter>"
    }
}
root@37d6a8a217c4:/var/www#
It doesn't monkey with my constraint.
Second, with `box install testbox`:
Copy code
root@37d6a8a217c4:/var/www# rm -rf testbox
root@37d6a8a217c4:/var/www# ls
Application.cfc  LICENSE  README.md  box.json  cfml  docker  test  var
root@37d6a8a217c4:/var/www# cat box.json
{
    "dependencies":{
    },
    "devDependencies":{
        "testbox":"^4.2.1+400"
    },
    "installPaths":{
        "testbox":"testbox/"
    },
    "testbox":{
        "runner":"<http://localhost:8888/test/runTests.cfm?reportFormat=testbox.system.reports.TextReporter>"
    }
}
root@37d6a8a217c4:/var/www# box install testbox
 √ | Installing package [forgebox:testbox]
   | √ | Installing package [forgebox:cbstreams@^1.5.0]
   | √ | Installing package [forgebox:mockdatacfc@^3.3.0+22]
root@37d6a8a217c4:/var/www# cat box.json
{
    "dependencies":{},
    "devDependencies":{
        "testbox":"^4.5.0+5"
    },
    "installPaths":{
        "testbox":"testbox/"
    },
    "testbox":{
        "runner":"<http://localhost:8888/test/runTests.cfm?reportFormat=testbox.system.reports.TextReporter>"
    }
}
root@37d6a8a217c4:/var/www#
This does monkey with my constraint. It shouldn't. Irrespective of what version it installs, it should never monkey with my version constraint. It's a constraint, it's not "the actual version". That is not the information being imparted there.
d
`box install testbox`says install the latest version. That is what the docs say.
and it should absolutely change what's in your box.json
either
box install
to install all my packages at the versions specificed in my box.json. OR,
box install <somepackage>
to install a specific package - either latest version if no version specified, or a specific version/range, etc. if specified.
npm works the same way fwiw:
Copy code
$ npm install int@0.1.2

added 1 package from 3 contributors and audited 1 package in 2.013s

$ cat package-lock.json
{
  "requires": true,
  "lockfileVersion": 1,
  "dependencies": {
    "int": {
      "version": "0.1.2",
      "resolved": "<https://registry.npmjs.org/int/-/int-0.1.2.tgz>",
      "integrity": "sha512-zmTKKSGQnruBcvkVdI+dBCE/JUMUou9kCNOpdiQb70kXpHtMM9LZo2A5sXlFwS0QrVOwSbgmsdYNoIxaQhxB8A=="
    }
  }
}

$ npm install int

+ int@0.2.0
updated 1 package and audited 1 package in 1.333s

$ cat package-lock.json
{
  "requires": true,
  "lockfileVersion": 1,
  "dependencies": {
    "int": {
      "version": "0.2.0",
      "resolved": "<https://registry.npmjs.org/int/-/int-0.2.0.tgz>",
      "integrity": "sha512-DpVz1YLYteAWaNsgbDXzzggi+7dplHBCWXqTHp85erZ6qJjDqbM+LC29hkCBC+zeHfFd7ig8gGYbcqLpJdcuLQ=="
    }
  }
}
^^ i.e. npm updated it because I asked it to just install the latest
a
Again, you are not demonstrating what you are saying. Firstly: that's not package.json, it's package-lock.json. Two different things. And again
0.1.2
and
0.2.0
are versions, not version constraints. package.json: specifies constraints package-lock.json: specifies versions. npm does not update package.json. Because it... has been implemented properly. I am actually surprised that
npm install
works that way though. I come from a php environment and using composer, and
composer install
first time around reads the constraint (from
composer.json
) and installs the most recent version that fulfils that constraint, and writes that version to
composer.lock
. Subsequent calls to
composer install
will continue to use the exact version specified in
composer.lock
, even if there are more recent versions available within the constraint. One needs to call
composer update
to recheck for more recent versions. Still: fair cop that
box install
will grab a more recent version than previously had been installed (same as npm), even if that's not how I'd do it (Composer seems better than npm in this regard, so I'm with Composer on this one). It still shouldn't monkey with the box.json constraints though.
Q: if we revised the
npm
example above to be:
Copy code
npm install
# all deps are installed at their latest constraint-fulfilling versions

# time passes, a dependency version has been updated

npm install
Does that second one just install the versions already in
package-lock.json
, or does it also look for updates to everything? IE I guess I'm asking if the update process only happens when one goes
npm install dependency_name
or is that
npm install
default behaviour?
d
ah interesting, I did not have a
package.json
- was just running in an empty dir and it then updated to latest in
package-lock.json
which it created (it does not create a package.json).
so yeah, npm, with its locking - works the way you say. BUT - box works the way it is intended to work, and the way it is documented. Which is to say:
box install package
is equivalent to
box install package@stable
. And it should be expected to do what it is doing.
Would be interesting to see if there are any plans for a package locking mechanism in the future and what that might mean for the expected behaviour.
a
Indeed. It's great that it does what it intends to do, ie: the docs says "it does x". However I think that it actually intends to do x is the problem here. It should be intending to do y. IE: it should not be... conflating the concepts of constraints and versions. They've missed a trick in that a package manager needs the guidance (something.json) and the current state (something-lock.json or something.lock or whatever). It needs both. It can't have just the one. We can document that a function called
pi()
returns
3
, and celebrate that when we call
pi()
we get
3
because that's exactly what it says it does in the docs. But
π
is an actual thing, and it's not
3
. --- I've had the requirement for handling version locking with @bdw429s previously, and he is aware of the shortfall. What was new to me (or I had forgotten I once knew? Unsure. It seemed surprising to me when I saw it this time, anyhow) was that it was changing box.json.
(and all that said, that's not to say I am off the hook for not having RTFMed 😉
d
cmon, software is not pi. There is no universal truth. Just an evolving (hopefully) sophistication. package-lock.json was not around when box package management was born am fairly sure. We can also understand something and make a different choice. For better or worse. Am curious re
box install somepackage
and expecting it to be constrained though. This is useful for just installing what you have defined of a single package, rather than installing what you have defined for all packages right? I certainly could have used that in the past, but absolutely haven't as I have always expected it to be equivalent to
box install somepackage@stable
.
Potentially hard to change track on that with backward compat.
what could be interesting, potentially - is adding a
box install <package>
argument/convention that says "install the version as specified in my box.json" file (only IF @bdw429s was concerned enough around breaking changes). That way, the package manager catches up with features of others, albeit with a slightly different convention.
Copy code
box install testbox@boxjson
box install testbox --constrained
^^ both quite yucky, I agree
a
composer doesn't add packages with
composer install
. All it does is to read
composer.lock
and install everything listed there. If there's no
composer.lock
, it will start with
composer.json
. If there's a
composer.lock
and a
composer.json
and they don't match, it will give a warning, but will still only refer to
composer.lock
. If one goes
composer require someone/something
, then it'll write an entry to
composer.json
with a
^
constraint of the current available version of the package, install that, and write that version to
composer.lock
. It also has a
--dev
switch to make it a dev dependency, not a prod one. If one wishes to re-scan for newer versions (ignoring the ones in
composer.lock
) one does
composer update
or
composer update someone/something
. Again, this writes the updated version(s) to
composer.lock
again. TBH, the package installer app doesn't need to be backwards compatible does it? If they want to add version-locking, that in itself is a big change in how the thing works, so the syntax for using it can / must change anyhow. Maybe for a single transition version there could be a
--oldskool
switch one gives to the
box
commands and it uses the old syntax, but that is marked as deprecated immediately, with a retirement date of six months hence or something.
d
Yeah, I don't necessarily have a problem with breaking it. But it should be considered at least.
^ created that JIRA issue
a
(NB: neither
composer install
nor
compose update
write to
composer.json
. Only
composer require
does.
I wish I used node / npm more now, so the curve to investigate it's minutiae wasn't so steep for me.
d
that's a nice subtlety also. I think box covers this with
--save=false
right? (that
composer
syntax is nice for sure, but I think so long as the features are available, then the syntax is less important.
a
It's important for ppl to understand why there's a need to have versions locked. In dev, one might want to mess around with newer versions of packages, but for an app being deployed to production, it's vital that the package versions are all known, so should use the exact versions listed in the lock file, and not go out looking for newer ones (as that's not an idempotent operation, so it could leave the application unstable if there's an incompatible version of a dependency).
d
Yes, absolutely agree.
a
yeah I only cite composer here at all cos it's the one that I know, and it's been around for ages & used by millions of devs daily, so it's pretty battle-hardened.
I'd have to go RTFM and mess around a lot to be able to speak about how npm does it, and why
d
And it makes sense to make the syntax as close as possible to existing hardened examples as you say.
a
I think box took its inspiration from npm, so that's the direction I'd recommend they continue.
(I might be wrong on that, but I recall Brad was unaware of composer even existing (I think) last time we spoke about it)
d
If a concept is useful, then it should be considered. how it is implemented should probably follow the existing design principals.
1
(but those design principals could/should also be revisited)
a
Yeah it's code not concrete 😄
Right. None of this is getting this blog article written.
d
ha ha
laters
a
See ya man. Always good to discuss stuff with ya
👏 1
👍 1
w
Interesting discussion! I think I totally agree. Never thought about the different meanings of constraints and versions, but that’s probably because I used commandbox too often, and npm and composer a lot less. But yes, you should be able to install exactly the same versions in production, and not some latest update which you missed in dev.
b
Would be interesting to see if there are any plans for a package locking mechanism in the future and what that might mean for the expected behaviour.
@domwatson Yes, Eric Peterson asks me about this every few months and Adam has as well. It's in the backlog and something I'll tackle when I have some time.
I don't know if it was linked above (I didn't see it) but Dom put in a ticket that I think is a great improvement and I've commented my feedback on that ticket: https://ortussolutions.atlassian.net/browse/COMMANDBOX-1477
I think box took its inspiration from npm,
This is correct
(I might be wrong on that, but I recall Brad was unaware of composer even existing (I think) last time we spoke about it)
This is incorrect. I don't live under a rock, mate 🙂 That said, I have never looked to composer for much guidance outside of checking out their terms and rules around package ownership in the past.
1
box install
doesn't quite understand the differences between constraints and versions
CommandBox absolutely knows the difference, except I don't tend to use the word "constraint", I'll usually say a "semnatic version range" which is perhaps more of an npm term. https://commandbox.ortusbooks.com/package-management/semantic-versioning#ranges When you specify an exact and full version such as
Copy code
install foobar@1.2.3
then there is no question what you want. That exact version is installed, and the exact version is stored in
box.json
and you will never get anything else. Each of the following examples would be considered a range-- meaning, go find the highest vesrion that satisfies the range
Copy code
install foobar
install foobar@be
install foobar@stable
install foobar@*
install foobar@1
install foobar@1.2
install foobar@1.x
install "foobar@>=2.0.0 <=2.5.0"
install "foobar@2.0.0 - 2.5.0"
install foobar@~1.2.3
install foobar@^1.2.3
install "foobar@1.0.0 || 2.0.0 || 3.0.0"
Above, you see examples of: • x ranges • greater/less than ranges • hyphen ranges • tilde ranges • caret ranges • logical OR
The test case for the Semnatic Version library I wrote was literally lifted right out of the docs/tests for the npm sem ver lib, so it was an exact copy (save a few modifications that I've added over the years for CommandBox's purposes)
So, no, CommandBox does not have any issues understanding the difference between an exact version and a range 🙂 The issue being described above was the misunderstanding that any missing version is defaulted to
stable
so
Copy code
install foobar
is the same as
Copy code
install foobar@stable
but I'm 100% ok with the suggestion in Dom's ticket to default it to the version or range found in the
box.json
for that package, if it exists.
👍 1