Skip to content

[file-diet] Refactor pkg/workflow/safe_outputs_config.go (1077 lines) into focused modules #20521

@github-actions

Description

@github-actions

Overview

The file pkg/workflow/safe_outputs_config.go has grown to 1077 lines, significantly exceeding the repository's healthy threshold of 800 lines. It currently spans three distinct functional domains mixed together: frontmatter extraction, GitHub App configuration & token generation, and messages/mentions parsing. Splitting it into focused modules will improve maintainability, testability, and discoverability.

Current State

  • File: pkg/workflow/safe_outputs_config.go
  • Size: 1077 lines
  • Test coverage: No test file found (safe_outputs_config_test.go does not exist)
  • Complexity: Medium-high — many parse helpers, YAML step generation with sorting logic, and recursive config merging
Full File Analysis

Functional Domains

Domain 1 — Frontmatter Extraction (lines 1–608)

  • extractSafeOutputsConfig() — dispatches to ~30 individual parseXxx() helpers and handles top-level scalar fields (allowed-domains, staged, env, github-token, max-patch-size, threat-detection, runs-on, messages, activation-comments, mentions, footer, group-reports, report-failure-as-issue, failure-issue-repo, max-bot-mentions, steps, id-token, concurrency-group, environment, jobs, github-app). This single function is ~500 lines with deeply nested if/switch blocks.
  • parseBaseSafeOutputConfig() — parses common max, github-token, and staged fields shared across many handler configs.

Domain 2 — GitHub App Configuration & Token Step Generation (lines 610–932)

  • GitHubAppConfig struct (type definition)
  • parseAppConfig() — parses app map
  • mergeAppFromIncludedConfigs() — merges app config from included workflow configs
  • buildGitHubAppTokenMintStep() — generates YAML steps for minting a GitHub App token; includes repository scope logic (single, multi, wildcard)
  • convertPermissionsToAppTokenFields() — maps GitHub Actions permissions → permission-* action inputs (12 permission types)
  • buildGitHubAppTokenInvalidationStep() — generates YAML steps for revoking the minted token
  • buildActivationAppTokenMintStep() — generates YAML steps for the activation job's app token
  • resolveActivationToken() — priority-order resolution of the activation token reference

Domain 3 — Messages & Mentions Configuration (lines 934–1077)

  • setStringFromMap() — helper to assign string values from a map
  • parseMessagesConfig() — parses ~15 message template fields from frontmatter
  • parseMentionsConfig() — parses boolean or object-form mentions config (allow-team-members, allow-context, allowed list with @ normalization, max)
  • serializeMessagesConfig() — marshals config to JSON for passing as an env var

Complexity Hotspots

  • extractSafeOutputsConfig() (lines 56–561): ~500 lines with 30+ sequential if blocks, each calling a different parser. Hard to unit-test in isolation.
  • buildGitHubAppTokenMintStep() (lines 721–786): Conditional YAML generation with sort.Strings for deterministic ordering — subtle behaviour that benefits from dedicated tests.
  • convertPermissionsToAppTokenFields() (lines 793–845): 12 sequential Get calls — at risk of silent omissions when new permissions are added.

Refactoring Strategy

Proposed File Splits

Based on the semantic analysis, split the file into three focused modules:

  1. safe_outputs_config.go (retained, reduced)

    • Functions: extractSafeOutputsConfig(), parseBaseSafeOutputConfig()
    • Responsibility: Frontmatter extraction and dispatch to parse helpers
    • Estimated LOC: ~580 → could be further reduced by extracting top-level scalar field parsing into a helper
  2. safe_outputs_app_config.go (new file)

    • Functions/Types: GitHubAppConfig, parseAppConfig(), mergeAppFromIncludedConfigs(), buildGitHubAppTokenMintStep(), convertPermissionsToAppTokenFields(), buildGitHubAppTokenInvalidationStep(), buildActivationAppTokenMintStep(), resolveActivationToken()
    • Responsibility: GitHub App token configuration, merging, and YAML step generation
    • Estimated LOC: ~320
  3. safe_outputs_messages_config.go (new file)

    • Functions: setStringFromMap(), parseMessagesConfig(), parseMentionsConfig(), serializeMessagesConfig()
    • Responsibility: Safe-output messages and mentions configuration parsing and serialization
    • Estimated LOC: ~145

Shared Utilities

setStringFromMap() is a small helper used only within the messages domain — it should live in safe_outputs_messages_config.go. If it turns out to be useful elsewhere, promote it to a safe_outputs_util.go.

Interface Abstractions

No new interfaces are strictly required for this split — all types are in the same workflow package. However, consider:

  • Grouping the 30+ parseXxxConfig() calls in extractSafeOutputsConfig() into a named helper (e.g., extractHandlerConfigs()) to improve readability without changing the split.
Test Coverage Plan

Add tests for each new file (currently zero tests exist for this file):

  1. safe_outputs_config_test.go

    • TestExtractSafeOutputsConfig_NoSafeOutputs — returns nil when key absent
    • TestExtractSafeOutputsConfig_EmptyMap — returns non-nil with defaults (missing-tool, missing-data, noop)
    • TestExtractSafeOutputsConfig_AllowedDomains — parses domain list
    • TestExtractSafeOutputsConfig_DefaultThreatDetection — auto-applied when safe outputs exist
    • TestParseBaseSafeOutputConfig_DefaultMax — default propagated correctly
    • TestParseBaseSafeOutputConfig_ExpressionMax$\{\{ ... }} strings accepted
    • Target coverage: >80%
  2. safe_outputs_app_config_test.go

    • TestParseAppConfig_RequiredFields — app-id and private-key
    • TestParseAppConfig_OptionalOwnerAndRepos — owner, repositories list
    • TestBuildGitHubAppTokenMintStep_SingleRepo — inline format
    • TestBuildGitHubAppTokenMintStep_MultiRepo — block scalar format
    • TestBuildGitHubAppTokenMintStep_WildcardRepo — org-wide (no repositories field)
    • TestBuildGitHubAppTokenMintStep_DefaultRepo — falls back to github.event.repository.name
    • TestConvertPermissionsToAppTokenFields — all 12 permission types mapped
    • TestBuildGitHubAppTokenInvalidationStep — always-run, conditional on token presence
    • TestMergeAppFromIncludedConfigs_TopLevelTakesPrecedence
    • TestMergeAppFromIncludedConfigs_FallsBackToIncluded
    • Target coverage: >80%
  3. safe_outputs_messages_config_test.go

    • TestParseMessagesConfig_AllFields — all 15 template fields parsed
    • TestParseMentionsConfig_BoolTrue / _BoolFalse
    • TestParseMentionsConfig_ObjectForm — allow-team-members, allow-context, allowed, max
    • TestParseMentionsConfig_AtNormalization@user stripped to user
    • TestSerializeMessagesConfig_NilReturnsEmpty
    • TestSerializeMessagesConfig_RoundTrip
    • Target coverage: >80%

Implementation Guidelines

  1. Preserve Behaviour: All existing functionality must work identically after the split
  2. Maintain Exports: Keep public API unchanged (GitHubAppConfig, exported functions)
  3. Add Tests First: Write tests for each new file before moving code
  4. Incremental Changes: Split one domain at a time and verify compilation between steps
  5. Run Tests Frequently: Verify make test-unit passes after each split
  6. Add Build Tags: Every new *_test.go file must have //go:build !integration at the top
  7. Run make agent-finish: Required before committing (formatting + lint + tests)

Acceptance Criteria

  • safe_outputs_config.go is reduced to ≤600 lines
  • safe_outputs_app_config.go is created (≤350 lines)
  • safe_outputs_messages_config.go is created (≤160 lines)
  • All three new test files created with ≥80% coverage target
  • All tests pass (make test-unit)
  • Code passes linting (make lint)
  • Build succeeds (make build)
  • No breaking changes to public API
Additional Context
  • Repository Guidelines: Follow patterns in AGENTS.md and scratchpad/cli-command-patterns.md
  • Code Organization: Prefer many small files grouped by functionality (see AGENTS.md)
  • Validation Complexity: Target 100–200 lines per file; hard limit 300 for validators
  • Testing Patterns: Match existing pkg/workflow/*_test.go patterns (table-driven, no mocks)
  • Related Files: pkg/workflow/safe_outputs_config_generation.go, pkg/workflow/safe_outputs_types.go

Priority: Medium
Effort: Medium (well-defined split boundaries; no circular deps; ~3 new files + tests)
Expected Impact: Improved maintainability, easier targeted testing, clearer module ownership

References: §22955041823

Generated by Daily File Diet ·

  • expires on Mar 13, 2026, 1:35 PM UTC

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions