mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-06-25 11:45:32 +02:00
Compare commits
6 Commits
c418399807
...
9c680a26f7
Author | SHA1 | Date | |
---|---|---|---|
9c680a26f7 | |||
c03330dcd3 | |||
dfcd35bacc | |||
4ec3037474 | |||
567622f523 | |||
4b824ecd6c |
2
ansible.cfg
Normal file
2
ansible.cfg
Normal file
@ -0,0 +1,2 @@
|
||||
[defaults]
|
||||
lookup_plugins = ./lookup_plugins
|
1
lookup_plugins/__init__.py
Normal file
1
lookup_plugins/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
10
lookup_plugins/colorscheme.py
Normal file
10
lookup_plugins/colorscheme.py
Normal file
@ -0,0 +1,10 @@
|
||||
from ansible.plugins.lookup import LookupBase
|
||||
from colorscheme_generator import generate_full_palette
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
def run(self, terms, variables=None, **kwargs):
|
||||
base_color = terms[0]
|
||||
count = kwargs.get('count')
|
||||
shades = kwargs.get('shades')
|
||||
invert_lightness = kwargs.get('invert_lightness', False)
|
||||
return [generate_full_palette(base_color, count=count, shades=shades, invert_lightness=invert_lightness)]
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
colorscheme-generator @ https://github.com/kevinveenbirkenbach/colorscheme-generator/archive/refs/tags/v0.3.0.zip
|
@ -1,8 +0,0 @@
|
||||
---
|
||||
- name: rebuild custom openproject docker image
|
||||
command:
|
||||
cmd: docker build --no-cache -t {{custom_openproject_image}} .
|
||||
chdir: "{{openproject_plugins_service}}"
|
||||
environment:
|
||||
COMPOSE_HTTP_TIMEOUT: 600
|
||||
DOCKER_CLIENT_TIMEOUT: 600
|
@ -21,16 +21,14 @@
|
||||
src: Gemfile.plugins
|
||||
dest: "{{openproject_plugins_service}}Gemfile.plugins"
|
||||
notify:
|
||||
- docker compose project setup
|
||||
- rebuild custom openproject docker image
|
||||
- docker compose project build and setup
|
||||
|
||||
- name: "Transfering Dockerfile to {{openproject_plugins_service}}Dockerfile"
|
||||
template:
|
||||
src: Dockerfile
|
||||
dest: "{{openproject_plugins_service}}Dockerfile"
|
||||
notify:
|
||||
- docker compose project setup
|
||||
- rebuild custom openproject docker image
|
||||
- docker compose project build and setup
|
||||
|
||||
- name: "include role docker-repository-setup for {{application_id}}"
|
||||
include_role:
|
||||
|
@ -6,6 +6,9 @@ x-op-app: &app
|
||||
volumes:
|
||||
- "data:/var/openproject/assets"
|
||||
- "{{dummy_volume}}:/var/openproject/pgdata" # This mount is unnecessary and just done to prevent anonymous volumes
|
||||
build:
|
||||
context: {{openproject_plugins_service}}
|
||||
dockerfile: Dockerfile
|
||||
|
||||
services:
|
||||
{% include 'roles/docker-central-database/templates/services/' + database_type + '.yml.j2' %}
|
||||
|
@ -93,8 +93,6 @@ class LookupModule(LookupBase):
|
||||
# Check if domain_url is a list. If so, select the first element.
|
||||
if isinstance(domain_url, list):
|
||||
domain_url = domain_url[0]
|
||||
else:
|
||||
domain_url = ""
|
||||
|
||||
# Construct the URL using the domain_url if available.
|
||||
url = "https://" + domain_url if domain_url else ""
|
||||
|
@ -5,11 +5,6 @@ services:
|
||||
dockerfile: {{ path_cymais_presentation_output.stdout }}/Dockerfile
|
||||
ports:
|
||||
- "127.0.0.1:{{ports.localhost.http[application_id]}}:5000"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:5000"]
|
||||
interval: 1m
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
volumes:
|
||||
- {{ path_cymais_presentation_output.stdout }}:/app
|
||||
- {{ path_cymais_output.stdout }}:/source
|
||||
|
@ -1,26 +1,34 @@
|
||||
# 🌍 Nginx Global Theming Role
|
||||
# 🌍 Global CSS Injection for Nginx
|
||||
|
||||
This **Ansible role** provides a **global theming solution** for Nginx-based web applications. It ensures a **consistent look and feel** across multiple applications by injecting a **unified global.css** with customizable theming options.
|
||||
---
|
||||
## Description
|
||||
|
||||
## 🚀 Features
|
||||
✅ **Automatic CSS Deployment** – Injects `global.css` into all Nginx-served applications.
|
||||
✅ **Dynamic Theming** – Uses `design.css.colors` from Ansible variables for **full customization**.
|
||||
✅ **Bootstrap Override Support** – Ensures Bootstrap-based apps use the **unified global styles**.
|
||||
✅ **Versioning System** – Prevents caching issues with automatic **timestamp-based versioning**.
|
||||
✅ **Dark Mode Support** – Automatically adapts to user preferences.
|
||||
✅ **Runs Once Per Deployment** – Avoids redundant executions with `run_once_nginx_global_css`.
|
||||
This Ansible role ensures **consistent global theming** across all Nginx-served applications by injecting a unified `global.css` file.
|
||||
The role leverages [`colorscheme-generator`](https://github.com/kevinveenbirkenbach/colorscheme-generator/) to generate a dynamic, customizable color palette for light and dark mode, compatible with popular web tools like **Bootstrap**, **Keycloak**, **Nextcloud**, **Taiga**, **Mastodon**, and many more.
|
||||
|
||||
---
|
||||
## Overview
|
||||
|
||||
## 🎨 Theming Details
|
||||
This role deploys a centralized global stylesheet (`global.css`) that overrides the default theming of web applications served via Nginx. It's optimized to run only once per deployment and generates a **cache-busting version number** based on file modification timestamps.
|
||||
It includes support for **dark mode**, **custom fonts**, and **extensive Bootstrap and UI component overrides**.
|
||||
|
||||
The **CSS template (`global.css.j2`)** dynamically applies the defined theme colors and ensures **Bootstrap, buttons, alerts, forms, and other UI elements** follow the **unified design**.
|
||||
## Purpose
|
||||
|
||||
## 🛠️ Contribution
|
||||
Feel free to **fork, modify, and improve** this role! Contributions are always welcome. 🛠️🔥
|
||||
The goal of this role is to provide a **single source of truth for theming** across your infrastructure.
|
||||
It makes all applications feel like part of the same ecosystem — visually and functionally.
|
||||
|
||||
---
|
||||
## Features
|
||||
|
||||
🚀 **Happy Theming!** 🎨✨
|
||||
*Created by [Kevin Veen-Birkenbach](https://www.veen.world) with the assistance of [ChatGPT](https://chatgpt.com/share/67a5fea3-4d5c-800f-8bc4-605712c02c9b).
|
||||
- 🎨 **Dynamic Theming** via [`colorscheme-generator`](https://github.com/kevinveenbirkenbach/colorscheme-generator/)
|
||||
- 📁 **Unified global.css** deployment for all Nginx applications
|
||||
- 🌒 **Dark mode support** out of the box
|
||||
- 🚫 **No duplication** – tasks run once per deployment
|
||||
- ⏱️ **Versioning logic** to bust browser cache
|
||||
- 🎯 **Bootstrap override compatibility**
|
||||
- 🧩 **Theme support for Keycloak, Nextcloud, Gitea, LAM, Peertube, and more**
|
||||
|
||||
## Credits 📝
|
||||
|
||||
Developed and maintained by **Kevin Veen-Birkenbach**.
|
||||
Learn more at [www.veen.world](https://www.veen.world)
|
||||
|
||||
Part of the [CyMaIS Project](https://github.com/kevinveenbirkenbach/cymais)
|
||||
License: [CyMaIS NonCommercial License (CNCL)](https://s.veen.world/cncl)
|
||||
|
@ -1,94 +0,0 @@
|
||||
import colorsys
|
||||
|
||||
def adjust_color(hex_color, target_lightness=None, lightness_change=0, hue_shift=0, saturation_change=0):
|
||||
"""
|
||||
Adjust a HEX color in HSL space.
|
||||
|
||||
- target_lightness: If provided (0 to 1), the lightness is set absolutely to this value.
|
||||
Otherwise, lightness_change is applied additively (in percentage points, where 100 => 1 in HSL).
|
||||
- lightness_change: Percentage points to add or subtract from lightness (if target_lightness is None).
|
||||
- hue_shift: Degrees to shift hue (e.g., +180 for complementary).
|
||||
- saturation_change: Percentage points to add or subtract from saturation.
|
||||
|
||||
Uses a 'cyclical' approach for lightness and saturation if no target_lightness is provided:
|
||||
If the new value goes above 1, it wraps around (subtract 1).
|
||||
If it goes below 0, it wraps around (add 1).
|
||||
"""
|
||||
|
||||
# Strip leading '#' if present
|
||||
hex_color = hex_color.lstrip('#')
|
||||
|
||||
# Parse the original RGB values
|
||||
r = int(hex_color[0:2], 16)
|
||||
g = int(hex_color[2:4], 16)
|
||||
b = int(hex_color[4:6], 16)
|
||||
|
||||
# Convert from [0..255] range to [0..1] for colorsys
|
||||
r /= 255.0
|
||||
g /= 255.0
|
||||
b /= 255.0
|
||||
|
||||
# Convert RGB -> HLS (colorsys uses HLS, also hier: Hue, Lightness, Saturation)
|
||||
h, l, s = colorsys.rgb_to_hls(r, g, b)
|
||||
|
||||
# Shift hue by (hue_shift / 360)
|
||||
h = (h + (hue_shift / 360.0)) % 1.0
|
||||
|
||||
# Adjust saturation (cyclically)
|
||||
s_new = s + (saturation_change / 100.0)
|
||||
if s_new > 1:
|
||||
s_new -= 1
|
||||
elif s_new < 0:
|
||||
s_new += 1
|
||||
|
||||
# Adjust lightness: either set to a target or change it additively (cyclically)
|
||||
if target_lightness is not None:
|
||||
l_new = target_lightness
|
||||
else:
|
||||
l_new = l + (lightness_change / 100.0)
|
||||
if l_new > 1:
|
||||
l_new -= 1
|
||||
elif l_new < 0:
|
||||
l_new += 1
|
||||
|
||||
# Convert back to RGB
|
||||
new_r, new_g, new_b = colorsys.hls_to_rgb(h, l_new, s_new)
|
||||
|
||||
# Scale back to [0..255] and format as HEX
|
||||
new_r = int(new_r * 255)
|
||||
new_g = int(new_g * 255)
|
||||
new_b = int(new_b * 255)
|
||||
|
||||
return '#{:02x}{:02x}{:02x}'.format(new_r, new_g, new_b)
|
||||
|
||||
def adjust_color_rgb(hex_color, target_lightness=None, lightness_change=0, hue_shift=0, saturation_change=0):
|
||||
"""
|
||||
Wrapper function for adjust_color.
|
||||
|
||||
Calls adjust_color to get the adjusted HEX color and then converts it to a string
|
||||
of comma-separated RGB values.
|
||||
"""
|
||||
adjusted_hex = adjust_color(
|
||||
hex_color,
|
||||
target_lightness=target_lightness,
|
||||
lightness_change=lightness_change,
|
||||
hue_shift=hue_shift,
|
||||
saturation_change=saturation_change
|
||||
)
|
||||
|
||||
# Remove '#' and parse the RGB components
|
||||
hex_val = adjusted_hex.lstrip('#')
|
||||
r = int(hex_val[0:2], 16)
|
||||
g = int(hex_val[2:4], 16)
|
||||
b = int(hex_val[4:6], 16)
|
||||
|
||||
return f"{r},{g},{b}"
|
||||
|
||||
# Integration in the FilterModule for Ansible
|
||||
class FilterModule(object):
|
||||
'''Custom filters for Ansible'''
|
||||
def filters(self):
|
||||
return {
|
||||
'adjust_color': adjust_color,
|
||||
'adjust_color_rgb': adjust_color_rgb,
|
||||
}
|
@ -1,2 +1,29 @@
|
||||
dependencies:
|
||||
---
|
||||
galaxy_info:
|
||||
author: "Kevin Veen-Birkenbach"
|
||||
description: "Global CSS injection for Nginx-based apps using dynamic colorschemes."
|
||||
license: "CyMaIS NonCommercial License (CNCL)"
|
||||
license_url: "https://s.veen.world/cncl"
|
||||
company: |
|
||||
Kevin Veen-Birkenbach
|
||||
Consulting & Coaching Solutions
|
||||
https://www.veen.world
|
||||
min_ansible_version: "2.9"
|
||||
platforms:
|
||||
- name: Archlinux
|
||||
versions:
|
||||
- rolling
|
||||
galaxy_tags:
|
||||
- nginx
|
||||
- css
|
||||
- colors
|
||||
- bootstrap
|
||||
- theming
|
||||
- dynamic
|
||||
- frontend
|
||||
- global
|
||||
repository: https://s.veen.world/cymais
|
||||
issue_tracker_url: https://s.veen.world/cymaisissues
|
||||
documentation: https://s.veen.world/cymais
|
||||
dependencies:
|
||||
- role: nginx
|
@ -1,6 +1,16 @@
|
||||
# Load this role via nginx-modifier-all for consistency
|
||||
|
||||
- name: Deploy global.css from template
|
||||
- name: Generate color palette with colorscheme-generator
|
||||
set_fact:
|
||||
color_palette: "{{ lookup('colorscheme', global_css_base_color, count=global_css_count, shades=global_css_shades) }}"
|
||||
when: run_once_nginx_global_css is not defined
|
||||
|
||||
- name: Generate inverted color palette with colorscheme-generator
|
||||
set_fact:
|
||||
inverted_color_palette: "{{ lookup('colorscheme', global_css_base_color, count=global_css_count, shades=global_css_shades, invert_lightness=True) }}"
|
||||
when: run_once_nginx_global_css is not defined
|
||||
|
||||
- name: Deploy global.css
|
||||
template:
|
||||
src: global.css.j2
|
||||
dest: "{{ global_css_destination }}"
|
||||
@ -9,18 +19,18 @@
|
||||
mode: '0644'
|
||||
when: run_once_nginx_global_css is not defined
|
||||
|
||||
- name: Get stat for global.css destination file
|
||||
- name: Get stat for global.css
|
||||
stat:
|
||||
path: "{{ global_css_destination }}"
|
||||
register: global_css_stat
|
||||
when: run_once_nginx_global_css is not defined
|
||||
|
||||
- name: Set global_css_version to file modification time
|
||||
- name: Set global_css_version
|
||||
set_fact:
|
||||
global_css_version: "{{ global_css_stat.stat.mtime }}"
|
||||
when: run_once_nginx_global_css is not defined
|
||||
|
||||
- name: Mark global css tasks as run once
|
||||
- name: Mark css as done
|
||||
set_fact:
|
||||
run_once_nginx_global_css: true
|
||||
when: run_once_nginx_global_css is not defined
|
File diff suppressed because it is too large
Load Diff
@ -1 +1,4 @@
|
||||
global_css_destination: "{{nginx.directories.data.global}}global.css"
|
||||
global_css_base_color: "{{ design.css.colors.base }}"
|
||||
global_css_count: 7
|
||||
global_css_shades: 100
|
@ -6,10 +6,11 @@
|
||||
cmd: "pkgmgr update pkgmgr"
|
||||
when: run_once_pkgmgr_update is not defined
|
||||
|
||||
- name: install {{ package_name }}
|
||||
- name: clone {{ package_name }}
|
||||
command:
|
||||
cmd: "pkgmgr install {{ package_name }} --clone-mode https"
|
||||
cmd: "pkgmgr clone {{ package_name }} --clone-mode https"
|
||||
notify: "{{ package_notify | default(omit) }}"
|
||||
ignore_errors: true
|
||||
|
||||
- name: update {{ package_name }}
|
||||
command:
|
||||
|
@ -115,6 +115,7 @@ def update_docker(directory):
|
||||
need_to_build = True
|
||||
|
||||
if need_to_build:
|
||||
# @todo Here a pull for openproject should be placed.
|
||||
run_command("docker-compose build")
|
||||
start_docker(directory)
|
||||
else:
|
||||
|
Loading…
x
Reference in New Issue
Block a user