tequio-core 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tequio_core-0.1.0/.env.example +64 -0
- tequio_core-0.1.0/.github/workflows/ci.yml +63 -0
- tequio_core-0.1.0/.github/workflows/docs.yml +37 -0
- tequio_core-0.1.0/.github/workflows/release.yml +54 -0
- tequio_core-0.1.0/.gitignore +32 -0
- tequio_core-0.1.0/CHANGELOG.md +112 -0
- tequio_core-0.1.0/CODE_OF_CONDUCT.md +58 -0
- tequio_core-0.1.0/CONTRIBUTING.md +166 -0
- tequio_core-0.1.0/LICENSE +21 -0
- tequio_core-0.1.0/PKG-INFO +30 -0
- tequio_core-0.1.0/README.md +289 -0
- tequio_core-0.1.0/Tests/Core/CeleryApp/test_Dispatch.py +28 -0
- tequio_core-0.1.0/Tests/Core/CeleryApp/test_Retry.py +41 -0
- tequio_core-0.1.0/Tests/Core/CeleryApp/test_WorkerTaskDiscovery.py +32 -0
- tequio_core-0.1.0/Tests/Core/Config/test_BrokerSettings.py +30 -0
- tequio_core-0.1.0/Tests/Core/Console/test_CliEntrypoint.py +39 -0
- tequio_core-0.1.0/Tests/Core/Console/test_CliErrorBoundary.py +20 -0
- tequio_core-0.1.0/Tests/Core/Console/test_Console.py +138 -0
- tequio_core-0.1.0/Tests/Core/Console/test_DiscoveryLoud.py +43 -0
- tequio_core-0.1.0/Tests/Core/Console/test_FormatCommandList.py +50 -0
- tequio_core-0.1.0/Tests/Core/Console/test_JornalLauncher.py +39 -0
- tequio_core-0.1.0/Tests/Core/Console/test_LauncherCommands.py +62 -0
- tequio_core-0.1.0/Tests/Core/Console/test_MakeWritesToAppDir.py +132 -0
- tequio_core-0.1.0/Tests/Core/Console/test_NewCommands.py +57 -0
- tequio_core-0.1.0/Tests/Core/Console/test_QueueWorkPool.py +86 -0
- tequio_core-0.1.0/Tests/Core/Console/test_Scaffold.py +52 -0
- tequio_core-0.1.0/Tests/Core/Console/test_ScheduleRunCommand.py +85 -0
- tequio_core-0.1.0/Tests/Core/Cron/test_Schedule.py +99 -0
- tequio_core-0.1.0/Tests/Core/Database/test_Factory.py +90 -0
- tequio_core-0.1.0/Tests/Core/Database/test_FakerLazy.py +37 -0
- tequio_core-0.1.0/Tests/Core/Database/test_Filtering.py +82 -0
- tequio_core-0.1.0/Tests/Core/Database/test_Migrations.py +84 -0
- tequio_core-0.1.0/Tests/Core/Database/test_Repository.py +179 -0
- tequio_core-0.1.0/Tests/Core/Database/test_Seeder.py +46 -0
- tequio_core-0.1.0/Tests/Core/Database/test_Session.py +39 -0
- tequio_core-0.1.0/Tests/Core/Database/test_Transactional.py +98 -0
- tequio_core-0.1.0/Tests/Core/Events/test_EventDispatch.py +93 -0
- tequio_core-0.1.0/Tests/Core/Events/test_Observer.py +34 -0
- tequio_core-0.1.0/Tests/Core/Jobs/test_Jobs.py +61 -0
- tequio_core-0.1.0/Tests/Core/Mail/test_Mail.py +65 -0
- tequio_core-0.1.0/Tests/Core/Mail/test_MailDriver.py +48 -0
- tequio_core-0.1.0/Tests/Core/Mail/test_Mailable.py +55 -0
- tequio_core-0.1.0/Tests/Core/Mail/test_Mailer.py +173 -0
- tequio_core-0.1.0/Tests/Core/Mail/test_MailerDispatch.py +50 -0
- tequio_core-0.1.0/Tests/Core/Mail/test_Tasks.py +163 -0
- tequio_core-0.1.0/Tests/Core/Mediator/test_Mediator.py +54 -0
- tequio_core-0.1.0/Tests/Core/Pipeline/test_Pipeline.py +43 -0
- tequio_core-0.1.0/Tests/Core/Registry/test_Registry.py +126 -0
- tequio_core-0.1.0/Tests/Core/Translate/test_I18n.py +164 -0
- tequio_core-0.1.0/Tests/Core/View/test_TemplateEngine.py +115 -0
- tequio_core-0.1.0/Tests/Core/test_Clock.py +20 -0
- tequio_core-0.1.0/Tests/Core/test_Cron.py +57 -0
- tequio_core-0.1.0/Tests/Core/test_FrameworkIsGeneric.py +36 -0
- tequio_core-0.1.0/Tests/Core/test_FreeLayoutDiscovery.py +97 -0
- tequio_core-0.1.0/Tests/Core/test_ModelDiscovery.py +20 -0
- tequio_core-0.1.0/Tests/Modules/Demo/test_DemoBuildingBlocks.py +130 -0
- tequio_core-0.1.0/Tests/Modules/Demo/test_DemoFlows.py +156 -0
- tequio_core-0.1.0/Tests/conftest.py +27 -0
- tequio_core-0.1.0/docker-compose.yml +45 -0
- tequio_core-0.1.0/documentation/01-introduccion.md +120 -0
- tequio_core-0.1.0/documentation/02-instalacion.md +125 -0
- tequio_core-0.1.0/documentation/03-configuracion.md +154 -0
- tequio_core-0.1.0/documentation/04-estructura-directorios.md +145 -0
- tequio_core-0.1.0/documentation/05-ciclo-de-vida.md +150 -0
- tequio_core-0.1.0/documentation/06-monolito-modular.md +260 -0
- tequio_core-0.1.0/documentation/07-consola-jornal.md +250 -0
- tequio_core-0.1.0/documentation/08-base-de-datos.md +208 -0
- tequio_core-0.1.0/documentation/09-modelos.md +194 -0
- tequio_core-0.1.0/documentation/10-repositorios-y-transacciones.md +272 -0
- tequio_core-0.1.0/documentation/11-filtrado-y-paginacion.md +293 -0
- tequio_core-0.1.0/documentation/12-jobs.md +181 -0
- tequio_core-0.1.0/documentation/13-colas-y-tareas.md +187 -0
- tequio_core-0.1.0/documentation/14-programacion-cron.md +283 -0
- tequio_core-0.1.0/documentation/15-eventos-y-observers.md +256 -0
- tequio_core-0.1.0/documentation/16-mediator.md +250 -0
- tequio_core-0.1.0/documentation/17-pipeline.md +222 -0
- tequio_core-0.1.0/documentation/18-logging.md +104 -0
- tequio_core-0.1.0/documentation/19-errores.md +215 -0
- tequio_core-0.1.0/documentation/20-correo.md +373 -0
- tequio_core-0.1.0/documentation/README.md +85 -0
- tequio_core-0.1.0/documentation/stylesheets/extra.css +20 -0
- tequio_core-0.1.0/jornal +18 -0
- tequio_core-0.1.0/logs/.gitkeep +0 -0
- tequio_core-0.1.0/migrations/env.py +50 -0
- tequio_core-0.1.0/migrations/script.py.mako +27 -0
- tequio_core-0.1.0/migrations/versions/.gitkeep +0 -0
- tequio_core-0.1.0/migrations/versions/b1f4notes01_notes.py +41 -0
- tequio_core-0.1.0/mkdocs.yml +90 -0
- tequio_core-0.1.0/pyproject.toml +151 -0
- tequio_core-0.1.0/src/tequio/Console/Commands/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Console/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Core/CeleryApp/CeleryApp.py +61 -0
- tequio_core-0.1.0/src/tequio/Core/CeleryApp/Dispatch.py +47 -0
- tequio_core-0.1.0/src/tequio/Core/CeleryApp/Retry.py +67 -0
- tequio_core-0.1.0/src/tequio/Core/CeleryApp/__init__.py +11 -0
- tequio_core-0.1.0/src/tequio/Core/Clock/Clock.py +42 -0
- tequio_core-0.1.0/src/tequio/Core/Clock/__init__.py +11 -0
- tequio_core-0.1.0/src/tequio/Core/Config/Settings.py +156 -0
- tequio_core-0.1.0/src/tequio/Core/Config/__init__.py +5 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Cli.py +133 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Commands/DbCommands.py +27 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Commands/MakeCommands.py +327 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Commands/MigrateCommands.py +72 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Commands/QueueWorkCommand.py +59 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Commands/ScheduleRunCommand.py +53 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Commands/ScheduleWorkCommand.py +43 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Commands/SeedCommands.py +33 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Commands/__init__.py +7 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Console.py +182 -0
- tequio_core-0.1.0/src/tequio/Core/Console/Scaffold.py +114 -0
- tequio_core-0.1.0/src/tequio/Core/Console/__init__.py +29 -0
- tequio_core-0.1.0/src/tequio/Core/Cron/Cron.py +188 -0
- tequio_core-0.1.0/src/tequio/Core/Cron/Schedule.py +133 -0
- tequio_core-0.1.0/src/tequio/Core/Cron/__init__.py +50 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Base.py +25 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Factory.py +59 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Faker.py +55 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Filtering.py +101 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Migrations.py +57 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Repository.py +192 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Seeder.py +32 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Session.py +87 -0
- tequio_core-0.1.0/src/tequio/Core/Database/SoftDelete.py +25 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Timestamp.py +27 -0
- tequio_core-0.1.0/src/tequio/Core/Database/Transactional.py +110 -0
- tequio_core-0.1.0/src/tequio/Core/Database/__init__.py +27 -0
- tequio_core-0.1.0/src/tequio/Core/Discovery.py +50 -0
- tequio_core-0.1.0/src/tequio/Core/Errors/Errors.py +101 -0
- tequio_core-0.1.0/src/tequio/Core/Errors/__init__.py +22 -0
- tequio_core-0.1.0/src/tequio/Core/Events/Dispatch.py +46 -0
- tequio_core-0.1.0/src/tequio/Core/Events/Observer.py +43 -0
- tequio_core-0.1.0/src/tequio/Core/Events/Tasks.py +54 -0
- tequio_core-0.1.0/src/tequio/Core/Events/__init__.py +19 -0
- tequio_core-0.1.0/src/tequio/Core/Jobs/Jobs.py +87 -0
- tequio_core-0.1.0/src/tequio/Core/Jobs/__init__.py +10 -0
- tequio_core-0.1.0/src/tequio/Core/Logging/Logging.py +88 -0
- tequio_core-0.1.0/src/tequio/Core/Logging/__init__.py +10 -0
- tequio_core-0.1.0/src/tequio/Core/Mail/Mail.py +56 -0
- tequio_core-0.1.0/src/tequio/Core/Mail/Mailable.py +91 -0
- tequio_core-0.1.0/src/tequio/Core/Mail/Mailer.py +285 -0
- tequio_core-0.1.0/src/tequio/Core/Mail/Tasks.py +162 -0
- tequio_core-0.1.0/src/tequio/Core/Mail/__init__.py +21 -0
- tequio_core-0.1.0/src/tequio/Core/Mediator/Mediator.py +62 -0
- tequio_core-0.1.0/src/tequio/Core/Mediator/__init__.py +20 -0
- tequio_core-0.1.0/src/tequio/Core/Pipeline/Pipeline.py +60 -0
- tequio_core-0.1.0/src/tequio/Core/Pipeline/__init__.py +10 -0
- tequio_core-0.1.0/src/tequio/Core/Registry/Registry.py +212 -0
- tequio_core-0.1.0/src/tequio/Core/Registry/__init__.py +29 -0
- tequio_core-0.1.0/src/tequio/Core/Translate/I18n.py +146 -0
- tequio_core-0.1.0/src/tequio/Core/Translate/__init__.py +14 -0
- tequio_core-0.1.0/src/tequio/Core/View/TemplateEngine.py +139 -0
- tequio_core-0.1.0/src/tequio/Core/View/__init__.py +12 -0
- tequio_core-0.1.0/src/tequio/Core/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Dictionaries/__init__.py +11 -0
- tequio_core-0.1.0/src/tequio/Models/Note.py +22 -0
- tequio_core-0.1.0/src/tequio/Models/__init__.py +22 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Commands.py +18 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Console/Commands/ArchiveNoteCommand.py +24 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Console/Commands/__init__.py +1 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Console/__init__.py +1 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Crons/DailyDigestCron.py +50 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Crons/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Events.py +23 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Factories/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Factories/factories.py +23 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Handlers/ArchiveNoteHandler.py +33 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Handlers/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Jobs/ExportNotesJob.py +25 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Jobs/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Mail/DailyDigestMailable.py +68 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Mail/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Observers/LogNoteCreated.py +30 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Observers/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Pipes/CleanContent.py +40 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Pipes/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Repositories/NoteRepository.py +15 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Repositories/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Resources/Static/logo.png +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Resources/Views/emails/digest.html.j2 +6 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Seeders/DemoSeeder.py +26 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Seeders/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Services/NoteService.py +59 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/Services/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Modules/Demo/__init__.py +19 -0
- tequio_core-0.1.0/src/tequio/Modules/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/Resources/Lang/Emails/master.en.yml +14 -0
- tequio_core-0.1.0/src/tequio/Resources/Lang/Emails/master.es.yml +15 -0
- tequio_core-0.1.0/src/tequio/Resources/Lang/Emails/mastersigned.en.yml +35 -0
- tequio_core-0.1.0/src/tequio/Resources/Lang/Emails/mastersigned.es.yml +41 -0
- tequio_core-0.1.0/src/tequio/Resources/Views/Emails/Trans/Footer/email_footer.html.j2 +31 -0
- tequio_core-0.1.0/src/tequio/Resources/Views/Emails/Trans/Styles/basic.html.j2 +133 -0
- tequio_core-0.1.0/src/tequio/Resources/Views/Emails/Trans/master.html.j2 +40 -0
- tequio_core-0.1.0/src/tequio/Resources/Views/Emails/Trans/mastersigned.html.j2 +35 -0
- tequio_core-0.1.0/src/tequio/__init__.py +0 -0
- tequio_core-0.1.0/src/tequio/_skeleton/.env.example.tmpl +62 -0
- tequio_core-0.1.0/src/tequio/_skeleton/.gitignore.tmpl +20 -0
- tequio_core-0.1.0/src/tequio/_skeleton/README.md.tmpl +41 -0
- tequio_core-0.1.0/src/tequio/_skeleton/app/Console/Commands/__init__.py.tmpl +0 -0
- tequio_core-0.1.0/src/tequio/_skeleton/app/Console/__init__.py.tmpl +0 -0
- tequio_core-0.1.0/src/tequio/_skeleton/app/Models/__init__.py.tmpl +15 -0
- tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/Hello/Console/Commands/HelloCommand.py.tmpl +18 -0
- tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/Hello/Console/Commands/__init__.py.tmpl +0 -0
- tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/Hello/Console/__init__.py.tmpl +0 -0
- tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/Hello/__init__.py.tmpl +1 -0
- tequio_core-0.1.0/src/tequio/_skeleton/app/Modules/__init__.py.tmpl +0 -0
- tequio_core-0.1.0/src/tequio/_skeleton/app/__init__.py.tmpl +0 -0
- tequio_core-0.1.0/src/tequio/_skeleton/docker-compose.yml.tmpl +25 -0
- tequio_core-0.1.0/src/tequio/_skeleton/jornal.tmpl +17 -0
- tequio_core-0.1.0/src/tequio/_skeleton/logs/.gitkeep.tmpl +1 -0
- tequio_core-0.1.0/src/tequio/_skeleton/migrations/env.py.tmpl +50 -0
- tequio_core-0.1.0/src/tequio/_skeleton/migrations/script.py.mako.tmpl +27 -0
- tequio_core-0.1.0/src/tequio/_skeleton/migrations/versions/.gitkeep.tmpl +1 -0
- tequio_core-0.1.0/src/tequio/_skeleton/pyproject.toml.tmpl +26 -0
- tequio_core-0.1.0/src/tequio/migrations/versions/b1f4notes01_notes.py +43 -0
- tequio_core-0.1.0/uv.lock +1397 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Copia este archivo a .env y ajusta. Estos valores asumen ejecución en DOCKER.
|
|
2
|
+
# Si corres en el HOST (uv run), usa localhost/127.0.0.1 en las URLs.
|
|
3
|
+
|
|
4
|
+
# --- Infraestructura ---
|
|
5
|
+
# Por default sqlite local (zero-config, igual que el skeleton). Para otro motor
|
|
6
|
+
# instala el extra del driver (uv sync --extra mysql) y cambia la URL:
|
|
7
|
+
# DATABASE_URL=mysql+pymysql://user:password@host:3306/database
|
|
8
|
+
# DATABASE_URL=postgresql+psycopg://user:password@host:5432/database
|
|
9
|
+
DATABASE_URL=sqlite:///tequio.db
|
|
10
|
+
|
|
11
|
+
# --- Colas / broker-agnostic ---
|
|
12
|
+
# BROKER_URL: vacío => redis local por default. Solo se usa para lo ENCOLADO (los
|
|
13
|
+
# flujos síncronos no lo tocan). Cambia el transporte sin tocar código:
|
|
14
|
+
# Redis: redis://localhost:6379/0
|
|
15
|
+
# RabbitMQ: amqp://guest:guest@localhost:5672// (docker compose ya lo levanta)
|
|
16
|
+
BROKER_URL=
|
|
17
|
+
# Result backend: OPCIONAL (crons fire-and-forget). Vacío => sin backend.
|
|
18
|
+
RESULT_BACKEND_URL=
|
|
19
|
+
# Store de locks para without_overlapping (redis). Vacío => redis local por default.
|
|
20
|
+
LOCK_URL=
|
|
21
|
+
# Reintentos de tasks ante fallos TRANSITORIOS (backoff exponencial con jitter). Son los
|
|
22
|
+
# DEFAULTS de retry_policy(); se pueden pisar A MANO en código por-task. Solo afectan a
|
|
23
|
+
# tasks que OPTAN por reintentar (autoretry_for), NUNCA a crons. 0 => sin reintentos.
|
|
24
|
+
TASK_MAX_RETRIES=3
|
|
25
|
+
TASK_RETRY_BACKOFF=2 # segundos base del 1er reintento (luego se duplica)
|
|
26
|
+
TASK_RETRY_BACKOFF_MAX=600 # tope del backoff entre reintentos (10 min)
|
|
27
|
+
#
|
|
28
|
+
# CLOUD (EDUCATIVO / EN CONSTRUCCIÓN — basado en docs, NO probado en docker, sin
|
|
29
|
+
# mantenimiento garantizado estos primeros meses; solo de referencia):
|
|
30
|
+
# AWS SQS: BROKER_URL=sqs://<AWS_KEY>:<AWS_SECRET>@ (dep: celery[sqs])
|
|
31
|
+
# Azure SB: BROKER_URL=azureservicebus://<SAS>@<namespace> (dep: azure-servicebus)
|
|
32
|
+
# GCP Pub/Sub: BROKER_URL=gcpubsub://projects/<PROJECT_ID> (dep: celery[gcpubsub])
|
|
33
|
+
# ActiveMQ NO es compatible con Celery (AMQP 1.0). Usa RabbitMQ como MQ.
|
|
34
|
+
# Si se omite, el default es la zona del HOST. IMPORTANTE fijarla explícita: un
|
|
35
|
+
# server suele estar en UTC y quien lo monta puede no ser quien programa.
|
|
36
|
+
TIMEZONE=America/Mexico_City
|
|
37
|
+
APP_ENV=local
|
|
38
|
+
# Nombre del proyecto (el default del framework es genérico "App"; pon el tuyo).
|
|
39
|
+
APP_NAME="My App"
|
|
40
|
+
# Locale de fallback de i18n (correos, API, etc.) cuando no se pasa uno explícito.
|
|
41
|
+
APP_FALLBACK_LOCALE=es
|
|
42
|
+
# Locale de Faker para factories/seeders (datos falsos). Cualquier locale de Faker:
|
|
43
|
+
# es_MX, es_ES, en_US, pt_BR, ... Default: es_MX.
|
|
44
|
+
FAKER_LOCALE=es_MX
|
|
45
|
+
|
|
46
|
+
# --- Logging (Loguru) ---
|
|
47
|
+
LOG_LEVEL=INFO
|
|
48
|
+
# LOG_JSON=true agrega logs/app.jsonl (JSON Lines) para Loki/Grafana.
|
|
49
|
+
LOG_JSON=false
|
|
50
|
+
|
|
51
|
+
# --- Correo ---
|
|
52
|
+
# MAIL_DRIVER: cómo se manda. smtp = SMTP real | log = lo escribe en el log (dev sin
|
|
53
|
+
# SMTP, cross-platform) | null = no-op (lo descarta).
|
|
54
|
+
MAIL_DRIVER=smtp
|
|
55
|
+
# En local apunta a Mailpit (puertos del docker-compose).
|
|
56
|
+
MAILPIT_SMTP_PORT=1025
|
|
57
|
+
MAILPIT_UI_PORT=8025
|
|
58
|
+
MAIL_HOST=<host> # ej. localhost (host) o mailpit (docker)
|
|
59
|
+
MAIL_PORT=1025
|
|
60
|
+
MAIL_USERNAME=
|
|
61
|
+
MAIL_PASSWORD=
|
|
62
|
+
MAIL_ENCRYPTION= # "" sin cifrado (Mailpit) | tls (STARTTLS) | ssl (SMTPS)
|
|
63
|
+
MAIL_FROM_ADDRESS=no-reply@example.com
|
|
64
|
+
MAIL_FROM_NAME="My App"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# Corre los MISMOS guardrails que en local (CONTRIBUTING §Guardrails) en cada push a main y en cada PR.
|
|
4
|
+
# tequio es worker-side: NO hay capa HTTP ni frontend, así que tampoco hay pasos de npm/Vite.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
# Cancela corridas viejas del mismo ref si llega un push nuevo.
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ci-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
quality:
|
|
17
|
+
name: Lint + Types + Imports + Tests + Build
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
# Sin .env en CI: damos un sqlite dummy explícito (DATABASE_URL ya tiene default en
|
|
20
|
+
# Settings, pero lo dejamos claro). Basta para importar (el engine es perezoso) y los
|
|
21
|
+
# tests son sin BD. Igual lo necesitan lint-imports y pytest (importan tequio.*).
|
|
22
|
+
env:
|
|
23
|
+
DATABASE_URL: "sqlite://"
|
|
24
|
+
steps:
|
|
25
|
+
- name: Checkout
|
|
26
|
+
uses: actions/checkout@v5
|
|
27
|
+
|
|
28
|
+
- name: Instalar uv (con Python 3.14 + caché)
|
|
29
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.14"
|
|
32
|
+
enable-cache: true
|
|
33
|
+
|
|
34
|
+
- name: Sincronizar dependencias (incluye grupo dev; reproducible con el lock)
|
|
35
|
+
run: uv sync --frozen
|
|
36
|
+
|
|
37
|
+
# Los 3 guardrails + tests, en pasos separados para ver de un vistazo cuál falla.
|
|
38
|
+
- name: Ruff format (solo verifica)
|
|
39
|
+
run: uv run ruff format --check .
|
|
40
|
+
|
|
41
|
+
- name: Ruff lint
|
|
42
|
+
run: uv run ruff check .
|
|
43
|
+
|
|
44
|
+
- name: Mypy (estricto)
|
|
45
|
+
run: uv run mypy
|
|
46
|
+
|
|
47
|
+
- name: "Fronteras (import-linter: worker-side + módulos)"
|
|
48
|
+
run: uv run lint-imports
|
|
49
|
+
|
|
50
|
+
- name: Pytest (rápidos, sin BD)
|
|
51
|
+
run: uv run pytest
|
|
52
|
+
|
|
53
|
+
# --- Gate de empaquetado: el wheel se construye en limpio y se puede importar ---
|
|
54
|
+
- name: Build sanity (uv build construye sdist + wheel)
|
|
55
|
+
run: uv build
|
|
56
|
+
|
|
57
|
+
- name: Smoke de instalación (wheel limpio + import + CLI tequio)
|
|
58
|
+
run: |
|
|
59
|
+
uv venv /tmp/tequio-smoke
|
|
60
|
+
uv pip install --python /tmp/tequio-smoke/bin/python dist/*.whl
|
|
61
|
+
/tmp/tequio-smoke/bin/python -c "import tequio; print('tequio OK')"
|
|
62
|
+
# El script `tequio` se instaló en el PATH del venv: que al menos liste sin tronar.
|
|
63
|
+
/tmp/tequio-smoke/bin/tequio list
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
# Publica la guía (documentation/) como sitio MkDocs Material en GitHub Pages en cada push a main.
|
|
4
|
+
# Requiere (una sola vez): Settings → Pages → Source = "Deploy from a branch" → rama "gh-pages".
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
paths:
|
|
9
|
+
- "documentation/**"
|
|
10
|
+
- "mkdocs.yml"
|
|
11
|
+
- ".github/workflows/docs.yml"
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write # gh-deploy publica el sitio en la rama gh-pages
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: docs-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
deploy:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- name: Checkout
|
|
25
|
+
uses: actions/checkout@v5
|
|
26
|
+
|
|
27
|
+
- name: Instalar uv (con Python 3.14 + caché)
|
|
28
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.14"
|
|
31
|
+
enable-cache: true
|
|
32
|
+
|
|
33
|
+
- name: Instalar dependencias de docs
|
|
34
|
+
run: uv sync --group docs
|
|
35
|
+
|
|
36
|
+
- name: Construir y publicar en gh-pages
|
|
37
|
+
run: uv run mkdocs gh-deploy --force --no-history
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Release (PyPI)
|
|
2
|
+
|
|
3
|
+
# Publica tequio-core a PyPI cuando empujas un tag de versión (p. ej. `git tag v0.1.0 && git push --tags`).
|
|
4
|
+
# Usa Trusted Publishing (OIDC): NO hay tokens ni secretos — PyPI confía en este workflow.
|
|
5
|
+
# Requisito una sola vez: configurar el "pending publisher" en PyPI para el proyecto tequio-core.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags: ["v*"]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
release:
|
|
12
|
+
name: Build + Publish (Trusted Publishing OIDC)
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
# 'environment' protege el publish: puedes exigir aprobación manual en Settings → Environments.
|
|
15
|
+
environment: pypi
|
|
16
|
+
permissions:
|
|
17
|
+
id-token: write # OBLIGATORIO para OIDC (Trusted Publishing, sin tokens)
|
|
18
|
+
contents: read # con un bloque permissions explícito, GitHub recorta el resto a 'none'
|
|
19
|
+
env:
|
|
20
|
+
DATABASE_URL: "sqlite://"
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout
|
|
23
|
+
uses: actions/checkout@v5
|
|
24
|
+
|
|
25
|
+
- name: Instalar uv (Python 3.14 + caché)
|
|
26
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.14"
|
|
29
|
+
enable-cache: true
|
|
30
|
+
|
|
31
|
+
- name: Sincronizar dependencias (reproducible con el lock)
|
|
32
|
+
run: uv sync --frozen
|
|
33
|
+
|
|
34
|
+
# --- Los guardrails ANTES de publicar: no se publica algo roto ---
|
|
35
|
+
- name: Ruff format
|
|
36
|
+
run: uv run ruff format --check .
|
|
37
|
+
- name: Ruff lint
|
|
38
|
+
run: uv run ruff check .
|
|
39
|
+
- name: Mypy
|
|
40
|
+
run: uv run mypy
|
|
41
|
+
- name: Import-linter
|
|
42
|
+
run: uv run lint-imports
|
|
43
|
+
- name: Pytest
|
|
44
|
+
run: uv run pytest
|
|
45
|
+
|
|
46
|
+
- name: Construir sdist + wheel
|
|
47
|
+
run: uv build --no-sources
|
|
48
|
+
|
|
49
|
+
# uv detecta el OIDC de GitHub Actions y publica vía Trusted Publishing (sin tokens).
|
|
50
|
+
# (el default es --trusted-publishing automatic; no se pasa flag).
|
|
51
|
+
# Tip primer release: si falla el OIDC, cambia temporalmente a
|
|
52
|
+
# `uv publish --trusted-publishing always` para ver el error exacto.
|
|
53
|
+
- name: Publicar a PyPI
|
|
54
|
+
run: uv publish
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Entorno Python
|
|
2
|
+
.venv/
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.pyc
|
|
5
|
+
*.pyo
|
|
6
|
+
|
|
7
|
+
# Caches de herramientas (se regeneran solas; NO se versionan).
|
|
8
|
+
.import_linter_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.claude/settings.local.json
|
|
13
|
+
|
|
14
|
+
# Secretos y configuración local (NUNCA al repo)
|
|
15
|
+
.env
|
|
16
|
+
|
|
17
|
+
# Logs (no versionamos contenido, sí la carpeta)
|
|
18
|
+
logs/*
|
|
19
|
+
!logs/.gitkeep
|
|
20
|
+
|
|
21
|
+
# BD sqlite local (p. ej. DATABASE_URL=sqlite:///tequio.db)
|
|
22
|
+
*.db
|
|
23
|
+
*.sqlite3
|
|
24
|
+
|
|
25
|
+
# Artefactos de build
|
|
26
|
+
dist/
|
|
27
|
+
|
|
28
|
+
# Sitio de docs generado por `mkdocs build` (se publica con gh-deploy; NO se versiona).
|
|
29
|
+
site/
|
|
30
|
+
|
|
31
|
+
# Artefacto runtime del beat de Celery (shelve que persiste el schedule; se regenera solo)
|
|
32
|
+
celerybeat-schedule*
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Todos los cambios notables de **tequio** se documentan aquí.
|
|
4
|
+
|
|
5
|
+
El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/) y el proyecto usa
|
|
6
|
+
[Versionado Semántico](https://semver.org/lang/es/). En `0.x` la API puede cambiar entre minors.
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-06-06
|
|
11
|
+
|
|
12
|
+
**Nacimiento de tequio**: la extracción **worker-side** de [milpa](https://github.com/calcifux/milpa)
|
|
13
|
+
`0.4.0`. El mismo estilo y el mismo kernel reutilizable (`Core`), pero **sin la capa web** —para
|
|
14
|
+
servicios de Python que NO sirven páginas ni API (daemons, pipelines, monitores, ETLs, cron-jobs)
|
|
15
|
+
y solo necesitan **trabajo en segundo plano + base de datos + consola**, con **correo** que vuelve
|
|
16
|
+
al worker. Un contrato de `import-linter` garantiza que la capa web nunca se vuelva a colar al core.
|
|
17
|
+
|
|
18
|
+
Forma tradicional vs estilo milpa: la forma tradicional arrastra todo el framework web (FastAPI,
|
|
19
|
+
auth, pipeline de assets) aunque el servicio nunca sirva HTTP; estilo milpa el worker baja de peso
|
|
20
|
+
quedándose solo con lo que de verdad usa (Celery + SQLAlchemy + Typer + correo), y el guardrail de
|
|
21
|
+
fronteras lo mantiene así.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
#### Lo que vino de milpa 0.4.0 (worker-side)
|
|
26
|
+
|
|
27
|
+
- **Background** — `@job` (on-demand, `.dispatch()`, con `broker_guard` que traduce el broker caído
|
|
28
|
+
en un error accionable) y `@cron_task` (agendado, anti-overlap con lock en Redis y gate por
|
|
29
|
+
`APP_ENV`), separados a propósito (job ≠ cron). Celery broker-agnóstico (`BROKER_URL`).
|
|
30
|
+
- **Patrones estilo milpa** (OPT-IN, auto-descubribles) — `Events`/`Observers` (1:N, transporte
|
|
31
|
+
adaptativo: worker si hay broker, si no síncrono), `Mediator` (command bus 1:1, `@handles`/`send`)
|
|
32
|
+
y `Pipeline` (modelo cebolla). Patrones que un arquitecto puede sugerir, no impuestos.
|
|
33
|
+
- **Datos estilo Spring Data** — `Repository[Model, Id]` tipado, `@transactional`,
|
|
34
|
+
`current_session`, `Factory`/`Seeder` (con Faker), soft-delete y timestamps automáticos; engine
|
|
35
|
+
**agnóstico del motor** (se elige por `DATABASE_URL`, default `sqlite` zero-config), migraciones
|
|
36
|
+
**Alembic** motor-agnósticas.
|
|
37
|
+
- **Errores que NUNCA fallan en silencio** — el CLI rinde un `DomainError` (esperado) como mensaje
|
|
38
|
+
accionable + su código (sin traceback crudo) y deja el traceback completo de un error inesperado
|
|
39
|
+
en el log.
|
|
40
|
+
- **Consola estilo artisan** — kernel Typer con descubrimiento automático de comandos (Core +
|
|
41
|
+
generales del proyecto + módulos): `queue work`, `schedule work`/`run`, `migrate`, `db seed`,
|
|
42
|
+
`make …`, salida en tabla rich.
|
|
43
|
+
- **Logging** — Loguru configurado (stderr conciso sin fuga de valores + archivo rotativo);
|
|
44
|
+
`LOG_JSON=true` agrega `logs/app.jsonl` (JSON Lines) para Loki/Grafana.
|
|
45
|
+
- **Config tipada** — `Settings` (pydantic-settings) lee el `.env`; la infraestructura sin default
|
|
46
|
+
(obligatoria) falla claro si falta.
|
|
47
|
+
- **Reloj inyectable** (`Core/Clock`) — `SystemClock` / `FixedClock` (= `Carbon::setTestNow`) para
|
|
48
|
+
congelar el tiempo en tests, estilo `java.time.Clock` de Spring.
|
|
49
|
+
|
|
50
|
+
#### Lo propio de tequio
|
|
51
|
+
|
|
52
|
+
- **Correo worker-side** (`Core/Mail`) — Mailables estilo Laravel (`Mail.send` / `Mail.queue`),
|
|
53
|
+
drivers (`smtp`/`log`/`null`), adjuntos por bytes o archivo y logo por CID. Vuelve al worker
|
|
54
|
+
porque muchísimos crons y jobs terminan mandando correo (justo el caso del `DailyDigestCron`).
|
|
55
|
+
- **TemplateEngine e i18n de correos worker-side** (`Core/View/TemplateEngine.py`, `Core/Translate`)
|
|
56
|
+
— los correos se renderizan con Jinja2 y se traducen con `i18nice`, todo worker-side; `jinja2` e
|
|
57
|
+
`i18n` quedan permitidos por el contrato de fronteras (lo prohibido es la capa HTTP/Auth/frontend,
|
|
58
|
+
no el correo).
|
|
59
|
+
- **Cola `emails` por convención** — los correos se encolan a la cola `emails`
|
|
60
|
+
(`queue work --queue emails`), el equivalente del `->onQueue('emails')` de Laravel; con
|
|
61
|
+
`MAIL_DRIVER=log` (default dev) el MIME se vuelca al log sin SMTP, y el `docker-compose.yml` trae
|
|
62
|
+
Mailpit para verlos en una UI.
|
|
63
|
+
- **`queue work --pool`** — opción para elegir el pool de Celery (`prefork`/`solo`/`threads`/
|
|
64
|
+
`gevent`); en **Windows**, si se omite, cae a `solo` automáticamente (el prefork de billiard no es
|
|
65
|
+
confiable ahí).
|
|
66
|
+
- **Encarpetado libre con discovery recursivo** — el discovery importa **todo el árbol** de cada
|
|
67
|
+
módulo (`import_submodules(..., recursive=True)`), así organizas tu app como quieras: la pieza se
|
|
68
|
+
descubre mientras lleve su decorador o herede de su base. La única convención con peso es
|
|
69
|
+
`Console/Commands/`. El guardrail `test_FreeLayoutDiscovery` fija esta libertad.
|
|
70
|
+
- **El beat agenda los `@cron_task`** — `collect_beat_schedule()` (en el `Registry`) **fusiona** dos
|
|
71
|
+
fuentes para el calendario del beat: (1) cada `@cron_task(schedule=…)` auto-descubierto —su
|
|
72
|
+
expresión cron convertida a `celery.schedules.crontab`— y (2) los `beat_schedule` declarados en
|
|
73
|
+
`Console/Kernel.py` (vía declarativa, **con precedencia** en colisiones de nombre). Así
|
|
74
|
+
`schedule work` (el beat) agenda los `@cron_task` **sin** escribir un `Kernel.py`. El conversor
|
|
75
|
+
cron-string → `crontab` exige **exactamente 5 campos**; si no, falla con un error claro (no agenda
|
|
76
|
+
mal en silencio). Los gates de ejecución (anti-overlap y `environments`) siguen viviendo en
|
|
77
|
+
`@cron_task` al ejecutar; el beat solo agenda. (Decisión consciente: este comportamiento **diverge
|
|
78
|
+
de milpa**, que solo agendaba lo declarado en `Console/Kernel.py`.)
|
|
79
|
+
- **Demo `Notes`** (`tequio new --demo`) — módulo de referencia corrible (notas, worker-side) que
|
|
80
|
+
ejercita TODO el stack: `@job`, `@cron_task` (un digest diario que **manda correo**),
|
|
81
|
+
eventos→observer (1:N), Mediator (1:1), Pipeline de limpieza y Mailables (i18n de correos), más
|
|
82
|
+
seeders/factories con Faker. Corre sobre SQLite sin levantar infraestructura. La nota es
|
|
83
|
+
deliberadamente mínima (`title`/`body`/`archived`): tequio no tiene Auth ni usuarios (eso vive en
|
|
84
|
+
milpa), así que la nota no tiene dueño.
|
|
85
|
+
- **Scaffolder `tequio new <app> [--demo]`** — genera un proyecto listo para correr desde un
|
|
86
|
+
skeleton embebido (estilo `laravel new`); el paquete se llama `tequio-core` en PyPI pero el import
|
|
87
|
+
y el comando siguen siendo `tequio`. `--demo` materializa el módulo Notes; sin él, un módulo
|
|
88
|
+
`Hello` mínimo.
|
|
89
|
+
- **Tres contratos `import-linter`** — (1) `forbidden`: `tequio.Core` **NO** depende de la capa web
|
|
90
|
+
(fastapi/starlette/uvicorn/slowapi/httpx/itsdangerous/pwdlib/jwt) —el contrato distintivo de
|
|
91
|
+
tequio—; (2) `forbidden`: el shared kernel (`Core`/`Models`/`Dictionaries`) no importa
|
|
92
|
+
`Modules`; (3) `independence`: los módulos no se importan entre sí.
|
|
93
|
+
- **Documentación completa** (mkdocs) — guía estilo Laravel en español: instalación, configuración,
|
|
94
|
+
ciclo de vida (del **worker**, sin request), monolito modular, consola (`jornal`), base de datos,
|
|
95
|
+
background (jobs, colas, cron + el reloj), los patrones estilo milpa, correo, errores de dominio y
|
|
96
|
+
logging. Cada feature contrasta la *forma tradicional* vs *estilo milpa* y se demuestra ejecutable
|
|
97
|
+
en el módulo Demo.
|
|
98
|
+
|
|
99
|
+
### Excluido (vive en milpa)
|
|
100
|
+
|
|
101
|
+
- **Http/FastAPI, Auth** (RBAC/ABAC, JWT/sesión, Passport), **Views/Vite** (frontend,
|
|
102
|
+
microfrontends, PWA) e **i18n de la UI**. tequio **sí** trae correo (Mailables + i18n de correos)
|
|
103
|
+
porque vuelve al worker. Si tu servicio necesita algo de lo excluido, usa
|
|
104
|
+
[milpa](https://github.com/calcifux/milpa).
|
|
105
|
+
|
|
106
|
+
### Notas
|
|
107
|
+
|
|
108
|
+
- Todo es **síncrono** (SQLAlchemy + Celery). Los tests corren **sin base de datos** (fakes +
|
|
109
|
+
monkeypatch) y el toolchain de gates es ruff + mypy strict + import-linter + pytest.
|
|
110
|
+
|
|
111
|
+
[Unreleased]: https://github.com/calcifux/tequio-core/compare/v0.1.0...HEAD
|
|
112
|
+
[0.1.0]: https://github.com/calcifux/tequio-core/releases/tag/v0.1.0
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Código de Conducta
|
|
2
|
+
|
|
3
|
+
## Nuestro compromiso
|
|
4
|
+
|
|
5
|
+
Como miembros, contribuyentes y mantenedores, nos comprometemos a hacer de la
|
|
6
|
+
participación en **tequio** una experiencia libre de acoso,
|
|
7
|
+
acogedora y respetuosa para todas las personas, sin importar su origen o
|
|
8
|
+
identidad. Nos comprometemos a actuar e interactuar de formas que construyan una
|
|
9
|
+
comunidad abierta, amable, diversa y sana.
|
|
10
|
+
|
|
11
|
+
## Nuestros estándares
|
|
12
|
+
|
|
13
|
+
Ejemplos de comportamiento que ayudan a crear un ambiente positivo:
|
|
14
|
+
|
|
15
|
+
- Mostrar empatía y amabilidad hacia los demás
|
|
16
|
+
- Respetar opiniones, puntos de vista y experiencias distintas
|
|
17
|
+
- Dar y aceptar con gracia retroalimentación constructiva
|
|
18
|
+
- Asumir la responsabilidad, disculparse con quienes se vean afectados por
|
|
19
|
+
nuestros errores y aprender de ellos
|
|
20
|
+
- Enfocarse en lo que es mejor para la comunidad en su conjunto
|
|
21
|
+
|
|
22
|
+
El acoso, las conductas abusivas o irrespetuosas, los ataques personales o
|
|
23
|
+
políticos, publicar información privada de terceros sin su consentimiento y
|
|
24
|
+
cualquier otra conducta que una persona razonable consideraría inapropiada en un
|
|
25
|
+
entorno profesional **no se toleran**.
|
|
26
|
+
|
|
27
|
+
## Estándar adoptado
|
|
28
|
+
|
|
29
|
+
Este proyecto adopta el **[Contributor Covenant, versión 2.1](https://www.contributor-covenant.org/es/version/2/1/code_of_conduct/)**.
|
|
30
|
+
El texto completo —con los estándares detallados y las cuatro guías de aplicación
|
|
31
|
+
(Corrección → Advertencia → Expulsión temporal → Expulsión permanente)— aplica a
|
|
32
|
+
este proyecto. Por favor léelo ahí.
|
|
33
|
+
|
|
34
|
+
## Alcance
|
|
35
|
+
|
|
36
|
+
Este Código de Conducta aplica en todos los espacios del proyecto (issues, pull
|
|
37
|
+
requests, discusiones, código) y cuando una persona representa oficialmente al
|
|
38
|
+
proyecto en espacios públicos.
|
|
39
|
+
|
|
40
|
+
## Aplicación
|
|
41
|
+
|
|
42
|
+
Las personas mantenedoras son responsables de aclarar y hacer cumplir estos
|
|
43
|
+
estándares, y pueden eliminar, editar o rechazar contribuciones que no se
|
|
44
|
+
alineen con este Código de Conducta.
|
|
45
|
+
|
|
46
|
+
Las instancias de comportamiento abusivo, de acoso o de cualquier otra forma
|
|
47
|
+
inaceptable pueden reportarse **en privado** a la persona mantenedora
|
|
48
|
+
(`@calcifux`) vía GitHub (abrir un *private security advisory* o contactarla
|
|
49
|
+
directamente).
|
|
50
|
+
<!-- TODO: agrega un email de contacto dedicado si prefieres uno en vez de GitHub. -->
|
|
51
|
+
Todas las quejas serán revisadas e investigadas de forma rápida y justa. Las
|
|
52
|
+
personas mantenedoras están obligadas a respetar la privacidad y la seguridad de
|
|
53
|
+
quien reporta.
|
|
54
|
+
|
|
55
|
+
## Atribución
|
|
56
|
+
|
|
57
|
+
Este Código de Conducta es una adaptación del
|
|
58
|
+
[Contributor Covenant](https://www.contributor-covenant.org/es/), versión 2.1.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Contribuir a tequio
|
|
2
|
+
|
|
3
|
+
¡Gracias por tu interés en contribuir! Este documento explica cómo levantar el proyecto
|
|
4
|
+
en local, las convenciones que seguimos y cómo enviar un Pull Request con las mejores
|
|
5
|
+
probabilidades de merge.
|
|
6
|
+
|
|
7
|
+
> Si vas a tocar un subsistema, lee primero su página en
|
|
8
|
+
> [`documentation/`](documentation/README.md): ahí está el "cómo funciona".
|
|
9
|
+
|
|
10
|
+
## ¿Qué es tequio (y qué no)?
|
|
11
|
+
|
|
12
|
+
tequio es la **extracción worker-side** de [milpa](https://github.com/calcifux/milpa):
|
|
13
|
+
el núcleo de trabajo en segundo plano (jobs, crons, eventos/observers, Mediator, Pipeline,
|
|
14
|
+
BD con SQLAlchemy + Alembic, logging y CLI). **No** trae capa HTTP, Mail, Auth,
|
|
15
|
+
Views/Vite ni i18n: si tu servicio sirve páginas o API, eso vive en
|
|
16
|
+
[milpa](https://github.com/calcifux/milpa), no aquí.
|
|
17
|
+
|
|
18
|
+
## Inicio rápido
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
git clone https://github.com/calcifux/tequio-core.git
|
|
22
|
+
cd tequio-core
|
|
23
|
+
uv sync # crea el venv y resuelve deps (incluye las de dev)
|
|
24
|
+
cp .env.example .env # ajusta lo que necesites (sqlite ya viene por default)
|
|
25
|
+
uv run pytest # si la suite pasa, ya puedes desarrollar
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Con `uv`, antepón `uv run` a los comandos; con el venv activo, omítelo. El launcher de
|
|
29
|
+
consola en la raíz es `jornal` (el mismo nombre que en milpa, a propósito).
|
|
30
|
+
|
|
31
|
+
> **¿Quieres ver algo funcionando ya?** Corre el demo (sqlite, sin infra de BD):
|
|
32
|
+
> ```bash
|
|
33
|
+
> uv run python jornal migrate make -m inicial && uv run python jornal migrate run
|
|
34
|
+
> uv run python jornal db seed # puebla notas con Faker (DemoSeeder)
|
|
35
|
+
> uv run python jornal queue work # arranca el worker (procesa los @job)
|
|
36
|
+
> ```
|
|
37
|
+
> Notes worker-side: jobs/crons/observers que escriben al log, Mediator, Pipeline,
|
|
38
|
+
> seeder con Faker. Para procesar la cola y el beat necesitas un broker (Redis); los
|
|
39
|
+
> flujos síncronos no lo tocan.
|
|
40
|
+
|
|
41
|
+
## Requisitos
|
|
42
|
+
|
|
43
|
+
- **Python 3.14+**
|
|
44
|
+
- **[uv](https://docs.astral.sh/uv/)** (recomendado) como gestor de entorno y deps
|
|
45
|
+
- **Docker NO** es necesario para los tests: son unitarios y **sin base de datos** (usan
|
|
46
|
+
fakes y monkeypatch, no servicios vivos). Para correr la cola/beat de verdad hace falta
|
|
47
|
+
un broker (Redis), no para contribuir/testear.
|
|
48
|
+
- **Git** con auth SSH o HTTPS a GitHub
|
|
49
|
+
|
|
50
|
+
## Estructura del repo
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
src/tequio/
|
|
54
|
+
Core/ ← EL FRAMEWORK (kernel genérico worker-side, reutilizable)
|
|
55
|
+
Models/ ← modelos SQLAlchemy compartidos (auto-discovery)
|
|
56
|
+
Dictionaries/ ← constantes de dominio compartidas
|
|
57
|
+
Modules/ ← los módulos del proyecto (independientes entre sí; Demo es el de referencia)
|
|
58
|
+
Console/ ← commands GENERALES del proyecto (opcional)
|
|
59
|
+
Tests/ ← espeja src/tequio/ 1:1; tests unitarios sin BD
|
|
60
|
+
documentation/ ← guía estilo Laravel (sitio MkDocs Material)
|
|
61
|
+
jornal ← entrypoint de consola en la raíz (el "artisan")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Detalle en [documentation/04-estructura-directorios.md](documentation/04-estructura-directorios.md).
|
|
65
|
+
|
|
66
|
+
## Las dos fronteras (no las rompas)
|
|
67
|
+
|
|
68
|
+
`import-linter` las fuerza en CI (ver `[tool.importlinter]` en `pyproject.toml`):
|
|
69
|
+
|
|
70
|
+
1. **tequio es worker-side**: `tequio.Core` **no** importa la capa web ni mail
|
|
71
|
+
(`fastapi`, `starlette`, `uvicorn`, `jinja2`, `jwt`, `i18n`, …). Ese contrato evita
|
|
72
|
+
que la capa HTTP/auth de milpa se vuelva a colar al núcleo.
|
|
73
|
+
2. **El kernel no depende de los módulos**: `tequio.Core` / `tequio.Models` /
|
|
74
|
+
`tequio.Dictionaries` no importan `tequio.Modules`.
|
|
75
|
+
3. **Los módulos son independientes entre sí**: `tequio.Modules.A` no importa
|
|
76
|
+
`tequio.Modules.B`.
|
|
77
|
+
|
|
78
|
+
Si dos módulos necesitan compartir algo, ese algo sube al kernel compartido. Ver
|
|
79
|
+
[documentation/06-monolito-modular.md](documentation/06-monolito-modular.md).
|
|
80
|
+
|
|
81
|
+
## Ramas y commits
|
|
82
|
+
|
|
83
|
+
- `main` siempre verde. No hagas push directo; abre un PR.
|
|
84
|
+
- Ramas de feature: `feat/cron-lock-timeout`, `fix/queue-retry-backoff`, `docs/install-guide`.
|
|
85
|
+
- [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `docs:`,
|
|
86
|
+
`refactor:`, `test:`, `ci:`, `chore:`.
|
|
87
|
+
|
|
88
|
+
## Estilo de código
|
|
89
|
+
|
|
90
|
+
- Identificadores en **inglés**; comentarios y docstrings en **español**, explicando el
|
|
91
|
+
*porqué* (no abreviar).
|
|
92
|
+
- Sin emojis en código/comentarios/docstrings.
|
|
93
|
+
- Tipos completos: `mypy` corre en modo **estricto** sobre `src/tequio` y `Tests/Core`.
|
|
94
|
+
- Un archivo por clase/responsabilidad (estilo Laravel/PascalCase en `src/tequio`).
|
|
95
|
+
- Formato e imports los maneja **Ruff** (no formatees a mano); cada archivo declara
|
|
96
|
+
`from __future__ import annotations` (lo exige y autofixea Ruff).
|
|
97
|
+
|
|
98
|
+
## Guardrails (lo que valida CI)
|
|
99
|
+
|
|
100
|
+
Antes de abrir el PR, corre todo y déjalo verde. Son los **3 guardrails** (lint, tipos,
|
|
101
|
+
fronteras) + **tests** + **build**:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
uv run ruff format . # formato | --check solo verifica (modo CI)
|
|
105
|
+
uv run ruff check . # lint | --fix arregla lo auto-arreglable
|
|
106
|
+
uv run mypy # tipos (estricto)
|
|
107
|
+
uv run lint-imports # fronteras (worker-side + módulos)
|
|
108
|
+
uv run pytest # tests (rápidos, sin BD)
|
|
109
|
+
uv build # empaqueta sdist + wheel (no se publica algo que no construye)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Todo de una:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
uv run ruff format --check . && uv run ruff check . && uv run mypy && uv run lint-imports && uv run pytest && uv build
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Contribuir al Core
|
|
119
|
+
|
|
120
|
+
tequio es un **framework**: las contribuciones van al **kernel** (`src/tequio/Core`), a sus
|
|
121
|
+
tests (`Tests/Core`) y a la documentación. **No** se trata de crear módulos de negocio
|
|
122
|
+
nuevos — eso lo hace cada proyecto que *usa* tequio, en su propio repo (con `tequio new`).
|
|
123
|
+
`src/tequio/Modules/Demo` es solo el módulo de **referencia/demo** (Notes worker-side):
|
|
124
|
+
tócalo únicamente para demostrar una capacidad del Core, no para meter features de dominio.
|
|
125
|
+
|
|
126
|
+
Al tocar el Core:
|
|
127
|
+
|
|
128
|
+
1. **Mantén el Core genérico.** Nada de dominio ni de un proyecto en particular (ni
|
|
129
|
+
nombres, ni reglas de negocio): el kernel debe servir igual a cualquier servicio
|
|
130
|
+
worker-side. Si dudas si algo es "del framework" o "de un proyecto", probablemente no
|
|
131
|
+
va en Core.
|
|
132
|
+
2. **Respeta las fronteras**: `tequio.Core` no importa la capa web/mail ni los módulos (el
|
|
133
|
+
discovery es dinámico, no por imports estáticos). Lo valida `lint-imports`.
|
|
134
|
+
3. **Espeja la estructura por capas**: cada subsistema vive en `src/tequio/Core/<Subsistema>/`
|
|
135
|
+
con un `__init__.py` que expone su API pública.
|
|
136
|
+
4. **Agrega tests** en `Tests/Core/...` (sin BD) y, si cambia comportamiento público,
|
|
137
|
+
**documéntalo** en `documentation/`.
|
|
138
|
+
|
|
139
|
+
Para entender un subsistema antes de tocarlo, lee su página en
|
|
140
|
+
[documentation/](documentation/README.md).
|
|
141
|
+
|
|
142
|
+
## Tests
|
|
143
|
+
|
|
144
|
+
- Espeja `src/tequio/` en `Tests/` (misma ruta). Tests **unitarios y sin BD**: usa fakes y
|
|
145
|
+
monkeypatch, no levantes Redis/Postgres.
|
|
146
|
+
- Corre uno solo: `uv run pytest Tests/Core/Cron/test_Schedule.py -x`.
|
|
147
|
+
|
|
148
|
+
## Checklist del PR
|
|
149
|
+
|
|
150
|
+
- [ ] `ruff format --check .` y `ruff check .` pasan
|
|
151
|
+
- [ ] `mypy` pasa (estricto)
|
|
152
|
+
- [ ] `lint-imports` pasa (no rompiste las fronteras)
|
|
153
|
+
- [ ] `pytest` pasa
|
|
154
|
+
- [ ] `uv build` construye sdist + wheel
|
|
155
|
+
- [ ] Subject en Conventional Commits
|
|
156
|
+
- [ ] Sin datos personales, secretos ni credenciales
|
|
157
|
+
- [ ] Si cambió comportamiento público, actualizaste `documentation/`
|
|
158
|
+
|
|
159
|
+
## Código de Conducta
|
|
160
|
+
|
|
161
|
+
Este proyecto adopta el [Código de Conducta](CODE_OF_CONDUCT.md) (Contributor Covenant
|
|
162
|
+
2.1). Al participar, aceptas cumplirlo.
|
|
163
|
+
|
|
164
|
+
## Licencia
|
|
165
|
+
|
|
166
|
+
Al contribuir aceptas que tu aporte queda bajo la [Licencia MIT](LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 @Calcifux (Carlos Guillermo Reyes Ramiro)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tequio-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Núcleo de trabajo en segundo plano estilo milpa: jobs, crons, eventos y BD (Celery + SQLAlchemy + Typer) sin capa HTTP.
|
|
5
|
+
Author: @Calcifux (Carlos Guillermo Reyes Ramiro)
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.14
|
|
9
|
+
Requires-Dist: alembic>=1.18.4
|
|
10
|
+
Requires-Dist: celery[redis]>=5.6.3
|
|
11
|
+
Requires-Dist: croniter>=6.2.2
|
|
12
|
+
Requires-Dist: i18nice[yaml]>=0.16
|
|
13
|
+
Requires-Dist: jinja2>=3.1.4
|
|
14
|
+
Requires-Dist: loguru>=0.7
|
|
15
|
+
Requires-Dist: pydantic-settings>=2.14.1
|
|
16
|
+
Requires-Dist: redis>=6.4.0
|
|
17
|
+
Requires-Dist: rich>=15.0.0
|
|
18
|
+
Requires-Dist: sqlalchemy-easy-softdelete>=0.9.0
|
|
19
|
+
Requires-Dist: sqlalchemy>=2.0.50
|
|
20
|
+
Requires-Dist: typer>=0.26.2
|
|
21
|
+
Requires-Dist: tzdata>=2026.2
|
|
22
|
+
Requires-Dist: tzlocal>=5.3.1
|
|
23
|
+
Provides-Extra: mssql
|
|
24
|
+
Requires-Dist: pyodbc>=5.2; extra == 'mssql'
|
|
25
|
+
Provides-Extra: mysql
|
|
26
|
+
Requires-Dist: pymysql>=1.2.0; extra == 'mysql'
|
|
27
|
+
Provides-Extra: oracle
|
|
28
|
+
Requires-Dist: oracledb>=2.5; extra == 'oracle'
|
|
29
|
+
Provides-Extra: postgres
|
|
30
|
+
Requires-Dist: psycopg[binary]>=3.2; extra == 'postgres'
|