[pull] main from TryGhost:main#1136
Merged
Merged
Conversation
closes https://linear.app/ghost/issue/BER-3556/ ref https://linear.app/ghost/issue/BER-3584/ ## Summary - Added the `commentsThreads` private Labs flag and Admin private feature toggle - Added comments-ui threaded display behind the `commentsThreads` flag - Supports nested reply display with infinite depth - Hides the "Replied to" context line when threaded display is active --------- Co-authored-by: Weyland Swart <weyland.swart@gmail.com>
…27858) closes https://linear.app/tryghost/issue/HKG-1699 - Redirect storage is moving away from local disk, and the next step needs confidence that the GCS-backed implementation behaves correctly against a real object-storage-like service. - Using MinIO here gives us that coverage locally and in CI without hitting real GCS, while still exercising the storage behaviours that mocks are likely to miss, like bucket setup, auth, endpoint config, and path-style requests. - The redirects bucket is separate from the media bucket so this test setup matches the storage boundary we expect in production, without changing the existing media upload flow.
ref https://linear.app/ghost/issue/BER-3529 When the gift subscriptions feature shipped, copy for the buyer purchase confirmation email and the recipient expiry reminder email was still under review, so the strings were intentionally left as raw English literals. Copy has now been reviewed for the most part, so this PR wraps member emails into translatable strings.
ref https://linear.app/ghost/issue/NY-1267 | Off | Live | |---|---| |  |  | This adds a little "off" or "live" indicator in the automation header.
ref #27503 ref https://linear.app/ghost/issue/BER-3554 The defensive fallback `gift = 0` was added in [#27503](#27503) as a temporary safety net as Admin is deployed ahead of the server on Ghost (Pro). Now that the corresponding server change (that includes `gift` in the member count) has been rolled out fully, we can remove the defensive fallback.
ref 986f78e Let's test this middleware without stubbing.
no ref
`Check app version bump` fails any PR that changes a file under
`apps/{portal,sodo-search,comments-ui,announcement-bar,signup-form}`
without bumping that app's `version` field. Renovate never bumps app
versions when it updates dependencies, so every dep bump that touches
one of those apps' `package.json` files dies on this check — including
current security PRs (postcss, vite, others).
This change exempts diffs whose only change inside a monitored app is
`package.json`. A human PR that edits both source and `package.json` in
the same app still trips the check, so the cache-busting guarantee for
actual code changes is preserved.
## Test plan
- [ ] Trigger the workflow on this PR — script touches no monitored app
files, so the check should report no app changes detected.
- [ ] Rebase one of the stuck Renovate security PRs (e.g. #27595,
#27354) onto this branch and confirm `Check app version bump` passes.
- [ ] Open a synthetic test PR that edits a source file under
`apps/portal/src` without bumping `apps/portal/package.json` and confirm
the check still fails.
## Summary - Removed the unreachable `protectBruteForce` stub in `services/auth/passwordreset.js`. It read a `tokenSecurity[key].count` value that was never assigned anywhere in the file, so the `count >= 10` guard was always false and the in-file rate limit never triggered. - Removed the matching `tokenSecurity` constant, the `tokenLocked` message, the `// @TODO` comment pointing at #7579 (2016), and the now-unused `protectBruteForce` export. - Dropped the awaited call from `api/endpoints/authentication.js` since the function only forwarded `{options, tokenParts}` unchanged on the success path. ## Why The actual brute-force protection for the password reset flow already lives at the route layer in `web/api/endpoints/admin/routes.js`: - `PUT /authentication/password_reset` is wrapped by `shared.middleware.brute.globalBlock` (express-brute, DB-persisted in the `brute` table, 50 attempts/IP/hour by default — configurable via `spam.global_block`). - `POST /authentication/password_reset` adds `globalReset` (per-IP) and `userReset` (per-username). The TODO predated that middleware being wired up to this route, which is why the in-file counter was never finished — the protection landed at the route layer instead. The stub had no tests covering it (`grep` across `ghost/core/test` returns nothing), wasn't called from any other consumer, and recently caused a false-positive in a security report. Deleting it removes a misleading code path without changing behavior.
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [@dnd-kit/utilities](https://redirect.github.com/clauderic/dnd-kit) ([source](https://redirect.github.com/clauderic/dnd-kit/tree/HEAD/packages/utilities)) | devDependencies | pin | [`^3.2.2` → `3.2.2`](https://renovatebot.com/diffs/npm/@dnd-kit%2futilities/3.2.2/3.2.2) | | [@testing-library/jest-dom](https://redirect.github.com/testing-library/jest-dom) | devDependencies | pin | [`^6` → `6.9.1`](https://renovatebot.com/diffs/npm/@testing-library%2fjest-dom/6.9.1/6.9.1) | | [@testing-library/jest-dom](https://redirect.github.com/testing-library/jest-dom) | devDependencies | pin | [`^6.9.1` → `6.9.1`](https://renovatebot.com/diffs/npm/@testing-library%2fjest-dom/6.9.1/6.9.1) | | [@vitest/coverage-v8](https://vitest.dev/guide/coverage) ([source](https://redirect.github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8)) | devDependencies | pin | [`^4.1.2` → `4.1.5`](https://renovatebot.com/diffs/npm/@vitest%2fcoverage-v8/4.1.5/4.1.5) | | [@vitest/coverage-v8](https://vitest.dev/guide/coverage) ([source](https://redirect.github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8)) | devDependencies | pin | [`~3.2.4` → `3.2.4`](https://renovatebot.com/diffs/npm/@vitest%2fcoverage-v8/3.2.4/3.2.4) | | [@vitest/coverage-v8](https://vitest.dev/guide/coverage) ([source](https://redirect.github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8)) | devDependencies | pin | [`^1.6.1` → `1.6.1`](https://renovatebot.com/diffs/npm/@vitest%2fcoverage-v8/1.6.1/1.6.1) | | [busboy](https://redirect.github.com/mscdex/busboy) | devDependencies | pin | [`^1.6.0` → `1.6.0`](https://renovatebot.com/diffs/npm/busboy/1.6.0/1.6.0) | | [color](https://redirect.github.com/Qix-/color) | dependencies | pin | [`^5.0.3` → `5.0.3`](https://renovatebot.com/diffs/npm/color/5.0.3/5.0.3) | | [glob](https://redirect.github.com/isaacs/node-glob) | devDependencies | pin | [`^13.0.6` → `13.0.6`](https://renovatebot.com/diffs/npm/glob/13.0.6/13.0.6) | | [glob](https://redirect.github.com/isaacs/node-glob) | devDependencies | pin | [`^10.5.0` → `10.5.0`](https://renovatebot.com/diffs/npm/glob/10.5.0/10.5.0) | | [node](https://redirect.github.com/nodejs/node) | final | pinDigest | → `8b1d14e` | | [node](https://redirect.github.com/nodejs/node) | final | pinDigest | → `752ea8a` | | [on-headers](https://redirect.github.com/jshttp/on-headers) | dependencies | pin | [`^1.1.0` → `1.1.0`](https://renovatebot.com/diffs/npm/on-headers/1.1.0/1.1.0) | | redis | service | pinDigest | → `352c1fd` | | [sinon](https://sinonjs.org/) ([source](https://redirect.github.com/sinonjs/sinon)) | devDependencies | pin | [`^21.1.1` → `21.1.1`](https://renovatebot.com/diffs/npm/sinon/21.1.1/21.1.1) | | [tailwindcss](https://tailwindcss.com) ([source](https://redirect.github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss)) | devDependencies | pin | [`^4.2.2` → `4.2.2`](https://renovatebot.com/diffs/npm/tailwindcss/4.2.2/4.2.2) | | [tsc-alias](https://redirect.github.com/justkey007/tsc-alias.git) ([source](https://redirect.github.com/justkey007/tsc-alias)) | devDependencies | pin | [`^1.8.17` → `1.8.17`](https://renovatebot.com/diffs/npm/tsc-alias/1.8.17/1.8.17) |⚠️ Renovate's pin functionality [does not currently](https://redirect.github.com/renovatebot/renovate/issues/40288) wire in the release age for a package, so the Minimum Release Age checks can apply. You will need to manually validate the Minimum Release Age for these package(s). Add the preset `:preserveSemverRanges` to your config if you don't want to pin your dependencies. --- ### Configuration 📅 **Schedule**: (in timezone Etc/UTC) - Branch creation - Only on Sunday and Saturday (`* * * * 0,6`) - Between 12:00 AM and 12:59 PM, only on Monday (`* 0-12 * * 1`) - Between 09:00 PM and 11:59 PM, Monday through Friday (`* 21-23 * * 1-5`) - Between 12:00 AM and 04:59 AM, Tuesday through Saturday (`* 0-4 * * 2-6`) - Automerge - Only on Sunday and Saturday (`* * * * 0,6`) - Between 12:00 AM and 12:59 PM, only on Monday (`* 0-12 * * 1`) - Between 10:00 PM and 11:59 PM, Monday through Friday (`* 22-23 * * 1-5`) - Between 12:00 AM and 04:59 AM, Tuesday through Saturday (`* 0-4 * * 2-6`) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/TryGhost/Ghost). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMTAuMiIsInVwZGF0ZWRJblZlciI6IjQzLjE1OS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Steve Larson <9larsons@gmail.com>
no ref `apps/shade/test/.eslintrc.cjs` extended `plugin:ghost/test` (non-TS-aware) while the test files are `.tsx`, and `apps/shade/.eslintrc.cjs` was missing `root: true`. ESLint's computed ruleset for shade test files therefore only had the `ghost` plugin loaded — no `react`, `react-hooks`, or `@typescript-eslint` rules ran on them. Confirmed with `eslint --print-config`. The misconfig hid **74 pre-existing rule violations**. This PR is pure lint hygiene — no production code or dependency changes.
ref https://linear.app/ghost/issue/DES-1382/tags-list-page-header-migration - Restructures `PageHeader` (`apps/shade`) into a single purpose component - Updates `List Page` component to incorporate PageHeader, ViewBar, FilterBar with a sticky header - Shade-only change — no consumer apps touched, the components changed in this PR are not consumed in the Admin yet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )