feat: expose safe-output results as workflow_call outputs#18914
feat: expose safe-output results as workflow_call outputs#18914
Conversation
- Add individual named outputs to safe_output_unified_handler_manager.cjs for first successful result of each safe output type (create_issue, create_pull_request, add_comment, push_to_pull_request_branch) - Add commit_sha to push_to_pull_request_branch.cjs return value - Add individual job-level outputs in compiler_safe_outputs_job.go for created_issue_number/url, created_pr_number/url, comment_id/url, push_commit_sha/url - Add injectWorkflowCallOutputs function in compiler_workflow_call.go to automatically inject on.workflow_call.outputs when workflow_call trigger is configured and safe-outputs are present - Update generateWorkflowBody to call injectWorkflowCallOutputs - Add comprehensive tests for the new functionality - Recompile all workflow lock files to include new outputs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
@copilot review changes, add tests. create a test script that valides compiled lock.yml files in the repo |
| core.setOutput("issues_to_assign_copilot", ""); | ||
| } | ||
|
|
||
| // Emit individual named outputs for the first successful result of each safe output type. |
There was a problem hiding this comment.
@copilot refactor into a separate file that populates the outputs
safe_outouts_action_outputs.cjs
There was a problem hiding this comment.
Extracted into safe_outputs_action_outputs.cjs (commit 030b5b0). The new module exports emitSafeOutputActionOutputs(processingResult), and safe_output_unified_handler_manager.cjs now calls it instead of the inline block. Added safe_outputs_action_outputs.test.cjs with 12 unit tests covering all four safe output types.
Add compiled_lock_files_test.go with 4 test functions that validate the compiled .lock.yml files in .github/workflows/ against their source .md files: - TestCompiledLockFiles_SafeOutputsJobOutputs: verifies the safe_outputs job exposes named outputs for each configured safe-output type (79 workflows checked) - TestCompiledLockFiles_WorkflowCallOutputs: verifies on.workflow_call.outputs is correctly populated for workflow_call + safe-output workflows - TestCompiledLockFiles_NoSpuriousWorkflowCallOutputs: verifies non- workflow_call workflows don't get spurious outputs injected (78 workflows) - TestCompiledLockFiles_SmokeWorkflowCallHasExpectedOutputs: focused test on the canonical smoke-workflow-call workflow Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Added
The tests use |
…ts.cjs Move the block that emits individual named step outputs (created_issue_number/url, created_pr_number/url, comment_id/url, push_commit_sha/url) from safe_output_unified_handler_manager.cjs into a new dedicated module safe_outputs_action_outputs.cjs, exported as emitSafeOutputActionOutputs(). Add safe_outputs_action_outputs.test.cjs with 12 test cases covering all four safe output types, array unwrapping for add_comment, error/skip scenarios, and info logging verification. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds first-class workflow_call outputs for safe-output results so reusable workflows can return created issue/PR/comment/push identifiers/URLs to callers.
Changes:
- JS runtime now emits named step outputs for the first successful safe-output result per type (issue/PR/comment/push).
- Go compiler wires those step outputs into
safe_outputsjob outputs and injectson.workflow_call.outputswhen applicable. - Adds unit/E2E tests for injection behavior and broad validation against compiled
.lock.ymlworkflows; regenerates affected lock files.
Reviewed changes
Copilot reviewed 104 out of 104 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/workflow/compiler_yaml.go | Injects on.workflow_call.outputs into the rendered on: section when safe-outputs are configured. |
| pkg/workflow/compiler_workflow_call.go | New injection/merge logic for on.workflow_call.outputs based on configured safe-output types. |
| pkg/workflow/compiler_workflow_call_test.go | New unit + end-to-end tests for workflow_call outputs injection and mapping. |
| pkg/workflow/compiler_safe_outputs_job.go | Adds named safe_outputs job outputs mapping to new step outputs. |
| pkg/workflow/compiled_lock_files_test.go | New repo-wide validation tests ensuring lock files expose expected job/workflow_call outputs. |
| actions/setup/js/safe_output_unified_handler_manager.cjs | Emits new named step outputs for created issue/PR/comment and push commit results. |
| actions/setup/js/push_to_pull_request_branch.cjs | Includes commit_sha in handler result for downstream output export. |
| .github/workflows/workflow-skill-extractor.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/workflow-normalizer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/workflow-health-manager.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/weekly-safe-outputs-spec-review.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/weekly-editors-health-check.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/video-analyzer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/unbloat-docs.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/ubuntu-image-analyzer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/tidy.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/test-create-pr-error-handling.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/technical-doc-writer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/super-linter.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/sub-issue-closer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/step-name-alignment.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/stale-repo-identifier.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-workflow-call.lock.yml | Regenerated compiled workflow to add on.workflow_call.outputs + new safe_outputs named outputs. |
| .github/workflows/smoke-test-tools.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-temporary-id.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-project.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-multi-pr.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-gemini.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-copilot.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-copilot-arm.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-codex.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-claude.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/smoke-agent.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/slide-deck-maintainer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/semantic-function-refactor.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/security-compliance.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/security-alert-burndown.campaign.g.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/scout.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/refiner.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/q.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/pr-triage-agent.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/poem-bot.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/plan.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/pdf-summary.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/mergefest.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/layout-spec-maintainer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/jsweep.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/issue-triage-agent.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/issue-monster.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/issue-arborist.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/instructions-janitor.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/hourly-ci-cleaner.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/gpclean.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/go-pattern-detector.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/go-logger.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/glossary-maintainer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/github-mcp-tools-report.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/functional-pragmatist.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/duplicate-code-detector.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/draft-pr-cleanup.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/discussion-task-miner.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/dictation-prompt.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/developer-docs-consolidator.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/dev.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/dev-hawk.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/dependabot-go-checker.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/dependabot-burner.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/delight.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/deep-report.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-workflow-updater.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-testify-uber-super-expert.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-team-status.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-syntax-error-quality.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-security-red-team.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-safe-outputs-conformance.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-safe-output-optimizer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-rendering-scripts-verifier.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-multi-device-docs-tester.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-mcp-concurrency-analysis.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-file-diet.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-fact.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-doc-updater.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-doc-healer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-cli-tools-tester.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-cli-performance.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-assign-issue-to-user.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/daily-architecture-diagram.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/craft.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/contribution-check.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/code-simplifier.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/code-scanning-fixer.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/cloclo.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/cli-version-checker.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/cli-consistency-checker.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/ci-doctor.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/ci-coach.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/changeset.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/breaking-change-checker.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/brave.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/bot-detection.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
| .github/workflows/archie.lock.yml | Regenerated compiled workflow to include new safe_outputs named outputs. |
Comments suppressed due to low confidence (2)
pkg/workflow/compiled_lock_files_test.go:178
- Same masking issue here: parse errors are treated as a skip, so a workflow that becomes unparsable won’t fail this validation. Consider requiring ParseWorkflowFile to succeed for any workflow being validated (especially those with workflow_call + safe-outputs).
actions/setup/js/safe_output_unified_handler_manager.cjs:1161 - The logic that picks the “first successful result” can select a success result that doesn’t actually contain the fields you export (e.g., add_comment can return {success: true, skipped: true, warning: ...} without commentId/url). In that case, comment_id/comment_url (or other outputs) won’t be exported even if a later result of the same type did create something. Consider selecting the first result that contains the expected keys (or scanning arrays for the first element with the expected keys) instead of the first success.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| safeOutputs, _, parseErr := parseSafeOutputsForLockFileTest(mdPath) | ||
| if parseErr != nil || safeOutputs == nil { | ||
| continue // skip workflows without safe-outputs or that fail to parse | ||
| } | ||
|
|
||
| lockBytes, err := os.ReadFile(lockPath) | ||
| if err != nil { | ||
| continue // lock file may not exist yet | ||
| } |
There was a problem hiding this comment.
This test silently skips workflows when parsing fails or when the corresponding .lock.yml file can’t be read. That can mask regressions (a broken workflow frontmatter or a missing compiled lock file would still let the suite pass). Consider failing the test for parse errors and missing lock files for any workflow that declares safe-outputs.
This issue also appears on line 175 of the same file.
| if existingOutputs, hasOutputs := workflowCallMap["outputs"].(map[string]any); hasOutputs { | ||
| for k, v := range existingOutputs { | ||
| // User-defined entries may be maps with description+value or plain strings | ||
| if outputMap, ok := v.(map[string]any); ok { | ||
| entry := workflowCallOutputEntry{} | ||
| if desc, ok := outputMap["description"].(string); ok { | ||
| entry.Description = desc | ||
| } | ||
| if val, ok := outputMap["value"].(string); ok { | ||
| entry.Value = val | ||
| } | ||
| mergedOutputs[k] = entry | ||
| } | ||
| } |
There was a problem hiding this comment.
This merge only preserves existing user-defined workflow_call outputs when each entry is a map (description/value). The comment notes outputs may also be plain strings, but those would currently be dropped. Either update the comment to match the supported schema, or preserve non-map entries (e.g., by merging into a map[string]any and keeping unknown value types as-is).
Workflows triggered via
workflow_callhad no way to surface results from safe-outputs (created issue numbers, PR URLs, commit SHAs, etc.) to callers. The compiled YAML never generatedon.workflow_call.outputs, leaving downstream jobs unable to chain on those results.Changes
JS runtime — emit individual step outputs
safe_outputs_action_outputs.cjs(new): Dedicated module exportingemitSafeOutputActionOutputs()that emits named step outputs for the first successful result of each type:created_issue_number/url,created_pr_number/url,comment_id/url,push_commit_sha/urlsafe_output_unified_handler_manager.cjs: CallsemitSafeOutputActionOutputs()after processingpush_to_pull_request_branch.cjs: Returnscommit_shaalongside the existingcommit_urlGo compiler — wire outputs through the job and workflow levels
compiler_safe_outputs_job.go: Adds job-level output declarations for each configured safe output type, mapping to the new step outputscompiler_workflow_call.go(new):injectWorkflowCallOutputsautomatically addson.workflow_call.outputswhenworkflow_callis a trigger and safe-outputs are configured. User-declared outputs in the frontmatter are preserved and take precedence.compiler_yaml.go: CallsinjectWorkflowCallOutputsduring workflow body generationTests
safe_outputs_action_outputs.test.cjs(new): 12 unit tests covering all four safe output types, array unwrapping foradd_comment, failed/empty result scenarios, and info logging verificationcompiler_workflow_call_test.go(new): Unit and end-to-end tests for the injection logic (17 test cases)compiled_lock_files_test.go(new): Validates all compiled.lock.ymlfiles in.github/workflows/against their source.mdfiles, with 4 test functions covering 79+ workflows:TestCompiledLockFiles_SafeOutputsJobOutputs— verifies thesafe_outputsjob exposes the correct named outputs for each configured safe-output typeTestCompiledLockFiles_WorkflowCallOutputs— verifieson.workflow_call.outputsis populated forworkflow_call+ safe-outputs workflowsTestCompiledLockFiles_NoSpuriousWorkflowCallOutputs— verifies non-workflow_callworkflows don't get spurious outputs injectedTestCompiledLockFiles_SmokeWorkflowCallHasExpectedOutputs— focused sub-tests on the canonicalsmoke-workflow-callworkflowResult
A workflow with
on.workflow_call+safe-outputs.create-issuenow compiles to:Callers can then chain on results:
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.