diff --git a/app/models/user.rb b/app/models/user.rb
index 4241b0ed36bdf0006085e3a995aa1b46af854131..8ba946317e595c12bfb8d847ab0f69a14f004958 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1015,7 +1015,7 @@ def can_be_removed?
   def ci_authorized_runners
     @ci_authorized_runners ||= begin
       runner_ids = Ci::RunnerProject
-        .where("ci_runner_projects.project_id IN (#{ci_projects_union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
+        .where(project: authorized_projects(Gitlab::Access::MASTER))
         .select(:runner_id)
       Ci::Runner.specific.where(id: runner_ids)
     end
@@ -1226,15 +1226,6 @@ def owned_projects_union
     ], remove_duplicates: false)
   end
 
-  def ci_projects_union
-    scope  = { access_level: [Gitlab::Access::MASTER, Gitlab::Access::OWNER] }
-    groups = groups_projects.where(members: scope)
-    other  = projects.where(members: scope)
-
-    Gitlab::SQL::Union.new([personal_projects.select(:id), groups.select(:id),
-                            other.select(:id)])
-  end
-
   # Added according to https://github.com/plataformatec/devise/blob/7df57d5081f9884849ca15e4fde179ef164a575f/README.md#activejob-integration
   def send_devise_notification(notification, *args)
     return true unless can?(:receive_notifications)
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index e4be953e810053c1b13f832b6d5053d676de9b98..b82d9c64296a4eb51ef169933315dbe709f0b327 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -54,8 +54,7 @@ def add_recipients(users, type, reason)
           users = users.includes(:notification_settings)
         end
 
-        users = Array(users)
-        users.compact!
+        users = Array(users).compact
         recipients.concat(users.map { |u| make_recipient(u, type, reason) })
       end
 
diff --git a/changelogs/unreleased/41981-allow-group-owner-to-enable-runners-from-subgroups.yml b/changelogs/unreleased/41981-allow-group-owner-to-enable-runners-from-subgroups.yml
new file mode 100644
index 0000000000000000000000000000000000000000..30481e7af84ac6daf3c997662b3d7f95cd226c32
--- /dev/null
+++ b/changelogs/unreleased/41981-allow-group-owner-to-enable-runners-from-subgroups.yml
@@ -0,0 +1,5 @@
+---
+title: 'Allow group owner to enable runners from subgroups (#41981)'
+merge_request: 18009
+author:
+type: fixed
diff --git a/changelogs/unreleased/zj-find-license-opt-out.yml b/changelogs/unreleased/zj-find-license-opt-out.yml
new file mode 100644
index 0000000000000000000000000000000000000000..be2656601a96a345ef8a894d6dc5ffdd23b6b551
--- /dev/null
+++ b/changelogs/unreleased/zj-find-license-opt-out.yml
@@ -0,0 +1,5 @@
+---
+title: Detect repository license on Gitaly by default
+merge_request:
+author:
+type: performance
diff --git a/doc/development/emails.md b/doc/development/emails.md
index 4dbf064fd75226877ba2fed2a27f7f713065a67b..73cac82caf09cad4539c3c0c2374acaea6b53e99 100644
--- a/doc/development/emails.md
+++ b/doc/development/emails.md
@@ -74,6 +74,24 @@ See the [Rails guides] for more info.
 
 1. Reply by email should now be working.
 
+## Email namespace
+
+If you need to implement a new feature which requires a new email handler, follow these rules:
+
+ - You must choose a namespace. The namespace cannot contain `/` or `+`, and cannot match `\h{16}`.
+ - If your feature is related to a project, you will append the namespace **after** the project path, separated by a `+`
+ - If you have different actions in the namespace, you add the actions **after** the namespace separated by a `+`. The action name cannot contain `/` or `+`, , and cannot match `\h{16}`.
+ - You will register your handlers in `lib/gitlab/email/handler.rb`
+
+Therefore, these are the only valid formats for an email handler:
+
+ - `path/to/project+namespace`
+ - `path/to/project+namespace+action`
+ - `namespace`
+ - `namespace+action`
+
+Please note that `path/to/project` is used in GitLab Premium as handler for the Service Desk feature.
+
 ---
 
 [Return to Development documentation](README.md)
diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb
index a616a80e8f5c3f0cbc2137dc1bb37d05224a458c..05a60deb7d39042eb7cbd996ae2594565a252fdb 100644
--- a/lib/gitlab/email/handler/create_issue_handler.rb
+++ b/lib/gitlab/email/handler/create_issue_handler.rb
@@ -14,7 +14,7 @@ def initialize(mail, mail_key)
         end
 
         def can_handle?
-          !incoming_email_token.nil?
+          !incoming_email_token.nil? && !incoming_email_token.include?("+") && !mail_key.include?(Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX)
         end
 
         def execute
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 9a4b95ac7cff0582d8c33a1d165f23e7ca5aad48..33e829b1e8c68481c025c7d1a24d573f1ab61759 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -1049,7 +1049,8 @@ def languages(ref = nil)
       end
 
       def license_short_name
-        gitaly_migrate(:license_short_name) do |is_enabled|
+        gitaly_migrate(:license_short_name,
+                       status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
           if is_enabled
             gitaly_repository_client.license_short_name
           else
diff --git a/spec/lib/gitlab/email/handler_spec.rb b/spec/lib/gitlab/email/handler_spec.rb
index 650b01c4df443f123c2153806a6278f85f7ab85f..cedbfcc0d1828a520824787ecfbc7b401195891b 100644
--- a/spec/lib/gitlab/email/handler_spec.rb
+++ b/spec/lib/gitlab/email/handler_spec.rb
@@ -14,4 +14,34 @@
       expect(described_class.for('email', '')).to be_nil
     end
   end
+
+  describe 'regexps are set properly' do
+    let(:addresses) do
+      %W(sent_notification_key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX} sent_notification_key path/to/project+merge-request+user_email_token path/to/project+user_email_token)
+    end
+
+    it 'picks each handler at least once' do
+      matched_handlers = addresses.map do |address|
+        described_class.for('email', address).class
+      end
+
+      expect(matched_handlers.uniq).to match_array(ce_handlers)
+    end
+
+    it 'can pick exactly one handler for each address' do
+      addresses.each do |address|
+        matched_handlers = ce_handlers.select do |handler|
+          handler.new('email', address).can_handle?
+        end
+
+        expect(matched_handlers.count).to eq(1), "#{address} matches #{matched_handlers.count} handlers: #{matched_handlers}"
+      end
+    end
+  end
+
+  def ce_handlers
+    @ce_handlers ||= Gitlab::Email::Handler::HANDLERS.reject do |handler|
+      handler.name.start_with?('Gitlab::Email::Handler::EE::')
+    end
+  end
 end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 60e152c93dd43ab6ef165baf18f650b21a08ebd5..cc1de3ded9ffe666fffb059b2efa727454095b8d 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1899,6 +1899,21 @@ def add_user(access)
 
       it_behaves_like :member
     end
+
+    context 'with subgroup with different owner for project runner', :nested_groups do
+      let(:group) { create(:group) }
+      let(:another_user) { create(:user) }
+      let(:subgroup) { create(:group, parent: group) }
+      let(:project) { create(:project, group: subgroup) }
+
+      def add_user(access)
+        group.add_user(user, access)
+        group.add_user(another_user, :owner)
+        subgroup.add_user(another_user, :owner)
+      end
+
+      it_behaves_like :member
+    end
   end
 
   describe '#projects_with_reporter_access_limited_to' do