Repository: phoenixframework/phoenix
Pull request #6216 (2025-04-23 16:29:58)

use label instead of fieldset-label
references https://github.com/phoenixframework/phoenix/issues/6204

See https://github.com/saadeghi/daisyui/blob/master/packages/daisyui/src/components/fieldset.css#L11.

The label itself looks exactly the same.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b17cab05..0df3e83e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,13 @@ This release introduces deprecation warnings for several features that have been
   * Specifying layouts without modules, such as `put_layout(conn, :print)` or `put_layout(conn, html: :print)` is deprecated
   * The `:trailing_slash` option in `Phoenix.Router` has been deprecated in favor of using `Phoenix.VerifiedRoutes`. The overall usage of helpers will be deprecated in the future
 
+## 1.8.0-rc.2 (2025-05-29)
+
+## Big Fixes
+  - [phx.gen.live] only subscribe to pubsub if connected
+  - [phx.gen.auth] remove unused current_password field
+  - [phx.gen.auth] use context_app for scopes to fix generated scopes in umbrella apps
+
 ## 1.8.0-rc.1 (2025-04-16)
 
 ## Enhancements
diff --git a/guides/authn_authz/mix_phx_gen_auth.md b/guides/authn_authz/mix_phx_gen_auth.md
index 843a05803..de5b74336 100644
--- a/guides/authn_authz/mix_phx_gen_auth.md
+++ b/guides/authn_authz/mix_phx_gen_auth.md
@@ -64,7 +64,7 @@ The following are notes about the generated authentication system.
 The generated code ships with an authentication module with a handful of plugs that fetch the current user, require authentication and so on. For instance, in an app named MyApp which had `mix phx.gen.auth Accounts User users` run on it, you will find a module named `MyAppWeb.UserAuth` with plugs such as:
 
   * `fetch_current_scope_for_user` - fetches the current user information if available and stores it as `:current_scope` assign
-  * `require_authenticated_user` - must be invoked after `fetch_current_user` and requires that a current user exists and is authenticated
+  * `require_authenticated_user` - must be invoked after `fetch_current_scope_for_user` and requires that a current user exists and is authenticated
   * `redirect_if_user_is_authenticated` - used for the few pages that must not be available to authenticated users (only generated for controller based authentication)
   * `require_sudo_mode` - used for pages that contain sensitive operations and enforces recent authentication
 
@@ -172,4 +172,4 @@ The following links describe the original implementation of the authentication s
   * José Valim's blog post - [An upcoming authentication solution for Phoenix](https://dashbit.co/blog/a-new-authentication-solution-for-phoenix)
   * 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/)
   * [Original design spec](https://github.com/dashbitco/mix_phx_gen_auth_demo/blob/auth/README.md)
-  * [Pull request on bare Phoenix app][https://github.com/dashbitco/mix_phx_gen_auth_demo/pull/1]
+  * [Pull request on bare Phoenix app](https://github.com/dashbitco/mix_phx_gen_auth_demo/pull/1)
diff --git a/guides/authn_authz/scopes.md b/guides/authn_authz/scopes.md
index 0d5e4940e..c40066fa1 100644
--- a/guides/authn_authz/scopes.md
+++ b/guides/authn_authz/scopes.md
@@ -1,12 +1,10 @@
 # Scopes
 
-A scope is a data structure used to keep information about the current request or session, such as the current user logged in, the organization/company it belongs to, permissions, and so on. Think about it as a container that holds information that is required in the huge majority of pages in your application. It can also hold important request metadata, such as IP addresses.
+A scope is a data structure used to keep information about the current request or session, such as the current user, the user's organization/company, permissions, and so on. Think of a scope as a container with information required by the majority of pages in your application. A scope can also hold request metadata, such as IP addresses.
 
-Scopes also play a very important role in security. OWASP (Open Worldwide Application Security Project) lists "Broken access control" as the biggest security risk in web applications. That's because most data in an application is not publicly available. Instead, it most often belongs to a user, a team, or an organization. Therefore, it is extremely important that, when you query the database, your queries, inserts, updates, and deletes are properly scoped to the current user/team/organization.
+Scopes play an important role in security. [OWASP](https://owasp.org/) lists "Broken access control" as a [top-10 security risk](https://owasp.org/Top10/). Most application data is private for a user, a team, or an organization. Your database CRUD operations must be properly scoped to the current user/team/organization. Phoenix generators such as `mix phx.gen.html`, `mix phx.gen.json`, and `mix phx.gen.live` automatically use your custom scopes.
 
-By using scopes, you have a single data structure that contains all relevant information, which is then passed around so all of your operations are properly scoped. By defining your own scopes, Phoenix generators such as `mix phx.gen.html`, `mix phx.gen.json`, and `mix phx.gen.live` will automatically make sure all operations pertain to that scope, ensuring that all generated code is safe by default.
-
-Scopes are also flexible: you can have more than one scope in your application and choose the relevant scope when invoking the relevant generator. When you run `mix phx.gen.auth`, it will automatically generate a scope for you, but you may also add your own.
+Scopes are flexible. You can have more than one scope in your application and choose the specific scope when invoking a generator. When you run `mix phx.gen.auth`, it will automatically generate a scope for you, but you may also add your own.
 
 This guide will:
 
@@ -17,7 +15,7 @@ This guide will:
 
 ## phx.gen.auth
 
-When you invoke `mix phx.gen.auth`, it will generate a default scope for you. This scope ties the generated resources to the currently authenticated user. Let's see it in action:
+The task `mix phx.gen.auth` will generate a default scope. This scope ties the generated resources to the currently authenticated user. Let's see it in action:
 
 ```console
 $ mix phx.gen.auth Accounts User users
@@ -84,7 +82,7 @@ end
 
 ## Integration of scopes in the Phoenix generators
 
-If a default scope is defined in your application's config, the generators will generate scoped resources by default. The generated LiveViews / Controllers will automatically pass the scope to the context functions. `mix phx.gen.auth` automatically sets its scope as default, if there is not already a default scope defined:
+If a default scope is defined in your application's config, the generators will build scoped resources by default. The generated LiveViews / Controllers will automatically pass the scope to the context functions. `mix phx.gen.auth` automatically sets its scope as default, if there is not already a default scope defined:
 
 ```elixir
 # config/config.exs
diff --git a/guides/deployment/fly.md b/guides/deployment/fly.md
index d62eedf50..00fbafb00 100644
--- a/guides/deployment/fly.md
+++ b/guides/deployment/fly.md
@@ -119,7 +119,7 @@ $ fly open
 
 Elixir and the Erlang VM have the incredible ability to be clustered together and pass messages seamlessly between nodes. Phoenix comes with all of the knobs in place, you only need to set the appropriate environment variables before deploying.
 
-If you used `fly launch` to deploy your app, those environement variables are already in place, if not, open up `rel/env.ssh.eex` and add:
+If you used `fly launch` to deploy your app, those environment variables are already in place, if not, open up `rel/env.ssh.eex` and add:
 
 ```sh
 export ERL_AFLAGS="-proto_dist inet6_tcp"
diff --git a/guides/introduction/installation.md b/guides/introduction/installation.md
index a3fa0aa39..3447e6af3 100644
--- a/guides/introduction/installation.md
+++ b/guides/introduction/installation.md
@@ -8,6 +8,8 @@ In order to build a Phoenix application, we will need a few dependencies install
 
 Please take a look at this list and make sure to install anything necessary for your system. Having dependencies installed in advance can prevent frustrating problems later on.
 
+If you just want to get started quickly, the [Up and Running](up_and_running.md) page includes a link to Phoenix Express, which will get Erlang, Elixir and Phoenix installed and running in seconds.
+
 ## Elixir 1.15 or later
 
 Phoenix is written in Elixir, and our application code will also be written in Elixir. We won't get far in a Phoenix app without it! The Elixir site maintains a great [Installation Page](https://elixir-lang.org/install.html) to help.
diff --git a/guides/introduction/up_and_running.md b/guides/introduction/up_and_running.md
index ff49af148..9ac72ad32 100644
--- a/guides/introduction/up_and_running.md
+++ b/guides/introduction/up_and_running.md
@@ -18,9 +18,7 @@ For Windows PowerShell:
 > curl.exe -fsSO https://new.phoenixframework.org/myapp.bat; .\myapp.bat
 ```
 
-The above will install Erlang, Elixir, and Phoenix, and generate a fresh Phoenix application. It will also automatically pick one of PostgreSQL or MySQL as the database, and fallback to SQLite if none of them are available. Once the command above completes, it will open up a Phoenix application, with the steps necessary to complete your installation.
-
-> Your Phoenix application name is taken from the path.
+The above will install Erlang, Elixir, and Phoenix, and generate a fresh Phoenix application. It will also automatically pick one of PostgreSQL or MySQL as the database, and fallback to SQLite if none of them are available. Once the command above completes, it will open up a Phoenix application, with the steps necessary to complete your installation. Note your Phoenix application name is taken from the path.
 
 If your operating system is not supported, or the command above fails, don't fret! You can still start your Phoenix application using `mix phx.new`.
 
diff --git a/installer/mix.exs b/installer/mix.exs
index 491420fba..de29070c6 100644
--- a/installer/mix.exs
+++ b/installer/mix.exs
@@ -6,7 +6,7 @@ end
 defmodule Phx.New.MixProject do
   use Mix.Project
 
-  @version "1.8.0-rc.1"
+  @version "1.8.0-rc.2"
   @scm_url "https://github.com/phoenixframework/phoenix"
 
   # If the elixir requirement is updated, we need to update:
diff --git a/installer/templates/phx_single/config/dev.exs b/installer/templates/phx_single/config/dev.exs
index e5d9f666a..5b5a2e52b 100644
--- a/installer/templates/phx_single/config/dev.exs
+++ b/installer/templates/phx_single/config/dev.exs
@@ -53,7 +53,7 @@ config :<%= @app_name %>, <%= @endpoint_module %>,
     patterns: [
       ~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$",<%= if @gettext do %>
       ~r"priv/gettext/.*(po)$",<% end %>
-      ~r"lib/<%= @lib_web_name %>/(controllers|live|components)/.*(ex|heex)$"
+      ~r"lib/<%= @lib_web_name %>/(?:controllers|live|components|router)/?.*\.(ex|heex)$"
     ]
   ]<% end %>
 
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs b/installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs
index 2d2e54a6e..3006e76fd 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs
+++ b/installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs
@@ -53,7 +53,7 @@ config :<%= @web_app_name %>, <%= @endpoint_module %>,
     patterns: [
       ~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$",<%= if @gettext do %>
       ~r"priv/gettext/.*(po)$",<% end %>
-      ~r"lib/<%= @web_app_name %>/(controllers|live|components)/.*(ex|heex)$"
+      ~r"lib/<%= @web_app_name %>/(?:controllers|live|components|router)/?.*\.(ex|heex)$"
     ]
   ]<% end %>
 
diff --git a/installer/templates/phx_web/components/core_components.ex b/installer/templates/phx_web/components/core_components.ex
index f16622700..fe5f2c70f 100644
--- a/installer/templates/phx_web/components/core_components.ex
+++ b/installer/templates/phx_web/components/core_components.ex
@@ -183,7 +183,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
     <fieldset class="fieldset mb-2">
       <label>
         <input type="hidden" name={@name} value="false" disabled={@rest[:disabled]} />
-        <span class="fieldset-label">
+        <span class="label">
           <input
             type="checkbox"
             id={@id}
@@ -204,7 +204,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
     ~H"""
     <fieldset class="fieldset mb-2">
       <label>
-        <span :if={@label} class="fieldset-label mb-1">{@label}</span>
+        <span :if={@label} class="label mb-1">{@label}</span>
         <select
           id={@id}
           name={@name}
@@ -225,7 +225,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
     ~H"""
     <fieldset class="fieldset mb-2">
       <label>
-        <span :if={@label} class="fieldset-label mb-1">{@label}</span>
+        <span :if={@label} class="label mb-1">{@label}</span>
         <textarea
           id={@id}
           name={@name}
@@ -246,7 +246,7 @@ defmodule <%= @web_namespace %>.CoreComponents do
     ~H"""
     <fieldset class="fieldset mb-2">
       <label>
-        <span :if={@label} class="fieldset-label mb-1">{@label}</span>
+        <span :if={@label} class="label mb-1">{@label}</span>
         <input
           type={@type}
           name={@name}
diff --git a/installer/templates/phx_web/components/layouts/root.html.heex b/installer/templates/phx_web/components/layouts/root.html.heex
index cb10aaa05..ef859d6ae 100644
--- a/installer/templates/phx_web/components/layouts/root.html.heex
+++ b/installer/templates/phx_web/components/layouts/root.html.heex
@@ -22,7 +22,9 @@
             document.documentElement.setAttribute("data-theme", theme);
           }
         };
-        setTheme(localStorage.getItem("phx:theme") || "system");
+        if (!document.documentElement.hasAttribute("data-theme")) {
+          setTheme(localStorage.getItem("phx:theme") || "system");
+        }
         window.addEventListener("storage", (e) => e.key === "phx:theme" && setTheme(e.newValue || "system"));
         window.addEventListener("phx:set-theme", ({ detail: { theme } }) => setTheme(theme));
       })();
diff --git a/lib/mix/phoenix/schema.ex b/lib/mix/phoenix/schema.ex
index cd4692c86..25aaf45b9 100644
--- a/lib/mix/phoenix/schema.ex
+++ b/lib/mix/phoenix/schema.ex
@@ -83,7 +83,7 @@ defmodule Mix.Phoenix.Schema do
     repo_alias = if String.ends_with?(Atom.to_string(repo), ".Repo"), do: "", else: ", as: Repo"
     file = Mix.Phoenix.context_lib_path(ctx_app, basename <> ".ex")
     table = opts[:table] || schema_plural
-    scope = Mix.Phoenix.Scope.scope_from_opts(otp_app, opts[:scope], opts[:no_scope])
+    scope = Mix.Phoenix.Scope.scope_from_opts(ctx_app, opts[:scope], opts[:no_scope])
     {cli_attrs, uniques, redacts} = extract_attr_flags(cli_attrs)
     {assocs, attrs} = partition_attrs_and_assocs(module, attrs(cli_attrs))
     types = types(attrs)
diff --git a/lib/phoenix/channel.ex b/lib/phoenix/channel.ex
index 0f902dc6b..d1b83bdd9 100644
--- a/lib/phoenix/channel.ex
+++ b/lib/phoenix/channel.ex
@@ -34,8 +34,8 @@ defmodule Phoenix.Channel do
 
   The first argument is the topic, the second argument is a map payload given by
   the client, and the third argument is an instance of `Phoenix.Socket`. The
-  `socket` to all channel callbacks, so check its module and documentation to
-  learn its fields and the different ways to interact with it.
+  `socket` is provided to all channel callbacks, so check its module and
+  documentation to learn its fields and the different ways to interact with it.
 
   ## Authorization
 
diff --git a/lib/phoenix/endpoint/supervisor.ex b/lib/phoenix/endpoint/supervisor.ex
index 8f8a52bcf..75027af9d 100644
--- a/lib/phoenix/endpoint/supervisor.ex
+++ b/lib/phoenix/endpoint/supervisor.ex
@@ -33,8 +33,26 @@ defmodule Phoenix.Endpoint.Supervisor do
       cond do
         Code.ensure_loaded?(mod) and function_exported?(mod, :init, 2) ->
           IO.warn(
-            "#{inspect(mod)}.init/2 is deprecated, use config/runtime.exs instead " <>
-              "or pass additional options when starting the endpoint in your supervision tree"
+            """
+            your #{inspect(mod)} defines a init/2 callback, which is now deprecated. \
+            This callback is invoked when your endpoint is initialized as part of your supervision tree. \
+            Instead, you should either:
+
+            1. Move all dynamic configuration to config/runtime.exs (preferred). For example:
+
+                # config/runtime.exs
+                import Config
+
+                if config_env() == :prod do
+                  config #{inspect(otp_app)}, #{inspect(mod)},
+                    http: [:inet6, port: System.fetch_env!("PORT")]
+                end
+
+            2. Pass the configuration you returned from the `init/2` callback \
+            as additional options when starting the endpoint in your supervision tree. \
+            For example: {#{inspect(mod)}, some_extra_options: true}
+            """,
+            []
           )
 
           {:ok, init_conf} = mod.init(:supervisor, env_conf)
@@ -88,6 +106,7 @@ defmodule Phoenix.Endpoint.Supervisor do
         server_children(mod, conf, server?) ++
         socket_children(mod, conf, :drainer_spec) ++
         watcher_children(mod, conf, server?)
+
     Supervisor.init(children, strategy: :one_for_one)
   end
 
diff --git a/mix.exs b/mix.exs
index 064c48ae3..1d218b96e 100644
--- a/mix.exs
+++ b/mix.exs
@@ -8,7 +8,7 @@ defmodule Phoenix.MixProject do
     end
   end
 
-  @version "1.8.0-rc.1"
+  @version "1.8.0-rc.2"
   @scm_url "https://github.com/phoenixframework/phoenix"
 
   # If the elixir requirement is updated, we need to make the installer
diff --git a/package.json b/package.json
index 41a691a1c..b58b027b3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "phoenix",
-  "version": "1.8.0-rc.1",
+  "version": "1.8.0-rc.2",
   "description": "The official JavaScript client for the Phoenix web framework.",
   "license": "MIT",
   "module": "./priv/static/phoenix.mjs",
diff --git a/priv/templates/phx.gen.auth/schema.ex b/priv/templates/phx.gen.auth/schema.ex
index 1ee64e0e6..28d55502f 100644
--- a/priv/templates/phx.gen.auth/schema.ex
+++ b/priv/templates/phx.gen.auth/schema.ex
@@ -7,7 +7,6 @@ defmodule <%= inspect schema.module %> do
     field :email, :string
     field :password, :string, virtual: true, redact: true
     field :hashed_password, :string, redact: true
-    field :current_password, :string, virtual: true, redact: true
     field :confirmed_at, <%= inspect schema.timestamp_type %>
     field :authenticated_at, <%= inspect schema.timestamp_type %>, virtual: true
 


Test patch:
diff --git a/installer/test/phx_new_test.exs b/installer/test/phx_new_test.exs
index bb47c040e..6766a85ad 100644
--- a/installer/test/phx_new_test.exs
+++ b/installer/test/phx_new_test.exs
@@ -141,7 +141,7 @@ defmodule Mix.Tasks.Phx.NewTest do
 
       assert_file("phx_blog/config/dev.exs", fn file ->
         assert file =~ "esbuild: {Esbuild,"
-        assert file =~ "lib/phx_blog_web/(controllers|live|components)/.*(ex|heex)"
+        assert file =~ "lib/phx_blog_web/(?:controllers|live|components|router)/?.*\\.(ex|heex)$"
         assert file =~ "http: [ip: {127, 0, 0, 1}"
       end)
 
diff --git a/installer/test/phx_new_umbrella_test.exs b/installer/test/phx_new_umbrella_test.exs
index 7d652c660..fd298a47c 100644
--- a/installer/test/phx_new_umbrella_test.exs
+++ b/installer/test/phx_new_umbrella_test.exs
@@ -71,7 +71,7 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
 
       assert_file(root_path(@app, "config/dev.exs"), fn file ->
         assert file =~ ~r[esbuild: {Esbuild]
-        assert file =~ "lib/#{@app}_web/(controllers|live|components)/.*(ex|heex)"
+        assert file =~ "lib/#{@app}_web/(?:controllers|live|components|router)/?.*\\.(ex|heex)$"
         assert file =~ "config :#{@app}_web, dev_routes: true"
       end)
 
diff --git a/test/mix/tasks/phx.gen.schema_test.exs b/test/mix/tasks/phx.gen.schema_test.exs
index 407c73091..20ba172e0 100644
--- a/test/mix/tasks/phx.gen.schema_test.exs
+++ b/test/mix/tasks/phx.gen.schema_test.exs
@@ -213,9 +213,7 @@ defmodule Mix.Tasks.Phx.Gen.SchemaTest do
 
   test "allows a custom migration dir", config do
     in_tmp_project(config.test, fn ->
-      Gen.Schema.run(
-        ~w(Blog.Post blog_posts title:string --migration-dir priv/custom_dir)
-      )
+      Gen.Schema.run(~w(Blog.Post blog_posts title:string --migration-dir priv/custom_dir))
 
       assert [migration] = Path.wildcard("priv/custom_dir/*_create_blog_posts.exs")
 
@@ -240,9 +238,7 @@ defmodule Mix.Tasks.Phx.Gen.SchemaTest do
 
   test "does not add maps to the required list", config do
     in_tmp_project(config.test, fn ->
-      Gen.Schema.run(
-        ~w(Blog.Post blog_posts title:string tags:map published_at:naive_datetime)
-      )
+      Gen.Schema.run(~w(Blog.Post blog_posts title:string tags:map published_at:naive_datetime))
 
       assert_file("lib/phoenix/blog/post.ex", fn file ->
         assert file =~ "cast(attrs, [:title, :tags, :published_at]"
@@ -283,9 +279,7 @@ defmodule Mix.Tasks.Phx.Gen.SchemaTest do
 
   test "generates unique indices", config do
     in_tmp_project(config.test, fn ->
-      Gen.Schema.run(
-        ~w(Blog.Post posts title:unique secret:redact unique_int:integer:unique)
-      )
+      Gen.Schema.run(~w(Blog.Post posts title:unique secret:redact unique_int:integer:unique))
 
       assert [migration] = Path.wildcard("priv/repo/migrations/*_create_posts.exs")
 
@@ -556,6 +550,48 @@ defmodule Mix.Tasks.Phx.Gen.SchemaTest do
         assert [_] = Path.wildcard("another_app/priv/repo/migrations/*_create_blog_posts.exs")
       end)
     end
+
+    test "generates scoped schema", config do
+      in_tmp_umbrella_project(config.test, fn ->
+        Application.put_env(:phoenix, :generators, context_app: {:another_app, "another_app"})
+
+        with_scope_env(
+          :another_app,
+          [
+            org: [
+              default: true,
+              module: MyApp.Accounts.Scope,
+              assign_key: :current_scope,
+              access_path: [:user, :org_id],
+              schema_key: :org_id,
+              schema_type: :id,
+              schema_table: :organizations
+            ]
+          ],
+          fn ->
+            Gen.Schema.run(~w(Blog.Post blog_posts title:string --scope org --binary-id))
+
+            assert_file("another_app/lib/another_app/blog/post.ex", fn file ->
+              assert file =~ "@primary_key {:id, :binary_id, autogenerate: true}"
+              assert file =~ "field :org_id, :id"
+            end)
+
+            assert [migration] =
+                     Path.wildcard("another_app/priv/repo/migrations/*_create_blog_posts.exs")
+
+            assert_file(migration, fn file ->
+              assert file =~ "create table(:blog_posts, primary_key: false) do"
+              assert file =~ "add :id, :binary_id, primary_key: true"
+
+              assert file =~
+                       "add :org_id, references(:organizations, type: :id, on_delete: :delete_all)"
+
+              assert file =~ "create index(:blog_posts, [:org_id])"
+            end)
+          end
+        )
+      end)
+    end
   end
 
   test "generates scoped schema", config do