Add conditional if expressions to runtime configuration#16638
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
if expressions to runtime configuration
There was a problem hiding this comment.
Pull request overview
This PR adds conditional execution support for runtime setup steps in GitHub Actions workflows by introducing an optional if field to runtime configurations. This enables file-based runtime detection, which is particularly useful for centrally-defined workflows using workflow_call that cannot provide runtime configuration at compile time.
Changes:
- Added
iffield toRuntimeConfigandRuntimeRequirementstructures to accept GitHub Actions conditional expressions - Modified step generation to emit
if:conditions in the generated YAML when specified - Updated schema validation to accept the new
ifproperty in runtime objects
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/workflow/frontmatter_types.go | Added If field to RuntimeConfig struct and updated parsing/conversion logic to handle optional if conditions |
| pkg/workflow/runtime_definitions.go | Added IfCondition field to RuntimeRequirement struct |
| pkg/workflow/runtime_overrides.go | Modified to extract and apply if conditions from runtime configuration |
| pkg/workflow/runtime_step_generator.go | Updated step generation to emit if: condition in YAML when present |
| pkg/parser/schemas/main_workflow_schema.json | Added schema validation for the new if property with description and examples |
| pkg/workflow/frontmatter_types_test.go | Added comprehensive unit tests for parsing and round-trip conversion of runtime configs with if conditions |
| pkg/workflow/runtime_setup_test.go | Added tests for step generation with various if conditions across different runtimes |
| pkg/workflow/runtime_integration_test.go | Added end-to-end integration tests validating the full compilation pipeline with conditional expressions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fmt.Sprintf(" uses: %s", actionRef), | ||
| } | ||
|
|
||
| // Add if condition if specified | ||
| if req.IfCondition != "" { | ||
| step = append(step, fmt.Sprintf(" if: %s", req.IfCondition)) | ||
| } | ||
|
|
There was a problem hiding this comment.
The if: condition is inserted after the uses: line but before the with: block. According to GitHub Actions YAML syntax, the if: condition should appear immediately after the step name and before uses:. The current placement may work but violates the conventional order of step properties (name, if, uses, with, run, etc.).
| fmt.Sprintf(" uses: %s", actionRef), | |
| } | |
| // Add if condition if specified | |
| if req.IfCondition != "" { | |
| step = append(step, fmt.Sprintf(" if: %s", req.IfCondition)) | |
| } | |
| } | |
| // Add if condition if specified (must appear before 'uses:' in the step) | |
| if req.IfCondition != "" { | |
| step = append(step, fmt.Sprintf(" if: %s", req.IfCondition)) | |
| } | |
| // Add the action reference after any 'if:' condition | |
| step = append(step, fmt.Sprintf(" uses: %s", actionRef)) |
| // Extract version (optional) | ||
| var version string | ||
| if versionAny, hasVersion := configMap["version"]; hasVersion { |
There was a problem hiding this comment.
The comment "Extract version (optional)" is misleading. While the version field itself is optional in the YAML, the changed logic now allows runtime configs with only an if condition and no version. Consider updating the comment to clarify that a runtime config can have either a version, an if condition, or both.
| nodeMap := map[string]any{} | ||
| if config.Node.Version != "" { | ||
| nodeMap["version"] = config.Node.Version | ||
| } | ||
| if config.Node.If != "" { | ||
| nodeMap["if"] = config.Node.If | ||
| } | ||
| if len(nodeMap) > 0 { | ||
| result["node"] = nodeMap | ||
| } |
There was a problem hiding this comment.
The conversion logic for each runtime (node, python, go, uv, bun, deno) is duplicated six times with only the runtime name and config field changing. This repetitive code could be refactored into a helper function that takes the runtime name and config as parameters, reducing code duplication and making future modifications easier.
Centrally-defined workflows using
workflow_callcannot provide runtime configuration at compile time. This adds support for GitHub Actions conditional expressions in theruntimessection to enable file-based runtime detection.Changes
Runtime Configuration
iffield toRuntimeConfigaccepting GitHub Actions expressionsIfConditionthroughRuntimeRequirementto step generationruntimesConfigToMapto preserveiffield in round-trip conversionCompilation Pipeline
generateSetupStepto emitif:condition in generated YAMLapplyRuntimeOverridesto extract and apply conditional expressionsifproperty in runtime objectsTesting
Example
Generates:
Supports full GitHub Actions expression syntax. Backward compatible - runtimes without
ifconditions unchanged.Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
https://api.github.com/graphql/usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env ck 'scripts/**/*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go m/_n�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env GOPATH); \ if coGOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)https://api.github.com/repos/actions/checkout/git/ref/tags/11bd71901bbe5b1630ceea73d27597364c9af683/usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/11bd71901bbe5b1630ceea73d27597364c9af683 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node /hom�� --check **/*.cjs 64/bin/go **/*.json --ignore-path ../../../.pretti/home/REDACTED/work/gh-aw/gh-aw/.github/workflows go(http block)https://api.github.com/repos/actions/checkout/git/ref/tags/v6/usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha xFkv/UoPwMwqRq6HGOSUMDB GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE 0313489/b375/imprev-parse -c che/go-build/a2/a2615bb3240425a6GOINSECURE GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc-f(http block)https://api.github.com/repos/actions/github-script/git/ref/tags/v7/usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v7 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGO111MODULE env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v7 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE erignore env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v7 --jq .object.sha che/go-build/f9/f9dd00c75c79c7baGOINSECURE GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc-f -o /tmp/go-build394-f -trimpath 64/bin/go -p github.com/githu-1 -lang=go1.25 go(http block)https://api.github.com/repos/actions/setup-go/git/ref/tags/4dc6199c7b1a012772edbd06daecab0f50c9053c/usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/4dc6199c7b1a012772edbd06daecab0f50c9053c --jq .object.sha k/gh-aw/gh-aw/pkg/stringutil/ideGOINSECURE k/gh-aw/gh-aw/pkg/stringutil/patGOMOD 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcrev-parse -o /tmp/go-build3940313489/b395/_pkGOINSECURE -trimpath 64/bin/go -p main -lang=go1.25 go(http block)https://api.github.com/repos/actions/setup-go/git/ref/tags/v5/usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v5 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node(http block)https://api.github.com/repos/actions/setup-go/git/ref/tags/v6/usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v6 --jq .object.sha GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env ck 'scripts/**/*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v6 --jq .object.sha g/styles/theme.gGOINSECURE g/styles/theme_tGOMOD 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGO111MODULE env 0313489/b326/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE b/gh-aw/pkg/work--norc GOMODCACHE go(http block)/usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v6 --jq .object.sha 35126d5394e2a1caGOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGO111MODULE env 0313489/b398/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE b/gh-aw/pkg/test--norc GOMODCACHE go(http block)https://api.github.com/repos/actions/setup-node/git/ref/tags/v6/usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq .object.sha GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env ck 'scripts/**/*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq .object.sha g/stringutil/ideGOINSECURE g/stringutil/patGOMOD 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGO111MODULE env 0313489/b395/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go(http block)/usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v6 --jq .object.sha "prettier" --check 'scripts/**/*GOINSECURE GOPROXY 9bc83b2626bd1c50ffc60cd633225807-d GOSUMDB GOWORK 64/bin/go go env -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet(http block)https://api.github.com/repos/anchore/sbom-action/git/ref/tags/v0/usr/bin/gh gh api /repos/anchore/sbom-action/git/ref/tags/v0 --jq .object.sha che/go-build/b7/remote.origin.url GOPROXY $name) { hasDiscussionsEnabled } } GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc-f -o /tmp/go-build394-f -trimpath 64/bin/go -p github.com/githu-C -lang=go1.25 go(http block)/usr/bin/gh gh api /repos/anchore/sbom-action/git/ref/tags/v0 --jq .object.sha k/gh-aw/gh-aw/pkremote.origin.url k/gh-aw/gh-aw/pkg/repoutil/repouGOMOD 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcrev-parse -o /tmp/go-build3940313489/b391/_pkGOINSECURE -trimpath 64/bin/go -p main -lang=go1.25 go(http block)https://api.github.com/repos/docker/build-push-action/git/ref/tags/v6/usr/bin/gh gh api /repos/docker/build-push-action/git/ref/tags/v6 --jq .object.sha che/go-build/12/12f9de6d07e84c14GOINSECURE GOPROXY $name) { hasDiscussionsEnabled } } GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcrev-parse -o /tmp/go-build3940313489/b382/_pkGOINSECURE -trimpath 64/bin/go -p github.com/githu-1 -lang=go1.25 go(http block)/usr/bin/gh gh api /repos/docker/build-push-action/git/ref/tags/v6 --jq .object.sha k/gh-aw/gh-aw/pkg/styles/theme.gGOINSECURE k/gh-aw/gh-aw/pkg/styles/theme_tGOMOD $name) { hasDiscussionsEnabled } } GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc-f -o /tmp/go-build394-f -trimpath 64/bin/go -p github.com/githu-1 -lang=go1.25 go(http block)https://api.github.com/repos/docker/login-action/git/ref/tags/v3/usr/bin/gh gh api /repos/docker/login-action/git/ref/tags/v3 --jq .object.sha che/go-build/28/2888c116b4dd41c7GOINSECURE GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcrev-parse -o /tmp/go-build3940313489/b383/_pkGOINSECURE -trimpath 64/bin/go -p github.com/githu-1 -lang=go1.25 go(http block)/usr/bin/gh gh api /repos/docker/login-action/git/ref/tags/v3 --jq .object.sha che/go-build/35/35126d5394e2a1caGOINSECURE GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcrev-parse -o /tmp/go-build3940313489/b398/_pkGOINSECURE -trimpath 64/bin/go -p github.com/githu-C -lang=go1.25 go(http block)https://api.github.com/repos/docker/metadata-action/git/ref/tags/v5/usr/bin/gh gh api /repos/docker/metadata-action/git/ref/tags/v5 --jq .object.sha 0313489/b387/embremote.origin.url GOPROXY 64/bin/go GOSUMDB GOWORK run-script/lib/ngraphql /opt/hostedtoolc-f -o /tmp/go-build394-f -trimpath 64/bin/go -p main -lang=go1.25 go(http block)/usr/bin/gh gh api /repos/docker/metadata-action/git/ref/tags/v5 --jq .object.sha k/gh-aw/gh-aw/pkg/sliceutil/slicGOINSECURE k/gh-aw/gh-aw/pkg/sliceutil/slicGOMOD 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc-f -o /tmp/go-build394-f -trimpath 64/bin/go -p main -lang=go1.25 go(http block)https://api.github.com/repos/docker/setup-buildx-action/git/ref/tags/v3/usr/bin/gh gh api /repos/docker/setup-buildx-action/git/ref/tags/v3 --jq .object.sha 0313489/b374/embedcfg GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolc-f -o /tmp/go-build394-f -trimpath 64/bin/go -p github.com/githu-C -lang=go1.25 go(http block)/usr/bin/gh gh api /repos/docker/setup-buildx-action/git/ref/tags/v3 --jq .object.sha che/go-build/b1/-f GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go /opt/hostedtoolcrev-parse @/tm�� x_amd64/vet git 64/bin/go -v origin 64/bin/go go(http block)https://api.github.com/repos/github/gh-aw/git/ref/tags/c4e091835c7a94dc7d3acb8ed3ae145afb4995f3/usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/c4e091835c7a94dc7d3acb8ed3ae145afb4995f3 --jq .object.sha tformat GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh(http block)https://api.github.com/repos/githubnext/agentics/git/ref/tags/-/usr/bin/gh gh api /repos/githubnext/agentics/git/ref/tags/- --jq .object.sha -json GO111MODULE $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node(http block)https://api.github.com/user/usr/bin/gh gh api user --jq .login d -n 10(http block)/usr/bin/gh gh api user --jq .login rt/yaml/yaml_default.go(http block)/usr/bin/gh gh api user --jq .login(http block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.