Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions actions/setup/js/runtime_import.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,13 @@ function evaluateExpression(expr) {
return evaluateExpression(rightExpr);
}

// Check if this is a needs.* or steps.* expression that should be looked up from environment variables
// Check if this is a needs.*, steps.*, or inputs.* expression that should be looked up from environment variables
// The compiler extracts these expressions and makes them available as GH_AW_* environment variables
// For example: needs.search_issues.outputs.issue_list → GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_LIST
if (trimmed.startsWith("needs.") || trimmed.startsWith("steps.")) {
// For inputs: inputs.errors → GH_AW_INPUTS_ERRORS
// This is required for workflow_call where inputs are not in context.payload.inputs;
// for workflow_dispatch, context.payload.inputs is populated but the env var lookup takes precedence.
if (trimmed.startsWith("needs.") || trimmed.startsWith("steps.") || trimmed.startsWith("inputs.")) {
// Convert expression to environment variable name
// e.g., "needs.search_issues.outputs.issue_list" → "GH_AW_NEEDS_SEARCH_ISSUES_OUTPUTS_ISSUE_LIST"
const envVarName = "GH_AW_" + trimmed.toUpperCase().replace(/\./g, "_");
Expand Down
60 changes: 60 additions & 0 deletions actions/setup/js/runtime_import.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,66 @@ describe("runtime_import", () => {
}
});

it("should return env var value for inputs.* expressions when set (workflow_call)", () => {
process.env.GH_AW_INPUTS_ERRORS = "some error list";
try {
expect(evaluateExpression("inputs.errors")).toBe("some error list");
} finally {
delete process.env.GH_AW_INPUTS_ERRORS;
}
});

it("should return empty string for inputs.* env var set to empty", () => {
process.env.GH_AW_INPUTS_BRANCH = "";
try {
expect(evaluateExpression("inputs.branch")).toBe("");
} finally {
delete process.env.GH_AW_INPUTS_BRANCH;
}
});

it("should prefer env var over context.payload.inputs for inputs.* expressions", () => {
// When both env var and payload.inputs are set, env var should win
// This ensures workflow_call inputs (delivered via env vars) are resolved correctly
// even when workflow_dispatch also populates context.payload.inputs
global.context.payload.inputs = { repository: "context-value" };
process.env.GH_AW_INPUTS_REPOSITORY = "env-value";
try {
expect(evaluateExpression("inputs.repository")).toBe("env-value");
} finally {
delete process.env.GH_AW_INPUTS_REPOSITORY;
delete global.context.payload.inputs;
}
});
Comment on lines +867 to +897

it("should resolve hyphenated inputs.* via context.payload.inputs fallback", () => {
// Hyphenated input names (e.g., inputs.task-description) are exported by the compiler
// as GH_AW_EXPR_<hash> (hash-based) rather than GH_AW_INPUTS_TASK-DESCRIPTION (simple).
// The simple env var lookup won't match, so the expression falls through to
// context.payload.inputs, which works for workflow_dispatch.
global.context.payload.inputs = { "task-description": "fix the bug" };
try {
expect(evaluateExpression("inputs.task-description")).toBe("fix the bug");
} finally {
delete global.context.payload.inputs;
}
});

it("should not resolve hyphenated inputs.* when context.payload.inputs is empty", () => {
// For workflow_call, context.payload.inputs is empty. Hyphenated input names
// can't be resolved via the simple env var conversion (which produces
// GH_AW_INPUTS_TASK-DESCRIPTION instead of the compiler's GH_AW_EXPR_<hash>).
// The compiler handles this via placeholder substitution in the heredoc-inlined
// prompt; runtime-import expressions rely on context.payload.inputs.
global.context.payload.inputs = {};
try {
const result = evaluateExpression("inputs.task-description");
expect(result).toContain("inputs.task-description");
} finally {
delete global.context.payload.inputs;
}
});

it("should handle missing properties gracefully", () => {
const result = evaluateExpression("github.event.nonexistent.property");
expect(result).toContain("github.event.nonexistent.property");
Expand Down
Loading