Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
42f17ba
[Actions] Create Enrich Pull Request Action WIP
onairmarc Nov 6, 2025
868676e
[Actions] Refactor Enrich Pull Request
onairmarc Nov 6, 2025
13154b3
[Actions] Suppress multiple runs if label is present
onairmarc Nov 6, 2025
9e4f8e5
[Actions] label circuit breaker wip
onairmarc Nov 6, 2025
9ab4c4f
[Actions] set pr title and description based on jira info
onairmarc Nov 6, 2025
0666db5
[Actions] add jira description sync support
onairmarc Nov 6, 2025
1946e3c
[Actions] parent prefix
onairmarc Nov 6, 2025
101a7bd
[Actions] jira-sync-complete label defaults to atlassian blue
onairmarc Nov 6, 2025
9511dfe
[Actions] add jira configuration options to action inputs
onairmarc Nov 7, 2025
0d1b144
[Actions] fix copy command in dockerfile
onairmarc Nov 7, 2025
a2ce255
[Actions] go mod tidy
onairmarc Nov 7, 2025
b975c36
[Actions] remove unused var
onairmarc Nov 7, 2025
5bb1388
[Actions] fix unused user defined formatting
onairmarc Nov 7, 2025
b95fc06
[Actions] add nil check to jira issue description
onairmarc Nov 7, 2025
ec9bf51
[Actions] remove branch name env from main in favor of driver impleme…
onairmarc Nov 7, 2025
36f1993
[Actions] exit if pr is not a valid int
onairmarc Nov 7, 2025
750f49d
[Actions] add import alias
onairmarc Nov 7, 2025
c79a527
[Docs] Add enrichPullRequest.md
onairmarc Nov 7, 2025
90e8c9c
[Actions] Add Comment to PR when Label Actions Fail
onairmarc Nov 10, 2025
9ef6537
[Actions] Update Jira Driver for EnrichPullRequest
onairmarc Nov 10, 2025
a76c0b2
[Actions] Don't Place Brackets Around Parent Prefix on Retrieval
onairmarc Nov 10, 2025
5263a26
[Actions] Early Return if Issue Key is Empty
onairmarc Nov 10, 2025
255beb9
[Docs] Update enrichPullRequest.md
onairmarc Nov 10, 2025
c7f0531
[Actions] early return
onairmarc Nov 10, 2025
aafacb4
[Actions] Add Driver/Strategy Validation
onairmarc Nov 10, 2025
fd094be
[Actions] Logger
onairmarc Nov 10, 2025
26c534b
[Actions] Add Jira Email Input
onairmarc Nov 10, 2025
5f24a30
[Actions] Add Missing Comma in log statement
onairmarc Nov 10, 2025
4f87db6
[Actions] Add Jira Auth Issue Handling
onairmarc Nov 10, 2025
18955a8
[Actions] Update String Concat Formatting
onairmarc Nov 10, 2025
f02af8c
[Actions] Early Return
onairmarc Nov 10, 2025
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
2 changes: 2 additions & 0 deletions .github/workflows/_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ jobs:
imageName: 'gh-action-create-github-release'
- actionPath: 'actions/github/jsonDiffAlert'
imageName: 'gh-action-json-diff-alert'
- actionPath: 'actions/github/enrichPullRequest'
imageName: 'gh-action-enrich-pull-request'
uses: ./.github/workflows/_github_createAndReleaseActionDockerImage.yml
with:
actionPath: ${{ matrix.actionPath }}
Expand Down
41 changes: 41 additions & 0 deletions actions/github/enrichPullRequest/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
FROM golang:1.24-alpine AS builder

WORKDIR /app

# Copy go mod and sum files
COPY go.mod go.sum ./

# Download dependencies
RUN go mod download

# Copy source code
COPY . .

# Build the binary
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# Final stage - minimal image
FROM alpine:latest

# Install ca-certificates for HTTPS requests
RUN apk --no-cache add ca-certificates

# Create a non-root user
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup

# Set working directory
WORKDIR /app

# Copy the binary from builder stage
COPY --from=builder /app/main .

# Set ownership and permissions
RUN chown appuser:appgroup /app/main && \
chmod +x /app/main

# Switch to non-root user
USER appuser

# Run the binary
ENTRYPOINT ["/app/main"]
74 changes: 74 additions & 0 deletions actions/github/enrichPullRequest/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: 'Enrich Pull Request'
description: "Enrich's the pull request with information from your project management system."
inputs:
repository:
description: 'The repository name'
required: true
type: string
pullRequestNumber:
description: 'The pull request number'
required: true
type: string
branch:
description: 'The branch name'
required: true
type: string
token:
description: 'GitHub token'
required: true
customFormatting:
description: "User defined custom formatting rules for specific words."
required: false
default: ""
strategy:
type: string
description: 'Which formatting strategy should be used'
required: false
default: 'branch-name'
jiraURL:
type: string
description: 'URL to your jira instance'
required: false
default: ''
jiraEmail:
type: string
description: 'Jira auth email'
required: false
default: ''
jiraToken:
type: string
description: 'Jira auth token'
required: false
default: ''
jiraEnableSyncLabel:
type: boolean
description: 'Use a sync label'
required: false
default: true
jiraEnableSyncDescription:
type: boolean
description: 'Sync Jira description to PR description'
required: false
default: true
jiraSyncLabelName:
type: string
description: 'Name of the sync label'
required: false
default: 'jira-sync-complete'
runs:
using: 'docker'
image: 'docker://ghcr.io/encoredigitalgroup/gh-action-enrich-pull-request:latest'
env:
GH_TOKEN: ${{ inputs.token }}
GH_REPOSITORY: ${{ inputs.repository }}
PR_NUMBER: ${{ inputs.pullRequestNumber }}
BRANCH_NAME: ${{ inputs.branch }}
ENABLE_EXPERIMENTS: ${{ inputs.enableExperiments }}
OPT_FMT_WORDS: ${{ inputs.customFormatting }}
OPT_FMT_STRATEGY: ${{ inputs.strategy }}
OPT_JIRA_URL: ${{ inputs.jiraURL }}
OPT_JIRA_EMAIL: ${{ inputs.jiraEmail }}
OPT_JIRA_TOKEN: ${{ inputs.jiraToken }}
OPT_ENABLE_JIRA_SYNC_LABEL: ${{ inputs.jiraEnableSyncLabel }}
OPT_JIRA_SYNC_LABEL_NAME: ${{ inputs.jiraSyncLabelName }}
OPT_ENABLE_JIRA_SYNC_DESCRIPTION: ${{ inputs.jiraEnableSyncDescription }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package branchname

import (
"fmt"
"os"
"regexp"

"github.com/EncoreDigitalGroup/golib/logger"

"github.com/EncoreDigitalGroup/ci-workflows/actions/github/enrichPullRequest/support/github"
)

var regexWithIssueType = regexp.MustCompile(`^(epic|feature|bugfix|hotfix)/([A-Z]+-[0-9]+)-(.+)$`)
var regexWithoutIssueType = regexp.MustCompile(`^([A-Z]+-[0-9]+)-(.+)$`)
var pullRequestTitle string

func Format(gh github.GitHub) {
branchName, err := gh.GetBranchName()
if err != nil {
logger.Error(err.Error())
os.Exit(1)
}

if !gh.BranchNameMatchesPRTitle(branchName) {
formattedTitle := formatTitle(gh, branchName)
gh.UpdatePRTitle(formattedTitle)
}
}

func GetIssueKeyFromBranchName(branchName string) (string, error) {
if matches := regexWithIssueType.FindStringSubmatch(branchName); matches != nil {
return matches[2], nil
} else if matches := regexWithoutIssueType.FindStringSubmatch(branchName); matches != nil {
return matches[1], nil
} else {
fmt.Println("Title does not match expected format")
logger.Info(pullRequestTitle)
return "", nil
}
}

func GetIssueNameFromBranchName(branchName string) (string, error) {
if matches := regexWithIssueType.FindStringSubmatch(branchName); matches != nil {
return matches[3], nil
} else if matches := regexWithoutIssueType.FindStringSubmatch(branchName); matches != nil {
return matches[2], nil
} else {
fmt.Println("Title does not match expected format")
logger.Info(pullRequestTitle)
return "", nil
}
Comment on lines +42 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do the same for the issue name extraction.

We need an error when we can't extract the human-readable name; otherwise we still go ahead and format [KEY] .

 func GetIssueNameFromBranchName(branchName string) (string, error) {
-    if matches := regexWithIssueType.FindStringSubmatch(branchName); matches != nil {
-        return matches[3], nil
-    } else if matches := regexWithoutIssueType.FindStringSubmatch(branchName); matches != nil {
-        return matches[2], nil
-    } else {
-        fmt.Println("Title does not match expected format")
-        logger.Info(pullRequestTitle)
-        return "", nil
-    }
+    if matches := regexWithIssueType.FindStringSubmatch(branchName); matches != nil {
+        return matches[3], nil
+    }
+    if matches := regexWithoutIssueType.FindStringSubmatch(branchName); matches != nil {
+        return matches[2], nil
+    }
+    return "", fmt.Errorf("branch name %q does not match expected format", branchName)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func GetIssueNameFromBranchName(branchName string) (string, error) {
if matches := regexWithIssueType.FindStringSubmatch(branchName); matches != nil {
return matches[3], nil
} else if matches := regexWithoutIssueType.FindStringSubmatch(branchName); matches != nil {
return matches[2], nil
} else {
fmt.Println("Title does not match expected format")
logger.Info(pullRequestTitle)
return "", nil
}
func GetIssueNameFromBranchName(branchName string) (string, error) {
if matches := regexWithIssueType.FindStringSubmatch(branchName); matches != nil {
return matches[3], nil
}
if matches := regexWithoutIssueType.FindStringSubmatch(branchName); matches != nil {
return matches[2], nil
}
return "", fmt.Errorf("branch name %q does not match expected format", branchName)
}
🤖 Prompt for AI Agents
In actions/github/enrichPullRequest/drivers/branch_name/branch_name.go around
lines 42 to 51, the function currently prints a message and logs an undefined
variable when it fails to extract the human-readable issue name; instead return
an error so callers won't continue formatting a missing title. Replace the
fmt.Println/logger.Info fallback with a returned error (e.g. return "",
fmt.Errorf("could not extract issue name from branch name: %s", branchName)),
ensuring the error is propagated to callers; add fmt to imports if needed and
remove use of the undefined pullRequestTitle.

}

func formatTitle(gh github.GitHub, branchName string) string {
issueKey, err := GetIssueKeyFromBranchName(branchName)
issueName, err := GetIssueNameFromBranchName(branchName)

if err != nil {
fmt.Println("Title does not match expected format")
logger.Error(err.Error())
return pullRequestTitle
}

return gh.ApplyFormatting(issueKey, issueName)
}
Comment on lines +54 to +65
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

formatTitle ignores parse errors.

Because both calls reuse the same err, any failure is lost and we still format [ ] . Make the helper return an error so callers can abort safely.

-func formatTitle(gh github.GitHub, branchName string) string {
-    issueKey, err := GetIssueKeyFromBranchName(branchName)
-    issueName, err := GetIssueNameFromBranchName(branchName)
-
-    if err != nil {
-        fmt.Println("Title does not match expected format")
-        logger.Error(err.Error())
-        return pullRequestTitle
-    }
-
-    return gh.ApplyFormatting(issueKey, issueName)
-}
+func formatTitle(gh github.GitHub, branchName string) (string, error) {
+    issueKey, err := GetIssueKeyFromBranchName(branchName)
+    if err != nil {
+        return "", err
+    }
+
+    issueName, err := GetIssueNameFromBranchName(branchName)
+    if err != nil {
+        return "", err
+    }
+
+    if issueKey == "" || issueName == "" {
+        return "", fmt.Errorf("branch name %q does not contain both issue key and name", branchName)
+    }
+
+    return gh.ApplyFormatting(issueKey, issueName), nil
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func formatTitle(gh github.GitHub, branchName string) string {
issueKey, err := GetIssueKeyFromBranchName(branchName)
issueName, err := GetIssueNameFromBranchName(branchName)
if err != nil {
fmt.Println("Title does not match expected format")
logger.Error(err.Error())
return pullRequestTitle
}
return gh.ApplyFormatting(issueKey, issueName)
}
func formatTitle(gh github.GitHub, branchName string) (string, error) {
issueKey, err := GetIssueKeyFromBranchName(branchName)
if err != nil {
return "", err
}
issueName, err := GetIssueNameFromBranchName(branchName)
if err != nil {
return "", err
}
if issueKey == "" || issueName == "" {
return "", fmt.Errorf("branch name %q does not contain both issue key and name", branchName)
}
return gh.ApplyFormatting(issueKey, issueName), nil
}
🤖 Prompt for AI Agents
In actions/github/enrichPullRequest/drivers/branch_name/branch_name.go around
lines 54 to 65, formatTitle currently reuses the same err for two calls and
swallows parse failures, causing invalid titles like "[ ] ". Change formatTitle
to return (string, error): call GetIssueKeyFromBranchName and
GetIssueNameFromBranchName into separate variables capturing each error, if
either returns an error return "" and that error; otherwise return the formatted
title and nil. Update all callers to handle the error (abort or skip formatting)
instead of assuming a valid title.

19 changes: 19 additions & 0 deletions actions/github/enrichPullRequest/drivers/drivers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package drivers

const BranchName = "branch-name"
const Jira = "jira"

func Validate(driver string) bool {
validDrivers := []string{
BranchName,
Jira,
}

for _, validDriver := range validDrivers {
if driver == validDriver {
return true
}
}

return false
}
Loading