Skip to content

[TT-17507] Move jira-linter to github-actions#138

Open
buraksezer wants to merge 1 commit into
mainfrom
feat/TT-17507/Move-jira-linter-to-github-actions
Open

[TT-17507] Move jira-linter to github-actions#138
buraksezer wants to merge 1 commit into
mainfrom
feat/TT-17507/Move-jira-linter-to-github-actions

Conversation

@buraksezer

Copy link
Copy Markdown
Contributor

PR for https://tyktech.atlassian.net/browse/TT-17507

Changes

  • Moved TykTechnologies/jira-linter composite action source code into jira-linter directory
  • Replaced unused cyrus-za/jira-lint reusable workflow with the in-house implementation
  • Added Go test job to CI
  • Updated dependabot config for Go module updates

Caller Migration

After merge, 20 repos need to update their jira-pr-validator.yaml:

TykTechnologies/jira-linter@mainTykTechnologies/github-actions/jira-linter@main

List of repos:

  • ara
  • graphql-translator
  • portal
  • portal-admin
  • portal-api-tests
  • storage
  • tyk
  • tyk-analytics
  • tyk-analytics-ui
  • tyk-ara-ui
  • tyk-charts
  • tyk-gql
  • tyk-identity-broker
  • tyk-operator-internal
  • tyk-pump
  • tyk-pump-proof
  • tyk-sink
  • tyk-sink-proof
  • tyk-sync-internal
  • tyk-ui

@buraksezer buraksezer requested a review from a team June 22, 2026 11:22
@probelabs

probelabs Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

This PR migrates the jira-linter functionality from a separate repository (TykTechnologies/jira-linter) into this github-actions monorepo as a self-contained composite action. The previous reusable workflow, which used the third-party cyrus-za/jira-lint action, has been replaced with this new in-house implementation written in Go.

The new action validates PRs against Jira tickets by extracting the ticket ID from the branch name or PR title, fetching details from the Jira API, and updating the PR description with the ticket's status and summary. It also validates the ticket's status against an approved list. Additionally, the PR introduces a new CI job to run tests for the Go linter and updates Dependabot to scan for its dependencies.

Files Changed Analysis

  • Added: A new jira-linter directory containing the Go source code (main.go, config.go, main_test.go), Go module files (go.mod, go.sum), a README.md, and the composite action definition (action.yaml).
  • Modified:
    • .github/workflows/jira-lint.yaml: Replaced the external cyrus-za/jira-lint action with a call to the new local ./jira-linter composite action. The required secrets have been updated to JIRA_USER_EMAIL and JIRA_TOKEN.
    • .github/workflows/ci-test.yml: Added a new job, test-jira-linter, to execute go test for the new linter. The existing test job was renamed to test-branch-suggestion for clarity.
    • .github/.dependabot.yml: Configured Dependabot to check for updates to the Go modules in the jira-linter directory.
    • docs/workflows/jira-lint.md: Updated documentation to reflect the new implementation, usage patterns (both as a reusable workflow and a composite action), and required secrets.
    • .gitignore: Added standard Go binary and test file patterns.

Architecture & Impact Assessment

  • What this PR accomplishes: It consolidates a core piece of the CI/CD validation process into the github-actions repository, centralizing maintenance and versioning. It replaces a third-party dependency with a custom Go application, providing greater control and customizability over the Jira validation logic.

  • Key technical changes introduced:

    • A new composite action (jira-linter/action.yaml) that builds and runs a Go command-line tool.
    • The Go application uses the Jira API for ticket validation and the GitHub API to update PR descriptions and post comments on failure.
    • The reusable workflow .github/workflows/jira-lint.yaml now serves as a wrapper around this new composite action.
  • Affected system components: This change directly impacts the PR validation workflow for all repositories that use the jira-lint.yaml reusable workflow. The author notes that 20 repositories will require updates to their workflow files to point to the new action path after this PR is merged.

  • Visualization:

graph TD
    A[Pull Request Event] --> B{jira-lint.yaml Workflow};
    B --> C[Checkout github-actions repo];
    C --> D[Run ./jira-linter composite action];
    D --> E[Go application executes];
    E -->|Fetches issue details| F(Jira API);
    E -->|Updates PR description & posts comments| G(GitHub API);
Loading

Scope Discovery & Context Expansion

The core change is the introduction of a self-contained Go application to handle Jira linting. The logic in jira-linter/cmd/linter/main.go performs the following steps:

  1. Parses environment variables for configuration (Jira credentials, PR details).
  2. Extracts a Jira issue ID (e.g., TT-12345) from the branch name or PR title.
  3. Connects to the Jira API to fetch the issue details.
  4. Validates that the issue's status is in an accepted list (e.g., "In Dev", "In Code Review").
  5. Connects to the GitHub API to update the PR's description with a formatted block containing the Jira ticket's summary and status.

The jira-linter/action.yaml file orchestrates this process within a GitHub Actions runner by setting up Go, building the binary, executing it, and using the marocchino/sticky-pull-request-comment action to post a failure comment if validation fails or delete it upon success. The broad impact across 20 repositories makes this a significant infrastructural change that standardizes a key part of the development workflow.

Metadata
  • Review Effort: 4 / 5
  • Primary Label: chore

Powered by Visor from Probelabs

Last updated: 2026-06-22T11:24:54.658Z | Triggered by: pr_opened | Commit: 3fd49c4

💡 TIP: You can chat with Visor using /visor ask <your question>

@probelabs

probelabs Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Security Issues (3)

Severity Location Issue
🟡 Warning jira-linter/action.yaml:59-63
The raw error output from the linter command is written to a file and then embedded directly inside a code block in a GitHub comment. If an error message contains sensitive information (e.g., parts of a URL with credentials, although unlikely with the current code) or characters that could break out of the code block, it could lead to information disclosure or content injection. The current implementation uses a code block, which mitigates most risks, but it's a potential vector if error messages change in the future.
💡 SuggestionBefore writing the error to the comment body, sanitize it to remove any potentially sensitive information or characters that could be used for injection attacks. Although the ```` ``` ```` block helps, an explicit sanitization step provides defense in depth.
🟡 Warning jira-linter/cmd/linter/main.go:48-50
The `jira-base-url` input is used to construct links in PR descriptions and is reflected in error messages without being validated as a proper URL. A malformed or malicious URL could lead to broken markdown, phishing links, or other content injection in the resulting PR comment or description. The current validation only checks if the URL is non-empty.
💡 SuggestionValidate that `config.Jira.BaseURL` is a valid HTTP or HTTPS URL. Use `net/url.ParseRequestURI` and check that the scheme is either `http` or `https` in the `loadConfig` function.
🟡 Warning jira-linter/cmd/linter/main.go:326-345
Data fetched from the Jira API, specifically the issue summary (`issue.Fields.Summary`) and status (`issue.Fields.Status.Name`), is directly embedded into a markdown table in the PR description. If these fields in Jira contain special markdown characters (e.g., `|`, `\n`), they could break the table formatting or inject unintended markdown. While GitHub sanitizes rendered content, it's a best practice to escape external data to prevent content injection.
💡 SuggestionSanitize the `issue.Fields.Summary` and `issue.Fields.Status.Name` strings before embedding them in the PR description. A simple approach is to replace markdown control characters like `|` with `\|` and newlines with a space to ensure they don't break the table structure.

Security Issues (3)

Severity Location Issue
🟡 Warning jira-linter/action.yaml:59-63
The raw error output from the linter command is written to a file and then embedded directly inside a code block in a GitHub comment. If an error message contains sensitive information (e.g., parts of a URL with credentials, although unlikely with the current code) or characters that could break out of the code block, it could lead to information disclosure or content injection. The current implementation uses a code block, which mitigates most risks, but it's a potential vector if error messages change in the future.
💡 SuggestionBefore writing the error to the comment body, sanitize it to remove any potentially sensitive information or characters that could be used for injection attacks. Although the ```` ``` ```` block helps, an explicit sanitization step provides defense in depth.
🟡 Warning jira-linter/cmd/linter/main.go:48-50
The `jira-base-url` input is used to construct links in PR descriptions and is reflected in error messages without being validated as a proper URL. A malformed or malicious URL could lead to broken markdown, phishing links, or other content injection in the resulting PR comment or description. The current validation only checks if the URL is non-empty.
💡 SuggestionValidate that `config.Jira.BaseURL` is a valid HTTP or HTTPS URL. Use `net/url.ParseRequestURI` and check that the scheme is either `http` or `https` in the `loadConfig` function.
🟡 Warning jira-linter/cmd/linter/main.go:326-345
Data fetched from the Jira API, specifically the issue summary (`issue.Fields.Summary`) and status (`issue.Fields.Status.Name`), is directly embedded into a markdown table in the PR description. If these fields in Jira contain special markdown characters (e.g., `|`, `\n`), they could break the table formatting or inject unintended markdown. While GitHub sanitizes rendered content, it's a best practice to escape external data to prevent content injection.
💡 SuggestionSanitize the `issue.Fields.Summary` and `issue.Fields.Status.Name` strings before embedding them in the PR description. A simple approach is to replace markdown control characters like `|` with `\|` and newlines with a space to ensure they don't break the table structure.
\n\n ### Architecture Issues (3)
Severity Location Issue
🟠 Error jira-linter/action.yaml:1
The composite action does not expose the Jira statuses for validation as an input. The underlying Go application supports custom statuses via a `--statuses` flag, but the action invokes it without this flag, forcing all users to rely on a hardcoded list of default statuses (`In Dev`, `In Code Review`, `Ready For Dev`, etc.). This limits the action's reusability across teams with different Jira workflows.
💡 SuggestionAdd a new input to `jira-linter/action.yaml` for accepted statuses and pass it to the linter command. This will allow consuming workflows to customize the validation rule.

In action.yaml:

  1. Add a jira-accepted-statuses input.
  2. Update the run step to pass this input to the linter executable via the --statuses flag.
🟠 Error jira-linter/cmd/linter/main.go:143
The HTTP client used for making API calls to Jira is created without a timeout. If the Jira API is unresponsive or slow, the GitHub Action job could hang indefinitely, leading to stalled CI/CD pipelines and wasted runner resources.
💡 SuggestionConfigure a reasonable timeout on the `http.Client` to prevent jobs from hanging. A 30-second timeout is a sensible starting point.
// jira-linter/cmd/linter/main.go:143
httpClient := &amp;http.Client{
    Timeout: 30 * time.Second, // Add this line
    Transport: &amp;basicAuthTransport{
        Email:    config.Jira.UserEmail,
        APIToken: config.Jira.APIToken,
    },
}
🟠 Error jira-linter/cmd/linter/main.go:120
The implementation for validating the Jira ticket ID in the branch and PR title does not match the documented validation rules. The README states that the 'PR title must contain the same Jira ticket ID as the branch', but the code is more lenient. It allows the ticket ID to exist in either the branch or the title, and only warns if they are different. This can lead to inconsistent validation and bypass the intended workflow.
💡 SuggestionRefactor the `validateBranchAndTitle` function to strictly enforce the documented rules: require a ticket ID in the branch, require a ticket ID in the title, and fail if they do not match.
// jira-linter/cmd/linter/main.go:120
func validateBranchAndTitle(config *Config, branchName string) (string, error) {
    issueIDInBranch, err := findIssueID(branchName)
    if err != nil {
        return &#34;&#34;, fmt.Errorf(&#34;branch name &#39;%s&#39; must contain a valid Jira ticket ID: %w&#34;, branchName, err)
    }

    issueIDInTitle, err := findIssueID(config.PR.Title)
    if err != nil {
        return &#34;&#34;, fmt.Errorf(&#34;PR title &#39;%s&#39; must contain a valid Jira ticket ID: %w&#34;, config.PR.Title, err)
    }

    if issueIDInTitle != issueIDInBranch {
        return &#34;&#34;, fmt.Errorf(&#34;PR title ticket ID &#39;%s&#39; does not match branch ticket ID &#39;%s&#39;&#34;, issueIDInTitle, issueIDInBranch)
    }

    return issueIDInBranch, nil
}

Performance Issues (3)

Severity Location Issue
🟡 Warning jira-linter/cmd/linter/main.go:159-165
The http.Client used for making requests to the Jira API is initialized without a timeout. If the Jira server is slow or unresponsive, API calls could hang indefinitely, causing the GitHub Action to fail only after its own timeout (default 6 hours). This can consume runner minutes and delay feedback.
💡 SuggestionConfigure a reasonable timeout on the http.Client to ensure that requests to Jira do not hang. A 30-second timeout is generally a good starting point for API clients.
🔧 Suggested Fix
httpClient := &http.Client{
		Timeout: 30 * time.Second, // Add a timeout
		Transport: &basicAuthTransport{
			Email:    config.Jira.UserEmail,
			APIToken: config.Jira.APIToken,
		},
	}
🟡 Warning jira-linter/cmd/linter/main.go:187
The regular expression in `findIssueID` is compiled each time the function is called. While not in a tight loop in this specific use case, `regexp.MustCompile` is a computationally expensive operation. It is a best practice to compile regular expressions once at the package level and reuse the compiled object.
💡 SuggestionTo avoid repeated compilation, define the compiled regex as a package-level variable. This ensures the compilation happens only once when the program starts.
🔧 Suggested Fix
var jiraIssueRegex = regexp.MustCompile(`(?i)^.*?([A-Z]{1,10}-[0-9]{1,10}).*?$`)

func findIssueID(input string) (string, error) {
match := jiraIssueRegex.FindStringSubmatch(input)

if len(match) &lt; 2 {
	return &#34;&#34;, fmt.Errorf(&#34;no valid Jira ticket ID found&#34;)
}

return strings.ToUpper(match[1]), nil

}

🟡 Warning jira-linter/cmd/linter/main.go:233
The function `updatePRDescriptionBody` makes a GitHub API call (`client.PullRequests.Get`) to fetch the pull request details solely to access its body. This information is already available in the GitHub Actions context (`github.event.pull_request.body`) where the linter is executed. This extra API call adds unnecessary network latency to the action's execution time.
💡 SuggestionOptimize the action by passing the PR body from the workflow context to the Go application via an environment variable. This eliminates one of the two GitHub API calls. You would need to update `jira-linter/action.yaml` to set the environment variable (e.g., `JL_PR_BODY: ${{ github.event.pull_request.body }}`), update `config.go` to read it, and modify `updatePRDescription` to use the body from the config instead of fetching it.

Powered by Visor from Probelabs

Last updated: 2026-06-22T11:24:31.298Z | Triggered by: pr_opened | Commit: 3fd49c4

💡 TIP: You can chat with Visor using /visor ask <your question>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant