Author SHA1 Message Date
Gitea BotandLunny Xiao bed218d62d Update the runner reference pages (#597)
Automated update of the generated runner reference pages from gitea/runner (main for the develop docs, the newest stable tag for every documented series), opened by the "update runner reference" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-597.docs-gitea-com.pages.dev

---------

Co-authored-by: Lunny Xiao <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/597
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-10-19 09:24:38 +00:00
Lunny Xiao 08aff1bae9 update to v28.2.0 (#594)
<!-- cloudflare-preview --> Preview: https://pr-594.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/594
Reviewed-by: silverwind <[email protected]>
2026-10-19 04:58:10 +00:00
Lunny Xiao 8cf8f49a95 Remove on-cloud-provider page (#593)
Cloud providers are now listed in awesome-gitea (https://gitea.com/gitea/awesome-gitea/pulls/202). Removes the page from the current docs and its zh-cn/zh-tw translations; released versioned docs are untouched.

Reviewed-on: https://gitea.com/gitea/docs/pulls/593
Reviewed-by: silverwind <[email protected]>
2026-10-18 19:06:41 +00:00
2af7b60fd9 docs(actions): document OIDC workload identity (#526)
Document the OpenID Connect tokens that Gitea Actions jobs can request since https://github.com/go-gitea/gitea/pull/39052, so they can sign in to cloud providers without long-lived secrets.

- Add an OpenID Connect page covering how to trust Gitea, request a token and match its claims
- Document the `id-token` permission scope and its maximum
- Translate both for zh-cn and zh-tw, which still listed `id-token` as unsupported

Co-authored-by: silverwind <[email protected]>
Co-authored-by: silverwind <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/526
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Max P. <[email protected]>
2026-10-18 07:12:16 +00:00
Jakub VránaandJakub Vrana db9e6f9f48 docs: document pre-filling issues and pull requests from query parameters (#592)
Describe the query parameters of the new issue and pull request pages, including those added in https://github.com/go-gitea/gitea/pull/39804.

---------

Co-authored-by: Jakub Vrana <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/592
Reviewed-by: wxiaoguang <[email protected]>
Reviewed-by: bircni <[email protected]>
Co-authored-by: Jakub Vrána <[email protected]>
2026-10-17 21:56:31 +00:00
Lunny Xiao b555a247e0 refactor: drop the remaining docusaurus names from identifiers and comments (#589)
Follow-up to #588.

- rename `DocusaurusSidebarItem` -> `SidebarSourceItem`, `fromDocusaurus` -> `fromSidebarSource`, `docusaurusSidebars` -> `sidebarSources`
- reword comments and READMEs that described behaviour by comparison with docusaurus; fix the stale note in `sites/docs/src/config/api.ts`
- drop `.docusaurus/` from `.gitignore`

No functional change. `pnpm check` has 0 errors and `pnpm build` still builds 6690 pages.

<!-- cloudflare-preview --> Preview: https://pr-589.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/589
Reviewed-by: silverwind <[email protected]>
2026-10-15 17:39:24 +00:00
Lunny Xiao 2275bef3bd refactor: rename i18n/*/docusaurus-plugin-content-docs to i18n/*/docs (#588)
Drops the leftover docusaurus name from the translated content directories.

- git mv i18n/{en-us,zh-cn,zh-tw}/docusaurus-plugin-content-docs -> i18n/{locale}/docs (pure renames, content unchanged)
- update the paths in packages/content-loader/src/products.ts, scripts/cut-version.mjs and sites/docs/README.md

current.json / version-*.json keep their names and stay beside the content.

Verified: pnpm build succeeds, 6690 pages built.

<!-- cloudflare-preview --> Preview: https://pr-588.docs-gitea-com.pages.dev
Reviewed-on: https://gitea.com/gitea/docs/pulls/588
Reviewed-by: Zettat123 <[email protected]>
2026-10-12 21:05:07 +00:00
Renovate Bot 1408e4890e chore(deps): update dependencies (#585)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | Type | Update | Pending |
|---|---|---|---|---|---|---|
| [@astrojs/starlight](https://starlight.astro.build) ([source](https://github.com/withastro/starlight/tree/HEAD/packages/starlight)) | [`0.42.4` → `0.42.5`](https://renovatebot.com/diffs/npm/@astrojs%2fstarlight/0.42.4/0.42.5) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@astrojs%2fstarlight/0.42.5?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@astrojs%2fstarlight/0.42.4/0.42.5?slim=true) | dependencies | patch | `0.42.6` |
| [actions/setup-node](https://github.com/actions/setup-node) ([changelog](https://github.com/actions/setup-node/compare/820762786026740c76f36085b0efc47a31fe5020..949feb2413d6458794dcd2491c4babbbce0c15c1)) | `8207627` → `949feb2` |  |  | action | digest |  |
| [astro](https://astro.build) ([source](https://github.com/withastro/astro/tree/HEAD/packages/astro)) | [`7.3.5` → `7.3.6`](https://renovatebot.com/diffs/npm/astro/7.3.5/7.3.6) | ![age](https://developer.mend.io/api/mc/badges/age/npm/astro/7.3.6?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/astro/7.3.5/7.3.6?slim=true) | dependencies | patch | `7.3.8` (+1) |
| [pnpm](https://pnpm.io) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm)) | [`12.8.2` → `12.10.1`](https://renovatebot.com/diffs/npm/pnpm/12.8.2/12.10.1) | ![age](https://developer.mend.io/api/mc/badges/age/npm/pnpm/12.10.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/pnpm/12.8.2/12.10.1?slim=true) | packageManager | minor | `12.11.2` (+2) |

---

### Release Notes

<details>
<summary>withastro/starlight (@&#8203;astrojs/starlight)</summary>

### [`v0.42.5`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0425)

[Compare Source](https://github.com/withastro/starlight/compare/@astrojs/[email protected]...@astrojs/[email protected])

##### Patch Changes

- [#&#8203;4227](https://github.com/withastro/starlight/pull/4227) [`5ba7a70`](https://github.com/withastro/starlight/commit/5ba7a70ad491ed8b5fc7033558dcc5428112cf97) Thanks [@&#8203;I90rche](https://github.com/I90rche)! - Adds Macedonian language support

</details>

<details>
<summary>withastro/astro (astro)</summary>

### [`v7.3.6`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#736)

[Compare Source](https://github.com/withastro/astro/compare/[email protected]@7.3.6)

##### Patch Changes

- [#&#8203;18166](https://github.com/withastro/astro/pull/18166) [`5134d0f`](https://github.com/withastro/astro/commit/5134d0f9e7478f23b1a590fa2416a2469c62ee23) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes an intermittent dev server crash when using `astro:actions` inside a server island with adapters that use a pre-bundled SSR environment (e.g. `@astrojs/cloudflare`)

- [#&#8203;18076](https://github.com/withastro/astro/pull/18076) [`8a2df66`](https://github.com/withastro/astro/commit/8a2df6675aa62fea382bce1cdb30c4260e0f104b) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes stale scoped styles during HMR when both markup and `<style>` are changed in a single save

- [#&#8203;18252](https://github.com/withastro/astro/pull/18252) [`2d29e7e`](https://github.com/withastro/astro/commit/2d29e7e38db65fcca7f241c967417003e20ecf43) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes CSS from other pages leaking into a page's `<head>` in dev when the page imports a module such as `astro:config/server`.

- [#&#8203;18000](https://github.com/withastro/astro/pull/18000) [`6724575`](https://github.com/withastro/astro/commit/67245751e02fb94223266d3c55cc7d7ac65ab060) Thanks [@&#8203;barclayd](https://github.com/barclayd)! - Fixes a bug where server islands containing framework components rendered empty in the dev server when using a custom `src/fetch.ts`

- [#&#8203;18241](https://github.com/withastro/astro/pull/18241) [`7c5fd6f`](https://github.com/withastro/astro/commit/7c5fd6f1faf4d87d0c9c5836ee47cc0d6d529ca3) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes the composable `i18n()` handler from `astro/fetch` and `astro/hono` returning an empty 404 for paths without a locale prefix. It now renders the custom 404 page, matching `astro()`.

- [#&#8203;18164](https://github.com/withastro/astro/pull/18164) [`1a6997f`](https://github.com/withastro/astro/commit/1a6997f280529650e4f1dacf0388e8fe07d05a1a) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes CSS Module HMR in dev when a component is rendered both with and without hydration on the same page. Astro now uses path-based class name hashing in dev mode so that editing CSS declarations no longer changes the generated selectors, allowing Vite's CSS HMR to update styles without a full page reload.

- [#&#8203;18243](https://github.com/withastro/astro/pull/18243) [`dd29d62`](https://github.com/withastro/astro/commit/dd29d62b1e7bd071804f822e90361cf5c9784682) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes `context.props` being `null` in middleware and endpoints when the composable `astro/hono` or `astro/fetch` `actions()` handler runs before `middleware()`, or when `pages()` runs without `middleware()`

- [#&#8203;18193](https://github.com/withastro/astro/pull/18193) [`95d5d16`](https://github.com/withastro/astro/commit/95d5d16ce983612e09b588bb249f76d273c872f7) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes `astro build` failing on Windows with Node.js 25+ with `ERR_INVALID_ARG_VALUE` when clearing the output directory hits a transient `EPERM` error

- [#&#8203;18220](https://github.com/withastro/astro/pull/18220) [`d6c13a4`](https://github.com/withastro/astro/commit/d6c13a48c682bb42da1eb295d520f9667035e1fb) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes type errors reported in Astro's built-in `<Picture />` and `<Font />` components when type-checking a project with `tsc` and `@astrojs/ts-content-mapper`

- [#&#8203;18133](https://github.com/withastro/astro/pull/18133) [`faac481`](https://github.com/withastro/astro/commit/faac481dc86efdd2e4987069a7298f2aea3f1c6c) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes lost request state when Vite discovers server dependencies during a request in Cloudflare dev mode

- [#&#8203;18146](https://github.com/withastro/astro/pull/18146) [`2af4516`](https://github.com/withastro/astro/commit/2af45163fb7757a533bdf929874278e0a7483cfc) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes CSS imported from an `injectScript('page')` script being dropped during build

- [#&#8203;18159](https://github.com/withastro/astro/pull/18159) [`e5f8fe0`](https://github.com/withastro/astro/commit/e5f8fe0fcfd1c1845ae425cbf874864ce5ab5a37) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes a hang when a `server:defer` component is inside a slot of another component in an MDX content collection entry

- [#&#8203;18246](https://github.com/withastro/astro/pull/18246) [`c3b42ad`](https://github.com/withastro/astro/commit/c3b42adb50612041b2e1c59f386065be934e5259) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes the `glob()` loader skipping content files whose paths contain `#` or `?`

- [#&#8203;18248](https://github.com/withastro/astro/pull/18248) [`b97184e`](https://github.com/withastro/astro/commit/b97184e53012404990da6788be5aa30f99c4cf6e) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes a bug where page routes added with `injectRoute()` returned a 404 when `i18n.routing.prefixDefaultLocale` was `true` and the route path had no locale prefix

- [#&#8203;18251](https://github.com/withastro/astro/pull/18251) [`3415263`](https://github.com/withastro/astro/commit/34152630b0dd77c2bfae68e568e01f824a8ee603) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes content collection changes being ignored in `astro dev` after the dev server restarts because of a config change

- [#&#8203;18226](https://github.com/withastro/astro/pull/18226) [`ad54af0`](https://github.com/withastro/astro/commit/ad54af063accfa123bf36c44a126da0404c480bb) Thanks [@&#8203;imharjot](https://github.com/imharjot)! - Fixes `Astro.cookies.get()` returning `undefined` for request cookies with empty values

- [#&#8203;18155](https://github.com/withastro/astro/pull/18155) [`37ab0e4`](https://github.com/withastro/astro/commit/37ab0e47ff53a688b667b6197807717bc4454e1b) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes nondeterministic ordering of the server manifest's `assets` array, ensuring builds with identical inputs produce byte-identical output

- [#&#8203;18099](https://github.com/withastro/astro/pull/18099) [`6987261`](https://github.com/withastro/astro/commit/69872618b7b7c915b2bd51a4b41e3a3e3116f3bb) Thanks [@&#8203;renovate](https://github.com/apps/renovate)! - Adds YAML parsing with timestamp and merge key support, and formats YAML errors consistently across Astro, Markdown, MDX, and Markdoc.

- [#&#8203;15595](https://github.com/withastro/astro/pull/15595) [`64e4039`](https://github.com/withastro/astro/commit/64e40396dc7e955986fd6d7bbd195f72556933d5) Thanks [@&#8203;qzio](https://github.com/qzio)! - Improves CSRF protection by taking modern browsers headers into account

- [#&#8203;18215](https://github.com/withastro/astro/pull/18215) [`941bd5e`](https://github.com/withastro/astro/commit/941bd5ecbe0b8ffdc9cde92880d402b8edc37d7a) Thanks [@&#8203;ump45nose](https://github.com/ump45nose)! - Fixes a runtime `ReferenceError` for inlined scripts that import a URL with `/* @&#8203;vite-ignore */`.

  Astro now inlines scripts after Vite replaces its preload markers, so ignored dynamic imports can remain inline without shipping an unresolved `__VITE_PRELOAD__` reference.

- [#&#8203;18179](https://github.com/withastro/astro/pull/18179) [`6caa659`](https://github.com/withastro/astro/commit/6caa6594afb557177e885077028db37506c409b7) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes custom View Transition direction names that are not valid CSS identifiers, such as names starting with a digit or containing spaces. These directions now match their animations correctly.

- [#&#8203;18250](https://github.com/withastro/astro/pull/18250) [`7eae39b`](https://github.com/withastro/astro/commit/7eae39bebbafc5a7b7763a8733a6e60ded5ead17) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes a 404 for a CSS file that a dynamic import preloads when the imported module uses a component that is also a hydrated island on the same page

- [#&#8203;18176](https://github.com/withastro/astro/pull/18176) [`7c9d00d`](https://github.com/withastro/astro/commit/7c9d00d251e42b577ed298879fe9e68a345bd776) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes duplicate CSS in production builds when a component is both server-rendered and used with `client:only` on the same page

- [#&#8203;18245](https://github.com/withastro/astro/pull/18245) [`492e95f`](https://github.com/withastro/astro/commit/492e95f8716829f1bd0ead70405ad982a0a5c105) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes the composable `astro/fetch` and `astro/hono` handlers (`trailingSlash()`, `redirects()`, `actions()`, `middleware()`, and `pages()`) so they reject over-encoded request paths with a `400 Bad Request`, matching `astro()`. For these requests, `redirects()` and `actions()` return the `400` response instead of `undefined`, so no redirect is issued and no action runs.

- [#&#8203;18078](https://github.com/withastro/astro/pull/18078) [`0a2ab10`](https://github.com/withastro/astro/commit/0a2ab102a91c141f42b6d7da9810db74f61d1fa0) Thanks [@&#8203;manuelgruber](https://github.com/manuelgruber)! - Fixes `Astro.rewrite()` and `context.rewrite()` selecting the wrong route in `astro dev` when two dynamic routes match the same path

- [#&#8203;18210](https://github.com/withastro/astro/pull/18210) [`62b13ba`](https://github.com/withastro/astro/commit/62b13ba3e0068057a47fbfc2ab8842b263c68f0d) Thanks [@&#8203;edmundhung](https://github.com/edmundhung)! - Allows Astro CLI commands to install tagged package versions

- [#&#8203;18069](https://github.com/withastro/astro/pull/18069) [`d39eb97`](https://github.com/withastro/astro/commit/d39eb97752ea6f5d02c82e6fe35fdbfb8d9f8982) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes a dev server dependency scan failure when an `.astro` file contains a literal `<script` in frontmatter or a template expression

- [#&#8203;18114](https://github.com/withastro/astro/pull/18114) [`c17d920`](https://github.com/withastro/astro/commit/c17d9209bef385ea88b6fee36e91ad4e635f4a86) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes Astro on StackBlitz and other WebContainer environments

- [#&#8203;18088](https://github.com/withastro/astro/pull/18088) [`cb767f9`](https://github.com/withastro/astro/commit/cb767f974e7ef1fec9b2d496e51b09c985c93947) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes a `MODULE_LEVEL_DIRECTIVE` warning during build caused by a stale `"use astro:head-inject"` directive in content collection propagated asset modules

- [#&#8203;18247](https://github.com/withastro/astro/pull/18247) [`2b3f73d`](https://github.com/withastro/astro/commit/2b3f73db230e3bff28ed9784a3b21257a97611e0) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes actions returning 500 for malformed JSON request bodies and undecodable action names. A `SyntaxError` from invalid JSON now returns 400 (`BAD_REQUEST`), and a `URIError` from a malformed percent-encoded action name now returns 404 (`NOT_FOUND`).

- [`1d9e910`](https://github.com/withastro/astro/commit/1d9e910a0308ef9699694a2871b3d71b6f5c3383) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Validates the `Host` header against `security.allowedDomains` when using the Node adapter

- [#&#8203;18249](https://github.com/withastro/astro/pull/18249) [`c7799a4`](https://github.com/withastro/astro/commit/c7799a4b21f2f7384cf070765f0912f9ac08848b) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes the `fontsource` font provider to resolve variable fonts when using the "Variable" suffix in the font name (e.g. `"Inter Variable"`)

- [#&#8203;18163](https://github.com/withastro/astro/pull/18163) [`e48da9d`](https://github.com/withastro/astro/commit/e48da9d4f6f293d8708bd8de9ae235429272f556) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes `<video muted>` losing its muted state after a ClientRouter navigation in Chrome

- [#&#8203;18244](https://github.com/withastro/astro/pull/18244) [`f67e7f7`](https://github.com/withastro/astro/commit/f67e7f7fe13e67131bb4e4c538970af60bdbbb94) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes localized error pages such as `src/pages/pt/404.astro` returning a `200` status when rendered through the `pages()` handler from `astro/fetch` or `astro/hono`

- [#&#8203;18242](https://github.com/withastro/astro/pull/18242) [`c5275e3`](https://github.com/withastro/astro/commit/c5275e3d5a688a7d246c9576b594870675b70e37) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes `experimental_AstroContainer` output including dev toolbar `data-astro-source-file` and `data-astro-source-loc` attributes when rendering components under Vitest

- Updated dependencies \[[`6987261`](https://github.com/withastro/astro/commit/69872618b7b7c915b2bd51a4b41e3a3e3116f3bb), [`62b13ba`](https://github.com/withastro/astro/commit/62b13ba3e0068057a47fbfc2ab8842b263c68f0d)]:
  - [@&#8203;astrojs/internal-helpers](https://github.com/astrojs/internal-helpers)@&#8203;0.12.0
  - [@&#8203;astrojs/markdown-satteri](https://github.com/astrojs/markdown-satteri)@&#8203;0.4.3

</details>

<details>
<summary>pnpm/pnpm (pnpm)</summary>

### [`v12.10.1`](https://github.com/pnpm/pnpm/releases/tag/v12.10.1): pnpm 12.10.1

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.10.0...v12.10.1)

This release fixes `pnpm install` failures after an `overrides` change and on a filtered frozen install with `catalogPrune`. It also fixes several bugs in the experimental `nodeLinker.type: loaded`, which now keeps its generated files in `node_modules`.

##### Patch Changes

- With `nodeLinker.type: loaded`, pnpm now writes its generated files to `node_modules`, which projects already ignore in git. The store manifest and loader are `node_modules/.pnpm/.store-manifest.json` and `node_modules/.pnpm/.store-loader.mjs`. Bin shims are in `node_modules/.bin`.

  Earlier versions wrote `.pnpm-store.json` and `.pnpm-store-loader.mjs` to the project root, and a `.pnpm` directory to the root and to each workspace package. Delete them after reinstalling.

- With `nodeLinker.type: loaded`, packages that ship their own `node_modules` directory, such as `npm` with its bundled dependencies, now load from the store. Before, one such package in the install stopped every Node.js process from starting.

- With `nodeLinker.type: loaded`, scripts can now run a Node.js runtime installed through `devEngines.runtime`. Before, every script that called `node` re-ran its own shim until it failed with "Argument list too long".

- With `nodeLinker.type: loaded`, Node.js processes start faster. In a project with 13,000 stored files, the startup overhead per process dropped from 67 ms to 18 ms.

- `pnpm install` no longer fails with `ERR_PNPM_NO_MATCHING_VERSION` after a change to `overrides` when the lockfile resolves an optional peer dependency to an npm alias of another package [#&#8203;16654](https://github.com/pnpm/pnpm/issues/16654).

- A frozen install with `catalogPrune` no longer removes catalog entries that `pnpm-lock.yaml` still records. Before, `pnpm install --frozen-lockfile --filter` failed with `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` when some workspace projects were missing from disk [#&#8203;16638](https://github.com/pnpm/pnpm/issues/16638).

- `pnpm install --fix-lockfile` no longer removes the `deprecated` and `hasBin` fields from lockfile entries [#&#8203;6600](https://github.com/pnpm/pnpm/issues/6600).

- With `enableGlobalVirtualStore`, an install that updates `node_modules` now repairs a package in the global virtual store that an interrupted install left without some of its dependency links or package files. Before, such an install kept the incomplete package if the project's `node_modules` already recorded it [#&#8203;16642](https://github.com/pnpm/pnpm/issues/16642).

- `pnpm install` now skips the Cargo and Python projects inside a nested directory that has its own `pnpm-workspace.yaml` or `.git` directory, such as a git worktree of the same workspace or a separate clone.

- The `Request took` warning for package metadata now starts timing when pnpm sends the request. Before, it also counted the time the request waited for a free request slot, so large installs printed it for requests the registry answered quickly.

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.10.0`](https://github.com/pnpm/pnpm/releases/tag/v12.10.0): pnpm 12.10.0

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.9.1...v12.10.0)

This release adds an experimental `loaded` node linker, lets `pnpm-lock.yaml` record resolution settings, and reads cached registry metadata faster. It also carries several security fixes, including one that stops a dependency version from writing files outside the global virtual store.

##### Minor Changes

- Added experimental `nodeLinker: { type: loaded }` installation. Compatible dependencies load directly from the content-addressable store through an automatically registered Node.js loader. `nodeLinker.excluded` selects packages and their dependency trees to install in the global virtual store.

- `lockfile.includeResolutionSettings: true` makes `pnpm-lock.yaml` record `autoDedupe`, `dedupeInjectedDeps`, `dedupePeerDependents` and `linkWorkspacePackages`. Installs then treat a lockfile that records other values as outdated. A lockfile that records `autoDedupe` is reused by later installs on any machine, so `pnpm run` after `pnpm install --frozen-lockfile` no longer starts another install [#&#8203;16583](https://github.com/pnpm/pnpm/issues/16583).

##### Patch Changes

##### Security

- `pnpm install` now prevents dependency versions with path traversal from writing files outside the global virtual store.

- pnpm now verifies locked config dependencies against their registry before installing them. Config dependencies must come from an npm registry. The lockfile can no longer replace the integrity of a config dependency pinned with `version+integrity`.

- Lockfile verification now checks the tarballs inside a `variations` resolution against the registry. A `name@version` lockfile entry with an empty `variations` resolution is now rejected.

- `pnpm audit signatures` now verifies signatures against the integrity recorded in the lockfile. Packages without a recorded integrity cannot pass signature verification.

- `pnpm install` and `pnpm publish` now reject archive metadata larger than 64 MiB before reading it into memory. Publishing a pre-built tarball also rejects manifests and README files larger than 64 MiB.

- Two URL or local path dependencies no longer share a virtual store directory when one URL has `+`, `#`, `:`, or `?` where the other has `/`. Such dependencies, including git dependencies pinned with `#`, now get a hash suffix on their directory name.

- The warning about an ignored project `.npmrc` registry setting no longer prints the username and password of a URL-scoped key such as `//user:[email protected]/${PATH}/:_authToken`.

##### Installing and resolving dependencies

- `pnpm install` now fails with `ERR_PNPM_UNSUPPORTED_PROTOCOL` when a dependency uses a specifier with a protocol pnpm does not support, such as Yarn's `patch:`. On Windows, such a specifier failed with `os error 123`. On other platforms, pnpm linked it to a directory that does not exist. Reading a `package.json` that fails now names the file [#&#8203;16590](https://github.com/pnpm/pnpm/issues/16590).

- `pnpm install` now fails with `ERR_PNPM_PACKAGE_MANIFEST_INVALID_ATTRIBUTE` when a project declares a dependency whose specifier is not a string, such as `"is-positive": 42`. Before, the dependency was silently left out of the lockfile. A `readPackage` hook can still correct the specifier.

- Fixed `pnpm install` failing with `ERR_PNPM_CMD_SHIM_RESOLVE_PATH` when an executable's parent directory contains a dangling symlink.

- `pnpm install --frozen-lockfile` no longer fails with `ERR_PNPM_RESOLUTION_SHAPE_MISMATCH` when a `name@version` lockfile entry has a resolution served by a custom fetcher [pnpm/tasks#108](https://github.com/pnpm/tasks/issues/108).

- `pnpm install --fix-lockfile` repairs a lockfile whose importer references a package that has no snapshot entry, as left by a badly merged lockfile. It failed with `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY` since 12.8.0 [#&#8203;16618](https://github.com/pnpm/pnpm/issues/16618).

- When a dependency moves an exact dependency of its own to an older version, a peer dependency that pnpm installed automatically now moves with it. Before, `pnpm install` and `pnpm dedupe` kept the newer locked version of the peer, so the lockfile held two copies of it, for example two copies of `vue` [pnpm/tasks#61](https://github.com/pnpm/tasks/issues/61).

- `pnpm dedupe` now reads registry metadata for a dependency pinned to an exact version, as `pnpm install` does. If the registry metadata disagreed with the package's `package.json`, the lockfile it wrote depended on whether `minimumReleaseAge` was set [#&#8203;16615](https://github.com/pnpm/pnpm/issues/16615).

##### Speed and size

- Dependency resolution reads cached registry metadata faster. The metadata cache moved to `<cache-dir>/v12/`, so the first install after upgrading downloads registry metadata again. A damaged cache entry is downloaded again, or reported as an error when `--offline` is set. `pnpm cache prune` also removes the metadata cache that older pnpm versions wrote under `<cache-dir>/v11/` [#&#8203;13512](https://github.com/pnpm/pnpm/pull/13512).

- Package metadata requests no longer wait behind queued tarball downloads when `maxSockets` or a proxy limits the connections to a registry. Large installs resolve faster and print fewer `Request took` warnings.

- Sped up installs in large workspaces on macOS when the dependency links already exist. pnpm now keeps a link that already points at the right package without trying to create it first. Relinking the direct dependencies of 1,000 workspace projects took 45 ms, down from 116 ms [pnpm/tasks#65](https://github.com/pnpm/tasks/issues/65).

- Commands in a project that pins a different pnpm version start about 13 ms faster on macOS. pnpm now runs the pinned version's binary directly, without the shell script in front of it [pnpm/tasks#66](https://github.com/pnpm/tasks/issues/66).

- The pnpm binary is about 0.9 MB smaller, and the arm64 Linux binary is about 1 MB smaller still.

##### Running scripts and commands

- `pnpm run` no longer prints `[ELIFECYCLE] Command failed ...` after Ctrl+C ends the script. pnpm still exits the way the script's shell did: on Windows with the shell's exit code (`cmd` reports `-1073741510`, PowerShell `1`), on Unix by re-raising `SIGINT` [#&#8203;16579](https://github.com/pnpm/pnpm/issues/16579).

- `pnpm run "/<regex>/"` now accepts JavaScript regular expression syntax such as lookahead and lookbehind. A selector like `"/^hello:(?!b).*$/"` failed with `ERR_PNPM_NO_SCRIPT` [#&#8203;16604](https://github.com/pnpm/pnpm/issues/16604).

- `pnpm run` and `pnpm exec` now forward `--config.*` command-line flags to the install started by `verifyDepsBeforeRun` [pnpm/tasks#60](https://github.com/pnpm/tasks/issues/60).

- On Windows, a process started by `pnpm run` or `pnpm exec` can again start a child with `CREATE_BREAKAWAY_FROM_JOB`. That child keeps running after pnpm exits, even if the command fails [#&#8203;16628](https://github.com/pnpm/pnpm/issues/16628).

- Empty `nodeOptions` values from command-line flags and environment variables now override lower-priority settings. Scripts retain `NODE_OPTIONS` from the parent environment or `extraEnv` when `nodeOptions` is empty.

- pnpm now reads the `failIfNoMatch` setting from `pnpm-workspace.yaml`, so a filter that matches no workspace project exits with code 1 when the setting is `true`. The new `--no-fail-if-no-match` flag turns the setting off for one command [#&#8203;16577](https://github.com/pnpm/pnpm/issues/16577).

##### Configuration, setup, and pnpm versions

- `pnpm config get --global` and `pnpm config list --global` now show only the global configuration, also when run inside a project. Settings from the project's `pnpm-workspace.yaml` and `.npmrc` were included before. The same applies to `--location=global` [#&#8203;16598](https://github.com/pnpm/pnpm/issues/16598).

- pnpm now prints config warnings, such as an unset environment variable in `.npmrc`, when loading the config fails.

- pnpm 11 releases older than 11.28.4 can run pnpm 12 again when the `packageManager` field pins it. Since 12.9.0 they failed with `SyntaxError: Invalid or unexpected token` [#&#8203;16594](https://github.com/pnpm/pnpm/issues/16594).

- On Windows, `pnpm self-update` no longer runs the update a second time when it replaces a `pnpm.cmd` linked by pnpm 12.8 or older. cmd.exe read on in the replaced `pnpm.cmd`, printed an error about a command that is not recognized, and ran the new pnpm once more [#&#8203;16573](https://github.com/pnpm/pnpm/issues/16573).

- `pnpm setup` now puts `$PNPM_HOME/bin` first on `PATH` in login shells that inherited it further down, such as the VS Code terminal on macOS. Before, another `node` took precedence over the one installed by `pnpm runtime set node -g`. Run `pnpm setup` again to update the block in your shell config [#&#8203;16635](https://github.com/pnpm/pnpm/issues/16635).

- `pnpm setup` now names the shell config file even if it is already up to date [#&#8203;16608](https://github.com/pnpm/pnpm/issues/16608).

##### Updating, auditing, and publishing

- `pnpm update --latest` now applies the `savePrefix` setting when it rewrites a dependency whose range has no operator of its own, such as `<2.0.0`.

- The interactive `pnpm audit --fix` picker now shows each patched version with the `saveExact` and `savePrefix` style that the override is written with [#&#8203;13209](https://github.com/pnpm/pnpm/issues/13209).

- `pnpm unpublish <pkg>@&#8203;<version>` now deletes the tarball under the registry's path when the registry is served under one, such as Gitea's npm registry. It used to send the delete to the host root and report success without removing the version [#&#8203;16568](https://github.com/pnpm/pnpm/issues/16568). It also no longer mistakes a sibling path such as `/npm-mirror/` for the registry path `/npm/` [pnpm/tasks#94](https://github.com/pnpm/tasks/issues/94).

##### Output and messages

- A warning about a project's `devEngines` or `packageManager` pin is now printed to stderr. A command such as `pnpm cache path` or `pnpm list --json` keeps only its own output on stdout [#&#8203;16584](https://github.com/pnpm/pnpm/issues/16584).

- `pnpm list` now reports the correct package paths when `nodeLinker` is `hoisted` [#&#8203;9593](https://github.com/pnpm/pnpm/issues/9593).

- Resolution errors now name the failing dependency and its parent packages. Fatal errors appear as structured error records with their error codes when using `--reporter=ndjson`.

- The error for an invalid git repository in the lockfile now has the code `ERR_PNPM_INVALID_GIT_REPOSITORY`. Its message now lists every rejected form of the value.

- `pnpm runtime --help` and `pnpm help runtime` now name the `set` subcommand and the runtimes it accepts [#&#8203;16580](https://github.com/pnpm/pnpm/issues/16580).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.9.1`](https://github.com/pnpm/pnpm/releases/tag/v12.9.1): pnpm 12.9.1

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.9.0...v12.9.1)

This release moves the WebContainer build into a separate `@pnpm/wasm` package, shrinks the `pnpm` package back to about 4 MB, and fixes `pnpm publish` with provenance from GitLab CI.

##### Patch Changes

- The WebAssembly build for StackBlitz WebContainers now ships as a separate `@pnpm/wasm` package. The `pnpm` and `@pnpm/exe` packages no longer include it, which brings their unpacked size back from about 55 MB to about 4 MB. In a WebContainer, install `@pnpm/wasm` with npm to get the `pnpm` command.

- `pnpm publish` with provenance from GitLab CI is no longer rejected by the npm registry with a 422 error. The provenance statement now includes the GitLab CI variables in `invocation.parameters`, as npm does [#&#8203;16551](https://github.com/pnpm/pnpm/issues/16551).

- `pnpm audit signatures` now uses the TLS settings of the redirect target when a registry redirects its signing-keys request, for example to registry.npmjs.org. A `cafile` scoped to a private registry no longer makes the redirected request fail [#&#8203;16541](https://github.com/pnpm/pnpm/issues/16541).

- Fixed `pnpm install --frozen-lockfile` rejecting an up-to-date lockfile when an injected workspace package uses a catalog entry in `peerDependencies` [#&#8203;16557](https://github.com/pnpm/pnpm/issues/16557).

- The `[<since>]` filter selector works again with Git 2.24 through 2.27 [#&#8203;16561](https://github.com/pnpm/pnpm/issues/16561). With Git older than 2.24, the selector now fails with an error that names the required Git version.

  It also detects changes in projects whose directory names contain non-ASCII characters. Such a change used to be credited to the parent project. `changedFilesIgnorePattern` and `testPattern` now match changed files whose names contain non-ASCII characters.

- The `pnpm` executable is about 10% smaller. On macOS arm64 it went from 45.1 MB to 40.3 MB.

- Sped up trust downgrade checks for packages with long release histories.

- With `optimisticRepeatInstall: false`, `pnpm install` now runs the projects' own lifecycle scripts, such as `prepare`, even when `node_modules` is already up to date [#&#8203;16545](https://github.com/pnpm/pnpm/issues/16545).

- `pnpm self-update` now fails for Homebrew-installed pnpm and prints the `brew upgrade` command for the installed formula, such as `brew upgrade pnpm` or `brew upgrade pnpm@11`. It used to install a second copy of pnpm that the Homebrew one kept shadowing [#&#8203;16547](https://github.com/pnpm/pnpm/issues/16547).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.9.0`](https://github.com/pnpm/pnpm/releases/tag/v12.9.0): pnpm 12.9

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.8.2...v12.9.0)

This release runs pnpm in StackBlitz WebContainers, adds a per-registry `networkConcurrency` setting, and records every installed project in the store. It also carries a security fix for `pnpm login`.

##### Minor Changes

- pnpm now automatically uses WebAssembly in StackBlitz WebContainers, including when installation scripts are disabled. Native installations continue to use the native executable when installation scripts are enabled.

- A `registries` entry can now set `networkConcurrency`, the most requests pnpm keeps in flight to that registry's origin. Requests to other registries keep the overall limit. The setting may live in `pnpm-workspace.yaml` or the global `config.yaml`.

  ```yaml
  registries:
    https://npm.corp.example.com/:
      scopes: ["@&#8203;acme"]
      networkConcurrency: 4
  ```

- `pnpm install` now records every project it installs in the store's `projects` directory, as a symlink to the project directory. A `--frozen-store` install without the global virtual store still records nothing. Only projects that used the global virtual store were recorded before [#&#8203;6929](https://github.com/pnpm/pnpm/issues/6929).

##### Patch Changes

- `pnpm login` no longer forwards credentials in its request body to another origin during redirects.

##### Installing packages

- Fixed `pnpm install` failing on Android with `ERR_PNPM_STORE_DIR_ACQUIRE_OPERATION_LOCK` [#&#8203;16508](https://github.com/pnpm/pnpm/issues/16508).

- `pnpm install --frozen-lockfile` again succeeds when a workspace project recorded in `pnpm-lock.yaml` has no directory, such as a project left out of a Docker build context. It still fails if the project's directory exists without a `package.json` [#&#8203;16453](https://github.com/pnpm/pnpm/issues/16453).

- `pnpm install --frozen-lockfile` no longer requires a `pnpm-lock.yaml` in a project that has no dependencies. It also succeeds when `pnpm-lock.yaml` records only the pinned pnpm version, as other commands write it when they run before the first install [#&#8203;16477](https://github.com/pnpm/pnpm/issues/16477).

- Fixed `pnpm install --frozen-lockfile` rejecting a fresh lockfile when an injected workspace dependency has an optional peer supplied by another workspace project [#&#8203;16428](https://github.com/pnpm/pnpm/issues/16428).

- With `nodeLinker: hoisted`, a filtered install now keeps the packages of the workspace projects an earlier install put in `node_modules`. This also covers the install that `pnpm --filter <selector> run` and `pnpm --filter <selector> exec` start before the command. Before, these installs removed every package that only the unselected projects needed [#&#8203;16483](https://github.com/pnpm/pnpm/issues/16483).

- `pnpm install` with `nodeLinker: hoisted` now refreshes directories supplied by custom fetchers when reinstalling. pnpm also keeps the symlinks inside those directories.

- With `enableGlobalVirtualStore` on, scripts can run entry points that a CommonJS require hook loads again, such as `ts-node index.ts`. They failed with `ERR_UNKNOWN_FILE_EXTENSION` on Node.js versions without built-in TypeScript support [#&#8203;16436](https://github.com/pnpm/pnpm/issues/16436).

- pnpm now keeps each project's current lockfile and hidden hoisted dependencies in its own `node_modules/.pnpm` when `virtualStoreDir` points at a shared global virtual store. `--virtual-store-dir` now sets the global virtual store's location too [pnpm/tasks#47](https://github.com/pnpm/tasks/issues/47).

- `pnpm clean` no longer deletes the project when `virtualStoreDir` or `globalVirtualStoreDir` is set to the project directory. It also leaves a directory outside the project alone when the setting reaches it through a symlink. It now removes a global virtual store that `globalVirtualStoreDir` places inside the project, as it does for `virtualStoreDir`.

##### Optional dependencies

- `pnpm install` no longer fails when a dependency of an optional dependency is missing from the registry. Like npm, pnpm now leaves out the nearest optional dependency above it, together with its subtree [#&#8203;16511](https://github.com/pnpm/pnpm/issues/16511).

- When an optional dependency fails to build, pnpm now removes its link from `node_modules`. A repeat `pnpm install` then reports "Already up to date" and no longer reruns the failing build [#&#8203;16468](https://github.com/pnpm/pnpm/issues/16468).

- `pnpm install` now prints a warning with the error when an optional dependency cannot be fetched and is skipped. The skipped package is no longer counted in the `Packages: +N` summary. The `pnpm:skipped-optional-dependency` log reports the skip with the `fetch_failure` reason [#&#8203;16514](https://github.com/pnpm/pnpm/issues/16514).

##### Resolving dependencies

- Fixed `pnpm install` changing an unchanged project's direct dependency to a sibling workspace's pinned version when its dependency tree contains a cycle [#&#8203;16417](https://github.com/pnpm/pnpm/issues/16417).

- With `autoDedupe` enabled, downgrading a dependency in one workspace project now moves the other projects to that version when it satisfies their ranges. This also applies to a filtered `pnpm --filter <project> add` [#&#8203;16432](https://github.com/pnpm/pnpm/issues/16432).

- `pnpm install` and `pnpm dedupe` now move an optional peer to the version already in the dependency graph when no other package provides its locked version anymore. After a bump such as `vue` 3.5.40 to 3.5.43, the lockfile kept a second copy of `@vue/server-renderer` for `@vue/test-utils` [#&#8203;16443](https://github.com/pnpm/pnpm/issues/16443).

- `pnpm dedupe --check` no longer fails right after `pnpm install` when a project's optional peer is satisfied by a package another workspace project installs. `pnpm dedupe` now picks the same versions for that package's dependencies as `pnpm install` [#&#8203;16447](https://github.com/pnpm/pnpm/issues/16447).

- `pnpm install` no longer re-resolves an up-to-date lockfile on every run when a patched package is a peer in a peer cycle [#&#8203;16418](https://github.com/pnpm/pnpm/issues/16418).

- With `autoDedupe` enabled, `pnpm install --lockfile-only` no longer resolves the dependency graph again when nothing changed since an earlier `--lockfile-only` install deduplicated the lockfile. Such an install keeps the lockfile even if versions were published since it was written, or if only a setting such as `resolutionMode` changed. Run `pnpm dedupe` to apply such a change [#&#8203;16458](https://github.com/pnpm/pnpm/issues/16458).

##### Speed and network

- A repeat `pnpm install` in a large workspace reports "Already up to date" faster [#&#8203;16487](https://github.com/pnpm/pnpm/issues/16487).

- Sped up dependency resolution of workspaces with many peer dependencies.

- `pnpm install` sends fewer registry metadata requests when the lockfile already decides which version a range resolves to. This now also covers ranges that several locked versions satisfy when one of them outranks the others, and direct dependencies kept at their locked version. Packages that `minimumReleaseAgeExclude` lists without a version now reuse cached registry metadata the same way they do when `minimumReleaseAge` is not set [#&#8203;16458](https://github.com/pnpm/pnpm/issues/16458).

- pnpm no longer downloads every packument again on each install from a registry whose metadata responses forbid caching, such as `Cache-Control: no-store`. pnpm revalidates the cached metadata with a conditional request, so a registry that supports conditional requests answers with a 304 when the package has not changed [#&#8203;16528](https://github.com/pnpm/pnpm/issues/16528).

- Cached metadata for a package published within `minimumReleaseAge` is now revalidated with its ETag, so the npm registry can answer `304 Not Modified`. Before, the next install that checked the cache downloaded the whole document again [#&#8203;16506](https://github.com/pnpm/pnpm/issues/16506).

- A fetch timeout while other downloads from the same host are still running now lowers concurrency for that host to one connection. Retries of that request, and later downloads from that host, use the lower concurrency. Other hosts keep the configured concurrency [#&#8203;12791](https://github.com/pnpm/pnpm/issues/12791).

- Sped up `pnpm install --offline` when the version a range picks is not in the store. While it looks for a version the store holds, pnpm now reads only the versions the range admits [#&#8203;16495](https://github.com/pnpm/pnpm/issues/16495).

- `pnpm install --offline` now reuses config dependency tarballs that are already present in the store [pnpm/tasks#46](https://github.com/pnpm/tasks/issues/46).

##### Running scripts

- `pnpm -s <script>` runs the script again, with `-s` meaning `--sequential` as it does for `pnpm run -s <script>`. pnpm rejected it with "unexpected argument '-s' found" [#&#8203;16446](https://github.com/pnpm/pnpm/issues/16446).

- `pnpm run` and `pnpm exec` now warn and run the command when the install that `verifyDepsBeforeRun` starts fails. This lets scripts run in sandboxes where pnpm cannot install, such as containers with a read-only store or no network [#&#8203;15173](https://github.com/pnpm/pnpm/issues/15173).

- A filtered `pnpm run` or `pnpm exec` now finds dependencies out of date when a workspace dependency of a selected project has no `node_modules` directory, as after a filtered install. With `verifyDepsBeforeRun: install`, pnpm installs that dependency before running the command [pnpm/tasks#45](https://github.com/pnpm/tasks/issues/45).

- Scripts run without a terminal no longer start a second `sh` each. One watchdog per pnpm command now ends every script's process group if pnpm is killed, so `pnpm -r run` across many projects starts half as many processes [#&#8203;16489](https://github.com/pnpm/pnpm/issues/16489).

- `Terminate batch job (Y/N)?` no longer appears after pressing Ctrl+C in a script started with `pnpm` from PowerShell or cmd on Windows [#&#8203;16502](https://github.com/pnpm/pnpm/issues/16502).

- `pnpm rebuild` and `pnpm approve-builds` refresh command launchers when a build changes a command's interpreter or replaces it with a native executable. Dependent packages' build scripts use the refreshed launchers.

- When `pnpm run <script>` or `pnpm <script>` finds nothing to run and `--filter` follows the script name, the error now suggests putting the filter option before the script name [#&#8203;4655](https://github.com/pnpm/pnpm/issues/4655).

- Package-name filters now support `?` to match one character [#&#8203;2817](https://github.com/pnpm/pnpm/issues/2817).

##### The pinned pnpm and `pnpm self-update`

- pnpm no longer downloads the project's pinned pnpm version again on every command when `nodeVersion` in `pnpm-workspace.yaml` names a different Node.js major than the `node` on `PATH`. Before, each of those commands took about a second longer and failed without network access [#&#8203;16497](https://github.com/pnpm/pnpm/issues/16497).

- Several `pnpm` commands started at once in a project that pins `packageManager` no longer fail with `The process cannot access the file because it is being used by another process` on Windows while the pinned pnpm is being installed.

- pnpm can now switch to a `packageManager` version below 11 on x64 musl Linux, such as Alpine [#&#8203;16467](https://github.com/pnpm/pnpm/issues/16467).

- A `devEngines.packageManager` range no longer makes pnpm replace the version recorded in `pnpm-lock.yaml` with the running pnpm while the recorded version still satisfies the range. When pnpm does record a version, it records the running pnpm only if it meets `minimumReleaseAge`. Otherwise it records the newest version in the range that meets it, or the running pnpm if none does [#&#8203;16431](https://github.com/pnpm/pnpm/issues/16431).

- On Windows, `pnpm self-update` now replaces a `pnpm.exe` left in `PNPM_HOME` or in `PNPM_HOME\bin`. Windows ran that executable in place of the updated `pnpm.cmd` shim, so `pnpm --version` kept printing the old version after a successful update. If the executable was in `PNPM_HOME`, `self-update` now asks you to run `pnpm setup` [#&#8203;9094](https://github.com/pnpm/pnpm/issues/9094).

- `pnpm self-update` now checks that a version installed as the JavaScript `pnpm` can start before making it the global `pnpm`. If Node.js is missing, the update fails and the current `pnpm` stays in place.

##### Other commands

- `pnpm deploy` now finds patches and local dependencies when the target directory sits under a symlink, such as `/tmp` on macOS. It failed with `ERR_PNPM_PATCH_NOT_FOUND` [#&#8203;16470](https://github.com/pnpm/pnpm/issues/16470).

- `pnpm deploy --legacy` now resolves the deployed project's relative `file:`, `link:`, and path dependencies from the project's own directory [#&#8203;16475](https://github.com/pnpm/pnpm/issues/16475).

- `pnpm update --global` now removes hard-linked executables from `PNPM_HOME` when migrating packages from the old global layout [#&#8203;16420](https://github.com/pnpm/pnpm/issues/16420).

- `pnpm store prune` no longer fails on store index entries that pnpm 11 wrote for git-hosted packages without a `package.json`. Entries that still cannot be read are kept and counted in the prune summary.

- `pnpm store prune` now aborts when an error other than a missing directory occurs while scanning project directories in the mark phase.

- `pnpm config get` and `pnpm config list` now report a setting given on the command line with `--config.<name>=<value>`. Before, a value such as `--config.node-linker=hoisted` reached the install but was absent from the reported configuration [#&#8203;16276](https://github.com/pnpm/pnpm/issues/16276).

- `pnpm -r pkg get` now reports every selected project when several share a package name. Projects with the same name are keyed by their directory relative to the workspace root. Before, only one of them appeared in the output.

- The install summary shows a `link:` dependency as `+ name <- path`, and the Node.js API's `hideLinkedPkgsDiff` reporter option leaves matching linked dependencies out of the summary.

- The `--force` help text of `pnpm install` and `pnpm add` now says that `--force` keeps skipping optional dependencies built for other platforms. It points to `forceIgnoresPlatform` and the `--os`, `--cpu`, and `--libc` options for installing them [#&#8203;16435](https://github.com/pnpm/pnpm/issues/16435).

- The `homepage` field of the published `pnpm` package points to <https://pnpm.io> again.

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTEuMiIsInVwZGF0ZWRJblZlciI6IjQzLjE5MS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

<!-- cloudflare-preview --> Preview: https://pr-585.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/585
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: Renovate Bot <[email protected]>
2026-10-12 16:24:14 +00:00
Gitea Bot adaae2f859 Update the runner reference pages (#586)
Automated update of the generated runner reference pages from gitea/runner (main for the develop docs, the newest stable tag for every documented series), opened by the "update runner reference" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-586.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/586
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-10-12 16:04:09 +00:00
silverwind 424c481e19 docs: drop the gc.reflogExpire default (#584)
Gitea no longer sets `gc.reflogExpire`, leaving reflog expiry to git's defaults, see https://github.com/go-gitea/gitea/pull/39703, backported in https://github.com/go-gitea/gitea/pull/39711.

Reviewed-on: https://gitea.com/gitea/docs/pulls/584
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-10-10 16:56:06 +00:00
silverwind ad95b7776c docs: update GO_GET_CLONE_URL_PROTOCOL default (#580)
Copy the new `app.example.ini` wording from https://github.com/go-gitea/gitea/pull/39674, backported in https://github.com/go-gitea/gitea/pull/39676. `GO_GET_CLONE_URL_PROTOCOL` now defaults to ssh when `DISABLE_HTTP_GIT` is enabled.

Reviewed-on: https://gitea.com/gitea/docs/pulls/580
Reviewed-by: wxiaoguang <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-10-08 13:28:26 +00:00
Lunny Xiao f940b0bc9e ci: generate the v28 awesome page and explain skipped previews (#579)
The v28 release (#564) added the `prepare-awesome-v%` Makefile rule, but the checks and publish workflows still only prepare awesome.md up to 1.27, so version-28/awesome.md is not generated in CI. Both workflows now call `prepare-awesome-v28`.

The preview step is skipped when Cloudflare secrets are unavailable, which is the case for pull requests from forks (#557 and #565 have no preview line, while same-repo PRs like #533 and #563 do). The skip is now a visible notice. Previewing fork PRs would need a separate trusted workflow and is not part of this change.

Reviewed-on: https://gitea.com/gitea/docs/pulls/579
Reviewed-by: bircni <[email protected]>
Co-authored-by: Lunny Xiao <[email protected]>
2026-10-07 04:29:39 +00:00
bircniandLunny Xiao 444879b492 update to 28.1.0 (#578)
Bump the v28 docs to the 28.1.0 release, following the 1.27.3 bump (#532):

- point the `28` version variables (`version`, `sourceVersion`, `dockerVersion`, `displayVersion`) at 28.1.0
- update the v28 entry in `update_api_docs.sh` to `28.1.0`
- regenerate `static/swagger-28.json` and `static/openapi3-28.json` from the `v28.1.0` tag

---------

Co-authored-by: Lunny Xiao <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/578
Reviewed-by: TheFox0x7 <[email protected]>
Co-authored-by: bircni <[email protected]>
2026-10-07 03:00:58 +00:00
bc7e2b3caa docs: link-local egress targets are allowable (#573)
Documents https://github.com/go-gitea/gitea/pull/39560: link-local addresses can be allowed with an explicit `ALLOWED_HOST_LIST` entry again, only cloud metadata endpoints stay hard-denied. Merge after that PR.

*Written by Claude.*

---------

Co-authored-by: bircni <[email protected]>
Co-authored-by: TheFox0x7 <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/573
Reviewed-by: bircni <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-10-06 21:56:56 +00:00
Renovate Bot 5823b20e70 chore(deps): update dependencies (#575)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [pnpm](https://github.com/pnpm/pnpm/tree/main/pnpm) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm)) | [`12.6.0` → `12.8.2`](https://renovatebot.com/diffs/npm/pnpm/12.6.0/12.8.2) | ![age](https://developer.mend.io/api/mc/badges/age/npm/pnpm/12.8.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/pnpm/12.6.0/12.8.2?slim=true) |
| [sharp](https://sharp.pixelplumbing.com) ([source](https://github.com/lovell/sharp), [changelog](https://github.com/lovell/sharp/blob/main/docs/src/content/docs/changelog.md)) | [`0.35.4` → `0.35.5`](https://renovatebot.com/diffs/npm/sharp/0.35.4/0.35.5) | ![age](https://developer.mend.io/api/mc/badges/age/npm/sharp/0.35.5?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/sharp/0.35.4/0.35.5?slim=true) |
| [starlight-openapi](https://github.com/HiDeoo/starlight-openapi) ([source](https://github.com/HiDeoo/starlight-openapi/tree/HEAD/packages/starlight-openapi)) | [`0.26.2` → `0.26.3`](https://renovatebot.com/diffs/npm/starlight-openapi/0.26.2/0.26.3) | ![age](https://developer.mend.io/api/mc/badges/age/npm/starlight-openapi/0.26.3?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/starlight-openapi/0.26.2/0.26.3?slim=true) |

---

### Release Notes

<details>
<summary>pnpm/pnpm (pnpm)</summary>

### [`v12.8.2`](https://github.com/pnpm/pnpm/releases/tag/v12.8.2): pnpm 12.8.2

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.8.1...v12.8.2)

pnpm 12.8.2 fixes a startup crash on Linux ppc64le and `UnknownIssuer` errors on systems without CA certificates. `pnpm run` no longer installs before every script on CI when `autoDedupe` is enabled, and resolution and hoisted installs on macOS are faster.

##### Patch Changes

##### Platforms and environments

- Fixed pnpm crashing on startup on Linux ppc64le [#&#8203;16380](https://github.com/pnpm/pnpm/issues/16380).

- Fixed installs failing with `UnknownIssuer` on Linux systems without CA certificates, such as `node:24-slim`, when `NODE_EXTRA_CA_CERTS` is set. The extra certificates now extend the bundled CA roots [#&#8203;16365](https://github.com/pnpm/pnpm/issues/16365).

- pnpm now creates its store operation locks and other per-user lock files in `$XDG_RUNTIME_DIR` when it points to a directory only the user can write to. Otherwise, pnpm still uses `/tmp` on Linux and macOS. Sandboxes that block writes to `/tmp` can point `XDG_RUNTIME_DIR` at a writable directory [#&#8203;16390](https://github.com/pnpm/pnpm/issues/16390).

- POSIX bin shims and the `pnpm`, `pn`, `pnpx`, and `pnx` launchers now run inside a Nix build, where the system default path holds none of the utilities they call. Installing again replaces the shims already in `node_modules` [#&#8203;16377](https://github.com/pnpm/pnpm/issues/16377).

- In a project that pins another pnpm version, pnpm now passes a command with an option it does not know to the pinned version. Before, pnpm rejected the option before switching, so `pnpm install --auto-dedupe` failed with "Unknown option" even though the pinned pnpm supports it [#&#8203;16353](https://github.com/pnpm/pnpm/issues/16353).

##### Installing and resolving dependencies

- `pnpm install --frozen-lockfile` now fails when `Cargo.lock` does not satisfy a dependency requirement in `Cargo.toml`. The error names the crate and the version the lockfile holds [#&#8203;16355](https://github.com/pnpm/pnpm/issues/16355).

- `pnpm install` returns "Already up to date" again in a workspace with injected workspace dependencies and a shared lockfile. Since 12.7.0 every repeat install in such a workspace ran the full install and copied the injected projects again.

- With `injectWorkspacePackages: true`, a fresh `pnpm install` now records a workspace dependency as `link:` when its injected copy differs from the project only by an optional peer that peer-dependent dedupe merges. It was recorded as a peer-suffixed `file:` copy [#&#8203;16354](https://github.com/pnpm/pnpm/issues/16354).

- `pnpm dedupe --check` now passes right after `pnpm dedupe` when deduplication merges variants of a package that differ only in their peers. A lockfile key whose peer suffix named a merged variant now names the variant that replaced it [#&#8203;16356](https://github.com/pnpm/pnpm/issues/16356).

- When `minimumReleaseAge` hides the version that `latest` points to, pnpm now falls back to a prerelease of the same major before a stable version of an older major. A stable version of the same major is still preferred. Before, while a new `1.0.0` was too new, `latest` fell back to an old `0.0.1` even though `1.0.0-beta.4` had been `latest` until then [#&#8203;16388](https://github.com/pnpm/pnpm/issues/16388).

- Git-hosted dependencies now respect `pmOnFail`. If it is set to anything other than `download`, a git-hosted dependency that pins a pnpm version is prepared by the running pnpm, and pnpm does not download the pinned version [#&#8203;16376](https://github.com/pnpm/pnpm/issues/16376).

- pnpmfile hooks such as `readPackage` now run once for a dependency that several packages request at the same time. They could run twice for it before.

- `childConcurrency` now defaults to 5, the documented value. It used to be capped at 4 and to follow the host's CPU count.

##### Running scripts

- `pnpm run` and `pnpm exec` no longer install dependencies before every script on CI when `autoDedupe` is enabled. `pnpm install --frozen-lockfile` now keeps the deduplication record left by an earlier install [#&#8203;16374](https://github.com/pnpm/pnpm/issues/16374).

- On macOS and Linux, lifecycle scripts and `pnpm run` now always get `PATH` from the `PATH` variable. When the environment also held a `Path` variable, a script sometimes got `Path`'s value, and failed with `node: not found` [#&#8203;16308](https://github.com/pnpm/pnpm/issues/16308).

- `pnpm run` now exits after a `SIGTERM` in a container where pnpm is PID 1 and the script runs pnpm again, as `"start": "pnpm serve"` does. Since 12.6.0 it kept waiting after the script had shut down, until the container runtime killed it.

##### Other commands and settings

- `pnpm config get globalShims`, `pnpm shim list`, and global installs no longer read `globalShims` from a project's `pnpm-workspace.yaml`. Only the global config file, the pnpm home's own `pnpm-workspace.yaml`, and `PNPM_CONFIG_GLOBAL_SHIMS` set it, so a repository cannot choose which globally installed packages get project-aware shims.

- `pnpm config set --location=project` refuses a machine-level setting such as `stateDir` or `scope` with `ERR_PNPM_CONFIG_SET_NOT_A_PROJECT_SETTING`, which names where the setting belongs. `pnpm config delete` still clears such a key from a project's `pnpm-workspace.yaml`.

- `pnpm deploy` no longer fails with `ERR_PNPM_DEPLOY_AMBIGUOUS_PEER` in a workspace with `injectWorkspacePackages: true` when a workspace package lists its peer dependency as a dev dependency too [#&#8203;16375](https://github.com/pnpm/pnpm/issues/16375).

- `pnpm deploy` no longer copies the workspace root's `packageManager` and `devEngines.packageManager` fields into the deployed `package.json` [#&#8203;16403](https://github.com/pnpm/pnpm/issues/16403).

- `pnpm publish` now includes bare `README` files and README files with Markdown extensions such as `readme.markdown` in registry metadata [#&#8203;12704](https://github.com/pnpm/pnpm/issues/12704).

- `pnpm store prune` now removes the packages that only expired `pnpm dlx` cache entries used. They were left in the store until the next `pnpm store prune` [#&#8203;16383](https://github.com/pnpm/pnpm/discussions/16383).

##### Performance

- Sped up dependency resolution in large workspaces, and when many dependencies request different ranges of the same package. Resolution also uses less memory.

- Sped up `pnpm install` with `nodeLinker: hoisted` on macOS when the lockfile is re-resolved, such as with `autoDedupe` enabled [#&#8203;16397](https://github.com/pnpm/pnpm/issues/16397).

- Sped up extracting package tarballs.

- `pnpm install` without `--frozen-lockfile` is faster on some machines in projects with a `pnpm-workspace.yaml`. Those installs linked with one worker thread per core, half of what a frozen install uses.

- On Windows, warm `pnpm install --frozen-lockfile` runs are 4-5% faster on 4- and 8-core machines. pnpm now links with one worker thread per core on Windows, between 4 and 16. This changes frozen installs and installs in projects without a `pnpm-workspace.yaml` on machines with 3 to 15 cores.

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.8.1`](https://github.com/pnpm/pnpm/releases/tag/v12.8.1): pnpm 12.8.1

[Compare Source](https://github.com/pnpm/pnpm/compare/[email protected])

pnpm 12.8.1 fixes `pnpm install --frozen-lockfile` rejecting lockfiles with injected workspace packages that have peers, restores the executable bit on files of local directory dependencies, makes `pnpm dedupe` converge, and uses less CPU on many-core machines.

##### Patch Changes

- `pnpm install --frozen-lockfile` no longer rejects a freshly generated lockfile when an injected workspace package has peer dependencies [#&#8203;16332](https://github.com/pnpm/pnpm/issues/16332).

- Executable files in a `file:` directory dependency or an injected workspace package keep their executable bit again. Since 12.8.0, pnpm installed these files without the permissions they have in their project.

- `pnpm dedupe` now reaches a stable lockfile when a package's peer suffix is long enough to be hashed. Before, each run could switch that package's key between the hashed and the spelled-out suffix, so `pnpm dedupe --check` always failed [#&#8203;16331](https://github.com/pnpm/pnpm/issues/16331).

- `pnpm install --frozen-lockfile`, the default in CI, now uses less CPU on machines with more than 8 cores. Warm installs on many-core Windows machines got up to 10% faster. Frozen installs now link with at most 16 worker threads.

- `verifyDepsBeforeRun` no longer reports dependencies as outdated after a filtered install just because `pnpm-lock.yaml` has a newer modification time. It checks the lockfile against the packages that install put in place. Before, `pnpm run` reinstalled the whole workspace with lifecycle scripts on, for example after a Docker `COPY` brought in a lockfile with a newer mtime [#&#8203;16322](https://github.com/pnpm/pnpm/issues/16322).

  After a filtered install, `verifyDepsBeforeRun` now also checks that the install put the selected projects' dependencies in place. A `node_modules` directory alone no longer counts as proof.

- `pnpm run` and `pnpm exec` no longer install a project that has never been installed and has nothing to install. Such a project declares no dependencies, no peer dependencies that `autoInstallPeers` would fetch, and no install lifecycle scripts. The command now runs without writing `node_modules` or `pnpm-lock.yaml` [#&#8203;16313](https://github.com/pnpm/pnpm/issues/16313).

- `pnpm update -g --latest` now upgrades globally installed packages beyond their saved version ranges [#&#8203;16320](https://github.com/pnpm/pnpm/issues/16320).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.8.0`](https://github.com/pnpm/pnpm/releases/tag/v12.8.0): pnpm 12.8

[Compare Source](https://github.com/pnpm/pnpm/compare/[email protected])

pnpm 12.8.0 warns when `pnpm pack` or `pnpm publish` would ship a `.env` file that `files` does not list, installs `sharedWorkspaceLockfile: false` workspaces concurrently, applies every setting passed as `--config.<name>=<value>`, and no longer leaves the Windows terminal stuck after Ctrl+C in a script.

##### Minor Changes

- `pnpm pack` and `pnpm publish` now warn when the tarball includes a `.env` or `.env.*` file that the `files` field of `package.json` does not list. Templates such as `.env.example` are not reported. List the file in `files` to publish it on purpose, or exclude it in `.npmignore` or `.gitignore` [#&#8203;7826](https://github.com/pnpm/pnpm/issues/7826).

- `pnpm pack` now honors `--silent`, `--reporter=silent`, and `--loglevel=silent` to hide the tarball contents and summary. With `--json`, lifecycle script output and the final JSON output remain visible [#&#8203;10297](https://github.com/pnpm/pnpm/issues/10297).

##### Patch Changes

##### Installing packages

- Installing through a `pnpr` server now records the pnpmfile checksum in the lockfile, so a later `pnpm install --frozen-lockfile` accepts that lockfile [#&#8203;14460](https://github.com/pnpm/pnpm/issues/14460). A frozen install through the pnpr server now fails if the pnpmfile changed. If the pnpmfile defines a `readPackage`, `afterAllResolved` or `preResolution` hook or custom resolvers, pnpm resolves dependencies locally and prints a warning that the pnpr server was not used.

  Installing through a `pnpr` server also links a workspace project at the directory its `publishConfig.directory` names. A server that does not forward the setting makes the install fail with `ERR_PNPM_PNPR_PUBLISH_DIRECTORY_MISMATCH`, so pnpm never writes a lockfile that points at the wrong directory. The server rejects a `publishConfig.directory` that points outside its project.

- Installing a git-hosted dependency that has to be built no longer fails when that dependency's own dependencies have build scripts nobody approved. pnpm skips those builds while preparing the dependency, as it does without `strictDepBuilds` [#&#8203;9764](https://github.com/pnpm/pnpm/issues/9764).

- A git-hosted dependency that is a pnpm workspace with no committed lockfile is now detected as a pnpm project [#&#8203;14011](https://github.com/pnpm/pnpm/issues/14011).

- `pnpm install --dev` and `pnpm fetch --dev` now install the optional dependencies of devDependencies, such as the platform binaries of Biome and oxlint. The project's own `optionalDependencies` are still skipped [#&#8203;9678](https://github.com/pnpm/pnpm/issues/9678).

- `pnpm install --offline` and `pnpm add --offline` now resolve a version range to the newest matching version whose tarball is already in the store. They used to pick the newest version in the cached metadata and fail with `ERR_PNPM_NO_OFFLINE_TARBALL` when its tarball was missing [#&#8203;10715](https://github.com/pnpm/pnpm/issues/10715).

- If an offline install fails because the registry metadata cache uses the layout from before pnpm 11.27 and 12.4, the error now names the older mirror on disk and explains that one online install repopulates the cache. The error also carries the `ERR_PNPM_NO_OFFLINE_META` code. `pnpm cache prune --help` now says that pnpm 11.26 and earlier, and pnpm 12.3 and earlier, depend on the directories it removes [#&#8203;15656](https://github.com/pnpm/pnpm/issues/15656).

- Running `pnpm install` now refreshes dependencies when a package declared with a local `file:` directory changes its dependencies [#&#8203;4623](https://github.com/pnpm/pnpm/issues/4623).

- A repeat `pnpm install` now keeps its fast up-to-date check when an override replaces a declared local `file:` dependency [#&#8203;12892](https://github.com/pnpm/pnpm/issues/12892).

- `pnpm install` now removes an optional dependency from `node_modules` if its install script fails. Code that checks whether the package is installed no longer finds a package that cannot load [#&#8203;8756](https://github.com/pnpm/pnpm/issues/8756).

- With `nodeLinker: hoisted`, `pnpm install` now restores a workspace project's `node_modules` after it was deleted. Before, the install printed "Already up to date" and left the project without the dependencies nested under it. On Windows, the install also no longer fails with "Access is denied" when another project's copy of a shared dependency links to the deleted directory.

- Under `nodeLinker: hoisted`, `pnpm install` now clears orphaned package directories that an interrupted or failed install leaves in a project's `node_modules`. A directory recorded by the previous install is removed, while an unrecorded directory is moved to `node_modules/.ignored`. A copy already in `.ignored` is never overwritten [#&#8203;13676](https://github.com/pnpm/pnpm/issues/13676).

- Concurrent installs no longer fail when they replace the same stale hoisted dependency link. Virtual store cleanup now keeps the temporary lockfiles that concurrent installs write.

##### Resolving and linking dependencies

- `pnpm install` no longer aborts on a failed allocation of many gigabytes when peer dependency ranges combine overlapping `||` alternatives [#&#8203;15867](https://github.com/pnpm/pnpm/issues/15867).

- `pnpm install` no longer fails when a package from the registry declares a `file:` dependency on a directory inside itself, such as `"@&#8203;types/css-tree": "file:./typings/css-tree"`. pnpm links that dependency to the directory inside the package, as npm and Yarn do. The lockfile records it as `link:<root>/typings/css-tree` [#&#8203;9141](https://github.com/pnpm/pnpm/issues/9141).

- An `npm:` alias written by `overrides` now stays in place when a change elsewhere makes pnpm re-resolve the aliased dependency. Before, pnpm could look up the alias name at the aliased version, which failed with `ERR_PNPM_NO_MATCHING_VERSION` or locked an unrelated package [#&#8203;16309](https://github.com/pnpm/pnpm/issues/16309).

- A peer dependency no longer resolves to two different versions for one package. This happened when the package peer-depends on another package and on one of that package's peers, and it is installed deeper than a direct dependency of the package that provides them [#&#8203;12098](https://github.com/pnpm/pnpm/issues/12098).

- An optional peer dependency is no longer resolved from another workspace project's package when the project provides one of that package's own peers at a version it rejects. This avoids bogus unmet peer errors [#&#8203;13989](https://github.com/pnpm/pnpm/issues/13989).

- `pnpm dedupe` no longer changes the lockfile on every run when a nested peer dependency is provided through an npm alias [#&#8203;15709](https://github.com/pnpm/pnpm/issues/15709).

- With `resolutionMode: time-based` and `minimumReleaseAge` both set, `pnpm install` no longer reports a subdependency as too new when only the time-based cutoff excludes it. Such subdependencies used to fail a strict install with `ERR_PNPM_NO_MATURE_MATCHING_VERSION`, or were added to `minimumReleaseAgeExclude` [#&#8203;13569](https://github.com/pnpm/pnpm/issues/13569). A transitive dependency that has no matching version published before the time-based cutoff now resolves to the lowest matching version allowed by `minimumReleaseAge`. pnpm picks a version younger than `minimumReleaseAge` only if no older version matches [#&#8203;16298](https://github.com/pnpm/pnpm/issues/16298).

- `pnpm install` retries registry metadata fetches that fail with a timeout, a dropped connection, or an interrupted response body before it applies `trustPolicy` or `minimumReleaseAge`. A transient fetch failure is not reported as `TRUST_DOWNGRADE` or `MINIMUM_RELEASE_AGE_VIOLATION` [#&#8203;12031](https://github.com/pnpm/pnpm/issues/12031).

- pnpm's built-in package compatibility database no longer applies to a project's own manifest. A project named like a published package, such as `vue-loader`, no longer gains dependencies on `pnpm install` or `pnpm update`. User-configured `packageExtensions` still apply to project manifests [#&#8203;11700](https://github.com/pnpm/pnpm/issues/11700).

- Packages in an external `virtualStoreDir` can resolve the project's direct dependencies selected by `hoistPattern`. Run `pnpm install --force` to repair an existing installation [#&#8203;5652](https://github.com/pnpm/pnpm/issues/5652).

- `pnpm install` now links the executables of auto-installed peer dependencies into the workspace root's `node_modules/.bin`, including after a frozen-lockfile reinstall [#&#8203;8511](https://github.com/pnpm/pnpm/issues/8511).

##### Lockfiles and frozen installs

- `pnpm install --frozen-lockfile` now works on a detached HEAD when `gitBranchLockfile` is enabled. The install reads the lockfiles of the local and remote-tracking branches that contain the checked-out commit. It still writes the shared `pnpm-lock.yaml` [#&#8203;7672](https://github.com/pnpm/pnpm/issues/7672).

- `pnpm install --frozen-lockfile` now accepts a lockfile that has no importer entry for a workspace package without dependencies. Such a package added after the lockfile was written made the install fail with `ERR_PNPM_PACKAGE_MANAGER_NO_IMPORTER` [#&#8203;15875](https://github.com/pnpm/pnpm/issues/15875).

- `pnpm install` now fails with `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY` when an importer references a dependency version that has no snapshot entry. Before, the install succeeded and left a `node_modules` symlink pointing at a missing virtual-store directory [#&#8203;14764](https://github.com/pnpm/pnpm/issues/14764).

- `pnpm install` on CI now fails on an outdated lockfile when `preferFrozenLockfile` is explicitly set to `true`. Setting it to `true` used to let CI update the lockfile [#&#8203;9072](https://github.com/pnpm/pnpm/pull/9072).

- With `gitBranchLockfile` enabled, each emoji or other character outside the Basic Multilingual Plane in a branch name now becomes `!!` in the lockfile name. Before, each such character became one `!`.

##### Workspaces and filtering

- `pnpm install` in a workspace with `sharedWorkspaceLockfile: false` now installs projects concurrently, up to `workspaceConcurrency` at a time [#&#8203;14480](https://github.com/pnpm/pnpm/issues/14480). A project is resolved, fetched, and written to its virtual store without waiting for the workspace projects it depends on. It waits for them only before it links its dependencies and runs its lifecycle scripts, so its scripts still run after theirs. A project with a `preinstall` or `pnpm:devPreinstall` script, or with an injected or `file:` workspace dependency, waits for its workspace dependencies before it starts.

  The installs of the projects also share their package metadata, lockfile verification, and store caches, so they use less CPU and memory when several projects depend on the same packages. An install with a pnpmfile no longer starts an extra Node.js process when the pnpmfile has no `preResolution` hook.

- With `enableGlobalVirtualStore` and `sharedWorkspaceLockfile: false`, each project now keeps its current lockfile and its hidden hoisted dependencies in its own `node_modules/.pnpm`. Before, every project wrote them to the workspace root's `node_modules/.pnpm`, so each repeat install treated the other projects' packages as its own and relinked them [#&#8203;14480](https://github.com/pnpm/pnpm/issues/14480).

- `pnpm rebuild`, `pnpm approve-builds`, and `pnpm ignored-builds` now work on the current project's `node_modules` when they run inside a project of a workspace with `sharedWorkspaceLockfile: false`. They used to read the workspace root's `node_modules`, so `pnpm rebuild` did not rebuild the project's dependencies and created a second virtual store at the workspace root [#&#8203;9402](https://github.com/pnpm/pnpm/issues/9402).

- `pnpm install` no longer creates a `node_modules` symlink inside the `publishConfig.directory` of a workspace package linked with `linkDirectory`. A build tool that cleaned its output directory through that symlink deleted the files of the package's dependencies. `pnpm install` also removes a symlink that an earlier install left there [#&#8203;16226](https://github.com/pnpm/pnpm/issues/16226). It also no longer fails with `ERR_PNPM_CMD_SHIM_RESOLVE_PATH` when such a package has a `bin` field and its `publishConfig.directory` does not exist yet.

- `pnpm install` no longer fails for an injected workspace dependency whose package publishes from a `publishConfig.directory` that its own `prepare` script builds. The injected copy now picks up that directory once `prepare` finishes building it. `pnpm install --frozen-lockfile` no longer reports the dependency as outdated while the directory has not been built yet [#&#8203;7811](https://github.com/pnpm/pnpm/issues/7811).

- An in-place edit to the source of an injected workspace package now shows up in its injected copy, unless a build writes to that package or `packageImportMethod` is set. pnpm hardlinks such packages under the default import method [#&#8203;4410](https://github.com/pnpm/pnpm/issues/4410). Scripts listed in `syncInjectedDepsAfterScripts` now update injected dependencies while they run, so a watcher on the injected package, such as a dev server, sees each change before the script exits.

- With `sharedWorkspaceLockfile: false`, an injected workspace package that has lifecycle scripts is now hard linked into the projects that depend on it. Before, pnpm left a plain copy, so later edits to the package did not reach those projects [#&#8203;9828](https://github.com/pnpm/pnpm/issues/9828).

- `injectWorkspacePackages` now hard links a workspace dependency declared with a relative path, such as `workspace:../foo`, the same way it already does for `workspace:*` [#&#8203;10446](https://github.com/pnpm/pnpm/issues/10446).

- Workspace discovery prunes dot-prefixed directories, so a `packages` pattern such as `**` no longer matches projects inside `.cache` and other hidden directories [#&#8203;16250](https://github.com/pnpm/pnpm/issues/16250).

- `pnpm import` in a workspace now keeps the versions pinned by a `yarn.lock` inside a workspace project [#&#8203;4385](https://github.com/pnpm/pnpm/issues/4385).

##### Store and caches

- Files imported from the store now follow the umask of the install that writes them. Installing with a umask of `077` no longer leaves imported files readable by the group and others [#&#8203;3807](https://github.com/pnpm/pnpm/issues/3807).

- `pnpm install` keeps the owner, group, and mode of files already in a shared store, including `index.db`. New store files and directories inherit the store directory's group-write bit. When that directory is setgid, new files inherit its group. pnpm does not change a file's owner or group [#&#8203;12765](https://github.com/pnpm/pnpm/issues/12765).

- When `pnpm install` repairs a store file that was modified through a hard link in `node_modules`, the repair now keeps the file's inode on Linux and macOS, so hard-linked copies in other projects are healed at the same time. On Windows the repair still replaces the file, so other projects are healed on their next install [#&#8203;3445](https://github.com/pnpm/pnpm/issues/3445).

- `pnpm install` now reports a full store at once when writing package files fails. It no longer retries the tarball [#&#8203;8581](https://github.com/pnpm/pnpm/issues/8581).

- pnpm now warns when it cannot hard link packages from an existing store in the pnpm home directory and falls back to a store on the project's filesystem. This can happen when the project is on another filesystem, such as a bind-mounted workspace in a container. The warning names both stores and suggests setting `storeDir` [#&#8203;14505](https://github.com/pnpm/pnpm/issues/14505).

- The side-effects cache now restores the symlinks that a build script creates inside a package. A warm install used to replace each of them with a copy of its target [#&#8203;12859](https://github.com/pnpm/pnpm/issues/12859).

  After upgrading, every package with a build script is built once more.

- The global virtual store and the side-effects cache now key built packages by the Node.js version that the root project's `devEngines.runtime` or `engines.runtime` pins. That is the Node.js their build scripts run with. A dependency that declares its own `engines.runtime` no longer changes the key for every other package.

- With `enableGlobalVirtualStore`, an install into a fresh `node_modules` no longer runs the build scripts of a dependency whose global virtual store slot an earlier install already built. `pnpm rebuild` still runs them [#&#8203;14480](https://github.com/pnpm/pnpm/issues/14480).

- Concurrent installs that share a global virtual store now run a package's build in its shared slot one at a time. A failed build leaves the slot in place and marks it for the next install to rebuild [#&#8203;15568](https://github.com/pnpm/pnpm/issues/15568).

- A warm `pnpm install` reuses on-disk package metadata for five minutes when the registry does not send an ETag. Registries that send an ETag, including the public npm registry, still revalidate with a conditional request. `pnpm update` still fetches current metadata [#&#8203;13976](https://github.com/pnpm/pnpm/issues/13976).

- pnpm no longer revalidates cached registry metadata when the registry sends `Cache-Control: max-age=0`, `no-cache`, or `no-store`. It downloads the metadata again, so a version newly published to such a registry is visible on the next install [#&#8203;13487](https://github.com/pnpm/pnpm/issues/13487).

- `pnpm install` honors `Cache-Control` for dependencies named with an `http:` or `https:` tarball URL. A fresh response is taken from the store with no request, and a stale one is revalidated with `If-None-Match` [#&#8203;15648](https://github.com/pnpm/pnpm/issues/15648).

##### Patched dependencies

- `pnpm install` now repairs a `pnpm-lock.yaml` whose `(patch_hash=<hash>)` dependency paths disagree with its `patchedDependencies` map, including paths that lack the hash their patch calls for. Before, pnpm accepted such a lockfile as up to date and kept the old patched files. `pnpm install --frozen-lockfile` now fails on such a lockfile with `ERR_PNPM_INCONSISTENT_PATCH_HASH`. It fails with `ERR_PNPM_UNCHECKABLE_PATCH_HASH` when a patch hash in the lockfile is malformed, or when the lockfile lacks the package version or patch entry that the check needs [#&#8203;15336](https://github.com/pnpm/pnpm/pull/15336).

- `pnpm install` with `nodeLinker: hoisted` now applies a patch once to each copy of a patched dependency in a workspace. Before, a copy that several workspace projects shared could receive the patch twice and end up with the patched content duplicated [#&#8203;7565](https://github.com/pnpm/pnpm/issues/7565).

- `pnpm install` and `pnpm fetch` now fail with `ERR_PNPM_PATCH_NOT_FOUND` when a patch file listed in `patchedDependencies` does not exist [#&#8203;5268](https://github.com/pnpm/pnpm/issues/5268).

- `engineStrict` now checks the patched `package.json` when a `patchedDependencies` entry changes `engines`. A patch that relaxes `engines.node` no longer fails the install against the published range [#&#8203;9603](https://github.com/pnpm/pnpm/issues/9603).

- `pnpm patch` now applies the existing patch file to the edit directory of a git-hosted dependency, as it already does for packages from the registry [#&#8203;9699](https://github.com/pnpm/pnpm/issues/9699).

##### Adding, updating, and removing dependencies

- `pnpm add <dir>` now warns when the added directory declares peer dependencies, as `pnpm link` does. The directory is saved as a `link:` dependency, and its peers are not resolved from the project that adds it. Use the `file:` protocol to have them resolved [#&#8203;5523](https://github.com/pnpm/pnpm/issues/5523).

- `pnpm add --save-types` no longer adds a `@types/*` package whose resolved version is deprecated. DefinitelyTyped publishes such stubs for packages that ship their own types, such as `@types/typescript` for `typescript` [#&#8203;15636](https://github.com/pnpm/pnpm/issues/15636).

- `pnpm version`, `pnpm add`, and `pnpm pkg set` keep JSON5 style when they update `package.json5`. ASCII identifier keys stay unquoted, strings keep JSON5 quotes, and indented files keep trailing commas [#&#8203;15717](https://github.com/pnpm/pnpm/issues/15717).

##### Running scripts and commands

- `pnpm run` and `pnpm exec` no longer install dependencies automatically when the root `package.json` still keeps `overrides`, `packageExtensions`, `patchedDependencies`, or `ignoredOptionalDependencies` in its `pnpm` field. pnpm no longer reads that field, so the install rewrote the lockfile without those settings. The command now fails and asks to move the settings to `pnpm-workspace.yaml` [#&#8203;16278](https://github.com/pnpm/pnpm/issues/16278).

- When `verifyDepsBeforeRun` triggers an install before a filtered `pnpm run` or `pnpm exec`, pnpm now installs only the selected projects and their dependencies. A later filtered command also installs a selected project that an earlier filtered install skipped [#&#8203;11865](https://github.com/pnpm/pnpm/issues/11865).

- `pnpm -r run /regexp/` now honors the `tasks` `dependsOn` declared for each script the selector matches, like running the script by name does. Matched scripts that depend on each other run in order. Each matched script runs once [#&#8203;15596](https://github.com/pnpm/pnpm/issues/15596).

- `pnpm run` exits with the code of a script that handles Ctrl+C and shuts down. A script that finished cleanly is not reported as a lifecycle failure. The commands after it in the same script still run [#&#8203;9945](https://github.com/pnpm/pnpm/issues/9945).

- pnpm no longer hangs after a lifecycle script exits while a process it started in the background keeps the script's output open. pnpm stops reading that output one second after the script exits [#&#8203;5730](https://github.com/pnpm/pnpm/issues/5730).

- `pnpm run` and lifecycle scripts use the configured `scriptShell`, including Git Bash on Windows, when `shellEmulator` is also enabled. `shellEmulator` still runs scripts when `scriptShell` is not set. Extra arguments passed to `pnpm run` are quoted for the shell that runs the script, so a Windows path stays intact [#&#8203;14719](https://github.com/pnpm/pnpm/issues/14719).

- With `enableGlobalVirtualStore`, dependency build scripts now see the workspace root's `node_modules/.bin`, as they do with a local virtual store. A `postinstall` script that runs `node` finds the Node.js installed by `devEngines.runtime` and no longer fails with "command not found" on machines without a system Node.js [#&#8203;15652](https://github.com/pnpm/pnpm/issues/15652). Dependency build scripts also see the bins of privately hoisted dependencies.

- Dependency install scripts now find the node-gyp bundled with pnpm when pnpm runs through a symlink, such as `node_modules/.bin/pnpm` or the `pnpm` that `npm install -g pnpm` links. They used to fail with `node-gyp: command not found` on macOS [#&#8203;15694](https://github.com/pnpm/pnpm/issues/15694).

- `pnpm run` and lifecycle scripts now set `npm_config_node_gyp` to the bundled `node-gyp` entry point. Tools that read the variable resolve the same `node-gyp` pnpm builds with. An `npm_config_node_gyp` value the environment already sets is kept as is [#&#8203;16270](https://github.com/pnpm/pnpm/issues/16270).

- Scripts now see the `npm_command` environment variable that npm sets. It holds `run-script` when the command runs a script, and the command's own name otherwise [#&#8203;16265](https://github.com/pnpm/pnpm/issues/16265).

- Commands run from a POSIX shell through a dependency's own `node_modules/.bin`, such as `node_modules/vite/node_modules/.bin/esbuild`, no longer fail with `MODULE_NOT_FOUND` [#&#8203;10189](https://github.com/pnpm/pnpm/issues/10189).

- `pnpx --version` and `pnpm dlx --version` now print the pnpm version. Other unknown options before the command are reported as errors. Before, pnpm tried to download a package named after the option [#&#8203;16259](https://github.com/pnpm/pnpm/issues/16259).

- `pnpm dlx` now keeps the virtual store of its cached installs in `node_modules/.pnpm`, like every other install [#&#8203;13955](https://github.com/pnpm/pnpm/issues/13955). `pnpm pack-app` now names the manifest of its runtime install directory `pnpm-pack-app-<target>`.

##### Publishing, packing, and deploying

- `pnpm pack` and `pnpm publish` now ship a file that the `files` field names even when another entry excludes the directory holding it. For example, `["**", "!dist", "dist/index.d.ts"]` ships `dist/index.d.ts` [#&#8203;16213](https://github.com/pnpm/pnpm/issues/16213).

- `pnpm pack` prunes a directory that a `files` field exclusion names, such as `!**/test`, excluding the directory and its contents from the packed package [#&#8203;15738](https://github.com/pnpm/pnpm/issues/15738).

- `pnpm publish` now waits at least 5 minutes for the registry to answer a publish request, like npm. This fixes "409 Conflict - Failed to save packument" errors when the registry is slow to answer [#&#8203;11454](https://github.com/pnpm/pnpm/issues/11454).

- `pnpm deploy --prod` no longer fails with `ERR_PNPM_OUTDATED_LOCKFILE` when the deployed project declares a `devEngines.runtime` with `onFail: download`. The runtime stays out of the deployed `node_modules` with the rest of the dev dependencies [#&#8203;15703](https://github.com/pnpm/pnpm/issues/15703).

- `pnpm deploy` with a shared lockfile now copies workspace dependencies into the deploy directory, even when `packageImportMethod` is set to `hardlink`. Before, their files were hard-linked to the workspace sources, so editing a source file also changed the deployed copy [#&#8203;12176](https://github.com/pnpm/pnpm/issues/12176).

- `pnpm deploy --legacy` no longer leaves broken links to nested local dependencies of workspace packages [#&#8203;9575](https://github.com/pnpm/pnpm/issues/9575).

##### Configuration and pnpmfile hooks

- Every setting pnpm supports can now be set with `--config.<name>=<value>` on the command line, not only the ones whose command also carries a matching flag. Before, `pnpm install --config.frozen-lockfile=true` dropped the setting and rewrote `pnpm-lock.yaml` as though the install had not been frozen [#&#8203;16276](https://github.com/pnpm/pnpm/issues/16276).

- Settings given on the command line, such as `--registry` and `--store-dir`, now take precedence over the values a pnpmfile `updateConfig` hook sets [#&#8203;14063](https://github.com/pnpm/pnpm/issues/14063).

- `pnpm config set --location=project` and `pnpm config delete --location=project`, run from a package inside a workspace, now write settings that belong in `pnpm-workspace.yaml` to the workspace root's `pnpm-workspace.yaml`. Before, they created a new `pnpm-workspace.yaml` in the current package, which made that package the workspace root. Settings stored in `.npmrc` are still written to the current directory [#&#8203;13757](https://github.com/pnpm/pnpm/issues/13757).

- pnpm now reads the workspace directory override from `PNPM_CONFIG_WORKSPACE_DIR`, like other settings. `NPM_CONFIG_WORKSPACE_DIR` still works as a fallback [#&#8203;16275](https://github.com/pnpm/pnpm/issues/16275).

- pnpm now fails with `ERR_PNPM_AUTH_INVALID_BASE64` when a registry's `_password` in `.npmrc` is not valid base64. Before, it sent the value as the raw password. A `username` or `_password` left empty, for example by an unset environment variable, now supplies no credential [#&#8203;16273](https://github.com/pnpm/pnpm/issues/16273).

- `proxy=false` now turns proxying off even when `HTTP_PROXY`, `HTTPS_PROXY`, or `ALL_PROXY` is set. pnpm no longer sends requests through a proxy named only in `ALL_PROXY`.

- `pnpm install` now runs the install hooks of a config dependency plugin's pnpmfile, including `readPackage`, `afterAllResolved`, and custom resolvers. Its pnpmfile is also counted in `pnpmfileChecksum`. Before, only the plugin's `updateConfig` hook ran, so a plugin could not change the resolved dependencies.

- A pnpmfile `fetchers` hook now runs once per package on a fresh install when it handles a resolution with a custom `type` or delegates a git-hosted one to the same subdirectory [#&#8203;15584](https://github.com/pnpm/pnpm/issues/15584). These packages were fetched a second time for installation, so the installed files could come from a different archive than the one their dependencies were read from. The hook also no longer runs twice when a `resolvers` hook returns a tarball resolution without a manifest [#&#8203;15025](https://github.com/pnpm/pnpm/issues/15025).

- `pnpm install` now re-fetches a package from a custom resolver when the `integrity` of its resolution changes, with or without `enableGlobalVirtualStore`. It used to update the lockfile but keep the old files in `node_modules` [#&#8203;15670](https://github.com/pnpm/pnpm/issues/15670).

- `pnpm install` now rejects invalid results from a `readPackage` hook. A hook that returns a non-object value fails with `ERR_PNPM_BAD_READ_PACKAGE_HOOK_RESULT` [#&#8203;15730](https://github.com/pnpm/pnpm/issues/15730). A hook that sets a dependency range to a value other than a string, such as `undefined`, fails with an error that names the dependency, the package and the pnpmfile. Delete the property to remove a dependency [#&#8203;15705](https://github.com/pnpm/pnpm/issues/15705).

##### Global packages, pnpm versions, and runtimes

- `pnpm update --global` now reinstalls the global packages that pnpm 10 installed into the previous global directory, `<global-dir>/5`, so their commands are linked into the pnpm home `bin` directory again and `pnpm list --global` lists them. Once every package is migrated, pnpm deletes the previous directory and the commands pnpm 10 linked into the pnpm home [#&#8203;11528](https://github.com/pnpm/pnpm/issues/11528).

- A signal sent to pnpm, such as `SIGTERM`, now reaches the pnpm that pnpm switches to because of `packageManager` or `devEngines.packageManager`, and the one that `pnpm with` runs. The signal used to be dropped, so scripts running under that pnpm never got to shut down [#&#8203;9948](https://github.com/pnpm/pnpm/issues/9948).

- On arm64 musl Linux, such as Alpine on ARM, switching to a pinned pnpm older than 12 now runs the JavaScript `pnpm` package. The standalone executable of those versions crashed at startup on that platform [#&#8203;10443](https://github.com/pnpm/pnpm/issues/10443).

- Global shims such as `node` now work when pnpm runs through a relative symlink, as with a Homebrew install. They were copies of that symlink and did not resolve from the global bin directory [#&#8203;15691](https://github.com/pnpm/pnpm/issues/15691).

- `pnpm env remove --global` deletes Node.js versions that pnpm installed into its own store, including when another tool installed pnpm [#&#8203;8357](https://github.com/pnpm/pnpm/issues/8357).

- `pnpm self-update` no longer suggests a downgrade when `minimumReleaseAge` holds back the registry's `latest` release. It now says that release is still within the cutoff [#&#8203;12006](https://github.com/pnpm/pnpm/issues/12006).

##### Windows

- Interrupting a script with Ctrl+C on Windows no longer leaves the terminal stuck [#&#8203;14860](https://github.com/pnpm/pnpm/issues/14860). A script that runs through a batch shim, as `vite dev` does through `vite.CMD`, made cmd.exe wait forever on its "Terminate batch job (Y/N)?" answer, and every following keystroke went to that prompt. pnpm now ends a cmd.exe script shell once it has sat for a second after the interrupt with nothing running under it. A script that takes longer to shut down is still waited for. A second Ctrl+C ends the script's shell at once.

- On Windows, `pnpm run` now passes the arguments after the script name to the script as typed. Before, `cmd` expanded `%VAR%` in them and backslashes arrived doubled. Line breaks still arrive as the two characters `\n`, because `cmd` cannot pass them. The command line pnpm prints for the script quotes the arguments the same way on every platform [#&#8203;16257](https://github.com/pnpm/pnpm/issues/16257).

- The Windows `pnpm.exe` runs on a clean Windows install that does not have the Visual C++ Redistributable. It used to exit immediately on startup because that runtime was missing [#&#8203;15723](https://github.com/pnpm/pnpm/issues/15723).

- On Windows, the `.cmd` command shims in `node_modules/.bin` now keep a `%` in the project path. Before, cmd.exe expanded it as a variable reference, so the command received a mangled `NODE_PATH` [#&#8203;15716](https://github.com/pnpm/pnpm/issues/15716). Command shims also run tools whose paths contain non-ASCII characters [#&#8203;6999](https://github.com/pnpm/pnpm/issues/6999), including the PowerShell shims in Windows PowerShell 5.1 [#&#8203;16217](https://github.com/pnpm/pnpm/issues/16217).

- Bin shims in `node_modules/.bin` run from Cygwin on Windows again. The shims passed a `/cygdrive/c/...` path to the Windows `node` found on `PATH`, so Node.js failed with `Cannot find module 'C:\cygdrive\c\...'` [#&#8203;12845](https://github.com/pnpm/pnpm/issues/12845).

- On Windows, installing pnpm with npm inside a project now writes `node_modules/.bin` shims that run `pnpm.exe`. A global install with `npm install --location=global` now gets the same shims as `npm install -g` [#&#8203;15688](https://github.com/pnpm/pnpm/issues/15688).

- `pnpm install` no longer fails with `ERR_PNPM_WORKSPACE_INVALID_GLOB` on Windows for a wildcard pattern such as `plugins/*/*` in `pnpm-workspace.yaml` when the workspace is on a different drive than the pnpm cache or state directory [#&#8203;16239](https://github.com/pnpm/pnpm/issues/16239).

- On Windows, `pnpm install` no longer skips a dependency's build script on a later install when the package ships an executable file and the script changes nothing inside the package directory [#&#8203;15667](https://github.com/pnpm/pnpm/issues/15667).

- `pnpm setup` no longer writes the `pn.ps1`, `pnpx.ps1`, and `pnx.ps1` PowerShell wrappers. It also removes the ones an earlier setup wrote. PowerShell now runs `pn`, `pnpx`, and `pnx` through their `.cmd` wrappers, like `pnpm` itself. Before, these aliases failed with a "not digitally signed" error wherever the execution policy blocks unsigned scripts [#&#8203;8444](https://github.com/pnpm/pnpm/issues/8444).

- `pnpm setup` on Windows no longer panics when an unrelated environment variable has a name containing a non-ASCII character. It skips that variable [#&#8203;15684](https://github.com/pnpm/pnpm/issues/15684).

- On Windows, `pnpm setup` repairs the `PNPM_HOME` registry type left by older pnpm versions, even when the configured directory has not changed.

- On Windows, the `ERR_PNPM_BAD_ENV_FOUND` error of `pnpm setup` now shows the value `PNPM_HOME` is currently set to. Before, it showed the directory pnpm wanted to set.

- On Windows, pnpm expands nested `%VAR%` references in `PNPM_HOME` and the other directory environment variables it uses for its home, store, cache, state, and config directories. pnpm fails with an error when a `%VAR%` reference remains after expansion [#&#8203;13236](https://github.com/pnpm/pnpm/issues/13236).

- On Windows, if the global bin directory is not in `PATH` and a `PATH` entry still contains an unexpanded variable such as `%PNPM_HOME%`, the error now names that entry. A variable referenced from the user `Path` must be set to a full path and stored as a plain string (`REG_SZ`) for the entry to expand [#&#8203;5283](https://github.com/pnpm/pnpm/issues/5283).

##### Inspecting dependencies

- `pnpm audit` and `pnpm audit signatures` now fail with an error when the lockfile contains unresolvable dependency references [#&#8203;13638](https://github.com/pnpm/pnpm/issues/13638).

- `pnpm licenses list` now reports the actual on-disk package locations when using `nodeLinker: hoisted` or `shamefully-hoist: true` [#&#8203;8589](https://github.com/pnpm/pnpm/issues/8589). With `--json`, its `paths` array now includes every installed copy of a package, including hoisted copies and isolated installations with different peer dependencies.

- `pnpm root` now prints the configured `modulesDir`. It used to print `node_modules` regardless of the setting. A project's own `modulesDir` from `packageConfigs` is printed too [#&#8203;9113](https://github.com/pnpm/pnpm/issues/9113).

##### Output and messages

- With the default and append-only reporters, installs with `--loglevel warn` or `--loglevel error` now print the full output of a failed install script. The output of successful scripts, including the root project's own install hooks, stays hidden. With `--loglevel warn`, pnpm also prints ignored build script warnings.

- When a dependency fails to resolve, the error now shows the cause. For example, a Node.js runtime download behind a proxy that re-signs TLS now reports `invalid peer certificate: UnknownIssuer` [#&#8203;9556](https://github.com/pnpm/pnpm/issues/9556).

- When installing a git dependency over SSH fails with `Permission denied (publickey)`, pnpm suggests checking the loaded keys with `ssh-add -l`. Resolving an SSH URL that refuses the key also shows a local HTTPS rewrite that leaves the recorded URL alone [#&#8203;13743](https://github.com/pnpm/pnpm/issues/13743).

- Lockfile verification now fails with `ERR_PNPM_TARBALL_URL_MISMATCH`, `ERR_PNPM_TARBALL_REVISION_MISMATCH`, or `ERR_PNPM_MISSING_NAMED_REGISTRY` when every rejected entry failed that check. These failures were reported as the generic `ERR_PNPM_LOCKFILE_RESOLUTION_VERIFICATION`.

- The lockfile verification error now suggests relaxing the policy that flagged an entry only if a fresh resolution still fails and you trust the affected packages. Errors from checks that no policy controls, such as a missing tarball integrity, no longer suggest relaxing a policy [#&#8203;14411](https://github.com/pnpm/pnpm/issues/14411).

- `pnpm install` no longer prints an extra `Progress:` line after the progress line is marked `done` [#&#8203;16184](https://github.com/pnpm/pnpm/issues/16184).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.7.0`](https://github.com/pnpm/pnpm/releases/tag/v12.7.0): pnpm 12.7

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.6.0...v12.7.0)

pnpm 12.7.0 ships with `.nvmrc` and `.node-version` support in the global `node` shim, `pnpm install --allow-build`, `pnpm publish --publish-wait-timeout`, and `pnpm-workspace.yaml` created from the `workspaces` field. `pnpm install --force` no longer installs optional dependencies built for other platforms. This release also carries security fixes for bin shims on Nix, for lifecycle scripts of packages in a `storeDir` inside the workspace, and for `userAgent` placeholders in `pnpm-workspace.yaml`.

##### Minor Changes

- `pnpm install --force` now keeps skipping optional dependencies whose `os`, `cpu` or `libc` do not match the host. It still refetches every package and lifts `engineStrict`. The new `forceIgnoresPlatform` setting restores the previous behaviour, installing optional dependencies of every platform under `--force` [#&#8203;6133](https://github.com/pnpm/pnpm/issues/6133).

- The global `node` shim created by pnpm now uses the Node.js version from the nearest `.nvmrc` or `.node-version` file when the project does not declare a Node.js runtime in `devEngines.runtime` or `engines.runtime` [#&#8203;4471](https://github.com/pnpm/pnpm/issues/4471). The nearest directory with a Node.js runtime declaration decides the version. Within one directory, `package.json` takes precedence over `.node-version`, which takes precedence over `.nvmrc`. An `.nvmrc` value that only nvm can act on, such as `system` or a custom alias, is ignored.

- `pnpm install` now supports the `--allow-build` option to selectively allow or deny package lifecycle scripts and record them in `pnpm-workspace.yaml` [#&#8203;15388](https://github.com/pnpm/pnpm/issues/15388).

- Added `pnpm publish --publish-wait-timeout <milliseconds>` to wait for published versions and their tarballs to become available from the registry. Set `publishWaitTimeout` in `pnpm-workspace.yaml` to configure a default. A value of `0` disables the check.

  Recursive publishing confirms availability before publishing dependent packages. If confirmation times out, the command fails.

  When `pnpm publish -r --report-summary` fails after some uploads were accepted, the summary file now lists those packages.

- `pnpm install` now creates `pnpm-workspace.yaml` from the `workspaces` field of the root `package.json` when the repository has no `pnpm-workspace.yaml`. The projects the field lists are linked on that same install. An existing `pnpm-workspace.yaml` is never changed. With `--ignore-workspace`, no file is created. If the `workspaces` field later differs from `packages` in `pnpm-workspace.yaml`, pnpm prints a warning [#&#8203;2255](https://github.com/pnpm/pnpm/issues/2255).

- When a project pins a pnpm version or a runtime that another pnpm process is installing at that moment, pnpm now waits a few seconds and then installs and runs a private copy of its own. It used to wait up to five minutes and then use the shared install directory without the lock. The private copy is removed once the command has run. `pnpm store prune` removes any private copy that a killed process left behind [#&#8203;15413](https://github.com/pnpm/pnpm/issues/15413).

- pnpm now keeps the blank lines between entries of `package.json` when it updates the file, for example on `pnpm add` [#&#8203;5602](https://github.com/pnpm/pnpm/issues/5602).

##### Patch Changes

##### Security

- pnpm no longer expands environment variables in a `userAgent` set in a project's `pnpm-workspace.yaml`. A `userAgent` with a placeholder in that file is now ignored. Before this fix, pnpm sent the variable's value to the configured registry [#&#8203;15415](https://github.com/pnpm/pnpm/issues/15415).

- On Nix, a dependency's bin named like a system utility such as `sed` can no longer redirect a POSIX bin shim or the `pnpm`, `pn`, `pnpx`, and `pnx` launchers. The shims and launchers now ignore `node_modules` and relative `PATH` entries while they locate their own files. Installing again replaces the shims already in `node_modules` [#&#8203;14883](https://github.com/pnpm/pnpm/issues/14883).

- pnpm no longer treats manifests inside its store, cache, state, or modules directories as workspace projects. Before, a `storeDir` inside the workspace could let lifecycle scripts of packages in the store run without `allowBuilds` approval [#&#8203;15033](https://github.com/pnpm/pnpm/issues/15033).

- Packages that run a lifecycle script are no longer hard-linked into the virtual store, so a build script can no longer rewrite the workspace source of an injected package or the store copy it was imported from [#&#8203;15483](https://github.com/pnpm/pnpm/issues/15483).

##### Installing packages

- Fixed `pnpm install`, `pnpm add`, `pnpm remove`, and `pnpm peers check` running out of memory when many packages share a missing peer dependency. This mostly affected projects with `autoInstallPeers: false` [#&#8203;15362](https://github.com/pnpm/pnpm/issues/15362).

- pnpm no longer hangs for up to 5 minutes after a pnpm process was killed while setting up the pnpm version pinned in `packageManager` or `devEngines` [#&#8203;15360](https://github.com/pnpm/pnpm/issues/15360), [#&#8203;15393](https://github.com/pnpm/pnpm/issues/15393). The killed process left behind a lock that every later pnpm command in the project waited on. pnpm now detects that the process holding a lock is gone and takes the lock over at once. The same applies to the locks pnpm takes while installing a managed runtime or writing the global bin directory. Two pnpm processes that are both still running keep waiting for each other as before.

- Requests to a registry or tarball server whose TLS certificate fails verification now fail at once. Such requests were retried for more than a minute without any output [#&#8203;9134](https://github.com/pnpm/pnpm/issues/9134).

- On macOS, pnpm now falls back to its bundled CA roots when system trust evaluation is unavailable, such as in a sandbox or when macOS cannot create an SSL policy for a registry connection. Installs failed or crashed on the first registry request in that case. Custom `ca` certificates are now honored directly [#&#8203;15329](https://github.com/pnpm/pnpm/issues/15329), [#&#8203;14461](https://github.com/pnpm/pnpm/issues/14461).

- `pnpm install` now caps concurrent connections to a proxy at 50 sockets by default [#&#8203;15280](https://github.com/pnpm/pnpm/issues/15280). It also immediately retries transient connection resets when downloading package archives.

- `pnpm install` now reuses a package already present in the store when an existing lockfile entry satisfies the dependency, avoiding registry requests that fail without authorization [#&#8203;2522](https://github.com/pnpm/pnpm/issues/2522).

- Installing or adding dependencies no longer fails when a previously installed local tarball file was deleted from disk [#&#8203;8367](https://github.com/pnpm/pnpm/issues/8367).

- `pnpm install` now installs the new version of a local tarball dependency whose file was replaced at the same path [#&#8203;2437](https://github.com/pnpm/pnpm/issues/2437). `pnpm install --frozen-lockfile` rejects such a changed tarball, even when the previous archive contents are in the store [#&#8203;1889](https://github.com/pnpm/pnpm/issues/1889).

- `pnpm install` now fetches committed submodules of git dependencies [#&#8203;1470](https://github.com/pnpm/pnpm/issues/1470).

- `pnpm install` now applies patches produced by `pnpm patch-commit` when an edit removes the trailing lines of a file along with its newline. The install no longer fails with `ERR_PNPM_INVALID_PATCH` ("expected end of hunk") [#&#8203;12451](https://github.com/pnpm/pnpm/issues/12451).

- `pnpm install` now preserves existing `node_modules` directories when a cross-device move reports `EXDEV` [#&#8203;14504](https://github.com/pnpm/pnpm/issues/14504).

- `pnpm install` no longer fails when writing the workspace state file encounters an error. Failures to update the state file now emit a warning instead of aborting the install [#&#8203;14550](https://github.com/pnpm/pnpm/issues/14550).

- Interrupting `pnpm install` with Ctrl+C or SIGTERM no longer leaves a temporary lockfile (`.pnpm-lock.yaml.*.tmp`) behind in the project [#&#8203;1418](https://github.com/pnpm/pnpm/issues/1418).

- `pnpm install` now relinks a direct dependency whose link in `node_modules` points to a missing target. Before, it reported "Already up to date" and left the broken link [#&#8203;9758](https://github.com/pnpm/pnpm/issues/9758).

- `pnpm install` uses less CPU when it links packages from a warm store. On Windows, a warm install could take several times longer than with pnpm 11 [#&#8203;15439](https://github.com/pnpm/pnpm/issues/15439).

- `pnpm install` now runs `node --version` once per run. A workspace whose projects keep their own lockfiles (`sharedWorkspaceLockfile: false`) previously ran the probe once or twice for every project, and on macOS the concurrent launches waited on each other, so a project could wait several seconds before its linking started.

- A repeat `pnpm install --frozen-lockfile` with `nodeLinker: hoisted` in a workspace no longer re-links `node_modules` when nothing changed.

- Custom fetcher hooks no longer run a second time during installation when an archive was already fetched during dependency resolution [#&#8203;15025](https://github.com/pnpm/pnpm/issues/15025).

- Fixed a package resolved by a `resolvers` pnpmfile hook installing without its own dependencies. This happened when the hook returned no `manifest` and a `fetchers` hook handled the resolution [#&#8203;15552](https://github.com/pnpm/pnpm/issues/15552).

- `pnpm install --prod` and other installs that skip `devDependencies` no longer run the `pnpm:devPreinstall` script [#&#8203;7065](https://github.com/pnpm/pnpm/issues/7065). They skip `prepare` lifecycle scripts too, as do installs given package arguments.

- `pnpm prune --prod` and production installs now remove devDependencies when `lockfile: false` is configured [#&#8203;2677](https://github.com/pnpm/pnpm/issues/2677).

- `pnpm install --prod`, `pnpm fetch --prod` and `pnpm deploy --prod` no longer install a devDependency that is only there to satisfy an optional peer dependency of a production dependency. `pnpm list`, `pnpm why`, `pnpm licenses`, `pnpm sbom` and `pnpm audit` leave it out of `--prod` results too. The same applies to `--dev`. A peer that is not optional is still installed and audited [#&#8203;15344](https://github.com/pnpm/pnpm/issues/15344).

- `pnpm install` no longer skips optional dependencies that the Node.js version locked for a `devEngines.runtime` range supports, when the range uses `onFail: download`. An explicitly set `nodeVersion` still takes priority [#&#8203;14628](https://github.com/pnpm/pnpm/issues/14628).

- `pnpm fetch` now also installs the pnpm version that `pnpm-lock.yaml` pins, when it differs from the running pnpm. A later `pnpm install --offline` that switches to the pinned version no longer fails because that version is missing from the store [#&#8203;11808](https://github.com/pnpm/pnpm/issues/11808).

- A dependency that ships a `binding.gyp` and sets `gypfile: false` no longer gets the `node-gyp rebuild` install script pnpm synthesizes for it. Such a dependency needs no `allowBuilds` entry and is no longer listed under "Ignored build scripts".

- `pnpm install` no longer adds `allowBuilds` placeholder entries to `pnpm-workspace.yaml` when it runs in CI or without a terminal. Interactive installs still add them [#&#8203;11574](https://github.com/pnpm/pnpm/issues/11574).

- pnpm now detects the same CI environments as pnpm 11, including AWS CodeBuild, which does not set `CI`. On these services `pnpm install` uses a frozen lockfile by default and fails with `ERR_PNPM_OUTDATED_LOCKFILE` when the lockfile is outdated.

##### Resolving and linking dependencies

- Installing through a pnpr server now installs a project's peer dependencies when `autoInstallPeers` is enabled. A project that declared only peer dependencies failed with `ERR_PNPM_OUTDATED_LOCKFILE` or skipped its peers [#&#8203;14833](https://github.com/pnpm/pnpm/issues/14833).

- pnpm now installs a dependency that a package also declares as an optional peer dependency, for example `lightningcss` in some vite builds. The dependency was missing from `node_modules`, so the package failed to import it [#&#8203;8912](https://github.com/pnpm/pnpm/issues/8912).

- Removal overrides such as `"parent>peer": "-"` now prevent optional peers from being installed from another workspace package [#&#8203;15008](https://github.com/pnpm/pnpm/issues/15008).

- Removing an entry from `overrides` now re-resolves the packages it targeted. A version the override had locked is no longer kept just because the declared range still accepts it [#&#8203;4587](https://github.com/pnpm/pnpm/issues/4587).

- `packageExtensions` and `overrides` entries with a ranged selector (such as `@<X` or `@*`) no longer match a dependency that has no `package.json`, such as a local directory dependency [#&#8203;15007](https://github.com/pnpm/pnpm/issues/15007).

- Trim leading and trailing whitespace from dependency override selectors in `pnpm.overrides` [#&#8203;6356](https://github.com/pnpm/pnpm/issues/6356).

- With `trustPolicy: no-downgrade`, pnpm now resolves the newest matching version that is not a trust downgrade. Previously a dependency failed with `ERR_PNPM_TRUST_DOWNGRADE` even when an older version satisfied its range. `pnpm self-update` picks its target version the same way. A request for an exact version still fails [#&#8203;14176](https://github.com/pnpm/pnpm/issues/14176).

- `pnpm install` now re-resolves a dependency when its manifest range is updated from a prerelease to a stable version. The lockfile previously retained the prerelease version and caused `--frozen-lockfile` to fail [#&#8203;15528](https://github.com/pnpm/pnpm/issues/15528).

- `pnpm install --ignore-pnpmfile` no longer removes `pnpmfileChecksum` from an up-to-date `pnpm-lock.yaml`. `pnpm install --frozen-lockfile --ignore-pnpmfile` no longer fails with `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` when the lockfile records a `pnpmfileChecksum`. A command that resolves dependencies with the pnpmfile ignored still writes the lockfile without it [#&#8203;10944](https://github.com/pnpm/pnpm/issues/10944).

- `pnpm install` and `pnpm peers check` now use local tarball packages' actual versions when checking peer dependencies. Compatible packages no longer fail with `strictPeerDependencies` enabled.

- `pnpm peers check` and the install-time peer dependency check now resolve peer dependencies from the workspace root when `resolvePeersFromWorkspaceRoot` is enabled [#&#8203;14982](https://github.com/pnpm/pnpm/issues/14982).

- `autoDedupe` and `pnpm dedupe` now move transitive dependencies to the version a `catalog:` dependency pins, as they already did for versions written directly in `package.json`. Previously they could move those dependencies to a higher version and keep both versions in the lockfile.

- `pnpm dedupe` now produces a stable lockfile when a dependency's range matches both a direct dependency and an `npm:` alias of the same package. The dependency resolves to the version of the direct dependency. Repeated runs previously alternated between two lockfiles [#&#8203;15588](https://github.com/pnpm/pnpm/issues/15588).

- Merging lockfiles now preserves recorded configuration fields such as `overrides`, `neverBuiltDependencies`, `patchedDependencies`, `packageExtensionsChecksum`, `settings`, and `catalogs` [#&#8203;8366](https://github.com/pnpm/pnpm/issues/8366).

- A lockfile entry whose resolution is unchanged now keeps its recorded `deprecated` message [#&#8203;5772](https://github.com/pnpm/pnpm/issues/5772).

- pnpm no longer writes a package's legacy array-form `engines`, such as `["node >= 0.8"]`, to the lockfile. It was recorded as an object keyed by index, such as `{'0': node >= 0.8}` [#&#8203;4518](https://github.com/pnpm/pnpm/issues/4518).

- Tarball URLs recorded in the lockfile now strip default HTTP and HTTPS ports (`:80` and `:443`) [#&#8203;15539](https://github.com/pnpm/pnpm/issues/15539).

- `node_modules/.package-map.json` no longer contains entries that point at directories that do not exist. Such entries appeared for packages installed only with peer dependencies, most visibly with `enableGlobalVirtualStore` [#&#8203;14938](https://github.com/pnpm/pnpm/issues/14938).

- With `nodeLinker: hoisted`, `hoistWorkspacePackages` now links each workspace project that `hoistPattern` or `publicHoistPattern` selects into the root `node_modules`, unless a hoisted package or a root dependency already uses its name. The project's bins are linked into the root `node_modules/.bin` [#&#8203;7553](https://github.com/pnpm/pnpm/issues/7553).

- With `nodeLinker: hoisted`, `pnpm install` now removes the commands of the packages it removes from `node_modules/.bin`, such as a nested copy deduped into the root `node_modules` [#&#8203;7568](https://github.com/pnpm/pnpm/issues/7568).

- `pnpm install` no longer puts a dependency's bin on `PATH` for that dependency's own lifecycle scripts before the bin's file exists. pnpm links such a bin after the dependency's build has run. It also removes such a bin left by an earlier install. This fixes installing the `node` package on Windows [#&#8203;15501](https://github.com/pnpm/pnpm/issues/15501).

- Dependencies and executable binaries are now correctly linked and accessible for workspace packages using `publishConfig.directory` and `publishConfig.linkDirectory` [#&#8203;8338](https://github.com/pnpm/pnpm/issues/8338).

- Bin linking leaves workspace and linked dependency files outside node\_modules unchanged. Already executable bin files no longer receive redundant permission changes.

##### Workspaces and filtering

- `pnpm install` now finds workspace projects reached through a symlink, such as a `packages` directory that links to a folder outside the workspace. It installs their dependencies, and the links in their `node_modules` resolve [#&#8203;1044](https://github.com/pnpm/pnpm/issues/1044).

- A dependency declared with `catalog:` now counts as a workspace dependency when its catalog entry points at a workspace project, for example `workspace:*` [#&#8203;15587](https://github.com/pnpm/pnpm/issues/15587). With `linkWorkspacePackages` enabled, so does an `npm:` alias of a workspace project, such as `"math-alias": "npm:math@^1.0.0"`. `pnpm -r run` runs that project first. `--filter <pkg>...` selects it.

- A `workspace:` dependency now resolves to a workspace project whose version is not valid semver, such as `1` or `1.0`. `workspace:*`, `workspace:^`, and `workspace:~` match it. A range identical to the version also matches it [#&#8203;4567](https://github.com/pnpm/pnpm/issues/4567).

- A `workspace:` dependency with an exact version now resolves to a workspace project whose version carries SemVer build metadata. For example, `workspace:0.5.6-next.3` matches a project at `0.5.6-next.3+f60facc` [#&#8203;6483](https://github.com/pnpm/pnpm/issues/6483).

- Secondary dependencies now prefer the version resolved by the local project's direct dependencies over versions from sibling workspace projects [#&#8203;7191](https://github.com/pnpm/pnpm/issues/7191).

- `pnpm install` now re-resolves a workspace project's auto-installed peer dependency when another workspace project changes its specifier for that package to one that excludes the locked version but still overlaps the peer range. The peer then resolves to the version a fresh install would pick [#&#8203;11800](https://github.com/pnpm/pnpm/issues/11800).

- `pnpm install --frozen-lockfile` now fails with `ERR_PNPM_OUTDATED_LOCKFILE` when `pnpm-lock.yaml` lists a workspace project whose directory or manifest file is missing. The install used to report success without installing that project's dependencies [#&#8203;7667](https://github.com/pnpm/pnpm/issues/7667).

- `pnpm install -r` now installs every workspace project when `recursiveInstall` is set to `false` in `pnpm-workspace.yaml` [#&#8203;7504](https://github.com/pnpm/pnpm/issues/7504).

- `pnpm install` with `--filter` now installs only the dependencies of the selected projects when using `nodeLinker: hoisted` [#&#8203;8882](https://github.com/pnpm/pnpm/issues/8882).

- `pnpm install` now updates an injected workspace dependency after that package's own dependencies change, when `shared-workspace-lockfile` is `false` [#&#8203;7209](https://github.com/pnpm/pnpm/issues/7209).

- `pnpm install` now copies the output of a workspace package's own `prepare`, `install`, or `postinstall` script into the injected copies of that package. Before, the injected copies kept only the files that existed before the script ran. `syncInjectedDepsAfterScripts` now also works when `modulesDir` is set [#&#8203;9464](https://github.com/pnpm/pnpm/issues/9464).

- `syncInjectedDepsAfterScripts` now copies files into injected dependencies when `node_modules` is on another filesystem than the package sources. The sync previously failed with a cross-device link error and made the script run exit with an error [#&#8203;14703](https://github.com/pnpm/pnpm/issues/14703).

- A `modulesDir` with several path segments, such as `www/modules`, now puts each workspace project's dependencies in `<project>/www/modules` on both fresh and frozen installs, and `pnpm bin` prints `<project>/www/modules/.bin` [#&#8203;15484](https://github.com/pnpm/pnpm/issues/15484).

- With `nodeLinker: hoisted`, pnpm now installs the root project's dependencies into a custom `modulesDir` instead of `node_modules`. With a custom `modulesDir`, the virtual store and its `lock.yaml` now default to `<modulesDir>/.pnpm`.

- A repeat `pnpm install` in a workspace with a custom `modulesDir` now takes the up-to-date fast path. Before, pnpm looked for each workspace project's dependencies in `node_modules` and ran a full install every time.

- pnpm now warns when a workspace install covers a project that has its own `pnpm-workspace.yaml`. The nested file's settings, such as `patchedDependencies`, do not apply when the outer workspace installs that project. pnpm reads settings only from the `pnpm-workspace.yaml` at the workspace root [#&#8203;11724](https://github.com/pnpm/pnpm/issues/11724).

- The `[<since>]` filter selector now compares against the commit where the current branch forked from `<since>`. Projects changed only by newer commits on `<since>` are no longer selected. Uncommitted changes are still included. In a shallow clone without that commit, pnpm compares against `<since>` directly, as before [#&#8203;9907](https://github.com/pnpm/pnpm/issues/9907).

- `--filter "[<since>]"` now selects workspace packages when dependency versions change in a catalog in `pnpm-workspace.yaml` [#&#8203;8718](https://github.com/pnpm/pnpm/issues/8718). It also selects projects that files were moved out of when git detects the move as a rename [#&#8203;15481](https://github.com/pnpm/pnpm/issues/15481).

- `--filter` now evaluates selectors in order, so later inclusion filters can re-include packages that an earlier exclusion filter excluded [#&#8203;9354](https://github.com/pnpm/pnpm/issues/9354).

##### Adding, updating, and removing dependencies

- `pnpm add` now saves changes to `package.json` before running lifecycle scripts, so a postinstall script failure leaves the added dependency in `package.json` [#&#8203;8627](https://github.com/pnpm/pnpm/issues/8627).

- `pnpm add` now saves the requested exact version when adding a dependency, even when the manifest already contains a version range [#&#8203;6040](https://github.com/pnpm/pnpm/issues/6040).

- `pnpm add <pkg>@&#8203;<version>` and `pnpm update <pkg>@&#8203;<version>` now move the catalog entry onto the named version when the entry's range already covers it. For example, `^7.22.17` becomes `^7.29.6`, the same way `pnpm update <pkg>` moves an entry to the version it resolves [#&#8203;13715](https://github.com/pnpm/pnpm/issues/13715).

- `pnpm add` and `pnpm install` keep an empty `peerDependencies`, `dependencies`, `devDependencies`, or `optionalDependencies` field that was already in `package.json`. pnpm still drops such a field when it removes the last entry itself, as `pnpm remove` does [#&#8203;5096](https://github.com/pnpm/pnpm/issues/5096).

- `pnpm update` now keeps a version range whose shape has no save prefix, such as `<= 3.0.0` or `>=1.0.0 <2.0.0`, when the updated version still satisfies it. Before, `<= 3.0.0` became `^3.0.0` [#&#8203;6714](https://github.com/pnpm/pnpm/issues/6714).

- `pnpm update <pkg>` now moves a package off a locked version the registry no longer serves, such as an unpublished release. The lockfile check for supply-chain policies such as `minimumReleaseAge` used to reject that version before the update could replace it [#&#8203;9953](https://github.com/pnpm/pnpm/issues/9953).

- `pnpm update --prod` no longer installs devDependencies when run in a project installed with `--prod` [#&#8203;8038](https://github.com/pnpm/pnpm/issues/8038).

- `pnpm update --interactive --workspace` now allows external dependencies to be updated.

- `pnpm outdated` and `pnpm update` now apply `minimumReleaseAge` to GitHub Actions. `minimumReleaseAgeExclude` entries match action names such as `actions/checkout` [#&#8203;13923](https://github.com/pnpm/pnpm/issues/13923).

- `pnpm remove` now accepts `--trust-lockfile` and `--no-trust-lockfile` to control supply-chain policy checks while removing a package [#&#8203;14406](https://github.com/pnpm/pnpm/issues/14406).

- `pnpm unlink` now removes the `link:` dependency that `pnpm link <dir>` added to `package.json`. The linked package is removed from `node_modules` and the lockfile. A `link:` dependency to another directory is kept [#&#8203;4219](https://github.com/pnpm/pnpm/issues/4219).

- `pnpm install` now prunes unreferenced catalog entries from `pnpm-workspace.yaml` when `catalogPrune: true` is configured [#&#8203;15273](https://github.com/pnpm/pnpm/issues/15273).

- `minimumReleaseAgeExcludePrune` and `trustPolicyExcludePrune` now work in workspaces with `shared-workspace-lockfile=false`. Once every project has been installed, pnpm drops an entry only if no project lockfile records it. Undecided `allowBuilds` entries are pruned the same way [#&#8203;14612](https://github.com/pnpm/pnpm/issues/14612).

- Exclude entries that pnpm writes to `pnpm-workspace.yaml` now match the file's list indentation and dominant quote style [#&#8203;15571](https://github.com/pnpm/pnpm/issues/15571), [#&#8203;15079](https://github.com/pnpm/pnpm/issues/15079).

- `pnpm import` now converts dependencies that use Yarn's `patch:` protocol. The dependency keeps the version it patches, and the patch file is added to `patchedDependencies` in `pnpm-workspace.yaml`. If the patch file is missing, pnpm prints a warning and imports the dependency without the patch [#&#8203;10278](https://github.com/pnpm/pnpm/issues/10278).

- `pnpm import` in a workspace now keeps the versions pinned by the root `yarn.lock`, `package-lock.json`, or `npm-shrinkwrap.json` when another workspace project's range allows a newer version. Before, the root project got the newest version in its range [#&#8203;4385](https://github.com/pnpm/pnpm/issues/4385).

- `pnpm patch`, `pnpm patch-commit`, and `pnpm patch-remove` now work in a project of a workspace with `sharedWorkspaceLockfile: false`. `pnpm patch` failed there with `ERR_PNPM_PATCH_NO_LOCKFILE` after a successful install. The reinstall after committing or removing a patch left the project's own `node_modules` unchanged [#&#8203;9926](https://github.com/pnpm/pnpm/issues/9926).

- `pnpm patch-commit` now resolves default patch directory locations when passed a package name or package specifier (such as `pnpm patch-commit <pkg>` or `pnpm patch-commit <pkg>@&#8203;<version>`).

- `pnpm patch-commit` now updates the lockfile snapshot and prunes removed dependencies when the patch modifies `package.json` [#&#8203;6866](https://github.com/pnpm/pnpm/issues/6866).

- `pnpm patch-commit` now falls back to copying package files when hard linking fails.

##### Running scripts and commands

- A script that pnpm runs without a terminal now ends when pnpm itself is killed. Killing pnpm's process group, as Playwright's `webServer` does to stop the command it started, used to leave the script running and holding the caller's output pipes open [#&#8203;15555](https://github.com/pnpm/pnpm/issues/15555).

- `pnpm --filter <project> <command>` and `pnpm -r <command>` now run a command installed in the selected projects' dependencies when none of them has a script by that name. This matches `pnpm <command>` in a single project. `pnpm run` with `--filter` or `-r` still reports the missing script [#&#8203;10151](https://github.com/pnpm/pnpm/issues/10151).

- `pnpm exec` and `pnpm dlx` now set `npm_execpath`, `INIT_CWD`, `npm_node_execpath`, and `NODE` in child environments when Node.js is available. Stale inherited `NODE` and `npm_node_execpath` variables are cleared when Node.js cannot be found on PATH [#&#8203;7037](https://github.com/pnpm/pnpm/issues/7037). Scripts that `pnpx` and `pnx` run now get pnpm itself as `npm_execpath`. A script that ran `$npm_execpath install` there ran `pnpm dlx install`.

- `pnpm exec` now sets the `PWD` environment variable to the directory the command runs in. Shells and tools that read `PWD` now report the logical path of a workspace package reached through a symlink [#&#8203;1550](https://github.com/pnpm/pnpm/issues/1550).

- A script that runs `pnpm run` no longer adds duplicate `node_modules/.bin` and `node-gyp-bin` entries to `PATH` [#&#8203;5352](https://github.com/pnpm/pnpm/issues/5352).

- Concurrent `pnpm run` and `pnpm exec` commands now serialize their dependency installs [#&#8203;14551](https://github.com/pnpm/pnpm/issues/14551).

- `pnpm run` and `pnpm exec` with `verifyDepsBeforeRun` now accept a moved project whose store is on the project's volume. Before, the check reported that the workspace structure had changed whenever the default store was not on the home volume.

- `verifyDepsBeforeRun` checks now account for project-specific `packageConfigs` overrides in workspaces with `sharedWorkspaceLockfile: false` [#&#8203;15545](https://github.com/pnpm/pnpm/issues/15545).

- `pnpm restart` now runs the "stop" and "start" scripts when the package has no "restart" script. Previously it ran "stop" and then failed with "Missing script: restart" [#&#8203;4750](https://github.com/pnpm/pnpm/issues/4750).

- `pnpm dlx` now keeps a separate cache entry for each Node.js major version. A package built under one Node.js major version, such as a native addon, is no longer reused under another [#&#8203;8611](https://github.com/pnpm/pnpm/issues/8611).

- `pnpm pipeline` no longer fails when run in a project outside a Git work tree or on a system without `git`. Tasks in those projects run without caching, and pnpm prints a warning explaining why [#&#8203;15601](https://github.com/pnpm/pnpm/issues/15601).

- A `runtime:` version range that contains `||` or a space, such as a `devEngines.runtime` version of `^22.18.0 || ^24.0.0`, now installs the requested runtime. pnpm used to install the npm package with the same name, such as `node` [#&#8203;14817](https://github.com/pnpm/pnpm/issues/14817).

- When the configured `scriptShell` does not exist, running a script now fails with an error that names the shell. Previously pnpm printed only an exit code or the package directory [#&#8203;7562](https://github.com/pnpm/pnpm/issues/7562).

- A script killed by a signal now fails with an error that names the signal, such as `Command failed with signal SIGKILL.` [#&#8203;9821](https://github.com/pnpm/pnpm/issues/9821).

##### Publishing, packing, and deploying

- `pnpm publish` now resolves `workspace:` dependencies from workspace manifests when `node_modules` is not installed. Previously, publishing without `node_modules` failed with `ERR_PNPM_CANNOT_RESOLVE_WORKSPACE_PROTOCOL` [#&#8203;6567](https://github.com/pnpm/pnpm/issues/6567).

- `pnpm publish` now honors `publishConfig["@&#8203;scope:registry"]` for a package in that scope. It takes precedence over the registry set for the same scope in `.npmrc` and over `publishConfig.registry` [#&#8203;12071](https://github.com/pnpm/pnpm/issues/12071).

- `pnpm pack` and `pnpm publish` now include bundled dependencies when using the isolated linker. This covers workspace packages and the dependencies of each bundled package. Bundled dependencies are also included when `publishConfig.directory` selects a build directory [#&#8203;1643](https://github.com/pnpm/pnpm/issues/1643).

- `pnpm pack`, `pnpm deploy`, and installs of local directory dependencies now keep symlinks that point to files or directories included in the package. `pnpm pack` leaves out symlinks that point outside the package [#&#8203;8208](https://github.com/pnpm/pnpm/issues/8208).

- `pnpm pack` now preserves file executable permissions in the packed tarball when source files are executable on disk.

- `pnpm publish` and `pnpm pack` now report a missing `version` or `name` field on a workspace dependency. Previously, pnpm reported that the dependency was not installed [#&#8203;4164](https://github.com/pnpm/pnpm/issues/4164).

- `pnpm publish` and `pnpm pack` now report an error when a bin script has a shebang line ending with CRLF [#&#8203;7311](https://github.com/pnpm/pnpm/issues/7311).

- `pnpm deploy` now copies the `packageManager` and `devEngines.packageManager` fields of the workspace root `package.json` into the deployed `package.json`, unless the deployed project pins a package manager itself [#&#8203;9079](https://github.com/pnpm/pnpm/issues/9079).

- `pnpm deploy` now puts the virtual store at `virtualStoreDir`, resolved against the deploy directory. A shared-lockfile deploy records `virtualStoreDir` in the deployed `pnpm-workspace.yaml`. With the global virtual store enabled or an absolute `virtualStoreDir`, the deploy still uses `node_modules/.pnpm` [#&#8203;8787](https://github.com/pnpm/pnpm/issues/8787).

- `pnpm deploy` now respects `--package-import-method` passed on the command line and reports the package import method correctly [#&#8203;7593](https://github.com/pnpm/pnpm/issues/7593).

- `pnpm deploy` does not run the `prepare` scripts of the deployed project [#&#8203;7282](https://github.com/pnpm/pnpm/issues/7282).

- `pnpm deploy --legacy` no longer rewrites the source workspace's `node_modules/.pnpm-workspace-state-v1.json` to describe only the deployed project [#&#8203;15352](https://github.com/pnpm/pnpm/issues/15352).

##### Manifests and configuration files

- pnpm now reads and updates `package.json5` project manifests. Manifest updates retain comments, and workspace discovery prefers `package.json`, then `package.json5`, then `package.yaml` [#&#8203;15129](https://github.com/pnpm/pnpm/issues/15129). `pnpm pack` includes exactly one `package.json` in the archive when the project uses an alternative manifest format, even when `.npmignore` or `files` excludes the source file.

- Git-hosted dependencies that use a `package.yaml` or `package.json5` manifest now honor its `files` field [#&#8203;7906](https://github.com/pnpm/pnpm/issues/7906).

- Fixed `pnpm version` failing on projects using a `package.yaml` manifest.

  Fixed `pnpm init` creating an extra `package.json` when `package.yaml` is already present.

- `pnpm version` now applies pending bumps to private workspace packages. A private package's changelog is written to its committed `CHANGELOG.md`, also when `versioning.changelog.storage` is `registry` [#&#8203;13736](https://github.com/pnpm/pnpm/issues/13736), [#&#8203;13519](https://github.com/pnpm/pnpm/issues/13519).

- `pnpm init` now supports the `--bare` option. It creates a `package.json` file with only the required fields [#&#8203;15538](https://github.com/pnpm/pnpm/issues/15538).

- The `reporter` setting is now honored when it is configured in `pnpm-workspace.yaml`, the global configuration, or the `PNPM_CONFIG_REPORTER` environment variable. Configured `reporter: silent` makes silent output the default. An explicit `--reporter` takes precedence [#&#8203;4879](https://github.com/pnpm/pnpm/issues/4879).

- `.npmrc` and `pnpm-workspace.yaml` files now support npm's `${VAR?}` placeholder. It expands to the value of `VAR`, or to an empty string without a warning when `VAR` is unset [#&#8203;14404](https://github.com/pnpm/pnpm/issues/14404).

- pnpm now expands environment variables in `_auth.authToken` values loaded from global `config.yaml` and `pnpm_config__auth`.

- pnpm now keeps the configured default registry when `_auth` holds credentials for several registries and some of those registries serve package scopes.

  Lockfile verification checks a tarball hosted on a scoped registry against that registry's metadata, unless the package's own scope has a registry assigned [#&#8203;15530](https://github.com/pnpm/pnpm/issues/15530).

- pnpm now parses the first setting in a `.npmrc` that starts with a UTF-8 byte order mark. Previously, the leading byte order mark caused the first line's key to be ignored [#&#8203;15353](https://github.com/pnpm/pnpm/issues/15353).

- pnpm now prints a warning when a `.npmrc`, `auth.ini`, or the file set by `npmrcAuthFile` exists but cannot be read. The settings in such a file were ignored without any message. A `.npmrc` that contains invalid UTF-8 is now read [#&#8203;5065](https://github.com/pnpm/pnpm/issues/5065).

- `pnpm config set` and `pnpm config delete` now preserve comments and repeated keys such as `ca=` in `.npmrc` [#&#8203;14851](https://github.com/pnpm/pnpm/issues/14851).

- `pnpm login` now logs back in to an existing user on registries without web login, such as verdaccio. The classic login request sends the username and password as basic auth, as `npm login` does [#&#8203;12055](https://github.com/pnpm/pnpm/issues/12055).

- Commands that do not use the store no longer create a temporary file in the project directory when they load their settings. These include `pnpm view`, `pnpm config`, `pnpm root`, `pnpm bin`, `pnpm exec`, `pnpm run`, the script shortcuts such as `pnpm test`, and the registry commands such as `pnpm whoami`, `pnpm dist-tag`, and `pnpm search`. `pnpm exec`, `pnpm run`, and the script shortcuts still create one in projects that declare `configDependencies`.

##### Global packages, pnpm versions, and runtimes

- Global commands such as `pnpm add --global`, `pnpm list --global`, and `pnpm bin --global` now run with the pnpm you invoked, even in a project that pins another pnpm version. Previously, a pin with `onFail: "download"` switched them to the pinned pnpm, and a pinned pnpm 10 or older failed because its global bin directory was not in `PATH` [#&#8203;14531](https://github.com/pnpm/pnpm/issues/14531).

- `pnpm add -g`, `pnpm update -g`, and `pnpm remove -g` no longer fail with `ERR_PNPM_PACKAGE_MANIFEST_IO_ERROR` when another global package's link into the store dangles, for example after the store was pruned. The pnpm install script failed the same way on such a machine.

- `pnpm update --global` now skips a global package installed from a `file:` path that no longer exists, prints a warning, and updates the remaining global packages. Previously the whole update failed with `ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND` [#&#8203;12533](https://github.com/pnpm/pnpm/issues/12533).

- `pnpm self-update` run in a project that pins pnpm through `packageManager` or `devEngines.packageManager` now also updates the global pnpm, as it does outside a project [#&#8203;14747](https://github.com/pnpm/pnpm/issues/14747).

- `pnpm self-update` no longer leaves the previous pnpm in the global packages when it was installed as `@pnpm/exe`. `pnpm ls -g` now lists a single pnpm [#&#8203;14709](https://github.com/pnpm/pnpm/issues/14709). `pnpm setup` now installs pnpm under the package name `pnpm` too, so both commands leave the same shims in the global bin directory. After a self-update on Windows, PowerShell ran pnpm through `pnpm.cmd` and asked "Terminate batch job (Y/N)?" on Ctrl+C [#&#8203;15567](https://github.com/pnpm/pnpm/issues/15567).

- `pnpm setup` failed with `Text file busy (os error 26)` when `$PNPM_HOME/bin` already held `pn`, `pnpx`, or `pnx` as links to the running pnpm executable. It now replaces those files and completes [#&#8203;15494](https://github.com/pnpm/pnpm/issues/15494).

- `pnpm setup` no longer deletes aliases and other lines that sit between a `# pnpm` comment and the pnpm block in a shell startup file [#&#8203;7067](https://github.com/pnpm/pnpm/issues/7067).

- When pnpm switches to the version a project pins, the `minimumReleaseAge` approvals for that version are now added to `minimumReleaseAgeExclude` in the project's `pnpm-workspace.yaml`. A project without that file gets one. Global commands leave the project's settings unchanged [#&#8203;15396](https://github.com/pnpm/pnpm/issues/15396).

- `pnpm env remove` now cleans up dangling Node.js executables and shims. Surviving global commands remain intact.

- Node.js runtime resolution now supports Windows ARM64. Node.js 20 and newer resolve native `win-arm64` builds, and older versions fall back to `win-x64` under emulation [#&#8203;7123](https://github.com/pnpm/pnpm/issues/7123).

##### Windows and WSL

- On Windows, `pnpm clean` and installs no longer fail immediately when another process uses a package in `node_modules`. pnpm waits up to a minute for an open file. It waits up to 5 seconds for a running program [#&#8203;15081](https://github.com/pnpm/pnpm/issues/15081).

- `pnpm install` in WSL now waits out Windows file locks on a Windows drive such as `/mnt/c`, as it already does on Windows. Before, an antivirus or indexer scan holding a file open could fail the install with `EACCES` [#&#8203;6155](https://github.com/pnpm/pnpm/issues/6155).

- On Windows, pnpm now retries saving `pnpm-lock.yaml` for up to a minute while another process holds the file open. The save used to fail at once with `EPERM`, `EBUSY`, or "Access is denied" [#&#8203;9461](https://github.com/pnpm/pnpm/issues/9461).

- pnpm now escapes trailing dots and spaces in `node_modules/.pnpm` directory names. Windows strips these characters, so a dependency such as `"parent-pkg": "file:../"` created a directory that could not be deleted or failed to install [#&#8203;8101](https://github.com/pnpm/pnpm/issues/8101).

- On Windows, bin shims run from Git Bash, MSYS2, or Cygwin now pass `NODE_PATH` to Node.js as Windows paths. A project installed from cmd or PowerShell gave its bins a `NODE_PATH` under the Git install directory when they ran from Git Bash. Installing again replaces the shims already in `node_modules` [#&#8203;3360](https://github.com/pnpm/pnpm/issues/3360).

- Fixed scripts failing with errors such as `'an-compile' is not recognized` when `scriptShell` is set to `cmd.exe` on Windows [#&#8203;7181](https://github.com/pnpm/pnpm/issues/7181).

- On Windows, the error for a `node_modules` directory that pnpm cannot move out of the way now names the directory and says that a file in it is probably in use by another process [#&#8203;7505](https://github.com/pnpm/pnpm/issues/7505).

##### Inspecting dependencies

- `pnpm audit` and `pnpm audit signatures` now check only the dependencies of the projects selected by `--filter`, `--filter-prod`, or `--workspace-root`. The filter used to be ignored, so a filtered audit reported the whole workspace [#&#8203;10982](https://github.com/pnpm/pnpm/issues/10982).

- `pnpm audit` now lists at least one dependency path from every workspace project that depends on a vulnerable package. Before, a project whose dependency was reached through more than 100 paths filled the path list, and other projects that depend on the same package were left out [#&#8203;12200](https://github.com/pnpm/pnpm/issues/12200).

- `pnpm audit --fix` now prunes redundant overrides when one vulnerable range is a subset of another for the same package [#&#8203;8577](https://github.com/pnpm/pnpm/issues/8577).

- Running `pnpm list` inside a workspace package without `--recursive` or a filter now lists only the current package [#&#8203;14494](https://github.com/pnpm/pnpm/issues/14494). `pnpm licenses list` does the same. Use `--recursive` or `--filter` to list the licenses of other workspace projects [#&#8203;5689](https://github.com/pnpm/pnpm/issues/5689).

- `pnpm list --only-projects` now prints every project selected with `--filter` or `--recursive`, including a project that has no workspace dependencies [#&#8203;9770](https://github.com/pnpm/pnpm/issues/9770). It also lists the workspace projects when `sharedWorkspaceLockfile` is `false` [#&#8203;7151](https://github.com/pnpm/pnpm/issues/7151), and a project that sets `publishConfig.directory` [#&#8203;10635](https://github.com/pnpm/pnpm/issues/10635). It no longer reports packages in `node_modules` that are missing from the lockfile [#&#8203;9528](https://github.com/pnpm/pnpm/issues/9528).

- `pnpm licenses list` failed or reported nothing in a workspace with `sharedWorkspaceLockfile: false`. It now reads the lockfile of each selected project [#&#8203;10140](https://github.com/pnpm/pnpm/issues/10140).

- With `nodeLinker: hoisted`, `pnpm licenses list` reported every license as `Unknown` and listed paths under `node_modules/.pnpm` that do not exist. It now reads each package from the directory where the hoisted linker placed it [#&#8203;8589](https://github.com/pnpm/pnpm/issues/8589).

- `pnpm outdated` and `pnpm -r outdated` now fail with `ERR_PNPM_NO_PACKAGE_IN_DEPENDENCIES` when a requested package selector does not match any dependency in the inspected projects [#&#8203;2319](https://github.com/pnpm/pnpm/issues/2319).

- `pnpm -r outdated --json` now includes every outdated workspace dependency when multiple projects depend on different versions or dependency types of the same package. Such a package is keyed by its current version and dependency type, for example `[email protected] (dev)` [#&#8203;7693](https://github.com/pnpm/pnpm/issues/7693).

- `pnpm sbom` filtered to a single workspace project now takes the `author`, `description`, `license`, `repository`, and `bugs` fields from the workspace root `package.json` when the project does not declare them. A field the project declares is never taken from the root, even when it is blank or `null` [#&#8203;14882](https://github.com/pnpm/pnpm/issues/14882).

- `pnpm store status` no longer reports a package as modified when it has build or postinstall scripts, peer dependencies, or skipped optional dependencies [#&#8203;15383](https://github.com/pnpm/pnpm/issues/15383). When packages were mutated, it now lists only those packages and no longer suggests running `pnpm install --force` [#&#8203;919](https://github.com/pnpm/pnpm/issues/919).

- `pnpm peers check` and the `ERR_PNPM_PEER_DEP_ISSUES` error now group peer dependency issues under the workspace project they were found in [#&#8203;15351](https://github.com/pnpm/pnpm/issues/15351).

##### Output and messages

- The error for an incompatible pnpm-lock.yaml now reports the lockfileVersion the file was generated with and the lockfileVersion the current pnpm supports [#&#8203;848](https://github.com/pnpm/pnpm/issues/848).

- When the registry stops sending data for longer than `fetchTimeout`, pnpm now reports that the metadata or tarball request timed out. Previously the error did not mention the timeout [#&#8203;3646](https://github.com/pnpm/pnpm/issues/3646).

- Fatal peer dependency errors and their hints are now written to stderr [#&#8203;5419](https://github.com/pnpm/pnpm/issues/5419).

- `pnpm run` with `--loglevel` set to `warn`, `error`, or `silent` (or the same `loglevel` setting) no longer prints the `$ <command>` line before a script, nor the summary of the install that `verifyDepsBeforeRun` runs first. Both are info-level output [#&#8203;8944](https://github.com/pnpm/pnpm/issues/8944).

- `pnpm run` and `pnpm exec` now print `No projects matched the filters in "<workspace>"` when `--filter` selects no project [#&#8203;8408](https://github.com/pnpm/pnpm/issues/8408).

- `pnpm dedupe` now counts each package reused from the store once in its progress output [#&#8203;15303](https://github.com/pnpm/pnpm/issues/15303).

- `pnpm add` now warns when replacing an existing dependency with a specifier pointing to a different source [#&#8203;14869](https://github.com/pnpm/pnpm/issues/14869).

- `pnpm link` now warns when linking a package that declares one or more peer dependencies, explaining that the linked dependency will not resolve peer dependencies from the target `node_modules` and suggesting the `file:` protocol instead.

- `pnpm import` now warns when package.json lists projects in a "workspaces" array and there is no "pnpm-workspace.yaml". Without that file, the import writes a lockfile for the root project only [#&#8203;5240](https://github.com/pnpm/pnpm/issues/5240).

- Bash completion now completes script names that contain a colon, such as `pnpm run test:u` to `pnpm run test:unit` [#&#8203;5482](https://github.com/pnpm/pnpm/issues/5482).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

</details>

<details>
<summary>lovell/sharp (sharp)</summary>

### [`v0.35.5`](https://github.com/lovell/sharp/releases/tag/v0.35.5)

[Compare Source](https://github.com/lovell/sharp/compare/v0.35.4...v0.35.5)

<https://github.com/lovell/sharp-libvips/releases/tag/v1.3.4>

- Add upper bounds check on length of `linear` and GIF `delay` arrays.

- Improve error handing when WebAssembly fallback also fails.
  [#&#8203;4593](https://github.com/lovell/sharp/pull/4593)
  [@&#8203;lazerg](https://github.com/lazerg)

- TypeScript: Allow multi-frame options for JXL output.
  [#&#8203;4602](https://github.com/lovell/sharp/pull/4602)
  [@&#8203;ramin-010](https://github.com/ramin-010)

- TypeScript: Remove non-existent named export.
  [#&#8203;4604](https://github.com/lovell/sharp/issues/4604)

- Increase accepted dimensions when extending an image.
  [#&#8203;4605](https://github.com/lovell/sharp/issues/4605)

- Improve gain map support for `extract` and `rotate` operations.
  [#&#8203;4606](https://github.com/lovell/sharp/issues/4606)

- Tests: Ensure composite tests pass on big endian platforms.
  [#&#8203;4609](https://github.com/lovell/sharp/issues/4609)

</details>

<details>
<summary>HiDeoo/starlight-openapi (starlight-openapi)</summary>

### [`v0.26.3`](https://github.com/HiDeoo/starlight-openapi/blob/HEAD/packages/starlight-openapi/CHANGELOG.md#0263)

[Compare Source](https://github.com/HiDeoo/starlight-openapi/compare/[email protected]@0.26.3)

##### Patch Changes

- [#&#8203;181](https://github.com/HiDeoo/starlight-openapi/pull/181) [`e81882f`](https://github.com/HiDeoo/starlight-openapi/commit/e81882ffd4a85b1c113fd477422ee6b524715d58) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Fixes a potential spacing issue with heading anchor links.

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTEuMiIsInVwZGF0ZWRJblZlciI6IjQzLjE5MS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

<!-- cloudflare-preview --> Preview: https://pr-575.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/575
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Renovate Bot <[email protected]>
2026-10-06 08:31:06 +00:00
Gitea Bot 8c2625b846 Update the latest api documents (#576)
Automated update of static/swagger-latest.json and static/openapi3-latest.json from the gitea main branch, opened by the "update api spec files" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-576.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/576
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-10-05 10:59:51 +00:00
Gitea Bot 140915b303 Update the runner reference pages (#577)
Automated update of the generated runner reference pages from gitea/runner (main for the develop docs, the newest stable tag for every documented series), opened by the "update runner reference" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-577.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/577
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-10-05 10:59:31 +00:00
silverwind e4cad3c2e8 docs: update session providers and cookie names (#572)
Docs for https://github.com/go-gitea/gitea/pull/39504, which removed the `mysql`, `postgres`, `couchbase` and `memcache` session providers and renamed the default cookies to `gitea_session` and `gitea_remember`. The English cheat sheet copies the `app.example.ini` wording, the zh-cn and zh-tw cheat sheets get the same values.

Reviewed-on: https://gitea.com/gitea/docs/pulls/572
Reviewed-by: Lunny Xiao <[email protected]>
Reviewed-by: wxiaoguang <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-10-02 20:58:18 +00:00
silverwind 784c530a5b docs: mirror app.example.ini wording in the cache section (#571)
Docs for https://github.com/go-gitea/gitea/pull/39530, which changed the `INTERVAL` description in `app.example.ini`. The cheat sheet's `[cache]` and `[cache.last_commit]` descriptions now copy the ini wording, including its memcache example.

Reviewed-on: https://gitea.com/gitea/docs/pulls/571
Reviewed-by: wxiaoguang <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-10-02 17:11:53 +00:00
JWheelMakerandsilverwind 8d7c0701dc docs/installation/with-docker.md Update PostgreSQL volume path for PostgreSQL 18 (#570)
The Docker Compose example currently mounts the PostgreSQL data directory to `/var/lib/postgresql/data`.

With PostgreSQL 18, the official PostgreSQL Docker image expects the volume to be mounted at `/var/lib/postgresql` instead. Mounting `/var/lib/postgresql/data` causes the container to fail during startup because the image detects PostgreSQL data at an unused mount point.

This change updates the example to use the recommended `/var/lib/postgresql` mount path for PostgreSQL 18 and newer.

---------

Co-authored-by: silverwind <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/570
Reviewed-by: silverwind <[email protected]>
Co-authored-by: JWheelMaker <[email protected]>
2026-10-01 16:12:52 +00:00
silverwind 7219c0e44d docs: fix the REPOSITORY_AVATAR_FALLBACK_IMAGE default (#569)
The cheat sheet lists `/img/repo_default.png` as the `REPOSITORY_AVATAR_FALLBACK_IMAGE` default, but public files moved under `/assets` in Gitea 1.15 (https://github.com/go-gitea/gitea/pull/15219), so that path 404s. Like `app.example.ini` in https://github.com/go-gitea/gitea/pull/39514, document the default as empty, falling back to Gitea's builtin repository avatar, and note that a custom value is used as-is without the sub-path. Applies to all documented versions and translations.

_Written by Claude Code (Opus 5.5)._

Reviewed-on: https://gitea.com/gitea/docs/pulls/569
Reviewed-by: wxiaoguang <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-10-01 08:28:50 +00:00
silverwind 8d93c189ac Refresh the latest api documents weekly (#568)
Reduce the frequency of these annoying "Update the latest api documents" PRs to once per week.

Reviewed-on: https://gitea.com/gitea/docs/pulls/568
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-10-01 00:13:22 +00:00
Gitea Bot 9fa74b7323 Update the latest api documents (#567)
Automated update of static/swagger-latest.json and static/openapi3-latest.json from the gitea main branch, opened by the "update api spec files" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-567.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/567
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-10-01 00:09:30 +00:00
silverwind 4a0ef0480b docs: update the SSH_SERVER_HOST_KEYS default and generated keys (#566)
Since https://github.com/go-gitea/gitea/pull/33974 (1.27), `SSH_SERVER_HOST_KEYS` defaults to `ssh/gitea.rsa, ssh/gitea.ed25519, ssh/gitea.ecdsa, ssh/gogs.rsa`. When none of the keys exist, Gitea generates 4096 bit RSA, 256 bit ECDSA and ED25519 keys instead of RSA alone. This updates next, 28 and 1.27 in all three languages, matching https://github.com/go-gitea/gitea/pull/39500.

Written by Claude.

Reviewed-on: https://gitea.com/gitea/docs/pulls/566
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-09-30 23:34:35 +00:00
bircniandsilverwind 7d9a82df8d add artifact preview (#533)
Document Actions artifact previews added in https://github.com/go-gitea/gitea/pull/36754, including supported content, authentication and sandboxing behavior, navigation, and size limits.

Also document the `ARTIFACT_PREVIEW_MAX_SIZE` configuration option so administrators can limit or disable previews.

Co-authored-by: silverwind <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/533
Reviewed-by: Lunny Xiao <[email protected]>
Reviewed-by: silverwind <[email protected]>
Co-authored-by: bircni <[email protected]>
2026-09-30 11:43:03 +00:00
6934d56056 docs: document egress modes and host lists (#557)
Documents the settings changed by https://github.com/go-gitea/gitea/pull/39426: `EGRESS_MODE`, the `[migrations]` `ALLOWED_HOST_LIST`/`BLOCKED_HOST_LIST` replacing `ALLOWED_DOMAINS`, `BLOCKED_DOMAINS` and `ALLOW_LOCALNETWORKS`, and the host list syntax with ports, CGNAT and reserved addresses. Also fixes the `socks://` examples, Go only speaks `socks5://`.

_Written by Claude._

---------

Co-authored-by: Lunny Xiao <[email protected]>
Co-authored-by: TheFox0x7 <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/557
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-09-30 11:42:29 +00:00
Gitea BotandLunny Xiao c28356267d Update the runner reference pages (#563)
Automated update of the generated runner reference pages from gitea/runner (main for the develop docs, the newest stable tag for every documented series), opened by the "update runner reference" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-563.docs-gitea-com.pages.dev

---------

Co-authored-by: Lunny Xiao <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/563
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-09-30 11:31:16 +00:00
silverwind c3d9115328 docs: update defaults changed by the install page refactor (#565)
https://github.com/go-gitea/gitea/pull/39400 made registration disabled by default, switched the session provider default to `file`, and derives `SSH_DOMAIN` and `NO_REPLY_ADDRESS` from `ROOT_URL` now that `[server] DOMAIN` is gone. Updates the next and v28 cheat sheets with the wording from `app.example.ini` in https://github.com/go-gitea/gitea/pull/39456.

Reviewed-on: https://gitea.com/gitea/docs/pulls/565
Reviewed-by: wxiaoguang <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-09-30 11:30:26 +00:00
Lunny Xiao ba60edbbda Add version v28 (#564)
<!-- cloudflare-preview --> Preview: https://pr-564.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/564
Reviewed-by: bircni <[email protected]>
Co-authored-by: Lunny Xiao <[email protected]>
2026-09-30 06:40:44 +00:00
Renovate Bot deb2f068d4 chore(deps): update dependencies (#562)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@astrojs/starlight](https://starlight.astro.build) ([source](https://github.com/withastro/starlight/tree/HEAD/packages/starlight)) | [`0.42.1` → `0.42.4`](https://renovatebot.com/diffs/npm/@astrojs%2fstarlight/0.42.1/0.42.4) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@astrojs%2fstarlight/0.42.4?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@astrojs%2fstarlight/0.42.1/0.42.4?slim=true) |
| [astro](https://astro.build) ([source](https://github.com/withastro/astro/tree/HEAD/packages/astro)) | [`7.3.2` → `7.3.5`](https://renovatebot.com/diffs/npm/astro/7.3.2/7.3.5) | ![age](https://developer.mend.io/api/mc/badges/age/npm/astro/7.3.5?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/astro/7.3.2/7.3.5?slim=true) |
| [pnpm](https://github.com/pnpm/pnpm/tree/main/pnpm) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm)) | [`12.4.2` → `12.6.0`](https://renovatebot.com/diffs/npm/pnpm/12.4.2/12.6.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/pnpm/12.6.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/pnpm/12.4.2/12.6.0?slim=true) |

---

### Release Notes

<details>
<summary>withastro/starlight (@&#8203;astrojs/starlight)</summary>

### [`v0.42.4`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0424)

[Compare Source](https://github.com/withastro/starlight/compare/@astrojs/[email protected]...@astrojs/[email protected])

##### Patch Changes

- [#&#8203;4149](https://github.com/withastro/starlight/pull/4149) [`fa10e87`](https://github.com/withastro/starlight/commit/fa10e87376f49f76a693e8d2b292aef65b959e37) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Optimizes rendering of large nested sidebars

- [#&#8203;4215](https://github.com/withastro/starlight/pull/4215) [`f791de6`](https://github.com/withastro/starlight/commit/f791de65e6e14e9011586004d79f78fcef3a427e) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Adds a new `seti:coffee` icon for CoffeeScript files in the `<FileTree>` component.

- [#&#8203;4215](https://github.com/withastro/starlight/pull/4215) [`f791de6`](https://github.com/withastro/starlight/commit/f791de65e6e14e9011586004d79f78fcef3a427e) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Fixes `<FileTree>` icons for `.ejs` and `npm-debug.log` files displaying the default file icon.

### [`v0.42.3`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0423)

[Compare Source](https://github.com/withastro/starlight/compare/@astrojs/[email protected]...@astrojs/[email protected])

##### Patch Changes

- [#&#8203;4211](https://github.com/withastro/starlight/pull/4211) [`33e21ec`](https://github.com/withastro/starlight/commit/33e21ec86274070df1725580c1e8134e34062c15) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Fixes a potential page freeze due to table of contents highlighting when a [`<PageTitle>` component](https://starlight.astro.build/reference/overrides/#pagetitle) override does not render a heading with the required `id="_top"`.

### [`v0.42.2`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0422)

[Compare Source](https://github.com/withastro/starlight/compare/@astrojs/[email protected]...@astrojs/[email protected])

##### Patch Changes

- [#&#8203;4201](https://github.com/withastro/starlight/pull/4201) [`fafd989`](https://github.com/withastro/starlight/commit/fafd989453b024081e51c0c92db920a369c8153e) Thanks [@&#8203;linux-universe](https://github.com/linux-universe)! - Add 1 new icon: `fluxer`

</details>

<details>
<summary>withastro/astro (astro)</summary>

### [`v7.3.5`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#735)

[Compare Source](https://github.com/withastro/astro/compare/[email protected]@7.3.5)

##### Patch Changes

- [#&#8203;17736](https://github.com/withastro/astro/pull/17736) [`2b8b2e8`](https://github.com/withastro/astro/commit/2b8b2e80169da0ab19055166438291c46fcfe320) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Adds a new container function called `renderComponent()`, which renders Astro components with inlined styles and scripts.

  Users must import the component with the new `?container` query string:

  ```js
  import { experimental_AstroContainer } from "astro/container";
  import TodoList from "../components/TodoList.astro?container";

  const container = await experimental_AstroContainer.create();

  const _string = container.renderComponent(TodoList);
  ```

### [`v7.3.4`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#734)

[Compare Source](https://github.com/withastro/astro/compare/[email protected]@7.3.4)

##### Patch Changes

- [#&#8203;18063](https://github.com/withastro/astro/pull/18063) [`40896ac`](https://github.com/withastro/astro/commit/40896acb744988d9d3c2015e810c57de26390b3f) Thanks [@&#8203;adamchal](https://github.com/adamchal)! - Fixes incremental builds repeatedly rendering unchanged pages when modules or compiled CSS reference bundled assets.

- [#&#8203;18053](https://github.com/withastro/astro/pull/18053) [`cf5d72f`](https://github.com/withastro/astro/commit/cf5d72f286c3c1185b7d39692b8ca8e789c16e02) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Improves the `astro check` error shown for TypeScript 7. The command now explains that TypeScript 7 is not currently supported and provides instructions for experimentally type-checking Astro files with TypeScript 7.1 and `@astrojs/ts-content-mapper`.

- [#&#8203;18086](https://github.com/withastro/astro/pull/18086) [`795a7e4`](https://github.com/withastro/astro/commit/795a7e44640e7ad513a590a362dc0d3259de7771) Thanks [@&#8203;ump45nose](https://github.com/ump45nose)! - Fix double-escaped ampersands in Markdown image `alt` and `title` attributes. The `__ASTRO_IMAGE_` round-trip now decodes the numeric (`&#x26;`) and named (`&amp;`) character references the Markdown processors emit, so an `&` in an alt or title is escaped exactly once in the final HTML instead of twice.

- [#&#8203;18074](https://github.com/withastro/astro/pull/18074) [`0429805`](https://github.com/withastro/astro/commit/042980585a75a12f3b0d63377483af98ef80e0f9) Thanks [@&#8203;SurefireStudios](https://github.com/SurefireStudios)! - Fix three error names that did not match their documented reference. `MissingLocale`, `MissingIndexForInternationalization` and `NoManifestAvailable` reported names ending in `Error` in the dev overlay, while their error reference pages are published under the unsuffixed names, so the name shown to users could not be found in the docs.

- [#&#8203;18007](https://github.com/withastro/astro/pull/18007) [`2245837`](https://github.com/withastro/astro/commit/22458379f5f258ac1df225f5af644b98b1b8237b) Thanks [@&#8203;L4XB](https://github.com/L4XB)! - Fixes the dev server re-evaluating the whole server module graph on every request. The `astro:head-metadata` plugin invalidated its component metadata virtual module from its own `transform` hook, so each evaluation of that module scheduled the next one. Adapters that run requests outside Vite's module runner, such as `@astrojs/cloudflare`, paid for a full re-evaluation of the server graph on every request for the lifetime of the process.

- [#&#8203;18096](https://github.com/withastro/astro/pull/18096) [`43657c4`](https://github.com/withastro/astro/commit/43657c4612f04cecf6e34d288eed1811c6508b74) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes domain-based i18n routing to respect `security.allowedDomains` when selecting a locale from request host headers

- [#&#8203;18043](https://github.com/withastro/astro/pull/18043) [`8a53a8b`](https://github.com/withastro/astro/commit/8a53a8b70f964e7bc127d6991cc9aca21e87750f) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes `image.responsiveStyles` emitting invalid `object-position` CSS values for same-axis keyword pairs (`top bottom`, `left right`, etc.)

- [#&#8203;18029](https://github.com/withastro/astro/pull/18029) [`c08252d`](https://github.com/withastro/astro/commit/c08252d6803d4af2890b4bd1c69e4b4fc49e2d04) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Runs `astro dev` and `astro preview` in the foreground when an AI agent is detected on Windows, allowing the agent to manage the process lifetime. Pass `--background` explicitly to request an Astro-managed background process. Agent-inferred backgrounding remains enabled on other platforms.

- Updated dependencies \[[`3fd16ee`](https://github.com/withastro/astro/commit/3fd16eeb5cd096a6ceb8cc3e70b89ed30d6fcd4d), [`8358d59`](https://github.com/withastro/astro/commit/8358d59cba754480c7d830c473837a0d7100ac7e)]:
  - [@&#8203;astrojs/markdown-satteri](https://github.com/astrojs/markdown-satteri)@&#8203;0.4.2

### [`v7.3.3`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#733)

[Compare Source](https://github.com/withastro/astro/compare/[email protected]@7.3.3)

##### Patch Changes

- [#&#8203;17651](https://github.com/withastro/astro/pull/17651) [`504333c`](https://github.com/withastro/astro/commit/504333c2a0c7da3ca06e39ce37eee832192ba8ea) Thanks [@&#8203;sxzz](https://github.com/sxzz)! - Refactors internal version handling to use a smaller, ESM-native dependency

- [#&#8203;17942](https://github.com/withastro/astro/pull/17942) [`0bc5715`](https://github.com/withastro/astro/commit/0bc5715a2990a84598a064ac8de52c08fffd1186) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Returns appropriate 400 and 404 responses from the image endpoint for invalid and missing local image paths

- [#&#8203;17700](https://github.com/withastro/astro/pull/17700) [`b2222fc`](https://github.com/withastro/astro/commit/b2222fc3cdbeb1b75029a109d13d19515e5260ae) Thanks [@&#8203;winklemad](https://github.com/winklemad)! - Fixes `Astro.preferredLocaleList` returning an empty list when a locale is configured with the object form (`{ path, codes }`) and the browser sends the code with different casing or an underscore, such as `en-US` matching a configured `en-us`

- [#&#8203;17941](https://github.com/withastro/astro/pull/17941) [`394ff79`](https://github.com/withastro/astro/commit/394ff79954e5822e3884947a07d84fef0c72f31c) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes `astro preview --ignore-lock` (and `astro dev --ignore-lock`) being refused when run from an AI agent environment. The flag now starts the server in the foreground instead of erroring, since agent detection only inferred background mode and was never explicitly requested. An explicit `--background` combined with `--ignore-lock` still errors.

- [#&#8203;17928](https://github.com/withastro/astro/pull/17928) [`3277927`](https://github.com/withastro/astro/commit/327792719475cf5b0023dd8983cfd0dc58070888) Thanks [@&#8203;ArmandPhilippot](https://github.com/ArmandPhilippot)! - Fixes TypeScript autocompletion for `getImage()` to suggest all available predefined options.

- [#&#8203;17928](https://github.com/withastro/astro/pull/17928) [`3277927`](https://github.com/withastro/astro/commit/327792719475cf5b0023dd8983cfd0dc58070888) Thanks [@&#8203;ArmandPhilippot](https://github.com/ArmandPhilippot)! - Fixes a type error in `getImage()` options that allowed passing both `widths` and `densities` at the same time.

- [#&#8203;17857](https://github.com/withastro/astro/pull/17857) [`2637ed1`](https://github.com/withastro/astro/commit/2637ed10afd26f1f29db673616c6225eb0d848a6) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Improves rendering performance

- [#&#8203;17943](https://github.com/withastro/astro/pull/17943) [`2fc7ce9`](https://github.com/withastro/astro/commit/2fc7ce98f982f45533d91b6ba5b95fce4eb54e11) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes a WebAssembly error when importing `astro:actions` in tests that run under `@cloudflare/vitest-pool-workers`

- [#&#8203;18018](https://github.com/withastro/astro/pull/18018) [`1b5a234`](https://github.com/withastro/astro/commit/1b5a234f5205995ff3a1c7707c093e49f1f601e7) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes trailing-slash redirect response body pointing to the incoming URL instead of the redirect target. The `location` header was correct, but the HTML body (`<meta http-equiv="refresh">`, `<title>`, and `<a>` tag) contained the original request path without the trailing-slash correction or query string.

- [#&#8203;17905](https://github.com/withastro/astro/pull/17905) [`eaf70fa`](https://github.com/withastro/astro/commit/eaf70fa3f0b5d01237d9af8110649ded2e239b5d) Thanks [@&#8203;SudoDevStudio](https://github.com/SudoDevStudio)! - Fixes custom dev toolbar apps losing their UI after client-side navigation with `<ClientRouter />`.

- [#&#8203;18011](https://github.com/withastro/astro/pull/18011) [`558b301`](https://github.com/withastro/astro/commit/558b301a3626e5f0fd3ae4e3997e0c6de53e9867) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes prerendered Cloudflare pages rendering as `[object Object]` when `nodejs_compat` is enabled in `wrangler.toml`

- [#&#8203;17944](https://github.com/withastro/astro/pull/17944) [`ba08e35`](https://github.com/withastro/astro/commit/ba08e35fdc306219b69428e3e1c4e5816e116956) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes a regression in `astro dev` where writes outside the module graph (for example, `@astrojs/cloudflare`'s `.wrangler/state` files) invalidated the middleware on every request, causing repeated SSR reloads. Such writes no longer invalidate the middleware.

- [#&#8203;17531](https://github.com/withastro/astro/pull/17531) [`ae837db`](https://github.com/withastro/astro/commit/ae837dbc692ba831ab9cc5bfebef403d72980ca0) Thanks [@&#8203;danilloestrela](https://github.com/danilloestrela)! - Updates `svgo` to `4.0.2` to resolve a security advisory

- [#&#8203;17953](https://github.com/withastro/astro/pull/17953) [`dbbf10e`](https://github.com/withastro/astro/commit/dbbf10e606f64b4ba2b8362868b6a27a9a937eb8) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes a one-time page reload shortly after the first load on cold dev-server starts when a project has framework components imported from MDX content entries. MDX files are now included in the dev dependency pre-bundling scan, so their framework dependencies are bundled up front instead of being discovered (and reloaded for) at runtime.

- [#&#8203;17955](https://github.com/withastro/astro/pull/17955) [`4e8ad9a`](https://github.com/withastro/astro/commit/4e8ad9a40affe6ae0a6c268a2100a203415479b6) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Improves dev server startup time. The content config and dev server app module graphs now begin compiling during server creation without blocking the server from listening. Request handling waits for the shared setup result when needed, cutting `astro dev` ready time by roughly a third on projects with a content config.

- [#&#8203;17960](https://github.com/withastro/astro/pull/17960) [`9838049`](https://github.com/withastro/astro/commit/98380490cef49418770102829f160942051a65b9) Thanks [@&#8203;Chy-Zaber-Bin-Zahid](https://github.com/Chy-Zaber-Bin-Zahid)! - Improves the diagnostics of some Astro errors.

- [#&#8203;17998](https://github.com/withastro/astro/pull/17998) [`0e5478d`](https://github.com/withastro/astro/commit/0e5478dff592653f3fa72ad51a38915648e4e8ad) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes SVG `<style>` elements nested inside `<defs>` or other container elements not being hashed for CSP

- [#&#8203;17994](https://github.com/withastro/astro/pull/17994) [`80f9f1d`](https://github.com/withastro/astro/commit/80f9f1d5364329de9ee97ef48d42fefe8535d2ef) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes `experimental.incrementalBuild` restoring pages with stale CSS after a preprocessor partial changes or missing original images referenced by restored pages

- [#&#8203;17889](https://github.com/withastro/astro/pull/17889) [`8ae6b46`](https://github.com/withastro/astro/commit/8ae6b4669c6543212d8384f883c33a4d50451aaa) Thanks [@&#8203;ajfAfg](https://github.com/ajfAfg)! - Fixes a bug where the dev server stripped the configured `base` from URLs that only share a prefix with it. With `base: '/s'`, requests to `/src/...` were rewritten to `/rc/...` and failed, breaking those pages during development.

- [#&#8203;17980](https://github.com/withastro/astro/pull/17980) [`cfccafa`](https://github.com/withastro/astro/commit/cfccafa9506250832b659b6c5c62c85ce081aee5) Thanks [@&#8203;gameroman](https://github.com/gameroman)! - Improves JSDoc for fonts api

- [#&#8203;17953](https://github.com/withastro/astro/pull/17953) [`dbbf10e`](https://github.com/withastro/astro/commit/dbbf10e606f64b4ba2b8362868b6a27a9a937eb8) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes CSS HMR for framework components rendered through content entries after `ClientRouter` navigation

- [#&#8203;17970](https://github.com/withastro/astro/pull/17970) [`0b4dc3a`](https://github.com/withastro/astro/commit/0b4dc3a211910b589d3805ab4dfedf11b0e712bb) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Improves serialization of transition animation values in generated CSS

- [#&#8203;17999](https://github.com/withastro/astro/pull/17999) [`30ef3cb`](https://github.com/withastro/astro/commit/30ef3cbb76550d45ec59e6c3201544df859c997a) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes content collection HMR not updating prerendered pages when an adapter enables a separate prerender environment (e.g. `@astrojs/cloudflare` with `prerenderEnvironment: 'node'`)

- [#&#8203;18002](https://github.com/withastro/astro/pull/18002) [`312ab49`](https://github.com/withastro/astro/commit/312ab49544eed1220453ed86c40896f49e31c7a9) Thanks [@&#8203;shoutoutuoadi325](https://github.com/shoutoutuoadi325)! - Fixes redirect targets being corrupted when a dynamic route parameter value contains `$` replacement patterns like `$&`

- [#&#8203;17937](https://github.com/withastro/astro/pull/17937) [`e294953`](https://github.com/withastro/astro/commit/e294953aa8aadd98d5be92e60a03037b05dbdfd4) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes a bug where the `glob()` content loader kept stale entries in the data store after the last file in a collection was deleted. Empty collections are now pruned correctly, and the file watcher is registered in dev so the first file added to an empty collection is picked up without a restart.

- [#&#8203;17945](https://github.com/withastro/astro/pull/17945) [`750b4db`](https://github.com/withastro/astro/commit/750b4dbe238f2a2b979503ffd780f46d19174ccb) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Pre-bundles renderer server entrypoints and the default console logger during dev so they are included in the initial optimization pass, preventing a mid-request re-optimization that could crash the dev server on Cloudflare (workerd).

</details>

<details>
<summary>pnpm/pnpm (pnpm)</summary>

### [`v12.6.0`](https://github.com/pnpm/pnpm/releases/tag/v12.6.0): pnpm 12.6

[Compare Source](https://github.com/pnpm/pnpm/compare/[email protected])

pnpm 12.6.0 ships with automatic dependency deduplication, relocatable node\_modules, package.yaml manifest editing, and --save-types support.

##### Minor Changes

- `autoDedupe` deduplicates compatible dependency versions during installation [#&#8203;7258](https://github.com/pnpm/pnpm/issues/7258). Enable it in `pnpm-workspace.yaml` or use `pnpm install --auto-dedupe` or `pnpm add --auto-dedupe`. Frozen installs leave the lockfile unchanged.

- `pnpm install`, `pnpm run`, and `pnpm exec` on macOS and Linux now reuse a `node_modules` directory and bin shims that moved or were copied together with their project [#&#8203;6937](https://github.com/pnpm/pnpm/issues/6937). The first command after the move checks the tree and records its new location, so project commands in `node_modules/.bin` keep working.

- `pnpm add --save-types` saves available `@types/*` packages in `devDependencies` alongside registry dependencies [#&#8203;3868](https://github.com/pnpm/pnpm/issues/3868). Packages that declare bundled TypeScript types are skipped. Set `saveTypes: true` in `pnpm-workspace.yaml` to enable this by default.

- `package.yaml` manifests can now be updated by `pnpm add`, `pnpm update`, `pnpm remove`, `pnpm pkg`, `pnpm link`, `pnpm set-script`, and `pnpm version` [#&#8203;2008](https://github.com/pnpm/pnpm/issues/2008). Existing comments and key order are preserved.

- Catalog entries can now use the `file:` and `link:` protocols [#&#8203;8642](https://github.com/pnpm/pnpm/issues/8642). A relative path or bare path in an entry, such as `./tarballs/foo.tgz`, is measured from the directory holding `pnpm-workspace.yaml`.

- `pnpm tasks status` lists running and waiting tasks in each concurrency group, and waiting tasks now take available slots in arrival order with higher `priority` tasks going first [#&#8203;15208](https://github.com/pnpm/pnpm/issues/15208). If workspaces use different limits for the same group, a later task can take a free slot that earlier tasks cannot use. A package script named `tasks` takes precedence; use `pnpm pm tasks status` when that script exists.

- `pnpm cache prune` deletes registry metadata cache directories that this version of pnpm can no longer read [#&#8203;15046](https://github.com/pnpm/pnpm/issues/15046). `pnpm cache prune --dry-run` lists what it would delete without removing anything.

- `macosBackup.excludeModulesDir` and `macosBackup.excludeStoreDir` on macOS can now exclude newly created modules, virtual-store, and package-store directories from Time Machine [#&#8203;6440](https://github.com/pnpm/pnpm/issues/6440). Set either to `true` in global configuration or using the `PNPM_CONFIG_MACOS_BACKUP_EXCLUDE_MODULES_DIR` and `PNPM_CONFIG_MACOS_BACKUP_EXCLUDE_STORE_DIR` environment variables.

- `pnpm add --tilde` is now an alias for `--save-prefix=~` [#&#8203;12863](https://github.com/pnpm/pnpm/issues/12863). The Yarn `-T` shorthand is not supported.

- `progress` setting and `--no-progress` option now turn off dependency and download progress lines [#&#8203;14065](https://github.com/pnpm/pnpm/issues/14065). Warnings, lifecycle output, and the dependency summary are still printed.

##### Patch Changes

##### Security

- POSIX bin shims now take `cygpath` and `wslpath` from the system default path on Cygwin, MSYS2, and WSL2 so a dependency cannot redirect another package's shim [#&#8203;14866](https://github.com/pnpm/pnpm/issues/14866).

- `pnpm install` warnings no longer carry the text of a package's deprecation notice, naming only the deprecated package and version [#&#8203;15099](https://github.com/pnpm/pnpm/issues/15099). A deprecation warning names the newest non-deprecated version when one exists, and control characters and line separators are stripped from package identifiers and warnings.

- `pnpm install` and other commands that report configuration warnings now warn when environment variables in project `.npmrc` credentials are ignored [#&#8203;15051](https://github.com/pnpm/pnpm/issues/15051).

##### Installing packages

- `pnpm install --frozen-lockfile` now succeeds when an optional dependency was unresolvable and skipped by the install that wrote the lockfile [#&#8203;3960](https://github.com/pnpm/pnpm/issues/3960).

- `pnpm install --frozen-lockfile` no longer installs dependencies of projects removed from `pnpm-workspace.yaml` [#&#8203;15248](https://github.com/pnpm/pnpm/issues/15248). Missing local tarballs used only by those projects no longer fail the install.

- `pnpm ci` now empties `node_modules` before installing in a project that declares a `clean` script [#&#8203;15276](https://github.com/pnpm/pnpm/issues/15276).

- `pnpm install --force` now re-imports every package into the virtual store [#&#8203;15030](https://github.com/pnpm/pnpm/issues/15030) and removes obsolete dependency links inside virtual-store packages when their dependencies change [#&#8203;15039](https://github.com/pnpm/pnpm/issues/15039).

- `preinstall` script for the root project now runs before dependencies are resolved and linked [#&#8203;3760](https://github.com/pnpm/pnpm/issues/3760).

- `pnpm install` now runs `pnpm:devPreinstall` when the root project uses `package.yaml` [#&#8203;15168](https://github.com/pnpm/pnpm/issues/15168).

- `pnpm install` now enforces the root project's `engines.node` range when `engineStrict` is enabled [#&#8203;3016](https://github.com/pnpm/pnpm/issues/3016).

- `pnpm install` now uses the running Node.js when `devEngines.runtime` declares a range without `onFail: download` [#&#8203;15230](https://github.com/pnpm/pnpm/issues/15230).

- `pnpm install` no longer hangs when a git dependency is fetched over SSH and ssh prompts for a passphrase or host key confirmation, running ssh in batch mode instead [#&#8203;2227](https://github.com/pnpm/pnpm/issues/2227).

- `pnpm install` now installs git-hosted dependencies without preparing them when their builds are explicitly denied by `allowBuilds` [#&#8203;10522](https://github.com/pnpm/pnpm/issues/10522).

- `pnpm install` now reuses an in-flight tarball download when another resolution of the same archive still needs its `package.json` [#&#8203;15037](https://github.com/pnpm/pnpm/issues/15037).

- `pnpm install --prod` no longer downloads registry packages that only a devDependency reaches [#&#8203;881](https://github.com/pnpm/pnpm/issues/881).

- `pnpm install --no-runtime --frozen-lockfile` with `nodeLinker: hoisted` no longer fails on repeated runs with a broken lockfile [#&#8203;15212](https://github.com/pnpm/pnpm/issues/15212).

##### Resolving and linking dependencies

- `pnpm install` and `pnpm update` now resolve a dependency range to the newest matching version that is not deprecated [#&#8203;15128](https://github.com/pnpm/pnpm/issues/15128).

- `pnpm add <pkg>` without a version now uses the catalog entry when the workspace already catalogs that package [#&#8203;14865](https://github.com/pnpm/pnpm/issues/14865).

- `pnpm install` now links workspace dependencies declared with plain version ranges when `excludeLinksFromLockfile` and `linkWorkspacePackages` are enabled [#&#8203;15133](https://github.com/pnpm/pnpm/issues/15133).

- `pnpm install` now resolves local tarball dependencies whose absolute `file:` paths contain `..` consistently and skips reinstallation on repeat installs [#&#8203;15190](https://github.com/pnpm/pnpm/issues/15190).

- `pnpm install` now installs dependencies when a custom resolver returns a local or git-hosted tarball without a manifest [#&#8203;15016](https://github.com/pnpm/pnpm/issues/15016).

- `pnpm.overrides` entries written as a bare path, such as `./local-dep`, are now measured from the directory holding `pnpm-workspace.yaml` [#&#8203;11131](https://github.com/pnpm/pnpm/issues/11131).

- `pnpm update --no-save` no longer bypasses version-scoped overrides when a dependency selector specifies a version [#&#8203;14923](https://github.com/pnpm/pnpm/issues/14923).

- `pnpm peers check` and strict peer dependency checks no longer reject compatible versions from named registries [#&#8203;15225](https://github.com/pnpm/pnpm/issues/15225).

- `pnpm outdated` and `pnpm update --interactive --latest` now include named-registry dependencies such as `work:2.1.0` and preserve their registry prefix [#&#8203;15226](https://github.com/pnpm/pnpm/issues/15226).

- Workspace projects selected by `hoistPattern` or `publicHoistPattern` are now hoisted on every install [#&#8203;3642](https://github.com/pnpm/pnpm/issues/3642).

- Workspace packages with SemVer build metadata are no longer skipped when they match the requested range and have the same version precedence as the registry package [#&#8203;2812](https://github.com/pnpm/pnpm/issues/2812).

- Sped up `pnpm dedupe` and `pnpm install` in projects with many convergence overrides by checking overrides concurrently [#&#8203;15175](https://github.com/pnpm/pnpm/issues/15175).

- `minimumReleaseAge` is no longer skipped for packages served by registries returning matching ETags for abbreviated and full package metadata [#&#8203;14925](https://github.com/pnpm/pnpm/issues/14925).

##### Running scripts and tasks

- `pnpm run` signal handling no longer delivers a redundant second `SIGINT` to child scripts on `Ctrl+C` in a terminal, and properly forwards termination signals when running non-interactively without a terminal [#&#8203;7374](https://github.com/pnpm/pnpm/issues/7374).

- `pnpm run` and `pnpm exec` in workspaces with `sharedWorkspaceLockfile: false` now verify dependencies in the selected projects rather than expecting a root workspace state [#&#8203;15272](https://github.com/pnpm/pnpm/issues/15272).

- `pnpm test` now forwards `--filter` arguments to the test script when the option follows the shortcut [#&#8203;15217](https://github.com/pnpm/pnpm/issues/15217).

- Recursive runs now start scripts matched by a `/pattern/` selector in parallel within `workspaceConcurrency` [#&#8203;14933](https://github.com/pnpm/pnpm/issues/14933).

- `pnpm deploy`, `pnpm rebuild`, `pnpm rb`, and `pnpm setup` now prefer a `package.json` script of the same name [#&#8203;14976](https://github.com/pnpm/pnpm/issues/14976).

- `modulesDir` custom directory names now support executable lookup and CommonJS plugin resolution across `pnpm run`, `pnpm exec`, `pnpm version` hooks, and lifecycle scripts [#&#8203;3604](https://github.com/pnpm/pnpm/issues/3604).

- `pnpm install-test` now accepts `--no-bail` directly and in recursive runs [#&#8203;3777](https://github.com/pnpm/pnpm/issues/3777).

##### Workspace and project configuration

- `pnpm` commands run in a project not included in the workspace now act on that project alone [#&#8203;3561](https://github.com/pnpm/pnpm/issues/3561).

- `pnpm-workspace.yaml` edits now preserve scalar YAML anchors and aliases [#&#8203;8245](https://github.com/pnpm/pnpm/issues/8245).

- `pnpm-workspace.yaml` now expands environment variable placeholders with fallback syntax in enum-valued settings such as `nodeLinker` [#&#8203;14914](https://github.com/pnpm/pnpm/issues/14914).

- `pnpmfile` configuration now loads a `.js` file as CommonJS or an ES module, following the nearest `package.json` [#&#8203;15141](https://github.com/pnpm/pnpm/issues/15141).

- `updateConfig` hook settings are now honored by `pnpm peers check`, `why`, `list`, `ll`, `licenses`, `audit`, `sbom`, `fetch`, `patch`, `patch-commit`, `patch-remove`, `approve-builds`, and `runtime` [#&#8203;15047](https://github.com/pnpm/pnpm/issues/15047), [#&#8203;15049](https://github.com/pnpm/pnpm/issues/15049).

- `readPackage` hook changes or removal now take added dependencies out of `pnpm-lock.yaml` and update dependencies when an existing lockfile is present [#&#8203;3735](https://github.com/pnpm/pnpm/issues/3735), [#&#8203;15136](https://github.com/pnpm/pnpm/issues/15136).

- `package.yaml` projects now record their pinned pnpm under `packageManagerDependencies` in `pnpm-lock.yaml` [#&#8203;15167](https://github.com/pnpm/pnpm/issues/15167).

- `packageManagerDependencies` pinning `@pnpm/exe` beside `pnpm` is no longer rewritten in `pnpm-lock.yaml` [#&#8203;14926](https://github.com/pnpm/pnpm/issues/14926).

- `pnpm` now preserves CRLF line endings when modifying project manifests [#&#8203;3529](https://github.com/pnpm/pnpm/issues/3529).

- `loglevel` setting is now honored when configured in `pnpm-workspace.yaml`, global configuration, or `PNPM_CONFIG_LOGLEVEL` [#&#8203;3122](https://github.com/pnpm/pnpm/issues/3122).

- `storeDir` values loaded from global configuration or `PNPM_CONFIG_STORE_DIR` now expand a leading `~/` to the user's home directory [#&#8203;6560](https://github.com/pnpm/pnpm/issues/6560).

- `--shared-workspace-lockfile` now produces a warning when passed on the command line outside a workspace [#&#8203;1617](https://github.com/pnpm/pnpm/issues/1617).

##### Windows

- `pnpm install` on Windows now runs dependency build scripts from long global virtual store paths and normalizes scoped package paths in lifecycle script `PATH` entries [#&#8203;15111](https://github.com/pnpm/pnpm/issues/15111).

- `pnpm install` across projects sharing a global virtual store on Windows no longer fails with `Access is denied`, file-exists errors, or transient sharing violations [#&#8203;15114](https://github.com/pnpm/pnpm/issues/15114), [#&#8203;15176](https://github.com/pnpm/pnpm/issues/15176), [#&#8203;15171](https://github.com/pnpm/pnpm/issues/15171).

- `pn`, `pnpx`, `pnx`, and `pnpm` now run when Git Bash, MSYS2, or Cygwin launches them through a Windows path [#&#8203;14884](https://github.com/pnpm/pnpm/issues/14884).

- `pnpm dlx` now reuses cached packages when Windows creates directory junctions for its cache links [#&#8203;15171](https://github.com/pnpm/pnpm/issues/15171).

- `pnpm pipeline --watch` now resolves Windows short paths so multiple path representations share the build cache [#&#8203;15105](https://github.com/pnpm/pnpm/issues/15105).

##### CLI commands and output

- `pnpm remove` now runs the project's own `preuninstall`, `uninstall`, and `postuninstall` scripts [#&#8203;3276](https://github.com/pnpm/pnpm/issues/3276).

- `pnpm remove -r` now fails before modifying manifests if any requested dependency is absent from all selected projects [#&#8203;2319](https://github.com/pnpm/pnpm/issues/2319).

- `pnpm update --peer` now updates ranges in `peerDependencies` [#&#8203;8081](https://github.com/pnpm/pnpm/issues/8081).

- `pnpm update` now moves `devEngines.runtime` and `engines.runtime` version ranges to the resolved Node.js version [#&#8203;14988](https://github.com/pnpm/pnpm/issues/14988).

- `pnpm update -g` no longer reinstalls unchanged packages [#&#8203;12002](https://github.com/pnpm/pnpm/issues/12002).

- `pnpm add -g`, `pnpm update -g`, and `pnpm remove -g` now recover a global package group whose `node_modules` directory was deleted [#&#8203;15093](https://github.com/pnpm/pnpm/issues/15093).

- `pnpm add -g` now installs local tarballs when `PNPM_HOME` contains `..` path segments [#&#8203;15118](https://github.com/pnpm/pnpm/issues/15118).

- `pnpm version` now reads `tagVersionPrefix` from `pnpm-workspace.yaml`, global config, or `PNPM_CONFIG_TAG_VERSION_PREFIX` when creating and reading Git tags [#&#8203;15044](https://github.com/pnpm/pnpm/issues/15044).

- `pnpm publish` now allows a detached Git HEAD in CI environments [#&#8203;5894](https://github.com/pnpm/pnpm/issues/5894).

- `pnpm store prune` now removes unreferenced files and packages from the content-addressable store [#&#8203;3635](https://github.com/pnpm/pnpm/issues/3635), as well as expired or superseded `pnpm dlx` cache data [#&#8203;15171](https://github.com/pnpm/pnpm/issues/15171).

- `pnpm cache list-registries` now prints decoded registry URLs [#&#8203;15046](https://github.com/pnpm/pnpm/issues/15046).

- `pnpm deploy` no longer triggers an install when running scripts in a read-only deployed filesystem [#&#8203;11617](https://github.com/pnpm/pnpm/issues/11617).

- `pnpm -r list --json` now outputs a single JSON array when `sharedWorkspaceLockfile` is `false`, and `--long` and `--parseable` read each project's own modules directory [#&#8203;15011](https://github.com/pnpm/pnpm/issues/15011).

- `pnpm sbom` now validates SPDX identifiers and expressions before emitting them as CycloneDX license IDs or expressions, falling back to a license name for non-SPDX values such as `UNLICENSED` [#&#8203;14786](https://github.com/pnpm/pnpm/issues/14786).

- `pnpm change check` now validates pending change intents in `.changeset/` [#&#8203;15183](https://github.com/pnpm/pnpm/issues/15183).

- `pnpm --filter` and `pnpm -F` shell completion now suggests workspace package names [#&#8203;15216](https://github.com/pnpm/pnpm/issues/15216). Completion candidates containing control or invisible formatting characters are omitted so package and script names cannot inject terminal escape sequences.

- `pnpm run` and `pnpm run-script` shell completion now suggests package scripts [#&#8203;15034](https://github.com/pnpm/pnpm/issues/15034).

- `pnpm --version` no longer creates a temporary file in the project directory during store detection [#&#8203;15264](https://github.com/pnpm/pnpm/issues/15264).

- `pnpm setup` now describes displayed configuration changes as "The following configuration changes were made" [#&#8203;15100](https://github.com/pnpm/pnpm/issues/15100).

- `minimumReleaseAge` approval prompts in `pnpm install` and `pnpm update -g` now count and display each package version once [#&#8203;15083](https://github.com/pnpm/pnpm/issues/15083), [#&#8203;15091](https://github.com/pnpm/pnpm/issues/15091).

- `.npmrc` authentication warnings now report when an empty environment variable removes an auth token and name the affected key [#&#8203;4806](https://github.com/pnpm/pnpm/issues/4806).

- The install summary now names the version each dependency resolved to when `node-linker` is `hoisted` [#&#8203;15161](https://github.com/pnpm/pnpm/issues/15161).

- `pnpm install` now re-links a package's global virtual store slot after `allowBuilds` changes [#&#8203;15117](https://github.com/pnpm/pnpm/issues/15117).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.5.1`](https://github.com/pnpm/pnpm/releases/tag/v12.5.1): pnpm 12.5.1

[Compare Source](https://github.com/pnpm/pnpm/compare/[email protected])

#### Patch Changes

- pnpm now reports an unknown task setting in `pnpm-workspace.yaml` and carries on. It used to refuse to start, so a project could not use a task setting that only the pnpm version its `packageManager` pins reads. The setting is still an error when the running pnpm is that pinned version.

- Python interpreter installation now retries historical release metadata requests. It caches the release list for up to 24 hours and refreshes it once after a lookup miss. When a release omits the current platform, the search samples at most eight other releases before reporting that the lookup is inconclusive.

- Python `registries` entries now route packages by exact names or trailing-prefix patterns in `packages`. Registry declaration order no longer affects resolution. A matched package resolves exclusively from its assigned registry, including transitive and build dependencies. Use `packages: ["*"]` to declare the default index.

- `pnpm install` no longer fails with "Too many levels of symbolic links" when a Cargo configuration file above the workspace is a symlink, such as a `~/.cargo/config.toml` linked from a dotfiles repository.

- `pnpm install` now returns "Already up to date" in a workspace where `dedupeDirectDeps` left a project without a `node_modules` directory of its own. Such a project forced a full install on every run.

- `pnpm install` no longer refuses the repeat-install fast path just because a changed `pnpm-lock.yaml` is 16 MiB or larger. Such a lockfile forced a full install on the run after every change.

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.5.0`](https://github.com/pnpm/pnpm/releases/tag/v12.5.0): pnpm 12.5

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.4.2...v12.5.0)

pnpm 12.5.0 makes Python a first-class ecosystem, accepts Package URLs in `pnpm add`, names whole platforms in `supportedArchitectures`, and gives tasks machine-wide concurrency limits. It also fixes an install that could reuse one package's downloaded tarball for another.

#### Minor Changes

##### Installing packages

- `pnpm add` accepts a [Package URL](https://github.com/package-url/purl-spec) in place of a package name. `pnpm add pkg:npm/[email protected].2` saves `express` to `package.json`. `pnpm add pkg:cargo/[email protected]` saves `serde` to `Cargo.toml`. `pnpm add pkg:pypi/[email protected].0` saves `requests` to `pyproject.toml`. `pkg` is now a reserved specifier prefix, whatever case it is written in, so a named registry can no longer be called `pkg`.

- A `registries` entry can now name the ecosystem it serves.

  ```yaml
  registries:
    https://internal.example/simple/:
      ecosystem: pypi
    https://pypi.org/simple/:
      ecosystem: pypi
    https://index.crates.io/:
      ecosystem: cargo
  ```

  `ecosystem` accepts `npm`, `cargo` and `pypi`. An entry that does not name one serves npm, as every entry did before.

  An ecosystem with several indexes searches them in the order they are declared. The first index that has a package supplies it, so the one declared last answers what none before it had.

  A `registries` entry may not carry credentials. pnpm reads them from `.npmrc`, matched by origin, for a PyPI index as for every other package source.

##### Configuring pnpm

- `supportedArchitectures` now accepts a list of platforms, in place of the `os`, `cpu` and `libc` axes.

  ```yaml
  supportedArchitectures:
    - linux-x64
    - darwin-arm64
    - win32-x64
  ```

  An install prepares for the platforms the list names, and for those only. A platform reads as `<os>-<cpu>`, with a C library on Linux, as in `linux-x64-musl` or `linux-x64-manylinux_2_28`. The Rust target triple of the same machine is accepted too, so `x86_64-unknown-linux-gnu` names the platform `linux-x64` names. A Linux platform that names no C library is the glibc platform. `current` is the platform the install runs on.

  The `os`, `cpu` and `libc` mapping keeps working and keeps its meaning.

- Added concurrency groups for tasks. A task in `pnpm-workspace.yaml` can name a `concurrencyGroup`. The new `concurrencyGroups` setting gives each group a limit. At most that many tasks of the group run at once on the machine, counted across every pnpm process, `pnpm pipeline` included. A task past the limit waits for a running one to finish. A script that calls `pnpm run` for a task of the same group runs under the slot its parent holds.

  ```yaml
  tasks:
    test:rust:
      concurrencyGroup: cargo
  concurrencyGroups:
    cargo: 2
  ```

- `tools` names the programs pnpm downloads, and `mirror` says where each one comes from.

  ```yaml
  tools:
    node:
      mirror: https://mirror.example.com/node/download
      channels:
        nightly: https://nightly.example.com/
    bun:
      mirror: https://mirror.example.com/bun
    python:
      mirror: https://mirror.example.com/python-build-standalone/releases
  ```

  `node`, `bun` and `python` can be named. Any other tool is refused.

  `mirror` is the base a tool's own layout hangs off.

  `channels` sends one release channel elsewhere. A channel neither it nor `node-mirror:<channel>` names is left to `mirror`. Only `node` publishes channels, so naming them for another tool is refused.

  Set it in the global `config.yaml` or in `PNPM_CONFIG_TOOLS`. A `pnpm-workspace.yaml` that names a tool mirror is ignored.

  `pnpm pack-app` downloads the Node.js it embeds through `tools.node`. `node-mirror:<channel>` keeps working and names the same thing as an entry under `channels`.

##### Python interpreters and environments

- `pnpm install` now chooses a Python interpreter for each project instead of installing every project with one interpreter [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945). A project is installed with the first interpreter on the machine that its `requires-python` accepts, so a workspace can hold projects that support different Python versions. pnpm reads `.python-version` too, and prefers the version it asks for. Set `python.executable` in `pnpm-workspace.yaml` to name one interpreter for every project.

- `pnpm install` now installs a Python interpreter when no interpreter on the machine fits the project [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945). The builds are [python-build-standalone](https://github.com/astral-sh/python-build-standalone)'s, which uv and rye install too. One interpreter is shared by every project on the machine, and a later install uses it without downloading anything. `runtimeOnFail` decides what an install with no interpreter that fits does, the way it does for a Node.js runtime. `error` reports the project instead of installing one. `warn` and `ignore` install with an interpreter the machine has that the project's `requires-python` rejects. `tools.python.mirror` names a mirror.

- Python environments now live in the store. Each project keeps only its `.venv` link, which points at the project's current environment generation under `python-envs` in the store. A repository with many Python projects no longer holds a `.pnpm/python-envs` directory in each of them. The next install relinks a `.venv` that an earlier release published. The old `.pnpm/python-envs` directory is left in place, since a running program may still use it, and can be deleted once none does. With `frozenStore` set, pnpm writes nothing to the store, so environments stay in the project's `.pnpm/python-envs` [#&#8203;15014](https://github.com/pnpm/pnpm/issues/15014).

- Python environments now use `packageImportMethod` to import wheel files from the store. Use `clone-or-copy` for copy-on-write clones with a copy fallback, or `copy` for independent files. Hardlinked files share writes with the store and other environments.

  Isolated Python build environments keep backend writes private with copy-on-write clones or copies.

##### Python projects and workspaces

- `pnpm install` now installs a Python project's own package, so the project can be imported and the commands in `[project.scripts]` run right after an install [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945). The installed package points at the source tree, so an edit to a module takes effect without another install. pnpm installs the package of a project that declares a `[build-system]`. `tool.uv.package` overrides that either way.

- `pnpm install` now installs a Python project in the workspace from its own source. Declare it under `[tool.uv.sources]`, as `shared = { workspace = true }` or `shared = { path = "../shared", editable = true }`. pnpm builds the project with the backend it declares. It installs the build editable, so an edit to the project takes effect without another install.

  Approve the build backend under `allowBuilds` in `pnpm-workspace.yaml` as a Package URL, as `pkg:pypi/hatchling: true`. An install that has not approved a backend does not build the projects that need it. The message names the key to add.

  `pnpm install` now refuses a requirement that names a project in the workspace when nothing declares where it comes from. It used to take that name from the index.

- The members of a uv workspace can now share one Python environment. Set `shared-environment = true` under `[tool.pnpm.python]` in the `pyproject.toml` that declares `[tool.uv.workspace]`. `pnpm install` then resolves every member as one graph into one `pylock.toml` and one `.venv` at the workspace root. Two members that require versions of a distribution no release satisfies at once are refused with an error naming both. Each project still gets an environment of its own by default [#&#8203;15015](https://github.com/pnpm/pnpm/issues/15015).

- Python projects can now select extras and dependency groups through `[tool.pnpm.python]` in `pyproject.toml` [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945). Workspace `python.extras` and `python.groups` defaults now skip names a project does not define.

- `pnpm install` now reads dynamic Python project metadata from the build backend [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945). Projects with only a `requirements.txt` file now get a Python environment and lockfile.

##### Python dependencies and lockfiles

- pnpm can now resolve `pylock.toml` for several platforms and Python versions at once. `supportedArchitectures` names the platforms to lock for and `python.versions` the versions. Every platform is paired with every version. One committed lockfile then serves Linux CI and macOS or Windows contributors [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945).

  ```yaml
  supportedArchitectures:
    - linux-x64-manylinux_2_28
    - darwin-arm64
    - win32-x64
  python:
    enabled: true
    versions: ['3.12', '3.13']
  ```

  The lockfile pins the wheel each environment takes for a distribution. It marks a package only some environments install. `pnpm install` takes the packages and wheels of the environment its interpreter matches, and refuses an interpreter none of them stand for. pnpm resolves a project that declares environments itself, not through the server `pnprServer` names. Naming neither setting locks for the interpreter running the install.

- `python.overrides` and `python.constraints` pin the versions a Python resolution may pick [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945). pnpm reads uv's own overrides and constraints from `pyproject.toml` too.

- `pnpm install` now supports Python dependencies from Git repositories [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945). Direct wheel URLs are also supported. Sources can be declared in `[tool.uv.sources]`. Git dependencies require `allowBuilds` approval.

- `pnpm install` can install a Python release that publishes no wheel this interpreter accepts, by building the source distribution the index serves beside it [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945). The archive is pinned in `pylock.toml` by name and SHA-256. A later install replays it from the store, offline included. Building a source distribution runs the release's own build backend. Approve it with `pkg:pypi/<distribution>: true` under `allowBuilds`.

  A resolution that finds no version of a distribution now says why. It tells apart a distribution no index publishes, one whose releases publish nothing this interpreter can install, and one whose versions the project's requirements exclude.

#### Patch Changes

##### Installing packages

- pnpm no longer reuses one package's downloaded tarball for another package whose resolution pins a different integrity hash to the same URL [#&#8203;15021](https://github.com/pnpm/pnpm/issues/15021).

- `pnpm install` and `pnpm add` now report an error when `package.json`, `pnpm-lock.yaml`, `pyproject.toml` or another file they snapshot before installing is a named pipe or a device. The command used to wait forever for something to write to it.

- `pnpm install --prod` and `pnpm install --dev` now record every dependency group in `pnpm-lock.yaml`. `node_modules` still holds only the groups the filter selects. They used to write the filter into the lockfile, so a later `pnpm install --frozen-lockfile` rejected it. `pnpm prune --prod`, `pnpm prune --dev`, and `pnpm prune --no-optional` behave the same way [#&#8203;14912](https://github.com/pnpm/pnpm/issues/14912).

- POSIX bin shims now convert a Windows-form path such as `C:\node_modules\.bin\tsc` correctly. The shim mangled the backslashes in such a path and could not reach the package it runs. Installing again replaces the shims already in `node_modules` [#&#8203;14867](https://github.com/pnpm/pnpm/issues/14867).

- Two pnpm processes installing one workspace at the same time no longer fail on Windows with "Access is denied" while writing `node_modules/.pnpm-workspace-state-v1.json`. The write now retries the transient lock the other process holds, as pnpm's other file writes do.

- pnpm now reads the manifest from the tarball when a pnpmfile `resolvers` hook returns a resolution without one. Such a package installed alone, with none of its own dependencies and no warning [#&#8203;15000](https://github.com/pnpm/pnpm/issues/15000).

- `pnpm install` now merges Git conflict markers in `pnpm-lock.yaml`. It parses both sides of the conflict and keeps the versions they locked. A conflict in the config dependencies recorded at the top of the lockfile is merged too [#&#8203;14880](https://github.com/pnpm/pnpm/issues/14880).

##### Cargo projects

- `pnpm install` can now generate `Cargo.lock` for workspaces with path or Git `[patch]` and `[replace]` overrides. Adding, removing, and updating crates also preserve these overrides [#&#8203;14950](https://github.com/pnpm/pnpm/issues/14950).

  Cargo lockfile resolution blocks unsupported Git transport helpers declared by transitive dependencies.

- `pnpm install` now vendors recursive Git submodules for Cargo dependencies at their pinned commits. Cargo builds can use these sources offline. Set Git's `protocol.file.allow` to `always` to fetch local file submodules. pnpm fetches cached Git crates again on the first online install [#&#8203;14951](https://github.com/pnpm/pnpm/issues/14951).

- `pnpm install` now generates `Cargo.lock` for workspaces with Git dependencies, including a dependency that omits a package version. It also downloads the Rust standard library's dependencies when Cargo configuration enables `build-std` [#&#8203;14944](https://github.com/pnpm/pnpm/issues/14944).

- `pnpm install` now handles weak Cargo features, written `crate?/feature`. Resolution failed when one dependency turned on an optional crate and another asked for a weak feature of it [#&#8203;14960](https://github.com/pnpm/pnpm/issues/14960). The generated `Cargo.lock` now also includes the dependencies weak features reference, which Cargo rejected with `--locked` for crates such as `uuid` [#&#8203;14978](https://github.com/pnpm/pnpm/issues/14978).

- `pnpm install` now generates `Cargo.lock` when a crate version it considers depends on a release the registry carries only as yanked. pnpm rules that version out and resolves the rest of the graph. Resolution failed with an error such as `no non-yanked version of napi-build satisfies ^3.0.0-beta` [#&#8203;14952](https://github.com/pnpm/pnpm/issues/14952).

- `pnpm install` now falls back to an older semver-incompatible version of a crate when the newest one a dependency range allows cannot be resolved. Ranges such as `>=1, <3` span several of them [#&#8203;14962](https://github.com/pnpm/pnpm/issues/14962).

##### Python projects

- `pnpm install` now honors uv workspace members when discovering Python projects. When no uv workspace declares a project, pnpm skips projects under conventional example, demo, documentation, template, `test`, `tests`, and test fixture directories [#&#8203;15058](https://github.com/pnpm/pnpm/issues/15058).

- `pnpm install --filter <selector>` now installs only the Python projects the selection asks for. A Python project that shares a directory with an npm workspace project is selected with that project. A Python project in a directory of its own is selected by the distribution it declares, by its path, or through the `[tool.uv.sources]` entries that reach it. Under `--fail-if-no-match`, a selector that names only a Python project is a match. `pnpm add --filter <selector> pypi:<package>` writes the requirement to every selected project [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945).

- `pnpm install` now installs wheels whose `RECORD` hashes disagree with their contents. The wheel archive's locked SHA-256 hash remains verified. pnpm writes correct hashes to the installed `RECORD` [#&#8203;15061](https://github.com/pnpm/pnpm/issues/15061).

- `pnpm install` now installs a Python wheel whose `WHEEL` file lists tags that differ from the ones in its filename. A wheel whose filename tags were changed after the build, such as `mysql-connector-python`, was rejected [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945).

- A Python release whose wheel metadata declares a requirement pnpm cannot read no longer fails the install. pnpm now resolves the project against the other releases of that package, and reports the unreadable requirement when none of them works.

- `pnpm install` no longer fails when a Python index lists a file pnpm cannot use, such as a release with no SHA-256 digest or an unreadable wheel filename. That file is left out and the project resolves against the remaining releases.

- `pnpm add pypi:<package>` in a directory that has no `pyproject.toml` now names the missing file and says where to run the command. It used to fail with a bare `No such file or directory (os error 2)` [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945).

##### Performance

- `pnpm audit` no longer hangs on dependency graphs with many shared dependencies [#&#8203;15005](https://github.com/pnpm/pnpm/issues/15005).

- Sped up `pnpm install` in Python workspaces with many projects. Projects now prepare concurrently. Projects with identical registry requirements also share fresh dependency resolutions [#&#8203;14945](https://github.com/pnpm/pnpm/issues/14945).

- Repeat installs through the Node-API bindings now return "Already up to date" when the project manifests still match `pnpm-lock.yaml`. Before, every such install reinstalled the whole tree. An install also no longer reinstalls when `pnpm-lock.yaml` differs from the installed dependencies only by packages no project depends on or by top-level keys pnpm does not define.

##### Other commands

- `pnpm deploy` now links commands exposed by workspace dependencies into the deployed project's `node_modules/.bin` directory [#&#8203;14899](https://github.com/pnpm/pnpm/issues/14899).

- `pnpm dlx` and `pnx` now prompt to approve dependency build scripts in interactive terminals [#&#8203;14943](https://github.com/pnpm/pnpm/issues/14943). Cached packages with pending builds also prompt for approval. Without an interactive terminal, use `--allow-build` to allow the required builds.

- `pnpm add -g` and `pnpm update -g` now ignore incomplete unrelated global package groups when every command from the replaced group is retained. Operations that could remove a global command still require complete ownership information.

- `pnpm pack` now writes tarball entries grouped by file extension and file name, the order npm uses. Packages that ship many same-named files, such as template collections, pack much smaller [#&#8203;14766](https://github.com/pnpm/pnpm/issues/14766).

- `pnpm outdated --long` fills the Details column with the package homepage again [#&#8203;14886](https://github.com/pnpm/pnpm/issues/14886).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTEuMiIsInVwZGF0ZWRJblZlciI6IjQzLjE5MS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

<!-- cloudflare-preview --> Preview: https://pr-562.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/562
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Renovate Bot <[email protected]>
2026-09-30 01:04:02 +00:00
Gitea Bot 309b26b73f Update the latest api documents (#561)
Automated update of static/swagger-latest.json and static/openapi3-latest.json from the gitea main branch, opened by the "update api spec files" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-561.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/561
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-09-28 03:08:43 +00:00
Gitea Bot fd708d28ed Update the latest api documents (#559)
Automated update of static/swagger-latest.json and static/openapi3-latest.json from the gitea main branch, opened by the "update api spec files" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-559.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/559
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-09-27 20:27:13 +00:00
Gitea Bot 470f582e97 Update the latest api documents (#558)
Automated update of static/swagger-latest.json and static/openapi3-latest.json from the gitea main branch, opened by the "update api spec files" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-558.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/558
Reviewed-by: bircni <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-09-26 07:07:05 +00:00
silverwind c4caf767ad docs: document self:, builtin:, $/ and runner tracing (#553)
Documents the `self:` and `builtin:` action prefixes from https://github.com/go-gitea/gitea/pull/39358, https://gitea.com/gitea/runner/pulls/1233 and https://gitea.com/gitea/runner/pulls/871, workflow-relative `$/` actions, and the runner's OpenTelemetry tracing from https://gitea.com/gitea/runner/pulls/1206.

1. Trims the action resolution and `DEFAULT_ACTIONS_URL` docs to state each fact once.
1. Drops outdated runner content from the FAQ and design pages.
1. Syncs zh-cn and zh-tw, including a port of the token permissions page they now link to.

_Written by Claude._

Reviewed-on: https://gitea.com/gitea/docs/pulls/553
Reviewed-by: bircni <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-09-25 02:51:09 +00:00
wxiaoguang f9124b16db Update docs/administration/config-cheat-sheet.md (#556)
<!-- cloudflare-preview --> Preview: https://pr-556.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/556
Reviewed-by: silverwind <[email protected]>
Co-authored-by: wxiaoguang <[email protected]>
2026-09-24 09:34:23 +00:00
Gitea BotandLunny Xiao b0b0c5090e Update the runner reference pages (#555)
Automated update of the generated runner reference pages from gitea/runner (main for the develop docs, the newest stable tag for every documented series), opened by the "update runner reference" scheduled workflow.

---------

Co-authored-by: Lunny Xiao <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/555
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-09-21 19:51:28 +00:00
Renovate Bot f95a334777 chore(deps): update dependencies (#554)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@astrojs/starlight](https://starlight.astro.build) ([source](https://github.com/withastro/starlight/tree/HEAD/packages/starlight)) | [`0.42.0` → `0.42.1`](https://renovatebot.com/diffs/npm/@astrojs%2fstarlight/0.42.0/0.42.1) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@astrojs%2fstarlight/0.42.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@astrojs%2fstarlight/0.42.0/0.42.1?slim=true) |
| [pnpm](https://github.com/pnpm/pnpm/tree/main/pnpm) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm)) | [`12.4.0` → `12.4.2`](https://renovatebot.com/diffs/npm/pnpm/12.4.0/12.4.2) | ![age](https://developer.mend.io/api/mc/badges/age/npm/pnpm/12.4.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/pnpm/12.4.0/12.4.2?slim=true) |
| [yaml](https://eemeli.org/yaml/) ([source](https://github.com/eemeli/yaml)) | [`2.9.0` → `2.9.1`](https://renovatebot.com/diffs/npm/yaml/2.9.0/2.9.1) | ![age](https://developer.mend.io/api/mc/badges/age/npm/yaml/2.9.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/yaml/2.9.0/2.9.1?slim=true) |

---

### Release Notes

<details>
<summary>withastro/starlight (@&#8203;astrojs/starlight)</summary>

### [`v0.42.1`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0421)

[Compare Source](https://github.com/withastro/starlight/compare/@astrojs/[email protected]...@astrojs/[email protected])

##### Patch Changes

- [#&#8203;4198](https://github.com/withastro/starlight/pull/4198) [`96a44d4`](https://github.com/withastro/starlight/commit/96a44d4f296b512febc6e631a587103ec5297872) Thanks [@&#8203;mayank99](https://github.com/mayank99)! - Removes the `popover` attribute from the sidebar pane on desktop viewports. This ensures the desktop sidebar doesn't stay in a "hidden" popover visibility state.

</details>

<details>
<summary>pnpm/pnpm (pnpm)</summary>

### [`v12.4.2`](https://github.com/pnpm/pnpm/releases/tag/v12.4.2): pnpm 12.4.2

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.4.1...v12.4.2)

pnpm 12.4.2 includes security fixes for executable shims and GitHub Actions links, more reliable installs, faster peer dependency checks in workspaces, and Python lockfiles that work across compatible targets.

#### Patch Changes

##### Security

- Dependency executables can no longer take over another package's POSIX bin shim through its shell helpers. Reinstall dependencies to replace existing shims [#&#8203;14837](https://github.com/pnpm/pnpm/issues/14837).

  On Cygwin, MSYS2, and WSL, shims still use `PATH` for Windows path conversion, so dependency executables can still redirect them there.

- GitHub Actions homepage links no longer expose server credentials. GitHub server URLs now require HTTPS, with HTTP allowed only for loopback hosts.

##### Installing packages

- pnpm no longer crashes at startup on FreeBSD and other Unix-like platforms. Platforms other than Windows and macOS use `~/.local/share/pnpm/store` by default [#&#8203;14859](https://github.com/pnpm/pnpm/issues/14859).

- `pnpm install` on Windows no longer fails with `ERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIR` when clearing `node_modules` containing linked dependencies, such as when changing `nodeLinker` [#&#8203;14790](https://github.com/pnpm/pnpm/issues/14790).

- `pnpm install <pkg>` now accepts `--prod` and `--dev`, including `--prod=false` [#&#8203;14868](https://github.com/pnpm/pnpm/issues/14868).

- `pnpm install` and `pnpm update` now honor `--ignore-workspace` in nested projects excluded from the surrounding workspace. The flag also skips that workspace's settings during the `packageManager` check [#&#8203;14809](https://github.com/pnpm/pnpm/issues/14809).

- `pnpm install` on macOS no longer reuses stale files for `file:` tarball or git-hosted tarball dependencies.

- `pnpm install` in a single-project directory now detects `package.json` edits made while the previous install was finishing [#&#8203;14890](https://github.com/pnpm/pnpm/issues/14890).

- `pnpm install --frozen-lockfile` now removes packages no longer reachable from any project in `pnpm-lock.yaml`. This also prevents repeated lifecycle script execution and unnecessary installs before `pnpm run` and `pnpm exec` with `verifyDepsBeforeRun` [#&#8203;14891](https://github.com/pnpm/pnpm/issues/14891).

##### Resolving and updating dependencies

- Node.js runtime resolution now reports network failures from unofficial-builds.nodejs.org. These failures previously omitted musl builds from `pnpm-lock.yaml`, making its contents depend on network access [#&#8203;14813](https://github.com/pnpm/pnpm/issues/14813).

- `pnpm install` now rejects invalid `peerDependencies` specifiers with `ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION`. A value such as `"foo": "[email protected]"` previously created a broken directory link [#&#8203;14791](https://github.com/pnpm/pnpm/issues/14791).

- `pnpm deploy` now writes plain registry versions in the deployed `package.json`, without peer dependency suffixes. The lockfile retains peer bindings, and npm aliases retain their target package names [#&#8203;14873](https://github.com/pnpm/pnpm/issues/14873).

- `pnpm add <git repository>` now names repositories without a `package.json` as `@owner/repo`, allowing dependencies on equally named repositories from different owners [#&#8203;14870](https://github.com/pnpm/pnpm/issues/14870).

- Peer dependency resolution now deduplicates packages whose child dependency resolves an optional peer in only some workspace projects, such as `next` with `styled-jsx`'s optional `babel-plugin-macros` peer [#&#8203;14800](https://github.com/pnpm/pnpm/issues/14800).

- `pnpm update` now settles the lockfile in one run when an upgrade removes the package providing an optional peer dependency [#&#8203;14895](https://github.com/pnpm/pnpm/issues/14895).

- `pnpm update --no-save` now preserves override-applied specifiers for dependencies it is not updating, preventing subsequent frozen installs from failing with `ERR_PNPM_OUTDATED_LOCKFILE` [#&#8203;14836](https://github.com/pnpm/pnpm/issues/14836).

- `pnpm update --no-save` now succeeds under `minimumReleaseAgeStrict` when every resolved version is old enough [#&#8203;14835](https://github.com/pnpm/pnpm/issues/14835).

##### Performance

- Workspace installs and `pnpm peers check` are faster when projects depend on each other, fixing a slowdown introduced in 12.3.0. Unmet peer dependencies of workspace packages are now reported only under projects that link them directly [#&#8203;14906](https://github.com/pnpm/pnpm/issues/14906).

- Hoisted installs use less memory when packages are cached. Frozen-lockfile hoisted installs on macOS are also faster when reusable package directories are cached.

##### Python projects

- `pnpm install --frozen-lockfile` now reuses `pylock.toml` across compatible Python targets, including after kernel updates. Reuse requires unchanged requirements, index, and `requires-python`, compatible wheels, and a locked dependency graph matching the target's markers [#&#8203;14843](https://github.com/pnpm/pnpm/issues/14843).

  The lockfile's `environments` marker now includes only the interpreter version and marker variables used by the dependency graph. Without `--frozen-lockfile`, pnpm warns and resolves again when the locked graph no longer matches the target.

- Python resolution no longer fails on malformed `Requires-Python` values, such as the trailing comma in `openpyxl` 3.0.x. pnpm treats these releases as declaring no interpreter range [#&#8203;14910](https://github.com/pnpm/pnpm/issues/14910).

- `pnpm add pypi:...` now rejects unsupported `--save-prefix` values before editing the manifest or resolving dependencies.

##### Workspaces and scripts

- Scripts listed in `syncInjectedDepsAfterScripts` no longer fail with `ERR_PNPM_INJECTED_DEPS_SYNC_READ_DIR` when the lockfile contains an injected package copy that no project depends on.

- `shellEmulator` now expands `${VAR}`, `${VAR:-default}`, and `${VAR:+alternative}` in scripts [#&#8203;14814](https://github.com/pnpm/pnpm/issues/14814).

- Cargo and Python project discovery now honors `!` exclusions in `pnpm-workspace.yaml` `packages`, skipping both parsing and generated source configuration for excluded projects [#&#8203;14844](https://github.com/pnpm/pnpm/issues/14844).

- `pnpm --filter "./packages/{app,lib}"` now selects either alternative. Brace alternatives can nest, span path separators, and combine with other wildcards.

- GitHub Actions updates now stop if an action reference changes during version resolution, and preserve unrelated workflow edits.

##### CLI and output

- `pn`, `pnpx`, and `pnx` now run the pnpm installed alongside them, even when that directory is absent from `PATH` or another pnpm comes first [#&#8203;14803](https://github.com/pnpm/pnpm/issues/14803).

- `pnpm --version` now reports failures to install or record a project's pinned pnpm, then prints the running CLI's version. It also honors `--store-dir` and `--store` [#&#8203;14831](https://github.com/pnpm/pnpm/issues/14831).

- `pnpm self-update` no longer reinstalls the active version when it was installed by the standalone installation script [#&#8203;14823](https://github.com/pnpm/pnpm/issues/14823).

- `pnpm t` and `pnpm tst` work again as aliases for `pnpm test`.

- `pnpm sbom` now emits valid repository URLs in CycloneDX `externalReferences[].url` and SPDX `homepage`. Shorthands such as `vercel/ms` become `git+https` URLs, embedded credentials are removed, and invalid repository values are omitted [#&#8203;14773](https://github.com/pnpm/pnpm/issues/14773).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.4.1`](https://github.com/pnpm/pnpm/releases/tag/v12.4.1): pnpm 12.4.1

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.4.0...v12.4.1)

pnpm 12.4.1 fixes installs that failed on filesystems refusing hard links or clones, on Android, and under `nodeLinker: hoisted`. Repeat installs are faster.

#### Patch Changes

##### Installing packages

- `pnpm install` no longer fails with `Operation not permitted` when the filesystem refuses a hard link or a copy-on-write clone [#&#8203;14722](https://github.com/pnpm/pnpm/issues/14722). Under `packageImportMethod: auto` and `clone-or-copy`, pnpm copies the file instead. EdenFS checkouts, which have no hard links, and rootless containers, which refuse the clone syscall, both hit this. An explicit `packageImportMethod: hardlink` or `clone` still reports the error.

  pnpm also copies a package file whose store entry has reached the filesystem's limit on names for one file, 1024 on NTFS and 65000 on ext4. Such a file failed the install under `packageImportMethod: hardlink`, and under `auto` it stopped pnpm hard linking for the rest of the install.

- `pnpm install` no longer writes a package file through a symlink left at the path it is importing to. Copying such a file overwrote whatever the link pointed at, and created that file when the link pointed nowhere. An executable package file also made the link's target executable.

- Fixed `pnpm install` and `pnpm dlx` on Android. Registry requests crashed because pnpm found no system CA certificates, so pnpm uses bundled ones there [#&#8203;14777](https://github.com/pnpm/pnpm/issues/14777). Imports also failed with "Permission denied" on filesystems that deny hard links and reflinks, and now fall back to copying [#&#8203;14780](https://github.com/pnpm/pnpm/issues/14780).

- `pnpm install` no longer fails with "Invalid cross-device link" while preserving a package's nested `node_modules` directory during a Docker build [#&#8203;14758](https://github.com/pnpm/pnpm/issues/14758).

- `pnpm install` no longer fails on a package tarball that carries a file at the archive root, such as the `._*` entries macOS `tar` adds [#&#8203;14701](https://github.com/pnpm/pnpm/issues/14701). The file is installed at the root of the package.

  A `file:` tarball packed without the usual `package/` directory is now recorded under the name and version from its own `package.json`. It was recorded under the alias the dependency was given, at version 0.0.0.

- Under `nodeLinker: hoisted`, `pnpm install` no longer re-imports packages that are already in place. A repeat install replaced the whole `node_modules` tree and reported `Packages: +N`. A package is still imported when its directory is missing, when its `package.json` no longer carries the installed version, when it is a `file:` dependency, and when it is patched. Lifecycle scripts no longer run again for a package left in place, and `pnpm rebuild` and a change to `allowBuilds` still reach it.

- `pnpm install` now runs a dependency's build scripts again when its side-effects cache entry has no files to restore [#&#8203;14717](https://github.com/pnpm/pnpm/issues/14717). Such builds were skipped and nothing was put in their place, so a script whose whole effect lands outside its own package directory, such as a git hook installer, never took effect. pnpm no longer publishes empty artifacts to the shared side-effects cache either.

##### Resolving and linking dependencies

- `pnpm install`, `pnpm add`, and `pnpm dedupe` now apply `ignoredOptionalDependencies` [#&#8203;14729](https://github.com/pnpm/pnpm/issues/14729). Matching optional dependencies are left out of the lockfile and are not installed. pnpm 12 installed them whenever it resolved dependencies from scratch.

- `pnpm install` no longer links a transitive dependency to a workspace package when `linkWorkspacePackages` is `true` and the dependency is declared with a plain version range [#&#8203;14781](https://github.com/pnpm/pnpm/issues/14781). Enabling `preferWorkspacePackages` does not change this. Set `linkWorkspacePackages: deep` to link them.

- `pnpm install` no longer leaves dangling dependency links in workspace packages located above the workspace root [#&#8203;14726](https://github.com/pnpm/pnpm/issues/14726).

- `pnpm install` and `pnpm add` no longer leave a dangling symlink in `node_modules` when a project starts depending directly on a package that the lockfile holds only as a transitive dependency with resolved peer dependencies [#&#8203;14714](https://github.com/pnpm/pnpm/issues/14714).

- `pnpm dedupe` now keeps a compatible auto-installed peer when another workspace project depends on a newer major [#&#8203;14697](https://github.com/pnpm/pnpm/issues/14697). Repeated runs alternated between compatible and incompatible peer versions.

- `pnpm peers check` no longer reports a peer dependency declared as `workspace:^`, `workspace:~`, or a bare `workspace:` as unmet [#&#8203;14770](https://github.com/pnpm/pnpm/issues/14770). pnpm reported these as unmet whatever version the linked workspace project supplied.

##### Performance

- Sped up repeat installs [#&#8203;14540](https://github.com/pnpm/pnpm/issues/14540). pnpm checks the store's files only for the packages it links into `node_modules`, instead of every package in the lockfile. Creating the command shims in `node_modules/.bin` makes about 1,500 fewer filesystem calls in a 76 project workspace. Installs that use the global virtual store read their slot paths from the cache directory instead of deriving them every time. Verifying a large lockfile also allocates less memory.

- Sped up `pnpm install` in Cargo workspaces with many member crates. Repeated installs reuse verified Cargo checksum metadata.

- Installing several packages from the same Git repository and commit now downloads the source once per install [#&#8203;14725](https://github.com/pnpm/pnpm/issues/14725). Each package still runs its prepare scripts in its own copy of the checkout.

##### Running scripts and tasks

- pnpm now passes Ctrl+C on to the script or command it started and waits for it to shut down [#&#8203;14723](https://github.com/pnpm/pnpm/issues/14723). pnpm exited first, so a script that was still writing landed on the shell prompt.

- `pnpm run "/pattern/" --no-bail` now lets every matched script finish after one of them fails [#&#8203;14718](https://github.com/pnpm/pnpm/issues/14718). The command exits with `ERR_PNPM_RUN_FAILED`, and its message lists the scripts that failed in the order they were selected.

- `pnpm pipeline` no longer fails on a project that tracks a symlink, such as a `CLAUDE.md` pointing at `AGENTS.md` [#&#8203;14692](https://github.com/pnpm/pnpm/issues/14692). Changing a symlinked input's target invalidates that task's cache, and `pnpm pipeline --no-cache` no longer hashes task inputs.

##### Commands

- `pnpm add -g`, `pnpm update -g`, and `pnpm remove -g` no longer change global bins or install directories after reading only part of an installed package group [#&#8203;13796](https://github.com/pnpm/pnpm/issues/13796). If any declared package manifest is missing, malformed, or unreadable, pnpm now fails before it activates or removes anything and leaves the existing global installation intact.

- `pnpm dedupe` now processes every workspace project by default, including workspaces that keep a separate lockfile per project [#&#8203;14732](https://github.com/pnpm/pnpm/issues/14732). Workspace filters select which projects it processes, and `--fail-if-no-match` exits with an error when no project matches.

- `pnpm update <name>@&#8203;<version>` now keeps the range operator the manifest declares [#&#8203;14745](https://github.com/pnpm/pnpm/issues/14745). Running `pnpm update [email protected]` on `"react": "^19.2.8"` writes `"react": "^19.3.0"`. A `jsr:` entry keeps its `jsr:` prefix, and a plain `pnpm update` now moves a `jsr:` range the way it moves an npm range.

- `pnpm --filter` directory selectors now support `?` wildcards and character classes such as `[ab]`. A `*` or `?` wildcard no longer selects a directory whose name starts with a dot, as on pnpm 11.

- `pnpm deploy --legacy` now prefers the dependency versions pinned in the source workspace lockfile when they still satisfy the deployed project's range [#&#8203;13857](https://github.com/pnpm/pnpm/issues/13857).

- `pnpm sbom` now leaves out a package's author field when the manifest author name is empty or contains only whitespace [#&#8203;14685](https://github.com/pnpm/pnpm/issues/14685). In a filtered or split workspace run, only a project with no `author` field inherits the workspace root's author.

  `pnpm sbom --sbom-format spdx` now writes `creationInfo.created` with whole seconds, such as `2026-09-08T10:38:21Z` [#&#8203;14684](https://github.com/pnpm/pnpm/issues/14684). The fractional seconds it carried were rejected by strict SPDX consumers.

##### Configuration

- The `updateConfig` pnpmfile hook now receives the resolved configuration, including settings that came from `.npmrc`, the command line, or a default [#&#8203;14676](https://github.com/pnpm/pnpm/issues/14676). Scoped registries are reported under `registriesByScope`, and a hook may rewrite that map to change where packages are fetched from. Registry credentials are reported under `configByUri`, as pnpm 11 reports them. An unset setting is left out rather than reported as `null`.

- `pnpm audit --fix` and the `minimumReleaseAgeStrict` approval prompt now keep the comments in `minimumReleaseAgeExclude` when they append an entry to it in `pnpm-workspace.yaml`. The rest of the list is left as written, and the `trustPolicyExcludePrune` and `minimumReleaseAgeExcludePrune` cleanups keep the comments of the entries they retain.

  `pnpm install` and `pnpm dedupe` now run those cleanups too [#&#8203;14759](https://github.com/pnpm/pnpm/issues/14759). Only `pnpm add`, `pnpm update`, and `pnpm remove` pruned the entries that the freshly written lockfile no longer resolves.

- `pnpm config set --global node-download-mirrors` no longer rejects the key [#&#8203;13611](https://github.com/pnpm/pnpm/issues/13611). The global config file already accepted `nodeDownloadMirrors`, but the command refused to write it.

- `NO_PROXY` entries that start with a dot, such as `.npmjs.org`, now bypass the proxy for the domain and its subdomains [#&#8203;14686](https://github.com/pnpm/pnpm/issues/14686).

- pnpm no longer creates a project `pnpm-lock.yaml` when `devEngines.packageManager.onFail` is `download` and lockfile writing is off through `lockfile: false` or `--no-lockfile` [#&#8203;14728](https://github.com/pnpm/pnpm/issues/14728). pnpm still switches to the pinned version.

- pnpm now writes `node_modules/.package-map.json` only when `nodeExperimentalPackageMap` is enabled. Nothing reads the file without that setting, and an install that stops writing the map removes the one a previous install left.

##### Windows

- `pnpm pipeline` no longer fails with intermittent access denied errors when concurrent tasks save their cache entries on Windows.

- Windows filesystem operations now retry permission errors for up to one second [#&#8203;14682](https://github.com/pnpm/pnpm/issues/14682). A permanent permission error delayed the failure by a minute. Sharing and lock violations keep their one minute retry budget.

##### Messages and output

- pnpm now warns when the root `package.json` declares a non-empty `workspaces` array and the project has no `pnpm-workspace.yaml` [#&#8203;2255](https://github.com/pnpm/pnpm/issues/2255). Such an install linked no project and said nothing about why.

- `ERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIR` now names the file or directory in `node_modules` that pnpm could not clean up. It reported only the underlying OS error, such as "Access is denied (os error 5)".

- `pnpm --help` no longer describes pnpm as experimental.

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

</details>

<details>
<summary>eemeli/yaml (yaml)</summary>

### [`v2.9.1`](https://github.com/eemeli/yaml/releases/tag/v2.9.1)

[Compare Source](https://github.com/eemeli/yaml/compare/v2.9.0...v2.9.1)

- Limit recursive merge aliases ([#&#8203;685](https://github.com/eemeli/yaml/issues/685), [#&#8203;713](https://github.com/eemeli/yaml/issues/713))
- Simplify line unfolding during quoted string parsing ([#&#8203;714](https://github.com/eemeli/yaml/issues/714))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTEuMiIsInVwZGF0ZWRJblZlciI6IjQzLjE5MS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

<!-- cloudflare-preview --> Preview: https://pr-554.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/554
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Renovate Bot <[email protected]>
2026-09-21 17:20:52 +00:00
Lunny Xiaoandbircni c5fdf2eb18 docs: use the maintained main-nightly docker tags (#550)
Fixes https://github.com/go-gitea/gitea/issues/39310

The docker install pages tell readers to use `:nightly` / `:nightless-rootless` for the latest development version, but those tags are no longer published — the registry only carries `main-nightly` and `main-nightly-rootless` (plus the per-release-branch `1.x-nightly` tags):

```
$ curl -s https://docker.gitea.com/v2/gitea/tags/list?n=1000 | tr ',' '\n' | grep -E '"(main-)?nightly(-rootless)?"'
"main-nightly"
"main-nightly-rootless"
```

Anyone who followed the rootless page has been pinned to an image that has not been rebuilt since 2025-08-23.

Changes:

- point the docker and rootless docker install pages at `:main-nightly` / `:main-nightly-rootless`, in English and in the zh-cn/zh-tw translations
- use `1.27-nightly` instead of the long gone `1.16-nightly` as the release branch example (the oldest one still published is `1.19-nightly`)
- fix the zh-cn/zh-tw claim that `:latest` installs a development version, and mention the nightly tags there the way the English page does
- render `@dockerVersion@` as `main-nightly` for the next version, so the compose snippets on the unreleased docs no longer reference `gitea:nightly`

Not touched: the 1.22–1.26 versioned docs carry the same stale tag, but those versions are already out of maintenance — happy to include them if preferred.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- cloudflare-preview --> Preview: https://pr-550.docs-gitea-com.pages.dev

---------

Co-authored-by: bircni <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/550
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Lunny Xiao <[email protected]>
2026-09-20 16:46:22 +00:00
8404944fbb docs: Document audit logging (#544)
Co-authored-by: Lunny Xiao <[email protected]>
Co-authored-by: silverwind <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/544
Reviewed-by: Lunny Xiao <[email protected]>
Reviewed-by: silverwind <[email protected]>
Co-authored-by: bircni <[email protected]>
2026-09-19 12:54:16 +00:00
Gitea Bot 9f58fd3d28 Update the latest api documents (#551)
Automated update of static/swagger-latest.json and static/openapi3-latest.json from the gitea main branch, opened by the "update api spec files" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-551.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/551
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-09-19 00:12:35 +00:00
silverwind 779acd1cb9 docs: document NOTICE_ON_SUCCESS for every cron task (#552)
Several cron tasks in the cheat sheet listed `ENABLE_SUCCESS_NOTICE` or `NO_SUCCESS_NOTICE`. Gitea reads neither, only `NOTICE_ON_SUCCESS`. `NO_SUCCESS_NOTICE` was removed in https://github.com/go-gitea/gitea/pull/19221.

1. Replace both dead keys with `NOTICE_ON_SUCCESS` (default `false`).
2. Fix "Notice if not success" descriptions, which inverted the key's meaning.
3. List the key for `cleanup_hook_task_table`, `cleanup_actions`, `cleanup_action_runs` and `gc_lfs`, which honor it but omitted it.

Matches https://github.com/go-gitea/gitea/pull/39352.

Written by Claude.

Reviewed-on: https://gitea.com/gitea/docs/pulls/552
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: silverwind <[email protected]>
2026-09-18 23:53:53 +00:00
5db4089ae6 chore(deps): update dependencies (#546)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@astrojs/starlight](https://starlight.astro.build) ([source](https://github.com/withastro/starlight/tree/HEAD/packages/starlight)) | [`0.41.11` → `0.42.0`](https://renovatebot.com/diffs/npm/@astrojs%2fstarlight/0.41.11/0.42.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@astrojs%2fstarlight/0.42.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@astrojs%2fstarlight/0.41.11/0.42.0?slim=true) |
| [@astrojs/starlight-docsearch](https://starlight.astro.build) ([source](https://github.com/withastro/starlight/tree/HEAD/packages/docsearch)) | [`0.7.0` → `0.8.0`](https://renovatebot.com/diffs/npm/@astrojs%2fstarlight-docsearch/0.7.0/0.8.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@astrojs%2fstarlight-docsearch/0.8.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@astrojs%2fstarlight-docsearch/0.7.0/0.8.0?slim=true) |
| [astro](https://astro.build) ([source](https://github.com/withastro/astro/tree/HEAD/packages/astro)) | [`7.2.10` → `7.3.2`](https://renovatebot.com/diffs/npm/astro/7.2.10/7.3.2) | ![age](https://developer.mend.io/api/mc/badges/age/npm/astro/7.3.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/astro/7.2.10/7.3.2?slim=true) |
| [pnpm](https://github.com/pnpm/pnpm/tree/main/pnpm) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm)) | [`12.2.1` → `12.4.0`](https://renovatebot.com/diffs/npm/pnpm/12.2.1/12.4.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/pnpm/12.4.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/pnpm/12.2.1/12.4.0?slim=true) |
| [starlight-openapi](https://github.com/HiDeoo/starlight-openapi) ([source](https://github.com/HiDeoo/starlight-openapi/tree/HEAD/packages/starlight-openapi)) | [`0.26.1` → `0.26.2`](https://renovatebot.com/diffs/npm/starlight-openapi/0.26.1/0.26.2) | ![age](https://developer.mend.io/api/mc/badges/age/npm/starlight-openapi/0.26.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/starlight-openapi/0.26.1/0.26.2?slim=true) |

---

### Release Notes

<details>
<summary>withastro/starlight (@&#8203;astrojs/starlight)</summary>

### [`v0.42.0`](https://github.com/withastro/starlight/blob/HEAD/packages/starlight/CHANGELOG.md#0420)

[Compare Source](https://github.com/withastro/starlight/compare/@astrojs/[email protected]...@astrojs/[email protected])

##### Minor Changes

- [#&#8203;3572](https://github.com/withastro/starlight/pull/3572) [`292fb17`](https://github.com/withastro/starlight/commit/292fb179cbf6e2cd970ced46359c520aa14a80b4) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Distributes package as JavaScript files with dedicated type declaration files instead of TypeScript source files.

- [#&#8203;4121](https://github.com/withastro/starlight/pull/4121) [`2623ae6`](https://github.com/withastro/starlight/commit/2623ae60af0fdafc71938f96153b0427fcc2b5aa) Thanks [@&#8203;delucis](https://github.com/delucis)! - Simplifies markup for Starlight’s mobile menu toggle

  **⚠️ Potentially breaking change:** If you use a theme plugin, custom styles, or component overrides targeting the `MobileMenuToggle` button or `PageFrame` components, you may need to adjust these for the new markup. The button is no longer wrapped in a `<starlight-menu-button>` custom element and no longer uses the `aria-expanded` attribute. Instead, you can use the `.sl-menu-button` class name to target the button and the `:popover-open` pseudo-class to style the menu open state specifically.

  In the following example, custom styles for the menu button are updated for the new approach:

  ```diff
  - starlight-menu-button button {
  + .sl-menu-button {
    color: var(--sl-color-text);
  }

  - starlight-menu-button[aria-expanded='true'] button {
  + .sl-menu-button:has(~ :popover-open) {
    color: var(--sl-color-text-accent-high);
  }
  ```

  See [`MobileMenuToggle.astro`](https://github.com/withastro/starlight/blob/main/packages/starlight/components/MobileMenuToggle.astro) and [`PageFrame.astro`](https://github.com/withastro/starlight/blob/main/packages/starlight/components/PageFrame.astro) on GitHub for the full source code of the updated components.

- [#&#8203;3572](https://github.com/withastro/starlight/pull/3572) [`292fb17`](https://github.com/withastro/starlight/commit/292fb179cbf6e2cd970ced46359c520aa14a80b4) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Removes the `tagline` configuration option, which was never used.

  If your configuration included a `tagline` option, you can safely remove it without any replacement.

- [#&#8203;4134](https://github.com/withastro/starlight/pull/4134) [`6135f01`](https://github.com/withastro/starlight/commit/6135f015b527453d7083ad01d2dc6c61d72fbefc) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Updates internal `@astrojs/mdx`, `@astrojs/markdown-satteri`, and `satteri` dependencies.

  ⚠️ **BREAKING CHANGE:** The following minimum versions are now required:

  - `astro` v7.2.10 or later
  - `@astrojs/markdown-satteri` 0.4.0 or later (if you use it)
  - `@astrojs/markdown-remark` 7.3.0 or later (if you use it)

  Please update Starlight and Astro together:

  ```sh
  npx @&#8203;astrojs/upgrade
  ```

- [#&#8203;4121](https://github.com/withastro/starlight/pull/4121) [`2623ae6`](https://github.com/withastro/starlight/commit/2623ae60af0fdafc71938f96153b0427fcc2b5aa) Thanks [@&#8203;delucis](https://github.com/delucis)! - Refactors Starlight’s mobile menu toggle to work when JavaScript fails or is disabled

  ⚠️ **BREAKING CHANGE:** This release drops official support for Chromium-based browsers prior to version 116 (released August 2023), Safari-based browsers prior to version 17.0 (released September 2023), and Firefox prior to version 125 (released April 2024). You can find a list of currently supported browsers and their versions using this [browserslist query](https://browsersl.ist/#q=%3E+0.5%25%2C+not+dead%2C+Chrome+%3E%3D+116%2C+Edge+%3E%3D+116%2C+Firefox+%3E%3D+125%2C+Safari+%3E%3D+17.0%2C+iOS+%3E%3D+17.0%2C+not+op_mini+all).

  This change also removes the `data-mobile-menu-expanded` attribute, which was previously added to `<body>` while the mobile menu is open. If you have custom code that was depending on this attribute, you will need to update it to use a new selector to check if the mobile menu is open.

  In the following example, a custom background colour for the site header while the menu is open is updated for the new approach:

  ```diff
  - [data-mobile-menu-expanded] header {
  + body:has(sl-sidebar-pane:popover-open) header {
    background-color: var(--sl-color-bg);
  }
  ```

</details>

<details>
<summary>withastro/starlight (@&#8203;astrojs/starlight-docsearch)</summary>

### [`v0.8.0`](https://github.com/withastro/starlight/blob/HEAD/packages/docsearch/CHANGELOG.md#080)

[Compare Source](https://github.com/withastro/starlight/compare/@astrojs/[email protected]...@astrojs/[email protected])

##### Minor Changes

- [#&#8203;3572](https://github.com/withastro/starlight/pull/3572) [`292fb17`](https://github.com/withastro/starlight/commit/292fb179cbf6e2cd970ced46359c520aa14a80b4) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Distributes package as JavaScript files with dedicated type declaration files instead of TypeScript source files.

</details>

<details>
<summary>withastro/astro (astro)</summary>

### [`v7.3.2`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#732)

[Compare Source](https://github.com/withastro/astro/compare/[email protected]@7.3.2)

##### Patch Changes

- [#&#8203;17896](https://github.com/withastro/astro/pull/17896) [`a548223`](https://github.com/withastro/astro/commit/a548223607b9bb146d5d90ddda495343f9a2a739) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes `<script>`/`<style>` rendering in MDX so that only literal content (including content injected by remark/rehype plugins) is treated as trusted markup. A dynamic value passed as a `<script>`/`<style>` child (e.g. `<script>{value}</script>`) is now escaped like any other element's content instead of being rendered raw. Use `set:html` to explicitly opt a dynamic value back into raw rendering.

- [#&#8203;17931](https://github.com/withastro/astro/pull/17931) [`c1a6a89`](https://github.com/withastro/astro/commit/c1a6a89efa577b8388f04c4b42d655913bb4b886) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes the dev toolbar returning a 504 "Outdated Optimize Dep" error when a workspace-linked package imports a dependency that Vite's initial scan did not discover

- [#&#8203;17908](https://github.com/withastro/astro/pull/17908) [`42e9188`](https://github.com/withastro/astro/commit/42e9188c4ba7360e5ab8ea4cd7f13d6abcf31879) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes i18n fallback routing replacing the first substring match instead of the actual locale segment, which mangled paths like `/energy/en/about` into `/esergy/en/about`

- [#&#8203;17936](https://github.com/withastro/astro/pull/17936) [`4b92ddc`](https://github.com/withastro/astro/commit/4b92ddc6ab0795ac78c30aedfe43b081dcf5b6b0) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Fixes sessions breaking in dev mode with the Cloudflare adapter when middleware is present

- Updated dependencies \[[`a548223`](https://github.com/withastro/astro/commit/a548223607b9bb146d5d90ddda495343f9a2a739)]:
  - [@&#8203;astrojs/markdown-satteri](https://github.com/astrojs/markdown-satteri)@&#8203;0.4.1

### [`v7.3.1`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#731)

[Compare Source](https://github.com/withastro/astro/compare/[email protected]@7.3.1)

##### Patch Changes

- [#&#8203;17899](https://github.com/withastro/astro/pull/17899) [`0389640`](https://github.com/withastro/astro/commit/03896405717471f7d6ff54986ed6beaec0cac94f) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixes an error that prevented projects using `astro:assets` from starting or building

### [`v7.3.0`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#730)

[Compare Source](https://github.com/withastro/astro/compare/[email protected]@7.3.0)

##### Minor Changes

- [#&#8203;17767](https://github.com/withastro/astro/pull/17767) [`ce7c91f`](https://github.com/withastro/astro/commit/ce7c91f77dbd7be03c04bc13f87af9d01fef6cef) Thanks [@&#8203;astro-factory](https://github.com/apps/astro-factory)! - Adds `--ignore-lock` flag to `astro preview`, allowing multiple preview servers to run simultaneously on different ports. This is useful for E2E testing workflows (e.g., Playwright) that need to run several preview servers at once.

- [#&#8203;17818](https://github.com/withastro/astro/pull/17818) [`c0b6581`](https://github.com/withastro/astro/commit/c0b65811dfa0dafa1aa04b7d6d67fd09250ff8c1) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds a `logger` parameter to image services hooks

  Custom image services now receive Astro's runtime logger as an extra argument. Messages logged with it are routed through the destination configured in `logger` and respect your log level, instead of being written straight to the console:

  ```ts
  import type { LocalImageService } from 'astro';

  const service: LocalImageService = {
    // ...
    async transform(inputBuffer, transform, imageConfig, logger) {
      logger.warn(`Could not optimize "${transform.src}". Passing it through unchanged.`);
      return { data: inputBuffer, format: 'png' };
    },
  };
  ```

  Astro's built-in Sharp service now uses this logger for the warnings it emits when it encounters an unexpected or unsupported source format.

- [#&#8203;17818](https://github.com/withastro/astro/pull/17818) [`c0b6581`](https://github.com/withastro/astro/commit/c0b65811dfa0dafa1aa04b7d6d67fd09250ff8c1) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds `logger` to the context object passed to cache providers

  Custom cache providers now receive Astro's runtime logger on the context passed to `onRequest()`. Messages logged with it are routed through the destination configured in `logger` and respect your log level, instead of being written straight to the console:

  ```ts
  import type { CacheProvider } from 'astro';

  const provider: CacheProvider = {
    name: 'my-cache',
    async onRequest({ request, url, logger }, next) {
      logger.warn(`Skipping cache for ${url.pathname} because the response sets a cookie.`);
      return next();
    },
    // ...
  };
  ```

  Astro's built-in `memoryCache()` provider now uses this logger for the warnings it emits when it skips caching a response that sets cookies, and when a background revalidation fails.

##### Patch Changes

- [#&#8203;17818](https://github.com/withastro/astro/pull/17818) [`c0b6581`](https://github.com/withastro/astro/commit/c0b65811dfa0dafa1aa04b7d6d67fd09250ff8c1) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Updates Astro's remaining internal warnings and errors to be written through the configured logger instead of directly to the console, when possible

- [#&#8203;17886](https://github.com/withastro/astro/pull/17886) [`e747cba`](https://github.com/withastro/astro/commit/e747cba07fcd2b9e7fb03c02ed42abfe2079daa2) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes the memory cache provider to skip responses with `Vary: Cookie` or `Vary: *`

- [#&#8203;17885](https://github.com/withastro/astro/pull/17885) [`916b738`](https://github.com/withastro/astro/commit/916b738b0447728f1c274e32677c9bac09be78f6) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Improves build performance for sites with a large number of pages coming from a large amount of different modules.

- [#&#8203;17795](https://github.com/withastro/astro/pull/17795) [`15e2deb`](https://github.com/withastro/astro/commit/15e2debc7e81d353410ff76a76c3bf75b7fb3070) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds concurrent rendering support for `experimental.incrementalBuild`, including when using `@astrojs/cloudflare`

  Incremental builds no longer disable caching when `build.concurrency` is greater than `1`. Projects that set `build.concurrency: 1` to keep the cache enabled can remove that workaround. Cloudflare builds also reduce serialization overhead for large prerendered pages.

- [#&#8203;17879](https://github.com/withastro/astro/pull/17879) [`21c34a6`](https://github.com/withastro/astro/commit/21c34a6816372220157ff7e6b697275360d3e367) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes missing styles, links, and scripts from content collection entries rendered inside server islands

- [#&#8203;17861](https://github.com/withastro/astro/pull/17861) [`3193988`](https://github.com/withastro/astro/commit/3193988d0566f53ecd10b03cd18669dcf230c8d6) Thanks [@&#8203;ethanstoner](https://github.com/ethanstoner)! - Fixes i18n fallback routes being generated with a corrupted path when the locale code also appears at the start of a later path segment. A page such as `src/pages/en/enterprise.astro` with `fallback: { es: 'en' }` produced the route `/es/esterprise` instead of `/es/enterprise`, so the fallback never matched the intended URL. Only the leading locale segment is rewritten now.

</details>

<details>
<summary>pnpm/pnpm (pnpm)</summary>

### [`v12.4.0`](https://github.com/pnpm/pnpm/releases/tag/v12.4.0): pnpm 12.4

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.3.4...v12.4.0)

#### Minor Changes

- pnpm can now manage npm, Python, and Cargo dependencies in the same workspace. Enable `python.enabled` or `cargo.enabled` in `pnpm-workspace.yaml`, then use `pnpm install` to install them together.

  - Add Python packages with `pnpm add pypi:<package>`. pnpm uses `pyproject.toml`, `pylock.toml`, and a managed `.venv`. Frozen and offline installs are supported, and `pnpm run` and `pnpm exec` make the environment's executables available [#&#8203;14566](https://github.com/pnpm/pnpm/issues/14566).
  - Add Rust crates with `pnpm add crate:<package>`. pnpm supports crates.io and custom sparse registries configured with `cargo.indexUrl`. Registry authentication supports pnpm credentials and, for crates.io, `CARGO_REGISTRY_TOKEN` or `$CARGO_HOME/credentials.toml`.

  Both ecosystems support faster dependency resolution through `pnprServer`, with local resolution as a fallback when the server does not support it.

- Added `pnpm pipeline [name]` to install frozen dependencies and run workspace tasks declared in `pipelines`. It selects affected projects, runs their task graph, and continues running tasks after a task fails.

  Tasks support `inputs`, `outputs`, `env`, and `cache` settings. Cached results restore task outputs and replay logs. Cargo tasks can reuse local build state between worktrees with `tasks.<name>.cargoTargetDir`. Set `includeWorkspaceRoot: true` to include root tasks.

  Use `pnpm pipeline --dry-run` to preview the task graph without installing configuration dependencies or running workspace hooks.

- Added support for Android on arm64 and x64, FreeBSD on x64, and Linux on ppc64le, s390x, and RISC-V (riscv64 with glibc) [#&#8203;14431](https://github.com/pnpm/pnpm/issues/14431), [#&#8203;14597](https://github.com/pnpm/pnpm/issues/14597), [#&#8203;7582](https://github.com/pnpm/pnpm/issues/7582).

- Added `trustPolicyExcludePrune` to automatically remove unused versions and packages from `trustPolicyExclude` when running `pnpm add`, `pnpm update`, or `pnpm remove`. It is disabled by default. Package name patterns such as `@scope/*` are kept, and cleanup is skipped when `sharedWorkspaceLockfile` is `false`.

- Added `pnpm change check` for CI validation of package versions against the `versioning.epics` bands and `versioning.fixed` groups in `pnpm-workspace.yaml`. It reports all violations, including packages that are not part of the current release.

#### Patch Changes

- Registry metadata is now kept separate for registries with different URL paths or schemes. This prevents installs from using another registry's package versions or tarball URLs, and keeps metadata fetched over HTTP from being reused for HTTPS [#&#8203;13558](https://github.com/pnpm/pnpm/issues/13558).

  The first install after upgrading refetches registry metadata. The package store is unchanged. `pnpm cache view` now shows full registry URLs. Scripts that parse the directory names from `pnpm cache list-registries` or `pnpm cache list` need updating.

- Patches that add build scripts or a `binding.gyp` now trigger a build, subject to build approval. Unapproved builds appear under "Ignored build scripts" [#&#8203;14648](https://github.com/pnpm/pnpm/issues/14648).

- Build scripts can now be rejected before installing a package with `pnpm add --allow-build=!<pkg>`, including global installs. `pnpm approve-builds <pkg>` and `pnpm approve-builds !<pkg>` also save decisions when no packages are awaiting approval. They warn if the named package is not awaiting approval [#&#8203;14067](https://github.com/pnpm/pnpm/issues/14067).

- A registry configured in `.npmrc` now takes precedence over registry settings saved by `pnpm login` in the global `config.yaml`. This fixes installs using the wrong registry after login [#&#8203;14614](https://github.com/pnpm/pnpm/issues/14614).

- Large downloads over slow connections no longer time out while data is still arriving. `fetch-timeout` now limits how long a request can go without making progress [#&#8203;14604](https://github.com/pnpm/pnpm/issues/14604).

- Sped up installs in workspaces with many projects when reusing a warm global virtual store [#&#8203;14540](https://github.com/pnpm/pnpm/issues/14540).

- `pnpm deploy` is faster in large workspaces and no longer fails with `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` when the project includes a `.pnpmfile.mjs` [#&#8203;14539](https://github.com/pnpm/pnpm/issues/14539), [#&#8203;14671](https://github.com/pnpm/pnpm/issues/14671).

- `pnpm add --workspace <pkg>` works again. It saves the dependency with the `workspace:` protocol and links it from the workspace. The command fails if no workspace project provides the package [#&#8203;14602](https://github.com/pnpm/pnpm/issues/14602).

- `pnpm add` and `pnpm install` now accept protocol-prefixed selectors such as `jsr:@&#8203;scope/pkg`, `npm:pkg@^1.0.0`, and `workspace:pkg@*` [#&#8203;14590](https://github.com/pnpm/pnpm/issues/14590). Installs with JSR dependencies in the lockfile also no longer fail with `ERR_PNPM_META_FETCH_FAIL` [#&#8203;14649](https://github.com/pnpm/pnpm/issues/14649).

- Boolean flags now accept explicit inline values. For example, `pnpm install --prod=false` installs devDependencies, while `--prod=true` skips them [#&#8203;14553](https://github.com/pnpm/pnpm/issues/14553).

- `pnpm install <pkg>` now accepts `--offline` and `--prefer-offline`, as `pnpm add <pkg>` already did [#&#8203;14194](https://github.com/pnpm/pnpm/pull/14194).

- Fixed `pnpm install --frozen-lockfile` rejecting a freshly generated lockfile when overrides use relative `file:` or `link:` paths in a workspace [#&#8203;14555](https://github.com/pnpm/pnpm/issues/14555).

- Fixed installs with config dependencies failing on symlinked lockfiles, such as those used by Bazel and Nix, when the config dependencies have not changed. Updates that would write through a symlink remain disallowed. Updating config dependencies also preserves lockfiles that start with a byte order mark [#&#8203;14372](https://github.com/pnpm/pnpm/issues/14372).

- Fixed package manager version pins being written to the wrong lockfile when `lockfileDir` is set. The pins also remain consistent across commands when version switching is disabled, avoiding unnecessary lockfile changes [#&#8203;14633](https://github.com/pnpm/pnpm/issues/14633), [#&#8203;14575](https://github.com/pnpm/pnpm/issues/14575).

- `pnpm import` now respects `lockfileDir` and branch lockfiles without modifying other lockfiles. Failed imports restore the destination lockfile [#&#8203;14563](https://github.com/pnpm/pnpm/issues/14563).

- `pnpm patch-commit` now produces valid patches when files are added or deleted. `pnpm install` also accepts patches that delete files without listing their contents, and patch files with CRLF line endings [#&#8203;14559](https://github.com/pnpm/pnpm/issues/14559), [#&#8203;14557](https://github.com/pnpm/pnpm/issues/14557).

- Fixed version ranges with partial upper bounds. For example, `<=16` now includes all 16.x versions, and `>=0.11 <=3` correctly accepts 3.0.1 [#&#8203;14419](https://github.com/pnpm/pnpm/issues/14419).

- Workspace package patterns now support `.` and `..` segments and repeated slashes. Patterns such as `./packages/*` and exclusions such as `!./packages/foo` now match correctly [#&#8203;14571](https://github.com/pnpm/pnpm/issues/14571).

- `packageConfigs` settings now apply to the specified projects when `sharedWorkspaceLockfile` is `false`, including `overrides`, `hoist`, `modulesDir`, `saveExact`, and `savePrefix`. Workspaces with a shared lockfile report which entries were ignored [#&#8203;14556](https://github.com/pnpm/pnpm/issues/14556).

- `pnpm run` and `pnpm exec` no longer report a changed workspace structure after a successful install when `sharedWorkspaceLockfile` is `false` and `verifyDepsBeforeRun` is enabled [#&#8203;14588](https://github.com/pnpm/pnpm/issues/14588).

- Commands run from a project's subdirectory now find the nearest ancestor with a manifest. This fixes commands such as `pnpm bin` returning paths under the wrong directory. `pnpm init` still creates its manifest in the current directory, and `pnpm exec` still runs there [#&#8203;14622](https://github.com/pnpm/pnpm/issues/14622).

- Relative `scriptShell` paths in `pnpm-workspace.yaml` now resolve from the workspace root, including when scripts run in nested packages. Bare command names such as `bash` still use `PATH` [#&#8203;14422](https://github.com/pnpm/pnpm/issues/14422).

- Fixed installing the pnpm version pinned in `packageManager` when `nodeLinker` is `hoisted`. Managed Node.js, Deno, and Bun installations also work when the global config uses `nodeLinker: hoisted` [#&#8203;14595](https://github.com/pnpm/pnpm/issues/14595).

- The JavaScript pnpm can again switch to a project's pinned pnpm version on platforms without a native binary for that version, such as Alpine Linux with pnpm 10 or Intel Macs with pnpm 11. If a native pnpm version does not support the platform, the error now names the missing target [#&#8203;13622](https://github.com/pnpm/pnpm/issues/13622).

- Provisioning Yarn 6 now uses `GH_TOKEN` or `GITHUB_TOKEN` when available to avoid GitHub's anonymous API rate limit in CI. Tokens are only sent when `strict-ssl` is enabled.

- Fixed concurrent installs sharing a global virtual store on macOS failing with "failed to import ... No such file or directory" [#&#8203;14560](https://github.com/pnpm/pnpm/issues/14560).

- Fixed `pnpm setup` failing with `ERR_PNPM_DIRECTORY_FETCHER_PATH_ESCAPE` on Windows. Local `file:` dependencies whose directories are symlinks or junctions are now packed correctly [#&#8203;14618](https://github.com/pnpm/pnpm/issues/14618).

- On Windows, installs now retry replacing command shims temporarily locked by another process [#&#8203;14549](https://github.com/pnpm/pnpm/issues/14549).

- Fixed argument forwarding on Windows with `shellEmulator` enabled. Paths ending in a backslash, line breaks, and literal shell expressions are preserved [#&#8203;14548](https://github.com/pnpm/pnpm/issues/14548).

- Windows store paths now consistently use backslashes in `pnpm store path` output and in the `storeDir` and `virtualStoreDir` fields of `node_modules/.modules.yaml`.

- Invalid certificates in `ca` or `cafile` no longer cause an `Invalid CA certificate` error. Valid certificates still apply, and blank `cert` or `key` values are treated as unset [#&#8203;14646](https://github.com/pnpm/pnpm/issues/14646).

- Installs now respect the archive extraction concurrency limit even after a download is abandoned [#&#8203;14585](https://github.com/pnpm/pnpm/issues/14585).

- `pnpm audit` summaries now exclude advisories ignored through `auditConfig.ignoreGhsas` and report them separately. When all advisories are ignored, the summary says so [#&#8203;14535](https://github.com/pnpm/pnpm/issues/14535).

- `pnpm pack --json` now reports errors as JSON. Lifecycle script output appears before the final JSON output.

- `pnpm outdated -r` now wraps the `Dependents` column, keeping the table readable when many workspace projects use the same dependency [#&#8203;14591](https://github.com/pnpm/pnpm/issues/14591).

- Shell completions now support the `pn` alias in bash, fish, pwsh, and zsh [#&#8203;11955](https://github.com/pnpm/pnpm/issues/11955).

- `pnpm version` now accepts `-m` as a short alias for `--message` [#&#8203;14567](https://github.com/pnpm/pnpm/issues/14567).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.3.4`](https://github.com/pnpm/pnpm/releases/tag/v12.3.4): pnpm 12.3.4

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.3.3...v12.3.4)

#### Patch Changes

- Sped up dependency resolution in large workspaces [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- pnpm 12 now accepts the boolean settings as command-line flags on every command that takes them in pnpm 11, for example `pnpm install --unsafe-perm`, `pnpm add foo --offline`, and `pnpm install --dangerously-allow-all-builds`. pnpm 12 rejected them with `unexpected argument`, which failed every install on Vercel, whose build runs `pnpm install --unsafe-perm` [#&#8203;14346](https://github.com/pnpm/pnpm/issues/14346).

  `pnpm remove` now accepts `--unsafe-perm`, the same flag `pnpm install`, `pnpm add`, and `pnpm update` take.

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/notion.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/notion_light.svg" />
            <img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.3.3`](https://github.com/pnpm/pnpm/releases/tag/v12.3.3): pnpm 12.3.3

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.3.2...v12.3.3)

#### Patch Changes

- Fixed concurrent installs sharing a store occasionally failing with an ENOENT error while importing a package file [#&#8203;14353](https://github.com/pnpm/pnpm/issues/14353).

- Sped up writing the lockfile in large workspaces [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- Sped up dependency resolution in large workspaces [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- pnpm now runs through Node.js when it was installed by a tool that skips build scripts, such as Vercel's `packageManager` provisioning, Bun, Deno, or `npm install --ignore-scripts`. Those installs previously failed with `syntax error near unexpected token ')'`. They still cannot run pnpm on Windows. On macOS only a shell can start it [#&#8203;14346](https://github.com/pnpm/pnpm/issues/14346).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/notion.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/notion_light.svg" />
            <img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.3.2`](https://github.com/pnpm/pnpm/releases/tag/v12.3.2): pnpm 12.3.2

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.3.1...v12.3.2)

#### Patch Changes

- `pnpm audit --fix update` no longer aborts when a vulnerable package has no safe version inside its declared range [#&#8203;14508](https://github.com/pnpm/pnpm/issues/14508). The run updates every package it can and lists the rest as remaining.

- `pnpm install` no longer reruns root lifecycle scripts when the global virtual store contains an unfinished-build marker in a package slot that the current lockfile does not use [pnpm/pnpm#14485](https://github.com/pnpm/pnpm/issues/14485).

- Sped up installs that have no lockfile. pnpm now links packages whose dependency subtree has no peer dependencies into the virtual store while resolution is still running.

- `pnpm run` and `pnpm exec` now start without reinstalling on filesystems that keep sub-millisecond mtimes, such as NTFS. Previously, every run on those filesystems reinstalled first [pnpm/pnpm#14486](https://github.com/pnpm/pnpm/issues/14486).

- `pnpm import` now keeps the versions recorded in `package-lock.json`, `npm-shrinkwrap.json`, or `yarn.lock` when it generates `pnpm-lock.yaml`. A range in `package.json`, a catalog, or an override still decides which versions are eligible, and the recorded version is preferred among them. The generated lockfile previously could pin newer versions than the source lockfile [#&#8203;14476](https://github.com/pnpm/pnpm/issues/14476).

  `pnpm import` in a workspace now imports every workspace project into the shared lockfile. It previously imported only the project in the current directory.

  `pnpm import` now fails with `ERR_PNPM_LOCKFILE_NOT_FOUND` when none of the three source lockfiles is present. It also fails with `ERR_PNPM_YARN_LOCKFILE_PARSE_FAILED` when it cannot parse `yarn.lock`. It previously generated a lockfile from scratch in both cases.

  `pnpm import` always resolves locally. It warns when `--pnpr-server` or the `pnpr-server` setting is given and does not use the server.

- Sped up installs in large workspaces. Discovering the workspace projects no longer enumerates every matched directory to learn which manifest files it holds [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- Sped up installs in large workspaces. The resolver and the peer pass allocate less for every dependency edge [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- `pnpm self-update`, `pnpm with`, and automatic package-manager version switching no longer wait through registry retry delays when a configured registry has no signatures and `registry.npmjs.org` is unavailable [#&#8203;14483](https://github.com/pnpm/pnpm/issues/14483).

- Sped up installs in large workspaces. Saving the lockfile is faster, and the install finishes without waiting for memory cleanup [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- `pnpm install` now relinks workspace packages when `publishConfig.linkDirectory` changes. Frozen installs report an outdated lockfile until it is regenerated [pnpm/pnpm#14488](https://github.com/pnpm/pnpm/issues/14488).

- The pnpm npm wrapper keeps its placeholder shebang-less so pnpm 11 can install pnpm 12 through the version store. Wrapper installs must allow lifecycle scripts to install the native binary [#&#8203;14502](https://github.com/pnpm/pnpm/issues/14502).

- Sped up dependency resolution when there is no lockfile, and for the dependencies a lockfile does not cover.

- Sped up installs in large workspaces. Workspace `link:` targets and importer ids are now derived from the paths' suffixes under the workspace root [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- `pnpm install` now reports "Already up to date" when local tarball dependencies have not changed [#&#8203;14495](https://github.com/pnpm/pnpm/issues/14495).

- `pnpm update` now accepts `--ignore-scripts` and skips lifecycle scripts during the update [pnpm/pnpm#14512](https://github.com/pnpm/pnpm/issues/14512).

- Sped up installs that restore a deleted `node_modules` from a warm global virtual store. pnpm no longer re-links packages that are already fully present in the global virtual store [#&#8203;14510](https://github.com/pnpm/pnpm/issues/14510).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/notion.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/notion_light.svg" />
            <img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.3.1`](https://github.com/pnpm/pnpm/releases/tag/v12.3.1): pnpm 12.3.1

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.3.0...v12.3.1)

#### Patch Changes

- Sped up installs in large workspaces: the anchor for re-rendering workspace `link:` targets is now derived once per project instead of once per dependency edge, and project ordering hashes paths by their raw bytes [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- After a self-update from pnpm 12.2 to 12.3, global commands such as `node`, `npm`, and `yarn` failed with `unexpected argument '--shim' found`. Global commands now launch normally, and their first launch migrates the global bin directory to native shims. When self-update downgrades to pnpm 12.2 or older, it keeps the newer native shims so those commands continue to work.

- Sped up installs in large workspaces. The check that verifies each project against the lockfile now runs the projects in parallel [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/notion.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/notion_light.svg" />
            <img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

### [`v12.3.0`](https://github.com/pnpm/pnpm/releases/tag/v12.3.0): pnpm 12.3

[Compare Source](https://github.com/pnpm/pnpm/compare/v12.2.1...v12.3.0)

#### Minor Changes

- Every context-aware global command (`node`, `deno`, `bun`, and the shims created with `pnpm shim add`) is now a native executable on every platform, so environment variables whose names are not valid shell identifiers reach these commands. On Windows, `<name>.exe` replaces the `.cmd` and `.ps1` shims for them. Shims written by earlier pnpm 12 releases are migrated on the next global install or self-update.

- `pnpm remove` and `pnpm update` now accept `--trust-lockfile`, `--no-trust-lockfile`, `--trust-policy`, `--trust-policy-exclude` and `--trust-policy-ignore-after`, the same flags `pnpm install` and `pnpm add` take, so the supply-chain settings can be overridden for a single run. `pnpm remove` verifies the lockfile against the active policies the way `pnpm install` does, and `--trust-lockfile` skips that pass for every entry, not only the package being removed.

  `pnpm` now also honors `--config.trust-lockfile=<value>`, and accepts the bare `--trust-lockfile` / `--no-trust-lockfile` spelling on the commands that previously took the setting from the config file alone.

#### Patch Changes

- `pnpm add <local directory>`, `pnpm add <local tarball>`, `pnpm add file:<path>` and `pnpm add <tarball URL>` work again. A specifier given without a `<name>@&#8203;` prefix is no longer read as a registry package name and rejected with `ERR_PNPM_PACKAGE_MANAGER_ADD_RESOLVE_LATEST` [#&#8203;14437](https://github.com/pnpm/pnpm/issues/14437).

- Fixed `pnpm deploy --legacy` ignoring `allowUnusedPatches` supplied through `--config.allow-unused-patches` or the `PNPM_CONFIG_ALLOW_UNUSED_PATCHES` environment variable [pnpm/pnpm#14450](https://github.com/pnpm/pnpm/issues/14450).

- Fixed `pnpm install --lockfile-only` writing a lockfile that referenced a missing peer-suffixed snapshot when an npm-aliased dependency took part in a cyclic peer dependency graph. The following `pnpm install --frozen-lockfile` failed with `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY` [#&#8203;14449](https://github.com/pnpm/pnpm/issues/14449).

- `pnpm config` now accepts `-g`/`--global`, `--location`, and `--json` before its subcommand [pnpm/pnpm#14421](https://github.com/pnpm/pnpm/issues/14421).

- `pnpm dedupe` now converges in one pass when it re-resolves a lockfile created by pnpm 11, so a second run no longer changes the lockfile [#&#8203;14455](https://github.com/pnpm/pnpm/issues/14455).

- Fixed detached child processes being terminated on Windows when another program launches `pnpm` directly, without a shell, as `nr` from `@antfu/ni` does [#&#8203;14447](https://github.com/pnpm/pnpm/issues/14447).

- Fixed `pnpm docs <package>@&#8203;<version>` ignoring the requested version. It now opens the selected version's homepage and reports a missing version instead of opening the package-level homepage [pnpm/pnpm#14428](https://github.com/pnpm/pnpm/issues/14428).

- Sped up installs in large workspaces. `pnpm-lock.yaml` is now read while the workspace projects are being discovered [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- Fixed filtered and recursive `pnpm run` and `pnpm exec` hanging when a script reads from the terminal. Interactive prompts work again in a script that pnpm never runs alongside another one, such as a single `--filter`ed project, `--workspace-concurrency=1`, a dependency chain, or a task declaring `concurrency: 1` [#&#8203;14397](https://github.com/pnpm/pnpm/issues/14397).

- Fixed false unmet peer errors for auto-installed peers in linked workspace packages.

- Fixed npm global installs on Windows so the PowerShell shims invoke `pnpm.exe`.

- Fixed `pnpm with current <command>` when global options precede it, such as `pnpm --workspace-root with current --version` [pnpm/pnpm#14413](https://github.com/pnpm/pnpm/issues/14413).

  A short-option cluster that mixes a global flag with an option owned by the command, such as `pnpm -ro dist pack-app`, is now parsed like the same options written after the command.

  An option written before the command name is now reported as an unknown option unless that command accepts it, instead of being taken for the command to run. `pnpm -P exec echo` and `pnpm -z exec echo` fail the way `pnpm --tag next exec echo` does.

- Apply pure insertions in zero-context patches at the correct line instead of one line early.

- Improved peer dependency resolution performance when many packages reuse the same peer ranges.

- `pnpm outdated` and `pnpm update` now follow local actions and reusable workflows referenced with GitHub's self-repository syntax (`uses: $/.github/actions/setup`) when looking for outdated GitHub Actions, the same way they follow `./` references.

- The `pnpm install --help` descriptions of `--prod` and `--dev` no longer claim that the flags take precedence over `NODE_ENV`. pnpm does not read `NODE_ENV` when selecting which dependency groups to install [#&#8203;14445](https://github.com/pnpm/pnpm/issues/14445).

- Sped up installs in large workspaces. The check that decides whether the lockfile needs updating no longer compares every project against every lockfile entry [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- Sped up dependency resolution in large workspaces that use `link:` dependencies [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- On Linux, pnpm now resolves registry hostnames through the system resolver (`getaddrinfo`), as it already does on macOS and Windows and as pnpm 11 did. Previously, an `/etc/resolv.conf` containing an option the bundled pure-Rust resolver did not recognize, such as `options no_tld_query`, made pnpm ignore the configured nameservers and silently query Google's public DNS instead [#&#8203;14469](https://github.com/pnpm/pnpm/issues/14469).

- Sped up dependency resolution in large workspaces. The resolver builds fewer lookup keys for each dependency [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- `catalogMode` and `--save-catalog` no longer move a local path, tarball, or `workspace:<path>` specifier into a catalog. Such a specifier is resolved against the project that declares it, so one catalog entry cannot mean the same directory for every project that references it [#&#8203;14437](https://github.com/pnpm/pnpm/issues/14437).

- Sped up installs in large workspaces. The workspace dependency graph is now built once per run instead of twice [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- Sped up writing `pnpm-lock.yaml` in large workspaces [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- Fixed non-frozen installs through a pnpr server failing instead of regenerating a conflicted lockfile.

- `pnpm update --interactive` renders its checklist the way pnpm 11 does. Group headings and column headers are separators the cursor skips instead of checkboxes that select nothing. The columns of one group line up with the next. `a` toggles all and `i` inverts the selection. The confirmed selection is echoed as a list of package names [#&#8203;14423](https://github.com/pnpm/pnpm/issues/14423).

- Fixed `pnpm config` commands targeting global configuration to skip project package manager version switching, allowing registry authentication to be configured before pnpm downloads a project-pinned version [pnpm/pnpm#14463](https://github.com/pnpm/pnpm/issues/14463).

- Fixed pnpm retaining the surrounding quotes in `.npmrc` values, including auth tokens expanded from environment variables. This restores authentication with registries configured using `:_authToken="${TOKEN}"` [pnpm/pnpm#14427](https://github.com/pnpm/pnpm/issues/14427).

- Fetch and tarball errors no longer print the secrets of the URL they name. Inline `user:pass@` credentials and the query string or fragment of a signed URL are hidden, so a failed install or `pnpm add <url>` cannot leak them into terminal scrollback or CI logs.

- When `dist-tags.latest` names a version whose manifest pnpm cannot read, the error now names that version and the field it could not decode, instead of reporting the tag as empty.

- Retry transient Windows file-lock errors, including sharing violations, while linking dependencies with the default (isolated) `nodeLinker`. This fixes [pnpm/pnpm#14407](https://github.com/pnpm/pnpm/issues/14407).

- `pnpm run`, `pnpm exec`, `pnpm rebuild`, and the script shortcuts such as `pnpm test` now load the pnpmfile, so `updateConfig` hook settings such as `extraEnv` and `extraBinPaths` reach the scripts they spawn [#&#8203;14433](https://github.com/pnpm/pnpm/issues/14433).

- The `pnpm` executable of the npm package now works when the package was installed without running its install scripts, as under `--ignore-scripts` or the default build-script block of pnpm and Bun [#&#8203;14346](https://github.com/pnpm/pnpm/issues/14346). In that case it runs through Node.js and, in a terminal, says how to switch to the native binary.

- Sped up installs in large workspaces. The resolver no longer copies the whole lockfile before resolving [#&#8203;14352](https://github.com/pnpm/pnpm/issues/14352).

- `minimumReleaseAgeStrict` now defaults to `true` when `minimumReleaseAge` is explicitly configured, whether in `pnpm-workspace.yaml`, the global `config.yaml`, a `PNPM_CONFIG_*` variable, or a CLI flag. The built-in 1440-minute default stays non-strict. Previously an explicit cutoff was treated as non-strict, so immature versions were silently added to `minimumReleaseAgeExclude` instead of being gated with a prompt [#&#8203;14409](https://github.com/pnpm/pnpm/issues/14409).

- Preserve environment variables whose names are not valid shell identifiers when launching Node.js installed by `pnpm runtime set node --global` on Unix [pnpm/pnpm#14417](https://github.com/pnpm/pnpm/issues/14417).

- Fixed `pnpm repo` and `pnpm docs` failing to open the Windows browser from WSL [pnpm/pnpm#14467](https://github.com/pnpm/pnpm/issues/14467).

- `pnpm link`, `pnpm outdated`, and `pnpm import` now apply pnpmfile `updateConfig` hooks before resolving dependencies.

- Fixed standalone installations to preserve the bundled `node-gyp` files used to build native dependencies.

- Fixed resolution against registries whose version manifests carry `_npmUser`, `dist.attestations`, `dist.unpackedSize`, `dist.fileCount`, or `peerDependenciesMeta` in a shape npm does not use. Such a version was skipped as though it had never been published, so `pnpm add` could fail with "no version found for the latest tag" even though the registry served it.

- `pnpm unpublish` now completes the two-factor authentication a registry asks for instead of failing with `ERR_PNPM_UNAUTHORIZED` while logged in. A 401 that is an OTP challenge starts the web-based authentication flow, or prompts for a classic one-time password. The obtained password is reused by every request of the run [#&#8203;14464](https://github.com/pnpm/pnpm/issues/14464).

- On Windows, pnpm now resolves host names through the system resolver instead of its own DNS client. The built-in client bound a UDP socket for every lookup, which made Windows Defender Firewall ask to allow `pnpm.exe` again after every `pnpm self-update` [#&#8203;14405](https://github.com/pnpm/pnpm/issues/14405).

<!-- sponsors -->

#### Platinum Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
      </td>
      <td align="center" valign="middle">
        <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
            <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/notion.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/notion_light.svg" />
            <img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion" />
          </picture>
        </a>
      </td>
    </tr>
  </tbody>
</table>

#### Gold Sponsors

<table>
  <tbody>
    <tr>
      <td align="center" valign="middle">
        <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
            <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
            <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
            <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
            <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
            <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
          </picture>
        </a>
      </td>
    </tr>
    <tr>
      <td align="center" valign="middle">
        <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
            <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
          <picture>
            <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
            <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
            <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
          </picture>
        </a>
      </td>
      <td align="center" valign="middle">
        <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
      </td>
    </tr>
  </tbody>
</table>

<!-- sponsors end -->

</details>

<details>
<summary>HiDeoo/starlight-openapi (starlight-openapi)</summary>

### [`v0.26.2`](https://github.com/HiDeoo/starlight-openapi/blob/HEAD/packages/starlight-openapi/CHANGELOG.md#0262)

[Compare Source](https://github.com/HiDeoo/starlight-openapi/compare/[email protected]@0.26.2)

##### Patch Changes

- [#&#8203;176](https://github.com/HiDeoo/starlight-openapi/pull/176) [`a4342e6`](https://github.com/HiDeoo/starlight-openapi/commit/a4342e6250ac0509b625e9b2569bff0603a94eec) Thanks [@&#8203;HiDeoo](https://github.com/HiDeoo)! - Fixes custom Expressive Code configuration being overwritten.

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTEuMiIsInVwZGF0ZWRJblZlciI6IjQzLjE5MS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

<!-- cloudflare-preview --> Preview: https://pr-546.docs-gitea-com.pages.dev

---------

Co-authored-by: silverwind <[email protected]>
Co-authored-by: silverwind <[email protected]>
Co-authored-by: Lunny Xiao <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/546
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Renovate Bot <[email protected]>
2026-09-18 00:22:46 +00:00
f1553bed2f docs(installation): adding podman quadlet instructions (#536)
I was reading the instructions from Docker, trying to adapt to my podman setup (with Quadlet) and through I could share it, some people might find it useful.

Signed-off-by: axel7083 <[email protected]>
Co-authored-by: silverwind <[email protected]>
Co-authored-by: Lunny Xiao <[email protected]>
Co-authored-by: axel7083 <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/536
Reviewed-by: Lunny Xiao <[email protected]>
Reviewed-by: silverwind <[email protected]>
Co-authored-by: axel7083 <[email protected]>
2026-09-17 23:10:02 +00:00
Gitea Bot 77e495f772 Update the latest api documents (#549)
Automated update of static/swagger-latest.json and static/openapi3-latest.json from the gitea main branch, opened by the "update api spec files" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-549.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/549
Co-authored-by: Gitea Bot <[email protected]>
2026-09-17 00:21:50 +00:00
Talya 3d302e72e2 Add commits in other repos documentation (#548)
Added in [go-gitea/gitea#22645](https://github.com/go-gitea/gitea/pull/22645) but not properly documented.

Reviewed-on: https://gitea.com/gitea/docs/pulls/548
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Talya <[email protected]>
2026-09-15 16:56:15 +00:00
kmiitandLunny Xiao 1a284521f1 docs: Sync zh-cn and zh-tw current and 1.27 with English (#545)
Co-authored-by: Lunny Xiao <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/545
Reviewed-by: Lunny Xiao <[email protected]>
Co-authored-by: kmiit <[email protected]>
2026-09-14 19:50:42 +00:00
Gitea Bot 42591cadac Update the runner reference pages (#547)
Automated update of the generated runner reference pages from gitea/runner (main for the develop docs, the newest stable tag for every documented series), opened by the "update runner reference" scheduled workflow.

<!-- cloudflare-preview --> Preview: https://pr-547.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/547
Reviewed-by: silverwind <[email protected]>
Co-authored-by: Gitea Bot <[email protected]>
2026-09-14 12:25:17 +00:00
Lunny Xiaoandbircni 3258717d62 fix(api): show the name of a type in the reference, document the gmail app password (#541)
Two unrelated but small fixes, closes #258 and closes #32.

**The reference does not name its types (#258)**

Since the api reference is rendered from the openapi 3.0 document, a field of type `ReviewStateType` is rendered as the bare string enum the `$ref` points at: starlight-openapi dereferences everything and the schema has no `title`, so the name of the type is nowhere on the page. Searching the current reference for `ReviewStateType` returns nothing, which is what the issue reports (the plugin also has no route for `components/schemas`, so there is no page per type either).

`normalizeSchema()` now titles every named schema of an openapi 3.0 document with its own name, which the plugin renders next to the type of a field:

- before: `state` · `string` · Allowed values: APPROVED …
- after: `state` · `ReviewStateType` · `string` · Allowed values: APPROVED …

The go doc comments start with the name of the type, so the description would repeat it ("ChangeFilesOptions — ChangeFilesOptions options for creating …"); a leading `<name> ` is stripped from the description.

For the record, how the three generations of documents describe that type:

| Versions | Type name | Allowed values |
| --- | --- | --- |
| 1.22 – 1.25 (swagger, `ReviewStateType` definition without `enum`) | shown | missing |
| 1.26 – 1.27 (swagger, enum inlined upstream) | missing | shown |
| 1.27, next (openapi 3.0, with this change) | shown | shown |

The released swagger documents are frozen, so 1.22 – 1.26 keep what they have; from 1.27 on the reference answers the question the issue asks.

**Gmail needs an app password (#32)**

The configuration in the guide is still correct after Google turned off password-only access, because it already asks for an app password. What was missing is why there is no alternative: Gitea's mailer authenticates with `CRAM-MD5`, `PLAIN` or `LOGIN` and has no XOAUTH2, so "Sign in with Google" is not an option. Added that, together with the two things people run into — an app password needs 2-step verification and a Workspace administrator can disable app passwords for the domain — and the `587` + `smtp+starttls` variant.

Verified by building the api and the next docs: the type names show up on the operation pages, the descriptions are not duplicated, and 1.26 and older are unchanged. `pnpm check` passes.

<!-- cloudflare-preview --> Preview: https://pr-541.docs-gitea-com.pages.dev

---------

Co-authored-by: bircni <[email protected]>
Reviewed-on: https://gitea.com/gitea/docs/pulls/541
Reviewed-by: bircni <[email protected]>
Co-authored-by: Lunny Xiao <[email protected]>
2026-09-12 11:27:21 +00:00
Lunny Xiao 9a6d0d90bf docs(lfs): document the sha the contents API expects for LFS files (#542)
Closes #243.

The `sha` of the contents API is compared against the ID of the blob the commit has at that path, and for a file tracked by Git LFS that blob is the pointer file. Sending the `lfs_oid` or a checksum of the file content fails with `sha does not match`, and nothing in the documentation said so.

Adds a section to the LFS page: use the `sha` from `GET /repos/{owner}/{repo}/contents/{filepath}`, not `lfs_oid`, and note that the API does turn the content into an LFS object by itself when `.gitattributes` tracks the path (`modifyFile()` stores the object and commits the pointer), so nothing else has to be done differently.

The swagger comments upstream are being fixed in https://github.com/go-gitea/gitea/pull/38915, which will reach `/api/next/` through the scheduled job and the released reference with 1.28. The documents of 1.22 – 1.27 are frozen, so the guide is the only place where readers of the current release can find it.

<!-- cloudflare-preview --> Preview: https://pr-542.docs-gitea-com.pages.dev

Reviewed-on: https://gitea.com/gitea/docs/pulls/542
Reviewed-by: bircni <[email protected]>
Co-authored-by: Lunny Xiao <[email protected]>
2026-09-12 11:16:22 +00:00
1814 changed files with 117074 additions and 4373 deletions

No files matched your search

+2 -2
View File
@@ -19,13 +19,13 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
with:
node-version: 24
cache: pnpm
- name: prepare awesome list
run: |
make prepare-awesome-latest prepare-awesome\#27 prepare-awesome\#26 prepare-awesome\#25 prepare-awesome\#24 prepare-awesome\#23 prepare-awesome\#22
make prepare-awesome-latest prepare-awesome\#27 prepare-awesome\#26 prepare-awesome\#25 prepare-awesome\#24 prepare-awesome\#23 prepare-awesome\#22 prepare-awesome-v28
- name: Install dependencies
run: pnpm install --frozen-lockfile
-48
View File
@@ -1,48 +0,0 @@
# The status check context of this job is "checks / depends-upstream
# (pull_request)", which the required status check `checks / *` of the protected
# main branch matches, so a pull request waiting for gitea cannot be merged.
# It is a workflow of its own so that a label change does not cancel the build
# and the preview deployment of the `checks` workflow in test.yaml.
#
# The check script comes from the pull request, like the workflow file itself:
# `pull_request` runs both from the head, so taking the script from the base
# branch would not gain anything and would leave this workflow unable to run on
# the pull request that adds it.
name: checks
on:
pull_request:
types:
- opened
- reopened
- synchronize
- edited
- labeled
- unlabeled
concurrency:
group: depends-upstream-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
depends-upstream:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: check the upstream dependencies
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
# optional, only lifts the rate limit of the unauthenticated github api
GITHUB_COM_TOKEN: ${{ secrets.GITHUB_COM_TOKEN }}
run: |
set -euo pipefail
# read the description and the labels from the api instead of the
# event context, so nothing from a pull request body is expanded into
# this script
api="${GITHUB_API_URL:-$GITHUB_SERVER_URL/api/v1}/repos/$GITHUB_REPOSITORY"
curl --silent --show-error --fail --output pull.json "$api/pulls/$PR_NUMBER"
./scripts/check-upstream-deps.sh pull.json
+3 -3
View File
@@ -16,13 +16,13 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
with:
node-version: 24
cache: pnpm
- name: prepare awesome list
run: |
make prepare-awesome-latest prepare-awesome\#27 prepare-awesome\#26 prepare-awesome\#25 prepare-awesome\#24 prepare-awesome\#23 prepare-awesome\#22
make prepare-awesome-latest prepare-awesome\#27 prepare-awesome\#26 prepare-awesome\#25 prepare-awesome\#24 prepare-awesome\#23 prepare-awesome\#22 prepare-awesome-v28
- name: install dependencies
run: pnpm install --frozen-lockfile
@@ -45,7 +45,7 @@ jobs:
# pull requests from forks do not get the secrets, the build above is
# then the only check and the preview is silently skipped
if [ -z "${CLOUDFLARE_API_TOKEN:-}" ] || [ -z "${CLOUDFLARE_ACCOUNT_ID:-}" ]; then
echo "no Cloudflare credentials available, skipping the preview deployment"
echo "::notice::no Cloudflare credentials available (pull request from a fork?), skipping the preview deployment"
exit 0
fi
@@ -1,29 +0,0 @@
name: unlabel merged upstream dependencies
on:
schedule:
- cron: '15 */6 * * *' # every 6 hours, quarter past
workflow_dispatch:
jobs:
unlabel:
if: github.repository == 'gitea/docs'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: drop the label from the pull requests upstream caught up with
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
GITHUB_COM_TOKEN: ${{ secrets.GITHUB_COM_TOKEN }}
run: |
set -euo pipefail
if [ -z "${DEPLOY_TOKEN:-}" ]; then
echo "secrets.DEPLOY_TOKEN is missing, cannot remove the label"
exit 1
fi
export GITEA_API="${GITHUB_API_URL:-$GITHUB_SERVER_URL/api/v1}/repos/$GITHUB_REPOSITORY"
./scripts/unlabel-upstream-deps.sh
+1 -1
View File
@@ -2,7 +2,7 @@ name: update api spec files
on:
schedule:
- cron: '0 */12 * * *' # every 12 hours on the hour
- cron: '0 3 * * 1' # every Monday at 03:00
workflow_dispatch:
env:
-1
View File
@@ -6,7 +6,6 @@ sites/*/dist
sites/*/.astro
sites/*/.cache
build/
.docusaurus/
# Misc
.DS_Store
+13 -3
View File
@@ -12,20 +12,30 @@ create_dir:
clone_awesome: create_dir
git clone --branch=$(GITEA_AWESOME_BRANCH) $(GITEA_AWESOME_REMOTE) .tmp/upstream-awesome || true
# the contributing guide moved to the gitea repository, the readme of
# awesome-gitea still links to it with a relative path
AWESOME_CONTRIBUTING_URL := https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md
FIX_AWESOME_LINKS := sed 's|](contributing.md)|]($(AWESOME_CONTRIBUTING_URL))|'
.PHONY: prepare-awesome-latest
prepare-awesome-latest: clone_awesome
cp .tmp/upstream-awesome/README.md docs/awesome.md
$(FIX_AWESOME_LINKS) .tmp/upstream-awesome/README.md > docs/awesome.md
.PHONY: prepare-awesome\#%
prepare-awesome\#%:
cp .tmp/upstream-awesome/README.md versioned_docs/version-1.$*/awesome.md
$(FIX_AWESOME_LINKS) .tmp/upstream-awesome/README.md > versioned_docs/version-1.$*/awesome.md
# versions numbered without the 1. prefix, like 28
.PHONY: prepare-awesome-v%
prepare-awesome-v%:
$(FIX_AWESOME_LINKS) .tmp/upstream-awesome/README.md > versioned_docs/version-$*/awesome.md
.PHONY: install
install:
pnpm install
.PHONY: prepare-docs
prepare-docs: install prepare-awesome-latest prepare-awesome\#22 prepare-awesome\#23 prepare-awesome\#24 prepare-awesome\#25 prepare-awesome\#26 prepare-awesome\#27
prepare-docs: install prepare-awesome-latest prepare-awesome\#22 prepare-awesome\#23 prepare-awesome\#24 prepare-awesome\#25 prepare-awesome\#26 prepare-awesome\#27 prepare-awesome-v28
.PHONY: build
build:
+3 -35
View File
@@ -7,8 +7,8 @@ The site covers three products, all served from this repository:
| Product | Content | Versions | Languages |
| --- | --- | --- | --- |
| Docs | `docs/`, `versioned_docs/`, `i18n/` | next, 1.27 … 1.22 | English, 简体中文, 繁體中文 |
| API | `static/swagger-*.json`, `static/openapi3-*.json` | next, 1.27 … 1.22 | English |
| Docs | `docs/`, `versioned_docs/`, `i18n/` | next, 28, 1.27 … 1.22 | English, 简体中文, 繁體中文 |
| API | `static/swagger-*.json`, `static/openapi3-*.json` | next, 28, 1.27 … 1.22 | English |
| Runner | `runner-docs/`, `runner-docs_versioned_docs/` | develop, 3 … 0 | English |
The enterprise documentation is built and deployed from its own repository and
@@ -48,38 +48,6 @@ The order of the top level sidebar groups comes from `sidebars.js` (and
`versioned_sidebars/` for released versions), the label and order of every other
group from the `_category_.json` of its directory.
### Documenting an unreleased feature
A page that describes a gitea change which is not merged yet must wait for it,
otherwise the site documents something no release has. Say so in the description
of the pull request, on a line of its own, and add the `depends-on-upstream`
label:
```
Depends on: gitea#35851
```
The label is what blocks the merge: the `depends-upstream` check fails while it
is set, unless every dependency is verified as merged, and `main` requires the
`checks` contexts. The check reads the `Depends on:` lines (`gitea#<id>` or the
full pull request url, several lines are allowed) to find out what to wait for,
so the label needs at least one of them. A `gitea#<id>` mentioned anywhere else
in the description is ignored.
Without the label, a dependency that the check finds unmerged still fails it, so
forgetting the label does not let an unreleased feature through.
The `unlabel merged upstream dependencies` job runs every 6 hours, removes the
label once every referenced pull request is merged and comments on the pull
request, which reruns the check. `scripts/check-upstream-deps.sh` is the check
itself and can be run locally against the json of a pull request.
The check asks the github api whether a pull request is merged, and the runners
share the 60 requests per hour it allows without authentication. The optional
`GITHUB_COM_TOKEN` secret (a token without scopes is enough) raises that limit;
without it an unverifiable dependency only fails the check when the label is
set.
## Cutting a version
```shell
@@ -111,7 +79,7 @@ make update-api-docs-latest # refresh only the documents of gitea main
```
The documents of gitea main are refreshed automatically: the `update api spec
files` workflow runs every 12 hours and opens a pull request whenever gitea main
files` workflow runs weekly and opens a pull request whenever gitea main
changed. Released versions are updated by hand when a new gitea version is
documented.
+2 -2
View File
@@ -2,8 +2,8 @@
# is copied into the build output by the publish workflow, so that it is not
# uploaded to S3/CloudFront where it would be publicly readable at /_headers.
#
# Unlike the docusaurus build, astro emits a single content hashed asset
# directory shared by every language, so no per locale rules are needed.
# The build emits a single content hashed asset directory shared by every
# language, so no per locale rules are needed.
# Content hashed build output (js, css, fonts). The file name changes with the
# content, so these can be cached forever.
+3 -3
View File
@@ -1,7 +1,7 @@
# Redirects for legacy URLs and routes the site no longer serves directly.
# The api reference and the runner docs are separate products, published in
# english only. Docusaurus served an empty localized copy of them.
# english only, so they have no localized copy.
/zh-cn/api/* /api/:splat 301
/zh-tw/api/* /api/:splat 301
/zh-cn/runner/* /runner/:splat 301
@@ -9,9 +9,9 @@
# The version served at the root of a product is also reachable under its
# number, which is what the release notes and old links use.
/api/1.27/* /api/:splat 301
/api/28/* /api/:splat 301
/runner/3/* /runner/:splat 301
/1.27/* /:splat 301
/28/* /:splat 301
# The local search plugin had a results page of its own, algolia docsearch is a
# modal opened from any page.
+153
View File
@@ -0,0 +1,153 @@
---
date: "2026-09-12T00:00:00+00:00"
slug: "audit-logging"
sidebar_position: 41
aliases:
- /en-us/audit-logging
---
# Audit Logging
Gitea can record security-relevant events as structured audit records: each
event has an `action`, `actor`, `scope`, `origin`, `message` and a JSON
`metadata` blob. Recording is **off by default** and does not change request
behavior when it is disabled.
This is separate from the application, router and access logs described in
[Logging Configuration](logging-config.md). Those logs are for operations and
debugging. The audit log is a durable trail of who changed what.
## Enable recording
Set `[audit].RECORD_OUTPUT` to `database` in `app.ini` and restart Gitea:
```ini
[audit]
RECORD_OUTPUT = database
RETENTION_DAYS = 30
```
Invalid values of `RECORD_OUTPUT` fall back to `disabled`. See the
[configuration cheat sheet](config-cheat-sheet.md#audit-audit) for every option.
With Docker, the same settings can be applied as
`GITEA__audit__RECORD_OUTPUT=database` and `GITEA__audit__RETENTION_DAYS=30`
before Gitea writes them into `app.ini`.
## View events
The Audit Log pages are always available and still list any events that were
stored earlier. When recording is disabled, the site administration page also
shows a warning.
| Audience | Location | Events shown |
| --- | --- | --- |
| Site administrators | Site administration → Monitoring → Audit Log (`/-/admin/monitor/audit_logs`) | Every event |
| Signed-in users | User settings → Audit Log (`/user/settings/audit_logs`) | Events scoped to that user |
| Organization owners | Organization settings → Audit Log (`/<org>/settings/audit_logs`) | Events scoped to that organization |
| Repository administrators | Repository settings → Audit Log (`/<owner>/<repo>/settings/audit_logs`) | Events scoped to that repository |
Each listing can be filtered by actor (username), action and origin, and sorted
by timestamp. The actor filter does not match the
[system actors](#actors-and-origins). Action filters accept an exact action or
a family prefix such as `user:impersonation` or `repository:webhook`.
Site administrators can download the current filtered result as JSONL
(newline-delimited JSON) from **Export JSONL**. The file name is
`gitea-audit-log-<timestamp>.jsonl`.
## Event shape
A recorded event contains:
- **Action**: a stable, colon-separated identifier such as `repository:archive`
or `user:accesstoken:add`.
- **Actor**: the user who performed the action. Actions taken while impersonating
a user record both the impersonator and the impersonated user.
- **Actor credential**: when the request authenticated with a named credential,
the event stores its kind and id, never the secret. Typical values:
`access-token:<id>`, `oauth2-grant:<id>`, `gitea-actions:<task id>`,
`deploy-key:<key id>`.
- **Scope**: the affected unit — `user`, `organization`, `repository` or
`system` (instance-wide).
- **Origin**: how the action was initiated — `ui`, `api`, `cli` or `system`.
- **Message**: a human-readable sentence rendered from a per-action template.
- **Metadata**: extra fields that fill the message template (token name, new
visibility, and so on). Secrets and token values are not stored.
- **IP address**: the client address of the request, when one exists. Events
triggered by a git push record the SSH client address for SSH pushes and
`127.0.0.1` for HTTP pushes.
- **Timestamp**: when the event was recorded.
An exported JSONL line looks like:
```json
{"action":"repository:archive","actor":{"type":"user","id":1,"name":"alice"},"scope":{"type":"repository","id":42,"name":"org/repo"},"message":"Archived repository org/repo.","time":"2026-09-12T11:00:00Z","ip_address":"203.0.113.10","origin":"ui"}
```
The `system:startup` message is kept stable across Gitea versions so operators
can parse it: `System started [Gitea {version}]`.
## Actors and origins
Most web and API requests pick up the signed-in user automatically.
Other entry points record a system actor:
- `(gitea-cli)` for `gitea admin` commands and the `system:startup` and
`system:shutdown` events
- `(gitea-auth-source)` for authentication-source syncs and for users an
authentication source creates or updates at sign-in
- `gitea-actions` for Gitea Actions tasks
- `(deploy-key)` for pushes with a deploy key
- `(Cron)` for scheduled cron tasks
- `Unknown` when no actor can be resolved
Events of the `(gitea-cli)` actor use origin `cli`. Cron tasks, git hooks and
other background work use origin `system`.
## What is recorded
Events cover security-relevant changes, grouped by action family:
- **User**: create and delete, admin, restricted and active status, name,
password and password reset requests, visibility, emails, authentication
source, 2FA, WebAuthn, OpenID, external logins, access tokens, OAuth2
applications and grants, SSH/GPG/principal keys, secrets, webhooks,
impersonation, failed two-factor authentication
- **Organization**: create and delete, name, visibility, members, teams,
OAuth2 applications, secrets, webhooks
- **Repository**: create, fork, archive, delete, rename, visibility, transfer,
mirrors, signing verification, collaborators and teams, default branch,
branch and tag protection, deploy keys, webhooks, secrets
- **Issues and pull requests**: create, delete, comments and merges
- **Projects and wiki pages**: create, update and delete
- **Actions**: enable, disable and dispatch a workflow
- **System**: startup and shutdown, instance-wide webhooks, authentication
sources, instance-wide OAuth2 applications
Recording never fails the request that triggered it. A write error is logged
and the original operation continues.
## What is not recorded
- Read-only access such as browsing, clone, fetch or API GET requests
- Logins, successful or failed, other than failed two-factor authentication
(use the [access log](logging-config.md) and
[Fail2ban Setup](fail2ban-setup.md) for authentication traffic)
- Edits to issues and pull requests
- Token or secret values
- Which SSH key a real user pushed with (deploy keys are named; ordinary SSH
user pushes are not)
## Retention
`RETENTION_DAYS` (default **30**) is the age after which recorded events are
eligible for deletion. Set it to `0` to keep events forever.
Cleanup is performed by the `cron.delete_old_audit_events` task (`SCHEDULE`
defaults to `@every 24h`, `OLDER_THAN` defaults to `[audit].RETENTION_DAYS`).
The task is registered only while `RECORD_OUTPUT` records events **and**
`RETENTION_DAYS` is greater than `0`. Turning recording off therefore also
stops pruning; existing rows stay until you delete them or turn recording back
on.
+84 -60
View File
@@ -130,8 +130,8 @@ In addition, there is _`StaticRootPath`_ which can be set as a built-in at build
HTTP protocol.
- `USE_COMPAT_SSH_URI`: **false**: Force ssh:// clone url instead of scp-style uri when
default SSH port is used.
- `GO_GET_CLONE_URL_PROTOCOL`: **https**: Value for the "go get" request returns the repository url as https or ssh
default is https.
- `GO_GET_CLONE_URL_PROTOCOL`: **_empty_**: Scheme of the returned URL for the "go get" response.
Default is "https" if DISABLE_HTTP_GIT=false or SSH is disabled, otherwise "ssh".
- `ACCESS_CONTROL_ALLOW_ORIGIN`: **_empty_**: Value for Access-Control-Allow-Origin header,
default is not to present.
@@ -348,8 +348,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `PROXY_PROTOCOL_TLS_BRIDGING`: **false**: When protocol is https, expect PROXY protocol headers after TLS negotiation.
- `PROXY_PROTOCOL_HEADER_TIMEOUT`: **5s**: Timeout to wait for PROXY protocol header (set to 0 to have no timeout)
- `PROXY_PROTOCOL_ACCEPT_UNKNOWN`: **false**: Accept PROXY protocol headers with Unknown type.
- `DOMAIN`: **localhost**: Domain name of this server.
- `ROOT_URL`: **`{PROTOCOL}://{DOMAIN}:{HTTP_PORT}/`**:
- `ROOT_URL`: **`{PROTOCOL}://localhost:{HTTP_PORT}/`**:
Overwrite the automatically generated public URL.
This is useful if the internal and the external URL don't match (e.g. behind a reverse proxy).
- `PUBLIC_URL_DETECTION`: **`auto`**: Controls how to generate the public URL. Most instances should use the "auto" behavior,
@@ -404,7 +403,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
If it is set to `(DOER_USERNAME)`, it will use current signed-in user's username.
This option is only for some advanced users who have configured their SSH reverse-proxy and need to use different usernames for git SSH clone.
Most users should just leave it blank and/or modify the `BUILTIN_SSH_SERVER_USER`.
- `SSH_DOMAIN`: **`{DOMAIN}`**: Domain name of this server, used for displayed clone URL.
- `SSH_DOMAIN`: **_empty_**: Domain name to be exposed in clone URL, defaults to the domain part of ROOT_URL
- `SSH_PORT`: **22**: SSH port displayed in clone URL. If you need a different "SSH clone port" from the real "SSH listen port", set the SSH_LISTEN_PORT separately.
- `SSH_LISTEN_HOST`: **0.0.0.0**: Listen address for the built-in SSH server.
- `SSH_LISTEN_PORT`: **`{SSH_PORT}`**: Port for the built-in SSH server.
@@ -420,7 +419,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `SSH_SERVER_CIPHERS`: **`chacha20-poly1305@openssh.com`, `aes128-ctr`, `aes192-ctr`, `aes256-ctr`, `aes128-gcm@openssh.com`, `aes256-gcm@openssh.com`**: For the built-in SSH server, choose the ciphers to support for SSH connections, for system SSH this setting has no effect.
- `SSH_SERVER_KEY_EXCHANGES`: **curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1**: For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, for system SSH this setting has no effect.
- `SSH_SERVER_MACS`: **`hmac-sha2-256-etm@openssh.com`, `hmac-sha2-256`, `hmac-sha1`**: For the built-in SSH server, choose the MACs to support for SSH connections, for system SSH this setting has no effect
- `SSH_SERVER_HOST_KEYS`: **ssh/gitea.rsa, ssh/gogs.rsa**: For the built-in SSH server, choose the keypairs to offer as the host key. The private key should be at `SSH_SERVER_HOST_KEY` and the public `SSH_SERVER_HOST_KEY.pub`. Relative paths are made absolute relative to the `APP_DATA_PATH`. If no key exists a 4096 bit RSA key will be created for you.
- `SSH_SERVER_HOST_KEYS`: **ssh/gitea.rsa, ssh/gitea.ed25519, ssh/gitea.ecdsa, ssh/gogs.rsa**: For the built-in SSH server, choose the keypairs to offer as the host key. The private key should be at `SSH_SERVER_HOST_KEY` and the public `SSH_SERVER_HOST_KEY.pub`. Relative paths are made absolute relative to the `APP_DATA_PATH`. If none of the keys exist, a 4096 bit RSA, a 256 bit ECDSA and an ED25519 key will be created for you.
- `SSH_KEY_TEST_PATH`: **/tmp**: Directory to create temporary files in when testing public keys using ssh-keygen, default is the system temporary directory.
- `SSH_KEYGEN_PATH`: **_empty_**: Use `ssh-keygen` to parse public SSH keys. The value is passed to the shell. By default, Gitea does the parsing itself.
- `SSH_EXPOSE_ANONYMOUS`: **false**: Enable exposure of SSH clone URL to anonymous visitors, default is false.
@@ -587,8 +586,7 @@ And the following unique queues:
- `SECRET_KEY`: **\<random at every install\>**: Global secret key. This key is VERY IMPORTANT, if you lost it, the data encrypted by it (like 2FA secret) can't be decrypted anymore.
- `SECRET_KEY_URI`: **_empty_**: Instead of defining SECRET_KEY, this option can be used to use the key stored in a file (example value: `file:/etc/gitea/secret_key`). It shouldn't be lost like SECRET_KEY.
- `LOGIN_REMEMBER_DAYS`: **31**: How long to remember that a user is logged in before requiring relogin (in days).
- `COOKIE_REMEMBER_NAME`: **gitea\_incredible**: Name of cookie used to store authentication
information.
- `COOKIE_REMEMBER_NAME`: **gitea\_remember**: Name of cookie used to store authentication information.
- `REVERSE_PROXY_LOGOUT_REDIRECT`: **_empty_**: URL or relative path to redirect users to after logout when authentication is handled by a reverse proxy or SSO. For example: `/my-sso/logout?return=/my-sso/home`.
- `REVERSE_PROXY_AUTHENTICATION_USER`: **X-WEBAUTH-USER**: Header name for reverse proxy
authentication.
@@ -647,14 +645,30 @@ And the following unique queues:
- `SUCCESSFUL_TOKENS_CACHE_SIZE`: **20**: Cache successful token hashes. API tokens are stored in the DB as pbkdf2 hashes however, this means that there is a potentially significant hashing load when there are multiple API operations. This cache will store the successfully hashed tokens in a LRU cache as a balance between performance and security.
- `DISABLE_QUERY_AUTH_TOKEN`: **false**: Reject API tokens sent in URL query string (Accept Header-based API tokens only). This setting will default to `true` in Gitea 1.23 and be deprecated in Gitea 1.24.
- `TWO_FACTOR_AUTH`: **_empty_**: set to enforced to enforce two factor authentication. Only available in Gitea 1.24 and later.
- `ALLOWED_HOST_LIST`: **external**: Webhook and oauth2 clients can only call allowed hosts for security reasons. Comma separated list.
- `EGRESS_MODE`: **lax**: Egress mode toggles between strictness of outgoing requests:
- `lax` non-public targets (private, loopback, link-local, CGNAT and special-use ranges) to be allowed, it allows all public ones
- `strict` requires an explicit allow of all addresses
- `ALLOWED_HOST_LIST`: **_empty_**: Webhook and oauth2 clients can only call allowed hosts for security reasons. Comma separated list, eg: `192.168.1.0/24:3000`, `[2001:db8::/32]:9090`, `*.mydomain.com:[80|443]`
- Built-in networks:
- `loopback`: 127.0.0.0/8 for IPv4 and ::1/128 for IPv6, localhost is included.
- `private`: RFC 1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and RFC 4193 (FC00::/7). Also called LAN/Intranet.
- `external`: A valid non-private unicast IP, you can access all hosts on public internet.
- `*`: All hosts are allowed.
- CIDR list: `1.2.3.0/8` for IPv4 and `2001:db8::/32` for IPv6
- Wildcard hosts: `*.mydomain.com`, `192.168.100.*`
- `private`: RFC 1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), RFC 4193 (FC00::/7) and RFC 6598 CGNAT (100.64.0.0/10). Also called LAN/Intranet.
- CIDR list: `1.2.3.0/8`, `2001:db8::/32`, and with a port `[2001:db8::/32]:9090` (IPv6 addresses and CIDRs need brackets when a port is given)
- Host matching: `example.com` matches the host and any subdomain, dot-anchored so `notexample.com` never matches. `*.example.com` and `.example.com` match only subdomains, the apex itself is excluded. IDN names must be given as punycode.
- All addresses can have ports specified. Accepted port specs:
- a single port: `192.168.1.0/24:3000`
- a range, both bounds inclusive: `*.mydomain.com:3000-3010`
- a bracketed set of ports and ranges, `|` separated: `*.mydomain.com:[80|443|3000-3010]`
- all ports: `*.mydomain.com:*`
- A portless entry covers all ports in `lax` mode, only 80 and 443 in `strict` mode
- Port specs apply only where the list is consulted: in `lax` mode that is non-public targets alone, public targets are allowed on every port whatever the list says. In `strict` mode every target is checked, so ports restrict public hosts too.
- Non-public targets need an IP or built-in entry, a host name entry alone never covers them.
- Reserved addresses (the IPv4-embedding NAT64, Teredo and 6to4 ranges, this-network, multicast and broadcast) are denied whatever the list says. To reach them configure an HTTP proxy
- This list is enforced on direct connections only. When an HTTP proxy is configured, restricting the proxied target is the proxy server's responsibility.
## Audit (`audit`)
- `RECORD_OUTPUT`: **disabled**: Where security-relevant events are recorded. `disabled` records nothing. `database` writes events to the `audit_event` table and shows them in the admin, organization, repository and user settings. Invalid values fall back to `disabled`. See [Audit Logging](audit-logging.md).
- `RETENTION_DAYS`: **30**: Days to keep recorded events. `0` keeps them forever. Pruning is done by `cron.delete_old_audit_events`. That task is only registered while `RECORD_OUTPUT` records events and `RETENTION_DAYS` is greater than `0`.
## Camo (`camo`)
@@ -665,8 +679,8 @@ And the following unique queues:
## OpenID (`openid`)
- `ENABLE_OPENID_SIGNIN`: **true**: Allow authentication in via OpenID.
- `ENABLE_OPENID_SIGNUP`: **! DISABLE\_REGISTRATION**: Allow registering via OpenID.
- `ENABLE_OPENID_SIGNIN`: **false**: Allow authentication in via OpenID.
- `ENABLE_OPENID_SIGNUP`: **false**: Allow registering via OpenID.
- `WHITELISTED_URIS`: **_empty_**: If non-empty, list of POSIX regex patterns matching
OpenID URI's to permit.
- `BLACKLISTED_URIS`: **_empty_**: If non-empty, list of POSIX regex patterns matching
@@ -701,7 +715,7 @@ And the following unique queues:
Requires `Mailer` to be enabled.
- `REGISTER_MANUAL_CONFIRM`: **false**: Enable this to manually confirm new registrations.
Requires `REGISTER_EMAIL_CONFIRM` to be disabled.
- `DISABLE_REGISTRATION`: **false**: Disable registration, after which only admin can create
- `DISABLE_REGISTRATION`: **true**: Disable registration, after which only admin can create
accounts for users.
- `REQUIRE_EXTERNAL_REGISTRATION_PASSWORD`: **false**: Enable this to force externally created
accounts (via GitHub, OpenID Connect, etc) to create a password.
@@ -760,7 +774,7 @@ And the following unique queues:
- `DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME`: **true**: Only allow users with write permissions to track time.
- `EMAIL_DOMAIN_ALLOWLIST`: **_empty_**: If non-empty, comma separated list of domain names that can only be used to register on this instance, wildcard is supported.
- `EMAIL_DOMAIN_BLOCKLIST`: **_empty_**: If non-empty, comma separated list of domain names that cannot be used to register on this instance, wildcard is supported.
- `SHOW_REGISTRATION_BUTTON`: **! DISABLE\_REGISTRATION**: Show Registration Button
- `SHOW_REGISTRATION_BUTTON`: **false**: Show Registration button, defaults to true only if both DISABLE_REGISTRATION and ALLOW_ONLY_EXTERNAL_REGISTRATION are false
- `SHOW_MILESTONES_DASHBOARD_PAGE`: **true** Enable this to show the milestones dashboard page - a view of all the user's milestones
- `AUTO_WATCH_NEW_REPOS`: **true**: Enable this to let all organisation users watch new repos when they are created
- `AUTO_WATCH_ON_CHANGES`: **false**: Enable this to make users watch a repository after their first commit to it
@@ -770,8 +784,7 @@ And the following unique queues:
- `DEFAULT_ORG_MEMBER_VISIBLE`: **false** True will make the membership of the users visible when added to the organisation.
- `ALLOW_ONLY_INTERNAL_REGISTRATION`: **false** Set to true to force registration only via Gitea.
- `ALLOW_ONLY_EXTERNAL_REGISTRATION`: **false** Set to true to force registration only using third-party services.
- `NO_REPLY_ADDRESS`: **noreply.DOMAIN** Value for the domain part of the user's email address in the Git log if user has set KeepEmailPrivate to true. DOMAIN resolves to the value in server.DOMAIN.
The user's email will be replaced with a concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
- `NO_REPLY_ADDRESS`: **_empty_**: Value for the domain part of the user's email address in the git log if user has set KeepEmailPrivate to true. The user's email will be replaced with a concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS. Default value is "noreply." + the domain part of ROOT_URL
- `USER_DELETE_WITH_COMMENTS_MAX_TIME`: **0** Minimum amount of time a user must exist before comments are kept when the user is deleted.
- `VALID_SITE_URL_SCHEMES`: **http, https**: Valid site url schemes for user profiles
@@ -804,9 +817,11 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
- `DELIVER_TIMEOUT`: **5**: Delivery timeout (sec) for shooting webhooks.
- `SKIP_TLS_VERIFY`: **false**: Allow insecure certification.
- `PAGING_NUM`: **10**: Number of webhook history events that are shown in one page.
- `PROXY_URL`: **_empty_**: Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy. If not given, will use global proxy setting.
- `PROXY_URL`: **_empty_**: Proxy server URL, support http://, https://, socks5://, blank will follow environment http_proxy/https_proxy. If not given, will use global proxy setting.
- `PROXY_HOSTS`: **_empty_`**: Comma separated list of host names requiring proxy. Glob patterns (*) are accepted; use ** to match all hosts. If not given, will use global proxy setting.
When a proxy is configured, Gitea does not enforce `[security]` `ALLOWED_HOST_LIST` on the proxied target, the proxy server is expected to restrict it.
## Mailer (`mailer`)
:::warning
@@ -881,27 +896,27 @@ In-Reply-To =
## Cache (`cache`)
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, `twoqueue` or `memcache`. (`twoqueue` represents a size limited LRU cache.)
- `INTERVAL`: **60**: Garbage Collection interval (sec), for memory and twoqueue cache only.
- `HOST`: **_empty_**: Connection string for `redis` and `memcache`. For `twoqueue` sets configuration for the queue. If left empty while `ADAPTER` is `redis`, it falls back to [redis/CONN_STR](#redis-redis) when that is set.
- `ADAPTER`: **memory**: Either `memory`, `redis`, `memcache`, or `twoqueue`.
- `INTERVAL`: **60**: For `memory` and `twoqueue`, GC interval in seconds.
- `HOST`: **_empty_**: For `redis` and `memcache`, connection host address. For `twoqueue` sets configuration for the queue. If left empty while `ADAPTER` is `redis`, it falls back to [redis/CONN_STR](#redis-redis) when that is set.
- Redis: `redis://:[email protected]:6379/0?pool_size=100&idle_timeout=180s` (See [queue/CONN_STR](#queue-queue-and-queue) for information on additional supported connection types)
- For a Redis cluster: `redis+cluster://:[email protected]:6379/0?pool_size=100&idle_timeout=180s`
- For a Redis sentinel: `redis+sentinel://:macaron@sentinel0:26379,sentinel1:26379,sentinel2:26379/0?pool_size=100&idle_timeout=180s&master_name=mymaster`
- Memcache: `127.0.0.1:9090;127.0.0.1:9091`
- Memcache: `127.0.0.1:11211`
- TwoQueue LRU cache: `{"size":50000,"recent_ratio":0.25,"ghost_ratio":0.5}` or `50000` representing the maximum number of objects stored in the cache.
- `ITEM_TTL`: **16h**: Time to keep items in cache if not used, Setting it to -1 disables caching.
- `ITEM_TTL`: **16h**: Time to keep items in cache if not used. Setting it to -1 disables caching.
## Cache - LastCommitCache settings (`cache.last_commit`)
- `ITEM_TTL`: **8760h**: Time to keep items in cache if not used, Setting it to -1 disables caching.
- `ITEM_TTL`: **8760h**: Time to keep items in cache if not used. Setting it to -1 disables caching.
- `COMMITS_COUNT`: **1000**: Only enable the cache when repository's commits count great than.
## Session (`session`)
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
- `PROVIDER`: **file**: Either "memory", "file", "redis" or "db", default is "file". "db" will reuse the configuration in `[database]`
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for db, empty (database config will be used); for others, the connection string. Relative paths will be made absolute against _`AppWorkPath`_. For the `redis` provider, if left empty it falls back to [redis/CONN_STR](#redis-redis) when that is set.
- `COOKIE_SECURE`:**_empty_**: `true` or `false`. Enable this to force using HTTPS for all session access. If not set, it defaults to `true` if the ROOT_URL is an HTTPS URL.
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
- `COOKIE_NAME`: **gitea\_session**: Session cookie name
- `GC_INTERVAL_TIME`: **86400**: GC interval in seconds.
- `SESSION_LIFE_TIME`: **86400**: Session life time in seconds, default is 86400 (1 day)
- `DOMAIN`: **_empty_**: Sets the cookie Domain
@@ -928,7 +943,7 @@ In-Reply-To =
- none = no avatar will be displayed
- random = random avatar will be generated
- image = default image will be used (which is set in `REPOSITORY_AVATAR_FALLBACK_IMAGE`)
- `REPOSITORY_AVATAR_FALLBACK_IMAGE`: **/img/repo_default.png**: Image used as default repository avatar (if `REPOSITORY_AVATAR_FALLBACK` is set to image and none was uploaded)
- `REPOSITORY_AVATAR_FALLBACK_IMAGE`: **_empty_**: Image used as default repository avatar (if `REPOSITORY_AVATAR_FALLBACK` is set to image and none was uploaded). The value is used as-is, without prepending the sub-path of `ROOT_URL`. Empty uses Gitea's builtin repository avatar.
## Project (`project`)
@@ -1047,7 +1062,7 @@ Release attachment has its own config options in `[repository.release]` section.
- `ENABLED`: **true**: Enable running Update mirrors task periodically.
- `SCHEDULE`: **@every 10m**: Cron syntax for scheduling update mirrors, e.g. `@every 3h`.
- `RUN_AT_START`: **false**: Run Update mirrors task when Gitea starts.
- `NOTICE_ON_SUCCESS`: **false**: Notice if not success
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `PULL_LIMIT`: **50**: Limit the number of mirrors added to the queue to this number (negative values mean no limit, 0 will result in no mirrors being queued effectively disabling pull mirror updating).
- `PUSH_LIMIT`: **50**: Limit the number of mirrors added to the queue to this number (negative values mean no limit, 0 will result in no mirrors being queued effectively disabling push mirror updating).
@@ -1056,7 +1071,7 @@ Release attachment has its own config options in `[repository.release]` section.
- `ENABLED`: **true**: Enable running repository health check task periodically.
- `SCHEDULE`: **@midnight**: Cron syntax for scheduling repository health check.
- `RUN_AT_START`: **false**: Run repository health check task when Gitea starts.
- `NOTICE_ON_SUCCESS`: **false**: Notice if not success
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `TIMEOUT`: **60s**: Time duration syntax for health check execution timeout.
- `ARGS`: **_empty_**: Arguments for command `git fsck`, e.g. `--unreachable --tags`. See more on http://git-scm.com/docs/git-fsck
@@ -1065,12 +1080,13 @@ Release attachment has its own config options in `[repository.release]` section.
- `SCHEDULE`: **@midnight**: Cron syntax for scheduling repository statistics check.
- `ENABLED`: **true**: Enable running repository statistics check task periodically.
- `RUN_AT_START`: **true**: Run repository statistics check task when Gitea starts.
- `NOTICE_ON_SUCCESS`: **false**: Notice if not success
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
#### Cron - Cleanup hook_task Table (`cron.cleanup_hook_task_table`)
- `ENABLED`: **true**: Enable cleanup hook_task job.
- `RUN_AT_START`: **false**: Run cleanup hook_task at start time (if ENABLED).
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@midnight**: Cron syntax for cleaning hook_task table.
- `CLEANUP_TYPE` **OlderThan** OlderThan or PerWebhook Method to cleanup hook_task, either by age (i.e. how long ago hook_task record was delivered) or by the number to keep per webhook (i.e. keep most recent x deliveries per webhook).
- `OLDER_THAN`: **168h**: If CLEANUP_TYPE is set to OlderThan, then any delivered hook_task records older than this expression will be deleted.
@@ -1090,7 +1106,7 @@ Update migrated repositories' issues and comments' posterid, it will always atte
- `ENABLED`: **true**: Enable update migration poster id job.
- `RUN_AT_START`: **true**: Update migrated repositories' issues and comments' posterid when starting server
- `NOTICE_ON_SUCCESS`: **false**: Notice if not success
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@midnight** : Interval as a duration between each synchronization, it will always attempt synchronization when the instance starts.
#### Cron - Sync External Users (`cron.sync_external_users`)
@@ -1099,7 +1115,7 @@ Synchronize external user data (only LDAP user synchronization is supported)
- `ENABLED`: **true**: Enable synchronize external user data job
- `RUN_AT_START`: **false**: Synchronize external user data when starting server
- `NOTICE_ON_SUCCESS`: **false**: Notice if not success
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@midnight** : Interval as a duration between each synchronization, it will always attempt synchronization when the instance starts.
- `UPDATE_EXISTING`: **true**: Create new users, update existing user data and disable users that are not in external source anymore (default) or only create new users if UPDATE_EXISTING is set to false.
@@ -1107,12 +1123,14 @@ Synchronize external user data (only LDAP user synchronization is supported)
- `ENABLED`: **true**: Enable cleanup expired actions assets job.
- `RUN_AT_START`: **true**: Run job at start time (if ENABLED).
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@midnight** : Cron syntax for the job.
#### Cron - Delete Old Action Runs (`cron.cleanup_action_runs`)
- `ENABLED`: **true**: Enable the job deleting action runs older than `RUN_RETENTION_DAYS`. Deletes nothing while that is 0.
- `RUN_AT_START`: **false**: Run job at start time (if ENABLED).
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@midnight**: Cron syntax for the job.
#### Cron - Cleanup Deleted Branches (`cron.deleted_branches_cleanup`)
@@ -1198,11 +1216,21 @@ Synchronize external user data (only LDAP user synchronization is supported)
- `SCHEDULE`: **@every 168h**: Cron syntax to set how often to check.
- `OLDER_THAN`: **8760h**: any action older than this expression will be deleted from database, suggest using `8760h` (1 year) because that's the max length of heatmap.
#### Cron - Delete old audit events (`cron.delete_old_audit_events`)
Deletes audit events older than the retention period. The task is only registered when `[audit].RECORD_OUTPUT` records events and `[audit].RETENTION_DAYS` is greater than `0`. See [Audit Logging](audit-logging.md).
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 24h**: Cron syntax to set how often to check.
- `OLDER_THAN`: **720h**: any audit event older than this expression will be deleted from the database. Defaults to `[audit].RETENTION_DAYS`.
#### Cron - Check for new Gitea versions (`cron.update_checker`)
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `ENABLE_SUCCESS_NOTICE`: **true**: Set to false to switch off success notices.
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 168h**: Cron syntax for scheduling a work, e.g. `@every 168h`.
- `HTTP_ENDPOINT`: **https://dl.gitea.com/gitea/version.json**: the endpoint that Gitea will check for newer versions
@@ -1210,7 +1238,7 @@ Synchronize external user data (only LDAP user synchronization is supported)
- `ENABLED`: **false**: Enable service.
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices.
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 168h**: Cron syntax to set how often to check.
- `OLDER_THAN`: **8760h**: any system notice older than this expression will be deleted from database.
@@ -1218,6 +1246,7 @@ Synchronize external user data (only LDAP user synchronization is supported)
- `ENABLED`: **false**: Enable service.
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 24h**: Cron syntax to set how often to check.
- `OLDER_THAN`: **168h**: Only attempt to garbage collect LFSMetaObjects older than this (default 7 days)
- `LAST_UPDATED_MORE_THAN_AGO`: **72h**: Only attempt to garbage collect LFSMetaObjects that have not been attempted to be garbage collected for this long (default 3 days)
@@ -1230,35 +1259,35 @@ Synchronize external user data (only LDAP user synchronization is supported)
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **true**: Run tasks at start up time (if ENABLED).
- `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices.
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@annually**: Cron syntax to set how often to rebuild
#### Cron - Stop running tasks which haven't been updated for a long time (`cron.stop_zombie_tasks`)
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **true**: Run tasks at start up time (if ENABLED).
- `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices.
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 5m**: Cron syntax to set how often tasks to run the check
#### Cron - Stop running tasks which have running status and continuous updates but don't end for a long time (`cron.stop_endless_tasks`)
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **true**: Run tasks at start up time (if ENABLED).
- `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices.
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 30m**: Cron syntax to set how often to run the check.
#### Cron - Cancel jobs which haven't been picked up for a long time (`cron.cancel_abandoned_jobs`)
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices.
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 6h**: Cron syntax to set how often to run the check.
#### Cron - Start cron based actions (`cron.start_schedule_tasks`)
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED).
- `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices.
- `NOTICE_ON_SUCCESS`: **false**: Set to true to switch on success notices.
- `SCHEDULE`: **@every 1m**: Cron syntax to set how often to schedule tasks
## Git (`git`)
@@ -1298,7 +1327,6 @@ These options are written into the gitconfig file under `[git] HOME_PATH` when G
- `diff.algorithm`: **histogram**
- `core.logAllRefUpdates`: **true**
- `gc.reflogExpire`: **90**
## Metrics (`metrics`)
@@ -1410,11 +1438,13 @@ in this mapping or the filetype using heuristics.
- `MAX_ATTEMPTS`: **3**: Max attempts per http/https request on migrations.
- `RETRY_BACKOFF`: **3**: Backoff time per http/https request retry (seconds)
- `ALLOWED_DOMAINS`: **_empty_**: Domains allowlist for migrating repositories, default is blank. It means external hosts will be allowed. Multiple domains could be separated by commas. Wildcard is supported: `github.com, *.github.com`.
- `BLOCKED_DOMAINS`: **_empty_**: Domains blocklist for migrating repositories, default is blank. Multiple domains could be separated by commas. When `ALLOWED_DOMAINS` is not blank, this option has a higher priority to deny domains. Wildcard is supported.
- `ALLOW_LOCALNETWORKS`: **false**: Allow private addresses defined by RFC 1918, RFC 1122, RFC 4632 and RFC 4291. If a domain is allowed by `ALLOWED_DOMAINS`, this option will be ignored.
- `EGRESS_MODE`: **lax**: Mode toggles between strictness of scanning outgoing requests, same format as `EGRESS_MODE` in [`security`](#security-security).
- `ALLOWED_HOST_LIST`: **_empty_**: Hosts migrations and mirrors may call, same format as `ALLOWED_HOST_LIST` in [`security`](#security-security). Private, loopback and link-local addresses need a builtin or CIDR entry. When unset, the deprecated `ALLOWED_DOMAINS` entries apply on every port with `strict` as the default mode, and the deprecated `ALLOW_LOCALNETWORKS` adds `private, loopback`.
- `BLOCKED_HOST_LIST`: **_empty_**: Hosts migrations and mirrors may never call, portless entries cover all ports.
- `SKIP_TLS_VERIFY`: **false**: Allow skip tls verify
These lists are enforced on direct connections only. When a proxy is configured (`[proxy]`, `[git.config]` `http.proxy` or the environment), restricting the proxied target is the proxy server's responsibility.
## Federation (`federation`)
- `ENABLED`: **false**: Enable/Disable federation capabilities
@@ -1649,14 +1679,16 @@ is `data/repo-archive` and the default of `MINIO_BASE_PATH` is `repo-archive/`.
## Proxy (`proxy`)
- `PROXY_ENABLED`: **false**: Enable the proxy if true, all requests to external via HTTP will be affected, if false, no proxy will be used even environment http_proxy/https_proxy
- `PROXY_URL`: **_empty_**: Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
- `PROXY_URL`: **_empty_**: Proxy server URL, support http://, https://, socks5://, blank will follow environment http_proxy/https_proxy
- `PROXY_HOSTS`: **_empty_**: Comma separated list of host names requiring proxy. Glob patterns (*) are accepted; use ** to match all hosts.
When a proxy is configured, Gitea does not enforce the egress lists (`[security]`, `[migrations]`) on the proxied target, the proxy server is expected to restrict it.
i.e.
```ini
PROXY_ENABLED = true
PROXY_URL = socks://127.0.0.1:1080
PROXY_URL = socks5://127.0.0.1:1080
PROXY_HOSTS = *.github.com
```
@@ -1675,6 +1707,7 @@ PROXY_HOSTS = *.github.com
- `ARTIFACT_RETENTION_DAYS`: **90**: Days to keep artifacts. Old artifacts will be deleted after this period. 0 means keep forever.
Changes only apply to newly uploaded artifacts, existing ones keep the expiry stored when they were uploaded.
Artifacts could have their own retention periods by setting the `retention-days` option in `actions/upload-artifact` step.
- `ARTIFACT_PREVIEW_MAX_SIZE`: **10485760**: Maximum total artifact size in bytes that can be browsed or previewed in the web UI. Set to `0` to disable artifact previews or `-1` for no limit. Individual files are also limited by [`MAX_DISPLAY_FILE_SIZE`](#ui-ui).
- `RUN_RETENTION_DAYS`: **400**: Days to keep completed runs. Old runs and everything under them will be deleted after this period. 0 means keep forever.
- `ZOMBIE_TASK_TIMEOUT`: **10m**: Timeout to stop the task which have running status, but haven't been updated for a long time
- `ENDLESS_TASK_TIMEOUT`: **3h**: Timeout to stop the tasks which have running status and continuous updates, but don't end for a long time
@@ -1684,18 +1717,9 @@ PROXY_HOSTS = *.github.com
- `SCOPED_WORKFLOW_DIRS`: **.gitea/scoped_workflows**: Comma-separated list of directories holding [scoped workflows](usage/actions/scoped-workflows.md) (workflows defined in a central source repository that run on other repositories). Must not overlap with `WORKFLOW_DIRS`. Leave empty so no directory is scanned for scoped workflows; none are found or run.
- `MAX_RERUN_ATTEMPTS`: **50**: Maximum number of attempts a single workflow run can have (initial run + reruns). Defaults value is 50. Set any positive value that fits your workflow needs.
`DEFAULT_ACTIONS_URL` indicates where the Gitea Actions runners should find the actions with relative path.
For example, `uses: actions/checkout@v4` means `https://github.com/actions/checkout@v4` since the value of `DEFAULT_ACTIONS_URL` is `github`.
And it can be changed to `self` to make it `root_url_of_your_gitea/actions/checkout@v4`.
Please note that using `self` is not recommended for most cases, as it could make names globally ambiguous.
Additionally, it requires you to mirror all the actions you need to your Gitea instance, which may not be worth it.
Therefore, please use `self` only if you understand what you are doing.
In earlier versions (`<= 1.19`), `DEFAULT_ACTIONS_URL` could be set to any custom URLs like `https://gitea.com` or `http://your-git-server,https://gitea.com`, and the default value was `https://gitea.com`.
However, later updates removed those options, and now the only options are `github` and `self`, with the default value being `github`.
However, if you want to use actions from other git server, you can use a complete URL in `uses` field, it's supported by Gitea (but not GitHub).
Like `uses: https://gitea.com/actions/checkout@v4` or `uses: http://your-git-server/actions/checkout@v4`.
`DEFAULT_ACTIONS_URL` sets where runners download actions without a host.
With `github`, `uses: actions/checkout@v4` resolves to `https://github.com/actions/checkout@v4`, with `self` to `<ROOT_URL>/actions/checkout@v4`.
`self` requires mirroring every action you use to your instance, so prefer naming the host per action instead: `uses: self:owner/repo@ref` for your instance, or a full URL like `uses: https://gitea.com/actions/checkout@v4` for any other host.
## Other (`other`)
+15
View File
@@ -86,6 +86,21 @@ PROTOCOL = smtps
Note that you'll need to create and use an [App password](https://support.google.com/accounts/answer/185833?hl=en) by enabling 2FA on your Google
account. You won't be able to use your Google account password directly.
An app password is the only option: Google
[turned off access for apps that sign in with a password alone](https://workspaceupdates.googleblog.com/2023/09/winding-down-google-sync-and-less-secure-apps-support.html)
in 2024 and asks applications to use OAuth instead, which Gitea's mailer does
not implement — it authenticates with `CRAM-MD5`, `PLAIN` or `LOGIN`.
Two things to keep in mind:
- app passwords require 2-step verification on the account, and a Google
Workspace administrator can switch them off for the whole domain. If they are
not available, send through the
[Workspace SMTP relay service](https://support.google.com/a/answer/2956491)
or through another mail provider.
- port `587` works as well, with `SMTP_PORT = 587` and
`PROTOCOL = smtp+starttls`.
### ProtonMail
This feature is currently only available for select Proton for Business customers and those with Visionary and Family plans with custom domain addresses. See [ProtonMail's SMTP documentation](https://proton.me/support/smtp-submission) for more information. This limitation can be circumvented by using the ProtonMail Bridge application.
+21
View File
@@ -41,3 +41,24 @@ client that causes SSH transfers to hang: https://github.com/git-lfs/git-lfs/pul
This can be worked around on all the client machines by setting the git config:
`git config --global lfs.ssh.automultiplex false`
:::
# Changing LFS files through the API
The [contents API](https://docs.gitea.com/api/operations/repo-change-files/)
works on LFS tracked files as well: send the file content as usual and Gitea
stores it as an LFS object and commits a pointer file, as long as the path is
matched by a `filter=lfs` rule in `.gitattributes`.
What is easy to get wrong is the `sha` of the file being changed, which
`PUT`/`DELETE /repos/{owner}/{repo}/contents/{filepath}` and
`POST /repos/{owner}/{repo}/contents` require for an existing file. Gitea
compares it with the ID of the blob the commit has at that path, and for an LFS
tracked file that blob is the **pointer file**, not the content:
- use the `sha` returned by
[`GET /repos/{owner}/{repo}/contents/{filepath}`](https://docs.gitea.com/api/operations/repo-get-contents/)
- do not use `lfs_oid` from the same response, which identifies the LFS object,
and do not use a checksum of the file itself
Sending anything else fails with `sha does not match`, even though the request
looks correct.
+2
View File
@@ -16,6 +16,8 @@ The logging configuration of Gitea mainly consists of 3 types of components:
There is a fully functional log output by default, so it is not necessary to define one.
Security-relevant events (repository transfers, token changes, impersonation, and similar) are recorded separately. See [Audit Logging](audit-logging.md).
## Collecting Logs for Help
To collect logs for help and issue report, see [Support Options](help/support.md).
+1 -1
View File
@@ -43,7 +43,7 @@ You can try it out using [the online demo](https://demo.gitea.com).
- **Security**
Gitea places a strong emphasis on security, offering features such as user permission management, access control lists, and more to ensure the security of code and data.
Gitea places a strong emphasis on security, offering features such as user permission management, access control lists, [audit logging](administration/audit-logging.md), and more to ensure the security of code and data.
- **Code Review**
+1
View File
@@ -34,6 +34,7 @@ _Symbols used in table:_
| Telemetry | **✘** | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
| Third-party render tool support | ✓ | ✘ | ✘ | ✘ | ✓ | ✘ | ✘ |
| WebAuthn (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✓ |
| Audit log | ✓ | ✓ | ⁄ | ✓ | ✓ | ? | ? |
| Extensive API | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Built-in Package/Container Registry | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ | ✘ |
| Sync commits to an external repo (push mirror) | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ | ✓ |
+2 -2
View File
@@ -23,7 +23,7 @@ version is @minNodeVersion@ and the latest LTS version is recommended.
:::note
Go version @minGoVersion@ or higher is required. However, it is recommended to
obtain the same version as our continuous integration, see the advice given in
[Hacking on Gitea](development/hacking-on-gitea.md)
[Hacking on Gitea](https://github.com/go-gitea/gitea/blob/main/docs/build-setup.md)
:::
## Download
@@ -73,7 +73,7 @@ To build from source, the following programs must be present on the system:
- `go` @minGoVersion@ or higher, see [here](https://go.dev/dl/)
- `node` @minNodeVersion@ or higher with `npm`, see [here](https://nodejs.org/en/download/)
- `make`, see [here](development/hacking-on-gitea.md#installing-make)
- `make`, see [here](https://github.com/go-gitea/gitea/blob/main/docs/build-setup.md#make)
Various [make tasks](https://github.com/go-gitea/gitea/blob/main/Makefile)
are provided to keep the build process as simple as possible.
-49
View File
@@ -1,49 +0,0 @@
---
date: "2016-12-01T16:00:00+02:00"
slug: "install-on-cloud-provider"
sidebar_position: 90
aliases:
- /en-us/install-on-cloud-provider
---
# Installation on Cloud Provider
## Cloudron
Gitea is available as a 1-click install on [Cloudron](https://cloudron.io).
Cloudron makes it easy to run apps like Gitea on your server and keep them up-to-date and secure.
[![Install](/cloudron.svg)](https://cloudron.io/button.html?app=io.gitea.cloudronapp)
The Gitea package is maintained [here](https://git.cloudron.io/cloudron/gitea-app).
There is a [demo instance](https://my.demo.cloudron.io) (username: cloudron password: cloudron) where
you can experiment with running Gitea.
## Linode
[Linode](https://www.linode.com/) has Gitea as an app in their marketplace.
To deploy Gitea to Linode, have a look at the [Linode Marketplace](https://www.linode.com/marketplace/apps/linode/gitea/).
## alwaysdata
[alwaysdata](https://www.alwaysdata.com/) has Gitea as an app in their marketplace.
To deploy Gitea to alwaysdata, have a look at the [alwaysdata Marketplace](https://www.alwaysdata.com/en/marketplace/gitea/).
## Exoscale
[Exoscale](https://www.exoscale.com/) provides Gitea managed by [Glasskube](https://glasskube.eu/) in their marketplace.
Exoscale is a European cloud service provider.
The package is maintained and update via the open source [Glasskube Kubernetes Operator](https://github.com/glasskube/operator).
To deploy Gitea to Exoscale, have a look at the [Exoscale Marketplace](https://www.exoscale.com/marketplace/listing/glasskube-gitea/).
## Sealos
Gitea is available as a one-click deployment on [Sealos](https://sealos.io/products/app-store/gitea).
The community-maintained template source is available [here](https://github.com/labring-actions/templates/tree/kb-0.9/template/gitea).
+1 -1
View File
@@ -66,7 +66,7 @@ server-1 | 2026-03-11T12:57:50.796198843Z /var/lib/gitea/git is not writable
server-1 | 2026-03-11T12:57:50.796235667Z docker setup failed
```
For a stable release you could use `:latest-rootless`, `:1-rootless` or specify a certain release like `:@dockerVersion@-rootless`, but if you'd like to use the latest development version then `:nightly-rootless` would be an appropriate tag. If you'd like to run the latest commit from a release branch you can use the `:1.x-nightly-rootless` tag, where x is the minor version of Gitea. (e.g. `:1.16-nightly-rootless`)
For a stable release you could use `:latest-rootless`, `:1-rootless` or specify a certain release like `:@dockerVersion@-rootless`, but if you'd like to use the latest development version then `:main-nightly-rootless` would be an appropriate tag. If you'd like to run the latest commit from a release branch you can use the `:1.x-nightly-rootless` tag, where x is the minor version of Gitea. (e.g. `:1.27-nightly-rootless`)
## Named volumes
+2 -2
View File
@@ -30,7 +30,7 @@ image as a service. Since there is no database available, one can be initialized
Create a directory like `gitea` and paste the following content into a file named `docker-compose.yml`.
Note that the volume should be owned by the user/group with the UID/GID specified in the config file.
If you don't give the volume correct permissions, the container may not start.
For a stable release you can use `:latest`, `:1` or specify a certain release like `:@dockerVersion@`, but if you'd like to use the latest development version of Gitea then you could use the `:nightly` tag. If you'd like to run the latest commit from a release branch you can use the `:1.x-nightly` tag, where x is the minor version of Gitea. (e.g. `:1.16-nightly`)
For a stable release you can use `:latest`, `:1` or specify a certain release like `:@dockerVersion@`, but if you'd like to use the latest development version of Gitea then you could use the `:main-nightly` tag. If you'd like to run the latest commit from a release branch you can use the `:1.x-nightly` tag, where x is the minor version of Gitea. (e.g. `:1.27-nightly`)
```yaml
networks:
@@ -191,7 +191,7 @@ services:
+ networks:
+ - gitea
+ volumes:
+ - ./postgres:/var/lib/postgresql/data
+ - ./postgres:/var/lib/postgresql
```
## Named volumes
+166
View File
@@ -0,0 +1,166 @@
---
date: "2026-09-03T20:00:00+02:00"
slug: "install-with-podman-quadlet"
sidebar_position: 65
aliases:
- /en-us/install-with-podman-quadlet
---
# Installation with Podman Quadlet
Similarly to [Docker](with-docker.md), [Podman](https://podman.io/) can be used to deploy Gitea.
This reference setup guides users through the setup based on Podman Quadlet.
Podman Quadlet is the native integration of Podman through systemd.
To learn more about Podman Quadlet, see [systemd units using Podman Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html).
This setup installs the quadlets in `~/.config/containers/systemd/`, the directory for rootless quadlets, and uses the rootless images.
If you install them in a rootful directory such as `/etc/containers/systemd/`, use the rootful images instead.
## Basics
Since Podman v6.0 a single `gitea.quadlets` file can hold multiple quadlets separated by the `---` delimiter.
Paste the following content into a file named `gitea.quadlets`
```ini
# FileName=gitea-data
[Volume]
VolumeName=gitea-data
---
# FileName=gitea-config
[Volume]
VolumeName=gitea-config
---
# FileName=gitea
[Container]
PublishPort=2222:2222
PublishPort=3000:3000
Image=docker.gitea.com/gitea:@dockerVersion@-rootless
ContainerName=gitea
Mount=type=volume,src=gitea-data.volume,destination=/var/lib/gitea
Mount=type=volume,src=gitea-config.volume,destination=/etc/gitea
[Service]
Restart=always
[Install]
# Start with the user session
WantedBy=default.target
```
You can run the installation command
```sh
$ podman quadlet install ./gitea.quadlets --application=gitea
/home/user/.config/containers/systemd/gitea/gitea-data.volume
/home/user/.config/containers/systemd/gitea/gitea-config.volume
/home/user/.config/containers/systemd/gitea/gitea.container
```
The `--application=gitea` flag allows us to tell podman to group the quadlets together, this can help for managing and organizing the quadlets files.
You can validate the quadlets are properly installed by looking at the list command
```sh
$ podman quadlet list
NAME UNIT NAME PATH ON DISK STATUS APPLICATION POD
gitea-config.volume gitea-config-volume.service /home/user/.config/containers/systemd/gitea/gitea-config.volume active/exited gitea
gitea-data.volume gitea-data-volume.service /home/user/.config/containers/systemd/gitea/gitea-data.volume active/exited gitea
gitea.container gitea.service /home/user/.config/containers/systemd/gitea/gitea.container failed/failed gitea
```
You can start the application by running `systemctl --user start gitea` command.
`WantedBy=default.target` starts the service when the user session starts.
Rootless user services do not run without a session, so to start Gitea at boot, enable lingering for the user with `loginctl enable-linger $USER`.
## Databases
### PostgreSQL database
To start Gitea in combination with a PostgreSQL database, apply these changes to the
`gitea.quadlets` file created above.
To start Gitea with a database, we need to create a network, so the database and Gitea container can communicate.
```diff
# FileName=gitea-data
[Volume]
VolumeName=gitea-data
---
# FileName=gitea-config
[Volume]
VolumeName=gitea-config
---
+ # FileName=gitea-postgres-data
+ [Volume]
+ VolumeName=gitea-postgres-data
+ ---
+ # FileName=gitea
+ [Network]
+ NetworkName=gitea
+ ---
+ # FileName=gitea-postgres
+ [Container]
+ Image=docker.io/library/postgres:18
+ ContainerName=gitea-postgres
+ Network=gitea.network
+ Mount=type=volume,src=gitea-postgres-data.volume,destination=/var/lib/postgresql
+ Environment=POSTGRES_USER=gitea
+ Environment=POSTGRES_PASSWORD=gitea
+ Environment=POSTGRES_DB=gitea
+
+ [Service]
+ Restart=always
+
+ [Install]
+ # Start with the user session
+ WantedBy=default.target
+ ---
# FileName=gitea
+ [Unit]
+ Requires=gitea-postgres.service
+ After=gitea-postgres.service
+
[Container]
PublishPort=2222:2222
PublishPort=3000:3000
Image=docker.gitea.com/gitea:@dockerVersion@-rootless
ContainerName=gitea
Mount=type=volume,src=gitea-data.volume,destination=/var/lib/gitea
Mount=type=volume,src=gitea-config.volume,destination=/etc/gitea
+ Network=gitea.network
+ Environment=GITEA__database__DB_TYPE=postgres
+ Environment=GITEA__database__HOST=gitea-postgres:5432
+ Environment=GITEA__database__NAME=gitea
+ Environment=GITEA__database__USER=gitea
+ Environment=GITEA__database__PASSWD=gitea
[Service]
Restart=always
[Install]
# Start with the user session
WantedBy=default.target
```
## Logs
As systemd manages the container we can use `journalctl` to access the logs
```sh
$ journalctl --user --follow --unit=gitea.service
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:263:runWeb() [I] Starting Gitea on PID: 2
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:115:showWebStartupMessage() [I] Gitea version: 1.27.3 built with go1.26.7-X:jsonv2 : bindata, timetzdata,
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:116:showWebStartupMessage() [I] * RunMode: prod
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:117:showWebStartupMessage() [I] * AppPath: /usr/local/bin/gitea
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:118:showWebStartupMessage() [I] * WorkPath: /var/lib/gitea
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:119:showWebStartupMessage() [I] * CustomPath: /var/lib/gitea/custom
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:120:showWebStartupMessage() [I] * ConfigFile: /etc/gitea/app.ini
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:121:showWebStartupMessage() [I] Prepare to run install page
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:329:listen() [I] Listen: http://0.0.0.0:3000
gitea[5967]: 2026/09/03 14:50:42 cmd/web.go:333:listen() [I] AppURL(ROOT_URL): http://localhost:3000/
gitea[5967]: 2026/09/03 14:50:42 modules/graceful/server.go:52:NewServer() [I] Starting new Web server: tcp:0.0.0.0:3000 on PID: 2
```
+31
View File
@@ -0,0 +1,31 @@
---
date: "2026-08-30T00:00:00+02:00"
slug: "artifacts"
sidebar_position: 70
---
# Artifacts
Workflow runs can store files such as test results, coverage reports, and build output as artifacts. Artifacts are uploaded by workflows, for example with [`actions/upload-artifact`](https://github.com/actions/upload-artifact), and appear on the run page after the upload completes.
## Previewing artifacts
Select an artifact on the run page to open its file browser and preview in the same tab. The preview identifies the originating run and attempt, and provides links back to that run and to download the complete artifact. Selecting files in the browser updates the preview in the same tab.
Artifact previews:
- require sign-in and access to the Actions run to open the preview page
- render text-based files, images (including SVG), and PDFs
- run HTML and JavaScript content in a sandboxed frame
Preview content is loaded through signed URLs that expire after one hour and do not require a session cookie. Reopen the preview page to obtain a fresh URL after it expires.
The file browser lists up to 2,000 files per artifact. Larger artifacts show a notice when the list is truncated.
Unsupported or oversized files can still be downloaded with the complete artifact.
## Instance configuration
Site administrators can limit the total artifact size that can be browsed or previewed with `ARTIFACT_PREVIEW_MAX_SIZE` in the `[actions]` section of `app.ini`. The default is 10 MiB. Set it to `0` to disable previews or `-1` to allow artifacts of any size.
The size of an individual previewed file is also limited by [`MAX_DISPLAY_FILE_SIZE`](../../administration/config-cheat-sheet.md#ui-ui) in the `[ui]` section.
+32 -42
View File
@@ -6,104 +6,94 @@ sidebar_position: 120
# Compared to GitHub Actions
Even though Gitea Actions is designed to be compatible with GitHub Actions, there are some differences between them.
Gitea Actions is designed to be compatible with GitHub Actions, with the differences below.
## Additional features
### Absolute action URLs
Gitea Actions supports defining actions via absolute URL, which means that you can use actions from any git repository.
Like `uses: https://github.com/actions/checkout@v4` or `uses: http://your_gitea.com/owner/repo@branch`.
`uses:` accepts an absolute URL to any git host, e.g. `uses: https://gitea.com/actions/checkout@v4`.
### Action prefixes
`uses:` accepts these prefixes:
- `self:` references your own Gitea instance, e.g. `uses: self:owner/repo@v1` or `uses: self:owner/repo/.gitea/workflows/build.yml@v1`
- `builtin:` runs an action built into Gitea Runner, which needs no download and no Node in the job image, e.g. `uses: builtin:checkout`
`builtin:checkout` accepts the `repository`, `ref`, `token`, `path` and `fetch-depth` inputs of `actions/checkout` with the same defaults, and fails on any other input.
### Workflow-relative actions
`$/` references the repository and commit of the workflow, or of the enclosing composite action, e.g. `uses: $/.gitea/actions/build`.
Unlike `./`, it needs no checkout first.
### Actions written in Go
Gitea Actions supports writing actions in Go.
See [Creating Go Actions](https://blog.gitea.com/creating-go-actions/).
### Support the non-standard syntax @yearly, @monthly, @weekly, @daily, @hourly on schedule
Github Actions doesn't support that. https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
`schedule` also accepts `@yearly`, `@monthly`, `@weekly`, `@daily` and `@hourly`, which [GitHub does not](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule).
## Unsupported workflows syntax
### `jobs.<job_id>.environment`
See [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idenvironment).
It's ignored by Gitea Actions now.
[`jobs.<job_id>.environment`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idenvironment) is ignored.
### Complex `runs-on`
See [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on).
[`runs-on`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on) supports:
Gitea Actions supports:
- Static strings: `runs-on: self-hosted`
- Arrays of labels: `runs-on: [linux, self-hosted]`
- String expressions: `runs-on: ${{ github.event_name == 'push' && 'ubuntu-latest' || 'self-hosted' }}`
- Arrays containing expressions: `runs-on: [linux, "${{ github.event_name == 'push' && 'ubuntu-latest' || 'self-hosted' }}"]`
## Missing features
### Package repository authorization
The `GITEA_TOKEN` for a job running within a repository should be able to publish to the associated package repository (i.e. to upload OCI images). See the "packages" scope for the "default access" in [Automatic token authentication](https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token).
This is not implemented in Gitea Actions now. A workaround for Gitea Actions is to use a Personal Access Token (PAT). See this [github issue and comment](https://github.com/go-gitea/gitea/issues/23642#issuecomment-2119876692) for tracking this feature.
`GITEA_TOKEN` cannot publish to the package registry of its repository, e.g. to push OCI images.
Use a personal access token instead, see [this issue](https://github.com/go-gitea/gitea/issues/23642#issuecomment-2119876692).
### Problem Matchers
Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI.
See [Problem matchers](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md).
It's ignored by Gitea Actions now.
[Problem matchers](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md) are ignored.
### Create an error annotation
See [Creating an annotation for an error](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-creating-an-annotation-for-an-error)
It's ignored by Gitea Actions now.
[Error annotations](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-creating-an-annotation-for-an-error) are ignored.
### Expressions
For [expressions](https://docs.github.com/en/actions/learn-github-actions/expressions), Gitea Actions supports standard GitHub functions (e.g. `success()`, `failure()`, `always()`, `cancelled()`, `format()`, `toJSON()`) and context variables (like `github`, `matrix`, `vars`, `env`).
[Expressions](https://docs.github.com/en/actions/learn-github-actions/expressions) support the standard GitHub functions and contexts.
## Missing UI features
### Pre and Post steps
Pre and Post steps don't have their own section in the job log user interface.
Pre and post steps have no section of their own in the job log.
### Services steps
Services steps don't have their own section in the job log user interface.
Service steps have no section of their own in the job log.
## Different behavior
### Job token permissions (`permissions`)
Gitea supports `permissions` and `jobs.<job_id>.permissions` to control the default `GITEA_TOKEN` permissions.
The effective permissions are clamped by the repository/owner settings and are further restricted for fork pull requests and cross-repository access.
GitHub-only scopes such as `statuses`, `checks`, `deployments`, `id-token`, `security-events`, and `pages` are not supported, while Gitea-specific scopes such as `code`, `releases`, `wiki`, and `projects` are available.
`permissions` and `jobs.<job_id>.permissions` control the `GITEA_TOKEN` permissions, clamped by the repository and owner settings and further restricted for fork pull requests and cross-repository access.
GitHub-only scopes like `statuses`, `checks`, `deployments`, `security-events` and `pages` are not supported, while Gitea adds `code`, `releases`, `wiki` and `projects`. `id-token` works like on GitHub, see [OpenID Connect](openid-connect.md).
See [Actions job token permissions](token-permissions.md).
### Downloading actions
Previously (Pre 1.21.0), `[actions].DEFAULT_ACTIONS_URL` defaulted to `https://gitea.com`.
We have since restricted this option to only allow two values (`github` and `self`).
When set to `github`, the new default, Gitea will download non-fully-qualified actions from `https://github.com`.
For example, if you use `uses: actions/checkout@v4`, it will download the checkout repository from `https://github.com/actions/checkout.git`.
If you want to download an action from another git hoster, you can use an absolute URL, e.g. `uses: https://gitea.com/actions/checkout@v4`.
If your Gitea instance is in an intranet or a restricted area, you can set the URL to `self` to only download actions from your own instance by default.
Of course, you can still use absolute URLs in workflows.
More details about the `[actions].DEFAULT_ACTIONS_URL` configuration can be found in the [Configuration Cheat Sheet](../../administration/config-cheat-sheet.md#actions-actions)。
Actions without a host, like `uses: actions/checkout@v4`, are downloaded from `https://github.com`, or from your own instance when `[actions].DEFAULT_ACTIONS_URL` is `self`.
See the [Configuration Cheat Sheet](../../administration/config-cheat-sheet.md#actions-actions).
For other sources, see [Absolute action URLs](#absolute-action-urls), [Action prefixes](#action-prefixes) and [Workflow-relative actions](#workflow-relative-actions).
### Context availability
Context availability is not checked, so you can use the env context on more places.
See [Context availability](https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability).
[Context availability](https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability) is not checked, so contexts like `env` work in more places.
+4 -4
View File
@@ -19,7 +19,7 @@ Additionally, the runner will introduce itself to Gitea and declare what kind of
Earlier, we mentioned that `runs-on: ubuntu-latest` in a workflow file means that the job will be run on a runner with the `ubuntu-latest` label.
But how does the runner know to run `ubuntu-latest`? The answer lies in mapping the label to an environment.
That's why when you add custom labels during registration, you will need to input some complex content like `my_custom_label:docker://centos:7`.
That's why each label of a runner names an environment, like `my_custom_label:docker://centos:7`.
This means that the runner can take the job which needs to run on `my_custom_label`, and it will run it via a docker container with the image `centos:7`.
Docker isn't the only option, though.
@@ -34,7 +34,6 @@ So,
- `my_custom_label:docker://node:18`: Run jobs labeled with `my_custom_label` using the `node:18` Docker image.
- `my_custom_label:host`: Run jobs labeled with `my_custom_label` directly on the host.
- `my_custom_label`: Same as `my_custom_label:host`.
- `my_custom_label:vm:ubuntu-latest`: (Example only, not implemented) Run jobs labeled with `my_custom_label` using a virtual machine with the `ubuntu-latest` ISO.
## Communication protocol
@@ -43,7 +42,7 @@ However, we did not think it was a good idea to have Gitea listen on a new port.
Instead, we wanted to reuse the HTTP port, which means we needed a protocol that is compatible with HTTP.
We chose to use gRPC over HTTP.
We use [actions-proto-def](https://gitea.com/gitea/actions-proto-def) and [actions-proto-go](https://gitea.com/gitea/actions-proto-go) to wire them up.
The protocol is defined in [actions-proto-def](https://gitea.com/gitea/actions-proto-def), its Go code lives in [actionslib](https://gitea.com/gitea/actionslib).
More information about gRPC can be found on [its website](https://grpc.io/).
## Network architecture
@@ -71,7 +70,8 @@ However, if a job container tries to fetch code from localhost, it will fail bec
### Connection 3, runner to internet
When you use some actions like `actions/checkout@v4`, the runner downloads the scripts, not the job containers.
By default, it downloads from [github.com](http://github.com/), so it requires access to the internet. If you configure the `DEFAULT_ACTIONS_URL` to `self`, then it will download from your Gitea instance by default. Then it will not connect to internet when downloading the action itself.
By default, it downloads them from [github.com](https://github.com/), which requires internet access.
With `DEFAULT_ACTIONS_URL` set to `self`, or with `uses: self:owner/repo@ref`, it downloads them from your Gitea instance instead.
It also downloads some docker images from Docker Hub by default, which also requires internet access.
However, internet access is not strictly necessary.
+17 -69
View File
@@ -27,50 +27,24 @@ Still, this is completely optional since both options have the same effect at th
## Where will the runner download scripts when using actions such as `actions/checkout@v4`?
There are tens of thousands of [actions scripts](https://github.com/marketplace?type=actions) in GitHub, and when you write `uses: actions/checkout@v4`, it downloads the scripts from [github.com/actions/checkout](http://github.com/actions/checkout) by default.
But what if you want to use actions from other places such as gitea.com instead of GitHub?
From `https://github.com` by default, or from your own instance when `[actions].DEFAULT_ACTIONS_URL` is `self`, see the [Configuration Cheat Sheet](../../administration/config-cheat-sheet.md#actions-actions).
The good news is that you can specify the URL prefix to use actions from anywhere.
This is an extra syntax in Gitea Actions.
For example:
To use an action from elsewhere, name its host:
- `uses: https://gitea.com/xxx/xxx@xxx`
- `uses: https://github.com/xxx/xxx@xxx`
- `uses: http://your_gitea_instance.com/xxx@xxx`
Be careful, the `https://` or `http://` prefix is necessary!
This is one of the differences from GitHub Actions which supports actions scripts only from GitHub.
But it should allow users much more flexibility in how they run Actions.
Alternatively, if you want your runners to download actions from your own Gitea instance by default, you can configure it by setting `[actions].DEFAULT_ACTIONS_URL`.
See [Configuration Cheat Sheet](../../administration/config-cheat-sheet.md#actions-actions).
- `uses: https://gitea.com/owner/repo@ref`
- `uses: http://your_gitea_instance.com/owner/repo@ref`
- `uses: self:owner/repo@ref` for your own Gitea instance
## How to limit the permission of the runners?
Runners have no more permissions than simply connecting to your Gitea instance.
When any runner receives a job to run, it will temporarily gain limited permission to the repository associated with the job.
If you want to give more permissions to the runner, allowing it to access more private repositories or external systems, you can pass [secrets](usage/actions/secrets.md) to it.
Refined permission control to Actions is a complicated job.
In the future, we will add more options to Gitea to make it more configurable, such as allowing more write access to repositories or read access to all repositories in the same organization.
Runners only connect to your Gitea instance.
For each job, a runner gets a `GITEA_TOKEN` limited to the job's repository, see [Actions job token permissions](token-permissions.md).
To give a job access to more private repositories or external systems, pass it [secrets](usage/actions/secrets.md).
## Which operating systems are supported by Gitea Runner?
We released official binaries for Linux, macOS, and Windows.
While other operating systems are theoretically supported if it is supported by golang and docker(docker mode enabled).
One thing to note is that if you choose to run jobs directly on the host instead of in job containers, the environmental differences between operating systems may cause unexpected failures.
For example, bash is not available on Windows in most cases, while act tries to use bash to run scripts by default.
Therefore, you need to specify `powershell` as the default shell in your workflow file, see [defaults.run](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#defaultsrun).
```yaml
defaults:
run:
shell: powershell
```
Official binaries are released for Linux, macOS and Windows.
Other systems supported by Go and Docker work in theory.
## How to avoid being hacked?
@@ -99,42 +73,13 @@ It is exciting to be able to reuse them.
## What if it runs on multiple labels, such as `runs-on: [label_a, label_b]`?
This is valid syntax.
It means that it should run on runners that have both the `label_a` **and** `label_b` labels, see [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on).
Unfortunately, the runner does not work this way until v0.2.11.
As mentioned, we map labels to environments:
- `ubuntu` → `ubuntu:22.04`
- `centos` → `centos:8`
But we need to map label groups to environments instead, like so:
- `[ubuntu]` → `ubuntu:22.04`
- `[with-gpu]` → `linux:with-gpu`
- `[ubuntu, with-gpu]` → `ubuntu:22.04_with-gpu`
We also need to re-design how tasks are assigned to runners.
A runner with `ubuntu`, `centos`, or `with-gpu` does not necessarily indicate that it can accept jobs with `[centos, with-gpu]`.
Therefore, the runner should inform the Gitea instance that it can only accept jobs with `[ubuntu]`, `[centos]`, `[with-gpu]`, and `[ubuntu, with-gpu]`.
This is not a technical problem, it was just overlooked in the early design.
See [runtime.go#L65](https://gitea.com/gitea/runner/src/commit/90b8cc6a7a48f45cc28b5ef9660ebf4061fcb336/runtime/runtime.go#L65).
Currently, the runner attempts to match everyone in the labels and uses the first match it finds.
The job runs on a runner that has all of the labels, as on [GitHub](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on).
The runner uses the environment of the first label it has.
## What is the difference between agent labels and custom labels for a runner?
![labels](/images/usage/actions/labels.png)
Agent labels are reported to the Gitea instance by the runner during registration.
Custom labels, on the other hand, are added manually by a Gitea administrator or owners of the organization or repository (depending on the level of the runner).
However, the design here needs improvement, as it currently has some rough edges.
You can add a custom label such as `centos` to a registered runner, which means the runner will receive jobs with `runs-on: centos`.
However, the runner may not know which environment to use for this label, resulting in it using a default image or leading to a logical dead end.
This default may not match user expectations.
See [runtime.go#L71](https://gitea.com/gitea/runner/src/commit/90b8cc6a7a48f45cc28b5ef9660ebf4061fcb336/runtime/runtime.go#L71).
In the meantime, we suggest that you re-register your runner if you want to change its labels.
Gitea no longer has custom labels.
A runner declares its labels every time it starts, from its `runner.labels` configuration or the labels given at registration, so change them there and restart the runner.
## Will there be more implementations for Gitea Actions runner?
@@ -177,3 +122,6 @@ For events supported only by GitHub, see GitHub's [documentation](https://docs.g
Go to the repository's **Settings** > **Actions** > **General** page and add collaborative owners.
The private repositories of collaborative owners are allowed to access the actions and workflows in the current repository.
To share within the same user or organization, add the private repositories under its **Settings** > **Actions** > **General** > **Cross-Repository Access** instead.
See [Cross-repository access](token-permissions.md#cross-repository-access).
+60
View File
@@ -0,0 +1,60 @@
---
date: "2026-08-23T00:00:00+00:00"
slug: "openid-connect"
sidebar_position: 40
---
# OpenID Connect
Jobs can request short-lived OpenID Connect (OIDC) tokens to sign in to cloud providers and other services without storing long-lived secrets. The tokens use the GitHub Actions format, so actions like `aws-actions/configure-aws-credentials`, `google-github-actions/auth` and `azure/login` work unchanged.
## Trusting Gitea
Add Gitea as an OIDC identity provider in the service:
- Issuer: `<ROOT_URL>/api/actions/oidc`
- Audience: `<ROOT_URL>/<owner>`, unless the workflow requests another one
Then limit which workflows can sign in with a condition on the [`sub` claim](#claims).
Gitea signs the tokens with a key it creates at `<APP_DATA_PATH>/jwt/actions_oidc.pem`. Instances behind a load balancer must share this file.
## Requesting a token
Grant the `id-token` permission to the workflow or job:
```yaml
permissions:
id-token: write
```
Default permissions never include it, and jobs from fork pull requests never get it. Owners and repositories can disable it with their [maximum token permissions](token-permissions.md#maximum-token-permissions-clamping).
Actions like the ones above request the token themselves. In a script, use `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN`:
```shell
curl --fail --silent --show-error \
--header "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=my-service"
```
The token is in the `value` field of the response and expires after five minutes.
## Claims
The `sub` claim names the owner and repository with their IDs, so a renamed or recreated repository cannot match an existing trust policy:
```text
repo:<owner>@<owner-id>/<repo>@<repo-id>:ref:<ref>
repo:<owner>@<owner-id>/<repo>@<repo-id>:pull_request
```
The `pull_request` event uses the second form, all other events use the first, including `pull_request_target`.
The other claims match GitHub's, like `repository`, `ref`, `sha`, `workflow_ref` and `job_workflow_ref`. The discovery document at `<ROOT_URL>/api/actions/oidc/.well-known/openid-configuration` lists them all.
Differences from GitHub Actions:
- There is no `environment` claim.
- Pull request runs use `refs/pull/<index>/head` and its commit, not the merge ref and merge commit.
- `run_number` counts runs per repository, not per workflow.
+2 -2
View File
@@ -102,8 +102,8 @@ Only a workflow that runs on an event that posts a commit status (`push`, `pull_
Scoped workflows can call reusable workflows:
- A **local** reference (`uses: ./...`) resolves against the **source** repository: the repository the calling workflow's content came from, not the consuming repository.
- A **cross-repository** reference (`uses: owner/repo/...@ref`) is resolved with the **consuming repository's** read permission. If it points to a private repository, make sure every consuming repository can read it; otherwise the workflow will fail there.
- A **local** reference (`uses: ./...` or `uses: $/...`) resolves against the **source** repository: the repository the calling workflow's content came from, not the consuming repository.
- A **cross-repository** reference (`uses: owner/repo/...@ref` or `uses: self:owner/repo/...@ref`) is resolved with the **consuming repository's** read permission. If it points to a private repository, make sure every consuming repository can read it; otherwise the workflow will fail there.
A reusable workflow can live under `SCOPED_WORKFLOW_DIRS` (or `WORKFLOW_DIRS`) of the source repository.
+7 -3
View File
@@ -53,13 +53,14 @@ Valid access mode values for each scope are `read`, `write`, and `none`.
- `wiki`
- `projects`
- `packages` (if supported by the target feature)
- `id-token` (`write` allows requesting [OpenID Connect tokens](openid-connect.md), `read` grants nothing)
If you specify both `contents` and a more granular scope (like `code` or `releases`), the granular scope wins for that unit.
### Compatibility notes
Gitea supports a subset of GitHub Actions permission scopes.
GitHub-only scopes such as `statuses`, `checks`, `deployments`, `id-token`, `security-events`, and `pages` are not currently supported by Gitea Actions.
GitHub-only scopes such as `statuses`, `checks`, `deployments`, `security-events`, and `pages` are not currently supported by Gitea Actions.
Gitea also exposes repository-unit scopes that do not exist as separate scopes in GitHub Actions:
@@ -75,15 +76,18 @@ If neither the workflow nor the job defines `permissions:`, Gitea uses the confi
- **Permissive**: read and write permissions for most units in the job's repository (backwards-compatible default).
- **Restricted**: read-only permissions for `code`, `releases`, and `packages` in the job's repository, and no access to other units by default.
Neither mode includes `id-token`.
## Maximum token permissions (clamping)
You can configure a maximum permission per repository unit.
You can configure a maximum permission per repository unit and for `id-token`.
The job's effective permissions are computed as:
`effective = min(requested, maximum)`
This means workflows can reduce permissions for a job, but cannot exceed your configured maximum.
If you don't configure a maximum, the maximum defaults to `write` for all scopes.
Maximums saved before Gitea supported `id-token` deny it until you set it to `write`.
## Where to configure
@@ -97,7 +101,7 @@ Note: Repository-level token permission settings are only shown once **Repositor
## Fork pull requests
For security reasons, workflows triggered by pull requests from forks are always restricted to read-only permissions for repository contents, regardless of workflow `permissions:` or settings.
For security reasons, workflows triggered by pull requests from forks are always restricted to read-only permissions for repository contents, regardless of workflow `permissions:` or settings. They also get no OpenID Connect tokens.
## Cross-repository access
+2 -2
View File
@@ -64,8 +64,8 @@ These are used internally by the runner and actions. They are typically not need
| Name | Description | Example |
|---|---|---|
| `ACTIONS_CACHE_URL` | URL for the actions cache service. | `http://192.168.1.10:8088/` |
| `ACTIONS_ID_TOKEN_REQUEST_TOKEN` | Token for OIDC requests. Only set when configured. | `***` |
| `ACTIONS_ID_TOKEN_REQUEST_URL` | URL to request OIDC tokens. Only set when configured. | `https://gitea.com/login/oauth/access_token` |
| `ACTIONS_ID_TOKEN_REQUEST_TOKEN` | Token for OIDC requests. Only set when `id-token: write` is granted. | `***` |
| `ACTIONS_ID_TOKEN_REQUEST_URL` | URL to request OIDC tokens. Only set when `id-token: write` is granted. | `https://gitea.com/api/actions/oidc/token?` |
| `ACTIONS_RESULTS_URL` | URL for storing artifacts. | `https://gitea.com` |
| `ACTIONS_RUNTIME_TOKEN` | Authentication token for the Actions pipeline API. | `***` |
| `ACTIONS_RUNTIME_URL` | URL for the Gitea Actions pipeline API. | `https://gitea.com/api/actions_pipeline/` |
@@ -15,7 +15,7 @@ creating issues and pull requests. This will cut down on the initial back and fo
of getting some clarifying details.
It is currently not possible to provide generic issue/pull-request templates globally.
Additionally, the New Issue page URL can be suffixed with `?title=Issue+Title&body=Issue+Text` and the form will be populated with those strings. Those strings will be used instead of the template if there is one.
Additionally, the New Issue page URL can be suffixed with `?title=Issue+Title&body=Issue+Text` and the form will be populated with those strings. Those strings will be used instead of the template if there is one. See [Pre-filling Issues and Pull Requests](prefill-issues-pull-requests.md) for all parameters.
## File names
@@ -0,0 +1,42 @@
---
date: "2026-10-17T00:00:00+00:00"
slug: "prefill-issues-pull-requests"
sidebar_position: 14
---
# Pre-filling Issues and Pull Requests
Links to the pages for creating issues and pull requests can pre-fill the form with query parameters.
This lets you link users from another app, a README or a project board to a form that is already categorized.
- New issue: `/{owner}/{repo}/issues/new?…`
- New pull request: `/{owner}/{repo}/compare/{base}...{head}?…`
For example, this link opens a new issue with a title, two labels and an assignee:
```
https://gitea.example.com/owner/repo/issues/new?title=Dashboard+crashes&label=kind/bug,area/dashboard&assignee=alice
```
## Parameters
| Parameter | Value |
| ------------- | --------------------------------------------------------------------------- |
| `title` | Title |
| `body` | Description |
| `label` | Comma-separated label IDs or names |
| `labels` | Alias of `label` |
| `milestone` | Milestone ID or name |
| `project` | Comma-separated project IDs or titles |
| `assignee` | Comma-separated user IDs or usernames |
| `template` | File name of the [template](issue-pull-request-templates.md) to use |
| `field:<id>` | Value of the field with this `id` in a YAML template |
IDs and names can be mixed and names are matched case-insensitively. Values that don't match anything in the repository are ignored.
Labels include the organization's labels, and closed milestones and projects can be selected too.
Milestones and projects are only offered to users who can write to issues or pull requests, so for other users these parameters have no effect.
The values from the link take precedence over the title, description, labels and assignees of the template.
When there are several issue templates to choose from, the parameters are passed through to the chosen one.
On the compare page, any of these parameters also opens the pull request form directly, as `expand=1` does.
File diff suppressed because it is too large. Load diff
@@ -14,6 +14,8 @@ aliases:
2. 将 `https://git.example.com/foo` 反向代理到 `http://gitea:3000/foo`
3. 确保反向代理不会解码 URI。`https://git.example.com/a%2Fb`的请求应该被传递给 `http://gitea:3000/a%2Fb`。
4. 确保 `Host` 和 `X-Forwarded-Proto` 头被正确的传递给 Gitea,使 Gitea 可以看到正在访问的真实 URL。
5. 确保 Web 服务器有完整的证书(包括所有中间证书和根证书),以便 `git clone` 和 `git pull` 正常工作。
6. Gitea 会在 `/-/ws` 使用 WebSocket 连接,用于通知计数等实时更新。请确保反向代理使用 HTTP/1.1,并转发 `Upgrade` 和 `Connection` 头,参见下面的示例。否则 Gitea 会回退为定期轮询。
## 使用子路径
@@ -39,6 +41,7 @@ server {
location / {
client_max_body_size 512M;
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Connection $http_connection;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
@@ -61,8 +64,9 @@ server {
# 确保 nginx 使用未转义 URI, 按原样保持 "%2F"。 确保 nginx 去除 "/gitea" 子路径前缀, 按原样传递 "/v2"。
rewrite ^ $request_uri;
rewrite ^(/gitea)?(/.*) $2 break;
rewrite ^/(gitea($|/))?(.*) /$3 break;
proxy_pass http://127.0.0.1:3000$uri;
proxy_http_version 1.1;
# 其他的常规 HTTP 表头,见上面“使用 Nginx 作为反向代理服务”小节的配置
proxy_set_header Connection $http_connection;
@@ -107,6 +111,7 @@ server {
}
location / {
# 其他指令见上面「使用 Nginx 作为反向代理服务」小节
proxy_pass http://localhost:3000;
}
}
@@ -123,6 +128,7 @@ server {
server_name git.example.com;
location / {
# 其他指令见上面「使用 Nginx 作为反向代理服务」小节
proxy_pass http://localhost:3000;
}
}
@@ -144,6 +150,20 @@ server {
}
```
## Nginx Proxy Manager
如果使用 Nginx Proxy Manager 作为 Gitea 的反向代理,配置与原生 Nginx 略有不同。
它会[默认给自定义 location 加上一些指令](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/master/docker/rootfs/etc/nginx/conf.d/include/proxy.conf),因此上面 Nginx 配置里对应的指令不要再写一遍。否则会因为指令重复(尤其是 `proxy_set_header Host $host`)出现 `400 bad request`。
创建 `/` 自定义 location 时,只需在其配置中加入:
```nginx
client_max_body_size 512M;
```
在代理主机上启用 `Websockets Support`,Nginx Proxy Manager 会自行加入 WebSocket 相关指令。再额外添加 `proxy_http_version` 会导致 Nginx 拒绝启动。
## 使用 Apache HTTPD 作为反向代理服务
如果您想使用 Apache HTTPD 作为 Gitea 的反向代理服务,您可以为您的 Apache HTTPD 作如下配置(在 Ubuntu 中,配置文件通常在 `/etc/apache2/httpd.conf` 目录下):
@@ -154,30 +174,23 @@ server {
ProxyPreserveHost On
ProxyRequests off
AllowEncodedSlashes NoDecode
ProxyPass / http://localhost:3000/ nocanon
ProxyPass / http://localhost:3000/ nocanon upgrade=websocket
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
</VirtualHost>
```
注:必须启用以下 Apache HTTPD 组件:`proxy`, `proxy_http`
:::note
必须启用以下 Apache HTTPD 组件:`proxy`, `proxy_http`。
`upgrade=websocket` 需要 Apache HTTPD 2.4.48 或更高版本。更旧的版本**必须**去掉它,否则 Apache HTTPD 将无法启动。
:::
如果希望用 Let's Encrypt 的 webroot 验证,请在 `ProxyPass` 之前加入 `ProxyPass /.well-known !`,避免把这些请求代理到 Gitea。
## 使用 Apache HTTPD 作为反向代理服务并将 Gitea 路由至一个子路径
如果您已经有一个域名并且想与 Gitea 共享该域名,您可以增加以下配置为 Gitea 添加路由规则(在 Ubuntu 中,配置文件通常在 `/etc/apache2/httpd.conf` 目录下):
```
<VirtualHost *:80>
...
<Proxy *>
Order allow,deny
Allow from all
</Proxy>
AllowEncodedSlashes NoDecode
# 注意: 路径和 URL 后面都不要写路径符号 '/'
ProxyPass /git http://localhost:3000 nocanon
</VirtualHost>
```
```apacheconf
<VirtualHost *:80>
...
@@ -187,7 +200,7 @@ server {
</Proxy>
AllowEncodedSlashes NoDecode
# 注意: 路径和 URL 后面都不要写路径符号 '/'
ProxyPass /git http://localhost:3000 nocanon
ProxyPass /git http://localhost:3000 nocanon upgrade=websocket
ProxyPreserveHost On
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
</VirtualHost>
@@ -195,7 +208,55 @@ server {
然后您**必须**在 Gitea 的配置文件中正确的添加类似 `[server] ROOT_URL = http://git.example.com/git/` 的配置项。
注:必须启用以下 Apache HTTPD 组件:`proxy`, `proxy_http`
:::note
必须启用以下 Apache HTTPD 组件:`proxy`, `proxy_http`。`upgrade=websocket` 需要 Apache HTTPD 2.4.48 或更高版本,更旧的版本请去掉它。
:::
## 使用 Apache HTTPD 直接提供静态资源
我们可以通过将资源分为静态和动态两种类型来调节性能。
CSS 文件、JavaScript 文件、图片和字体是静态内容。首页、仓库视图和工单列表是动态内容。
Apache HTTPD 可以直接提供静态资源,并且只代理动态资源请求给 Gitea。
将 Gitea 源代码仓库的一个快照下载到 `/path/to/gitea/`。
在此之后,在本地仓库目录运行 `make frontend` 来生成静态资源。在这个情况下,我们只对 `public/` 目录感兴趣,您可以删除剩下的其他目录。
(为了生成静态资源,您需要安装一个[带 npm 的 Node ](https://nodejs.org/en/download/)和 `make`)
取决于您的用户量的大小,您可以将流量分离到两个不同的服务器,或者为静态资源配置一个 cdn。
### 单服务器节点,单域名
将 `[server] STATIC_URL_PREFIX = /_/static` 写入您的 Gitea 配置文件:
```apacheconf
<VirtualHost *:80>
...
<Proxy *>
Order allow,deny
Allow from all
</Proxy>
ProxyPass /_/static/ !
Alias /_/static/ /path/to/gitea/public/
<Directory /path/to/gitea/public/>
Options FollowSymlinks
AllowOverride None
Require all granted
</Directory>
AllowEncodedSlashes NoDecode
# 注意: 路径和 URL 后面都不要写路径符号 '/'
ProxyPass / http://localhost:3000/ nocanon upgrade=websocket
ProxyPreserveHost On
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
</VirtualHost>
```
:::note
必须启用以下 Apache HTTPD 组件:`proxy`, `proxy_http`。`upgrade=websocket` 需要 Apache HTTPD 2.4.48 或更高版本,更旧的版本请去掉它。
:::
## 使用 Caddy 作为反向代理服务
@@ -231,11 +292,13 @@ git.example.com {
- 使用 Microsoft Web Platform Installer 5.1 (WebPI) 安装 Application Request Routing(简称 ARR),或者在 [IIS.net](https://www.iis.net/downloads/microsoft/application-request-routing) 下载这个插件。
- 一但这个模块被安装到 IIS 上,你将会在 IIS 管理控制台看到一个叫做 URL Rewrite 的新图标。
- 安装 Windows 功能 `WebSocket Protocol`,否则 ARR 无法代理 Gitea 的 WebSocket 端点。
- 打开 IIS 管理控制台,在左边的列表中点击 `Gitea Proxy` 网页。在中间选中并且双击 URL Rewrite 的图标来加载 URL 重写的面板。
- 在管理控制台的右边选择 `Add Rule` 操作,并且在 `Inbound and Outbound Rules` 分类中选择 `Reverse Proxy Rule`。
- 在 Inbound Rules 中, 将 server name 设置为 Gitea 正在运行的主机以及对应端口。例如,如果你在 localhost 的 3000 端口上运行 Gitea,则设置为 `127.0.0.1:3000`。
- 启用 SSL Offloading
- 在 Outbound Rules 中,确保设置了 `Rewrite the domain names of the links in HTTP response`,并且将 `From:` 设置为上面的 server name,将 `To:` 设置为你的外部访问名称,例如:`git.example.com`
- 关闭 Application Request Routing 的 `Server Proxy Settings` 中的 `Reverse rewrite host in response headers`(在 IIS 管理器中选择服务器节点,打开 `Application Request Routing Cache`,再打开右侧的 `Server Proxy Settings`)。若保持开启,ARR 会把 `Location` 头里的主机改回内部地址,导致登录或表单提交后的重定向把浏览器送到 `127.0.0.1:3000` 而不是 `git.example.com`。
- 现在,根据下面的内容为您的网页编辑 `web.config`(将 `127.0.0.1:3000` 和 `git.example.com` 改为适当的值)
```xml
@@ -12,6 +12,7 @@ aliases:
通常情况下, `ENABLE_SWAGGER` 默认开启并且参数 `MAX_RESPONSE_ITEMS` 默认为 50。您可以从 [Config Cheat Sheet](../administration/config-cheat-sheet.md) 中获取更多配置相关信息。
<a id="authentication"></a>
## 通过 API 认证
Gitea 支持以下几种 API 认证方式:
@@ -12,7 +12,7 @@ aliases:
接下来,[安装 Node.js 和 npm](https://nodejs.org/zh-cn/download/), 这是构建 JavaScript 和 CSS 文件所需的。最低支持的 Node.js 版本是 @minNodeVersion@,建议使用最新的 LTS 版本。
**注意**:需要 Go 版本 @minGoVersion@ 或更高版本。不过,建议获取与我们的持续集成(continuous integration, CI)相同的版本,请参阅在 [Hacking on Gitea](development/hacking-on-gitea.md) 中给出的建议。
**注意**:需要 Go 版本 @minGoVersion@ 或更高版本。不过,建议获取与我们的持续集成(continuous integration, CI)相同的版本,请参阅在 [Hacking on Gitea](https://github.com/go-gitea/gitea/blob/main/docs/build-setup.md) 中给出的建议。
## 下载
@@ -53,7 +53,7 @@ git checkout @sourceVersion@ # or git checkout pr-xyz
- `go` @minGoVersion@ 或更高版本,请参阅 [这里](https://go.dev/dl/)
- `node` @minNodeVersion@ 或更高版本,并且安装 `npm`, 请参阅 [这里](https://nodejs.org/zh-cn/download/)
- `make`, 请参阅 [这里](development/hacking-on-gitea.md)
- `make`, 请参阅 [这里](https://github.com/go-gitea/gitea/blob/main/docs/build-setup.md)
为了尽可能简化编译过程,提供了各种 [make 任务](https://github.com/go-gitea/gitea/blob/main/Makefile)。
@@ -53,7 +53,7 @@ sudo chown 1000:1000 config/ data/
> 如果未为卷设置正确的权限,容器可能无法启动。
对于稳定版本,您可以使用 `:latest-rootless`、`:1-rootless`,或指定特定的版本,如: `@dockerVersion@-rootless`。如果您想使用最新的开发版本,则可以使用 `:dev-rootless` 标签。如果您想运行发布分支的最新提交,可以使用 `:1.x-dev-rootless` 标签,其中 x 是 Gitea 的次要版本号(例如:`1.16-dev-rootless`)。
对于稳定版本,您可以使用 `:latest-rootless`、`:1-rootless`,或指定特定的版本,如: `@dockerVersion@-rootless`。如果您想使用最新的开发版本,则可以使用 `:main-nightly-rootless` 标签。如果您想运行发布分支的最新提交,可以使用 `:1.x-nightly-rootless` 标签,其中 x 是 Gitea 的次要版本号(例如:`1.27-nightly-rootless`)。
## 自定义端口
@@ -14,7 +14,7 @@ Gitea 在其 Docker Hub 组织内提供自动更新的 Docker 镜像。可以始
## 基本
最简单的设置只是创建一个卷和一个网络,然后将 `docker.gitea.com/gitea:latest` 镜像作为服务启动。由于没有可用的数据库,因此可以使用 SQLite3 初始化数据库。创建一个类似 `gitea` 的目录,并将以下内容粘贴到名为 `docker-compose.yml` 的文件中。请注意,该卷应由配置文件中指定的 UID/GID 的用户/组拥有。如果您不授予卷正确的权限,则容器可能无法启动。另请注意,标签 `:latest` 将安装当前的开发版本。对于稳定的发行版,您可以使用 `:1` 或指定某个发行版,例如 `@dockerVersion@`。
最简单的设置只是创建一个卷和一个网络,然后将 `docker.gitea.com/gitea:latest` 镜像作为服务启动。由于没有可用的数据库,因此可以使用 SQLite3 初始化数据库。创建一个类似 `gitea` 的目录,并将以下内容粘贴到名为 `docker-compose.yml` 的文件中。请注意,该卷应由配置文件中指定的 UID/GID 的用户/组拥有。如果您不授予卷正确的权限,则容器可能无法启动。另请注意,标签 `:latest` 安装的是最新的稳定版本。对于稳定的发行版,您也可以使用 `:1` 或指定某个发行版,例如 `@dockerVersion@`。如果您想使用最新的开发版本,则可以使用 `:main-nightly` 标签;如果您想运行发布分支的最新提交,可以使用 `:1.x-nightly` 标签,其中 x 是 Gitea 的次要版本号(例如:`1.27-nightly`)。
```yaml
version: "3"
@@ -0,0 +1,102 @@
---
date: "2023-05-24T15:00:00+08:00"
slug: "comparison"
sidebar_position: 15
---
# 与GitHub Actions的对比
Gitea Actions旨在与GitHub Actions兼容,差异如下。
## 额外功能
<a id="absolute-action-urls"></a>
### Action URL绝对路径
`uses:`支持指向任意Git托管平台的绝对URL,例如`uses: https://gitea.com/actions/checkout@v4`。
<a id="action-prefixes"></a>
### Action前缀
`uses:`支持以下前缀:
- `self:`引用您自己的Gitea实例,例如`uses: self:owner/repo@v1`或`uses: self:owner/repo/.gitea/workflows/build.yml@v1`
- `builtin:`运行Gitea Runner内置的Action,无需下载,Job镜像中也无需Node,例如`uses: builtin:checkout`
`builtin:checkout`支持`actions/checkout`的`repository`、`ref`、`token`、`path`和`fetch-depth`输入,默认值相同,其他输入会使步骤失败。
<a id="workflow-relative-actions"></a>
### 相对于工作流的Actions
`$/`引用工作流所在的仓库和提交,或外层复合Action所在的仓库和提交,例如`uses: $/.gitea/actions/build`。
与`./`不同,它无需事先检出代码。
### 使用Go编写Actions
请参阅[创建Go Actions](https://blog.gitea.com/creating-go-actions/)。
### 支持非标准的调度语法 @yearly, @monthly, @weekly, @daily, @hourly
`schedule`还支持`@yearly`、`@monthly`、`@weekly`、`@daily`和`@hourly`,而[GitHub不支持](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule)。
## 不支持的工作流语法
### `jobs.<job_id>.environment`
[`jobs.<job_id>.environment`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idenvironment)会被忽略。
### 复杂的`runs-on`
[`runs-on`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on)支持:
- 静态字符串:`runs-on: self-hosted`
- 标签数组:`runs-on: [linux, self-hosted]`
- 字符串表达式:`runs-on: ${{ github.event_name == 'push' && 'ubuntu-latest' || 'self-hosted' }}`
- 包含表达式的数组:`runs-on: [linux, "${{ github.event_name == 'push' && 'ubuntu-latest' || 'self-hosted' }}"]`
## 缺失的功能
### 软件包仓库授权
`GITEA_TOKEN`无法发布到其所属仓库的软件包注册表,例如无法推送OCI镜像。
请改用个人访问令牌,参见[此问题](https://github.com/go-gitea/gitea/issues/23642#issuecomment-2119876692)。
### 问题匹配器
[问题匹配器](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md)会被忽略。
### 为错误创建注释
[错误注释](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-creating-an-annotation-for-an-error)会被忽略。
### 表达式
[表达式](https://docs.github.com/en/actions/learn-github-actions/expressions)支持GitHub的标准函数和上下文。
## 缺失的UI功能
### 预处理和后处理步骤
预处理和后处理步骤在Job日志中没有单独的区块。
### 服务步骤
服务步骤在Job日志中没有单独的区块。
## 不一样的行为
### `permissions`和`jobs.<job_id>.permissions`
`permissions`和`jobs.<job_id>.permissions`控制`GITEA_TOKEN`的权限,该权限受仓库和所有者设置的限制,并在fork拉取请求和跨仓库访问时受到进一步限制。
不支持`statuses`、`checks`、`deployments`、`security-events`和`pages`等GitHub专有的权限范围,而Gitea额外提供了`code`、`releases`、`wiki`和`projects`。`id-token`与GitHub相同,请参阅[OpenID Connect](openid-connect.md)。
请参阅[Actions Job令牌权限](token-permissions.md)。
### 下载Actions
未指定主机的Actions(如`uses: actions/checkout@v4`)会从`https://github.com`下载,当`[actions].DEFAULT_ACTIONS_URL`为`self`时则从您自己的实例下载。
请参阅[配置备忘单](../../administration/config-cheat-sheet.md#actions-actions)。
其他来源请参阅[Action URL绝对路径](#absolute-action-urls)、[Action前缀](#action-prefixes)和[相对于工作流的Actions](#workflow-relative-actions)。
### 上下文可用性
不检查[上下文可用性](https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability),因此`env`等上下文可以在更多地方使用。
@@ -19,7 +19,7 @@ Gitea Runner部分基于[nektos/act](https://github.com/nektos/act)的硬分支
之前,我们提到工作流文件中的 `runs-on: ubuntu-latest` 表示该Job将在具有`ubuntu-latest`标签的Runner上运行。
但是,Runner如何知道要运行 `ubuntu-latest`?答案在于将标签映射到环境。
这就是为什么在注册过程中添加自定义标签时,需要输入一些复杂内容,比如`my_custom_label:docker://centos:7`。
这就是为什么Runner的每个标签都会指定一个环境,比如`my_custom_label:docker://centos:7`。
这意味着Runner可以接受需要在`my_custom_label`上运行的Job,并通过使用`centos:7`镜像的Docker容器来运行它。
然而,Docker不是唯一的选择。
@@ -35,7 +35,6 @@ Runner 也支持直接在主机上运行Job。
- `my_custom_label:docker://node:18`:使用`node:18 Docker`镜像运行带有`my_custom_label`标签的Job。
- `my_custom_label:host`:在主机上直接运行带有`my_custom_label`标签的Job。
- `my_custom_label`:等同于`my_custom_label:host`。
- `my_custom_label:vm:ubuntu-latest`:(仅为示例,未实现)使用带有`ubuntu-latest` ISO的虚拟机运行带有`my_custom_label`标签的Job。
## 通信协议
@@ -44,7 +43,7 @@ Runner 也支持直接在主机上运行Job。
相反,我们希望重用HTTP端口,这意味着我们需要一个与HTTP兼容的协议。
因此,我们选择使用基于HTTP的gRPC。
我们使用[actions-proto-def](https://gitea.com/gitea/actions-proto-def) 和 [actions-proto-go](https://gitea.com/gitea/actions-proto-go) 进行连接。
该协议定义在[actions-proto-def](https://gitea.com/gitea/actions-proto-def)中,其Go代码位于[actionslib](https://gitea.com/gitea/actionslib)。
有关 gRPC 的更多信息,请访问[其官方网站](https://grpc.io/)。
## 网络架构
@@ -72,8 +71,8 @@ Runner 必须能够连接到Gitea以接收任务并发送执行结果回来。
### 连接 3,runner 到互联网
当您使用诸如 `actions/checkout@v4` 的一些Actions时, runner下载的是脚本,而不是Job容器。
默认情况下,它从[github.com](http://github.com/)下载,因此需要访问互联网。如果您设置的是 self,
那么默认将从您的当前Gitea实例下载,那么此步骤不需要连接到互联网。
默认情况下,它从[github.com](https://github.com/)下载这些脚本,这需要访问互联网。
如果将`DEFAULT_ACTIONS_URL`设置为`self`,或使用`uses: self:owner/repo@ref`,它会改为从您的Gitea实例下载。
它还默认从Docker Hub下载一些Docker镜像,这也需要互联网访问。
然而,互联网访问并不是绝对必需的。
@@ -27,34 +27,19 @@ DEFAULT_REPO_UNITS = ...,repo.actions
## 使用`actions/checkout@v4`等Actions时,Job容器会从何处下载脚本?
GitHub 上有成千上万个 [Actions 脚本](https://github.com/marketplace?type=actions)。
当您编写 `uses: actions/checkout@v4` 时,它默认会从 [github.com/actions/checkout](https://github.com/actions/checkout) 下载脚本。
那如果您想使用一些托管在其它平台上的脚本呢,比如在 gitea.com 上的?
默认从`https://github.com`下载,当`[actions].DEFAULT_ACTIONS_URL`为`self`时则从您自己的实例下载,参见[配置速查表](../../administration/config-cheat-sheet.md#actions-actions)。
好消息是,您可以指定要从任何位置使用Actions的URL前缀。
这是Gitea Actions中的额外语法。
例如:
要使用其他位置的Action,请指明其主机:
- `uses: https://gitea.com/xxx/xxx@xxx`
- `uses: https://github.com/xxx/xxx@xxx`
- `uses: http://your_gitea_instance.com/xxx@xxx`
注意,`https://`或`http://`前缀是必需的!
这是与 GitHub Actions 的一个区别,GitHub Actions 只允许使用托管在 GitHub 上的 actions 脚本。
但用户理应拥有权利去灵活决定如何运行 Actions。
另外,如果您希望您的 Runner 默认从您自己的 Gitea 实例下载 Actions,可以通过设置 `[actions].DEFAULT_ACTIONS_URL`进行配置。
参见[配置速查表](../../administration/config-cheat-sheet.md#actions-actions)。
- `uses: https://gitea.com/owner/repo@ref`
- `uses: http://your_gitea_instance.com/owner/repo@ref`
- `uses: self:owner/repo@ref`,用于您自己的Gitea实例
## 如何限制Runner的权限?
Runner仅具有连接到您的Gitea实例的权限。
当任何Runner接收到要运行的Job时,它将临时获得与Job关联的仓库的有限权限。
如果您想为Runner提供更多权限,允许它访问更多私有仓库或外部系统,您可以向其传递[密钥](usage/actions/secrets.md)。
对于 Actions 的细粒度权限控制是一项复杂的工作。
在未来,我们将添加更多选项以使Gitea更可配置,例如允许对仓库进行更多写访问或对同一组织中的所有仓库进行读访问。
Runner仅连接到您的Gitea实例。
对于每个Job,Runner会获得一个仅限于该Job所属仓库的`GITEA_TOKEN`,参见[Actions Job令牌权限](token-permissions.md)。
如需让Job访问更多私有仓库或外部系统,请向其传递[密钥](usage/actions/secrets.md)。
## 如何避免被黑客攻击?
@@ -75,19 +60,8 @@ Runner仅具有连接到您的Gitea实例的权限。
## Runner 支持哪些操作系统?
它在Linux、macOS和Windows上运行良好。
虽然理论上支持其他操作系统,但需要进一步测试。
需要注意的一点是,如果选择直接在主机上运行Job而不是在Job容器中运行,操作系统之间的环境差异可能会导致意外的失败。
例如,在大多数情况下,Windows上没有可用的bash,而act尝试默认使用bash运行脚本。
因此,您需要在工作流文件中将默认shell指定为`powershell`,参考[defaults.run](https://docs.github.com/zh/actions/using-workflows/workflow-syntax-for-github-actions#defaultsrun)。
```yaml
defaults:
run:
shell: powershell
```
官方发布了Linux、macOS和Windows的二进制文件。
理论上,Go和Docker支持的其他系统也可以运行。
## 为什么选择GitHub Actions?为什么不选择与GitLab CI/CD兼容的工具?
@@ -99,42 +73,13 @@ defaults:
## 如果它在多个标签上运行,例如 `runs-on: [label_a, label_b]`,会发生什么?
这是有效的语法。
它意味着它应该在具有`label_a` **和** `label_b`标签的Runner上运行,参考[GitHub Actions的工作流语法](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on)。
不幸的是,runner 并不支持这种方式。
如上所述,我们将标签映射到环境:
- `ubuntu` → `ubuntu:22.04`
- `centos` → `centos:8`
但我们需要将标签组映射到环境,例如:
- `[ubuntu]` → `ubuntu:22.04`
- `[with-gpu]` → `linux:with-gpu`
- `[ubuntu, with-gpu]` → `ubuntu:22.04_with-gpu`
我们还需要重新设计任务分配给Runner的方式。
具有`ubuntu`、`centos`或`with-gpu`的Runner并不一定表示它可以接受`[centos, with-gpu]`的Job。
因此,Runner应该通知Gitea实例它只能接受具有 `[ubuntu]`、`[centos]`、`[with-gpu]` 和 `[ubuntu, with-gpu]`的Job。
这不是一个技术问题,只是在早期设计中被忽视了。
参见[runtime.go#L65](https://gitea.com/gitea/runner/src/commit/90b8cc6a7a48f45cc28b5ef9660ebf4061fcb336/runtime/runtime.go#L65)。
目前,runner 尝试匹配标签中的每一个,并使用找到的第一个匹配项。
该Job会在拥有所有这些标签的Runner上运行,与[GitHub](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on)一致。
Runner会使用这些标签中它所拥有的第一个标签对应的环境。
## 代理标签和自定义标签对于Runner有什么区别?
![labels](/images/usage/actions/labels.png)
代理标签是由 Runner 在注册过程中向 Gitea 实例报告的。
而自定义标签则是由Gitea的管理员或组织或仓库的所有者手动添加的(取决于Runner所属的级别)。
然而,目前这方面的设计还有待改进,因为它目前存在一些不完善之处。
您可以向已注册的Runner添加自定义标签,比如 `centos`,这意味着该Runner将接收具有`runs-on: centos`的Job。
然而,Runner可能不知道要使用哪个环境来执行该标签,导致它使用默认镜像或导致逻辑死胡同。
这个默认值可能与用户的期望不符。
参见[runtime.go#L71](https://gitea.com/gitea/runner/src/commit/90b8cc6a7a48f45cc28b5ef9660ebf4061fcb336/runtime/runtime.go#L71)。
与此同时,如果您想更改Runner的标签,我们建议您重新注册Runner。
Gitea已不再有自定义标签。
Runner每次启动时都会声明其标签,标签来自`runner.labels`配置或注册时指定的标签,因此请在那里修改并重启Runner。
## Gitea Actions runner会有更多的实现吗?
@@ -165,3 +110,11 @@ defaults:
> 对于 `pull_request` 事件,在 [GitHub Actions](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request) 中 `ref` 是 `refs/pull/:prNumber/merge`,它指向这个拉取请求合并提交的一个预览。但是 Gitea 没有这种 reference。
> 因此,Gitea Actions 中 `ref` 是 `refs/pull/:prNumber/head`,它指向这个拉取请求的头分支而不是合并提交的预览。
## 如何共享私有仓库中的Actions和可复用工作流?
前往仓库的**设置** > **Actions** > **常规**页面并添加协作所有者。
协作所有者的私有仓库可以访问当前仓库中的Actions和工作流。
如需在同一用户或组织内共享,请改为在该用户或组织的**设置** > **Actions** > **常规** > **跨仓库访问**中添加这些私有仓库。
参见[跨仓库访问](token-permissions.md#cross-repository-access)。
@@ -0,0 +1,63 @@
---
date: "2026-08-23T00:00:00+00:00"
slug: "openid-connect"
sidebar_position: 40
---
# OpenID Connect
Job可以请求短期有效的OpenID Connect(OIDC)令牌来登录云服务商和其他服务,无需存储长期有效的密钥。这些令牌采用GitHub Actions的格式,因此`aws-actions/configure-aws-credentials`、`google-github-actions/auth`和`azure/login`等Action无需修改即可使用。
<a id="trusting-gitea"></a>
## 信任Gitea
在服务中将Gitea添加为OIDC身份提供方:
- Issuer:`<ROOT_URL>/api/actions/oidc`
- Audience:`<ROOT_URL>/<owner>`,除非工作流请求了其他值
然后通过[`sub`声明](#claims)上的条件限制哪些工作流可以登录。
Gitea使用其在`<APP_DATA_PATH>/jwt/actions_oidc.pem`创建的密钥签名令牌。负载均衡器后的多个实例必须共享此文件。
<a id="requesting-a-token"></a>
## 请求令牌
为工作流或Job授予`id-token`权限:
```yaml
permissions:
id-token: write
```
默认权限从不包含此权限,来自fork拉取请求的Job也永远无法获得。所有者和仓库可以通过[最大令牌权限](token-permissions.md#maximum-token-permissions-clamping)禁用它。
上述Action会自行请求令牌。在脚本中,请使用`ACTIONS_ID_TOKEN_REQUEST_URL`和`ACTIONS_ID_TOKEN_REQUEST_TOKEN`:
```shell
curl --fail --silent --show-error \
--header "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=my-service"
```
令牌位于响应的`value`字段中,五分钟后过期。
<a id="claims"></a>
## 声明
`sub`声明包含所有者和仓库的名称及ID,因此重命名或重新创建的仓库无法匹配已有的信任策略:
```text
repo:<owner>@<owner-id>/<repo>@<repo-id>:ref:<ref>
repo:<owner>@<owner-id>/<repo>@<repo-id>:pull_request
```
`pull_request`事件使用第二种格式,其他所有事件(包括`pull_request_target`)使用第一种。
其他声明与GitHub相同,例如`repository`、`ref`、`sha`、`workflow_ref`和`job_workflow_ref`。位于`<ROOT_URL>/api/actions/oidc/.well-known/openid-configuration`的发现文档列出了所有声明。
与GitHub Actions的区别:
- 没有`environment`声明。
- 拉取请求的运行使用`refs/pull/<index>/head`及其提交,而不是合并引用和合并提交。
- `run_number`按仓库计数,而不是按工作流计数。
@@ -102,8 +102,8 @@ SCOPED_WORKFLOW_DIRS = .gitea/scoped_workflows
作用域工作流可以调用可复用工作流:
- **本地**引用(`uses: ./...`)会相对于**源**仓库解析:即调用方工作流内容的来源仓库,而非消费方仓库。
- **跨仓库**引用(`uses: owner/repo/...@ref`)会以**消费方仓库的**读取权限来解析。如果它指向一个私有仓库,请确保每个消费方仓库都能读取它,否则工作流会在那里失败。
- **本地**引用(`uses: ./...` 或 `uses: $/...`)会相对于**源**仓库解析:即调用方工作流内容的来源仓库,而非消费方仓库。
- **跨仓库**引用(`uses: owner/repo/...@ref` 或 `uses: self:owner/repo/...@ref`)会以**消费方仓库的**读取权限来解析。如果它指向一个私有仓库,请确保每个消费方仓库都能读取它,否则工作流会在那里失败。
可复用工作流可以放在源仓库的 `SCOPED_WORKFLOW_DIRS`(或 `WORKFLOW_DIRS`)之下。
Loaded 100 of 1814 files, more files were not shown because too many files have changed in this diff. Show more