Skip to content

feat: exclude @vaadin packages from the minimum frontend package age (#25620) (CP: 25.2) - #25795

Merged
ZheSun88 merged 2 commits into
25.2from
cherry-pick-25620-to-25.2-1789713169286
Sep 18, 2026
Merged

ZheSun88 merged 2 commits into
25.2from
cherry-pick-25620-to-25.2-1789713169286

Conversation

@totally-not-ai

@totally-not-ai totally-not-ai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A build that started right after a Vaadin release could fail, because the minimum frontend package age blocks packages that were published a moment ago. Vaadin now tells npm and pnpm to exempt @vaadin/* from that check, and warns when the package manager cannot do it. The PR also stops pnpm from starting a second, failing install of its own before a postinstall script.

What changed

Behavior change 1: every build that runs with a minimum frontend package age now gets extra install arguments, and some builds get a new warning. Nothing is added when no age applies. This affects all users who leave the age check on (the default).

Behavior change 2: a postinstall script that exits with a non-zero status now fails the build with an ExecutionFailedException that names the package and includes the script output. Before, the exit code was thrown away and the build continued as if nothing happened. This affects any project whose postinstall script fails — the build now stops instead of continuing with a broken node_modules.

Excluding the Vaadin packages:

  • npm 11.17.0 and newer: --min-release-age-exclude=@vaadin/* is passed. It exempts the matching packages from both --min-release-age and --before.
  • pnpm 10.17.0 and newer: --config.minimum-release-age-exclude=@vaadin/* is passed. A single pattern is passed twice, because pnpm reads the setting as a list only when the argument occurs more than once, and pnpm 11 excludes every package when the value is a plain string.
  • Patterns already configured in .npmrc / pnpm config are kept. A command line value replaces the configured list instead of adding to it, so Vaadin reads the configured patterns and passes them along with @vaadin/*. Without this, a project that excludes its own scope silently lost that exclusion.
  • bun, older npm, older pnpm: nothing can be passed, so the build is warned that an install during the first day after a Vaadin release may fail. The warning names the remedy for the package manager in use — upgrade npm to 11.17.0 (first shipped with Node.js 24.19.0), upgrade pnpm to 10.17.0, or, for bun, list the @vaadin packages one by one in minimumReleaseAgeExcludes in a bunfig.toml, since bun matches exact names only.
  • bun without a warning: the project bunfig.toml is now read, and the warning is skipped when a @vaadin package is an actual value of minimumReleaseAgeExcludes. A commented-out line or an unrelated mention is not enough.
  • minimumFrontendPackageAgeDays = 0: Vaadin still passes no age argument, but an age that npm or pnpm resolves from its own configuration still applies to the install. That age is now reported as applying, so the @vaadin packages are excluded from it instead of being blocked silently. When the package manager itself resolves an age of 0, nothing applies and nothing is excluded or warned about.
  • Reading a list-valued setting no longer splits list entries on commas. Only a comma-separated string value is split, because a list entry is complete on its own and may contain a comma of its own in a brace expansion such as @acme/{ui,core}.
  • Javadoc for minimumFrontendPackageAgeDays in Options, InitParameters, BuildFrontendMojo and BuildDevBundleMojo now documents the exclusion and the required npm/Node.js/pnpm versions.

The pnpm postinstall fix:

  • The command that runs a postinstall script now gets --config.verify-deps-before-run=false when pnpm is in use. Before running a script, pnpm checks that node_modules is up to date, and that check starts its own install. That install repeats the one Flow just ran, but without the arguments that exempt the Vaadin packages from the minimum release age and without --ignore-scripts. When it failed, the postinstall script was never run.
  • Nothing extra is passed for npm or bun. Neither checks anything before running a script, and bun fails on an argument it does not know.
  • The postinstall command is now also logged at debug level, next to the install command.

No public or protected API changed. The new methods are package-private.

Use case

A team builds their app with bun, and CI runs a few minutes after a Vaadin release. The install fails because the freshly published @vaadin packages are younger than the minimum age, and bun cannot take an exclusion from the command line. The build log now tells them what to do: add the packages they depend on to a bunfig.toml next to package.json.

[install]
minimumReleaseAgeExcludes = ["@vaadin/react-components"]

After that the install succeeds, and Vaadin stops printing the warning on every build.

Test summary

# Status What the test verifies Why it matters
1 ✅ npm gets --min-release-age-exclude=@vaadin/* and no warning Core fix: a build right after a release must not be blocked
2 ✅ pnpm gets --config.minimum-release-age-exclude=@vaadin/* twice A single occurrence makes pnpm 11 exclude every package, turning the age check off
3 ✅ Patterns configured in the package manager are passed along with @vaadin/* A command line value replaces them, so a project would silently lose its own exclusion
4 ✅ A real pnpm install command contains the exclude argument Pins that the argument actually reaches the install, not just the resolver
5 ✅ npm/pnpm too old or bun: no argument, and a warning naming the version or the bunfig setting The only signal a user gets when the exclusion is impossible
6 ✅ A bunfig.toml listing a @vaadin package silences the warning; other packages or a comment do not A bun project that fixed it must not be nagged; a fake match must not hide a real problem
7 ✅ With Vaadin configured to 0 and no package manager age: no argument, no exclusion, no warning A project that opted out must stay fully opted out
8 ✅ With Vaadin configured to 0 but npm configured with 7 days: the age applies, so @vaadin/* is excluded The silently blocking case this PR fixes
9 ✅ A package manager age of 0 counts as no age at all Avoids arguments and warnings when nothing is blocked
10 ✅ List settings are read from an array and from a comma-separated string; a brace expansion in a list is kept whole; a missing key gives an empty list Splitting @acme/{ui,core} would produce two broken patterns
11 ✅ A postinstall script that exits non-zero makes execute() throw, and the message names the package and contains what the script printed This was silently ignored before; without it a broken install looks like a successful build
12 ✅ The command actually run for pnpm contains --config.verify-deps-before-run=false Pins the fix end-to-end — dropping the call that adds the argument would otherwise keep the suite green
13 ✅ Postinstall options resolve to exactly that one flag for pnpm and to nothing for npm and bun The exact flag spelling is what stops pnpm's extra install, and bun fails on an unknown argument
14 ❗ gap npmSupportsMinReleaseAgeExclude / pnpmSupportsMinimumReleaseAgeExclude comparing a real reported version against 11.17.0 / 10.17.0 A wrong threshold would pass an unknown argument to an old tool; callers only mock these
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_npm_excludesVaadinPackages → 1
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_pnpm_excludesVaadinPackagesAsAList → 2
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_configuredPatterns_areKept → 3
  • TaskRunPnpmInstallTest.runPnpmInstall_excludesVaadinPackagesFromTheMinimumAge → 4
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_npmTooOld_warnsInsteadOfExcluding → 5
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_pnpmTooOld_warnsInsteadOfExcluding → 5
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_bun_warnsInsteadOfExcluding → 5
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_bunfigListsThePackages_noWarning → 6
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_bunfigListsOtherPackages_warns → 6
  • TaskRunNpmInstallTest.resolveMinimumFrontendPackageAge_zeroConfigured_noArgumentAndNoAge → 7, 9
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_noAgeApplies_noArgumentOrWarning → 7
  • TaskRunNpmInstallTest.resolveMinimumFrontendPackageAge_zeroConfiguredWithNpmrcValue_ageStillApplies → 8
  • TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_npmrcValue_isStillExcludedFrom → 8
  • TaskRunNpmInstallTest.resolveMinimumFrontendPackageAge_npmrcValueOfZero_noAgeApplies → 9
  • FrontendToolsTest.getConfiguredSettingValues_listAndCommaSeparatedValue_areRead → 10
  • FrontendToolsTest.getConfiguredSettingValues_braceExpansionInAList_isKeptTogether → 10
  • FrontendToolsTest.getConfiguredSettingValues_keyWithoutValue_isEmpty → 10
  • TaskRunNpmInstallTest.runNpmInstall_postInstallFails_buildFailsWithTheScriptOutput → 11 (inherited by TaskRunPnpmInstallTest, so it runs for npm and pnpm)
  • TaskRunPnpmInstallTest.runPnpmInstall_postinstallDoesNotVerifyTheDependencies → 12
  • TaskRunNpmInstallTest.postinstallArguments_pnpm_doesNotVerifyTheDependencies → 13
  • TaskRunNpmInstallTest.postinstallArguments_npmAndBun_needNone → 13

Left untested on purpose: the exact wording of the warning (only the key facts are asserted), the unreadable-bunfig.toml path, which just falls back to warning, and the happy path of a postinstall script that succeeds, which the existing runNpmInstall_postInstall_* tests already pin. There is no end-to-end bun test class, so bun is covered at the argument level only.

…25620)

## Summary

The minimum frontend package age blocks packages that were published
very recently. Because the `@vaadin` packages are pinned to the platform
version, a build started right after a Vaadin release had no older
version to fall back to and failed. Vaadin now tells the package manager
to exempt `@vaadin/*` from that check, and warns when the package
manager cannot do it.

## What changed

**Behavior change:** every build that runs with a minimum frontend
package age now gets extra install arguments, and some builds get a new
warning. Nothing is added when no age applies.

- **npm 11.17.0 and newer:** `--min-release-age-exclude=@vaadin/*` is
passed. It exempts the matching packages from both `--min-release-age`
and `--before`.
- **pnpm 10.17.0 and newer:**
`--config.minimum-release-age-exclude=@vaadin/*` is passed. When there
is a single pattern it is passed twice, because pnpm reads the setting
as a list only when the argument occurs more than once, and pnpm 11
excludes *every* package when the value is a plain string.
- **Patterns already configured in `.npmrc` / pnpm config are kept.** A
command line value replaces the configured list instead of adding to it,
so Vaadin reads the configured patterns and passes them along with
`@vaadin/*`. Without this, a project that excludes its own scope
silently lost that exclusion.
- **bun, older npm, older pnpm:** nothing can be passed, so the build is
warned that an install during the first day after a Vaadin release may
fail. The warning names the remedy for the package manager in use —
upgrade npm to 11.17.0 (first shipped with Node.js 24.19.0), upgrade
pnpm to 10.17.0, or, for bun, list the `@vaadin` packages one by one in
`minimumReleaseAgeExcludes` in a `bunfig.toml`, since bun matches exact
names only.
- **bun without a warning:** the project `bunfig.toml` is now read, and
the warning is skipped when a `@vaadin` package is an actual value of
`minimumReleaseAgeExcludes`. A commented-out line or an unrelated
mention is not enough.
- **`minimumFrontendPackageAgeDays = 0`:** Vaadin still passes no age
argument, but an age that npm or pnpm resolves from its own
configuration still applies to the install. That age is now reported as
applying, so the `@vaadin` packages are excluded from it instead of
being blocked silently. When the package manager itself resolves an age
of `0`, nothing applies and nothing is excluded or warned about.
- Reading a list-valued setting no longer splits list entries on commas.
Only a comma-separated string value is split, because a list entry is
complete on its own and may contain a comma of its own in a brace
expansion such as `@acme/{ui,core}`.
- Javadoc for `minimumFrontendPackageAgeDays` in `Options`,
`InitParameters`, `BuildFrontendMojo` and `BuildDevBundleMojo` now
documents the exclusion and the required npm/Node.js/pnpm versions.

No public or protected API changed. The new methods and the internal
`resolveMinimumFrontendPackageAge` rename are package-private.

## Use case

A team builds their app with bun, and CI runs a few minutes after a
Vaadin release. The install fails because the freshly published
`@vaadin` packages are younger than the minimum age, and bun cannot take
an exclusion from the command line. The build log now tells them what to
do: add the packages they depend on to a `bunfig.toml` next to
`package.json`.

```toml
[install]
minimumReleaseAgeExcludes = ["@vaadin/react-components"]
```

After that the install succeeds, and Vaadin stops printing the warning
on every build.

## Test summary

| # | Status | What the test verifies | Why it matters |
|---|--------|------------------------|----------------|
| 1 | ✅ | npm gets `--min-release-age-exclude=@vaadin/*` and no warning
| Core fix: a build right after a release must not be blocked |
| 2 | ✅ | pnpm gets `--config.minimum-release-age-exclude=@vaadin/*`
twice | A single occurrence makes pnpm 11 exclude every package, turning
the age check off |
| 3 | ✅ | Patterns configured in the package manager are passed along
with `@vaadin/*` | A command line value replaces them, so a project
would silently lose its own exclusion |
| 4 | ✅ | A real pnpm install command contains the exclude argument |
Pins that the argument actually reaches the install, not just the
resolver |
| 5 | ✅ | npm/pnpm too old or bun: no argument, and a warning naming the
version or the bunfig setting | The only signal a user gets when the
exclusion is impossible |
| 6 | ✅ | A `bunfig.toml` listing a `@vaadin` package silences the
warning; other packages or a comment do not | A bun project that fixed
it must not be nagged; a fake match must not hide a real problem |
| 7 | ✅ | With Vaadin configured to `0` and no package manager age: no
argument, no exclusion, no warning | A project that opted out must stay
fully opted out |
| 8 | ✅ | With Vaadin configured to `0` but npm configured with 7 days:
the age applies, so `@vaadin/*` is excluded | The silently blocking case
this PR fixes |
| 9 | ✅ | A package manager age of `0` counts as no age at all | Avoids
arguments and warnings when nothing is blocked |
| 10 | ✅ | List settings are read from an array and from a
comma-separated string; a brace expansion in a list is kept whole; a
null/empty key gives an empty list | Splitting `@acme/{ui,core}` would
produce two broken patterns |
| 11 | ❗ **gap** | `npmSupportsMinReleaseAgeExclude` /
`pnpmSupportsMinimumReleaseAgeExclude` comparing a real reported version
against 11.17.0 / 10.17.0 | A wrong threshold would pass an unknown
argument to an old tool; callers only mock these |

-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_npm_excludesVaadinPackages`
— 1
-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_pnpm_excludesVaadinPackagesAsAList`
— 2
-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_configuredPatterns_areKept`
— 3
-
`TaskRunPnpmInstallTest.runPnpmInstall_excludesVaadinPackagesFromTheMinimumAge`
— 4
-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_npmTooOld_warnsInsteadOfExcluding`
— 5
-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_pnpmTooOld_warnsInsteadOfExcluding`
— 5
-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_bun_warnsInsteadOfExcluding`
— 5
-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_bunfigListsThePackages_noWarning`
— 6
-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_bunfigListsOtherPackages_warns`
— 6
-
`TaskRunNpmInstallTest.resolveMinimumFrontendPackageAge_zeroConfigured_noArgumentAndNoAge`
— 7, 9
-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_noAgeApplies_noArgumentOrWarning`
— 7
-
`TaskRunNpmInstallTest.resolveMinimumFrontendPackageAge_zeroConfiguredWithNpmrcValue_ageStillApplies`
— 8
-
`TaskRunNpmInstallTest.minimumFrontendPackageAgeExclude_npmrcValue_isStillExcludedFrom`
— 8
-
`TaskRunNpmInstallTest.resolveMinimumFrontendPackageAge_npmrcValueOfZero_noAgeApplies`
— 9
-
`FrontendToolsTest.getConfiguredSettingValues_listAndCommaSeparatedValue_areRead`
— 10
-
`FrontendToolsTest.getConfiguredSettingValues_braceExpansionInAList_isKeptTogether`
— 10
- `FrontendToolsTest.getConfiguredSettingValues_keyWithoutValue_isEmpty`
— 10

Left untested on purpose: the exact wording of the warning (only the key
facts are asserted), and the unreadable-`bunfig.toml` path, which just
falls back to warning.

---------

Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com>
Co-authored-by: Artur Signell <artur@vaadin.com>
@github-actions

github-actions Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

 1 385 files  ±0   1 385 suites  ±0   1h 29m 44s ⏱️ - 3m 13s
10 428 tests ±0  10 360 ✅ ±0  68 💤 ±0  0 ❌ ±0 
10 783 runs  ±0  10 714 ✅ ±0  69 💤 ±0  0 ❌ ±0 

Results for commit 584fcb85. ± Comparison against base commit 795bc8b.

♻️ This comment has been updated with latest results.

## Summary

When a build uses pnpm, pnpm started a second, unwanted install of its
own right before running a `postinstall` script, and that install often
failed. This change turns that check off, so the script runs. A failing
`postinstall` script now also fails the build instead of being silently
ignored.

Fixes #21662, Fixes #24333

## What changed

**Behavior change:** a `postinstall` script that exits with a non-zero
status now fails the build with an `ExecutionFailedException` that names
the package and includes the script output. Before, the exit code was
thrown away and the build continued as if nothing happened. This affects
any project whose `postinstall` script fails — the build now stops
instead of continuing with a broken `node_modules`.

- The command that runs a `postinstall` script now gets
`--config.verify-deps-before-run=false` when pnpm is in use. Before
running a script, pnpm checks that `node_modules` is up to date, and
that check starts its own install. That install repeats the one Flow
just ran, but without the arguments that exempt Vaadin's own packages
from the minimum release age and without `--ignore-scripts`. When it
failed, the `postinstall` script was never run.
- Nothing extra is passed for npm or bun. Neither checks anything before
running a script, and bun fails on an argument it does not know.
- The postinstall command is now also logged at debug level, next to the
install command.

No public or protected API changed. The new helper is package-private.

## Test summary

| # | Status | What the test verifies | Why it matters |
|---|--------|------------------------|----------------|
| 1 | ✅ | A `postinstall` script that exits non-zero makes `execute()`
throw, and the message names the package and contains what the script
printed | This was silently ignored before; without it a broken install
looks like a successful build |
| 2 | ✅ | The command actually run for pnpm contains
`--config.verify-deps-before-run=false` | Pins the fix end-to-end —
dropping the call that adds the argument would otherwise keep the suite
green |
| 3 | ✅ | pnpm options resolve to exactly
`--config.verify-deps-before-run=false` | The exact flag spelling is
what stops pnpm's extra install |
| 4 | ✅ | npm and bun options resolve to no arguments | bun fails on an
unknown argument, so passing it would break every bun build |

-
`TaskRunNpmInstallTest.runNpmInstall_postInstallFails_buildFailsWithTheScriptOutput`
→ 1 (inherited by `TaskRunPnpmInstallTest`, so it runs for npm and pnpm)
-
`TaskRunPnpmInstallTest.runPnpmInstall_postinstallDoesNotVerifyTheDependencies`
→ 2
-
`TaskRunNpmInstallTest.postinstallArguments_pnpm_doesNotVerifyTheDependencies`
→ 3
- `TaskRunNpmInstallTest.postinstallArguments_npmAndBun_needNone` → 4

Left untested on purpose: the happy path — a `postinstall` script that
succeeds and lets the build pass — is already pinned by the existing
`runNpmInstall_postInstall_*` tests. There is no end-to-end bun test
class, so bun is covered at the argument level only (row 4).

---------

Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com>
@sonarqubecloud

Copy link
Copy Markdown

@ZheSun88
ZheSun88 merged commit 7af5bbe into 25.2 Sep 18, 2026
35 checks passed
@ZheSun88
ZheSun88 deleted the cherry-pick-25620-to-25.2-1789713169286 branch September 18, 2026 07:35
@vaadin-bot

Copy link
Copy Markdown
Collaborator

This ticket/PR has been released with Vaadin 25.2.9.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants