# Lifecycle and Execution

Generic artifacts support per-stage configuration and smart execution caching. This page covers lifecycle overrides, auto-injected environment variables, and the rules that determine when your container runs or is skipped.

## Lifecycle configuration

The `lifecycle` key in `bricks.json` lets you override the default container configuration for individual deployment stages. Each stage (`plan`, `apply`, `destroy`) can specify its own image, command, args, env vars, or skip the stage entirely.

### When to use lifecycle config vs. environment variable detection

<table><thead><tr><th width="228.703125">Approach</th><th>Best for</th></tr></thead><tbody><tr><td><strong>Lifecycle config</strong></td><td>Different CLI arguments per stage, different images, skipping stages</td></tr><tr><td><strong><code>BRICKS_ACTION</code> detection</strong></td><td>A single script that branches on the current stage</td></tr></tbody></table>

### Basic structure

```json
{
  "native": {
    "type": "generic",
    "image": "custom/tool:1.0",
    "command": ["tool"],
    "args": ["default-action"],
    "lifecycle": {
      "plan": {
        "args": ["plan", "--detailed"]
      },
      "apply": {
        "args": ["apply", "--auto-approve"]
      },
      "destroy": {
        "args": ["destroy", "--force"]
      }
    }
  }
}
```

### Phase fields

Each phase (`plan`, `apply`, `destroy`) supports these fields:

<table><thead><tr><th width="103.76171875">Field</th><th width="97.78515625">Type</th><th>Description</th><th>Default</th></tr></thead><tbody><tr><td><code>image</code></td><td>string</td><td>Container image for this phase</td><td>Inherits from <code>native.image</code></td></tr><tr><td><code>command</code></td><td>array</td><td>Command to execute</td><td>Inherits from <code>native.command</code></td></tr><tr><td><code>args</code></td><td>array</td><td>Command arguments</td><td>Inherits from <code>native.args</code></td></tr><tr><td><code>env_vars</code></td><td>object</td><td>Environment variables (merged with native)</td><td>Inherits from <code>native.env_vars</code></td></tr><tr><td><code>skip</code></td><td>boolean</td><td>Skip execution for this phase (plan and destroy only)</td><td><code>false</code></td></tr></tbody></table>

Phase fields **override** the native configuration. If a field is not specified in the phase, it falls back to the native value. Environment variables are **merged**, with phase values taking precedence.

## Skipping stages

Set `skip: true` to prevent the container from executing during a stage. This is supported for `plan` and `destroy` only: `apply` cannot be skipped.

```json
{
  "native": {
    "type": "generic",
    "image": "python:3.11-slim",
    "command": ["python"],
    "args": ["healthcheck.py"],
    "lifecycle": {
      "destroy": {
        "skip": true
      }
    }
  }
}
```

Common use cases for skipping:

* **Skip destroy**: read-only scripts, health checks, monitoring tools
* **Skip plan**: operations that don't benefit from a preview phase

When a stage is skipped, the container does not execute, but the deployment stage still completes successfully.

## Environment variables

Bluebricks injects the following environment variables into every Generic artifact execution:

### BRICKS\_ACTION

The current deployment stage. Your script can use this to branch behavior without lifecycle config.

<table><thead><tr><th width="169.7890625">Value</th><th>Stage</th></tr></thead><tbody><tr><td><code>plan</code></td><td>Preview/planning phase</td></tr><tr><td><code>apply</code></td><td>Create or update operation</td></tr><tr><td><code>plan-destroy</code></td><td>Preview before destruction</td></tr><tr><td><code>destroy</code></td><td>Cleanup/removal operation</td></tr></tbody></table>

```python
import os

action = os.environ.get('BRICKS_ACTION')

if action == 'plan':
    validate_configuration()
elif action == 'apply':
    deploy_resources()
elif action in ('plan-destroy', 'destroy'):
    cleanup_resources()
```

### BRICKS\_STATE

Base64-encoded JSON containing the previous deployment's output. Available when state exists from a prior execution.

```python
import os, json, base64

state_b64 = os.environ.get('BRICKS_STATE')
if state_b64:
    state = json.loads(base64.b64decode(state_b64))
    previous_outputs = state.get('output', {})
    resource_id = previous_outputs.get('resource_id')
```

### BRICKS\_JOB\_ID

UUID identifying the current job execution. Useful for logging and correlation.

```python
job_id = os.environ.get('BRICKS_JOB_ID')
print(f"[{job_id}] Starting operation")
```

## Execution behavior

Generic artifacts use input tracking to decide when to run your container:

### Container executes when:

* **First deployment**: no previous outputs exist
* **Inputs change**: props or secrets are modified
* **Version changes**: the package version is bumped
* **Forced execution**: using techniques like the `now()` function

### Container skips when:

* **Inputs unchanged**: props and secrets are identical to the previous run
* **Outputs exist**: previous execution results are cached
* **Same version**: the package version hasn't changed

{% hint style="warning" %}
**Container code changes do NOT trigger re-execution.** If you fix a bug in your script but don't change any inputs or the package version, existing deployments won't pick up the fix. You must bump the version or change an input to force re-execution.
{% endhint %}

## Forcing execution

When you need a Generic artifact to run on every deployment regardless of input changes (e.g., health checks, cleanup scripts), use the `now()` function to ensure inputs always differ:

```json
{
  "props": {
    "api_endpoint": {
      "type": "string",
      "description": "API endpoint to check"
    },
    "force_run": {
      "type": "string",
      "description": "Timestamp to force execution",
      "value": "now().Format('2006-01-02T15:04:05Z')"
    }
  }
}
```

Because `force_run` changes on every deployment, the input hash always differs and the container always executes.

**Alternative:** bump the package version to force a single re-execution.

## Example: database migration

A complete example showing lifecycle configuration for a database migration tool:

```json
{
  "name": "db-migration",
  "version": "1.0.0",
  "native": {
    "type": "generic",
    "image": "migrate/migrate:latest",
    "command": ["migrate"],
    "args": [
      "-source", "file:///workspace/migrations",
      "-database", "postgres://...",
      "up"
    ],
    "lifecycle": {
      "plan": {
        "args": [
          "-source", "file:///workspace/migrations",
          "-database", "postgres://...",
          "version"
        ]
      },
      "destroy": {
        "args": [
          "-source", "file:///workspace/migrations",
          "-database", "postgres://...",
          "down"
        ]
      }
    }
  },
  "props": {
    "database_url": {
      "type": "string",
      "description": "PostgreSQL connection string"
    }
  }
}
```

<table><thead><tr><th width="129.11328125">Stage</th><th>What happens</th></tr></thead><tbody><tr><td><strong>Plan</strong></td><td>Checks the current migration version</td></tr><tr><td><strong>Apply</strong></td><td>Runs migrations up</td></tr><tr><td><strong>Destroy</strong></td><td>Runs migrations down</td></tr></tbody></table>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bluebricks.co/docs/orchestration/packages/artifacts-overview/generic/lifecycle.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
