mirror of
https://github.com/ublue-os/forge.git
synced 2025-07-17 15:15:45 +03:00
wip - nicegui
This commit is contained in:
parent
c3ed45a21b
commit
85b0b3fdc5
39 changed files with 2748 additions and 896 deletions
25
anvil/Containerfile
Normal file
25
anvil/Containerfile
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Source Image
|
||||
FROM docker.io/library/python:3.11-alpine3.19
|
||||
|
||||
# Environment vars
|
||||
ENV PIPX_BIN_DIR="/usr/local/py-utils"
|
||||
ENV PIPX_HOME="/usr/local/pipx"
|
||||
ENV POETRY_VERSION="1.8.2"
|
||||
ENV POETRY_VIRTUALENVS_CREATE="true"
|
||||
ENV POETRY_VIRTUALENVS_IN_PROJECT="true"
|
||||
ENV PATH="${PATH}:${PIPX_BIN_DIR}:${PIPX_HOME}/venvs/poetry/bin:/anvil/.venv/bin"
|
||||
|
||||
# Install system dependencies
|
||||
RUN apk --no-cache add pipx openssh bash
|
||||
RUN pipx install poetry==${POETRY_VERSION}
|
||||
|
||||
# Install ansible and dependencies
|
||||
WORKDIR /anvil
|
||||
COPY . .
|
||||
RUN poetry install --no-root
|
||||
CMD poetry env use .venv/bin/python && \
|
||||
chmod +x entrypoint.sh
|
||||
RUN ansible-galaxy collection install -r ./ansible/collections/requirements.yml
|
||||
|
||||
# Set entrypoint
|
||||
ENTRYPOINT [ "/anvil/entrypoint.sh" ]
|
23
anvil/ansible.cfg
Normal file
23
anvil/ansible.cfg
Normal file
|
@ -0,0 +1,23 @@
|
|||
[defaults]
|
||||
# Inventory location
|
||||
inventory = ./ansible/inventory.yml
|
||||
# Location for roles
|
||||
roles_path = ./ansible/roles
|
||||
# Location for collections
|
||||
collection_paths = ./ansible/collections
|
||||
# Localtion for plugins & modules
|
||||
library = ./ansible/library
|
||||
# SSH
|
||||
private_key_file = /certs/ssh/ublue-os_forge-id_ed25519
|
||||
# Console log settings
|
||||
display_skipped_hosts = false
|
||||
# Use the YAML callback plugin.
|
||||
stdout_callback = yaml
|
||||
# Use the stdout_callback when running ad-hoc commands.
|
||||
bin_ansible_callbacks = True
|
||||
# Callback plugins
|
||||
callbacks_enabled=ansible.posix.profile_tasks
|
||||
# Skip SSH host key checking
|
||||
host_key_checking = False
|
||||
# Disable cowsay
|
||||
nocows = 1
|
12
anvil/ansible/.ansible-lint
Normal file
12
anvil/ansible/.ansible-lint
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
# .ansible-lint
|
||||
|
||||
# https://ansible-lint.readthedocs.io/profiles/
|
||||
profile: production
|
||||
|
||||
# Ansible-lint does not fail on warnings from the rules or tags listed below
|
||||
warn_list:
|
||||
- galaxy[version-incorrect]
|
||||
- no-jinja-nesting
|
||||
- package-latest
|
||||
- name[template]
|
8
anvil/ansible/collections/requirements.yml
Normal file
8
anvil/ansible/collections/requirements.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
collections:
|
||||
- name: ansible.posix
|
||||
version: 1.5.4
|
||||
- name: community.general
|
||||
version: 8.6.0
|
||||
- name: containers.podman
|
||||
version: 1.13.0
|
4
anvil/ansible/group_vars/all/data.yml
Normal file
4
anvil/ansible/group_vars/all/data.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
# data variables
|
||||
forge_data_path: "{{ lookup('ansible.builtin.env', 'FORGE_DATA_PATH', default=ansible_facts.env.HOME + '/ublue-os_forge') }}"
|
||||
forge_data_default_variables_file_path: "{{ forge_data_path }}/forge_default_vars.env"
|
5
anvil/ansible/group_vars/all/git.yml
Normal file
5
anvil/ansible/group_vars/all/git.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
# git variables
|
||||
forge_git_repository_url: "{{ lookup('ansible.builtin.env', 'FORGE_GIT_REPOSITORY_URL', default='https://github.com/ublue-os/bluefin.git') }}"
|
||||
forge_git_repository_destination: "{{ lookup('ansible.builtin.env', 'FORGE_GIT_REPOSITORY_DESTINATION', default=forge_data_path + '/bluefin') }}"
|
||||
forge_git_repository_version: "{{ lookup('ansible.builtin.env', 'FORGE_GIT_REPOSITORY_VERSION', default='main') }}"
|
3
anvil/ansible/group_vars/all/registry.yml
Normal file
3
anvil/ansible/group_vars/all/registry.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
# container registry variables
|
||||
forge_registry_url: "{{ lookup('ansible.builtin.env', 'FORGE_REGISTRY_URL', default='registry.ublue.local') }}"
|
4
anvil/ansible/host_vars/localhost.yml
Normal file
4
anvil/ansible/host_vars/localhost.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
# localhost variables
|
||||
ansible_connection: local
|
||||
ansible_python_interpreter: "{{ ansible_playbook_python }}"
|
9
anvil/ansible/inventory.yml
Normal file
9
anvil/ansible/inventory.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
all:
|
||||
hosts:
|
||||
localhost:
|
||||
host.ublue.local:
|
||||
ansible_host: host.containers.internal
|
||||
ansible_user: "{{ lookup('ansible.builtin.env', 'ANSIBLE_HOST_USER') }}"
|
||||
ansible_become_password: "{{ lookup('ansible.builtin.env', 'ANSIBLE_HOST_BECOME_PASSWORD') }}"
|
||||
children:
|
49
anvil/ansible/playbooks/configure_host.yml
Normal file
49
anvil/ansible/playbooks/configure_host.yml
Normal file
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
- name: Configure host system
|
||||
hosts: host.ublue.local
|
||||
gather_facts: true
|
||||
tasks:
|
||||
- name: Add ublue.local entries to /etc/hosts
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/hosts
|
||||
search_string: 127.0.0.1 rvproxy.ublue.local registry.ublue.local forge.ublue.local
|
||||
line: 127.0.0.1 rvproxy.ublue.local registry.ublue.local forge.ublue.local
|
||||
state: present
|
||||
become: true
|
||||
|
||||
- name: Add ublue.local TSL root certificate to trust anchors
|
||||
ansible.builtin.copy:
|
||||
src: /certs/tls/ublue-os_forge-root.pem
|
||||
dest: /etc/pki/ca-trust/source/anchors/ublue-os_forge-root.pem
|
||||
force: true
|
||||
mode: "0644"
|
||||
become: true
|
||||
|
||||
- name: Update ca-trust store
|
||||
ansible.builtin.command:
|
||||
cmd: update-ca-trust
|
||||
changed_when: false
|
||||
become: true
|
||||
|
||||
- name: Create default directory for forge content
|
||||
ansible.builtin.file:
|
||||
path: "{{ forge_data_path }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Create default configuration variable file
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ forge_data_default_variables_file_path }}"
|
||||
content: |
|
||||
## ublue-os forge configuration variables defaults
|
||||
## For more details got to https://github.com/ublue-os/forge/blob/main/docs/variables.md
|
||||
|
||||
{% for item in __vars_used %}
|
||||
#{{ item | upper }}={{ lookup('ansible.builtin.vars', item) }}
|
||||
{% endfor %}
|
||||
backup: true
|
||||
owner: "{{ ansible_facts.env.USER }}"
|
||||
mode: "0644"
|
||||
vars:
|
||||
__vars_used: "{{ lookup('ansible.builtin.varnames', __regex_search, wantlist=true) }}"
|
||||
__regex_search: ^forge_.+
|
29
anvil/ansible/playbooks/project_build.yml
Normal file
29
anvil/ansible/playbooks/project_build.yml
Normal file
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
- name: Build project
|
||||
hosts: host.ublue.local
|
||||
gather_facts: true
|
||||
pre_tasks:
|
||||
- name: DEBUG - forge variables
|
||||
ansible.builtin.include_role:
|
||||
name: debug_forge_vars
|
||||
|
||||
tasks:
|
||||
- name: Build and push image to registry
|
||||
containers.podman.podman_image:
|
||||
name: "{{ forge_git_repository_url | regex_search(__regex_search) }}"
|
||||
tag: latest
|
||||
path: "{{ forge_git_repository_destination }}"
|
||||
build:
|
||||
file: Containerfile
|
||||
format: oci
|
||||
pull: false
|
||||
push: true
|
||||
push_args:
|
||||
dest: "{{ forge_registry_url }}"
|
||||
vars:
|
||||
__regex_search: (?<=/)[^/]+(?=\.git)
|
||||
register: __podman_image_info
|
||||
|
||||
- name: INFO | Status from build and push
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ __podman_image_info }}"
|
19
anvil/ansible/playbooks/project_clone.yml
Normal file
19
anvil/ansible/playbooks/project_clone.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
- name: Clone project
|
||||
hosts: host.ublue.local
|
||||
gather_facts: true
|
||||
pre_tasks:
|
||||
- name: DEBUG - forge variables
|
||||
ansible.builtin.include_role:
|
||||
name: debug_forge_vars
|
||||
tasks:
|
||||
- name: Clone project
|
||||
ansible.builtin.git:
|
||||
repo: "{{ forge_git_repository_url }}"
|
||||
dest: "{{ forge_git_repository_destination }}"
|
||||
version: "{{ forge_git_repository_version }}"
|
||||
register: __git_clone_info
|
||||
|
||||
- name: INFO | Status from git clone
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ __git_clone_info }}"
|
46
anvil/ansible/roles/debug_forge_vars/README.md
Normal file
46
anvil/ansible/roles/debug_forge_vars/README.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Role - debug_forge_vars
|
||||
|
||||
This role is used for debugging purposes only.
|
||||
|
||||
## Variables
|
||||
|
||||
The role has the following variables defined.
|
||||
|
||||
### default/main.yml
|
||||
|
||||
In the [main.yml](./defaults/main.yml/) all variables are defined which are
|
||||
used in the context of debugging. Usually end-users should not worry about them to much.
|
||||
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
|
||||
| name | type | default value | description |
|
||||
| ------------------------------- | ---- | ------------- | ------------------------------------------------------------------------------------------- |
|
||||
| `forge_debug_vars_regex_search` | str | ^forge\_.+ | Python regex search term. Useful if you want to print out all variables starting with `xyz` |
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
|
||||
## Example Playbook Usage
|
||||
|
||||
This role is best included in a playbook as pre-task:
|
||||
|
||||
```yaml
|
||||
pre_tasks:
|
||||
- name: DEBUG - forge variables
|
||||
ansible.builtin.include_role:
|
||||
name: debug_forge_vars
|
||||
```
|
||||
|
||||
With the role included you can launch the playbook in verbose mode `ansible-playbook -v`.
|
||||
This will print all variables found with the regex search term defined in the `forge_debug_vars_regex_search`
|
||||
variable.
|
||||
|
||||
You can modify the `forge_debug_vars_regex_search` term by changing it via the vars statement
|
||||
|
||||
```yaml
|
||||
pre_tasks:
|
||||
- name: DEBUG - forge git variables
|
||||
ansible.builtin.include_role:
|
||||
name: debug_forge_vars
|
||||
vars:
|
||||
forge_debug_vars_regex_search: ^forge_git.+
|
||||
```
|
2
anvil/ansible/roles/debug_forge_vars/defaults/main.yml
Normal file
2
anvil/ansible/roles/debug_forge_vars/defaults/main.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Default vars for this role
|
||||
forge_debug_vars_regex_search: "{{ lookup('ansible.builtin.env', 'FORGE_DEBUG_VARS_REGEX_SEARCH', default='^forge_.+') }}"
|
13
anvil/ansible/roles/debug_forge_vars/tasks/main.yml
Normal file
13
anvil/ansible/roles/debug_forge_vars/tasks/main.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
# main task file for this role
|
||||
|
||||
- name: DEBUG | forge variables
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ item }}: {{ lookup('ansible.builtin.vars', item) }}"
|
||||
verbosity: 1
|
||||
loop: "{{ __forge_vars_used }}"
|
||||
loop_control:
|
||||
extended: true
|
||||
label: "{{ ansible_loop.index }}/{{ ansible_loop.length }}"
|
||||
vars:
|
||||
__forge_vars_used: "{{ lookup('ansible.builtin.varnames', forge_debug_vars_regex_search, wantlist=true) }}"
|
3
anvil/entrypoint.sh
Executable file
3
anvil/entrypoint.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#! /bin/bash
|
||||
## Start nicegui
|
||||
poetry env use .venv/bin/python && poetry run python nicegui/main.py
|
59
anvil/nicegui/_tests/main.py
Normal file
59
anvil/nicegui/_tests/main.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env python3
|
||||
import asyncio
|
||||
import os.path
|
||||
import platform
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
async def run_command(command: str) -> None:
|
||||
"""Run a command in the background and display the output in the pre-created dialog."""
|
||||
dialog.open()
|
||||
result.content = ''
|
||||
command = command.replace('python3', sys.executable) # NOTE replace with machine-independent Python path (#1240)
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*shlex.split(command, posix='win' not in sys.platform.lower()),
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT,
|
||||
cwd=os.path.dirname(os.path.abspath(__file__))
|
||||
)
|
||||
# NOTE we need to read the output in chunks, otherwise the process will block
|
||||
output = ''
|
||||
while True:
|
||||
new = await process.stdout.read(4096)
|
||||
if not new:
|
||||
break
|
||||
output += new.decode()
|
||||
# NOTE the content of the markdown element is replaced every time we have new output
|
||||
result.content = f'```\n{output}\n```'
|
||||
|
||||
with ui.header().classes(replace='row items-center') as header:
|
||||
ui.button(on_click=lambda: left_drawer.toggle(), icon='menu').props('flat color=white')
|
||||
with ui.tabs() as tabs:
|
||||
ui.tab('Home')
|
||||
ui.tab('Ansible')
|
||||
ui.tab('Registry')
|
||||
|
||||
with ui.footer(value=False) as footer:
|
||||
ui.label('Footer')
|
||||
|
||||
with ui.left_drawer().classes('bg-blue-100') as left_drawer:
|
||||
ui.label('Side menu')
|
||||
|
||||
ui.button('Build Project', on_click=lambda: run_command('python3 runner.py')).props('no-caps')
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
result = ui.markdown()
|
||||
|
||||
|
||||
with ui.page_sticky(position='bottom-right', x_offset=20, y_offset=20):
|
||||
ui.button(on_click=footer.toggle, icon='contact_support').props('fab')
|
||||
|
||||
with ui.tab_panels(tabs, value='A').classes('w-full'):
|
||||
with ui.tab_panel('A'):
|
||||
ui.label('Content of A')
|
||||
with ui.tab_panel('B'):
|
||||
ui.label('Content of B')
|
||||
with ui.tab_panel('C'):
|
||||
ui.label('Content of C')
|
||||
|
||||
ui.run()
|
14
anvil/nicegui/_tests/runner.py
Normal file
14
anvil/nicegui/_tests/runner.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
import sys
|
||||
import ansible_runner
|
||||
|
||||
# run ansible/generic commands in interactive mode locally
|
||||
out, err, rc = ansible_runner.run_command(
|
||||
executable_cmd='ansible-playbook',
|
||||
cmdline_args=['../playbooks/project_clone.yml', '-i', '../inventory.yml'],
|
||||
input_fd=sys.stdin,
|
||||
output_fd=sys.stdout,
|
||||
error_fd=sys.stderr,
|
||||
)
|
||||
print("rc: {}".format(rc))
|
||||
print("out: {}".format(out))
|
||||
print("err: {}".format(err))
|
88
anvil/nicegui/_tests/test.py
Normal file
88
anvil/nicegui/_tests/test.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
# from nicegui import ui
|
||||
|
||||
# @ui.page('/')
|
||||
# def page_layout():
|
||||
# ui.label('CONTENT')
|
||||
# [ui.label(f'Line {i}') for i in range(100)]
|
||||
# with ui.header(elevated=True).style('background-color: #3874c8').classes('items-center justify-between'):
|
||||
# ui.button(on_click=lambda: left_drawer.toggle(), icon='menu').props('flat color=white')
|
||||
# ui.label('HEADER')
|
||||
# with ui.left_drawer(fixed=False).style('background-color: #ebf1fa').props('bordered') as left_drawer:
|
||||
# ui.label('LEFT DRAWER')
|
||||
# with ui.footer().style('background-color: #3874c8'):
|
||||
# ui.label('FOOTER')
|
||||
|
||||
# # ui.link('show page with fancy layout', page_layout)
|
||||
|
||||
# ui.run()
|
||||
|
||||
# from nicegui import ui
|
||||
|
||||
# with ui.header().classes(replace='row items-center') as header:
|
||||
# ui.button(on_click=lambda: left_drawer.toggle(), icon='menu').props('flat color=white')
|
||||
# with ui.tabs() as tabs:
|
||||
# ui.tab('Home')
|
||||
# ui.tab('Ansible')
|
||||
# ui.tab('Registry')
|
||||
|
||||
# with ui.footer(value=False) as footer:
|
||||
# ui.label('Footer')
|
||||
|
||||
# with ui.left_drawer().classes('bg-blue-100') as left_drawer:
|
||||
# ui.label('Side menu')
|
||||
|
||||
# with ui.page_sticky(position='bottom-right', x_offset=20, y_offset=20):
|
||||
# ui.button(on_click=footer.toggle, icon='contact_support').props('fab')
|
||||
|
||||
# with ui.tab_panels(tabs, value='A').classes('w-full'):
|
||||
# with ui.tab_panel('A'):
|
||||
# ui.label('Content of A')
|
||||
# with ui.tab_panel('B'):
|
||||
# ui.label('Content of B')
|
||||
# with ui.tab_panel('C'):
|
||||
# ui.label('Content of C')
|
||||
|
||||
# ui.run()
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import asyncio
|
||||
import os.path
|
||||
import platform
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
|
||||
async def run_command(command: str) -> None:
|
||||
"""Run a command in the background and display the output in the pre-created dialog."""
|
||||
dialog.open()
|
||||
result.content = ''
|
||||
command = command.replace('python3', sys.executable) # NOTE replace with machine-independent Python path (#1240)
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*shlex.split(command, posix='win' not in sys.platform.lower()),
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT,
|
||||
cwd=os.path.dirname(os.path.abspath(__file__))
|
||||
)
|
||||
# NOTE we need to read the output in chunks, otherwise the process will block
|
||||
output = ''
|
||||
while True:
|
||||
new = await process.stdout.read(4096)
|
||||
if not new:
|
||||
break
|
||||
output += new.decode()
|
||||
# NOTE the content of the markdown element is replaced every time we have new output
|
||||
result.content = f'```\n{output}\n```'
|
||||
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
result = ui.markdown()
|
||||
|
||||
ui.button('python3 runner.py', on_click=lambda: run_command('python3 runner.py')).props('no-caps')
|
||||
ui.button('python3 slow.py', on_click=lambda: run_command('python3 slow.py')).props('no-caps')
|
||||
with ui.row().classes('items-center'):
|
||||
ui.button('python3 hello.py "<message>"', on_click=lambda: run_command(f'python3 hello.py "{message.value}"')) \
|
||||
.props('no-caps')
|
||||
message = ui.input('message', value='NiceGUI')
|
||||
|
||||
# NOTE: On Windows reload must be disabled to make asyncio.create_subprocess_exec work (see https://github.com/zauberzeug/nicegui/issues/486)
|
||||
ui.run(reload=platform.system() != 'Windows')
|
35
anvil/nicegui/main.py
Executable file
35
anvil/nicegui/main.py
Executable file
|
@ -0,0 +1,35 @@
|
|||
import pages
|
||||
import pages.about
|
||||
import pages.ansible
|
||||
import pages.home
|
||||
import pages.registry
|
||||
import theme
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
|
||||
@ui.page("/")
|
||||
def index_page() -> None:
|
||||
with theme.frame("Home"):
|
||||
pages.home.content()
|
||||
|
||||
|
||||
@ui.page("/ansible")
|
||||
def ansible_page() -> None:
|
||||
with theme.frame("Ansible"):
|
||||
pages.ansible.content()
|
||||
|
||||
|
||||
@ui.page("/registry")
|
||||
def registry_page() -> None:
|
||||
with theme.frame("Registry"):
|
||||
pages.registry.content()
|
||||
|
||||
|
||||
@ui.page("/about")
|
||||
def about_page() -> None:
|
||||
with theme.frame("About"):
|
||||
pages.about.content()
|
||||
|
||||
|
||||
ui.run(title="uBlue Forge", port=3000)
|
8
anvil/nicegui/menu.py
Normal file
8
anvil/nicegui/menu.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from nicegui import ui
|
||||
|
||||
|
||||
def menu() -> None:
|
||||
ui.link("Home", "/").classes(replace="text-white")
|
||||
ui.link("Ansible", "/ansible").classes(replace="text-white")
|
||||
ui.link("Registry", "/registry").classes(replace="text-white")
|
||||
ui.link("About", "/about").classes(replace="text-white")
|
1
anvil/nicegui/pages/__init__.py
Normal file
1
anvil/nicegui/pages/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from nicegui import ui
|
10
anvil/nicegui/pages/about.py
Normal file
10
anvil/nicegui/pages/about.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from nicegui import ui
|
||||
from utils import get_project_root
|
||||
|
||||
|
||||
def content() -> None:
|
||||
project_root = str(get_project_root())
|
||||
ui.label("Work in progress...").classes("text-h6")
|
||||
ui.image(project_root + "/nicegui/pages/assets/work-in-progress.png").classes(
|
||||
"w-[200%]"
|
||||
)
|
54
anvil/nicegui/pages/ansible.py
Normal file
54
anvil/nicegui/pages/ansible.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import ansible_runner
|
||||
import re
|
||||
from nicegui import ui
|
||||
from utils import get_project_root
|
||||
|
||||
|
||||
# Ansible integration
|
||||
def run_ansible_playbook(playbook_name: str, ngui_log: ui.log):
|
||||
project_root = str(get_project_root())
|
||||
playbook_path = project_root + "/ansible/playbooks/"
|
||||
inventory_path = project_root + "/ansible/inventory.yml"
|
||||
response, error, return_code = ansible_runner.interface.run_command(
|
||||
executable_cmd="ansible-playbook",
|
||||
cmdline_args=[playbook_path + playbook_name, "-i", inventory_path],
|
||||
)
|
||||
# remove color characters from response until clear how to display them in a log
|
||||
ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-9;#]+[mGK]?)")
|
||||
ansible_log = format(ansi_escape.sub("", response))
|
||||
ngui_log.clear()
|
||||
ngui_log.push(ansible_log)
|
||||
|
||||
|
||||
# Page content
|
||||
def content() -> None:
|
||||
with ui.row().classes("w-full"):
|
||||
with ui.row().classes("w-full"):
|
||||
# First Row
|
||||
with ui.card().classes("h-full"):
|
||||
ui.label("Build").classes("text-h6")
|
||||
ui.button(
|
||||
text="Clone project",
|
||||
on_click=lambda: run_ansible_playbook(
|
||||
"project_clone.yml", ngui_log=log
|
||||
),
|
||||
)
|
||||
ui.button(
|
||||
text="Build project",
|
||||
on_click=lambda: run_ansible_playbook(
|
||||
"project_build.yml", ngui_log=log
|
||||
),
|
||||
)
|
||||
# Second Row
|
||||
with ui.card().classes("h-full"):
|
||||
ui.label("Deploy").classes("text-h6")
|
||||
ui.button(
|
||||
"Deploy VM",
|
||||
on_click=lambda: ui.notify("This playbook is not implemented yet"),
|
||||
)
|
||||
|
||||
with ui.row().classes("w-full"):
|
||||
with ui.card().classes("w-full"):
|
||||
ui.label("Playbook Log").classes("text-h6")
|
||||
ui.button("Clear Log", on_click=lambda: log.clear())
|
||||
log = ui.log().classes("w-full h-full")
|
BIN
anvil/nicegui/pages/assets/work-in-progress.png
Normal file
BIN
anvil/nicegui/pages/assets/work-in-progress.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 524 KiB |
10
anvil/nicegui/pages/home.py
Normal file
10
anvil/nicegui/pages/home.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from nicegui import ui
|
||||
from utils import get_project_root
|
||||
|
||||
|
||||
def content() -> None:
|
||||
project_root = str(get_project_root())
|
||||
ui.label("Work in progress...").classes("text-h6")
|
||||
ui.image(project_root + "/nicegui/pages/assets/work-in-progress.png").classes(
|
||||
"w-[200%]"
|
||||
)
|
10
anvil/nicegui/pages/registry.py
Normal file
10
anvil/nicegui/pages/registry.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from nicegui import ui
|
||||
from utils import get_project_root
|
||||
|
||||
|
||||
def content() -> None:
|
||||
project_root = str(get_project_root())
|
||||
ui.label("Work in progress...").classes("text-h6")
|
||||
ui.image(project_root + "/nicegui/pages/assets/work-in-progress.png").classes(
|
||||
"w-[200%]"
|
||||
)
|
26
anvil/nicegui/theme.py
Normal file
26
anvil/nicegui/theme.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from contextlib import contextmanager
|
||||
|
||||
from menu import menu
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
|
||||
@contextmanager
|
||||
def frame(navigation_title: str, enable_right_drawer: bool = False):
|
||||
"""Custom page frame to share the same styling and behavior across all pages"""
|
||||
ui.colors(primary="#4051b5", secondary="#dddbff", accent="#171d9a")
|
||||
with ui.header():
|
||||
with ui.row():
|
||||
menu()
|
||||
ui.space()
|
||||
with ui.link(target="https://github.com/ublue-os/forge", new_tab=True):
|
||||
ui.icon("eva-github").classes("text-2xl")
|
||||
|
||||
with ui.column().classes():
|
||||
ui.label(navigation_title).classes("text-h4")
|
||||
yield
|
||||
|
||||
with ui.footer(value=False):
|
||||
ui.add_head_html(
|
||||
'<link href="https://unpkg.com/eva-icons@1.1.3/style/eva-icons.css" rel="stylesheet" />'
|
||||
)
|
5
anvil/nicegui/utils.py
Normal file
5
anvil/nicegui/utils.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from pathlib import Path
|
||||
|
||||
|
||||
def get_project_root() -> Path:
|
||||
return Path(__file__).parent.parent
|
2238
anvil/poetry.lock
generated
Normal file
2238
anvil/poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
21
anvil/pyproject.toml
Normal file
21
anvil/pyproject.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[tool.poetry]
|
||||
name = "ublue-os_forge"
|
||||
version = "0.1.0"
|
||||
description = "Ansible automation for Universal Blue"
|
||||
authors = ["Stephan Lüscher <stephan.luescher@mykolab.com>"]
|
||||
license = "../LICENSE"
|
||||
readme = "../README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
ansible-core = "^2.16"
|
||||
jmespath = "^1.0"
|
||||
nicegui = "^1.4.23"
|
||||
ansible-runner = "^2.3.6"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ansible-lint = { version = "^24.2", markers = 'platform_system != "Windows"' } # https://github.com/ansible/ansible-lint/issues/2730#issuecomment-1330406601
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
Loading…
Add table
Add a link
Reference in a new issue