Skip to content

Harden jira codex PR workflow#504

Merged
jirhiker merged 1 commit intostagingfrom
jira-automation
Feb 13, 2026
Merged

Harden jira codex PR workflow#504
jirhiker merged 1 commit intostagingfrom
jira-automation

Conversation

@jirhiker
Copy link
Copy Markdown
Member

Summary

  • harden Jira API calls with fail-fast + retries
  • make GITHUB_OUTPUT writes safe for multiline values
  • pin actions to immutable SHAs
  • add job timeout, branch slug fallback, and safer push behavior
  • use env command variables for format/lint/test

Notes

  • only updates .github/workflows/jira_codex_pr.yml

Copilot AI review requested due to automatic review settings February 13, 2026 16:04
@jirhiker jirhiker merged commit 299dd34 into staging Feb 13, 2026
11 checks passed
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 329e7617b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

fi

git fetch origin "$BASE_BRANCH"
CHANGED=$(git diff --name-only "origin/$BASE_BRANCH...HEAD" || true)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Inspect forbidden paths in uncommitted changes

The forbidden-path guard runs before the commit, but git diff --name-only "origin/$BASE_BRANCH...HEAD" only compares commits (git diff -h shows the <commit>...<commit> form), so it does not include the working-tree edits produced by Codex in this job. That means changes under .github/, infra/, terraform/, etc. can bypass this policy and still get committed/pushed in later steps.

Useful? React with 👍 / 👎.

set -euo pipefail
curl -fsS --retry 3 --retry-all-errors -u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
-H "Accept: application/json" \
"$JIRA_BASE_URL/rest/api/3/issue/$JIRA_KEY" > jira.json
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid committing Jira payload data into the repo

This writes the Jira API response to jira.json in the repository root, and later the workflow does git add -A, which stages all untracked files; as a result, Jira issue payloads can be committed into PRs unintentionally. This also causes the "there is a diff" check to pass even when no real code changes were made, because jira.json itself creates a diff.

Useful? React with 👍 / 👎.

run: |
set -euo pipefail
git add -A
git commit -m "${JIRA_KEY}: ${SUMMARY}" || echo "Nothing new to commit."
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fail the job when git commit returns an error

Using git commit ... || echo "Nothing new to commit." masks all commit failures and continues to push. Since the previous step already requires a dirty worktree, a non-zero git commit here indicates a real error (for example identity/rejection issues), and suppressing it can push a branch without the generated changes and create stale or empty PR updates.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new GitHub Actions workflow for automated Jira ticket implementation using Codex (an AI code generation tool). The workflow is triggered via repository_dispatch events, fetches Jira ticket details, generates code changes via Codex, runs quality checks (format/lint/test), and creates or updates a pull request. The workflow includes several hardening improvements like fail-fast patterns, retry logic on API calls, safe multiline GITHUB_OUTPUT handling, job timeouts, and guardrails for allowed projects and forbidden paths.

Changes:

  • Adds comprehensive Jira-to-PR automation workflow with AI code generation
  • Implements security guardrails (project allowlists, required labels, forbidden path protection)
  • Includes retry logic and fail-fast patterns for Jira API calls

Comment on lines +44 to +62
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

- name: Ensure jq exists
run: |
set -euo pipefail
if ! command -v jq >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y jq
fi

- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Set up uv (with cache)
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The action versions are pinned to SHAs which deviates from the established convention in this repository. All other workflows (tests.yml, format_code.yml, CD_production.yml) use semantic version tags (v4, v5, v6) for actions like checkout, setup-python, and setup-uv. For consistency and maintainability, consider using semantic version tags instead of SHA pins. Semantic versions are easier to understand, manage, and update, while still providing security through trusted GitHub Actions publishers.

Copilot uses AI. Check for mistakes.
- name: Sync dependencies (pyproject/uv.lock)
run: |
set -euo pipefail
uv sync --all-extras --dev
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The workflow uses 'uv sync --all-extras --dev' which differs from the established pattern 'uv sync --locked --all-extras --dev' used in tests.yml:66. The '--locked' flag ensures that uv uses the exact versions from uv.lock without updating it, which is critical for deterministic CI builds. While line 69 checks for uv.lock existence, without --locked, uv might still update the lock file if dependencies are out of sync, undermining the determinism check.

Suggested change
uv sync --all-extras --dev
uv sync --locked --all-extras --dev

Copilot uses AI. Check for mistakes.
Comment on lines +352 to +354
--head "$BRANCH")
echo "Created PR: $URL"
echo "PR_URL=$URL" >> $GITHUB_OUTPUT
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The PR number is only set in the GITHUB_OUTPUT when an existing PR is found (line 346), but not when a new PR is created (lines 348-355). This means downstream steps that might depend on PR_NUMBER will not have access to it for newly created PRs. While the current workflow doesn't appear to use PR_NUMBER after this step, for consistency and future-proofing, the PR number should be extracted and set for new PRs as well. The 'gh pr create' command returns a URL, so you'd need to parse it or use 'gh pr list' afterward to get the number.

Suggested change
--head "$BRANCH")
echo "Created PR: $URL"
echo "PR_URL=$URL" >> $GITHUB_OUTPUT
--head "$BRANCH")
NUM="${URL##*/}"
echo "Created PR: $URL"
echo "PR_URL=$URL" >> $GITHUB_OUTPUT
echo "PR_NUMBER=$NUM" >> $GITHUB_OUTPUT

Copilot uses AI. Check for mistakes.
set -euo pipefail
git add -A
git commit -m "${JIRA_KEY}: ${SUMMARY}" || echo "Nothing new to commit."
git push --set-upstream origin "$BRANCH"
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The 'git push' command on line 303 lacks the '--force-with-lease' option or any handling for diverged branches. If the remote branch has been updated by another process (or manually) since the workflow last fetched it, the push will fail. Since line 285-293 checks for changes but line 302 might skip the commit, there's a scenario where the workflow could fail here unexpectedly. Consider adding error handling or using '--force-with-lease' if overwrites are intentional.

Suggested change
git push --set-upstream origin "$BRANCH"
git push --force-with-lease --set-upstream origin "$BRANCH"

Copilot uses AI. Check for mistakes.
Comment on lines +295 to +302
- name: Commit & push (idempotent)
env:
JIRA_KEY: ${{ steps.jira.outputs.JIRA_KEY }}
SUMMARY: ${{ steps.issue.outputs.SUMMARY }}
run: |
set -euo pipefail
git add -A
git commit -m "${JIRA_KEY}: ${SUMMARY}" || echo "Nothing new to commit."
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Git user configuration is missing before the commit step. Other workflows in this repository (CD_production.yml:103-104, CD_staging.yml:104-105) explicitly set 'git config --global user.name' and 'user.email' before committing. Without this configuration, the git commit on line 302 will fail with an error about missing user identity. Add a step to configure git user before the commit step.

Copilot uses AI. Check for mistakes.
git fetch origin "$BASE_BRANCH"
CHANGED=$(git diff --name-only "origin/$BASE_BRANCH...HEAD" || true)

if echo "$CHANGED" | grep -E "$FORBIDDEN_REGEX"; then
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The grep command on line 268 uses '-E' for extended regex matching of the forbidden paths. However, the command doesn't handle the case where CHANGED is empty (no files changed). An empty string piped to grep will cause grep to hang waiting for stdin. While line 266 uses '|| true' which should prevent errors, if there are no changes, $CHANGED will be empty and 'echo ""' | grep -E' will not match anything and exit 1, which is correct. However, for clarity and robustness, consider adding a check for empty CHANGED before the grep.

Suggested change
if echo "$CHANGED" | grep -E "$FORBIDDEN_REGEX"; then
if [ -n "$CHANGED" ] && echo "$CHANGED" | grep -E "$FORBIDDEN_REGEX"; then

Copilot uses AI. Check for mistakes.
Comment on lines +281 to +283
eval "$FORMAT_COMMAND"
eval "$LINT_COMMAND"
eval "$TEST_COMMAND"
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The 'eval' command is used to execute commands from environment variables, which could be a security risk if those variables are ever influenced by untrusted input. While in this case the commands come from the workflow's own env section (lines 33-35), this pattern should be avoided. Instead, directly execute the commands without eval, or if flexibility is needed, use a more restrictive approach like parameter arrays.

Suggested change
eval "$FORMAT_COMMAND"
eval "$LINT_COMMAND"
eval "$TEST_COMMAND"
$FORMAT_COMMAND
$LINT_COMMAND
$TEST_COMMAND

Copilot uses AI. Check for mistakes.
REQUIRED_CUSTOM_FIELD_ID: "" # optional; e.g. "customfield_12345" (leave empty to disable)

# ---------------- BRANCH/PR ----------------
BASE_BRANCH: "main"
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The workflow hardcodes BASE_BRANCH as "main" (line 24), but all other workflows in this repository use "production" as the main branch (see tests.yml:8, CD_production.yml:5, format_code.yml:7). The repository appears to follow a production/staging/transfer branch model, not a main-based model. Using "main" as the base branch will likely cause issues if that branch doesn't exist or isn't the actual integration target.

Suggested change
BASE_BRANCH: "main"
BASE_BRANCH: "production"

Copilot uses AI. Check for mistakes.
Comment on lines +121 to +137
{
echo "SUMMARY<<EOF"
echo "$SUMMARY"
echo "EOF"
echo "ISSUE_TYPE<<EOF"
echo "$ISSUE_TYPE"
echo "EOF"
echo "PROJECT_KEY<<EOF"
echo "$PROJECT_KEY"
echo "EOF"
echo "LABELS<<EOF"
echo "$LABELS"
echo "EOF"
echo "DESC<<EOF"
echo "$DESC_TRIMMED"
echo "EOF"
} >> "$GITHUB_OUTPUT"
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The GITHUB_OUTPUT assignments use heredoc syntax (EOF) for multiline safety, which is good. However, for LABELS (line 131-133) and PROJECT_KEY (line 128-130), which are typically single-line values, this might be unnecessary. More importantly, the DESC_TRIMMED value is truncated to MAX_DESC_CHARS (line 119), but if it contains the string "EOF" naturally, the heredoc parsing could break. Consider using a more unique delimiter like "EODESC_${RANDOM}" or validate that the content doesn't contain the delimiter.

Copilot uses AI. Check for mistakes.
-H "Content-Type: application/json" \
-X POST \
--data "$payload" \
"$JIRA_BASE_URL/rest/api/3/issue/$JIRA_KEY/comment" > /dev/null
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

If the Jira comment API call fails (line 387-392), the workflow will fail due to 'set -euo pipefail' on line 365. However, posting a comment back to Jira is not critical to the workflow's main purpose (creating/updating the PR). Consider adding '|| true' to the curl command or wrapping it in a conditional to make this step non-blocking, so that PR creation succeeds even if Jira commenting fails. This would make the workflow more resilient to Jira API issues.

Suggested change
"$JIRA_BASE_URL/rest/api/3/issue/$JIRA_KEY/comment" > /dev/null
"$JIRA_BASE_URL/rest/api/3/issue/$JIRA_KEY/comment" > /dev/null || {
echo "Warning: Failed to post comment to Jira; continuing without failing workflow." >&2
}

Copilot uses AI. Check for mistakes.
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.

2 participants