Skip to content

Duplicate Code: Safe-Output Config Parsing Repeated Across create-issue/create-discussion/create-pull-request #23228

@github-actions

Description

@github-actions

Overview

The parse*Config implementations for safe-output creation actions repeat the same parse pipeline across three files with mostly field-level differences. This creates a maintenance hotspot where validation and parsing behavior can drift.

The duplicated sequence appears in:

  • create-issue
  • create-discussion
  • create-pull-request

Critical Information

  • Severity: Medium
  • Occurrences: 3
  • Duplication size: ~20-30 lines repeated per implementation (core parsing flow)
  • Risk: Inconsistent behavior when updating preprocessing/unmarshal/default logic

Duplication Details

Pattern: repeated parse pipeline in parse*Config methods:

  1. Check key exists in outputMap
  2. Extract configData
  3. preprocessExpiresField(...)
  4. preprocessBoolFieldAsString(...) loop
  5. preprocessIntFieldAsString(..., "max", ...)
  6. unmarshalConfig(...) with identical fallback handling
  7. Default max handling

Locations:

  • pkg/workflow/create_issue.go:29
  • pkg/workflow/create_discussion.go:31
  • pkg/workflow/create_pull_request.go:42
Example duplicated block
// Unmarshal into typed config struct
var config CreateIssuesConfig
if err := unmarshalConfig(outputMap, "create-issue", &config, createIssueLog); err != nil {
	createIssueLog.Printf("Failed to unmarshal config: %v", err)
	// For backward compatibility, handle nil/empty config
	config = CreateIssuesConfig{}
}

// Set default max if not specified
if config.Max == nil {
	config.Max = defaultIntStr(1)
}

Equivalent logic exists in:

  • CreateDiscussionsConfig unmarshal/default path
  • CreatePullRequestsConfig unmarshal/default path

Impact Analysis

  • Maintainability: Any parser behavior change must be applied in 3 places.
  • Bug risk: Easy to introduce subtle divergence in bool/int preprocessing or defaulting semantics.
  • Code bloat: Repeated orchestration code obscures entity-specific rules.

Refactoring Recommendations

  1. Extract shared parse orchestration helper
  • Suggested location: pkg/workflow/safe_outputs_parser_shared.go
  • Candidate API: generic/helper that accepts key name, logger, bool fields, and a post-process callback for entity-specific defaults.
  • Estimated effort: Medium (3-5 hours)
  1. Keep entity-specific behavior in focused hooks
  • Example hooks:
    • assignee normalization (create-issue)
    • category normalization/fallback (create-discussion)
    • reviewers/protected-files logic (create-pull-request)
  • Benefit: shared validation path + explicit per-entity extension points

Implementation Checklist

  • Define common parse pipeline helper
  • Migrate parseIssuesConfig
  • Migrate parseDiscussionsConfig
  • Migrate parsePullRequestsConfig
  • Add regression tests for shared preprocessing/default behavior
  • Verify no behavior drift in existing safe-output tests

Analysis Metadata

  • Analyzed Files: 6 focused files (pkg/workflow/*.go parser files + related checks)
  • Detection Method: Serena semantic symbol analysis + pattern search
  • Commit: e4a7c883cf12265be930e4c387d2ff06edd533cf
  • Analysis Date: 2026-03-27 UTC

References:

Generated by Duplicate Code Detector ·

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    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