diff --git a/conformance-tests/2023-09/EXPR/job_templates/3.6--let-host-context-symbols.yaml b/conformance-tests/2023-09/EXPR/job_templates/3.6--let-host-context-symbols.yaml index e1f9630f..e3dea561 100644 --- a/conformance-tests/2023-09/EXPR/job_templates/3.6--let-host-context-symbols.yaml +++ b/conformance-tests/2023-09/EXPR/job_templates/3.6--let-host-context-symbols.yaml @@ -21,10 +21,16 @@ steps: let: - work_dir = Session.WorkingDirectory / 'output' - frame_file = Param.OutputDir / string(Task.Param.Frame) + - data_file = Task.File.Data + embeddedFiles: + - name: Data + type: TEXT + filename: data.txt + data: "hello" actions: onRun: command: echo - args: ["{{work_dir}}", "{{frame_file}}"] + args: ["{{work_dir}}", "{{frame_file}}", "{{data_file}}"] - name: SimpleActionLet parameterSpace: diff --git a/conformance-tests/2023-09/EXPR/jobs/7.3--task-file-expr-properties.test.yaml b/conformance-tests/2023-09/EXPR/jobs/7.3--task-file-expr-properties.test.yaml new file mode 100644 index 00000000..49fe274a --- /dev/null +++ b/conformance-tests/2023-09/EXPR/jobs/7.3--task-file-expr-properties.test.yaml @@ -0,0 +1,37 @@ +template: + specificationVersion: jobtemplate-2023-09 + extensions: + - EXPR + name: TestJob + parameterDefinitions: + - name: Greeting + type: STRING + default: hello + steps: + - name: Step1 + script: + embeddedFiles: + - name: config + type: TEXT + data: "greeting={{Param.Greeting}}" + let: + - "cfg = Task.File.config" + - "has_parent = len(string(cfg.parent)) > 0" + actions: + onRun: + command: python + args: + - -c + - | + import os + path = {{ repr_py(cfg) }} + exists = os.path.isfile(path) + content = open(path).read() if exists else '' + print(f'EXISTS:{exists}') + print(f'CONTENT:{content}') + print(r'HAS_PARENT:{{ has_parent }}') +expected: + output: + - "EXISTS:True" + - "CONTENT:greeting=hello" + - "HAS_PARENT:true" diff --git a/rfcs/0005-expression-language.md b/rfcs/0005-expression-language.md index 362e70f0..54257ab8 100644 --- a/rfcs/0005-expression-language.md +++ b/rfcs/0005-expression-language.md @@ -1964,8 +1964,11 @@ steps: ##### ScriptTemplate Extension When `let` appears in a `ScriptTemplate`, bindings are evaluated once per task (or once -per environment action). The bound names are available in `actions` and can reference -`Task.Param`: +per environment action). The bound names are available in `actions` and `embeddedFiles`, +and can reference `Task.Param.*` as well as the embedded file path symbols of the same +script (`Task.File.*` in a step script, `Env.File.*` in an environment script) — the +file path is determined before the bindings are evaluated, even though the file content +is evaluated separately: ```yaml script: diff --git a/wiki/2023-09-Template-Schemas.md b/wiki/2023-09-Template-Schemas.md index 9ee72ff7..d37ea415 100644 --- a/wiki/2023-09-Template-Schemas.md +++ b/wiki/2023-09-Template-Schemas.md @@ -1458,16 +1458,17 @@ and where the bound names are available: | Location | Can Reference | Bound Names Available In | |----------|---------------|--------------------------| | `.let` | `Param.*`, `RawParam.*`, `Job.Name`, `Step.Name`, earlier bindings in same `let` | *stepEnvironments*, *hostRequirements*, *parameterSpace*, *script* (including nested `.let` or `.let`) | -| `.let` | `Param.*`, `RawParam.*`, `Task.Param.*`, `Task.RawParam.*`, `Session.*`, `Job.Name`, `Step.Name`, step-level bindings, earlier bindings in same `let` | *actions*, *embeddedFiles* | +| `.let` | `Param.*`, `RawParam.*`, `Task.Param.*`, `Task.RawParam.*`, `Session.*`, `Task.File.*`, `Job.Name`, `Step.Name`, step-level bindings, earlier bindings in same `let` | *actions*, *embeddedFiles* | | `.let` | `Param.*`, `RawParam.*`, `Task.Param.*`, `Task.RawParam.*`, `Session.*`, `Job.Name`, `Step.Name`, step-level bindings, earlier bindings in same `let` | *script*, *args* | | `.let` | `Param.*`, `RawParam.*`, `Session.*`, `Env.File.*`, `Job.Name`, earlier bindings in same `let` | *actions*, *embeddedFiles* | Note: `Task.Param.*` and `Task.RawParam.*` are only available in `` and `` contexts because task parameter values are not known until task execution time. -Note: `Env.File.*` symbols in `.let` refer to embedded files defined in the same ``. -The file path is determined before evaluation, so `let` bindings can reference the path where the file will be written, -even though the file content (which may also contain format strings) is evaluated separately. +Note: `Task.File.*` symbols in `.let` and `Env.File.*` symbols in `.let` refer to +embedded files defined in the same script. The file path is determined before evaluation, so `let` bindings can +reference the path where the file will be written, even though the file content (which may also contain format +strings) is evaluated separately. Note: `Job.Name` is concrete at job creation, so it is available in every `let` scope. `Step.Name` is scoped to the Step Template (see [7.3.1](#731-value-references)), so it is available in the Step-side `let` scopes but not in