From 855012b6f8f90378d94f9d25795cabad7b02f14e Mon Sep 17 00:00:00 2001 From: Nicholas Hodaly Date: Wed, 25 Jun 2025 11:27:52 -0700 Subject: [PATCH] Update default and optional services branch property --- src/lib/static/blogcontent/blogcontent.json | 2 +- src/lib/static/blogcontent/blogcontent.ts | 2 +- src/pages/docs/features/_meta.ts | 3 +++ src/pages/docs/schema/index.mdx | 9 ++++----- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/lib/static/blogcontent/blogcontent.json b/src/lib/static/blogcontent/blogcontent.json index a707884..cee9e46 100644 --- a/src/lib/static/blogcontent/blogcontent.json +++ b/src/lib/static/blogcontent/blogcontent.json @@ -32,7 +32,7 @@ "description": "Deploy services using Helm directly in Kubernetes without external CI/CD dependencies", "date": "2025-01-29", "path": "docs/features/native-helm-deployment", - "body": "This feature is still in alpha and might change with breaking changes.\n\n\n**Native Helm** is an alternative deployment method that runs Helm deployments directly within Kubernetes jobs, eliminating the need for external CI/CD systems. This provides a more self-contained and portable deployment solution.\n\n\n Native Helm deployment is an opt-in feature that can be enabled globally or\n per-service.\n\n\n## Overview\n\nWhen enabled, Native Helm:\n\n- Creates Kubernetes jobs to execute Helm deployments\n- Runs in ephemeral namespaces with proper RBAC\n- Provides real-time deployment logs via WebSocket\n- Handles concurrent deployments automatically\n- Supports all standard Helm chart types\n\n## Quickstart\n\nWant to try native Helm deployment? Here's the fastest way to get started:\n\n```yaml filename=\"lifecycle.yaml\" {5}\nservices:\n - name: my-api\n defaultUUID: \"dev-0\"\n helm:\n deploymentMethod: \"native\" # That's it!\n chart:\n name: \"local\"\n valueFiles:\n - \"./helm/values.yaml\"\n```\n\nThis configuration:\n\n1. Enables native Helm for the `my-api` service\n2. Uses a local Helm chart from your repository\n3. Applies values from `./helm/values.yaml`\n4. Runs deployment as a Kubernetes job\n\n\n To enable native Helm for all services at once, see [Global\n Configuration](#enabling-native-helm).\n\n\n## Configuration\n\n### Enabling Native Helm\n\nThere are two ways to enable native Helm deployment:\n\n#### Per Service Configuration\n\nEnable native Helm for individual services:\n\n```yaml {4} filename=\"lifecycle.yaml\"\nservices:\n - name: my-service\n helm:\n deploymentMethod: \"native\" # Enable for this service only\n chart:\n name: my-chart\n```\n\n#### Global Configuration\n\nEnable native Helm for all services:\n\n```yaml {3} filename=\"lifecycle.yaml\"\nhelm:\n nativeHelm:\n enabled: true # Enable for all services\n```\n\n### Configuration Precedence\n\nLifecycle uses a hierarchical configuration system with three levels of precedence:\n\n1. **helmDefaults** - Base defaults for all deployments (database: `global_config` table)\n2. **Chart-specific config** - Per-chart defaults (database: `global_config` table)\n3. **Service YAML config** - Service-specific overrides (highest priority)\n\n\n Service-level configuration always takes precedence over global defaults.\n\n\n### Global Configuration (Database)\n\nGlobal configurations are stored in the `global_config` table in the database. Each configuration is stored as a row with:\n\n- **key**: The configuration name (e.g., 'helmDefaults', 'postgresql', 'redis')\n- **config**: JSON object containing the configuration\n\n#### helmDefaults Configuration\n\nStored in database with key `helmDefaults`:\n\n```json\n{\n \"nativeHelm\": {\n \"enabled\": true,\n \"defaultArgs\": \"--wait --timeout 30m\",\n \"defaultHelmVersion\": \"3.12.0\"\n }\n}\n```\n\n**Field Descriptions**:\n\n- `enabled`: When `true`, enables native Helm deployment for all services unless they explicitly set `deploymentMethod: \"ci\"`\n- `defaultArgs`: Arguments automatically appended to every Helm command (appears before service-specific args)\n- `defaultHelmVersion`: The Helm version to use when not specified at the service or chart level\n\n#### Chart-specific Configuration\n\nExample: PostgreSQL configuration stored with key `postgresql`:\n\n```json\n{\n \"version\": \"3.13.0\",\n \"args\": \"--force --timeout 60m0s --wait\",\n \"chart\": {\n \"name\": \"postgresql\",\n \"repoUrl\": \"https://charts.bitnami.com/bitnami\",\n \"version\": \"12.9.0\",\n \"values\": [\"auth.username=postgres_user\", \"auth.database=postgres_db\"]\n }\n}\n```\n\n\n These global configurations are managed by administrators and stored in the\n database. They provide consistent defaults across all environments and can be\n overridden at the service level.\n\n\n## Usage Examples\n\n### Quick Experiment: Deploy Jenkins!\n\nWant to see native Helm in action? Let's deploy everyone's favorite CI/CD tool - Jenkins! This example shows how easy it is to deploy popular applications using native Helm.\n\n```yaml filename=\"lifecycle.yaml\"\nenvironment:\n defaultServices:\n - name: \"my-app\"\n - name: \"jenkins\" # Add Jenkins to your default services\n\nservices:\n - name: \"jenkins\"\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/apps\"\n branchName: \"main\"\n chart:\n name: \"jenkins\"\n repoUrl: \"https://charts.bitnami.com/bitnami\"\n version: \"13.6.8\"\n values:\n - \"service.type=NodePort\" # Override default LoadBalancer to avoid cloud resource costs\n```\n\n\n 🎉 That's it! With just a few lines of configuration, you'll have Jenkins\n running in your Kubernetes cluster. The `service.type=NodePort` override is\n important - the Bitnami Jenkins chart defaults to `LoadBalancer` which would\n consume cloud resources like an AWS ELB or GCP Load Balancer.\n\n\nTo access your Jenkins instance:\n\n1. Check the deployment status in your PR comment\n2. Click the **Deploy Logs** link to monitor the deployment\n3. Once deployed, Jenkins will be available at the internal hostname\n\n\n For more Jenkins configuration options and values, check out the [Bitnami\n Jenkins chart\n documentation](https://github.com/bitnami/charts/tree/main/bitnami/jenkins).\n This same pattern works for any Bitnami chart (PostgreSQL, Redis, MongoDB) or\n any other public Helm chart!\n\n\n### Basic Service Deployment\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: web-api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: web-app\n version: \"1.2.0\"\n```\n\n### PostgreSQL with Overrides\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: database\n helm:\n deploymentMethod: \"native\"\n version: \"3.14.0\" # Override Helm version\n args: \"--atomic\" # Override deployment args\n chart:\n name: postgresql\n values: # Additional values merged with defaults\n - \"persistence.size=20Gi\"\n - \"replicaCount=2\"\n```\n\n### Custom Environment Variables\n\nLifecycle supports flexible environment variable formatting through the `envMapping` configuration. This feature allows you to control how environment variables from your service configuration are passed to your Helm chart.\n\n\n **Why envMapping?** Different Helm charts expect environment variables in\n different formats. Some expect an array of objects with `name` and `value`\n fields (Kubernetes standard), while others expect a simple key-value map. The\n `envMapping` feature lets you adapt to your chart's requirements.\n\n\n#### Default envMapping Configuration\n\nYou can define default `envMapping` configurations in the `global_config` database table. These defaults apply to all services using that chart unless overridden at the service level.\n\n**Example: Setting defaults for your organization's chart**\n\n```json\n// In global_config table, key: \"myorg-web-app\"\n{\n \"chart\": {\n \"name\": \"myorg-web-app\",\n \"repoUrl\": \"https://charts.myorg.com\"\n },\n \"envMapping\": {\n \"app\": {\n \"format\": \"array\",\n \"path\": \"deployment.containers[0].env\"\n }\n }\n}\n```\n\nWith this configuration, any service using the `myorg-web-app` chart will automatically use array format for environment variables:\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: \"myorg-web-app\" # Inherits envMapping from global_config\n docker:\n app:\n env:\n API_KEY: \"secret\"\n # These will be formatted as array automatically\n```\n\n\n Setting `envMapping` in global_config is particularly useful when: - You have\n a standard organizational chart used by many services - You want consistent\n environment variable handling across services - You're migrating multiple\n services and want to reduce configuration duplication\n\n\n#### Array Format\n\nBest for charts that expect Kubernetes-style env arrays.\n\n```yaml {7-9} filename=\"lifecycle.yaml\"\nservices:\n - name: api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: local\n envMapping:\n app:\n format: \"array\"\n path: \"env\"\n docker:\n app:\n env:\n DATABASE_URL: \"postgres://localhost:5432/mydb\"\n API_KEY: \"secret-key-123\"\n NODE_ENV: \"production\"\n```\n\n**This produces the following Helm values:**\n\n```bash\n--set env[0].name=DATABASE_URL\n--set env[0].value=postgres://localhost:5432/mydb\n--set env[1].name=API_KEY\n--set env[1].value=secret-key-123\n--set env[2].name=NODE_ENV\n--set env[2].value=production\n```\n\n**Your chart's values.yaml would use it like:**\n\n```yaml\nenv:\n - name: DATABASE_URL\n value: postgres://localhost:5432/mydb\n - name: API_KEY\n value: secret-key-123\n - name: NODE_ENV\n value: production\n```\n\n#### Map Format\n\nBest for charts that expect a simple key-value object.\n\n```yaml {7-9} filename=\"lifecycle.yaml\"\nservices:\n - name: api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: local\n envMapping:\n app:\n format: \"map\"\n path: \"envVars\"\n docker:\n app:\n env:\n DATABASE_URL: \"postgres://localhost:5432/mydb\"\n API_KEY: \"secret-key-123\"\n NODE_ENV: \"production\"\n```\n\n**This produces the following Helm values:**\n\n```bash\n--set envVars.DATABASE__URL=postgres://localhost:5432/mydb\n--set envVars.API__KEY=secret-key-123\n--set envVars.NODE__ENV=production\n```\n\n\n Note: Underscores in environment variable names are converted to double\n underscores (`__`) in map format to avoid Helm parsing issues.\n\n\n**Your chart's values.yaml would use it like:**\n\n```yaml\nenvVars:\n DATABASE__URL: postgres://localhost:5432/mydb\n API__KEY: secret-key-123\n NODE__ENV: production\n```\n\n#### Complete Example with Multiple Services\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n # Service using array format (common for standard Kubernetes deployments)\n - name: frontend\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/apps\"\n branchName: \"main\"\n envMapping:\n app:\n format: \"array\"\n path: \"deployment.env\"\n chart:\n name: \"./charts/web-app\"\n docker:\n app:\n dockerfilePath: \"frontend/Dockerfile\"\n env:\n REACT_APP_API_URL: \"https://api.example.com\"\n REACT_APP_VERSION: \"{{build.uuid}}\"\n\n # Service using map format (common for custom charts)\n - name: backend\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/apps\"\n branchName: \"main\"\n envMapping:\n app:\n format: \"map\"\n path: \"config.environment\"\n chart:\n name: \"./charts/api\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/backend.dockerfile\"\n ports:\n - 3000\n env:\n NODE_ENV: \"production\"\n SERVICE_NAME: \"backend\"\n\n - name: \"mysql-database\"\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/api-services\"\n branchName: \"main\"\n chart:\n name: \"mysql\" # Using public Helm chart\n version: \"9.14.1\"\n repoUrl: \"https://charts.bitnami.com/bitnami\"\n valueFiles:\n - \"deploy/helm/mysql-values.yaml\"\n```\n\n## Templated Variables\n\nLifecycle supports template variables in Helm values that are resolved at deployment time. These variables allow you to reference dynamic values like build UUIDs, docker tags, and internal hostnames.\n\n### Available Variables\n\nTemplate variables use the format `{{{variableName}}}` and are replaced with actual values during deployment:\n\n| Variable | Description | Example Value |\n| ------------------------------------ | ------------------------- | ---------------------------------------- |\n| `{{{serviceName_dockerTag}}}` | Docker tag for a service | `main-abc123` |\n| `{{{serviceName_dockerImage}}}` | Full docker image path | `registry.com/org/repo:main-abc123` |\n| `{{{serviceName_internalHostname}}}` | Internal service hostname | `api-service.env-uuid.svc.cluster.local` |\n| `{{{build.uuid}}}` | Build UUID | `env-12345` |\n| `{{{build.namespace}}}` | Kubernetes namespace | `env-12345` |\n\n### Usage in Values\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: web-api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: \"./charts/app\"\n values:\n - \"image.tag={{{web-api_dockerTag}}}\"\n - \"backend.url=http://{{{backend-service_internalHostname}}}:8080\"\n - \"env.BUILD_ID={{{build.uuid}}}\"\n```\n\n\n**Docker Image Mapping**: When using custom charts, you'll need to map `{{{serviceName_dockerImage}}}` or `{{{serviceName_dockerTag}}}` to your chart's expected value path. Common patterns include:\n- `image.repository` and `image.tag` (most common)\n- `deployment.image` (single image string)\n- `app.image` or `application.image`\n- Custom paths specific to your chart\n\nCheck your chart's `values.yaml` to determine the correct path.\n\n\n\n#### Image Mapping Examples\n\n```yaml filename=\"lifecycle.yaml\"\n# Example 1: Separate repository and tag (most common)\nservices:\n - name: web-api\n helm:\n chart:\n name: \"./charts/standard\"\n values:\n - \"image.repository=registry.com/org/web-api\" # Static repository\n - \"image.tag={{{web-api_dockerTag}}}\" # Dynamic tag only\n\n# Example 2: Combined image string\nservices:\n - name: worker\n helm:\n chart:\n name: \"./charts/custom\"\n values:\n - \"deployment.image={{{worker_dockerImage}}}\" # Full image with tag\n\n# Example 3: Nested structure\nservices:\n - name: backend\n helm:\n chart:\n name: \"./charts/microservice\"\n values:\n - \"app.container.image={{{backend_dockerImage}}}\" # Full image with tag\n```\n\n\n**Important**: Always use triple braces `{{{variable}}}` instead of double braces `{{variable}}` for Lifecycle template variables. This prevents Helm from trying to process them as Helm template functions and ensures they are passed through correctly for Lifecycle to resolve.\n\n\n### Template Resolution Order\n\n1. Lifecycle resolves `{{{variables}}}` before passing values to Helm\n2. The resolved values are then passed to Helm using `--set` flags\n3. Helm processes its own template functions (if any) after receiving the resolved values\n\n### Example with Service Dependencies\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: api-gateway\n helm:\n chart:\n name: \"./charts/gateway\"\n values:\n - \"config.authServiceUrl=http://{{{auth-service_internalHostname}}}:3000\"\n - \"config.userServiceUrl=http://{{{user-service_internalHostname}}}:3000\"\n - \"image.tag={{{api-gateway_dockerTag}}}\"\n\n - name: auth-service\n helm:\n chart:\n name: \"./charts/microservice\"\n values:\n - \"image.tag={{{auth-service_dockerTag}}}\"\n - \"database.host={{{postgres-db_internalHostname}}}\"\n```\n\n## Deployment Process\n\n\n 1. **Job Creation**: A Kubernetes job is created in the ephemeral namespace 2.\n **RBAC Setup**: Service account with namespace-scoped permissions is created\n 3. **Git Clone**: Init container clones the repository 4. **Helm Deploy**:\n Main container executes the Helm deployment 5. **Monitoring**: Logs are\n streamed in real-time via WebSocket\n\n\n### Concurrent Deployment Handling\n\nNative Helm automatically handles concurrent deployments by:\n\n- Detecting existing deployment jobs\n- Force-deleting the old job\n- Starting the new deployment\n\nThis ensures the newest deployment always takes precedence.\n\n## Monitoring Deployments\n\n### Deploy Logs Access\n\nFor services using native Helm deployment, you can access deployment logs through the Lifecycle PR comment:\n\n1. Add the `lifecycle-status-comments!` label to your PR\n2. In the status comment that appears, you'll see a **Deploy Logs** link for each service using native Helm\n3. Click the link to view real-time deployment logs\n\n### Log Contents\n\nThe deployment logs show:\n\n- Git repository cloning progress (`clone-repo` container)\n- Helm deployment execution (`helm-deploy` container)\n- Real-time streaming of all deployment output\n- Success or failure status\n\n## Chart Types\n\nLifecycle automatically detects and handles three chart types:\n\n| Type | Detection | Features |\n| ------------- | -------------------------------------------- | ---------------------------------------------- |\n| **ORG_CHART** | Matches `orgChartName` AND has `helm.docker` | Docker image injection, env var transformation |\n| **LOCAL** | Name is \"local\" or starts with \"./\" or \"../\" | Flexible `envMapping` support |\n| **PUBLIC** | Everything else | Standard labels and tolerations |\n\n\n The `orgChartName` is configured in the database's `global_config` table with\n key `orgChart`. This allows organizations to define their standard internal\n Helm chart.\n\n\n## Troubleshooting\n\n### Deployment Fails with \"Another Operation in Progress\"\n\n**Symptom**: Helm reports an existing operation is blocking deployment\n\n**Solution**: Native Helm automatically handles this by killing existing jobs. If the issue persists:\n\n```bash\n# Check for stuck jobs\nkubectl get jobs -n env-{uuid} -l service={serviceName}\n\n# Force delete if needed\nkubectl delete job {jobName} -n env-{uuid} --force --grace-period=0\n```\n\n### Environment Variables Not Working\n\n**Symptom**: Environment variables not passed to the deployment\n\n**Common Issues**:\n\n1. `envMapping` placed under `chart` instead of directly under `helm`\n2. Incorrect format specification (array vs map)\n3. Missing path configuration\n\n**Correct Configuration**:\n\n```yaml {4-7}\nhelm:\n deploymentMethod: \"native\"\n chart:\n name: local\n envMapping: # Correct: directly under helm\n app:\n format: \"array\"\n path: \"env\"\n```\n\n## Migration Example\n\nHere's a complete example showing how to migrate from GitHub-type services to Helm-type services:\n\n### Before: GitHub-type Services\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: \"api-gateway\"\n github:\n repository: \"myorg/api-services\"\n branchName: \"main\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/api.dockerfile\"\n env:\n BACKEND_URL: \"{{backend-service_internalHostname}}:3000\"\n LOG_LEVEL: \"info\"\n ENV_NAME: \"production\"\n ports:\n - 8080\n deployment:\n public: true\n resource:\n cpu:\n request: \"100m\"\n memory:\n request: \"256Mi\"\n readiness:\n tcpSocketPort: 8080\n hostnames:\n host: \"example.com\"\n defaultInternalHostname: \"api-gateway-prod\"\n defaultPublicUrl: \"api.example.com\"\n\n - name: \"backend-service\"\n github:\n repository: \"myorg/api-services\"\n branchName: \"main\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/backend.dockerfile\"\n ports:\n - 3000\n env:\n NODE_ENV: \"production\"\n SERVICE_NAME: \"backend\"\n deployment:\n public: false\n resource:\n cpu:\n request: \"50m\"\n memory:\n request: \"128Mi\"\n readiness:\n tcpSocketPort: 3000\n\n - name: \"mysql-database\"\n docker:\n dockerImage: \"mysql\"\n defaultTag: \"8.0-debian\"\n ports:\n - 3306\n env:\n MYSQL_ROOT_PASSWORD: \"strongpassword123\"\n MYSQL_DATABASE: \"app_database\"\n MYSQL_USER: \"app_user\"\n MYSQL_PASSWORD: \"apppassword456\"\n deployment:\n public: false\n resource:\n cpu:\n request: \"100m\"\n memory:\n request: \"512Mi\"\n readiness:\n tcpSocketPort: 3306\n serviceDisks:\n - name: \"mysql-data\"\n mountPath: \"/var/lib/mysql\"\n accessModes: \"ReadWriteOnce\"\n storageSize: \"10Gi\"\n```\n\n### After: Helm-type Services with Native Deployment\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: \"api-gateway\"\n helm:\n deploymentMethod: \"native\" # Enable native Helm\n version: \"3.14.0\"\n repository: \"myorg/api-services\"\n branchName: \"main\"\n args: \"--wait --timeout 10m\"\n envMapping:\n app:\n format: \"array\"\n path: \"containers.api.env\"\n chart:\n name: \"./charts/microservices\"\n values:\n - 'image.tag=\"{{{api-gateway_dockerTag}}}\"'\n - \"service.type=LoadBalancer\"\n - \"ingress.enabled=true\"\n valueFiles:\n - \"deploy/helm/base-values.yaml\"\n - \"deploy/helm/api-gateway-values.yaml\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/api.dockerfile\"\n env:\n BACKEND_URL: \"{{backend-service_internalHostname}}:3000\"\n LOG_LEVEL: \"info\"\n ENV_NAME: \"production\"\n ports:\n - 8080\n\n - name: \"backend-service\"\n helm:\n deploymentMethod: \"native\"\n version: \"3.14.0\"\n repository: \"myorg/api-services\"\n branchName: \"main\"\n envMapping:\n app:\n format: \"map\" # Using map format for this service\n path: \"env\"\n chart:\n name: \"./charts/microservices\"\n values:\n - 'image.tag=\"{{{backend-service_dockerTag}}}\"'\n - \"replicaCount=2\"\n valueFiles:\n - \"deploy/helm/base-values.yaml\"\n - \"deploy/helm/backend-values.yaml\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/backend.dockerfile\"\n ports:\n - 3000\n env:\n NODE_ENV: \"production\"\n SERVICE_NAME: \"backend\"\n\n - name: \"mysql-database\"\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/api-services\"\n branchName: \"main\"\n chart:\n name: \"mysql\" # Using public Helm chart\n version: \"9.14.1\"\n repoUrl: \"https://charts.bitnami.com/bitnami\"\n valueFiles:\n - \"deploy/helm/mysql-values.yaml\"\n```\n\n### Key Migration Points\n\n1. **Service Type Change**: Changed from `github:` to `helm:` configuration\n2. **Repository Location**: `repository` and `branchName` move from under `github:` to directly under `helm:`\n3. **Deployment Method**: Added `deploymentMethod: \"native\"` to enable native Helm\n4. **Chart Configuration**: Added `chart:` section with local or public charts\n5. **Environment Mapping**: Added `envMapping:` to control how environment variables are passed\n6. **Helm Arguments**: Added `args:` for Helm command customization\n7. **Docker Configuration**: Kept existing `docker:` config for build process\n\n\n Note that when converting from GitHub-type to Helm-type services, the\n `repository` and `branchName` fields move from being nested under `github:` to\n being directly under `helm:`.\n\n\n\n Many configuration options (like Helm version, args, and chart details) can be\n defined in the `global_config` database table, making the service YAML\n cleaner. Only override when needed." + "body": "This feature is still in alpha and might change with breaking changes.\n\n\n**Native Helm** is an alternative deployment method that runs Helm deployments directly within Kubernetes jobs, eliminating the need for external CI/CD systems. This provides a more self-contained and portable deployment solution.\n\n\n Native Helm deployment is an opt-in feature that can be enabled globally or\n per-service.\n\n\n## Overview\n\nWhen enabled, Native Helm:\n\n- Creates Kubernetes jobs to execute Helm deployments\n- Runs in ephemeral namespaces with proper RBAC\n- Provides real-time deployment logs via WebSocket\n- Handles concurrent deployments automatically\n- Supports all standard Helm chart types\n\n## Quickstart\n\nWant to try native Helm deployment? Here's the fastest way to get started:\n\n```yaml filename=\"lifecycle.yaml\" {5}\nservices:\n - name: my-api\n defaultUUID: \"dev-0\"\n helm:\n deploymentMethod: \"native\" # That's it!\n chart:\n name: \"local\"\n valueFiles:\n - \"./helm/values.yaml\"\n```\n\nThis configuration:\n\n1. Enables native Helm for the `my-api` service\n2. Uses a local Helm chart from your repository\n3. Applies values from `./helm/values.yaml`\n4. Runs deployment as a Kubernetes job\n\n\n To enable native Helm for all services at once, see [Global\n Configuration](#enabling-native-helm).\n\n\n## Configuration\n\n### Enabling Native Helm\n\nThere are two ways to enable native Helm deployment:\n\n#### Per Service Configuration\n\nEnable native Helm for individual services:\n\n```yaml {4} filename=\"lifecycle.yaml\"\nservices:\n - name: my-service\n helm:\n deploymentMethod: \"native\" # Enable for this service only\n chart:\n name: my-chart\n```\n\n#### Global Configuration\n\nEnable native Helm for all services:\n\n```yaml {3} filename=\"lifecycle.yaml\"\nhelm:\n nativeHelm:\n enabled: true # Enable for all services\n```\n\n### Configuration Precedence\n\nLifecycle uses a hierarchical configuration system with three levels of precedence:\n\n1. **helmDefaults** - Base defaults for all deployments (database: `global_config` table)\n2. **Chart-specific config** - Per-chart defaults (database: `global_config` table)\n3. **Service YAML config** - Service-specific overrides (highest priority)\n\n\n Service-level configuration always takes precedence over global defaults.\n\n\n### Global Configuration (Database)\n\nGlobal configurations are stored in the `global_config` table in the database. Each configuration is stored as a row with:\n\n- **key**: The configuration name (e.g., 'helmDefaults', 'postgresql', 'redis')\n- **config**: JSON object containing the configuration\n\n#### helmDefaults Configuration\n\nStored in database with key `helmDefaults`:\n\n```json\n{\n \"nativeHelm\": {\n \"enabled\": true,\n \"defaultArgs\": \"--wait --timeout 30m\",\n \"defaultHelmVersion\": \"3.12.0\"\n }\n}\n```\n\n**Field Descriptions**:\n\n- `enabled`: When `true`, enables native Helm deployment for all services unless they explicitly set `deploymentMethod: \"ci\"`\n- `defaultArgs`: Arguments automatically appended to every Helm command (appears before service-specific args)\n- `defaultHelmVersion`: The Helm version to use when not specified at the service or chart level\n\n#### Chart-specific Configuration\n\nExample: PostgreSQL configuration stored with key `postgresql`:\n\n```json\n{\n \"version\": \"3.13.0\",\n \"args\": \"--force --timeout 60m0s --wait\",\n \"chart\": {\n \"name\": \"postgresql\",\n \"repoUrl\": \"https://charts.bitnami.com/bitnami\",\n \"version\": \"12.9.0\",\n \"values\": [\"auth.username=postgres_user\", \"auth.database=postgres_db\"]\n }\n}\n```\n\n\n These global configurations are managed by administrators and stored in the\n database. They provide consistent defaults across all environments and can be\n overridden at the service level.\n\n\n## Usage Examples\n\n### Quick Experiment: Deploy Jenkins!\n\nWant to see native Helm in action? Let's deploy everyone's favorite CI/CD tool - Jenkins! This example shows how easy it is to deploy popular applications using native Helm.\n\n```yaml filename=\"lifecycle.yaml\"\nenvironment:\n defaultServices:\n - name: \"my-app\"\n - name: \"jenkins\" # Add Jenkins to your default services\n\nservices:\n - name: \"jenkins\"\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/apps\"\n branchName: \"main\"\n chart:\n name: \"jenkins\"\n repoUrl: \"https://charts.bitnami.com/bitnami\"\n version: \"13.6.8\"\n values:\n - \"service.type=ClusterIP\"\n - \"ingress.enabled=true\"\n - \"ingress.hostname={{jenkins_publicUrl}}\"\n - \"ingress.ingressClassName=nginx\"\n```\n\n\n 🎉 That's it! With just a few lines of configuration, you'll have Jenkins\n running in your Kubernetes cluster.\n\n\nTo access your Jenkins instance:\n\n1. Check the deployment status in your PR comment\n2. Click the **Deploy Logs** link to monitor the deployment\n3. Once deployed, Jenkins will be available at the internal hostname\n\n\n For more Jenkins configuration options and values, check out the [Bitnami\n Jenkins chart\n documentation](https://github.com/bitnami/charts/tree/main/bitnami/jenkins).\n This same pattern works for any Bitnami chart (PostgreSQL, Redis, MongoDB) or\n any other public Helm chart!\n\n\n### Basic Service Deployment\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: web-api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: web-app\n version: \"1.2.0\"\n```\n\n### PostgreSQL with Overrides\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: database\n helm:\n deploymentMethod: \"native\"\n version: \"3.14.0\" # Override Helm version\n args: \"--atomic\" # Override deployment args\n chart:\n name: postgresql\n values: # Additional values merged with defaults\n - \"persistence.size=20Gi\"\n - \"replicaCount=2\"\n```\n\n### Custom Environment Variables\n\nLifecycle supports flexible environment variable formatting through the `envMapping` configuration. This feature allows you to control how environment variables from your service configuration are passed to your Helm chart.\n\n\n **Why envMapping?** Different Helm charts expect environment variables in\n different formats. Some expect an array of objects with `name` and `value`\n fields (Kubernetes standard), while others expect a simple key-value map. The\n `envMapping` feature lets you adapt to your chart's requirements.\n\n\n#### Default envMapping Configuration\n\nYou can define default `envMapping` configurations in the `global_config` database table. These defaults apply to all services using that chart unless overridden at the service level.\n\n**Example: Setting defaults for your organization's chart**\n\n```json\n// In global_config table, key: \"myorg-web-app\"\n{\n \"chart\": {\n \"name\": \"myorg-web-app\",\n \"repoUrl\": \"https://charts.myorg.com\"\n },\n \"envMapping\": {\n \"app\": {\n \"format\": \"array\",\n \"path\": \"deployment.containers[0].env\"\n }\n }\n}\n```\n\nWith this configuration, any service using the `myorg-web-app` chart will automatically use array format for environment variables:\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: \"myorg-web-app\" # Inherits envMapping from global_config\n docker:\n app:\n env:\n API_KEY: \"secret\"\n # These will be formatted as array automatically\n```\n\n\n Setting `envMapping` in global_config is particularly useful when: - You have\n a standard organizational chart used by many services - You want consistent\n environment variable handling across services - You're migrating multiple\n services and want to reduce configuration duplication\n\n\n#### Array Format\n\nBest for charts that expect Kubernetes-style env arrays.\n\n```yaml {7-9} filename=\"lifecycle.yaml\"\nservices:\n - name: api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: local\n envMapping:\n app:\n format: \"array\"\n path: \"env\"\n docker:\n app:\n env:\n DATABASE_URL: \"postgres://localhost:5432/mydb\"\n API_KEY: \"secret-key-123\"\n NODE_ENV: \"production\"\n```\n\n**This produces the following Helm values:**\n\n```bash\n--set env[0].name=DATABASE_URL\n--set env[0].value=postgres://localhost:5432/mydb\n--set env[1].name=API_KEY\n--set env[1].value=secret-key-123\n--set env[2].name=NODE_ENV\n--set env[2].value=production\n```\n\n**Your chart's values.yaml would use it like:**\n\n```yaml\nenv:\n - name: DATABASE_URL\n value: postgres://localhost:5432/mydb\n - name: API_KEY\n value: secret-key-123\n - name: NODE_ENV\n value: production\n```\n\n#### Map Format\n\nBest for charts that expect a simple key-value object.\n\n```yaml {7-9} filename=\"lifecycle.yaml\"\nservices:\n - name: api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: local\n envMapping:\n app:\n format: \"map\"\n path: \"envVars\"\n docker:\n app:\n env:\n DATABASE_URL: \"postgres://localhost:5432/mydb\"\n API_KEY: \"secret-key-123\"\n NODE_ENV: \"production\"\n```\n\n**This produces the following Helm values:**\n\n```bash\n--set envVars.DATABASE__URL=postgres://localhost:5432/mydb\n--set envVars.API__KEY=secret-key-123\n--set envVars.NODE__ENV=production\n```\n\n\n Note: Underscores in environment variable names are converted to double\n underscores (`__`) in map format to avoid Helm parsing issues.\n\n\n**Your chart's values.yaml would use it like:**\n\n```yaml\nenvVars:\n DATABASE__URL: postgres://localhost:5432/mydb\n API__KEY: secret-key-123\n NODE__ENV: production\n```\n\n#### Complete Example with Multiple Services\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n # Service using array format (common for standard Kubernetes deployments)\n - name: frontend\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/apps\"\n branchName: \"main\"\n envMapping:\n app:\n format: \"array\"\n path: \"deployment.env\"\n chart:\n name: \"./charts/web-app\"\n docker:\n app:\n dockerfilePath: \"frontend/Dockerfile\"\n env:\n REACT_APP_API_URL: \"https://api.example.com\"\n REACT_APP_VERSION: \"{{build.uuid}}\"\n\n # Service using map format (common for custom charts)\n - name: backend\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/apps\"\n branchName: \"main\"\n envMapping:\n app:\n format: \"map\"\n path: \"config.environment\"\n chart:\n name: \"./charts/api\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/backend.dockerfile\"\n ports:\n - 3000\n env:\n NODE_ENV: \"production\"\n SERVICE_NAME: \"backend\"\n\n - name: \"mysql-database\"\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/api-services\"\n branchName: \"main\"\n chart:\n name: \"mysql\" # Using public Helm chart\n version: \"9.14.1\"\n repoUrl: \"https://charts.bitnami.com/bitnami\"\n valueFiles:\n - \"deploy/helm/mysql-values.yaml\"\n```\n\n## Templated Variables\n\nLifecycle supports template variables in Helm values that are resolved at deployment time. These variables allow you to reference dynamic values like build UUIDs, docker tags, and internal hostnames.\n\n### Available Variables\n\nTemplate variables use the format `{{{variableName}}}` and are replaced with actual values during deployment:\n\n| Variable | Description | Example Value |\n| ------------------------------------ | ------------------------- | ---------------------------------------- |\n| `{{{serviceName_dockerTag}}}` | Docker tag for a service | `main-abc123` |\n| `{{{serviceName_dockerImage}}}` | Full docker image path | `registry.com/org/repo:main-abc123` |\n| `{{{serviceName_internalHostname}}}` | Internal service hostname | `api-service.env-uuid.svc.cluster.local` |\n| `{{{build.uuid}}}` | Build UUID | `env-12345` |\n| `{{{build.namespace}}}` | Kubernetes namespace | `env-12345` |\n\n### Usage in Values\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: web-api\n helm:\n deploymentMethod: \"native\"\n chart:\n name: \"./charts/app\"\n values:\n - \"image.tag={{{web-api_dockerTag}}}\"\n - \"backend.url=http://{{{backend-service_internalHostname}}}:8080\"\n - \"env.BUILD_ID={{{build.uuid}}}\"\n```\n\n\n**Docker Image Mapping**: When using custom charts, you'll need to map `{{{serviceName_dockerImage}}}` or `{{{serviceName_dockerTag}}}` to your chart's expected value path. Common patterns include:\n- `image.repository` and `image.tag` (most common)\n- `deployment.image` (single image string)\n- `app.image` or `application.image`\n- Custom paths specific to your chart\n\nCheck your chart's `values.yaml` to determine the correct path.\n\n\n\n#### Image Mapping Examples\n\n```yaml filename=\"lifecycle.yaml\"\n# Example 1: Separate repository and tag (most common)\nservices:\n - name: web-api\n helm:\n chart:\n name: \"./charts/standard\"\n values:\n - \"image.repository=registry.com/org/web-api\" # Static repository\n - \"image.tag={{{web-api_dockerTag}}}\" # Dynamic tag only\n\n# Example 2: Combined image string\nservices:\n - name: worker\n helm:\n chart:\n name: \"./charts/custom\"\n values:\n - \"deployment.image={{{worker_dockerImage}}}\" # Full image with tag\n\n# Example 3: Nested structure\nservices:\n - name: backend\n helm:\n chart:\n name: \"./charts/microservice\"\n values:\n - \"app.container.image={{{backend_dockerImage}}}\" # Full image with tag\n```\n\n\n**Important**: Always use triple braces `{{{variable}}}` instead of double braces `{{variable}}` for Lifecycle template variables. This prevents Helm from trying to process them as Helm template functions and ensures they are passed through correctly for Lifecycle to resolve.\n\n\n### Template Resolution Order\n\n1. Lifecycle resolves `{{{variables}}}` before passing values to Helm\n2. The resolved values are then passed to Helm using `--set` flags\n3. Helm processes its own template functions (if any) after receiving the resolved values\n\n### Example with Service Dependencies\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: api-gateway\n helm:\n chart:\n name: \"./charts/gateway\"\n values:\n - \"config.authServiceUrl=http://{{{auth-service_internalHostname}}}:3000\"\n - \"config.userServiceUrl=http://{{{user-service_internalHostname}}}:3000\"\n - \"image.tag={{{api-gateway_dockerTag}}}\"\n\n - name: auth-service\n helm:\n chart:\n name: \"./charts/microservice\"\n values:\n - \"image.tag={{{auth-service_dockerTag}}}\"\n - \"database.host={{{postgres-db_internalHostname}}}\"\n```\n\n## Deployment Process\n\n\n 1. **Job Creation**: A Kubernetes job is created in the ephemeral namespace 2.\n **RBAC Setup**: Service account with namespace-scoped permissions is created\n 3. **Git Clone**: Init container clones the repository 4. **Helm Deploy**:\n Main container executes the Helm deployment 5. **Monitoring**: Logs are\n streamed in real-time via WebSocket\n\n\n### Concurrent Deployment Handling\n\nNative Helm automatically handles concurrent deployments by:\n\n- Detecting existing deployment jobs\n- Force-deleting the old job\n- Starting the new deployment\n\nThis ensures the newest deployment always takes precedence.\n\n## Monitoring Deployments\n\n### Deploy Logs Access\n\nFor services using native Helm deployment, you can access deployment logs through the Lifecycle PR comment:\n\n1. Add the `lifecycle-status-comments!` label to your PR\n2. In the status comment that appears, you'll see a **Deploy Logs** link for each service using native Helm\n3. Click the link to view real-time deployment logs\n\n### Log Contents\n\nThe deployment logs show:\n\n- Git repository cloning progress (`clone-repo` container)\n- Helm deployment execution (`helm-deploy` container)\n- Real-time streaming of all deployment output\n- Success or failure status\n\n## Chart Types\n\nLifecycle automatically detects and handles three chart types:\n\n| Type | Detection | Features |\n| ------------- | -------------------------------------------- | ---------------------------------------------- |\n| **ORG_CHART** | Matches `orgChartName` AND has `helm.docker` | Docker image injection, env var transformation |\n| **LOCAL** | Name is \"local\" or starts with \"./\" or \"../\" | Flexible `envMapping` support |\n| **PUBLIC** | Everything else | Standard labels and tolerations |\n\n\n The `orgChartName` is configured in the database's `global_config` table with\n key `orgChart`. This allows organizations to define their standard internal\n Helm chart.\n\n\n## Troubleshooting\n\n### Deployment Fails with \"Another Operation in Progress\"\n\n**Symptom**: Helm reports an existing operation is blocking deployment\n\n**Solution**: Native Helm automatically handles this by killing existing jobs. If the issue persists:\n\n```bash\n# Check for stuck jobs\nkubectl get jobs -n env-{uuid} -l service={serviceName}\n\n# Force delete if needed\nkubectl delete job {jobName} -n env-{uuid} --force --grace-period=0\n```\n\n### Environment Variables Not Working\n\n**Symptom**: Environment variables not passed to the deployment\n\n**Common Issues**:\n\n1. `envMapping` placed under `chart` instead of directly under `helm`\n2. Incorrect format specification (array vs map)\n3. Missing path configuration\n\n**Correct Configuration**:\n\n```yaml {4-7}\nhelm:\n deploymentMethod: \"native\"\n chart:\n name: local\n envMapping: # Correct: directly under helm\n app:\n format: \"array\"\n path: \"env\"\n```\n\n## Migration Example\n\nHere's a complete example showing how to migrate from GitHub-type services to Helm-type services:\n\n### Before: GitHub-type Services\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: \"api-gateway\"\n github:\n repository: \"myorg/api-services\"\n branchName: \"main\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/api.dockerfile\"\n env:\n BACKEND_URL: \"{{backend-service_internalHostname}}:3000\"\n LOG_LEVEL: \"info\"\n ENV_NAME: \"production\"\n ports:\n - 8080\n deployment:\n public: true\n resource:\n cpu:\n request: \"100m\"\n memory:\n request: \"256Mi\"\n readiness:\n tcpSocketPort: 8080\n hostnames:\n host: \"example.com\"\n defaultInternalHostname: \"api-gateway-prod\"\n defaultPublicUrl: \"api.example.com\"\n\n - name: \"backend-service\"\n github:\n repository: \"myorg/api-services\"\n branchName: \"main\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/backend.dockerfile\"\n ports:\n - 3000\n env:\n NODE_ENV: \"production\"\n SERVICE_NAME: \"backend\"\n deployment:\n public: false\n resource:\n cpu:\n request: \"50m\"\n memory:\n request: \"128Mi\"\n readiness:\n tcpSocketPort: 3000\n\n - name: \"mysql-database\"\n docker:\n dockerImage: \"mysql\"\n defaultTag: \"8.0-debian\"\n ports:\n - 3306\n env:\n MYSQL_ROOT_PASSWORD: \"strongpassword123\"\n MYSQL_DATABASE: \"app_database\"\n MYSQL_USER: \"app_user\"\n MYSQL_PASSWORD: \"apppassword456\"\n deployment:\n public: false\n resource:\n cpu:\n request: \"100m\"\n memory:\n request: \"512Mi\"\n readiness:\n tcpSocketPort: 3306\n serviceDisks:\n - name: \"mysql-data\"\n mountPath: \"/var/lib/mysql\"\n accessModes: \"ReadWriteOnce\"\n storageSize: \"10Gi\"\n```\n\n### After: Helm-type Services with Native Deployment\n\n```yaml filename=\"lifecycle.yaml\"\nservices:\n - name: \"api-gateway\"\n helm:\n deploymentMethod: \"native\" # Enable native Helm\n version: \"3.14.0\"\n repository: \"myorg/api-services\"\n branchName: \"main\"\n args: \"--wait --timeout 10m\"\n envMapping:\n app:\n format: \"array\"\n path: \"containers.api.env\"\n chart:\n name: \"./charts/microservices\"\n values:\n - 'image.tag=\"{{{api-gateway_dockerTag}}}\"'\n - \"service.type=LoadBalancer\"\n - \"ingress.enabled=true\"\n valueFiles:\n - \"deploy/helm/base-values.yaml\"\n - \"deploy/helm/api-gateway-values.yaml\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/api.dockerfile\"\n env:\n BACKEND_URL: \"{{backend-service_internalHostname}}:3000\"\n LOG_LEVEL: \"info\"\n ENV_NAME: \"production\"\n ports:\n - 8080\n\n - name: \"backend-service\"\n helm:\n deploymentMethod: \"native\"\n version: \"3.14.0\"\n repository: \"myorg/api-services\"\n branchName: \"main\"\n envMapping:\n app:\n format: \"map\" # Using map format for this service\n path: \"env\"\n chart:\n name: \"./charts/microservices\"\n values:\n - 'image.tag=\"{{{backend-service_dockerTag}}}\"'\n - \"replicaCount=2\"\n valueFiles:\n - \"deploy/helm/base-values.yaml\"\n - \"deploy/helm/backend-values.yaml\"\n docker:\n builder:\n engine: \"buildkit\"\n defaultTag: \"main\"\n app:\n dockerfilePath: \"docker/backend.dockerfile\"\n ports:\n - 3000\n env:\n NODE_ENV: \"production\"\n SERVICE_NAME: \"backend\"\n\n - name: \"mysql-database\"\n helm:\n deploymentMethod: \"native\"\n repository: \"myorg/api-services\"\n branchName: \"main\"\n chart:\n name: \"mysql\" # Using public Helm chart\n version: \"9.14.1\"\n repoUrl: \"https://charts.bitnami.com/bitnami\"\n valueFiles:\n - \"deploy/helm/mysql-values.yaml\"\n```\n\n### Key Migration Points\n\n1. **Service Type Change**: Changed from `github:` to `helm:` configuration\n2. **Repository Location**: `repository` and `branchName` move from under `github:` to directly under `helm:`\n3. **Deployment Method**: Added `deploymentMethod: \"native\"` to enable native Helm\n4. **Chart Configuration**: Added `chart:` section with local or public charts\n5. **Environment Mapping**: Added `envMapping:` to control how environment variables are passed\n6. **Helm Arguments**: Added `args:` for Helm command customization\n7. **Docker Configuration**: Kept existing `docker:` config for build process\n\n\n Note that when converting from GitHub-type to Helm-type services, the\n `repository` and `branchName` fields move from being nested under `github:` to\n being directly under `helm:`.\n\n\n\n Many configuration options (like Helm version, args, and chart details) can be\n defined in the `global_config` database table, making the service YAML\n cleaner. Only override when needed." }, { "title": "Template Variables", diff --git a/src/lib/static/blogcontent/blogcontent.ts b/src/lib/static/blogcontent/blogcontent.ts index f37454c..58ba54b 100644 --- a/src/lib/static/blogcontent/blogcontent.ts +++ b/src/lib/static/blogcontent/blogcontent.ts @@ -35,7 +35,7 @@ export const blogContent = [ "Deploy services using Helm directly in Kubernetes without external CI/CD dependencies", date: "2025-01-29", path: "docs/features/native-helm-deployment", - body: 'This feature is still in alpha and might change with breaking changes.\n\n\n**Native Helm** is an alternative deployment method that runs Helm deployments directly within Kubernetes jobs, eliminating the need for external CI/CD systems. This provides a more self-contained and portable deployment solution.\n\n\n Native Helm deployment is an opt-in feature that can be enabled globally or\n per-service.\n\n\n## Overview\n\nWhen enabled, Native Helm:\n\n- Creates Kubernetes jobs to execute Helm deployments\n- Runs in ephemeral namespaces with proper RBAC\n- Provides real-time deployment logs via WebSocket\n- Handles concurrent deployments automatically\n- Supports all standard Helm chart types\n\n## Quickstart\n\nWant to try native Helm deployment? Here\'s the fastest way to get started:\n\n```yaml filename="lifecycle.yaml" {5}\nservices:\n - name: my-api\n defaultUUID: "dev-0"\n helm:\n deploymentMethod: "native" # That\'s it!\n chart:\n name: "local"\n valueFiles:\n - "./helm/values.yaml"\n```\n\nThis configuration:\n\n1. Enables native Helm for the `my-api` service\n2. Uses a local Helm chart from your repository\n3. Applies values from `./helm/values.yaml`\n4. Runs deployment as a Kubernetes job\n\n\n To enable native Helm for all services at once, see [Global\n Configuration](#enabling-native-helm).\n\n\n## Configuration\n\n### Enabling Native Helm\n\nThere are two ways to enable native Helm deployment:\n\n#### Per Service Configuration\n\nEnable native Helm for individual services:\n\n```yaml {4} filename="lifecycle.yaml"\nservices:\n - name: my-service\n helm:\n deploymentMethod: "native" # Enable for this service only\n chart:\n name: my-chart\n```\n\n#### Global Configuration\n\nEnable native Helm for all services:\n\n```yaml {3} filename="lifecycle.yaml"\nhelm:\n nativeHelm:\n enabled: true # Enable for all services\n```\n\n### Configuration Precedence\n\nLifecycle uses a hierarchical configuration system with three levels of precedence:\n\n1. **helmDefaults** - Base defaults for all deployments (database: `global_config` table)\n2. **Chart-specific config** - Per-chart defaults (database: `global_config` table)\n3. **Service YAML config** - Service-specific overrides (highest priority)\n\n\n Service-level configuration always takes precedence over global defaults.\n\n\n### Global Configuration (Database)\n\nGlobal configurations are stored in the `global_config` table in the database. Each configuration is stored as a row with:\n\n- **key**: The configuration name (e.g., \'helmDefaults\', \'postgresql\', \'redis\')\n- **config**: JSON object containing the configuration\n\n#### helmDefaults Configuration\n\nStored in database with key `helmDefaults`:\n\n```json\n{\n "nativeHelm": {\n "enabled": true,\n "defaultArgs": "--wait --timeout 30m",\n "defaultHelmVersion": "3.12.0"\n }\n}\n```\n\n**Field Descriptions**:\n\n- `enabled`: When `true`, enables native Helm deployment for all services unless they explicitly set `deploymentMethod: "ci"`\n- `defaultArgs`: Arguments automatically appended to every Helm command (appears before service-specific args)\n- `defaultHelmVersion`: The Helm version to use when not specified at the service or chart level\n\n#### Chart-specific Configuration\n\nExample: PostgreSQL configuration stored with key `postgresql`:\n\n```json\n{\n "version": "3.13.0",\n "args": "--force --timeout 60m0s --wait",\n "chart": {\n "name": "postgresql",\n "repoUrl": "https://charts.bitnami.com/bitnami",\n "version": "12.9.0",\n "values": ["auth.username=postgres_user", "auth.database=postgres_db"]\n }\n}\n```\n\n\n These global configurations are managed by administrators and stored in the\n database. They provide consistent defaults across all environments and can be\n overridden at the service level.\n\n\n## Usage Examples\n\n### Quick Experiment: Deploy Jenkins!\n\nWant to see native Helm in action? Let\'s deploy everyone\'s favorite CI/CD tool - Jenkins! This example shows how easy it is to deploy popular applications using native Helm.\n\n```yaml filename="lifecycle.yaml"\nenvironment:\n defaultServices:\n - name: "my-app"\n - name: "jenkins" # Add Jenkins to your default services\n\nservices:\n - name: "jenkins"\n helm:\n deploymentMethod: "native"\n repository: "myorg/apps"\n branchName: "main"\n chart:\n name: "jenkins"\n repoUrl: "https://charts.bitnami.com/bitnami"\n version: "13.6.8"\n values:\n - "service.type=NodePort" # Override default LoadBalancer to avoid cloud resource costs\n```\n\n\n 🎉 That\'s it! With just a few lines of configuration, you\'ll have Jenkins\n running in your Kubernetes cluster. The `service.type=NodePort` override is\n important - the Bitnami Jenkins chart defaults to `LoadBalancer` which would\n consume cloud resources like an AWS ELB or GCP Load Balancer.\n\n\nTo access your Jenkins instance:\n\n1. Check the deployment status in your PR comment\n2. Click the **Deploy Logs** link to monitor the deployment\n3. Once deployed, Jenkins will be available at the internal hostname\n\n\n For more Jenkins configuration options and values, check out the [Bitnami\n Jenkins chart\n documentation](https://github.com/bitnami/charts/tree/main/bitnami/jenkins).\n This same pattern works for any Bitnami chart (PostgreSQL, Redis, MongoDB) or\n any other public Helm chart!\n\n\n### Basic Service Deployment\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: web-api\n helm:\n deploymentMethod: "native"\n chart:\n name: web-app\n version: "1.2.0"\n```\n\n### PostgreSQL with Overrides\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: database\n helm:\n deploymentMethod: "native"\n version: "3.14.0" # Override Helm version\n args: "--atomic" # Override deployment args\n chart:\n name: postgresql\n values: # Additional values merged with defaults\n - "persistence.size=20Gi"\n - "replicaCount=2"\n```\n\n### Custom Environment Variables\n\nLifecycle supports flexible environment variable formatting through the `envMapping` configuration. This feature allows you to control how environment variables from your service configuration are passed to your Helm chart.\n\n\n **Why envMapping?** Different Helm charts expect environment variables in\n different formats. Some expect an array of objects with `name` and `value`\n fields (Kubernetes standard), while others expect a simple key-value map. The\n `envMapping` feature lets you adapt to your chart\'s requirements.\n\n\n#### Default envMapping Configuration\n\nYou can define default `envMapping` configurations in the `global_config` database table. These defaults apply to all services using that chart unless overridden at the service level.\n\n**Example: Setting defaults for your organization\'s chart**\n\n```json\n// In global_config table, key: "myorg-web-app"\n{\n "chart": {\n "name": "myorg-web-app",\n "repoUrl": "https://charts.myorg.com"\n },\n "envMapping": {\n "app": {\n "format": "array",\n "path": "deployment.containers[0].env"\n }\n }\n}\n```\n\nWith this configuration, any service using the `myorg-web-app` chart will automatically use array format for environment variables:\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: api\n helm:\n deploymentMethod: "native"\n chart:\n name: "myorg-web-app" # Inherits envMapping from global_config\n docker:\n app:\n env:\n API_KEY: "secret"\n # These will be formatted as array automatically\n```\n\n\n Setting `envMapping` in global_config is particularly useful when: - You have\n a standard organizational chart used by many services - You want consistent\n environment variable handling across services - You\'re migrating multiple\n services and want to reduce configuration duplication\n\n\n#### Array Format\n\nBest for charts that expect Kubernetes-style env arrays.\n\n```yaml {7-9} filename="lifecycle.yaml"\nservices:\n - name: api\n helm:\n deploymentMethod: "native"\n chart:\n name: local\n envMapping:\n app:\n format: "array"\n path: "env"\n docker:\n app:\n env:\n DATABASE_URL: "postgres://localhost:5432/mydb"\n API_KEY: "secret-key-123"\n NODE_ENV: "production"\n```\n\n**This produces the following Helm values:**\n\n```bash\n--set env[0].name=DATABASE_URL\n--set env[0].value=postgres://localhost:5432/mydb\n--set env[1].name=API_KEY\n--set env[1].value=secret-key-123\n--set env[2].name=NODE_ENV\n--set env[2].value=production\n```\n\n**Your chart\'s values.yaml would use it like:**\n\n```yaml\nenv:\n - name: DATABASE_URL\n value: postgres://localhost:5432/mydb\n - name: API_KEY\n value: secret-key-123\n - name: NODE_ENV\n value: production\n```\n\n#### Map Format\n\nBest for charts that expect a simple key-value object.\n\n```yaml {7-9} filename="lifecycle.yaml"\nservices:\n - name: api\n helm:\n deploymentMethod: "native"\n chart:\n name: local\n envMapping:\n app:\n format: "map"\n path: "envVars"\n docker:\n app:\n env:\n DATABASE_URL: "postgres://localhost:5432/mydb"\n API_KEY: "secret-key-123"\n NODE_ENV: "production"\n```\n\n**This produces the following Helm values:**\n\n```bash\n--set envVars.DATABASE__URL=postgres://localhost:5432/mydb\n--set envVars.API__KEY=secret-key-123\n--set envVars.NODE__ENV=production\n```\n\n\n Note: Underscores in environment variable names are converted to double\n underscores (`__`) in map format to avoid Helm parsing issues.\n\n\n**Your chart\'s values.yaml would use it like:**\n\n```yaml\nenvVars:\n DATABASE__URL: postgres://localhost:5432/mydb\n API__KEY: secret-key-123\n NODE__ENV: production\n```\n\n#### Complete Example with Multiple Services\n\n```yaml filename="lifecycle.yaml"\nservices:\n # Service using array format (common for standard Kubernetes deployments)\n - name: frontend\n helm:\n deploymentMethod: "native"\n repository: "myorg/apps"\n branchName: "main"\n envMapping:\n app:\n format: "array"\n path: "deployment.env"\n chart:\n name: "./charts/web-app"\n docker:\n app:\n dockerfilePath: "frontend/Dockerfile"\n env:\n REACT_APP_API_URL: "https://api.example.com"\n REACT_APP_VERSION: "{{build.uuid}}"\n\n # Service using map format (common for custom charts)\n - name: backend\n helm:\n deploymentMethod: "native"\n repository: "myorg/apps"\n branchName: "main"\n envMapping:\n app:\n format: "map"\n path: "config.environment"\n chart:\n name: "./charts/api"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/backend.dockerfile"\n ports:\n - 3000\n env:\n NODE_ENV: "production"\n SERVICE_NAME: "backend"\n\n - name: "mysql-database"\n helm:\n deploymentMethod: "native"\n repository: "myorg/api-services"\n branchName: "main"\n chart:\n name: "mysql" # Using public Helm chart\n version: "9.14.1"\n repoUrl: "https://charts.bitnami.com/bitnami"\n valueFiles:\n - "deploy/helm/mysql-values.yaml"\n```\n\n## Templated Variables\n\nLifecycle supports template variables in Helm values that are resolved at deployment time. These variables allow you to reference dynamic values like build UUIDs, docker tags, and internal hostnames.\n\n### Available Variables\n\nTemplate variables use the format `{{{variableName}}}` and are replaced with actual values during deployment:\n\n| Variable | Description | Example Value |\n| ------------------------------------ | ------------------------- | ---------------------------------------- |\n| `{{{serviceName_dockerTag}}}` | Docker tag for a service | `main-abc123` |\n| `{{{serviceName_dockerImage}}}` | Full docker image path | `registry.com/org/repo:main-abc123` |\n| `{{{serviceName_internalHostname}}}` | Internal service hostname | `api-service.env-uuid.svc.cluster.local` |\n| `{{{build.uuid}}}` | Build UUID | `env-12345` |\n| `{{{build.namespace}}}` | Kubernetes namespace | `env-12345` |\n\n### Usage in Values\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: web-api\n helm:\n deploymentMethod: "native"\n chart:\n name: "./charts/app"\n values:\n - "image.tag={{{web-api_dockerTag}}}"\n - "backend.url=http://{{{backend-service_internalHostname}}}:8080"\n - "env.BUILD_ID={{{build.uuid}}}"\n```\n\n\n**Docker Image Mapping**: When using custom charts, you\'ll need to map `{{{serviceName_dockerImage}}}` or `{{{serviceName_dockerTag}}}` to your chart\'s expected value path. Common patterns include:\n- `image.repository` and `image.tag` (most common)\n- `deployment.image` (single image string)\n- `app.image` or `application.image`\n- Custom paths specific to your chart\n\nCheck your chart\'s `values.yaml` to determine the correct path.\n\n\n\n#### Image Mapping Examples\n\n```yaml filename="lifecycle.yaml"\n# Example 1: Separate repository and tag (most common)\nservices:\n - name: web-api\n helm:\n chart:\n name: "./charts/standard"\n values:\n - "image.repository=registry.com/org/web-api" # Static repository\n - "image.tag={{{web-api_dockerTag}}}" # Dynamic tag only\n\n# Example 2: Combined image string\nservices:\n - name: worker\n helm:\n chart:\n name: "./charts/custom"\n values:\n - "deployment.image={{{worker_dockerImage}}}" # Full image with tag\n\n# Example 3: Nested structure\nservices:\n - name: backend\n helm:\n chart:\n name: "./charts/microservice"\n values:\n - "app.container.image={{{backend_dockerImage}}}" # Full image with tag\n```\n\n\n**Important**: Always use triple braces `{{{variable}}}` instead of double braces `{{variable}}` for Lifecycle template variables. This prevents Helm from trying to process them as Helm template functions and ensures they are passed through correctly for Lifecycle to resolve.\n\n\n### Template Resolution Order\n\n1. Lifecycle resolves `{{{variables}}}` before passing values to Helm\n2. The resolved values are then passed to Helm using `--set` flags\n3. Helm processes its own template functions (if any) after receiving the resolved values\n\n### Example with Service Dependencies\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: api-gateway\n helm:\n chart:\n name: "./charts/gateway"\n values:\n - "config.authServiceUrl=http://{{{auth-service_internalHostname}}}:3000"\n - "config.userServiceUrl=http://{{{user-service_internalHostname}}}:3000"\n - "image.tag={{{api-gateway_dockerTag}}}"\n\n - name: auth-service\n helm:\n chart:\n name: "./charts/microservice"\n values:\n - "image.tag={{{auth-service_dockerTag}}}"\n - "database.host={{{postgres-db_internalHostname}}}"\n```\n\n## Deployment Process\n\n\n 1. **Job Creation**: A Kubernetes job is created in the ephemeral namespace 2.\n **RBAC Setup**: Service account with namespace-scoped permissions is created\n 3. **Git Clone**: Init container clones the repository 4. **Helm Deploy**:\n Main container executes the Helm deployment 5. **Monitoring**: Logs are\n streamed in real-time via WebSocket\n\n\n### Concurrent Deployment Handling\n\nNative Helm automatically handles concurrent deployments by:\n\n- Detecting existing deployment jobs\n- Force-deleting the old job\n- Starting the new deployment\n\nThis ensures the newest deployment always takes precedence.\n\n## Monitoring Deployments\n\n### Deploy Logs Access\n\nFor services using native Helm deployment, you can access deployment logs through the Lifecycle PR comment:\n\n1. Add the `lifecycle-status-comments!` label to your PR\n2. In the status comment that appears, you\'ll see a **Deploy Logs** link for each service using native Helm\n3. Click the link to view real-time deployment logs\n\n### Log Contents\n\nThe deployment logs show:\n\n- Git repository cloning progress (`clone-repo` container)\n- Helm deployment execution (`helm-deploy` container)\n- Real-time streaming of all deployment output\n- Success or failure status\n\n## Chart Types\n\nLifecycle automatically detects and handles three chart types:\n\n| Type | Detection | Features |\n| ------------- | -------------------------------------------- | ---------------------------------------------- |\n| **ORG_CHART** | Matches `orgChartName` AND has `helm.docker` | Docker image injection, env var transformation |\n| **LOCAL** | Name is "local" or starts with "./" or "../" | Flexible `envMapping` support |\n| **PUBLIC** | Everything else | Standard labels and tolerations |\n\n\n The `orgChartName` is configured in the database\'s `global_config` table with\n key `orgChart`. This allows organizations to define their standard internal\n Helm chart.\n\n\n## Troubleshooting\n\n### Deployment Fails with "Another Operation in Progress"\n\n**Symptom**: Helm reports an existing operation is blocking deployment\n\n**Solution**: Native Helm automatically handles this by killing existing jobs. If the issue persists:\n\n```bash\n# Check for stuck jobs\nkubectl get jobs -n env-{uuid} -l service={serviceName}\n\n# Force delete if needed\nkubectl delete job {jobName} -n env-{uuid} --force --grace-period=0\n```\n\n### Environment Variables Not Working\n\n**Symptom**: Environment variables not passed to the deployment\n\n**Common Issues**:\n\n1. `envMapping` placed under `chart` instead of directly under `helm`\n2. Incorrect format specification (array vs map)\n3. Missing path configuration\n\n**Correct Configuration**:\n\n```yaml {4-7}\nhelm:\n deploymentMethod: "native"\n chart:\n name: local\n envMapping: # Correct: directly under helm\n app:\n format: "array"\n path: "env"\n```\n\n## Migration Example\n\nHere\'s a complete example showing how to migrate from GitHub-type services to Helm-type services:\n\n### Before: GitHub-type Services\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: "api-gateway"\n github:\n repository: "myorg/api-services"\n branchName: "main"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/api.dockerfile"\n env:\n BACKEND_URL: "{{backend-service_internalHostname}}:3000"\n LOG_LEVEL: "info"\n ENV_NAME: "production"\n ports:\n - 8080\n deployment:\n public: true\n resource:\n cpu:\n request: "100m"\n memory:\n request: "256Mi"\n readiness:\n tcpSocketPort: 8080\n hostnames:\n host: "example.com"\n defaultInternalHostname: "api-gateway-prod"\n defaultPublicUrl: "api.example.com"\n\n - name: "backend-service"\n github:\n repository: "myorg/api-services"\n branchName: "main"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/backend.dockerfile"\n ports:\n - 3000\n env:\n NODE_ENV: "production"\n SERVICE_NAME: "backend"\n deployment:\n public: false\n resource:\n cpu:\n request: "50m"\n memory:\n request: "128Mi"\n readiness:\n tcpSocketPort: 3000\n\n - name: "mysql-database"\n docker:\n dockerImage: "mysql"\n defaultTag: "8.0-debian"\n ports:\n - 3306\n env:\n MYSQL_ROOT_PASSWORD: "strongpassword123"\n MYSQL_DATABASE: "app_database"\n MYSQL_USER: "app_user"\n MYSQL_PASSWORD: "apppassword456"\n deployment:\n public: false\n resource:\n cpu:\n request: "100m"\n memory:\n request: "512Mi"\n readiness:\n tcpSocketPort: 3306\n serviceDisks:\n - name: "mysql-data"\n mountPath: "/var/lib/mysql"\n accessModes: "ReadWriteOnce"\n storageSize: "10Gi"\n```\n\n### After: Helm-type Services with Native Deployment\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: "api-gateway"\n helm:\n deploymentMethod: "native" # Enable native Helm\n version: "3.14.0"\n repository: "myorg/api-services"\n branchName: "main"\n args: "--wait --timeout 10m"\n envMapping:\n app:\n format: "array"\n path: "containers.api.env"\n chart:\n name: "./charts/microservices"\n values:\n - \'image.tag="{{{api-gateway_dockerTag}}}"\'\n - "service.type=LoadBalancer"\n - "ingress.enabled=true"\n valueFiles:\n - "deploy/helm/base-values.yaml"\n - "deploy/helm/api-gateway-values.yaml"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/api.dockerfile"\n env:\n BACKEND_URL: "{{backend-service_internalHostname}}:3000"\n LOG_LEVEL: "info"\n ENV_NAME: "production"\n ports:\n - 8080\n\n - name: "backend-service"\n helm:\n deploymentMethod: "native"\n version: "3.14.0"\n repository: "myorg/api-services"\n branchName: "main"\n envMapping:\n app:\n format: "map" # Using map format for this service\n path: "env"\n chart:\n name: "./charts/microservices"\n values:\n - \'image.tag="{{{backend-service_dockerTag}}}"\'\n - "replicaCount=2"\n valueFiles:\n - "deploy/helm/base-values.yaml"\n - "deploy/helm/backend-values.yaml"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/backend.dockerfile"\n ports:\n - 3000\n env:\n NODE_ENV: "production"\n SERVICE_NAME: "backend"\n\n - name: "mysql-database"\n helm:\n deploymentMethod: "native"\n repository: "myorg/api-services"\n branchName: "main"\n chart:\n name: "mysql" # Using public Helm chart\n version: "9.14.1"\n repoUrl: "https://charts.bitnami.com/bitnami"\n valueFiles:\n - "deploy/helm/mysql-values.yaml"\n```\n\n### Key Migration Points\n\n1. **Service Type Change**: Changed from `github:` to `helm:` configuration\n2. **Repository Location**: `repository` and `branchName` move from under `github:` to directly under `helm:`\n3. **Deployment Method**: Added `deploymentMethod: "native"` to enable native Helm\n4. **Chart Configuration**: Added `chart:` section with local or public charts\n5. **Environment Mapping**: Added `envMapping:` to control how environment variables are passed\n6. **Helm Arguments**: Added `args:` for Helm command customization\n7. **Docker Configuration**: Kept existing `docker:` config for build process\n\n\n Note that when converting from GitHub-type to Helm-type services, the\n `repository` and `branchName` fields move from being nested under `github:` to\n being directly under `helm:`.\n\n\n\n Many configuration options (like Helm version, args, and chart details) can be\n defined in the `global_config` database table, making the service YAML\n cleaner. Only override when needed.', + body: 'This feature is still in alpha and might change with breaking changes.\n\n\n**Native Helm** is an alternative deployment method that runs Helm deployments directly within Kubernetes jobs, eliminating the need for external CI/CD systems. This provides a more self-contained and portable deployment solution.\n\n\n Native Helm deployment is an opt-in feature that can be enabled globally or\n per-service.\n\n\n## Overview\n\nWhen enabled, Native Helm:\n\n- Creates Kubernetes jobs to execute Helm deployments\n- Runs in ephemeral namespaces with proper RBAC\n- Provides real-time deployment logs via WebSocket\n- Handles concurrent deployments automatically\n- Supports all standard Helm chart types\n\n## Quickstart\n\nWant to try native Helm deployment? Here\'s the fastest way to get started:\n\n```yaml filename="lifecycle.yaml" {5}\nservices:\n - name: my-api\n defaultUUID: "dev-0"\n helm:\n deploymentMethod: "native" # That\'s it!\n chart:\n name: "local"\n valueFiles:\n - "./helm/values.yaml"\n```\n\nThis configuration:\n\n1. Enables native Helm for the `my-api` service\n2. Uses a local Helm chart from your repository\n3. Applies values from `./helm/values.yaml`\n4. Runs deployment as a Kubernetes job\n\n\n To enable native Helm for all services at once, see [Global\n Configuration](#enabling-native-helm).\n\n\n## Configuration\n\n### Enabling Native Helm\n\nThere are two ways to enable native Helm deployment:\n\n#### Per Service Configuration\n\nEnable native Helm for individual services:\n\n```yaml {4} filename="lifecycle.yaml"\nservices:\n - name: my-service\n helm:\n deploymentMethod: "native" # Enable for this service only\n chart:\n name: my-chart\n```\n\n#### Global Configuration\n\nEnable native Helm for all services:\n\n```yaml {3} filename="lifecycle.yaml"\nhelm:\n nativeHelm:\n enabled: true # Enable for all services\n```\n\n### Configuration Precedence\n\nLifecycle uses a hierarchical configuration system with three levels of precedence:\n\n1. **helmDefaults** - Base defaults for all deployments (database: `global_config` table)\n2. **Chart-specific config** - Per-chart defaults (database: `global_config` table)\n3. **Service YAML config** - Service-specific overrides (highest priority)\n\n\n Service-level configuration always takes precedence over global defaults.\n\n\n### Global Configuration (Database)\n\nGlobal configurations are stored in the `global_config` table in the database. Each configuration is stored as a row with:\n\n- **key**: The configuration name (e.g., \'helmDefaults\', \'postgresql\', \'redis\')\n- **config**: JSON object containing the configuration\n\n#### helmDefaults Configuration\n\nStored in database with key `helmDefaults`:\n\n```json\n{\n "nativeHelm": {\n "enabled": true,\n "defaultArgs": "--wait --timeout 30m",\n "defaultHelmVersion": "3.12.0"\n }\n}\n```\n\n**Field Descriptions**:\n\n- `enabled`: When `true`, enables native Helm deployment for all services unless they explicitly set `deploymentMethod: "ci"`\n- `defaultArgs`: Arguments automatically appended to every Helm command (appears before service-specific args)\n- `defaultHelmVersion`: The Helm version to use when not specified at the service or chart level\n\n#### Chart-specific Configuration\n\nExample: PostgreSQL configuration stored with key `postgresql`:\n\n```json\n{\n "version": "3.13.0",\n "args": "--force --timeout 60m0s --wait",\n "chart": {\n "name": "postgresql",\n "repoUrl": "https://charts.bitnami.com/bitnami",\n "version": "12.9.0",\n "values": ["auth.username=postgres_user", "auth.database=postgres_db"]\n }\n}\n```\n\n\n These global configurations are managed by administrators and stored in the\n database. They provide consistent defaults across all environments and can be\n overridden at the service level.\n\n\n## Usage Examples\n\n### Quick Experiment: Deploy Jenkins!\n\nWant to see native Helm in action? Let\'s deploy everyone\'s favorite CI/CD tool - Jenkins! This example shows how easy it is to deploy popular applications using native Helm.\n\n```yaml filename="lifecycle.yaml"\nenvironment:\n defaultServices:\n - name: "my-app"\n - name: "jenkins" # Add Jenkins to your default services\n\nservices:\n - name: "jenkins"\n helm:\n deploymentMethod: "native"\n repository: "myorg/apps"\n branchName: "main"\n chart:\n name: "jenkins"\n repoUrl: "https://charts.bitnami.com/bitnami"\n version: "13.6.8"\n values:\n - "service.type=ClusterIP"\n - "ingress.enabled=true"\n - "ingress.hostname={{jenkins_publicUrl}}"\n - "ingress.ingressClassName=nginx"\n```\n\n\n 🎉 That\'s it! With just a few lines of configuration, you\'ll have Jenkins\n running in your Kubernetes cluster.\n\n\nTo access your Jenkins instance:\n\n1. Check the deployment status in your PR comment\n2. Click the **Deploy Logs** link to monitor the deployment\n3. Once deployed, Jenkins will be available at the internal hostname\n\n\n For more Jenkins configuration options and values, check out the [Bitnami\n Jenkins chart\n documentation](https://github.com/bitnami/charts/tree/main/bitnami/jenkins).\n This same pattern works for any Bitnami chart (PostgreSQL, Redis, MongoDB) or\n any other public Helm chart!\n\n\n### Basic Service Deployment\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: web-api\n helm:\n deploymentMethod: "native"\n chart:\n name: web-app\n version: "1.2.0"\n```\n\n### PostgreSQL with Overrides\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: database\n helm:\n deploymentMethod: "native"\n version: "3.14.0" # Override Helm version\n args: "--atomic" # Override deployment args\n chart:\n name: postgresql\n values: # Additional values merged with defaults\n - "persistence.size=20Gi"\n - "replicaCount=2"\n```\n\n### Custom Environment Variables\n\nLifecycle supports flexible environment variable formatting through the `envMapping` configuration. This feature allows you to control how environment variables from your service configuration are passed to your Helm chart.\n\n\n **Why envMapping?** Different Helm charts expect environment variables in\n different formats. Some expect an array of objects with `name` and `value`\n fields (Kubernetes standard), while others expect a simple key-value map. The\n `envMapping` feature lets you adapt to your chart\'s requirements.\n\n\n#### Default envMapping Configuration\n\nYou can define default `envMapping` configurations in the `global_config` database table. These defaults apply to all services using that chart unless overridden at the service level.\n\n**Example: Setting defaults for your organization\'s chart**\n\n```json\n// In global_config table, key: "myorg-web-app"\n{\n "chart": {\n "name": "myorg-web-app",\n "repoUrl": "https://charts.myorg.com"\n },\n "envMapping": {\n "app": {\n "format": "array",\n "path": "deployment.containers[0].env"\n }\n }\n}\n```\n\nWith this configuration, any service using the `myorg-web-app` chart will automatically use array format for environment variables:\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: api\n helm:\n deploymentMethod: "native"\n chart:\n name: "myorg-web-app" # Inherits envMapping from global_config\n docker:\n app:\n env:\n API_KEY: "secret"\n # These will be formatted as array automatically\n```\n\n\n Setting `envMapping` in global_config is particularly useful when: - You have\n a standard organizational chart used by many services - You want consistent\n environment variable handling across services - You\'re migrating multiple\n services and want to reduce configuration duplication\n\n\n#### Array Format\n\nBest for charts that expect Kubernetes-style env arrays.\n\n```yaml {7-9} filename="lifecycle.yaml"\nservices:\n - name: api\n helm:\n deploymentMethod: "native"\n chart:\n name: local\n envMapping:\n app:\n format: "array"\n path: "env"\n docker:\n app:\n env:\n DATABASE_URL: "postgres://localhost:5432/mydb"\n API_KEY: "secret-key-123"\n NODE_ENV: "production"\n```\n\n**This produces the following Helm values:**\n\n```bash\n--set env[0].name=DATABASE_URL\n--set env[0].value=postgres://localhost:5432/mydb\n--set env[1].name=API_KEY\n--set env[1].value=secret-key-123\n--set env[2].name=NODE_ENV\n--set env[2].value=production\n```\n\n**Your chart\'s values.yaml would use it like:**\n\n```yaml\nenv:\n - name: DATABASE_URL\n value: postgres://localhost:5432/mydb\n - name: API_KEY\n value: secret-key-123\n - name: NODE_ENV\n value: production\n```\n\n#### Map Format\n\nBest for charts that expect a simple key-value object.\n\n```yaml {7-9} filename="lifecycle.yaml"\nservices:\n - name: api\n helm:\n deploymentMethod: "native"\n chart:\n name: local\n envMapping:\n app:\n format: "map"\n path: "envVars"\n docker:\n app:\n env:\n DATABASE_URL: "postgres://localhost:5432/mydb"\n API_KEY: "secret-key-123"\n NODE_ENV: "production"\n```\n\n**This produces the following Helm values:**\n\n```bash\n--set envVars.DATABASE__URL=postgres://localhost:5432/mydb\n--set envVars.API__KEY=secret-key-123\n--set envVars.NODE__ENV=production\n```\n\n\n Note: Underscores in environment variable names are converted to double\n underscores (`__`) in map format to avoid Helm parsing issues.\n\n\n**Your chart\'s values.yaml would use it like:**\n\n```yaml\nenvVars:\n DATABASE__URL: postgres://localhost:5432/mydb\n API__KEY: secret-key-123\n NODE__ENV: production\n```\n\n#### Complete Example with Multiple Services\n\n```yaml filename="lifecycle.yaml"\nservices:\n # Service using array format (common for standard Kubernetes deployments)\n - name: frontend\n helm:\n deploymentMethod: "native"\n repository: "myorg/apps"\n branchName: "main"\n envMapping:\n app:\n format: "array"\n path: "deployment.env"\n chart:\n name: "./charts/web-app"\n docker:\n app:\n dockerfilePath: "frontend/Dockerfile"\n env:\n REACT_APP_API_URL: "https://api.example.com"\n REACT_APP_VERSION: "{{build.uuid}}"\n\n # Service using map format (common for custom charts)\n - name: backend\n helm:\n deploymentMethod: "native"\n repository: "myorg/apps"\n branchName: "main"\n envMapping:\n app:\n format: "map"\n path: "config.environment"\n chart:\n name: "./charts/api"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/backend.dockerfile"\n ports:\n - 3000\n env:\n NODE_ENV: "production"\n SERVICE_NAME: "backend"\n\n - name: "mysql-database"\n helm:\n deploymentMethod: "native"\n repository: "myorg/api-services"\n branchName: "main"\n chart:\n name: "mysql" # Using public Helm chart\n version: "9.14.1"\n repoUrl: "https://charts.bitnami.com/bitnami"\n valueFiles:\n - "deploy/helm/mysql-values.yaml"\n```\n\n## Templated Variables\n\nLifecycle supports template variables in Helm values that are resolved at deployment time. These variables allow you to reference dynamic values like build UUIDs, docker tags, and internal hostnames.\n\n### Available Variables\n\nTemplate variables use the format `{{{variableName}}}` and are replaced with actual values during deployment:\n\n| Variable | Description | Example Value |\n| ------------------------------------ | ------------------------- | ---------------------------------------- |\n| `{{{serviceName_dockerTag}}}` | Docker tag for a service | `main-abc123` |\n| `{{{serviceName_dockerImage}}}` | Full docker image path | `registry.com/org/repo:main-abc123` |\n| `{{{serviceName_internalHostname}}}` | Internal service hostname | `api-service.env-uuid.svc.cluster.local` |\n| `{{{build.uuid}}}` | Build UUID | `env-12345` |\n| `{{{build.namespace}}}` | Kubernetes namespace | `env-12345` |\n\n### Usage in Values\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: web-api\n helm:\n deploymentMethod: "native"\n chart:\n name: "./charts/app"\n values:\n - "image.tag={{{web-api_dockerTag}}}"\n - "backend.url=http://{{{backend-service_internalHostname}}}:8080"\n - "env.BUILD_ID={{{build.uuid}}}"\n```\n\n\n**Docker Image Mapping**: When using custom charts, you\'ll need to map `{{{serviceName_dockerImage}}}` or `{{{serviceName_dockerTag}}}` to your chart\'s expected value path. Common patterns include:\n- `image.repository` and `image.tag` (most common)\n- `deployment.image` (single image string)\n- `app.image` or `application.image`\n- Custom paths specific to your chart\n\nCheck your chart\'s `values.yaml` to determine the correct path.\n\n\n\n#### Image Mapping Examples\n\n```yaml filename="lifecycle.yaml"\n# Example 1: Separate repository and tag (most common)\nservices:\n - name: web-api\n helm:\n chart:\n name: "./charts/standard"\n values:\n - "image.repository=registry.com/org/web-api" # Static repository\n - "image.tag={{{web-api_dockerTag}}}" # Dynamic tag only\n\n# Example 2: Combined image string\nservices:\n - name: worker\n helm:\n chart:\n name: "./charts/custom"\n values:\n - "deployment.image={{{worker_dockerImage}}}" # Full image with tag\n\n# Example 3: Nested structure\nservices:\n - name: backend\n helm:\n chart:\n name: "./charts/microservice"\n values:\n - "app.container.image={{{backend_dockerImage}}}" # Full image with tag\n```\n\n\n**Important**: Always use triple braces `{{{variable}}}` instead of double braces `{{variable}}` for Lifecycle template variables. This prevents Helm from trying to process them as Helm template functions and ensures they are passed through correctly for Lifecycle to resolve.\n\n\n### Template Resolution Order\n\n1. Lifecycle resolves `{{{variables}}}` before passing values to Helm\n2. The resolved values are then passed to Helm using `--set` flags\n3. Helm processes its own template functions (if any) after receiving the resolved values\n\n### Example with Service Dependencies\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: api-gateway\n helm:\n chart:\n name: "./charts/gateway"\n values:\n - "config.authServiceUrl=http://{{{auth-service_internalHostname}}}:3000"\n - "config.userServiceUrl=http://{{{user-service_internalHostname}}}:3000"\n - "image.tag={{{api-gateway_dockerTag}}}"\n\n - name: auth-service\n helm:\n chart:\n name: "./charts/microservice"\n values:\n - "image.tag={{{auth-service_dockerTag}}}"\n - "database.host={{{postgres-db_internalHostname}}}"\n```\n\n## Deployment Process\n\n\n 1. **Job Creation**: A Kubernetes job is created in the ephemeral namespace 2.\n **RBAC Setup**: Service account with namespace-scoped permissions is created\n 3. **Git Clone**: Init container clones the repository 4. **Helm Deploy**:\n Main container executes the Helm deployment 5. **Monitoring**: Logs are\n streamed in real-time via WebSocket\n\n\n### Concurrent Deployment Handling\n\nNative Helm automatically handles concurrent deployments by:\n\n- Detecting existing deployment jobs\n- Force-deleting the old job\n- Starting the new deployment\n\nThis ensures the newest deployment always takes precedence.\n\n## Monitoring Deployments\n\n### Deploy Logs Access\n\nFor services using native Helm deployment, you can access deployment logs through the Lifecycle PR comment:\n\n1. Add the `lifecycle-status-comments!` label to your PR\n2. In the status comment that appears, you\'ll see a **Deploy Logs** link for each service using native Helm\n3. Click the link to view real-time deployment logs\n\n### Log Contents\n\nThe deployment logs show:\n\n- Git repository cloning progress (`clone-repo` container)\n- Helm deployment execution (`helm-deploy` container)\n- Real-time streaming of all deployment output\n- Success or failure status\n\n## Chart Types\n\nLifecycle automatically detects and handles three chart types:\n\n| Type | Detection | Features |\n| ------------- | -------------------------------------------- | ---------------------------------------------- |\n| **ORG_CHART** | Matches `orgChartName` AND has `helm.docker` | Docker image injection, env var transformation |\n| **LOCAL** | Name is "local" or starts with "./" or "../" | Flexible `envMapping` support |\n| **PUBLIC** | Everything else | Standard labels and tolerations |\n\n\n The `orgChartName` is configured in the database\'s `global_config` table with\n key `orgChart`. This allows organizations to define their standard internal\n Helm chart.\n\n\n## Troubleshooting\n\n### Deployment Fails with "Another Operation in Progress"\n\n**Symptom**: Helm reports an existing operation is blocking deployment\n\n**Solution**: Native Helm automatically handles this by killing existing jobs. If the issue persists:\n\n```bash\n# Check for stuck jobs\nkubectl get jobs -n env-{uuid} -l service={serviceName}\n\n# Force delete if needed\nkubectl delete job {jobName} -n env-{uuid} --force --grace-period=0\n```\n\n### Environment Variables Not Working\n\n**Symptom**: Environment variables not passed to the deployment\n\n**Common Issues**:\n\n1. `envMapping` placed under `chart` instead of directly under `helm`\n2. Incorrect format specification (array vs map)\n3. Missing path configuration\n\n**Correct Configuration**:\n\n```yaml {4-7}\nhelm:\n deploymentMethod: "native"\n chart:\n name: local\n envMapping: # Correct: directly under helm\n app:\n format: "array"\n path: "env"\n```\n\n## Migration Example\n\nHere\'s a complete example showing how to migrate from GitHub-type services to Helm-type services:\n\n### Before: GitHub-type Services\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: "api-gateway"\n github:\n repository: "myorg/api-services"\n branchName: "main"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/api.dockerfile"\n env:\n BACKEND_URL: "{{backend-service_internalHostname}}:3000"\n LOG_LEVEL: "info"\n ENV_NAME: "production"\n ports:\n - 8080\n deployment:\n public: true\n resource:\n cpu:\n request: "100m"\n memory:\n request: "256Mi"\n readiness:\n tcpSocketPort: 8080\n hostnames:\n host: "example.com"\n defaultInternalHostname: "api-gateway-prod"\n defaultPublicUrl: "api.example.com"\n\n - name: "backend-service"\n github:\n repository: "myorg/api-services"\n branchName: "main"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/backend.dockerfile"\n ports:\n - 3000\n env:\n NODE_ENV: "production"\n SERVICE_NAME: "backend"\n deployment:\n public: false\n resource:\n cpu:\n request: "50m"\n memory:\n request: "128Mi"\n readiness:\n tcpSocketPort: 3000\n\n - name: "mysql-database"\n docker:\n dockerImage: "mysql"\n defaultTag: "8.0-debian"\n ports:\n - 3306\n env:\n MYSQL_ROOT_PASSWORD: "strongpassword123"\n MYSQL_DATABASE: "app_database"\n MYSQL_USER: "app_user"\n MYSQL_PASSWORD: "apppassword456"\n deployment:\n public: false\n resource:\n cpu:\n request: "100m"\n memory:\n request: "512Mi"\n readiness:\n tcpSocketPort: 3306\n serviceDisks:\n - name: "mysql-data"\n mountPath: "/var/lib/mysql"\n accessModes: "ReadWriteOnce"\n storageSize: "10Gi"\n```\n\n### After: Helm-type Services with Native Deployment\n\n```yaml filename="lifecycle.yaml"\nservices:\n - name: "api-gateway"\n helm:\n deploymentMethod: "native" # Enable native Helm\n version: "3.14.0"\n repository: "myorg/api-services"\n branchName: "main"\n args: "--wait --timeout 10m"\n envMapping:\n app:\n format: "array"\n path: "containers.api.env"\n chart:\n name: "./charts/microservices"\n values:\n - \'image.tag="{{{api-gateway_dockerTag}}}"\'\n - "service.type=LoadBalancer"\n - "ingress.enabled=true"\n valueFiles:\n - "deploy/helm/base-values.yaml"\n - "deploy/helm/api-gateway-values.yaml"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/api.dockerfile"\n env:\n BACKEND_URL: "{{backend-service_internalHostname}}:3000"\n LOG_LEVEL: "info"\n ENV_NAME: "production"\n ports:\n - 8080\n\n - name: "backend-service"\n helm:\n deploymentMethod: "native"\n version: "3.14.0"\n repository: "myorg/api-services"\n branchName: "main"\n envMapping:\n app:\n format: "map" # Using map format for this service\n path: "env"\n chart:\n name: "./charts/microservices"\n values:\n - \'image.tag="{{{backend-service_dockerTag}}}"\'\n - "replicaCount=2"\n valueFiles:\n - "deploy/helm/base-values.yaml"\n - "deploy/helm/backend-values.yaml"\n docker:\n builder:\n engine: "buildkit"\n defaultTag: "main"\n app:\n dockerfilePath: "docker/backend.dockerfile"\n ports:\n - 3000\n env:\n NODE_ENV: "production"\n SERVICE_NAME: "backend"\n\n - name: "mysql-database"\n helm:\n deploymentMethod: "native"\n repository: "myorg/api-services"\n branchName: "main"\n chart:\n name: "mysql" # Using public Helm chart\n version: "9.14.1"\n repoUrl: "https://charts.bitnami.com/bitnami"\n valueFiles:\n - "deploy/helm/mysql-values.yaml"\n```\n\n### Key Migration Points\n\n1. **Service Type Change**: Changed from `github:` to `helm:` configuration\n2. **Repository Location**: `repository` and `branchName` move from under `github:` to directly under `helm:`\n3. **Deployment Method**: Added `deploymentMethod: "native"` to enable native Helm\n4. **Chart Configuration**: Added `chart:` section with local or public charts\n5. **Environment Mapping**: Added `envMapping:` to control how environment variables are passed\n6. **Helm Arguments**: Added `args:` for Helm command customization\n7. **Docker Configuration**: Kept existing `docker:` config for build process\n\n\n Note that when converting from GitHub-type to Helm-type services, the\n `repository` and `branchName` fields move from being nested under `github:` to\n being directly under `helm:`.\n\n\n\n Many configuration options (like Helm version, args, and chart details) can be\n defined in the `global_config` database table, making the service YAML\n cleaner. Only override when needed.', }, { title: "Template Variables", diff --git a/src/pages/docs/features/_meta.ts b/src/pages/docs/features/_meta.ts index 1fbc310..fe87c16 100644 --- a/src/pages/docs/features/_meta.ts +++ b/src/pages/docs/features/_meta.ts @@ -26,5 +26,8 @@ export default { }, "service-dependencies": { "title": "Service Dependencies" + }, + "native-helm-deployment": { + "title": "Native Helm Deployment" } }; \ No newline at end of file diff --git a/src/pages/docs/schema/index.mdx b/src/pages/docs/schema/index.mdx index 378d71c..062b776 100644 --- a/src/pages/docs/schema/index.mdx +++ b/src/pages/docs/schema/index.mdx @@ -46,12 +46,12 @@ environment: - name: "defined-in-lifecycle" - name: "define-outside-lifecycle" repository: "org/repo" - branchName: "main" + branch: "main" optionalServices: - name: "optional-service-1" - name: "optional-service-2" repository: "org/repo" - branchName: "main" + branch: "main" ... ``` @@ -131,7 +131,7 @@ environment: - name: "always-deployed-2" - name: "define-outside-lifecycle-always-deployed" repository: "org/repo" - branchName: "main" + branch: "main" ... ``` @@ -163,7 +163,7 @@ environment: - name: "optional-service-1" - name: "optional-service-2" repository: "org/repo" - branchName: "main" + branch: "main" ... ``` @@ -606,7 +606,6 @@ services: ```yaml --- - environment: ... services: - name: "my-service"