Convert Harness v0 expressions to v1 format. Useful for converting individual expressions outside of a full pipeline conversion, or for understanding how specific expressions transform.
POST /api/v1/convert/expression
Default HTTP port: 8092
There are two ways to provide context for expression conversion:
Pass the raw v0 pipeline YAML and the server automatically derives the step-type map, v1 path map, and enables FQN mode — the same way pipeline, template, input-set, and trigger conversions build context internally.
{
"expression": "<+pipeline.stages.build.spec.execution.steps.step1.output>",
"context_pipeline_yaml": "pipeline:\n name: my-pipeline\n stages:\n - stage:\n identifier: build\n type: CI\n spec:\n execution:\n steps:\n - step:\n identifier: step1\n type: Run\n spec:\n command: echo hello\n"
}Explicitly supply the step-type map and other context fields. This is useful when you don't have the full pipeline YAML or need fine-grained control.
{
"expression": "<+pipeline.stages.build.spec.execution.steps.step1.output>",
"context": {
"current_step_id": "step1",
"current_step_type": "Run",
"current_step_v1_path": "pipeline.stages.build.steps.step1",
"step_type_map": {
"step1": "Run",
"step2": "Action"
},
"step_v1_path_map": {
"step1": "pipeline.stages.build.steps.step1",
"step2": "pipeline.stages.build.steps.step2"
},
"use_fqn": true
}
}Note: When
context_pipeline_yamlis provided, thecontextfield is ignored.
| Field | Type | Required | Description |
|---|---|---|---|
expression |
string | One of expression, expressions, or remote_file required |
A single v0 expression to convert |
expressions |
string[] | One of expression, expressions, or remote_file required |
Multiple v0 expressions to convert |
remote_file |
string | One of expression, expressions, or remote_file required |
Raw contents of a remote file (manifest, values.yaml, config, etc.) with embedded <+...> expressions. All expressions are converted in place. |
context_pipeline_yaml |
string | Optional | Raw v0 pipeline YAML; server derives context automatically (recommended) |
context |
object | Optional | Manual context for conversion (ignored when context_pipeline_yaml is provided) |
| Field | Type | Description |
|---|---|---|
current_step_id |
string | ID of the current step (when converting expressions inside a step) |
current_step_type |
string | Type of the current step (e.g., "Run", "Action", "Plugin") |
current_step_v1_path |
string | V1 FQN base path to the current step |
step_type_map |
map[string]string | Maps step IDs to their types for all steps in the pipeline |
step_v1_path_map |
map[string]string | Maps step IDs to their v1 FQN base paths |
use_fqn |
boolean | Enable FQN mode for step expressions |
{
"expression": "<+pipeline.stages.build.steps.step1.output>",
"checksum": "sha256:abc123..."
}{
"expressions": {
"<+pipeline.stages.build.spec.execution.steps.step1.output>": "<+pipeline.stages.build.steps.step1.output>",
"<+stage.spec.execution.steps.step2.output>": "<+stage.steps.step2.output>"
},
"checksum": "sha256:def456..."
}{
"remote_file": "apiVersion: apps/v1\nmetadata:\n name: <+pipeline.inputs.appName>\n namespace: <+pipeline.stages.deploy.steps.deploy1.namespace>\n",
"checksum": "sha256:ghi789..."
}Convert a simple expression without context:
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d '{
"expression": "<+pipeline.stages.build.spec.execution.steps.step1.output>"
}'Response:
{
"expression": "<+pipeline.stages.build.steps.step1.output>",
"checksum": "sha256:abc123..."
}Convert a step-relative expression with step type context:
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d '{
"expression": "<+step.spec.command>",
"context": {
"current_step_type": "Run"
}
}'Response:
{
"expression": "<+step.spec.script>",
"checksum": "sha256:abc123"
}Convert to fully qualified names when use_fqn is enabled:
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d '{
"expression": "<+step.spec.command>",
"context": {
"current_step_type": "Run",
"current_step_v1_path": "pipeline.stages.build.steps.runStep1",
"use_fqn": true
}
}'Response:
{
"expression": "<+pipeline.stages.build.steps.runStep1.spec.script>",
"checksum": "sha256:abc123"
}Convert multiple expressions at once:
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d '{
"expressions": [
"<+pipeline.stages.build.spec.execution.steps.step1.output>",
"<+stage.spec.execution.steps.step2.output>",
"<+pipeline.variables.myVar>"
]
}'Response:
{
"expressions": {
"<+pipeline.stages.build.spec.execution.steps.step1.output>": "<+pipeline.stages.build.steps.step1.output>",
"<+stage.spec.execution.steps.step2.output>": "<+stage.steps.step2.output>",
"<+pipeline.variables.myVar>": "<+pipeline.variables.myVar>"
},
"checksum": "sha256:abc123"
}Convert all expressions embedded in a remote file (manifest, values.yaml, config, etc.):
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d '{
"remote_file": "apiVersion: apps/v1\nmetadata:\n name: <+pipeline.variables.appName>\n image: <+pipeline.stages.build.spec.execution.steps.build1.output>\n"
}'Response:
{
"remote_file": "apiVersion: apps/v1\nmetadata:\n name: <+pipeline.variables.appName>\n image: <+pipeline.stages.build.steps.build1.output>\n",
"checksum": "sha256:abc123"
}For step-type-aware and FQN conversion inside a remote file, pass the pipeline YAML:
PIPELINE_YAML=$(cat my_v0_pipeline.yaml)
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg file "$(cat manifest.yaml)" \
--arg yaml "$PIPELINE_YAML" \
'{remote_file: $file, pipeline_yaml: $yaml}')"Instead of manually constructing the context, pass the full v0 pipeline YAML and let the server derive the context automatically:
# Read the pipeline YAML from a file
PIPELINE_YAML=$(cat my_v0_pipeline.yaml)
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg expr '<+pipeline.stages.build.spec.execution.steps.step1.output>' \
--arg yaml "$PIPELINE_YAML" \
'{expression: $expr, context_pipeline_yaml: $yaml}')"Response:
{
"expression": "<+pipeline.stages.build.steps.step1.output>",
"checksum": "sha256:abc123"
}When converting expressions that reference other steps (using steps.STEPID):
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d '{
"expression": "<+steps.otherStep.spec.command>",
"context": {
"current_step_id": "currentStep",
"current_step_type": "Run",
"current_step_v1_path": "pipeline.stages.build.steps.currentStep",
"step_type_map": {
"currentStep": "Run",
"otherStep": "Run"
},
"step_v1_path_map": {
"currentStep": "pipeline.stages.build.steps.currentStep",
"otherStep": "pipeline.stages.build.steps.otherStep"
},
"use_fqn": true
}
}'The expression conversion is also available via gRPC.
Default gRPC port: 8090
service GoConvertService {
rpc ConvertExpression(ExpressionConvertRequest) returns (ExpressionConvertResponse);
}
message ExpressionConvertRequest {
string expression = 1;
repeated string expressions = 2;
string context_pipeline_yaml = 3;
ExpressionContext context = 4;
string remote_file = 5;
}
message ExpressionContext {
string current_step_id = 1;
string current_step_type = 2;
string current_step_v1_path = 3;
map<string, string> step_type_map = 4;
map<string, string> step_v1_path_map = 5;
bool use_fqn = 6;
}
message ExpressionConvertResponse {
string expression = 1;
map<string, string> expressions = 2;
string checksum = 3;
string remote_file = 4;
}grpcurl -plaintext -d '{
"expression": "<+pipeline.variables.myVar>"
}' localhost:8090 io.harness.pms.conversion.proto.GoConvertService/ConvertExpressionThe convert_client.py script supports expression conversion via --expression, --expressions, and --remote-file:
# Single expression (HTTP)
python convert_client.py --expression '<+pipeline.variables.foo>'
# Multiple expressions (HTTP)
python convert_client.py --expressions '<+pipeline.variables.foo>' '<+stage.spec.execution.steps.s1.output>'
# Remote file — convert all expressions in a manifest/config file
python convert_client.py --remote-file manifest.yaml
# Remote file with pipeline YAML context for FQN resolution
python convert_client.py --remote-file manifest.yaml --context-pipeline my_v0_pipeline.yaml
# With pipeline YAML context for FQN resolution
python convert_client.py --expression '<+pipeline.stages.build.spec.execution.steps.step1.output>' \
--context-pipeline my_v0_pipeline.yaml
# Via gRPC
python convert_client.py --grpc --expression '<+pipeline.variables.foo>'
# Via gRPC with remote file
python convert_client.py --grpc --remote-file manifest.yaml --context-pipeline my_v0_pipeline.yaml| V0 Expression | V1 Expression | Notes |
|---|---|---|
<+pipeline.stages.STAGE.spec.execution.steps.STEP.*> |
<+pipeline.stages.STAGE.steps.STEP.*> |
Removes spec.execution |
<+stage.spec.execution.steps.STEP.*> |
<+stage.steps.STEP.*> |
Removes spec.execution |
<+pipeline.stages.STAGE.spec.execution.rollbackSteps.STEP.*> |
<+pipeline.stages.STAGE.rollback.STEP.*> |
Rollback steps |
| V0 Expression | V1 Expression | Step Type |
|---|---|---|
<+step.spec.command> |
<+step.spec.script> |
Run |
<+step.spec.image> |
<+step.spec.container.image> |
Run |
<+step.spec.shell> |
<+step.spec.shell> |
Run |
<+step.spec.envVariables.X> |
<+step.spec.env.X> |
Run |
Use the expression conversion directly in Go code:
package main
import (
"fmt"
"github.com/drone/go-convert/service/converter"
)
func main() {
// Simple conversion without context
result := converter.ConvertExpression(
"<+pipeline.stages.build.spec.execution.steps.step1.output>",
nil,
)
fmt.Println(result) // <+pipeline.stages.build.steps.step1.output>
// Automatic context from pipeline YAML (recommended)
pipelineYAML := `pipeline:
name: my-pipeline
stages:
- stage:
identifier: build
type: CI
spec:
execution:
steps:
- step:
identifier: step1
type: Run
spec:
command: echo hello`
result = converter.ConvertExpressionWithPipeline(
"<+pipeline.stages.build.spec.execution.steps.step1.output>",
pipelineYAML,
)
fmt.Println(result) // <+pipeline.stages.build.steps.step1.output>
// Context-aware conversion (relative, manual context)
ctx := &converter.ExpressionContext{
CurrentStepType: "Run",
}
result = converter.ConvertExpression("<+step.spec.command>", ctx)
fmt.Println(result) // <+step.spec.script>
// Context-aware conversion (FQN mode, manual context)
ctx = &converter.ExpressionContext{
CurrentStepType: "Run",
CurrentStepV1Path: "pipeline.stages.build.steps.runStep1",
UseFQN: true,
}
result = converter.ConvertExpression("<+step.spec.command>", ctx)
fmt.Println(result) // <+pipeline.stages.build.steps.runStep1.spec.script>
// Batch conversion with pipeline YAML context
expressions := []string{
"<+pipeline.variables.myVar>",
"<+stage.spec.execution.steps.step1.output>",
}
results := converter.ConvertExpressionsWithPipeline(expressions, pipelineYAML)
for orig, converted := range results {
fmt.Printf("%s -> %s\n", orig, converted)
}
}{
"code": "MISSING_FIELD",
"message": "either 'expression' or 'expressions' field is required"
}{
"code": "INVALID_JSON",
"message": "unexpected EOF"
}-
pipeline_yamlis the recommended approach: Pass the raw v0 pipeline YAML and the server automatically derives all context (step types, v1 paths, FQN mode). This is the same mechanism used by pipeline, template, input-set, and trigger conversions. -
Context is optional: Basic path conversions work without context (e.g.,
spec.execution.steps→steps). Context is only needed for step-type-specific field conversions. -
pipeline_yamlsupersedescontext: When both are provided,pipeline_yamltakes precedence andcontextis ignored. -
Step type resolution: For step-specific field conversions (like
spec.command→spec.scriptfor Run steps), provide:current_step_type— for expressions starting withstep.step_type_map— for expressions referencing other steps viasteps.STEPID
-
FQN mode: When
use_fqn: true:- Relative expressions (
step.spec.X) become fully qualified (pipeline.stages.STAGE.steps.STEP.spec.X) - Requires
current_step_v1_pathfor the current step - Requires
step_v1_path_mapfor cross-step references
- Relative expressions (
-
Non-expression strings: Input without
<+markers is returned unchanged.