Solved None key bugs and implemented tests to prevent it in the future

This commit is contained in:
Kevin Veen-Birkenbach 2025-07-08 02:13:47 +02:00
parent 9159a0c7d3
commit e729706ec6
No known key found for this signature in database
GPG Key ID: 44D8F11FD62F878E
16 changed files with 68 additions and 19 deletions

View File

@ -8,7 +8,6 @@ features:
css: true css: true
portfolio_iframe: true portfolio_iframe: true
central_database: true central_database: true
credentials:
domains: domains:
canonical: canonical:
- "accounting.{{ primary_domain }}" - "accounting.{{ primary_domain }}"

View File

@ -1,7 +1,6 @@
image: image:
web: "attendize_web:latest" web: "attendize_web:latest"
worker: "attendize_worker:latest" worker: "attendize_worker:latest"
credentials:
features: features:
matomo: true matomo: true
css: true css: true

View File

@ -1,6 +1,5 @@
enable_greenlight: "true" enable_greenlight: "true"
setup: false setup: false
credentials:
database: database:
name: "multiple_databases" name: "multiple_databases"
username: "postgres2" username: "postgres2"

View File

@ -2,7 +2,6 @@ images:
pds: "ghcr.io/bluesky-social/pds:latest" pds: "ghcr.io/bluesky-social/pds:latest"
pds: pds:
version: "latest" version: "latest"
credentials:
features: features:
matomo: true matomo: true
css: true css: true

View File

@ -1,7 +1,6 @@
network: "discourse_default" # Name of the docker network network: "discourse_default" # Name of the docker network
container: "discourse_application" # Name of the container application container: "discourse_application" # Name of the container application
repository: "discourse_repository" # Name of the repository folder repository: "discourse_repository" # Name of the repository folder
credentials:
features: features:
matomo: true matomo: true
css: true css: true

View File

@ -24,8 +24,8 @@ oauth2_proxy:
application: "application" application: "application"
port: "80" port: "80"
addons: addons:
keycloakpassword: keycloakpassword: {}
ldapauth: ldapauth: {}
docker: docker:
services: services:
database: database:

View File

@ -19,7 +19,6 @@ features:
ldap: true ldap: true
central_database: true central_database: true
oauth2: false # Doesn't make sense to activate it atm, because login is possible on homepage oauth2: false # Doesn't make sense to activate it atm, because login is possible on homepage
credentials:
domains: domains:
canonical: canonical:
- "audio.{{ primary_domain }}" - "audio.{{ primary_domain }}"

View File

@ -1,7 +1,6 @@
images: images:
keycloak: "quay.io/keycloak/keycloak:latest" keycloak: "quay.io/keycloak/keycloak:latest"
import_realm: True # If True realm will be imported. If false skip. import_realm: True # If True realm will be imported. If false skip.
credentials:
features: features:
matomo: true matomo: true
css: false css: false

View File

@ -3,7 +3,6 @@ images:
oauth2_proxy: oauth2_proxy:
application: application application: application
port: 80 port: 80
credentials:
features: features:
matomo: true matomo: true
css: true css: true

View File

@ -6,6 +6,5 @@ network:
public: False # Set to true in inventory file if you want to expose the LDAP port to the internet public: False # Set to true in inventory file if you want to expose the LDAP port to the internet
hostname: "ldap" # Hostname of the LDAP Server in the central_ldap network hostname: "ldap" # Hostname of the LDAP Server in the central_ldap network
webinterface: "lam" # The webinterface which should be used. Possible: lam and phpldapadmin webinterface: "lam" # The webinterface which should be used. Possible: lam and phpldapadmin
credentials:
features: features:
ldap: true ldap: true

View File

@ -3,7 +3,6 @@ oidc:
email_by_username: true # If true, then the mail is set by the username. If wrong then the OIDC user email is used email_by_username: true # If true, then the mail is set by the username. If wrong then the OIDC user email is used
enable_user_creation: true # Users will be created if not existing enable_user_creation: true # Users will be created if not existing
domain: "{{primary_domain}}" # The main domain from which mails will be send \ email suffix behind @ domain: "{{primary_domain}}" # The main domain from which mails will be send \ email suffix behind @
credentials:
features: features:
matomo: true matomo: true
css: false css: false

View File

@ -3,7 +3,6 @@ images:
streaming: "ghcr.io/mastodon/mastodon-streaming:latest" streaming: "ghcr.io/mastodon/mastodon-streaming:latest"
single_user_mode: false # Set true for initial setup single_user_mode: false # Set true for initial setup
setup: false # Set true in inventory file to execute the setup and initializing procedures setup: false # Set true in inventory file to execute the setup and initializing procedures
credentials:
features: features:
matomo: true matomo: true
css: true css: true

View File

@ -24,7 +24,6 @@ oidc:
# @see https://apps.nextcloud.com/apps/oidc_login # @see https://apps.nextcloud.com/apps/oidc_login
# @see https://apps.nextcloud.com/apps/sociallogin # @see https://apps.nextcloud.com/apps/sociallogin
flavor: "oidc_login" # Keeping on sociallogin because the other option is not implemented yet flavor: "oidc_login" # Keeping on sociallogin because the other option is not implemented yet
credentials:
features: features:
matomo: true matomo: true
css: false css: false

View File

@ -16,7 +16,7 @@ features:
central_database: false # Enable Central Database Network central_database: false # Enable Central Database Network
recaptcha: false # Enable ReCaptcha recaptcha: false # Enable ReCaptcha
oauth2: false # Enable the OAuth2-Proy oauth2: false # Enable the OAuth2-Proy
csp: csp: {}
domains: domains:
canonical: canonical:
- "icons.{{ primary_domain }}" - "icons.{{ primary_domain }}"

View File

@ -1,5 +1,5 @@
version: "latest" version: "latest"
oidc: oidc: {}
# Taiga doesn't have a functioning oidc support at the moment # Taiga doesn't have a functioning oidc support at the moment
# See # See
# - https://community.taiga.io/t/taiga-and-oidc-plugin/4866 # - https://community.taiga.io/t/taiga-and-oidc-plugin/4866

View File

@ -0,0 +1,61 @@
import os
import glob
import yaml
import unittest
def find_none_values(data, prefix=None):
"""
Recursively find keys with None values in a nested dict or list.
Returns a list of (path, value) tuples where value is None.
"""
errors = []
if prefix is None:
prefix = []
if isinstance(data, dict):
for key, value in data.items():
path = prefix + [str(key)]
if value is None:
errors.append((".".join(path), value))
elif isinstance(value, (dict, list)):
errors.extend(find_none_values(value, path))
elif isinstance(data, list):
for idx, item in enumerate(data):
path = prefix + [f"[{idx}]"]
if item is None:
errors.append((".".join(path), item))
elif isinstance(item, (dict, list)):
errors.extend(find_none_values(item, path))
return errors
class TestConfigurationNoNone(unittest.TestCase):
def test_configuration_files_have_no_none_values(self):
# Find all configuration.yml files under roles/*/vars
pattern = os.path.join(
os.path.dirname(__file__),
os.pardir, os.pardir,
'roles', '*', 'vars', 'configuration.yml'
)
files = glob.glob(pattern)
self.assertTrue(files, f"No configuration.yml files found with pattern: {pattern}")
all_errors = []
for filepath in files:
with open(filepath, 'r') as f:
try:
data = yaml.safe_load(f)
except yaml.YAMLError as e:
self.fail(f"Failed to parse YAML in {filepath}: {e}")
errors = find_none_values(data)
for path, value in errors:
all_errors.append(f"{filepath}: Key '{path}' is None")
if all_errors:
self.fail("None values found in configuration files:\n" + "\n".join(all_errors))
if __name__ == '__main__':
unittest.main()