mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-12-19 23:33:03 +00:00
Release version 0.3.0
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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: []
|
|
||||||
@@ -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 }}"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 }}"'
|
||||||
|
|||||||
Reference in New Issue
Block a user