Skip to content

🎨 Removed the discontinued Tenor GIF provider#29194

Merged
renatoworks merged 4 commits into
mainfrom
tenor-sunset
Jul 9, 2026
Merged

🎨 Removed the discontinued Tenor GIF provider#29194
renatoworks merged 4 commits into
mainfrom
tenor-sunset

Conversation

@renatoworks

@renatoworks renatoworks commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Describe the problem you are solving

This PR removes Tenor entirely from the editor's GIF card and from Ghost's config surface. Google shut down the Tenor API for app developers on 2026-06-30, so the Tenor fallback added during the Klipy migration (TryGhost/Koenig#1932, #28109) now points at a dead endpoint. Klipy is the sole GIF provider going forward.

This is the final code phase of the Tenor → Klipy migration (Linear: PT-10, PT-11). Docs were already updated (PT-13); infra key removal (PT-12) happens separately.

Changelog for devs

  • Removed the Tenor branch from the GIF provider resolution in koenig-lexical (getGifProviderConfig now resolves Klipy or null), along with the provider-switching prop plumbing, Tenor error shapes, Tenor demo config, and all Tenor-specific tests and fixtures
  • Removed the tenor config key from Ghost: config defaults, public-config service, config output serializer, the admin-x Config type, both editors' card configs, and the related tests, fixtures and API snapshot
  • Fixed the GIF card in the Automations email editor (apps/posts): it was copied from the welcome-email editor before Klipy support landed and only ever had Tenor wiring, so its GIF card pointed at the dead API (behind the automations labs flag)
  • Removed the dead gh-tenor-* CSS in Ghost Admin (leftover from the pre-Lexical GIF selector removed in ✨ Added Klipy as a GIF provider alongside Tenor #28109) and the VITE_TENOR_API_KEY env line from the koenig demo deploy workflow
  • Folded the Klipy-specific e2e test into the main GIF test suite: keyboard insertion, Esc dismissal and error display now run against the Klipy path (previously Tenor-only), and a new unit test pins that an empty/keyless klipy config resolves to no provider

Changelog for customers

  • Removed the discontinued Tenor GIF provider from the editor; Klipy (klipy.apiKey in config) is now the only GIF provider

Notes

  • Behaviour for deployments with a stale tenor key: the key is silently ignored (nconf drops unknown keys, all readers are gone). The GIF card is hidden unless a Klipy key is configured — the intended outcome, since the Tenor API no longer answers. Klipy-configured deployments (incl. Ghost(Pro)) see no behaviour change.
  • Public API surface change: the /config/ endpoint no longer returns a tenor object. Any external integration reading config.tenor gets undefined; justified by the upstream shutdown, and old cached admin bundles degrade safely via optional chaining.
  • The comment in gif.ts about Klipy exposing a "Tenor-compatible v2 API" is kept intentionally — it explains why the client speaks v2 paths and Tenor-style request params.
  • The media.tenor.com URLs in a kg-converters mobiledoc fixture and the "Tenor Sans" font references are unrelated to the GIF API and intentionally untouched. Google confirmed existing media.tenor.com asset URLs keep serving (PT-2), so historic embedded GIFs in published content are unaffected.
  • Two secretlint-disable-next-line comments were added for the scanner's apiKey: false positive (established repo pattern); the flagged values are identifiers, not secrets.
  • Verified locally: koenig-lexical unit (116) + full acceptance suite (739), ghost/core public-config unit + config API e2e (snapshot regenerated), admin-x-settings welcome-emails acceptance (both Klipy GIF tests), lint on all affected projects. Reviewed by 4 parallel review agents (scores 8–10, no blockers).

Technical debt

  • ImageNode.isHidden checks !config?.klipy while getGifProviderConfig requires klipy.apiKey — a host passing klipy: {apiKey: null} straight through would show the card with no provider. Pre-existing mismatch (same held for tenor), no current host triggers it; follow-up to tighten to !config?.klipy?.apiKey + a null guard in makeRequest.
  • The Automations email editor fix ships without acceptance tests — apps/posts has no acceptance harness for automations yet. Follow-up: mirror the two welcome-emails GIF tests (shown with Klipy / hidden without), which would have caught the original miss.
  • checkStatus in gif.ts still reads response.headers.map['content-type'] (whatwg-fetch polyfill internal). Pre-existing, documented in the feat: Add Klipy as a GIF provider alongside Tenor Koenig#1932 PR body; unchanged here.

PR Scope (mark all that apply)

  • Improves the user experience
  • Enhances the readability of our code
  • Simplifies the maintenance of our software
  • Fixes a bug
  • Provide a core layer to allow one of the points above

Checklist before requesting a review (mark all that apply)

  • I have performed a self-review of my code
  • It's simple enough
  • The PR is not extensive
  • It includes documentation
  • I have added thorough tests.
  • Do we need to implement analytics?
  • Will this be part of a product update? If yes, please write one phrase about this update.

@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b58e37ff-7b15-4a5f-9a69-9be9f286c923

📥 Commits

Reviewing files that changed from the base of the PR and between 524dc4a and baeac77.

⛔ Files ignored due to path filters (1)
  • ghost/core/test/e2e-api/admin/__snapshots__/config.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (25)
  • .github/workflows/koenig-demo.yml
  • apps/admin-x-framework/src/api/config.ts
  • apps/admin-x-framework/src/test/responses/config.json
  • apps/admin-x-settings/src/components/settings/membership/member-emails/member-email-editor.tsx
  • apps/admin-x-settings/test/acceptance/membership/member-welcome-emails.test.ts
  • apps/posts/src/views/Automations/components/email-modal/email-editor.tsx
  • ghost/admin/app/components/koenig-lexical-editor.js
  • ghost/admin/app/styles/components/unsplash.css
  • ghost/core/core/server/api/endpoints/utils/serializers/output/config.js
  • ghost/core/core/server/services/public-config/config.js
  • ghost/core/core/shared/config/defaults.json
  • ghost/core/test/e2e-api/admin/utils.js
  • ghost/core/test/unit/server/services/public-config/config.test.js
  • koenig/koenig-lexical/README.md
  • koenig/koenig-lexical/demo/DemoApp.tsx
  • koenig/koenig-lexical/demo/HtmlOutputDemo.tsx
  • koenig/koenig-lexical/demo/RestrictedContentDemo.tsx
  • koenig/koenig-lexical/demo/utils/gifConfig.ts
  • koenig/koenig-lexical/src/components/ui/GifPlugin.tsx
  • koenig/koenig-lexical/src/components/ui/GifSelector.stories.tsx
  • koenig/koenig-lexical/src/components/ui/GifSelector.tsx
  • koenig/koenig-lexical/src/nodes/ImageNode.tsx
  • koenig/koenig-lexical/src/utils/services/gif.ts
  • koenig/koenig-lexical/test/e2e/cards/image-card.test.ts
  • koenig/koenig-lexical/test/unit/utils/gif-provider.test.ts
💤 Files with no reviewable changes (9)
  • koenig/koenig-lexical/src/components/ui/GifPlugin.tsx
  • ghost/core/core/server/services/public-config/config.js
  • ghost/admin/app/components/koenig-lexical-editor.js
  • ghost/core/core/shared/config/defaults.json
  • apps/admin-x-framework/src/api/config.ts
  • ghost/core/core/server/api/endpoints/utils/serializers/output/config.js
  • ghost/core/test/e2e-api/admin/utils.js
  • apps/admin-x-framework/src/test/responses/config.json
  • ghost/core/test/unit/server/services/public-config/config.test.js
✅ Files skipped from review due to trivial changes (1)
  • koenig/koenig-lexical/README.md
🚧 Files skipped from review as they are similar to previous changes (15)
  • koenig/koenig-lexical/src/nodes/ImageNode.tsx
  • .github/workflows/koenig-demo.yml
  • koenig/koenig-lexical/demo/RestrictedContentDemo.tsx
  • koenig/koenig-lexical/test/unit/utils/gif-provider.test.ts
  • koenig/koenig-lexical/demo/HtmlOutputDemo.tsx
  • koenig/koenig-lexical/src/components/ui/GifSelector.tsx
  • koenig/koenig-lexical/src/components/ui/GifSelector.stories.tsx
  • koenig/koenig-lexical/demo/DemoApp.tsx
  • koenig/koenig-lexical/src/utils/services/gif.ts
  • apps/admin-x-settings/src/components/settings/membership/member-emails/member-email-editor.tsx
  • apps/posts/src/views/Automations/components/email-modal/email-editor.tsx
  • koenig/koenig-lexical/demo/utils/gifConfig.ts
  • apps/admin-x-settings/test/acceptance/membership/member-welcome-emails.test.ts
  • ghost/admin/app/styles/components/unsplash.css
  • koenig/koenig-lexical/test/e2e/cards/image-card.test.ts

Walkthrough

This pull request removes Tenor GIF provider support and switches the related configuration, editor wiring, demos, and tests to Klipy. Server and shared config defaults, config serialization, public-config output, and config validation were updated to drop tenor and expose klipy instead. Koenig’s gif service, selector, plugin, and image node now use Klipy-only behavior, while admin and posts editor integrations pass Klipy config into Koenig. Demo setup, Storybook, documentation, and CSS were also updated, and the affected unit, acceptance, and e2e tests now mock Klipy and use Klipy fixtures.

Suggested reviewers: kevinansfield, jonatansberg

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: removing the discontinued Tenor GIF provider.
Description check ✅ Passed The description is directly related to the changeset and clearly explains the Tenor-to-Klipy migration cleanup.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tenor-sunset

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 524dc4a

Command Status Duration Result
nx run @tryghost/admin-x-settings:test:acceptance ✅ Succeeded 10m 36s View ↗
nx run ghost:test:ci:integration ✅ Succeeded 2m 38s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded <1s View ↗
nx run-many -t test:unit -p @tryghost/kg-markdo... ✅ Succeeded 27s View ↗
nx run ghost:test:integration ✅ Succeeded 3m 3s View ↗
nx run ghost:test:legacy ✅ Succeeded 3m 2s View ↗
nx run @tryghost/admin:build ✅ Succeeded 2m 56s View ↗
nx run ghost:test:e2e ✅ Succeeded 2m 37s View ↗
Additional runs (9) ✅ Succeeded ... View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-09 11:09:54 UTC

@renatoworks renatoworks requested a review from kevinansfield July 9, 2026 10:55
ref https://linear.app/ghost/issue/PT-10/remove-tenor-entirely-from-koenig

- Google shut down the Tenor API on 2026-06-30, so the Tenor fallback in the
  GIF card has been calling a dead endpoint since; Klipy is now the sole provider
- removes the Tenor branch from provider resolution, the provider-switching
  placeholder/prop plumbing, the Tenor error shapes, demo config, and all
  Tenor-specific tests and fixtures
- the provider-agnostic naming introduced during the Klipy migration makes this
  a pure deletion with no behaviour change for Klipy-configured deployments
ref https://linear.app/ghost/issue/PT-11/remove-tenor-config-from-ghost

- with Tenor gone from the Koenig GIF card, the tenor config key has no
  consumer left; removing it keeps the public /config/ API surface honest and
  stops self-hosters setting a key that does nothing
- drops the key from config defaults, the public-config service, the config
  output serializer, the admin Config type and both editors' card configs,
  plus the related tests, fixtures and API snapshot
…or API

ref https://linear.app/ghost/issue/PT-11/remove-tenor-config-from-ghost

- the automations email editor was copied from the welcome-email editor
  (#28117) while the Klipy migration was still in review, so it only ever got
  Tenor wiring and never picked up the Klipy config added in #28109
- behind the automations labs flag: with a tenor key configured the GIF card
  searched the shut-down API and errored; with only a klipy key it was hidden
- wires klipy through the card config exactly like member-email-editor.tsx
ref https://linear.app/ghost/issue/PT-10/remove-tenor-entirely-from-koenig

- the gh-tenor-* styles belonged to the pre-Lexical Ember GIF selector whose
  service was already removed in #28109; nothing references them
- the koenig demo deploy no longer needs the TENOR_API_KEY secret; removing
  the env line so the workflow stops depending on a secret slated for deletion
@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

triggerFileDialog: false
},
matches: ['gif', 'giphy', 'tenor', 'klipy'],
matches: ['gif', 'giphy', 'klipy'],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: It might be worth keeping tenor here so we don't break anyone's muscle-memory? Although I doubt anyone is actually typing /tenor to insert a gif card

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.40%. Comparing base (0978130) to head (baeac77).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #29194      +/-   ##
==========================================
- Coverage   74.41%   74.40%   -0.01%     
==========================================
  Files        1569     1569              
  Lines      136004   136001       -3     
  Branches    16528    16526       -2     
==========================================
- Hits       101201   101195       -6     
- Misses      33769    33772       +3     
  Partials     1034     1034              
Flag Coverage Δ
e2e-tests 76.56% <ø> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@renatoworks renatoworks merged commit 8b60ef5 into main Jul 9, 2026
51 checks passed
@renatoworks renatoworks deleted the tenor-sunset branch July 9, 2026 11:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants