Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions confconsole.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/python3
# Copyright (c) 2008 Alon Swartz <alon@turnkeylinux.org> - all rights reserved
"""TurnKey Configuration Console
"""Keel Linux Configuration Console

Options:
-h, --help Display this help and exit
Expand Down Expand Up @@ -40,6 +40,11 @@
PLUGIN_PATH = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "plugins.d"
)
# The name at the top of every screen and, unless APPNAME_PATH names the
# appliance otherwise, before the hostname in the menu titles.
BRAND = "Keel Linux"
TITLE = f"{BRAND} Configuration Console"
APPNAME_PATH = "/etc/appname"

handler = JournalHandler(SYSLOG_IDENTIFIER="confconsole")
handler.setFormatter(logging.Formatter("%(name)s: %(message)s"))
Expand Down Expand Up @@ -399,20 +404,19 @@ def __init__(
eventManager: plugin.EventManager,
advanced_enabled: bool = True,
) -> None:
title = "TurnKey GNU/Linux Configuration Console"
self.width = 65
self.height = 25

self.console = Console(title, self.width, self.height)
self.console = Console(TITLE, self.width, self.height)

# sometimes it would be nice to have the appname be something other
# than the hostname. Allow developers to create file containing the
# appname in /etc/appname
try:
with open("/etc/appname", 'r') as fob:
with open(APPNAME_PATH, 'r') as fob:
self.appname = fob.read().rstrip()
except FileNotFoundError:
self.appname = f"TurnKey Linux {netinfo.get_hostname().upper()}"
self.appname = f"{BRAND} {netinfo.get_hostname().upper()}"

self.installer = Installer(path="/usr/bin/di-live")

Expand Down
35 changes: 35 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
confconsole (2.2.3+keel10) trixie; urgency=low

* Every screen names Keel Linux. The backtitle read "TurnKey GNU/Linux
Configuration Console" and, without /etc/appname, the menus were
titled "TurnKey Linux <HOSTNAME>"; they read "Keel Linux
Configuration Console" and "Keel Linux <HOSTNAME>" (BRAND, TITLE).
/etc/appname still names the appliance when it exists. The role and
Keel Cloud first boot screens (keelfirstboot.py) read "Keel Linux -
First boot configuration", the backtitle of the inithooks dialogs
they run between, instead of "Keel - First boot configuration".
* Mail relay: "this server" sends mail directly, not "TurnKey servers";
the sign up link is Brevo's own page instead of TurnKey's referral
link through the TurnKey Hub, and the SendinBlue entry is Brevo's
relay, not TurnKey's preferred gateway. Its key is now Brevo, the
name the code after the menu already tested for: with the old key
the Brevo host and port were never filled in.
* Let's Encrypt and the security update settings point at this
repository's documentation (docs/Lets_encrypt.rst,
docs/System_settings.rst) instead of turnkeylinux.org, and a missing
DNS provider list is reported to the confconsole issues here, not to
TurnKey.
* --help says "Keel Linux Configuration Console" (the module
docstring). turnkey-lexicon's help sends problems to the confconsole
issues here instead of TurnKey's forum and tracker, and no longer
calls confconsole TurnKey's.
* Kept: the turnkey-lexicon and turnkey-install-security-updates
commands (installed names, run by the screens), the Python class
name TurnkeyConsole, the package dependencies on turnkey-netinfo and
turnkey-conffile, and the copyright notices. The docs/ and README
still describe TurnKey; they are not shown on an appliance.
* tests/test_brand.py checks the backtitle, the appname and that no
string of a shipped screen names TurnKey or TKLBAM.

-- Marcos Méndez <mendez.foto@gmail.com> Fri, 02 Oct 2026 04:00:00 +0000

confconsole (2.2.3+keel9) trixie; urgency=low

* Usage screen: the maintainer's console art in tiers. keelbanner picks
Expand Down
2 changes: 1 addition & 1 deletion keelfirstboot.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
ROLE = "role"
CLOUD = "cloud"
USAGE = "Syntax: keelfirstboot.py role|cloud"
BACKTITLE = "Keel - First boot configuration"
BACKTITLE = "Keel Linux - First boot configuration"
TTY = "/dev/tty"
# confconsole's box height, and the rows the back title and the shadow
# take on a terminal smaller than that
Expand Down
17 changes: 9 additions & 8 deletions plugins.d/Lets_Encrypt/get_certificate.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@
enter each domain into a box, one domain per box. Empty boxes will be ignored.

Wildcard domains are supported, but only when using DNS-01 challenge. Alias
will be auto generated, so should not be entered here. See:
https://www.turnkeylinux.org/docs/confconsole/letsencrypt#wildcard
will be auto generated, so should not be entered here.

To generate multiple certificates, please consult the advanced docs:
https://www.turnkeylinux.org/docs/letsencrypt#advanced
For wildcards and for multiple certificates, please consult the docs:
https://github.com/Keel-Linux/confconsole/blob/master/docs/Lets_encrypt.rst
"""

dehydrated_conf = "/etc/dehydrated"
Expand Down Expand Up @@ -245,7 +244,8 @@ def run() -> None:
"Error",
f"Dehydrated not installed or {dehydrated_conf} not found,"
" dehydrated can be installed via apt from the Debian repos.\n\n"
"More info: www.turnkeylinux.org/docs/letsencrypt",
"More info: https://github.com/Keel-Linux/confconsole/blob/"
"master/docs/Lets_encrypt.rst",
autosize=True,
)
return
Expand Down Expand Up @@ -304,7 +304,8 @@ def run() -> None:
if not providers:
console.msgbox(
"Error",
"No providers found, please report to TurnKey",
"No providers found, please report it at"
" https://github.com/Keel-Linux/confconsole/issues",
autosize=True,
)
return
Expand Down Expand Up @@ -360,8 +361,8 @@ def run() -> None:
ret, values = console.form(
"Lexicon configuration",
"Review and adjust current lexicon configuration as"
"necessary.\n\n Please see https://www.turnkeylinux.org/docs/"
"confconsole/letsencrypt#dns-01",
"necessary.\n\n Please see https://github.com/Keel-Linux/"
"confconsole/blob/master/docs/Lets_encrypt.rst",
fields,
autosize=True,
)
Expand Down
10 changes: 5 additions & 5 deletions plugins.d/Mail_Relaying/mail_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
TITLE = "Mail Relay"

TEXT = (
"By default, TurnKey servers send e-mail directly. An SMTP relay provides"
"By default, this server sends e-mail directly. An SMTP relay provides"
" more robust mail deliverability.\n\n"
"Send up to 9000 emails per month with a free Brevo account. To sign up,"
" open the below URL in your web browser and follow the prompts:\n\n"
"https://hub.turnkeylinux.org/email"
"Brevo (formerly SendinBlue) offers a free plan. To sign up, open the"
" below URL in your web browser and follow the prompts:\n\n"
"https://www.brevo.com/"
)

FORMNOTE = (
Expand Down Expand Up @@ -81,7 +81,7 @@ def run():
TITLE,
TEXT,
[
("SendinBlue", "TurnKey's preferred SMTP gateway"),
("Brevo", "Brevo (formerly SendinBlue) SMTP relay"),
("Custom", "Custom mail relay configuration"),
("Deconfigure", "Erase current mail relay settings"),
],
Expand Down
7 changes: 5 additions & 2 deletions plugins.d/System_Settings/Secupdates_adv_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
CONF_DEFAULT = "/etc/cron-apt/action-available.d/5-install.default"
CONF_ALT = "/etc/cron-apt/action-available.d/5-install.alt"

doc_url = "www.turnkeylinux.org/secupdates#issue-res"
doc_url = (
"https://github.com/Keel-Linux/confconsole/blob/master/docs/"
"System_settings.rst"
)

info_default = """
This is the historic and default TurnKey cronapt behaviour. Only packages \
This is the historic and default cron-apt behaviour. Only packages \
from the repos listed in security.sources.list will be installed. \
Missing dependencies (extremely rare) will not be installed and will cause \
package removal. This package removal may cause one or more services to fail.\
Expand Down
123 changes: 123 additions & 0 deletions tests/test_brand.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""What confconsole shows the operator names Keel Linux, not TurnKey.

The backtitle at the top of every screen, the appliance name the menus
are titled with, and the text of the screens a Keel appliance ships are
checked for TurnKey's name and addresses. What stays, and why, is
ALLOWED: the names of commands a screen runs or tells the operator to
run, kept so that existing installs and documentation keep working.
"""

import ast
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parent.parent

# Every Python file whose strings reach a screen.
SHOWN = [
"confconsole.py",
"plugins.d/Mail_Relaying/mail_relay.py",
"plugins.d/Lets_Encrypt/get_certificate.py",
"plugins.d/Lets_Encrypt/dns_01.py",
"plugins.d/System_Settings/Secupdates_adv_conf.py",
"plugins.d/System_Settings/Security_Update.py",
]
# Command names, not branding: the lexicon wrapper confconsole installs
# and the security update command inithooks installs.
ALLOWED = ["turnkey-lexicon", "turnkey-install-security-updates"]


def strings(path: str) -> list[str]:
"""Every string literal of the Python file at PATH but its docstrings
and the names it imports"""
tree = ast.parse((ROOT / path).read_text())
skipped = set()
for node in ast.walk(tree):
if isinstance(node, (ast.Module, ast.FunctionDef, ast.ClassDef)):
if ast.get_docstring(node, clean=False) is not None:
skipped.add(id(node.body[0].value))
return [
node.value
for node in ast.walk(tree)
if isinstance(node, ast.Constant)
and isinstance(node.value, str)
and id(node) not in skipped
]


def names_turnkey(text: str) -> bool:
for allowed in ALLOWED:
text = text.replace(allowed, "")
return "turnkey" in text.lower() or "tklbam" in text.lower()


class FakePluginManager:
def updateGlobals(self, values):
self.globals = values


@pytest.fixture
def console_title(confconsole, monkeypatch):
"""The backtitle and appname a TurnkeyConsole is created with."""
seen = {}

class FakeConsole:
def __init__(self, title, width, height):
seen["title"] = title

monkeypatch.setattr(confconsole, "Console", FakeConsole)
monkeypatch.setattr(confconsole.netinfo, "get_hostname", lambda: "web")
monkeypatch.setattr(confconsole, "APPNAME_PATH", "/nonexistent/appname")
tc = confconsole.TurnkeyConsole(FakePluginManager(), object())
return seen["title"], tc.appname


def test_backtitle_names_keel_linux(console_title):
title, _ = console_title
assert title == "Keel Linux Configuration Console"


def test_appname_without_etc_appname_names_keel_linux(console_title):
_, appname = console_title
assert appname == "Keel Linux WEB"


def test_first_boot_screens_carry_the_inithooks_backtitle():
# The role and Keel Cloud screens run between the inithooks dialogs,
# whose backtitle is "Keel Linux - First boot configuration"
# (Keel-Linux/inithooks libinithooks/dialog_wrapper.py); the same words
# keep the top line still as the operator moves from one to the next.
import keelfirstboot

assert keelfirstboot.BACKTITLE == "Keel Linux - First boot configuration"


def test_etc_appname_still_wins(confconsole, monkeypatch, tmp_path):
path = tmp_path / "appname"
path.write_text("My Site\n")
monkeypatch.setattr(confconsole, "Console", lambda *args: None)
monkeypatch.setattr(confconsole, "APPNAME_PATH", str(path))
tc = confconsole.TurnkeyConsole(FakePluginManager(), object())
assert tc.appname == "My Site"


@pytest.mark.parametrize("path", SHOWN)
def test_no_screen_text_names_turnkey(path):
shown = [text for text in strings(path) if names_turnkey(text)]
assert shown == []


def test_help_text_names_keel_linux(confconsole):
# The module docstring is what --help prints.
assert confconsole.__doc__.splitlines()[0] == (
"Keel Linux Configuration Console"
)
assert "turnkey" not in confconsole.USAGE.lower()


def test_lexicon_wrapper_sends_problems_to_keel():
text = (ROOT / "turnkey-lexicon").read_text()
assert "turnkeylinux" not in text
assert "TurnKey" not in text
assert "https://github.com/Keel-Linux/confconsole/issues" in text
10 changes: 3 additions & 7 deletions turnkey-lexicon
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,9 @@ Then select:

Advanced >> Let's Encrypt >> Get Certificate >> DNS-01

If you encounter problems, please report to TurnKey, either via our forums:
If you encounter problems, please open an issue:

https://www.turnkeylinux.org/forum/support

Or open an issue on our tracker:

https://github.com/turnkeylinux/tracker/issues
https://github.com/Keel-Linux/confconsole/issues
EOF
exit 1
}
Expand All @@ -36,7 +32,7 @@ Syntax $(basename "$0") [-h|--help] [-l|--lexicon-help] [<lexicon_args>] [<provi
Lexicon is a tool to manipulate DNS records on various DNS providers in a
standardized way.

TurnKey Confconsole leverages lexicon to support Let's Encrypt
Confconsole leverages lexicon to support Let's Encrypt
DNS challenges, via Dehydrated. This script ensures that lexicon is run
within it's required virtual environment.

Expand Down
Loading