Skip to content

Deploy + Secrets — la convención per-producto

Cada producto tiene su propio plan de deploy y su propio catálogo de secretos. AWaC formaliza ambos como assets versionados que viven en <product-org>/agent-stack/. Los flujos (cómo se ejecuta cada deploy, cómo se resuelven los secretos) son universales y viven en <transversal-org>/agent-stack-core + topical stacks.

Introducido en CLI v0.6.0 (schemas) + v0.7.0 (comandos wsp deploy y wsp secrets check).

<product>/agent-stack/
├── awac.yml ← stack repo + repos del producto + module_convention
├── deploy.yml ← componentes deployables + targets + flujos pre/post
└── devvault.yml ← catálogo de secretos (NUNCA vault_path local)

Schemas formales: wsp schema deploy y wsp schema devvault.


Declara qué se deploya, en qué orden, contra qué targets, y qué pre-steps + aprobaciones humanas son obligatorios.

schema: deploy/1
product: <slug> # debe coincidir con awac.yml#product
description: <texto opcional>
components:
- name: <stable_id> # snake_case o kebab-case
target: <target_type> # ver tabla abajo
repo: <org>/<name> # opcional (para componentes cross-repo)
requires_human_approval: true # default true
pre_steps: # workflow IDs que deben PASAR antes
- run_tests_local
- terraform_plan
promote_after_pass: # opcional, para flujos two-step
- target_repo: <org>/<name>
target_branch: <branch>
require_pass_on: <target> # solo promueve si ese target acka
rollback_window_minutes: 30 # opcional
<target>: # sub-bloque target-específico
...
TargetDescripciónTopical workflow
odoo_shOdoo SaaS hosting (push GH → Odoo.SH watches branch).erp-partners/agent-stack/workflows/deploy_to_odoo_sh.md
aws_ecsECS Fargate / EC2-backed.getGanemo/agent-stack-aws/workflows/deploy_to_aws_ecs.md
aws_lambdaLambda functions.getGanemo/agent-stack-aws/workflows/deploy_to_aws_lambda.md
aws_ec2_ssmEC2 con SSH cerrado, deploy vía SSM.<transversal-org>/agent-stack-aws/workflows/deploy_to_aws_ec2_ssm.md
cloudflare_pagesCloudflare Pages static + workers.<transversal-org>/agent-stack-cloudflare/workflows/deploy_to_cloudflare_pages.md
cloudflare_workersCloudflare Workers serverless.<transversal-org>/agent-stack-cloudflare/workflows/deploy_to_cloudflare_workers.md
github_pagesGitHub Pages.<transversal-org>/agent-stack-core/workflows/deploy_to_github_pages.md
manualPasos no automatizados (último recurso, documentar en pre_steps).n/a

El workflow router deploy_product (en <transversal-org>/agent-stack-core/workflows/):

  1. Lee <product-org>/agent-stack/deploy.yml.
  2. Para cada componente: corre pre_steps en orden. Cualquier fallo → abort.
  3. Si requires_human_approval: true: imprime el plan, PARA, espera ack explícito por componente.
  4. Delega al deploy_to_<target>.md topical workflow.
  5. Tras success del target, ejecuta promote_after_pass (si declara require_pass_on y el target ackó).
  6. Honra rollback_window_minutes post-deploy.
  7. Reporta resultado y registra entrada en <product-org>/project_management/...progreso.md.

El CLI no ejecuta el deploy — solo planifica. La ejecución es workflow-driven (ADR 009).

deploy_to_odoo_sh codifica el patrón completo:

StepAcción
0Pre-condition: run_odoo_tests_docker_wsl PASS (mandatorio).
1Lee la sección del componente del deploy.yml.
2Ack humano explícito — un “ok” anterior en el chat NO cuenta.
3Resuelve dependencias recursivas (__manifest__.py#depends).
4Push al repo Odoo.SH-watched (staging).
5Recupera token GH (Windows Credential Manager).
6Polling del build status vía GH commit statuses + browser fallback.
7Recupera instance_url via browser y actualiza deploy.yml.
8Verifica ir.logging — entries W/E/C en módulos custom = BLOCK.
9En falla real: lee logs, fix, restart desde Step 3.
10Promote a erp-partners/<module>:<19.0> solo si Step 8 limpió. Fast-forward only — refuse force-push.
11Reporta SUCCESS/FAILURE al router.
Terminal window
wsp deploy <product> [--component <name>] [--json]

Plan-only: imprime los componentes resueltos, sus targets, pre_steps, ack requirement, promotions. Validación schema antes de imprimir. Para ejecutar de verdad → invocar el workflow router.


Declara los secretos que el producto necesita, mapeados de nombre lógico a path relativo dentro del vault local del developer.

LayerVive enPerVersionado
Catálogo (logical → relative path)<product-org>/agent-stack/devvault.ymlproducto
Vault path local~/.devvault/.config.ymlmáquina
Secret values~/.devvault/<vault_path>/<relative-path>máquina

Esta separación es la decisión central (ADR 010): el catálogo es per-producto y compartido entre devs; el path local NUNCA va a un repo (cada developer mantiene su propio ~/.devvault/).

schema: devvault/1
product: <slug>
description: <opcional>
secrets:
<logical_name>: <relative_path>
aws: aws/<product>.yml
cloudflare: providers/cloudflare.yml
odoo: odoo/general.yml
github_app: github/<product>-app.yml
github_pem: github/<product>-app.pem

El schema prohíbe explícitamente la clave vault_path dentro del catálogo — meterla ahí es el error más común y el motivo por el que el modelo se separó en dos.

~/.devvault/.config.yml
vault_path: "/absolute/path/to/your/devvault"

Los secret values mismos los popula el developer desde su password manager (1Password, Bitwarden, etc.). Eso queda fuera del scope de AWaC.

Patrón canónico (codificado en la rule use_devvault.md):

import yaml
from pathlib import Path
# 1. Path local del vault
config = yaml.safe_load((Path.home() / ".devvault" / ".config.yml").read_text())
vault_path = Path(config["vault_path"])
# 2. Catálogo del producto
catalog = yaml.safe_load(Path("<product-org>/agent-stack/devvault.yml").read_text())
# 3. Resolver y leer
secret_path = vault_path / catalog["secrets"]["aws"]
aws_secrets = yaml.safe_load(secret_path.read_text())
Terminal window
wsp secrets check <product> [--json]

Read-only. Reporta por entrada [ok] / [missing] / [unreadable]. Nunca abre ni imprime el contenido. Exit 0 si todos OK; 1 si alguno falta.

wsp doctor también incluye un check devvault_config que verifica que ~/.devvault/.config.yml exista y vault_path resuelva.


Producto con orchestrator AWS + módulo Odoo

Section titled “Producto con orchestrator AWS + módulo Odoo”
# <product-org>/agent-stack/deploy.yml
schema: deploy/1
product: my-product
components:
- name: orchestrator
target: aws_ec2_ssm
repo: my-product/orchestrator
requires_human_approval: true
pre_steps: [run_tests_local]
rollback_window_minutes: 30
- name: my_product_portal_module
target: odoo_sh
requires_human_approval: true
odoo_sh:
project: my-org-my-product-portal
branch: main
modules: [my_product_portal]
pre_steps: [run_odoo_tests_docker_wsl]
promote_after_pass:
- target_repo: erp-partners/my_product_portal
target_branch: "19.0"
require_pass_on: odoo_sh
# <product-org>/agent-stack/devvault.yml
schema: devvault/1
product: my-product
secrets:
aws_account: aws/account.yml
aws: aws/my-product.yml
cloudflare: providers/cloudflare.yml
odoo: odoo/general.yml
github_app: github/my-product-app.yml
github_pem: github/my-product-app.pem

Producto con varios módulos Odoo + API en ECS

Section titled “Producto con varios módulos Odoo + API en ECS”

Combina varios módulos Odoo (<product>_saas + <product>_core) con un servicio backend (target aws_ecs).

api (aws_ecs) + web (cloudflare_pages, sin requires_human_approval porque CF Pages auto-publica preview por push) + deploy_orchestration (manual, Docker+Nginx).


  1. Hardcodear secretos en cualquier archivo del repo, incluso “placeholder” tipo API_KEY="changeme". Usar <placeholder> en config files committeados y resolver del vault en runtime.
  2. Meter vault_path en <product>/agent-stack/devvault.yml. El schema lo rechaza; el dev path es per-machine.
  3. Saltar pre_steps “porque tarda”. Los pre_steps existen porque el alternative (deployar código roto) es peor.
  4. Saltar requires_human_approval porque “el usuario dijo deployá hace 5 minutos”. El ack es per-componente, per-deploy.
  5. promote_after_pass ejecutado antes que el target acka. Esa es exactamente la falla que require_pass_on previene.
  6. Force-push en promote. La rama canónica es producción truth — reconciliar, no overwriting.
  7. Inventar un deploy procedure porque el spec no existe. La acción correcta es invocar la skill create_deploy_spec, no improvisar.

  • Schemas: wsp schema deploy / wsp schema devvault.
  • Skill create_deploy_spec (publicada en <transversal-org>/agent-stack-core/skills/).
  • Rules use_deploy_spec, use_devvault (publicadas en <transversal-org>/agent-stack-core/rules/).
  • Workflow router deploy_product (publicado en <transversal-org>/agent-stack-core/workflows/).
  • Topical (Odoo): workflow deploy_to_odoo_sh (publicado en erp-partners/agent-stack/workflows/).
  • ADRs: 009 (deploy.yml separado de awac.yml), 010 (devvault two-layer model).
  • Comandos: wsp deploy, wsp secrets check, wsp doctor. Ver 05-cli-reference.md.