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
Expression conversion is FQN-only: context is supplied as a v1 pipeline
YAML (context_pipeline_yaml) which the server walks into the FQN-keyed step
lookup, plus an optional current_fqn that anchors the call-site for
step-group chaining and step-type-aware resolution. There are no manual step maps.
{
"expression": "<+step.spec.command>",
"context_pipeline_yaml": "pipeline:\n identifier: myPipeline\n stages:\n - stage:\n identifier: build\n steps:\n - step:\n identifier: compile\n type: Run\n spec:\n shell: Sh\n run: go build ./...\n",
"current_fqn": "pipeline.stages.build.steps.compile"
}| 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 | A v1 pipeline YAML. The server walks it into the FQN-keyed step lookup and resolves step references against it in FQN mode. |
current_fqn |
string | Optional | Full v1 FQN of the step the expression lives in, e.g. pipeline.stages.prod.steps.G1.steps.deploy. Used with context_pipeline_yaml to derive the call-site stage, enclosing step-group chain, and current step type (for step.spec.* resolution). |
{
"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 step.spec.* expression to a fully qualified name. The
server looks up current_fqn in the v1 context_pipeline_yaml to recover the
step type, so the type-specific field mapping (command → script for Run) is
applied automatically:
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg expr '<+step.spec.command>' \
--arg yaml "$(cat my_v1_pipeline.yaml)" \
--arg fqn 'pipeline.stages.build.steps.runStep1' \
'{expression: $expr, context_pipeline_yaml: $yaml, current_fqn: $fqn}')"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 v1
pipeline YAML (and optionally current_fqn to anchor step.* self-references):
PIPELINE_YAML=$(cat my_v1_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, context_pipeline_yaml: $yaml}')"Pass the full v1 pipeline YAML and let the server derive the FQN context automatically:
# Read the v1 pipeline YAML from a file
PIPELINE_YAML=$(cat my_v1_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 an expression that references another step (steps.STEPID),
supply the v1 context_pipeline_yaml and the current_fqn of the call-site
step. The referenced step is resolved against the FQN-keyed lookup:
curl -X POST http://localhost:8092/api/v1/convert/expression \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg expr '<+steps.otherStep.spec.command>' \
--arg yaml "$(cat my_v1_pipeline.yaml)" \
--arg fqn 'pipeline.stages.build.steps.currentStep' \
'{expression: $expr, context_pipeline_yaml: $yaml, current_fqn: $fqn}')"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; // v1 pipeline YAML
string current_fqn = 4; // call-site v1 FQN
string remote_file = 5;
}
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 |
<+...template.templateInputs.*> |
<+...template.with.overlay.*> |
Template inputs at pipeline / stage / stepGroup / step level (e.g. <+step.template.templateInputs> → <+step.template.with.overlay>) |
These <+step.*> self-references are resolved by deriving the current step's
type from current_fqn looked up in the FQN-keyed step map built from
context_pipeline_yaml. The step prefix is rewritten to the step's full v1
FQN. For example, with current_fqn = pipeline.stages.build.steps.compile
(a Run step), <+step.spec.command> becomes
<+pipeline.stages.build.steps.compile.spec.script>.
| V0 Expression | V1 Expression (relative) | 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 a v1 pipeline YAML
pipelineYAML := `pipeline:
identifier: myPipeline
stages:
- stage:
identifier: build
steps:
- step:
identifier: step1
type: Run
spec:
shell: Sh
run: echo hello`
result = converter.ConvertExpressionWithPipeline(
"<+pipeline.stages.build.spec.execution.steps.step1.output>",
pipelineYAML,
)
fmt.Println(result) // <+pipeline.stages.build.steps.step1.output>
// FQN-mode conversion: supply the v1 context pipeline + call-site FQN.
// The step type is recovered from the FQN lookup, so step.spec.command
// becomes step.spec.script for a Run step.
ctx := &converter.ExpressionContext{
ContextPipelineYAML: pipelineYAML,
CurrentFQN: "pipeline.stages.build.steps.step1",
}
result = converter.ConvertExpression("<+step.spec.command>", ctx)
fmt.Println(result) // <+pipeline.stages.build.steps.step1.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"
}-
context_pipeline_yamlmust be a v1 pipeline: The server parses it as a v1 pipeline and walks it into the FQN-keyed step lookup. A v0 pipeline (or an unparseable document) is ignored and conversion falls back to structural-only. -
Context is optional: Basic path conversions work without context (e.g.,
spec.execution.steps→steps).context_pipeline_yamlis only needed for step-type-specific field conversions and FQN resolution. -
Step type is derived, not passed: For step-specific field conversions (like
spec.command→spec.scriptfor Run steps), supplycurrent_fqnpointing at the call-site step withincontext_pipeline_yaml; the server recovers the step type from the FQN lookup. There are no manual step maps. -
FQN mode is enabled automatically whenever
context_pipeline_yamlyields a usable step lookup. Relative expressions (step.spec.X) then become fully qualified (pipeline.stages.STAGE.steps.STEP.spec.X);current_fqnanchors the call-site forstep.*self-references and step-group chaining. -
Non-expression strings: Input without
<+markers is returned unchanged.