ADR-066: Generator Output Modes

Status

Proposed

Date

2025-12-19

Context

Hub runs in multiple environments (CLI, Docker, AWS ECS). Generators need flexible output options since ECS containers have ephemeral filesystems. Different integration scenarios require different delivery methods.

Decision

Support six output modes:

┌──────────────┬─────────────────────────────────────────────────────┐
│ Mode         │ Behavior                                            │
├──────────────┼─────────────────────────────────────────────────────┤
│ FILESYSTEM   │ Write to local disk (CLI, local Docker)            │
│ ARCHIVE      │ Return ZIP in response body (binary)               │
│ URL          │ Store temp, return download URL (n8n, Slack, Web)  │
│ S3           │ Upload to S3, return URL                            │
│ GITHUB       │ Create repo + push, return URL                      │
│ EMAIL        │ Send as email attachment                            │
└──────────────┴─────────────────────────────────────────────────────┘

Mode Selection Guide

┌─────────────────────────────────────────────────────────────────────┐
│  Use Case                          │  Recommended Mode              │
├────────────────────────────────────┼────────────────────────────────┤
│  CLI / Local development           │  FILESYSTEM                    │
│  API client handles binary         │  ARCHIVE                       │
│  n8n / Slack / Web integrations    │  URL                           │
│  Permanent storage / AWS           │  S3                            │
│  CI/CD / Collaboration             │  GITHUB                        │
│  Non-technical users / Offline     │  EMAIL                         │
└─────────────────────────────────────────────────────────────────────┘

Configuration

# Output mode
idempiere.hub.generator.output-mode=${GENERATOR_OUTPUT_MODE:filesystem}

# S3 settings
idempiere.hub.generator.s3.bucket=${GENERATOR_S3_BUCKET:}
idempiere.hub.generator.s3.prefix=${GENERATOR_S3_PREFIX:generated/}

# GitHub settings
idempiere.hub.github.default-org=${GITHUB_DEFAULT_ORG:}
idempiere.hub.github.default-private=${GITHUB_DEFAULT_PRIVATE:true}

# URL/Download settings
idempiere.hub.artifacts.base-url=${ARTIFACTS_BASE_URL:}
idempiere.hub.artifacts.default-ttl-minutes=${ARTIFACTS_TTL:60}

# Email settings
idempiere.hub.email.smtp-host=${SMTP_HOST:}
idempiere.hub.email.from=${EMAIL_FROM:noreply@cloudempiere.com}

API

POST /api/generate/plugin
{
  "artifactId": "org.acme.rating",
  "output": "url"  // or: filesystem, archive, s3, github, email
  "email": "dev@company.com"  // required for email mode
}

Response (url mode):
{
  "download_url": "https://hub.cloudempiere.com/api/artifacts/abc123/download",
  "filename": "org.acme.rating.zip",
  "expires_at": "2025-12-19T15:00:00Z"
}

Response (github mode):
{
  "github_url": "https://github.com/cloudempiere/org.acme.rating",
  "clone_url": "https://github.com/cloudempiere/org.acme.rating.git",
  "commit": "abc123"
}

Response (email mode):
{
  "sent_to": "dev@company.com",
  "filename": "org.acme.rating.zip"
}

Download Endpoint

GET /api/artifacts/{id}/download

Response: Binary ZIP file
Headers:
  Content-Type: application/zip
  Content-Disposition: attachment; filename="org.acme.rating.zip"

ECS Deployment

ECS Task
├── Ephemeral /tmp (generate here)
├── Secrets Manager (GITHUB_TOKEN, SMTP credentials)
└── Output: URL, S3, GitHub, or Email (no persistent storage needed)

Implementation

Services in org.idempiere.cli.services.infrastructure:

Service Purpose
ArtifactStorageService Store/retrieve temp artifacts for URL mode
ArchiveService Create ZIP archives
GitHubService Create repos, push code
CredentialService Get tokens/secrets

Path: /docs/developers/architecture/idempiere-hub/066-generator-output-modes