Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup NPM Packaging #214

Merged
merged 3 commits into from
Oct 11, 2018
Merged

Setup NPM Packaging #214

merged 3 commits into from
Oct 11, 2018

Conversation

leostera
Copy link
Collaborator

@leostera leostera commented Oct 10, 2018

Opening this PR for visibility/referencing, but I'll be modifying the history heavily until I get something I'm comfortable with.

Setting up esy seemed straightforward, although adding dependencies one by one manually is always annoying.

This is missing a few things:

  • Pin tyxml/markup/etc to their appropriate versions (some tests are failing due to sexp indentation)
  • Run on Travis for switch 4.02.3
  • Trim down package (@jordwalke's suggestion)
  • Publish from Travis for switch 4.02.3 on master branch tag

But so far I've managed to install the npm package locally and it runs alright 👍

ostera/odoc λ make npm
esy install
info install 0.2.11
esy make build
dune build
esy release
info release 0.2.11
dune build
info Creating npm release
# ...so much output here exporting packages
info Exporting ocaml-4.2.3005-a85217cf: done
info Configuring release
info Done!

ostera/odoc λ cd _release && npm --global install .
/usr/local/bin/odoc -> /usr/local/lib/node_modules/odoc/bin/odoc

> odoc@1.2.0 postinstall /usr/local/lib/node_modules/odoc
> node ./esyInstallRelease.js

importing: opam__slash__uutf-1.0.1-1e184456
# another good deal of imports here
importing: esy_ocaml__slash__substs-0.0.1-441fe663
Done!
+ odoc@1.2.0
updated 1 package in 4.38s

odoc/_release λ /usr/local/lib/node_modules/odoc/bin/odoc
Available subcommands: compile, html, html-fragment, support-files, css, compile-deps,
html-deps, compile-targets, html-targets
See --help for more information.

odoc/_release λ /usr/local/lib/node_modules/odoc/bin/odoc --version
%%VERSION%%

If you can please give it a go!

cc/ @aantron @rizo @lpw25 @ryyppy @grabbou @avsm @ulrikstrid

ref #195

@jordwalke
Copy link

HI, I'm curious if this is the kind of package that actually benefits from being a globally installable command line utility from npm? (I am not yet familiar with odoc but eager to learn).

I did notice that you did not trim out any of the binary artifacts, so here's an example of how to do that. It makes the prebuilt release much leaner: https://github.com/esy-ocaml/hello-reason/blob/master/package.json#L13

@dbuenzli
Copy link
Contributor

HI, I'm curious if this is the kind of package that actually benefits from being a globally installable command line utility from npm? (I am not yet familiar with odoc but eager to learn).

I'd say this is unlikely odoc which relies on cmt/cmti/cmi files is tied to the version of the compiler you used to generate these artefacts.

@leostera
Copy link
Collaborator Author

leostera commented Oct 10, 2018

Hej @jordwalke as it is right now using it at all requires opam, which is a hindrance if you're using an bsb-only setup.

I'll grant you that the current usage flow is far from ideal (as you can see from the suggested script in the readme), but having a release pipeline for npm makes it easier to iterate and gather feedback from the other side of the community.

@dbuenzli maybe we should make it clear that this npm package is intended for usage with the 4.02.3+buckle-master switch? I'm open to ideas to enforce this too.

And 👍 will trim down the package when I get home 📦

@aantron
Copy link
Contributor

aantron commented Oct 10, 2018

@Ostera I don't think this package requires a switch that users need to be aware of. IIRC we can just choose the proper compiler version in esy.json, so that the binary is built on 4.02.3, and then it can be used with BuckleScript.

I don't think we need an opam switch around at runtime.

@leostera
Copy link
Collaborator Author

@aantron not at runtime, but they won't be able to generate docs from .cmt[i]s built with a different switch.

@rizo
Copy link
Collaborator

rizo commented Oct 10, 2018

Once again working on BuckleScript and dune projects on the same machine is going to be hard with global installations...

@aantron
Copy link
Contributor

aantron commented Oct 10, 2018

@Ostera It's not the switch but the compiler version that matters (actually, the format of .cmt[i] files). As long as the OCaml version our build internally uses to build the odoc binary is the same as what's being used by BuckleScript (4.02.3), the user doesn't need to know about it. They only need an easy way of finding the binary, probably through NPM.

@leostera
Copy link
Collaborator Author

@rizo: Once again working on BuckleScript and dune projects on the same machine is going to be
hard with global installations...

You can always do local installs and ./node_modules/.bin/odoc

@aantron: It's not the switch but the compiler version that matters

My bad, still mix up the wording about it! This should work just as fine for any BuckleScript version.

@leostera leostera force-pushed the npm-packaging branch 2 times, most recently from 05d6dc2 to 2516057 Compare October 10, 2018 20:47
@jordwalke
Copy link

jordwalke commented Oct 10, 2018

@Ostera I don't think this package requires a switch that users need to be aware of. IIRC we can just choose the proper compiler version in esy.json, so that the binary is built on 4.02.3, and then it can be used with BuckleScript.

Yes, you can choose 4.02.3 in the esy.json file and things will work on any project that uses a 4.02.3 compiler including BuckleScript. I can just imagine that people are going to run into some issues when the try to run odoc on a local opam or esy project that uses a different version of the compiler. Still, it would be kind of cool to have a globally installed way to build docs. A couple of ideas/suggestions:

  • Make sure the error message is really friendly when the compiler version isn't what is expected.
  • Perhaps package up prebuilt binaries into a package names to avoid conflicts.

For the second point, what about making the prebuilt binaries under a package name of odoc-402. Ideally odoc would also build/install a binary named odoc402, and then you would list that in the releasedBinaries field. Then if all of that were true you could have multiple global npm prebuilt binaries existing globally, none of their package names conflicting, and none of the binaries they put in the global path conflicting. If it's too hard to change odoc to also produce a copy of the main binary under the name odoc402, then maybe at least make the hard dependency on compiler version apparent in the released binary package name.
I just wanted to avoid people getting a really bad error when they try to run odoc on a local Dune project on 4.06.
Thoughts?

#!/bin/bash

if [[ $ESY_BUILD == YES ]]; then
npm --global install esy

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andreypopp should this be pinned to some stable version?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, esy@0.3.x would be relatively safe (considering this project requires features new in 0.3.x, otherwise it's better to pin to 0.2.x).

.travis.yml Outdated

env:
- OCAML=4.02.3
- OCAML=4.02.3+buckle-master
- OCAML=4.02.3+buckle-master ESY_BUILD=YES ESY__CACHE=/home/travis/.esy
Copy link

@jordwalke jordwalke Oct 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you help me understand what this accomplishes? (Using buckle-master?) I've never understood why people ever use buckle-master for things. I've never come across a scenario when it was needed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to build odoc using this version of the compiler to be able to read .cmti files compiled with it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why do you need +buckle-master? Why can't you just use 4.02.3. Is the cmt data structure different for buck-master than for plain 4.02.3? I've used plain 4.02.3 to parse bs's .cmt files in the past without any trouble.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ostera AFAIK esy will use its own internal oap switch to build the odoc binary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aantron @jordwalke I left that in there because I was installing opam/ocaml even for the esy build. I realize now that it's not needed at all, so I will remove it and make the installation optional.

package.json Outdated
"Leo White <leo@lpw25.net>"
],
"esy": {
"test": "make test",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that this does nothing, you need to put it into "script.test" if you want to run esy test.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't using it anyway, will remove!

@leostera leostera force-pushed the npm-packaging branch 2 times, most recently from 59fc37c to c83921e Compare October 10, 2018 22:37
@leostera
Copy link
Collaborator Author

Alright! This looks a little tidier now, let's do another round of reviews ⚔️ — I took the liberty to optimize the CI process, not to have to wait ~7 mins on every change, but instead ~2. Feedback welcome there too (I'm open to submitting a separate PR for those changes if need be).

@aantron: Just like with the automatic publishing of docs, let's keep this publishing manual. Any advice on caching the opam/ocaml installations any futher?

@jordwalke, @andreypopp: Could you provide insights on how to properly cache the build directory of esy? I'm noticing that dune runs again, rebuilding and retesting the whole project rather than doing it incrementally.

@rizo: is there anything you'd like changed about this PR that would make you more comfortable with the way we would publish the odoc binary? I've been thinking about this and I think the approach I'd like best would be an odoc release that understands several compiler versions (so you only need one for every compiler), but I might be missing some underlying complexity of developing/building/distributing such a binary.

@ryyppy @grabbou @dbuenzli: would like to hear your thoughts on this too 🗣

@leostera
Copy link
Collaborator Author

@jordwalke: I just wanted to avoid people getting a really bad error when they try to run odoc on
a local Dune project on 4.06.

Shouldn't they be running it with esy dune build @doc? which should use the opam installed version that's appropriate for their compiler version, if I understood that particular esy usage.

npm --global install esy@0.3.x
esy --version
else
echo "Nothing to be done."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this else branch needed? Seeing this message in the log is not informative IMO.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looked strange to see the script marked as ran but no output whatsoever. Perhaps I can change the text to "Skipping esy installation." ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up joining the install scripts after removing the OCAML env variable from the esy build. This is no longer an issue.

.ci/install.sh Outdated
@@ -0,0 +1,11 @@
#!/bin/bash
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be some convention but I feel like this script should be called setup, init or install-deps.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like install-deps. Conveys a little more info about what's being installed.

- /home/travis/.opam
- /home/travis/.esy
- /home/travis/.nvm
- ./_build/default
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this desirable? Do we want to keep cache even for tests executions across builds?

Copy link

@jordwalke jordwalke Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same question. You only want to cache builds of your dependencies typically (the ~/.esy/ directory).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coming from bazel I'm used to letting the build tool cache very aggressively — assuming we trust dune to re-build and re-test incrementally, should this be a problem?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be fine with this if we cleaned the html _scratch directory between test executions. Otherwise the previously generated files that are not generated in next runs might interfere with the result (i.e., it should be an error in this case).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout! Seems like a good before_script. Let's see how it works.

Makefile Outdated
@@ -2,6 +2,19 @@
build :
dune build

.PHONY : npm
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make npm seems a bit ambigious to me. Again maybe this is some sort of convention but I’d call it npm-publish.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be called npm-publish, because, as I understand, it just packages things in a local _release directory. But it shouldn't be called npm either.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make npm-release ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-release has the danger of being interpreted as the verb, which makes it seem dangerous. How about npm-package?

esy build

.PHONY : npm-test
npm-test :
Copy link
Collaborator

@rizo rizo Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems very meta (ie a make command that calls esy that calls make again). How is it different to make test?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rizo This runs make test in the esy environment. It doesn't seem too bad.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we can just call esy make test, but I fear that from looking at the files we currently have it won't be obvious that this should be run (at some point) to make sure the build works as an npm package.

We can also document it in the contribution guidelines.

package.json Outdated
"name": "odoc",
"version": "1.2.0",
"description": "The OCaml/Reason Documentation Generator",
"main": "index.js",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, are you trying to sneak in some uncommited JS? 😛

Copy link
Contributor

@aantron aantron Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that will be a generated file.

EDIT: well, we are not targeting JS at all, so I'm not sure why we need this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated. Should be removed from the package.json!

@rizo
Copy link
Collaborator

rizo commented Oct 11, 2018

I might be missing some underlying complexity of developing/building/distributing such a binary

The ability to work with cmt/cmti/cmi files produced by different compiler versions is currently hard because of inconsistencies across typed parsetrees. We would need a typed version of ocaml-migrate-parsetree which doesn’t exist AFAIK. The ad-hoc approach would be too painfull.

@rizo
Copy link
Collaborator

rizo commented Oct 11, 2018

@jordwalke regarding your suggestion to add a version suffix to the binary name: it would avoid the conflict with the opam-installed version but I don’t understand how the build tools would find the needed versioned binaries..

@aantron
Copy link
Contributor

aantron commented Oct 11, 2018

The ability to work with cmt/cmti/cmi files produced by different compiler versions is currently hard because of inconsistencies across typed parsetrees. We would need a typed version of ocaml-migrate-parsetree which doesn’t exist AFAIK. The ad-hoc approach would be too painfull.

@rizo This might not actually be a problem for odoc, in principle.

odoc already has enough code to load Typedtrees from compiler versions 4.02-4.07, and largely convert them into its own representation. (src/model).

This leaves the remaining direct uses of Typedtree. Typedtree is used only in the loaders themselves cmt.ml, cmti.ml, and ident_env.ml. That's where (almost?) all of our conditional compilation is, too, but it doesn't look too bad.

It may be possible to link loaders for all OCaml versions into a single odoc binary, and reconcile them somehow. The big issue would be how to avoid duplicating the vast majority of the shared code. Perhaps we can do it with a functor or some preprocessing. We would also likely need to vendor a copy of the relevant files in compiler-libs for each version of the compiler.

@rizo
Copy link
Collaborator

rizo commented Oct 11, 2018

@aantron Thanks for clarifying that. I’m not sure I like the megabinary idea :) But having cross-version portability would be very nice indeed.

.ci/build.sh Outdated
@@ -0,0 +1,11 @@
#!/bin/bash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to rename directory .ci/ to test/ci/, so it doesn't have to be hidden, and doesn't clutter the GitHub repo.

.travis.yml Outdated

env:
- OCAML=4.02.3
- OCAML=4.02.3+buckle-master
- OCAML=4.02.3+buckle-master ESY_BUILD=YES ESY__CACHE=/home/travis/.esy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ostera AFAIK esy will use its own internal oap switch to build the odoc binary.

.travis.yml Outdated
- ./_build/default

install:
- ./.ci/install.sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend not breaking this up into files. If you'd like to move it to a shell script (for conditionals?), I suggest to create one shell script, and do things there. Perhaps one for building and one for installing, if caching happens between those two steps.

With many scripts, and some commands in the .travis.yml file, people have to look through a lot of little files to understand what is going on.

Copy link
Collaborator Author

@leostera leostera Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aantron a small problem with this approach is that the eval $(opam env) call has to be made from an individual line in the .travis.yml file to affect the rest of the build.

eval from within scripts only affects the current environment, not it's parent.

We can bundle the install.sh and install-esy.sh, but we'd need a separate script that is run after the eval to be able to output the ocaml/opam -[-]version commands.

install:
  - ./.ci/install.sh
  - "if [[ $OCAML ]]; then eval `opam config env`; fi"
  - ./.ci/print-versions.sh

Let me know what you think.

Makefile Outdated
@@ -2,6 +2,19 @@
build :
dune build

.PHONY : npm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be called npm-publish, because, as I understand, it just packages things in a local _release directory. But it shouldn't be called npm either.

esy build

.PHONY : npm-test
npm-test :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rizo This runs make test in the esy environment. It doesn't seem too bad.

package.json Outdated
"name": "odoc",
"version": "1.2.0",
"description": "The OCaml/Reason Documentation Generator",
"main": "index.js",
Copy link
Contributor

@aantron aantron Oct 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that will be a generated file.

EDIT: well, we are not targeting JS at all, so I'm not sure why we need this.

@jordwalke
Copy link

@jordwalke: I just wanted to avoid people getting a really bad error when they try to run odoc on
a local Dune project on 4.06.

Shouldn't they be running it with esy dune build @doc? which should use the opam installed version that's appropriate for their compiler version, if I understood that particular esy usage.

Perhaps but when BS upgrades to 4.06 the same thing will happen and we'll need to clearly make sure the user knows what version their globally installed odoc is compatible with. At least a good error message is in order. I don't have too much to add besides that. Happy to see odoc get into more hands either way.

@leostera
Copy link
Collaborator Author

@aantron @rizo I've addressed most of your suggestions. Let me know how you want to proceed!

I understand there's an open question of how would this odoc installation work with opam switching compilers, but from a practical point of view:

  1. dune/esy users will run it through dune build @doc,
  2. npm users can install it locally and run it via runscripts or ./node_modules/.bin/odoc

So I don't see it as a blocker at the moment. Happy to be shown wrong! 🙌

@aantron
Copy link
Contributor

aantron commented Oct 11, 2018

@Ostera I don't think there's any issue. The esy/NPM odoc does not interfere with opam switches. It's how you described in your list.

Copy link
Contributor

@aantron aantron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks pretty much fine to me. We can work out any remaining quirks around release time, and maybe refactor the CI scripts over time.

language: generic
language: node_js
node_js:
- "10"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is using this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Installing esy requires a node environment, which we can either manage ourselves or let Travis manage for us.

+ Create package.json for 4.02.3005 switch with necessary dependencies
+ Make targets to build, test, and package for npm
+ Ignore esy output folders
This will cache the opam switch and parts of the dune build, making sure
that the test-generated files (located in the `_scratch` folder) are
always cleaned up before running the tests.
@aantron aantron merged commit 7a8a8a0 into ocaml:master Oct 11, 2018
@aantron
Copy link
Contributor

aantron commented Oct 11, 2018

Thanks @Ostera and the reviewers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants