Now that Tailwind CSS v4 is released, we can setup a proper release
workflow again. This setup mimics the workflow of how we did it in v3,
but adjusted to make it work on the v4 codebase.
Whenever a PR is merged into the `next` branch, we will publish an
insiders release to npm using the following version number:
`0.0.0-insiders.<commit-hash>`. Note: insiders releases will not have a
GitHub release associated with them, therefore the `standalone-cli`
won't be available as an insiders release.
For the normal release procedure, the following steps are required:
1. Manually version the packages (e.g.: `pnpm run version-packages`)
2. Create a git tag for the version you want to release
3. Push the updated package.json files and the git tag to the repository
Next, a GitHub action will kick in once a `tag` is being pushed.
The GitHub action will run a build, and create a draft release on GitHub
that will contain:
1. The CHANGELOG.md contents for the last version
2. The `standalone-cli` artifacts attached to the drafted release
Once you are happy with the draft, you can publish the draft on GitHub.
This in turn will trigger another GitHub action that will publish the
packages to npm.
Whenever an insiders release or a normal release completes, we will also
trigger Tailwind Play, to update its dependencies to the latest version
of Tailwind CSS.
---
Note: some of the GitHub Action workflows still refer to the `next`
branch instead of the `main` branch. If we later today want to get a new
release out, we can merge `next` into `main` and update the workflows
accordingly.
---
This is hard to test, but I started from the existing release.yml file
and adjusted things accordingly. The biggest change is related to the
insiders version. If you look at this temporary
[commit](572dddfc33),
you can see that the publishing (dry-run) seems to work as expected:
<img width="1508" alt="image"
src="https://github.com/user-attachments/assets/c075e788-dcbc-4200-aa32-2b9a3c54d629"
/>
This PR improves the CI workflow names such that they are a bit more
pretty.
E.g.:
```diff
- CI / tests (20, namespace-profile-default, true)
+ CI / Linux
```
This PR improves the integration tests in two ways:
1. Make the integration tests more reliable and thus less flakey
2. Make the integration tests faster (by introducing concurrency)
Tried a lot of different things to make sure that these tests are fast
and stable.
---
The biggest issue we noticed is that some tests are flakey, these are
tests with long running dev-mode processes where watchers are being used
and/or dev servers are created.
To solve this, all the tests that spawn a process look at stdout/stderr
and wait for a message from the process to know whether we can start
making changes.
For example, in case of an Astro project, you get a `watching for file
changes` message. In case of Nuxt project you can wait for an `server
warmed up in` and in case of Next.js there is a `Ready in` message.
These depend on the tools being used, so this is hardcoded per test
instead of a magically automatic solution.
These messages allow us to wait until all the initial necessary work,
internal watchers and/or dev servers are setup before we start making
changes to the files and/or request CSS stylesheets before the server(s)
are ready.
---
Another improvement is how we setup the dev servers. Before, we used to
try and get a free port on the system and use a `--port` flag or a
`PORT` environment variable. Instead of doing this (which is slow), we
rely on the process itself to show a URL with a port. Basically all
tools will try to find a free port if the default port is in use. We can
then use the stdout/stderr messages to get the URL and the port to use.
To reduce the amount of potential conflicts in ports, we used to run
every test and every file sequentially to basically guarantee that ports
are free. With this new approach where we rely on the process, I noticed
that we don't really run into this issue again (I reran the tests
multiple times and they were always stable)
<img width="316" alt="image"
src="https://github.com/user-attachments/assets/b75ddab4-f919-4995-85d0-f212b603e5c2"
/>
Note: these tests run Linux, Windows and macOS in this branch just for
testing purposes. Once this is done, we will only run Linux tests on PRs
and run all 3 of them on the `next` branch.
We do make the tests concurrent by default now, which in theory means
that there could be conflicts (which in practice means that the process
has to do a few more tries to find a free port). To reduce these
conflicts, we split up the integration tests such that Vite, PostCSS,
CLI, … tests all run in a separate job in the GitHub actions workflow.
<img width="312" alt="image"
src="https://github.com/user-attachments/assets/fe9a58a1-98eb-4d9b-8845-a7c8a7af5766"
/>
Comparing this branch against the `next` branch, this is what CI looks
like right now:
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="594" alt="image"
src="https://github.com/user-attachments/assets/540d21eb-ab03-42e8-9f6f-b3a071fc7635"
/> | <img width="672" alt="image"
src="https://github.com/user-attachments/assets/8ef2e891-08a1-464b-9954-4153174ebce7"
/> |
There also was a point in time where I introduced sequential tests such
that all spawned processes still run after each other, but so far I
didn't run into issues if we keep them concurrent so I dropped that
code.
Some small changes I made to make things more reliable:
1. When relying on stdout/stderr messages, we split lines on `\n` and we
strip all the ANSI escapes which allows us to not worry about special
ANSI characters when finding the URL or a specific message to wait for.
2. Once a test is done, we `child.kill()` the spawned process. If that
doesn't work, for whatever reason, we run a `child.kill('SIGKILL')` to
force kill the process. This could technically lead to some memory or
files not being cleaned up properly, but once CI is done, everything is
thrown away anyway.
3. As you can see in the screenshots, I used some nicer names for the
workflows.
| `next` | `feat/improve-integration-tests` |
| --- | --- |
| <img width="276" alt="image"
src="https://github.com/user-attachments/assets/e574bb53-e21b-4619-9cdb-515431b255b9"
/> | <img width="179" alt="image"
src="https://github.com/user-attachments/assets/8bc75119-fb91-4500-a1d0-bd09f74c93ad"
/> |
They also look a bit nicer in the PR overview as well:
<img width="929" alt="image"
src="https://github.com/user-attachments/assets/04fc71fc-74b0-4e7c-9047-2aada664efef"
/>
The very last commit just filters out Windows and macOS tests again for
PRs (but they are executed on the `next` branch.
---
### Nest steps
I think for now we are in a pretty good state, but there are some things
we can do to further improve everything (mainly make things faster) but
aren't necessary. I also ran into issue while trying it so there is more
work to do.
1. More splits — instead of having a Vite folder and PostCSS folder, we
can go a step further and have folders for Next.js, Astro, Nuxt, Remix,
…
2. Caching — right now we have to run the build step for every OS on
every "job". We can re-use the work here by introducing a setup job that
the other jobs rely on. @thecrypticace and I tried it already, but were
running into some Bun specific Standalone CLI issues when doing that.
3. Remote caching — we could re-enable remote caching such that the
`build` step can be full turbo (e.g.: after a PR is merged in `next` and
we run everything again)
When releasing a new beta version, we publish everything to a `next`
tag, this is important so that you can still use `npm install
tailwindcss` and get the current v3 instead of the beta v4 version.
However, some packages don't have a meaning before the v4 release. This
PR aliases the `next` tag to the `latest` tag for the following
packages:
- `@tailwindcss/upgrade`
This in turn allow you to run `npx @tailwindcss/upgrade` for example,
instead of using `npx @tailwindcss/upgrade@next`.
---
> [!NOTE]
> I actually have no idea how to properly test this without actually
running it in CI. The `npm dist-tag` command doesn't have a `--dry-run`
flag. Additionally, when running this command locally we have to
authenticate (obviously) and in CI we typically don't have to do this
because of the `NODE_AUTH_TOKEN` (at least that's the case when running
`npm publish`) so I'm hoping this Just Works™ as expected.
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
Closes#15131
This PR adds a CI step to build the Windows ARM artifacts on the Windows
x64 CI runners after some trial and error with the custom Windows ARM
runners which turned out to not be necessary.
## Test Plan
I changed the workflow so that it uploads the tarballs to the build
artifact and runs on the PR. Then, I downloaded the artifacts and
installed them in a npm project on Windows with ARM:
<img width="1187" alt="Screenshot 2024-11-27 at 11 35 15"
src="https://github.com/user-attachments/assets/1732ac3e-9410-4ed8-a49c-279faf6df50f">
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
We're trialing [namespace.so](https://namespace.so/) for their custom
GitHub Action workers. So far we're seeing an improvement of 3 minutes
in our pipeline (used to take 6min 30sec and now it's 3min 30sec, an
almost 50% improvement!).
We can always revert this PR, but I recommend we try it out by enabling
it for our CI for the next few days.
This PR changes it so that only the Ubuntu runner starts when doing a
pull request. On a successfull `next` merge, all runners shoould start.
Furthermore this increases the retry count for integration test to 3.
This is mainly so that rare Windows flakes we still see won't become
noise when we enabled the Discord notification.
- Avoid cross-device copying in Windows CI by setting the tests dir to
the same drive as the workspace.
- Disable LTO and use a faster linker for the Rust build
Buid: ~3min -> ~2min
Integration Tests: ~8min -> ~3min20s
This PR adds a new standalone client: A single-binary file that you can
use to run Tailwind v4 without having a node setup. To make this work we
use Bun's single-binary build which can properly package up native
modules and the bun runtime for us so we do not have to rely on any
expand-into-tmp-folder-at-runtime workarounds.
When running locally, `pnpm build` will now standalone artifacts inside
`packages/@tailwindcss-standalone/dist`. Note that since we do not build
Oxide for other environments in the local setup, you won't be able to
use the standalone artifacts for other platforms in local dev mode.
Unfortunately Bun does not have support for Windows ARM builds yet but
we found that using the `bun-baseline` runtime for Windows x64 would
make the builds work fine in ARM emulation mode:

Some Bun related issues we faced and worked around:
- We found that the regular Windows x64 build of `bun` does not run on
Windows ARM via emulation. Instead, we have to use the `bun-baseline`
builds which emulate correctly.
- When we tried to bundle artifacts with [embed
directories](https://bun.sh/docs/bundler/executables#embed-directories),
node binary dependencies were no longer resolved correctly even though
they would still be bundled and accessible within the [`embeddedFiles`
list](https://bun.sh/docs/bundler/executables#listing-embedded-files).
We worked around this by using the `import * as from ... with { type:
"file" };` and patching the resolver we use in our CLI.
- If you have an import to a module that is used as a regular import
_and_ a `with { type: "file" }`, it will either return the module in
both cases _or_ the file path when we would expect only the `with {
type: "file" }` import to return the path. We do read the Tailwind CSS
version via the file system and `require.resolve()` in the CLI and via
`import * from './package.json'` in core and had to work around this by
patching the version resolution in our CLI.
```ts
import packageJson from "./package.json"
import packageJsonPath from "./package.json" with {type: "file"}
// We do not expect these to be equal
packageJson === packageJsonPath
```
- We can not customize the app icon used for Windows `.exe` builds
without decompiling the binary. For now we will leave the default but
one workaround is to [use tools like
ResourceHacker](698d9c4bd1)
to decompile the binary first.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR fixes the release workflow
- We added a postbuild step so that any arguments/flags passed to the
`pnpm run build` command are forwarded to the underlying command.
- We made sure that we run any `pnpm` specific flags before the actual
command
- Cleaned up the CI workflow a bit
This PR adds a new root `/integrations` folder that will be the home of
integration tests. The idea of these tests is to use Tailwind in various
setups just like our users would (by only using the publishable npm
builds).
To avoid issues with concurrent tests making changes to the file system,
to make it very easy to test through a range of versions, and to avoid
changing configuration objects over and over in test runs, we decided to
inline the scaffolding completely into the test file and have no
examples checked into the repo.
Here's an example of how this can look like for a simple Vite test:
```ts
test('works with production builds', {
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"@tailwindcss/vite": "workspace:^",
"tailwindcss": "workspace:^"
},
"devDependencies": {
"vite": "^5.3.5"
}
}
`,
'vite.config.ts': ts`
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
export default defineConfig({
build: { cssMinify: false },
plugins: [tailwindcss()],
})
`,
'index.html': html`
<head>
<link rel="stylesheet" href="./src/index.css">
</head>
<body>
<div class="underline m-2">Hello, world!</div>
</body>
`,
'src/index.css': css`
@import 'tailwindcss/theme' reference;
@import 'tailwindcss/utilities';
`,
},
},
async ({ fs, exec }) => {
await exec('pnpm vite build')
expect.assertions(2)
for (let [path, content] of await fs.glob('dist/**/*.css')) {
expect(path).toMatch(/\.css$/)
expect(stripTailwindComment(content)).toMatchInlineSnapshot(
`
".m-2 {
margin: var(--spacing-2, .5rem);
}
.underline {
text-decoration-line: underline;
}"
`,
)
}
},
)
```
By defining all dependencies this way, we never have to worry about
which fixtures are checked in and can more easily describe changes to
the setup.
For ergonomics, we've also added the [`embed` prettier
plugin](https://github.com/Sec-ant/prettier-plugin-embed). This will
mean that files inlined in the `fs` setup are properly indented. No
extra work needed!
If you're using VS Code, I can also recommend the [Language
Literals](https://marketplace.visualstudio.com/items?itemName=sissel.language-literals)
extension so that syntax highlighting also _just works_.
A neat feature of inlining the scaffolding like this is to make it very
simple to test through a variety of versions. For example, here's how we
can set up a test against Vite 5 and Vite 4:
```js
;['^4.5.3', '^5.3.5'].forEach(viteVersion => {
test(`works with production builds for Vite ${viteVersion}`, {
fs: {
'package.json': json`
{
"type": "module",
"devDependencies": {
"vite": "${viteVersion}"
}
}
`,
async () => {
// Do something
},
)
})
```
## Philosophy
Before we dive into the specifics, I want to clearly state the design
considerations we have chosen for this new test suite:
- All file mutations should be done in temp folders, nothing should ever
mess with your working directory
- Windows as a first-class citizen
- Have a clean and simple API that describes the test setup only using
public APIs
- Focus on reliability (make sure cleanup scripts work and are tolerant
to various error scenarios)
- If a user reports an issue with a specific configuration, we want to
be able to reproduce them with integration tests, no matter how obscure
the setup (this means the test need to be in control of most of the
variables)
- Tests should be reasonably fast (obviously this depends on the
integration. If we use a slow build tool, we can't magically speed it
up, but our overhead should be minimal).
## How it works
The current implementation provides a custom `test` helper function
that, when used, sets up the environment according to the configuration.
It'll create a new temporary directory and create all files, ensuring
things like proper `\r\n` line endings on Windows.
We do have to patch the `package.json` specifically, since we can not
use public versions of the tailwindcss packages as we want to be able to
test against a development build. To make this happen, every `pnpm
build` run now creates tarballs of the npm modules (that contain only
the files that would also in the published build). We then patch the
`package.json` to rewrite `workspace:^` versions to link to those
tarballs. We found this to work reliably on Windows and macOS as well as
being fast enough to not cause any issues. Furthermore we also decided
to use `pnpm` as the version manager for integration tests because of
it's global module cache (so installing `vite` is fast as soon as you
installed it once).
The test function will receive a few utilities that it can use to more
easily interact with the temp dir. One example is a `fs.glob` function
that you can use to easily find files in eventual `dist/` directories or
helpers around `spawn` and `exec` that make sure that processes are
cleaned up correctly.
Because we use tarballs from our build dependencies, working on changes
requires a workflow where you run `pnpm build` before running `pnpm
test:integrations`. However it also means we can run clients like our
CLI client with no additional overhead—just install the dependency like
any user would and set up your test cases this way.
## Test plan
This PR also includes two Vite specific integration tests: One testing a
static build (`pnpm vite build`) and one a dev mode build (`pnpm vite
dev`) that also makes changes to the file system and asserts that the
resources properly update.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR updates vitest to v2. The changes are mostly around using fork
instead of threads for how tests are run which should fix one of the
issues we've found.
Ever since adding the unit tests on Windows, we started seeing
occacional flags of vitest crashing with the following error:
```
ELIFECYCLE Command failed with exit code 3221225477.
Error: Process completed with exit code 1.
```
When reading the [v2
changelog](https://github.com/vitest-dev/vitest/releases/tag/v2.0.0) we
saw many bug fixes related to segfaulting so we believe this was the
issue.
When upgrading `vitest` alone, we got a bunch of dependency mismatches
though (specifically, vite was installed two times with different peer
dependencies for `@types/node` which causes our vite plugin's `Plugin`
type to be different from the one in the vite playground. Yikes. These
were eventually fixed by having pnpm create a new lockfile for us. So,
unfortunatly this PR also bumps a bunch of patch versions for some
transitive dependencies. Tests seem fine, though 🤞
This PR also removes the `bench` script from CI. It doesn't give us
value in its current state (since it's not reporting when performance
regresses) but added a few seconds of unnecessary overhead to each test
run.
This changes the V4 CI to run on any pull request change (so an opened,
reopened, and updated PR), regardless if the PR is directed into the
`next` branch or not.
This is helpful for testing stacked PRs like:
https://github.com/tailwindlabs/tailwindcss/pull/14078
This PR changes the GitHub action workflow for V4 to start running all
unit tests and build on both Ubuntu (our current default) _and_ Windows.
This is to ensure we catch issues with paths and other Windows-specific
things sooner. It does, however, not replace testing on Windows.
* Add basic `addVariant` plugin support
* Return early
* Load plugins right away instead later
* Use correct type for variant name
* Preliminary support for addVariant plugins in PostCSS plugin
* Add test for compounding plugin variants
* Add basic `loadPlugin` support to Vite plugin
* Add basic `loadPlugin` support to CLI
* add `io.ts` for integrations
* use shared `loadPlugin` from `tailwindcss/io`
* add `tailwindcss-test-utils` to `@tailwindcss/cli` and `@tailwindcss/vite`
* only add `tailwindcss-test-utils` to `tailwindcss` as a dev dependency
Because `src/io.ts` is requiring the plugin.
* move `tailwindcss-test-utils` to `@tailwindcss/postcss `
This is the spot where we actually need it.
* use newer pnpm version
* Duplicate loadPlugin implementation instead of exporting io file
* Remove another io reference
* update changelog
---------
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
* move `oxide/crates` to `crates`
* ignore `target/` folder
* ensure pnpm points to `crates` instead of `oxide/crates`
* ensure all paths point to `crates` instead of `oxide/crates`
* update `oxide/crates` -> `crates` path in workflows
* use correct path in .prettierignore
* rename `crates/core` to `crates/oxide`
* remove oxide folder
* fix test script to run `cargo test` directly
* add `pre-publish-optimizations` script
* handle `@import` ourselves
This implementation is fairly simple right now, because we don't have
to worry about resolving folders or modules since we don't use them.
* pretty print index.css file
* update changelog
* Revert "handle `@import` ourselves"
This reverts commit 13a46404c16ee67e4e1b7eaf58ae67321113eb07.
* drop the `1.`
* Update scripts/pre-publish-optimizations.mjs
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* Update CHANGELOG.md
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
* run prettier
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
* Add provenance to all packages
Based on #13097 by @saibotk
Add [provenance](https://docs.npmjs.com/generating-provenance-statements) for all published packages.
---
Co-authored-by: saibotk <git@saibotk.de>
* Document reason for id-token permission
* Update changelog
This way we can re-use the workflow that already exists on the `master`
branch and it will allow us to trigger manual releases from the `next`
branch without making changes to the `master` branch.
* bump `postcss-load-config` in the oxide engine
* bump `postcss-load-config` in the stable engine
* update changelog
* Switch to stable
* Update Node to v14
* Update to latest dependency versions
* Update test helper for new version of `rimraf`
Co-Authored-By: Jordan Pittman <jordan@cryptica.me>
* Downgrade `lightningcss` to `v1.18.0`
Co-Authored-By: Jordan Pittman <jordan@cryptica.me>
* Switch back to oxide
* Update Github actions from Node 12 to Node 14
* Update oxide dependencies
* Update stable dependencies
* Update `content-resolution` integration test dependencies
* Update `postcss-cli` integration test dependencies
* Update `rollup` integration test dependencies
* Update `rollup-sass` integration test dependencies
* Update `vite` integration test dependencies
* Update `webpack-5` integration test dependencies
* Update changelog
* Remove `color-name` dependency
* Replace `quick-lru` dependency with `@alloc/quick-lru`
* Replace `quick-lru` dependency with `@alloc/quick-lru` in stable
* Fix standalone CLI test
---------
Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* Don’t move `::deep` pseudo element to end of selector when using `@apply`
* Update changelog
* Move pseudo-elements in two passes
* Rewrite pseudo-element relocation logic
* Update test
`::test` is an unknown pseudo element and therefore may be actionable _and_ nestable
* Add tests
* Simplify tests
* Simplify
* run tests on CI multiple times
This works around the timeouts/flakeyness of GitHub Actions
* Update formatting
* Add comment
* Mark webkit peusdo elements as terminal
* update comment
* only execute the `global-setup` once
* Simplify
NO SORT FN YAY
* Use typedefs
* Update changelog
* Update changelog
* update again
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>