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

Package the tailwindcss binary executable #96

Merged
merged 9 commits into from
Dec 17, 2021

Conversation

flavorjones
Copy link
Member

@flavorjones flavorjones commented Dec 16, 2021

This is an exploration of how we could package and redistribute the binaries from https://github.com/tailwindlabs/tailwindcss/releases with this gem.

Approach

The general approach followed here is to create a new gem file for each of the native binary platforms supported upstream:

  • x86_64-linux
  • x86_64-darwin
  • x64-mingw32
  • arm64-darwin

in addition to the vanilla ruby gem which is unchanged by this PR. In each of those "native" gems, there will be two additional files:

  • exe/<platform>/tailwindcss, the binary executable downloaded from tailwindlabs/tailwindcss (note the rename from upstream release)
  • exe/tailwindcss, a generic ruby script to find and run the binary

Note that the exe/tailwindcss script is required because rubygems requires that executables declared in a gemspec must be Ruby scripts (and we must declare an executable in order to hook into the user's shell $PATH).

Also note that the final gem sizes are much larger because of the size of the tailwind executable:

$ ls -glSr pkg/*gem
-rw-rw-r-- 1 flavorjones  1063424 2021-12-16 01:05 pkg/tailwindcss-rails-1.0.0.gem
-rw-rw-r-- 1 flavorjones 16760832 2021-12-16 01:06 pkg/tailwindcss-rails-1.0.0-x64-mingw32.gem
-rw-rw-r-- 1 flavorjones 18651136 2021-12-16 01:06 pkg/tailwindcss-rails-1.0.0-x86_64-darwin.gem
-rw-rw-r-- 1 flavorjones 19821056 2021-12-16 00:47 pkg/tailwindcss-rails-1.0.0-x86_64-linux.gem
-rw-rw-r-- 1 flavorjones 20329472 2021-12-16 01:05 pkg/tailwindcss-rails-1.0.0-arm64-darwin.gem

Tests

I've added a github actions pipeline to verify the executables work as desired on Linux, MacOS, and Windows.

Some questions to consider

What is the desired user experience for people not on one of the platforms published by tailwindcss?

  • This would include BSD users, ARM64 Linux users (Graviton), JRuby users, and 32-bit anything users.
  • Right now the tailwindcss executable will simply not be present, which may be confusing.
  • But shipping a tailwindcss script to notify the user may shadow the real executable when the user installs it, which may be even more confusing.

What license or copyright declarations should be made in this project regarding redistributing tailwindcss?

  • One approach I've seen (and which I use in Nokogiri) is to create a separate file LICENSE-DEPENDENCIES which lists the libraries being redistributed.
  • This PR takes that approach, and includes that file in the native gems, but I'm open to other opinions.

What should the process be to update to a newer version of the binaries?

  • In rakelib/package.rake there's a constant that can be manually updated, is that fine?
  • Should the "update tailwind" github action pipeline do something here?

The binary isn't going to be available to anyone who's bundling using a git URL.

  • If that's a problem, we can solve it a few different ways ...
  • We could check in the binaries to the repo (but I'm not a huge fan of using git to store large opaque blobs)
  • We could have the exe/tailwindcss script download the binary if it's not available
    • This is basically the approach outlined in the next section

Alternative approaches you might consider instead

  • use a shim that will download the binary executable on demand (similar to how https://github.com/flavorjones/chromedriver-helper used to work).
    • pro: no need for multiple gems, no need for large gem files
    • pro: no need to ship new gems if a new upstream version drops
    • con: challenging to create a reproducible development environment
    • con: users are on their own to choose to update or forget to update, and we may know better

@flavorjones
Copy link
Member Author

There's also some risk that a download will be incomplete or the file will get corrupted on disk, and then that bad file will be packaged and shipped.

We should probably be validating checksums, but a human would have to be involved today because checksums aren't provided with https://github.com/tailwindlabs/tailwindcss/releases releases.

@flavorjones flavorjones force-pushed the flavorjones-vendor-tailwindcss-executable branch from c5050d7 to dff2539 Compare December 16, 2021 06:38
@flavorjones
Copy link
Member Author

Pushed a fix for darwin.

@flavorjones flavorjones force-pushed the flavorjones-vendor-tailwindcss-executable branch from dff2539 to 488de21 Compare December 16, 2021 06:41
@flavorjones flavorjones force-pushed the flavorjones-vendor-tailwindcss-executable branch from 488de21 to 04f41ce Compare December 16, 2021 06:51
@flavorjones
Copy link
Member Author

Modified the name of the binary executable to be simply "tailwindcss".

@muriloime
Copy link

This is very exciting. @flavorjones Does this PR mean that this gem now supports tailwind.config.js files?

@dixpac
Copy link
Contributor

dixpac commented Dec 17, 2021

I've played with this a little bit, and it works fine. It is beautiful. We have the full TW power without the node ❤️

I've few remarks:

  1. tailwindcss.config.js
    I think we want to generate our own tailwindcss.config.js on the install task, instead of user running taillwindcss init.
    We need to setup basic content(old purging paths) in order to get the tw styles generated, otherwise output will be pretty much empty css file.
module.exports = { 
  content: [
    './app/helpers/**/*.rb',
    './app/javascript/**/*.js',
    './app/views/**/*.erb',
   ...
  ],  
  theme: {
    extend: {...}, 
  },  
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/aspect-ratio'),
    require('@tailwindcss/typography'),
  ], 
}
  1. Tailwindcss input.css file
    We need to include tailwind "funcionalities" in order to get the styles:
@tailwind base;
@tailwind components;          
@tailwind utilities;  

I guess this also need to be generated thought the engine on the install script.
After we need to handle dance between input css and output css files.

  1. We can remove custom sprockets purger, since tw binary will handle that.
  2. We should note that users would probably want to run the --watch in the separate terminal

Shim approach

con: challenging to create a reproducible development environment

In what ways it is more challenging, then having separate gems per platform?

@dixpac
Copy link
Contributor

dixpac commented Dec 17, 2021

@muriloime Yes. Including all the Tailwind custom functions and directives, such as @apply, and JIT.

@flavorjones
Copy link
Member Author

@dixpac Thanks for the comments! I actually know very little about tailwind so if you or @dhh have concrete ideas on how to finish the integration work please feel free to push more commits onto the branch.

In what ways is [creating a reproducible development environment] more challenging, then having separate gems per platform?

By this comment I was referring to an antipattern that arose among chromedriver-helper users, wherein the version of chromedriver wasn't locked. The gem grabbed whatever was current when you ran it. This led to differences in behavior between static development machines (using cached older version) and CI containers (which downloaded the latest version), for example; or between different development machines. This particular flavor of problem was challenging to diagnose because the fetch was done lazily and the underlying binary's version wasn't obvious.

@dhh
Copy link
Member

dhh commented Dec 17, 2021

Working on the direct integration now! Should have something ready for us to play with very soon.

@dhh dhh merged commit ab00b7c into rails:main Dec 17, 2021
@flavorjones flavorjones deleted the flavorjones-vendor-tailwindcss-executable branch December 18, 2021 02:48
@hudon hudon mentioned this pull request Dec 18, 2021
@dixpac
Copy link
Contributor

dixpac commented Dec 18, 2021

@flavorjones interesting thing I've just noticed (while trying to convert one real-world open source rails project using tw with node).

The projects Gemfile.lock had following listed under the PLATFORMS

  PLATFORMS:
  ruby
  x86_64-darwin-19
  x86_64-darwin-20

I use Mac M1, so I've installed the gem run the installer everything looked fine. But running bin/rails tailwindcss:build just failed silently. Only after I've added arm64-darwin-20 inisde the lock PLATFORMS list, then bundler installed the correct version and everything worked fine.

This may be just bad luck for me on this project, but it may be something that could possibly happen to others also.

@flavorjones
Copy link
Member Author

@dixpac ack. I'll try to take a look today.

flavorjones added a commit to flavorjones/tailwindcss-rails that referenced this pull request Dec 19, 2021
See rails#101 and comments in rails#96 for examples of what's confusing users.
dhh pushed a commit that referenced this pull request Dec 19, 2021
…n't correct (#102)

* prefactor: expose constants related to upstream tailwindcss

The following constants have been moved from rakelib/package.rake into
lib/tailwindcss/upstream.rb to be accessible from within the installed
gem:

- TAILWINDCSS_VERSION → Tailwindcss::Upstream::VERSION
- TAILWINDCSS_NATIVE_PLATFORMS -> Tailwindcss::Upstream::NATIVE_PLATFORMS

* fix: provide more helpful error messages

See #101 and comments in #96 for examples of what's confusing users.
@flavorjones
Copy link
Member Author

See #102 for a potential fix

@dixpac
Copy link
Contributor

dixpac commented Dec 21, 2021

@flavorjones it works 👏

flavorjones added a commit that referenced this pull request Sep 3, 2022
This should have been removed in #96 when `yarn.lock` was deleted.

See #187 for some discussion of automating updates.
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.

4 participants