Skip to content

Feature: parameterize safe-output PR policy fields in workflow_call workflows #29173

@bbonafed

Description

@bbonafed

Problem

Safe-output pull request workflows often need caller-specific policy choices in reusable workflow_call workflows. Today several PR safe-output policy fields are compile-time-only because they are validated as static enums or static strings.

High-value examples include:

safe-outputs:
  push-to-pull-request-branch:
    protected-files: ${{ inputs.protected-files-policy }}
    patch-format: ${{ inputs.patch-format }}

Without expression support, reusable workflow authors need separate workflow files for otherwise-identical workflows that differ only in protected-file policy or patch transport format.

Related context: #23724 confirmed appetite for runtime parameterization of frontmatter fields where safe. Earlier follow-ups covered timeout/version fields, but not safe-output PR policy fields.

Desired Behavior

Allow expression strings for enum-like PR policy fields while preserving existing literal validation:

on:
  workflow_call:
    inputs:
      protected-files-policy:
        type: string
        default: fallback-to-issue
      patch-format:
        type: string
        default: am
---
safe-outputs:
  push-to-pull-request-branch:
    protected-files: ${{ inputs.protected-files-policy }}
    patch-format: ${{ inputs.patch-format }}

Literal values must still work and should continue to receive compile-time enum validation:

safe-outputs:
  push-to-pull-request-branch:
    protected-files: fallback-to-issue
    patch-format: am

Proposed Scope

Start with enum-like policy fields that do not change the generated job permission model:

  • safe-outputs.push-to-pull-request-branch.protected-files
  • safe-outputs.push-to-pull-request-branch.patch-format
  • safe-outputs.create-pull-request.protected-files
  • safe-outputs.create-pull-request.patch-format
  • Any shared validation helper used by both handlers, such as validateStringEnumField, so the behavior stays consistent.

If object-form protected-files cannot reasonably support expressions for nested policy, it is acceptable to start with expression support for the string policy form only.

Implementation Plan

  1. Schema updates

    • Update the relevant protected-files and patch-format schema definitions to accept either the current enum literals or a GitHub Actions expression string.
    • Keep object-form protected-files behavior unchanged unless nested expression support is straightforward.
  2. Parser and validation updates

    • Update enum validation helpers so literal strings are validated against the existing allowlist, while expression strings are accepted and passed through.
    • Preserve clear errors for invalid literal strings.
    • Ensure both create-PR and push-to-PR-branch paths use the same helper behavior.
  3. Compilation/runtime behavior

    • Ensure the expression string is emitted into the generated handler config in a runtime-evaluable way.
    • Confirm JavaScript safe-output handlers evaluate the resolved runtime value against the same policy enum before applying it.
    • Fail closed at runtime if the resolved value is not one of the allowed policies/formats.
  4. Tests

    • Add schema/parser tests for literal valid values, literal invalid values, and expression values.
    • Add compiler tests showing expressions appear in the safe-output handler config.
    • Add JS runtime tests for invalid resolved values to ensure fail-closed behavior.
  5. Documentation

    • Update PR safe-output docs with reusable-workflow examples.
    • Document the allowed runtime-resolved values for each field.

Acceptance Criteria

  • protected-files and patch-format literal values remain backward compatible.
  • Expression strings are accepted for the scoped PR safe-output policy fields.
  • Invalid literal strings remain compile-time errors.
  • Invalid runtime-resolved expression values fail closed before mutating repository state.
  • Create-PR and push-to-PR-branch behavior is consistent.
  • Tests and docs are updated.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions