Merge pull request 'pass_context' (#10) from pass_context into develop

Reviewed-on: https://hattori.ztsh.eu/paas/karl/pulls/10
This commit is contained in:
Piotr Dec 2025-11-04 00:15:51 +01:00
commit 8cf4f719b5
10 changed files with 24 additions and 26 deletions

View file

@ -10,6 +10,7 @@ class Request:
started: str started: str
files: List[str] files: List[str]
@dataclass @dataclass
class Response: class Response:
status: int status: int

View file

@ -1,6 +1,7 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Type from typing import Type
# TODO: unnecessary? # TODO: unnecessary?
@dataclass @dataclass
@ -8,6 +9,7 @@ class PathItem:
name: str name: str
t: Type t: Type
@dataclass @dataclass
class Path: class Path:
path: list[PathItem] = field(default_factory=list) path: list[PathItem] = field(default_factory=list)
@ -43,6 +45,7 @@ class Password:
def path(self): def path(self):
return self.group.path.append(self.name, type(self)) return self.group.path.append(self.name, type(self))
class UnencryptedPassword(Password): class UnencryptedPassword(Password):
def __init__(self, name: str, value: str, group: Group): def __init__(self, name: str, value: str, group: Group):
super().__init__(name, group) super().__init__(name, group)

View file

@ -1,6 +1,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import List from typing import List
@dataclass @dataclass
class WoodpeckerEvent: class WoodpeckerEvent:
_id: str _id: str

View file

@ -38,4 +38,3 @@ class DockerService:
def reload(self, compose_path: Path): def reload(self, compose_path: Path):
cmd = ["sudo", "docker", "compose", "-f", str(compose_path), "up", "-d"] cmd = ["sudo", "docker", "compose", "-f", str(compose_path), "up", "-d"]
# TODO: subprocess # TODO: subprocess

View file

@ -13,7 +13,7 @@ class SimpleValueTemplate(Template):
class ComplexValueTemplate(SimpleValueTemplate): class ComplexValueTemplate(SimpleValueTemplate):
delimiter = '@' delimiter = '%'
@injectable @injectable

View file

@ -1,6 +1,8 @@
import os.path import os.path
import shutil from contextlib import contextmanager
from typing import Any, Generator
import keyring
from injectable import injectable from injectable import injectable
from pykeepass import PyKeePass, create_database from pykeepass import PyKeePass, create_database
@ -12,22 +14,16 @@ class Passwords:
settings = get_settings() settings = get_settings()
with open(settings.kp.secret, "r") as fh: with open(settings.kp.secret, "r") as fh:
secret = fh.read().splitlines()[0] keyring.set_password("karl", "kp", fh.read().splitlines()[0])
self._path = settings.kp.file self._path = settings.kp.file
self._kp_org = self._open_or_create(self._path, secret)
self._kp = self._open_lock(self._path, secret)
@staticmethod @contextmanager
def _open_or_create(path, password) -> PyKeePass: def open(self, mode: str = "r") -> Generator[PyKeePass | Any, Any, None]:
if os.path.exists(path): kp = PyKeePass(self._path, password=keyring.get_password("karl", "kp")) \
return PyKeePass(path, password=password) if os.path.exists(self._path) else create_database(self._path, password=keyring.get_password("karl", "kp"))
return create_database(path, password) yield kp
if mode == "rw":
@staticmethod kp.save()
def _open_lock(path, password) -> PyKeePass:
lock_path = path + ".lock"
shutil.copyfile(path, lock_path)
return Passwords._open_or_create(lock_path, password)
def get_values(self, keys: list[str]) -> dict[str, str]: def get_values(self, keys: list[str]) -> dict[str, str]:
output = {} output = {}
@ -36,7 +32,8 @@ class Passwords:
path = key_parts[:-1] if len(key_parts) > 2 else None path = key_parts[:-1] if len(key_parts) > 2 else None
entry_name = key_parts[-2] entry_name = key_parts[-2]
field_name = key_parts[-1] field_name = key_parts[-1]
kp_entry = self._kp_org.find_entries(path=path, first=True, title=entry_name) with self.open() as kp:
kp_entry = kp.find_entries(path=path, first=True, title=entry_name)
output[k] = self._get_field_value(kp_entry, field_name) output[k] = self._get_field_value(kp_entry, field_name)
return output return output
@ -53,8 +50,3 @@ class Passwords:
return kp_entry.url return kp_entry.url
case _: case _:
return kp_entry.get_custom_property(field_name) return kp_entry.get_custom_property(field_name)
def save(self):
# nadpisz plik źródłowy zmianami z lock
self._kp.save()
shutil.copyfile(self._path + ".lock", self._path)

View file

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>{{ title }}</title> <title>{{ title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta content="width=device-width, initial-scale=1" name="viewport"/>
<style> <style>
body { body {
font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";

View file

@ -17,6 +17,7 @@ dependencies = [
"injectable==4.0.1", "injectable==4.0.1",
"py-automapper>=2.2.0", "py-automapper>=2.2.0",
"fastapi-utils>=0.8.0", "fastapi-utils>=0.8.0",
"keyring>=25.6.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]

View file

@ -1,3 +1,3 @@
value: ${sample} value: ${sample}
nested: ${some.nested.value} nested: ${some.nested.value}
custom: @{custom.field} custom: %{custom.field}

View file

@ -17,6 +17,7 @@ class TestMo(TestCase):
with open(target_path, 'r') as f: with open(target_path, 'r') as f:
content = f.read() content = f.read()
self.assertFalse(content.__contains__('${')) self.assertFalse(content.__contains__('${'))
self.assertFalse(content.__contains__('%{'))
parsed = yaml.load(content, Loader=yaml.FullLoader) parsed = yaml.load(content, Loader=yaml.FullLoader)
self.assertEqual('some_pass', parsed['value']) self.assertEqual('some_pass', parsed['value'])
self.assertEqual('nested_pass', parsed['nested']) self.assertEqual('nested_pass', parsed['nested'])