diff --git a/CHANGELOG.md b/CHANGELOG.md index 59a8a92682..19be8ea68e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented in this file. +## [4.4.1] - 2025-07-09 + +### Fixed + +- Fix nearly every sub-directory being crawled as part of Vite build (#35323 by @ClearlyClaire) +- Fix assets not building when Redis is unavailable (#35321 by @oneiros) +- Fix replying from media modal or pop-in-player tagging user `@undefined` (#35317 by @ClearlyClaire) +- Fix support for special characters in various environment variables (#35314 by @mjankowski and @ClearlyClaire) +- Fix some database migrations failing for indexes manually removed by admins (#35309 by @mjankowski) + ## [4.4.0] - 2025-07-08 ### Added diff --git a/app/javascript/mastodon/features/picture_in_picture/components/footer.tsx b/app/javascript/mastodon/features/picture_in_picture/components/footer.tsx index 26cb2172a9..080aaca451 100644 --- a/app/javascript/mastodon/features/picture_in_picture/components/footer.tsx +++ b/app/javascript/mastodon/features/picture_in_picture/components/footer.tsx @@ -1,4 +1,4 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { defineMessages, useIntl } from 'react-intl'; @@ -21,6 +21,9 @@ import { openModal } from 'mastodon/actions/modal'; import { IconButton } from 'mastodon/components/icon_button'; import { useIdentity } from 'mastodon/identity_context'; import { me } from 'mastodon/initial_state'; +import type { Status } from 'mastodon/models/status'; +import { makeGetStatus } from 'mastodon/selectors'; +import type { RootState } from 'mastodon/store'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; const messages = defineMessages({ @@ -47,6 +50,11 @@ const messages = defineMessages({ open: { id: 'status.open', defaultMessage: 'Expand this status' }, }); +type GetStatusSelector = ( + state: RootState, + props: { id?: string | null; contextType?: string }, +) => Status | null; + export const Footer: React.FC<{ statusId: string; withOpenButton?: boolean; @@ -56,7 +64,8 @@ export const Footer: React.FC<{ const intl = useIntl(); const history = useHistory(); const dispatch = useAppDispatch(); - const status = useAppSelector((state) => state.statuses.get(statusId)); + const getStatus = useMemo(() => makeGetStatus(), []) as GetStatusSelector; + const status = useAppSelector((state) => getStatus(state, { id: statusId })); const accountId = status?.get('account') as string | undefined; const account = useAppSelector((state) => accountId ? state.accounts.get(accountId) : undefined, diff --git a/config/cache_buster.yml b/config/cache_buster.yml index 709c0eba88..09d6cfc6ea 100644 --- a/config/cache_buster.yml +++ b/config/cache_buster.yml @@ -1,5 +1,5 @@ shared: enabled: <%= ENV.fetch('CACHE_BUSTER_ENABLED', 'false') == 'true' %> - secret_header: <%= ENV.fetch('CACHE_BUSTER_SECRET_HEADER', nil) %> - secret: <%= ENV.fetch('CACHE_BUSTER_SECRET', nil) %> + secret_header: <%= ENV.fetch('CACHE_BUSTER_SECRET_HEADER', nil)&.to_json %> + secret: <%= ENV.fetch('CACHE_BUSTER_SECRET', nil)&.to_json %> http_method: <%= ENV.fetch('CACHE_BUSTER_HTTP_METHOD', 'GET') %> diff --git a/config/email.yml b/config/email.yml index a6d34c3006..f39aa2545a 100644 --- a/config/email.yml +++ b/config/email.yml @@ -2,14 +2,14 @@ # keys are added here. production: delivery_method: <%= ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp') %> - from_address: <%= ENV.fetch('SMTP_FROM_ADDRESS', 'notifications@localhost') %> - reply_to: <%= ENV.fetch('SMTP_REPLY_TO', nil) %> - return_path: <%= ENV.fetch('SMTP_RETURN_PATH', nil) %> + from_address: <%= ENV.fetch('SMTP_FROM_ADDRESS', 'notifications@localhost')&.to_json %> + reply_to: <%= ENV.fetch('SMTP_REPLY_TO', nil)&.to_json %> + return_path: <%= ENV.fetch('SMTP_RETURN_PATH', nil)&.to_json %> smtp_settings: port: <%= ENV.fetch('SMTP_PORT', nil) %> - address: <%= ENV.fetch('SMTP_SERVER', nil) %> - user_name: <%= ENV.fetch('SMTP_LOGIN', nil) %> - password: <%= ENV.fetch('SMTP_PASSWORD', nil) %> + address: <%= ENV.fetch('SMTP_SERVER', nil)&.to_json %> + user_name: <%= ENV.fetch('SMTP_LOGIN', nil)&.to_json %> + password: <%= ENV.fetch('SMTP_PASSWORD', nil)&.to_json %> domain: <%= ENV.fetch('SMTP_DOMAIN', ENV.fetch('LOCAL_DOMAIN', nil)) %> authentication: <%= ENV.fetch('SMTP_AUTH_METHOD', 'plain') %> ca_file: <%= ENV.fetch('SMTP_CA_FILE', '/etc/ssl/certs/ca-certificates.crt') %> @@ -22,9 +22,9 @@ production: bulk_mail: smtp_settings: port: <%= ENV.fetch('BULK_SMTP_PORT', nil) %> - address: <%= ENV.fetch('BULK_SMTP_SERVER', nil) %> - user_name: <%= ENV.fetch('BULK_SMTP_LOGIN', nil) %> - password: <%= ENV.fetch('BULK_SMTP_PASSWORD', nil) %> + address: <%= ENV.fetch('BULK_SMTP_SERVER', nil)&.to_json %> + user_name: <%= ENV.fetch('BULK_SMTP_LOGIN', nil)&.to_json %> + password: <%= ENV.fetch('BULK_SMTP_PASSWORD', nil)&.to_json %> domain: <%= ENV.fetch('BULK_SMTP_DOMAIN', ENV.fetch('LOCAL_DOMAIN', nil)) %> authentication: <%= ENV.fetch('BULK_SMTP_AUTH_METHOD', 'plain') %> ca_file: <%= ENV.fetch('BULK_SMTP_CA_FILE', '/etc/ssl/certs/ca-certificates.crt') %> diff --git a/config/initializers/settings_digests.rb b/config/initializers/settings_digests.rb index 2a5d925c70..85599ee1b0 100644 --- a/config/initializers/settings_digests.rb +++ b/config/initializers/settings_digests.rb @@ -3,7 +3,7 @@ Rails.application.config.to_prepare do custom_css = begin Setting.custom_css - rescue ActiveRecord::AdapterError # Running without a database, not migrated, no connection, etc + rescue # Running without a cache, database, not migrated, no connection, etc nil end diff --git a/config/mastodon.yml b/config/mastodon.yml index cc44375807..a97d36ddf4 100644 --- a/config/mastodon.yml +++ b/config/mastodon.yml @@ -2,14 +2,14 @@ shared: experimental_features: <%= ENV.fetch('EXPERIMENTAL_FEATURES', nil) %> limited_federation_mode: <%= (ENV.fetch('LIMITED_FEDERATION_MODE', nil) || ENV.fetch('WHITELIST_MODE', nil)) == 'true' %> - self_destruct_value: <%= ENV.fetch('SELF_DESTRUCT', nil) %> - software_update_url: <%= ENV.fetch('UPDATE_CHECK_URL', 'https://api.joinmastodon.org/update-check') %> + self_destruct_value: <%= ENV.fetch('SELF_DESTRUCT', nil)&.to_json %> + software_update_url: <%= ENV.fetch('UPDATE_CHECK_URL', 'https://api.joinmastodon.org/update-check')&.to_json %> source: - base_url: <%= ENV.fetch('SOURCE_BASE_URL', nil) %> + base_url: <%= ENV.fetch('SOURCE_BASE_URL', nil)&.to_json %> repository: <%= ENV.fetch('GITHUB_REPOSITORY', 'glitch-soc/mastodon') %> tag: <%= ENV.fetch('SOURCE_TAG', nil) %> version: - metadata: <%= ['glitch', ENV.fetch('MASTODON_VERSION_METADATA', nil)].compact_blank.join('.') %> - prerelease: <%= ENV.fetch('MASTODON_VERSION_PRERELEASE', nil) %> + metadata: <%= ['glitch', ENV.fetch('MASTODON_VERSION_METADATA', nil)].compact_blank.join('.')&.to_json %> + prerelease: <%= ENV.fetch('MASTODON_VERSION_PRERELEASE', nil)&.to_json %> test: experimental_features: <%= [ENV.fetch('EXPERIMENTAL_FEATURES', nil), 'testing_only'].compact.join(',') %> diff --git a/config/translation.yml b/config/translation.yml index e074c5d0f2..75754928ee 100644 --- a/config/translation.yml +++ b/config/translation.yml @@ -1,7 +1,7 @@ shared: deepl: - api_key: <%= ENV.fetch('DEEPL_API_KEY', nil) %> + api_key: <%= ENV.fetch('DEEPL_API_KEY', nil)&.to_json %> plan: <%= ENV.fetch('DEEPL_PLAN', 'free') %> libre_translate: - api_key: <%= ENV.fetch('LIBRE_TRANSLATE_API_KEY', nil) %> - endpoint: <%= ENV.fetch('LIBRE_TRANSLATE_ENDPOINT', nil) %> + api_key: <%= ENV.fetch('LIBRE_TRANSLATE_API_KEY', nil)&.to_json %> + endpoint: <%= ENV.fetch('LIBRE_TRANSLATE_ENDPOINT', nil)&.to_json %> diff --git a/config/vapid.yml b/config/vapid.yml index c3ee806fd6..49d8cd0de8 100644 --- a/config/vapid.yml +++ b/config/vapid.yml @@ -13,5 +13,5 @@ # https://rossta.net/blog/using-the-web-push-api-with-vapid.html # shared: - private_key: <%= ENV.fetch('VAPID_PRIVATE_KEY', nil) %> - public_key: <%= ENV.fetch('VAPID_PUBLIC_KEY', nil) %> + private_key: <%= ENV.fetch('VAPID_PRIVATE_KEY', nil)&.to_json %> + public_key: <%= ENV.fetch('VAPID_PUBLIC_KEY', nil)&.to_json %> diff --git a/db/migrate/20241014010506_remove_duplicate_indexes.rb b/db/migrate/20241014010506_remove_duplicate_indexes.rb index 50e0e6ffcf..0c71936c43 100644 --- a/db/migrate/20241014010506_remove_duplicate_indexes.rb +++ b/db/migrate/20241014010506_remove_duplicate_indexes.rb @@ -2,9 +2,11 @@ class RemoveDuplicateIndexes < ActiveRecord::Migration[7.1] def change - remove_index :account_aliases, :account_id - remove_index :account_relationship_severance_events, :account_id - remove_index :custom_filter_statuses, :status_id - remove_index :webauthn_credentials, :user_id + with_options if_exists: true do + remove_index :account_aliases, :account_id + remove_index :account_relationship_severance_events, :account_id + remove_index :custom_filter_statuses, :status_id + remove_index :webauthn_credentials, :user_id + end end end diff --git a/docker-compose.yml b/docker-compose.yml index 2e0c3c16d2..fb84eb96a1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,7 +59,7 @@ services: web: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # build: . - image: ghcr.io/glitch-soc/mastodon:v4.4.0 + image: ghcr.io/glitch-soc/mastodon:v4.4.1 restart: always env_file: .env.production command: bundle exec puma -C config/puma.rb @@ -83,7 +83,7 @@ services: # build: # dockerfile: ./streaming/Dockerfile # context: . - image: ghcr.io/glitch-soc/mastodon-streaming:v4.4.0 + image: ghcr.io/glitch-soc/mastodon-streaming:v4.4.1 restart: always env_file: .env.production command: node ./streaming/index.js @@ -102,7 +102,7 @@ services: sidekiq: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes # build: . - image: ghcr.io/glitch-soc/mastodon:v4.4.0 + image: ghcr.io/glitch-soc/mastodon:v4.4.1 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 3eed9a5206..9ccb5b5049 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ module Mastodon end def patch - 0 + 1 end def default_prerelease diff --git a/spec/configuration/email_spec.rb b/spec/configuration/email_spec.rb new file mode 100644 index 0000000000..2838beb645 --- /dev/null +++ b/spec/configuration/email_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Configuration for email', type: :feature do + context 'with special characters in SMTP_PASSWORD env variable' do + let(:password) { ']]123456789[["!:@<>/\\=' } + + around do |example| + ClimateControl.modify SMTP_PASSWORD: password do + example.run + end + end + + it 'parses value correctly' do + expect(Rails.application.config_for(:email, env: :production)) + .to include( + smtp_settings: include(password: password) + ) + end + end +end diff --git a/vite.config.mts b/vite.config.mts index c0e456d563..43228c2161 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -79,7 +79,7 @@ export const config: UserConfigFnPromise = async ({ mode, command }) => { }, }, plugins: [ - tsconfigPaths(), + tsconfigPaths({ projects: [path.resolve(__dirname, 'tsconfig.json')] }), RailsPlugin({ compress: mode === 'production' && command === 'build', sri: {