9 Commits

Author SHA1 Message Date
0e89d89b45 Make sound support optional and guard against missing audio dependencies
- Move simpleaudio to optional dependency (audio extra)
- Add DummySound fallback when optional audio libs are unavailable
- Import simpleaudio/numpy lazily with ImportError handling
- Remove Docker-specific sound disabling logic
- Improve typing and robustness of sound utilities

https://chatgpt.com/share/693dec1d-60bc-800f-8ffe-3886a9c265bd
2025-12-13 23:43:36 +01:00
d0882433c8 Refactor setup workflow and make install robust via virtualenv
- Introduce a dedicated Python virtualenv (deps target) and run all setup scripts through it
- Fix missing PyYAML errors in clean, CI, and Nix environments
- Refactor build defaults into cli/setup for clearer semantics
- Make setup deterministic and independent from system Python
- Replace early Makefile shell expansion with runtime evaluation
- Rename messy-test to test-messy and update deploy logic and tests accordingly
- Keep setup and test targets consistent across Makefile, CLI, and unit tests

https://chatgpt.com/share/693de226-00ac-800f-8cbd-06552b2f283c
2025-12-13 23:00:13 +01:00
600d7a1fe8 Ignored python package build files 2025-12-13 22:13:53 +01:00
0580839705 Makefile: unify Python interpreter via PYTHON variable
Avoids mixed system/Nix/venv Python usage and fixes missing PyYAML errors.

https://chatgpt.com/share/693dd6b2-14f0-800f-9b95-368d58b68f49
2025-12-13 22:12:12 +01:00
7070100363 Added missing PyYAML 2025-12-13 21:41:29 +01:00
ad813df0c5 Switch to pyproject.toml for Python dependencies
Introduce pyproject.toml as the single source of truth for Python dependencies.
Remove legacy requirements.txt and simplify requirements.yml to Ansible collections only.
Drop pytest in favor of the built-in unittest framework.

https://chatgpt.com/share/693dbe8c-8b64-800f-a6e5-41b7d21ae7e0
2025-12-13 20:29:09 +01:00
f8e2aa2b93 Added mirrors 2025-12-13 09:27:04 +01:00
d0a2c3fada Release version 0.2.1 2025-12-10 21:14:47 +01:00
75eaecce5b **Remove obsolete installation/administration docs, fix pgAdmin server mode condition, normalize git repository vars, and ensure correct application_id for web-app-sphinx**
* Remove outdated `Installation.md` and `Administration.md` documentation from Akaunting and Peertube roles
* Fix `server_mode` conditional in `web-app-pgadmin` to avoid unintended defaults
* Normalize formatting of git repository variables in `web-app-roulette-wheel`
* Explicitly set `application_id` when loading `sys-stk-full-stateless` in `web-app-sphinx` to prevent scoping issues

https://chatgpt.com/share/6939d42e-483c-800f-b0fc-be61caab615d
2025-12-10 21:12:15 +01:00
24 changed files with 312 additions and 271 deletions

2
.gitignore vendored
View File

@@ -10,3 +10,5 @@ venv
*tree.json
roles/list.json
*.pyc
*.egg-info
build

View File

@@ -1,3 +1,8 @@
## [0.2.1] - 2025-12-10
* restored full deployability of the Sphinx app by fixing the application_id scoping bug.
## [0.2.0] - 2025-12-10
* Added full Nix installer integration with dynamic upstream SHA256 verification, OS-specific installation paths, template-driven configuration, and updated pkgmgr integration.

3
MIRRORS Normal file
View File

@@ -0,0 +1,3 @@
git@github.com:infinito-nexus/core.git
ssh://git@code.infinito.nexus:2201/infinito/nexus.git
git@github.com:kevinveenbirkenbach/infinito-nexus.git

View File

@@ -1,12 +1,14 @@
SHELL := /usr/bin/env bash
VENV ?= .venv
PYTHON := $(VENV)/bin/python
PIP := $(PYTHON) -m pip
ROLES_DIR := ./roles
APPLICATIONS_OUT := ./group_vars/all/04_applications.yml
APPLICATIONS_SCRIPT := ./cli/build/defaults/applications.py
APPLICATIONS_SCRIPT := ./cli/setup/applications.py
USERS_SCRIPT := ./cli/setup/users.py
USERS_OUT := ./group_vars/all/03_users.yml
USERS_SCRIPT := ./cli/build/defaults/users.py
INCLUDES_SCRIPT := ./cli/build/role_include.py
INCLUDE_GROUPS := $(shell python3 main.py meta categories invokable -s "-" --no-signal | tr '\n' ' ')
# Directory where these include-files will be written
INCLUDES_OUT_DIR := ./tasks/groups
@@ -19,7 +21,7 @@ RESERVED_USERNAMES := $(shell \
| paste -sd, - \
)
.PHONY: build install test
.PHONY: deps setup setup-clean test-messy test install
clean-keep-logs:
@echo "🧹 Cleaning ignored files but keeping logs/…"
@@ -31,11 +33,11 @@ clean:
list:
@echo Generating the roles list
python3 main.py build roles_list
$(PYTHON) main.py build roles_list
tree:
@echo Generating Tree
python3 main.py build tree -D 2 --no-signal
$(PYTHON) main.py build tree -D 2 --no-signal
mig: list tree
@echo Creating meta data for meta infinity graph
@@ -45,41 +47,51 @@ dockerignore:
cat .gitignore > .dockerignore
echo ".git" >> .dockerignore
messy-build: dockerignore
setup: deps dockerignore
@echo "🔧 Generating users defaults → $(USERS_OUT)"
python3 $(USERS_SCRIPT) \
$(PYTHON) $(USERS_SCRIPT) \
--roles-dir $(ROLES_DIR) \
--output $(USERS_OUT) \
--reserved-usernames "$(RESERVED_USERNAMES)"
@echo "✅ Users defaults written to $(USERS_OUT)\n"
@echo "🔧 Generating applications defaults → $(APPLICATIONS_OUT)"
python3 $(APPLICATIONS_SCRIPT) \
$(PYTHON) $(APPLICATIONS_SCRIPT) \
--roles-dir $(ROLES_DIR) \
--output-file $(APPLICATIONS_OUT)
@echo "✅ Applications defaults written to $(APPLICATIONS_OUT)\n"
@echo "🔧 Generating role-include files for each group…"
@mkdir -p $(INCLUDES_OUT_DIR)
@$(foreach grp,$(INCLUDE_GROUPS), \
out=$(INCLUDES_OUT_DIR)/$(grp)roles.yml; \
echo "→ Building $$out (pattern: '$(grp)')…"; \
python3 $(INCLUDES_SCRIPT) $(ROLES_DIR) \
-p $(grp) -o $$out; \
@INCLUDE_GROUPS="$$( $(PYTHON) main.py meta categories invokable -s "-" --no-signal | tr '\n' ' ' )"; \
for grp in $$INCLUDE_GROUPS; do \
out="$(INCLUDES_OUT_DIR)/$${grp}roles.yml"; \
echo "→ Building $$out (pattern: '$$grp')…"; \
$(PYTHON) $(INCLUDES_SCRIPT) $(ROLES_DIR) -p $$grp -o $$out; \
echo "$$out"; \
)
done
messy-test:
setup-clean: clean setup
@echo "Full build with cleanup before was executed."
test-messy:
@echo "🧪 Running Python tests…"
PYTHONPATH=. python -m unittest discover -s tests
PYTHONPATH=. $(PYTHON) -m unittest discover -s tests
@echo "📑 Checking Ansible syntax…"
ansible-playbook -i localhost, -c local $(foreach f,$(wildcard group_vars/all/*.yml),-e @$(f)) playbook.yml --syntax-check
install: build
@echo "⚙️ Install complete."
test: setup-clean test-messy
@echo "Full test with setup-clean before was executed."
build: clean messy-build
@echo "Full build with cleanup before was executed."
deps:
@if [ ! -x "$(PYTHON)" ]; then \
echo "🐍 Creating virtualenv $(VENV)"; \
python3 -m venv $(VENV); \
fi
@echo "📦 Installing Python dependencies"
@$(PIP) install --upgrade pip setuptools wheel
@$(PIP) install -e .
install: deps
@echo "✅ Python environment installed (editable)."
test: build messy-test
@echo "Full test with build before was executed."

View File

@@ -95,8 +95,8 @@ def run_ansible_playbook(
# 4) Test Phase
# ---------------------------------------------------------
if not skip_tests:
print("\n🧪 Running tests (make messy-test)...\n")
subprocess.run(["make", "messy-test"], check=True)
print("\n🧪 Running tests (make test-messy)...\n")
subprocess.run(["make", "test-messy"], check=True)
else:
print("\n🧪 Tests skipped (--skip-tests)\n")

View File

@@ -209,7 +209,7 @@ def print_global_help(available, cli_dir):
Fore.CYAN
))
print(color_text(
" corresponds to `cli/build/defaults/users.py`.",
" corresponds to `cli/setup/users.py`.",
Fore.CYAN
))
print()

View File

@@ -1,186 +1,227 @@
import os
import warnings
class DummySound:
@staticmethod
def play_start_sound(): pass
def play_start_sound() -> None:
pass
@staticmethod
def play_infinito_intro_sound(): pass
def play_infinito_intro_sound() -> None:
pass
@staticmethod
def play_finished_successfully_sound(): pass
def play_finished_successfully_sound() -> None:
pass
@staticmethod
def play_finished_failed_sound(): pass
def play_finished_failed_sound() -> None:
pass
@staticmethod
def play_warning_sound(): pass
def play_warning_sound() -> None:
pass
_IN_DOCKER = os.path.exists('/.dockerenv')
if _IN_DOCKER:
Sound = DummySound
else:
try:
import numpy as np
import simpleaudio as sa
import shutil, subprocess, tempfile, wave as wavmod
class Sound:
"""
Sound effects for the application with enhanced complexity.
Each sound uses at least 6 distinct tones and lasts no more than max_length seconds,
except the intro sound which is a detailed 26-second Berlin techno-style build-up, 12-second celebration with a descending-fifth chord sequence of 7 chords, and breakdown with melodic background.
Transitions between phases now crossfade over 3 seconds for smoother flow.
"""
try:
import numpy as np
import simpleaudio as sa
import shutil
import subprocess
import tempfile
import wave as wavmod
fs = 44100 # Sampling rate (samples per second)
complexity_factor = 10 # Number of harmonics to sum for richer timbres
max_length = 2.0 # Maximum total duration of any sound in seconds
class Sound:
"""
Sound effects for the application.
"""
@staticmethod
def _generate_complex_wave(frequency: float, duration: float, harmonics: int = None) -> np.ndarray:
if harmonics is None:
harmonics = Sound.complexity_factor
t = np.linspace(0, duration, int(Sound.fs * duration), False)
wave = np.zeros_like(t)
for n in range(1, harmonics + 1):
wave += (1 / n) * np.sin(2 * np.pi * frequency * n * t)
# ADSR envelope
attack = int(0.02 * Sound.fs)
release = int(0.05 * Sound.fs)
env = np.ones_like(wave)
env[:attack] = np.linspace(0, 1, attack)
env[-release:] = np.linspace(1, 0, release)
wave *= env
wave /= np.max(np.abs(wave))
return (wave * (2**15 - 1)).astype(np.int16)
fs = 44100
complexity_factor = 10
max_length = 2.0
@staticmethod
def _crossfade(w1: np.ndarray, w2: np.ndarray, fade_len: int) -> np.ndarray:
# Ensure fade_len less than each
fade_len = min(fade_len, len(w1), len(w2))
fade_out = np.linspace(1, 0, fade_len)
fade_in = np.linspace(0, 1, fade_len)
w1_end = w1[-fade_len:] * fade_out
w2_start = w2[:fade_len] * fade_in
middle = (w1_end + w2_start).astype(np.int16)
return np.concatenate([w1[:-fade_len], middle, w2[fade_len:]])
@staticmethod
def _generate_complex_wave(
frequency: float,
duration: float,
harmonics: int | None = None,
) -> np.ndarray:
if harmonics is None:
harmonics = Sound.complexity_factor
@staticmethod
def _play_via_system(wave: np.ndarray):
# Write a temp WAV and play it via available system player
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as f:
fname = f.name
try:
with wavmod.open(fname, "wb") as w:
w.setnchannels(1)
w.setsampwidth(2)
w.setframerate(Sound.fs)
w.writeframes(wave.tobytes())
def run(cmd):
return subprocess.run(
cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
).returncode == 0
# Preferred order: PipeWire → PulseAudio → ALSA → ffplay
if shutil.which("pw-play") and run(["pw-play", fname]): return
if shutil.which("paplay") and run(["paplay", fname]): return
if shutil.which("aplay") and run(["aplay", "-q", fname]): return
if shutil.which("ffplay") and run(["ffplay", "-autoexit", "-nodisp", fname]): return
# Last resort if no system player exists: simpleaudio
play_obj = sa.play_buffer(wave, 1, 2, Sound.fs)
play_obj.wait_done()
finally:
try: os.unlink(fname)
except Exception: pass
t = np.linspace(0, duration, int(Sound.fs * duration), False)
wave = np.zeros_like(t)
@staticmethod
def _play(wave: np.ndarray):
# Switch via env: system | simpleaudio | auto (default)
backend = os.getenv("INFINITO_AUDIO_BACKEND", "auto").lower()
if backend == "system":
return Sound._play_via_system(wave)
if backend == "simpleaudio":
play_obj = sa.play_buffer(wave, 1, 2, Sound.fs)
play_obj.wait_done()
for n in range(1, harmonics + 1):
wave += (1 / n) * np.sin(2 * np.pi * frequency * n * t)
# ADSR envelope
attack = int(0.02 * Sound.fs)
release = int(0.05 * Sound.fs)
env = np.ones_like(wave)
env[:attack] = np.linspace(0, 1, attack)
env[-release:] = np.linspace(1, 0, release)
wave *= env
wave /= np.max(np.abs(wave))
return (wave * (2**15 - 1)).astype(np.int16)
@staticmethod
def _crossfade(w1: np.ndarray, w2: np.ndarray, fade_len: int) -> np.ndarray:
fade_len = min(fade_len, len(w1), len(w2))
if fade_len <= 0:
return np.concatenate([w1, w2])
fade_out = np.linspace(1, 0, fade_len)
fade_in = np.linspace(0, 1, fade_len)
w1_end = w1[-fade_len:].astype(np.float32) * fade_out
w2_start = w2[:fade_len].astype(np.float32) * fade_in
middle = (w1_end + w2_start).astype(np.int16)
return np.concatenate([w1[:-fade_len], middle, w2[fade_len:]])
@staticmethod
def _play_via_system(wave: np.ndarray) -> None:
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as f:
fname = f.name
try:
with wavmod.open(fname, "wb") as w:
w.setnchannels(1)
w.setsampwidth(2)
w.setframerate(Sound.fs)
w.writeframes(wave.tobytes())
def run(cmd: list[str]) -> bool:
return (
subprocess.run(
cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
).returncode
== 0
)
if shutil.which("pw-play") and run(["pw-play", fname]):
return
# auto: try simpleaudio first; if it fails, fall back to system
if shutil.which("paplay") and run(["paplay", fname]):
return
if shutil.which("aplay") and run(["aplay", "-q", fname]):
return
if shutil.which("ffplay") and run(["ffplay", "-autoexit", "-nodisp", fname]):
return
play_obj = sa.play_buffer(wave, 1, 2, Sound.fs)
play_obj.wait_done()
finally:
try:
play_obj = sa.play_buffer(wave, 1, 2, Sound.fs)
play_obj.wait_done()
os.unlink(fname)
except Exception:
Sound._play_via_system(wave)
pass
@classmethod
def play_infinito_intro_sound(cls):
# Phase durations
build_time = 10.0
celebr_time = 12.0
breakdown_time = 10.0
overlap = 3.0 # seconds of crossfade
bass_seg = 0.125 # 1/8s kick
melody_seg = 0.25 # 2/8s melody
bass_freq = 65.41 # C2 kick
melody_freqs = [261.63, 293.66, 329.63, 392.00, 440.00, 523.25]
@staticmethod
def _play(wave: np.ndarray) -> None:
backend = os.getenv("INFINITO_AUDIO_BACKEND", "auto").lower()
# Build-up phase
steps = int(build_time / (bass_seg + melody_seg))
build_seq = []
for i in range(steps):
amp = (i + 1) / steps
b = cls._generate_complex_wave(bass_freq, bass_seg).astype(np.float32) * amp
m = cls._generate_complex_wave(melody_freqs[i % len(melody_freqs)], melody_seg).astype(np.float32) * amp
build_seq.append(b.astype(np.int16))
build_seq.append(m.astype(np.int16))
build_wave = np.concatenate(build_seq)
if backend == "system":
Sound._play_via_system(wave)
return
# Celebration phase: 7 descending-fifth chords
roots = [523.25, 349.23, 233.08, 155.56, 103.83, 69.30, 46.25]
chord_time = celebr_time / len(roots)
celebr_seq = []
for root in roots:
t = np.linspace(0, chord_time, int(cls.fs * chord_time), False)
chord = sum(np.sin(2 * np.pi * f * t) for f in [root, root * 5/4, root * 3/2])
chord /= np.max(np.abs(chord))
celebr_seq.append((chord * (2**15 - 1)).astype(np.int16))
celebr_wave = np.concatenate(celebr_seq)
if backend == "simpleaudio":
play_obj = sa.play_buffer(wave, 1, 2, Sound.fs)
play_obj.wait_done()
return
# Breakdown phase (mirror of build-up)
breakdown_wave = np.concatenate(list(reversed(build_seq)))
# auto
try:
play_obj = sa.play_buffer(wave, 1, 2, Sound.fs)
play_obj.wait_done()
except Exception:
Sound._play_via_system(wave)
# Crossfade transitions
fade_samples = int(overlap * cls.fs)
bc = cls._crossfade(build_wave, celebr_wave, fade_samples)
full = cls._crossfade(bc, breakdown_wave, fade_samples)
@classmethod
def play_infinito_intro_sound(cls) -> None:
build_time = 10.0
celebr_time = 12.0
breakdown_time = 10.0
overlap = 3.0
cls._play(full)
bass_seg = 0.125
melody_seg = 0.25
bass_freq = 65.41
melody_freqs = [261.63, 293.66, 329.63, 392.00, 440.00, 523.25]
@classmethod
def play_start_sound(cls):
freqs = [523.25, 659.26, 783.99, 880.00, 1046.50, 1174.66]
cls._prepare_and_play(freqs)
steps = int(build_time / (bass_seg + melody_seg))
build_seq: list[np.ndarray] = []
@classmethod
def play_finished_successfully_sound(cls):
freqs = [523.25, 587.33, 659.26, 783.99, 880.00, 987.77]
cls._prepare_and_play(freqs)
for i in range(steps):
amp = (i + 1) / steps
b = cls._generate_complex_wave(bass_freq, bass_seg).astype(np.float32) * amp
m = cls._generate_complex_wave(
melody_freqs[i % len(melody_freqs)], melody_seg
).astype(np.float32) * amp
build_seq.append(b.astype(np.int16))
build_seq.append(m.astype(np.int16))
@classmethod
def play_finished_failed_sound(cls):
freqs = [880.00, 830.61, 783.99, 659.26, 622.25, 523.25]
durations = [0.4, 0.3, 0.25, 0.25, 0.25, 0.25]
cls._prepare_and_play(freqs, durations)
build_wave = np.concatenate(build_seq)
@classmethod
def play_warning_sound(cls):
freqs = [700.00, 550.00, 750.00, 500.00, 800.00, 450.00]
cls._prepare_and_play(freqs)
roots = [523.25, 349.23, 233.08, 155.56, 103.83, 69.30, 46.25]
chord_time = celebr_time / len(roots)
celebr_seq: list[np.ndarray] = []
@classmethod
def _prepare_and_play(cls, freqs, durations=None):
count = len(freqs)
if durations is None:
durations = [cls.max_length / count] * count
else:
total = sum(durations)
durations = [d * cls.max_length / total for d in durations]
waves = [cls._generate_complex_wave(f, d) for f, d in zip(freqs, durations)]
cls._play(np.concatenate(waves))
except Exception:
warnings.warn("Sound support disabled: numpy or simpleaudio could not be imported", RuntimeWarning)
Sound = DummySound
for root in roots:
t = np.linspace(0, chord_time, int(cls.fs * chord_time), False)
chord = sum(np.sin(2 * np.pi * f * t) for f in [root, root * 5 / 4, root * 3 / 2])
chord /= np.max(np.abs(chord))
celebr_seq.append((chord * (2**15 - 1)).astype(np.int16))
celebr_wave = np.concatenate(celebr_seq)
breakdown_wave = np.concatenate(list(reversed(build_seq)))
fade_samples = int(overlap * cls.fs)
bc = cls._crossfade(build_wave, celebr_wave, fade_samples)
full = cls._crossfade(bc, breakdown_wave, fade_samples)
cls._play(full)
@classmethod
def play_start_sound(cls) -> None:
freqs = [523.25, 659.26, 783.99, 880.00, 1046.50, 1174.66]
cls._prepare_and_play(freqs)
@classmethod
def play_finished_successfully_sound(cls) -> None:
freqs = [523.25, 587.33, 659.26, 783.99, 880.00, 987.77]
cls._prepare_and_play(freqs)
@classmethod
def play_finished_failed_sound(cls) -> None:
freqs = [880.00, 830.61, 783.99, 659.26, 622.25, 523.25]
durations = [0.4, 0.3, 0.25, 0.25, 0.25, 0.25]
cls._prepare_and_play(freqs, durations)
@classmethod
def play_warning_sound(cls) -> None:
freqs = [700.00, 550.00, 750.00, 500.00, 800.00, 450.00]
cls._prepare_and_play(freqs)
@classmethod
def _prepare_and_play(cls, freqs: list[float], durations: list[float] | None = None) -> None:
count = len(freqs)
if durations is None:
durations = [cls.max_length / count] * count
else:
total = sum(durations)
durations = [d * cls.max_length / total for d in durations]
waves = [cls._generate_complex_wave(f, d) for f, d in zip(freqs, durations)]
cls._play(np.concatenate(waves))
except ImportError as exc:
warnings.warn(f"Sound support disabled: {exc}", RuntimeWarning)
Sound = DummySound

48
pyproject.toml Normal file
View File

@@ -0,0 +1,48 @@
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "infinito-nexus"
version = "0.0.0"
description = "Infinito.Nexus"
readme = "README.md"
requires-python = ">=3.10"
license = { file = "LICENSE.md" }
dependencies = [
"numpy",
"ansible",
"colorscheme-generator @ https://github.com/kevinveenbirkenbach/colorscheme-generator/archive/refs/tags/v0.3.0.zip",
"bcrypt",
"ruamel.yaml",
"PyYAML",
"tld",
"passlib",
"requests",
]
[project.optional-dependencies]
audio = [
"simpleaudio",
]
[tool.setuptools]
# Non-src layout: explicitly control packaged modules
packages = { find = { where = ["."], include = [
"cli*",
"filter_plugins*",
"lookup_plugins*",
"module_utils*",
"library*",
], exclude = [
"roles*",
"assets*",
"docs*",
"templates*",
"logs*",
"tasks*",
"tests*",
"__pycache__*",
] } }
include-package-data = true

View File

@@ -1,9 +0,0 @@
colorscheme-generator @ https://github.com/kevinveenbirkenbach/colorscheme-generator/archive/refs/tags/v0.3.0.zip
numpy
bcrypt
ruamel.yaml
tld
passlib
requests
ansible
pytest

View File

@@ -2,8 +2,3 @@ collections:
- name: kewlfft.aur
- name: community.general
- name: hetzner.hcloud
yay:
- python-simpleaudio
- python-numpy
pacman:
- ansible

View File

@@ -1,29 +0,0 @@
# Installation Guide
1. **Navigate to the Docker Compose Directory**
Change into the directory where the Docker Compose files reside.
```bash
cd {{ PATH_DOCKER_COMPOSE_INSTANCES }}akaunting/
```
2. **Set Environment Variables**
Ensure timeouts are increased to handle long operations:
```bash
export COMPOSE_HTTP_TIMEOUT=600
export DOCKER_CLIENT_TIMEOUT=600
```
3. **Start Akaunting Service**
Run the setup command with the `AKAUNTING_SETUP` variable:
```bash
AKAUNTING_SETUP=true docker-compose -p akaunting up -d
```
4. **Finalizing Setup**
After verifying that the web interface works, restart services:
```bash
docker-compose down
docker-compose -p akaunting up -d
```
For further details, visit the [Akaunting Documentation](https://akaunting.com/) and the [Akaunting GitHub Repository](https://github.com/akaunting/docker).

View File

@@ -1,29 +0,0 @@
# Administration
## track docker container status
```bash
watch -n 2 "docker ps -a | grep peertube"
```
## clean rebuild
```bash
cd {{ PATH_DOCKER_COMPOSE_INSTANCES }}peertube/ &&
docker-compose down
docker volume rm peertube_assets peertube_config peertube_data peertube_database peertube_redis
docker-compose up -d
```
## access terminal
```bash
docker-compose exec -it application /bin/bash
```
## update config
```bash
apt update && apt install nano && nano ./config/default.yaml
```
## get root pasword
```bash
docker logs peertube-application-1 | grep -A1 root
```

View File

@@ -5,4 +5,4 @@
- name: "configure pgadmin servers"
include_tasks: configuration.yml
when: applications | get_app_conf(application_id, 'server_mode', True) | bool
when: applications | get_app_conf(application_id, 'server_mode') | bool

View File

@@ -3,6 +3,6 @@
name: sys-stk-full-stateless
vars:
docker_compose_flush_handlers: true
docker_git_repository_address: "https://github.com/kevinveenbirkenbach/roulette-wheel.git"
docker_git_repository_address: "https://github.com/kevinveenbirkenbach/roulette-wheel.git"
docker_git_repository_pull: true
docker_git_repository_branch: "master"
docker_git_repository_branch: "master"

View File

@@ -16,6 +16,8 @@
- name: "load docker, proxy for '{{ application_id }}'"
include_role:
name: sys-stk-full-stateless
vars:
application_id: "web-app-sphinx"
# Hack because it wasn't possible to fix an handler bug in pkgmgr install
- name: „Trigger“ docker compose up

View File

@@ -234,8 +234,8 @@ class TestRunAnsiblePlaybook(unittest.TestCase):
"Expected 'make messy-build' when skip_build=False",
)
self.assertTrue(
any(call == ["make", "messy-test"] for call in calls),
"Expected 'make messy-test' when skip_tests=False",
any(call == ["make", "test-messy"] for call in calls),
"Expected 'make test-messy' when skip_tests=False",
)
self.assertTrue(
any(
@@ -330,7 +330,7 @@ class TestRunAnsiblePlaybook(unittest.TestCase):
# No cleanup, no build, no tests, no inventory validation
self.assertFalse(any(call == ["make", "clean"] for call in calls))
self.assertFalse(any(call == ["make", "messy-build"] for call in calls))
self.assertFalse(any(call == ["make", "messy-test"] for call in calls))
self.assertFalse(any(call == ["make", "test-messy"] for call in calls))
self.assertFalse(
any(
isinstance(call, list)

View File

@@ -10,7 +10,7 @@ import subprocess
class TestGenerateDefaultApplications(unittest.TestCase):
def setUp(self):
# Path to the generator script under test
self.script_path = Path(__file__).resolve().parents[5] / "cli" / "build" / "defaults" / "applications.py"
self.script_path = Path(__file__).resolve().parents[4] / "cli" / "setup" / "applications.py"
# Create temp role structure
self.temp_dir = Path(tempfile.mkdtemp())
self.roles_dir = self.temp_dir / "roles"
@@ -32,7 +32,7 @@ class TestGenerateDefaultApplications(unittest.TestCase):
shutil.rmtree(self.temp_dir)
def test_script_generates_expected_yaml(self):
script_path = Path(__file__).resolve().parent.parent.parent.parent.parent.parent / "cli/build/defaults/applications.py"
script_path = Path(__file__).resolve().parent.parent.parent.parent.parent.parent / "cli/setup/applications.py"
result = subprocess.run(
[

View File

@@ -45,7 +45,7 @@ class TestGenerateDefaultApplicationsUsers(unittest.TestCase):
When a users.yml exists with defined users, the script should inject a 'users'
mapping in the generated YAML, mapping each username to a Jinja2 reference.
"""
script_path = Path(__file__).resolve().parents[5] / "cli" / "build/defaults/applications.py"
script_path = Path(__file__).resolve().parents[4] / "cli" / "setup/applications.py"
result = subprocess.run([
"python3", str(script_path),
"--roles-dir", str(self.roles_dir),

View File

@@ -7,7 +7,7 @@ import yaml
from collections import OrderedDict
# Add cli/ to import path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../..", "cli/build/defaults/")))
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../..", "cli/setup/")))
import users
@@ -159,7 +159,7 @@ class TestGenerateUsers(unittest.TestCase):
out_file = tmpdir / "users.yml"
# Resolve script path like in other tests (relative to repo root)
script_path = Path(__file__).resolve().parents[5] / "cli" / "build" / "defaults" / "users.py"
script_path = Path(__file__).resolve().parents[4] / "cli" / "setup" / "users.py"
# Run generator
result = subprocess.run(
@@ -215,7 +215,7 @@ class TestGenerateUsers(unittest.TestCase):
yaml.safe_dump({"users": users_map}, f)
out_file = tmpdir / "users.yml"
script_path = Path(__file__).resolve().parents[5] / "cli" / "build" / "defaults" / "users.py"
script_path = Path(__file__).resolve().parents[5] / "cli" / "setup" / "users.py"
# First run
r1 = subprocess.run(
@@ -303,7 +303,7 @@ class TestGenerateUsers(unittest.TestCase):
)
out_file = tmpdir / "users.yml"
script_path = Path(__file__).resolve().parents[5] / "cli" / "build" / "defaults" / "users.py"
script_path = Path(__file__).resolve().parents[5] / "cli" / "setup" / "users.py"
result = subprocess.run(
[

View File

@@ -69,7 +69,7 @@ class TestMainHelpers(unittest.TestCase):
"""
available = [
(None, "deploy"),
("build/defaults", "users"),
("setup", "users"),
]
main.show_full_help_for_all("/fake/cli", available)