config: name the instance, not just the profile
One name for the server, on the browser tab and on the phone,
read from PRO_TACTS_INSTANCE_NAME, since the profile's variable was outgrown;
rake dev calls itself "pro-tacts (dev)" unless you export a name of your own,
and a deployment still setting PROFILE_NAME falls back to "pro-tacts" alone.
Assisted-by: Claude Opus 5 via Claude Code
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
diff --git a/Rakefile b/Rakefile
index 36bf003..f757efe 100644
--- a/Rakefile
+++ b/Rakefile
@@ -28,6 +28,7 @@ task :dev do
# log/exchange.log stays the deployment default. An exported
# PRO_TACTS_EXCHANGE_LOG (e.g. stderr) wins over both.
ENV["PRO_TACTS_EXCHANGE_LOG"] ||= "log/dev.log"
+ ENV["PRO_TACTS_INSTANCE_NAME"] ||= "pro-tacts (dev)"
File.truncate("log/dev.log", 0) if File.exist?("log/dev.log")
Dir.mktmpdir("pro-tacts-dev") do |dir|
diff --git a/lib/pro_tacts/admin/layout.rb b/lib/pro_tacts/admin/layout.rb
index b342e26..5c1430a 100644
--- a/lib/pro_tacts/admin/layout.rb
+++ b/lib/pro_tacts/admin/layout.rb
@@ -49,7 +49,9 @@ module ProTacts
head do
meta(charset: "utf-8")
meta(name: "viewport", content: "width=device-width, initial-scale=1")
- title { "pro-tacts — #{@title}" }
+ # The instance's name, so a tab says which server it is
+ # (`rake dev` names its own).
+ title { "#{ProTacts.config.instance_name} — #{@title}" }
# No webfont (docs/DESIGN.md), so there is no font link
# among these. What was here before was a remote Google
# Fonts stylesheet, and the self-hosted woff2s meant to
diff --git a/lib/pro_tacts/config.rb b/lib/pro_tacts/config.rb
index 335a12f..3ff9e03 100644
--- a/lib/pro_tacts/config.rb
+++ b/lib/pro_tacts/config.rb
@@ -62,15 +62,13 @@ module ProTacts
Pathname.new(@env.fetch("PRO_TACTS_EXCHANGE_LOG", data_dir / "log/exchange.log"))
end
- # The CardDAV account's display name, PRO_TACTS_PROFILE_NAME — what
- # a device labels the account it installs, and the one way to tell
- # two pro-tacts accounts apart on the same phone. nil when unset
- # rather than a default: the name belongs to the profile, so
- # ProTacts::Profile::DEFAULT_NAME is the fallback and this reports
- # only the override.
- #: () -> String?
- def profile_name
- @env.fetch("PRO_TACTS_PROFILE_NAME", nil)
+ # What this running server calls itself, PRO_TACTS_INSTANCE_NAME:
+ # the CardDAV account a device installs and the browser tab both
+ # carry it, so two pro-tacts servers can be told apart on the same
+ # phone or in the same window.
+ #: () -> String
+ def instance_name
+ @env.fetch("PRO_TACTS_INSTANCE_NAME", "pro-tacts")
end
# The running image's version, baked in as a build arg (see
diff --git a/lib/pro_tacts/profile.rb b/lib/pro_tacts/profile.rb
index b699566..960885c 100644
--- a/lib/pro_tacts/profile.rb
+++ b/lib/pro_tacts/profile.rb
@@ -32,20 +32,6 @@ module ProTacts
class Profile
IDENTIFIER_PREFIX = "dev.kejadlen.pro-tacts.carddav"
- # The account name when the environment names none. A constant
- # because the install screen states the name the download is about
- # to carry and must not restate it as a second literal.
- DEFAULT_NAME = "pro-tacts"
-
- # The name every render and the install screen state. A method
- # rather than a render parameter because there is only one source
- # for it: a caller passing a name of its own would be labelling an
- # account no device installs.
- #: () -> String
- def self.account_name
- ProTacts.config.profile_name || DEFAULT_NAME
- end
-
# The server ignores the username and password: identity comes from
# the header the proxy in front of it writes (see
# ProTacts::ProxyAuth). /setup passes the requester's login anyway,
@@ -54,7 +40,9 @@ module ProTacts
# and dropping it is untested.
#: (hostname: String, username: String) -> String
def self.render(hostname:, username:)
- name = account_name
+ # The instance's name rather than a render parameter: a caller
+ # passing its own would label an account no device installs.
+ name = ProTacts.config.instance_name
identifier = "#{IDENTIFIER_PREFIX}.#{host_digest(hostname)}.#{unique_hex}"
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |x|
diff --git a/lib/pro_tacts/web/setup.rb b/lib/pro_tacts/web/setup.rb
index c8345fc..424186a 100644
--- a/lib/pro_tacts/web/setup.rb
+++ b/lib/pro_tacts/web/setup.rb
@@ -23,7 +23,7 @@ module ProTacts
r.is do
r.get do
response["Content-Type"] = "text/html; charset=utf-8"
- Admin::DeviceSetup.call(hostname: r.host, name: Profile.account_name)
+ Admin::DeviceSetup.call(hostname: r.host, name: ProTacts.config.instance_name)
end
end
diff --git a/test/pro_tacts/admin/test_device_setup.rb b/test/pro_tacts/admin/test_device_setup.rb
index 777fd5b..2fdd066 100644
--- a/test/pro_tacts/admin/test_device_setup.rb
+++ b/test/pro_tacts/admin/test_device_setup.rb
@@ -39,7 +39,7 @@ class AdminDeviceSetupTest < Minitest::Test
assert_equal 200, last_response.status
assert_equal "text/html; charset=utf-8", last_response["Content-Type"]
assert_includes last_response.body, "box.example.ts.net"
- assert_includes last_response.body, ProTacts::Profile::DEFAULT_NAME
+ assert_includes last_response.body, %(<dd class="type-body-sm">pro-tacts</dd>)
assert_includes last_response.body, "/setup/carddav.mobileconfig"
end
end
@@ -48,7 +48,7 @@ class AdminDeviceSetupTest < Minitest::Test
# reaching only one of the two would put a name on the screen that the
# installed account does not have.
def test_the_configured_name_reaches_the_screen_and_the_document
- with_config("PRO_TACTS_PROFILE_NAME" => "pro-tacts (dev)") do
+ with_config("PRO_TACTS_INSTANCE_NAME" => "pro-tacts (dev)") do
get "/setup"
assert_includes last_response.body, %(<dd class="type-body-sm">pro-tacts (dev)</dd>)
diff --git a/test/pro_tacts/admin/test_footer.rb b/test/pro_tacts/admin/test_footer.rb
index c4cc8ef..6cd0e09 100644
--- a/test/pro_tacts/admin/test_footer.rb
+++ b/test/pro_tacts/admin/test_footer.rb
@@ -82,4 +82,14 @@ class AdminFooterTest < Minitest::Test
refute_includes last_response.body, "data-server"
end
+
+ def test_the_title_names_the_instance
+ with_env({})
+
+ assert_includes last_response.body, "<title>pro-tacts — Device setup</title>"
+
+ with_env("PRO_TACTS_INSTANCE_NAME" => "pro-tacts (dev)")
+
+ assert_includes last_response.body, "<title>pro-tacts (dev) — Device setup</title>"
+ end
end
diff --git a/test/pro_tacts/test_config.rb b/test/pro_tacts/test_config.rb
index 8dc0a5a..d2d0102 100644
--- a/test/pro_tacts/test_config.rb
+++ b/test/pro_tacts/test_config.rb
@@ -58,12 +58,11 @@ class ConfigTest < Minitest::Test
assert_equal Pathname.new("stderr"), ProTacts::Config.new("PRO_TACTS_EXCHANGE_LOG" => "stderr").exchange_log_path
end
- # nil rather than a default, so the fallback stays Profile's alone.
- def test_profile_name_is_nil_when_unset
- assert_nil ProTacts::Config.new({}).profile_name
+ def test_instance_name_defaults_to_pro_tacts
+ assert_equal "pro-tacts", ProTacts::Config.new({}).instance_name
end
- def test_profile_name_is_passed_through
- assert_equal "pro-tacts (dev)", ProTacts::Config.new("PRO_TACTS_PROFILE_NAME" => "pro-tacts (dev)").profile_name
+ def test_instance_name_is_overridable
+ assert_equal "pro-tacts (dev)", ProTacts::Config.new("PRO_TACTS_INSTANCE_NAME" => "pro-tacts (dev)").instance_name
end
end
diff --git a/test/pro_tacts/test_profile.rb b/test/pro_tacts/test_profile.rb
index 004b2aa..15f6b7f 100644
--- a/test/pro_tacts/test_profile.rb
+++ b/test/pro_tacts/test_profile.rb
@@ -45,7 +45,7 @@ class ProfileTest < Minitest::Test
end
def test_name_can_be_overridden
- with_config("PRO_TACTS_PROFILE_NAME" => "pro-tacts (dev)") do
+ with_config("PRO_TACTS_INSTANCE_NAME" => "pro-tacts (dev)") do
xml = render
assert_match(%r{<key>CardDAVAccountDescription</key>\s*<string>pro-tacts \(dev\)</string>}, xml)