Repository: phoenixframework/phoenix (elixir)

Issue:
Documentation for `assert_error_sent/2` does not match actual behavior
### Environment

* Elixir version (elixir -v): Elixir 1.14.2 (compiled with Erlang/OTP 25)
* Phoenix version (mix deps): 1.7.3
* Operating system: macOS 12.6.3

### Expected behavior

Test as described in the example for [assert_error_sent/2](https://hexdocs.pm/phoenix/1.7.3/Phoenix.ConnTest.html#assert_error_sent/2) to be working


### Actual behavior

```
  1) test GET /users/not-found (VerifyAssertErrorSentWeb.ErrorHTMLTest)
     test/verify_assert_error_sent_web/controllers/error_html_test.exs:16
     expected error to be sent as 404 status, but response sent 404 without error
     code: assert_error_sent(:not_found, fn ->
     stacktrace:
       (phoenix 1.7.3) lib/phoenix/test/conn_test.ex:669: Phoenix.ConnTest.assert_error_sent/2
       test/verify_assert_error_sent_web/controllers/error_html_test.exs:17: (test)
```

### Context

This is introduced by https://github.com/phoenixframework/phoenix/pull/5445, if I revert those changes, my test succeed again. I do not know if `RenderErrors.__catch__/5`, `assert_error_sent/2` or the documentation needs to be changed.

Pull request:
Replace 'not found' with 'internal server error' in assert_error_sent/2 examples
Replace 'not found' with 'internal server error' in assert_error_sent/2 code examples. 
This could reduce confusion since "not found" errors are no re-raised.
Closes #5480

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d7920768..37e408649 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -46,6 +46,14 @@ styled with Tailwind CSS by default. You can opt-out of Tailwind CSS with the `-
 flag (the Tailwind CSS classes are kept in the generated components as reference for
 future styling).
 
+## 1.7.8
+
+### Bug Fixes
+  * Fix `mix phx.gen.release --docker` failing with `:http_util` error on Elixir v1.15
+
+## Enhancements
+  * Add support for static resources with fragments in `~p`
+
 ## 1.7.7 (2023-07-10)
 
 ## Enhancements
diff --git a/assets/package-lock.json b/assets/package-lock.json
index a2bc098fa..ada1a8aa7 100644
--- a/assets/package-lock.json
+++ b/assets/package-lock.json
@@ -9067,9 +9067,9 @@
       }
     },
     "node_modules/word-wrap": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
-      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
+      "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
       "dev": true,
       "engines": {
         "node": ">=0.10.0"
@@ -16188,9 +16188,9 @@
       }
     },
     "word-wrap": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
-      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
+      "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
       "dev": true
     },
     "workerpool": {
diff --git a/guides/authentication/mix_phx_gen_auth.md b/guides/authentication/mix_phx_gen_auth.md
index 4597b8b87..a5776f39a 100644
--- a/guides/authentication/mix_phx_gen_auth.md
+++ b/guides/authentication/mix_phx_gen_auth.md
@@ -118,6 +118,7 @@ Check out `mix phx.gen.auth` for more details, such as using a different passwor
 
 The following links have more information regarding the motivation and design of the code this generates.
 
+  * Berenice Medel's blog post on generating LiveViews for authentication (rather than conventional Controllers & Views) - [Bringing Phoenix Authentication to Life](https://fly.io/phoenix-files/phx-gen-auth/)
   * José Valim's blog post - [An upcoming authentication solution for Phoenix](https://dashbit.co/blog/a-new-authentication-solution-for-phoenix)
   * The [original `phx_gen_auth` repo][phx_gen_auth repo] (for Phoenix 1.5 applications) - This is a great resource to see discussions around decisions that have been made in earlier versions of the project.
   * [Original pull request on bare Phoenix app][auth PR]
diff --git a/guides/components.md b/guides/components.md
index 721649ac2..65934210b 100644
--- a/guides/components.md
+++ b/guides/components.md
@@ -183,27 +183,27 @@ In other words, after rendering your page, the result is placed in the `@inner_c
 
 Phoenix provides all kinds of conveniences to control which layout should be rendered. For example, the `Phoenix.Controller` module provides the `put_root_layout/2` function for us to switch _root layouts_. This takes `conn` as its first argument and a keyword list of formats and their layouts. You can set it to `false` to disable the layout altogether.
 
-You can edit the `home` action of `PageController` in `lib/hello_web/controllers/page_controller.ex` to look like this.
+You can edit the `index` action of `HelloController` in `lib/hello_web/controllers/hello_controller.ex` to look like this.
 
 ```elixir
-def home(conn, _params) do
+def index(conn, _params) do
   conn
   |> put_root_layout(html: false)
-  |> render(:home)
+  |> render(:index)
 end
 ```
 
-After reloading [http://localhost:4000/](http://localhost:4000/), we should see a very different page, one with no title, logo image, or CSS styling at all.
+After reloading [http://localhost:4000/hello](http://localhost:4000/hello), we should see a very different page, one with no title or CSS styling at all.
 
 To customize the application layout, we invoke a similar function named `put_layout/2`. Let's actually create another layout and render the index template into it. As an example, let's say we had a different layout for the admin section of our application which didn't have the logo image. To do this, copy the existing `app.html.heex` to a new file `admin.html.heex` in the same directory `lib/hello_web/components/layouts`. Then remove everything inside the `<header>...</header>` tags (or change it to whatever you desire) in the new file.
 
-Now, in the `home` action of the controller of `lib/hello_web/controllers/page_controller.ex`, add the following:
+Now, in the `index` action of the controller of `lib/hello_web/controllers/hello_controller.ex`, add the following:
 
 ```elixir
-def home(conn, _params) do
+def index(conn, _params) do
   conn
   |> put_layout(html: :admin)
-  |> render(:home)
+  |> render(:index)
 end
 ```
 
diff --git a/guides/contexts.md b/guides/contexts.md
index 7ce19df4e..77ca267ad 100644
--- a/guides/contexts.md
+++ b/guides/contexts.md
@@ -800,17 +800,17 @@ Let's implement the new interface for the `ShoppingCart` context API in `lib/hel
 -  alias Hello.ShoppingCart.Cart
 +  alias Hello.ShoppingCart.{Cart, CartItem}
 
-  def get_cart_by_user_uuid(user_uuid) do
-    Repo.one(
-      from(c in Cart,
-        where: c.user_uuid == ^user_uuid,
-        left_join: i in assoc(c, :items),
-        left_join: p in assoc(i, :product),
-        order_by: [asc: i.inserted_at],
-        preload: [items: {i, product: p}]
-      )
-    )
-  end
++  def get_cart_by_user_uuid(user_uuid) do
++    Repo.one(
++      from(c in Cart,
++        where: c.user_uuid == ^user_uuid,
++        left_join: i in assoc(c, :items),
++        left_join: p in assoc(i, :product),
++        order_by: [asc: i.inserted_at],
++        preload: [items: {i, product: p}]
++      )
++    )
++  end
 
 - def create_cart(attrs \\ %{}) do
 -   %Cart{}
diff --git a/guides/deployment/releases.md b/guides/deployment/releases.md
index a492533da..a78989975 100644
--- a/guides/deployment/releases.md
+++ b/guides/deployment/releases.md
@@ -70,9 +70,9 @@ To list all commands:
 
 ```
 
-The `phx.gen.release` task generated a few files for us to assist in releases. First, it created `server` and `migrate` *overlay* scripts for conveniently running the phoenix server inside a release or invoking migrations from a release. The files in the `rel/overlays` directory are copied into every release environment. Next, it generated a `release.ex` file which is used to invoked Ecto migrations without a dependency on `mix` itself.
+The `phx.gen.release` task generated a few files for us to assist in releases. First, it created `server` and `migrate` *overlay* scripts for conveniently running the phoenix server inside a release or invoking migrations from a release. The files in the `rel/overlays` directory are copied into every release environment. Next, it generated a `release.ex` file which is used to invoke Ecto migrations without a dependency on `mix` itself.
 
-*Note*: If you are a docker user, you can pass the `--docker` flag to `mix phx.gen.release` to generate a Dockerfile ready for deployment.
+*Note*: If you are a Docker user, you can pass the `--docker` flag to `mix phx.gen.release` to generate a Dockerfile ready for deployment.
 
 Next, we can invoke `mix release` to build the release:
 
diff --git a/guides/introduction/community.md b/guides/introduction/community.md
index 6f2e9225b..a5142f926 100644
--- a/guides/introduction/community.md
+++ b/guides/introduction/community.md
@@ -51,3 +51,5 @@ There are a number of places to connect with community members at all experience
   * [LearnPhoenix.tv: Learn how to Build Fast, Dependable Web Apps with Phoenix (2017)](https://www.learnphoenix.tv/)
 
   * [LearnPhoenix.io: Build Scalable, Real-Time Apps with Phoenix, React, and React Native (2016)](https://www.learnphoenix.io/)
+
+  * [Phoenix on Rails: Elixir and Phoenix for Ruby on Rails developers (by George Arrowsmith - 2023)](https://phoenixonrails.com)
diff --git a/guides/real_time/channels.md b/guides/real_time/channels.md
index f3d7a01b4..bce3d8d60 100644
--- a/guides/real_time/channels.md
+++ b/guides/real_time/channels.md
@@ -410,7 +410,7 @@ Now our `conn.assigns` contains the `current_user` and `user_token`.
 
 ### Step 2 - Pass the Token to the JavaScript
 
-Next, we need to pass this token to JavaScript. We can do so inside a script tag in `web/templates/layout/app.html.heex` right above the app.js script, as follows:
+Next, we need to pass this token to JavaScript. We can do so inside a script tag in `lib/hello_web/components/layouts/app.html.heex` right above the app.js script, as follows:
 
 ```heex
 <script>window.userToken = "<%= assigns[:user_token] %>";</script>
@@ -419,7 +419,7 @@ Next, we need to pass this token to JavaScript. We can do so inside a script tag
 
 ### Step 3 - Pass the Token to the Socket Constructor and Verify
 
-We also need to pass the `:params` to the socket constructor and verify the user token in the `connect/3` function. To do so, edit `web/channels/user_socket.ex`, as follows:
+We also need to pass the `:params` to the socket constructor and verify the user token in the `connect/3` function. To do so, edit `lib/hello_web/channels/user_socket.ex`, as follows:
 
 ```elixir
 def connect(%{"token" => token}, socket, _connect_info) do
@@ -461,7 +461,7 @@ channel.join()
 export default socket
 ```
 
-Note that token authentication is preferable since it's transport agnostic and well-suited for long running-connections like channels, as opposed to using sessions or authentication approaches.
+Note that token authentication is preferable since it's transport agnostic and well-suited for long running-connections like channels, as opposed to using sessions or other authentication approaches.
 
 ## Fault Tolerance and Reliability Guarantees
 
diff --git a/installer/templates/phx_web/components/core_components.ex b/installer/templates/phx_web/components/core_components.ex
index b686599bc..2fe64b24b 100644
--- a/installer/templates/phx_web/components/core_components.ex
+++ b/installer/templates/phx_web/components/core_components.ex
@@ -2,9 +2,9 @@ defmodule <%= @web_namespace %>.CoreComponents do
   @moduledoc """
   Provides core UI components.
 
-  At the first glance, this module may seem daunting, but its goal is
-  to provide some core building blocks in your application, such as modals,
-  tables, and forms. The components are mostly markup and well documented
+  At first glance, this module may seem daunting, but its goal is to provide
+  core building blocks for your application, such as modals, tables, and
+  forms. The components consist mostly of markup and are well-documented
   with doc strings and declarative assigns. You may customize and style
   them in any way you want, based on your application growth and needs.
 
@@ -97,7 +97,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
       <.flash kind={:info} flash={@flash} />
       <.flash kind={:info} phx-mounted={show("#flash")}>Welcome Back!</.flash>
   """
-  attr :id, :string, default: "flash", doc: "the optional id of flash container"
+  attr :id, :string, default: nil, doc: "the optional id of flash container"
   attr :flash, :map, default: %{}, doc: "the map of flash messages to display"
   attr :title, :string, default: nil
   attr :kind, :atom, values: [:info, :error], doc: "used for styling and flash lookup"
@@ -109,7 +109,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
     ~H"""
     <div
       :if={msg = render_slot(@inner_block) || Phoenix.Flash.get(@flash, @kind)}
-      id={@id}
+      id={@id || "flash-#{@kind}"}
       phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
       role="alert"
       class={[
@@ -140,33 +140,36 @@ defmodule <%= @web_namespace %>.CoreComponents do
       <.flash_group flash={@flash} />
   """
   attr :flash, :map, required: true, doc: "the map of flash messages"
+  attr :id, :string, default: "flash-group", doc: "the optional id of flash container"
 
   def flash_group(assigns) do
     ~H"""
-    <.flash kind={:info} title="Success!" flash={@flash} />
-    <.flash kind={:error} title="Error!" flash={@flash} />
-    <.flash
-      id="client-error"
-      kind={:error}
-      title="We can't find the internet"
-      phx-disconnected={show(".phx-client-error #client-error")}
-      phx-connected={hide("#client-error")}
-      hidden
-    >
-      Attempting to reconnect <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
-    </.flash>
-
-    <.flash
-      id="server-error"
-      kind={:error}
-      title="Something went wrong!"
-      phx-disconnected={show(".phx-server-error #server-error")}
-      phx-connected={hide("#server-error")}
-      hidden
-    >
-      Hang in there while we get back on track
-      <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
-    </.flash>
+    <div id={@id}>
+      <.flash kind={:info} title="Success!" flash={@flash} />
+      <.flash kind={:error} title="Error!" flash={@flash} />
+      <.flash
+        id="client-error"
+        kind={:error}
+        title="We can't find the internet"
+        phx-disconnected={show(".phx-client-error #client-error")}
+        phx-connected={hide("#client-error")}
+        hidden
+      >
+        Attempting to reconnect <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+      </.flash>
+
+      <.flash
+        id="server-error"
+        kind={:error}
+        title="Something went wrong!"
+        phx-disconnected={show(".phx-server-error #server-error")}
+        phx-connected={hide("#server-error")}
+        hidden
+      >
+        Hang in there while we get back on track
+        <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+      </.flash>
+    </div>
     """
   end
 
@@ -470,8 +473,10 @@ defmodule <%= @web_namespace %>.CoreComponents do
       <table class="w-[40rem] mt-11 sm:w-full">
         <thead class="text-sm text-left leading-6 text-zinc-500">
           <tr>
-            <th :for={col <- @col} class="p-0 pr-6 pb-4 font-normal"><%%= col[:label] %></th>
-            <th class="relative p-0 pb-4"><span class="sr-only"><%= if @gettext do %><%%= gettext("Actions") %><% else %>Actions<% end %></span></th>
+            <th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal"><%%= col[:label] %></th>
+            <th :if={@action != []} class="relative p-0 pb-4">
+              <span class="sr-only"><%= if @gettext do %><%%= gettext("Actions") %><% else %>Actions<% end %></span>
+            </th>
           </tr>
         </thead>
         <tbody
diff --git a/lib/mix/tasks/phx.gen.auth.ex b/lib/mix/tasks/phx.gen.auth.ex
index db4d40065..3401f5670 100644
--- a/lib/mix/tasks/phx.gen.auth.ex
+++ b/lib/mix/tasks/phx.gen.auth.ex
@@ -2,7 +2,7 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
   @shortdoc "Generates authentication logic for a resource"
 
   @moduledoc """
-  Generates authentication logic for a resource.
+  Generates authentication logic and related views for a resource.
 
       $ mix phx.gen.auth Accounts User users
 
@@ -12,6 +12,20 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
   Additional information and security considerations are detailed in the
   [`mix phx.gen.auth` guide](mix_phx_gen_auth.html).
 
+  ## LiveView vs conventional Controllers & Views
+
+  Authentication views can either be generated to use LiveView by passing
+  the `--live` option, or they can use conventional Phoenix
+  Controllers & Views by passing `--no-live`.
+
+  If neither of these options are provided, a prompt will be displayed.
+
+  Using the `--live` option is advised if you plan on using LiveView
+  elsewhere in your application. The user experience when navigating between
+  LiveViews can be tightly controlled, allowing you to let your users navigate
+  to authentication views without necessarily triggering a new HTTP request
+  each time (which would result in a full page load).
+
   ## Password hashing
 
   The password hashing mechanism defaults to `bcrypt` for
diff --git a/lib/mix/tasks/phx.gen.json.ex b/lib/mix/tasks/phx.gen.json.ex
index e2c4a8a00..dbe787845 100644
--- a/lib/mix/tasks/phx.gen.json.ex
+++ b/lib/mix/tasks/phx.gen.json.ex
@@ -140,7 +140,7 @@ defmodule Mix.Tasks.Phx.Gen.Json do
     [
       {:eex, "controller.ex", Path.join([controller_pre, "#{singular}_controller.ex"])},
       {:eex, "json.ex", Path.join([controller_pre, "#{singular}_json.ex"])},
-      {:new_eex, "changeset_json.ex", Path.join([controller_pre, "changeset_json.ex"])},
+      {:new_eex, "changeset_json.ex", Path.join([web, "controllers/changeset_json.ex"])},
       {:eex, "controller_test.exs", Path.join([test_pre, "#{singular}_controller_test.exs"])},
       {:new_eex, "fallback_controller.ex", Path.join([web, "controllers/fallback_controller.ex"])}
     ]
diff --git a/lib/mix/tasks/phx.gen.release.ex b/lib/mix/tasks/phx.gen.release.ex
index dc97c567e..d1b09db61 100644
--- a/lib/mix/tasks/phx.gen.release.ex
+++ b/lib/mix/tasks/phx.gen.release.ex
@@ -232,12 +232,19 @@ defmodule Mix.Tasks.Phx.Gen.Release do
     end
   end
 
+  defp ensure_app!(app) do
+    if function_exported?(Mix, :ensure_application!, 1) do
+      apply(Mix, :ensure_application!, [app])
+    else
+      {:ok, _} = Application.ensure_all_started(app)
+    end
+  end
+
   defp fetch_body!(url) do
     url = String.to_charlist(url)
     Logger.debug("Fetching latest image information from #{url}")
-
-    {:ok, _} = Application.ensure_all_started(:inets)
-    {:ok, _} = Application.ensure_all_started(:ssl)
+    ensure_app!(:inets)
+    ensure_app!(:ssl)
 
     if proxy = System.get_env("HTTP_PROXY") || System.get_env("http_proxy") do
       Logger.debug("Using HTTP_PROXY: #{proxy}")
diff --git a/lib/mix/tasks/phx.routes.ex b/lib/mix/tasks/phx.routes.ex
index d3d090400..d12348c4a 100644
--- a/lib/mix/tasks/phx.routes.ex
+++ b/lib/mix/tasks/phx.routes.ex
@@ -34,6 +34,7 @@ defmodule Mix.Tasks.Phx.Routes do
   ## Options
 
     * `--info` - locate the controller function definition called by the given url
+    * `--method` - what HTTP method to use with the given url, only works when used with `--info` and defaults to `get`
 
   ## Examples
 
@@ -51,6 +52,13 @@ defmodule Mix.Tasks.Phx.Routes do
         Module: RouteInfoTestWeb.PageController
         Function: :index
         /home/my_app/controllers/page_controller.ex:4
+
+  Print information about the controller function called by a specified url and HTTP method:
+
+      $ mix phx.routes --info http://0.0.0.0:4000/users --method post
+        Module: RouteInfoTestWeb.UserController
+        Function: :create
+        /home/my_app/controllers/user_controller.ex:24
   """
 
   @doc false
@@ -78,10 +86,11 @@ defmodule Mix.Tasks.Phx.Routes do
     end
   end
 
-  def get_url_info(url, {router_mod, _opts}) do
+  def get_url_info(url, {router_mod, opts}) do
     %{path: path} = URI.parse(url)
 
-    meta = Phoenix.Router.route_info(router_mod, "GET", path, "")
+    method = opts |> Keyword.get(:method, "get") |> String.upcase()
+    meta = Phoenix.Router.route_info(router_mod, method, path, "")
     %{plug: plug, plug_opts: plug_opts} = meta
 
     {module, func_name} =
diff --git a/lib/phoenix/channel.ex b/lib/phoenix/channel.ex
index 8080aff3c..48ac52850 100644
--- a/lib/phoenix/channel.ex
+++ b/lib/phoenix/channel.ex
@@ -347,7 +347,7 @@ defmodule Phoenix.Channel do
   alias Phoenix.Socket
   alias Phoenix.Channel.Server
 
-  @type payload :: map | {:binary, binary}
+  @type payload :: map | term | {:binary, binary}
   @type reply :: status :: atom | {status :: atom, response :: payload}
   @type socket_ref ::
           {transport_pid :: Pid, serializer :: module, topic :: binary, ref :: binary,
@@ -359,6 +359,8 @@ defmodule Phoenix.Channel do
   To authorize a socket, return `{:ok, socket}` or `{:ok, reply, socket}`. To
   refuse authorization, return `{:error, reason}`.
 
+  Payloads are serialized before sending with the configured serializer.
+
   ## Example
 
       def join("room:lobby", payload, socket) do
@@ -378,12 +380,13 @@ defmodule Phoenix.Channel do
   @doc """
   Handle incoming `event`s.
 
+  Payloads are serialized before sending with the configured serializer.
+
   ## Example
 
       def handle_in("ping", payload, socket) do
         {:reply, {:ok, payload}, socket}
       end
-
   """
   @callback handle_in(event :: String.t(), payload :: payload, socket :: Socket.t()) ::
               {:noreply, Socket.t()}
@@ -663,6 +666,8 @@ defmodule Phoenix.Channel do
   client may expect only one reply; in that case, `push/3` would be preferable
   for the additional messages.
 
+  Payloads are serialized before sending with the configured serializer.
+
   ## Examples
 
       def handle_in("work", payload, socket) do
diff --git a/lib/phoenix/channel/server.ex b/lib/phoenix/channel/server.ex
index e3f2ff686..b5b4906d6 100644
--- a/lib/phoenix/channel/server.ex
+++ b/lib/phoenix/channel/server.ex
@@ -235,6 +235,8 @@ defmodule Phoenix.Channel.Server do
   @doc """
   Pushes a message with the given topic, event and payload
   to the given process.
+
+  Payloads are serialized before sending with the configured serializer.
   """
   def push(pid, join_ref, topic, event, payload, serializer)
       when is_binary(topic) and is_binary(event) do
@@ -245,6 +247,8 @@ defmodule Phoenix.Channel.Server do
 
   @doc """
   Replies to a given ref to the transport process.
+
+  Payloads are serialized before sending with the configured serializer.
   """
   def reply(pid, join_ref, ref, topic, {status, payload}, serializer)
       when is_binary(topic) do
diff --git a/lib/phoenix/code_reloader/proxy.ex b/lib/phoenix/code_reloader/proxy.ex
index 2ada872bf..c6a6b0fd0 100644
--- a/lib/phoenix/code_reloader/proxy.ex
+++ b/lib/phoenix/code_reloader/proxy.ex
@@ -62,10 +62,14 @@ defmodule Phoenix.CodeReloader.Proxy do
     "\n#{message}\n"
   end
 
-  defp diagnostic_to_chars(%{severity: severity, message: message, file: file, position: position}) do
+  defp diagnostic_to_chars(%{severity: severity, message: message, file: file, position: position}) when is_binary(file) do
     "\n#{severity}: #{message}\n  #{Path.relative_to_cwd(file)}#{position(position)}\n"
   end
 
+  defp diagnostic_to_chars(%{severity: severity, message: message}) do
+    "\n#{severity}: #{message}\n"
+  end
+
   defp position({line, col}), do: ":#{line}:#{col}"
   defp position(line) when is_integer(line) and line > 0, do: ":#{line}"
   defp position(_), do: ""
diff --git a/lib/phoenix/endpoint.ex b/lib/phoenix/endpoint.ex
index 7af9c3991..551456970 100644
--- a/lib/phoenix/endpoint.ex
+++ b/lib/phoenix/endpoint.ex
@@ -578,7 +578,20 @@ defmodule Phoenix.Endpoint do
       @doc """
       Generates a route to a static file in `priv/static`.
       """
-      def static_path(path), do: persistent!().static_path <> elem(static_lookup(path), 0)
+      def static_path(path) do
+        {path, fragment} = path_and_fragment(path)
+
+        persistent!().static_path <> elem(static_lookup(path), 0) <> fragment
+      end
+
+      defp path_and_fragment(path_incl_fragment) do
+        path_incl_fragment
+        |> String.split("#", parts: 2)
+        |> case do
+          [path, fragment] -> {path, "#" <> fragment}
+          [path | _] -> {path, ""}
+        end
+      end
 
       @doc """
       Generates a base64-encoded cryptographic hash (sha512) to a static file
diff --git a/lib/phoenix/router.ex b/lib/phoenix/router.ex
index 0039ed72f..bcc589db7 100644
--- a/lib/phoenix/router.ex
+++ b/lib/phoenix/router.ex
@@ -853,7 +853,13 @@ defmodule Phoenix.Router do
   @doc """
   Defines a list of plugs (and pipelines) to send the connection through.
 
-  See `pipeline/2` for more information.
+  Plugs are specified using the atom name of any imported 2-arity function
+  which takes a `%Plug.Conn{}` and options and returns a `%Plug.Conn{}`; for
+  example, `:require_authenticated_user`.
+
+  Pipelines are defined in the router; see `pipeline/2` for more information.
+
+      pipe_through [:my_imported_function, :my_pipeline]
   """
   defmacro pipe_through(pipes) do
     pipes =
diff --git a/lib/phoenix/socket.ex b/lib/phoenix/socket.ex
index e30e665e3..468c0c043 100644
--- a/lib/phoenix/socket.ex
+++ b/lib/phoenix/socket.ex
@@ -555,13 +555,13 @@ defmodule Phoenix.Socket do
             {:ok, serializer}
 
           :error ->
-            Logger.error "The client's requested transport version \"#{vsn}\" " <>
+            Logger.warning "The client's requested transport version \"#{vsn}\" " <>
                           "does not match server's version requirements of #{inspect serializers}"
             :error
         end
 
       :error ->
-        Logger.error "Client sent invalid transport version \"#{vsn}\""
+        Logger.warning "Client sent invalid transport version \"#{vsn}\""
         :error
     end
   end
@@ -599,7 +599,7 @@ defmodule Phoenix.Socket do
             {:ok, {state, %{socket | id: id}}}
 
           invalid ->
-            Logger.error "#{inspect handler}.id/1 returned invalid identifier " <>
+            Logger.warning "#{inspect handler}.id/1 returned invalid identifier " <>
                            "#{inspect invalid}. Expected nil or a string."
             :error
         end
diff --git a/priv/templates/phx.gen.live/core_components.ex b/priv/templates/phx.gen.live/core_components.ex
index b686599bc..2fe64b24b 100644
--- a/priv/templates/phx.gen.live/core_components.ex
+++ b/priv/templates/phx.gen.live/core_components.ex
@@ -2,9 +2,9 @@ defmodule <%= @web_namespace %>.CoreComponents do
   @moduledoc """
   Provides core UI components.
 
-  At the first glance, this module may seem daunting, but its goal is
-  to provide some core building blocks in your application, such as modals,
-  tables, and forms. The components are mostly markup and well documented
+  At first glance, this module may seem daunting, but its goal is to provide
+  core building blocks for your application, such as modals, tables, and
+  forms. The components consist mostly of markup and are well-documented
   with doc strings and declarative assigns. You may customize and style
   them in any way you want, based on your application growth and needs.
 
@@ -97,7 +97,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
       <.flash kind={:info} flash={@flash} />
       <.flash kind={:info} phx-mounted={show("#flash")}>Welcome Back!</.flash>
   """
-  attr :id, :string, default: "flash", doc: "the optional id of flash container"
+  attr :id, :string, default: nil, doc: "the optional id of flash container"
   attr :flash, :map, default: %{}, doc: "the map of flash messages to display"
   attr :title, :string, default: nil
   attr :kind, :atom, values: [:info, :error], doc: "used for styling and flash lookup"
@@ -109,7 +109,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
     ~H"""
     <div
       :if={msg = render_slot(@inner_block) || Phoenix.Flash.get(@flash, @kind)}
-      id={@id}
+      id={@id || "flash-#{@kind}"}
       phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
       role="alert"
       class={[
@@ -140,33 +140,36 @@ defmodule <%= @web_namespace %>.CoreComponents do
       <.flash_group flash={@flash} />
   """
   attr :flash, :map, required: true, doc: "the map of flash messages"
+  attr :id, :string, default: "flash-group", doc: "the optional id of flash container"
 
   def flash_group(assigns) do
     ~H"""
-    <.flash kind={:info} title="Success!" flash={@flash} />
-    <.flash kind={:error} title="Error!" flash={@flash} />
-    <.flash
-      id="client-error"
-      kind={:error}
-      title="We can't find the internet"
-      phx-disconnected={show(".phx-client-error #client-error")}
-      phx-connected={hide("#client-error")}
-      hidden
-    >
-      Attempting to reconnect <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
-    </.flash>
-
-    <.flash
-      id="server-error"
-      kind={:error}
-      title="Something went wrong!"
-      phx-disconnected={show(".phx-server-error #server-error")}
-      phx-connected={hide("#server-error")}
-      hidden
-    >
-      Hang in there while we get back on track
-      <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
-    </.flash>
+    <div id={@id}>
+      <.flash kind={:info} title="Success!" flash={@flash} />
+      <.flash kind={:error} title="Error!" flash={@flash} />
+      <.flash
+        id="client-error"
+        kind={:error}
+        title="We can't find the internet"
+        phx-disconnected={show(".phx-client-error #client-error")}
+        phx-connected={hide("#client-error")}
+        hidden
+      >
+        Attempting to reconnect <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+      </.flash>
+
+      <.flash
+        id="server-error"
+        kind={:error}
+        title="Something went wrong!"
+        phx-disconnected={show(".phx-server-error #server-error")}
+        phx-connected={hide("#server-error")}
+        hidden
+      >
+        Hang in there while we get back on track
+        <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+      </.flash>
+    </div>
     """
   end
 
@@ -470,8 +473,10 @@ defmodule <%= @web_namespace %>.CoreComponents do
       <table class="w-[40rem] mt-11 sm:w-full">
         <thead class="text-sm text-left leading-6 text-zinc-500">
           <tr>
-            <th :for={col <- @col} class="p-0 pr-6 pb-4 font-normal"><%%= col[:label] %></th>
-            <th class="relative p-0 pb-4"><span class="sr-only"><%= if @gettext do %><%%= gettext("Actions") %><% else %>Actions<% end %></span></th>
+            <th :for={col <- @col} class="p-0 pb-4 pr-6 font-normal"><%%= col[:label] %></th>
+            <th :if={@action != []} class="relative p-0 pb-4">
+              <span class="sr-only"><%= if @gettext do %><%%= gettext("Actions") %><% else %>Actions<% end %></span>
+            </th>
           </tr>
         </thead>
         <tbody
diff --git a/priv/templates/phx.gen.release/Dockerfile.eex b/priv/templates/phx.gen.release/Dockerfile.eex
index 409d3733e..8f7c77614 100644
--- a/priv/templates/phx.gen.release/Dockerfile.eex
+++ b/priv/templates/phx.gen.release/Dockerfile.eex
@@ -67,7 +67,8 @@ RUN mix release
 # the compiled release and other runtime necessities
 FROM ${RUNNER_IMAGE}
 
-RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \
+RUN apt-get update -y && \
+  apt-get install -y libstdc++6 openssl libncurses5 locales ca-certificates \
   && apt-get clean && rm -f /var/lib/apt/lists/*_*
 
 # Set the locale


Test patch:
diff --git a/lib/phoenix/test/conn_test.ex b/lib/phoenix/test/conn_test.ex
index 520938bbf..89de6ae86 100644
--- a/lib/phoenix/test/conn_test.ex
+++ b/lib/phoenix/test/conn_test.ex
@@ -642,22 +642,22 @@ defmodule Phoenix.ConnTest do
   by your MyApp.ErrorView.
 
   The function accepts a status either as an integer HTTP status or
-  atom, such as `404` or `:not_found`. The list of allowed atoms is available
+  atom, such as `500` or `:internal_server_error`. The list of allowed atoms is available
   in `Plug.Conn.Status`. If an error is raised, a 3-tuple of the wrapped
   response is returned matching the status, headers, and body of the response:
 
-      {404, [{"content-type", "text/html"} | _], "Page not found"}
+      {500, [{"content-type", "text/html"} | _], "Internal Server Error"}
 
   ## Examples
 
-      assert_error_sent :not_found, fn ->
-        get(build_conn(), "/users/not-found")
+      assert_error_sent :internal_server_error, fn ->
+        get(build_conn(), "/broken/route")
       end
 
-      response = assert_error_sent 404, fn ->
-        get(build_conn(), "/users/not-found")
+      response = assert_error_sent 500, fn ->
+        get(build_conn(), "/broken/route")
       end
-      assert {404, [_h | _t], "Page not found"} = response
+      assert {500, [_h | _t], "Internal Server Error"} = response
   """
   @spec assert_error_sent(integer | atom, function) :: {integer, list, term}
   def assert_error_sent(status_int_or_atom, func) do
diff --git a/test/mix/tasks/phx.gen.json_test.exs b/test/mix/tasks/phx.gen.json_test.exs
index 40999e5b4..61067dbf6 100644
--- a/test/mix/tasks/phx.gen.json_test.exs
+++ b/test/mix/tasks/phx.gen.json_test.exs
@@ -181,7 +181,7 @@ defmodule Mix.Tasks.Phx.Gen.JsonTest do
         assert file =~ "defmodule PhoenixWeb.Blog.PostJSON"
       end)
 
-      assert_file("lib/phoenix_web/controllers/blog/changeset_json.ex", fn file ->
+      assert_file("lib/phoenix_web/controllers/changeset_json.ex", fn file ->
         assert file =~ "Ecto.Changeset.traverse_errors(changeset, &translate_error/1)"
       end)
 
@@ -344,7 +344,7 @@ defmodule Mix.Tasks.Phx.Gen.JsonTest do
 
       Gen.Json.run(~w(Blog Post posts title:string --web Blog))
 
-      assert_file("lib/phoenix_web/controllers/blog/changeset_json.ex", fn file ->
+      assert_file("lib/phoenix_web/controllers/changeset_json.ex", fn file ->
         assert file =~
                  "Ecto.Changeset.traverse_errors(changeset, &PhoenixWeb.CoreComponents.translate_error/1)"
       end)
diff --git a/test/phoenix/endpoint/endpoint_test.exs b/test/phoenix/endpoint/endpoint_test.exs
index bd8077e19..beb946a99 100644
--- a/test/phoenix/endpoint/endpoint_test.exs
+++ b/test/phoenix/endpoint/endpoint_test.exs
@@ -178,6 +178,18 @@ defmodule Phoenix.Endpoint.EndpointTest do
     assert Endpoint.static_path("/foo.css") == "/foo-d978852bea6530fcd197b5445ed008fd.css"
   end
 
+  test "uses correct path for resources with fragment identifier" do
+    config = put_in(@config[:cache_manifest_skip_vsn], false)
+    assert Endpoint.config_change([{Endpoint, config}], []) == :ok
+
+    assert Endpoint.static_path("/foo.css#info") ==
+             "/foo-d978852bea6530fcd197b5445ed008fd.css?vsn=d#info"
+
+    # assert that even multiple presences of a number sign are treated as a fragment
+    assert Endpoint.static_path("/foo.css#info#me") ==
+             "/foo-d978852bea6530fcd197b5445ed008fd.css?vsn=d#info#me"
+  end
+
   @tag :capture_log
   test "invokes init/2 callback" do
     defmodule InitEndpoint do
diff --git a/test/phoenix/integration/endpoint_test.exs b/test/phoenix/integration/endpoint_test.exs
index 2fe130960..07c09f9f4 100644
--- a/test/phoenix/integration/endpoint_test.exs
+++ b/test/phoenix/integration/endpoint_test.exs
@@ -2,7 +2,7 @@ Code.require_file "../../support/http_client.exs", __DIR__
 Code.require_file "../../support/endpoint_helper.exs", __DIR__
 
 defmodule Phoenix.Integration.EndpointTest do
-  use ExUnit.Case
+  use ExUnit.Case, async: false
   import ExUnit.CaptureLog
 
   import Phoenix.Integration.EndpointHelper
diff --git a/test/phoenix/integration/websocket_channels_test.exs b/test/phoenix/integration/websocket_channels_test.exs
index e077116d0..ac8f62f1d 100644
--- a/test/phoenix/integration/websocket_channels_test.exs
+++ b/test/phoenix/integration/websocket_channels_test.exs
@@ -1,7 +1,7 @@
 Code.require_file "../../support/websocket_client.exs", __DIR__
 
 defmodule Phoenix.Integration.WebSocketChannelsTest do
-  use ExUnit.Case, async: false
+  use ExUnit.Case
   import ExUnit.CaptureLog
 
   alias Phoenix.Integration.WebsocketClient