Release version 0.3.0

This commit is contained in:
2025-12-17 12:33:30 +01:00
parent b94c0fc050
commit da06943f29
10 changed files with 148 additions and 51 deletions

View File

@@ -1,3 +1,11 @@
## [0.3.0] - 2025-12-17
* - Introduced a layered Docker architecture: Infinito.Nexus now builds on pre-built pkgmgr base images, with a clear separation between base tooling, application source, and runtime logic.
- Standardized container paths (`/opt/src/infinito`) and switched to a global virtual environment to ensure reproducible builds and consistent test execution.
- Unit and lint tests now run reliably on this new layer model, both locally and in CI.
- Refactored build, setup, and deploy workflows to match the new layered design and improve maintainability.
## [0.2.1] - 2025-12-10 ## [0.2.1] - 2025-12-10
* restored full deployability of the Sphinx app by fixing the application_id scoping bug. * restored full deployability of the Sphinx app by fixing the application_id scoping bug.

View File

@@ -1,27 +1,55 @@
import os import os
import warnings import warnings
from typing import Optional
_SOUND_DISABLED_REASON: Optional[str] = None
_SOUND_WARNED: bool = False
def _warn_sound_disabled_once() -> None:
"""
Emit the 'Sound support disabled' warning at most once per Python process.
Important:
- Do NOT warn at import time (avoids noisy unit test output).
- Warn only when a sound function is actually called.
"""
global _SOUND_WARNED
if _SOUND_WARNED:
return
if not _SOUND_DISABLED_REASON:
return
_SOUND_WARNED = True
warnings.warn(
f"Sound support disabled: {_SOUND_DISABLED_REASON}",
RuntimeWarning,
stacklevel=2,
)
class DummySound: class DummySound:
@staticmethod @staticmethod
def play_start_sound() -> None: def play_start_sound() -> None:
pass _warn_sound_disabled_once()
@staticmethod @staticmethod
def play_infinito_intro_sound() -> None: def play_infinito_intro_sound() -> None:
pass _warn_sound_disabled_once()
@staticmethod @staticmethod
def play_finished_successfully_sound() -> None: def play_finished_successfully_sound() -> None:
pass _warn_sound_disabled_once()
@staticmethod @staticmethod
def play_finished_failed_sound() -> None: def play_finished_failed_sound() -> None:
pass _warn_sound_disabled_once()
@staticmethod @staticmethod
def play_warning_sound() -> None: def play_warning_sound() -> None:
pass _warn_sound_disabled_once()
try: try:
@@ -210,7 +238,9 @@ try:
cls._prepare_and_play(freqs) cls._prepare_and_play(freqs)
@classmethod @classmethod
def _prepare_and_play(cls, freqs: list[float], durations: list[float] | None = None) -> None: def _prepare_and_play(
cls, freqs: list[float], durations: list[float] | None = None
) -> None:
count = len(freqs) count = len(freqs)
if durations is None: if durations is None:
@@ -223,5 +253,7 @@ try:
cls._play(np.concatenate(waves)) cls._play(np.concatenate(waves))
except ImportError as exc: except ImportError as exc:
warnings.warn(f"Sound support disabled: {exc}", RuntimeWarning) # Do NOT warn at import time — this module is used in many unit tests / subprocess calls.
# Warn only when a sound method is actually invoked.
_SOUND_DISABLED_REASON = str(exc)
Sound = DummySound Sound = DummySound

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "infinito-nexus" name = "infinito-nexus"
version = "0.0.0" version = "0.3.0"
description = "Infinito.Nexus" description = "Infinito.Nexus"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"

View File

@@ -4,7 +4,7 @@ galaxy_info:
description: "Installs and links Nextcloud desktop client folders for cloud-integrated user environments." description: "Installs and links Nextcloud desktop client folders for cloud-integrated user environments."
license: "Infinito.Nexus NonCommercial License" license: "Infinito.Nexus NonCommercial License"
license_url: "https://s.infinito.nexus/license" license_url: "https://s.infinito.nexus/license"
company: | company: |
Kevin Veen-Birkenbach Kevin Veen-Birkenbach
Consulting & Coaching Solutions Consulting & Coaching Solutions
https://www.veen.world https://www.veen.world
@@ -23,5 +23,3 @@ galaxy_info:
repository: https://s.infinito.nexus/code repository: https://s.infinito.nexus/code
issue_tracker_url: https://s.infinito.nexus/issues issue_tracker_url: https://s.infinito.nexus/issues
documentation: "https://docs.infinito.nexus/" documentation: "https://docs.infinito.nexus/"
dependencies: []

View File

@@ -3,32 +3,45 @@
name: nextcloud-client name: nextcloud-client
state: present state: present
- name: Link homefolders to cloud - name: Initialize dotlinker mapping list
ansible.builtin.file: ansible.builtin.set_fact:
src: "{{nextcloud_cloud_directory}}{{ item }}" nextcloud_dotlinker_mappings: []
dest: "{{nextcloud_user_home_directory}}{{ item }}"
owner: "{{ users[desktop_username].username }}"
group: "{{ users[desktop_username].username }}"
state: link
force: yes
ignore_errors: true # Just temporary @todo remove
loop:
- Templates
- Documents
- Videos
- Pictures
- Music
- Desktop
- Software
- Downloads
- Workspaces
- Books
- Screenshots
- name: Link dump folder - name: Build dotlinker mappings for Nextcloud folders
ansible.builtin.file: ansible.builtin.set_fact:
src: "{{nextcloud_cloud_directory}}InstantUpload" nextcloud_dotlinker_mappings: >-
dest: "{{nextcloud_user_home_directory}}Dump" {{
owner: "{{ users[desktop_username].username }}" nextcloud_dotlinker_mappings + [
group: "{{ users[desktop_username].username }}" {
state: link 'name': 'nextcloud-' ~ (item | lower),
'backend': 'cloud',
'src': nextcloud_user_home_directory ~ item,
'dest': nextcloud_cloud_directory ~ item
}
]
}}
loop: "{{ nextcloud_dotlinker_folders }}"
- name: Add dump mapping (Dump -> InstantUpload)
ansible.builtin.set_fact:
nextcloud_dotlinker_mappings: >-
{{
nextcloud_dotlinker_mappings + [
{
'name': 'nextcloud-dump',
'backend': 'cloud',
'src': nextcloud_user_home_directory ~ 'Dump',
'dest': nextcloud_cloud_directory ~ 'InstantUpload'
}
]
}}
- name: Apply Nextcloud folder mappings via dotlinker
ansible.builtin.include_role:
name: desk-dotlinker
vars:
dotlinker_user: "{{ users[desktop_username].username }}"
dotlinker_config_path: "{{ nextcloud_user_home_directory }}.config/dotlinker/config.yaml"
dotlinker_replace: true
dotlinker_apply: true
dotlinker_mappings: "{{ nextcloud_dotlinker_mappings }}"

View File

@@ -1,4 +1,17 @@
application_id: desk-nextcloud application_id: desk-nextcloud
nextcloud_user_home_directory: "/home/{{ users[desktop_username].username }}/" nextcloud_user_home_directory: "/home/{{ users[desktop_username].username }}/"
nextcloud_cloud_fqdn: "{{ applications | get_app_conf(application_id, 'credentials.cloud_fqdn') }}" nextcloud_cloud_fqdn: "{{ applications | get_app_conf(application_id, 'credentials.cloud_fqdn') }}"
nextcloud_cloud_directory: '{{ nextcloud_user_home_directory }}Clouds/{{nextcloud_cloud_fqdn}}/{{ users[desktop_username].username }}/' nextcloud_cloud_directory: "{{ nextcloud_user_home_directory }}Clouds/{{ nextcloud_cloud_fqdn }}/{{ users[desktop_username].username }}/"
nextcloud_dotlinker_folders:
- Templates
- Documents
- Videos
- Pictures
- Music
- Desktop
- Software
- Downloads
- Workspaces
- Books
- Screenshots

View File

@@ -5,6 +5,7 @@
- sys-bkp-provider - sys-bkp-provider
- sys-ctl-alm-compose - sys-ctl-alm-compose
- sys-lock - sys-lock
- dev-nix
- include_tasks: 02_pkgmgr_routines.yml - include_tasks: 02_pkgmgr_routines.yml
when: backup_docker_to_local_folder is not defined when: backup_docker_to_local_folder is not defined

View File

@@ -1,19 +1,50 @@
- block: - block:
- name: "pkgmgr install {{ BKP_DOCKER_2_LOC_PKG }}" - name: "Install/update {{ BKP_DOCKER_2_LOC_PKG }} via pkgmgr (nix run)"
include_role: become: true
name: pkgmgr-install ansible.builtin.command:
vars: argv:
package_name: "{{ BKP_DOCKER_2_LOC_PKG }}" - nix
- run
- --no-write-lock-file
- "github:kevinveenbirkenbach/package-manager#pkgmgr"
- --
- update
- "{{ BKP_DOCKER_2_LOC_PKG }}"
- --dependencies
- --clone-mode
- shallow
register: pkgmgr_update_result
changed_when: >
('already up to date' not in ((pkgmgr_update_result.stdout | default('') | lower)
~ ' ' ~ (pkgmgr_update_result.stderr | default('') | lower)))
and
('no command defined' not in ((pkgmgr_update_result.stdout | default('') | lower)
~ ' ' ~ (pkgmgr_update_result.stderr | default('') | lower)))
failed_when: >
(pkgmgr_update_result.rc != 0)
and
('no command defined' not in ((pkgmgr_update_result.stdout | default('') | lower)
~ ' ' ~ (pkgmgr_update_result.stderr | default('') | lower)))
- name: "Retrieve {{ BKP_DOCKER_2_LOC_PKG }} path from pkgmgr" - name: "Retrieve {{ BKP_DOCKER_2_LOC_PKG }} path via pkgmgr (nix run)"
command: "pkgmgr path {{ BKP_DOCKER_2_LOC_PKG }}" become: true
ansible.builtin.command:
argv:
- nix
- run
- --no-write-lock-file
- "github:kevinveenbirkenbach/package-manager#pkgmgr"
- --
- path
- "{{ BKP_DOCKER_2_LOC_PKG }}"
register: pkgmgr_output register: pkgmgr_output
changed_when: false changed_when: false
- name: Set fact for backup_docker_to_local_folder - name: Set fact for backup_docker_to_local_folder
set_fact: ansible.builtin.set_fact:
backup_docker_to_local_folder: "{{ pkgmgr_output.stdout }}/" backup_docker_to_local_folder: "{{ (pkgmgr_output.stdout | trim) ~ '/' }}"
changed_when: false changed_when: false
when: backup_docker_to_local_folder is not defined when: backup_docker_to_local_folder is not defined
vars: vars:
BKP_DOCKER_2_LOC_PKG: backup-docker-to-local BKP_DOCKER_2_LOC_PKG: backup-docker-to-local

View File

@@ -6,7 +6,7 @@ configuration:
default_push_create_private: True # Default private when creating a new repository with push-to-create. default_push_create_private: True # Default private when creating a new repository with push-to-create.
features: features:
matomo: true matomo: true
css: false css: true
desktop: true desktop: true
central_database: true central_database: true
ldap: true ldap: true

View File

@@ -14,7 +14,8 @@ GITEA_LDAP_AUTH_ARGS:
- '--bind-dn "{{ LDAP.DN.ADMINISTRATOR.DATA }}"' - '--bind-dn "{{ LDAP.DN.ADMINISTRATOR.DATA }}"'
- '--bind-password "{{ LDAP.BIND_CREDENTIAL }}"' - '--bind-password "{{ LDAP.BIND_CREDENTIAL }}"'
- '--user-search-base "{{ LDAP.DN.OU.USERS }}"' - '--user-search-base "{{ LDAP.DN.OU.USERS }}"'
- '--user-filter "(&(objectClass=inetOrgPerson)(uid=%s))"' - '--user-filter "(&(objectClass=inetOrgPerson)(uid=%s))"'
- '--admin-filter "(memberOf=cn={{ application_id }}-administrator,{{ LDAP.DN.OU.ROLES }})"'
- '--username-attribute "{{ LDAP.USER.ATTRIBUTES.ID }}"' - '--username-attribute "{{ LDAP.USER.ATTRIBUTES.ID }}"'
- '--firstname-attribute "{{ LDAP.USER.ATTRIBUTES.FIRSTNAME }}"' - '--firstname-attribute "{{ LDAP.USER.ATTRIBUTES.FIRSTNAME }}"'
- '--surname-attribute "{{ LDAP.USER.ATTRIBUTES.SURNAME }}"' - '--surname-attribute "{{ LDAP.USER.ATTRIBUTES.SURNAME }}"'